Skip to content

Commit 6dbc606

Browse files
committed
added codes and text on RL
1 parent 112821f commit 6dbc606

File tree

3 files changed

+746
-0
lines changed

3 files changed

+746
-0
lines changed
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import numpy as np
2+
import random
3+
import matplotlib.pyplot as plt
4+
from collections import Counter
5+
6+
# =============================== #
7+
# Quantum Gate Classes #
8+
# =============================== #
9+
10+
class Gate:
11+
def __init__(self, matrix, targets):
12+
self.matrix = np.array(matrix, dtype=np.complex128)
13+
self.targets = targets
14+
15+
class OneQubitGate(Gate):
16+
def __init__(self, matrix, target):
17+
super().__init__(matrix, [target])
18+
19+
class TwoQubitGate(Gate):
20+
def __init__(self, matrix, control, target):
21+
super().__init__(matrix, [control, target])
22+
23+
# One-qubit standard gates
24+
def I(): return np.eye(2)
25+
def X(): return np.array([[0,1],[1,0]])
26+
def Y(): return np.array([[0,-1j],[1j,0]])
27+
def Z(): return np.array([[1,0],[0,-1]])
28+
def H(): return (1/np.sqrt(2))*np.array([[1,1],[1,-1]])
29+
def S(): return np.array([[1,0],[0,1j]])
30+
def T(): return np.array([[1,0],[0,np.exp(1j*np.pi/4)]])
31+
32+
def Rx(theta):
33+
return np.array([
34+
[np.cos(theta/2), -1j*np.sin(theta/2)],
35+
[-1j*np.sin(theta/2), np.cos(theta/2)]
36+
])
37+
38+
def Ry(theta):
39+
return np.array([
40+
[np.cos(theta/2), -np.sin(theta/2)],
41+
[np.sin(theta/2), np.cos(theta/2)]
42+
])
43+
44+
def Rz(theta):
45+
return np.array([
46+
[np.exp(-1j*theta/2), 0],
47+
[0, np.exp(1j*theta/2)]
48+
])
49+
50+
# Two-qubit gates
51+
def CNOT():
52+
return np.array([
53+
[1,0,0,0],
54+
[0,1,0,0],
55+
[0,0,0,1],
56+
[0,0,1,0]
57+
])
58+
59+
def CZ():
60+
return np.array([
61+
[1,0,0,0],
62+
[0,1,0,0],
63+
[0,0,1,0],
64+
[0,0,0,-1]
65+
])
66+
67+
def SWAP():
68+
return np.array([
69+
[1,0,0,0],
70+
[0,0,1,0],
71+
[0,1,0,0],
72+
[0,0,0,1]
73+
])
74+
75+
# =============================== #
76+
# Quantum Circuit Class #
77+
# =============================== #
78+
79+
class Circuit:
80+
def __init__(self, num_qubits):
81+
self.n = num_qubits
82+
self.reset()
83+
84+
def reset(self):
85+
self.state = np.zeros(2**self.n, dtype=np.complex128)
86+
self.state[0] = 1.0
87+
self.gates = []
88+
89+
def add_gate(self, gate):
90+
self.gates.append(gate)
91+
92+
def run(self):
93+
for gate in self.gates:
94+
self.apply_gate(gate)
95+
96+
def apply_gate(self, gate):
97+
full_U = self.expand_gate(gate)
98+
self.state = full_U @ self.state
99+
100+
def expand_gate(self, gate):
101+
n = self.n
102+
if len(gate.targets) == 1:
103+
# One-qubit gate
104+
target = gate.targets[0]
105+
ops = [I()]*n
106+
ops[target] = gate.matrix
107+
return self.tensor_product(ops)
108+
elif len(gate.targets) == 2:
109+
# Two-qubit gate
110+
t0, t1 = gate.targets
111+
ops = [I()]*n
112+
# Insert identity first, apply 4x4 gate manually:
113+
full = np.eye(1, dtype=np.complex128)
114+
for i in range(n):
115+
if i == t0:
116+
full = np.kron(full, np.eye(2))
117+
elif i == t1:
118+
full = np.kron(full, np.eye(2))
119+
else:
120+
full = np.kron(full, I())
121+
122+
# Reshape state space to insert 4x4 gate
123+
axes = list(range(n))
124+
axes.remove(t0)
125+
axes.remove(t1)
126+
axes = [t0, t1] + axes
127+
128+
perm = np.argsort(axes)
129+
U = gate.matrix
130+
131+
full_gate = np.tensordot(U, full.reshape([2]*2*n), axes=0)
132+
full_gate = np.moveaxis(full_gate, list(range(2*n)), perm*2 + perm*2)
133+
return full_gate.reshape(2**n, 2**n)
134+
else:
135+
raise ValueError("Only 1- and 2-qubit gates supported")
136+
137+
def tensor_product(self, matrices):
138+
result = matrices[0]
139+
for m in matrices[1:]:
140+
result = np.kron(result, m)
141+
return result
142+
143+
def get_statevector(self):
144+
return self.state
145+
146+
def get_probabilities(self):
147+
return np.abs(self.state)**2
148+
149+
def measure(self, shots=1024):
150+
probs = self.get_probabilities()
151+
basis_states = [format(i, f'0{self.n}b') for i in range(2**self.n)]
152+
samples = random.choices(basis_states, weights=probs, k=shots)
153+
return dict(Counter(samples))
154+
155+
def visualize_probabilities(self, title="State Probabilities"):
156+
probs = self.get_probabilities()
157+
basis = [format(i, f'0{self.n}b') for i in range(2**self.n)]
158+
plt.bar(basis, probs, color='teal')
159+
plt.xlabel("Basis States")
160+
plt.ylabel("Probability")
161+
plt.title(title)
162+
plt.show()
163+
164+
# =============================== #
165+
# Noise models #
166+
# =============================== #
167+
168+
def apply_bit_flip(state, p):
169+
noisy_state = state.copy()
170+
for i in range(len(state)):
171+
if random.random() < p:
172+
flipped = i ^ 1 # flip LSB (bit flip on qubit 0 for example)
173+
noisy_state[flipped] += state[i]
174+
noisy_state[i] = 0
175+
return noisy_state / np.linalg.norm(noisy_state)
176+
177+
def apply_depolarizing(state, p):
178+
d = len(state)
179+
noisy_state = (1 - p) * state + p / d * np.ones(d)
180+
return noisy_state / np.linalg.norm(noisy_state)
181+
182+
# =============================== #
183+
# Bell state generator #
184+
# =============================== #
185+
186+
def bell_state(label="Phi+"):
187+
c = Circuit(2)
188+
c.add_gate(OneQubitGate(H(), 0))
189+
c.add_gate(TwoQubitGate(CNOT(), 0, 1))
190+
if label == "Phi+":
191+
pass
192+
elif label == "Phi-":
193+
c.add_gate(OneQubitGate(Z(), 0))
194+
elif label == "Psi+":
195+
c.add_gate(OneQubitGate(X(), 1))
196+
elif label == "Psi-":
197+
c.add_gate(OneQubitGate(X(), 1))
198+
c.add_gate(OneQubitGate(Z(), 0))
199+
else:
200+
raise ValueError("Unknown Bell state")
201+
c.run()
202+
return c
203+
204+
# =============================== #
205+
# Demonstration #
206+
# =============================== #
207+
208+
if __name__ == "__main__":
209+
210+
labels = ["Phi+", "Phi-", "Psi+", "Psi-"]
211+
shots = 1000
212+
213+
for label in labels:
214+
print(f"\n{label} state:")
215+
c = bell_state(label)
216+
print("Statevector:", c.get_statevector())
217+
results = c.measure(shots=shots)
218+
print(f"Measurement (shots={shots}):", results)
219+
c.visualize_probabilities(title=f"{label} state probabilities")
220+
221+

0 commit comments

Comments
 (0)