Skip to content

Commit

Permalink
[minor] update example/three_node_eg_ep_es.ipynb
Browse files Browse the repository at this point in the history
  • Loading branch information
caitaozhan committed Dec 14, 2024
1 parent 116d622 commit 8d77ddb
Show file tree
Hide file tree
Showing 2 changed files with 399 additions and 51 deletions.
350 changes: 350 additions & 0 deletions example/three_node_eg_ep_es-old.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,350 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Three Node Entanglement Distribution\n",
"\n",
"For metropolitan-scale quantum networks to be usable, performing entanglement generation alone is insufficient. Addtionally, mannually adding rules is not a scalable way to configure network. As such, we must add protocols to purify and swap the generated entanglements. Rules about these protocols are created and installed automatically by the network manager. We then have a roughly three-step process:\n",
"\n",
"1. Generate entanglement between quantum memory pairs on adjacent nodes\n",
"2. Consume several entangled pairs to produce a single, high-fidelity entangled pairs\n",
"3. \"Swap\" the entanglement of two memory pairs, consuming the two shorter-distance entanglements to produce one at a greater distance\n",
"\n",
"These steps may be repeated several times to achieve entanglement over a large distance or a large number of nodes. In this file, we use SeQUeNCe to simulate entanglement distribution between three linear network nodes. Entanglement generation, purification, and swapping protocols are used. The network topology is shown below:\n",
"\n",
"<img src=\"./notebook_images/3_node_topo.png\" width=\"700\"/>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example\n",
"\n",
"Here we build the simulation using only tools directly from SeQUeNCe with their default behavior and rules. This is in contrast to the other notebook example files, where we add custom metrics and create custom rules for network operation.\n",
"\n",
"### Import\n",
"\n",
"We must first import the necessary tools from SeQUeNCe to run our simulations.\n",
"\n",
"- `Timeline` is the main simulation tool, providing an interface for the discrete-event simulation kernel.\n",
"- `QuantumRouter` provides a ready-to-use quantum router implementing SeQUeNCe's modular design. `BSMNode` provides a simpler, ready-to-use quantum node placed between routers as required by the entanglement generation protocol. This node uses Bell state measurement to generate entanglement between routers.\n",
"- `QuantumChannel` and `ClassicalChannel` are communication links between quantum nodes, providing models of optical fibers."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# !pip install ipywidgets # in case ipywidgets is not installed\n",
"from ipywidgets import interact\n",
"from matplotlib import pyplot as plt\n",
"import time"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"from sequence.kernel.timeline import Timeline\n",
"from sequence.topology.node import QuantumRouter, BSMNode\n",
"from sequence.components.optical_channel import QuantumChannel, ClassicalChannel"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Building the Simulation\n",
"\n",
"We are now ready to build the simulation itself. This example follows the usual simulation writing process to ensure that all tools function properly:\n",
"\n",
"1. Create the timeline for the simulation\n",
"2. Create the simulated network topology (here this is done explicitly, but this may also be handled by functions of the `Topology` class under `sequence.topology.topology`)\n",
" - This includes adjustment of default hardware parameters, as necessary\n",
" - This also includes creation of a static routing table for entanglement paths\n",
"3. Instantiate custom protocols and ensure all protocols are set up (paired) properly (if necessary)\n",
"4. Initialize and run the simulation\n",
"5. Collect and display the desired metrics\n",
"\n",
"For this example, the desired metric is the number of completed entanglements over time and their fidelities. Several elements of SeQUeNCe, including the Resource Management module, automatically collect simple metrics such as these. For custom or more advanced metrics, custom code may need to be written and applied. See the documentation for a list of metrics provided by default for each simulation tool."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"def test(sim_time, cc_delay, qc_atten, qc_dist):\n",
" \"\"\"\n",
" sim_time: duration of simulation time (ms)\n",
" cc_delay: delay on classical channels (ms)\n",
" qc_atten: attenuation on quantum channels (dB/m)\n",
" qc_dist: distance of quantum channels (km)\n",
" \"\"\"\n",
" \n",
" PS_PER_MS = 1e9\n",
" M_PER_KM = 1e3\n",
" \n",
" # convert units for cc delay (to ps) and qc distance (to m)\n",
" cc_delay *= PS_PER_MS\n",
" qc_dist *= M_PER_KM\n",
" \n",
" raw_fidelity = 0.85\n",
" \n",
" # construct the simulation timeline; the constructor argument is the simulation time (in ps)\n",
" tl = Timeline(sim_time * PS_PER_MS)\n",
" \n",
" ## create our quantum network and update parameters as needed\n",
" \n",
" # first, construct the quantum routers\n",
" # (with arguments for the node name, timeline, and number of quantum memories)\n",
" r1 = QuantumRouter(\"r1\", tl, 50)\n",
" r2 = QuantumRouter(\"r2\", tl, 100)\n",
" r3 = QuantumRouter(\"r3\", tl, 50)\n",
" # next, construct the BSM nodes\n",
" # (with arguments for the node name, timeline, and the names of connected routers)\n",
" m1 = BSMNode(\"m1\", tl, [\"r1\", \"r2\"])\n",
" m2 = BSMNode(\"m2\", tl, [\"r2\", \"r3\"])\n",
" \n",
" r1.add_bsm_node(m1.name, r2.name)\n",
" r2.add_bsm_node(m1.name, r1.name)\n",
" r2.add_bsm_node(m2.name, r3.name)\n",
" r3.add_bsm_node(m2.name, r2.name)\n",
" \n",
" # set seeds for random generators\n",
" nodes = [r1, r2, r3, m1, m2]\n",
" for i, node in enumerate(nodes):\n",
" node.set_seed(i)\n",
" \n",
" for node in [r1, r2, r3]:\n",
" memory_array = node.get_components_by_type(\"MemoryArray\")[0]\n",
" # we update the coherence time (measured in seconds) here\n",
" memory_array.update_memory_params(\"coherence_time\", 10)\n",
" # and similarly update the fidelity of entanglement for the memories\n",
" memory_array.update_memory_params(\"raw_fidelity\", raw_fidelity)\n",
" \n",
" # create all-to-all classical connections\n",
" for node1 in nodes:\n",
" for node2 in nodes:\n",
" if node1 == node2:\n",
" continue\n",
" # construct a classical communication channel\n",
" # (with arguments for the channel name, timeline, length (in m), and delay (in ps))\n",
" cc = ClassicalChannel(\"cc_%s_%s\"%(node1.name, node2.name), tl, 1e3, delay=cc_delay)\n",
" cc.set_ends(node1, node2.name)\n",
" \n",
" # create quantum channels linking r1 and r2 to m1\n",
" # (with arguments for the channel name, timeline, attenuation (in dB/m), and distance (in m))\n",
" qc0 = QuantumChannel(\"qc_r1_m1\", tl, qc_atten, qc_dist)\n",
" qc1 = QuantumChannel(\"qc_r2_m1\", tl, qc_atten, qc_dist)\n",
" qc0.set_ends(r1, m1.name)\n",
" qc1.set_ends(r2, m1.name)\n",
" # create quantum channels linking r2 and r3 to m2\n",
" qc2 = QuantumChannel(\"qc_r2_m2\", tl, qc_atten, qc_dist)\n",
" qc3 = QuantumChannel(\"qc_r3_m2\", tl, qc_atten, qc_dist)\n",
" qc2.set_ends(r2, m2.name)\n",
" qc3.set_ends(r3, m2.name)\n",
"\n",
" # create routing table manually\n",
" # note that the routing table is based on quantum links, not classical\n",
" # the arguments are the names of the destination node and the next node in the best path towards the destination\n",
" r1.network_manager.protocol_stack[0].add_forwarding_rule(\"r2\", \"r2\")\n",
" r1.network_manager.protocol_stack[0].add_forwarding_rule(\"r3\", \"r2\")\n",
" r2.network_manager.protocol_stack[0].add_forwarding_rule(\"r1\", \"r1\")\n",
" r2.network_manager.protocol_stack[0].add_forwarding_rule(\"r3\", \"r3\")\n",
" r3.network_manager.protocol_stack[0].add_forwarding_rule(\"r1\", \"r2\")\n",
" r3.network_manager.protocol_stack[0].add_forwarding_rule(\"r2\", \"r2\")\n",
" \n",
" ## run our simulation\n",
" \n",
" tl.init()\n",
" # we use the network manager of an end router to make our entanglement request\n",
" # here, the arguments are:\n",
" # (1) the destination node name,\n",
" # (2) the start time (in ps) of entanglement,\n",
" # (3) the end time (in ps) of entanglement,\n",
" # (4) the number of memories to entangle, and\n",
" # (5) the desired fidelity of entanglement.\n",
" r1.network_manager.request(\"r3\", 1e12, 1e14, 50, 0.9)\n",
"\n",
" tick = time.time()\n",
" tl.run()\n",
" print(\"execution time %.2f sec\" % (time.time() - tick))\n",
" \n",
" ## display metrics for entangled memories\n",
" \n",
" fig, (ax1, ax2, ax3) = plt.subplots(1, 3)\n",
" fig.set_size_inches(12, 5)\n",
"\n",
" # entangled memories on r1\n",
" # here, we plot the number of entangled memories versus time for r1\n",
" data = []\n",
" for info in r1.resource_manager.memory_manager:\n",
" if info.entangle_time > 0:\n",
" data.append(info.entangle_time / 1e12)\n",
" data.sort()\n",
" ax1.plot(data, range(1, len(data) + 1), marker=\"o\")\n",
" ax1.set_title(\"r1\")\n",
" ax1.set_ylabel(\"Number of Entangled Memories\")\n",
" \n",
" # entangled memories on r2\n",
" data = []\n",
" for info in r2.resource_manager.memory_manager:\n",
" if info.entangle_time > 0:\n",
" data.append(info.entangle_time / 1e12)\n",
" data.sort()\n",
" ax2.plot(data, range(1, len(data) + 1), marker=\"o\")\n",
" ax2.set_title(\"r2\")\n",
" ax2.set_xlabel(\"Simulation Time (s)\")\n",
" \n",
" # entangled memories on r3\n",
" data = []\n",
" for info in r3.resource_manager.memory_manager:\n",
" if info.entangle_time > 0:\n",
" data.append(info.entangle_time / 1e12)\n",
" data.sort()\n",
" ax3.plot(data, range(1, len(data) + 1), marker=\"o\")\n",
" ax3.set_title(\"r3\")\n",
" \n",
" fig.tight_layout()\n",
"\n",
" ## display metrics for memory fidelities\n",
" \n",
" fig, (ax1, ax2, ax3) = plt.subplots(1, 3)\n",
" fig.set_size_inches(12, 5)\n",
" \n",
" # display collected metric for memory fidelities on r1\n",
" # in this case, a bar chart of memory fidelity at each index\n",
" data = []\n",
" for info in r1.resource_manager.memory_manager:\n",
" data.append(info.fidelity)\n",
" ax1.bar(range(len(data)), data)\n",
" ax1.plot([0, len(data)], [raw_fidelity, raw_fidelity], \"k--\")\n",
" ax1.plot([0, len(data)], [0.9, 0.9], \"k--\")\n",
" ax1.set_ylim(0.7,1)\n",
" ax1.set_title(\"r1\")\n",
" ax1.set_ylabel(\"Fidelity\")\n",
"\n",
" # display collected metric for memory fidelities on r2\n",
" data = []\n",
" for info in r2.resource_manager.memory_manager:\n",
" data.append(info.fidelity)\n",
" ax2.bar(range(len(data)), data)\n",
" ax2.plot([0, len(data)], [raw_fidelity, raw_fidelity], \"k--\")\n",
" ax2.plot([0, len(data)], [0.9, 0.9], \"k--\")\n",
" ax2.set_ylim(0.7,1)\n",
" ax2.set_title(\"r2\")\n",
" ax2.set_xlabel(\"Memory Number\")\n",
"\n",
" # display collected metric for memory fidelities on r3\n",
" data = []\n",
" for info in r3.resource_manager.memory_manager:\n",
" data.append(info.fidelity)\n",
" ax3.bar(range(len(data)), data)\n",
" ax3.plot([0, len(data)], [raw_fidelity, raw_fidelity], \"k--\")\n",
" ax3.plot([0, len(data)], [0.9, 0.9], \"k--\")\n",
" ax3.set_ylim(0.7,1)\n",
" ax3.set_title(\"r3\")\n",
" \n",
" fig.tight_layout()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Running the Simulation\n",
"\n",
"All that is left is to run the simulation with user input. Note that the inclusion of more protocols, memories, and nodes may cause this simulation to run significantly longer than the `two_node_eg` example.\n",
"\n",
"Parameters:\n",
"\n",
" sim_time: duration of simulation time (ms)\n",
" cc_delay: delay on classical channels (ms)\n",
" qc_atten: attenuation on quantum channels (db/m)\n",
" qc_dist: distance of quantum channels (km)\n",
" \n",
"The maximum execution time (`sim_time=4000`, `cc_delay=100`, `qc_atten=1e-5`, `qc_dist=1`) is around 5 minutes. "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "3ac58091b34a48859e6116198cda0bd3",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"interactive(children=(IntSlider(value=3000, description='sim_time', max=4000, min=2000, step=500), FloatSlider…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<function __main__.test(sim_time, cc_delay, qc_atten, qc_dist)>"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"interactive_plot = interact(test, sim_time=(2000, 4000, 500), cc_delay=(0.1, 1, 0.1), qc_atten=[1e-5, 2e-5, 3e-5], qc_dist=(1, 10, 1))\n",
"interactive_plot"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Results\n",
"\n",
"We note that the number of entangled memories increses exponentially over time. This is due to memory expiration, with a coherence time of 0.3 seconds, as well as the consumption of memories for purification and swapping soon after they are entangled. We also see distinct layers of fidelities, reflecting the number of times an individual memory has been purified or swapped."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "test",
"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.12.7"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
Loading

0 comments on commit 8d77ddb

Please # to comment.