Skip to content

Commit f628589

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: tqsd#91
1 parent af3020a commit f628589

File tree

1 file changed

+323
-0
lines changed

1 file changed

+323
-0
lines changed
Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
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+
code_length: int
44+
45+
def __init__(self, h: Host, code_length: int = 3):
46+
self.code_length = code_length
47+
self.physical = [Qubit(h) for _ in range(code_length)]
48+
49+
def __getitem__(self, index):
50+
return self.physical[index]
51+
52+
def H(self): # this maps ⌈|0>⌋ to ⌈|+>⌋
53+
self.physical[0].H()
54+
for k in range(1, self.code_length):
55+
self.physical[0].cnot(self.physical[k])
56+
57+
58+
# This circuit is from https://arxiv.org/pdf/quant-ph/0002039.pdf, page 9. It
59+
# lets two peers perform a remote CNOT using a single EPR pair.
60+
@dataclass()
61+
class RemoteCNOT:
62+
"""
63+
RemoteCNOT teleports qubits, |alpha> and |beta>, through an EPR pair
64+
composed of |red> and |blue>. The result of the protocol is that
65+
|red> = |alpha>
66+
|blue> = |beta ⊕ alpha>
67+
"""
68+
left: Host
69+
right: Host
70+
71+
def left_protocol(self, alpha: Qubit, red: Qubit):
72+
red.cnot(alpha)
73+
x = alpha.measure()
74+
if x == 1:
75+
red.X()
76+
self.left.send_classical(self.right.host_id, str(x), await_ack=True)
77+
78+
z = self.left.get_next_classical(self.right.host_id, wait=-1).content
79+
if z == '1':
80+
red.Z()
81+
82+
def right_protocol(self, blue: Qubit, beta: Qubit):
83+
beta.cnot(blue)
84+
beta.H()
85+
86+
x = self.right.get_next_classical(self.left.host_id, wait=-1).content
87+
if x == '1':
88+
blue.X()
89+
90+
z = beta.measure()
91+
self.right.send_classical(self.left.host_id, str(z), await_ack=True)
92+
if z == 1:
93+
blue.Z()
94+
95+
96+
@dataclass(init=False)
97+
class EncodedGenerationProtocol:
98+
"""
99+
EncodedGenerationProtocol establishes an encoded Φ^+ between left and right
100+
hosts. This is done as follows.
101+
102+
1. locally prepare encoded states ⌈|+>⌋ and ⌈|0>⌋
103+
104+
For each each physical qubit:
105+
106+
2. left creates an EPR pair
107+
3. left distributes half of the EPR pair to right
108+
4. peers use the EPR pair to perform a transverse teleportation-based CNOT
109+
"""
110+
left: Host
111+
right: Host
112+
remote_cnot: RemoteCNOT
113+
code_length: int
114+
115+
def __init__(self, left, right, code_length=3):
116+
self.left = left
117+
self.right = right
118+
self.remote_cnot = RemoteCNOT(left, right)
119+
self.code_length = code_length
120+
121+
def left_protocol(self, left: Host, n: int):
122+
logical = RepetitionCodedQubit(self.left, self.code_length)
123+
logical.H()
124+
125+
for k, physical in enumerate(logical):
126+
epr = send_epr(self.left, self.right)
127+
self.remote_cnot.left_protocol(physical, epr)
128+
self.left.add_qubit(self.left.host_id, epr, f"{n}>{k}")
129+
130+
def right_protocol(self, right: Host, n: int):
131+
# prepare an encoded |0>
132+
logical = RepetitionCodedQubit(right, self.code_length)
133+
134+
out = []
135+
for k, physical in enumerate(logical):
136+
epr = self.right.get_qubit(self.left.host_id, wait=-1)
137+
self.remote_cnot.right_protocol(epr, physical)
138+
right.add_qubit(right.host_id, epr, f"{n}<{k}")
139+
out.append(epr)
140+
141+
142+
def encoded_connection(host: Host, left: Host, right: Host, logical_qubit: int,
143+
code_length: int = 3):
144+
"""
145+
Perform transverse measurements of the code block for logical_qubit in the
146+
Bell basis.
147+
148+
The logical measurement result is the mode of the physical measurement
149+
results.
150+
151+
The logical measurement result is sent to the repeater's neighbours, hosts
152+
`left` and `right`.
153+
"""
154+
ms = []
155+
for k in range(code_length):
156+
p = host.get_qubit_by_id(f"{logical_qubit}>{k}")
157+
q = host.get_qubit_by_id(f"{logical_qubit}<{k}")
158+
ms.append(str(Ebit.from_bell_measurement(p, q)))
159+
host.send_classical(left.host_id, mode(ms), await_ack=True)
160+
host.send_classical(right.host_id, mode(ms), await_ack=True)
161+
162+
163+
def pauli_frame_left(host: Host, right: Host, n: int, code_length: int = 3):
164+
msg = host.get_next_classical(right.host_id, wait=-1).content
165+
for k in range(code_length):
166+
q = host.get_qubit_by_id(f"{n}>{k}")
167+
if msg == 'psi+':
168+
q.X()
169+
elif msg == 'phi-':
170+
q.Z()
171+
172+
173+
def pauli_frame_right(host: Host, left: Host, logical_qubit: int,
174+
code_length: int = 3):
175+
msg = host.get_next_classical(left.host_id, wait=-1).content
176+
for k in range(code_length):
177+
q = host.get_qubit_by_id(f"{logical_qubit}<{k}")
178+
if msg == 'psi-':
179+
q.Y()
180+
181+
182+
def pretty_print_logical_qubit(h, n, code_length, left_side=True):
183+
if n > 0:
184+
print('|', end='')
185+
for k in range(code_length):
186+
if left_side:
187+
p = h.get_qubit_by_id(f"{n}>{k}")
188+
else:
189+
p = h.get_qubit_by_id(f"{n}<{k}")
190+
print(f"{p.measure()}", end='')
191+
192+
193+
def check_correlations(hosts: list[Host], logical_qubits: int,
194+
code_length: int = 3):
195+
print("Checking correlations")
196+
print("Alice: ", end='')
197+
for n in range(logical_qubits):
198+
pretty_print_logical_qubit(hosts[0], n, code_length)
199+
print()
200+
print("Bob: ", end='')
201+
for n in range(logical_qubits):
202+
pretty_print_logical_qubit(hosts[-1], n, code_length, left_side=False)
203+
print()
204+
205+
206+
@dataclass(init=False)
207+
class LinearRelayNetwork:
208+
"""
209+
LinearRelaynetwork creates a line topology where boundary nodes, Alice and
210+
Bob, are separated by repeaters-many repeater nodes.
211+
212+
The method `run_with_repetition_code` will establish an encoded Φ^+ between
213+
Alice and Bob in three steps.
214+
215+
1. Encoded generation. An encoded Φ^+ is established between neighbours in
216+
three steps:
217+
1.1. memory qubits are prepared in encoded states |+> and |0> at
218+
neighbouring stations.
219+
1.2. a physical Bell pair is generated and shared for each physical qubit
220+
in the code block
221+
1.3. the prepared memory qubits are teleported through remote CNOT gates to
222+
create an encoded |00> + |11>
223+
224+
2. Encoded Connection. A transverse Bell basis measurement is performed at
225+
each repeater node. The measurement result is sent to the appropriate
226+
neighbours.
227+
228+
3. Pauli Frame Correction. A local gate is applied transversely to account
229+
for the measurement outcome and create an encoded Φ+ between non-
230+
neighbouring peers.
231+
"""
232+
network: Network
233+
hosts: list[Host]
234+
235+
def __init__(self, repeaters, delay=0.1, x_error_rate=0.3):
236+
self.network = Network.get_instance()
237+
peers = ["Alice"] + [f"Polly_{k}" for k in range(repeaters)] + ["Bob"]
238+
self.network.delay = delay
239+
self.network.x_error_rate = x_error_rate
240+
241+
hosts = list(map(lambda x: Host(x), peers))
242+
self.hosts = hosts
243+
244+
for k in range(len(peers)-1):
245+
hosts[k].add_connection(hosts[k+1].host_id)
246+
hosts[k+1].add_connection(hosts[k].host_id)
247+
248+
for host in hosts:
249+
self.network.add_host(host)
250+
host.start()
251+
252+
self.network.start(nodes=peers)
253+
254+
def __len__(self):
255+
return len(self.hosts)
256+
257+
def __getitem__(self, i):
258+
return self.hosts[i]
259+
260+
def __enter__(self):
261+
return self
262+
263+
def __exit__(self, *args):
264+
self.network.stop()
265+
266+
@property
267+
def num_repeaters(self):
268+
return len(self.hosts) - 2
269+
270+
def run_with_repetition_code(self, logical_qubits: int = 3,
271+
code_length: int = 3):
272+
print(f"Establishing {logical_qubits} logical Φ^+"
273+
f" with code length {code_length}"
274+
f" using {self.num_repeaters} intermediate repeater nodes")
275+
276+
egs = [EncodedGenerationProtocol(self[i], self[i+1],
277+
code_length=code_length)
278+
for i in range(len(self)-1)]
279+
280+
# Repeat the protocol to assemble encoded entangled qubits.
281+
for n in range(logical_qubits):
282+
# 1. Encoded Generation. Generate one logical Φ^+ between
283+
# neighbours.
284+
ts = []
285+
for eg in egs:
286+
ts.append(eg.left.run_protocol(eg.left_protocol, (n,)))
287+
ts.append(eg.right.run_protocol(eg.right_protocol, (n,)))
288+
for t in ts:
289+
t.join()
290+
291+
# Perform the swap synchronously from the left-most repeater. This
292+
# creates a sequence like:
293+
# Alice <-> Polly_0 <-> Polly_1 <-> Bob
294+
# Alice <-------------> Polly_1 <-> Bob
295+
# Alice <-------------------------> Bob
296+
for k in range(self.num_repeaters):
297+
a, p, b = self[0], self[k+1], self[k+2]
298+
299+
# 2. Encoded Connection
300+
t1 = p.run_protocol(encoded_connection, (a, b, n, code_length))
301+
302+
# 3. Establish Pauli Frame
303+
t2 = a.run_protocol(pauli_frame_left, (p, n, code_length))
304+
t3 = b.run_protocol(pauli_frame_right, (p, n, code_length))
305+
306+
t1.join()
307+
t2.join()
308+
t3.join()
309+
310+
311+
def main():
312+
logical_qubits = 5
313+
code_length = 3
314+
repeater_nodes = 2
315+
316+
with LinearRelayNetwork(repeater_nodes) as network:
317+
network.run_with_repetition_code(logical_qubits=logical_qubits,
318+
code_length=code_length)
319+
check_correlations(network.hosts, logical_qubits, code_length)
320+
321+
322+
if __name__ == "__main__":
323+
main()

0 commit comments

Comments
 (0)