CommunicationLayer ^^^^^^^^^^^^^^^^^^ A :code:`CommunicationLayer` is the provider-facing interface used by a :ref:`RemoteComputer` to communicate with a remote simulator or QPU. Any external provider can integrate its platform with Perceval by implementing this abstract class and passing an instance to :ref:`RemoteComputer`: .. code-block:: python import perceval as pcvl communication_layer = ProviderCommunicationLayer(...) computer = pcvl.RemoteComputer(communication_layer) The :code:`RemoteComputer` handles Perceval concerns such as validating computations, applying error mitigation, preparing payloads, waiting for capacity, and exposing synchronous and asynchronous executions. The communication layer is responsible for translating those operations to the provider API. .. seealso:: The usable implementations of this class: :ref:`QuandelaCommunicationLayer`, :ref:`ScalewayCommunicationLayer` and :ref:`KipuCommunicationLayer`. Implementation contract ======================= An implementation must provide four groups of operations: * Platform discovery through :meth:`get_specs()`, :meth:`get_commands()`, :meth:`get_performances()`, and :meth:`get_remote_status()`. * Job submission through :meth:`send()`, which returns a serializable provider-defined remote identifier. * Job monitoring through :meth:`get_job_status()` and :meth:`get_results()`. * Resource management through :meth:`get_availability()` and :meth:`cancel()`. The optional :meth:`start_session()`, :meth:`stop_session()`, and :meth:`delete_session()` hooks support providers that require an explicit session. Their default implementations do nothing, which is suitable for stateless providers. They are called by the corresponding :class:`RemoteComputer ` lifecycle methods. Job identity and state ====================== :meth:`send()` returns a remote identifier whose type is chosen by the provider. The same value is later passed to :meth:`get_job_status()`, :meth:`get_results()`, and :meth:`cancel()`. It must therefore identify the submitted job for as long as that job can be monitored or its results retrieved. A communication layer should not store mutable state for individual jobs. A single instance can be shared by several executions, including concurrent ones, so the remote identifier must be sufficient to perform every job-specific operation. Provider-wide caches and session state are allowed, but mutable state must be safe for concurrent access. Payloads and results ==================== The payload received by :meth:`send()` is generated by :class:`PayloadGenerator `. It contains a :ref:`Computation` or :ref:`ComputationIterator` and may contain error mitigations, a noise model, or provider-specific computer parameters. Provider implementations can use :meth:`PayloadGenerator.get_computation ` and :meth:`PayloadGenerator.read_configuration_from_payload ` to inspect it before converting it to the provider's wire format. :meth:`get_results()` must return a Perceval result dictionary, as the one returned by any :ref:`Computer`. Provider-specific performance or diagnostic fields may be included alongside them. Status refreshes ================ :meth:`get_job_status()` returns an :ref:`ExecutionStatus` representing the current provider state. Progress, timing, and failure details should be populated when the provider exposes them. Transient communication failures can be represented by returning :code:`None`. Perceval keeps the last known status and passes the number of consecutive failed refreshes back as :code:`refresh_errors` on the next call. Implementations should use that value to distinguish a temporary provider failure from a permanent error and eventually raise when retrying is no longer appropriate. Availability ============ :meth:`get_availability()` reports how many jobs can be submitted immediately. :code:`RemoteComputer` waits while this value is :code:`0`, so implementations must also return :code:`0` when capacity cannot be determined safely, or :code:`1` if no such API call exists. The value must never be negative. Provider implementation outline =============================== The following outline shows how a provider client maps onto the interface. The concrete request and response translation is provider-specific: .. code-block:: python import perceval as pcvl class ProviderCommunicationLayer(pcvl.CommunicationLayer): def __init__(self, client): self._client = client def get_specs(self) -> pcvl.PlatformSpecs: return self._client.fetch_platform_specs() def get_commands(self) -> list[pcvl.Command]: return self.get_specs().commands def get_performances(self) -> dict: return self._client.fetch_performances() def get_remote_status(self) -> str: return self._client.fetch_platform_status() def send(self, payload): provider_payload = self._convert_payload(payload) return self._client.submit(provider_payload) def get_job_status(self, remote_id, refresh_errors=0): response = self._client.fetch_job(remote_id) return self._convert_status(response) def get_results(self, remote_id) -> dict: response = self._client.fetch_results(remote_id) return self._convert_results(response) def cancel(self, remote_id) -> None: self._client.cancel(remote_id) def get_availability(self) -> int: return max(0, self._client.available_slots()) If the provider exposes an RPC API compatible with Perceval's RPC handler protocol, :class:`RPCBasedCommunicationLayer ` can provide the common payload serialization, platform discovery, status conversion, result conversion, and cancellation logic. Persistence =========== An :ref:`ExecutionGroup` serializes its executions and their remote computers. A provider that wants its executions to survive across Python sessions must register its communication layer and any required client objects with Perceval's :ref:`serialization` system. Authentication secrets should not be written into execution archives; restore them from the provider's configuration or environment when deserializing instead (see :ref:`RemoteConfig`). Class reference =============== .. autoclass:: perceval.runtime.communication_layer.CommunicationLayer :members: