Skip to content

Commit 038c966

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 af3020a commit 038c966

1 file changed

Lines changed: 324 additions & 0 deletions

File tree

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
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 LinearRepeaterNetwork:
208+
"""
209+
LinearRepeaternetwork creates a linear topology where boundary nodes, Alice
210+
and 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+
218+
1.1. memory qubits are prepared in encoded states |+> and |0> at
219+
neighbouring stations.
220+
1.2. a physical Bell pair is generated and shared for each physical qubit
221+
in the code block
222+
1.3. the prepared memory qubits are teleported through remote CNOT gates to
223+
create an encoded |00> + |11>
224+
225+
2. Encoded Connection. A transverse Bell basis measurement is performed at
226+
each repeater node. The measurement result is sent to the appropriate
227+
neighbours.
228+
229+
3. Pauli Frame Correction. A local gate is applied transversely to account
230+
for the measurement outcome and create an encoded Φ+ between non-
231+
neighbouring peers.
232+
"""
233+
network: Network
234+
hosts: list[Host]
235+
236+
def __init__(self, repeaters, delay=0.1, x_error_rate=0.3):
237+
self.network = Network.get_instance()
238+
peers = ["Alice"] + [f"Polly_{k}" for k in range(repeaters)] + ["Bob"]
239+
self.network.delay = delay
240+
self.network.x_error_rate = x_error_rate
241+
242+
hosts = list(map(lambda x: Host(x), peers))
243+
self.hosts = hosts
244+
245+
for k in range(len(peers)-1):
246+
hosts[k].add_connection(hosts[k+1].host_id)
247+
hosts[k+1].add_connection(hosts[k].host_id)
248+
249+
for host in hosts:
250+
self.network.add_host(host)
251+
host.start()
252+
253+
self.network.start(nodes=peers)
254+
255+
def __len__(self):
256+
return len(self.hosts)
257+
258+
def __getitem__(self, i):
259+
return self.hosts[i]
260+
261+
def __enter__(self):
262+
return self
263+
264+
def __exit__(self, *args):
265+
self.network.stop()
266+
267+
@property
268+
def num_repeaters(self):
269+
return len(self.hosts) - 2
270+
271+
def run_with_repetition_code(self, logical_qubits: int = 3,
272+
code_length: int = 3):
273+
print(f"Establishing {logical_qubits} logical Φ^+"
274+
f" with code length {code_length}"
275+
f" using {self.num_repeaters} intermediate repeater nodes")
276+
277+
egs = [EncodedGenerationProtocol(self[i], self[i+1],
278+
code_length=code_length)
279+
for i in range(len(self)-1)]
280+
281+
# Repeat the protocol to assemble encoded entangled qubits.
282+
for n in range(logical_qubits):
283+
# 1. Encoded Generation. Generate one logical Φ^+ between
284+
# neighbours.
285+
ts = []
286+
for eg in egs:
287+
ts.append(eg.left.run_protocol(eg.left_protocol, (n,)))
288+
ts.append(eg.right.run_protocol(eg.right_protocol, (n,)))
289+
for t in ts:
290+
t.join()
291+
292+
# Perform the swap synchronously from the left-most repeater. This
293+
# creates a sequence like:
294+
# Alice <=> Polly_0 <=> Polly_1 <=> Bob
295+
# Alice <=============> Polly_1 <=> Bob
296+
# Alice <=========================> Bob
297+
for k in range(self.num_repeaters):
298+
a, p, b = self[0], self[k+1], self[k+2]
299+
300+
# 2. Encoded Connection
301+
t1 = p.run_protocol(encoded_connection, (a, b, n, code_length))
302+
303+
# 3. Establish Pauli Frame
304+
t2 = a.run_protocol(pauli_frame_left, (p, n, code_length))
305+
t3 = b.run_protocol(pauli_frame_right, (p, n, code_length))
306+
307+
t1.join()
308+
t2.join()
309+
t3.join()
310+
311+
312+
def main():
313+
logical_qubits = 5
314+
code_length = 3
315+
repeater_nodes = 2
316+
317+
with LinearRepeaterNetwork(repeater_nodes) as network:
318+
network.run_with_repetition_code(logical_qubits=logical_qubits,
319+
code_length=code_length)
320+
check_correlations(network.hosts, logical_qubits, code_length)
321+
322+
323+
if __name__ == "__main__":
324+
main()

0 commit comments

Comments
 (0)