diff --git a/docs/source/tutorial/chapter6/custom_app.py b/docs/source/tutorial/chapter6/custom_app.py index bcc778a72..98de0d79d 100644 --- a/docs/source/tutorial/chapter6/custom_app.py +++ b/docs/source/tutorial/chapter6/custom_app.py @@ -1,4 +1,5 @@ from __future__ import annotations +from sequence.app.app import App from sequence.kernel.process import Process from sequence.kernel.event import Event from sequence.topology.router_net_topo import RouterNetTopo @@ -11,10 +12,9 @@ from sequence.topology.node import QuantumRouter -class PeriodicApp: +class PeriodicApp(App): def __init__(self, node: QuantumRouter, other: str, memory_size=25, target_fidelity=0.9): - self.node = node - self.node.set_app(self) + super().__init__(node) self.other = other self.memory_size = memory_size self.target_fidelity = target_fidelity @@ -36,6 +36,9 @@ def get_reservation_result(self, reservation: "Reservation", result: bool): else: print("Reservation failed at time", self.node.timeline.now() * 1e-12) + def get_other_reservation(self, reservation: "Reservation"): + pass + def get_memory(self, info: "MemoryInfo"): if info.state == "ENTANGLED" and info.remote_node == self.other: print("\t{} app received memory {} ENTANGLED at time {}".format( @@ -43,13 +46,15 @@ def get_memory(self, info: "MemoryInfo"): self.node.resource_manager.update(None, info.memory, "RAW") -class ResetApp: +class ResetApp(App): def __init__(self, node, other_node_name, target_fidelity=0.9): - self.node = node - self.node.set_app(self) + super().__init__(node) self.other_node_name = other_node_name self.target_fidelity = target_fidelity + def get_reservation_result(self, reservation: "Reservation", result: bool): + pass + def get_other_reservation(self, reservation): """called when receiving the request from the initiating node. diff --git a/sequence/app/__init__.py b/sequence/app/__init__.py index 8995e836e..d45ba3d72 100644 --- a/sequence/app/__init__.py +++ b/sequence/app/__init__.py @@ -1,4 +1,5 @@ -__all__ = ['random_request'] +__all__ = ["app", "random_request", "teleport_app", "request_app"] + def __dir__(): return sorted(__all__) diff --git a/sequence/app/app.py b/sequence/app/app.py new file mode 100644 index 000000000..1ce4d6d7e --- /dev/null +++ b/sequence/app/app.py @@ -0,0 +1,81 @@ +"""Abstract base class for SeQUeNCe applications. + +This module defines the `App` interface that all node-attached applications +must implement. The three abstract callback methods, `get_memory`, `get_reservation_result`, +and `get_other_reservation` are correspond to the calls that `QuantumRouter` already +makes on whatever application is attached via `set_app`. + +Subclasses that track throughput (e.g. `RequestApp`) should override it with a +meaningful implementation. +""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ..network_management.reservation import Reservation + from ..resource_management.memory_manager import MemoryInfo + from ..topology.node import QuantumRouter + + +class App(ABC): + """Abstract base application attached to a quantum network node. + + Every concrete application must subclass `App` and implement the three + abstract callback methods that `QuantumRouter` relies on. + + The constructor automatically registers the application on the node via + `node.set_app(self)`, so subclasses should always call + `super().__init__(node)`! + + Attributes: + node (QuantumRouter): The node this application is attached to. + name (str): Human-readable name for the application instance. + """ + + def __init__(self, node: QuantumRouter): + self.node: QuantumRouter = node + self.node.set_app(self) + self.name: str = f"{self.node.name}.{self.__class__.__name__}" + + @abstractmethod + def get_memory(self, info: MemoryInfo) -> None: + """Called when an entangled memory becomes available. + + Args: + info (MemoryInfo): Information about the available memory. + """ + pass + + @abstractmethod + def get_reservation_result(self, reservation: Reservation, result: bool) -> None: + """Called when a reservation result is received from the network manager. + + Args: + reservation (Reservation): The reservation that completed. + result (bool): Whether the reservation was approved. + """ + pass + + @abstractmethod + def get_other_reservation(self, reservation: Reservation) -> None: + """Called when a reservation initiated by another node is approved and + uses this node as the responder. + + Args: + reservation (Reservation): The approved reservation. + """ + pass + + def set_name(self, name: str) -> None: + """Override the default application name. + + Args: + name (str): New name for the application. + """ + self.name = name + + def __str__(self) -> str: + return self.name diff --git a/sequence/app/request_app.py b/sequence/app/request_app.py index e8d0bc04d..1804c2cc8 100644 --- a/sequence/app/request_app.py +++ b/sequence/app/request_app.py @@ -1,4 +1,5 @@ from __future__ import annotations +import warnings from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -6,12 +7,13 @@ from ..network_management.reservation import Reservation from ..resource_management.memory_manager import MemoryInfo +from .app import App from ..kernel.event import Event from ..kernel.process import Process from ..utils import log -class RequestApp: +class RequestApp(App): """Code for the request application. This application will create a request for entanglement. @@ -35,8 +37,7 @@ class RequestApp: """ def __init__(self, node: QuantumRouter): - self.node: QuantumRouter = node - self.node.set_app(self) + super().__init__(node) self.responder: str = "" self.start_t: int = -1 self.end_t: int = -1 @@ -46,7 +47,6 @@ def __init__(self, node: QuantumRouter): self.memory_counter: int = 0 self.path: list[str] = [] self.memo_to_reservation: dict[int, Reservation] = {} - self.name: str = f"{self.node.name}.RequestApp" def start(self, responder: str, start_t: int, end_t: int, memo_size: int, fidelity: float): """Method to start the application. @@ -143,6 +143,11 @@ def get_memory(self, info: MemoryInfo) -> None: self.node.resource_manager.update(None, info.memory, "RAW") def get_throughput(self) -> float: + warnings.warn( + "get_throughput is deprecated and will be removed in a future release, use THROUGHPUT_METRIC from the metrics module instead", + category=FutureWarning, + stacklevel=2, + ) return self.memory_counter / (self.end_t - self.start_t) * 1e12 @@ -167,8 +172,3 @@ def schedule_reservation(self, reservation: Reservation) -> None: event = Event(reservation.end_time, process) self.node.timeline.schedule(event) - def set_name(self, name: str): - self.name = name - - def __str__(self) -> str: - return self.name \ No newline at end of file diff --git a/sequence/topology/node.py b/sequence/topology/node.py index ccf7580b5..701c71593 100644 --- a/sequence/topology/node.py +++ b/sequence/topology/node.py @@ -20,6 +20,7 @@ from ..components.optical_channel import QuantumChannel, ClassicalChannel from ..components.memory import Memory from ..components.photon import Photon + from ..app.app import App from ..app.request_app import RequestApp from ..app.teleport_app import TeleportApp @@ -441,12 +442,12 @@ def memory_expire(self, memory: "Memory") -> None: """ self.resource_manager.memory_expire(memory) - def set_app(self, app: "RequestApp"): + def set_app(self, app: "App"): """Method to add an application to the node. NOTE: a quantum router can only have one application at a time. Args: - app (RequestApp): the application to add. + app (App): the application to add. """ self.app = app