Skip to content

Commit a73ca82

Browse files
committed
examples: add a second generation repeater
The quantum repeater from https://arxiv.org/pdf/0809.3629.pdf with repetition code is modelled. fixes: #91
1 parent 4c76943 commit a73ca82

1 file changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
from dataclasses import dataclass
2+
from statistics import mode
3+
4+
from qunetsim.components import Host
5+
from qunetsim.objects import Logger, Qubit
6+
from qunetsim.components import Network
7+
8+
Logger.DISABLED = True
9+
10+
11+
@dataclass()
12+
class Ebit:
13+
val: tuple[int, int]
14+
15+
def __str__(self):
16+
return {
17+
(0, 0): "phi+",
18+
(0, 1): "psi+",
19+
(1, 0): "phi-",
20+
(1, 1): "psi-",
21+
}[self.val]
22+
23+
@staticmethod
24+
def from_bell_measurement(a: Qubit, b: Qubit):
25+
a.cnot(b)
26+
a.H()
27+
x = a.measure()
28+
y = b.measure()
29+
return Ebit((x, y))
30+
31+
32+
def send_epr(host, peer):
33+
a, b = Qubit(host), Qubit(host)
34+
a.H()
35+
a.cnot(b)
36+
host.send_qubit(peer.host_id, b)
37+
return a
38+
39+
40+
@dataclass(init=False)
41+
class RepetitionCodedQubit:
42+
physical: list[Qubit]
43+
44+
def __init__(self, h: Host):
45+
self.physical = [Qubit(h), Qubit(h), Qubit(h)]
46+
47+
def __getitem__(self, index):
48+
return self.physical[index]
49+
50+
def H(self): # this maps ⌈|0>⌋ to ⌈|+>⌋
51+
self.physical[0].H()
52+
self.physical[0].cnot(self.physical[1])
53+
self.physical[0].cnot(self.physical[2])
54+
55+
56+
# This circuit is from https://arxiv.org/pdf/quant-ph/0002039.pdf, page 9. It
57+
# lets two peers perform a remote CNOT using a single EPR pair.
58+
@dataclass()
59+
class RemoteCNOT:
60+
"""
61+
RemoteCNOT teleports qubits, |alpha> and |beta>, through an EPR pair
62+
composed of |red> and |blue>. The result of the protocol is that
63+
|red> = |alpha>
64+
|blue> = |beta ⊕ alpha>
65+
"""
66+
left: Host
67+
right: Host
68+
69+
def left_protocol(self, alpha: Qubit, red: Qubit):
70+
red.cnot(alpha)
71+
x = alpha.measure()
72+
if x == 1:
73+
red.X()
74+
self.left.send_classical(self.right.host_id, str(x), await_ack=True)
75+
76+
z = self.left.get_next_classical(self.right.host_id, wait=-1).content
77+
if z == '1':
78+
red.Z()
79+
80+
def right_protocol(self, blue: Qubit, beta: Qubit):
81+
beta.cnot(blue)
82+
beta.H()
83+
84+
x = self.right.get_next_classical(self.left.host_id, wait=-1).content
85+
if x == '1':
86+
blue.X()
87+
88+
z = beta.measure()
89+
self.right.send_classical(self.left.host_id, str(z), await_ack=True)
90+
if z == 1:
91+
blue.Z()
92+
93+
94+
@dataclass()
95+
class EncodedGenerationProtocol:
96+
"""
97+
EncodedGenerationProtocol establishes an encoded Φ^+ between left and right
98+
hosts. This is done as follows.
99+
100+
1. locally prepare encoded states ⌈|+>⌋ and ⌈|0>⌋
101+
102+
For each each physical qubit:
103+
104+
2. left creates an EPR pair
105+
3. left distributes half of the EPR pair to right
106+
4. peers use the EPR pair to perform a transverse teleportation-based CNOT
107+
"""
108+
left: Host
109+
right: Host
110+
remote_cnot: RemoteCNOT
111+
112+
def __init__(self, left, right):
113+
self.left = left
114+
self.right = right
115+
self.remote_cnot = RemoteCNOT(left, right)
116+
117+
def left_protocol(self, left: Host, n: int):
118+
logical = RepetitionCodedQubit(self.left)
119+
logical.H()
120+
121+
for k, physical in enumerate(logical):
122+
epr = send_epr(self.left, self.right)
123+
self.remote_cnot.left_protocol(physical, epr)
124+
self.left.add_qubit(self.left.host_id, epr, f"{n}>{k}")
125+
126+
def right_protocol(self, right: Host, n: int):
127+
# prepare an encoded |0>
128+
logical = RepetitionCodedQubit(right)
129+
130+
for k, physical in enumerate(logical):
131+
epr = self.right.get_qubit(self.left.host_id, wait=-1)
132+
self.remote_cnot.right_protocol(epr, physical)
133+
right.add_qubit(right.host_id, epr, f"{n}<{k}")
134+
135+
def get_logical_qubit(self, host, n):
136+
return (host.get_qubit_by_id(f"{n}>{k}") for k in range(3))
137+
138+
139+
def encoded_connection(host: Host, left: Host, right: Host, n: int):
140+
ms = []
141+
for k in range(3):
142+
p = host.get_qubit_by_id(f"{n}>{k}")
143+
q = host.get_qubit_by_id(f"{n}<{k}")
144+
ms.append(str(Ebit.from_bell_measurement(p, q)))
145+
host.send_classical(left.host_id, mode(ms), await_ack=True)
146+
host.send_classical(right.host_id, mode(ms), await_ack=True)
147+
148+
149+
def pauli_frame_left(host: Host, right: Host, n: int):
150+
msg = host.get_next_classical(right.host_id, wait=-1).content
151+
for k in range(3):
152+
q = host.get_qubit_by_id(f"{n}>{k}")
153+
if msg == 'psi+':
154+
q.X()
155+
elif msg == 'phi-':
156+
q.Z()
157+
158+
159+
def pauli_frame_right(host: Host, left: Host, n: int):
160+
msg = host.get_next_classical(left.host_id, wait=-1).content
161+
for k in range(3):
162+
q = host.get_qubit_by_id(f"{n}<{k}")
163+
if msg == 'psi-':
164+
q.Y()
165+
166+
167+
def check_correlations(hosts, logical_qubits):
168+
print("Alice: ", end='')
169+
for n in range(logical_qubits):
170+
for k in range(3):
171+
p = hosts[0].get_qubit_by_id(f"{n}>{k}")
172+
print(f"{p.measure()}", end='')
173+
print()
174+
print("Bob: ", end='')
175+
for n in range(logical_qubits):
176+
for k in range(3):
177+
q = hosts[-1].get_qubit_by_id(f"{n}<{k}")
178+
print(f"{q.measure()}", end='')
179+
print()
180+
181+
182+
def new_network(repeaters=2):
183+
network = Network.get_instance()
184+
peers = ["Alice"] + [f"Polly{k}" for k in range(repeaters)] + ["Bob"]
185+
network.delay = 0.1
186+
network.x_error_rate = 0.3
187+
188+
hosts = list(map(lambda x: Host(x), peers))
189+
190+
for k in range(len(peers)-1):
191+
hosts[k].add_connection(hosts[k+1].host_id)
192+
hosts[k+1].add_connection(hosts[k].host_id)
193+
194+
for host in hosts:
195+
network.add_host(host)
196+
host.start()
197+
198+
network.start(peers)
199+
return network, hosts
200+
201+
202+
def main():
203+
logical_qubits = 5
204+
repeater_nodes = 2
205+
206+
print(f"Establishing {logical_qubits} logical Φ^+"
207+
f" using {repeater_nodes} intermediate repeater nodes")
208+
209+
network, hosts = new_network(repeater_nodes)
210+
egs = [EncodedGenerationProtocol(hosts[i], hosts[i+1])
211+
for i in range(len(hosts)-1)]
212+
213+
for n in range(logical_qubits):
214+
# 1. Encoded Generation. Generate one logical Φ^+ between neighbours.
215+
ts = []
216+
for eg in egs:
217+
ts.append(eg.left.run_protocol(eg.left_protocol, (n,)))
218+
ts.append(eg.right.run_protocol(eg.right_protocol, (n,)))
219+
for t in ts:
220+
t.join()
221+
222+
# Perform the swap synchronously from the left-most repeater. This
223+
# creates a sequence like:
224+
# Alice <-> Polly0 <-> Polly1 <-> Bob
225+
# Alice <------------> Polly1 <-> Bob
226+
# Alice <-----------------------> Bob
227+
for k in range(len(hosts)-2):
228+
a, p, b = hosts[0], hosts[k+1], hosts[k+2]
229+
230+
# 2. Encoded Connection
231+
t1 = p.run_protocol(encoded_connection, (a, b, n,))
232+
233+
# 3. Establish Pauli Frame
234+
t2 = a.run_protocol(pauli_frame_left, (p, n))
235+
t3 = b.run_protocol(pauli_frame_right, (p, n))
236+
t1.join()
237+
t2.join()
238+
t3.join()
239+
240+
# Check that the boundary is correlated!
241+
check_correlations(hosts, logical_qubits)
242+
network.stop()
243+
244+
245+
if __name__ == "__main__":
246+
main()

0 commit comments

Comments
 (0)