From f2a858d2e9ed9fbcbe3350cac631daeba931d53f Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Tue, 13 Sep 2022 17:24:58 -0500 Subject: [PATCH 01/17] Fix purification.py fidelity update; remove success probability --- src/entanglement_management/purification.py | 15 +-------------- .../entanglement_management/test_purification.py | 6 +++++- 2 files changed, 6 insertions(+), 15 deletions(-) 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/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 From 5cc5d8d2b74df72ca350c3ead13873c4f6859ce0 Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Tue, 13 Sep 2022 17:43:34 -0500 Subject: [PATCH 02/17] Move some modules to parallel folder; create p_router_net_topo.py --- parallel/src/p_quantum_manager.py | 43 +++++ parallel/src/p_router_net_topo.py | 48 +++++ parallel/src/p_timeline.py | 137 ++++++++++++++ parallel/src/quantum_manager_client.py | 249 +++++++++++++++++++++++++ parallel/src/quantum_manager_server.py | 249 +++++++++++++++++++++++++ src/kernel/p_timeline.py | 1 - 6 files changed, 726 insertions(+), 1 deletion(-) create mode 100644 parallel/src/p_quantum_manager.py create mode 100644 parallel/src/p_router_net_topo.py create mode 100644 parallel/src/p_timeline.py create mode 100644 parallel/src/quantum_manager_client.py create mode 100644 parallel/src/quantum_manager_server.py diff --git a/parallel/src/p_quantum_manager.py b/parallel/src/p_quantum_manager.py new file mode 100644 index 00000000..abd02462 --- /dev/null +++ b/parallel/src/p_quantum_manager.py @@ -0,0 +1,43 @@ +"""This module defines the quantum manager class, to track quantum states. + +The states may currently be defined in two possible ways: + - KetState (with the QuantumManagerKet class) + - DensityMatrix (with the QuantumManagerDensity class) + +The manager defines an API for interacting with quantum states. +""" +from typing import List, Dict + +from .quantum_manager import QuantumManagerKet, QuantumManagerDensity + + +class ParallelQuantumManagerKet(QuantumManagerKet): + """Class to track and manage quantum states with the ket vector formalism.""" + + def __init__(self, states): + super().__init__() + self.states = states + + def run_circuit(self, circuit: "Circuit", keys: List[int], + meas_samp=None) -> Dict[int, int]: + ret_dict = super().run_circuit(circuit, keys, meas_samp) + return ret_dict + + def remove(self, key: int) -> None: + del self.states[key] + + +class ParallelQuantumManagerDensity(QuantumManagerDensity): + """Class to track and manage states with the density matrix formalism.""" + + def __init__(self, states): + super().__init__() + self.states = states + + def run_circuit(self, circuit: "Circuit", keys: List[int], + meas_samp=None) -> Dict[int, int]: + ret_dict = super().run_circuit(circuit, keys, meas_samp) + return ret_dict + + def remove(self, key: int) -> None: + del self.states[key] 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/parallel/src/p_timeline.py b/parallel/src/p_timeline.py new file mode 100644 index 00000000..259b7dae --- /dev/null +++ b/parallel/src/p_timeline.py @@ -0,0 +1,137 @@ +from typing import List + +from mpi4py import MPI +from time import time + +from .timeline import Timeline +from .event import Event +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. + 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. + These buffers are exchanged between timelines at regular synchronization intervals. + All Parallel Timelines in a simulation communicate with a Quantum Manager Server for shared quantum states. + + 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. + lookahead (int): defines width of time window for execution (simulation time between synchronization). + quantum_manager (QuantumManagerClient): local quantum manager client to communicate with server. + """ + + def __init__(self, lookahead: int, stop_time=float('inf'), formalism=KET_STATE_FORMALISM, + qm_ip=None, qm_port=None): + """Constructor for the ParallelTimeline class. + + Also creates a quantum manager client, unless `qm_ip` and `qm_port` are both set to None. + + Args: + lookahead (int): sets the timeline lookahead time. + stop_time (int): stop (simulation) time of simulation (default inf). + formalism (str): formalism to use for storing quantum states (default 'KET'). + qm_ip (str): IP address for the quantum manager server (default None). + qm_port (int): port to connect to for quantum manager server (default None). + """ + + super(ParallelTimeline, self).__init__(stop_time, formalism) + + self.id = MPI.COMM_WORLD.Get_rank() + self.foreign_entities = {} + self.event_buffer = [[] for _ in range(MPI.COMM_WORLD.Get_size())] + self.lookahead = lookahead + if qm_ip is not None and qm_port is not None: + self.quantum_manager = QuantumManagerClient(formalism, qm_ip, qm_port) + + self.show_progress = False + + self.buffer_min_ts = float('inf') + + self.sync_counter = 0 + self.exchange_counter = 0 + self.computing_time = 0 + self.communication_time = 0 + + def schedule(self, event: 'Event'): + """Method to schedule an event.""" + + if type(event.process.owner) is str \ + and event.process.owner in self.foreign_entities: + self.buffer_min_ts = min(self.buffer_min_ts, event.time) + tl_id = self.foreign_entities[event.process.owner] + self.event_buffer[tl_id].append(event) + self.schedule_counter += 1 + else: + super(ParallelTimeline, self).schedule(event) + + def top_time(self) -> float: + """Method to get the timestamp of the soonest event in the local queue. + + Used for the conservative synchronization algorithm. + If the event queue is empty, returns infinity. + """ + + if len(self.events) > 0: + return self.events.top().time + else: + return float('inf') + + def run(self): + while self.time < self.stop_time: + tick = time() + min_time = min(self.buffer_min_ts, self.top_time()) + for buf in self.event_buffer: + buf.append(min_time) + inbox = MPI.COMM_WORLD.alltoall(self.event_buffer) + self.communication_time += time() - tick + + for buff in self.event_buffer: + buff.clear() + self.buffer_min_ts = float('inf') + + for events in inbox: + min_time = min(min_time, events.pop()) + for event in events: + self.exchange_counter += 1 + self.schedule(event) + + assert min_time >= self.time + + if min_time >= self.stop_time: + break + + self.sync_counter += 1 + + sync_time = min(min_time + self.lookahead, self.stop_time) + self.time = min_time + + tick = time() + while len(self.events) > 0 and self.events.top().time < sync_time: + event = self.events.pop() + if event.is_invalid(): + continue + assert self.time <= event.time, "invalid event time for process scheduled on " + str( + event.process.owner) + self.time = event.time + event.process.run() + self.run_counter += 1 + if isinstance(self.quantum_manager, QuantumManagerClient): + self.quantum_manager.flush_before_sync() + self.computing_time += time() - tick + + def add_foreign_entity(self, entity_name: str, foreign_id: int): + """Adds the name of an entity on another parallel timeline. + + Args: + entity_name (str): name of the entity on another parallel timeline. + foreign_id (int): id of the process containing the entity. + """ + + self.foreign_entities[entity_name] = foreign_id diff --git a/parallel/src/quantum_manager_client.py b/parallel/src/quantum_manager_client.py new file mode 100644 index 00000000..bb4a13cd --- /dev/null +++ b/parallel/src/quantum_manager_client.py @@ -0,0 +1,249 @@ +"""This module defines the QuantumManagerClient class. + +This client provides the same interface as the QuantumManager class for manipulating qubits. +Qubits only managed by the local process are stored within a QuantumManager class instance. +Qubits managed or accessed between processes are stored on a remote quantum manager server. +""" +from collections import defaultdict +from socket import socket +from typing import List, TYPE_CHECKING, Any +from time import time +from uuid import uuid4 + +if TYPE_CHECKING: + from .p_timeline import ParallelTimeline + +from .quantum_manager import QuantumManagerKet, QuantumManagerDensity, KetState, \ + KET_STATE_FORMALISM, DENSITY_MATRIX_FORMALISM +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. + + Unless otherwise noted, the operation of all functions are the same as those of the QuantumManagerClass. + + Attributes: + formalism (str): formalism to use for quantum manager (must match server). + ip (str): ip address of quantum manager server. + 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. + """ + + def __init__(self, formalism: str, ip: str, port: int): + """Constructor for QuantumManagerClient class. + + Args: + formalism (str): formalism to use for quantum manager. + ip (str): ip of quantum manager server. + port (int): port of quantum manager server. + """ + self.formalism = formalism + self.ip = ip + self.port = port + self.socket = socket() + self.managed_qubits = set() + self.io_time = 0 + self.type_counter = defaultdict(lambda: 0) + self.client_call_counter = 0 + self.message_buffer = [] + + self.socket.connect((self.ip, self.port)) + self.socket.settimeout(20) + + # local quantum manager + if formalism == KET_STATE_FORMALISM: + self.qm = QuantumManagerKet() + elif formalism == DENSITY_MATRIX_FORMALISM: + self.qm = QuantumManagerDensity() + else: + raise Exception("Invalid formalism {} given".format(formalism)) + + def get_socket_to_server(self) -> "socket": + return self.socket + + def disconnect_from_server(self): + """Closes socket connection with quantum manager server. + + Uses a message of type `QuantumManagerServer.CLOSE`. + + Side Effects: + closes socket attribute connection. + """ + + self._send_message(QuantumManagerMsgType.CLOSE, [], [], + expecting_receive=False) + self.flush_message_buffer() + + def new(self, state=(complex(1), complex(0))) -> int: + """Method to get a new state from server. + + Args: + state (List): if None, state will be in default state. Otherwise, must match formalism of server. + + Returns: + int: key for the new state generated. + """ + self.client_call_counter += 1 + key = uuid4().int + # below code cannot be removed because of the assertion in the + # move_manage_to_client function + self.qm.set([key], state) + self.move_manage_to_client([key], state) + return key + + def get(self, key: int) -> any: + self.client_call_counter += 1 + if self._check_local([key]): + return self.qm.get(key) + else: + state_raw = self._send_message(QuantumManagerMsgType.GET, [key], + []) + state = KetState([0, 1], [0]) + state.deserialize(state_raw) + return state + + def run_circuit(self, circuit: "Circuit", keys: List[int], meas_samp=None) -> any: + self.client_call_counter += 1 + if self._check_local(keys): + return self.qm.run_circuit(circuit, keys, meas_samp) + else: + visited_qubits = set() + for key in keys: + if key in visited_qubits: + continue + if self.is_managed_by_server(key): + visited_qubits.add(key) + else: + state = self.qm.get(key) + for state_key in state.keys: + visited_qubits.add(state_key) + assert not self.is_managed_by_server(state_key) + self.move_manage_to_server(state.keys[0]) + # todo: move qubit to client if all keys of entangled qubits belong + # to the client + if len(circuit.measured_qubits) == 0: + self._send_message(QuantumManagerMsgType.RUN, + list(visited_qubits), + [circuit, keys], False) + return {} + + ret_val_raw = self._send_message(QuantumManagerMsgType.RUN, + list(visited_qubits), + [circuit, keys, meas_samp]) + + ret_val = {} + for key in ret_val_raw: + ret_val[int(key, 16)] = ret_val_raw[key] + + for measured_q in ret_val: + if not measured_q in self.qm.states: + continue + if ret_val[measured_q] == 1: + self.move_manage_to_client([measured_q], [0, 1]) + else: + self.move_manage_to_client([measured_q], [1, 0]) + return ret_val + + def set(self, keys: List[int], amplitudes: any) -> None: + self.client_call_counter += 1 + if self._check_local(keys): + self.qm.set(keys, amplitudes) + elif all(key in self.qm.states for key in keys): + self.move_manage_to_client(keys, amplitudes) + else: + for key in keys: + self.move_manage_to_server(key, sync_state=False) + self._send_message(QuantumManagerMsgType.SET, keys, [amplitudes], + False) + + def remove(self, key: int) -> None: + self.client_call_counter += 1 + self._send_message(QuantumManagerMsgType.REMOVE, [key], []) + self.qm.remove(key) + + def kill(self) -> None: + """Method to terminate the connected server. + + Uses a message of type `QuantumManagerMsgType.TERMINATE`. + + Side Effects: + Will end all processes on the remote server. + """ + self.client_call_counter += 1 + self._send_message(QuantumManagerMsgType.TERMINATE, [], [], + expecting_receive=False) + + def is_managed_by_server(self, qubit_key: int) -> bool: + return qubit_key not in self.managed_qubits + + def move_manage_to_server(self, qubit_key: int, sync_state=True): + """Move management of a qubit from the local quantum manager to quantum manager server. + + Args: + qubit_key (int): qubit key to move. + sync_state (bool): indicates if server state should be set to match local (default `True`). + """ + + if self.is_managed_by_server(qubit_key): + return + if qubit_key in self.qm.states: + state = self.qm.get(qubit_key) + for key in state.keys: + self.managed_qubits.remove(key) + if sync_state: + self._send_message(QuantumManagerMsgType.SET, state.keys, + [state.state], False) + + def move_manage_to_client(self, qubit_keys: List[int], amplitute: 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) + + def _send_message(self, msg_type, keys: List, args: List, + expecting_receive=True) -> any: + """Sends a message to the remote quantum manager server. + + Args: + msg_type (QuantumManagerMsgType): type of message to send. + keys (List[int]): list of all keys affected by message process. + args (List[any]): any other arguments used for the message. + expecting_receive (bool): indicates if client should block until response received (default `True`). + + Returns: + any: result of process on quantum manager server (if `expecting_receive` is `True`). + """ + + self.type_counter[msg_type.name] += 1 + + msg = QuantumManagerMessage(msg_type, keys, args) + self.message_buffer.append(msg) + + if expecting_receive: + self.flush_message_buffer() + tick = time() + received_msg = recv_msg_with_length(self.socket) + self.io_time += time() - tick + return received_msg + + def flush_message_buffer(self): + if len(self.message_buffer) > 0: + tick = time() + msgs = [msg.serialize() for msg in self.message_buffer] + send_msg_with_length(self.socket, msgs) + self.io_time += time() - tick + self.message_buffer = [] + + def flush_before_sync(self): + if len(self.message_buffer) > 0: + self._send_message(QuantumManagerMsgType.SYNC, [], []) + + def _check_local(self, keys: List[int]): + return not any([self.is_managed_by_server(key) for key in keys]) diff --git a/parallel/src/quantum_manager_server.py b/parallel/src/quantum_manager_server.py new file mode 100644 index 00000000..37d60658 --- /dev/null +++ b/parallel/src/quantum_manager_server.py @@ -0,0 +1,249 @@ +"""This module defines the function to use for the quantum manager server. + +This function should be started on a separate process for parallel simulation, \ + using the `mpi_tests/qm_server.py` script or similar. +Additionally defined are utility functions for socket connections and the messages used by the client/server. +""" + +from enum import Enum, auto +import socket +import argparse +from ipaddress import ip_address +import select +from typing import List +from time import time +from json import dump + +from .p_quantum_manager import ParallelQuantumManagerKet, \ + ParallelQuantumManagerDensity +from ..utils.communication import send_msg_with_length, recv_msg_with_length +from ..components.circuit import Circuit + + +def valid_port(port): + port = int(port) + if 1 <= port <= 65535: + return port + else: + raise argparse.ArgumentTypeError( + '%d is not a valid port number' % port) + + +def valid_ip(ip): + _ip = ip_address(ip) + return ip + + +def generate_arg_parser(): + # TODO: delete? + parser = argparse.ArgumentParser(description='The server of quantum manager') + parser.add_argument('ip', type=valid_ip, help='listening IP address') + parser.add_argument('port', type=valid_port, help='listening port number') + return parser + + +class QuantumManagerMsgType(Enum): + GET = 0 + SET = 1 + RUN = 2 + REMOVE = 3 + TERMINATE = 4 + CLOSE = 5 + CONNECT = 6 + CONNECTED = 7 + SYNC = 8 + + +class QuantumManagerMessage(): + """Message for quantum manager communication. + + Attributes: + type (Enum): type of message. + keys (List[int]): list of ALL keys serviced by request. + args (List[any]): list of other arguments for the request. + """ + + def __init__(self, msg_type: QuantumManagerMsgType, keys: 'List[int]', + args: 'List[Any]'): + self.type = msg_type + self.keys = keys + self.args = args + + def __repr__(self): + return str(self.type) + ' ' + str(self.args) + + def serialize(self): + """Serializes the data stored in the message. + + Returns: + Dict[str, any]: A dictionary with the following keys: + - "type": name of the type for the message. + - "keys": keys for all modified qubits in the quantum state manager, in hex format. + - "args": JSON-like serialized version of arguments + """ + + hex_keys = [hex(key) for key in self.keys] + + args = {} + if self.type == QuantumManagerMsgType.SET: + amplitudes = [] + for cplx_n in self.args[0]: + if type(cplx_n) == float: + amplitudes.append(cplx_n) + amplitudes.append(0) + else: + amplitudes.append(cplx_n.real) + amplitudes.append(cplx_n.imag) + + args["amplitudes"] = amplitudes + + elif self.type == QuantumManagerMsgType.RUN: + args["circuit"] = self.args[0].serialize() + args["keys"] = [hex(key) for key in self.args[1]] + args["meas_samp"] = -1 + if len(self.args) > 2: + args["meas_samp"] = self.args[2] + + return {"type": self.type.name, "keys": hex_keys, "args": args} + + def deserialize(self, j_data): + """Method to reconstruct a message from serialized data. + + Args: + j_data: serialized QuantumManagerMessage data. + """ + + self.keys = j_data["keys"] + + if j_data["type"] == "GET": + self.type = QuantumManagerMsgType.GET + + elif j_data["type"] == "SET": + self.type = QuantumManagerMsgType.SET + cmplx_n_list = [] + for i in range(0, len(j_data["args"]["amplitudes"]), 2): + cmplx = complex(j_data["args"]["amplitudes"][i], + j_data["args"]["amplitudes"][i + 1]) + cmplx_n_list.append(cmplx) + self.args = [cmplx_n_list] + elif j_data["type"] == "RUN": + self.type = QuantumManagerMsgType.RUN + # use hex string as key temporarily + c_raw = j_data["args"]["circuit"] + circuit = Circuit(1) + circuit.deserialize(c_raw) + keys = j_data["args"]["keys"] + meas_samp = j_data["args"]["meas_samp"] + self.args = [circuit, keys, meas_samp] + + elif j_data["type"] == "CLOSE": + self.type = QuantumManagerMsgType.CLOSE + elif j_data["type"] == "SYNC": + self.type = QuantumManagerMsgType.SYNC + + +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. + Will block processing until all clients connected. + + Args: + ip (str): ip address server should bind to. + port (int): port server should bind to. + client_num (int): number of remote clients that should be connected (one per process). + formalism (str): formalism to use for quantum manager (default `"KET"` for ket vector). + log_file (str): output log file to store server information (default `"server_log.json"`). + """ + + s = socket.socket() + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind((ip, port)) + s.listen() + print("listening at:", ip, port) + + timing_comp = {} + traffic_counter = 0 + msg_counter = 0 + + # initialize shared data + if formalism == "KET": + qm = ParallelQuantumManagerKet({}) + elif formalism == "DENSITY": + qm = ParallelQuantumManagerDensity({}) + + sockets = [] + for _ in range(client_num): + c, addr = s.accept() + sockets.append(c) + + while sockets: + readable, writeable, exceptional = select.select(sockets, [], [], 1) + for s in readable: + msgs = recv_msg_with_length(s) + + traffic_counter += 1 + msg_counter += len(msgs) + + for m_raw in msgs: + msg = QuantumManagerMessage(None, [], []) + msg.deserialize(m_raw) + return_val = None + + tick = time() + if msg.type == QuantumManagerMsgType.CLOSE: + s.close() + sockets.remove(s) + break + + elif msg.type == QuantumManagerMsgType.GET: + assert len(msg.args) == 0 + state = qm.get(msg.keys[0]) + return_val = state.serialize() + + elif msg.type == QuantumManagerMsgType.RUN: + assert len(msg.args) == 2 or len(msg.args) == 3 + circuit, keys, meas_samp = msg.args + return_val = qm.run_circuit(circuit, keys, meas_samp) + if len(return_val) == 0: + return_val = None + + elif msg.type == QuantumManagerMsgType.SET: + assert len(msg.args) == 1 + qm.set(msg.keys, msg.args[0]) + + elif msg.type == QuantumManagerMsgType.REMOVE: + assert len(msg.keys) == 1 + assert len(msg.args) == 0 + key = msg.keys[0] + qm.remove(key) + + elif msg.type == QuantumManagerMsgType.TERMINATE: + for s in sockets: + s.close() + sockets = [] + + elif msg.type == QuantumManagerMsgType.SYNC: + return_val = True + + else: + raise Exception( + "Quantum manager session received invalid message type {}".format( + msg.type)) + + # send return value + if return_val is not None: + send_msg_with_length(s, return_val) + + if not msg.type in timing_comp: + timing_comp[msg.type] = 0 + timing_comp[msg.type] += time() - tick + + # record timing and performance information + data = {"msg_counter": msg_counter, "traffic_counter": traffic_counter} + for msg_type in timing_comp: + data[f"{msg_type.name}_timer"] = timing_comp[msg_type] + + with open(log_file, 'w') as fh: + dump(data, fh) diff --git a/src/kernel/p_timeline.py b/src/kernel/p_timeline.py index 8383b6f5..259b7dae 100644 --- a/src/kernel/p_timeline.py +++ b/src/kernel/p_timeline.py @@ -5,7 +5,6 @@ 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 From 31f46705ead38b95b53862c17e02c61c249ac297 Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 14:30:35 -0500 Subject: [PATCH 03/17] Move files; add definition for 'psequence' package --- parallel/qm_server.py | 6 +- parallel/setup.py | 27 ++ parallel/src/p_quantum_manager.py | 2 +- parallel/src/p_timeline.py | 11 +- parallel/src/quantum_manager_client.py | 22 +- parallel/src/quantum_manager_server.py | 12 +- .../tests_parallel}/kernel/test_p_timeline.py | 0 src/kernel/__init__.py | 2 +- src/kernel/p_quantum_manager.py | 43 --- src/kernel/p_timeline.py | 137 ---------- src/kernel/quantum_manager_client.py | 249 ------------------ src/kernel/quantum_manager_server.py | 249 ------------------ 12 files changed, 51 insertions(+), 709 deletions(-) create mode 100644 parallel/setup.py rename {tests_parallel => parallel/tests_parallel}/kernel/test_p_timeline.py (100%) delete mode 100644 src/kernel/p_quantum_manager.py delete mode 100644 src/kernel/p_timeline.py delete mode 100644 src/kernel/quantum_manager_client.py delete mode 100644 src/kernel/quantum_manager_server.py 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/setup.py b/parallel/setup.py new file mode 100644 index 00000000..0f2fb911 --- /dev/null +++ b/parallel/setup.py @@ -0,0 +1,27 @@ +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=['sequence-parallel', 'sequence-parallel.p_quantum_manager', 'sequence-parallel.p_router_net_topo', + 'sequence-parallel.p_timeline', 'sequence-parallel.p_timeline', + 'sequence-parallel.quantum_manager_client', 'sequence-parallel.quantum_manager_server'], + package_dir={'sequence': 'src'}, + package_data={'sequence': ['gui/user_templates.json', 'gui/default_params.json', 'gui/starlight.json']}, + include_package_data=True, + install_requires=[ + 'numpy>=1.22', + 'pandas', + 'qutip>=4.6.0', + 'tqdm>=4.54.0', + 'mpi4py', + 'pytest-mpi', + 'sequence>=0.5.0' + ], +) diff --git a/parallel/src/p_quantum_manager.py b/parallel/src/p_quantum_manager.py index abd02462..5766024d 100644 --- a/parallel/src/p_quantum_manager.py +++ b/parallel/src/p_quantum_manager.py @@ -8,7 +8,7 @@ """ from typing import List, Dict -from .quantum_manager import QuantumManagerKet, QuantumManagerDensity +from src.kernel.quantum_manager import QuantumManagerKet, QuantumManagerDensity class ParallelQuantumManagerKet(QuantumManagerKet): diff --git a/parallel/src/p_timeline.py b/parallel/src/p_timeline.py index 259b7dae..0e651ab1 100644 --- a/parallel/src/p_timeline.py +++ b/parallel/src/p_timeline.py @@ -2,17 +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_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. @@ -22,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/parallel/src/quantum_manager_client.py b/parallel/src/quantum_manager_client.py index bb4a13cd..0051a3c8 100644 --- a/parallel/src/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 sequence.utils.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/parallel/src/quantum_manager_server.py b/parallel/src/quantum_manager_server.py index 37d60658..8233205d 100644 --- a/parallel/src/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 sequence.utils.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): @@ -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 diff --git a/tests_parallel/kernel/test_p_timeline.py b/parallel/tests_parallel/kernel/test_p_timeline.py similarity index 100% rename from tests_parallel/kernel/test_p_timeline.py rename to parallel/tests_parallel/kernel/test_p_timeline.py 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/kernel/p_quantum_manager.py b/src/kernel/p_quantum_manager.py deleted file mode 100644 index abd02462..00000000 --- a/src/kernel/p_quantum_manager.py +++ /dev/null @@ -1,43 +0,0 @@ -"""This module defines the quantum manager class, to track quantum states. - -The states may currently be defined in two possible ways: - - KetState (with the QuantumManagerKet class) - - DensityMatrix (with the QuantumManagerDensity class) - -The manager defines an API for interacting with quantum states. -""" -from typing import List, Dict - -from .quantum_manager import QuantumManagerKet, QuantumManagerDensity - - -class ParallelQuantumManagerKet(QuantumManagerKet): - """Class to track and manage quantum states with the ket vector formalism.""" - - def __init__(self, states): - super().__init__() - self.states = states - - def run_circuit(self, circuit: "Circuit", keys: List[int], - meas_samp=None) -> Dict[int, int]: - ret_dict = super().run_circuit(circuit, keys, meas_samp) - return ret_dict - - def remove(self, key: int) -> None: - del self.states[key] - - -class ParallelQuantumManagerDensity(QuantumManagerDensity): - """Class to track and manage states with the density matrix formalism.""" - - def __init__(self, states): - super().__init__() - self.states = states - - def run_circuit(self, circuit: "Circuit", keys: List[int], - meas_samp=None) -> Dict[int, int]: - ret_dict = super().run_circuit(circuit, keys, meas_samp) - return ret_dict - - def remove(self, key: int) -> None: - del self.states[key] diff --git a/src/kernel/p_timeline.py b/src/kernel/p_timeline.py deleted file mode 100644 index 259b7dae..00000000 --- a/src/kernel/p_timeline.py +++ /dev/null @@ -1,137 +0,0 @@ -from typing import List - -from mpi4py import MPI -from time import time - -from .timeline import Timeline -from .event import Event -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. - 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. - These buffers are exchanged between timelines at regular synchronization intervals. - All Parallel Timelines in a simulation communicate with a Quantum Manager Server for shared quantum states. - - 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. - lookahead (int): defines width of time window for execution (simulation time between synchronization). - quantum_manager (QuantumManagerClient): local quantum manager client to communicate with server. - """ - - def __init__(self, lookahead: int, stop_time=float('inf'), formalism=KET_STATE_FORMALISM, - qm_ip=None, qm_port=None): - """Constructor for the ParallelTimeline class. - - Also creates a quantum manager client, unless `qm_ip` and `qm_port` are both set to None. - - Args: - lookahead (int): sets the timeline lookahead time. - stop_time (int): stop (simulation) time of simulation (default inf). - formalism (str): formalism to use for storing quantum states (default 'KET'). - qm_ip (str): IP address for the quantum manager server (default None). - qm_port (int): port to connect to for quantum manager server (default None). - """ - - super(ParallelTimeline, self).__init__(stop_time, formalism) - - self.id = MPI.COMM_WORLD.Get_rank() - self.foreign_entities = {} - self.event_buffer = [[] for _ in range(MPI.COMM_WORLD.Get_size())] - self.lookahead = lookahead - if qm_ip is not None and qm_port is not None: - self.quantum_manager = QuantumManagerClient(formalism, qm_ip, qm_port) - - self.show_progress = False - - self.buffer_min_ts = float('inf') - - self.sync_counter = 0 - self.exchange_counter = 0 - self.computing_time = 0 - self.communication_time = 0 - - def schedule(self, event: 'Event'): - """Method to schedule an event.""" - - if type(event.process.owner) is str \ - and event.process.owner in self.foreign_entities: - self.buffer_min_ts = min(self.buffer_min_ts, event.time) - tl_id = self.foreign_entities[event.process.owner] - self.event_buffer[tl_id].append(event) - self.schedule_counter += 1 - else: - super(ParallelTimeline, self).schedule(event) - - def top_time(self) -> float: - """Method to get the timestamp of the soonest event in the local queue. - - Used for the conservative synchronization algorithm. - If the event queue is empty, returns infinity. - """ - - if len(self.events) > 0: - return self.events.top().time - else: - return float('inf') - - def run(self): - while self.time < self.stop_time: - tick = time() - min_time = min(self.buffer_min_ts, self.top_time()) - for buf in self.event_buffer: - buf.append(min_time) - inbox = MPI.COMM_WORLD.alltoall(self.event_buffer) - self.communication_time += time() - tick - - for buff in self.event_buffer: - buff.clear() - self.buffer_min_ts = float('inf') - - for events in inbox: - min_time = min(min_time, events.pop()) - for event in events: - self.exchange_counter += 1 - self.schedule(event) - - assert min_time >= self.time - - if min_time >= self.stop_time: - break - - self.sync_counter += 1 - - sync_time = min(min_time + self.lookahead, self.stop_time) - self.time = min_time - - tick = time() - while len(self.events) > 0 and self.events.top().time < sync_time: - event = self.events.pop() - if event.is_invalid(): - continue - assert self.time <= event.time, "invalid event time for process scheduled on " + str( - event.process.owner) - self.time = event.time - event.process.run() - self.run_counter += 1 - if isinstance(self.quantum_manager, QuantumManagerClient): - self.quantum_manager.flush_before_sync() - self.computing_time += time() - tick - - def add_foreign_entity(self, entity_name: str, foreign_id: int): - """Adds the name of an entity on another parallel timeline. - - Args: - entity_name (str): name of the entity on another parallel timeline. - foreign_id (int): id of the process containing the entity. - """ - - self.foreign_entities[entity_name] = foreign_id diff --git a/src/kernel/quantum_manager_client.py b/src/kernel/quantum_manager_client.py deleted file mode 100644 index bb4a13cd..00000000 --- a/src/kernel/quantum_manager_client.py +++ /dev/null @@ -1,249 +0,0 @@ -"""This module defines the QuantumManagerClient class. - -This client provides the same interface as the QuantumManager class for manipulating qubits. -Qubits only managed by the local process are stored within a QuantumManager class instance. -Qubits managed or accessed between processes are stored on a remote quantum manager server. -""" -from collections import defaultdict -from socket import socket -from typing import List, TYPE_CHECKING, Any -from time import time -from uuid import uuid4 - -if TYPE_CHECKING: - from .p_timeline import ParallelTimeline - -from .quantum_manager import QuantumManagerKet, QuantumManagerDensity, KetState, \ - KET_STATE_FORMALISM, DENSITY_MATRIX_FORMALISM -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. - - Unless otherwise noted, the operation of all functions are the same as those of the QuantumManagerClass. - - Attributes: - formalism (str): formalism to use for quantum manager (must match server). - ip (str): ip address of quantum manager server. - 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. - """ - - def __init__(self, formalism: str, ip: str, port: int): - """Constructor for QuantumManagerClient class. - - Args: - formalism (str): formalism to use for quantum manager. - ip (str): ip of quantum manager server. - port (int): port of quantum manager server. - """ - self.formalism = formalism - self.ip = ip - self.port = port - self.socket = socket() - self.managed_qubits = set() - self.io_time = 0 - self.type_counter = defaultdict(lambda: 0) - self.client_call_counter = 0 - self.message_buffer = [] - - self.socket.connect((self.ip, self.port)) - self.socket.settimeout(20) - - # local quantum manager - if formalism == KET_STATE_FORMALISM: - self.qm = QuantumManagerKet() - elif formalism == DENSITY_MATRIX_FORMALISM: - self.qm = QuantumManagerDensity() - else: - raise Exception("Invalid formalism {} given".format(formalism)) - - def get_socket_to_server(self) -> "socket": - return self.socket - - def disconnect_from_server(self): - """Closes socket connection with quantum manager server. - - Uses a message of type `QuantumManagerServer.CLOSE`. - - Side Effects: - closes socket attribute connection. - """ - - self._send_message(QuantumManagerMsgType.CLOSE, [], [], - expecting_receive=False) - self.flush_message_buffer() - - def new(self, state=(complex(1), complex(0))) -> int: - """Method to get a new state from server. - - Args: - state (List): if None, state will be in default state. Otherwise, must match formalism of server. - - Returns: - int: key for the new state generated. - """ - self.client_call_counter += 1 - key = uuid4().int - # below code cannot be removed because of the assertion in the - # move_manage_to_client function - self.qm.set([key], state) - self.move_manage_to_client([key], state) - return key - - def get(self, key: int) -> any: - self.client_call_counter += 1 - if self._check_local([key]): - return self.qm.get(key) - else: - state_raw = self._send_message(QuantumManagerMsgType.GET, [key], - []) - state = KetState([0, 1], [0]) - state.deserialize(state_raw) - return state - - def run_circuit(self, circuit: "Circuit", keys: List[int], meas_samp=None) -> any: - self.client_call_counter += 1 - if self._check_local(keys): - return self.qm.run_circuit(circuit, keys, meas_samp) - else: - visited_qubits = set() - for key in keys: - if key in visited_qubits: - continue - if self.is_managed_by_server(key): - visited_qubits.add(key) - else: - state = self.qm.get(key) - for state_key in state.keys: - visited_qubits.add(state_key) - assert not self.is_managed_by_server(state_key) - self.move_manage_to_server(state.keys[0]) - # todo: move qubit to client if all keys of entangled qubits belong - # to the client - if len(circuit.measured_qubits) == 0: - self._send_message(QuantumManagerMsgType.RUN, - list(visited_qubits), - [circuit, keys], False) - return {} - - ret_val_raw = self._send_message(QuantumManagerMsgType.RUN, - list(visited_qubits), - [circuit, keys, meas_samp]) - - ret_val = {} - for key in ret_val_raw: - ret_val[int(key, 16)] = ret_val_raw[key] - - for measured_q in ret_val: - if not measured_q in self.qm.states: - continue - if ret_val[measured_q] == 1: - self.move_manage_to_client([measured_q], [0, 1]) - else: - self.move_manage_to_client([measured_q], [1, 0]) - return ret_val - - def set(self, keys: List[int], amplitudes: any) -> None: - self.client_call_counter += 1 - if self._check_local(keys): - self.qm.set(keys, amplitudes) - elif all(key in self.qm.states for key in keys): - self.move_manage_to_client(keys, amplitudes) - else: - for key in keys: - self.move_manage_to_server(key, sync_state=False) - self._send_message(QuantumManagerMsgType.SET, keys, [amplitudes], - False) - - def remove(self, key: int) -> None: - self.client_call_counter += 1 - self._send_message(QuantumManagerMsgType.REMOVE, [key], []) - self.qm.remove(key) - - def kill(self) -> None: - """Method to terminate the connected server. - - Uses a message of type `QuantumManagerMsgType.TERMINATE`. - - Side Effects: - Will end all processes on the remote server. - """ - self.client_call_counter += 1 - self._send_message(QuantumManagerMsgType.TERMINATE, [], [], - expecting_receive=False) - - def is_managed_by_server(self, qubit_key: int) -> bool: - return qubit_key not in self.managed_qubits - - def move_manage_to_server(self, qubit_key: int, sync_state=True): - """Move management of a qubit from the local quantum manager to quantum manager server. - - Args: - qubit_key (int): qubit key to move. - sync_state (bool): indicates if server state should be set to match local (default `True`). - """ - - if self.is_managed_by_server(qubit_key): - return - if qubit_key in self.qm.states: - state = self.qm.get(qubit_key) - for key in state.keys: - self.managed_qubits.remove(key) - if sync_state: - self._send_message(QuantumManagerMsgType.SET, state.keys, - [state.state], False) - - def move_manage_to_client(self, qubit_keys: List[int], amplitute: 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) - - def _send_message(self, msg_type, keys: List, args: List, - expecting_receive=True) -> any: - """Sends a message to the remote quantum manager server. - - Args: - msg_type (QuantumManagerMsgType): type of message to send. - keys (List[int]): list of all keys affected by message process. - args (List[any]): any other arguments used for the message. - expecting_receive (bool): indicates if client should block until response received (default `True`). - - Returns: - any: result of process on quantum manager server (if `expecting_receive` is `True`). - """ - - self.type_counter[msg_type.name] += 1 - - msg = QuantumManagerMessage(msg_type, keys, args) - self.message_buffer.append(msg) - - if expecting_receive: - self.flush_message_buffer() - tick = time() - received_msg = recv_msg_with_length(self.socket) - self.io_time += time() - tick - return received_msg - - def flush_message_buffer(self): - if len(self.message_buffer) > 0: - tick = time() - msgs = [msg.serialize() for msg in self.message_buffer] - send_msg_with_length(self.socket, msgs) - self.io_time += time() - tick - self.message_buffer = [] - - def flush_before_sync(self): - if len(self.message_buffer) > 0: - self._send_message(QuantumManagerMsgType.SYNC, [], []) - - def _check_local(self, keys: List[int]): - return not any([self.is_managed_by_server(key) for key in keys]) diff --git a/src/kernel/quantum_manager_server.py b/src/kernel/quantum_manager_server.py deleted file mode 100644 index 37d60658..00000000 --- a/src/kernel/quantum_manager_server.py +++ /dev/null @@ -1,249 +0,0 @@ -"""This module defines the function to use for the quantum manager server. - -This function should be started on a separate process for parallel simulation, \ - using the `mpi_tests/qm_server.py` script or similar. -Additionally defined are utility functions for socket connections and the messages used by the client/server. -""" - -from enum import Enum, auto -import socket -import argparse -from ipaddress import ip_address -import select -from typing import List -from time import time -from json import dump - -from .p_quantum_manager import ParallelQuantumManagerKet, \ - ParallelQuantumManagerDensity -from ..utils.communication import send_msg_with_length, recv_msg_with_length -from ..components.circuit import Circuit - - -def valid_port(port): - port = int(port) - if 1 <= port <= 65535: - return port - else: - raise argparse.ArgumentTypeError( - '%d is not a valid port number' % port) - - -def valid_ip(ip): - _ip = ip_address(ip) - return ip - - -def generate_arg_parser(): - # TODO: delete? - parser = argparse.ArgumentParser(description='The server of quantum manager') - parser.add_argument('ip', type=valid_ip, help='listening IP address') - parser.add_argument('port', type=valid_port, help='listening port number') - return parser - - -class QuantumManagerMsgType(Enum): - GET = 0 - SET = 1 - RUN = 2 - REMOVE = 3 - TERMINATE = 4 - CLOSE = 5 - CONNECT = 6 - CONNECTED = 7 - SYNC = 8 - - -class QuantumManagerMessage(): - """Message for quantum manager communication. - - Attributes: - type (Enum): type of message. - keys (List[int]): list of ALL keys serviced by request. - args (List[any]): list of other arguments for the request. - """ - - def __init__(self, msg_type: QuantumManagerMsgType, keys: 'List[int]', - args: 'List[Any]'): - self.type = msg_type - self.keys = keys - self.args = args - - def __repr__(self): - return str(self.type) + ' ' + str(self.args) - - def serialize(self): - """Serializes the data stored in the message. - - Returns: - Dict[str, any]: A dictionary with the following keys: - - "type": name of the type for the message. - - "keys": keys for all modified qubits in the quantum state manager, in hex format. - - "args": JSON-like serialized version of arguments - """ - - hex_keys = [hex(key) for key in self.keys] - - args = {} - if self.type == QuantumManagerMsgType.SET: - amplitudes = [] - for cplx_n in self.args[0]: - if type(cplx_n) == float: - amplitudes.append(cplx_n) - amplitudes.append(0) - else: - amplitudes.append(cplx_n.real) - amplitudes.append(cplx_n.imag) - - args["amplitudes"] = amplitudes - - elif self.type == QuantumManagerMsgType.RUN: - args["circuit"] = self.args[0].serialize() - args["keys"] = [hex(key) for key in self.args[1]] - args["meas_samp"] = -1 - if len(self.args) > 2: - args["meas_samp"] = self.args[2] - - return {"type": self.type.name, "keys": hex_keys, "args": args} - - def deserialize(self, j_data): - """Method to reconstruct a message from serialized data. - - Args: - j_data: serialized QuantumManagerMessage data. - """ - - self.keys = j_data["keys"] - - if j_data["type"] == "GET": - self.type = QuantumManagerMsgType.GET - - elif j_data["type"] == "SET": - self.type = QuantumManagerMsgType.SET - cmplx_n_list = [] - for i in range(0, len(j_data["args"]["amplitudes"]), 2): - cmplx = complex(j_data["args"]["amplitudes"][i], - j_data["args"]["amplitudes"][i + 1]) - cmplx_n_list.append(cmplx) - self.args = [cmplx_n_list] - elif j_data["type"] == "RUN": - self.type = QuantumManagerMsgType.RUN - # use hex string as key temporarily - c_raw = j_data["args"]["circuit"] - circuit = Circuit(1) - circuit.deserialize(c_raw) - keys = j_data["args"]["keys"] - meas_samp = j_data["args"]["meas_samp"] - self.args = [circuit, keys, meas_samp] - - elif j_data["type"] == "CLOSE": - self.type = QuantumManagerMsgType.CLOSE - elif j_data["type"] == "SYNC": - self.type = QuantumManagerMsgType.SYNC - - -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. - Will block processing until all clients connected. - - Args: - ip (str): ip address server should bind to. - port (int): port server should bind to. - client_num (int): number of remote clients that should be connected (one per process). - formalism (str): formalism to use for quantum manager (default `"KET"` for ket vector). - log_file (str): output log file to store server information (default `"server_log.json"`). - """ - - s = socket.socket() - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - s.bind((ip, port)) - s.listen() - print("listening at:", ip, port) - - timing_comp = {} - traffic_counter = 0 - msg_counter = 0 - - # initialize shared data - if formalism == "KET": - qm = ParallelQuantumManagerKet({}) - elif formalism == "DENSITY": - qm = ParallelQuantumManagerDensity({}) - - sockets = [] - for _ in range(client_num): - c, addr = s.accept() - sockets.append(c) - - while sockets: - readable, writeable, exceptional = select.select(sockets, [], [], 1) - for s in readable: - msgs = recv_msg_with_length(s) - - traffic_counter += 1 - msg_counter += len(msgs) - - for m_raw in msgs: - msg = QuantumManagerMessage(None, [], []) - msg.deserialize(m_raw) - return_val = None - - tick = time() - if msg.type == QuantumManagerMsgType.CLOSE: - s.close() - sockets.remove(s) - break - - elif msg.type == QuantumManagerMsgType.GET: - assert len(msg.args) == 0 - state = qm.get(msg.keys[0]) - return_val = state.serialize() - - elif msg.type == QuantumManagerMsgType.RUN: - assert len(msg.args) == 2 or len(msg.args) == 3 - circuit, keys, meas_samp = msg.args - return_val = qm.run_circuit(circuit, keys, meas_samp) - if len(return_val) == 0: - return_val = None - - elif msg.type == QuantumManagerMsgType.SET: - assert len(msg.args) == 1 - qm.set(msg.keys, msg.args[0]) - - elif msg.type == QuantumManagerMsgType.REMOVE: - assert len(msg.keys) == 1 - assert len(msg.args) == 0 - key = msg.keys[0] - qm.remove(key) - - elif msg.type == QuantumManagerMsgType.TERMINATE: - for s in sockets: - s.close() - sockets = [] - - elif msg.type == QuantumManagerMsgType.SYNC: - return_val = True - - else: - raise Exception( - "Quantum manager session received invalid message type {}".format( - msg.type)) - - # send return value - if return_val is not None: - send_msg_with_length(s, return_val) - - if not msg.type in timing_comp: - timing_comp[msg.type] = 0 - timing_comp[msg.type] += time() - tick - - # record timing and performance information - data = {"msg_counter": msg_counter, "traffic_counter": traffic_counter} - for msg_type in timing_comp: - data[f"{msg_type.name}_timer"] = timing_comp[msg_type] - - with open(log_file, 'w') as fh: - dump(data, fh) From 1860d9a417b859b2d28b2a53cd5f427e0bf02509 Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 15:00:48 -0500 Subject: [PATCH 04/17] Update makefiles, requirements, workflow (for testing) --- .github/workflows/main.yml | 18 ++++++++++++------ Makefile | 5 +---- parallel/Makefile | 8 ++++++++ parallel/requirements.txt | 7 +++++++ requirements.txt | 4 +--- 5 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 parallel/Makefile create mode 100644 parallel/requirements.txt diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f3e8e149..98381f31 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: @@ -21,10 +21,6 @@ jobs: 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 @@ -33,4 +29,14 @@ jobs: - 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: | make test_parallel 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/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/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/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 From 2bdf7e7c0d069ef3c7ebf9d689f596fc01dba5c4 Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 15:07:14 -0500 Subject: [PATCH 05/17] remove mpi4py from setup.py --- .github/workflows/main.yml | 6 ++++++ setup.py | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 98381f31..06dbeb87 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,26 +17,32 @@ 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: 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: | make test_parallel diff --git a/setup.py b/setup.py index b3ddff13..ef0d7e81 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,5 @@ 'dash-table', 'dash-cytoscape', 'plotly', - 'mpi4py', - 'pytest-mpi', ], ) From 677137e06ba827fcb943e0b67e4cc5592cfc03ee Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 15:16:28 -0500 Subject: [PATCH 06/17] remove mpi from router topo --- src/topology/router_net_topo.py | 71 ++++++++++++++------------------- 1 file changed, 29 insertions(+), 42 deletions(-) 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] From 4de7a36ac87fdf044d0a78e8274adb6e351ed5ef Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 15:24:17 -0500 Subject: [PATCH 07/17] rename packages in setup.py --- parallel/setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/parallel/setup.py b/parallel/setup.py index 0f2fb911..ed544f3b 100644 --- a/parallel/setup.py +++ b/parallel/setup.py @@ -9,9 +9,9 @@ " 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=['sequence-parallel', 'sequence-parallel.p_quantum_manager', 'sequence-parallel.p_router_net_topo', - 'sequence-parallel.p_timeline', 'sequence-parallel.p_timeline', - 'sequence-parallel.quantum_manager_client', 'sequence-parallel.quantum_manager_server'], + packages=['psequence', 'psequence.p_quantum_manager', 'psequence.p_router_net_topo', + 'psequence.p_timeline', 'psequence.p_timeline', + 'psequence.quantum_manager_client', 'psequence.quantum_manager_server'], package_dir={'sequence': 'src'}, package_data={'sequence': ['gui/user_templates.json', 'gui/default_params.json', 'gui/starlight.json']}, include_package_data=True, From cc34b7dd1191a72dc6126a216213b15156dc38b6 Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 15:31:38 -0500 Subject: [PATCH 08/17] update setup.py and add __init__.py; update parallel test --- parallel/setup.py | 9 +++------ parallel/src/__init__.py | 4 ++++ parallel/tests_parallel/kernel/test_p_timeline.py | 3 ++- 3 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 parallel/src/__init__.py diff --git a/parallel/setup.py b/parallel/setup.py index ed544f3b..e0f7099a 100644 --- a/parallel/setup.py +++ b/parallel/setup.py @@ -9,11 +9,8 @@ " 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', 'psequence.p_quantum_manager', 'psequence.p_router_net_topo', - 'psequence.p_timeline', 'psequence.p_timeline', - 'psequence.quantum_manager_client', 'psequence.quantum_manager_server'], - package_dir={'sequence': 'src'}, - package_data={'sequence': ['gui/user_templates.json', 'gui/default_params.json', 'gui/starlight.json']}, + packages=['psequence'], + package_dir={'psequence': 'src'}, include_package_data=True, install_requires=[ 'numpy>=1.22', @@ -22,6 +19,6 @@ 'tqdm>=4.54.0', 'mpi4py', 'pytest-mpi', - 'sequence>=0.5.0' + '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/parallel/tests_parallel/kernel/test_p_timeline.py b/parallel/tests_parallel/kernel/test_p_timeline.py index 1675c774..ce921b09 100644 --- a/parallel/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() From 8bddb7ea710d1d83735e78a8528e12d1064678da Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 15:41:36 -0500 Subject: [PATCH 09/17] update workflow --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 06dbeb87..a462ac82 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -45,4 +45,5 @@ jobs: - name: Test parallel with pytest run: | + cd parallel make test_parallel From b15089925b07e8db438eb986e648fc2ed8387fc8 Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 15:48:40 -0500 Subject: [PATCH 10/17] bug fixes for parallel imports --- parallel/src/p_quantum_manager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/parallel/src/p_quantum_manager.py b/parallel/src/p_quantum_manager.py index 5766024d..5432d34a 100644 --- a/parallel/src/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 src.kernel.quantum_manager import QuantumManagerKet, QuantumManagerDensity +if TYPE_CHECKING: + from sequence.components.circuit import Circuit class ParallelQuantumManagerKet(QuantumManagerKet): From c18cbedcb1124d09b4fb4a2c0333563e5453c969 Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 16:05:43 -0500 Subject: [PATCH 11/17] change imports for parallel example files --- parallel/example/net_with_flow_info.py | 4 ++-- parallel/example/p_ring.py | 2 +- parallel/example/rand_flow_by_hop.py | 14 +++++++------- parallel/example/test_phold.py | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) 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..cd13350f 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.p_timeline import ParallelTimeline if __name__ == '__main__': From cae180dc308b31cfc18a9947bba516478368ece1 Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 16:13:53 -0500 Subject: [PATCH 12/17] minor qmanager_timing.py adjustments --- parallel/src/quantum_manager_server.py | 5 ++--- utils/qmanager_timing.py | 10 ++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/parallel/src/quantum_manager_server.py b/parallel/src/quantum_manager_server.py index 8233205d..e095cf7f 100644 --- a/parallel/src/quantum_manager_server.py +++ b/parallel/src/quantum_manager_server.py @@ -53,7 +53,7 @@ class QuantumManagerMsgType(Enum): SYNC = 8 -class QuantumManagerMessage(): +class QuantumManagerMessage: """Message for quantum manager communication. Attributes: @@ -140,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/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)) From 203f48f24f9669a2b8841811f0167b412d45c659 Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 16:18:59 -0500 Subject: [PATCH 13/17] Move parallel utils to psequence --- parallel/example/test_phold.py | 2 +- {src/utils => parallel/src}/communication.py | 0 {src/utils => parallel/src}/phold.py | 13 ++++++------- src/utils/__init__.py | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) rename {src/utils => parallel/src}/communication.py (100%) rename {src/utils => parallel/src}/phold.py (79%) diff --git a/parallel/example/test_phold.py b/parallel/example/test_phold.py index cd13350f..daf12f0a 100644 --- a/parallel/example/test_phold.py +++ b/parallel/example/test_phold.py @@ -1,7 +1,7 @@ from mpi4py import MPI from numpy.random import seed -from sequence.utils.phold import PholdNode +from psequence.phold import PholdNode from psequence.p_timeline import ParallelTimeline 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/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/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__) From 2bbcb9438723741131d65b7bd98d55281ad137ec Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Wed, 14 Sep 2022 16:25:25 -0500 Subject: [PATCH 14/17] bug fix in qm client/server --- parallel/src/quantum_manager_client.py | 2 +- parallel/src/quantum_manager_server.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/parallel/src/quantum_manager_client.py b/parallel/src/quantum_manager_client.py index 0051a3c8..38fc2786 100644 --- a/parallel/src/quantum_manager_client.py +++ b/parallel/src/quantum_manager_client.py @@ -12,7 +12,7 @@ from sequence.kernel.quantum_manager import QuantumManagerKet, QuantumManagerDensity, KetState, \ KET_STATE_FORMALISM, DENSITY_MATRIX_FORMALISM from sequence.components.circuit import Circuit -from sequence.utils.communication import send_msg_with_length, recv_msg_with_length +from .communication import send_msg_with_length, recv_msg_with_length from .quantum_manager_server import QuantumManagerMsgType, \ QuantumManagerMessage diff --git a/parallel/src/quantum_manager_server.py b/parallel/src/quantum_manager_server.py index e095cf7f..7524745a 100644 --- a/parallel/src/quantum_manager_server.py +++ b/parallel/src/quantum_manager_server.py @@ -13,7 +13,7 @@ from typing import List from time import time from json import dump -from sequence.utils.communication import send_msg_with_length, recv_msg_with_length +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 f70ac40fab5f77e18daa573f50e0719742231df8 Mon Sep 17 00:00:00 2001 From: allenyxzang Date: Wed, 14 Sep 2022 17:17:40 -0500 Subject: [PATCH 15/17] Add notebook for modifying method/function in scripts --- .../modify_script_method_function.ipynb | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 example/QCE_demos/modify_script_method_function.ipynb 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 +} From 1385ea82aa9bc3d8c1c6e3a83ad4bfca1dd2b2da Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Thu, 15 Sep 2022 16:54:52 -0500 Subject: [PATCH 16/17] Update parallel documentation --- docs/source/index.rst | 6 ++++++ docs/source/references/kernel/top.rst | 3 --- docs/source/references/parallel/communication.rst | 5 +++++ .../references/parallel/p_quantum_manager.rst | 5 +++++ .../references/parallel/p_router_net_topo.rst | 5 +++++ .../references/{kernel => parallel}/p_timeline.rst | 2 +- .../quantum_manager_client.rst | 2 +- .../quantum_manager_server.rst | 2 +- docs/source/references/parallel/top.rst | 14 ++++++++++++++ 9 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 docs/source/references/parallel/communication.rst create mode 100644 docs/source/references/parallel/p_quantum_manager.rst create mode 100644 docs/source/references/parallel/p_router_net_topo.rst rename docs/source/references/{kernel => parallel}/p_timeline.rst (56%) rename docs/source/references/{kernel => parallel}/quantum_manager_client.rst (53%) rename docs/source/references/{kernel => parallel}/quantum_manager_server.rst (53%) create mode 100644 docs/source/references/parallel/top.rst 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 From ff6d89c65c1ea9bb5f6cae8601915694a0c4bf06 Mon Sep 17 00:00:00 2001 From: Alex-Kolar Date: Thu, 15 Sep 2022 16:55:53 -0500 Subject: [PATCH 17/17] Update version to 0.5.1 --- CHANGELOG.md | 11 +++++++++++ docs/source/conf.py | 2 +- setup.py | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) 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/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/setup.py b/setup.py index 23d7a484..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.",