$\newcommand{\bra}[1]{\left\langle{#1}\right|}$ $\newcommand{\ket}[1]{\left|{#1}\right\rangle}$
The purpose of this notebook is to simulate the CHSH experiment described in the IBM Quantum Experience tutorial in the section entitled
Multiple Qubits, Gates, and Entangled States/Entanglement and Bell Tests
First change your working directory to the qubiter directory in your computer, and add its path to the path environment variable.
import os
import sys
print(os.getcwd())
os.chdir('../../')
print(os.getcwd())
sys.path.insert(0,os.getcwd())
/home/rrtucci/PycharmProjects/qubiter/qubiter/jupyter_notebooks /home/rrtucci/PycharmProjects/qubiter
from qubiter.SEO_writer import *
from qubiter.SEO_simulator import *
from qubiter.StateVec import *
import numpy as np
loaded OneQubitGate, WITHOUT autograd.numpy
First, we define matrices $S, T, H, \sigma_X, \sigma_Y, \sigma_Z, I_2$. We will denote the Pauli matrices by $\sigma_X, \sigma_Y, \sigma_Z$
Recal that
$S = diag(1, i) = e^{i \frac{\pi}{4}} e^{-i \frac{\pi}{4} \sigma_Z}$
$T = diag(1, e^{i \frac{\pi}{4}}) = e^{i \frac{\pi}{8}} e^{-i \frac{\pi}{8} \sigma_Z}$
$H = \frac{1}{\sqrt{2}}(\sigma_Z + \sigma_X)$
$H^2 = 1$
$H\sigma_X H = \sigma_Z$
smat = np.matrix([[1, 0], [0, 1j]])
tmat = np.matrix([[1, 0], [0, np.exp(1j*np.pi/4)]])
had = np.matrix([[1, 1], [1, -1]])/np.sqrt(2)
sigx = np.matrix([[0, 1], [1, 0]])
sigy = np.matrix([[0, -1j], [1j, 0]])
sigz = np.matrix([[1, 0], [0, -1]])
id2 = np.matrix([[1, 0], [0, 1]])
Define $\sigma_{n} = \hat{n}\cdot\vec{\sigma}$ for any 3dim unit vector $\hat{n}$.
Recall that
$\sigma_Z\ket{0_Z} = \ket{0_Z}$
$\sigma_Z\ket{1_Z} = -\ket{1_Z}$,
or, more succinctly,
$\sigma_Z\ket{b_Z} = (-1)^b\ket{b_Z}$
for $b=0,1$.
Likewise,
$\sigma_n\ket{b_n} = (-1)^b\ket{b_n}$
for any 3dim unit vector $\hat{n}$ and $b=0, 1$.
One can show by Taylor expansion that
$e^{i\theta \sigma_n} = \cos(\theta) + i\sigma_n \sin(\theta)$
def exp_mat2(theta, vec4):
# vec4 is 4 dimensional np.array. Zero component not used.
unit_vec = np.array([0, vec4[1], vec4[2], vec4[3]])
unit_vec = unit_vec/np.linalg.norm(unit_vec)
mat = unit_vec[1]*sigx + unit_vec[2]*sigy + unit_vec[3]*sigz
return np.cos(theta)*id2 + 1j*mat*np.sin(theta)
Define
$roty = e^{i \frac{\pi}{8}\sigma_Y}$
$\hat{w} = \frac{1}{\sqrt{2}}(\hat{x} + \hat{z})$
$\hat{v} = \frac{1}{\sqrt{2}}(-\hat{x} + \hat{z})$
$sigw = \sigma_W = \frac{1}{\sqrt{2}}(\sigma_X + \sigma_Z)$
$sigv = \sigma_V = \frac{1}{\sqrt{2}}(-\sigma_X + \sigma_Z)$
roty = exp_mat2(np.pi/8, np.array([0, 0, 1, 0]))
sigw = (sigx + sigz)/np.sqrt(2)
sigv = (-sigx + sigz)/np.sqrt(2)
Check that
$\sigma_W = e^{-i \frac{\pi}{8}\sigma_Y}\sigma_Z e^{i \frac{\pi}{8}\sigma_Y}$
$\sigma_V = e^{i \frac{\pi}{8}\sigma_Y}\sigma_Z e^{-i \frac{\pi}{8}\sigma_Y}$
print(np.linalg.norm(sigw - roty.getH()*sigz*roty))
print(np.linalg.norm(sigv - roty*sigz*roty.getH()))
1.5700924586837752e-16 1.5700924586837752e-16
Check that
$ e^{i \frac{\pi}{8}\sigma_Y} = e^{-i \frac{\pi}{8}} S^\dagger H T H S$
roty1 = np.exp(-1j*np.pi/8)*smat.getH()*had*tmat*had*smat
print(np.linalg.norm(roty - roty1))
3.9641608582688534e-16
Therefore, (Note that $S$ and $\sigma_Z$ are both diagonal so they commute)
$\sigma_W = (S^\dagger H T^\dagger H S) \sigma_Z (S^\dagger H T H S)= (S^\dagger H T^\dagger H ) \sigma_Z ( H T H S)$
$\sigma_V = (S^\dagger H T H ) \sigma_Z ( H T^\dagger H S)$
Note that
$\sigma_Z =\ket{0_Z}\bra{0_Z} - \ket{1_Z}\bra{1_Z} $
so the same is true if we replace the $Z$ by $W$ or $V$ or any 3dim unit vector.
Therefore, a W measurement $\bra{b_W}$ is related to a Z measurment $\bra{ b_Z}$ by
$\bra{ b_W} = \bra{ b_Z} H T H S$
Likewise,
$\bra{ b_V} = \bra{ b_Z} H T^\dagger H S$
for $b= 0, 1$
Note that
$\bra{\psi} \sigma_A(0) \sigma_B(1)\ket{\psi} =\bra{\psi} \begin{array}{c} (\ket{0_A}\bra{0_A} - \ket{1_A}\bra{1_A} )(0) \\ (\ket{0_B}\bra{0_B} - \ket{1_B}\bra{1_B})(1) \end{array} \ket{\psi}$
so
$\bra{\psi} \sigma_A(0) \sigma_B(1)\ket{\psi} = Prob(0, 0) + Prob(1, 1) - Prob(0, 1) - Prob(1, 0)$
def write_bell_plus(file_prefix, bell_only=True, extra_had=False, t_herm=False):
num_qbits = 2
z_axis = 3
emb = CktEmbedder(num_qbits, num_qbits)
print('-------------------', file_prefix)
wr = SEO_writer(file_prefix, emb)
wr.write_one_qbit_gate(0, OneQubitGate.had2)
control_pos = 0
target_pos = 1
trols = Controls.new_single_trol(num_qbits, control_pos, kind=True)
wr.write_controlled_one_qbit_gate(
target_pos, trols, OneQubitGate.sigx)
if not bell_only:
if extra_had:
wr.write_one_qbit_gate(0, OneQubitGate.had2) # H(0)
wr.write_one_qbit_gate(1, OneQubitGate.rot_ax, [-np.pi/4, z_axis]) # S(1)
wr.write_one_qbit_gate(1, OneQubitGate.had2) # H(1)
if t_herm:
pm_one = -1
else:
pm_one = 1
wr.write_one_qbit_gate(1, OneQubitGate.rot_ax,
[-pm_one*np.pi/8, z_axis]) # T(1) if pm_one=1
wr.write_one_qbit_gate(1, OneQubitGate.had2) # H(1)
wr.close_files()
wr.print_pic_file(jup=True)
init_st_vec = StateVec.get_standard_basis_st_vec([0, 0])
sim = SEO_simulator(file_prefix, num_qbits, init_st_vec)
StateVec.describe_st_vec_dict(sim.cur_st_vec_dict, print_st_vec=True, do_pp=True,
omit_zero_amps=True, show_pp_probs=True)
fin_st_vec = sim.cur_st_vec_dict["pure"]
print('Prob(bit0=j, bit1=k) for j,k=0,1:')
prob_arr = np.abs(fin_st_vec.arr)**2
print(prob_arr)
mean = prob_arr[0, 0] \
+ prob_arr[1, 1] \
- prob_arr[0, 1] \
- prob_arr[1, 0]
print('mean=', mean)
return mean
# sigz(0)sigz(1) measurement
file_prefix = 'bell_zz_meas'
mean_zz = write_bell_plus(file_prefix, bell_only=True)
------------------- bell_zz_meas
1 | | H | 2 | X---@ |
*********branch= pure state vector: ZL convention (Zero bit Last in state tuple) (00)ZL ( 0.707107 + 0.000000j) prob=0.500000 (11)ZL ( 0.707107 + 0.000000j) prob=0.500000 total probability of state vector (=one if no measurements)= 1.000000 dictionary with key=qubit, value=(Prob(0), Prob(1)) {0: (0.5, 0.5), 1: (0.5, 0.5)} Prob(bit0=j, bit1=k) for j,k=0,1: [[0.5 0. ] [0. 0.5]] mean= 0.9999999999999998
# sigz(0)sigw(1) measurement
file_prefix = 'bell_zw_meas'
mean_zw = write_bell_plus(file_prefix, bell_only=False, extra_had=False, t_herm=False)
------------------- bell_zw_meas
1 | | H | 2 | X---@ | 3 | Rz | | 4 | H | | 5 | Rz | | 6 | H | |
*********branch= pure state vector: ZL convention (Zero bit Last in state tuple) (00)ZL ( 0.461940 - 0.461940j) prob=0.426777 (10)ZL (-0.191342 - 0.191342j) prob=0.073223 (01)ZL ( 0.191342 - 0.191342j) prob=0.073223 (11)ZL ( 0.461940 + 0.461940j) prob=0.426777 total probability of state vector (=one if no measurements)= 1.000000 dictionary with key=qubit, value=(Prob(0), Prob(1)) {0: (0.5, 0.5), 1: (0.5, 0.5)} Prob(bit0=j, bit1=k) for j,k=0,1: [[0.4267767 0.0732233] [0.0732233 0.4267767]] mean= 0.7071067811865472
# sigz(0)sigv(1) measurement
file_prefix = 'bell_zv_meas'
mean_zv = write_bell_plus(file_prefix, bell_only=False, extra_had=False, t_herm=True)
------------------- bell_zv_meas
1 | | H | 2 | X---@ | 3 | Rz | | 4 | H | | 5 | Rz | | 6 | H | |
*********branch= pure state vector: ZL convention (Zero bit Last in state tuple) (00)ZL ( 0.461940 - 0.461940j) prob=0.426777 (10)ZL ( 0.191342 + 0.191342j) prob=0.073223 (01)ZL (-0.191342 + 0.191342j) prob=0.073223 (11)ZL ( 0.461940 + 0.461940j) prob=0.426777 total probability of state vector (=one if no measurements)= 1.000000 dictionary with key=qubit, value=(Prob(0), Prob(1)) {0: (0.5, 0.5), 1: (0.5, 0.5)} Prob(bit0=j, bit1=k) for j,k=0,1: [[0.4267767 0.0732233] [0.0732233 0.4267767]] mean= 0.7071067811865472
# sigx(0)sigw(1) measurement
file_prefix = 'bell_xw_meas'
mean_xw = write_bell_plus(file_prefix, bell_only=False, extra_had=True, t_herm=False)
------------------- bell_xw_meas
1 | | H | 2 | X---@ | 3 | | H | 4 | Rz | | 5 | H | | 6 | Rz | | 7 | H | |
*********branch= pure state vector: ZL convention (Zero bit Last in state tuple) (00)ZL ( 0.461940 - 0.461940j) prob=0.426777 (10)ZL ( 0.191342 + 0.191342j) prob=0.073223 (01)ZL ( 0.191342 - 0.191342j) prob=0.073223 (11)ZL (-0.461940 - 0.461940j) prob=0.426777 total probability of state vector (=one if no measurements)= 1.000000 dictionary with key=qubit, value=(Prob(0), Prob(1)) {0: (0.5, 0.5), 1: (0.5, 0.5)} Prob(bit0=j, bit1=k) for j,k=0,1: [[0.4267767 0.0732233] [0.0732233 0.4267767]] mean= 0.7071067811865469
# sigx(0)sigv(1) measurement
file_prefix = 'bell_xv_meas'
mean_xv = write_bell_plus(file_prefix, bell_only=False, extra_had=True, t_herm=True)
------------------- bell_xv_meas
1 | | H | 2 | X---@ | 3 | | H | 4 | Rz | | 5 | H | | 6 | Rz | | 7 | H | |
*********branch= pure state vector: ZL convention (Zero bit Last in state tuple) (00)ZL ( 0.191342 - 0.191342j) prob=0.073223 (10)ZL ( 0.461940 + 0.461940j) prob=0.426777 (01)ZL ( 0.461940 - 0.461940j) prob=0.426777 (11)ZL (-0.191342 - 0.191342j) prob=0.073223 total probability of state vector (=one if no measurements)= 1.000000 dictionary with key=qubit, value=(Prob(0), Prob(1)) {0: (0.5, 0.5), 1: (0.5, 0.5)} Prob(bit0=j, bit1=k) for j,k=0,1: [[0.0732233 0.4267767] [0.4267767 0.0732233]] mean= -0.7071067811865468
Let
$mean\_ab = \bra{\psi} \sigma_A(0) \sigma_B(1)\ket{\psi}$
where
$\ket{\psi} = \frac{1}{\sqrt{2}}(\ket{00}+ \ket{11})$
Ckeck that
$C = mean\_zw + mean\_zv + mean\_xw - mean\_xv = 2\sqrt{2}$
The classical analogue of $C$ satisfies $|C| \leq 2$
print('-----------------------')
print('mean_zw + mean_zv + mean_xw - mean_xv - 2*np.sqrt(2)=',
mean_zw + mean_zv + mean_xw - mean_xv - 2*np.sqrt(2))
----------------------- mean_zw + mean_zv + mean_xw - mean_xv - 2*np.sqrt(2)= -2.220446049250313e-15