diff --git a/README.md b/README.md index de133628..c3833735 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,15 @@

-

Quantum Networking in SeQUeNCe: Scalable, Customizable, Easy Debugging

+

Quantum Networking in SeQUeNCe: Customizable, Scalable, Easy Debugging

+ + +[![Documentation](https://img.shields.io/readthedocs/sequence-rtd-tutorial)](https://sequence-rtd-tutorial.readthedocs.io/) +[![PyPi](https://img.shields.io/pypi/v/sequence)](https://pypi.org/project/sequence/) +[![Qutip](https://img.shields.io/badge/integration%20-Qutip-blue)](https://qutip.org/) +[![Paper](https://img.shields.io/badge/10.1088%2F2058-9565%2Fac22f6?label=DOI)](https://iopscience.iop.org/article/10.1088/2058-9565/ac22f6) + +
@@ -58,7 +66,7 @@ publisher = {IOP Publishing}, ## Running the GUI Once SeQUeNCe has been installed as described above, simply run the `gui.py` script found in the root of the project directory ``` -$ python gui.py +python gui.py ``` ## Usage Examples @@ -70,12 +78,12 @@ Code for the experiments performed in our paper can be found in the file `starli ### Jupyter Notebook Examples The example folder contains several scripts that can be run with jupyter notebook for easy editing and visualization. These files require that the notebook package be installed (Anaconda recommended): ``` -$ pip install notebook -$ pip install ipywidgets +pip install notebook +pip install ipywidgets ``` To run each file, simply run ``` -$ jupyter notebook +jupyter notebook ``` These examples include: * `BB84_eg.ipynb`, which uses the BB84 protocol to distribute secure keys between two quantum nodes @@ -89,7 +97,7 @@ The example directory contains an example json file `starlight.json` to specify To view a network, simply run the script and specify the relative location of your json file: ``` -$ python utils/draw_topo.py example/starlight.json +python utils/draw_topo.py example/starlight.json ``` This script also supports a flag `-m` to visualize BSM nodes created by default on quantum links between routers. diff --git a/docs/requirements.txt b/docs/requirements.txt index ceeb3653..7676974e 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,3 @@ recommonmark sphinx-rtd-theme -sequence \ No newline at end of file +sequence diff --git a/docs/source/index.rst b/docs/source/index.rst index 66325334..ddbcc0e5 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -24,15 +24,15 @@ Welcome to the SeQUeNCe documentation page :maxdepth: 1 :caption: Module References: - references/kernel/top + references/application/top references/components/top references/entanglement_management/top - references/resource_management/top + references/kernel/top + references/misc/top references/network_management/top - references/application/top - references/topology/top references/qkd/top - references/misc/top + references/resource_management/top + references/topology/top .. toctree:: :maxdepth: 1 diff --git a/docs/source/references/application/request_app.rst b/docs/source/references/application/request_app.rst new file mode 100644 index 00000000..1f365826 --- /dev/null +++ b/docs/source/references/application/request_app.rst @@ -0,0 +1,5 @@ +Request Request +============== + +.. automodule:: sequence.app.request_app + :members: diff --git a/docs/source/references/application/top.rst b/docs/source/references/application/top.rst index e87cb3fc..774fa955 100644 --- a/docs/source/references/application/top.rst +++ b/docs/source/references/application/top.rst @@ -6,4 +6,5 @@ The Application module contains code to utilize and test quantum network resourc .. toctree:: :maxdepth: 2 + request_app random_request diff --git a/docs/source/references/misc/constants.rst b/docs/source/references/misc/constants.rst new file mode 100644 index 00000000..b8642acc --- /dev/null +++ b/docs/source/references/misc/constants.rst @@ -0,0 +1,5 @@ +Constants +======== + +.. automodule:: sequence.constants + :members: diff --git a/docs/source/references/misc/top.rst b/docs/source/references/misc/top.rst index eb9bc529..c9cb12e9 100644 --- a/docs/source/references/misc/top.rst +++ b/docs/source/references/misc/top.rst @@ -6,6 +6,7 @@ Miscellaneous SeQUeNCe modules. .. toctree:: :maxdepth: 2 + constants message protocol utils/top diff --git a/docs/source/references/misc/utils/config_generator.rst b/docs/source/references/misc/utils/config_generator.rst new file mode 100644 index 00000000..8493ef56 --- /dev/null +++ b/docs/source/references/misc/utils/config_generator.rst @@ -0,0 +1,5 @@ +Config Generator +======== + +.. automodule:: sequence.utils.config_generator + :members: diff --git a/docs/source/references/misc/utils/top.rst b/docs/source/references/misc/utils/top.rst index 4715a946..c0fc90f7 100644 --- a/docs/source/references/misc/utils/top.rst +++ b/docs/source/references/misc/utils/top.rst @@ -6,5 +6,6 @@ Utilities for SeQUeNCe. .. toctree:: :maxdepth: 2 + config_generator encoding - log + log \ No newline at end of file diff --git a/example/ANTS_demos_2023/GUI_and_code_demo.ipynb b/example/ANTS_demos_2023/GUI_and_code_demo.ipynb index b1cc1794..2cd246c5 100644 --- a/example/ANTS_demos_2023/GUI_and_code_demo.ipynb +++ b/example/ANTS_demos_2023/GUI_and_code_demo.ipynb @@ -162,7 +162,7 @@ "def test(sim_time=1.5, qc_atten=1e-5):\n", " \"\"\"\n", " sim_time: duration of simulation time (s)\n", - " qc_atten: quantum channel attenuation (dB/km)\n", + " qc_atten: quantum channel attenuation (dB/m)\n", " \"\"\"\n", " \n", " network_config = \"star_network.json\"\n", diff --git a/example/ANTS_demos_2023/GUI_and_code_demo_filled.ipynb b/example/ANTS_demos_2023/GUI_and_code_demo_filled.ipynb index cf460475..43a7dc0c 100644 --- a/example/ANTS_demos_2023/GUI_and_code_demo_filled.ipynb +++ b/example/ANTS_demos_2023/GUI_and_code_demo_filled.ipynb @@ -164,7 +164,7 @@ "def test(sim_time=1.5, qc_atten=1e-5):\n", " \"\"\"\n", " sim_time: duration of simulation time (s)\n", - " qc_atten: quantum channel attenuation (dB/km)\n", + " qc_atten: quantum channel attenuation (dB/m)\n", " \"\"\"\n", " \n", " network_config = \"star_network.json\"\n", diff --git a/example/QCE_demos_2022/GUI_and_code_demo.ipynb b/example/QCE_demos_2022/GUI_and_code_demo.ipynb index cf460475..43a7dc0c 100644 --- a/example/QCE_demos_2022/GUI_and_code_demo.ipynb +++ b/example/QCE_demos_2022/GUI_and_code_demo.ipynb @@ -164,7 +164,7 @@ "def test(sim_time=1.5, qc_atten=1e-5):\n", " \"\"\"\n", " sim_time: duration of simulation time (s)\n", - " qc_atten: quantum channel attenuation (dB/km)\n", + " qc_atten: quantum channel attenuation (dB/m)\n", " \"\"\"\n", " \n", " network_config = \"star_network.json\"\n", diff --git a/example/QCE_demos_2022/Teleport_demo.ipynb b/example/QCE_demos_2022/Teleport_demo.ipynb index 68bd9e1c..1cc2c5bf 100644 --- a/example/QCE_demos_2022/Teleport_demo.ipynb +++ b/example/QCE_demos_2022/Teleport_demo.ipynb @@ -263,7 +263,7 @@ "def test(sim_time=1.5, qc_atten=1e-5):\n", " \"\"\"\n", " sim_time: duration of simulation time (ms)\n", - " qc_atten: quantum channel attenuation (dB/km)\n", + " qc_atten: quantum channel attenuation (dB/m)\n", " \"\"\"\n", " \n", " network_config = \"star_network.json\"\n", diff --git a/example/QCE_demos_2022/Teleport_demo_filled.ipynb b/example/QCE_demos_2022/Teleport_demo_filled.ipynb index acadf7c1..c92d2d22 100644 --- a/example/QCE_demos_2022/Teleport_demo_filled.ipynb +++ b/example/QCE_demos_2022/Teleport_demo_filled.ipynb @@ -249,7 +249,7 @@ "def test(sim_time=1.5, qc_atten=1e-5):\n", " \"\"\"\n", " sim_time: duration of simulation time (ms)\n", - " qc_atten: quantum channel attenuation (dB/km)\n", + " qc_atten: quantum channel attenuation (dB/m)\n", " \"\"\"\n", " \n", " network_config = \"star_network.json\"\n", diff --git a/example/QCE_demos_2023/GUI_and_code_demo.ipynb b/example/QCE_demos_2023/GUI_and_code_demo.ipynb index b1cc1794..2cd246c5 100644 --- a/example/QCE_demos_2023/GUI_and_code_demo.ipynb +++ b/example/QCE_demos_2023/GUI_and_code_demo.ipynb @@ -162,7 +162,7 @@ "def test(sim_time=1.5, qc_atten=1e-5):\n", " \"\"\"\n", " sim_time: duration of simulation time (s)\n", - " qc_atten: quantum channel attenuation (dB/km)\n", + " qc_atten: quantum channel attenuation (dB/m)\n", " \"\"\"\n", " \n", " network_config = \"star_network.json\"\n", diff --git a/example/QCE_demos_2023/GUI_and_code_demo_filled.ipynb b/example/QCE_demos_2023/GUI_and_code_demo_filled.ipynb index cf460475..43a7dc0c 100644 --- a/example/QCE_demos_2023/GUI_and_code_demo_filled.ipynb +++ b/example/QCE_demos_2023/GUI_and_code_demo_filled.ipynb @@ -164,7 +164,7 @@ "def test(sim_time=1.5, qc_atten=1e-5):\n", " \"\"\"\n", " sim_time: duration of simulation time (s)\n", - " qc_atten: quantum channel attenuation (dB/km)\n", + " qc_atten: quantum channel attenuation (dB/m)\n", " \"\"\"\n", " \n", " network_config = \"star_network.json\"\n", diff --git a/example/qkd.ipynb b/example/qkd.ipynb index ca6770cc..bc4ec525 100644 --- a/example/qkd.ipynb +++ b/example/qkd.ipynb @@ -133,7 +133,7 @@ " cc0.set_ends(n1, n2.name)\n", " cc1.set_ends(n2, n1.name)\n", " # construct a quantum communication channel\n", - " # (with arguments for the channel name, timeline, attenuation (in dB/km), and distance (in m))\n", + " # (with arguments for the channel name, timeline, attenuation (in db/m), and distance (in m))\n", " qc0 = QuantumChannel(\"qc_n1_n2\", tl, attenuation=1e-5, distance=1e3,\n", " polarization_fidelity=0.97)\n", " qc1 = QuantumChannel(\"qc_n2_n1\", tl, attenuation=1e-5, distance=1e3,\n", diff --git a/example/random_request_network.ipynb b/example/random_request_network.ipynb index 38ed1a5f..313a2381 100644 --- a/example/random_request_network.ipynb +++ b/example/random_request_network.ipynb @@ -85,7 +85,7 @@ "def test(sim_time, qc_atten):\n", " \"\"\"\n", " sim_time: duration of simulation time (ms)\n", - " qc_atten: quantum channel attenuation (dB/km)\n", + " qc_atten: quantum channel attenuation (dB/m)\n", " \"\"\"\n", " network_config = \"star_network.json\"\n", "\n", diff --git a/example/random_request_network_mod.ipynb b/example/random_request_network_mod.ipynb index 2966650e..c427a398 100644 --- a/example/random_request_network_mod.ipynb +++ b/example/random_request_network_mod.ipynb @@ -87,7 +87,7 @@ "def test(sim_time, qc_atten):\n", " \"\"\"\n", " sim_time: duration of simulation time (ms)\n", - " qc_atten: quantum channel attenuation (dB/km)\n", + " qc_atten: quantum channel attenuation (db/m)\n", " \"\"\"\n", " \n", " network_config = \"star_network.json\"\n", diff --git a/example/three_node_eg_ep_es.ipynb b/example/three_node_eg_ep_es.ipynb index 72189e0f..a9ad7179 100644 --- a/example/three_node_eg_ep_es.ipynb +++ b/example/three_node_eg_ep_es.ipynb @@ -145,7 +145,7 @@ " cc.set_ends(node1, node2.name)\n", " \n", " # create quantum channels linking r1 and r2 to m1\n", - " # (with arguments for the channel name, timeline, attenuation (in dB/km), and distance (in m))\n", + " # (with arguments for the channel name, timeline, attenuation (in dB/m), and distance (in m))\n", " qc0 = QuantumChannel(\"qc_r1_m1\", tl, qc_atten, qc_dist)\n", " qc1 = QuantumChannel(\"qc_r2_m1\", tl, qc_atten, qc_dist)\n", " qc0.set_ends(r1, m1.name)\n", diff --git a/example/two_node_eg.ipynb b/example/two_node_eg.ipynb index ad592b9c..110569fa 100644 --- a/example/two_node_eg.ipynb +++ b/example/two_node_eg.ipynb @@ -186,7 +186,7 @@ " \n", " # create linear quantum network between routers and middle node\n", " # for this, we create quantum channels\n", - " # (with arguments for the channel name, timeline, attenuation (in dB/km), and distance (in m))\n", + " # (with arguments for the channel name, timeline, attenuation (in dB/m), and distance (in m))\n", " qc1 = QuantumChannel(\"qc_r1_m1\", tl, qc_atten, qc_dist)\n", " qc1.set_ends(r1, m1.name)\n", " qc2 = QuantumChannel(\"qc_r2_m1\", tl, qc_atten, qc_dist)\n", diff --git a/sequence/app/request_app.py b/sequence/app/request_app.py index c4cfa947..941969b6 100644 --- a/sequence/app/request_app.py +++ b/sequence/app/request_app.py @@ -145,7 +145,6 @@ def get_other_reservation(self, reservation: "Reservation") -> None: def schedule_reservation(self, reservation: "Reservation") -> None: if reservation.initiator == self.node.name: - # NOTE: self.path will be an issue when there are multiple reservation.path at the same time self.path = reservation.path reservation_protocol = self.node.network_manager.protocol_stack[1] diff --git a/sequence/components/memory.py b/sequence/components/memory.py index 35dd1b56..50d0a40f 100644 --- a/sequence/components/memory.py +++ b/sequence/components/memory.py @@ -119,7 +119,7 @@ class Memory(Entity): fidelity (float): (current) fidelity of memory. frequency (float): maximum frequency at which memory can be excited. efficiency (float): probability of emitting a photon when excited. - coherence_time (float): average usable lifetime of memory (in seconds). + coherence_time (float): average usable lifetime of memory (in seconds). Negative value means infinite coherence time. wavelength (float): wavelength (in nm) of emitted photons. qstate_key (int): key for associated quantum state in timeline's quantum manager. memory_array (MemoryArray): memory array aggregating current memory. diff --git a/sequence/network_management/reservation.py b/sequence/network_management/reservation.py index aede8c8c..f51381bb 100644 --- a/sequence/network_management/reservation.py +++ b/sequence/network_management/reservation.py @@ -45,7 +45,7 @@ class ResourceReservationMessage(Message): receiver (str): name of destination protocol instance. reservation (Reservation): reservation object relayed between nodes. qcaps (List[QCaps]): cumulative quantum capacity object list (if `msg_type == REQUEST`) - path (List[str]): cumulative node list for entanglement path (if `msg_type == APPROVE`) + path (List[str]): cumulative node list for entanglement path (if `msg_type == APPROVE` or `msg_type == REJECT`) """ def __init__(self, msg_type: any, receiver: str, reservation: "Reservation", **kwargs): @@ -54,7 +54,7 @@ def __init__(self, msg_type: any, receiver: str, reservation: "Reservation", **k if self.msg_type is RSVPMsgType.REQUEST: self.qcaps = [] elif self.msg_type is RSVPMsgType.REJECT: - pass + self.path = kwargs["path"] elif self.msg_type is RSVPMsgType.APPROVE: self.path = kwargs["path"] else: @@ -365,7 +365,6 @@ def push(self, responder: str, start_time: int, end_time: int, memory_size: int, def pop(self, src: str, msg: "ResourceReservationMessage"): """Method to receive messages from lower protocols. - NOTE (caitao, 3/19/2024): argument "src" not used Messages may be of 3 types, causing different network manager behavior: 1. REQUEST: requests are evaluated, and forwarded along the path if accepted. @@ -387,39 +386,54 @@ def pop(self, src: str, msg: "ResourceReservationMessage"): if msg.msg_type == RSVPMsgType.REQUEST: assert self.owner.timeline.now() < msg.reservation.start_time - if self.schedule(msg.reservation): - qcap = QCap(self.owner.name) - msg.qcaps.append(qcap) + qcap = QCap(self.owner.name) + msg.qcaps.append(qcap) + path = [qcap.node for qcap in msg.qcaps] + if self.schedule(msg.reservation): # schedule success if self.owner.name == msg.reservation.responder: - path = [qcap.node for qcap in msg.qcaps] rules = self.create_rules(path, reservation=msg.reservation) self.load_rules(rules, msg.reservation) msg.reservation.set_path(path) new_msg = ResourceReservationMessage(RSVPMsgType.APPROVE, self.name, msg.reservation, path=path) self._pop(msg=msg) - self._push(dst=msg.reservation.initiator, msg=new_msg) + self._push(dst=None, msg=new_msg, next_hop=src) else: self._push(dst=msg.reservation.responder, msg=msg) - else: - new_msg = ResourceReservationMessage(RSVPMsgType.REJECT, self.name, msg.reservation) - self._push(dst=msg.reservation.initiator, msg=new_msg) + else: # schedule failed + new_msg = ResourceReservationMessage(RSVPMsgType.REJECT, self.name, msg.reservation, path=path) + self._push(dst=None, msg=new_msg, next_hop=src) elif msg.msg_type == RSVPMsgType.REJECT: for card in self.timecards: card.remove(msg.reservation) if msg.reservation.initiator == self.owner.name: self._pop(msg=msg) else: - self._push(dst=msg.reservation.initiator, msg=msg) + next_hop = self.next_hop_when_tracing_back(msg.path) + self._push(dst=None, msg=msg, next_hop=next_hop) elif msg.msg_type == RSVPMsgType.APPROVE: rules = self.create_rules(msg.path, msg.reservation) self.load_rules(rules, msg.reservation) if msg.reservation.initiator == self.owner.name: self._pop(msg=msg) else: - self._push(dst=msg.reservation.initiator, msg=msg) + next_hop = self.next_hop_when_tracing_back(msg.path) + self._push(dst=None, msg=msg, next_hop=next_hop) else: raise Exception("Unknown type of message", msg.msg_type) + def next_hop_when_tracing_back(self, path: List[str]) -> str: + '''the next hop when going back from the responder to the initiator + + Args: + path (List[str]): a list of router names that goes from initiator to responder + Return: + str: the name of the next hop + ''' + cur_index = path.index(self.owner.name) + assert cur_index >= 1, f'{cur_index} must be larger equal than 1' + next_hop = path[cur_index - 1] + return next_hop + def schedule(self, reservation: "Reservation") -> bool: """Method to attempt reservation request. If attempt succeeded, return True; otherwise, return False. diff --git a/sequence/network_management/routing.py b/sequence/network_management/routing.py index 90dce917..c85c0ae4 100644 --- a/sequence/network_management/routing.py +++ b/sequence/network_management/routing.py @@ -69,23 +69,29 @@ def update_forwarding_rule(self, dst: str, next_node: str): self.forwarding_table[dst] = next_node - def push(self, dst: str, msg: "Message"): + def push(self, dst: str, msg: "Message", next_hop: str = None): """Method to receive message from upper protocols. Routing packages the message and forwards it to the next node in the optimal path (determined by the forwarding table). Args: - dst (str): name of destination node. + dst (str): name of destination node. If not None, resort to the forwarding table to get the next hop. msg (Message): message to relay. + next_hop (str): name of next hop. If dst is None, next_hop shouldn't be None. next_hop directly tells the next hop. Side Effects: Will invoke `push` method of lower protocol or network manager. """ assert dst != self.owner.name - dst = self.forwarding_table[dst] new_msg = StaticRoutingMessage(Enum, self.name, msg) - self._push(dst=dst, msg=new_msg) + if dst: # if dst is not None, use the forwarding table + next_hop = self.forwarding_table[dst] + self._push(dst=next_hop, msg=new_msg) + elif next_hop: # if next_hop is not None, use next_hop + self._push(dst=next_hop, msg=new_msg) + else: + raise Exception(f'Both dst and next_hop are None!') def pop(self, src: str, msg: "StaticRoutingMessage"): """Message to receive reservation messages. diff --git a/sequence/qkd/BB84.py b/sequence/qkd/BB84.py index af839df9..e9d637b9 100644 --- a/sequence/qkd/BB84.py +++ b/sequence/qkd/BB84.py @@ -85,7 +85,7 @@ class BB84(StackProtocol): The BB84 protocol uses photons to create a secure key between two QKD Nodes. Attributes: - own (QKDNode): node that protocol instance is attached to. + owner (QKDNode): node that protocol instance is attached to. name (str): label for protocol instance. role (int): determines if instance is "alice" or "bob" node. working (bool): shows if protocol is currently working on a key. @@ -107,7 +107,7 @@ def __init__(self, owner: "QKDNode", name: str, lightsource: str, qsdetector: st """Constructor for BB84 class. Args: - own (QKDNode): node hosting protocol instance. + owner (QKDNode): node hosting protocol instance. name (str): name of protocol instance. lightsource (str): name of lightsource for QKD qsdetector (str): name of QSDetector for QKD diff --git a/sequence/topology/node.py b/sequence/topology/node.py index e28c2762..afc7de0d 100644 --- a/sequence/topology/node.py +++ b/sequence/topology/node.py @@ -74,7 +74,7 @@ def init(self) -> None: def set_seed(self, seed: int) -> None: self.generator = np.random.default_rng(seed) - def get_generator(self): + def get_generator(self) -> np.random.Generator: return self.generator def add_component(self, component: Entity) -> None: diff --git a/sequence/utils/config_generator.py b/sequence/utils/config_generator.py index 2a835db3..4c867c59 100644 --- a/sequence/utils/config_generator.py +++ b/sequence/utils/config_generator.py @@ -19,7 +19,7 @@ def add_default_args(parser): """ parser.add_argument('memo_size', type=int, help='number of memories per node') - parser.add_argument('qc_length', type=float, help='distance between nodes (in m)') + parser.add_argument('qc_length', type=float, help='distance between nodes (in km)') parser.add_argument('qc_atten', type=float, help='quantum channel attenuation (in dB/m)') parser.add_argument('cc_delay', type=float, help='classical channel delay (in ms)') parser.add_argument('-d', '--directory', type=str, default='tmp', help='name of output directory') diff --git a/sequence/utils/log.py b/sequence/utils/log.py index 30259163..04ef5e4f 100644 --- a/sequence/utils/log.py +++ b/sequence/utils/log.py @@ -67,7 +67,8 @@ def track_module(module_name: str): """Sets a given module to be tracked by logger.""" global _log_modules - _log_modules.append(module_name) + if module_name not in _log_modules: + _log_modules.append(module_name) def remove_module(module_name: str): diff --git a/tests/network_management/test_reservation.py b/tests/network_management/test_reservation.py index 0b1d049d..e67d5437 100644 --- a/tests/network_management/test_reservation.py +++ b/tests/network_management/test_reservation.py @@ -131,7 +131,7 @@ def reset(node): msg.qcaps.append(QCap("n0")) n1.rsvp.pop("n0", msg) assert len(n1.pop_log) == 1 and len(n1.push_log) == 1 - assert n1.push_log[0]["dst"] == "n0" + assert n1.push_log[0]["next_hop"] == "n0" assert n1.push_log[0]["msg"].msg_type == RSVPMsgType.APPROVE assert len(n1.push_log[0]["msg"].path) == 2 for card in n1.rsvp.timecards: @@ -144,7 +144,7 @@ def reset(node): msg.qcaps.append(QCap("n0")) n1.rsvp.pop("n0", msg) assert len(n1.pop_log) == 0 and len(n1.push_log) == 1 - assert n1.push_log[0]["dst"] == "n0" + assert n1.push_log[0]["next_hop"] == "n0" assert n1.push_log[0]["msg"].msg_type == RSVPMsgType.REJECT for card in n1.rsvp.timecards: assert len(card.reservations) == 0 @@ -193,7 +193,7 @@ def reset(node): msg = ResourceReservationMessage(RSVPMsgType.APPROVE, n1.rsvp.name, reservation, path=["n0", "n1", "n2"]) n1.rsvp.pop("n2", msg) assert len(n1.pop_log) == 0 and len(n1.push_log) == 1 - assert n1.push_log[0]["dst"] == "n0" and n1.push_log[0]["msg"].msg_type == RSVPMsgType.APPROVE + assert n1.push_log[0]["next_hop"] == "n0" and n1.push_log[0]["msg"].msg_type == RSVPMsgType.APPROVE reset(n1)