Skip to content

Commit 145ef74

Browse files
3 things were fixed:
- The teleport example now no longer uses run_application which makes no sense when running on multiple machines - The NetQASM server was fixed to allow only one application connection per node per time. - We cleanup once an app disconnects. The above change made this cleanup quite simple, as the NetQASM addition to simulaqron was not designed to support multiple apps per node. This is a bug fix which allows the simulaqron backend (virtual node, netqasm node) to keep running while the user applications can be executed multiple times.
1 parent 6692ed8 commit 145ef74

File tree

11 files changed

+191
-145
lines changed

11 files changed

+191
-145
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
Distributed version of the teleport example, using simple NetQASM code both for Alice and Bob.
3+
4+
How to run this example:
5+
6+
1. First of all, make sure you are not already running existing simulaqron programs. You can run
7+
8+
simulaqron stop
9+
sh terminate.sh
10+
11+
which should get rid of all things running for the teleport example itself. If you want to be a bit
12+
more radical and confident you are not running other things to preserve you can run:
13+
14+
simulaqron stop
15+
pkill -9 python
16+
17+
This will kill ALL python processes run by you so beware.
18+
19+
If you have a debugging enabled, you may also wish to wipe old log files by running
20+
21+
rm /tmp/simulaqron*
22+
23+
24+
Now you should have a clear slate!
25+
26+
2. Now you can run this example:
27+
28+
First, we want to start the simulaqron virtual node backend and the NetQASM frontend your apps will their NetQASM subroutines to.
29+
30+
31+
** Single Machine
32+
If you are running everything on the same machine, first we start the simulaqron backend by typing
33+
34+
simulaqron start --node Alice,Bob
35+
36+
This needs to be done only once. Now type
37+
38+
sh run.sh
39+
40+
There is no need to restart the simulaqron backend again if you want to re-run your example.
41+
42+
** Multiple machines
43+
44+
If you are starting on two different machines run:
45+
46+
simulaqron start --node Alice
47+
simulaqron start --node Bob
48+
49+
on the machines you will use as Alice and Bob respectively. Again this needs to be run only once.
50+
51+
Now you can run:
52+
on Bob:
53+
python teleport-bob.py
54+
55+
on Alice:
56+
python teleport-alice.py
57+
58+
The code assumes you start Bob before starting Alice. Using your knowledge of network programming
59+
from our ping pong example - do you have an idea to make this more robust?
60+
61+
62+
63+

examples/distributed/teleport/network-alice.json

Lines changed: 0 additions & 40 deletions
This file was deleted.

examples/distributed/teleport/network-bob.json

Lines changed: 0 additions & 40 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/sh
2+
3+
# Check if SimulaQron is already running
4+
if [ ! -f ~/.simulaqron_pids/simulaqron_network_default.pid ]; then
5+
simulaqron start --nodes=Alice,Bob --network-config-file ./networkConfig.json
6+
fi
7+
8+
python3 teleport-bob.py &
9+
python3 teleport-alice.py
10+
11+
12+
13+
14+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"name": "default",
4+
"nodes": [
5+
{
6+
"Alice": {
7+
"app_socket": ["localhost", 8821],
8+
"qnodeos_socket": ["localhost", 8822],
9+
"vnode_socket": ["localhost", 8823]
10+
}
11+
},
12+
{
13+
"Bob": {
14+
"app_socket": ["localhost", 8831],
15+
"qnodeos_socket": ["localhost", 8832],
16+
"vnode_socket": ["localhost", 8833]
17+
}
18+
}
19+
],
20+
"topology": null
21+
}
22+
]
Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,29 @@
1-
from pathlib import Path
2-
31
from netqasm.runtime.settings import set_simulator
4-
from netqasm.runtime.application import default_app_instance
5-
6-
from simulaqron.run.run import run_applications
7-
82
set_simulator("simulaqron")
93

10-
from netqasm.sdk.external import NetQASMConnection # noqa: E402
11-
from netqasm.sdk import Qubit, EPRSocket # noqa: E402
4+
from simulaqron.settings import simulaqron_settings
5+
simulaqron_settings.network_config_file = "./networkConfig.json"
126

7+
from netqasm.sdk.external import NetQASMConnection
8+
from netqasm.sdk import Qubit, EPRSocket
139

1410
def run_alice():
15-
epr_socket: EPRSocket = EPRSocket("Bob")
11+
epr_socket = EPRSocket("Bob")
1612
with NetQASMConnection("Alice", epr_sockets=[epr_socket]) as alice:
1713
# Create a qubit
1814
q = Qubit(alice)
1915
q.H()
20-
2116
# Create entanglement
2217
epr = epr_socket.create_keep()[0]
23-
2418
# Teleport
2519
q.cnot(epr)
2620
q.H()
2721
m1 = q.measure()
2822
m2 = epr.measure()
29-
return m1, m2
23+
return int(m1), int(m2)
3024

3125

3226
if __name__ == "__main__":
33-
apps = default_app_instance(
34-
[
35-
("Alice", run_alice)
36-
]
37-
)
38-
network_cfg_path = Path(__file__).parent / "network-alice.json"
39-
raw_results = run_applications(
40-
apps, use_app_config=False, enable_logging=False, network_cfg=network_cfg_path.resolve()
41-
)
42-
43-
results = {}
44-
45-
for name, raw_result in raw_results[0].items():
46-
if isinstance(raw_result, tuple):
47-
results[name] = tuple(int(result) for result in raw_result)
48-
else:
49-
results[name] = int(raw_result)
27+
results = run_alice()
28+
print(f"Alice measurements: m1={results[0]}, m2={results[1]}")
5029

51-
print(results)
Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,22 @@
1-
from pathlib import Path
2-
31
from netqasm.runtime.settings import set_simulator
2+
set_simulator("simulaqron")
43

5-
from netqasm.runtime.application import default_app_instance
6-
7-
from simulaqron.run.run import run_applications
4+
from simulaqron.settings import simulaqron_settings
5+
simulaqron_settings.network_config_file = "./networkConfig.json"
86

9-
set_simulator("simulaqron")
10-
from netqasm.sdk.external import NetQASMConnection # noqa: E402
11-
from netqasm.sdk import EPRSocket # noqa: E402
7+
from netqasm.sdk.external import NetQASMConnection
8+
from netqasm.sdk import EPRSocket
129

1310

1411
def run_bob():
15-
epr_socket: EPRSocket = EPRSocket("Alice")
12+
epr_socket = EPRSocket("Alice")
1613
with NetQASMConnection("Bob", epr_sockets=[epr_socket]):
1714
entangled_qubit = epr_socket.recv_keep()[0]
1815
meas = entangled_qubit.measure()
19-
return meas
16+
return int(meas)
2017

2118

2219
if __name__ == "__main__":
23-
apps = default_app_instance(
24-
[
25-
("Bob", run_bob)
26-
]
27-
)
28-
network_cfg_path = Path(__file__).parent / "network-alice.json"
29-
raw_results = run_applications(
30-
apps, use_app_config=False, enable_logging=False, network_cfg=network_cfg_path.resolve()
31-
)
32-
33-
results = {}
34-
35-
for name, raw_result in raw_results[0].items():
36-
if isinstance(raw_result, tuple):
37-
results[name] = tuple(int(result) for result in raw_result)
38-
else:
39-
results[name] = int(raw_result)
20+
result = run_bob()
21+
print(f"Bob measurement: {result}")
4022

41-
print(results)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env sh
2+
TEST_PIDS=$(ps aux | grep python | grep -E "teleport" | awk {'print $2'})
3+
if [ "$TEST_PIDS" != "" ]
4+
then
5+
kill -9 $TEST_PIDS
6+
fi
7+
8+
simulaqron stop
9+
10+
# Check if SimulaQron is running
11+
if [ -f ~/.simulaqron_pids/simulaqron_network_default.pid ]; then
12+
cat $HOME/.simulaqron_pids/simulaqron_network_default.pid | xargs kill -9
13+
fi
14+

simulaqron/netqasm_backend/factory.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,51 @@ def __init__(self, factory: "NetQASMFactory"):
8585
self._logger.debug("Initialized Protocol")
8686

8787
def connectionMade(self):
88+
self._logger.info("Connection made")
8889
pass
8990

9091
def connectionLost(self, reason=connectionDone):
91-
pass
92+
self._logger.info(f"Connection lost: {reason}")
93+
self.factory._active_protocol = None
94+
self._cleanup_all()
95+
96+
def _cleanup_all(self):
97+
"""Clean up all state - Note that we allow only ONE connection
98+
to one NetQASM node server at a time, making the below safe.
99+
100+
If we were to ever decide to allow multiple, the below is too radical
101+
and decidedly unsafe.
102+
"""
103+
self._logger.info("Cleaning up all state")
104+
105+
# Clear qubit list
106+
self.factory.qubitList.clear()
107+
108+
# Clear protocol class state
109+
NetQASMProtocol._next_q_id.clear()
110+
NetQASMProtocol._next_ent_id.clear()
111+
112+
# Clear executioner class state
113+
from simulaqron.netqasm_backend.executioner import VanillaSimulaQronExecutioner
114+
VanillaSimulaQronExecutioner._next_create_id.clear()
115+
116+
# Clear executioner instance state
117+
executor = self.messageHandler._executor
118+
if hasattr(executor, '_network_stack'):
119+
executor._network_stack._sockets.clear()
120+
if hasattr(executor, '_epr_create_requests'):
121+
executor._epr_create_requests.clear()
122+
if hasattr(executor, '_epr_recv_requests'):
123+
executor._epr_recv_requests.clear()
124+
125+
# Reset shared memory
126+
try:
127+
from netqasm.sdk.shared_memory import SharedMemoryManager
128+
SharedMemoryManager.reset_memories()
129+
except Exception as e:
130+
self._logger.debug(f"Could not reset shared memory: {e}")
131+
132+
self._logger.info("Cleanup complete")
92133

93134
def dataReceived(self, data):
94135
"""
@@ -201,6 +242,12 @@ def __init__(
201242
# topology
202243
self.topology = network_config[network_name].topology
203244

245+
# Track active connection - we will allow only one at a time
246+
# as the code for the netqasm backend was not designed for multiple
247+
# even though the virtual Node backend of SimulaQron would allow
248+
# that
249+
self._active_protocol = None
250+
204251
def stop(self):
205252
yield call_method(self.virtRoot, "stop_vnode")
206253
reactor.stop()
@@ -209,7 +256,9 @@ def buildProtocol(self, addr):
209256
"""
210257
Return an instance of NetQASMProtocol when a connection is made.
211258
"""
212-
return NetQASMProtocol(self)
259+
protocol = NetQASMProtocol(self)
260+
self._active_protocol = protocol
261+
return protocol
213262

214263
def set_virtual_node(self, virtRoot):
215264
"""

0 commit comments

Comments
 (0)