CommunicationLayer

A CommunicationLayer is the provider-facing interface used by a 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 RemoteComputer:

import perceval as pcvl

communication_layer = ProviderCommunicationLayer(...)
computer = pcvl.RemoteComputer(communication_layer)

The 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.

See also

The usable implementations of this class: QuandelaCommunicationLayer, ScalewayCommunicationLayer and KipuCommunicationLayer.

Implementation contract

An implementation must provide four groups of operations:

  • Platform discovery through get_specs(), get_commands(), get_performances(), and get_remote_status().

  • Job submission through send(), which returns a serializable provider-defined remote identifier.

  • Job monitoring through get_job_status() and get_results().

  • Resource management through get_availability() and cancel().

The optional start_session(), stop_session(), and 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 RemoteComputer lifecycle methods.

Job identity and state

send() returns a remote identifier whose type is chosen by the provider. The same value is later passed to get_job_status(), get_results(), and 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 send() is generated by PayloadGenerator. It contains a Computation or ComputationIterator and may contain error mitigations, a noise model, or provider-specific computer parameters. Provider implementations can use PayloadGenerator.get_computation and PayloadGenerator.read_configuration_from_payload to inspect it before converting it to the provider’s wire format.

get_results() must return a Perceval result dictionary, as the one returned by any Computer. Provider-specific performance or diagnostic fields may be included alongside them.

Status refreshes

get_job_status() returns an 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 None. Perceval keeps the last known status and passes the number of consecutive failed refreshes back as 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

get_availability() reports how many jobs can be submitted immediately. RemoteComputer waits while this value is 0, so implementations must also return 0 when capacity cannot be determined safely, or 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:

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, RPCBasedCommunicationLayer can provide the common payload serialization, platform discovery, status conversion, result conversion, and cancellation logic.

Persistence

An 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 serialization system. Authentication secrets should not be written into execution archives; restore them from the provider’s configuration or environment when deserializing instead (see RemoteConfig).

Class reference

class perceval.runtime.communication_layer.CommunicationLayer

Provider-facing interface between RemoteComputer and a remote platform.

A communication layer translates Perceval payloads and lifecycle operations into calls to a provider service. Implementations should not retain mutable state for individual jobs: the remote identifier returned by send() is passed to all later status, result, and cancellation calls. Provider-wide caches and session state are allowed.

Implementations may be used concurrently by several executions. They should therefore avoid shared per-job state and make any mutable cache or session state concurrency-safe.

abstract cancel(remote_id)

Request cancellation of a remote job.

The request need not make cancellation immediate; later calls to get_job_status() communicate the final state.

Parameters:

remote_id (TypeVar(RemoteId)) – Identifier returned by send().

Return type:

None

delete_session()

Permanently delete the current provider session.

Stateless implementations may keep the default no-op implementation.

Return type:

None

abstract get_availability()

Return the number of concurrent jobs that can currently be submitted by the user.

Return 0 when the provider has no free capacity or availability cannot be established. Always returns 1 if there is no API call to get this number. The value must not be negative.

Return type:

int

abstract get_commands()

Return the commands implemented by the remote platform.

Each command name must identify an operation that the provider can execute, and its signature must describe the parameters accepted by that operation.

Return type:

list[Command]

abstract get_job_status(remote_id, refresh_errors=0)

Retrieve the current status of a remote job.

Returning None signals a transient refresh failure. RemoteComputer keeps the previous status and passes the number of consecutive failures back on the next call, so an implementation can eventually raise a permanent error.

Parameters:
  • remote_id (TypeVar(RemoteId)) – Identifier returned by send().

  • refresh_errors (int) – Number of consecutive previous calls that returned None.

Return type:

Optional[ExecutionStatus]

Returns:

The current execution status, or None after a recoverable refresh failure.

abstract get_performances()

Return the platform performance characterization.

The mapping is exposed as RemoteComputer.performance and may also be used to derive its noise model.

Return type:

dict

abstract get_platform_details()

Return any kind of information that the platform could provide. No particular field is guaranteed to exist here, nor do their types. This should only be used for information purpose or on specific computers, and always check that the read field exists before accessing it.

Return type:

dict

abstract get_remote_status()

Return the provider’s current platform status as a human-readable string.

Return type:

str

abstract get_results(remote_id)

Retrieve the results of a completed remote job.

Parameters:

remote_id (TypeVar(RemoteId)) – Identifier returned by send().

Return type:

dict

Returns:

A Perceval result dictionary, normally containing "results" or "results_list" and any associated performance data.

Raises:

Exception – If the provider cannot retrieve the results.

abstract get_specs()

Return the capabilities and constraints of the target platform.

Return type:

PlatformSpecs

Returns:

The target platform specifications.

abstract property name: str

The name of the remote platform.

abstract send(payload)

Submit a Perceval payload to the remote platform.

The payload is produced by PayloadGenerator and contains a Computation or ComputationIterator, plus optional noise, mitigation, and computer-specific parameters. An implementation is responsible for translating or serializing it into its provider’s request format.

Parameters:

payload (dict) – The payload to submit.

Return type:

TypeVar(RemoteId)

Returns:

A provider-defined, stable job identifier accepted by the other job methods.

start_session()

Start or acquire a provider session.

Stateless implementations may keep the default no-op implementation.

Return type:

None

stop_session()

Stop or release the current provider session without deleting it.

Stateless implementations may keep the default no-op implementation.

Return type:

None