 Trusted Notebook" width="500 px" align="left">

# Wigner Functions
---
### Contributers
Russell P. Rundle$^{1,2}$, Todd Tilma$^{1,3}$, Vincent M. Dwyer$^{1,2}$, Mark J. Everitt (m.j.everitt@physics.org)$^{1}$

1 Quantum Systems Engineering Research Group, Physics Dpartment, Loughborough University, UK

2 Wolfson School, Loughborough University, UK

3 Tokyo Institute of Technology, Japan

## Introduction

In this notebook we demonstrate how to create Wigner functions by either using the full state or by measuring points in phase space. We will show the different methods which can be used by inputting an arbitrary state or just measuring the state on both the simulators and the IBM Quantum Experience.
The spin Wigner function presented here is based on work from [*T Tilma, MJ Everitt, JH Samson, WJ Munro, K Nemoto, Phys. Rev. Lett. 117, 180401*](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.117.180401) and can be calculated analytically as

$W(\boldsymbol{\Omega}) = \mathrm{Tr}\left[\hat{\rho} \; \hat{U}(\boldsymbol{\Omega}) \hat{\Pi} \hat{U}^\dagger(\boldsymbol{\Omega})\right]$

Where $\hat{U}$ is the rotation by the Euler angles and $\hat{\Pi}$ is the parity, such that

$\hat{\Pi} = \frac{1}{2}
\begin{pmatrix}
1+\sqrt{3} & 0\\
0 & 1-\sqrt{3}
\end{pmatrix}$

for one qubit. The Hilbert space for a single qubit is spanned by the Euler angles where $\theta$ (the elevation) and $\phi$ (the azimuth) thus the full Wigner function for $n$ qubits is given by $2n$ degrees of freedom.

When there is more than one qubit, we take the Kronecker tensor product of the $\hat{U}$s and $\hat{\Pi}$. In the following examples, various methods of extracting information from 2 entangled qubits from four degrees of freedom are shown.
These methods include taking an the equal angle slice, where we set the angles $\theta_0 = \theta_1 = \theta$ and $\phi_0 = \phi_1 = \phi$. We can also set some angles to be constant values as we plot the remaining angles against each other, an example of this can be seen when plotting the plaquettes where we set $\phi_0=\phi_1=0$ and we plot $\theta_0$ against $\theta_1$. Finally we show how we can simplify the Wigner function by just taking a 2 dimensional curve around the function, this is shown when we look at the equal angle equatorial slice where we take $\theta_i=\theta=\pi/2$ for all $\theta$s and $\phi_i=\phi$ for all values of $\phi$ where $0\leq\phi<2\pi$.
With this visualisation methods we show how these points in phase space can be measured directly by using the Wigner function tomography module and it works by using the u3 gates to rotate to points in phase space. Below is an example for measing points for a Bell-state, the first two gates create the Bell-state and the two u3 are example rotations to a point in phase space.

# Wigner Functions for Arbitrary States

First we will look at the Wigner function tomography module and show how we can create the spin Wigner function on a Bloch sphere for an arbitrary state. All that is necessary is to input a density matrix or state vector for a known state and then set the desired resolution for the plot (if no resolution is set, the mesh of the sphere will default to 100x100)

```python
# importing the QISKit
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, execute, Aer, IBMQ

# import numpy
import numpy as np

# import tomography library
import qiskit.tools.qcvv.tomography as tomo

#visualization packages
from wigner import plot_wigner_function, plot_wigner_data
```

## Wigner Function for an entangled Bell-state
We begin by creating the density matrix for a Bell-state (note that the matrices and state vectors need to be created in numpy matrix format), $\left(|00\rangle+|11\rangle\right)/\sqrt{2}$, which is given by

$
\frac{1}{2}
\begin{pmatrix}
1 & 0 & 0 & 1 \\
0 & 0 & 0 & 0 \\
0 & 0 & 0 & 0 \\
1 & 0 & 0 & 1
\end{pmatrix}
$

```python
density_matrix = np.matrix([[0.5, 0, 0, 0.5],[0, 0, 0, 0],[0, 0, 0, 0],[0.5, 0, 0, 0.5]])
print(density_matrix)
```

    [[0.5 0.  0.  0.5]
     [0.  0.  0.  0. ]
     [0.  0.  0.  0. ]
     [0.5 0.  0.  0.5]]

This density matrix can then be put into wigner_function which plots the Bloch sphere Wigner function for the equal angle slice, i.e. where $\theta_0 = \theta_1 = \theta$ and $\phi_0 = \phi_1 = \phi$

```python
plot_wigner_function(density_matrix, res=200)
```

If it is desired to calculate the analytic expression for the Wigner function, we can import the sympy package and run the Wigner function calculations symbolically. The below code is almost identical to the code in the Wigner function tomography module but edited to work with symbolics rather than run through the points in the mesh of the sphere.

```python
import sympy as sym
from sympy.physics.quantum import TensorProduct

num = int(np.log2(len(density_matrix)))
harr = sym.sqrt(3)
Delta_su2 = sym.zeros(2)
Delta = sym.ones(1)
for qubit in range(num):
    phi = sym.Indexed('phi', qubit)
    theta = sym.Indexed('theta', qubit)
    costheta  = harr*sym.cos(2*theta)
    sintheta  = harr*sym.sin(2*theta)
    Delta_su2[0,0] =  (1+costheta)/2
    Delta_su2[0,1] = -(sym.exp(2j*phi)*sintheta)/2
    Delta_su2[1,0] = -(sym.exp(-2j*phi)*sintheta)/2
    Delta_su2[1,1] =  (1-costheta)/2
    Delta = TensorProduct(Delta,Delta_su2)
W = sym.trace(density_matrix*Delta)
print(sym.latex(W))
```

    0.5 \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) + 0.5 \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) + 0.375 e^{2.0 i {\phi}_{0}} e^{2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{0} \right )} \sin{\left (2 {\theta}_{1} \right )} + 0.375 e^{- 2.0 i {\phi}_{0}} e^{- 2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{0} \right )} \sin{\left (2 {\theta}_{1} \right )}

pasting this result into a latex environment returns the Wigner function

$W = 0.5 \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) + 0.5 \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) + 0.375 e^{2.0 i \phi_{0}} e^{2.0 i \phi_{1}} \sin{\left (2 \theta_{0} \right )} \sin{\left (2 \theta_{1} \right )} + 0.375 e^{- 2.0 i \phi_{0}} e^{- 2.0 i \phi_{1}} \sin{\left (2 \theta_{0} \right )} \sin{\left (2 \theta_{1} \right )}$

### Wigner function using State tomography

We can create the density matrix for a given state by using the state tomography module, more information for the tomography module can be found in the [quantum state tomography tutorial](https://github.com/QISKit/qiskit-tutorial/blob/master/3_qcvv/state_tomography.ipynb). First creating an entangled Bell-state with the following set of gates and then taking the state tomography, we can create the spin Wigner function for the measured tomography

```python
number_of_qubits = 2
backend = Aer.get_backend('qasm_simulator')
shots = 1024
bell_qubits = [0, 1]
qr = QuantumRegister(2, name='qr')
cr = ClassicalRegister(2, name='cr')
bell = QuantumCircuit(qr, cr, name='Bell')
bell.h(qr[0])
bell.cx(qr[0],qr[1]);
```

The above code generates the circuits needed to create the Bell-state and below we show how to then build the density matrix for the state using the tomography module. The density matrix created can then be put into the Wigner fucntion visualisation module

```python
bell_tomo_set = tomo.state_tomography_set([0, 1])
bell_tomo_circuits = tomo.create_tomography_circuits(bell, qr, cr, bell_tomo_set)
bell_tomo_result = execute(bell_tomo_circuits, backend=backend, shots=shots).result()
bell_tomo_data = tomo.tomography_data(bell_tomo_result, 'Bell', bell_tomo_set)
rho_fit_sim = tomo.fit_tomography_data(bell_tomo_data)
plot_wigner_function(np.matrix(rho_fit_sim), res=200)
```

Again, using the same code from before, we can calculate the analytic expression for the Wigner function from the generated density matrix, it can be seen that since the density matrix is less sparse, the resulting Wigner function has many more terms

```python
Delta_su2 = sym.zeros(2)
Delta = sym.ones(1)
for qubit in range(num):
    phi = sym.Indexed('phi', qubit)
    theta = sym.Indexed('theta', qubit)
    costheta  = harr*sym.cos(2*theta)
    sintheta  = harr*sym.sin(2*theta)
    Delta_su2[0,0] =  (1+costheta)/2
    Delta_su2[0,1] = -(sym.exp(2j*phi)*sintheta)/2
    Delta_su2[1,0] = -(sym.exp(-2j*phi)*sintheta)/2
    Delta_su2[1,1] =  (1-costheta)/2
    Delta = TensorProduct(Delta,Delta_su2)
W = sym.trace(np.matrix(rho_fit_sim)*Delta)
print(sym.latex(W))
```

    0.499936777529623 \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) + 0.00184002904462755 \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) - \frac{\sqrt{3} \left(0.000646212815416196 - 0.00312198107705811 i\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) e^{2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{1} \right )}}{2} - \frac{\sqrt{3} \left(0.000646212815416196 + 0.00312198107705811 i\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) e^{- 2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{1} \right )}}{2} + 0.00184601514148403 \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) + 0.496377178284265 \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) - \frac{\sqrt{3} \left(0.00182791613202358 + 0.00242837881744504 i\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) e^{2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{1} \right )}}{2} - \frac{\sqrt{3} \left(0.00182791613202358 - 0.00242837881744504 i\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) e^{- 2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{1} \right )}}{2} - \frac{\sqrt{3} \left(-0.0018595169535378 - 0.00449366757773884 i\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) e^{2.0 i {\phi}_{0}} \sin{\left (2 {\theta}_{0} \right )}}{2} - \frac{\sqrt{3} \left(-0.0018595169535378 + 0.00449366757773884 i\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) e^{- 2.0 i {\phi}_{0}} \sin{\left (2 {\theta}_{0} \right )}}{2} - \frac{\sqrt{3} \left(0.00429726480202502 + 0.00515185287893124 i\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) e^{2.0 i {\phi}_{0}} \sin{\left (2 {\theta}_{0} \right )}}{2} - \frac{\sqrt{3} \left(0.00429726480202502 - 0.00515185287893124 i\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) e^{- 2.0 i {\phi}_{0}} \sin{\left (2 {\theta}_{0} \right )}}{2} + \frac{3 \left(0.493176776136537 + 0.00470038875792548 i\right) e^{2.0 i {\phi}_{0}} e^{2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{0} \right )} \sin{\left (2 {\theta}_{1} \right )}}{4} + \frac{3 \left(0.000998839809504471 + 0.0015077093321832 i\right) e^{2.0 i {\phi}_{0}} e^{- 2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{0} \right )} \sin{\left (2 {\theta}_{1} \right )}}{4} + \frac{3 \left(0.000998839809504471 - 0.0015077093321832 i\right) e^{- 2.0 i {\phi}_{0}} e^{2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{0} \right )} \sin{\left (2 {\theta}_{1} \right )}}{4} + \frac{3 \left(0.493176776136537 - 0.00470038875792548 i\right) e^{- 2.0 i {\phi}_{0}} e^{- 2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{0} \right )} \sin{\left (2 {\theta}_{1} \right )}}{4}

$W = 0.493698558640376 \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) + 0.00275286172575906 \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) - \frac{\sqrt{3}}{2} \left(-0.00534735110770023 - 0.00770494561267218 i\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) e^{2.0 i \phi_{1}} \sin{\left (2 \theta_{1} \right )} - \frac{\sqrt{3}}{2} \left(-0.00534735110770023 + 0.00770494561267218 i\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) e^{- 2.0 i \phi_{1}} \sin{\left (2 \theta_{1} \right )} + 0.00266528247889166 \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) + 0.500883297154973 \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) - \frac{\sqrt{3}}{2} \left(-0.0015149834996068 + 0.000102164609935808 i\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) e^{2.0 i \phi_{1}} \sin{\left (2 \theta_{1} \right )} - \frac{\sqrt{3}}{2} \left(-0.0015149834996068 - 0.000102164609935808 i\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) e^{- 2.0 i \phi_{1}} \sin{\left (2 \theta_{1} \right )} - \frac{\sqrt{3}}{2} \left(-0.00628509857352323 - 0.000924492486476053 i\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) e^{2.0 i \phi_{0}} \sin{\left (2 \theta_{0} \right )} - \frac{\sqrt{3}}{2} \left(-0.00628509857352323 + 0.000924492486476053 i\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) e^{- 2.0 i \phi_{0}} \sin{\left (2 \theta_{0} \right )} - \frac{\sqrt{3}}{2} \left(-0.000462681165102337 + 0.00867331154973143 i\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) e^{2.0 i \phi_{0}} \sin{\left (2 \theta_{0} \right )} - \frac{\sqrt{3}}{2} \left(-0.000462681165102337 - 0.00867331154973143 i\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) e^{- 2.0 i \phi_{0}} \sin{\left (2 \theta_{0} \right )} + \frac{3}{4} \left(0.492697839219187 - 0.0085595434721166 i\right) e^{2.0 i \phi_{0}} e^{2.0 i \phi_{1}} \sin{\left (2 \theta_{0} \right )} \sin{\left (2 \theta_{1} \right )} + \frac{3}{4} \left(0.00247818568149116 + 0.000867188901661959 i\right) e^{2.0 i \phi_{0}} e^{- 2.0 i \phi_{1}} \sin{\left (2 \theta_{0} \right )} \sin{\left (2 \theta_{1} \right )} + \frac{3}{4} \left(0.00247818568149116 - 0.000867188901661959 i\right) e^{- 2.0 i \phi_{0}} e^{2.0 i \phi_{1}} \sin{\left (2 \theta_{0} \right )} \sin{\left (2 \theta_{1} \right )} + \frac{3}{4} \left(0.492697839219187 + 0.0085595434721166 i\right) e^{- 2.0 i \phi_{0}} e^{- 2.0 i \phi_{1}} \sin{\left (2 \theta_{0} \right )} \sin{\left (2 \theta_{1} \right )}$

# Experiment

```python
IBMQ.load_accounts()

backend = IBMQ.get_backend('ibmqx2')
max_credits = 8
shots = 1024
bell_qubits = [0, 1]
bell_tomo_set = tomo.state_tomography_set(bell_qubits)
bell_tomo_circuits = tomo.create_tomography_circuits(bell, qr, cr, bell_tomo_set)
bell_tomo_result = execute(bell_tomo_circuits, backend=backend, shots=shots,
                           max_credits=max_credits).result()
bell_tomo_data = tomo.tomography_data(bell_tomo_result, 'Bell', bell_tomo_set)
rho_fit_ibmqx = tomo.fit_tomography_data(bell_tomo_data)
plot_wigner_function(np.matrix(rho_fit_ibmqx), res=100)
```

```python
print(rho_fit_ibmqx)
```

    [[0.48153775+0.j         0.04986367-0.02071181j 0.05420926-0.01399036j
      0.42550767+0.00541452j]
     [0.04986367+0.02071181j 0.02967715+0.j         0.01582514-0.00135436j
      0.05000208+0.00902968j]
     [0.05420926+0.01399036j 0.01582514+0.00135436j 0.04391773+0.j
      0.05013479-0.00670961j]
     [0.42550767-0.00541452j 0.05000208-0.00902968j 0.05013479+0.00670961j
      0.44486737+0.j        ]]

```python
Delta_su2 = sym.zeros(2)
Delta = sym.ones(1)
for qubit in range(num):
    phi = sym.Indexed('phi', qubit)
    theta = sym.Indexed('theta', qubit)
    costheta  = harr*sym.cos(2*theta)
    sintheta  = harr*sym.sin(2*theta)
    Delta_su2[0,0] =  (1+costheta)/2
    Delta_su2[0,1] = -(sym.exp(2j*phi)*sintheta)/2
    Delta_su2[1,0] = -(sym.exp(-2j*phi)*sintheta)/2
    Delta_su2[1,1] =  (1-costheta)/2
    Delta = TensorProduct(Delta,Delta_su2)
W = sym.trace(np.matrix(rho_fit_ibmqx)*Delta)
print(sym.latex(W))
```

    0.444867367250469 \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) + 0.043917727243482 \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) - \frac{\sqrt{3} \left(0.0501347850838195 + 0.00670960560900125 i\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) e^{2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{1} \right )}}{2} - \frac{\sqrt{3} \left(0.0501347850838195 - 0.00670960560900125 i\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) e^{- 2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{1} \right )}}{2} + 0.0296771540842934 \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) + 0.481537751421756 \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) - \frac{\sqrt{3} \left(0.0498636678793869 + 0.0207118088508713 i\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) e^{2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{1} \right )}}{2} - \frac{\sqrt{3} \left(0.0498636678793869 - 0.0207118088508713 i\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{0} \right )}}{2} + \frac{1}{2}\right) e^{- 2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{1} \right )}}{2} - \frac{\sqrt{3} \left(0.0500020838134834 - 0.00902968211006529 i\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) e^{2.0 i {\phi}_{0}} \sin{\left (2 {\theta}_{0} \right )}}{2} - \frac{\sqrt{3} \left(0.0500020838134834 + 0.00902968211006529 i\right) \left(- \frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) e^{- 2.0 i {\phi}_{0}} \sin{\left (2 {\theta}_{0} \right )}}{2} - \frac{\sqrt{3} \left(0.0542092572641403 + 0.0139903637993998 i\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) e^{2.0 i {\phi}_{0}} \sin{\left (2 {\theta}_{0} \right )}}{2} - \frac{\sqrt{3} \left(0.0542092572641403 - 0.0139903637993998 i\right) \left(\frac{\sqrt{3} \cos{\left (2 {\theta}_{1} \right )}}{2} + \frac{1}{2}\right) e^{- 2.0 i {\phi}_{0}} \sin{\left (2 {\theta}_{0} \right )}}{2} + \frac{3 \left(0.425507670232922 - 0.00541452224395614 i\right) e^{2.0 i {\phi}_{0}} e^{2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{0} \right )} \sin{\left (2 {\theta}_{1} \right )}}{4} + \frac{3 \left(0.015825135999363 + 0.00135435891394559 i\right) e^{2.0 i {\phi}_{0}} e^{- 2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{0} \right )} \sin{\left (2 {\theta}_{1} \right )}}{4} + \frac{3 \left(0.015825135999363 - 0.00135435891394559 i\right) e^{- 2.0 i {\phi}_{0}} e^{2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{0} \right )} \sin{\left (2 {\theta}_{1} \right )}}{4} + \frac{3 \left(0.425507670232922 + 0.00541452224395614 i\right) e^{- 2.0 i {\phi}_{0}} e^{- 2.0 i {\phi}_{1}} \sin{\left (2 {\theta}_{0} \right )} \sin{\left (2 {\theta}_{1} \right )}}{4}

$0.32699519833677 \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) + 0.124306773174647 \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) - \frac{\sqrt{3}}{2} \left(0.0285310304913017 + 0.0238741788035984 i\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) e^{2.0 i \phi_{1}} \sin{\left (2 \theta_{1} \right )} - \frac{\sqrt{3}}{2} \left(0.0285310304913017 - 0.0238741788035984 i\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) e^{- 2.0 i \phi_{1}} \sin{\left (2 \theta_{1} \right )} + 0.124202902962916 \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) + 0.424495125525667 \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) - \frac{\sqrt{3}}{2} \left(0.0490359773360699 + 0.0381005337507329 i\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) e^{2.0 i \phi_{1}} \sin{\left (2 \theta_{1} \right )} - \frac{\sqrt{3}}{2} \left(0.0490359773360699 - 0.0381005337507329 i\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{0} \right )} + \frac{1}{2}\right) e^{- 2.0 i \phi_{1}} \sin{\left (2 \theta_{1} \right )} - \frac{\sqrt{3}}{2} \left(0.0512856636758563 + 0.0414367583959388 i\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) e^{2.0 i \phi_{0}} \sin{\left (2 \theta_{0} \right )} - \frac{\sqrt{3}}{2} \left(0.0512856636758563 - 0.0414367583959388 i\right) \left(- \frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) e^{- 2.0 i \phi_{0}} \sin{\left (2 \theta_{0} \right )} - \frac{\sqrt{3}}{2} \left(0.0408691303527403 + 0.0280408874045363 i\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) e^{2.0 i \phi_{0}} \sin{\left (2 \theta_{0} \right )} - \frac{\sqrt{3}}{2} \left(0.0408691303527403 - 0.0280408874045363 i\right) \left(\frac{\sqrt{3}}{2} \cos{\left (2 \theta_{1} \right )} + \frac{1}{2}\right) e^{- 2.0 i \phi_{0}} \sin{\left (2 \theta_{0} \right )} + \frac{3}{4} \left(0.285095302985003 - 0.00922257353994807 i\right) e^{2.0 i \phi_{0}} e^{2.0 i \phi_{1}} \sin{\left (2 \theta_{0} \right )} \sin{\left (2 \theta_{1} \right )} + \frac{3}{4} \left(-0.035281529513114 - 0.00875077955598942 i\right) e^{2.0 i \phi_{0}} e^{- 2.0 i \phi_{1}} \sin{\left (2 \theta_{0} \right )} \sin{\left (2 \theta_{1} \right )} + \frac{3}{4} \left(-0.035281529513114 + 0.00875077955598942 i\right) e^{- 2.0 i \phi_{0}} e^{2.0 i \phi_{1}} \sin{\left (2 \theta_{0} \right )} \sin{\left (2 \theta_{1} \right )} + \frac{3}{4} \left(0.285095302985003 + 0.00922257353994807 i\right) e^{- 2.0 i \phi_{0}} e^{- 2.0 i \phi_{1}} \sin{\left (2 \theta_{0} \right )} \sin{\left (2 \theta_{1} \right )}$

# Measuring Points in Phase Space

We will now look at how to create the circuits to measure set points in phase space. The following results are based on the figures in [*RP Rundle, PW Mills, T Tilma, JH Samson, MJ Everitt, Phys. Rev. A 96, 022117*](r p rundle prl)

## Plaquette Visualisation
Staying with the bell state, we will look here at the slice where $\phi_0=\phi_1=0$ and we plot $\theta_0$ against $\theta_1$

```python
theta1_points = 8
theta2_points = 8
number_of_points = theta1_points*theta2_points
the1 = [0]*number_of_points
the2 = [0]*number_of_points #initialize theta values
phis = [[0]*number_of_points]*number_of_qubits #set phi values to 0

point = 0
for i in range(theta1_points):
    for k in range(theta2_points):

        the1[point] = 2*i*np.pi/theta1_points
        the2[point] = 2*k*np.pi/theta2_points #create the values of theta for all points on plot
        point += 1

thetas = np.vstack((the1,the2))
bell_circuits = tomo.build_wigner_circuits(bell, phis, thetas,
                                           bell_qubits, qr, cr)

backend = Aer.get_backend('qasm_simulator')
shots = 1024
bell_result = execute(bell_circuits, backend=backend, shots=shots).result()
print(bell_result.status)
```

    COMPLETED

```python
wdata = tomo.wigner_data(bell_result, bell_qubits,
                        bell_circuits, shots=shots)
wdata = np.matrix(wdata)
wdata = wdata.reshape(theta1_points,theta2_points)
plot_wigner_data(wdata, method='plaquette')
```

## Curve on a Wigner fucntion
We can also take a line around phase space and plot a curve. A useful line to take is the equator of the equal angle slice, below we plot this slice also for the Bell-state. When we have an $n$-qubit maximally entangled state of the form $\left(|0...0\rangle + |1...1\rangle\right)/\sqrt{2}$, the equatorial slice will show sinusoidal behaviour with frequency $n$.

```python
equator_points = 64
theta = [np.pi/2]*equator_points
phi = [0]*equator_points

point = 0
for i in range(equator_points):
    phi[i] = 2*i*np.pi/equator_points

thetas = np.vstack((theta,theta))
phis = np.vstack((phi,phi))
bell_eq_circuits = tomo.build_wigner_circuits(bell, phis, thetas,
                                              bell_qubits, qr, cr)
bell_eq_result = execute(bell_eq_circuits, backend=backend, shots=shots).result()
wdata_eq = tomo.wigner_data(bell_eq_result,  bell_qubits,
                        bell_eq_circuits, shots=shots)
plot_wigner_data(wdata_eq, method='curve')
```

Recreating the equatorial slice of a five qubit GHZ state, $\left(|00000\rangle - |11111\rangle\right)/\sqrt{2}$ from the paper, the following gates are needed...

```python
number_of_qubits = 5
backend = Aer.get_backend('qasm_simulator')
shots = 1024
ghz_qubits = [0, 1, 2, 3, 4]
qr = QuantumRegister(5, name='qr')
cr = ClassicalRegister(5, name='cr')
ghz = QuantumCircuit(qr, cr, name='ghz')
ghz.h(qr[0])
ghz.h(qr[1])
ghz.x(qr[2])
ghz.h(qr[3])
ghz.h(qr[4])
ghz.cx(qr[0],qr[2])
ghz.cx(qr[1],qr[2])
ghz.cx(qr[3],qr[2])
ghz.cx(qr[4],qr[2])
ghz.h(qr[0])
ghz.h(qr[1])
ghz.h(qr[2])
ghz.h(qr[3])
ghz.h(qr[4])

equator_points = 64
thetas = [[np.pi/2]*equator_points]*number_of_qubits
phi = [0]*equator_points

point = 0
for i in range(equator_points):
    phi[i] = 2*i*np.pi/equator_points
phis = np.vstack((phi,phi,phi,phi,phi))
ghz_eq_circuits = tomo.build_wigner_circuits(ghz, phis, thetas,
                                             ghz_qubits, qr, cr)
ghz_eq_result = execute(ghz_eq_circuits, backend=backend, shots=shots).result()
wghzdata_eq = tomo.wigner_data(ghz_eq_result, ghz_qubits,
                               ghz_eq_circuits, shots=shots)
plot_wigner_data(wghzdata_eq, method='curve')
```

Since all the data for the plot is stored within `wghzdata_eq`, we can import matplotlib and go wild with different ways of plotting

```python
import matplotlib.pyplot as plt
plt.plot(phi, wghzdata_eq, 'o')
plt.axis([0, 2*np.pi, -0.6, 0.6])
plt.show()
```

```python
density_matrix = np.zeros((32,32))
density_matrix[0][0] = 0.5
density_matrix[0][31] = -0.5
density_matrix[31][0] = -0.5
density_matrix[31][31] = 0.5
plot_wigner_function(density_matrix, res=200)
```


# Project 1: Navigation

## 1: Learning algorithm

### Description of algorithm

Generally, in Q-learning, the goal is to find the true underlying Q-values, represented by $q^*$. In Deep Q-learning, neural networks are used to approximate $q^*$. The objective is then to find the weights, $w$, of the Q-network which best approximates $q^*$. That is, to minimize

\begin{equation} \min_w (q^*(s,a) - q(s,a;w))^2  \end{equation}

The Double Q-Network model with an Experience Buffer and Fixed Q-targets can be summarized as follows:

1. We start by creating a Replay Buffer (for Experience Replay) of size BUFFER_SIZE. This means that we will save the last BUFFER_SIZE experiences consisting of ($s_t$, $a_t$, $r_t$, $s_{t+1}$, $d_t$) tuples where
    * $s_t$ is the state at step t,
    * $a_t$ is the action chosen at step t,
    * $r_t$ is the reward received after taking action $a_t$ at state $s_t$,
    * $s_{t+1}$ is the resulting next state, and
    * $d_t$ which is True if we ended up in a terminal state.

The motivation for the Replay Buffer is to break the correleation between consecutive (state, action)-pairs. By sampling randomly from this "memory", we break this correlation.

2. Next, two neural networks are initialized with random weights. The network $q(s,a;w)$ is used to approximate the true underlying Q-values. Therefore, if we only use one network, we would end up with the update

\begin{equation} \Delta w = \alpha(r + \gamma \max_a q(s',a;w) - q(s,a;w))\nabla_w q(s,a;w)  , \end{equation}

which turns out to be unstable, since the weights we are updating are also present in the TD-target (i.e., $r + \gamma \max_a q(s',a;w)$). Therefore, we introduce "fixed Q-targets". This means that during learning, we fix the target, $w'$, and get

\begin{equation} \Delta w = \alpha(r + \gamma \max_a q(s',a;w') - q(s,a;w))\nabla_w q(s,a;w). \end{equation}

3. After initializations, we proceed iteratively:
    * From the given state $s_t$, choose action using an $\epsilon$-greedy strategy and observe reward $r_t$, the next state $s_{t+1}$ and boolean $d_t$ to see if the episode has ended.
    * Store the experience ($s_t$, $a_t$, $r_t$, $s_{t+1}$, $d_t$) in the Replay Buffer.
    * If ``t % UPDATE_EVERY = 0``, we draw a sample of BATCH_SIZE from the Replay Buffer, and take an optimization step according to the equation above.
    * Update the fixed Q-targets after learning, by the soft update
        \begin{equation} w' = \tau w + (1-\tau) w'. \end{equation}
    This process continues until convergence. In this particular case, the problem is considered solved when the agent obtains an average score above 13 over 100 consecutive episodes.

### Chosen Hyperparameters

* ``BUFFER_SIZE = int(1e5)``:  Replay buffer size
* ``BATCH_SIZE = 64``:         Minibatch size
* ``GAMMA = 0.99``:            Discount factor
* ``TAU = 1e-3``:              For soft update of target parameters
* ``LR = 5e-4``:               Learning rate
* ``UPDATE_EVERY = 4``:        How often to update the network

### Neural Network

The neural network used is a simple feed-forward network with fully connected layers:

* Layer 1: (state_size, 64)
* ReLU 1
* Layer 2: (64, 64)
* ReLU 2
* Layer 3: (64, action_size)

Here, state_size = 37 and action_size = 4.

## 2: Plot of Rewards

This setup was able to solve the problem in 462 episodes. A plot of score per episode is illustraded below:

## 3: Ideas for Future Work

While the problem is solved reasonably quickly with basic Deep Q-learning, the agent could possibly improved in ways including:

* Search for optimal hyperparameters and neural network size/shape.
* Try extensions of the Q-learning algorithm, including "Double DQN" and "Dueling DQN", and apply prioritized experience replay rather than the current uniform implementation.
* Observe the raw pixels instead (or in addition to) the current ray-based "sensor" inputs.


Solution to: [Day 2: Compound Event Probability](https://www.hackerrank.com/challenges/s10-mcq-3/problem)

Table of Contents

- Table of Contents
- Compound Events, Mutually Exclusive Events, and Collectively Exhaustive Events
- Math Solution
    - Information
- Monte Carlo Solution
    - Process
    - Imports
    - Instantiate Urns
    - Drawing permutations
    - Main

```javascript
%%javascript
$.getScript('https://kmahelona.github.io/ipython_notebook_goodies/ipython_notebook_toc.js')
```

    <IPython.core.display.Javascript object>

## Compound Events, Mutually Exclusive Events, and Collectively Exhaustive Events
Let's consider 2 events: A and B.
A compound event is a combination of 2 or more simple events.
- If A and B are simple events, then A $\cup$ B denotes the occurrence of either A *or* B.
- Similarly, A $\cap$ B denotes the occurrence of A *and* B occuring together

A and B are said to be **mutually exclusive** or disjoint if they have no events in common.

\begin{equation}
\large
A \cap B = 0
\end{equation}
or...
\begin{equation}
\large
P(A) \cap P(B) = 0
\end{equation}

The probability of any of 2 or more events occurring is the union ($\cup$) of events. Because disjoint probabilities have no common events, the probability of the union of disjoint events is the sum of the events' individual probabilities. A and B are said to be **collectively exhaustive** if their union covers all events in the sample space
\begin{equation}
\large
A \cup B = S
\end{equation}
or...
\begin{equation}
\large
P(A \cup B) = 1
\end{equation}

This brings us to our next **fundamental rule of probability**:
- if 2 events, A and B, are disjoint, then the probability of either event is the sum of the probabilities of the events

\begin{equation}
\large
P(\text{A or B}) = P(A) + P(B)
\end{equation}

If the outcome of the first event (A) has no impact on the second event (B), then they are considered to be independent (e.g., tossing a fair coin).

This brings us to the next fundamental rule of probability: *the multiplication rule**. It states that
- if two events, A and B, are independent, then the probability of both events is the product of the probabilities for each event

\begin{equation}
\large
P(\text{A and B}) = P(A) * P(B)
\end{equation}

The chance of all events occurring in a sequence of events is called the intersection ($\cap$) of those events.

# Math Solution

- Urn X contains 4 red balls and 3 black balls, 7 balls total.
- Urn Y contains 5 red balls and 4 black balls, 9 balls total.
- Urn Z contains 4 red balls and 4 black balls, 8 balls total.

## Information
- Experiment: Given 3 urns with 2 options, what is the compound probability of drawing 2 red and 1 black.
- Space: 8 * 9 * 8 = 576 possible permutations
- To calculate the probability of drawing one black ball and 2 red balls from different urns (independent events), we can multiply probabilites between urns. More specifically, we can look at the probability of drawing one black ball from one urn and the probability of drawing 2 red balls from the other two urns.
- Formula:

\begin{equation}
\large
\sum_{0}^{N}P(\text{1 black ball})
\end{equation}

\begin{equation}
\large
P(A) = (\frac{3}{7}*\frac{5}{9}*\frac{1}{2}) + (\frac{4}{7}*\frac{4}{9}*\frac{1}{2}) + (\frac{4}{7}*\frac{5}{9}*\frac{1}{2})
\end{equation}

\begin{equation}
\large
P(A) = \frac{15}{126} + \frac{16}{126} + \frac{20}{126}
\end{equation}

\begin{equation}
\large
P(A) = \frac{51}{126}
\end{equation}

\begin{equation}
\large
P(A) = \frac{17}{42}
\end{equation}

# Monte Carlo Solution

## Process
- Use A urns with B black balls and C red balls, represented by (1, 0) respectively
- Create all possible permutations of balls drawn from each urn.
- Count SUM(combinations) == 1, or where only 1 black ball.
- Divide by S, (P(S)), or the total number of events.

## Imports

```python
from typing import List, Tuple
```

## Instantiate Urns

```python
def create_urns(urn_balls: Tuple[Tuple[int, int]]) -> List[List[int]]:
	"""Returns a list of urns and their number of red and black balls.

	Input is a tuple of tuples representing each urn and their number of red and black balls.
	Red and black balls are represented by 0 and 1 respectively
	"""
	urns = []
	for balls in urn_balls:
		# red & black balls, respectively
		urn = [0 for val in range(balls[0])] + [1 for val in range(balls[1])]
		urns.append(urn)
	return urns
```

## Drawing permutations

```python
def get_all_combos(urns: List[List[int]]) -> List[List[int]]:
	"""Returns list of all combinations of drawn balls from urns."""

	def get_balls_from_urn(urn: List[int]) -> int:
		"""Generator function to yield balls from urn."""
		for ball in urn:
			yield ball

	def recursively_draw_balls(processed_urns: int, ball_combos: List[List[int]]) -> List[List[int]]:
		"""Recursive function to draw balls from urns."""
		## Base case
		if processed_urns == len(urns):
			return ball_combos

		new_ball_combos = []

		## Draw first balls
		if not len(ball_combos):
			new_ball_combos = [[val] for val in get_balls_from_urn(urns[processed_urns])]

		## Create new lists for each list
		else:
			for combo in ball_combos:
				new_combo = [combo + [val] for val in get_balls_from_urn(urns[processed_urns])]
				new_ball_combos += new_combo

		processed_urns += 1
		return recursively_draw_balls(processed_urns, new_ball_combos)

	return recursively_draw_balls(0, list())
```

## Main

```python
def main():
	## Create urns with balls
	urn_balls = (
		(4, 3),			# red, black respectively
		(5, 4),
		(4, 4)
	)
	urns = create_urns(urn_balls)

	## Create all possible permutations
	ball_combos = get_all_combos(urns)

	## Count permutations where SUM == 1, i.e. 1 black ball.
	one_black_ball_permus = 0
	for combo in ball_combos:
		if sum(combo) == 1:
			one_black_ball_permus += 1

	## P(A) /P(s)
	p_a = one_black_ball_permus / len(ball_combos)
	print( round(p_a, 5))
```

```python
if __name__ == "__main__":
	main()
```

    0.40476