-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added example for creating a fully functional server for one of the…
… nodes
- Loading branch information
1 parent
44acb17
commit 2cf1b67
Showing
4 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
from typing import List | ||
|
||
from netqasm.sdk.connection import BaseNetQASMConnection | ||
from netsquid_netbuilder.logger import LogManager | ||
from netsquid_protocols import CSocketListener, QueueProtocol, SleepingProtocol | ||
|
||
from squidasm.sim.stack.program import Program, ProgramContext, ProgramMeta | ||
|
||
REQUEST_EPR_PAIR_GENERATION_MSG = "Request to initiate EPR pair generation" | ||
CONFIRM_EPR_PAIR_GENERATION_MSG = "confirmation to initiate EPR pair generation" | ||
|
||
|
||
class ClientProgram(Program): | ||
def __init__(self, node_name: str, server_name: str, request_start_time=0): | ||
self.node_name = node_name | ||
self.server_name = server_name | ||
self.logger = LogManager.get_stack_logger(f"Program {self.node_name}") | ||
self.request_start_time = request_start_time | ||
|
||
@property | ||
def meta(self) -> ProgramMeta: | ||
return ProgramMeta( | ||
name="tutorial_program", | ||
csockets=[self.server_name], | ||
epr_sockets=[self.server_name], | ||
max_qubits=1, | ||
) | ||
|
||
def run(self, context: ProgramContext): | ||
csocket = context.csockets[self.server_name] | ||
epr_socket = context.epr_sockets[self.server_name] | ||
connection = context.connection | ||
|
||
# Wait to the desired time using a WaitingProtocol | ||
waiting_protocol = SleepingProtocol() | ||
yield from waiting_protocol.sleep(end_time=self.request_start_time) | ||
|
||
# submit the request to the server | ||
message = REQUEST_EPR_PAIR_GENERATION_MSG | ||
csocket.send(message) | ||
self.logger.info(f"sending message {message} to {self.server_name}") | ||
|
||
# wait for confirmation from the server | ||
message = yield from csocket.recv() | ||
self.logger.info(f"received message {message} from {self.server_name}") | ||
if message != CONFIRM_EPR_PAIR_GENERATION_MSG: | ||
raise RuntimeError(f"Expected confirmation but received: {message}") | ||
|
||
# Execute the request | ||
epr_qubit = epr_socket.create_keep()[0] | ||
epr_qubit.H() | ||
result = epr_qubit.measure() | ||
yield from connection.flush() | ||
self.logger.info(f"measures local EPR qubit: {result}") | ||
|
||
return {} | ||
|
||
|
||
class ServerProgram(Program): | ||
def __init__(self, clients: List[str]): | ||
self.clients = clients | ||
self.node_name = "server" | ||
self.logger = LogManager.get_stack_logger(f"Program {self.node_name}") | ||
|
||
@property | ||
def meta(self) -> ProgramMeta: | ||
return ProgramMeta( | ||
name="tutorial_program", | ||
csockets=self.clients, | ||
epr_sockets=self.clients, | ||
max_qubits=1, | ||
) | ||
|
||
def run(self, context: ProgramContext): | ||
connection: BaseNetQASMConnection = context.connection | ||
|
||
# We use the Queue protocol to manage the queue and signal for new queue items | ||
queue_protocol = QueueProtocol() | ||
queue_protocol.start() | ||
|
||
# Set up a CSocketListener for each client that will forward requests to the queue | ||
for client in self.clients: | ||
thread = CSocketListener(context, client, queue_protocol, self.logger) | ||
thread.start() | ||
|
||
while True: | ||
# Wait for a new request if applicable and process the next item in the queue | ||
client_name, msg = yield from queue_protocol.pop() | ||
csocket = context.csockets[client_name] | ||
epr_socket = context.epr_sockets[client_name] | ||
|
||
if msg != REQUEST_EPR_PAIR_GENERATION_MSG: | ||
raise RuntimeError(f"Received unsupported request: {msg}") | ||
self.logger.info(f"start processing request from {client_name}") | ||
|
||
# Send confirmation to the client | ||
message = CONFIRM_EPR_PAIR_GENERATION_MSG | ||
csocket.send(message) | ||
self.logger.info(f"sending message {message} to {client_name}") | ||
|
||
# Start processing the request | ||
epr_qubit = epr_socket.recv_keep()[0] | ||
epr_qubit.H() | ||
result = epr_qubit.measure() | ||
yield from connection.flush() | ||
self.logger.info(f"measures local EPR qubit: {result}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
from logging import Logger | ||
from typing import Generator, Tuple | ||
|
||
from netqasm.sdk.classical_communication.socket import Socket | ||
from netsquid.protocols import Protocol | ||
|
||
from squidasm.sim.stack.program import ProgramContext | ||
|
||
|
||
class SleepingProtocol(Protocol): | ||
"""Netsquid protocol to support waiting or sleeping in a Program.""" | ||
|
||
def sleep(self, duration=0, end_time=0): | ||
""" | ||
Sleep for a certain duration or until a certain end time. | ||
Specify either duration or end time, but not both. | ||
Requires usage of `yield from` before method call to function. | ||
:param duration: Time to sleep in nanoseconds. | ||
:param end_time: Simulation time to sleep to in nanoseconds. | ||
""" | ||
yield self.await_timer(duration=duration, end_time=end_time) | ||
|
||
|
||
class QueueProtocol(Protocol): | ||
"""Netsquid protocol for a queue, that supports waiting for a new item, when the queue is empty.""" | ||
|
||
QUEUE_STATUS_CHANGE_SIGNAL = "Queue has been updated" | ||
|
||
def __init__(self): | ||
self._queue = [] | ||
self.add_signal(self.QUEUE_STATUS_CHANGE_SIGNAL) | ||
|
||
def push(self, msg_source: str, msg: str): | ||
""" | ||
Put a new item on the queue. | ||
:param msg_source: Source of the message. | ||
:param msg: The message. | ||
""" | ||
self._queue.append((msg_source, msg)) | ||
self.send_signal(self.QUEUE_STATUS_CHANGE_SIGNAL) | ||
|
||
def pop(self) -> Generator[None, None, Tuple[str, str]]: | ||
""" | ||
Take an item of the queue. Waits for a new item if the queue is empty. | ||
:return: A tuple with the source of the message and the message. | ||
""" | ||
if len(self._queue) == 0: | ||
yield self.await_signal(sender=self, signal_label=self.QUEUE_STATUS_CHANGE_SIGNAL) # fmt: skip | ||
return self._queue.pop() | ||
|
||
|
||
class CSocketListener(Protocol): | ||
"""Netsquid protocol that listens to messages on a CSocket and forwards them to the QueueProtocol.""" | ||
|
||
def __init__( | ||
self, | ||
context: ProgramContext, | ||
peer_name: str, | ||
queue_protocol: QueueProtocol, | ||
logger: Logger, | ||
): | ||
""" | ||
:param context: ProgramContext of the current program. | ||
:param peer_name: Name of the remote node this Listener should listen to. | ||
:param queue_protocol: The QueueProtocol where messages are forwarded to. | ||
:param logger: A Logger. | ||
""" | ||
self._context = context | ||
self._peer_name = peer_name | ||
self._queue_protocol = queue_protocol | ||
self.logger = logger.getChild(f"PortListener({peer_name})") | ||
|
||
def run(self): | ||
csocket: Socket = self._context.csockets[self._peer_name] | ||
while True: | ||
message = yield from csocket.recv() | ||
self._queue_protocol.push(self._peer_name, str(message)) | ||
self.logger.info(f"Received message: {message}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import logging | ||
|
||
import numpy.random | ||
from application import ClientProgram, ServerProgram | ||
from netsquid_magic.models.perfect import PerfectLinkConfig | ||
from netsquid_netbuilder.modules.clinks.default import DefaultCLinkConfig | ||
from netsquid_netbuilder.util.network_generation import create_complete_graph_network | ||
|
||
from squidasm.run.stack.run import run | ||
|
||
num_nodes = 6 | ||
random_request_start_times = False | ||
node_names = [f"Node_{i}" for i in range(num_nodes)] | ||
|
||
cfg = create_complete_graph_network( | ||
node_names, | ||
"perfect", | ||
PerfectLinkConfig(state_delay=100), | ||
clink_typ="default", | ||
clink_cfg=DefaultCLinkConfig(delay=100), | ||
) | ||
|
||
server_name = node_names[0] | ||
client_names = node_names[1:] | ||
|
||
programs = {server_name: ServerProgram(clients=client_names)} | ||
for client in client_names: | ||
programs[client] = ClientProgram(client, server_name) | ||
|
||
# Set log level for all programs | ||
for program in programs.values(): | ||
program.logger.setLevel(logging.INFO) | ||
|
||
if random_request_start_times: | ||
# Set the clients to start their requests at a random time between 0 and 400 ns | ||
for client in client_names: | ||
programs[client].request_start_time = numpy.random.randint(0, 400) | ||
|
||
run(config=cfg, programs=programs, num_times=1) |