-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMagic_State_V.py
More file actions
172 lines (143 loc) · 4.94 KB
/
Magic_State_V.py
File metadata and controls
172 lines (143 loc) · 4.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
"""
Code to understand magic states by visualizing single qubit transformations
around the bloch sphere explicitly with the random circuits of the
Hadamard and T gate.
Code Written by Andrew Projansky
Project Start Date: 7/21/2022
"""
import numpy as np
import matplotlib.pyplot as plt
import random
"""
Defines all universal gates used
"""
H = np.array([[1 / np.sqrt(2), 1 / np.sqrt(2)], [1 / np.sqrt(2), -1 / np.sqrt(2)]])
S = np.array([[1, 0], [0, 1j]])
T = np.array([[np.exp(-1j * np.pi / 8), 0], [0, np.exp(1j * np.pi / 8)]])
Z = np.array([[1, 0], [0, -1]])
CNot = np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]])
I = np.array([[1, 0], [0, 1]])
"""
Defines all useful states and matrix products for simulation
"""
magic_state = np.array([1/np.sqrt(2),1/np.sqrt(2)*np.exp(1j*np.pi/4)])
rho_m = np.tensordot(magic_state, np.conjugate(magic_state),0)
sig_z_p = np.kron(I, Z)
gate_dict = {1: T, 2: H}
proj = {0: np.array([[1,0],[0,0]]), 1: np.array([[0,0],[0,1]])}
def cc(gate):
"""
Returns complex conjugate of gate
Parameters
----------
gate: list
Unitary gate that can be applied on a state
"""
return np.transpose(np.matrix.conjugate(gate))
def partial_conditional_measure(rho_t):
"""
Performs partial trace over the ancilla system, then applies
conditional S gate to the leftover state.
Parameters
----------
rho_t: list
Hermetian total density matrix to which operations and
traces will be applied to
"""
#one day maybe make projection generators rather than just having a list
bc = get_bias(rho_t)
fullp = np.kron(I, proj[bc])
rhopr = np.matmul(rho_t, fullp)
rho_p = np.matmul(fullp, rhopr)/(np.trace(rhopr))
rho_red = partial_trace(rho_p)
if bc == 1:
rho_red = np.matmul(S, np.matmul(rho_red, cc(S)))
return rho_red
def get_bias(rho):
"""
For a multi_qubit density matrix in which we wish to measure over the
final qubit, we find the weighted distribution of whether to choose the
0 or 1 state projector, and biased coin flip to pick one or the other
Parameters
----------
rho: list
Hermetian density matrix which we take an expectation value over
identiy tensor sigma z
"""
e_v = np.real(np.trace( np.matmul(rho, sig_z_p) ))
ran = random.random()
return 0 if ran > (0.5+e_v/2) else 1
def partial_trace(rho):
"""
partial traces over final qubit
#should generalize this to any size matrix when I have time
- involves defining new matrix as being generated by taking
2x2 traces in tons of section - just have to automate it
Parameters
----------
rho: density matrix we're going to take the partial trace over
"""
new_matrix = np.array([[rho[0][0]+rho[1][1],rho[0][2]+rho[1][3]],
[rho[2][0]+rho[3][1],rho[2][2]+rho[3][3]]])
return new_matrix
class Experiment:
"""
Parameters
----------
num_steps : int, optional
total number of steps. The default is 1.
magic: bool, optional
Boolean value to determine whether to use magic state or not. The
default is False
state: list, optional
Initial state position. The default is the plus Z eigenstate
Attributes
----------
angles : list
angles from states Used for plotting
"""
def __init__(
self,
num_steps: int = 1,
magic: bool = False,
state: list = np.array([[1,0],[0,0]])
):
self.num_steps = num_steps
self.magic = magic
self.state = state
self.angles = [[],[],[]]
################# run functions ##################################
def run_stepwise(self):
for i in range(self.num_steps):
gate_choice = random.randint(1,2)
gate = gate_dict[gate_choice]
if (gate_choice == 1) and (self.magic):
self.magic_step()
else:
self.state = np.matmul(gate, np.matmul(self.state, cc(gate)))
self.__gen_xyz_points()
def magic_step(self):
rho_t = np.kron(self.state, rho_m)
rho_t = np.matmul(CNot, np.matmul(rho_t, cc(CNot)))
self.state = partial_conditional_measure(rho_t)
##############plot functions ########################
def plot(self):
fig = plt.figure()
ax = fig.add_subplot(projection="3d")
u, v = np.mgrid[0 : 2 * np.pi : 50j, 0 : np.pi : 50j]
xs = np.cos(u) * np.sin(v)
ys = np.sin(u) * np.sin(v)
zs = np.cos(v)
ax.plot_surface(xs, ys, zs, color="lightgrey", alpha=0.3)
ax.scatter(self.angles[0], self.angles[1], self.angles[2], marker="o", alpha=0.9)
plt.show()
def __gen_xyz_points(self):
self.angles[0].append(2*np.real(self.state[1][0]))
self.angles[1].append(2*np.imag(self.state[1][0]))
self.angles[2].append(2*self.state[0][0]-1)
#%%
exp = Experiment(num_steps=100, magic=True)
exp.run_stepwise()
exp.plot()
del exp
#%%