Backend adapters¶
Four worked implementations of the Executor
contract. Application code never imports these; the CLI selects one from its
flag.
Note
netqasm and cunqa are imported at module level by their adapters and
are mocked when these docs are built, since neither can be installed in a
public CI runner. Signatures and docstrings are accurate; the types they
borrow from those packages are not resolved into links.
CUNQA¶
The reference backend: HPC emulation through virtual QPUs, and the only adapter implementing every communication primitive. See CUNQA backend.
Executor and configuration¶
Executor adapter for the CUNQA backend.
This module provides an implementation of the Executor interface for
running applications with the CUNQA backend.
A NetQMPI run needs one vQPU per rank. There are two ways to get them, and the configuration picks between them:
Attach (the default): the vQPUs are already up, raised by the user before the
netqmpicommand withqraise, and the run takes the ones it needs from a single family. The allocation outlives the run, so several programs can be launched against the same vQPUs without paying for a SLURM job each time, and a family larger than the run is fine — the vQPUs no rank is using are kept busy with a trivial circuit, since the family’s executor runs a round only once every one of its vQPUs has submitted something.Raise (
qraise: truein the config): the adapter raises the vQPUs itself, runs, and drops them again. The vQPU definition to raise them with is thebackendsetting, so the qubit budget of the run is under the user’s control.
- class netqmpi.runtime.adapters.cunqa.cunqa_executor.CunqaRunConfig[source]¶
Bases:
RunConfigExtension of
RunConfigwith CUNQA-specific execution parameters.- Variables:
qraise (bool) – Whether to raise the vQPUs for this run and drop them afterwards. When
False(the default), the run attaches to vQPUs that are already up.backend (str | None) – Path to the vQPU definition file the vQPUs are raised with, which is what fixes their qubit budget. Only meaningful when
qraiseis enabled;Noneleaves the choice to CUNQA’s own default.simulator (str) – Simulator backing each vQPU. Raise-mode only.
time (str) – Wall-clock reservation for the SLURM job, as
D-HH:MM:SSorHH:MM:SS. Raise-mode only.family (str | None) – Family the vQPUs belong to. Selects which of the running vQPUs to attach to, or names the family to raise.
co_located (bool) – Whether the vQPUs are reachable from other nodes (CUNQA’s co-located mode) rather than only from the node they run on (hpc mode).
- class netqmpi.runtime.adapters.cunqa.cunqa_executor.CunqaExecutorAdapter[source]¶
Bases:
ExecutorExecutor adapter for the CUNQA backend.
This adapter enables execution through CUNQA while conforming to the common interface defined by
Executor.- __init__(size, config=None)[source]¶
Initialize the CUNQA executor adapter.
- Parameters:
size (int) – Number of available CUNQA nodes.
config (CunqaRunConfig) – Backend-specific configuration parameters.
- create_circuit(num_qubits, num_clbits, comm)[source]¶
Create a CUNQA circuit adapter.
- Parameters:
num_qubits (int) – Number of qubits in the circuit.
num_clbits (int) – Number of classical bits in the circuit.
comm (CunqaCommunicator) – Communicator associated with the circuit.
- Returns:
A
CunqaCircuitAdapterinstance.- Return type:
Circuit adapter¶
Adapter for the CUNQA backend circuit.
This module implements the Circuit interface for CUNQA circuits.
Local operations are translated one by one, exactly as the abstract
Circuit dispatch expects. Collective ones
cannot: CUNQA’s telegate helpers (cunqa.qc_protocols.cat_entangler()
and cunqa.qc_protocols.cat_disentangler()) write instructions into
every participating circuit in a single call, so they can only run once
all the ranks are known and each of them has been translated up to the
matching call. translate_group() performs that joint pass, walking
every rank’s operation stream and stopping at the collective calls to
expand them in one go.
The rooted transfers (qscatter, qgather) sit in between: they are
collective for the user, since every rank has to call them, but each rank’s
half is a sequence of ordinary teledata blocks that CUNQA pairs up by tag
at run time, so they need no joint expansion.
- netqmpi.runtime.adapters.cunqa.cunqa_circuit.PROTOCOL_CLREG = 'netqmpi_protocol'¶
Name of the classical register holding the correction bits of the distributed protocols, kept apart from the user’s own register.
- class netqmpi.runtime.adapters.cunqa.cunqa_circuit.CunqaCircuitAdapter[source]¶
Bases:
CircuitCircuit adapter for the CUNQA backend.
This class wraps a
CunqaCircuitinstance and exposes the common interface defined by the abstractCircuitbase class.- __init__(num_qubits, num_clbits, comm)[source]¶
Initialize the CUNQA circuit adapter.
The underlying
CunqaCircuitstarts with no communication qubit and no protocol classical bits: how many are needed only becomes known once the application has been traced, so they are added byprepare()right before the instructions are emitted.- Parameters:
num_qubits (int) – Number of qubits in the circuit.
num_clbits (int) – Number of classical bits in the circuit.
comm (CunqaCommunicator) – Communicator associated with the circuit.
- property cunqa_circuit: cunqa.circuit.CunqaCircuit¶
Return the underlying CUNQA circuit.
- Returns:
The wrapped
CunqaCircuit.
- prepare()[source]¶
Reserve on the CUNQA circuit the resources the trace asked for.
Adds the communication qubits and the protocol classical register sized by the trace. Must run before any instruction is emitted, since the protocol register has to sit after the user’s own bits.
Calling it twice is a no-op: a second call would append a further register and quietly renumber the protocol bits.
- Return type:
None
- netqmpi.runtime.adapters.cunqa.cunqa_circuit.idle_circuit(index)[source]¶
Build the trivial circuit sent to a vQPU that no rank is using.
CUNQA runs one executor per family of vQPUs, and every round that executor waits for a circuit from each vQPU of the family before it runs anything: a vQPU left out does not sit idle, it holds up the whole family. A run with fewer ranks than the family has vQPUs therefore submits this circuit to each of the spare ones, which does nothing, is over immediately, and whose counts are discarded.
- Parameters:
index (int) – Position of the spare vQPU, used to give the circuit an id of its own.
- Returns:
A one-qubit circuit holding a single measurement.
- Return type:
cunqa.circuit.CunqaCircuit
- netqmpi.runtime.adapters.cunqa.cunqa_circuit.check_transfers(adapters)[source]¶
Check that every point-to-point transfer of the group has both halves.
A
qsendand itsqrecvare paired at run time by the tag both sides derive from the ranks involved, so a transfer whose other half was never traced is not an error CUNQA can report: the vQPU that made the call simply waits for a partner that never comes, and the run hangs with nothing to show for it. Reading the group’s own records is enough to see it coming, and to say which rank is left waiting for what.Only the backend-agnostic records are read, so any backend that pairs its transfers by tag can use this as it stands.
- Parameters:
adapters (Dict[int, CunqaCircuitAdapter]) – Circuit adapter of every rank, keyed by rank.
- Raises:
RuntimeError – If a
qsendhas no matchingqrecv, or aqrecvno matchingqsend.- Return type:
None
- netqmpi.runtime.adapters.cunqa.cunqa_circuit.translate_group(adapters)[source]¶
Translate the circuits of a whole group of ranks into CUNQA circuits.
Every rank’s operation stream is drained until it reaches a collective call. Once all the participants of a collective are waiting on it, the call is expanded into all of their circuits at once and they resume. This mirrors what the ranks would do if they really ran side by side, while keeping each circuit’s instructions in program order.
- Parameters:
adapters (Dict[int, CunqaCircuitAdapter]) – Circuit adapter of every rank, keyed by rank.
- Returns:
The translated CUNQA circuits, ordered by rank.
- Raises:
RuntimeError – If a collective names a rank outside the group, or if the ranks block on collectives that never match — the trace equivalent of a deadlock.
- Return type:
List[cunqa.circuit.CunqaCircuit]
Communicator¶
Concrete BaseCommunicator backed
by the CUNQA runtime.
This communicator adapts the backend-specific communication layer to the
backend-agnostic QMPICommunicator interface.
CUNQA takes the whole distributed program at once — every rank’s circuit
is submitted together and the vQPUs resolve the communication directives
between them at run time. The ranks therefore do not execute as they are
traced: each of them records its circuits, and the last one to leave its
with comm: block triggers the joint translation and the actual run.
- class netqmpi.runtime.adapters.cunqa.cunqa_communicator.CunqaSession[source]¶
Bases:
objectState shared by every rank of a single NetQMPI run.
Holds what only makes sense for the program as a whole: the vQPUs, the run configuration, the communicators that have already finished tracing, and the results once they are back.
The results of a distributed program are the results of every rank — a single rank’s counts say nothing about the correlations the program was written to produce — so they are kept here, whole, and shared with all the communicators rather than split among them.
- Variables:
size – Number of ranks in the run.
qpus – vQPUs backing the ranks, ordered by rank.
idle_qpus – vQPUs of the same family that no rank is using.
config – Run configuration shared by every rank.
communicators – Communicator of each rank, keyed by rank.
finished – Ranks that are done tracing.
results – Counts of every rank, keyed by rank, once the run is over.
- __init__(size, qpus, config, idle_qpus=None)[source]¶
Initialize the session.
- Parameters:
size (int) – Number of ranks in the run.
qpus (List[cunqa.qpu.QPU]) – vQPUs backing the ranks, ordered by rank.
config (RunConfig) – Run configuration shared by every rank.
idle_qpus (List[cunqa.qpu.QPU] | None) – vQPUs of the same family that no rank is using, and which have to be kept busy anyway so the family’s executor is not left waiting for them.
- Return type:
None
- class netqmpi.runtime.adapters.cunqa.cunqa_communicator.CunqaCommunicator[source]¶
Bases:
QMPICommunicatorCUNQA-backed communicator for a single rank.
This class provides the communicator implementation used by the CUNQA backend and is injected into
QMPICommunicator.- Parameters:
rank – Numeric index of the current rank.
size – Total number of ranks in the communicator.
qpu – vQPU backing this rank.
config – Run configuration.
session – State shared with the other ranks of the run.
- __init__(rank, size, qpu, config, session=None)[source]¶
Initialize the communicator.
- Parameters:
rank (int) – Numeric index of the current rank.
size (int) – Total number of ranks in the communicator.
qpu (cunqa.qpu.QPU) – vQPU backing this rank.
config (RunConfig) – Run configuration.
session (CunqaSession) – State shared with the other ranks of the run. When omitted, a single-rank session is created.
- Return type:
None
- property session: CunqaSession¶
Return the state shared with the other ranks of the run.
- Returns:
The session this communicator belongs to.
NetQASM / SquidASM¶
Low-level quantum-network simulation. See NetQASM / SquidASM backend.
Executor and configuration¶
NetQASM backend adapter.
This module implements the full
Executor contract for the NetQASM
simulator, including circuit creation, application construction, and
simulation execution.
This is the only file in the NetQASM adapter layer allowed to import
from netqasm.*.
- class netqmpi.runtime.adapters.netqasm.netqasm_executor.NetQASMRunConfig[source]¶
Bases:
RunConfigExtension of
RunConfigwith NetQASM-specific simulation parameters.- Variables:
netqasm_major (int) – Major NetQASM release this run expects, 2 by default. The
--netqasm1.0flag sets it to 1. It selects an environment rather than an adapter: the API this backend uses is the same in both releases, so the value is checked against what is installed and the run stops early if they disagree.shots (int) – Number of times the program is simulated. Overrides the generic default of 1024, which is wrong by two orders of magnitude for this backend: SquidASM simulates the whole network once per shot, at roughly a second each on a two-rank program, so the generic default would take a quarter of an hour and look like a hang. Raise it with
--shotswhen the statistics matter more than the wait.formalism (netqasm.runtime.settings.Formalism) – Quantum state formalism to use in the simulation.
network_config (Any | None) – Network configuration describing the simulated topology. If
None, the default topology is used.log_cfg (Any | None) – NetQASM log configuration controlling per-rank instruction logging.
- formalism: netqasm.runtime.settings.Formalism¶
- argv = None¶
- __init__(shots=50, netqasm_major=2, formalism=<factory>, enable_logging=True, hardware='generic', post_function=None, network_config=None, log_cfg=None, roles='roles.yaml')¶
- class netqmpi.runtime.adapters.netqasm.netqasm_executor.NetQASMExecutorAdapter[source]¶
Bases:
ExecutorExecutor implementation for the NetQASM backend.
This adapter handles circuit creation, application construction, and simulation execution for the NetQASM runtime.
- __init__(size, config=None)[source]¶
Initialize the NetQASM executor adapter.
- Parameters:
size (int) – Number of available NetQASM nodes.
config (NetQASMRunConfig) – NetQASM-specific configuration.
- Raises:
RuntimeError – If the installed NetQASM is not the major release the configuration asks for.
- Return type:
None
- create_circuit(num_qubits, num_clbits, comm)[source]¶
Create a NetQASM circuit adapter.
- Parameters:
num_qubits (int) – Number of qubits in the circuit.
num_clbits (int) – Number of classical bits in the circuit.
comm (NetQASMCommunicator) – Communicator bound to the circuit.
- Returns:
A
NetQASMCircuitAdapterinstance.- Return type:
- build_apps(file, size)[source]¶
Load a file and build a NetQASM application instance.
The resulting application instance contains one program per rank, each wrapping the user
mainfunction with an injectedEnvironment.- Parameters:
- Returns:
A
ApplicationInstanceready to be passed torun().- Raises:
ValueError – If
fileisNoneor does not point to a Python file.- Return type:
- run(apps)[source]¶
Run an application instance through the NetQASM simulator.
- Parameters:
app_instance – Application instance returned by
build_apps().apps (Any)
- Return type:
None
Circuit adapter¶
Circuit adapter for the NetQASM eager-execution model.
NetQASM does not provide a circuit object: each instruction is dispatched
to the simulator as soon as it is invoked on a
netqasm.sdk.qubit.Qubit. Therefore, this adapter:
Allocates a
Qubitarray in__init__using the active connection exposed byself._comm.Overrides the gate methods of the base
Circuitso that each method first delegates tosuper()to record the operation in theOperationContainer, and then executes the corresponding NetQASM SDK call immediately.Overrides the inter-rank communication primitives (
qsend,qrecv,qscatter,qgather,expose, andunexpose) with the concrete teleportation and collective protocols implemented through the NetQASM SDK.Keeps
translate()as a no-op, since execution has already taken place eagerly.Flushes the connection in
build()and returns the qubit array together with the classical measurement results.
- class netqmpi.runtime.adapters.netqasm.netqasm_circuit.NetQASMCircuitAdapter[source]¶
Bases:
CircuitEager-execution circuit adapter for NetQASM.
This adapter executes operations immediately on live NetQASM qubits while still recording them through the base
Circuitinterface.- Variables:
_qubits – Live NetQASM qubits allocated at construction time.
_results – Classical measurement results indexed by classical bit.
- __init__(num_qubits, num_clbits, comm)[source]¶
Initialize the NetQASM circuit adapter.
- Parameters:
num_qubits (int) – Number of qubits to allocate.
num_clbits (int) – Number of classical result slots.
comm (NetQASMCommunicator) – Communicator providing the active NetQASM connection.
- Return type:
None
- reset_round()[source]¶
Clear the state a simulated round leaves behind.
Only the qubits are per-round. The emitted operations are closures that look their qubits up when they run, so one translation serves every shot; re-translating each time simply grew the list by a full copy of the program.
- Return type:
None
Communicator¶
Concrete BaseCommunicator backed
by the NetQASM SDK.
This is the only communicator module allowed to import from
netqasm.*. It provides the low-level resource management delegated by
the backend-agnostic QMPICommunicator facade, including
connections, EPR sockets, and classical sockets.
- class netqmpi.runtime.adapters.netqasm.netqasm_communicator.NetQASMCommunicator[source]¶
Bases:
QMPICommunicatorNetQASM-backed communicator for a single rank.
This class provides the backend-specific communication resources used by the NetQASM runtime adapter, including the NetQASM connection, EPR sockets, and lazily created classical sockets.
- Parameters:
rank – Numeric index of the current rank.
size – Total number of ranks in the communicator.
_config – NetQASM application configuration associated with this rank.
- netqasm_circuits = []¶
- communicators: List[NetQASMCommunicator] = []¶
Every rank’s communicator for the current run, so the state that a single simulated round leaves behind can be cleared before the next.
- __init__(rank, size, config)[source]¶
Initialize the NetQASM communicator.
- Parameters:
rank (int) – Numeric index of the current rank.
size (int) – Total number of ranks in the communicator.
_config – NetQASM application configuration associated with this rank.
config (NetQASMRunConfig)
- Return type:
None
- property epr_sockets: Dict[str, Dict[str, netqasm.sdk.EPRSocket]]¶
Return the EPR sockets indexed by rank name.
- Returns:
A nested mapping of EPR sockets.
- classmethod reset_run()[source]¶
Forget the programs and communicators of a finished run.
- Return type:
None
- get_socket(my_rank, other_rank)[source]¶
Return a classical socket between two ranks.
A fresh one every time, deliberately. These used to be cached for the life of the communicator, which survives the network they were opened on: the second shot then reached for a socket belonging to a torn-down network and failed with “Socket is not connected so cannot send”. A socket is cheap, and it belongs to one connection.
- get_epr_socket(my_rank, other_rank)[source]¶
Return the EPR socket between two ranks.
- Parameters:
- Returns:
The EPR socket connecting the two ranks.
- Raises:
RuntimeError – If the requested EPR socket does not exist.
- Return type:
netqasm.sdk.EPRSocket
- create_qubit()[source]¶
Create a new qubit on the underlying NetQASM connection.
- Returns:
A newly allocated NetQASM qubit.
- property connection: netqasm.sdk.external.NetQASMConnection¶
Return the underlying NetQASM connection.
- Returns:
The active NetQASM connection.
Qiskit Aer¶
Shot-based circuit simulation on one monolithic circuit. See Qiskit Aer backend.
Executor¶
Executor adapter for Qiskit AerSimulator.
This module provides the AerExecutorAdapter implementation of
the Executor interface for running
NetQMPI applications on Qiskit’s AerSimulator backend.
- class netqmpi.runtime.adapters.aer.aer_executor.AerExecutorAdapter[source]¶
Bases:
ExecutorExecutor adapter that runs NetQMPI apps on Qiskit’s AerSimulator.
Owns the single global QuantumCircuit every rank writes into. It is built by
lay_out()once the ranks have finished tracing, because only then are the widths they each asked for known; a rank’s slice is sized to its own request rather than to a width assumed common to all.run()launches every rank in a separate thread.build_apps()installs athreading.BarrieronAerCommunicatorso that__exit__can synchronise all threads before and after the simulation.- __init__(size, config=None)[source]¶
Initialize the AerSimulator executor adapter.
- Parameters:
size (int) – Number of parallel ranks to simulate.
config (AerSimulatorConfig) – AerSimulator-specific configuration. Defaults to
AerSimulatorConfigwith its built-in defaults.
- Return type:
None
- create_circuit(num_qubits, num_clbits, comm)[source]¶
Create an AerCircuitAdapter for one rank.
No space is reserved here. The ranks may ask for registers of different widths — a
qscatterroot holds one qubit per receiver while each receiver holds one — so a rank’s slice cannot be placed from its rank index and its own width alone; doing that overlapped the slices and silently corrupted the program.lay_out()places them all once every rank has finished tracing.Thread-safe: multiple ranks may call this simultaneously.
- Parameters:
num_qubits (int) – Number of qubits for this rank’s circuit slice.
num_clbits (int) – Number of classical bits for this rank’s circuit slice.
comm (AerCommunicator) – Communicator associated with this rank.
- Returns:
An
AerCircuitAdapterwhose slice is placed later.- Return type:
- lay_out(groups)[source]¶
Build the global circuit and give every rank its slice.
Slices are laid out group by group and, within a group, in rank order, so the bit layout of the resulting histogram is deterministic and independent of the order in which the rank threads happened to reach
create_circuit().- Parameters:
groups (List[List[AerCircuitAdapter]]) – The circuits of each distributed program, rank-ordered within each group.
- Return type:
None
- build_apps(file, size)[source]¶
Build one callable wrapper per rank and install the sync barrier.
Creates all
AerCommunicatorinstances and then installs athreading.Barrieron the class so that every rank’s__exit__can synchronise before the simulation runs.
- run(apps)[source]¶
Launch every rank in a separate thread and wait for all to finish.
Running ranks concurrently is required so that the
threading.BarrierinAerCommunicator.__exit__can synchronise them: all N threads must reach the barrier for any of them to proceed past it.- Parameters:
apps (List[Any]) – List of callables returned by
build_apps().- Raises:
Exception – Whatever the designated thread raised while translating or simulating, re-raised here once every rank has been released.
- Return type:
None
Configuration¶
Backend-specific configuration for Qiskit AerSimulator runs.
- class netqmpi.runtime.adapters.aer.aer_run_config.AerSimulatorConfig[source]¶
Bases:
RunConfigExtension of
RunConfigwith Qiskit AerSimulator-specific fields.- Variables:
shots (int) – Number of simulation shots.
transfer_mode (str) –
Qubit transfer protocol for qsend/qrecv. Only
"swap"exists today;"teleport"is accepted by the dataclass but raisesNotImplementedErrorat translation time."swap"moves the state with a SWAP straight across the global register. That is unphysical — no entanglement is consumed, no classical correction is sent, and nothing can go wrong — which is exactly what makes this backend useful as a correctness reference: a wrong answer here is a bug in the translation, never decoherence. It is not a model of a quantum network, and fidelities measured on it say nothing about one.Implementing
"teleport"is only worth doing together with an Aer noise model. On a noiseless simulator a teleportation circuit returns exactly what the SWAP returns, just with more gates and a pair of ancillas per transfer, so on its own it would add cost without adding information.seed_simulator (int | None) – Optional RNG seed for reproducible simulations.
Circuit adapter¶
Circuit adapter for Qiskit AerSimulator.
Translates SDK operations into Qiskit gates appended directly to a shared global QuantumCircuit owned by the executor. Every local qubit index is shifted by the rank’s offset before being written to the global register.
- class netqmpi.runtime.adapters.aer.aer_circuit.AerCircuitAdapter[source]¶
Bases:
CircuitCircuit adapter that writes operations into a shared global QuantumCircuit.
Each rank owns a contiguous slice
[qubit_offset, qubit_offset + num_qubits)of the global qubit register and the analogous slice of the classical register. All translate methods map local indices to global indices before appending gates.A rank’s slice is not sized or placed until every rank has finished tracing: the ranks may ask for registers of different widths — a
qscatterroot holds one qubit per receiver while the receivers hold one each — so where a slice starts cannot be known from the rank index alone.assign_slice()fills the offsets in once the layout is settled.A communication qubit belongs to no slice at all. It addresses a control another rank has lent through an open
exposewindow, and_global()resolves it to that rank’s data qubit for as long as the window is open.- __init__(num_qubits, num_clbits, comm)[source]¶
Initialize the AerCircuitAdapter.
- Parameters:
num_qubits (int) – Number of qubits for this rank’s circuit slice.
num_clbits (int) – Number of classical bits for this rank’s circuit slice.
comm (AerCommunicator) – Communicator owning this rank.
- Return type:
None
- assign_slice(global_circuit, qubit_offset, clbit_offset)[source]¶
Place this rank’s slice in the global circuit.
Called once every rank has finished tracing, so that the widths each of them asked for are all known and the slices can be laid out without overlapping.
- lend_control(slot, control)[source]¶
Point a communication-qubit slot at the control another rank lent.
- release_control(slot)[source]¶
Close a slot opened by
lend_control().- Parameters:
slot (int) – Communication-qubit slot the window reserved.
- Return type:
None
- emit_transfer(op, source, target)[source]¶
Move one qubit from a sender’s slot to a receiver’s slot.
In
swapmode the move is an unphysical SWAP straight across the global register: no entanglement is consumed and no noise is introduced, which is what makes this backend a correctness reference rather than a model of a network.- Parameters:
- Raises:
NotImplementedError – When
transfer_modeis"teleport".- Return type:
None
- netqmpi.runtime.adapters.aer.aer_circuit.BLOCKING = (<class 'netqmpi.sdk.operations.qmpi.QSend'>, <class 'netqmpi.sdk.operations.qmpi.QRecv'>, <class 'netqmpi.sdk.operations.qmpi.CollectiveOperation'>)¶
they need a partner rank to be sitting on the matching call before anything can be written out.
- Type:
Operations a rank cannot emit on its own
- netqmpi.runtime.adapters.aer.aer_circuit.translate_group(adapters)[source]¶
Translate the circuits of a whole group of ranks into the global circuit.
Aer runs every rank inside a single
QuantumCircuit, so the order in which instructions are appended is the order in which they execute. Translating one rank fully and then the next therefore only works when the program’s cross-rank dependencies happen to follow rank order: a chain 0 -> 1 -> 2 survives it, while a control that returns to rank 0 between hops does not, and the run then produces a wrong answer with no error raised.This pass instead interleaves the ranks the way they would really run. Each rank advances through its own operations until it reaches something it cannot emit alone — a transfer, or a collective — and that call is expanded once every rank it involves is waiting on it, after which they all resume. The result is an emission order that respects every dependency the program expressed.
Pairing a
qsendwith itsqrecvalso supplies what a single-rank pass cannot: the receiver’s own qubit index. The transfer moves the state from the sender’s slot to the slot the receiver asked for, instead of assuming both sides chose the same local index. Anexposelikewise needs the root’s record for the qubit being lent and each receiver’s for the slot it is lent into.- Parameters:
adapters (Dict[int, AerCircuitAdapter]) – Circuit adapter of every rank, keyed by rank.
- Raises:
RuntimeError – If the ranks block on calls that never match — the trace-time equivalent of a deadlock — or if a matched transfer disagrees on how many qubits it moves.
- Return type:
None
Communicator¶
Communicator adapter for Qiskit AerSimulator.
Manages the context lifecycle for a single rank. The global
QuantumCircuit is owned by AerExecutorAdapter; this class
coordinates the barrier synchronization that ensures all ranks have
finished building their circuits before the simulation runs, and that
all ranks receive results before any of them continue past the
with env.comm: block.
- class netqmpi.runtime.adapters.aer.aer_communicator.AerCommunicator[source]¶
Bases:
QMPICommunicatorAerSimulator-backed communicator for a single rank.
All N ranks run concurrently in separate threads.
__exit__uses athreading.Barrierto synchronise them:Every rank finishes building its circuit ops and reaches the barrier.
One designated thread translates all the ranks’ circuits jointly — interleaving them so that transfers pair up and cross-rank dependencies survive — and runs the simulation.
All threads are released with results available and continue past the
with env.comm:block simultaneously.
The barrier and class-level communicator list are reset after the last rank exits so the adapter is reusable within the same process.
- Parameters:
rank – Numeric index of the current rank.
size – Total number of ranks.
config – AerSimulator-specific configuration.
executor – Executor that owns the global QuantumCircuit.
- communicators: List[AerCommunicator] = []¶
- __init__(rank, size, config, executor)[source]¶
Initialize the communicator.
- Parameters:
rank (int) – Numeric index of the current rank.
size (int) – Total number of ranks in the communicator.
config (AerSimulatorConfig) – AerSimulator-specific configuration.
executor (AerExecutorAdapter) – Executor that owns the global QuantumCircuit.
- Return type:
None
Qoala¶
Quantum-internet node execution environment, simulation only. See Qoala backend.
Executor and configuration¶
Executor adapter for the Qoala backend (simulation only).
Qoala is a NetSquid-based simulator of Qoala-spec quantum-internet nodes; there is no real-hardware execution path, so this backend is explicitly simulation-only and must not be treated as on par with a physical deployment.
Like the NetQASM adapter, the whole N-node network runs inside a single Python
process as one NetSquid discrete-event simulation (one ProcNode context per
rank), not as N operating-system processes. Each rank compiles its circuit to a
.iqoala program; when all ranks are ready, run_simulation() builds the
Qoala network, submits one batch (of shots iterations) per node, pairs the
remote PIDs for entanglement, runs the simulation and returns a per-rank
measurement histogram.
This is the only module in the Qoala adapter allowed to import qoala.* /
netsquid. Those imports are performed lazily inside run_simulation()
so that merely selecting the backend (and building apps) does not pull in the
heavy NetSquid runtime until a simulation actually runs.
- class netqmpi.runtime.adapters.qoala.qoala_executor.QoalaQDeviceConfig[source]¶
Bases:
objectHardware parameters of a single node’s quantum device (the Qoala analogue of SquidASM’s
qdevice_cfg). Applied uniformly to every node.Durations are in nanoseconds.
T1 == T2 == 0means “no memory noise” (Qoala’s convention for a perfect qubit); depolarising probabilities of 0 mean noiseless gates. The defaults therefore describe a perfect qdevice.Note (documented limitation):
init_timeandmeasure_timeare exposed independently by building the topology per-instruction. Depolarising noise is applied to the single-/two-qubit gates only;INSTR_INITandINSTR_MEASUREcarry their own duration but no depolarising error, so there is no separate readout-flip model here.- Variables:
t1 (float) – Amplitude-damping time (ns). 0 disables amplitude damping.
t2 (float) – Dephasing time (ns). 0 disables dephasing. Requires
t2 <= 2 * t1when both are non-zero.single_qubit_gate_time (float) – Duration (ns) of single-qubit gates.
two_qubit_gate_time (float) – Duration (ns) of two-qubit gates.
init_time (float) – Duration (ns) of qubit initialization.
measure_time (float) – Duration (ns) of measurement.
single_qubit_gate_depolar_prob (float) – Depolarising probability of single-qubit gates.
two_qubit_gate_depolar_prob (float) – Depolarising probability of two-qubit gates.
- classmethod from_dict(data)[source]¶
Build a config from a plain dict, rejecting unknown keys.
- Parameters:
- Return type:
- __init__(t1=0.0, t2=0.0, single_qubit_gate_time=5000.0, two_qubit_gate_time=200000.0, init_time=5000.0, measure_time=5000.0, single_qubit_gate_depolar_prob=0.0, two_qubit_gate_depolar_prob=0.0)¶
- class netqmpi.runtime.adapters.qoala.qoala_executor.QoalaRunConfig[source]¶
Bases:
RunConfigExtension of
RunConfigwith Qoala/NetSquid simulation parameters.- Variables:
num_qubits_per_node (int | None) – Physical qubits exposed by every node. If
None, it is inferred from the compiled circuits (user qubits plus a teleportation scratch slot).link_duration (float) – EPR-pair generation time (ns) for the links.
qnos_instr_time (float) – Duration (ns) of a single quantum-processor instruction.
hw_config (netqmpi.runtime.adapters.qoala.qoala_executor.QoalaQDeviceConfig | None) – Per-node qdevice hardware parameters. If
None, a perfect qdevice (no memory/gate noise) is used.link_fidelity (float) – Fidelity of the generated EPR pairs to the ideal Bell state, in
[0.25, 1.0].1.0(default) uses perfect links; values below 1.0 use a depolarising link withprob_max_mixed = (4/3)(1 - link_fidelity).seed (int | None) – Optional NetSquid random seed for reproducible runs.
- hw_config: QoalaQDeviceConfig | None = None¶
- classmethod from_dict(data)[source]¶
Build a Qoala run config from a dict, translating the nested
hardwareblock into aQoalaQDeviceConfig.- Parameters:
data (Dict[str, Any]) – Merged config settings for the Qoala backend.
- Returns:
A
QoalaRunConfiginstance.- Return type:
- __init__(shots=1024, num_qubits_per_node=None, link_duration=1000.0, qnos_instr_time=1000.0, hw_config=None, link_fidelity=1.0, seed=None)¶
- class netqmpi.runtime.adapters.qoala.qoala_executor.QoalaExecutorAdapter[source]¶
Bases:
ExecutorExecutor adapter for the Qoala simulator.
Handles circuit creation and per-rank application construction, and owns the shared simulation driver invoked once all ranks have compiled their program.
- __init__(size, config=None)[source]¶
Initialize the Qoala executor adapter.
- Parameters:
size (int) – Number of parallel quantum nodes to simulate.
config (QoalaRunConfig) – Qoala-specific configuration. Defaults to
QoalaRunConfigwith its built-in defaults.
- Return type:
None
- create_circuit(num_qubits, num_clbits, comm)[source]¶
Create a Qoala circuit adapter.
- Parameters:
num_qubits (int) – Number of qubits in the circuit.
num_clbits (int) – Number of classical bits in the circuit.
comm (QoalaCommunicator) – Communicator associated with the circuit.
- Returns:
A
QoalaCircuitAdapterinstance.- Return type:
- run(apps)[source]¶
Run every rank’s
main. The joint simulation is triggered by the last rank leaving itswith commblock (seerun_simulation()).- Parameters:
apps (List[Any]) – Callables returned by
build_apps().- Return type:
None
- run_simulation(registry)[source]¶
Build the Qoala network and run one simulation for all ranks.
- Parameters:
registry (Dict[int, Tuple[QoalaProgramSpec, QoalaCommunicator]]) – Mapping
rank -> (program spec, communicator)gathered as each rank left itswith commblock.- Returns:
Mapping
rank -> {bitstring: count}with the measurement histogram for each rank (empty for ranks that measure nothing).- Return type:
Circuit adapter¶
Circuit adapter for the Qoala backend (simulation only).
Qoala programs are structured very differently from an eager gate stream:
a program is a list of host-code basic blocks (typed CL/CC/QL/QC)
that invoke local routines (NetQASM subroutines) and request routines
(EPR generation). This adapter therefore acts as a small compiler that walks
the flat NetQMPI OperationContainer and emits
the textual .iqoala representation of one program per rank.
The generated text is parsed into a QoalaProgram by
qoala_executor (the only module that
imports qoala.*). Keeping this file free of qoala imports means the
whole .iqoala generation is pure-Python and independently testable, and it
preserves the lazy-import contract used by the other backends.
Compilation rules (see docs/design/qoala-backend.md for the full mapping):
Consecutive local gates / measurements are accumulated into a single local routine, flushed as a
QLblock whenever a communication boundary (qsend/qrecv) is reached or at the end of the circuit.qsendbecomes the teleportation sender triad: aQCEPR-create request, aQLBell-state-measurement routine, and aCLblock sending the two correction bits.qrecvbecomes the teleportation receiver triad: aQCEPR-receive request, twoCCblocks receiving the correction bits, and aQLblock applying the Pauli corrections. The received qubit lands directly in the target virtual-qubit slot.
Scope (v1): local gates, measure, qsend and qrecv. Every other
inter-rank primitive raises NotImplementedError, mirroring the CUNQA
adapter.
- class netqmpi.runtime.adapters.qoala.qoala_circuit.QoalaProgramSpec[source]¶
Bases:
objectBackend-neutral description of the
.iqoalaprogram built for one rank.- Variables:
iqoala_text (str) – Full serialized
.iqoalaprogram, ready to be parsed byQoalaParser.program_input (dict) – Mapping of program parameter names to values (remote node ids), consumed as a
ProgramInputby the executor.num_qubits (int) – Minimum number of physical qubits the node must expose for this program (user qubits plus one teleportation scratch slot).
outputs (List[Tuple[int, str]]) – Ordered
(clbit_index, host_var_name)pairs describing which host variables are returned viareturn_result, used to rebuild a measurement bitstring.
- class netqmpi.runtime.adapters.qoala.qoala_circuit.QoalaCircuitAdapter[source]¶
Bases:
CircuitCompiles a NetQMPI circuit into a textual Qoala program (simulation only).
The adapter does not execute anything: it records operations through the base
Circuitfluent API and, onbuild_program(), walks them to emit the.iqoalatext for this rank.- __init__(num_qubits, num_clbits, comm)[source]¶
Initialize the Qoala circuit adapter.
- Parameters:
num_qubits (int) – Number of user qubits in the circuit.
num_clbits (int) – Number of classical bits in the circuit.
comm (QoalaCommunicator) – Communicator providing rank/size and rank naming helpers.
- Return type:
None
- build_program()[source]¶
Compile the recorded operations into a
QoalaProgramSpec.- Returns:
The
.iqoalatext plus the metadata needed to run it and to reconstruct measurement results.- Return type:
Communicator¶
Concrete QMPICommunicator for the Qoala
backend (simulation only).
Following the NetQASM adapter’s model, all ranks run in the same process and the
joint NetSquid simulation is deferred until every rank has left its with comm
block. Each rank compiles its circuit to a .iqoala program on __exit__
and registers it; when the last rank registers, the executor builds the Qoala
network and runs one simulation for all ranks at once.
This module imports no qoala package: program text is produced by
QoalaCircuitAdapter and
the simulation is driven by
QoalaExecutorAdapter.
- class netqmpi.runtime.adapters.qoala.qoala_communicator.QoalaCommunicator[source]¶
Bases:
QMPICommunicatorQoala-backed communicator for a single rank.
The communicator only orchestrates: it does not execute quantum operations (the circuit is deferred and compiled to a Qoala program). It collects one program per rank and, once all
sizeranks are ready, asks the executor to run the joint simulation and stores the per-rank measurement histogram inresults.- __init__(rank, size, config, executor)[source]¶
Initialize the Qoala communicator.
- Parameters:
rank (int) – Numeric index of the current rank.
size (int) – Total number of ranks in the communicator.
config (Any) – Backend configuration (
QoalaRunConfig).executor (QoalaExecutorAdapter) – Executor that owns the shared simulation driver.
- Return type:
None