[](https://colab.research.google.com/github/QuCAI-Lab/QHack2022/blob/dev/solutions_notebook.ipynb)

[](https://github.com/QuCAI-Lab/QHack2022/blob/dev/LICENSE.md)

[](https://github.com/QuCAI-Lab/QHack2022/graphs/commit-activity)
[](https://github.com/QuCAI-Lab/QHack2022/releases)
[](https://github.com/QuCAI-Lab/QHack2022/commits)
[](https://github.com/QuCAI-Lab/QHack2022/fork)

  QHack2022  -  Coding Challenge Solutions

 Hackeinberg Team Repository

 Author¹: Lucas Camponogara Viera
¹National Taiwan Normal University (國立臺灣師範大學).

[comment]: <> ()

View source on GitHub.

---

In this jupyter notebook, the reader may find some of the solutions of the QHack2022 coding challenge.

---

```
%autosave 30
```

    Autosaving every 30 seconds

#    License

You don't wanna be a copycat laggard.

```
#@title Copyright 2022.
# This code is part of QHack2022.
#
# (C) Copyright NTNU QuCAI-Lab, 2022.
#
# This code is licensed under the Creative Commons Zero v1.0 Universal License.
# You may obtain a copy of the License at https://github.com/QuCAI-Lab/QHack2022/blob/dev/LICENSE.md.
```

#    Google Drive Credentials

**Run this cell only if you wish to read the dataset from your Google Drive folder.**

```
from google.colab import drive
drive.mount('/content/gdrive')
```

    Mounted at /content/gdrive

```
!find . -maxdepth 2 -type d -exec ls -ld "{}" \;
```

    drwxr-xr-x 1 root root 4096 Mar  6 03:24 .
    drwxr-xr-x 1 root root 4096 Feb 18 14:33 ./.config
    drwxr-xr-x 3 root root 4096 Feb 18 14:32 ./.config/logs
    drwxr-xr-x 2 root root 4096 Feb 18 14:32 ./.config/configurations
    drwx------ 6 root root 4096 Mar  6 03:24 ./gdrive
    dr-x------ 2 root root 4096 Mar  6 03:24 ./gdrive/.shortcut-targets-by-id
    dr-x------ 2 root root 4096 Mar  6 03:24 ./gdrive/.file-revisions-by-id
    drwx------ 9 root root 4096 Mar  6 03:24 ./gdrive/MyDrive
    drwx------ 2 root root 4096 Mar  6 03:24 ./gdrive/Shareddrives
    drwx------ 5 root root 4096 Mar  6 03:24 ./gdrive/.Trash-0
    drwxr-xr-x 1 root root 4096 Feb 18 14:33 ./sample_data

```
!pwd # Display the current working directory.
print('')

#%cd /content/gdrive/'My Drive/'
!ls -l # List files/folders in the current directory.
```

    /content

    total 8
    drwx------ 6 root root 4096 Mar  6 03:24 gdrive
    drwxr-xr-x 1 root root 4096 Feb 18 14:33 sample_data

**To read a file:**

```python
dataset_dir = F'/content/gdrive/My Drive/{...}.extension'
with open(dataset_dir, 'r') as f:
  input = f.readlines()
```

**Example:**

```
dataset_dir='/content/gdrive/My Drive/Hackeinberg/dataset/'
in1 = dataset_dir+'1.in'
in2 = dataset_dir+'2.in'

def read(file):
  with open(file, 'r') as f:
    filein = f.readlines()
    inputs = filein[0].split(";")
    print('filein = {}\nfilein[0] = {}\ninputs = {}'.format(filein, filein[0], inputs))
    return inputs
```

```
inputs = read(in1)
input1 = ['4','Hadamard','0','CNOT','0,1','Hadamard','0']
inputs == input1
```

    filein = ['4;Hadamard;0;CNOT;0,1;Hadamard;0']
    filein[0] = 4;Hadamard;0;CNOT;0,1;Hadamard;0
    inputs = ['4', 'Hadamard', '0', 'CNOT', '0,1', 'Hadamard', '0']

    True

```
inputs = read(in2)
input2 = ['4','DoubleExcitation','0,1,2,3','0.732','SingleExcitation','0,1','1.0']
inputs == input2
```

    filein = ['4;DoubleExcitation;0,1,2,3;0.732;SingleExcitation;0,1;1.0']
    filein[0] = 4;DoubleExcitation;0,1,2,3;0.732;SingleExcitation;0,1;1.0
    inputs = ['4', 'DoubleExcitation', '0,1,2,3', '0.732', 'SingleExcitation', '0,1', '1.0']

    True

#    Colab's memory card

"Executing a code cell within a GPU or TPU runtime does not mean that GPU or TPU is being used."

[For more information about GPU usage on Colab](https://research.google.com/colaboratory/faq.html#gpu-availability).

## CPU

1. Click on the tab `Runtime -> Change runtime type`.
1. Click on `Hardware accelerator`.
1. Select `None` from the drop-down menu and click `Save`.
1. Click `Connect` in the upper right corner and select `Connect to hosted runtime` (if not automatically).

```
!free -h    # Show CPU Memory.
              # 'free' = wasted memory (not being used).
              # 'available' = memory for allocation.
              # 'buff/cache' used memory for cache (Data has been read) and buffer (data is being written).
              # 'free' + 'buff/cache' = 'available'.

print('')
!lscpu      # Show CPU Architecture.
```

                  total        used        free      shared  buff/cache   available
    Mem:            12G        927M        7.8G        1.2M        4.0G         11G
    Swap:            0B          0B          0B

    Architecture:        x86_64
    CPU op-mode(s):      32-bit, 64-bit
    Byte Order:          Little Endian
    CPU(s):              2
    On-line CPU(s) list: 0,1
    Thread(s) per core:  2
    Core(s) per socket:  1
    Socket(s):           1
    NUMA node(s):        1
    Vendor ID:           GenuineIntel
    CPU family:          6
    Model:               79
    Model name:          Intel(R) Xeon(R) CPU @ 2.20GHz
    Stepping:            0
    CPU MHz:             2200.220
    BogoMIPS:            4400.44
    Hypervisor vendor:   KVM
    Virtualization type: full
    L1d cache:           32K
    L1i cache:           32K
    L2 cache:            256K
    L3 cache:            56320K
    NUMA node0 CPU(s):   0,1
    Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap xsaveopt arat md_clear arch_capabilities

## GPU

1. Click on the tab `Runtime -> Change runtime type`.
1. Click on `Hardware accelerator`.
1. Select `GPU` from the drop-down menu and click `Save`.
1. Click `Connect` in the upper right corner and select `Connect to hosted runtime` (if not automatically).

```
!nvidia-smi #Show GPU Card
```

    NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.

#    Pip Install

**Run this cell only if you are running this jupyter notebook on Google Colab.**

Installing `PennyLane`.

```
'''
# Installing a non-default library

!python3 -m pip install <library_name>

# Alternative

!apt-get -qq install -y <library_name> && python3 -m pip install -U <library_name>

# Upgrading an installed library

!python3 -m pip install -U --upgrade <library_name>
'''
```

    '\n# Installing a non-default library\n\n!python3 -m pip install <library_name>\n\n# Alternative \n\n!apt-get -qq install -y <library_name> && python3 -m pip install -U <library_name> \n\n# Upgrading an installed library\n\n!python3 -m pip install -U --upgrade <library_name> \n'

```
!pip --version
```

    pip 21.1.3 from /usr/local/lib/python3.7/dist-packages/pip (python 3.7)

**PennyLane:**

```
!python3 -m pip install PennyLane==0.21.0
```

    Collecting PennyLane==0.21.0
      Downloading PennyLane-0.21.0-py3-none-any.whl (800 kB)
         |████████████████████████████████| 800 kB 30.5 MB/s
    Requirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from PennyLane==0.21.0) (1.21.5)
    Collecting pennylane-lightning>=0.21
      Downloading PennyLane_Lightning-0.22.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (8.2 MB)
         |████████████████████████████████| 8.2 MB 40.1 MB/s
    Requirement already satisfied: networkx in /usr/local/lib/python3.7/dist-packages (from PennyLane==0.21.0) (2.6.3)
    Requirement already satisfied: autograd in /usr/local/lib/python3.7/dist-packages (from PennyLane==0.21.0) (1.3)
    Collecting retworkx
      Downloading retworkx-0.11.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (1.6 MB)
         |████████████████████████████████| 1.6 MB 45.8 MB/s
    Requirement already satisfied: cachetools in /usr/local/lib/python3.7/dist-packages (from PennyLane==0.21.0) (4.2.4)
    Requirement already satisfied: scipy in /usr/local/lib/python3.7/dist-packages (from PennyLane==0.21.0) (1.4.1)
    Requirement already satisfied: appdirs in /usr/local/lib/python3.7/dist-packages (from PennyLane==0.21.0) (1.4.4)
    Collecting toml
      Downloading toml-0.10.2-py2.py3-none-any.whl (16 kB)
    Collecting autoray
      Downloading autoray-0.2.5-py3-none-any.whl (16 kB)
    Collecting semantic-version==2.6
      Downloading semantic_version-2.6.0-py3-none-any.whl (14 kB)
    Collecting ninja
      Downloading ninja-1.10.2.3-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl (108 kB)
         |████████████████████████████████| 108 kB 55.0 MB/s
    Requirement already satisfied: future>=0.15.2 in /usr/local/lib/python3.7/dist-packages (from autograd->PennyLane==0.21.0) (0.16.0)
    Installing collected packages: ninja, toml, semantic-version, retworkx, pennylane-lightning, autoray, PennyLane
    Successfully installed PennyLane-0.21.0 autoray-0.2.5 ninja-1.10.2.3 pennylane-lightning-0.22.0 retworkx-0.11.0 semantic-version-2.6.0 toml-0.10.2

#    Dependencies

Alternatively, you may resort to the `environment.txt` file from the [official GitHub repository](https://github.com/QuCAI-Lab/QHack2022/blob/dev/requirements.txt).

```
import sys, subprocess
import pennylane as qml
from pennylane import numpy as np
import matplotlib

if "google.colab" in sys.modules:
  cmd = "python3 -m pip install --upgrade watermark"
  process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
```

```
# If you get hit by the error "No module named watermark", run this cell twice!

%load_ext watermark
%watermark -a 'Lucas Camponogara Viera' -gu 'camponogaraviera' -e 'vieracamponogara@gmail.com' -ws 'https://qucai-lab.github.io/' -w -u -d -v -m -iv -r -b
```

    The watermark extension is already loaded. To reload it, use:
      %reload_ext watermark
    Author: Lucas Camponogara Viera

    Github username: camponogaraviera

    Email: vieracamponogara@gmail.com

    Website: https://qucai-lab.github.io/

    Last updated: 2022-03-22

    Python implementation: CPython
    Python version       : 3.7.12
    IPython version      : 5.5.0

    Compiler    : GCC 7.5.0
    OS          : Linux
    Release     : 5.4.144+
    Machine     : x86_64
    Processor   : x86_64
    CPU cores   : 2
    Architecture: 64bit

    Git repo:

    Git branch:

    sys       : 3.7.12 (default, Jan 15 2022, 18:48:18)
    [GCC 7.5.0]
    pennylane : 0.21.0
    IPython   : 5.5.0
    matplotlib: 3.2.2

    Watermark: 2.3.0

```
#%watermark?
```

```
qml.about()
```

    WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
    Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
    To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.

    Name: PennyLane
    Version: 0.21.0
    Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
    Home-page: https://github.com/XanaduAI/pennylane
    Author: None
    Author-email: None
    License: Apache License 2.0
    Location: /usr/local/lib/python3.7/dist-packages
    Requires: autoray, autograd, cachetools, numpy, scipy, toml, retworkx, networkx, semantic-version, pennylane-lightning, appdirs
    Required-by: PennyLane-Lightning
    Platform info:           Linux-5.4.144+-x86_64-with-Ubuntu-18.04-bionic
    Python version:          3.7.12
    Numpy version:           1.21.5
    Scipy version:           1.4.1
    Installed devices:

```
import IPython
print(IPython.sys_info())
```

    {'commit_hash': 'b467d487e',
     'commit_source': 'installation',
     'default_encoding': 'UTF-8',
     'ipython_path': '/usr/local/lib/python3.7/dist-packages/IPython',
     'ipython_version': '5.5.0',
     'os_name': 'posix',
     'platform': 'Linux-5.4.144+-x86_64-with-Ubuntu-18.04-bionic',
     'sys_executable': '/usr/bin/python3',
     'sys_platform': 'linux',
     'sys_version': '3.7.12 (default, Jan 15 2022, 18:48:18) \n[GCC 7.5.0]'}

#    Problem Solutions

## PennyLane101

### pennylane101_100_OrderMatters_solution

Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/pennylane101_100_OrderMatters_template/order_matters_template.py).

```
#! /usr/bin/python3

import sys
import pennylane as qml
from pennylane import numpy as np

def compare_circuits(angles):
    """Given two angles, compare two circuit outputs that have their order of operations flipped: RX then RY VERSUS RY then RX.
    Args:
        - angles (np.ndarray): Two angles
    Returns:
        - (float): | < \sigma^x >_1 - < \sigma^x >_2 |
    """

    # QHACK #

    # define a device and quantum functions/circuits here

    # QHACK #

    dev=qml.device("default.qubit", wires=1) # Define the quantum device.

    @qml.qnode(dev)
    def circuit1(angles):                    # Create the first quantum function.
      qml.RX(angles[0], wires=0)
      qml.RY(angles[1], wires=0)
      return qml.expval(qml.PauliX(0))

    @qml.qnode(dev)
    def circuit2(angles):                    # Create the second quantum function.
      qml.RY(angles[1], wires=0)
      qml.RX(angles[0], wires=0)
      return qml.expval(qml.PauliX(0))

    abs=np.sqrt((circuit1(angles)-circuit2(angles))**2) # Process the results from both circuits.
    return float(abs)                                   # Return the absolute value of the difference between the circuit outputs.

    # QHACK #
'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    angles = np.array(sys.stdin.read().split(","), dtype=float)
    output = compare_circuits(angles)
    print(f"{output:.6f}")
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    angles = np.array(sys.stdin.read().split(","), dtype=float)\n    output = compare_circuits(angles)\n    print(f"{output:.6f}")\n'

```
in1 = [3.79894785, 0.71678115]
ans1=1.177019
in2  = [5.88344281, 0.30672784]
ans2=0.023805
ans=[ans1, ans2]
inputs=[in1, in2]

for angles in inputs:
  output=compare_circuits(angles)
  print(f"{output:.6f}", round(output,6) == ans[inputs.index(angles)])
```

    1.177019 True
    0.023805 True

```
dev=qml.device("default.qubit", wires=1)
@qml.qnode(dev)
def circuit(angles):
  qml.RX(angles[0], wires=0)
  qml.RY(angles[1], wires=0)
  return qml.expval(qml.PauliX(0))
print(circuit(in1))
fig, ax = qml.draw_mpl(circuit, decimals=6)(in2)
```

### pennylane101_200_KnowYourDevices_solution

Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/pennylane101_200_KnowYourDevices_template/know_your_devices_template.py).

```
#! /usr/bin/python3
import sys
import pennylane as qml
from pennylane import numpy as np

def matrix_norm(mixed_state, pure_state):
    """Computes the matrix one-norm of the difference between mixed and pure states.

    Args:
        - mixed_state (np.tensor): A density matrix
        - pure_state (np.tensor): A pure state

    Returns:
        - (float): The matrix one-norm
    """

    return np.sum(np.abs(mixed_state - np.outer(pure_state, np.conj(pure_state))))

def compare_circuits(num_wires, params):
    """Function that returns the matrix norm between the mixed- and pure-state versions of the same state.

    Args:
        - num_wires (int): The number of qubits / wires
        - params (list(np.ndarray)): Two arrays with num_wires floats that correspond to angles of y-rotations
        for each wire

    Returns:
        - mat_norm (float): The matrix one-norm
    """

    # QHACK #
    # define devices

    # add a decorator here
    dev=qml.device("default.qubit", wires=num_wires)
    @qml.qnode(dev)
    def pure_circuit():
        """A circuit that contains `num_wires` y-rotation gates.
        The argument params[0] are the parameters you should use here to define the y-rotations.

        Returns:
            - (np.tensor): A state vector
        """
        # create the circuit here
        for i in range(len(params[0])):
          qml.RY(params[0][i], wires=i)
        return qml.state()

    # add a decorator here
    dev2=qml.device("default.mixed", wires=num_wires)
    @qml.qnode(dev2)
    def mixed_circuit():
        """A circuit that contains `num_wires` y-rotation gates.
        The argument params[1] are the parameters you should use here to define the y-rotations.

        Returns:
            - (np.tensor): A density matrix
        """
        # create the circuit here
        for i in range(len(params[1])):
          qml.RY(params[1][i], wires=i)
        return qml.state()

    # QHACK #

    # DO NOT MODIFY any of the next lines in this scope
    mixed_state = mixed_circuit()
    pure_state = pure_circuit()
    mat_norm = matrix_norm(mixed_state, pure_state)

    return mat_norm

'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    inputs = sys.stdin.read().split(",")
    num_wires = int(inputs[0])
    l = int(len(inputs[1:]) / 2)
    params = [
        np.array(inputs[1 : (l + 1)], dtype=float),  # for pure circuit
        np.array(inputs[(l + 1) :], dtype=float),  # for mixed circuit
    ]

    output = compare_circuits(num_wires, params)
    print(f"{output:.6f}")
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    inputs = sys.stdin.read().split(",")\n    num_wires = int(inputs[0])\n    l = int(len(inputs[1:]) / 2)\n    params = [\n        np.array(inputs[1 : (l + 1)], dtype=float),  # for pure circuit\n        np.array(inputs[(l + 1) :], dtype=float),  # for mixed circuit\n    ]\n\n    output = compare_circuits(num_wires, params)\n    print(f"{output:.6f}")\n'

```
in1 = [2, 0.17085641, 4.69202289, 0.4478956, 3.69402896]
ans1= 1.765778
in2 = [4, 2.1051399, 5.02920013, 1.48450097, 4.06791362, 5.19513356, 0.65491091, 3.0127959, 4.03855719]
ans2= 15.747060
ans=[ans1,ans2]
input=[in1, in2]

for inputs in input:
  num_wires = int(inputs[0])
  l = int(len(inputs[1:]) / 2)
  params = [
      np.array(inputs[1 : (l + 1)], dtype=float),  # for pure state circuit
      np.array(inputs[(l + 1) :], dtype=float),  # for mixed state circuit
  ]

  output = compare_circuits(num_wires, params)
  print(f"{output:.6f}", output.round(6) == ans[input.index(inputs)])
```

    1.765778 True
    15.747060 True

### pennylane101_300_superdense_coding_solution

Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/pennylane101_300_superdense_coding_template/superdense_coding_template.py).

```
#! /usr/bin/python3

import sys
import pennylane as qml
from pennylane import numpy as np

dev = qml.device("default.qubit", wires=2)

@qml.qnode(dev)
def superdense_coding(bits, alpha):
    """Construct a quantum circuit that implements superdense coding, given a not necessarily maximally entangled state

    Args:
        - bits (int): 0 (binary: 00), 1 (binary: 01), 2 (binary: 10), or 3 (binary: 11), Alice's bits that she wants to communicate to Bob.
        - alpha (float): angle parametrizing the entangled state

    Returns:
        - (np.tensor): Probability that Bob will guess Alice's bits correctly
    """

    # QHACK #

    # Prepare entangled state here
    qml.U3(alpha*2,0,0, wires=0)
    qml.CNOT(wires=[0, 1])
    # Implement Alice's operations on her qubit here
    if bits == 0:
      pass
    elif bits==1:
      qml.PauliX(0)
    elif bits == 3:
      qml.PauliX(0)
      qml.PauliZ(0)
    else:
      qml.PauliZ(0)
    # Implement Bob's measurement procedure here
    qml.CNOT(wires=[0, 1])
    qml.Hadamard(wires=0)

    # QHACK #

    return qml.probs(wires=[0, 1])

def return_probs(bits, alpha):
    """Returns the output of the superdense_coding function for a given index (bits)"""
    # DO NOT MODIFY anything in this code block
    return superdense_coding(bits, alpha)[bits].numpy()

'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    inputs = sys.stdin.read().split(",")
    output = return_probs(int(inputs[0]), float(inputs[1]))
    print(f"{output:.6f}")
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    inputs = sys.stdin.read().split(",")\n    output = return_probs(int(inputs[0]), float(inputs[1]))\n    print(f"{output:.6f}")\n'

```
param = [[0, 0.7854], [2, 0.4]]
ans=[1.0, 0.858678]
i=0
for x in param:
  output = return_probs(int(x[0]), float(x[1]))
  print(f"{output:.6f}", round(output, 6) == ans[i])
  i+=1
```

    1.000000 True
    0.858678 True

```
input=[[0, 0.7854], [1, 0.7854], [2, 0.4], [3, 0.4]]
label_dict = {0:'I', 1: 'X', 2: 'Z', 3: 'ZX'}

i=0
for inputs in input:
  print('Alice\'s applied gate:', label_dict[i])
  qml.draw_mpl(superdense_coding, decimals=6)(inputs[0], inputs[1])
  i+=1
```

### pennylane101_400_FiniteDifferenceGradient_solution
Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/pennylane101_400_FiniteDifferenceGradient_template/finite_difference_template.py).

```
#! /usr/bin/python3

import sys
import pennylane as qml
from pennylane import numpy as np

dev = qml.device("default.qubit", wires=3)

def my_finite_diff_grad(params):
    """Function that returns the gradients of the cost function (defined below) with respect
    to all parameters in params.

    Args:
        - params (np.ndarray): The parameters needed to create the variational circuit.

    Returns:
        - gradients (np.ndarray): the gradient w.r.t. each parameter
    """

    gradients = np.zeros([len(params)])
    for i in range(len(params)):
        # QHACK #
        delta = 0.000001
        tensor1=params.copy()
        tensor1[i]+=delta/2
        tensor2=params.copy()
        tensor2[i]-=delta/2
        grad=(cost(tensor1)-cost(tensor2))/delta
        gradients[i]=grad

        # QHACK #

    return gradients

def variational_circuit(params):
    """A layered variational circuit. The first layer comprises of x, y, and z rotations on wires
    0, 1, and 2, respectively. The second layer is a ring of CNOT gates. The final layer comprises
    of x, y, and z rotations on wires 0, 1, and 2, respectively.
    """

    # DO NOT MODIFY anything in this code block
    qml.RX(params[0], wires=0)
    qml.RY(params[1], wires=1)
    qml.RZ(params[2], wires=2)

    qml.broadcast(qml.CNOT, wires=[0, 1, 2], pattern="ring")

    qml.RX(params[3], wires=0)
    qml.RY(params[4], wires=1)
    qml.RZ(params[5], wires=2)

    qml.broadcast(qml.CNOT, wires=[0, 1, 2], pattern="ring")

@qml.qnode(dev)
def cost(params):
    """A QNode that pairs the variational_circuit with an expectation value measurement.

    Args:
        - params (np.ndarray): Variational circuit parameters

    Returns:
        - (float): qml.expval(qml.PauliY(0) @ qml.PauliZ(2))
    """

    # DO NOT MODIFY anything in this code block
    variational_circuit(params)
    return qml.expval(qml.PauliY(0) @ qml.PauliZ(2))

'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    params = np.array(sys.stdin.read().split(","), dtype=float)
    output = my_finite_diff_grad(params)
    print(*output, sep=",")
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    params = np.array(sys.stdin.read().split(","), dtype=float)\n    output = my_finite_diff_grad(params)\n    print(*output, sep=",")\n'

```
in1=np.array([0.37454012, 0.95071431, 0.73199394, 0.59865848, 0.15601864, 0.15599452], dtype=float)
ans1 = -0.06518877,-0.02728919,-0.0,-0.09339346,-0.76106754,0.0
in2=np.array([0.7286407160000131, 0.604431298517586, 0.5966297756635066, 0.3361356009032367, 0.1633339827097956, 0.2086455467304198], dtype=float)
ans2=-0.0293733,-0.08410477,0.0,-0.09423404,-0.59528383,0.0

for params in [in1, in2]:
  output=my_finite_diff_grad(params)
  print(*output.round(6), sep=",")
```

    -0.065189,-0.027289,-0.0,-0.093393,-0.761068,0.0
    -0.029373,-0.084105,0.0,-0.094234,-0.595284,0.0

### pennylane101_500_BitflipErrorCode_solution

Template [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/pennylane101_500_BitflipErrorCode_template/bitflip_error_template.py).

```
#! /usr/bin/python3

import sys
import pennylane as qml
from pennylane import numpy as np

def error_wire(circuit_output):
    """Function that returns an error readout.

    Args:
        - circuit_output (?): the output of the `circuit` function.

    Returns:
        - (np.ndarray): a length-4 array that reveals the statistics of the
        error channel. It should display your algorithm's statistical prediction for
        whether an error occurred on wire `k` (k in {1,2,3}). The zeroth element represents
        the probability that a bitflip error does not occur.

        e.g., [0.28, 0.0, 0.72, 0.0] means a 28% chance no bitflip error occurs, but if one
        does occur it occurs on qubit #2 with a 72% chance.
    """

    # QHACK #

    # process the circuit output here and return which qubit was the victim of a bitflip error!
    return [circuit_output[0], circuit_output[3], circuit_output[2], circuit_output[1]]

    # QHACK #

dev = qml.device("default.mixed", wires=3)

@qml.qnode(dev)
def circuit(p, alpha, tampered_wire):
    """A quantum circuit that will be able to identify bitflip errors.

    DO NOT MODIFY any already-written lines in this function.

    Args:
        p (float): The bit flip probability
        alpha (float): The parameter used to calculate `density_matrix(alpha)`
        tampered_wire (int): The wire that may or may not be flipped (zero-index)

    Returns:
        Some expectation value, state, probs, ... you decide!
    """

    qml.QubitDensityMatrix(density_matrix(alpha), wires=[0, 1, 2])

    # QHACK #

    # put any input processing gates here
    qml.CNOT(wires=[0,1])
    qml.CNOT(wires=[0,2])

    qml.BitFlip(p, wires=int(tampered_wire))

    # put any gates here after the bitflip error has occurred
    qml.CNOT(wires=[0,1])
    qml.CNOT(wires=[0,2])
    qml.Toffoli(wires=[2,1,0])
    # return something!
    return  qml.probs(wires=[1,2])
    # QHACK #

def density_matrix(alpha):
    """Creates a density matrix from a pure state."""
    # DO NOT MODIFY anything in this code block
    psi = alpha * np.array([1, 0], dtype=float) + np.sqrt(1 - alpha**2) * np.array(
        [0, 1], dtype=float
    )
    psi = np.kron(psi, np.array([1, 0, 0, 0], dtype=float))
    return np.outer(psi, np.conj(psi))
'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    inputs = np.array(sys.stdin.read().split(","), dtype=float)
    p, alpha, tampered_wire = inputs[0], inputs[1], int(inputs[2])

    error_readout = np.zeros(4, dtype=float)
    circuit_output = circuit(p, alpha, tampered_wire)
    error_readout = error_wire(circuit_output)

    print(*error_readout, sep=",")
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    inputs = np.array(sys.stdin.read().split(","), dtype=float)\n    p, alpha, tampered_wire = inputs[0], inputs[1], int(inputs[2])\n\n    error_readout = np.zeros(4, dtype=float)\n    circuit_output = circuit(p, alpha, tampered_wire)\n    error_readout = error_wire(circuit_output)\n\n    print(*error_readout, sep=",")\n'

```
in1=np.array([0.8, 0.1234, 0], dtype=float)
ans1=0.19999999999999993,0.7999999999999999,0.0,0.0
in2=np.array([1, 0.9876, 2], dtype=float)
ans2=0.0,0.0,0.0,1.0
ans=[ans1,ans2]
j=0
for inputs in [in1, in2]:
  p, alpha, tampered_wire = inputs[0], inputs[1], int(inputs[2])
  error_readout = np.zeros(4, dtype=float)
  circuit_output = circuit(p, alpha, tampered_wire)
  error_readout = error_wire(circuit_output)
  print(*error_readout, sep=",")
  print(*[error_readout[i]==ans[j][i] for i in range(4)])
  fig, ax = qml.draw_mpl(circuit, decimals=6)(p,alpha,tampered_wire)
  j+=1
```

## Algorithms

### algorithms_100_DeutschJozsa_solution

Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/algorithms_100_DeutschJozsa_template/deutsch_jozsa_template.py).

```
import sys
import pennylane as qml
from pennylane import numpy as np

def deutsch_jozsa(oracle):
    """This function will determine whether an oracle defined by a function f is constant or balanced.

    Args:
        - oracle (function): Encoding of the f function as a quantum gate. The first two qubits refer to the input and the third to the output.

    Returns:
        - (str): "constant" or "balanced"
    """

    dev = qml.device("default.qubit", wires=3, shots=1)

    @qml.qnode(dev)
    def circuit():
        """Implements the Deutsch Jozsa algorithm."""

        # QHACK #

        # Insert any pre-oracle processing here
        qml.Hadamard(wires=0)
        qml.Hadamard(wires=1)
        qml.PauliX(wires=2)
        qml.Hadamard(wires=2)

        oracle()  # DO NOT MODIFY this line

        # Insert any post-oracle processing here
        qml.Hadamard(wires=0)
        qml.Hadamard(wires=1)

        # QHACK #

        return qml.sample(wires=range(2))

    sample = circuit()

    # QHACK #
    prob = 1/2**(len(sample)) * (sum(sample))**2
    if prob == 0:
        func = 'constant'
    else:
        func = 'balanced'

    # From `sample` (a single call to the circuit), determine whether the function is constant or balanced.
    return func
    # QHACK #

'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    inputs = sys.stdin.read().split(",")
    numbers = [int(i) for i in inputs]

    def oracle():
        for i in numbers:
            qml.CNOT(wires=[i, 2])

    output = deutsch_jozsa(oracle)
    print(output)
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    inputs = sys.stdin.read().split(",")\n    numbers = [int(i) for i in inputs]\n\n    def oracle():\n        for i in numbers:\n            qml.CNOT(wires=[i, 2])\n\n    output = deutsch_jozsa(oracle)\n    print(output)\n'

```
in1 = [0]
#ans1='balanced'
in2 = [1,1]
#ans2='constant'

if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    for inputs in [in1, in2]:
      numbers = [int(i) for i in inputs]
      def oracle():
          for i in numbers:
              qml.CNOT(wires=[i, 2])

      output = deutsch_jozsa(oracle)
      print(output)
```

    balanced
    constant

```
dev = qml.device("default.qubit", wires=3, shots=1)
@qml.qnode(dev)
def circuit():
    qml.Hadamard(wires=0)
    qml.Hadamard(wires=1)
    qml.PauliX(wires=2)
    qml.Hadamard(wires=2)
    oracle()
    qml.Hadamard(wires=0)
    qml.Hadamard(wires=1)
    return qml.sample(wires=range(2))
fig, ax = qml.draw_mpl(circuit, decimals=6)()
```

### algorithms_200_AdaptingTopology_solution

Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/algorithms_200_AdaptingTopology_template/adapting_topology_template.py).

```
#! /usr/bin/python3
import sys
from pennylane import numpy as np
import pennylane as qml

graph = {
    0: [1],
    1: [0, 2, 3, 4],
    2: [1],
    3: [1],
    4: [1, 5, 7, 8],
    5: [4, 6],
    6: [5, 7],
    7: [4, 6],
    8: [4],
}

def n_swaps(cnot):
    """Count the minimum number of swaps needed to create the equivalent CNOT.
    Args:
        - cnot (qml.Operation): A CNOT gate that needs to be implemented on the hardware
        You can find out the wires on which an operator works by asking for the 'wires' attribute: 'cnot.wires'
    Returns:
        - (int): minimum number of swaps
    """

    # QHACK #
    import queue
    def minEdgeBFS(edges, u, v, n):

        # visited[n] for keeping track
        # of visited node in BFS
        visited = [0] * n

        # Initialize distances as 0
        distance = [0] * n

        # queue to do BFS.
        Q = queue.Queue()
        distance[u] = 0

        Q.put(u)
        visited[u] = True
        while (not Q.empty()):
            x = Q.get()

            for i in range(len(edges[x])):
                if (visited[edges[x][i]]):
                    continue

                # update distance for i
                distance[edges[x][i]] = distance[x] + 1
                Q.put(edges[x][i])
                visited[edges[x][i]] = 1
        return distance[v] - 1
    return 2*minEdgeBFS(graph, cnot.wires[0], cnot.wires[1], len(graph.keys()))
    # QHACK #
'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    inputs = sys.stdin.read().split(",")
    output = n_swaps(qml.CNOT(wires=[int(i) for i in inputs]))
    print(f"{output}")
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    inputs = sys.stdin.read().split(",")\n    output = n_swaps(qml.CNOT(wires=[int(i) for i in inputs]))\n    print(f"{output}")\n'

```
in1 = 0, 1
ans1=0
in2=8,2
ans2=4
ans=[ans1,ans2]
i=0
for inputs in [in1, in2]:
  output = n_swaps(qml.CNOT(wires=[int(i) for i in inputs]))
  print(f"{output}", output== ans[i])
  i=+1
```

    0 True
    4 True

### algorithms_300_AdderQFT_solution

Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/algorithms_300_AdderQFT_template/adder_QFT_template.py).

```
#! /usr/bin/python3

import sys
from pennylane import numpy as np
import pennylane as qml

def qfunc_adder(m, wires):
    """Quantum function capable of adding m units to a basic state given as input.

    Args:
        - m (int): units to add.
        - wires (list(int)): list of wires in which the function will be executed on.
    """

    qml.QFT(wires=wires)

    # QHACK #
    angle = np.pi * m
    for i in wires:
        qml.PhaseShift(angle/(2**i), wires=i)
    # QHACK #

    qml.QFT(wires=wires).inv()
'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    inputs = sys.stdin.read().split(",")
    m = int(inputs[0])
    n_wires = int(inputs[1])
    wires = range(n_wires)

    dev = qml.device("default.qubit", wires=wires, shots=1)

    @qml.qnode(dev)
    def test_circuit():
        # Input:  |2^{N-1}>
        qml.PauliX(wires=0)

        qfunc_adder(m, wires)
        return qml.sample()

    output = test_circuit()
    print(*output, sep=",")
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    inputs = sys.stdin.read().split(",")\n    m = int(inputs[0])\n    n_wires = int(inputs[1])\n    wires = range(n_wires)\n\n    dev = qml.device("default.qubit", wires=wires, shots=1)\n\n    @qml.qnode(dev)\n    def test_circuit():\n        # Input:  |2^{N-1}>\n        qml.PauliX(wires=0)\n\n        qfunc_adder(m, wires)\n        return qml.sample()\n\n    output = test_circuit()\n    print(*output, sep=",")\n'

```
in1=[2,3]
#ans: 1,1,0
in2=[5,4]
#ans: 1,1,0,1

if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    for inputs in [in1, in2]:
      #inputs = sys.stdin.read().split(",")
      m = int(inputs[0])
      n_wires = int(inputs[1])
      wires = range(n_wires)

      dev = qml.device("default.qubit", wires=wires, shots=1)

      @qml.qnode(dev)
      def test_circuit():
          # Input:  |2^{N-1}>
          qml.PauliX(wires=0)

          qfunc_adder(m, wires)
          return qml.sample()

      output = test_circuit()
      print(*output, sep=",")
```

    1,1,0
    1,1,0,1

## Quantum Machine Learning

### qml_100_GeneratingFourierState_solution

Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/qml_100_GeneratingFourierState_template/generating_fourier_state_template.py).

```
#! /usr/bin/python3

import sys
from pennylane import numpy as np
import pennylane as qml

def generating_fourier_state(n_qubits, m):
    """Function which, given the number of qubits and an integer m, returns the circuit and the angles that generate the state
    QFT|m> following the above template.
    Args:
        - n_qubits (int): number of qubits in the circuit.
        - m (int): basis state that we generate. For example, for 'm = 3' and 'n_qubits = 4'
        we would generate the state QFT|0011> (3 in binary is 11).
    Returns:
       - (qml.QNode): circuit used to generate the state.
       - (list[float]): angles that generate the state QFT|m>.
    """

    dev = qml.device("default.qubit", wires=n_qubits)

    @qml.qnode(dev)
    def circuit(angles):
        """This is the quantum circuit that we will use."""

        # QHACK #

        # Add the template of the statement with the angles passed as an argument.
        for w in range(n_qubits):
            qml.Hadamard(wires=w)
            qml.RZ(angles[w],wires=w)

        # QHACK #

        # We apply QFT^-1 to return to the computational basis.
        # This will help us to see how well we have done.
        qml.adjoint(qml.QFT)(wires=range(n_qubits))

        # We return the probabilities of seeing each basis state.
        return qml.probs(wires=range(n_qubits))

    def error(angles):
        """This function will determine, given a set of angles, how well it approximates
        the desired state. Here it will be necessary to call the circuit to work with these results.
        """

        probs = circuit(angles)
        # QHACK #

        # The return error should be smaller when the state m is more likely to be obtained.
        target=np.zeros(2**n_qubits)
        target[m]=1

        loss=np.sum((target-probs)**2)
        return loss
        # QHACK #

    # This subroutine will find the angles that minimize the error function.
    # Do not modify anything from here.

    opt = qml.AdamOptimizer(stepsize=0.8)
    epochs = 5000

    angles = np.zeros(n_qubits, requires_grad=True)
    for epoch in range(epochs):
        angles = opt.step(error, angles)
        angles = np.clip(opt.step(error, angles), -2 * np.pi, 2 * np.pi)

    return circuit, angles

'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    inputs = sys.stdin.read().split(",")
    n_qubits = int(inputs[0])
    m = int(inputs[1])

    output = generating_fourier_state(n_qubits, m)
    output[0](output[1])
    dev = qml.device("default.qubit", wires=n_qubits)

    @qml.qnode(dev)
    def check_with_arbitrary_state():
        for i in range(n_qubits):
            qml.RY(i, wires=i)
        for op in output[0].qtape.operations:
            qml.apply(op)
        return qml.state()

    print(",".join([f"{p.real.round(5)},{p.imag.round(5)}" for p in check_with_arbitrary_state()]))
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    inputs = sys.stdin.read().split(",")\n    n_qubits = int(inputs[0])\n    m = int(inputs[1])\n\n    output = generating_fourier_state(n_qubits, m)\n    output[0](output[1])\n    dev = qml.device("default.qubit", wires=n_qubits)\n\n    @qml.qnode(dev)\n    def check_with_arbitrary_state():\n        for i in range(n_qubits):\n            qml.RY(i, wires=i)\n        for op in output[0].qtape.operations:\n            qml.apply(op)\n        return qml.state()\n\n    print(",".join([f"{p.real.round(5)},{p.imag.round(5)}" for p in check_with_arbitrary_state()]))\n'

```
in1=[2,3]
#Ans1=[0.0,0.0,0.33901,-0.33901,0.0,0.0,0.62054,-0.62054]
in2=[3,2]
#Ans2=[-0.28526,-0.18317,0.0,-0.0,-0.33528,-0.33528,-0.0,0.0,-0.18317,-0.28526,-0.0,0.0,-0.52217,-0.52217,-0.0,0.0]

for inputs in [in1, in2]:
  n_qubits = int(inputs[0])
  m = int(inputs[1])

  output = generating_fourier_state(n_qubits, m)
  output[0](output[1])
  dev = qml.device("default.qubit", wires=n_qubits)

  @qml.qnode(dev)
  def check_with_arbitrary_state():
      for i in range(n_qubits):
          qml.RY(i, wires=i)
      for op in output[0].qtape.operations:
          qml.apply(op)
      return qml.state()

  print(",".join([f"{p.real.round(5)},{p.imag.round(5)}" for p in check_with_arbitrary_state()]))
```

    0.0,0.0,0.33901,-0.33901,0.0,0.0,0.62054,-0.62054
    -0.28526,-0.18317,0.0,-0.0,-0.33528,-0.33528,-0.0,0.0,-0.18316,-0.28526,-0.0,0.0,-0.52217,-0.52217,-0.0,0.0

### qml_200_WhoLikesTheBeatles_solution

Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/qml_200_WhoLikesTheBeatles_template/who_likes_the_beatles_template.py).

```
#! /usr/bin/python3

import sys
from pennylane import numpy as np
import pennylane as qml

def distance(A, B):
    """Function that returns the distance between two vectors.

    Args:
        - A (list[int]): person's information: [age, minutes spent watching TV].
        - B (list[int]): person's information: [age, minutes spent watching TV].

    Returns:
        - (float): distance between the two feature vectors.
    """

    # QHACK #

    # The Swap test is a method that allows you to calculate |<A|B>|^2 , you could use it to help you.
    # The qml.AmplitudeEmbedding operator could help you too.
    # qubit_number = int(np.ceil(np.log2(len(A))))
    # dev = qml.device('default.qubit', wires=3 * qubit_number)
    # # dev = qml.device("default.qubit", ...
    # @qml.qnode(dev)
    def calc_angle(v1, v2):
        unit_vector_1 = v1 / np.linalg.norm(v1)
        unit_vector_2 = v2 / np.linalg.norm(v2)
        dot_product = np.dot(unit_vector_1, unit_vector_2)
        angle = np.arccos(dot_product)
        return angle
    dist = np.sqrt(2*(1-np.abs(np.cos(calc_angle(A, B)))))

    return dist
    # QHACK #

def predict(dataset, new, k):
    """Function that given a dataset, determines if a new person do like Beatles or not.

    Args:
        - dataset (list): List with the age, minutes that different people watch TV, and if they like Beatles.
        - new (list(int)): Age and TV minutes of the person we want to classify.
        - k (int): number of nearby neighbors to be taken into account.

    Returns:
        - (str): "YES" if they like Beatles, "NO" otherwise.
    """

    # DO NOT MODIFY anything in this code block

    def k_nearest_classes():
        """Function that returns a list of k near neighbors."""
        distances = []
        for data in dataset:
            distances.append(distance(data[0], new))
        nearest = []
        for _ in range(k):
            indx = np.argmin(distances)
            nearest.append(indx)
            distances[indx] += 2

        return [dataset[i][1] for i in nearest]

    output = k_nearest_classes()

    return (
        "YES" if len([i for i in output if i == "YES"]) > len(output) / 2 else "NO",
        float(distance(dataset[0][0], new)),
    )
'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    inputs = sys.stdin.read().split(",")
    dataset = []
    new = [int(inputs[0]), int(inputs[1])]
    k = int(inputs[2])
    for i in range(3, len(inputs), 3):
        dataset.append([[int(inputs[i + 0]), int(inputs[i + 1])], str(inputs[i + 2])])

    output = predict(dataset, new, k)
    sol = 0 if output[0] == "YES" else 1
    print(f"{sol},{output[1]}")
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    inputs = sys.stdin.read().split(",")\n    dataset = []\n    new = [int(inputs[0]), int(inputs[1])]\n    k = int(inputs[2])\n    for i in range(3, len(inputs), 3):\n        dataset.append([[int(inputs[i + 0]), int(inputs[i + 1])], str(inputs[i + 2])])\n\n    output = predict(dataset, new, k)\n    sol = 0 if output[0] == "YES" else 1\n    print(f"{sol},{output[1]}")\n'

```
#ans1=0,0.4210528986556
#ans2=0,0.9264606253589677

for i in range (1,3):
  dataset_dir = F'/content/gdrive/My Drive/Contests/QHack2022/Coding Challenge/dataset/Beatles/{i}.in'
  with open(dataset_dir, 'r') as f:
    filein = f.readlines()
    inputs = filein[0].split(",")

  dataset = []
  new = [int(inputs[0]), int(inputs[1])]
  k = int(inputs[2])
  for i in range(3, len(inputs), 3):
      dataset.append([[int(inputs[i + 0]), int(inputs[i + 1])], str(inputs[i + 2])])

  output = predict(dataset, new, k)
  sol = 0 if output[0] == "YES" else 1
  print(f"{sol},{output[1]}")
```

    0,0.42105289865559875
    0,0.926460625358967

### qml_400_BuildingQRAM_template_solution

Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/qml_400_BuildingQRAM_template/building_QRAM_template.py).

The original idea was to use the RYGate(theta).control(3) instruction from qiskit to build the multicontrolledRY gate, however, PennyLane does not currently support said instruction via the pennylane-qiskit plugin. Therefore, one needs to hardcode the CCCRY gate. Available approaches:

1) Define the 16x16 CCCRY gate matrix as a numpy.array() and pass it to the argument of the [qml.QubitUnitary()](https://pennylane.readthedocs.io/en/stable/code/api/pennylane.QubitUnitary.html) class. One more step: requires to set up a block matrix [[identity, 0], [0, U]] according to Tom Bromley's answer on the [XANADU discussion forum](https://discuss.pennylane.ai/t/control-of-a-quantum-state/796).

2) Decompose the CCCRY gate into cNOTs and CRY gates. This would require to find the V and $V^{\dagger}$ matrices of the decomposed circuit, solve the eigenvalue-eingenvector equation and calculate the fourth root square of the eigenvalues.

3) The chosen approach: define the ry target gate and the control wires (qubits) using the [qml.ControlledQubitUnitary()](https://pennylane.readthedocs.io/en/stable/code/api/pennylane.ControlledQubitUnitary.html) class from PennyLane.

#### Solution

```
#! /usr/bin/python3

import sys
from pennylane import numpy as np
import pennylane as qml

def qRAM(thetas):
    """Function that generates the superposition state.

    Args:
        - thetas (list(float)): list of parameters (angles) to perform rotations.

    Returns:
        - (list(complex)): final state.
    """

    # QHACK #

    # Use this space to create auxiliary functions if you need it.

    def CCCRY(theta, i):

      '''
      Function that returns the CCCRY gate using the qml.ControlledQubitUnitary() class from PennyLane.
      '''

      ry  = np.array([[np.cos(theta/2), -np.sin(theta/2)],
                      [np.sin(theta/2), np.cos(theta/2)]])

      lst=[str(j) for j in basis[i]]
      gate = qml.ControlledQubitUnitary(ry, control_wires=[0, 1, 2], wires=3, control_values=str(''.join(lst)))
      return gate

    n=3
    basis=[]
    for i in range(2**n):
      arr = []
      for j in list(f'{i:0{n}b}'):
        arr.append(int(j))
      basis.append(arr)

    # QHACK #

    dev = qml.device("default.qubit", wires=range(4))
    global circuit
    @qml.qnode(dev)
    def circuit():

        # QHACK #

        # Create your circuit: the first three qubits will refer to the index, the fourth to the RY rotation.
        for i in range (3):
          qml.Hadamard(wires=i)
        for i in range(len(thetas)):
          CCCRY(thetas[i], i)

        # QHACK #

        return qml.state()

    return circuit()

'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    inputs = sys.stdin.read().split(",")
    thetas = np.array(inputs, dtype=float)

    output = qRAM(thetas)
    output = [float(i.real.round(6)) for i in output]
    print(*output, sep=",")
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    inputs = sys.stdin.read().split(",")\n    thetas = np.array(inputs, dtype=float)\n\n    output = qRAM(thetas)\n    output = [float(i.real.round(6)) for i in output]\n    print(*output, sep=",")\n'

```
in1=[0,0,0,0,0,0,0,0]
Ans1=[0.353553,0.0,0.353553,0.0,0.353553,0.0,0.353553,0.0,0.353553,-0.0,0.353553,-0.0,0.353553,0.0,0.353553,0.0]
in2=[3.141592653589793,3.141592653589793,3.141592653589793,3.141592653589793,3.141592653589793,3.141592653589793,3.141592653589793,3.141592653589793]
Ans2= [0.0,0.353553,0.0,0.353553,0.0,0.353553,0.0,0.353553,-0.0,0.353553,-0.0,0.353553,-0.0,0.353553,-0.0,0.353553]
in3=[0,0.2,0.5,0.1,0.1,0.2,0.4,0.6]
Ans3=[0.353553,0.0,0.351787,0.035296,0.342562,0.087471,0.353112,0.01767,0.353112,0.01767,0.351787,0.035296,0.346506,0.07024,0.337762,0.104482]

for inputs in [in1, in2, in3]:
  if __name__ == "__main__":
      # DO NOT MODIFY anything in this code block
      thetas = np.array(inputs, dtype=float)

      output = qRAM(thetas)
      output = [float(i.real.round(6)) for i in output]
      print(*output, sep=",")
      fig, ax = qml.draw_mpl(circuit, decimals=6)()
```

#### **Bonus**

Approaches 1 and 2 with PennyLane and Qiskit.

##### **1) CCCRY gate from scratch (tensor product of its primitives)**

The algebra looks like this:

$CRY = I\otimes |0\rangle\langle 0\rangle + RY(\theta) \otimes |1 \rangle \langle 1|$

$CCRY = I\otimes I\otimes |0\rangle\langle 0\rangle + CRY \otimes |1 \rangle \langle 1|$

$CCCRY = I\otimes I\otimes I\otimes |0\rangle\langle 0\rangle + CCRY \otimes |1 \rangle \langle 1|$

```
from pennylane import numpy as np

def CRY(theta):
  '''
  Function that defines the CRY gate. Where CRY = CU3(0,0,theta).
  '''
  CRY  = np.array([[1, 0, 0, 0],
                  [0, np.cos(theta/2), 0, -np.sin(theta/2)],
                  [0, 0, 1, 0],
                  [0, np.sin(theta/2), 0, np.cos(theta/2)]])
  return CRY

CRY(np.pi).round()
```

    tensor([[ 1.,  0.,  0.,  0.],
            [ 0.,  0.,  0., -1.],
            [ 0.,  0.,  1.,  0.],
            [ 0.,  1.,  0.,  0.]], requires_grad=True)

```
def CCCRY(theta):
  '''
  Function that returns the CCCRY gate coded with numpy from tensor product of its primitives.
  '''

  # First, define the CRY gate, where CRY = CU3(0,0,theta).
  CRY  = np.array([[1, 0, 0, 0],
                  [0, np.cos(theta/2), 0, -np.sin(theta/2)],
                  [0, 0, 1, 0],
                  [0, np.sin(theta/2), 0, np.cos(theta/2)]])

  psi_0 = np.array([1, 0])
  psi_1 = np.array([0, 1])

  outer0 = np.outer(psi_0, psi_0)
  outer1 = np.outer(psi_1, psi_1)

  ccry = np.kron(np.identity(4), outer0) + np.kron(CRY, outer1)
  cccry = np.kron(np.identity(8), outer0) + np.kron(ccry, outer1)

  gate = qml.QubitUnitary(cccry, wires=[0, 1, 2, 3])

  return gate

CCCRY(0) # Evaluating for angle 0.
```

    QubitUnitary(tensor([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
            [0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
            [0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
            [0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
            [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
            [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],
            [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]], requires_grad=True), wires=[0, 1, 2, 3])

**Using Qiskit:**

```
!pip install qiskit
```

    Requirement already satisfied: qiskit in /usr/local/lib/python3.7/dist-packages (0.34.2)
    Requirement already satisfied: qiskit-ibmq-provider==0.18.3 in /usr/local/lib/python3.7/dist-packages (from qiskit) (0.18.3)
    Requirement already satisfied: qiskit-ignis==0.7.0 in /usr/local/lib/python3.7/dist-packages (from qiskit) (0.7.0)
    Requirement already satisfied: qiskit-terra==0.19.2 in /usr/local/lib/python3.7/dist-packages (from qiskit) (0.19.2)
    Requirement already satisfied: qiskit-aer==0.10.3 in /usr/local/lib/python3.7/dist-packages (from qiskit) (0.10.3)
    Requirement already satisfied: numpy>=1.16.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-aer==0.10.3->qiskit) (1.21.5)
    Requirement already satisfied: scipy>=1.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aer==0.10.3->qiskit) (1.7.3)
    Requirement already satisfied: requests-ntlm>=1.1.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.18.3->qiskit) (1.1.0)
    Requirement already satisfied: requests>=2.19 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.18.3->qiskit) (2.23.0)
    Requirement already satisfied: websocket-client>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.18.3->qiskit) (1.3.1)
    Requirement already satisfied: python-dateutil>=2.8.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.18.3->qiskit) (2.8.2)
    Requirement already satisfied: urllib3>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.18.3->qiskit) (1.24.3)
    Requirement already satisfied: setuptools>=40.1.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-ignis==0.7.0->qiskit) (57.4.0)
    Requirement already satisfied: retworkx>=0.8.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-ignis==0.7.0->qiskit) (0.11.0)
    Requirement already satisfied: ply>=3.10 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.19.2->qiskit) (3.11)
    Requirement already satisfied: psutil>=5 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.19.2->qiskit) (5.4.8)
    Requirement already satisfied: tweedledum<2.0,>=1.1 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.19.2->qiskit) (1.1.1)
    Requirement already satisfied: symengine>=0.8 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.19.2->qiskit) (0.9.2)
    Requirement already satisfied: python-constraint>=1.4 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.19.2->qiskit) (1.4.0)
    Requirement already satisfied: stevedore>=3.0.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.19.2->qiskit) (3.5.0)
    Requirement already satisfied: dill>=0.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.19.2->qiskit) (0.3.4)
    Requirement already satisfied: sympy>=1.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.19.2->qiskit) (1.7.1)
    Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.8.0->qiskit-ibmq-provider==0.18.3->qiskit) (1.15.0)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.18.3->qiskit) (3.0.4)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.18.3->qiskit) (2.10)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.18.3->qiskit) (2021.10.8)
    Requirement already satisfied: cryptography>=1.3 in /usr/local/lib/python3.7/dist-packages (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.18.3->qiskit) (36.0.2)
    Requirement already satisfied: ntlm-auth>=1.0.2 in /usr/local/lib/python3.7/dist-packages (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.18.3->qiskit) (1.5.0)
    Requirement already satisfied: cffi>=1.12 in /usr/local/lib/python3.7/dist-packages (from cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.18.3->qiskit) (1.15.0)
    Requirement already satisfied: pycparser in /usr/local/lib/python3.7/dist-packages (from cffi>=1.12->cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.18.3->qiskit) (2.21)
    Requirement already satisfied: pbr!=2.1.0,>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from stevedore>=3.0.0->qiskit-terra==0.19.2->qiskit) (5.8.1)
    Requirement already satisfied: importlib-metadata>=1.7.0 in /usr/local/lib/python3.7/dist-packages (from stevedore>=3.0.0->qiskit-terra==0.19.2->qiskit) (2.1.3)
    Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata>=1.7.0->stevedore>=3.0.0->qiskit-terra==0.19.2->qiskit) (3.7.0)
    Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.7/dist-packages (from sympy>=1.3->qiskit-terra==0.19.2->qiskit) (1.2.1)

```
from qiskit import QuantumCircuit, QuantumRegister, transpile
from qiskit.circuit.library.standard_gates import RYGate
from qiskit.circuit import Parameter
from qiskit import BasicAer

backend = BasicAer.get_backend('unitary_simulator')

qr=QuantumRegister(2)
qc=QuantumCircuit(qr)
a=Parameter('a') # You can replace a with your choice of angle here.
CRYg=RYGate(np.pi).control(1) # Or passing the angle directly here.
qc.append(CRYg,qr)
print(qc)
#print(qc.draw())
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
```

    q85_0: ────■────
           ┌───┴───┐
    q85_1: ┤ Ry(π) ├
           └───────┘
    Pass: UnitarySynthesis - 0.01144 (ms)
    Pass: UnrollCustomDefinitions - 0.05627 (ms)
    Begin BasisTranslator from source basis {('cry', 2)} to target basis {'u1', 'x', 'rz', 'u3', 'measure', 'snapshot', 'delay', 'cx', 'sx', 'id', 'reset', 'barrier', 'unitary', 'u2'}.
    Basis translation path search completed in 0.021s.
    Basis translation paths composed in 0.075s.
    Basis translation instructions replaced in 0.002s.
    Pass: BasisTranslator - 109.61914 (ms)
    Pass: RemoveResetInZeroState - 0.01860 (ms)
    Pass: Depth - 0.03767 (ms)
    Pass: FixedPoint - 0.02599 (ms)
    Pass: Optimize1qGatesDecomposition - 3.59464 (ms)
    Pass: CXCancellation - 0.05293 (ms)
    Pass: UnitarySynthesis - 0.01669 (ms)
    Pass: UnrollCustomDefinitions - 0.14544 (ms)
    Begin BasisTranslator from source basis {('cx', 2), ('u2', 1)} to target basis {'u1', 'x', 'rz', 'u3', 'measure', 'snapshot', 'delay', 'cx', 'sx', 'id', 'reset', 'barrier', 'unitary', 'u2'}.
    Basis translation path search completed in 0.001s.
    Basis translation paths composed in 0.000s.
    Basis translation instructions replaced in 0.000s.
    Pass: BasisTranslator - 14.85324 (ms)
    Pass: Depth - 0.03505 (ms)
    Pass: FixedPoint - 0.02337 (ms)
    Pass: Optimize1qGatesDecomposition - 2.71153 (ms)
    Pass: CXCancellation - 0.05817 (ms)
    Pass: UnitarySynthesis - 0.01693 (ms)
    Pass: UnrollCustomDefinitions - 0.05722 (ms)
    Begin BasisTranslator from source basis {('cx', 2), ('u2', 1)} to target basis {'u1', 'x', 'rz', 'u3', 'measure', 'snapshot', 'delay', 'cx', 'sx', 'id', 'reset', 'barrier', 'unitary', 'u2'}.
    Basis translation path search completed in 0.001s.
    Basis translation paths composed in 0.001s.
    Basis translation instructions replaced in 0.000s.
    Pass: BasisTranslator - 9.08780 (ms)
    Pass: ContainsInstruction - 0.01478 (ms)
    Total Transpile Time - 215.13820 (ms)
    Total Assembly Time - 0.17762 (ms)
    "unitary_simulator" only supports 1 shot. Setting shots=1.

    array([[ 1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j, -1.-0.j],
           [ 0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j],
           [ 0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j]])

- $CCRY = I\otimes I\otimes |0\rangle\langle 0\rangle + CRY \otimes |1 \rangle \langle 1|$

```
psi_0 = np.array([1, 0])
psi_1 = np.array([0, 1])

outer0 = np.outer(psi_0, psi_0)
outer1 = np.outer(psi_1, psi_1)

id4=np.identity(4)

ccry = np.kron(id4, outer0) + np.kron(CRY(np.pi), outer1)
ccry.round()
```

    tensor([[ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
            [ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.],
            [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0., -1.],
            [ 0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.],
            [ 0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.]], requires_grad=True)

```
from qiskit import QuantumCircuit, QuantumRegister, transpile
from qiskit.circuit.library.standard_gates import RYGate
from qiskit.circuit import Parameter
from qiskit import BasicAer

backend = BasicAer.get_backend('unitary_simulator')
qr=QuantumRegister(3)
qc=QuantumCircuit(qr)
a=Parameter('a') # You can replace a with your choice of angle here.
CCRY=RYGate(np.pi).control(2) # Or passing the angle directly here.
qc.append(CCRY,qr)
print(qc)
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
```

    q42_0: ────■────
               │
    q42_1: ────■────
           ┌───┴───┐
    q42_2: ┤ Ry(π) ├
           └───────┘
    Pass: UnitarySynthesis - 0.01121 (ms)
    Pass: UnrollCustomDefinitions - 0.97299 (ms)
    Begin BasisTranslator from source basis {('ry', 1), ('ccx', 3)} to target basis {'u1', 'x', 'rz', 'u3', 'measure', 'snapshot', 'delay', 'cx', 'sx', 'id', 'reset', 'barrier', 'unitary', 'u2'}.
    Basis translation path search completed in 0.021s.
    Basis translation paths composed in 0.047s.
    Basis translation instructions replaced in 0.002s.
    Pass: BasisTranslator - 78.46189 (ms)
    Pass: RemoveResetInZeroState - 0.02861 (ms)
    Pass: Depth - 0.04268 (ms)
    Pass: FixedPoint - 0.02360 (ms)
    Pass: Optimize1qGatesDecomposition - 9.78231 (ms)
    Pass: CXCancellation - 0.12493 (ms)
    Pass: UnitarySynthesis - 0.01597 (ms)
    Pass: UnrollCustomDefinitions - 0.13447 (ms)
    Begin BasisTranslator from source basis {('u1', 1), ('cx', 2), ('u2', 1)} to target basis {'u1', 'x', 'rz', 'u3', 'measure', 'snapshot', 'delay', 'cx', 'sx', 'id', 'reset', 'barrier', 'unitary', 'u2'}.
    Basis translation path search completed in 0.000s.
    Basis translation paths composed in 0.001s.
    Basis translation instructions replaced in 0.000s.
    Pass: BasisTranslator - 8.47244 (ms)
    Pass: Depth - 0.05341 (ms)
    Pass: FixedPoint - 0.02360 (ms)
    Pass: Optimize1qGatesDecomposition - 11.36684 (ms)
    Pass: CXCancellation - 0.11635 (ms)
    Pass: UnitarySynthesis - 0.01645 (ms)
    Pass: UnrollCustomDefinitions - 0.41437 (ms)
    Begin BasisTranslator from source basis {('u1', 1), ('cx', 2), ('u2', 1)} to target basis {'u1', 'x', 'rz', 'u3', 'measure', 'snapshot', 'delay', 'cx', 'sx', 'id', 'reset', 'barrier', 'unitary', 'u2'}.
    Basis translation path search completed in 0.000s.
    Basis translation paths composed in 0.003s.
    Basis translation instructions replaced in 0.000s.
    Pass: BasisTranslator - 9.82809 (ms)
    Pass: Depth - 0.04005 (ms)
    Pass: FixedPoint - 0.02217 (ms)
    Pass: Optimize1qGatesDecomposition - 7.84516 (ms)
    Pass: CXCancellation - 0.11587 (ms)
    Pass: UnitarySynthesis - 0.01597 (ms)
    Pass: UnrollCustomDefinitions - 0.12851 (ms)
    Begin BasisTranslator from source basis {('u1', 1), ('cx', 2), ('u2', 1)} to target basis {'u1', 'x', 'rz', 'u3', 'measure', 'snapshot', 'delay', 'cx', 'sx', 'id', 'reset', 'barrier', 'unitary', 'u2'}.
    Basis translation path search completed in 0.000s.
    Basis translation paths composed in 0.001s.
    Basis translation instructions replaced in 0.000s.
    Pass: BasisTranslator - 6.37126 (ms)
    Pass: ContainsInstruction - 0.01407 (ms)
    Total Transpile Time - 197.58224 (ms)
    Total Assembly Time - 0.17500 (ms)
    "unitary_simulator" only supports 1 shot. Setting shots=1.

    array([[ 1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
            -1.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j]])

- $CCCRY = I\otimes I\otimes I\otimes |0\rangle\langle 0\rangle + CCRY \otimes |1 \rangle \langle 1|$

```
cccry = np.kron(np.identity(8), outer0) + np.kron(ccry, outer1)
cccry.round()
```

    tensor([[ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.],
            [ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.],
            [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.],
            [ 0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0., -1.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,
              0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,
              0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,
              0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,
              0.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
              1.,  0.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  1.,  0.],
            [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.]], requires_grad=True)

```
from qiskit import QuantumCircuit, QuantumRegister, transpile
from qiskit.circuit.library.standard_gates import RYGate
from qiskit.circuit import Parameter
from qiskit import BasicAer

backend = BasicAer.get_backend('unitary_simulator')
qr=QuantumRegister(4)
qc=QuantumCircuit(qr)
a=Parameter('a') # You can replace a with your choice of angle here.
CCRY=RYGate(np.pi).control(3) # Or passing the angle directly here.
qc.append(CCRY,qr)
print(qc)
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
```

    q56_0: ────■────
               │
    q56_1: ────■────
               │
    q56_2: ────■────
           ┌───┴───┐
    q56_3: ┤ Ry(π) ├
           └───────┘
    Pass: UnitarySynthesis - 0.00811 (ms)
    Pass: UnrollCustomDefinitions - 4.09937 (ms)
    Begin BasisTranslator from source basis {('u', 1), ('cx', 2), ('p', 1)} to target basis {'u1', 'x', 'rz', 'u3', 'measure', 'snapshot', 'delay', 'cx', 'sx', 'id', 'reset', 'barrier', 'unitary', 'u2'}.
    Basis translation path search completed in 0.003s.
    Basis translation paths composed in 0.006s.
    Basis translation instructions replaced in 0.008s.
    Pass: BasisTranslator - 25.99406 (ms)
    Pass: RemoveResetInZeroState - 0.04315 (ms)
    Pass: Depth - 0.05531 (ms)
    Pass: FixedPoint - 0.02337 (ms)
    Pass: Optimize1qGatesDecomposition - 14.12964 (ms)
    Pass: CXCancellation - 0.18859 (ms)
    Pass: UnitarySynthesis - 0.01645 (ms)
    Pass: UnrollCustomDefinitions - 0.16713 (ms)
    Begin BasisTranslator from source basis {('u3', 1), ('cx', 2)} to target basis {'u1', 'x', 'rz', 'u3', 'measure', 'snapshot', 'delay', 'cx', 'sx', 'id', 'reset', 'barrier', 'unitary', 'u2'}.
    Basis translation path search completed in 0.000s.
    Basis translation paths composed in 0.001s.
    Basis translation instructions replaced in 0.000s.
    Pass: BasisTranslator - 6.92129 (ms)
    Pass: Depth - 0.05412 (ms)
    Pass: FixedPoint - 0.02313 (ms)
    Pass: Optimize1qGatesDecomposition - 21.16537 (ms)
    Pass: CXCancellation - 0.14496 (ms)
    Pass: UnitarySynthesis - 0.01693 (ms)
    Pass: UnrollCustomDefinitions - 0.12732 (ms)
    Begin BasisTranslator from source basis {('u3', 1), ('cx', 2)} to target basis {'u1', 'x', 'rz', 'u3', 'measure', 'snapshot', 'delay', 'cx', 'sx', 'id', 'reset', 'barrier', 'unitary', 'u2'}.
    Basis translation path search completed in 0.000s.
    Basis translation paths composed in 0.000s.
    Basis translation instructions replaced in 0.000s.
    Pass: BasisTranslator - 14.92429 (ms)
    Pass: Depth - 0.04506 (ms)
    Pass: FixedPoint - 0.02241 (ms)
    Pass: Optimize1qGatesDecomposition - 13.02290 (ms)
    Pass: CXCancellation - 0.15664 (ms)
    Pass: UnitarySynthesis - 0.06413 (ms)
    Pass: UnrollCustomDefinitions - 0.20313 (ms)
    Begin BasisTranslator from source basis {('u3', 1), ('cx', 2)} to target basis {'u1', 'x', 'rz', 'u3', 'measure', 'snapshot', 'delay', 'cx', 'sx', 'id', 'reset', 'barrier', 'unitary', 'u2'}.
    Basis translation path search completed in 0.000s.
    Basis translation paths composed in 0.000s.
    Basis translation instructions replaced in 0.000s.
    Pass: BasisTranslator - 7.99203 (ms)
    Pass: ContainsInstruction - 0.01287 (ms)
    Total Transpile Time - 183.41327 (ms)
    Total Assembly Time - 0.19264 (ms)
    "unitary_simulator" only supports 1 shot. Setting shots=1.

    array([[ 1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j, -1.-0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,
             0.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             1.+0.j,  0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j,  0.+0.j]])

##### **2) CCCRU gate decomposition**

```
def CYdagger(gate, theta):
  return np.conjugate(gate(theta))

def my_ansatz(gate, gate_dagger, **kwargs):
  '''
  This function performs the decomposition of a MultiControlled gate according to Barenco et al., 1995, pg. 17.
  '''

  qml.QubitUnitary(gate, wires=[0, 3])
  qml.CNOT(wires=[0,1])
  qml.QubitUnitary(gate_dagger, wires=[1, 3]) #adjunt
  qml.CNOT(wires=[0,1])
  qml.QubitUnitary(gate, wires=[1, 3])
  qml.CNOT(wires=[1,2])
  qml.QubitUnitary(gate_dagger, wires=[2, 3]) #adjunt
  qml.CNOT(wires=[0,2])
  qml.QubitUnitary(gate, wires=[2, 3])
  qml.CNOT(wires=[1,2])
  qml.QubitUnitary(gate_dagger, wires=[2, 3]) #adjunt
  qml.CNOT(wires=[0,2])
  qml.QubitUnitary(gate, wires=[2, 3])
```

## Quantum Chemistry

### qchem_100_IsParticlePreserving_solution

Template [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/qchem_100_IsParticlePreserving_template/particle_conservation_template.py).

```
#! /usr/bin/python3

import sys
import pennylane as qml
from pennylane import numpy as np

def binary_list(m, n):
    """Converts number m to binary encoded on a list of length n

    Args:
        - m (int): Number to convert to binary
        - n (int): Number of wires in the circuit

    Returns:
        - (list(int)): Binary stored as a list of length n
    """

    arr = []
    # QHACK #
    for i in list(f'{m:0{n}b}'):
      arr.append(int(i))
    # QHACK #
    return arr

def basis_states(n):
    """Given a number n, returns a list of all binary_list(m,n) for m < 2**n, thus providing all basis states
         for a circuit of n wires

    Args:
        - n(int): integer representing the number of wires in the circuit

    Returns:
        - (list(list(int))): list of basis states represented as lists of 0s and 1s.
    """

    arr = []

    # QHACK #
    for i in range(2**n):
      basis = binary_list(i, n)
      arr.append(basis)
    # QHACK #

    return arr

def is_particle_preserving(circuit, n):
    """Given a circuit and its number of wires n, returns 1 if it preserves the number of particles, and 0 if it does not

    Args:
        - circuit (qml.QNode): A QNode that has a state such as [0,0,1,0] as an input and outputs the final state after performing
        quantum operation
        - n (int): the number of wires of circuit

    Returns:
        - (bool): True / False according to whether the input circuit preserves the number of particles or not
    """

    # QHACK #
    list_of_basis = basis_states(n)
    arr=[]
    for basis in list_of_basis:
      statevector = circuit(basis)
      arr.append(sum(x !=0 for x in statevector)) # hamming_weight
    result = all(element == arr[0] for element in arr)
    if result:
      return False
    else:
      return True
    # QHACK #

'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    inputs = sys.stdin.read().split(";")
    gate_list = []
    wire_list = []
    param_list = []
    i = 1

    while i < len(inputs):
        gate_obj = getattr(qml, str(inputs[i]))
        gate_wires = gate_obj.num_wires
        input_wires = list(map(int, str(inputs[i + 1]).split(",")))
        gate_list.append(str(inputs[i]))
        wire_list.append(input_wires)
        if "non_parametric_ops" not in gate_obj.__module__.split("."):
            input_params = list(map(float, str(inputs[i + 2]).split(",")))
            param_list.append(input_params)
            i += 1
        i += 2

    wire_list = np.array(wire_list, dtype=object)
    param_list = np.array(param_list, dtype=object)

    n = int(inputs[0])
    dev = qml.device("default.qubit", wires=n)

    @qml.qnode(dev)
    def circ(gate_list, wire_list, param_list, state):
        qml.BasisState(np.array(state), wires=range(n))
        j = 0
        for i in range(len(gate_list)):
            gate = getattr(qml, str(gate_list[i]))
            if "non_parametric_ops" not in gate.__module__.split("."):
                gate(*param_list[j], wires=[int(w) for w in wire_list[i]])
                j += 1
            else:
                gate(wires=[int(w) for w in wire_list[i]])
        return qml.state()

    def circuit(state):
        return circ(gate_list, wire_list, param_list, state)

    output = is_particle_preserving(circuit, n)

    print(output)
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    inputs = sys.stdin.read().split(";")\n    gate_list = []\n    wire_list = []\n    param_list = []\n    i = 1\n\n    while i < len(inputs):\n        gate_obj = getattr(qml, str(inputs[i]))\n        gate_wires = gate_obj.num_wires\n        input_wires = list(map(int, str(inputs[i + 1]).split(",")))\n        gate_list.append(str(inputs[i]))\n        wire_list.append(input_wires)\n        if "non_parametric_ops" not in gate_obj.__module__.split("."):\n            input_params = list(map(float, str(inputs[i + 2]).split(",")))\n            param_list.append(input_params)\n            i += 1\n        i += 2\n\n    wire_list = np.array(wire_list, dtype=object)\n    param_list = np.array(param_list, dtype=object)\n\n    n = int(inputs[0])\n    dev = qml.device("default.qubit", wires=n)\n\n    @qml.qnode(dev)\n    def circ(gate_list, wire_list, param_list, state):\n        qml.BasisState(np.array(state), wires=range(n))\n        j = 0\n        for i in range(len(gate_list)):\n            gate = getattr(qml, str(gate_list[i]))\n            if "non_parametric_ops" not in gate.__module__.split("."):\n                gate(*param_list[j], wires=[int(w) for w in wire_list[i]])\n                j += 1\n            else:\n                gate(wires=[int(w) for w in wire_list[i]])\n        return qml.state()\n\n    def circuit(state):\n        return circ(gate_list, wire_list, param_list, state)\n\n    output = is_particle_preserving(circuit, n)\n\n    print(output)\n'

```
in1 = ['4','Hadamard','0','CNOT','0,1','Hadamard','0']
#Ans: False
in2 = ['4','DoubleExcitation','0,1,2,3','0.732','SingleExcitation','0,1','1.0']
#Ans: True
```

```
for inputs in [in1, in2]:
  gate_list = []
  wire_list = []
  param_list = []
  i = 1

  while i < len(inputs):
      gate_obj = getattr(qml, str(inputs[i]))
      gate_wires = gate_obj.num_wires
      input_wires = list(map(int, str(inputs[i + 1]).split(",")))
      gate_list.append(str(inputs[i]))
      wire_list.append(input_wires)
      if "non_parametric_ops" not in gate_obj.__module__.split("."):
          input_params = list(map(float, str(inputs[i + 2]).split(",")))
          param_list.append(input_params)
          i += 1
      i += 2

  wire_list = np.array(wire_list, dtype=object)
  param_list = np.array(param_list, dtype=object)

  n = int(inputs[0])
  dev = qml.device("default.qubit", wires=n)

  @qml.qnode(dev)
  def circ(gate_list, wire_list, param_list, state):
      qml.BasisState(np.array(state), wires=range(n))
      j = 0
      for i in range(len(gate_list)):
          gate = getattr(qml, str(gate_list[i]))
          if "non_parametric_ops" not in gate.__module__.split("."):
              gate(*param_list[j], wires=[int(w) for w in wire_list[i]])
              j += 1
          else:
              gate(wires=[int(w) for w in wire_list[i]])
      return qml.state()

  def circuit(state):
      return circ(gate_list, wire_list, param_list, state)

  output = is_particle_preserving(circuit, n)

  print(output)
```

    False
    True

### qchem_200_OptimizingMeasurements_solution

Template [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/qchem_200_OptimizingMeasurements_template/optimizing_measurements_template.py).

```
#! /usr/bin/python3

import sys

def check_simplification(op1, op2):
    """As we have seen in the problem statement, given two Pauli operators, you could obtain the expected value
    of each of them by running a single circuit following the two defined rules. This function will determine whether,
    given two Pauli operators, such a simplification can be produced.

    Args:
        - op1 (list(str)): First Pauli word (list of Pauli operators), e.g., ["Y", "I", "Z", "I"].
        - op2 (list(str)): Second Pauli word (list of Pauli operators), e.g., ["Y", "I", "X", "I"].

    Returns:
        - (bool): 'True' if we can simplify them, 'False' otherwise. For the example args above, the third qubit does not allow simplification, so the function would return `False`.
    """

    # QHACK
    '''
    for i in op1:
      for j in op2:
        value=False
        if i==j or i=='I' or j=='I':
          value=True
    return value
    '''
    # This algorithm has the same effect as the one above, however with complexity O(N) rather than O(N**2). Both lists must have the same dimension.
    for i in range(len(op1)):
      if op1[i] != op2[i] and op1[i] != 'I' and op2[i] != 'I':
        return False
    return True
    # QHACK

def join_operators(op1, op2):
    """This function will receive two operators that we know can be simplified
    and returns the operator corresponding to the union of the two previous ones.

    Args:
        - op1 (list(str)): First Pauli word (list of Pauli operators), e.g., ["Y", "I", "Z", "I"].
        - op2 (list(str)): Second Pauli word (list of Pauli operators), e.g., ["Y", "X", "I", "I"].

    Returns:
        - (list(str)): Pauli operator corresponding to the union of op1 and op2.
        For the case above the output would be ["Y", "X", "Z", "I"]
    """

    # QHACK
    result = []
    if check_simplification(op1, op2):
      for index, elem in enumerate(op1):
        if elem == op2[index] or elem=="I":
          result.append(op2[index])
        elif op2[index]=='I':
          result.append(elem)
      return result
    else:
      return None
    # QHACK

def optimize_measurements(obs_hamiltonian):
    """This function will go through the list of Pauli words provided in the statement, grouping the operators
    following the simplification process of the previous functions.

    Args:
        - obs_hamiltonian (list(list(str))): Groups of Pauli words making up the Hamiltonian.

    Returns:
        - (list(list(str))): The chosen Pauli operators to measure after grouping.
    """

    final_solution = []

    for op1 in obs_hamiltonian:
        added = False
        for i, op2 in enumerate(final_solution):

            if check_simplification(op1, op2):
                final_solution[i] = join_operators(op1, op2)
                added = True
                break
        if not added:
            final_solution.append(op1)

    return final_solution

def compression_ratio(obs_hamiltonian, final_solution):
    """Function that calculates the compression ratio of the procedure.

    Args:
        - obs_hamiltonian (list(list(str))): Groups of Pauli operators making up the Hamiltonian.
        - final_solution (list(list(str))): Your final selection of observables.

    Returns:
        - (float): Compression ratio your solution.
    """

    # QHACK
    initial=len(obs_hamiltonian)
    final=len(final_solution)
    r=1-(final/initial)
    return r
    # QHACK

'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block

    inputs = sys.stdin.read().split(",")

    obs_hamiltonian = []
    # open file and read the content in a list
    aux = []
    for i, line in enumerate(inputs):
        if i == 0:
            first = int(line)
        else:
            aux.append(line[0])
            if i % first == 0:
                obs_hamiltonian.append(aux)
                aux = []

    output = optimize_measurements(obs_hamiltonian)
    print(compression_ratio(obs_hamiltonian, output))
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n\n    inputs = sys.stdin.read().split(",")\n\n    obs_hamiltonian = []\n    # open file and read the content in a list\n    aux = []\n    for i, line in enumerate(inputs):\n        if i == 0:\n            first = int(line)\n        else:\n            aux.append(line[0])\n            if i % first == 0:\n                obs_hamiltonian.append(aux)\n                aux = []\n\n    output = optimize_measurements(obs_hamiltonian)\n    print(compression_ratio(obs_hamiltonian, output))\n'

```
in1=['4','Z','I','I','I','Z','Y','I','I','X','I','X','Y','I','Z','I','I']
#Ans: 0.5

in2=['4','Z','X','I','I','Z','Y','I','I','X','I','X','Y','I','Z','I','I']
#Ans 0.25

for inputs in [in1, in2]:
  obs_hamiltonian = []
  aux = []
  for i, line in enumerate(inputs):
      if i == 0:
          first = int(line)
      else:
          aux.append(line[0])
          if i % first == 0:
              obs_hamiltonian.append(aux)
              aux = []

  output = optimize_measurements(obs_hamiltonian)
  print(compression_ratio(obs_hamiltonian, output))
```

    0.5
    0.25

## Games

### games_100_TardigradeMasquerade_solution

Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/games_100_TardigradeMasquerade_template/tardigrade_template.py)

```
import sys
import pennylane as qml
from pennylane import numpy as np

def second_renyi_entropy(rho):
    """Computes the second Renyi entropy of a given density matrix."""
    # DO NOT MODIFY anything in this code block
    rho_diag_2 = np.diagonal(rho) ** 2.0
    return -np.real(np.log(np.sum(rho_diag_2)))

def compute_entanglement(theta):
    """Computes the second Renyi entropy of circuits with and without a tardigrade present.

    Args:
        - theta (float): the angle that defines the state psi_ABT

    Returns:
        - (float): The entanglement entropy of qubit B with no tardigrade
        initially present
        - (float): The entanglement entropy of qubit B where the tardigrade
        was initially present
    """

    dev = qml.device("default.qubit", wires=3)

    # QHACK #
    @qml.qnode(dev)
    def circuits(theta, tartigrade):
      if not tartigrade:
        qml.Hadamard(wires=0)
        qml.CNOT(wires=[0, 1])
        qml.PauliX(wires=0)
        return qml.density_matrix(wires=[0])

    def partial_trace(rho, qubit_2_keep): # Credits: GitHub @neversakura.
        num_qubit = int(np.log2(rho.shape[0]))
        qubit_axis = [(i, num_qubit + i) for i in range(num_qubit)
                      if i not in qubit_2_keep]
        minus_factor = [(i, 2 * i) for i in range(len(qubit_axis))]
        minus_qubit_axis = [(q[0] - m[0], q[1] - m[1])
                            for q, m in zip(qubit_axis, minus_factor)]
        rho_res = np.reshape(rho, [2, 2] * num_qubit)
        qubit_left = num_qubit - len(qubit_axis)
        for i, j in minus_qubit_axis:
            rho_res = np.trace(rho_res, axis1=i, axis2=j)
        if qubit_left > 1:
            rho_res = np.reshape(rho_res, [2 ** qubit_left] * 2)
        return rho_res

    psi_0 = np.array([1, 0])
    psi_1 = np.array([0, 1])
    g_bt = np.kron(psi_0, psi_0)
    e_bt=np.cos(theta/2)*np.kron(psi_1,psi_0)+np.sin(theta/2)*np.kron(psi_0,psi_1)
    psi_abt = 1/np.sqrt(2)*(np.kron(psi_0, e_bt)+np.kron(psi_1, g_bt))
    rho_abt = np.outer(psi_abt, np.conj(psi_abt))
    rho_b = partial_trace(rho_abt, [1])
    mu_b = circuits(theta, 0)
    s_mub = second_renyi_entropy(mu_b)
    s_rhob = second_renyi_entropy(rho_b)

    return s_mub, s_rhob
    # QHACK #

'''
if __name__ == "__main__":
    # DO NOT MODIFY anything in this code block
    theta = np.array(sys.stdin.read(), dtype=float)

    S2_without_tardigrade, S2_with_tardigrade = compute_entanglement(theta)
    print(*[S2_without_tardigrade, S2_with_tardigrade], sep=",")
'''
```

    '\nif __name__ == "__main__":\n    # DO NOT MODIFY anything in this code block\n    theta = np.array(sys.stdin.read(), dtype=float)\n\n    S2_without_tardigrade, S2_with_tardigrade = compute_entanglement(theta)\n    print(*[S2_without_tardigrade, S2_with_tardigrade], sep=",")\n'

```
in1=0.7745598277086928
in2=4.91504584447079
ans1=0.6931471805599457,0.6730064814954014
ans2=0.6931471805599457,0.545165726123828

for theta in [in1, in2]:
  S2_without_tardigrade, S2_with_tardigrade = compute_entanglement(theta)
  print(*[S2_without_tardigrade, S2_with_tardigrade], sep=",")
```

    0.6931471805599457,0.6730064814954021
    0.6931471805599457,0.5451657261238287

### games_200_CHSH_solution

Template: [here](https://github.com/XanaduAI/QHack/blob/master/Coding_Challenges/games_200_CHSH_template/CHSH_game_template.py)

```
#! /usr/bin/python3

import sys
import pennylane as qml
from pennylane import numpy as np

dev = qml.device("default.qubit", wires=2)

def prepare_entangled(alpha, beta):
    """Construct a circuit that prepares the (not necessarily maximally) entangled state in terms of alpha and beta
    Do not forget to normalize.

    Args:
        - alpha (float): real coefficient of |00>
        - beta (float): real coefficient of |11>
    """

    # QHACK #
    theta=2*np.arctan2(beta,alpha)
    qml.RY(theta,wires=0)
    qml.CNOT(wires=[0,1])

    # QHACK #

@qml.qnode(dev)
def chsh_circuit(theta_A0, theta_A1, theta_B0, theta_B1, x, y, alpha, beta):
    """Construct a circuit that implements Alice's and Bob's measurements in the rotated bases

    Args:
        - theta_A0 (float): angle that Alice chooses when she receives x=0
        - theta_A1 (float): angle that Alice chooses when she receives x=1
        - theta_B0 (float): angle that Bob chooses when he receives x=0
        - theta_B1 (float): angle that Bob chooses when he receives x=1
        - x (int): bit received by Alice
        - y (int): bit received by Bob
        - alpha (float): real coefficient of |00>
        - beta (float): real coefficient of |11>

    Returns:
        - (np.tensor): Probabilities of each basis state
    """

    prepare_entangled(alpha, beta)

    # QHACK #
    if x==0:
        qml.RY(2*theta_A0,wires=0)
    else:
        qml.RY(2*theta_A1,wires=0)
    if y==0:
        qml.RY(2*theta_B0+np.pi/2,wires=1)
    else:
        qml.RY(2*theta_B1+np.pi/2,wires=1)
    # QHACK #

    return qml.probs(wires=[0, 1])

def winning_prob(params, alpha, beta):
    """Define a function that returns the probability of Alice and Bob winning the game.

    Args:
        - params (list(float)): List containing [theta_A0,theta_A1,theta_B0,theta_B1]
        - alpha (float): real coefficient of |00>
        - beta (float): real coefficient of |11>

    Returns:
        - (float): Probability of winning the game
    """

    # QHACK #
    win=0
    p=chsh_circuit(params[0],params[1],params[2],
                   params[3],1,1,alpha,beta)
    win=win+p[1]+p[2]
    p=chsh_circuit(params[0],params[1],params[2],
                   params[3],0,1,alpha,beta)
    win=win+p[0]+p[3]
    p=chsh_circuit(params[0],params[1],params[2],
                   params[3],1,0,alpha,beta)
    win=win+p[0]+p[3]
    p=chsh_circuit(params[0],params[1],params[2],
                   params[3],0,0,alpha,beta)
    win=win+p[0]+p[3]
    # QHACK #

    return win/4

def optimize(alpha, beta):
    """Define a function that optimizes theta_A0, theta_A1, theta_B0, theta_B1 to maximize the probability of winning the game

    Args:
        - alpha (float): real coefficient of |00>
        - beta (float): real coefficient of |11>

    Returns:
        - (float): Probability of winning
    """

    def cost(params):
        """Define a cost function that only depends on params, given alpha and beta fixed"""
        return 1-winning_prob(params, alpha, beta)**2

    # QHACK #

    #Initialize parameters, choose an optimization method and number of steps
    init_params = np.zeros(4, requires_grad=True)
    opt = qml.AdagradOptimizer(stepsize=0.23)
    steps = 100

    # QHACK #

    # set the initial parameter values
    params = init_params

    for i in range(steps):
        # update the circuit parameters
        # QHACK #

        params = opt.step(cost, params)

        # QHACK #

    return winning_prob(params, alpha, beta)
'''
if __name__ == '__main__':
    inputs = sys.stdin.read().split(",")
    output = optimize(float(inputs[0]), float(inputs[1]))
    print(f"{output}")
'''
```

    '\nif __name__ == \'__main__\':\n    inputs = sys.stdin.read().split(",")\n    output = optimize(float(inputs[0]), float(inputs[1]))\n    print(f"{output}")\n'

```
in1=1,1
#0.85355
in2=1.0,0.0
#0.75

for inputs in [in1, in2]:
  output = optimize(float(inputs[0]), float(inputs[1]))
  print(f"{output}")
```

    0.8535533905932737
    0.75

#    References

\[1] [QHack2022 problemset templates](https://github.com/XanaduAI/QHack/tree/master/Coding_Challenges).

[2] [PennyLane Documentation](https://pennylane.readthedocs.io/en/stable/).

[3] Nielsen MA, Chuang IL. 2010. Quantum Computation and Quantum Information. New York: [Cambridge Univ. Press.](https://doi.org/10.1017/CBO9780511976667) 10th Anniv. Ed.