SDK

The user-facing layer. Application code depends on these classes and nothing else, which is what makes a NetQMPI program backend-agnostic.

Environment

Environment object injected into every user main() function.

This class acts as the bridge between the runtime layer, which sets up the multi-process execution environment and selects the backend, and the SDK layer, which provides the user-facing programming API.

By depending only on this class, user applications remain fully backend-agnostic and do not need to import any concrete executor or backend adapter.

class netqmpi.sdk.environment.Environment[source]

Bases: object

Runtime context injected into every user main() function.

This class encapsulates two responsibilities:

Example

def main(env: Environment = None):

rank = env.comm.rank circuit = env.create_circuit(num_qubits=2, num_clbits=1)

with env.comm:

…

Parameters:
  • comm – Communicator associated with this rank.

  • executor – Executor responsible for creating backend-specific circuit instances.

__init__(comm, executor)[source]

Initialize the environment.

Parameters:
  • comm (QMPICommunicator) – Communicator associated with this rank.

  • executor (Executor) – Executor responsible for creating backend-specific circuit instances.

Return type:

None

property comm: QMPICommunicator

Return the communicator associated with this rank.

Returns:

The rank communicator.

create_circuit(num_qubits, num_clbits)[source]

Create a backend-specific quantum circuit.

This method delegates circuit creation to the underlying Executor, allowing user code to remain backend-agnostic.

Parameters:
  • num_qubits (int) – Number of qubits in the circuit.

  • num_clbits (int) – Number of classical bits in the circuit.

Returns:

A backend-specific Circuit instance ready to receive quantum operations.

Return type:

Circuit

Communicator

High-level MPI-style communicator.

This module defines the backend-agnostic communicator interface exposed to user application code through Environment.comm. Concrete backend implementations are injected by the runtime or executor layer.

No backend-specific package (such as netqasm or cunqa) is imported here.

class netqmpi.sdk.communicator.QMPICommunicator[source]

Bases: ABC

Backend-agnostic facade for rank-based communication.

This class exposes the communication interface required by user code and by Circuit, while delegating the backend-specific behavior to concrete subclasses.

It provides:

  • rank and size properties.

  • Context-manager support for connection lifecycle handling.

  • Utility helpers for rank naming and neighbor traversal.

__init__(rank, size)[source]

Initialize the communicator.

Parameters:
  • rank (int) – Numeric index of the current rank.

  • size (int) – Total number of ranks in the communicator.

Return type:

None

circuits: List[Circuit]
results: Dict
property rank: int

Return the numeric index of the current rank.

Returns:

The current rank.

property size: int

Return the total number of ranks in the communicator.

Returns:

The communicator size.

qsend(circuit, qubits, dest_rank)[source]

Send a qubit to the destination rank using teleportation.

Parameters:
  • circuit (Circuit) – Circuit holding the qubits to send.

  • qubits (List[int]) – Local qubit indices to send.

  • dest_rank (int) – Destination rank.

Return type:

None

qrecv(circuit, qubits, src_rank)[source]

Receive a qubit from the source rank using teleportation.

Parameters:
  • circuit (Circuit) – Circuit receiving the qubits.

  • qubits (List[int]) – Local qubit indices that will hold the incoming state.

  • src_rank (int) – Source rank.

Return type:

None

qscatter(circuit, qubits, root)[source]

Scatter the qubits of the root among the other ranks.

Collective call: every rank of the communicator has to reach it. The root passes its whole buffer, split into one chunk per other rank in rank order; each of those ranks passes the local qubits its chunk lands on. The transfers move the qubits, and unlike MPI_Scatter the root keeps no chunk: it ends the call holding none of what it scattered.

Parameters:
  • circuit (Circuit) – Circuit of the calling rank.

  • qubits (List[int]) – The whole buffer on the root, this rank’s landing qubits elsewhere.

  • root (int) – Rank whose buffer is scattered.

Returns:

The local qubits holding this rank’s chunk, empty on the root.

Return type:

List[int]

qgather(circuit, qubits, root)[source]

Gather the qubits of every rank into the root.

Collective call, like MPI_Gather, and the mirror image of qscatter(): the root passes the whole buffer the chunks land on, every other rank the qubits it contributes. Here too the qubits are moved, so the contributors are left with theirs back in |0⟩.

Parameters:
  • circuit (Circuit) – Circuit of the calling rank.

  • qubits (List[int]) – The whole buffer on the root, this rank’s contribution elsewhere.

  • root (int) – Rank the qubits are gathered into.

Returns:

The whole buffer on the root, this rank’s contribution elsewhere.

Return type:

List[int]

expose(circuit, qubit, ranks, root=None)[source]

Open a telegate window sharing a control qubit across ranks.

Collective call: every rank in [root] + ranks must reach it. The root lends the state of qubit to the other participants, each of which gets back the index of a local communication qubit carrying that control until the matching unexpose().

Parameters:
  • circuit (Circuit) – Circuit of the calling rank.

  • qubit (int | None) – Data qubit to expose. Read on the root only.

  • ranks (List[int]) – Ranks the qubit is exposed to.

  • root (int | None) – Rank exposing its qubit. Defaults to the calling rank.

Returns:

The qubit index this rank must use as control, or None if it does not take part in the window.

Return type:

int | None

unexpose(circuit, ranks, root=None)[source]

Close the telegate window opened by the matching expose().

Parameters:
  • circuit (Circuit) – Circuit of the calling rank.

  • ranks (List[int]) – Ranks the qubit was exposed to.

  • root (int | None) – Rank that exposed its qubit. Defaults to the calling rank.

Return type:

None

get_rank_name(rank)[source]

Return the canonical string name for a rank.

Parameters:

rank (int) – Numeric rank identifier.

Returns:

The canonical rank name.

Return type:

str

get_next_rank(rank)[source]

Return the next rank in cyclic order.

Parameters:

rank (int) – Reference rank.

Returns:

The next rank modulo the communicator size.

Return type:

int

get_prev_rank(rank)[source]

Return the previous rank in cyclic order.

Parameters:

rank (int) – Reference rank.

Returns:

The previous rank modulo the communicator size.

Return type:

int

Circuit

Base abstraction for quantum circuits.

This module defines the contract that all circuit adapters must follow. It provides a backend-agnostic circuit representation based on generic operations and exposes the abstract hooks required by concrete backend implementations.

Qubit indices span two ranges. Indices below Circuit.num_qubits address the data qubits the user asked for; indices from there on address the communication qubits the runtime reserves for distributed protocols, and are only ever produced by Circuit.expose(). Both ranges are accepted by the gate API, so a control qubit borrowed from a remote rank is used exactly like a local one.

class netqmpi.sdk.circuit.Circuit[source]

Bases: ABC

Abstract base class representing a quantum circuit.

This class provides:

  • An OperationContainer storing operations according to the Composite pattern.

  • A fluent gate API (h, cx, rx, measure, etc.) that appends operations to the container and returns self for chaining.

  • Abstract hooks translate() and build() that concrete backend adapters must implement.

Variables:
  • num_qubits – Number of qubits in the circuit.

  • num_clbits – Number of classical bits in the circuit.

__init__(num_qubits, num_clbits, comm)[source]

Initialize the circuit.

Parameters:
  • num_qubits (int) – Number of qubits in the circuit.

  • num_clbits (int) – Number of classical bits in the circuit.

  • comm (QMPICommunicator) – Communicator associated with the circuit.

Return type:

None

property num_qubits: int

Return the number of qubits in the circuit.

Returns:

The number of qubits.

property num_clbits: int

Return the number of classical bits in the circuit.

Returns:

The number of classical bits.

property num_comm_qubits: int

Return how many communication qubits the circuit needs.

This is the largest number of communication qubits held at the same time by the distributed protocols traced so far, and it is only final once the circuit has been fully traced.

Returns:

The number of communication qubits to reserve on the backend.

property num_protocol_clbits: int

Return how many classical bits the distributed protocols need.

These bits carry the correction outcomes of teledata/telegate and are additional to the num_clbits requested by the user, so a protocol never clobbers a user measurement.

Returns:

The number of protocol classical bits to reserve on the backend.

property ops: OperationContainer

Return the root operation container.

Returns:

The operation container storing the circuit operations.

property comm: QMPICommunicator

Return the communicator associated with the circuit.

Returns:

The circuit communicator.

translate(op)[source]

Dispatch an operation to its corresponding translation method.

Parameters:

op (Operation) – Operation to translate.

Returns:

The translated backend instruction or instructions.

Raises:

TypeError – If the operation type is unknown.

Return type:

Any

comm_qubit(slot)[source]

Return the circuit-wide index of a communication-qubit slot.

Communication qubits are addressed right after the data qubits, so the value returned here can be handed to any gate of the fluent API just like a data qubit index.

Parameters:

slot (int) – Communication-qubit slot reserved by a protocol.

Returns:

The qubit index addressing that slot.

Return type:

int

h(qubit)[source]

Apply a Hadamard gate to a qubit.

Parameters:

qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

x(qubit)[source]

Apply a Pauli-X gate to a qubit.

Parameters:

qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

y(qubit)[source]

Apply a Pauli-Y gate to a qubit.

Parameters:

qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

z(qubit)[source]

Apply a Pauli-Z gate to a qubit.

Parameters:

qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

s(qubit)[source]

Apply an S gate to a qubit.

Parameters:

qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

sdg(qubit)[source]

Apply an S-dagger gate to a qubit.

Parameters:

qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

t(qubit)[source]

Apply a T gate to a qubit.

Parameters:

qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

tdg(qubit)[source]

Apply a T-dagger gate to a qubit.

Parameters:

qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

rx(theta, qubit)[source]

Apply an X-axis rotation to a qubit.

Parameters:
  • theta (float) – Rotation angle in radians.

  • qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

ry(theta, qubit)[source]

Apply a Y-axis rotation to a qubit.

Parameters:
  • theta (float) – Rotation angle in radians.

  • qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

rz(theta, qubit)[source]

Apply a Z-axis rotation to a qubit.

Parameters:
  • theta (float) – Rotation angle in radians.

  • qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

cx(control, target)[source]

Apply a controlled-X gate.

Parameters:
  • control (int) – Control qubit index.

  • target (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

cz(control, target)[source]

Apply a controlled-Z gate.

Parameters:
  • control (int) – Control qubit index.

  • target (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

cs(control, target)[source]

Apply a controlled-S gate.

Parameters:
  • control (int) – Control qubit index.

  • target (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

ct(control, target)[source]

Apply a controlled-T gate.

Parameters:
  • control (int) – Control qubit index.

  • target (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

swap(qubit1, qubit2)[source]

Apply a SWAP gate between two qubits.

Parameters:
  • qubit1 (int) – First qubit index.

  • qubit2 (int) – Second qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

cp(control, target, theta)[source]

Apply a controlled phase gate.

Generalises cs() (theta = pi/2) and ct() (theta = pi/4), which is what the rotations of a QFT are made of.

Parameters:
  • control (int) – Control qubit index.

  • target (int) – Target qubit index.

  • theta (float) – Phase angle in radians.

Returns:

The current circuit instance.

Return type:

Circuit

crz(theta, control, target)[source]

Apply a controlled-RZ gate.

Parameters:
  • theta (float) – Rotation angle in radians.

  • control (int) – Control qubit index.

  • target (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

ccx(control1, control2, target)[source]

Apply a Toffoli gate.

Parameters:
  • control1 (int) – First control qubit index.

  • control2 (int) – Second control qubit index.

  • target (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

measure(qubit, cbit)[source]

Measure a qubit into a classical bit.

Parameters:
  • qubit (int) – Measured qubit index.

  • cbit (int) – Destination classical bit index.

Returns:

The current circuit instance.

Return type:

Circuit

measure_all()[source]

Measure every qubit into the classical bit of the same index.

Returns:

The current circuit instance.

Raises:

ValueError – If there are fewer classical bits than qubits.

Return type:

Circuit

reset(qubit)[source]

Reset a qubit to the |0⟩ state.

Parameters:

qubit (int) – Target qubit index.

Returns:

The current circuit instance.

Return type:

Circuit

barrier(qubits=None)[source]

Insert a barrier.

Parameters:

qubits (List[int] | None) – Qubits to include in the barrier. If None, the barrier applies to the full circuit.

Returns:

The current circuit instance.

Return type:

Circuit

qsend(qubits, dest_rank)[source]

Send qubits to another rank.

The backend adapter decides the concrete transfer protocol; each qubit is transferred by its own protocol block, which borrows one communication qubit and two protocol classical bits for as long as the transfer lasts.

Parameters:
  • qubits (List[int]) – Local qubit indices to send.

  • dest_rank (int) – Destination rank.

Returns:

The current circuit instance.

Return type:

Circuit

qrecv(qubits, src_rank)[source]

Receive qubits from another rank into local qubit slots.

Parameters:
  • qubits (List[int]) – Local qubit indices that will receive the incoming qubits.

  • src_rank (int) – Source rank.

Returns:

The current circuit instance.

Return type:

Circuit

qscatter(qubits, root)[source]

Scatter the qubits of the root among the other ranks.

Collective call: every rank of the communicator must reach it. The root passes its whole buffer, which is split into one chunk per other rank in rank order, and each of those ranks passes the local qubits its chunk is to land on — as many as the root reserved for it.

Unlike MPI_Scatter, the root keeps no chunk of its own: the buffer is shared out among the other ranks only, so a root scattering two qubits over two ranks is left holding none of them. Qubits are moved, not copied: the whole buffer is teleported away, and the root’s qubits are back in |0⟩ once the call returns. The qubits a chunk lands on must be in |0⟩ when the call is reached, as they must be for a plain qrecv(): whatever they held is not saved anywhere, the transfer destroys it.

Parameters:
  • qubits (List[int]) – The whole buffer on the root, this rank’s landing qubits on every other rank.

  • root (int) – Rank whose buffer is scattered.

Returns:

The local qubits holding this rank’s chunk – the ones passed in on every rank but the root, and an empty list on the root, which keeps nothing.

Raises:
  • IndexError – If a qubit index is not a data qubit of this rank.

  • ValueError – If the root is not a rank of the communicator, if it is the only rank, if the buffer is empty, or if it does not split evenly among the other ranks.

Return type:

List[int]

qgather(qubits, root)[source]

Gather the qubits of every rank into the root.

Collective call, like MPI_Gather, and the mirror image of qscatter(): the root passes the whole buffer the chunks are to land on — its own chunk, at position root, already holding its contribution — and every other rank passes the local qubits it contributes.

Qubits are moved here as well, so once the call is over the contributors are left with theirs back in |0⟩ and only the root holds the gathered data. The slots the root gathers into — every one of its buffer but its own chunk — must be in |0⟩ when the call is reached, exactly as for a plain qrecv().

Parameters:
  • qubits (List[int]) – The whole buffer on the root, this rank’s contribution on every other rank.

  • root (int) – Rank the qubits are gathered into.

Returns:

The whole buffer on the root, this rank’s contribution elsewhere.

Raises:
  • IndexError – If a qubit index is not a data qubit of this rank.

  • ValueError – If the root is not a rank of the communicator, if the buffer is empty, or if the root’s buffer does not split evenly among the ranks.

Return type:

List[int]

expose(qubit, ranks, root=None)[source]

Open a telegate window sharing a control qubit across ranks.

This is a collective call: every rank in [root] + ranks must reach it, exactly as they all reach an MPI_Bcast. The root lends the state of qubit to the other participants, which each receive it on a communication qubit of their own and can then use it as a local control until the matching unexpose().

Parameters:
  • qubit (int | None) – Data qubit to expose. Read on the root only; the other participants may pass None.

  • ranks (List[int]) – Ranks the qubit is exposed to.

  • root (int | None) – Rank exposing its qubit. Defaults to the calling rank.

Returns:

The qubit index to use as control on this rank — qubit itself on the root, the freshly reserved communication qubit on every receiver — or None if this rank does not take part.

Raises:
  • IndexError – If the root exposes something other than a data qubit.

  • ValueError – If the participant list is empty or names no receiver.

Return type:

int | None

unexpose(ranks, root=None)[source]

Close the telegate window opened by the matching expose().

Collective as well: the same ranks that opened the window must close it. The communication qubit and the protocol classical bits it held are returned to the pool, so a later window can reuse them.

Parameters:
  • ranks (List[int]) – Ranks the qubit was exposed to.

  • root (int | None) – Rank that exposed its qubit. Defaults to the calling rank.

Returns:

The current circuit instance.

Raises:

RuntimeError – If no matching expose window is open.

Return type:

Circuit

Resources

Reusable index pools for the resources a distributed protocol borrows.

Communication qubits and the classical bits used by the correction rounds of teledata/telegate are scarce, backend-managed resources: they are taken when a protocol block opens and given back when it closes, so two blocks that never overlap in time can share the same physical resource.

This module provides the tiny allocator both the circuit layer and the backend adapters rely on to agree, without any inter-rank communication, on which slot each protocol block uses.

class netqmpi.sdk.resources.IndexPool[source]

Bases: object

Allocator of small non-negative indices with reuse.

Indices are handed out from a free list first and only then from a fresh counter, which keeps the total footprint at the maximum number of simultaneously held indices rather than the total number of acquisitions.

Example:

pool = IndexPool()
a = pool.acquire(2)   # [0, 1]
pool.release(a)
b = pool.acquire(1)   # [0]  -- reused
pool.size             # 2
__init__()[source]

Initialize an empty pool.

Return type:

None

property size: int

Return how many distinct indices the pool ever handed out.

Returns:

The high-water mark of the allocator.

acquire(count=1)[source]

Reserve count indices.

Parameters:

count (int) – Number of indices to reserve.

Returns:

The reserved indices, in ascending order.

Raises:

ValueError – If count is not strictly positive.

Return type:

List[int]

release(indices)[source]

Give indices back to the pool so later blocks can reuse them.

Parameters:

indices (List[int]) – Indices previously returned by acquire().

Return type:

None

Operations

The operation model recorded by a circuit and consumed by a backend adapter. Every class can be imported directly from netqmpi.sdk.operations.

Base operation

Abstract base for all quantum operations (Command pattern).

class netqmpi.sdk.operations.operation.Operation[source]

Bases: ABC

Abstract base class for all quantum operations.

Follows the Command pattern: each subclass encapsulates all the information needed to describe a single quantum action, keeping it independent of any backend.

Variables:

qubits (List[int]) – Qubit indices this operation acts on.

__init__(qubits)[source]
Parameters:

qubits (List[int]) – Qubit indices this operation acts on.

Raises:

TypeError – If qubits is not a list of integers.

Return type:

None

property qubits: List[int]

Returns a copy of the qubit indices this operation acts on.

Container

Composite container for quantum operations.

class netqmpi.sdk.operations.container.OperationContainer[source]

Bases: Operation

Composite container for quantum operations.

Implements the Composite pattern: it can hold both leaf Operation instances and nested OperationContainer objects, allowing circuits to be built hierarchically.

flatten() produces a depth-first iterator over every leaf Operation in insertion order.

Example:

ops = OperationContainer()
ops.add(Gate('H', [0])).add(Measure(0, 0))

sub = OperationContainer()
sub.add(Gate('X', [1]))
ops.add_circuit(sub)

for op in ops.flatten():
    print(op)
__init__()[source]
Parameters:

qubits – Qubit indices this operation acts on.

Raises:

TypeError – If qubits is not a list of integers.

Return type:

None

property children: List[Operation | OperationContainer]

Direct children, leaves and sub-containers alike, in insertion order.

Unlike flatten(), this keeps the nesting: a sub-container comes out whole, so a caller dispatching on the operation type still sees what kind of block it is instead of only its leaves.

property qubits: List[int]

Union of all qubit indices across children, in insertion order.

add(operation)[source]

Append a single leaf Operation.

Parameters:

operation (Operation) – The operation to add.

Returns:

self, enabling method chaining.

Raises:

TypeError – If operation is not an Operation instance.

Return type:

OperationContainer

flatten()[source]

Depth-first iterator over all leaf operations.

Yields:

Each Operation in the order they were added, recursing into nested containers.

Return type:

Iterator[Operation]

Gates

Unitary gate operations (Command pattern).

class netqmpi.sdk.operations.gate.Gate[source]

Bases: Operation

Generic unitary single gate (H, X, RZ, U3, …).

Variables:
  • name (str) – Standard gate name, normalised to upper-case.

  • qubits (List[int]) – Qubits the gate acts on.

  • params (List[float]) – Optional rotation / angle parameters.

__init__(name, qubits, params=None)[source]
Parameters:
  • name (str) – Gate identifier (e.g. 'H', 'X', 'RZ').

  • qubits (List[int]) – Qubit indices.

  • params (List[float] | None) – Rotation angles or gate parameters (default: []).

Raises:

ValueError – If name is empty.

Return type:

None

property name: str

Gate name in upper-case.

property params: List[float]

Returns a copy of the gate parameters.

class netqmpi.sdk.operations.gate.ControlledGate[source]

Bases: Operation

Generic controlled gate.

Wraps one or more target Gate objects behind a set of control qubits. The overall qubit list is controls + all target qubits.

Variables:
  • controls (List[int]) – Control qubit indices.

  • targets (List[Gate]) – Gates applied when all controls are |1⟩.

__init__(controls, targets)[source]
Parameters:
  • controls (List[int]) – Control qubit indices.

  • targets (List[Gate]) – Gate instances to apply conditionally.

Raises:
Return type:

None

property controls: List[int]

Control qubit indices.

property targets: List[Gate]

Target gates applied under control.

class netqmpi.sdk.operations.gate.ClassicalControlledGate[source]

Bases: Operation

Gate conditioned on the value of one or more classical bits.

Unlike ControlledGate (whose controls are qubits), here the controls are classical bit indices — typically the results of prior measurements. The gate fires when all listed cbits equal 1.

The qubits property returns only the target qubits (the cbits are classical and therefore not part of the quantum register).

Variables:
  • cbits (List[int]) – Classical bit indices used as conditions.

  • targets (List[Gate]) – Gates applied when all conditions are satisfied.

__init__(cbits, targets)[source]
Parameters:
  • cbits (List[int]) – Classical bit indices acting as conditions.

  • targets (List[Gate]) – Gate instances to apply when all cbits are 1.

Raises:
Return type:

None

property cbits: List[int]

Classical bit indices used as conditions.

property targets: List[Gate]

Target gates applied when all conditions are satisfied.

Non-unitary operations

Non-unitary quantum operations: Measure, Reset, Barrier.

class netqmpi.sdk.operations.non_unitary.Measure[source]

Bases: Operation

Measurement — collapses a qubit and stores the outcome in a classical bit.

Variables:
  • qubit (int) – Qubit index to measure.

  • cbit (int) – Classical bit index that receives the result.

__init__(qubit, cbit)[source]
Parameters:
  • qubit (int) – Qubit index to measure.

  • cbit (int) – Classical bit index for the result.

Return type:

None

property qubit: int

Qubit index.

property cbit: int

Classical bit index.

class netqmpi.sdk.operations.non_unitary.Reset[source]

Bases: Operation

Reset — unconditionally sets a qubit back to |0⟩.

Variables:

qubit (int) – Qubit index to reset.

__init__(qubit)[source]
Parameters:

qubit (int) – Qubit index to reset.

Return type:

None

property qubit: int

Qubit index.

class netqmpi.sdk.operations.non_unitary.Barrier[source]

Bases: Operation

Barrier — prevents the compiler from re-ordering operations across it.

An empty qubit list means the barrier spans the whole circuit.

Variables:

qubits (List[int]) – Qubits the barrier spans.

__init__(qubits=None)[source]
Parameters:

qubits (List[int] | None) – Qubits the barrier spans. Defaults to [] (full-circuit barrier).

Return type:

None

Communication operations

Inter-rank communication primitives as first-class Operations.

Each class encodes the intent of a distributed quantum operation. The concrete backend adapter is responsible for implementing the protocol (e.g. teleportation, GHZ) inside Circuit.translate(op).

All classes inherit from Operation, so they flow through OperationContainer and flatten() exactly like any gate or measurement.

Three families of primitives live here:

  • Point-to-point operations (QSend, QRecv), which every rank can translate on its own because the backend emits an independent instruction block on each side.

  • Rooted transfers (RootedTransfer subclasses such as QScatter and QGather), which every rank must call but which expand, on each of them, into the point-to-point transfers above. They are containers holding those transfers, so a backend that can send and receive a qubit gets them for free.

  • Collective operations (CollectiveOperation subclasses such as Expose and Unexpose), whose backend expansion writes instructions into every participating circuit at once and therefore can only be emitted when all participants have reached the matching call. Each participant carries the resources it contributes to the protocol (a communication-qubit slot, protocol classical bits) plus a tag that is identical across ranks, so the runtime can pair the calls up without any trace-time communication.

class netqmpi.sdk.operations.qmpi.CollectiveOperation[source]

Bases: Operation

Base class for operations that must be expanded jointly by all ranks.

A collective operation is recorded independently by every participating rank, but the backend can only translate it once all participants are sitting on the matching call. Two records match when they have the same type and the same tag.

Variables:
  • rank (int) – Rank whose circuit holds this record.

  • ranks (List[int]) – Participating ranks, in protocol order.

  • tag (str) – Identifier shared by every participant.

__init__(qubits, rank, ranks, tag)[source]
Parameters:
  • qubits (List[int]) – Local qubit indices the record acts on.

  • rank (int) – Rank owning this record.

  • ranks (List[int]) – Participating ranks, in protocol order.

  • tag (str) – Identifier shared by every participant.

Raises:

ValueError – If ranks is empty or tag is not a string.

Return type:

None

property rank: int

Rank owning this record.

property ranks: List[int]

Participating ranks, in protocol order.

property tag: str

Identifier shared by every participant.

matches(other)[source]

Report whether other is this rank’s counterpart of the same call.

Parameters:

other (object) – Candidate record held by another rank.

Returns:

True if both records belong to the same collective call.

Return type:

bool

class netqmpi.sdk.operations.qmpi.QSend[source]

Bases: Operation

Send local qubits to a remote rank.

The protocol (e.g. teleportation) is chosen by the backend adapter.

Variables:
  • qubits (List[int]) – Local qubit indices to send (consumed).

  • dest_rank (int) – Destination rank.

  • comm_slot (int) – Communication-qubit slot reserved locally.

  • clbits (List[int]) – Protocol classical bits reserved locally.

  • tag (str) – Identifier shared with the matching QRecv.

__init__(qubits, dest_rank, comm_slot=None, clbits=None, tag=None)[source]
Parameters:
  • qubits (List[int]) – Local qubit indices to send.

  • dest_rank (int) – Rank of the receiving process.

  • comm_slot (int | None) – Communication-qubit slot reserved for the transfer.

  • clbits (List[int] | None) – Protocol classical bits reserved for the transfer.

  • tag (str | None) – Identifier shared with the matching QRecv.

Raises:

ValueError – If qubits is empty or dest_rank is negative.

Return type:

None

property dest_rank: int

Destination rank.

property comm_slot: int | None

Communication-qubit slot reserved for the transfer.

property clbits: List[int]

Protocol classical bits reserved for the transfer.

property tag: str | None

Identifier shared with the matching QRecv.

class netqmpi.sdk.operations.qmpi.QRecv[source]

Bases: Operation

Receive qubits from a remote rank into local qubit slots.

Variables:
  • qubits (List[int]) – Local qubit indices where the state will land.

  • src_rank (int) – Source rank.

  • comm_slot (int) – Communication-qubit slot reserved locally.

  • clbits (List[int]) – Protocol classical bits reserved locally.

  • tag (str) – Identifier shared with the matching QSend.

__init__(qubits, src_rank, comm_slot=None, clbits=None, tag=None)[source]
Parameters:
  • qubits (List[int]) – Local qubit indices to receive into. len(qubits) determines how many qubits are expected.

  • src_rank (int) – Rank of the sending process.

  • comm_slot (int | None) – Communication-qubit slot reserved for the transfer.

  • clbits (List[int] | None) – Protocol classical bits reserved for the transfer.

  • tag (str | None) – Identifier shared with the matching QSend.

Raises:

ValueError – If qubits is empty or src_rank is negative.

Return type:

None

property src_rank: int

Source rank.

property n_qubits: int

Number of qubits to receive.

property comm_slot: int | None

Communication-qubit slot reserved for the transfer.

property clbits: List[int]

Protocol classical bits reserved for the transfer.

property tag: str | None

Identifier shared with the matching QSend.

class netqmpi.sdk.operations.qmpi.RootedTransfer[source]

Bases: OperationContainer

Base class for the rooted collectives built out of teledata.

QScatter and QGather move qubits between one root and every other rank. Because a quantum state cannot be copied, they can only be built out of transfers that consume the source qubit: each of them expands into one QSend per qubit leaving this rank and one QRecv per qubit arriving, and the record keeps those children so the backends translate the collective through the very same point-to-point path they already implement.

These records are deliberately not CollectiveOperation instances. A collective in that sense is one whose backend expansion writes into every participating circuit at once, and so has to wait for all the ranks; here each side is an ordinary point-to-point transfer that the runtime pairs up by tag, so every rank can be translated on its own.

Variables:
  • rank (int) – Rank owning this record.

  • root (int) – Rank the qubits are scattered from / gathered into.

  • ranks (List[int]) – Participating ranks, in rank order.

  • qubits (List[int]) – Local qubits taking part: the whole buffer on the root, this rank’s chunk elsewhere.

__init__(rank, root, ranks, qubits)[source]
Parameters:
  • rank (int) – Rank owning this record.

  • root (int) – Rank the qubits are scattered from / gathered into.

  • ranks (List[int]) – Participating ranks, in rank order.

  • qubits (List[int]) – Local qubits this rank contributes or receives.

Raises:

ValueError – If the participant list is empty, if it does not contain both rank and root, or if qubits is empty.

Return type:

None

property qubits: List[int]

Local qubits taking part in the transfer.

property rank: int

Rank owning this record.

property root: int

Rank the qubits are scattered from or gathered into.

property ranks: List[int]

Participating ranks, in rank order.

property is_root: bool

Whether the rank owning this record is the root.

class netqmpi.sdk.operations.qmpi.QScatter[source]

Bases: RootedTransfer

Scatter the qubits held by the root among the other ranks.

MPI_Scatter with qubits instead of bytes, save for one thing: the root keeps no chunk of its own. Its buffer is split into one chunk per other rank, in rank order, and handing a qubit over means moving it, so the whole buffer is teleported away and the root is left with its qubits back in |0⟩. After the call the data it scattered lives on the receivers alone.

Variables:
  • rank (int) – Rank owning this record.

  • root (int) – Rank whose buffer is scattered.

  • ranks (List[int]) – Participating ranks, in rank order.

  • qubits (List[int]) – Local qubits taking part: the whole buffer on the root, this rank’s chunk elsewhere.

property sender_rank: int

Rank that scatters the qubits.

class netqmpi.sdk.operations.qmpi.QGather[source]

Bases: RootedTransfer

Gather the qubits of every rank into the root.

The mirror image of QScatter, and MPI_Gather with qubits instead of bytes: rank r contributes its chunk, and the root ends up holding all of them in rank order. Here too the transfer moves the qubits, so once the call is over the contributors are left with theirs back in |0⟩ and only the root holds the data.

Variables:
  • rank (int) – Rank owning this record.

  • root (int) – Rank the qubits are gathered into.

  • ranks (List[int]) – Participating ranks, in rank order.

  • qubits (List[int]) – Local qubits taking part: the whole buffer on the root, this rank’s chunk elsewhere.

property recv_rank: int

Rank that gathers the qubits.

class netqmpi.sdk.operations.qmpi.Expose[source]

Bases: CollectiveOperation

Open a telegate window sharing a control qubit across ranks.

The root rank lends the state of one of its data qubits to every other participant, which receives it on a local communication qubit and can then apply locally-controlled gates with it. The backend realises this with a shared GHZ state (cat-entangler); the window is closed by the matching Unexpose.

Every participant records its own Expose, holding only the resources it contributes: one communication-qubit slot and the protocol classical bits used for the corrections (len(ranks) - 1 bits on the root, one bit on each receiver).

Variables:
  • rank (int) – Rank owning this record.

  • root (int) – Rank that exposes its data qubit.

  • ranks (List[int]) – Participants, root first.

  • data_qubit (int) – Exposed data qubit (root only, else None).

  • comm_slot (int) – Local communication-qubit slot.

  • clbits (List[int]) – Local protocol classical bits.

  • tag (str) – Identifier shared by every participant.

__init__(rank, root, ranks, tag, comm_slot, clbits, data_qubit=None)[source]
Parameters:
  • rank (int) – Rank owning this record.

  • root (int) – Rank exposing its data qubit.

  • ranks (List[int]) – Participants, root first.

  • tag (str) – Identifier shared by every participant.

  • comm_slot (int) – Local communication-qubit slot.

  • clbits (List[int]) – Local protocol classical bits.

  • data_qubit (int | None) – Exposed data qubit, on the root only.

Raises:

ValueError – If the participant list is inconsistent with root, or if the root does not provide a data qubit.

Return type:

None

property root: int

Rank that exposes its data qubit.

property receivers: List[int]

Ranks that receive the exposed control qubit.

property data_qubit: int | None

Exposed data qubit, or None outside the root.

property comm_slot: int

Local communication-qubit slot used by the protocol.

property clbits: List[int]

Local protocol classical bits used by the protocol.

class netqmpi.sdk.operations.qmpi.Unexpose[source]

Bases: CollectiveOperation

Close a telegate window opened by Expose.

Carries the very same resources as the Expose it closes, so the backend can emit the cat-disentangler (comm-qubit measurement and the phase correction on the root’s data qubit) without re-deriving them.

Variables:
  • rank (int) – Rank owning this record.

  • root (int) – Rank that exposed its data qubit.

  • ranks (List[int]) – Participants, root first.

  • data_qubit (int) – Exposed data qubit (root only, else None).

  • comm_slot (int) – Local communication-qubit slot.

  • clbits (List[int]) – Local protocol classical bits.

  • tag (str) – Identifier shared by every participant.

__init__(rank, root, ranks, tag, comm_slot, clbits, data_qubit=None)[source]
Parameters:
  • rank (int) – Rank owning this record.

  • root (int) – Rank that exposed its data qubit.

  • ranks (List[int]) – Participants, root first.

  • tag (str) – Identifier shared by every participant.

  • comm_slot (int) – Local communication-qubit slot.

  • clbits (List[int]) – Local protocol classical bits.

  • data_qubit (int | None) – Exposed data qubit, on the root only.

Return type:

None

classmethod closing(expose)[source]

Build the record that closes a given Expose.

Parameters:

expose (Expose) – The expose record opened by this rank.

Returns:

An Unexpose carrying the same protocol resources.

Return type:

Unexpose

property root: int

Rank that exposed its data qubit.

property receivers: List[int]

Ranks that received the exposed control qubit.

property data_qubit: int | None

Exposed data qubit, or None outside the root.

property comm_slot: int

Local communication-qubit slot used by the protocol.

property clbits: List[int]

Local protocol classical bits used by the protocol.