diff --git a/examples/stack/bqc/README.md b/examples/stack/bqc/README.md deleted file mode 100644 index 7157bac0..00000000 --- a/examples/stack/bqc/README.md +++ /dev/null @@ -1,32 +0,0 @@ -## BQC application - -A server performs an Effective Computation (EC) on behalf of a client. - -In the so-called *computation rounds*, the client makes the server perform the EC. -In the *trap rounds*, the client sends dummy information to the server just to check -if the server is behaving honestly. - -### Effective Computation (EC) -Effective Computation (**EC**): `H Rz[beta] H Rz[alpha] |+>` followed by a measurement in the Z-basis. - -*(Exact prepared state)*: `Z^(m1) H Rz[beta] H Rz[alpha] |+>` -![](fig/5_6_effective.png) - -### EC implementation using RSP and MBQC -![](fig/5_6_generic.png) - -Blue values are sent from server to client. -Red values are computed by the client and sent to the server. - -### Inputs -- `alpha`: parameter of EC -- `beta`: parameter of EC -- `theta1`: randomly chosen by client from `[0, pi/4, ..., 7pi/4]`. Does not affect the EC. -- `theta2`: randomly chosen by client from `[0, pi/4, ..., 7pi/4]`. Does not affect the EC. - -### Expected measurement outcomes -- `p1`: uniformly random -- `p2`: uniformly random -- `m1`: uniformly random -- `m2`: outcome of **EC**. Expected statistics depend on alpha and beta. - diff --git a/examples/stack/bqc/example_bqc_nv.py b/examples/stack/bqc/example_bqc_nv.py index 9ea725fd..a52f7d5a 100644 --- a/examples/stack/bqc/example_bqc_nv.py +++ b/examples/stack/bqc/example_bqc_nv.py @@ -11,7 +11,7 @@ from netqasm.sdk.qubit import Qubit from pydynaa import EventExpression -from squidasm.run.stack.config import NVQDeviceConfig, StackNetworkConfig +from squidasm.run.stack.config import StackNetworkConfig from squidasm.run.stack.run import run from squidasm.sim.stack.common import LogManager from squidasm.sim.stack.csocket import ClassicalSocket @@ -244,8 +244,6 @@ def trap_round( cfg_file = os.path.join(os.path.dirname(__file__), "config_nv.yaml") cfg = StackNetworkConfig.from_file(cfg_file) - cfg.stacks[0].qdevice_cfg = NVQDeviceConfig.perfect_config() - cfg.stacks[1].qdevice_cfg = NVQDeviceConfig.perfect_config() # computation_round(cfg, num_times, alpha=PI_OVER_2, beta=PI_OVER_2) trap_round(cfg=cfg, num_times=num_times, dummy=2) diff --git a/examples/stack/bqc/fig/5_6_generic.png b/examples/stack/bqc/fig/5_6_generic.png index 6a679432..629ac444 100644 Binary files a/examples/stack/bqc/fig/5_6_generic.png and b/examples/stack/bqc/fig/5_6_generic.png differ diff --git a/examples/stack/bqc/fig/trap_dummy_1.png b/examples/stack/bqc/fig/trap_dummy_1.png new file mode 100644 index 00000000..9e2c2a3b Binary files /dev/null and b/examples/stack/bqc/fig/trap_dummy_1.png differ diff --git a/examples/stack/bqc/fig/trap_dummy_2.png b/examples/stack/bqc/fig/trap_dummy_2.png new file mode 100644 index 00000000..8b9e8598 Binary files /dev/null and b/examples/stack/bqc/fig/trap_dummy_2.png differ diff --git a/examples/stack/link_layer_custom_subrt/example_ll_custom_subrt.py b/examples/stack/link_layer_custom_subrt/example_ll_custom_subrt.py index 1061f78e..52b44676 100644 --- a/examples/stack/link_layer_custom_subrt/example_ll_custom_subrt.py +++ b/examples/stack/link_layer_custom_subrt/example_ll_custom_subrt.py @@ -13,7 +13,6 @@ StackConfig, StackNetworkConfig, ) -from squidasm.run.stack.run import run from squidasm.sim.stack.common import LogManager from squidasm.sim.stack.program import Program, ProgramContext, ProgramMeta @@ -186,8 +185,8 @@ def post_create(conn, q, pair): if __name__ == "__main__": - LogManager.set_log_level("WARNING") - # LogManager.log_to_file(os.path.join(os.path.dirname(__file__), "debug.log")) + LogManager.set_log_level("DEBUG") + LogManager.log_to_file(os.path.join(os.path.dirname(__file__), "debug.log")) num_times = 1 @@ -208,6 +207,6 @@ def post_create(conn, q, pair): client_program = ClientProgram(num_repetitions=2) server_program = ServerProgram(num_repetitions=2) - results = run(cfg, {"client": client_program, "server": server_program}, num_times) + # results = run(cfg, {"client": client_program, "server": server_program}, num_times) - print(results) + # print(results) diff --git a/examples/stack/qne_bqc/README.md b/examples/stack/qne_bqc/README.md new file mode 100644 index 00000000..2e2e3cde --- /dev/null +++ b/examples/stack/qne_bqc/README.md @@ -0,0 +1,75 @@ + +## Prerequisites +`squidasm` relies on [NetSquid](https://netsquid.org/). +To install NetSquid, an account is needed. See the NetSquid website how this is done. + +### Installation +Clone the squidasm repostitory and checkout the `qne-hardware` branch: +``` +git clone https://github.com/QuTech-Delft/squidasm +cd squidasm +git checkout qne-hardware +``` + +Make sure you have two environment variables set with your NetSquid username and password. +These are needed for the installation of squidasm. +``` +export NETSQUIDPYPI_USER= +export NETSQUIDPYPI_PWD= +``` + +Install `squidasm`: +``` +make install-tests +``` + +After installation, the simulations can be run as described below. + +## BQC application + +We consider a simple BQC application consisting of a client and a server. +A server performs an Effective Computation (EC) on behalf of the client. + +In the so-called *computation rounds*, the client makes the server perform the EC. +In the *trap rounds*, the client sends dummy information to the server just to check +if the server is behaving honestly. + +### Effective Computation (EC) +Effective Computation (**EC**): `H Rz[beta] H Rz[alpha] |+>` followed by a measurement in the Z-basis. +(Exact prepared state: `Z^(m1) H Rz[beta] H Rz[alpha] |+>` ) +![](fig/5_6_effective.png) + +### EC implementation using Remote State Preparation and Measurement Based Quantum Computation +The following circuit produces equivalent results to the EC. +It is a 'blind version' of the EC. +![](fig/5_6_generic.png) + +Blue values are sent from server to client. +Red values are computed by the client and sent to the server. + +### Inputs +- `alpha`: parameter of EC +- `beta`: parameter of EC +- `theta1`: randomly chosen by client from `[0, pi/4, ..., 7pi/4]`. Does not affect the EC. +- `theta2`: randomly chosen by client from `[0, pi/4, ..., 7pi/4]`. Does not affect the EC. + +### Expected measurement outcomes +- `p1`: uniformly random +- `p2`: uniformly random +- `m1`: uniformly random +- `m2`: outcome of **EC**. Expected statistics depend on alpha and beta. + + +## Simulation scripts +Examples of simulation code are in the `examples/stack` directory. The `examples/stack/qne_bqc` directory in there can be used to do simulations for the QNE parameter estimation. +It contains BQC application code file and some configuration files. `example_bqc_nv.py` is the full BQC application, including trap rounds and +computation of the error rate. Simply running the file with + +``` +python examples/stack/qne_bqc/example_bqc_nv.py +``` +will run the simulation. The data it produces is simply printed or ignored. +One can simply add code to e.g. store the data to a file or to create plots from them. + +The `config_nv.yaml` file contains configuration parameters for the hardware of the nodes and the entanglement link. +Check https://docs.netsquid.org/snippets/netsquid-nv/modules/magic_distributor.html#netsquid_nv.magic_distributor.NVSingleClickMagicDistributor for information about the link parameters, and the comments in the `config_nv.yaml` file itself for the node parameters. \ No newline at end of file diff --git a/examples/stack/qne_bqc/config_nv.yaml b/examples/stack/qne_bqc/config_nv.yaml new file mode 100644 index 00000000..8001224d --- /dev/null +++ b/examples/stack/qne_bqc/config_nv.yaml @@ -0,0 +1,83 @@ +qdevice_cfg: &qdevice_cfg + # number of qubits per NV + num_qubits: 2 + + # initialization error of the electron spin + electron_init_depolar_prob: 0.05 + + # error of the single-qubit gate + electron_single_qubit_depolar_prob: 0.0 + + # measurement errors (prob_error_X is the probability that outcome X is flipped to 1 - X) + prob_error_0: 0.05 + prob_error_1: 0.005 + + # initialization error of the carbon nuclear spin + carbon_init_depolar_prob: 0.05 + + # error of the Z-rotation gate on the carbon nuclear spin + carbon_z_rot_depolar_prob: 0.001 + + # error of the native NV two-qubit gate + ec_gate_depolar_prob: 0.008 + + # delta_w and tau_decay + delta_w: 0 + tau_decay: 1.0 + + # coherence times + electron_T1: 1_000_000_000 + electron_T2: 300_000_000 + carbon_T1: 150_000_000_000 + carbon_T2: 1_500_000_000 + + # gate execution times + carbon_init: 310_000 + carbon_rot_x: 500_000 + carbon_rot_y: 500_000 + carbon_rot_z: 500_000 + electron_init: 2_000 + electron_rot_x: 5_000 + electron_rot_y: 5_000 + electron_rot_z: 5_000 + ec_controlled_dir_x: 500_000 + ec_controlled_dir_y: 500_000 + measure: 3_700 + +stacks: + - name: client + qdevice_typ: nv + qdevice_cfg: + <<: *qdevice_cfg + - name: server + qdevice_typ: nv + qdevice_cfg: + <<: *qdevice_cfg + +link_cfg: &link_cfg + cycle_time: 5_000 + alpha_A: 0.1 + alpha_B: 0.1 + length_A: 0.005 + length_B: 0.005 + p_loss_init_A: null + p_loss_length_A: null + speed_of_light_A: null + p_loss_init_B: null + p_loss_length_B: null + speed_of_light_B: null + dark_count_probability: null + detector_efficiency: null + visibility: null + num_resolving: null + std_electron_electron_phase_drift: null + coherent_phase: null + p_double_exc: null + p_fail_class_corr: null + +links: + - stack1: client + stack2: server + typ: nv + cfg: + <<: *link_cfg \ No newline at end of file diff --git a/examples/stack/qne_bqc/example_bqc_nv.py b/examples/stack/qne_bqc/example_bqc_nv.py new file mode 100644 index 00000000..713e9845 --- /dev/null +++ b/examples/stack/qne_bqc/example_bqc_nv.py @@ -0,0 +1,253 @@ +from __future__ import annotations + +import math +import os +from typing import Any, Dict, Generator + +import netsquid as ns +from netqasm.lang.ir import BreakpointAction +from netqasm.sdk.connection import BaseNetQASMConnection +from netqasm.sdk.futures import Future, RegFuture +from netqasm.sdk.qubit import Qubit + +from pydynaa import EventExpression +from squidasm.run.stack.config import StackNetworkConfig +from squidasm.run.stack.run import run +from squidasm.sim.stack.common import LogManager +from squidasm.sim.stack.csocket import ClassicalSocket +from squidasm.sim.stack.program import Program, ProgramContext, ProgramMeta + +# BQC application run on NV hardware. + + +class ClientProgram(Program): + PEER = "server" + + def __init__( + self, + alpha: float, + beta: float, + trap: bool, + dummy: int, + theta1: float, + theta2: float, + r1: int, + r2: int, + ): + self._alpha = alpha + self._beta = beta + self._trap = trap + self._dummy = dummy + self._theta1 = theta1 + self._theta2 = theta2 + self._r1 = r1 + self._r2 = r2 + + @property + def meta(self) -> ProgramMeta: + return ProgramMeta( + name="client_program", + parameters={ + "alpha": self._alpha, + "beta": self._beta, + "trap": self._trap, + "dummy": self._dummy, + "theta1": self._theta1, + "theta2": self._theta2, + "r1": self._r1, + "r2": self._r2, + }, + csockets=[self.PEER], + epr_sockets=[self.PEER], + max_qubits=2, + ) + + def run( + self, context: ProgramContext + ) -> Generator[EventExpression, None, Dict[str, Any]]: + conn = context.connection + epr_socket = context.epr_sockets[self.PEER] + csocket: ClassicalSocket = context.csockets[self.PEER] + + p1: Future + p2: Future + outcomes = conn.new_array(length=2) + + def post_create(_: BaseNetQASMConnection, q: Qubit, index: RegFuture): + with index.if_eq(0): + if not (self._trap and self._dummy == 2): + q.rot_Z(angle=self._theta2) + q.H() + + with index.if_eq(1): + if not (self._trap and self._dummy == 1): + q.rot_Z(angle=self._theta1) + q.H() + q.measure(future=outcomes.get_future_index(index)) + + epr_socket.create_keep(2, sequential=True, post_routine=post_create) + + yield from conn.flush() + + p1 = int(outcomes.get_future_index(1)) + p2 = int(outcomes.get_future_index(0)) + + if self._trap and self._dummy == 2: + delta1 = -self._theta1 + (p1 + self._r1) * math.pi + else: + delta1 = self._alpha - self._theta1 + (p1 + self._r1) * math.pi + csocket.send_float(delta1) + + m1 = yield from csocket.recv_int() + if self._trap and self._dummy == 1: + delta2 = -self._theta2 + (p2 + self._r2) * math.pi + else: + delta2 = ( + math.pow(-1, (m1 + self._r1)) * self._beta + - self._theta2 + + (p2 + self._r2) * math.pi + ) + csocket.send_float(delta2) + + return {"p1": p1, "p2": p2} + + +class ServerProgram(Program): + PEER = "client" + + @property + def meta(self) -> ProgramMeta: + return ProgramMeta( + name="server_program", + parameters={}, + csockets=[self.PEER], + epr_sockets=[self.PEER], + max_qubits=2, + ) + + def run( + self, context: ProgramContext + ) -> Generator[EventExpression, None, Dict[str, Any]]: + conn = context.connection + epr_socket = context.epr_sockets[self.PEER] + csocket: ClassicalSocket = context.csockets[self.PEER] + + # Create EPR Pair + epr1, epr2 = epr_socket.recv_keep(2) + epr2.cphase(epr1) + + yield from conn.flush() + + delta1 = yield from csocket.recv_float() + + epr2.rot_Z(angle=delta1) + epr2.H() + m1 = epr2.measure(store_array=False) + yield from conn.flush() + + m1 = int(m1) + + csocket.send_int(m1) + + delta2 = yield from csocket.recv_float() + + epr1.rot_Z(angle=delta2) + epr1.H() + conn.insert_breakpoint(BreakpointAction.DUMP_LOCAL_STATE) + m2 = epr1.measure(store_array=False) + yield from conn.flush() + + m2 = int(m2) + return {"m1": m1, "m2": m2} + + +PI = math.pi +PI_OVER_2 = math.pi / 2 + + +def computation_round( + cfg: StackNetworkConfig, + num_times: int = 1, + alpha: float = 0.0, + beta: float = 0.0, + theta1: float = 0.0, + theta2: float = 0.0, +) -> None: + client_program = ClientProgram( + alpha=alpha, + beta=beta, + trap=False, + dummy=-1, + theta1=theta1, + theta2=theta2, + r1=0, + r2=0, + ) + server_program = ServerProgram() + + _, server_results = run( + cfg, {"client": client_program, "server": server_program}, num_times=num_times + ) + + m2s = [result["m2"] for result in server_results] + num_zeros = len([m for m in m2s if m == 0]) + frac0 = round(num_zeros / num_times, 2) + frac1 = 1 - frac0 + print(f"dist (0, 1) = ({frac0}, {frac1})") + + +def trap_round( + cfg: StackNetworkConfig, + num_times: int = 1, + alpha: float = 0.0, + beta: float = 0.0, + theta1: float = 0.0, + theta2: float = 0.0, + dummy: int = 1, +) -> None: + client_program = ClientProgram( + alpha=alpha, + beta=beta, + trap=True, + dummy=dummy, + theta1=theta1, + theta2=theta2, + r1=0, + r2=0, + ) + server_program = ServerProgram() + + client_results, server_results = run( + cfg, {"client": client_program, "server": server_program}, num_times=num_times + ) + + p1s = [result["p1"] for result in client_results] + p2s = [result["p2"] for result in client_results] + m1s = [result["m1"] for result in server_results] + m2s = [result["m2"] for result in server_results] + + assert dummy in [1, 2] + if dummy == 1: + num_fails = len([(p, m) for (p, m) in zip(p1s, m2s) if p != m]) + else: + num_fails = len([(p, m) for (p, m) in zip(p2s, m1s) if p != m]) + + frac_fail = round(num_fails / num_times, 2) + print(f"fail rate: {frac_fail}") + + +if __name__ == "__main__": + num_times = 100 + LogManager.set_log_level("WARNING") + + cwd = os.path.dirname(__file__) + log_file = os.path.join(cwd, "qne_bqc.log") + + LogManager.log_to_file(log_file) + ns.set_qstate_formalism(ns.qubits.qformalism.QFormalism.DM) + + cfg_file = os.path.join(cwd, "config_nv.yaml") + cfg = StackNetworkConfig.from_file(cfg_file) + + # computation_round(cfg, num_times, alpha=PI_OVER_2, beta=PI_OVER_2) + trap_round(cfg=cfg, num_times=num_times, dummy=2) diff --git a/examples/stack/qne_bqc/fig/5_6_effective.png b/examples/stack/qne_bqc/fig/5_6_effective.png new file mode 100644 index 00000000..258154c5 Binary files /dev/null and b/examples/stack/qne_bqc/fig/5_6_effective.png differ diff --git a/examples/stack/qne_bqc/fig/5_6_generic.png b/examples/stack/qne_bqc/fig/5_6_generic.png new file mode 100644 index 00000000..629ac444 Binary files /dev/null and b/examples/stack/qne_bqc/fig/5_6_generic.png differ diff --git a/examples/stack/qne_teleport/README.md b/examples/stack/qne_teleport/README.md new file mode 100644 index 00000000..75b50a2f --- /dev/null +++ b/examples/stack/qne_teleport/README.md @@ -0,0 +1,25 @@ +## Prerequisites +`squidasm` relies on [NetSquid](https://netsquid.org/). +To install NetSquid, an account is needed. See the NetSquid website how this is done. + +### Installation +Clone the squidasm repostitory and checkout the `qne-hardware` branch: +``` +git clone https://github.com/QuTech-Delft/squidasm +cd squidasm +git checkout qne-hardware +``` + +Make sure you have two environment variables set with your NetSquid username and password. +These are needed for the installation of squidasm. +``` +export NETSQUIDPYPI_USER= +export NETSQUIDPYPI_PWD= +``` + +Install `squidasm`: +``` +make install-tests +``` + +After installation, the simulations can be run. diff --git a/examples/stack/qne_teleport/config_nv.yaml b/examples/stack/qne_teleport/config_nv.yaml new file mode 100644 index 00000000..6b920e5c --- /dev/null +++ b/examples/stack/qne_teleport/config_nv.yaml @@ -0,0 +1,84 @@ +qdevice_cfg: &qdevice_cfg + # number of qubits per NV + num_qubits: 2 + + # initialization error of the electron spin + electron_init_depolar_prob: 0.05 + + # error of the single-qubit gate + electron_single_qubit_depolar_prob: 0.0 + + # measurement errors (prob_error_X is the probability that outcome X is flipped to 1 - X) + prob_error_0: 0.05 + prob_error_1: 0.005 + + # initialization error of the carbon nuclear spin + carbon_init_depolar_prob: 0.05 + + # error of the Z-rotation gate on the carbon nuclear spin + carbon_z_rot_depolar_prob: 0.001 + + # error of the native NV two-qubit gate + ec_gate_depolar_prob: 0.008 + + # delta_w and tau_decay + delta_w: 0 + tau_decay: 1.0 + + # coherence times + electron_T1: 1_000_000_000 + electron_T2: 300_000_000 + carbon_T1: 150_000_000_000 + carbon_T2: 1_500_000_000 + + # gate execution times + carbon_init: 310_000 + carbon_rot_x: 500_000 + carbon_rot_y: 500_000 + carbon_rot_z: 500_000 + electron_init: 2_000 + electron_rot_x: 5_000 + electron_rot_y: 5_000 + electron_rot_z: 5_000 + ec_controlled_dir_x: 500_000 + ec_controlled_dir_y: 500_000 + measure: 3_700 + +stacks: + - name: sender + qdevice_typ: nv + qdevice_cfg: + <<: *qdevice_cfg + - name: receiver + qdevice_typ: nv + qdevice_cfg: + <<: *qdevice_cfg + +link_cfg: &link_cfg + cycle_time: 5_000 + alpha_A: 0.1 + alpha_B: 0.1 + length_A: 0.005 + length_B: 0.005 + p_loss_init_A: null + p_loss_length_A: null + speed_of_light_A: null + p_loss_init_B: null + p_loss_length_B: null + speed_of_light_B: null + dark_count_probability: null + detector_efficiency: null + visibility: null + num_resolving: null + std_electron_electron_phase_drift: null + coherent_phase: null + p_double_exc: null + p_fail_class_corr: null + +links: + - stack1: sender + stack2: receiver + typ: nv + cfg: + <<: *link_cfg + host_host_latency: 10_000_000 \ No newline at end of file diff --git a/examples/stack/qne_teleport/example_teleport_nv.py b/examples/stack/qne_teleport/example_teleport_nv.py new file mode 100644 index 00000000..596c9f23 --- /dev/null +++ b/examples/stack/qne_teleport/example_teleport_nv.py @@ -0,0 +1,189 @@ +from __future__ import annotations + +import math +import os +from typing import Any, Dict, Generator, List + +import netsquid as ns +import numpy as np +from netqasm.lang.ir import BreakpointAction +from netqasm.sdk.qubit import Qubit +from netqasm.sdk.toolbox import set_qubit_state +from netsquid.qubits import qubitapi + +from pydynaa import EventExpression +from squidasm.run.stack.config import StackNetworkConfig +from squidasm.run.stack.run import run +from squidasm.sim.stack.common import LogManager +from squidasm.sim.stack.csocket import ClassicalSocket +from squidasm.sim.stack.globals import GlobalSimData +from squidasm.sim.stack.program import Program, ProgramContext, ProgramMeta + +PI = math.pi +PI_OVER_2 = math.pi / 2 + + +class SenderProgram(Program): + PEER = "receiver" + + def __init__( + self, + theta: float, + phi: float, + ): + self._theta = theta + self._phi = phi + + @property + def meta(self) -> ProgramMeta: + return ProgramMeta( + name="sender_program", + parameters={ + "theta": self._theta, + "phi": self._phi, + }, + csockets=[self.PEER], + epr_sockets=[self.PEER], + max_qubits=2, + ) + + def run( + self, context: ProgramContext + ) -> Generator[EventExpression, None, Dict[str, Any]]: + conn = context.connection + epr_socket = context.epr_sockets[self.PEER] + csocket: ClassicalSocket = context.csockets[self.PEER] + + e = epr_socket.create_keep()[0] + + q = Qubit(conn) + set_qubit_state(q, self._phi, self._theta) + + q.cnot(e) + q.H() + m1 = q.measure() + m2 = e.measure() + + yield from conn.flush() + + m1, m2 = int(m1), int(m2) + + csocket.send_int(m1) + csocket.send_int(m2) + + return {"m1": m1, "m2": m2} + + +class ReceiverProgram(Program): + PEER = "sender" + + @property + def meta(self) -> ProgramMeta: + return ProgramMeta( + name="receiver_program", + parameters={}, + csockets=[self.PEER], + epr_sockets=[self.PEER], + max_qubits=1, + ) + + def run( + self, context: ProgramContext + ) -> Generator[EventExpression, None, Dict[str, Any]]: + conn = context.connection + epr_socket = context.epr_sockets[self.PEER] + csocket: ClassicalSocket = context.csockets[self.PEER] + + e = epr_socket.recv_keep()[0] + yield from conn.flush() + + m1 = yield from csocket.recv_int() + m2 = yield from csocket.recv_int() + + if m2 == 1: + e.X() + if m1 == 1: + e.Z() + + # Store the quantum state so we can later inspect it. + conn.insert_breakpoint(BreakpointAction.DUMP_LOCAL_STATE) + + # Measure so quantum memory is available again for next round. + e.measure() + + yield from conn.flush() + + # Return the state that was teleported. + breakpoint = GlobalSimData.get_last_breakpoint_state() + state = breakpoint["receiver"][0] + return {"state": state} + + +def do_teleportation( + cfg: StackNetworkConfig, + num_times: int = 1, + phi: float = 0.0, + theta: float = 0.0, + verbose: bool = False, +) -> None: + sender_program = SenderProgram(phi=phi, theta=theta) + receiver_program = ReceiverProgram() + + sender_results, receiver_results = run( + cfg, + {"sender": sender_program, "receiver": receiver_program}, + num_times=num_times, + ) + + fidelities: List[float] = [] + for i in range(num_times): + m1 = sender_results[i]["m1"] + m2 = sender_results[i]["m2"] + state = receiver_results[i]["state"] + if verbose: + print( + f"\nresult {i}:\nm1 = {m1}, m2 = {m2}, state = \n{np.around(state, 4)}" + ) + + teleported = qubitapi.create_qubits(1)[0] + qubitapi.assign_qstate(teleported, state) + + rotate_theta = ns.create_rotation_op(angle=theta, rotation_axis=(0, 1, 0)) + rotate_phi = ns.create_rotation_op(angle=phi, rotation_axis=(0, 0, 1)) + + expected = qubitapi.create_qubits(1)[0] + qubitapi.operate(expected, rotate_theta) + qubitapi.operate(expected, rotate_phi) + + fid = qubitapi.fidelity(teleported, qubitapi.reduced_dm(expected), squared=True) + if verbose: + print(f"fidelity = {fid}") + fidelities.append(fid) + + if verbose: + print(f"\nall fidelities: {fidelities}") + print(f"average fidelity: {round(sum(fidelities) / len(fidelities), 3)}") + + +if __name__ == "__main__": + num_times = 20 + LogManager.set_log_level("WARNING") + + cwd = os.path.dirname(__file__) + log_file = os.path.join(cwd, "qne_teleport.log") + + LogManager.log_to_file(log_file) + ns.set_qstate_formalism(ns.qubits.qformalism.QFormalism.DM) + + cfg_file = os.path.join(cwd, "config_nv.yaml") + cfg = StackNetworkConfig.from_file(cfg_file) + + # State to be teleported is defined by phi and theta. + # Starting from |0>, the state is rotated around the Y-axis by phi, + # and rotated around the Z-axis by theta. + # E.g. theta=pi/2 and phi=pi results in the |-> (negative X) state. + + do_teleportation(cfg, num_times, phi=0, theta=0) + do_teleportation(cfg, num_times, phi=PI, theta=0) + do_teleportation(cfg, num_times, phi=0, theta=PI_OVER_2) + # ... diff --git a/setup.cfg b/setup.cfg index 519ab7a9..62b08b82 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,9 +20,9 @@ install_requires = pydantic >=1.8.2 pydynaa >=0.3, <1.0 netsquid >=1.0.6, <2.0 - netsquid-magic >=12.1, <13.0.0 - netsquid-nv >=8.0, <9.0 - netqasm ==0.11.0a3 + netsquid-magic >=13.1.1, <14.0.0 + netsquid-nv >=9.2.1, <10.0 + netqasm ==0.11.4 [options.extras_require] tests = diff --git a/squidasm/run/stack/build.py b/squidasm/run/stack/build.py index 63313bd3..2361c439 100644 --- a/squidasm/run/stack/build.py +++ b/squidasm/run/stack/build.py @@ -229,4 +229,6 @@ def build_nv_qdevice(name: str, cfg: NVQDeviceConfig) -> QuantumProcessor: mem_noise_models=mem_noise_models, phys_instructions=phys_instructions, ) + qmem.add_property("delta_w", cfg.delta_w) + qmem.add_property("tau_decay", cfg.tau_decay) return qmem diff --git a/squidasm/run/stack/config.py b/squidasm/run/stack/config.py index 1ec04180..ebb41db8 100644 --- a/squidasm/run/stack/config.py +++ b/squidasm/run/stack/config.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, List +from typing import Any, List, Optional import yaml from pydantic import BaseModel @@ -67,6 +67,10 @@ class NVQDeviceConfig(BaseModel): # error of the native NV two-qubit gate ec_gate_depolar_prob: float = 0.008 + # delta_w and tau_decay + delta_w: float = 0 + tau_decay: float = 1.0 + # coherence times electron_T1: int = 1_000_000_000 electron_T2: int = 300_000_000 @@ -79,9 +83,9 @@ class NVQDeviceConfig(BaseModel): carbon_rot_y: int = 500_000 carbon_rot_z: int = 500_000 electron_init: int = 2_000 - electron_rot_x: int = 5 - electron_rot_y: int = 5 - electron_rot_z: int = 5 + electron_rot_x: int = 5_000 + electron_rot_y: int = 5_000 + electron_rot_z: int = 5_000 ec_controlled_dir_x: int = 500_000 ec_controlled_dir_y: int = 500_000 measure: int = 3_700 @@ -110,6 +114,7 @@ class StackConfig(BaseModel): name: str qdevice_typ: str qdevice_cfg: Any + host_qnos_latency: float = 0.0 @classmethod def from_file(cls, path: str) -> StackConfig: @@ -121,6 +126,7 @@ def perfect_generic_config(cls, name: str) -> StackConfig: name=name, qdevice_typ="generic", qdevice_cfg=GenericQDeviceConfig.perfect_config(), + host_qnos_latency=0.0, ) @@ -133,18 +139,47 @@ class DepolariseLinkConfig(BaseModel): def from_file(cls, path: str) -> DepolariseLinkConfig: return _from_file(path, DepolariseLinkConfig) # type: ignore + @classmethod + def perfect_config(cls) -> DepolariseLinkConfig: + return DepolariseLinkConfig(fidelity=1, prob_success=1, t_cycle=1e1) + class NVLinkConfig(BaseModel): + cycle_time: float + alpha_A: float + alpha_B: float length_A: float length_B: float - full_cycle: float - cycle_time: float - alpha: float + p_loss_init_A: Optional[float] + p_loss_length_A: Optional[float] + speed_of_light_A: Optional[float] + p_loss_init_B: Optional[float] + p_loss_length_B: Optional[float] + speed_of_light_B: Optional[float] + dark_count_probability: Optional[float] + detector_efficiency: Optional[float] + visibility: Optional[float] + num_resolving: Optional[bool] + std_electron_electron_phase_drift: Optional[float] + coherent_phase: Optional[float] + p_double_exc: Optional[float] + p_fail_class_corr: Optional[float] @classmethod def from_file(cls, path: str) -> NVLinkConfig: return _from_file(path, NVLinkConfig) # type: ignore + @classmethod + def perfect_config(cls) -> NVLinkConfig: + return NVLinkConfig( + prob_success=1, + cycle_time=1, + alpha_A=0.1, + alpha_B=0.1, + length_A=0.001, + length_B=0.001, + ) + class HeraldedLinkConfig(BaseModel): length: float @@ -166,6 +201,8 @@ class LinkConfig(BaseModel): stack2: str typ: str cfg: Any + host_host_latency: float = 0.0 + qnos_qnos_latency: float = 0.0 @classmethod def from_file(cls, path: str) -> LinkConfig: @@ -173,7 +210,14 @@ def from_file(cls, path: str) -> LinkConfig: @classmethod def perfect_config(cls, stack1: str, stack2: str) -> LinkConfig: - return LinkConfig(stack1=stack1, stack2=stack2, typ="perfect", cfg=None) + return LinkConfig( + stack1=stack1, + stack2=stack2, + typ="perfect", + cfg=None, + host_host_latency=0.0, + qnos_qnos_latency=0.0, + ) class StackNetworkConfig(BaseModel): diff --git a/squidasm/run/stack/run.py b/squidasm/run/stack/run.py index 9770864d..72c7c121 100644 --- a/squidasm/run/stack/run.py +++ b/squidasm/run/stack/run.py @@ -8,6 +8,7 @@ MagicLinkLayerProtocol, MagicLinkLayerProtocolWithSignaling, SingleClickTranslationUnit, + TranslationUnit, ) from netsquid_magic.magic_distributor import ( DepolariseWithFailureMagicDistributor, @@ -32,6 +33,28 @@ from squidasm.sim.stack.stack import NodeStack, StackNetwork +class EmptyTranslationUnit(TranslationUnit): + def __call__(self, request, **fixed_parameters): + return self.request_to_parameters(request, **fixed_parameters) + + def request_to_parameters(self, request, **fixed_parameters): + return {} + + +class NVMagicDistributor(NVSingleClickMagicDistributor): + def get_bell_state(self, midpoint_outcome): + try: + status, label = midpoint_outcome + except ValueError: + raise ValueError("Unknown midpoint outcome {}".format(midpoint_outcome)) + if status != "success": + raise ValueError("Unknown midpoint outcome {}".format(midpoint_outcome)) + return label + + def _read_bright_state_parameter_from_nodes(self): + return {} + + def fidelity_to_prob_max_mixed(fid: float) -> float: return (1 - fid) * 4.0 / 3.0 @@ -49,22 +72,35 @@ def _setup_network(config: StackNetworkConfig) -> StackNetwork: if not isinstance(qdevice_cfg, NVQDeviceConfig): qdevice_cfg = NVQDeviceConfig(**cfg.qdevice_cfg) qdevice = build_nv_qdevice(f"qdevice_{cfg.name}", cfg=qdevice_cfg) - stack = NodeStack(cfg.name, qdevice_type="nv", qdevice=qdevice) + stack = NodeStack( + cfg.name, + qdevice_type="nv", + qdevice=qdevice, + host_qnos_latency=cfg.host_qnos_latency, + ) elif cfg.qdevice_typ == "generic": qdevice_cfg = cfg.qdevice_cfg if not isinstance(qdevice_cfg, GenericQDeviceConfig): qdevice_cfg = GenericQDeviceConfig(**cfg.qdevice_cfg) qdevice = build_generic_qdevice(f"qdevice_{cfg.name}", cfg=qdevice_cfg) - stack = NodeStack(cfg.name, qdevice_type="generic", qdevice=qdevice) + stack = NodeStack( + cfg.name, + qdevice_type="generic", + qdevice=qdevice, + host_qnos_latency=cfg.host_qnos_latency, + ) NetSquidContext.add_node(stack.node.ID, cfg.name) stacks[cfg.name] = stack - for (_, s1), (_, s2) in itertools.combinations(stacks.items(), 2): - s1.connect_to(s2) - for link in config.links: stack1 = stacks[link.stack1] stack2 = stacks[link.stack2] + stack1.connect_to( + stack2, + host_host_delay=link.host_host_latency, + qnos_qnos_delay=link.qnos_qnos_latency, + ) + if link.typ == "perfect": link_dist = PerfectStateMagicDistributor( nodes=[stack1.node, stack2.node], state_delay=1000.0 @@ -84,14 +120,30 @@ def _setup_network(config: StackNetworkConfig) -> StackNetwork: link_cfg = link.cfg if not isinstance(link_cfg, NVLinkConfig): link_cfg = NVLinkConfig(**link.cfg) - link_dist = NVSingleClickMagicDistributor( + # link_dist = NVSingleClickMagicDistributor( + link_dist = NVMagicDistributor( nodes=[stack1.node, stack2.node], + cycle_time=link_cfg.cycle_time, length_A=link_cfg.length_A, length_B=link_cfg.length_B, - full_cycle=link_cfg.full_cycle, - cycle_time=link_cfg.cycle_time, - alpha=link_cfg.alpha, + p_loss_init_A=link_cfg.p_loss_init_A, + p_loss_length_A=link_cfg.p_loss_length_A, + speed_of_light_A=link_cfg.speed_of_light_A, + p_loss_init_B=link_cfg.p_loss_init_B, + p_loss_length_B=link_cfg.p_loss_length_B, + speed_of_light_B=link_cfg.speed_of_light_B, + dark_count_probability=link_cfg.dark_count_probability, + detector_efficiency=link_cfg.detector_efficiency, + visibility=link_cfg.visibility, + num_resolving=link_cfg.num_resolving, + std_electron_electron_phase_drift=link_cfg.std_electron_electron_phase_drift, + coherent_phase=link_cfg.coherent_phase, + p_double_exc=link_cfg.p_double_exc, + p_fail_class_corr=link_cfg.p_fail_class_corr, ) + # link_dist.fixed_delivery_parameters[0]["cycle_time"] = 1337 + link_dist.fixed_delivery_parameters[0]["alpha_A"] = link_cfg.alpha_A + link_dist.fixed_delivery_parameters[0]["alpha_B"] = link_cfg.alpha_B elif link.typ == "heralded": link_cfg = link.cfg if not isinstance(link_cfg, HeraldedLinkConfig): @@ -105,10 +157,15 @@ def _setup_network(config: StackNetworkConfig) -> StackNetwork: else: raise ValueError + if link.typ == "nv": + translation_unit = EmptyTranslationUnit() + else: + translation_unit = SingleClickTranslationUnit() + link_prot = MagicLinkLayerProtocolWithSignaling( nodes=[stack1.node, stack2.node], magic_distributor=link_dist, - translation_unit=SingleClickTranslationUnit(), + translation_unit=translation_unit, ) stack1.assign_ll_protocol(link_prot) stack2.assign_ll_protocol(link_prot) diff --git a/squidasm/sim/stack/common.py b/squidasm/sim/stack/common.py index 8fb2aab2..10693167 100644 --- a/squidasm/sim/stack/common.py +++ b/squidasm/sim/stack/common.py @@ -6,7 +6,9 @@ from netqasm.lang import operand from netqasm.lang.encoding import RegisterName from netqasm.sdk.shared_memory import Arrays, RegisterGroup, setup_registers +from netsquid.components.channel import Channel from netsquid.components.component import Component, Port +from netsquid.nodes.connections import DirectConnection from netsquid.protocols import Protocol from pydynaa import EventExpression @@ -391,3 +393,10 @@ class NVPhysicalQuantumMemory(PhysicalQuantumMemory): def __init__(self, qubit_count: int) -> None: super().__init__(qubit_count) self._comm_qubit_ids: Set[int] = {0} + + +class DelayedClassicalConnection(DirectConnection): + def __init__(self, name: str, delay: float) -> None: + chan1 = Channel(f"chan_1_{name}", delay=delay) + chan2 = Channel(f"chan_2_{name}", delay=delay) + super().__init__(name, channel_AtoB=chan1, channel_BtoA=chan2) diff --git a/squidasm/sim/stack/handler.py b/squidasm/sim/stack/handler.py index c8b1beea..52bf8e71 100644 --- a/squidasm/sim/stack/handler.py +++ b/squidasm/sim/stack/handler.py @@ -53,24 +53,16 @@ class HandlerComponent(Component): def __init__(self, node: Node) -> None: super().__init__(f"{node.name}_handler") self._node = node - self.add_ports(["proc_out", "proc_in"]) - self.add_ports(["host_out", "host_in"]) + self.add_ports(["proc"]) + self.add_ports(["host"]) @property - def processor_in_port(self) -> Port: - return self.ports["proc_in"] + def processor_port(self) -> Port: + return self.ports["proc"] @property - def processor_out_port(self) -> Port: - return self.ports["proc_out"] - - @property - def host_in_port(self) -> Port: - return self.ports["host_in"] - - @property - def host_out_port(self) -> Port: - return self.ports["host_out"] + def host_port(self) -> Port: + return self.ports["host"] @property def node(self) -> Node: @@ -125,11 +117,11 @@ def __init__( self.add_listener( "host", - PortListener(self._comp.ports["host_in"], SIGNAL_HOST_HAND_MSG), + PortListener(self._comp.ports["host"], SIGNAL_HOST_HAND_MSG), ) self.add_listener( "processor", - PortListener(self._comp.ports["proc_in"], SIGNAL_PROC_HAND_MSG), + PortListener(self._comp.ports["proc"], SIGNAL_PROC_HAND_MSG), ) # Number of applications that were handled so far. Used as a unique ID for @@ -176,13 +168,13 @@ def flavour(self, flavour: Optional[flavour.Flavour]) -> None: self._flavour = flavour def _send_host_msg(self, msg: Any) -> None: - self._comp.host_out_port.tx_output(msg) + self._comp.host_port.tx_output(msg) def _receive_host_msg(self) -> Generator[EventExpression, None, str]: return (yield from self._receive_msg("host", SIGNAL_HOST_HAND_MSG)) def _send_processor_msg(self, msg: str) -> None: - self._comp.processor_out_port.tx_output(msg) + self._comp.processor_port.tx_output(msg) def _receive_processor_msg(self) -> Generator[EventExpression, None, str]: return (yield from self._receive_msg("processor", SIGNAL_PROC_HAND_MSG)) diff --git a/squidasm/sim/stack/host.py b/squidasm/sim/stack/host.py index 4e22c305..ee6167ef 100644 --- a/squidasm/sim/stack/host.py +++ b/squidasm/sim/stack/host.py @@ -32,24 +32,16 @@ class HostComponent(Component): def __init__(self, node: Node) -> None: super().__init__(f"{node.name}_host") - self.add_ports(["qnos_in", "qnos_out"]) - self.add_ports(["peer_in", "peer_out"]) + self.add_ports(["qnos"]) + self.add_ports(["peer"]) @property - def qnos_in_port(self) -> Port: - return self.ports["qnos_in"] + def qnos_port(self) -> Port: + return self.ports["qnos"] @property - def qnos_out_port(self) -> Port: - return self.ports["qnos_out"] - - @property - def peer_in_port(self) -> Port: - return self.ports["peer_in"] - - @property - def peer_out_port(self) -> Port: - return self.ports["peer_out"] + def peer_port(self) -> Port: + return self.ports["peer"] class Host(ComponentProtocol): @@ -66,11 +58,11 @@ def __init__(self, comp: HostComponent, qdevice_type: Optional[str] = "nv") -> N self.add_listener( "qnos", - PortListener(self._comp.ports["qnos_in"], SIGNAL_HAND_HOST_MSG), + PortListener(self._comp.ports["qnos"], SIGNAL_HAND_HOST_MSG), ) self.add_listener( "peer", - PortListener(self._comp.ports["peer_in"], SIGNAL_HOST_HOST_MSG), + PortListener(self._comp.ports["peer"], SIGNAL_HOST_HOST_MSG), ) if qdevice_type == "nv": @@ -100,13 +92,13 @@ def compiler(self, typ: Optional[Type[SubroutineTranspiler]]) -> None: self._compiler = typ def send_qnos_msg(self, msg: bytes) -> None: - self._comp.qnos_out_port.tx_output(msg) + self._comp.qnos_port.tx_output(msg) def receive_qnos_msg(self) -> Generator[EventExpression, None, str]: return (yield from self._receive_msg("qnos", SIGNAL_HAND_HOST_MSG)) def send_peer_msg(self, msg: str) -> None: - self._comp.peer_out_port.tx_output(msg) + self._comp.peer_port.tx_output(msg) def receive_peer_msg(self) -> Generator[EventExpression, None, str]: return (yield from self._receive_msg("peer", SIGNAL_HOST_HOST_MSG)) diff --git a/squidasm/sim/stack/netstack.py b/squidasm/sim/stack/netstack.py index c12c17bf..54e7f857 100644 --- a/squidasm/sim/stack/netstack.py +++ b/squidasm/sim/stack/netstack.py @@ -76,24 +76,16 @@ class NetstackComponent(Component): def __init__(self, node: Node) -> None: super().__init__(f"{node.name}_netstack") self._node = node - self.add_ports(["proc_out", "proc_in"]) - self.add_ports(["peer_out", "peer_in"]) + self.add_ports(["proc"]) + self.add_ports(["peer"]) @property - def processor_in_port(self) -> Port: - return self.ports["proc_in"] + def processor_port(self) -> Port: + return self.ports["proc"] @property - def processor_out_port(self) -> Port: - return self.ports["proc_out"] - - @property - def peer_in_port(self) -> Port: - return self.ports["peer_in"] - - @property - def peer_out_port(self) -> Port: - return self.ports["peer_out"] + def peer_port(self) -> Port: + return self.ports["peer"] @property def node(self) -> Node: @@ -115,7 +107,9 @@ class EprSocket: class Netstack(ComponentProtocol): """NetSquid protocol representing the QNodeOS network stack.""" - def __init__(self, comp: NetstackComponent, qnos: Qnos) -> None: + def __init__( + self, comp: NetstackComponent, qnos: Qnos, correct_bell_states: bool = False + ) -> None: """Network stack protocol constructor. Typically created indirectly through constructing a `Qnos` instance. @@ -126,13 +120,15 @@ def __init__(self, comp: NetstackComponent, qnos: Qnos) -> None: self._comp = comp self._qnos = qnos + self._correct_bell_states = correct_bell_states + self.add_listener( "processor", - PortListener(self._comp.processor_in_port, SIGNAL_PROC_NSTK_MSG), + PortListener(self._comp.processor_port, SIGNAL_PROC_NSTK_MSG), ) self.add_listener( "peer", - PortListener(self._comp.peer_in_port, SIGNAL_PEER_NSTK_MSG), + PortListener(self._comp.peer_port, SIGNAL_PEER_NSTK_MSG), ) self._egp: Optional[EgpProtocol] = None @@ -159,7 +155,7 @@ def open_epr_socket(self, app_id: int, socket_id: int, remote_node_id: int) -> N def _send_processor_msg(self, msg: str) -> None: """Send a message to the processor.""" - self._comp.processor_out_port.tx_output(msg) + self._comp.processor_port.tx_output(msg) def _receive_processor_msg(self) -> Generator[EventExpression, None, str]: """Receive a message from the processor. Block until there is at least one @@ -170,7 +166,7 @@ def _send_peer_msg(self, msg: str) -> None: """Send a message to the network stack of the other node. NOTE: for now we assume there is only one other node, which is 'the' peer.""" - self._comp.peer_out_port.tx_output(msg) + self._comp.peer_port.tx_output(msg) def _receive_peer_msg(self) -> Generator[EventExpression, None, str]: """Receive a message from the network stack of the other node. Block until @@ -336,23 +332,25 @@ def handle_create_ck_request( ResCreateAndKeep.__name__, receiver=self ) self._logger.info(f"got result for pair {pair_index}: {result}") - - # Bell state corrections. Resulting state is always Phi+ (i.e. B00). - if result.bell_state == BellIndex.B00: - pass - elif result.bell_state == BellIndex.B01: - prog = QuantumProgram() - prog.apply(INSTR_ROT_X, qubit_indices=[0], angle=PI) - yield self.qdevice.execute_program(prog) - elif result.bell_state == BellIndex.B10: - prog = QuantumProgram() - prog.apply(INSTR_ROT_Z, qubit_indices=[0], angle=PI) - yield self.qdevice.execute_program(prog) - elif result.bell_state == BellIndex.B11: - prog = QuantumProgram() - prog.apply(INSTR_ROT_X, qubit_indices=[0], angle=PI) - prog.apply(INSTR_ROT_Z, qubit_indices=[0], angle=PI) - yield self.qdevice.execute_program(prog) + self._logger.info(f"bell state: {result.bell_state}") + + if self._correct_bell_states: + # Bell state corrections. Resulting state is always Phi+ (i.e. B00). + if result.bell_state == BellIndex.B00: + pass + elif result.bell_state == BellIndex.B01: + prog = QuantumProgram() + prog.apply(INSTR_ROT_X, qubit_indices=[0], angle=PI) + yield self.qdevice.execute_program(prog) + elif result.bell_state == BellIndex.B10: + prog = QuantumProgram() + prog.apply(INSTR_ROT_Z, qubit_indices=[0], angle=PI) + yield self.qdevice.execute_program(prog) + elif result.bell_state == BellIndex.B11: + prog = QuantumProgram() + prog.apply(INSTR_ROT_X, qubit_indices=[0], angle=PI) + prog.apply(INSTR_ROT_Z, qubit_indices=[0], angle=PI) + yield self.qdevice.execute_program(prog) virt_id = app_mem.get_array_value(req.qubit_array_addr, pair_index) app_mem.map_virt_id(virt_id, phys_id) diff --git a/squidasm/sim/stack/processor.py b/squidasm/sim/stack/processor.py index 0e912eea..506a4e01 100644 --- a/squidasm/sim/stack/processor.py +++ b/squidasm/sim/stack/processor.py @@ -72,24 +72,16 @@ class ProcessorComponent(Component): def __init__(self, node: Node) -> None: super().__init__(f"{node.name}_processor") self._node = node - self.add_ports(["nstk_out", "nstk_in"]) - self.add_ports(["hand_out", "hand_in"]) + self.add_ports(["nstk"]) + self.add_ports(["hand"]) @property - def netstack_in_port(self) -> Port: - return self.ports["nstk_in"] + def netstack_port(self) -> Port: + return self.ports["nstk"] @property - def netstack_out_port(self) -> Port: - return self.ports["nstk_out"] - - @property - def handler_in_port(self) -> Port: - return self.ports["hand_in"] - - @property - def handler_out_port(self) -> Port: - return self.ports["hand_out"] + def handler_port(self) -> Port: + return self.ports["hand"] @property def qdevice(self) -> QuantumProcessor: @@ -116,11 +108,11 @@ def __init__(self, comp: ProcessorComponent, qnos: Qnos) -> None: self.add_listener( "handler", - PortListener(self._comp.ports["hand_in"], SIGNAL_HAND_PROC_MSG), + PortListener(self._comp.ports["hand"], SIGNAL_HAND_PROC_MSG), ) self.add_listener( "netstack", - PortListener(self._comp.ports["nstk_in"], SIGNAL_NSTK_PROC_MSG), + PortListener(self._comp.ports["nstk"], SIGNAL_NSTK_PROC_MSG), ) self.add_signal(SIGNAL_MEMORY_FREED) @@ -141,13 +133,13 @@ def qdevice(self) -> QuantumProcessor: return self._comp.qdevice def _send_handler_msg(self, msg: str) -> None: - self._comp.handler_out_port.tx_output(msg) + self._comp.handler_port.tx_output(msg) def _receive_handler_msg(self) -> Generator[EventExpression, None, str]: return (yield from self._receive_msg("handler", SIGNAL_HAND_PROC_MSG)) def _send_netstack_msg(self, msg: str) -> None: - self._comp.netstack_out_port.tx_output(msg) + self._comp.netstack_port.tx_output(msg) def _receive_netstack_msg(self) -> Generator[EventExpression, None, str]: return (yield from self._receive_msg("netstack", SIGNAL_NSTK_PROC_MSG)) diff --git a/squidasm/sim/stack/qnos.py b/squidasm/sim/stack/qnos.py index 23150be4..c136f449 100644 --- a/squidasm/sim/stack/qnos.py +++ b/squidasm/sim/stack/qnos.py @@ -41,10 +41,10 @@ def __init__(self, node: Node) -> None: self._node = node # Ports for communicating with Host - self.add_ports(["host_out", "host_in"]) + self.add_ports(["host"]) # Ports for communicating with other nodes - self.add_ports(["peer_out", "peer_in"]) + self.add_ports(["peer"]) comp_handler = HandlerComponent(node) self.add_subcomponent(comp_handler, "handler") @@ -55,25 +55,14 @@ def __init__(self, node: Node) -> None: comp_netstack = NetstackComponent(node) self.add_subcomponent(comp_netstack, "netstack") - self.netstack_comp.ports["peer_out"].forward_output(self.peer_out_port) - self.peer_in_port.forward_input(self.netstack_comp.ports["peer_in"]) + self.netstack_comp.peer_port.forward_output(self.peer_port) + self.peer_port.forward_input(self.netstack_comp.peer_port) - self.handler_comp.ports["host_out"].forward_output(self.host_out_port) - self.host_in_port.forward_input(self.handler_comp.ports["host_in"]) + self.handler_comp.host_port.forward_output(self.host_port) + self.host_port.forward_input(self.handler_comp.host_port) - self.handler_comp.processor_out_port.connect( - self.processor_comp.handler_in_port - ) - self.handler_comp.processor_in_port.connect( - self.processor_comp.handler_out_port - ) - - self.processor_comp.netstack_out_port.connect( - self.netstack_comp.processor_in_port - ) - self.processor_comp.netstack_in_port.connect( - self.netstack_comp.processor_out_port - ) + self.handler_comp.processor_port.connect(self.processor_comp.handler_port) + self.processor_comp.netstack_port.connect(self.netstack_comp.processor_port) @property def handler_comp(self) -> HandlerComponent: @@ -92,20 +81,12 @@ def qdevice(self) -> QuantumProcessor: return self.node.qmemory @property - def host_in_port(self) -> Port: - return self.ports["host_in"] - - @property - def host_out_port(self) -> Port: - return self.ports["host_out"] - - @property - def peer_in_port(self) -> Port: - return self.ports["peer_in"] + def host_port(self) -> Port: + return self.ports["host"] @property - def peer_out_port(self) -> Port: - return self.ports["peer_out"] + def peer_port(self) -> Port: + return self.ports["peer"] @property def node(self) -> Node: @@ -115,7 +96,12 @@ def node(self) -> Node: class Qnos(Protocol): """NetSquid protocol representing a QNodeOS instance.""" - def __init__(self, comp: QnosComponent, qdevice_type: Optional[str] = "nv") -> None: + def __init__( + self, + comp: QnosComponent, + qdevice_type: Optional[str] = "nv", + correct_bell_states: bool = False, + ) -> None: """Qnos protocol constructor. :param comp: NetSquid component representing the QNodeOS instance @@ -126,7 +112,7 @@ def __init__(self, comp: QnosComponent, qdevice_type: Optional[str] = "nv") -> N # Create internal protocols. self.handler = Handler(comp.handler_comp, self, qdevice_type) - self.netstack = Netstack(comp.netstack_comp, self) + self.netstack = Netstack(comp.netstack_comp, self, correct_bell_states) if qdevice_type == "generic": self.processor = GenericProcessor(comp.processor_comp, self) self._physical_memory = PhysicalQuantumMemory(comp.qdevice.num_positions) diff --git a/squidasm/sim/stack/stack.py b/squidasm/sim/stack/stack.py index 8a397ef3..ee1934c3 100644 --- a/squidasm/sim/stack/stack.py +++ b/squidasm/sim/stack/stack.py @@ -12,6 +12,7 @@ MagicLinkLayerProtocolWithSignaling, ) +from squidasm.sim.stack.common import DelayedClassicalConnection from squidasm.sim.stack.host import Host, HostComponent from squidasm.sim.stack.qnos import Qnos, QnosComponent @@ -39,6 +40,7 @@ def __init__( name: str, qdevice: QuantumProcessor, node_id: Optional[int] = None, + host_qnos_latency: float = 0.0, ) -> None: """ProcessingNode constructor. Typically created indirectly through constructing a `NodeStack`.""" @@ -51,17 +53,23 @@ def __init__( host_comp = HostComponent(self) self.add_subcomponent(host_comp, "host") - self.host_comp.ports["qnos_out"].connect(self.qnos_comp.ports["host_in"]) - self.host_comp.ports["qnos_in"].connect(self.qnos_comp.ports["host_out"]) + host_qnos_connection = DelayedClassicalConnection( + f"host_qnos_{self.name}", host_qnos_latency + ) + self.add_subcomponent(host_qnos_connection, "host_qnos_connection") + + self.host_comp.qnos_port.connect(host_qnos_connection.port_A) + self.qnos_comp.host_port.connect(host_qnos_connection.port_B) # Ports for communicating with other nodes - self.add_ports(["qnos_peer_out", "qnos_peer_in"]) - self.add_ports(["host_peer_out", "host_peer_in"]) + self.add_ports(["qnos_peer"]) + self.add_ports(["host_peer"]) + + self.qnos_comp.peer_port.forward_output(self.qnos_peer_port) + self.qnos_peer_port.forward_input(self.qnos_comp.peer_port) - self.qnos_comp.peer_out_port.forward_output(self.qnos_peer_out_port) - self.qnos_peer_in_port.forward_input(self.qnos_comp.peer_in_port) - self.host_comp.peer_out_port.forward_output(self.host_peer_out_port) - self.host_peer_in_port.forward_input(self.host_comp.peer_in_port) + self.host_comp.peer_port.forward_output(self.host_peer_port) + self.host_peer_port.forward_input(self.host_comp.peer_port) @property def qnos_comp(self) -> QnosComponent: @@ -76,20 +84,12 @@ def qdevice(self) -> QuantumProcessor: return self.qmemory @property - def host_peer_in_port(self) -> Port: - return self.ports["host_peer_in"] + def host_peer_port(self) -> Port: + return self.ports["host_peer"] @property - def host_peer_out_port(self) -> Port: - return self.ports["host_peer_out"] - - @property - def qnos_peer_in_port(self) -> Port: - return self.ports["qnos_peer_in"] - - @property - def qnos_peer_out_port(self) -> Port: - return self.ports["qnos_peer_out"] + def qnos_peer_port(self) -> Port: + return self.ports["qnos_peer"] class NodeStack(Protocol): @@ -107,7 +107,9 @@ def __init__( qdevice_type: Optional[str] = "generic", qdevice: Optional[QuantumProcessor] = None, node_id: Optional[int] = None, + host_qnos_latency: float = 0.0, use_default_components: bool = True, + qnos_corrects_bell_states: bool = False, ) -> None: """NodeStack constructor. @@ -129,7 +131,7 @@ def __init__( self._node = node else: assert qdevice is not None - self._node = ProcessingNode(name, qdevice, node_id) + self._node = ProcessingNode(name, qdevice, node_id, host_qnos_latency) self._host: Optional[Host] = None self._qnos: Optional[Qnos] = None @@ -139,7 +141,7 @@ def __init__( # created and added to this NodeStack. if use_default_components: self._host = Host(self.host_comp, qdevice_type) - self._qnos = Qnos(self.qnos_comp, qdevice_type) + self._qnos = Qnos(self.qnos_comp, qdevice_type, qnos_corrects_bell_states) def assign_ll_protocol(self, prot: MagicLinkLayerProtocolWithSignaling) -> None: """Set the link layer protocol to use for entanglement generation. @@ -180,13 +182,26 @@ def qnos(self) -> Qnos: def qnos(self, qnos: Qnos) -> None: self._qnos = qnos - def connect_to(self, other: NodeStack) -> None: + def connect_to( + self, + other: NodeStack, + host_host_delay: float = 1e6, + qnos_qnos_delay: float = 1e6, + ) -> None: """Create connections between ports of this NodeStack and those of another NodeStack.""" - self.node.host_peer_out_port.connect(other.node.host_peer_in_port) - self.node.host_peer_in_port.connect(other.node.host_peer_out_port) - self.node.qnos_peer_out_port.connect(other.node.qnos_peer_in_port) - self.node.qnos_peer_in_port.connect(other.node.qnos_peer_out_port) + + host_host_connection = DelayedClassicalConnection( + f"host_host_{self.node.name}_{other.node.name}", host_host_delay + ) + self.node.host_peer_port.connect(host_host_connection.port_A) + other.node.host_peer_port.connect(host_host_connection.port_B) + + qnos_qnos_connection = DelayedClassicalConnection( + f"qnos_qnos_{self.node.name}_{other.node.name}", qnos_qnos_delay + ) + self.node.qnos_peer_port.connect(qnos_qnos_connection.port_A) + other.node.qnos_peer_port.connect(qnos_qnos_connection.port_B) def start(self) -> None: assert self._host is not None diff --git a/tests/stack/test_handler.py b/tests/stack/test_handler.py index ab81e0f0..047920cc 100644 --- a/tests/stack/test_handler.py +++ b/tests/stack/test_handler.py @@ -2,18 +2,17 @@ from typing import Generator, Optional, Type import netsquid as ns +import pytest from netqasm.lang.parsing import parse_text_subroutine from netsquid.components import QuantumProcessor from netsquid.qubits import ketstates, qubitapi -from netsquid_magic.link_layer import ( - MagicLinkLayerProtocolWithSignaling, - SingleClickTranslationUnit, -) +from netsquid_magic.link_layer import MagicLinkLayerProtocolWithSignaling from netsquid_nv.magic_distributor import NVSingleClickMagicDistributor from pydynaa import EventExpression from squidasm.run.stack.build import build_nv_qdevice from squidasm.run.stack.config import NVQDeviceConfig +from squidasm.run.stack.run import EmptyTranslationUnit from squidasm.sim.stack.handler import Handler from squidasm.sim.stack.qnos import Qnos from squidasm.sim.stack.stack import NodeStack @@ -38,13 +37,14 @@ def setUp(self) -> None: nodes=[self._alice.node, self._bob.node], length_A=0.001, length_B=0.001, - full_cycle=0.001, - t_cycle=10, + cycle_time=10, ) + self._link_dist.fixed_delivery_parameters[0]["alpha_A"] = 1e-3 + self._link_dist.fixed_delivery_parameters[0]["alpha_B"] = 1e-3 self._link_prot = MagicLinkLayerProtocolWithSignaling( nodes=[self._alice.node, self._bob.node], magic_distributor=self._link_dist, - translation_unit=SingleClickTranslationUnit(), + translation_unit=EmptyTranslationUnit(), ) self._alice.assign_ll_protocol(self._link_prot) @@ -66,6 +66,7 @@ def tearDown(self) -> None: if self._check_cmem: self._check_cmem(self._alice.qnos.app_memories, self._bob.qnos.app_memories) + @pytest.mark.filterwarnings("ignore::UserWarning") def test_entangle_ck(self): SUBRT_1 = """ # NETQASM 1.0 diff --git a/tests/stack/test_processor.py b/tests/stack/test_processor.py index e69ad7e9..241c56d5 100644 --- a/tests/stack/test_processor.py +++ b/tests/stack/test_processor.py @@ -2,19 +2,18 @@ from typing import Dict, Generator import netsquid as ns +import pytest from netqasm.lang.instr.flavour import NVFlavour from netqasm.lang.parsing import parse_text_subroutine from netsquid.components import QuantumProcessor from netsquid.qubits import ketstates, qubitapi -from netsquid_magic.link_layer import ( - MagicLinkLayerProtocolWithSignaling, - SingleClickTranslationUnit, -) +from netsquid_magic.link_layer import MagicLinkLayerProtocolWithSignaling from netsquid_nv.magic_distributor import NVSingleClickMagicDistributor from pydynaa import EventExpression from squidasm.run.stack.build import build_nv_qdevice from squidasm.run.stack.config import NVQDeviceConfig +from squidasm.run.stack.run import EmptyTranslationUnit from squidasm.sim.stack.common import AppMemory from squidasm.sim.stack.processor import NVProcessor from squidasm.sim.stack.qnos import Qnos @@ -44,13 +43,14 @@ def setUp(self) -> None: nodes=[self._alice.node, self._bob.node], length_A=0.001, length_B=0.001, - full_cycle=0.001, - t_cycle=10, + cycle_time=10, ) + self._link_dist.fixed_delivery_parameters[0]["alpha_A"] = 1e-3 + self._link_dist.fixed_delivery_parameters[0]["alpha_B"] = 1e-3 self._link_prot = MagicLinkLayerProtocolWithSignaling( nodes=[self._alice.node, self._bob.node], magic_distributor=self._link_dist, - translation_unit=SingleClickTranslationUnit(), + translation_unit=EmptyTranslationUnit(), ) self._alice.assign_ll_protocol(self._link_prot) @@ -68,6 +68,7 @@ def tearDown(self) -> None: if self._check_cmem: self._check_cmem(self._alice.qnos.app_memories, self._bob.qnos.app_memories) + @pytest.mark.filterwarnings("ignore::UserWarning") def test_entangle_ck(self): APP_ID = 0 diff --git a/tests/stack/test_two_nodes.py b/tests/stack/test_two_nodes.py index 95d6e044..1dea0434 100644 --- a/tests/stack/test_two_nodes.py +++ b/tests/stack/test_two_nodes.py @@ -2,6 +2,7 @@ from typing import Dict, Generator, Optional, Type import netsquid as ns +import pytest from netqasm.backend.messages import ( InitNewAppMessage, OpenEPRSocketMessage, @@ -12,15 +13,13 @@ from netqasm.lang.parsing import parse_text_subroutine from netsquid.components import QuantumProcessor from netsquid.qubits import ketstates, qubitapi -from netsquid_magic.link_layer import ( - MagicLinkLayerProtocolWithSignaling, - SingleClickTranslationUnit, -) +from netsquid_magic.link_layer import MagicLinkLayerProtocolWithSignaling from netsquid_nv.magic_distributor import NVSingleClickMagicDistributor from pydynaa import EventExpression from squidasm.run.stack.build import build_nv_qdevice from squidasm.run.stack.config import NVQDeviceConfig +from squidasm.run.stack.run import EmptyTranslationUnit from squidasm.sim.stack.common import AppMemory from squidasm.sim.stack.host import Host from squidasm.sim.stack.processor import NVProcessor @@ -34,10 +33,13 @@ def setUp(self) -> None: nv_cfg = NVQDeviceConfig.perfect_config() alice_qdevice = build_nv_qdevice("nv_qdevice_alice", cfg=nv_cfg) self._alice = NodeStack( - "alice", qdevice_type="nv", qdevice=alice_qdevice, node_id=0 + "alice", + qdevice_type="nv", + qdevice=alice_qdevice, + node_id=0, ) self._alice.host = Host(self._alice.host_comp) - self._alice.qnos = Qnos(self._alice.qnos_comp) + self._alice.qnos = Qnos(self._alice.qnos_comp, correct_bell_states=True) self._alice.qnos.processor = NVProcessor( self._alice.qnos_comp.processor_comp, self._alice.qnos ) @@ -45,9 +47,14 @@ def setUp(self) -> None: self._alice.qnos.handler.should_clear_memory = False bob_qdevice = build_nv_qdevice("nv_qdevice_bob", cfg=nv_cfg) - self._bob = NodeStack("bob", qdevice_type="nv", qdevice=bob_qdevice, node_id=1) + self._bob = NodeStack( + "bob", + qdevice_type="nv", + qdevice=bob_qdevice, + node_id=1, + ) self._bob.host = Host(self._bob.host_comp) - self._bob.qnos = Qnos(self._bob.qnos_comp) + self._bob.qnos = Qnos(self._bob.qnos_comp, correct_bell_states=True) self._bob.qnos.processor = NVProcessor( self._bob.qnos_comp.processor_comp, self._bob.qnos ) @@ -63,13 +70,14 @@ def setUp(self) -> None: nodes=[self._alice.node, self._bob.node], length_A=0.001, length_B=0.001, - full_cycle=0.001, - t_cycle=10, + cycle_time=10, ) + self._link_dist.fixed_delivery_parameters[0]["alpha_A"] = 1e-3 + self._link_dist.fixed_delivery_parameters[0]["alpha_B"] = 1e-3 self._link_prot = MagicLinkLayerProtocolWithSignaling( nodes=[self._alice.node, self._bob.node], magic_distributor=self._link_dist, - translation_unit=SingleClickTranslationUnit(), + translation_unit=EmptyTranslationUnit(), ) self._alice.assign_ll_protocol(self._link_prot) @@ -91,6 +99,7 @@ def tearDown(self) -> None: if self._check_cmem: self._check_cmem(self._alice.qnos.app_memories, self._bob.qnos.app_memories) + @pytest.mark.filterwarnings("ignore::UserWarning") def test_entangle_ck(self): SUBRT_1 = """ # NETQASM 1.0 @@ -169,6 +178,7 @@ def check_qmem( self._check_qmem = check_qmem self._check_cmem = None + @pytest.mark.filterwarnings("ignore::UserWarning") def test_entangle_ck_with_measure(self): SUBRT_1 = """ # NETQASM 1.0 @@ -298,6 +308,7 @@ def check_cmem( self._check_qmem = None self._check_cmem = check_cmem + @pytest.mark.filterwarnings("ignore::UserWarning") def test_entangle_md(self): SUBRT_1 = """ # NETQASM 1.0 @@ -389,6 +400,7 @@ def check_cmem( self._check_qmem = None self._check_cmem = check_cmem + @pytest.mark.filterwarnings("ignore::UserWarning") def test_entangle_md_with_wait(self): SUBRT_1 = """ # NETQASM 1.0 @@ -490,6 +502,7 @@ def check_cmem( self._check_qmem = None self._check_cmem = check_cmem + @pytest.mark.filterwarnings("ignore::UserWarning") def test_entangle_ck_with_post_routine(self): SUBRT_1 = """ # NETQASM 1.0