Backends

The qtest.backends subpackage abstracts circuit execution behind a single Backend protocol. Qiskit (default), Cirq, and PennyLane implementations all ship in the box and are auto-registered under the names "qiskit", "cirq", and "pennylane". Select one per call (backend=CirqBackend()) or globally (set_default_backend() / --qtest-backend).

Each backend operates on its SDK’s native circuit type — a qiskit.QuantumCircuit, a cirq.Circuit, or a pennylane.tape.QuantumScript respectively — and measurement-bitstring endianness follows that SDK’s convention (Qiskit is little-endian; Cirq and PennyLane put the lowest-index qubit leftmost). qtest’s NoiseModel targets Qiskit Aer, so the Cirq and PennyLane backends raise NotImplementedError if a noise_model is supplied.

Install the optional SDKs with the matching extra:

pip install 'qtest-quantum[cirq]'        # cirq-core
pip install 'qtest-quantum[pennylane]'   # pennylane

Public API

Backend

Abstract base class for quantum circuit execution backends.

CircuitResources

Structural / resource metrics of a circuit, free of any execution.

QiskitBackend

Qiskit simulator backend.

CirqBackend

Cirq state-vector simulator backend.

PennyLaneBackend

PennyLane default.qubit simulator backend.

get_backend

Instantiate and return a backend.

get_default_backend

Return a fresh instance of the current default backend.

set_default_backend

Set name as the default backend.

register_backend

Register backend_class under name.

list_available_backends

Return a sorted list of registered backend names.

Reference

Backend abstraction layer for qtest.

Public API:

Importing this package does not import any quantum SDK; each SDK import happens lazily, the first time that backend’s execution method is called.

class qtest.backends.Backend[source]

Bases: ABC

Abstract base class for quantum circuit execution backends.

abstractmethod run_circuit(circuit, shots=None, seed=None, noise_model=None)[source]

Run circuit and return measurement counts.

Parameters:
  • circuit (Any) – A circuit object native to the backend (e.g. QuantumCircuit).

  • shots (int | None) – Number of shots. If None, the backend’s configured default is used.

  • seed (int | None) – Optional seed for reproducible sampling.

  • noise_model (Any | None) – Optional qtest.noise.NoiseModel describing the noise to apply during simulation. None (the default) runs an ideal, noiseless simulation. Backends that cannot simulate noise should raise NotImplementedError when a non-None model is passed.

Returns:

Mapping {bitstring: count} whose values sum to shots.

Return type:

dict[str, int]

abstractmethod get_statevector(circuit)[source]

Return the state vector produced by circuit (no measurement).

The returned array has dtype=complex and length 2**n_qubits.

Parameters:

circuit (Any)

Return type:

ndarray

abstractmethod get_unitary(circuit)[source]

Return the unitary matrix of circuit.

The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits). Raises if circuit contains non-unitary instructions (measurement, reset, …).

Parameters:

circuit (Any)

Return type:

ndarray

get_density_matrix(circuit, noise_model=None)[source]

Return the density matrix produced by circuit.

With noise_model=None this is the pure-state projector \(|\psi\rangle\langle\psi|\); with a noise model it is the mixed state resulting from noisy evolution. The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits).

The default implementation raises NotImplementedError; backends capable of density-matrix simulation override it.

Parameters:
  • circuit (Any)

  • noise_model (Any | None)

Return type:

ndarray

get_resources(circuit)[source]

Return structural resource metrics for circuit.

This is a static analysis of the circuit — no simulation is run, so it works on circuits of any width. The default implementation raises NotImplementedError; backends able to introspect their native circuit type override it.

Parameters:

circuit (Any)

Return type:

CircuitResources

abstract property name: str

Human-readable backend identifier.

abstract property supports_statevector: bool

Whether get_statevector() / get_unitary() are supported.

class qtest.backends.CirqBackend(shots=1024)[source]

Bases: Backend

Cirq state-vector simulator backend.

Parameters:

shots (int) – Default shot count for run_circuit().

property name: str

Human-readable backend identifier.

property supports_statevector: bool

Whether get_statevector() / get_unitary() are supported.

run_circuit(circuit, shots=None, seed=None, noise_model=None)[source]

Run circuit and return measurement counts.

Parameters:
  • circuit (Any) – A circuit object native to the backend (e.g. QuantumCircuit).

  • shots (int | None) – Number of shots. If None, the backend’s configured default is used.

  • seed (int | None) – Optional seed for reproducible sampling.

  • noise_model (Any | None) – Optional qtest.noise.NoiseModel describing the noise to apply during simulation. None (the default) runs an ideal, noiseless simulation. Backends that cannot simulate noise should raise NotImplementedError when a non-None model is passed.

Returns:

Mapping {bitstring: count} whose values sum to shots.

Return type:

dict[str, int]

get_statevector(circuit)[source]

Return the state vector produced by circuit (no measurement).

The returned array has dtype=complex and length 2**n_qubits.

Parameters:

circuit (Any)

Return type:

ndarray

get_unitary(circuit)[source]

Return the unitary matrix of circuit.

The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits). Raises if circuit contains non-unitary instructions (measurement, reset, …).

Parameters:

circuit (Any)

Return type:

ndarray

get_resources(circuit)[source]

Return structural resource metrics for circuit.

This is a static analysis of the circuit — no simulation is run, so it works on circuits of any width. The default implementation raises NotImplementedError; backends able to introspect their native circuit type override it.

Parameters:

circuit (Any)

Return type:

CircuitResources

class qtest.backends.PennyLaneBackend(shots=1024)[source]

Bases: Backend

PennyLane default.qubit simulator backend.

Parameters:

shots (int) – Default shot count for run_circuit().

property name: str

Human-readable backend identifier.

property supports_statevector: bool

Whether get_statevector() / get_unitary() are supported.

run_circuit(circuit, shots=None, seed=None, noise_model=None)[source]

Run circuit and return measurement counts.

Parameters:
  • circuit (Any) – A circuit object native to the backend (e.g. QuantumCircuit).

  • shots (int | None) – Number of shots. If None, the backend’s configured default is used.

  • seed (int | None) – Optional seed for reproducible sampling.

  • noise_model (Any | None) – Optional qtest.noise.NoiseModel describing the noise to apply during simulation. None (the default) runs an ideal, noiseless simulation. Backends that cannot simulate noise should raise NotImplementedError when a non-None model is passed.

Returns:

Mapping {bitstring: count} whose values sum to shots.

Return type:

dict[str, int]

get_statevector(circuit)[source]

Return the state vector produced by circuit (no measurement).

The returned array has dtype=complex and length 2**n_qubits.

Parameters:

circuit (Any)

Return type:

ndarray

get_unitary(circuit)[source]

Return the unitary matrix of circuit.

The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits). Raises if circuit contains non-unitary instructions (measurement, reset, …).

Parameters:

circuit (Any)

Return type:

ndarray

class qtest.backends.QiskitBackend(simulator_name='aer_simulator', shots=1024, optimization_level=1)[source]

Bases: Backend

Qiskit simulator backend.

Parameters:
  • simulator_name (str) – Cosmetic identifier reported via name. The actual simulator chosen at run time is qiskit_aer.AerSimulator if available, else qiskit.providers.basic_provider.BasicSimulator.

  • shots (int) – Default shot count used when run_circuit() is called without an explicit shots argument.

  • optimization_level (int) – Default transpile optimisation level (one of 0, 1, 2, 3).

Notes

The constructor arguments are defaults; per-call arguments to run_circuit() override them. The backend keeps no other state between calls — two calls with identical arguments produce identical output (assuming the same seed).

property name: str

Human-readable backend identifier.

property supports_statevector: bool

Whether get_statevector() / get_unitary() are supported.

run_circuit(circuit, shots=None, seed=None, noise_model=None)[source]

Run circuit and return measurement counts.

Parameters:
  • circuit (Any) – A circuit object native to the backend (e.g. QuantumCircuit).

  • shots (int | None) – Number of shots. If None, the backend’s configured default is used.

  • seed (int | None) – Optional seed for reproducible sampling.

  • noise_model (Any | None) – Optional qtest.noise.NoiseModel describing the noise to apply during simulation. None (the default) runs an ideal, noiseless simulation. Backends that cannot simulate noise should raise NotImplementedError when a non-None model is passed.

Returns:

Mapping {bitstring: count} whose values sum to shots.

Return type:

dict[str, int]

get_statevector(circuit)[source]

Return the state vector produced by circuit (no measurement).

The returned array has dtype=complex and length 2**n_qubits.

Parameters:

circuit (Any)

Return type:

ndarray

get_density_matrix(circuit, noise_model=None)[source]

Return the density matrix produced by circuit.

With noise_model=None this is the pure-state projector \(|\psi\rangle\langle\psi|\); with a noise model it is the mixed state resulting from noisy evolution. The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits).

The default implementation raises NotImplementedError; backends capable of density-matrix simulation override it.

Parameters:
  • circuit (Any)

  • noise_model (Any | None)

Return type:

ndarray

get_unitary(circuit)[source]

Return the unitary matrix of circuit.

The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits). Raises if circuit contains non-unitary instructions (measurement, reset, …).

Parameters:

circuit (Any)

Return type:

ndarray

get_resources(circuit)[source]

Return structural resource metrics for circuit.

This is a static analysis of the circuit — no simulation is run, so it works on circuits of any width. The default implementation raises NotImplementedError; backends able to introspect their native circuit type override it.

Parameters:

circuit (Any)

Return type:

CircuitResources

qtest.backends.get_backend(name=None)[source]

Instantiate and return a backend.

If name is None, the current default backend is used. Raises KeyError if name is not registered, or RuntimeError if no default is set.

Parameters:

name (str | None)

Return type:

Backend

qtest.backends.get_default_backend()[source]

Return a fresh instance of the current default backend.

Return type:

Backend

qtest.backends.list_available_backends()[source]

Return a sorted list of registered backend names.

Return type:

list[str]

qtest.backends.register_backend(name, backend_class)[source]

Register backend_class under name.

Subsequent registrations under the same name overwrite the previous entry.

Parameters:
Return type:

None

qtest.backends.set_default_backend(name)[source]

Set name as the default backend. Must already be registered.

Parameters:

name (str)

Return type:

None

Backend protocol

Abstract backend interface for qtest.

A Backend is a stateless adapter that translates qtest’s public API into a specific quantum SDK’s primitives (Qiskit today; Cirq / PennyLane later). It exposes three core operations — running a circuit with measurements, extracting the post-circuit state vector, and extracting the circuit’s unitary — plus a couple of metadata properties.

Concrete backends MUST be free of cross-call mutable state: a given configuration must produce the same output for the same input on every call. Constructor arguments are immutable defaults; per-call overrides (e.g. shots) are supported where applicable.

class qtest.backends.base.CircuitResources(num_qubits, depth, size, gate_counts=<factory>, two_qubit_count=0, multi_qubit_count=0)[source]

Bases: object

Structural / resource metrics of a circuit, free of any execution.

These describe how expensive a circuit is to run on hardware — the quantities transpilers and optimisers try to minimise — and are the basis for qtest.assertions.resources.

Parameters:
num_qubits: int

Number of qubits the circuit acts on.

depth: int

Circuit depth (length of the critical path of gates).

size: int

Total number of operations (gate instances).

gate_counts: dict[str, int]

Mapping {gate_name: count}; gate names are backend-specific.

two_qubit_count: int = 0

Number of gates acting on exactly two qubits.

multi_qubit_count: int = 0

Number of gates acting on two or more qubits.

property t_count: int

Number of T / T-dagger gates (a key fault-tolerant cost metric).

class qtest.backends.base.Backend[source]

Bases: ABC

Abstract base class for quantum circuit execution backends.

abstractmethod run_circuit(circuit, shots=None, seed=None, noise_model=None)[source]

Run circuit and return measurement counts.

Parameters:
  • circuit (Any) – A circuit object native to the backend (e.g. QuantumCircuit).

  • shots (int | None) – Number of shots. If None, the backend’s configured default is used.

  • seed (int | None) – Optional seed for reproducible sampling.

  • noise_model (Any | None) – Optional qtest.noise.NoiseModel describing the noise to apply during simulation. None (the default) runs an ideal, noiseless simulation. Backends that cannot simulate noise should raise NotImplementedError when a non-None model is passed.

Returns:

Mapping {bitstring: count} whose values sum to shots.

Return type:

dict[str, int]

abstractmethod get_statevector(circuit)[source]

Return the state vector produced by circuit (no measurement).

The returned array has dtype=complex and length 2**n_qubits.

Parameters:

circuit (Any)

Return type:

ndarray

abstractmethod get_unitary(circuit)[source]

Return the unitary matrix of circuit.

The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits). Raises if circuit contains non-unitary instructions (measurement, reset, …).

Parameters:

circuit (Any)

Return type:

ndarray

get_density_matrix(circuit, noise_model=None)[source]

Return the density matrix produced by circuit.

With noise_model=None this is the pure-state projector \(|\psi\rangle\langle\psi|\); with a noise model it is the mixed state resulting from noisy evolution. The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits).

The default implementation raises NotImplementedError; backends capable of density-matrix simulation override it.

Parameters:
  • circuit (Any)

  • noise_model (Any | None)

Return type:

ndarray

get_resources(circuit)[source]

Return structural resource metrics for circuit.

This is a static analysis of the circuit — no simulation is run, so it works on circuits of any width. The default implementation raises NotImplementedError; backends able to introspect their native circuit type override it.

Parameters:

circuit (Any)

Return type:

CircuitResources

abstract property name: str

Human-readable backend identifier.

abstract property supports_statevector: bool

Whether get_statevector() / get_unitary() are supported.

Registry

Module-level registry of available backends.

The registry stores a mapping {name: Backend subclass} and tracks which name is the current default. The Qiskit backend is auto-registered on import and set as the default.

Registration stores the class (a zero-argument-callable factory) so that each call to get_backend() returns a fresh, stateless instance — honouring the no-cross-call-state contract documented in qtest.backends.base.Backend.

qtest.backends.registry.register_backend(name, backend_class)[source]

Register backend_class under name.

Subsequent registrations under the same name overwrite the previous entry.

Parameters:
Return type:

None

qtest.backends.registry.get_backend(name=None)[source]

Instantiate and return a backend.

If name is None, the current default backend is used. Raises KeyError if name is not registered, or RuntimeError if no default is set.

Parameters:

name (str | None)

Return type:

Backend

qtest.backends.registry.get_default_backend()[source]

Return a fresh instance of the current default backend.

Return type:

Backend

qtest.backends.registry.set_default_backend(name)[source]

Set name as the default backend. Must already be registered.

Parameters:

name (str)

Return type:

None

qtest.backends.registry.list_available_backends()[source]

Return a sorted list of registered backend names.

Return type:

list[str]

Qiskit backend

Qiskit-based simulator backend for qtest.

This module is import-safe even when Qiskit is not installed: all import qiskit ... calls are deferred to method bodies. Constructing QiskitBackend does not require Qiskit; calling any of its execution methods does.

Qiskit 1.0+ removed qiskit.execute and qiskit.BasicAer; we use qiskit.transpile + backend.run(...) for sampling, and qiskit.quantum_info.{Statevector, Operator} for state / unitary extraction (no Aer required for those).

class qtest.backends.qiskit_backend.QiskitBackend(simulator_name='aer_simulator', shots=1024, optimization_level=1)[source]

Bases: Backend

Qiskit simulator backend.

Parameters:
  • simulator_name (str) – Cosmetic identifier reported via name. The actual simulator chosen at run time is qiskit_aer.AerSimulator if available, else qiskit.providers.basic_provider.BasicSimulator.

  • shots (int) – Default shot count used when run_circuit() is called without an explicit shots argument.

  • optimization_level (int) – Default transpile optimisation level (one of 0, 1, 2, 3).

Notes

The constructor arguments are defaults; per-call arguments to run_circuit() override them. The backend keeps no other state between calls — two calls with identical arguments produce identical output (assuming the same seed).

property name: str

Human-readable backend identifier.

property supports_statevector: bool

Whether get_statevector() / get_unitary() are supported.

run_circuit(circuit, shots=None, seed=None, noise_model=None)[source]

Run circuit and return measurement counts.

Parameters:
  • circuit (Any) – A circuit object native to the backend (e.g. QuantumCircuit).

  • shots (int | None) – Number of shots. If None, the backend’s configured default is used.

  • seed (int | None) – Optional seed for reproducible sampling.

  • noise_model (Any | None) – Optional qtest.noise.NoiseModel describing the noise to apply during simulation. None (the default) runs an ideal, noiseless simulation. Backends that cannot simulate noise should raise NotImplementedError when a non-None model is passed.

Returns:

Mapping {bitstring: count} whose values sum to shots.

Return type:

dict[str, int]

get_statevector(circuit)[source]

Return the state vector produced by circuit (no measurement).

The returned array has dtype=complex and length 2**n_qubits.

Parameters:

circuit (Any)

Return type:

ndarray

get_density_matrix(circuit, noise_model=None)[source]

Return the density matrix produced by circuit.

With noise_model=None this is the pure-state projector \(|\psi\rangle\langle\psi|\); with a noise model it is the mixed state resulting from noisy evolution. The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits).

The default implementation raises NotImplementedError; backends capable of density-matrix simulation override it.

Parameters:
  • circuit (Any)

  • noise_model (Any | None)

Return type:

ndarray

get_unitary(circuit)[source]

Return the unitary matrix of circuit.

The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits). Raises if circuit contains non-unitary instructions (measurement, reset, …).

Parameters:

circuit (Any)

Return type:

ndarray

get_resources(circuit)[source]

Return structural resource metrics for circuit.

This is a static analysis of the circuit — no simulation is run, so it works on circuits of any width. The default implementation raises NotImplementedError; backends able to introspect their native circuit type override it.

Parameters:

circuit (Any)

Return type:

CircuitResources

Cirq backend

Cirq-based simulator backend for qtest.

Import-safe without Cirq: every import cirq is deferred to a method body, so constructing CirqBackend (and importing this module) never requires Cirq. Calling an execution method does.

Native circuit type

This backend operates on cirq.Circuit objects over cirq.LineQubit (or any sortable qubit). Measurement bitstrings are ordered with the lowest-index qubit leftmost (Cirq’s big-endian convention), which is the opposite of Qiskit’s. Keep your expected dicts consistent with whichever backend you select.

Noise

qtest’s NoiseModel is built on Qiskit Aer, so passing a noise_model to this backend raises NotImplementedError. Noiseless simulation is fully supported.

class qtest.backends.cirq_backend.CirqBackend(shots=1024)[source]

Bases: Backend

Cirq state-vector simulator backend.

Parameters:

shots (int) – Default shot count for run_circuit().

property name: str

Human-readable backend identifier.

property supports_statevector: bool

Whether get_statevector() / get_unitary() are supported.

run_circuit(circuit, shots=None, seed=None, noise_model=None)[source]

Run circuit and return measurement counts.

Parameters:
  • circuit (Any) – A circuit object native to the backend (e.g. QuantumCircuit).

  • shots (int | None) – Number of shots. If None, the backend’s configured default is used.

  • seed (int | None) – Optional seed for reproducible sampling.

  • noise_model (Any | None) – Optional qtest.noise.NoiseModel describing the noise to apply during simulation. None (the default) runs an ideal, noiseless simulation. Backends that cannot simulate noise should raise NotImplementedError when a non-None model is passed.

Returns:

Mapping {bitstring: count} whose values sum to shots.

Return type:

dict[str, int]

get_statevector(circuit)[source]

Return the state vector produced by circuit (no measurement).

The returned array has dtype=complex and length 2**n_qubits.

Parameters:

circuit (Any)

Return type:

ndarray

get_unitary(circuit)[source]

Return the unitary matrix of circuit.

The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits). Raises if circuit contains non-unitary instructions (measurement, reset, …).

Parameters:

circuit (Any)

Return type:

ndarray

get_resources(circuit)[source]

Return structural resource metrics for circuit.

This is a static analysis of the circuit — no simulation is run, so it works on circuits of any width. The default implementation raises NotImplementedError; backends able to introspect their native circuit type override it.

Parameters:

circuit (Any)

Return type:

CircuitResources

PennyLane backend

PennyLane-based simulator backend for qtest.

Import-safe without PennyLane: every import pennylane is deferred to a method body, so constructing PennyLaneBackend (and importing this module) never requires PennyLane.

Native circuit type

Unlike Qiskit/Cirq, PennyLane has no free-standing “circuit” object — programs are expressed as quantum functions bound to a device. This backend therefore operates on a pennylane.tape.QuantumScript (a.k.a. QuantumTape), which is the closest device-independent container of operations. Build one as:

import pennylane as qml
tape = qml.tape.QuantumScript([qml.Hadamard(0), qml.CNOT([0, 1])])

Measurement bitstrings are ordered with the lowest-index wire leftmost. Existing measurements on the tape are ignored; qtest supplies its own.

Noise

qtest’s NoiseModel targets Qiskit Aer, so passing a noise_model here raises NotImplementedError.

class qtest.backends.pennylane_backend.PennyLaneBackend(shots=1024)[source]

Bases: Backend

PennyLane default.qubit simulator backend.

Parameters:

shots (int) – Default shot count for run_circuit().

property name: str

Human-readable backend identifier.

property supports_statevector: bool

Whether get_statevector() / get_unitary() are supported.

run_circuit(circuit, shots=None, seed=None, noise_model=None)[source]

Run circuit and return measurement counts.

Parameters:
  • circuit (Any) – A circuit object native to the backend (e.g. QuantumCircuit).

  • shots (int | None) – Number of shots. If None, the backend’s configured default is used.

  • seed (int | None) – Optional seed for reproducible sampling.

  • noise_model (Any | None) – Optional qtest.noise.NoiseModel describing the noise to apply during simulation. None (the default) runs an ideal, noiseless simulation. Backends that cannot simulate noise should raise NotImplementedError when a non-None model is passed.

Returns:

Mapping {bitstring: count} whose values sum to shots.

Return type:

dict[str, int]

get_statevector(circuit)[source]

Return the state vector produced by circuit (no measurement).

The returned array has dtype=complex and length 2**n_qubits.

Parameters:

circuit (Any)

Return type:

ndarray

get_unitary(circuit)[source]

Return the unitary matrix of circuit.

The returned array has dtype=complex and shape (2**n_qubits, 2**n_qubits). Raises if circuit contains non-unitary instructions (measurement, reset, …).

Parameters:

circuit (Any)

Return type:

ndarray