diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9273e205..787b21f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -172,3 +172,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed
- Removed support for scipy version 1.11. This is currently causing some issues with qutip.
+
+## [0.6.2]
+### Added
+- Moved around and added a few files in the `examples` folder
+ - These are primarily for the IEEE QCE 2023 conference
+
+### Changed
+- Modified the topology output of the GUI to be compatible with new topology upgrades
+- Several bug fixes in the GUI
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 808060f0..bdee7c97 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -22,7 +22,7 @@
author = 'Xiaoliang Wu, Joaquin Chung, Alexander Kolar, Eugene Wang, Tian Zhong, Rajkumar Kettimuthu, Martin Suchara'
# The full version, including alpha/beta/rc tags
-release = '0.6.1'
+release = '0.6.2'
# -- General configuration ---------------------------------------------------
diff --git a/example/QCE_demos/GUI_and_code_demo.ipynb b/example/QCE_demos_2022/GUI_and_code_demo.ipynb
similarity index 100%
rename from example/QCE_demos/GUI_and_code_demo.ipynb
rename to example/QCE_demos_2022/GUI_and_code_demo.ipynb
diff --git a/example/QCE_demos/Teleport_demo.ipynb b/example/QCE_demos_2022/Teleport_demo.ipynb
similarity index 100%
rename from example/QCE_demos/Teleport_demo.ipynb
rename to example/QCE_demos_2022/Teleport_demo.ipynb
diff --git a/example/QCE_demos/Teleport_demo_filled.ipynb b/example/QCE_demos_2022/Teleport_demo_filled.ipynb
similarity index 100%
rename from example/QCE_demos/Teleport_demo_filled.ipynb
rename to example/QCE_demos_2022/Teleport_demo_filled.ipynb
diff --git a/example/QCE_demos/images/star_network.png b/example/QCE_demos_2022/images/star_network.png
similarity index 100%
rename from example/QCE_demos/images/star_network.png
rename to example/QCE_demos_2022/images/star_network.png
diff --git a/example/QCE_demos/modify_script_method_function.ipynb b/example/QCE_demos_2022/modify_script_method_function.ipynb
similarity index 100%
rename from example/QCE_demos/modify_script_method_function.ipynb
rename to example/QCE_demos_2022/modify_script_method_function.ipynb
diff --git a/example/QCE_demos/star_network.json b/example/QCE_demos_2022/star_network.json
similarity index 100%
rename from example/QCE_demos/star_network.json
rename to example/QCE_demos_2022/star_network.json
diff --git a/example/QCE_demos/star_network_modified.json b/example/QCE_demos_2022/star_network_modified.json
similarity index 100%
rename from example/QCE_demos/star_network_modified.json
rename to example/QCE_demos_2022/star_network_modified.json
diff --git a/example/QCE_demos_2023/GUI_and_code_demo.ipynb b/example/QCE_demos_2023/GUI_and_code_demo.ipynb
new file mode 100644
index 00000000..b1cc1794
--- /dev/null
+++ b/example/QCE_demos_2023/GUI_and_code_demo.ipynb
@@ -0,0 +1,360 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Network with Applications\n",
+ "\n",
+ "In this file, we'll demonstrate the simulation of a more complicated network topology with applications. These applications will act on each node, requesting memories to be entangled and then recording metrics. The network topology, including hardware components, is shown below:\n",
+ "\n",
+ "\n",
+ "\n",
+ "In this file, we construct the network described above and add a custom app to two nodes. Most of the code has been written, but some will need to be filled in. We'll be building the topology from an external json file constructed using the GUI.\n",
+ "\n",
+ "## Imports\n",
+ "We must first import the necessary tools from SeQUeNCe. For building our specific request, we will import:\n",
+ "\n",
+ "- `RequestApp` is an example application included with SeQUeNCe. We will investigate its behavior through a class that inherits from this.\n",
+ "- `Message` is a wrapper for classical messages to exchange between nodes.\n",
+ "\n",
+ "Finally, for creating the network itself, we will import:\n",
+ "\n",
+ "- `Topology` is a powerful class for creating and managing complex network topologies. We'll be using the `RouterNetTopo` subclass to build our network and intefrace with specific nodes and node types."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ipywidgets import interact\n",
+ "import time\n",
+ "\n",
+ "# for building application\n",
+ "from sequence.app.request_app import RequestApp\n",
+ "from sequence.message import Message\n",
+ "\n",
+ "# for building network\n",
+ "from sequence.topology.router_net_topo import RouterNetTopo"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Custom Application\n",
+ "\n",
+ "We may now define our custom application. When receiving an entangled memory, we will announce its reception; we will then use the new `AppMessage` class to communicate results to the other node. The receiving node will wait when receiving an entangled memory. Once the classical message is received from the originator, it will release its local memory to be reused. This mimics the behavior of a teleportation operation."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "START_TIME = 1e12\n",
+ "\n",
+ "class TeleportMessage(Message):\n",
+ " def __init__(self, msg_type, receiver, memo_name: str):\n",
+ " super().__init__(msg_type, receiver)\n",
+ " self.memo_name = memo_name\n",
+ "\n",
+ "class TeleportApp(RequestApp):\n",
+ " def __init__(self, node, name, other_name):\n",
+ " super().__init__(node)\n",
+ " self.name = name\n",
+ " self.other_name = other_name\n",
+ " node.protocols.append(self)\n",
+ " self.memos_to_measure = {}\n",
+ " \n",
+ " # collect metrics:\n",
+ " self.latency = 0\n",
+ " self.count = 0\n",
+ " \n",
+ " def get_memory(self, info: \"MemoryInfo\") -> None:\n",
+ " \"\"\"Method to receive entangled memories.\n",
+ "\n",
+ " Will check if the received memory is qualified.\n",
+ " If it's a qualified memory, the application sets memory to RAW state\n",
+ " and release back to resource manager.\n",
+ " The counter of entanglement memories, 'memory_counter', is added.\n",
+ " Otherwise, the application does not modify the state of memory and\n",
+ " release back to the resource manager.\n",
+ "\n",
+ " Args:\n",
+ " info (MemoryInfo): info on the qualified entangled memory.\n",
+ " \"\"\"\n",
+ "\n",
+ " if info.state != \"ENTANGLED\":\n",
+ " return\n",
+ "\n",
+ " if info.index in self.memo_to_reserve:\n",
+ " reservation = self.memo_to_reserve[info.index]\n",
+ " \n",
+ " if info.remote_node == reservation.responder and info.fidelity >= reservation.fidelity:\n",
+ " # we are initiator, and want to teleport qubit\n",
+ " print(\"node {} memory {} entangled with other memory {}\".format(\n",
+ " self.node.name, info.index, info.remote_memo))\n",
+ " \n",
+ " # record metrics\n",
+ " if self.count == 0:\n",
+ " # record latency\n",
+ " pass\n",
+ " \n",
+ " # send message to other node\n",
+ " message = TeleportMessage(None, self.other_name, info.remote_memo)\n",
+ " \n",
+ " # reset local memory\n",
+ " \n",
+ " elif info.remote_node == reservation.initiator and info.fidelity >= reservation.fidelity:\n",
+ " # we are responder, and want to receive qubit\n",
+ " # need to wait on message from sender to correct entanled memory\n",
+ " self.memos_to_measure[info.memory.name] = info.memory\n",
+ " \n",
+ " def received_message(self, src, message):\n",
+ " memo_name = message.memo_name\n",
+ " \n",
+ " print(\"node {} received teleportation message for memory {}\".format(\n",
+ " self.node.name, memo_name))\n",
+ " \n",
+ " # reset local memory\n",
+ " memory = self.memos_to_measure.pop(memo_name)\n",
+ " self.node.resource_manager.update(None, memory, \"RAW\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Building the Simulation\n",
+ "\n",
+ "We'll now construct the network and add our applications. This example follows the usual process to ensure that all tools function properly:\n",
+ "1. Create the topology instance for the simulation to manage our network\n",
+ " - This class will create a simulation timeline\n",
+ " - This instantiates the Simulation Kernel (see below)\n",
+ "2. Create the simulated network topology. In this case, we are using an external JSON file to specify nodes and their connectivity.\n",
+ " - This includes specifying hardware parameters in the `set_parameters` function, defined later\n",
+ " - This instantiates the Hardware, Entanglement Management, Resource Management, and Network Management modules\n",
+ "3. Install custom protocols/applications and ensure all are set up properly\n",
+ " - This instantiates the Application module\n",
+ "4. Initialize and run the simulation\n",
+ "5. Collect and display the desired metrics\n",
+ "\n",
+ "The JSON file specifies that network nodes should be of type `QuantumRouter`, a node type defined by SeQUeNCe. This will automatically create all necessary hardware and protocol instances on the nodes, and the `Topology` class will automatically generate `BSMNode` instances on the quantum channels between such nodes.\n",
+ "\n",
+ "To construct an application, we need:\n",
+ "- The node to attach the application to\n",
+ "- The name of the application instance\n",
+ "- The name of the node to teleport qubits to.\n",
+ "\n",
+ "We can get a list of all desired application nodes, in this case routers, from the `Topology` class with the `get_nodes_by_type` method. We then set an application on each one."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def test(sim_time=1.5, qc_atten=1e-5):\n",
+ " \"\"\"\n",
+ " sim_time: duration of simulation time (s)\n",
+ " qc_atten: quantum channel attenuation (dB/km)\n",
+ " \"\"\"\n",
+ " \n",
+ " network_config = \"star_network.json\"\n",
+ " \n",
+ " # here, we make a new topology using the configuration JSON file.\n",
+ " # we then modify some of the simulation parameters of the network.\n",
+ " network_topo = RouterNetTopo(network_config)\n",
+ " set_parameters(network_topo, sim_time, qc_atten)\n",
+ " \n",
+ " # get two end nodes\n",
+ " start_node_name = \"router1\"\n",
+ " end_node_name = \"router2\"\n",
+ " node1 = node2 = None\n",
+ "\n",
+ " for router in network_topo.get_nodes_by_type(RouterNetTopo.QUANTUM_ROUTER):\n",
+ " if router.name == start_node_name:\n",
+ " node1 = router\n",
+ " elif router.name == end_node_name:\n",
+ " node2 = router\n",
+ " \n",
+ " start_app_name = \"start_app\"\n",
+ " end_app_name = \"end_app\"\n",
+ "\n",
+ " # create applications\n",
+ " start_app = None\n",
+ " end_app = None\n",
+ " \n",
+ " # run the simulation\n",
+ " tl = network_topo.get_timeline()\n",
+ " tl.show_progress = False\n",
+ " tl.init()\n",
+ " \n",
+ " start_app.start(end_node_name, START_TIME, 2e12, 10, 0.9)\n",
+ " tick = time.time()\n",
+ " tl.run()\n",
+ " \n",
+ " print(\"\\n\")\n",
+ " print(\"Execution time: {:.3f} seconds\".format(time.time() - tick))\n",
+ " \n",
+ " print(\"\\n\")\n",
+ " print(\"Latency: {:.3f} s\".format(start_app.latency))\n",
+ " print(\"Number of entangled memories:\", start_app.count)\n",
+ " print(\"Average throughput: {:.3f} pairs/s\".format(\n",
+ " start_app.count / (sim_time - (START_TIME * 1e-12))))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Setting parameters\n",
+ "\n",
+ "Here we define the `set_parameters` function we used earlier. This function will take a `Topology` as input and change many parameters to desired values. This will be covered in greater detail in workshop 3.\n",
+ "\n",
+ "The simulation time limit will be set using the `get_timeline` method.\n",
+ "\n",
+ "Quantum memories and detectors are hardware elements, and so parameters are changed by accessing the hardware included with the `QuantumRouter` and `BSMNode` node types. Many complex hardware elements, such as bsm devices or memory arrays, have methods to update parameters for all included hardware elements. This includes `update_memory_params` to change all memories in an array or `update_detector_params` to change all detectors.\n",
+ "\n",
+ "We will also set the success probability and swapping degradation of the entanglement swapping protocol. This will be set in the Network management Module (specifically the reservation protocol), as this information is necessary to create and manage the rules for the Resource Management module.\n",
+ "\n",
+ "Lastly, we'll update some parameters of the quantum channels. Quantum channels (and, similarly, classical channels) can be accessed from the `Topology` class as the `qchannels` field. Since these are individual hardware elements, we will set the parameters directly."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def set_parameters(topology, simulation_time, attenuation):\n",
+ " \"\"\"\n",
+ " simulation_time: duration of simulation time (s)\n",
+ " attenuation: attenuation on quantum channels (db/m)\n",
+ " \"\"\"\n",
+ " \n",
+ " PS_PER_S = 1e12\n",
+ " \n",
+ " # set timeline stop time\n",
+ " topology.get_timeline().stop_time = (simulation_time * PS_PER_S)\n",
+ " \n",
+ " # set memory parameters\n",
+ " MEMO_FREQ = 2e3\n",
+ " MEMO_EXPIRE = 0\n",
+ " MEMO_EFFICIENCY = 1\n",
+ " MEMO_FIDELITY = 0.9349367588934053\n",
+ " for node in topology.get_nodes_by_type(RouterNetTopo.QUANTUM_ROUTER):\n",
+ " memory_array = node.get_components_by_type(\"MemoryArray\")[0]\n",
+ " memory_array.update_memory_params(\"frequency\", MEMO_FREQ)\n",
+ " memory_array.update_memory_params(\"coherence_time\", MEMO_EXPIRE)\n",
+ " memory_array.update_memory_params(\"efficiency\", MEMO_EFFICIENCY)\n",
+ " memory_array.update_memory_params(\"raw_fidelity\", MEMO_FIDELITY)\n",
+ "\n",
+ " # set detector parameters\n",
+ " DETECTOR_EFFICIENCY = 0.9\n",
+ " DETECTOR_COUNT_RATE = 5e7\n",
+ " DETECTOR_RESOLUTION = 100\n",
+ " for node in topology.get_nodes_by_type(RouterNetTopo.BSM_NODE):\n",
+ " bsm = node.get_components_by_type(\"SingleAtomBSM\")[0]\n",
+ " bsm.update_detectors_params(\"efficiency\", DETECTOR_EFFICIENCY)\n",
+ " bsm.update_detectors_params(\"count_rate\", DETECTOR_COUNT_RATE)\n",
+ " bsm.update_detectors_params(\"time_resolution\", DETECTOR_RESOLUTION)\n",
+ " \n",
+ " # set entanglement swapping parameters\n",
+ " SWAP_SUCC_PROB = 0.90\n",
+ " SWAP_DEGRADATION = 0.99\n",
+ " for node in topology.get_nodes_by_type(RouterNetTopo.QUANTUM_ROUTER):\n",
+ " node.network_manager.protocol_stack[1].set_swapping_success_rate(SWAP_SUCC_PROB)\n",
+ " node.network_manager.protocol_stack[1].set_swapping_degradation(SWAP_DEGRADATION)\n",
+ " \n",
+ " # set quantum channel parameters\n",
+ " ATTENUATION = attenuation\n",
+ " QC_FREQ = 1e11\n",
+ " for qc in topology.qchannels:\n",
+ " qc.attenuation = ATTENUATION\n",
+ " qc.frequency = QC_FREQ"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Running the Simulation\n",
+ "\n",
+ "All that is left is to run the simulation with user input. We'll specify:\n",
+ "\n",
+ " sim_time: duration of simulation time (s)\n",
+ " qc_atten: attenuation on quantum channels (dB/m)\n",
+ "\n",
+ "Note that different hardware parameters or network topologies may cause the simulation to run for a very long time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "3015b584b71c4bcd9eaa6db134a806de",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "interactive(children=(FloatSlider(value=1.5, description='sim_time', max=2.0, min=1.0), Dropdown(description='…"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 25,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "interact(test, sim_time=(1, 2, 0.1), qc_atten=[0, 1e-5, 2e-5])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "venv",
+ "language": "python",
+ "name": "venv"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/example/QCE_demos_2023/GUI_and_code_demo_filled.ipynb b/example/QCE_demos_2023/GUI_and_code_demo_filled.ipynb
new file mode 100644
index 00000000..cf460475
--- /dev/null
+++ b/example/QCE_demos_2023/GUI_and_code_demo_filled.ipynb
@@ -0,0 +1,361 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Network with Applications\n",
+ "\n",
+ "In this file, we'll demonstrate the simulation of a more complicated network topology with applications. These applications will act on each node, requesting memories to be entangled and then recording metrics. The network topology, including hardware components, is shown below:\n",
+ "\n",
+ "\n",
+ "\n",
+ "In this file, we construct the network described above and add a custom app to two nodes. Most of the code has been written, but some will need to be filled in. We'll be building the topology from an external json file constructed using the GUI.\n",
+ "\n",
+ "## Imports\n",
+ "We must first import the necessary tools from SeQUeNCe. For building our specific request, we will import:\n",
+ "\n",
+ "- `RequestApp` is an example application included with SeQUeNCe. We will investigate its behavior through a class that inherits from this.\n",
+ "- `Message` is a wrapper for classical messages to exchange between nodes.\n",
+ "\n",
+ "Finally, for creating the network itself, we will import:\n",
+ "\n",
+ "- `Topology` is a powerful class for creating and managing complex network topologies. We'll be using the `RouterNetTopo` subclass to build our network and intefrace with specific nodes and node types."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ipywidgets import interact\n",
+ "import time\n",
+ "\n",
+ "# for building application\n",
+ "from sequence.app.request_app import RequestApp\n",
+ "from sequence.message import Message\n",
+ "\n",
+ "# for building network\n",
+ "from sequence.topology.router_net_topo import RouterNetTopo"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Custom Application\n",
+ "\n",
+ "We may now define our custom application. When receiving an entangled memory, we will announce its reception; we will then use the new `AppMessage` class to communicate results to the other node. The receiving node will wait when receiving an entangled memory. Once the classical message is received from the originator, it will release its local memory to be reused. This mimics the behavior of a teleportation operation."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "START_TIME = 1e12\n",
+ "\n",
+ "class TeleportMessage(Message):\n",
+ " def __init__(self, msg_type, receiver, memo_name: str):\n",
+ " super().__init__(msg_type, receiver)\n",
+ " self.memo_name = memo_name\n",
+ "\n",
+ "class TeleportApp(RequestApp):\n",
+ " def __init__(self, node, name, other_name):\n",
+ " super().__init__(node)\n",
+ " self.name = name\n",
+ " self.other_name = other_name\n",
+ " node.protocols.append(self)\n",
+ " self.memos_to_measure = {}\n",
+ " \n",
+ " # collect metrics:\n",
+ " self.latency = 0\n",
+ " self.count = 0\n",
+ " \n",
+ " def get_memory(self, info: \"MemoryInfo\") -> None:\n",
+ " \"\"\"Method to receive entangled memories.\n",
+ "\n",
+ " Will check if the received memory is qualified.\n",
+ " If it's a qualified memory, the application sets memory to RAW state\n",
+ " and release back to resource manager.\n",
+ " The counter of entanglement memories, 'memory_counter', is added.\n",
+ " Otherwise, the application does not modify the state of memory and\n",
+ " release back to the resource manager.\n",
+ "\n",
+ " Args:\n",
+ " info (MemoryInfo): info on the qualified entangled memory.\n",
+ " \"\"\"\n",
+ "\n",
+ " if info.state != \"ENTANGLED\":\n",
+ " return\n",
+ "\n",
+ " if info.index in self.memo_to_reserve:\n",
+ " reservation = self.memo_to_reserve[info.index]\n",
+ " \n",
+ " if info.remote_node == reservation.responder and info.fidelity >= reservation.fidelity:\n",
+ " # we are initiator, and want to teleport qubit\n",
+ " print(\"node {} memory {} entangled with other memory {}\".format(\n",
+ " self.node.name, info.index, info.remote_memo))\n",
+ " \n",
+ " # record metrics\n",
+ " if self.count == 0:\n",
+ " self.latency = (self.node.timeline.now() - START_TIME) * 1e-12\n",
+ " self.count += 1\n",
+ " \n",
+ " # send message to other node\n",
+ " message = TeleportMessage(None, self.other_name, info.remote_memo)\n",
+ " self.node.send_message(self.responder, message)\n",
+ " \n",
+ " # reset local memory\n",
+ " self.node.resource_manager.update(None, info.memory, \"RAW\")\n",
+ " \n",
+ " elif info.remote_node == reservation.initiator and info.fidelity >= reservation.fidelity:\n",
+ " # we are responder, and want to receive qubit\n",
+ " # need to wait on message from sender to correct entanled memory\n",
+ " self.memos_to_measure[info.memory.name] = info.memory\n",
+ " \n",
+ " def received_message(self, src, message):\n",
+ " memo_name = message.memo_name\n",
+ " \n",
+ " print(\"node {} received teleportation message for memory {}\".format(\n",
+ " self.node.name, memo_name))\n",
+ " \n",
+ " # reset local memory\n",
+ " memory = self.memos_to_measure.pop(memo_name)\n",
+ " self.node.resource_manager.update(None, memory, \"RAW\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Building the Simulation\n",
+ "\n",
+ "We'll now construct the network and add our applications. This example follows the usual process to ensure that all tools function properly:\n",
+ "1. Create the topology instance for the simulation to manage our network\n",
+ " - This class will create a simulation timeline\n",
+ " - This instantiates the Simulation Kernel (see below)\n",
+ "2. Create the simulated network topology. In this case, we are using an external JSON file to specify nodes and their connectivity.\n",
+ " - This includes specifying hardware parameters in the `set_parameters` function, defined later\n",
+ " - This instantiates the Hardware, Entanglement Management, Resource Management, and Network Management modules\n",
+ "3. Install custom protocols/applications and ensure all are set up properly\n",
+ " - This instantiates the Application module\n",
+ "4. Initialize and run the simulation\n",
+ "5. Collect and display the desired metrics\n",
+ "\n",
+ "The JSON file specifies that network nodes should be of type `QuantumRouter`, a node type defined by SeQUeNCe. This will automatically create all necessary hardware and protocol instances on the nodes, and the `Topology` class will automatically generate `BSMNode` instances on the quantum channels between such nodes.\n",
+ "\n",
+ "To construct an application, we need:\n",
+ "- The node to attach the application to\n",
+ "- The name of the application instance\n",
+ "- The name of the node to teleport qubits to.\n",
+ "\n",
+ "We can get a list of all desired application nodes, in this case routers, from the `Topology` class with the `get_nodes_by_type` method. We then set an application on each one."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def test(sim_time=1.5, qc_atten=1e-5):\n",
+ " \"\"\"\n",
+ " sim_time: duration of simulation time (s)\n",
+ " qc_atten: quantum channel attenuation (dB/km)\n",
+ " \"\"\"\n",
+ " \n",
+ " network_config = \"star_network.json\"\n",
+ " \n",
+ " # here, we make a new topology using the configuration JSON file.\n",
+ " # we then modify some of the simulation parameters of the network.\n",
+ " network_topo = RouterNetTopo(network_config)\n",
+ " set_parameters(network_topo, sim_time, qc_atten)\n",
+ " \n",
+ " # get two end nodes and create application\n",
+ " start_node_name = \"router1\"\n",
+ " end_node_name = \"router2\"\n",
+ " node1 = node2 = None\n",
+ "\n",
+ " for router in network_topo.get_nodes_by_type(RouterNetTopo.QUANTUM_ROUTER):\n",
+ " if router.name == start_node_name:\n",
+ " node1 = router\n",
+ " elif router.name == end_node_name:\n",
+ " node2 = router\n",
+ " \n",
+ " start_app_name = \"start_app\"\n",
+ " end_app_name = \"end_app\"\n",
+ " \n",
+ " start_app = TeleportApp(node1, start_app_name, end_app_name)\n",
+ " end_app = TeleportApp(node2, end_app_name, start_app_name)\n",
+ " \n",
+ " # run the simulation\n",
+ " tl = network_topo.get_timeline()\n",
+ " tl.show_progress = False\n",
+ " tl.init()\n",
+ " \n",
+ " start_app.start(end_node_name, START_TIME, 2e12, 10, 0.9)\n",
+ " tick = time.time()\n",
+ " tl.run()\n",
+ " \n",
+ " print(\"\\n\")\n",
+ " print(\"Execution time: {:.3f} seconds\".format(time.time() - tick))\n",
+ " \n",
+ " print(\"\\n\")\n",
+ " print(\"Latency: {:.3f} s\".format(start_app.latency))\n",
+ " print(\"Number of entangled memories:\", start_app.count)\n",
+ " print(\"Average throughput: {:.3f} pairs/s\".format(\n",
+ " start_app.count / (sim_time - (START_TIME * 1e-12))))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Setting parameters\n",
+ "\n",
+ "Here we define the `set_parameters` function we used earlier. This function will take a `Topology` as input and change many parameters to desired values. This will be covered in greater detail in workshop 3.\n",
+ "\n",
+ "The simulation time limit will be set using the `get_timeline` method.\n",
+ "\n",
+ "Quantum memories and detectors are hardware elements, and so parameters are changed by accessing the hardware included with the `QuantumRouter` and `BSMNode` node types. Many complex hardware elements, such as bsm devices or memory arrays, have methods to update parameters for all included hardware elements. This includes `update_memory_params` to change all memories in an array or `update_detector_params` to change all detectors.\n",
+ "\n",
+ "We will also set the success probability and swapping degradation of the entanglement swapping protocol. This will be set in the Network management Module (specifically the reservation protocol), as this information is necessary to create and manage the rules for the Resource Management module.\n",
+ "\n",
+ "Lastly, we'll update some parameters of the quantum channels. Quantum channels (and, similarly, classical channels) can be accessed from the `Topology` class as the `qchannels` field. Since these are individual hardware elements, we will set the parameters directly."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def set_parameters(topology, simulation_time, attenuation):\n",
+ " \"\"\"\n",
+ " simulation_time: duration of simulation time (s)\n",
+ " attenuation: attenuation on quantum channels (db/m)\n",
+ " \"\"\"\n",
+ " \n",
+ " PS_PER_S = 1e12\n",
+ " \n",
+ " # set timeline stop time\n",
+ " topology.get_timeline().stop_time = (simulation_time * PS_PER_S)\n",
+ " \n",
+ " # set memory parameters\n",
+ " MEMO_FREQ = 2e3\n",
+ " MEMO_EXPIRE = 0\n",
+ " MEMO_EFFICIENCY = 1\n",
+ " MEMO_FIDELITY = 0.9349367588934053\n",
+ " for node in topology.get_nodes_by_type(RouterNetTopo.QUANTUM_ROUTER):\n",
+ " memory_array = node.get_components_by_type(\"MemoryArray\")[0]\n",
+ " memory_array.update_memory_params(\"frequency\", MEMO_FREQ)\n",
+ " memory_array.update_memory_params(\"coherence_time\", MEMO_EXPIRE)\n",
+ " memory_array.update_memory_params(\"efficiency\", MEMO_EFFICIENCY)\n",
+ " memory_array.update_memory_params(\"raw_fidelity\", MEMO_FIDELITY)\n",
+ "\n",
+ " # set detector parameters\n",
+ " DETECTOR_EFFICIENCY = 0.9\n",
+ " DETECTOR_COUNT_RATE = 5e7\n",
+ " DETECTOR_RESOLUTION = 100\n",
+ " for node in topology.get_nodes_by_type(RouterNetTopo.BSM_NODE):\n",
+ " bsm = node.get_components_by_type(\"SingleAtomBSM\")[0]\n",
+ " bsm.update_detectors_params(\"efficiency\", DETECTOR_EFFICIENCY)\n",
+ " bsm.update_detectors_params(\"count_rate\", DETECTOR_COUNT_RATE)\n",
+ " bsm.update_detectors_params(\"time_resolution\", DETECTOR_RESOLUTION)\n",
+ " \n",
+ " # set entanglement swapping parameters\n",
+ " SWAP_SUCC_PROB = 0.90\n",
+ " SWAP_DEGRADATION = 0.99\n",
+ " for node in topology.get_nodes_by_type(RouterNetTopo.QUANTUM_ROUTER):\n",
+ " node.network_manager.protocol_stack[1].set_swapping_success_rate(SWAP_SUCC_PROB)\n",
+ " node.network_manager.protocol_stack[1].set_swapping_degradation(SWAP_DEGRADATION)\n",
+ " \n",
+ " # set quantum channel parameters\n",
+ " ATTENUATION = attenuation\n",
+ " QC_FREQ = 1e11\n",
+ " for qc in topology.qchannels:\n",
+ " qc.attenuation = ATTENUATION\n",
+ " qc.frequency = QC_FREQ"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Running the Simulation\n",
+ "\n",
+ "All that is left is to run the simulation with user input. We'll specify:\n",
+ "\n",
+ " sim_time: duration of simulation time (s)\n",
+ " qc_atten: attenuation on quantum channels (dB/m)\n",
+ "\n",
+ "Note that different hardware parameters or network topologies may cause the simulation to run for a very long time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "3015b584b71c4bcd9eaa6db134a806de",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "interactive(children=(FloatSlider(value=1.5, description='sim_time', max=2.0, min=1.0), Dropdown(description='…"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 25,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "interact(test, sim_time=(1, 2, 0.1), qc_atten=[0, 1e-5, 2e-5])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "venv",
+ "language": "python",
+ "name": "venv"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/example/QCE_demos_2023/images/star_network.png b/example/QCE_demos_2023/images/star_network.png
new file mode 100644
index 00000000..10b048e1
Binary files /dev/null and b/example/QCE_demos_2023/images/star_network.png differ
diff --git a/example/QCE_demos_2023/modify_script_method_function.ipynb b/example/QCE_demos_2023/modify_script_method_function.ipynb
new file mode 100644
index 00000000..e03236ed
--- /dev/null
+++ b/example/QCE_demos_2023/modify_script_method_function.ipynb
@@ -0,0 +1,158 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a1107282",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\"\"\"To modify the `improved_fidelity` static method of entanglement purification protocol (`BBPSSW`).\n",
+ "\n",
+ "The modification is aimed at demonstrating entanglement purification under a different error model,\n",
+ "i.e. only single-qubit bit-flip errors exist. Thus the imperfect entangled states are no longer Werner-type\n",
+ "but simply a mixture of two Bell states: `desired_state` and its counterpart under single-qubit bit-flip.\n",
+ "In this case the improved fidelity is different from more general case where Werner states are considered.\n",
+ "\"\"\"\n",
+ "\n",
+ "from sequence.entanglement_management.purification import BBPSSW\n",
+ "\n",
+ "\n",
+ "def test_fid(F):\n",
+ " \"\"\"Only for testing this approach of modification.\"\"\"\n",
+ " \n",
+ " return 0.88\n",
+ "\n",
+ "def original_fid(F):\n",
+ " \"\"\"Only for record of original method in script before modification.\"\"\"\n",
+ " \n",
+ " return (F ** 2 + ((1 - F) / 3) ** 2) / (F ** 2 + 2 * F * (1 - F) / 3 + 5 * ((1 - F) / 3) ** 2)\n",
+ "\n",
+ "def new_improved_fid(F):\n",
+ " \"\"\"The improved fidelity corresponding under bit-flip error model.\"\"\"\n",
+ " \n",
+ " return F ** 2 / (F ** 2 + (1 - F) ** 2)\n",
+ "\n",
+ "BBPSSW.improved_fidelity = new_improved_fid"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "af43fb2c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\"\"\"To modify the `_set_state_with_fidelity` function defined in SeQUeNCe BSM modele.\n",
+ "\n",
+ "This modification is corresponding to the previous modification of purification improved fidelity.\n",
+ "Previously the physically generated entangled states are of Werner type, to demonstrate the difference\n",
+ "now they should be mixture of only two Bell states as discussed above.\n",
+ "\"\"\"\n",
+ "\n",
+ "import sequence.components.bsm as bsm_module\n",
+ "from sequence.kernel.quantum_manager import KET_STATE_FORMALISM, DENSITY_MATRIX_FORMALISM\n",
+ "\n",
+ "\n",
+ "BSM = bsm_module.BSM\n",
+ "\n",
+ "def test_method(keys, desired_state, fidelity, rng, qm):\n",
+ " \"\"\"Only for testing this approach of modification.\"\"\"\n",
+ " \n",
+ " possible_states = [BSM._phi_plus, BSM._phi_minus,\n",
+ " BSM._psi_plus, BSM._psi_minus]\n",
+ " assert desired_state in possible_states\n",
+ "\n",
+ " if qm.formalism == KET_STATE_FORMALISM:\n",
+ " qm.set(keys, desired_state)\n",
+ "\n",
+ " elif qm.formalism == DENSITY_MATRIX_FORMALISM:\n",
+ " state = outer(desired_state, desired_state)\n",
+ " qm.set(keys, state)\n",
+ "\n",
+ " else:\n",
+ " raise Exception(\"Invalid quantum manager with formalism {}\".format(qm.formalism))\n",
+ " \n",
+ "def _set_state_with_fidelity(keys, desired_state, fidelity, rng, qm):\n",
+ " \"\"\"Only for record of original method in script before modification.\"\"\"\n",
+ "\n",
+ " possible_states = [BSM._phi_plus, BSM._phi_minus,\n",
+ " BSM._psi_plus, BSM._psi_minus]\n",
+ " assert desired_state in possible_states\n",
+ "\n",
+ " if qm.formalism == KET_STATE_FORMALISM:\n",
+ " probabilities = [(1 - fidelity) / 3] * 4\n",
+ " probabilities[possible_states.index(desired_state)] = fidelity\n",
+ " state_ind = rng.choice(4, p=probabilities)\n",
+ " qm.set(keys, possible_states[state_ind])\n",
+ "\n",
+ " elif qm.formalism == DENSITY_MATRIX_FORMALISM:\n",
+ " multipliers = [(1 - fidelity) / 3] * 4\n",
+ " multipliers[possible_states.index(desired_state)] = fidelity\n",
+ " state = zeros((4, 4))\n",
+ " for mult, pure in zip(multipliers, possible_states):\n",
+ " state = add(state, mult * outer(pure, pure))\n",
+ " qm.set(keys, state)\n",
+ "\n",
+ " else:\n",
+ " raise Exception(\"Invalid quantum manager with formalism {}\".format(qm.formalism))\n",
+ "\n",
+ "def new_set_state(keys, desired_state, fidelity, rng, qm):\n",
+ " \"\"\"The new function to set generated entangled states under a different assumed error model.\"\"\"\n",
+ "\n",
+ " possible_states = [BSM._phi_plus, BSM._phi_minus,\n",
+ " BSM._psi_plus, BSM._psi_minus]\n",
+ " assert desired_state in possible_states\n",
+ "\n",
+ " if qm.formalism == KET_STATE_FORMALISM:\n",
+ " probabilities = [0] * 4\n",
+ " probabilities[possible_states.index(desired_state)] = fidelity\n",
+ " if desired_state == BSM._psi_plus:\n",
+ " probabilities[possible_states.index(BSM._phi_plus)] = 1 - fidelity\n",
+ " elif desired_state == BSM._psi_minus:\n",
+ " probabilities[possible_states.index(BSM._phi_minus)] = 1 - fidelity\n",
+ " state_ind = rng.choice(4, p=probabilities)\n",
+ " qm.set(keys, possible_states[state_ind])\n",
+ "\n",
+ " elif qm.formalism == DENSITY_MATRIX_FORMALISM:\n",
+ " multipliers = [0] * 4\n",
+ " multipliers[possible_states.index(desired_state)] = fidelity\n",
+ " if desired_state == BSM._psi_plus:\n",
+ " probabilities[possible_states.index(BSM._phi_plus)] = 1 - fidelity\n",
+ " elif desired_state == BSM._psi_minus:\n",
+ " probabilities[possible_states.index(BSM._phi_minus)] = 1 - fidelity\n",
+ " state = zeros((4, 4))\n",
+ " for mult, pure in zip(multipliers, possible_states):\n",
+ " state = add(state, mult * outer(pure, pure))\n",
+ " qm.set(keys, state)\n",
+ "\n",
+ " else:\n",
+ " raise Exception(\"Invalid quantum manager with formalism {}\".format(qm.formalism))\n",
+ "\n",
+ "\n",
+ "bsm_module._set_state_with_fidelity = new_set_state"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.13"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/example/QCE_demos_2023/star_network.json b/example/QCE_demos_2023/star_network.json
new file mode 100644
index 00000000..09b2e25a
--- /dev/null
+++ b/example/QCE_demos_2023/star_network.json
@@ -0,0 +1,118 @@
+{
+ "nodes": [
+ {
+ "name": "center",
+ "type": "QuantumRouter",
+ "seed": 0,
+ "memo_size": 50
+ },
+ {
+ "name": "router1",
+ "type": "QuantumRouter",
+ "seed": 1,
+ "memo_size": 50
+ },
+ {
+ "name": "router2",
+ "type": "QuantumRouter",
+ "seed": 2,
+ "memo_size": 50
+ },
+ {
+ "name": "router3",
+ "type": "QuantumRouter",
+ "seed": 3,
+ "memo_size": 50
+ },
+ {
+ "name": "router4",
+ "type": "QuantumRouter",
+ "seed": 4,
+ "memo_size": 50
+ }
+ ],
+ "qconnections": [
+ {
+ "node1": "center",
+ "node2": "router1",
+ "attenuation": 0.0002,
+ "distance": 500,
+ "type": "meet_in_the_middle"
+ },
+ {
+ "node1": "center",
+ "node2": "router2",
+ "attenuation": 0.0002,
+ "distance": 500,
+ "type": "meet_in_the_middle"
+ },
+ {
+ "node1": "center",
+ "node2": "router3",
+ "attenuation": 0.0002,
+ "distance": 500,
+ "type": "meet_in_the_middle"
+ },
+ {
+ "node1": "center",
+ "node2": "router4",
+ "attenuation": 0.0002,
+ "distance": 500,
+ "type": "meet_in_the_middle"
+ }
+ ],
+ "is_parallel": false,
+ "stop_time": 2000000000000,
+ "cconnections": [
+ {
+ "node1": "center",
+ "node2": "router1",
+ "delay": 500000000
+ },
+ {
+ "node1": "center",
+ "node2": "router2",
+ "delay": 500000000
+ },
+ {
+ "node1": "center",
+ "node2": "router3",
+ "delay": 500000000
+ },
+ {
+ "node1": "center",
+ "node2": "router4",
+ "delay": 500000000
+ },
+ {
+ "node1": "router1",
+ "node2": "router2",
+ "delay": 1000000000
+ },
+ {
+ "node1": "router1",
+ "node2": "router3",
+ "delay": 1000000000
+ },
+ {
+ "node1": "router1",
+ "node2": "router4",
+ "delay": 1000000000
+ },
+ {
+ "node1": "router2",
+ "node2": "router3",
+ "delay": 1000000000
+ },
+ {
+ "node1": "router2",
+ "node2": "router4",
+ "delay": 1000000000
+ },
+ {
+ "node1": "router3",
+ "node2": "router4",
+ "delay": 1000000000
+ }
+ ]
+}
diff --git a/setup.py b/setup.py
index b3a4f86b..96af5754 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
setup(
name="sequence",
- version="0.6.1",
+ version="0.6.2",
author="Xiaoliang Wu, Joaquin Chung, Alexander Kolar, Alexander Kiefer, Eugene Wang, Tian Zhong, Rajkumar Kettimuthu, Martin Suchara",
author_email="xwu64@hawk.iit.edu, chungmiranda@anl.gov, akolar@anl.gov, akiefer@iu.edu, eugenewang@yahoo.com, tzh@uchicago.edu, kettimut@mcs.anl.gov, msuchara@anl.gov",
description="Simulator of Quantum Network Communication: SEQUENCE-Python is a prototype version of the official SEQUENCE release.",
diff --git a/src/gui/app.py b/src/gui/app.py
index f23de8d7..bf4c348a 100644
--- a/src/gui/app.py
+++ b/src/gui/app.py
@@ -54,7 +54,7 @@
]
DIRECTORY, _ = os.path.split(__file__)
-TEMPLATES = '/user_templates.json'
+TEMPLATES = '/default_templates.json'
class QuantumGUI:
@@ -101,31 +101,28 @@ def __init__(self, graph_in, templates=None, delays=None, tdm=None):
self.data = graph_in
self.cc_delays = delays
self.qc_tdm = tdm
- self.defaults = {}
- with open(DIRECTORY + '/default_params.json', 'r') as json_file:
- self.defaults = json.load(json_file)
- json_file.close()
+ # self.defaults = {}
+ # with open(DIRECTORY + '/default_params.json', 'r') as json_file:
+ # self.defaults = json.load(json_file)
+ # json_file.close()
if templates is None:
-
if os.path.exists(DIRECTORY + TEMPLATES):
with open(DIRECTORY + TEMPLATES, 'r') as json_file:
self.templates = json.load(json_file)
- json_file.close()
else:
user_defaults = {}
for x in TYPES:
user_defaults[x] = {}
self.templates = user_defaults
- with open(DIRECTORY + '/user_templates.json', 'w') as outfile:
+ with open(DIRECTORY + TEMPLATES, 'w') as outfile:
json.dump(
user_defaults,
outfile,
sort_keys=True,
indent=4
)
- outfile.close()
# TODO: re-add simulation
# self.simulation = GUI_Sim(0, 0, 'NOTSET', 'init', self)
@@ -291,20 +288,26 @@ def graph_to_topology(self):
# q_delay = self.qc_tdm.copy()
c_delay = self.cc_delays.copy()
+ # add node dict
nodes_top = []
-
for node in nodes:
- nodes_top.append(
- {
- Topology.NAME: node[1]['label'],
- Topology.TYPE: node[1]['node_type'],
- Topology.SEED: None,
- RouterNetTopo.MEMO_ARRAY_SIZE: 50
- }
- )
-
+ node_type = node[1]['node_type']
+ node_template_name = node[1]['data']['template']
+ node_template = self.templates[node_type][node_template_name]
+
+ node_dict = {
+ Topology.NAME: node[1]['label'],
+ Topology.TYPE: node_type,
+ Topology.SEED: None,
+ Topology.TEMPLATE: node_template_name
+ }
+ if node_type == RouterNetTopo.QUANTUM_ROUTER:
+ node_dict[RouterNetTopo.MEMO_ARRAY_SIZE] = node_template['memo_size']
+
+ nodes_top.append(node_dict)
+
+ # add quantum connections dict
qconnections = []
-
for edge in edges:
qconnections.append(
{
@@ -316,6 +319,7 @@ def graph_to_topology(self):
}
)
+ # add classical connections dict
cchannels = []
table = c_delay.to_numpy()
labels = list(c_delay.columns)
@@ -332,10 +336,28 @@ def graph_to_topology(self):
}
)
+ # add templates dict
+ templates = {}
+ for temp_name, temp_vals in self.templates['QuantumRouter'].items():
+ templates[temp_name] = {
+ 'MemoryArray': self.templates['Memory'][temp_vals['mem_type']]
+ }
+ for temp_name, temp_vals in self.templates['BSMNode'].items():
+ templates[temp_name] = {
+ 'SingleAtomBSM': {
+ 'detectors': [
+ self.templates['Detector'][temp_vals['detector_1']],
+ self.templates['Detector'][temp_vals['detector_2']]
+ ]
+ }
+ }
+
+ # collect and finalize
output = {
Topology.ALL_NODE: nodes_top,
Topology.ALL_QC_CONNECT: qconnections,
Topology.ALL_C_CHANNEL: cchannels,
+ Topology.ALL_TEMPLATES: templates,
RouterNetTopo.IS_PARALLEL: False,
Topology.STOP_TIME: int(1e12)
@@ -343,7 +365,7 @@ def graph_to_topology(self):
return output
- def _callback_add_node(self, add_node_name, add_node_type):
+ def _callback_add_node(self, add_node_name, add_node_type, add_node_template):
"""Function which adds a node with the given name and type to the current graph.
Args:
@@ -367,7 +389,7 @@ def _callback_add_node(self, add_node_name, add_node_type):
if data == add_node_name:
return [dash.no_update, 'Node already exists']
- new_node = GraphNode(add_node_name, add_node_type, 'test')
+ new_node = GraphNode(add_node_name, add_node_type, add_node_template)
new_graph.add_node(
add_node_name,
label=add_node_name,
@@ -558,7 +580,7 @@ def save_all(self, path):
if not os.path.exists(DIRECTORY+'/data'):
os.mkdir(DIRECTORY+'/data')
- self.save_templates(new_path)
+ # self.save_templates(new_path)
self.save_simulation(new_path)
self.save_topology(new_path)
return new_path
@@ -690,6 +712,7 @@ def get_app(self, name):
State('node_to_add_name', 'value'),
State('type_menu', 'value'),
+ State('add_template_menu', 'value'),
State('edge_properties', 'children'),
State('from_node', 'value'),
State('to_node', 'value'),
@@ -707,6 +730,7 @@ def edit_graph(
update_delay,
node_name,
node_to_add_type,
+ node_to_add_template,
properties,
from_node,
to_node,
@@ -735,7 +759,7 @@ def edit_graph(
input_id = ctx.triggered[0]['prop_id'].split('.')[0]
# print(input_id)
if input_id == 'add_node':
- info = self._callback_add_node(node_name, node_to_add_type)
+ info = self._callback_add_node(node_name, node_to_add_type, node_to_add_template)
graph_data = info[0]
err_msg = info[1]
@@ -814,11 +838,13 @@ def edit_graph(
elif input_id == 'submit_edit':
# print(selected)
edited = self.parse_edit(selected)
+
+ # for an edge
if 'source' in edited:
src = last_clicked['source']
trgt = last_clicked['target']
new_data = {k: edited[k] for k in EDGE_DICT_ORDER}
- self.data.edges[src][trgt]['data'] = new_data
+ self.data.edges[src, trgt]['data'] = new_data
gd = nx.readwrite.cytoscape_data(self.data)['elements']
err_msg = ''
@@ -835,6 +861,7 @@ def edit_graph(
delay_columns
]
+ # for a node
else:
old_name = last_clicked['name']
self.data.nodes[old_name]['data'] = edited
@@ -1001,13 +1028,13 @@ def template_menu(edgeType, save_click, temp, temp_type, temp_name):
if edgeType == 'QuantumRouter':
opts = list(self.templates['Memory'].keys())
return [router_template, '', opts, '']
- elif edgeType == 'Protocol':
- return [protocol_template, '', '', '']
+ elif edgeType == 'QKDNode':
+ return [qkd_template, '', '', '']
elif edgeType == 'Memory':
return [quantum_memory_template, '', '', '']
elif edgeType == 'Detector':
return [detector_template, '', '', '']
- elif edgeType == 'BSM_node':
+ elif edgeType == 'BSMNode':
opts = list(self.templates['Detector'].keys())
return [bsm_template, '', '', opts]
elif input_id == 'save_template':
@@ -1018,7 +1045,7 @@ def template_menu(edgeType, save_click, temp, temp_type, temp_name):
parsed = {temp_name: self.parse_node(temp)}
new_templates[temp_type].update(parsed)
self.templates = new_templates
- return [dash.no_update, 'Template Saved', '', '']
+ return [dash.no_update, 'Template Saved', dash.no_update, '']
else:
opts = list(self.templates['Memory'].keys())
return [router_template, '', opts, '']
@@ -1197,7 +1224,15 @@ def updateTypeMenu(data):
return [makeDropdownOptions(data), data[0]]
@app.callback(
- Output("detec_type", "options"),
+ Output("detec_type_1", "options"),
+ Input('detec_opts', 'data'),
+ prevent_initial_call=True,
+ )
+ def updateTypeMenu(data):
+ return makeDropdownOptions(data)
+
+ @app.callback(
+ Output("detec_type_2", "options"),
Input('detec_opts', 'data'),
prevent_initial_call=True,
)
diff --git a/src/gui/default_templates.json b/src/gui/default_templates.json
new file mode 100644
index 00000000..eaadf1d8
--- /dev/null
+++ b/src/gui/default_templates.json
@@ -0,0 +1,43 @@
+{
+ "BSMNode": {
+ "default_BSM": {
+ "detector_1": "default_detector",
+ "detector_2": "default_detector"
+ }
+ },
+ "Detector": {
+ "default_detector": {
+ "count_rate": 50000000.0,
+ "dark_count": 0,
+ "efficiency": 0.8,
+ "resolution": 100.0
+ }
+ },
+ "Entanglement": {
+ "default_entanglement": {
+ "degredation": 0.99,
+ "succ_prob": 0.9
+ }
+ },
+ "Memory": {
+ "default_memory": {
+ "coherence_time": 1300000000000.0,
+ "efficiency": 0.75,
+ "frequency": 2000.0,
+ "raw_fidelity": 0.85
+ }
+ },
+ "PhotonSource": {},
+ "QKDNode": {
+ "default_QKD": {
+ "encoding": "polarization",
+ "stack_size": 5
+ }
+ },
+ "QuantumRouter": {
+ "default_router": {
+ "mem_type": "default_memory",
+ "memo_size": 50
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/gui/layout.py b/src/gui/layout.py
index b31e2c95..b617a68b 100644
--- a/src/gui/layout.py
+++ b/src/gui/layout.py
@@ -26,6 +26,10 @@ def make_item(menu, label, num, icon):
'font-size': '18px',
'padding-right': '10px',
'overflowX': 'hidden'
+ },
+ selected_style={
+ 'text-align': 'left',
+ 'font-size': '18px'
}
)
@@ -175,15 +179,13 @@ def graph_element(graph, name):
Constant containing all available class in the GUI
"""
TYPES = [
- 'Quantum_Repeater',
'QuantumRouter',
- 'Photon_Source',
+ 'PhotonSource',
'Detector',
- # 'QuantumErrorCorrection',
'BSM_node',
- # 'Temp',
'Memory',
- # 'Protocol'
+ 'QKD',
+ 'Entanglement'
]
@@ -196,7 +198,7 @@ def graph_element(graph, name):
def genImages():
images = {
- 'Quantum_Repeater': 'repeater.png',
+ 'QuantumRepeater': 'repeater.png',
'QuantumRouter': 'router.png',
'Photon_Source': 'photonsource.png',
'Detector': 'detector.png',
diff --git a/src/gui/menus.py b/src/gui/menus.py
index 99a16390..462d2572 100644
--- a/src/gui/menus.py
+++ b/src/gui/menus.py
@@ -18,16 +18,17 @@
Mapping of all types in the GUI to their representative colors
"""
TYPE_COLORS = {
- 'Quantum_Repeater': '#4D9DE0',
- 'QuantumRouter': '#E15554',
- 'Photon_Source': '#E1BC29',
- 'Detector': '#3BB273',
- 'QuantumErrorCorrection': '#7768AE ',
- 'BSM_node': '#FFC857',
+ 'QuantumRepeater': '#4d9de0',
+ 'QuantumRouter': '#e15554',
+ 'PhotonSource': '#e1bc29',
+ 'Detector': '#3bb273',
+ 'QuantumErrorCorrection': '#7768ae',
+ 'BSMNode': '#ffc857',
'Quantum': '#8634eb',
'Classical': '#345feb',
'Memory': '#8a34ab',
- 'Temp': '#084C61',
+ 'Temp': '#084c61',
+ 'QKDNode': '#cc99ff'
}
@@ -35,7 +36,22 @@
Default node type options for dropdown menus
"""
-OPTIONS = [
+OPTIONS_NODE = [
+ {
+ 'label': 'Quantum Router',
+ 'value': 'QuantumRouter'
+ },
+ {
+ 'label': 'BSM Node',
+ 'value': 'BSMNode'
+ },
+ {
+ 'label': 'QKD Node',
+ 'value': 'QKDNode'
+ }
+]
+
+OPTIONS_TEMPLATE = [
{
'label': 'Quantum Router',
'value': 'QuantumRouter'
@@ -48,30 +64,18 @@
'label': 'Detector',
'value': 'Detector'
},
- # {
- # 'label': 'Protocol',
- # 'value': 'Protocol'
- # },
{
'label': 'BSM Node',
- 'value': 'BSM_node'
- },
- {
- 'label': 'Quantum Repeater',
- 'value': 'Quantum_Repeater'
+ 'value': 'BSMNode'
},
- # {
- # 'label': 'Quantum Error Correction',
- # 'value': 'QuantumErrorCorrection'
- # },
{
'label': 'Photon Source',
- 'value': 'Photon_Source'
+ 'value': 'PhotonSource'
},
- # {
- # 'label': 'Temp',
- # 'value': 'Temp'
- # }
+ {
+ 'label': 'QKD Node',
+ 'value': 'QKDNode'
+ }
]
DIRECTORY, _ = os.path.split(__file__)
@@ -156,7 +160,7 @@ def getSelectedNodeMenu(values, templates):
out.append(
getDropdownField(
values['type'],
- OPTIONS,
+ OPTIONS_NODE,
'Node Type:',
'selected_node_type',
'type'
@@ -201,13 +205,13 @@ def getSelectedEdgeMenu(values, nodes, link_types):
),
getInputField(
values['attenuation'],
- 'Attenuation:',
+ 'Attenuation (dB/m):',
'selected_attenuation',
'attenuation'
),
getInputField(
values['distance'],
- 'Distance:',
+ 'Distance (m):',
'selected_distance',
'distance'
)
@@ -244,13 +248,13 @@ def getTimeUnits(id_extra):
classic_edge = [
getInputField(
'',
- 'Distance:',
+ 'Distance (m):',
'distance_input',
''
),
getInputField(
'',
- 'Attenuation:',
+ 'Attenuation (dB/m):',
'attenuation_input',
''
)
@@ -259,13 +263,13 @@ def getTimeUnits(id_extra):
quantum_edge = [
getInputField(
'',
- 'Distance:',
+ 'Distance (m):',
'distance_input',
''
),
getInputField(
'',
- 'Attenuation:',
+ 'Attenuation (dB/m):',
'attenuation_input',
''
)
@@ -276,7 +280,8 @@ def getTimeUnits(id_extra):
dbc.Input(
id='mem_size',
className='memo_size',
- placeholder='Memory Array Size'
+ placeholder='Memory Array Size',
+ type='number'
),
dbc.Label('Memory Type'),
dcc.Dropdown(
@@ -320,7 +325,7 @@ def getTimeUnits(id_extra):
dbc.Input(id='mem_eff_in', className='efficiency', placeholder='0.75'),
dbc.Label('Fidelity'),
- dbc.Input(id='fidelity_in', className='fidelity', placeholder='500'),
+ dbc.Input(id='fidelity_in', className='fidelity', placeholder='0.85'),
]
detector_template = [
@@ -351,18 +356,37 @@ def getTimeUnits(id_extra):
]
bsm_template = [
- dbc.Label('Detector Type'),
+ dbc.Label('Detector 1 Type'),
+ dcc.Dropdown(
+ id='detec_type_1',
+ className='detector_type',
+ value='',
+ options=[]
+ ),
+ dbc.Label('Detector 2 Type'),
dcc.Dropdown(
- id='detec_type',
+ id='detec_type_2',
className='detector_type',
value='',
options=[]
),
]
-
-protocol_template = [
-
+qkd_template = [
+ dbc.Label('Photon Encoding'),
+ dcc.Dropdown(
+ id='encoding_in',
+ className='encoding',
+ value='',
+ options=["polarization", "time_bin"]
+ ),
+ dbc.Label('Protocol Stack Size'),
+ dcc.Dropdown(
+ id='stack_size_in',
+ className='stack_size',
+ value='',
+ options=[1, 2, 3, 4, 5]
+ ),
]
# New #
@@ -382,8 +406,8 @@ def getTimeUnits(id_extra):
place='Enter Node ID'
),
getDropdownField(
- 'Quantum_Router',
- OPTIONS,
+ 'QuantumRouter',
+ OPTIONS_NODE,
'Type:',
'type_menu',
''
@@ -497,8 +521,8 @@ def getTimeUnits(id_extra):
place='Enter ID'
),
getDropdownField(
- 'Quantum_Router',
- OPTIONS,
+ 'QuantumRouter',
+ OPTIONS_TEMPLATE,
'Type:',
'template_type_menu',
''
@@ -847,7 +871,7 @@ def makeLegend(values):
style={
'position': 'relative',
'top': '0px',
- 'left': '0px'
+ 'left': '10px'
}
),
dbc.Row(
@@ -900,12 +924,12 @@ def makeLegend(values):
href='https://github.com/sequence-toolbox/SeQUeNCe/issues', # nopep8
),
],
- nav=True,
+ label="More",
group=True,
size='sm',
+ nav=True,
in_navbar=True,
- label="More",
- right=True,
+ # right=True,
toggle_style={
'color': 'white'
}
diff --git a/src/gui/simulator_bindings.py b/src/gui/simulator_bindings.py
index 8830dfdd..0a570df7 100644
--- a/src/gui/simulator_bindings.py
+++ b/src/gui/simulator_bindings.py
@@ -43,7 +43,7 @@ def __init__(self, sim_time: int, time_scale: int, logging: str, sim_name, confi
self.timeline,
**node_temp
)
- elif node_type == "Quantum_Router":
+ elif node_type == "QuantumRouter":
mem = node_temp.pop('mem_type')
mem_config = self.sim_templates[mem].copy()
node_in = QuantumRouter(
diff --git a/src/gui/user_templates.json b/src/gui/user_templates.json
index 3c08eebd..534bbdef 100644
--- a/src/gui/user_templates.json
+++ b/src/gui/user_templates.json
@@ -35,8 +35,8 @@
}
},
"QuantumErrorCorrection": {},
- "Quantum_Repeater": {},
- "Quantum_Router": {
+ "QuantumRepeater": {},
+ "QuantumRouter": {
"default_router": {
"mem_type": "default_memory",
"memo_size": 50
diff --git a/tests/kernel/test_quantum_state.py b/tests/kernel/test_quantum_state.py
index d38267c4..12fb9b9d 100644
--- a/tests/kernel/test_quantum_state.py
+++ b/tests/kernel/test_quantum_state.py
@@ -123,3 +123,8 @@ def test_measure_entangled():
counter += 1
assert abs(0.5 - counter / 1000) < 0.1
+
+def test_polarization_noise():
+ qs = FreeQuantumState()
+ pass
+