diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f3e8e149..a462ac82 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,9 +2,9 @@ name: main on: push: - branches: [master, RnD] + branches: [master, RnD, remove_mpi] pull_request: - branches: [master. RnD] + branches: [master. RnD, remove_mpi] jobs: build: @@ -17,20 +17,33 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - - name: Set up MPI - uses: mpi4py/setup-mpi@v1 - with: - mpi: ${{ matrix.mpi }} + - name: Install dependencies run: | pip install pytest make setup make install + - name: Test with pytest run: | make test + + - name: Set up MPI + uses: mpi4py/setup-mpi@v1 + with: + mpi: ${{ matrix.mpi }} + + - name: Set up parallel code + run: | + cd parallel + make install + + - name: Test parallel with pytest + run: | + cd parallel make test_parallel diff --git a/CHANGELOG.md b/CHANGELOG.md index 61ea2b9c..1326dca8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,3 +108,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Polished observer functionality - Reworked some protocols to utilize new interface - Some bug fixes for GUI + +## [0.5.1] +### Changed +- Moved all parallel execution code to the parallel directory + - Now installed as separate package `psequence` + - New setup.py file and makefile specified in parallel folder + - Minimum sequence requirement 0.5.1 +- Parallel scripts are temporarily broken, will need to be rewritten for new structure + +### Removed +- Removed `mpi4py` and `pytest-mpi` requirements for main package diff --git a/Makefile b/Makefile index 7260ece4..4e7a6265 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,4 @@ jupyter: test: pip3 install . - pytest ./tests - -test_parallel: - mpiexec -n 2 pytest ./tests_parallel --with-mpi \ No newline at end of file + pytest ./tests \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 276dc77c..dbf545b8 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.5.0' +release = '0.5.1' # -- General configuration --------------------------------------------------- diff --git a/docs/source/index.rst b/docs/source/index.rst index c8136515..d138c602 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -35,6 +35,12 @@ Welcome to the SeQUeNCe documentation page references/qkd/top references/misc/top +.. toctree:: + :maxdepth: 1 + :caption: Parallel Module References: + + references/parallel/top + Indices and tables ================== diff --git a/docs/source/references/kernel/top.rst b/docs/source/references/kernel/top.rst index a93e16a4..31a601d9 100644 --- a/docs/source/references/kernel/top.rst +++ b/docs/source/references/kernel/top.rst @@ -12,7 +12,4 @@ The Kernel module provides the discrete event simulator for SeQUeNCe. process timeline quantum_manager - p_timeline - quantum_manager_client - quantum_manager_server quantum_state diff --git a/docs/source/references/parallel/communication.rst b/docs/source/references/parallel/communication.rst new file mode 100644 index 00000000..94107fd4 --- /dev/null +++ b/docs/source/references/parallel/communication.rst @@ -0,0 +1,5 @@ +Process Communication +===================== + +.. automodule:: parallel.src.communication + :members: diff --git a/docs/source/references/parallel/p_quantum_manager.rst b/docs/source/references/parallel/p_quantum_manager.rst new file mode 100644 index 00000000..8daf78d7 --- /dev/null +++ b/docs/source/references/parallel/p_quantum_manager.rst @@ -0,0 +1,5 @@ +Parallel Quantum Manager +============================ + +.. automodule:: parallel.src.p_quantum_manager + :members: diff --git a/docs/source/references/parallel/p_router_net_topo.rst b/docs/source/references/parallel/p_router_net_topo.rst new file mode 100644 index 00000000..57d26409 --- /dev/null +++ b/docs/source/references/parallel/p_router_net_topo.rst @@ -0,0 +1,5 @@ +Parallel Router Net Topology +============================ + +.. automodule:: parallel.src.p_router_net_topo + :members: diff --git a/docs/source/references/kernel/p_timeline.rst b/docs/source/references/parallel/p_timeline.rst similarity index 56% rename from docs/source/references/kernel/p_timeline.rst rename to docs/source/references/parallel/p_timeline.rst index 1deff3de..00b0d5d2 100644 --- a/docs/source/references/kernel/p_timeline.rst +++ b/docs/source/references/parallel/p_timeline.rst @@ -1,5 +1,5 @@ Parallel Timeline ================= -.. automodule:: src.kernel.p_timeline +.. automodule:: parallel.src.p_timeline :members: diff --git a/docs/source/references/kernel/quantum_manager_client.rst b/docs/source/references/parallel/quantum_manager_client.rst similarity index 53% rename from docs/source/references/kernel/quantum_manager_client.rst rename to docs/source/references/parallel/quantum_manager_client.rst index f8ca721c..10d21a91 100644 --- a/docs/source/references/kernel/quantum_manager_client.rst +++ b/docs/source/references/parallel/quantum_manager_client.rst @@ -1,5 +1,5 @@ Quantum Manager Client ====================== -.. automodule:: src.kernel.quantum_manager_client +.. automodule:: parallel.src.quantum_manager_client :members: diff --git a/docs/source/references/kernel/quantum_manager_server.rst b/docs/source/references/parallel/quantum_manager_server.rst similarity index 53% rename from docs/source/references/kernel/quantum_manager_server.rst rename to docs/source/references/parallel/quantum_manager_server.rst index b42a22a5..a4651b00 100644 --- a/docs/source/references/kernel/quantum_manager_server.rst +++ b/docs/source/references/parallel/quantum_manager_server.rst @@ -1,5 +1,5 @@ Quantum Manager Server ====================== -.. automodule:: src.kernel.quantum_manager_server +.. automodule:: parallel.src.quantum_manager_server :members: diff --git a/docs/source/references/parallel/top.rst b/docs/source/references/parallel/top.rst new file mode 100644 index 00000000..5eb936c7 --- /dev/null +++ b/docs/source/references/parallel/top.rst @@ -0,0 +1,14 @@ +Parallel +======== + +The parallel `psequence` package provides a parallel execution implementation for SeQUeNCe. + +.. toctree:: + :maxdepth: 2 + + p_timeline + p_router_net_topo + p_quantum_manager + quantum_manager_client + quantum_manager_server + communication diff --git a/example/QCE_demos/modify_script_method_function.ipynb b/example/QCE_demos/modify_script_method_function.ipynb new file mode 100644 index 00000000..e03236ed --- /dev/null +++ b/example/QCE_demos/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/parallel/Makefile b/parallel/Makefile new file mode 100644 index 00000000..0b0602bf --- /dev/null +++ b/parallel/Makefile @@ -0,0 +1,8 @@ +install_no_pip: + python3 setup.py install + +install: + pip3 install . + +test_parallel: + mpiexec -n 2 pytest ./tests_parallel --with-mpi \ No newline at end of file diff --git a/parallel/example/net_with_flow_info.py b/parallel/example/net_with_flow_info.py index 6a846744..33977e54 100644 --- a/parallel/example/net_with_flow_info.py +++ b/parallel/example/net_with_flow_info.py @@ -5,7 +5,7 @@ from json import load, dump from sequence.app.request_app import RequestApp -from sequence.topology.router_net_topo import RouterNetTopo +from psequence.p_router_net_topo import ParallelRouterNetTopo def main(config_file: str, flow_info_file: str, log_path: str): @@ -21,7 +21,7 @@ def main(config_file: str, flow_info_file: str, log_path: str): mpi_rank = MPI.COMM_WORLD.Get_rank() mpi_size = MPI.COMM_WORLD.Get_size() - topo = RouterNetTopo(config_file) + topo = ParallelRouterNetTopo(config_file) tl = topo.get_timeline() tl.stop_time = STOP_TIME diff --git a/parallel/example/p_ring.py b/parallel/example/p_ring.py index 7e025765..04618ee4 100644 --- a/parallel/example/p_ring.py +++ b/parallel/example/p_ring.py @@ -4,10 +4,10 @@ from time import time import numpy as np -from sequence.kernel.p_timeline import ParallelTimeline from sequence.topology.node import QuantumRouter, BSMNode from sequence.components.optical_channel import ClassicalChannel, QuantumChannel from sequence.app.request_app import RequestApp +from psequence.p_timeline import ParallelTimeline import sequence.utils.log as log diff --git a/parallel/example/rand_flow_by_hop.py b/parallel/example/rand_flow_by_hop.py index e5ec5b24..7c6399c2 100644 --- a/parallel/example/rand_flow_by_hop.py +++ b/parallel/example/rand_flow_by_hop.py @@ -9,7 +9,7 @@ from sequence.kernel.event import Event from sequence.app.random_request import RandomRequestApp from sequence.app.request_app import RequestApp -from sequence.topology.router_net_topo import RouterNetTopo +from psequence.p_router_net_topo import ParallelRouterNetTopo import sequence.utils.log as log if TYPE_CHECKING: @@ -58,13 +58,13 @@ def get_net_qc_graph(config_file: str): config = load(fh) graph = {} - for node in config[RouterNetTopo.ALL_NODE]: - if node[RouterNetTopo.TYPE] == RouterNetTopo.QUANTUM_ROUTER: - graph[node[RouterNetTopo.NAME]] = [] + for node in config[ParallelRouterNetTopo.ALL_NODE]: + if node[ParallelRouterNetTopo.TYPE] == ParallelRouterNetTopo.QUANTUM_ROUTER: + graph[node[ParallelRouterNetTopo.NAME]] = [] bsm_to_router_map = {} - for qc in config[RouterNetTopo.ALL_Q_CHANNEL]: - router, bsm = qc[RouterNetTopo.SRC], qc[RouterNetTopo.DST] + for qc in config[ParallelRouterNetTopo.ALL_Q_CHANNEL]: + router, bsm = qc[ParallelRouterNetTopo.SRC], qc[ParallelRouterNetTopo.DST] if not bsm in bsm_to_router_map: bsm_to_router_map[bsm] = router else: @@ -116,7 +116,7 @@ def main(config_file: str, log_path: str): mpi_rank = MPI.COMM_WORLD.Get_rank() mpi_size = MPI.COMM_WORLD.Get_size() - topo = RouterNetTopo(config_file) + topo = ParallelRouterNetTopo(config_file) tl = topo.get_timeline() tl.stop_time = STOP_TIME diff --git a/parallel/example/test_phold.py b/parallel/example/test_phold.py index f3533ade..daf12f0a 100644 --- a/parallel/example/test_phold.py +++ b/parallel/example/test_phold.py @@ -1,8 +1,8 @@ from mpi4py import MPI from numpy.random import seed -from sequence.kernel.p_timeline import ParallelTimeline -from sequence.utils.phold import PholdNode +from psequence.phold import PholdNode +from psequence.p_timeline import ParallelTimeline if __name__ == '__main__': diff --git a/parallel/qm_server.py b/parallel/qm_server.py index fbbc57b1..9e3a0ef5 100644 --- a/parallel/qm_server.py +++ b/parallel/qm_server.py @@ -4,13 +4,12 @@ To change the formalism, add an argument to the `start_server` function (see the kernel/quantum_manager_server.py module). Arguments: - IP (str): ip address to listen on. + ip (str): ip address to listen on. port (int): port to listen on. client_num (int): number of quantum manager clients linked to the server. """ -from sequence.kernel.quantum_manager_server import start_server, valid_ip, \ - valid_port +from psequence.quantum_manager_server import start_server, valid_ip, valid_port import argparse if __name__ == '__main__': @@ -23,4 +22,3 @@ args = parser.parse_args() start_server(args.ip, args.port, args.client_num) - diff --git a/parallel/requirements.txt b/parallel/requirements.txt new file mode 100644 index 00000000..d9cd44c7 --- /dev/null +++ b/parallel/requirements.txt @@ -0,0 +1,7 @@ +numpy>=1.22 +pandas +qutip>=4.6.0 +tqdm>=4.54.0 +mpi4py +pytest-mpi +sequence>=0.5.0 \ No newline at end of file diff --git a/parallel/setup.py b/parallel/setup.py new file mode 100644 index 00000000..e0f7099a --- /dev/null +++ b/parallel/setup.py @@ -0,0 +1,24 @@ +from setuptools import setup + +setup( + name="psequence", + version="0.1.0", + 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="Parallel extension for SEQUENCE." + " SEQUENCE-Python is a prototype version of the official SEQUENCE release.", + packages=['psequence'], + package_dir={'psequence': 'src'}, + include_package_data=True, + install_requires=[ + 'numpy>=1.22', + 'pandas', + 'qutip>=4.6.0', + 'tqdm>=4.54.0', + 'mpi4py', + 'pytest-mpi', + 'sequence>=0.4.0' + ], +) diff --git a/parallel/src/__init__.py b/parallel/src/__init__.py new file mode 100644 index 00000000..f0c8fdb5 --- /dev/null +++ b/parallel/src/__init__.py @@ -0,0 +1,4 @@ +__all__ = ['p_quantum_manager', 'p_router_net_topo', 'p_timeline', 'quantum_manager_client', 'quantum_manager_server'] + +def __dir__(): + return sorted(__all__) diff --git a/src/utils/communication.py b/parallel/src/communication.py similarity index 100% rename from src/utils/communication.py rename to parallel/src/communication.py diff --git a/src/kernel/p_quantum_manager.py b/parallel/src/p_quantum_manager.py similarity index 86% rename from src/kernel/p_quantum_manager.py rename to parallel/src/p_quantum_manager.py index abd02462..5432d34a 100644 --- a/src/kernel/p_quantum_manager.py +++ b/parallel/src/p_quantum_manager.py @@ -6,9 +6,11 @@ The manager defines an API for interacting with quantum states. """ -from typing import List, Dict +from typing import List, Dict, TYPE_CHECKING +from sequence.kernel.quantum_manager import QuantumManagerKet, QuantumManagerDensity -from .quantum_manager import QuantumManagerKet, QuantumManagerDensity +if TYPE_CHECKING: + from sequence.components.circuit import Circuit class ParallelQuantumManagerKet(QuantumManagerKet): diff --git a/parallel/src/p_router_net_topo.py b/parallel/src/p_router_net_topo.py new file mode 100644 index 00000000..520750ab --- /dev/null +++ b/parallel/src/p_router_net_topo.py @@ -0,0 +1,48 @@ +from mpi4py import MPI +from sequence.topology.topology import Topology as Topo +from sequence.topology.router_net_topo import RouterNetTopo +from sequence.topology.node import QuantumRouter, BSMNode + +from .p_timeline import ParallelTimeline + + +class ParallelRouterNetTopo(RouterNetTopo): + def _add_timeline(self, config): + stop_time = config.get(Topo.STOP_TIME, float('inf')) + assert MPI.COMM_WORLD.Get_size() == config[self.PROC_NUM] + lookahead = config[self.LOOKAHEAD] + ip = config[self.IP] + port = config[self.PORT] + self.tl = ParallelTimeline(lookahead, qm_ip=ip, qm_port=port, stop_time=stop_time) + + def _add_nodes(self, config): + rank = MPI.COMM_WORLD.Get_rank() + size = MPI.COMM_WORLD.Get_size() + + for node in config[Topo.ALL_NODE]: + seed, type = node[Topo.SEED], node[Topo.TYPE], + group, name = node.get(self.GROUP, 0), node[Topo.NAME] + assert group < size, "Group id is out of scope" \ + " ({} >= {}).".format(group, size) + if group == rank: + if type == self.BSM_NODE: + others = self.bsm_to_router_map[name] + node_obj = BSMNode(name, self.tl, others) + elif type == self.QUANTUM_ROUTER: + memo_size = node.get(self.MEMO_ARRAY_SIZE, 0) + if memo_size: + node_obj = QuantumRouter(name, self.tl, memo_size) + else: + print("WARN: the size of memory on quantum router {} " + "is not set".format(name)) + node_obj = QuantumRouter(name, self.tl) + else: + raise NotImplementedError("Unknown type of node") + + node_obj.set_seed(seed) + if type in self.nodes: + self.nodes[type].append(node_obj) + else: + self.nodes[type] = [node_obj] + else: + self.tl.add_foreign_entity(name, group) diff --git a/src/kernel/p_timeline.py b/parallel/src/p_timeline.py similarity index 95% rename from src/kernel/p_timeline.py rename to parallel/src/p_timeline.py index 8383b6f5..0e651ab1 100644 --- a/src/kernel/p_timeline.py +++ b/parallel/src/p_timeline.py @@ -2,18 +2,17 @@ from mpi4py import MPI from time import time +from sequence.kernel.timeline import Timeline +from sequence.kernel.event import Event +from sequence.kernel.quantum_manager import KET_STATE_FORMALISM -from .timeline import Timeline -from .event import Event -from .quantum_manager import KET_STATE_FORMALISM from .quantum_manager_client import QuantumManagerClient -from .quantum_manager import KET_STATE_FORMALISM class ParallelTimeline(Timeline): """Class for a simulation timeline with parallel computation. - The Parallel Timeline acts behaves similarly to the Timeline class, maintianing and executing a queue of events. + The Parallel Timeline acts behaves similarly to the Timeline class, maintaining and executing a queue of events. There is one Parallel Timeline per simulation process. Each timeline controls a subset of the simulated network nodes. For events executed on nodes belonging to other timelines, an event buffer is maintained. @@ -23,7 +22,8 @@ class ParallelTimeline(Timeline): Attributes: id (int): rank of MPI process running the Parallel Timeline instance. foreign_entities (Dict[str, int]): mapping of object names on other processes to process id. - event_buffer(List[List[Event]]): stores events for execution on foreign entities; swapped during synchronization. + event_buffer(List[List[Event]]): stores events for execution on foreign entities; + swapped during synchronization. lookahead (int): defines width of time window for execution (simulation time between synchronization). quantum_manager (QuantumManagerClient): local quantum manager client to communicate with server. """ diff --git a/src/utils/phold.py b/parallel/src/phold.py similarity index 79% rename from src/utils/phold.py rename to parallel/src/phold.py index 51e2c565..8c4c7fd7 100644 --- a/src/utils/phold.py +++ b/parallel/src/phold.py @@ -1,17 +1,16 @@ from numpy.random import choice, exponential -from typing import TYPE_CHECKING +from typing import List, TYPE_CHECKING -from ..kernel.process import Process -from ..kernel.event import Event +from sequence.kernel.process import Process +from sequence.kernel.event import Event if TYPE_CHECKING: - from ..kernel.p_timeline import ParallelTimeline + from .p_timeline import ParallelTimeline -class PholdNode(): - +class PholdNode: def __init__(self, name: 'str', timeline: 'ParallelTimeline', - init_work: int, lookahead: int, neighbors: 'List[str]'): + init_work: int, lookahead: int, neighbors: List[str]): self.name = name self.timeline = timeline timeline.entities[name] = self diff --git a/src/kernel/quantum_manager_client.py b/parallel/src/quantum_manager_client.py similarity index 94% rename from src/kernel/quantum_manager_client.py rename to parallel/src/quantum_manager_client.py index bb4a13cd..38fc2786 100644 --- a/src/kernel/quantum_manager_client.py +++ b/parallel/src/quantum_manager_client.py @@ -6,23 +6,20 @@ """ from collections import defaultdict from socket import socket -from typing import List, TYPE_CHECKING, Any +from typing import List from time import time from uuid import uuid4 - -if TYPE_CHECKING: - from .p_timeline import ParallelTimeline - -from .quantum_manager import QuantumManagerKet, QuantumManagerDensity, KetState, \ +from sequence.kernel.quantum_manager import QuantumManagerKet, QuantumManagerDensity, KetState, \ KET_STATE_FORMALISM, DENSITY_MATRIX_FORMALISM +from sequence.components.circuit import Circuit +from .communication import send_msg_with_length, recv_msg_with_length + from .quantum_manager_server import QuantumManagerMsgType, \ QuantumManagerMessage -from ..components.circuit import Circuit -from ..utils.communication import send_msg_with_length, recv_msg_with_length -class QuantumManagerClient(): - """Class to pocess interactions with remote quantum manager server. +class QuantumManagerClient: + """Class to process interactions with remote quantum manager server. Unless otherwise noted, the operation of all functions are the same as those of the QuantumManagerClass. @@ -32,7 +29,6 @@ class QuantumManagerClient(): port (int): port of quantum manager server. socket (socket): socket for communication with server. managed_qubits (set): keys for all qubits managed locally by client. - timeline (ParallelTimeline): local timeline. message_buffer (List): list of messages to send to quantum manager server. """ @@ -201,11 +197,11 @@ def move_manage_to_server(self, qubit_key: int, sync_state=True): self._send_message(QuantumManagerMsgType.SET, state.keys, [state.state], False) - def move_manage_to_client(self, qubit_keys: List[int], amplitute: Any): + def move_manage_to_client(self, qubit_keys: List[int], amplitude: any): assert all(qubit_key in self.qm.states for qubit_key in qubit_keys) for key in qubit_keys: self.managed_qubits.add(key) - self.qm.set(qubit_keys, amplitute) + self.qm.set(qubit_keys, amplitude) def _send_message(self, msg_type, keys: List, args: List, expecting_receive=True) -> any: diff --git a/src/kernel/quantum_manager_server.py b/parallel/src/quantum_manager_server.py similarity index 94% rename from src/kernel/quantum_manager_server.py rename to parallel/src/quantum_manager_server.py index 37d60658..7524745a 100644 --- a/src/kernel/quantum_manager_server.py +++ b/parallel/src/quantum_manager_server.py @@ -5,7 +5,7 @@ Additionally defined are utility functions for socket connections and the messages used by the client/server. """ -from enum import Enum, auto +from enum import Enum import socket import argparse from ipaddress import ip_address @@ -13,11 +13,10 @@ from typing import List from time import time from json import dump +from .communication import send_msg_with_length, recv_msg_with_length +from sequence.components.circuit import Circuit -from .p_quantum_manager import ParallelQuantumManagerKet, \ - ParallelQuantumManagerDensity -from ..utils.communication import send_msg_with_length, recv_msg_with_length -from ..components.circuit import Circuit +from .p_quantum_manager import ParallelQuantumManagerKet, ParallelQuantumManagerDensity def valid_port(port): @@ -54,7 +53,7 @@ class QuantumManagerMsgType(Enum): SYNC = 8 -class QuantumManagerMessage(): +class QuantumManagerMessage: """Message for quantum manager communication. Attributes: @@ -63,8 +62,7 @@ class QuantumManagerMessage(): args (List[any]): list of other arguments for the request. """ - def __init__(self, msg_type: QuantumManagerMsgType, keys: 'List[int]', - args: 'List[Any]'): + def __init__(self, msg_type: QuantumManagerMsgType, keys: List[int], args: List[any]): self.type = msg_type self.keys = keys self.args = args @@ -142,8 +140,7 @@ def deserialize(self, j_data): self.type = QuantumManagerMsgType.SYNC -def start_server(ip: str, port: int, client_num, formalism="KET", - log_file="server_log.json"): +def start_server(ip: str, port: int, client_num, formalism="KET", log_file="server_log.json"): """Main function to run quantum manager server. Will run until all clients have disconnected or `TERMINATE` message received. diff --git a/tests_parallel/kernel/test_p_timeline.py b/parallel/tests_parallel/kernel/test_p_timeline.py similarity index 96% rename from tests_parallel/kernel/test_p_timeline.py rename to parallel/tests_parallel/kernel/test_p_timeline.py index 1675c774..ce921b09 100644 --- a/tests_parallel/kernel/test_p_timeline.py +++ b/parallel/tests_parallel/kernel/test_p_timeline.py @@ -1,10 +1,11 @@ import pytest from mpi4py import MPI -from sequence.kernel.p_timeline import ParallelTimeline from sequence.kernel.entity import Entity from sequence.kernel.process import Process from sequence.kernel.event import Event +from psequence.p_timeline import ParallelTimeline + rank = MPI.COMM_WORLD.Get_rank() size = MPI.COMM_WORLD.Get_size() diff --git a/requirements.txt b/requirements.txt index fc65d277..1b7afc7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,4 @@ plotly pandas qutip>=4.6.0 tqdm>=4.54.0 -networkx -mpi4py -pytest-mpi \ No newline at end of file +networkx \ No newline at end of file diff --git a/setup.py b/setup.py index ea59f9e7..90825916 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="sequence", - version="0.5.0", + version="0.5.1", 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.", @@ -27,7 +27,5 @@ 'dash-table', 'dash-cytoscape', 'plotly', - 'mpi4py', - 'pytest-mpi', ], ) diff --git a/src/entanglement_management/purification.py b/src/entanglement_management/purification.py index 2d7b6731..a2a801c7 100644 --- a/src/entanglement_management/purification.py +++ b/src/entanglement_management/purification.py @@ -174,7 +174,7 @@ def received_message(self, src: str, msg: BBPSSWMessage) -> None: self.update_resource_manager(self.meas_memo, "RAW") if self.meas_res == msg.meas_res: - self.kept_memo.fidelity = self.improved_fidelity(self.kept_memo.fidelity) + self.kept_memo.fidelity = BBPSSW.improved_fidelity(self.kept_memo.fidelity) self.update_resource_manager(self.kept_memo, state="ENTANGLED") else: self.update_resource_manager(self.kept_memo, state="RAW") @@ -199,19 +199,6 @@ def memory_expire(self, memory: "Memory") -> None: def release(self) -> None: pass - @staticmethod - @lru_cache(maxsize=128) - def success_probability(F: float) -> float: - """Method to calculate probability of purification success. - - Formula comes from Dur and Briegel (2007) page 14. - - Args: - F (float): fidelity of entanglement. - """ - - return F ** 2 + 2 * F * (1 - F) / 3 + 5 * ((1 - F) / 3) ** 2 - @staticmethod @lru_cache(maxsize=128) def improved_fidelity(F: float) -> float: diff --git a/src/kernel/__init__.py b/src/kernel/__init__.py index ef0cb062..a196f15e 100644 --- a/src/kernel/__init__.py +++ b/src/kernel/__init__.py @@ -1,4 +1,4 @@ -__all__ = ['entity', 'event', 'eventlist', 'process', 'quantum_manager', 'quantum_utils', 'timeline'] +__all__ = ['entity', 'event', 'eventlist', 'process', 'quantum_manager', 'quantum_state', 'quantum_utils', 'timeline'] def __dir__(): return sorted(__all__) diff --git a/src/topology/router_net_topo.py b/src/topology/router_net_topo.py index a95a828d..94ad3d27 100644 --- a/src/topology/router_net_topo.py +++ b/src/topology/router_net_topo.py @@ -1,11 +1,9 @@ from json import load -from mpi4py import MPI from networkx import Graph, dijkstra_path, exception from numpy import mean from .topology import Topology as Topo from ..kernel.timeline import Timeline -from ..kernel.p_timeline import ParallelTimeline from .node import BSMNode, QuantumRouter @@ -16,7 +14,7 @@ class RouterNetTopo(Topo): nodes, quantum channels, classical channels and timeline for simulation could be generated by using this class. Different processes in the parallel simulation could use the same configuration file to generate network - requrired for the parallel simulation. + required for the parallel simulation. Attributes: bsm_to_router_map (Dict[str, List[Node]]): mapping of bsm node to two connected routers @@ -55,16 +53,12 @@ def _load(self, filename): self._add_qchannels(config) self._add_cchannels(config) self._add_cconnections(config) - self._generate_forwaring_table(config) + self._generate_forwarding_table(config) def _add_timeline(self, config): stop_time = config.get(Topo.STOP_TIME, float('inf')) if config.get(self.IS_PARALLEL, False): - assert MPI.COMM_WORLD.Get_size() == config[self.PROC_NUM] - lookahead = config[self.LOOKAHEAD] - ip = config[self.IP] - port = config[self.PORT] - self.tl = ParallelTimeline(lookahead, qm_ip=ip, qm_port=port, stop_time=stop_time) + raise Exception("Please install 'psequence' package for parallel simulations.") else: self.tl = Timeline(stop_time) @@ -77,36 +71,29 @@ def _map_bsm_routers(self, config): self.bsm_to_router_map[dst] = [src] def _add_nodes(self, config): - rank = MPI.COMM_WORLD.Get_rank() - size = MPI.COMM_WORLD.Get_size() - for node in config[Topo.ALL_NODE]: - seed, type = node[Topo.SEED], node[Topo.TYPE], - group, name = node.get(self.GROUP, 0), node[Topo.NAME] - assert group < size, "Group id is out of scope" \ - " ({} >= {}).".format(group, size) - if group == rank: - if type == self.BSM_NODE: - others = self.bsm_to_router_map[name] - node_obj = BSMNode(name, self.tl, others) - elif type == self.QUANTUM_ROUTER: - memo_size = node.get(self.MEMO_ARRAY_SIZE, 0) - if memo_size: - node_obj = QuantumRouter(name, self.tl, memo_size) - else: - print("WARN: the size of memory on quantum router {} " - "is not set".format(name)) - node_obj = QuantumRouter(name, self.tl) + seed = node[Topo.SEED] + node_type = node[Topo.TYPE] + name = node[Topo.NAME] + + if node_type == self.BSM_NODE: + others = self.bsm_to_router_map[name] + node_obj = BSMNode(name, self.tl, others) + elif node_type == self.QUANTUM_ROUTER: + memo_size = node.get(self.MEMO_ARRAY_SIZE, 0) + if memo_size: + node_obj = QuantumRouter(name, self.tl, memo_size) else: - raise NotImplementedError("Unknown type of node") + print("WARN: the size of memory on quantum router {} is not set".format(name)) + node_obj = QuantumRouter(name, self.tl) + else: + raise NotImplementedError("Unknown type of node") - node_obj.set_seed(seed) - if type in self.nodes: - self.nodes[type].append(node_obj) - else: - self.nodes[type] = [node_obj] + node_obj.set_seed(seed) + if node_type in self.nodes: + self.nodes[node_type].append(node_obj) else: - self.tl.add_foreign_entity(name, group) + self.nodes[node_type] = [node_obj] def _add_bsm_node_to_router(self): for bsm in self.bsm_to_router_map: @@ -124,7 +111,7 @@ def _add_qconnections(self, config): node2 = q_connect[Topo.CONNECT_NODE_2] attenuation = q_connect[Topo.ATTENUATION] distance = q_connect[Topo.DISTANCE] // 2 - type = q_connect[Topo.TYPE] + channel_type = q_connect[Topo.TYPE] cc_delay = [] for cc in config.get(self.ALL_C_CHANNEL, []): if cc[self.SRC] == node1 and cc[self.DST] == node2: @@ -143,7 +130,7 @@ def _add_qconnections(self, config): if len(cc_delay) == 0: assert 0, q_connect cc_delay = mean(cc_delay) // 2 - if type == self.MEET_IN_THE_MID: + if channel_type == self.MEET_IN_THE_MID: bsm_name = "BSM.{}.{}.auto".format(node1, node2) bsm_info = {self.NAME: bsm_name, self.TYPE: self.BSM_NODE, @@ -157,7 +144,7 @@ def _add_qconnections(self, config): self.DST: bsm_name, self.DISTANCE: distance, self.ATTENUATION: attenuation} - if not self.ALL_Q_CHANNEL in config: + if self.ALL_Q_CHANNEL not in config: config[self.ALL_Q_CHANNEL] = [] config[self.ALL_Q_CHANNEL].append(qc_info) @@ -167,7 +154,7 @@ def _add_qconnections(self, config): self.DST: bsm_name, self.DISTANCE: distance, self.DELAY: cc_delay} - if not self.ALL_C_CHANNEL in config: + if self.ALL_C_CHANNEL not in config: config[self.ALL_C_CHANNEL] = [] config[self.ALL_C_CHANNEL].append(cc_info) @@ -181,7 +168,7 @@ def _add_qconnections(self, config): else: raise NotImplementedError("Unknown type of quantum connection") - def _generate_forwaring_table(self, config): + def _generate_forwarding_table(self, config): graph = Graph() for node in config[Topo.ALL_NODE]: if node[Topo.TYPE] == self.QUANTUM_ROUTER: @@ -191,7 +178,7 @@ def _generate_forwaring_table(self, config): if config[self.IS_PARALLEL]: for qc in config[self.ALL_Q_CHANNEL]: router, bsm = qc[self.SRC], qc[self.DST] - if not bsm in costs: + if bsm not in costs: costs[bsm] = [router, qc[self.DISTANCE]] else: costs[bsm] = [router] + costs[bsm] @@ -199,7 +186,7 @@ def _generate_forwaring_table(self, config): else: for qc in self.qchannels: router, bsm = qc.sender.name, qc.receiver - if not bsm in costs: + if bsm not in costs: costs[bsm] = [router, qc.distance] else: costs[bsm] = [router] + costs[bsm] diff --git a/src/utils/__init__.py b/src/utils/__init__.py index df375a74..1f596578 100644 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -1,4 +1,4 @@ -__all__ = ['encoding', 'quantum_state', 'log', 'phold'] +__all__ = ['encoding', 'log'] def __dir__(): return sorted(__all__) diff --git a/tests/entanglement_management/test_purification.py b/tests/entanglement_management/test_purification.py index 1080e62f..593c37ce 100644 --- a/tests/entanglement_management/test_purification.py +++ b/tests/entanglement_management/test_purification.py @@ -23,6 +23,10 @@ BELL_STATES = [phi_plus, phi_minus, psi_plus, psi_minus] +def success_probability(F: float) -> float: + return F ** 2 + 2 * F * (1 - F) / 3 + 5 * ((1 - F) / 3) ** 2 + + class FakeResourceManager: def __init__(self, owner): self.log = [] @@ -669,4 +673,4 @@ def test_BBPSSW_success_rate(): tl.run() - assert abs(counter1 / (counter1 + counter2) - BBPSSW.success_probability(fidelity)) < 0.1 + assert abs(counter1 / (counter1 + counter2) - success_probability(fidelity)) < 0.1 diff --git a/utils/qmanager_timing.py b/utils/qmanager_timing.py index 08315940..3abde2f8 100644 --- a/utils/qmanager_timing.py +++ b/utils/qmanager_timing.py @@ -2,8 +2,7 @@ import multiprocessing import numpy as np -from sequence.kernel.quantum_manager_server import generate_arg_parser, start_server, kill_server, \ - QuantumManagerMessage, QuantumManagerMsgType +from psequence.quantum_manager_server import generate_arg_parser, start_server from sequence.kernel.quantum_manager_client import QuantumManagerClient from sequence.components.circuit import Circuit @@ -15,7 +14,7 @@ def client_function(ip, port): key = client.new() # send request to get state - ket_vec = client.get(key) + _ = client.get(key) # run Hadamard gate circ = Circuit(1) @@ -23,7 +22,7 @@ def client_function(ip, port): client.run_circuit(circ, [key]) # get state again to verify - ket_vec = client.get(key) + _ = client.get(key) NUM_TRIALS = 10 @@ -45,8 +44,7 @@ def client_function(ip, port): print("\ttime:", end - start) times.append(end - start) -# close server -kill_server(args.ip, args.port) +p.kill() print("average time:", np.mean(times))