Skip to content

Commit c92e704

Browse files
committed
Add first version of the "extended GHZ" example using the new SDK
1 parent 4190426 commit c92e704

9 files changed

Lines changed: 391 additions & 4 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import logging
2+
3+
from asyncio import StreamReader, StreamWriter
4+
from pathlib import Path
5+
6+
from simulaqron.general.host_config import SocketsConfig
7+
from simulaqron.sdk.protocol import SimulaQronClassicalClient
8+
from simulaqron.settings import network_config, simulaqron_settings
9+
10+
# This is recipe to use NetQASM with simulaqron backend.
11+
from netqasm.runtime.settings import set_simulator
12+
13+
from simulaqron.settings.network_config import NodeConfigType
14+
15+
set_simulator("simulaqron")
16+
17+
# Importing NetQASM connection, Qubit and EPR socket must be *after*
18+
# setting the simulator for NetQASM
19+
from netqasm.logging.glob import set_log_level # noqa: E402
20+
from netqasm.sdk.external import NetQASMConnection # noqa: E402
21+
from netqasm.sdk import EPRSocket # noqa: E402
22+
23+
24+
# This function contains the code of the classical client
25+
async def alice_program(reader: StreamReader, writer: StreamWriter) -> int:
26+
# This is "Alice": the start node of the GHZ chain
27+
this_node_name = "Alice"
28+
remote_node_name = "Bob" # A node with this name *must* exist in "simulaqron_network.json"
29+
30+
logging.debug("LOCAL %s: Running client side program.", this_node_name)
31+
32+
epr_socket = EPRSocket(remote_node_name)
33+
# To start executing quantum operations, we need to create a NetQASM connection
34+
with NetQASMConnection(this_node_name, epr_sockets=[epr_socket]):
35+
# Create an entangled qubit with Bob
36+
epr = epr_socket.create_keep()[0]
37+
38+
writer.write("receive_qubit".encode("utf-8"))
39+
answer = await reader.read(100)
40+
41+
assert answer.decode("utf-8") == "continue"
42+
43+
m1 = epr.measure()
44+
# Any value that comes from NetQASM *need* to be retrieved ("casted" to int)
45+
# *after* the connection is closed (or after flushing the connection, untested)
46+
m1_val = int(m1)
47+
return m1_val
48+
49+
50+
if __name__ == "__main__":
51+
logging.basicConfig(
52+
format="%(asctime)s:%(levelname)s:%(name)s:%(filename)s:%(lineno)d:%(message)s",
53+
level=logging.DEBUG,
54+
force=True
55+
)
56+
# We set the netqasm log level to "info" to avoid verbose output from the internals.
57+
set_log_level(logging.INFO)
58+
59+
# Load the simulaqron settings file
60+
simulaqron_config_file = Path("simulaqron_settings.json")
61+
simulaqron_settings.read_from_file(simulaqron_config_file)
62+
63+
# Load the file network configuration file
64+
# We still need this file to correctly interact with the SimulaQron backend (QNodeOS and Virtual Node)
65+
network_config_file = Path("simulaqron_network.json")
66+
network_config.read_from_file(network_config_file)
67+
68+
# Some data for this node:
69+
network_name = "default" # A network with this name *must* exist in "simulaqron_network.json"
70+
node_name = "Alice"
71+
other_node_name = "Bob" # A node with this name *must* exist in "simulaqron_network.json"
72+
73+
classical_sockets = SocketsConfig(network_config, network_name, NodeConfigType.APP)
74+
75+
# The general nodes interaction of this application is
76+
# Alice ----> Bob ----> Charlie ----> Bob ----> Alice
77+
# This means:
78+
# 1. Alice entangles a qubit with Bob, and waits until Bob has finished.
79+
# 2. Bob received the entangled qubit, creates a new local one and entangles it with the one received
80+
81+
# This node will act as a classical client, waiting for a classical answer from Bob
82+
# to measure the qubit
83+
client = SimulaQronClassicalClient(classical_sockets)
84+
85+
result = client.run_client(other_node_name, alice_program)
86+
#result = alice_program(1, 0)
87+
88+
print(f"{node_name}: My outcome is '{result}'")
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import logging
2+
3+
from asyncio import StreamReader, StreamWriter
4+
from pathlib import Path
5+
6+
from simulaqron.general.host_config import SocketsConfig
7+
from simulaqron.sdk.protocol import SimulaQronClassicalClient, SimulaQronClassicalServer
8+
from simulaqron.settings import network_config, simulaqron_settings
9+
10+
# This is recipe to use NetQASM with simulaqron backend.
11+
from netqasm.runtime.settings import set_simulator
12+
13+
from simulaqron.settings.network_config import NodeConfigType
14+
15+
set_simulator("simulaqron")
16+
17+
# Importing NetQASM connection, Qubit and EPR socket must be *after*
18+
# setting the simulator for NetQASM
19+
from netqasm.logging.glob import set_log_level # noqa: E402
20+
from netqasm.sdk.external import NetQASMConnection # noqa: E402
21+
from netqasm.sdk import EPRSocket # noqa: E402
22+
23+
24+
async def send_to_charlie(reader: StreamReader, writer: StreamWriter):
25+
writer.write("receive_qubit".encode("utf-8"))
26+
message = await reader.read(100)
27+
assert message.decode("utf-8") == "continue"
28+
29+
30+
# This function contains the code of the classical client
31+
async def bob_program(reader: StreamReader, writer: StreamWriter) -> int:
32+
# This is "Bob": the middle node of the GHZ chain
33+
this_node_name = "Bob"
34+
start_node_name = "Alice" # A node with this name *must* exist in "simulaqron_network.json"
35+
end_node_name = "Charlie" # A node with this name *must* exist in "simulaqron_network.json"
36+
37+
logging.debug("LOCAL %s: Running client side program.", this_node_name)
38+
39+
message = await reader.read(100)
40+
assert message.decode("utf-8") == "receive_qubit"
41+
42+
epr_socket_alice = EPRSocket(start_node_name)
43+
epr_socket_charlie = EPRSocket(end_node_name)
44+
45+
sockets = SocketsConfig(network_config, "default", NodeConfigType.APP)
46+
47+
charlie_client = SimulaQronClassicalClient(sockets)
48+
# To start executing quantum operations, we need to create a NetQASM connection
49+
with NetQASMConnection(this_node_name, epr_sockets=[epr_socket_alice, epr_socket_charlie]) as bob:
50+
# Receive an entangled qubit
51+
epr_alice = epr_socket_alice.recv_keep()[0]
52+
53+
# Create a new entangled with Charlie
54+
epr_charlie = epr_socket_charlie.create_keep()[0]
55+
56+
await charlie_client.connect_and_run(end_node_name, send_to_charlie)
57+
58+
# Create the GHZ state by entangling the fresh qubit
59+
epr_alice.cnot(epr_charlie)
60+
61+
writer.write("continue".encode("utf-8"))
62+
63+
# And simply measure it
64+
m1 = epr_charlie.measure()
65+
# Any value that comes from NetQASM *need* to be retrieved ("casted" to int)
66+
# *after* the connection is closed (or after flushing the connection, untested)
67+
m1_val = int(m1)
68+
print(f"{this_node_name}: My outcome is '{m1_val}'")
69+
return 0
70+
71+
72+
if __name__ == "__main__":
73+
logging.basicConfig(
74+
format="%(asctime)s:%(levelname)s:%(name)s:%(filename)s:%(lineno)d:%(message)s",
75+
level=logging.DEBUG,
76+
force=True
77+
)
78+
# We set the netqasm log level to "info" to avoid verbose output from the internals.
79+
set_log_level(logging.INFO)
80+
81+
# Load the simulaqron settings file
82+
simulaqron_config_file = Path("simulaqron_settings.json")
83+
simulaqron_settings.read_from_file(simulaqron_config_file)
84+
85+
# Load the file network configuration file
86+
# We still need this file to correctly interact with the SimulaQron backend (QNodeOS and Virtual Node)
87+
network_config_file = Path("simulaqron_network.json")
88+
network_config.read_from_file(network_config_file)
89+
90+
# Some data for this node:
91+
network_name = "default" # A network with this name *must* exist in "simulaqron_network.json"
92+
node_name = "Bob" # A node with this name *must* exist in "simulaqron_network.json"
93+
start_node_name = "Alice" # A node with this name *must* exist in "simulaqron_network.json"
94+
end_node_name = "Charlie" # A node with this name *must* exist in "simulaqron_network.json"
95+
96+
classical_sockets = SocketsConfig(network_config, network_name, NodeConfigType.APP)
97+
98+
server = SimulaQronClassicalServer(classical_sockets, node_name)
99+
client = SimulaQronClassicalClient(classical_sockets)
100+
101+
server.register_client_handler(bob_program)
102+
server.start_serving()
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import logging
2+
3+
from asyncio import StreamReader, StreamWriter
4+
from pathlib import Path
5+
6+
from simulaqron.general.host_config import SocketsConfig
7+
from simulaqron.sdk.protocol import SimulaQronClassicalServer
8+
from simulaqron.settings import network_config, simulaqron_settings
9+
10+
# This is recipe to use NetQASM with simulaqron backend.
11+
from netqasm.runtime.settings import set_simulator
12+
13+
from simulaqron.settings.network_config import NodeConfigType
14+
15+
set_simulator("simulaqron")
16+
17+
# Importing NetQASM connection, Qubit and EPR socket must be *after*
18+
# setting the simulator for NetQASM
19+
from netqasm.logging.glob import set_log_level # noqa: E402
20+
from netqasm.sdk.external import NetQASMConnection # noqa: E402
21+
from netqasm.sdk import EPRSocket # noqa: E402
22+
23+
24+
# This function contains the code of the classical client
25+
async def charlie_program(reader: StreamReader, writer: StreamWriter) -> int:
26+
# This is "Charlie": the end node of the GHZ chain
27+
this_node_name = "Charlie"
28+
remote_node_name = "Bob"
29+
logging.debug("LOCAL %s: Running client side program.", this_node_name)
30+
31+
message = await reader.read(100)
32+
assert message.decode("utf-8") == "receive_qubit"
33+
epr_socket = EPRSocket(remote_node_name)
34+
# To start executing quantum operations, we need to create a NetQASM connection
35+
with NetQASMConnection(this_node_name, epr_sockets=[epr_socket]) as charlie:
36+
# Receive an entangled qubit
37+
epr = epr_socket.recv_keep()[0]
38+
39+
writer.write("continue".encode("utf-8"))
40+
41+
print("here2")
42+
# And simply measure it
43+
m1 = epr.measure()
44+
# Any value that comes from NetQASM *need* to be retrieved ("casted" to int)
45+
# *after* the connection is closed (or after flushing the connection, untested)
46+
m1_val = int(m1)
47+
48+
print(f"{this_node_name}: My outcome is '{m1_val}'")
49+
return 0
50+
51+
52+
if __name__ == "__main__":
53+
logging.basicConfig(
54+
format="%(asctime)s:%(levelname)s:%(name)s:%(filename)s:%(lineno)d:%(message)s",
55+
level=logging.DEBUG,
56+
force=True
57+
)
58+
# We set the netqasm log level to "info" to avoid verbose output from the internals.
59+
set_log_level(logging.INFO)
60+
61+
# Load the simulaqron settings file
62+
simulaqron_config_file = Path("simulaqron_settings.json")
63+
simulaqron_settings.read_from_file(simulaqron_config_file)
64+
65+
# Load the file network configuration file
66+
# We still need this file to correctly interact with the SimulaQron backend (QNodeOS and Virtual Node)
67+
network_config_file = Path("simulaqron_network.json")
68+
network_config.read_from_file(network_config_file)
69+
70+
# Some data for this node:
71+
network_name = "default" # A network with this name *must* exist in "simulaqron_network.json"
72+
node_name = "Charlie" # A node with this name *must* exist in "simulaqron_network.json"
73+
other_node_name = "Bob"
74+
75+
sockets = SocketsConfig(network_config, network_name, NodeConfigType.APP)
76+
77+
server = SimulaQronClassicalServer(sockets, node_name)
78+
server.register_client_handler(charlie_program)
79+
server.start_serving()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
3+
./terminate.sh
4+
sleep 1
5+
./run.sh

examples/new-sdk/extendGHZ/run.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env bash
2+
3+
# Flag used to determine whether to start simulaqron backend or not
4+
# This is useful when using this script to run the application in different machines.
5+
START_SIMULAQRON=true
6+
7+
# Process the arguments, if any
8+
while [[ $# -gt 0 ]]; do
9+
case $1 in
10+
-n|--no-start-simulaqron)
11+
START_SIMULAQRON=false
12+
shift
13+
;;
14+
esac
15+
done
16+
17+
if [ "$START_SIMULAQRON" = true ]; then
18+
# Check if SimulaQron is already running
19+
if [ ! -f ~/.simulaqron_pids/simulaqron_network_default.pid ]; then
20+
# If not, start simulaqron backend for both nodes
21+
simulaqron start --nodes=Alice,Bob,Charlie --network-config-file simulaqron_network.json --simulaqron-config-file simulaqron_settings.json
22+
fi
23+
fi
24+
25+
python3 charlieTest.py &
26+
sleep 1
27+
python3 bobTest.py &
28+
sleep 1
29+
python3 aliceTest.py
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
"Charlie": {
21+
"app_socket": ["localhost", 8841],
22+
"qnodeos_socket": ["localhost", 8842],
23+
"vnode_socket": ["localhost", 8843]
24+
}
25+
},
26+
{
27+
"David": {
28+
"app_socket": ["localhost", 8871],
29+
"qnodeos_socket": ["localhost", 8872],
30+
"vnode_socket": ["localhost", 8873]
31+
}
32+
}
33+
],
34+
"topology": null
35+
}
36+
]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"max_qubits": 20,
3+
"max_registers": 1000,
4+
"conn_retry_time": 0.5,
5+
"conn_max_retries": 10,
6+
"recv_timeout": 100,
7+
"recv_retry_time": 0.1,
8+
"recv_max_retries": 10,
9+
"log_level": 30,
10+
"sim_backend": "projectq",
11+
"noisy_qubits": false,
12+
"max_app_waiting_time": -1.0,
13+
"t1": 1.0
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env sh
2+
TEST_PIDS=$(ps aux | grep python | grep -E "Test" | awk {'print $2'})
3+
if [ "$TEST_PIDS" != "" ]
4+
then
5+
kill -9 $TEST_PIDS
6+
fi
7+
8+
# Check if SimulaQron is running
9+
if [ -f ~/.simulaqron_pids/simulaqron_network_default.pid ]; then
10+
if ! simulaqron stop
11+
then
12+
# Kill the process, only if simulaqron could not be stopped gracefully
13+
cat $HOME/.simulaqron_pids/simulaqron_network_default.pid | xargs kill -9
14+
fi
15+
fi
16+

0 commit comments

Comments
 (0)