Qiskit Backend#
Context and Purpose#
This adapter exposes Qiskit’s qiskit.primitives.Sampler through the
QuantumBackend interface. It builds (or accepts)
a parameterized circuit that encodes the last input vector x with per-qubit
RY rotations and returns one signed average per measured wire (expectation-like
value derived from bitstring counts).
Conceptual Foundations#
Shape stability (output–wire alignment): The constructor enforces
num_qubits == output_dimand raisesValueErrorotherwise, ensuring each output index corresponds to a wire.Minimal default ansatz: The fallback circuit applies one
RY(angle)per wire and a finalbarrier, then callsmeasure_all()to ensure classical bits are present for Sampler validation. Measurement can be implicit in some flows, but explicitmeasure_all()keeps versions compatible.Signed averages from counts: The execution aggregates bitstrings into a per-wire signed mean (bit
'1'→ +1, bit'0'→ −1). Because Qiskit bitstrings are big-endian, the code reverses the bitstring to map to wire indices0..N-1.Bounded future projections:
project_future()generatesKbranches by applying smooth additive perturbations in a linear range and squashing withtanhfor numeric stability.
Integration Guidelines#
Use this backend when you need shot-based sampling with a uniform interface across simulators and (optionally) providers that expose the Sampler API.
Provide a custom
circuit_builder(x) -> QuantumCircuitto introduce entanglement or domain-specific structure; the default circuit has no entangling gates.Keep
output_dimandnum_qubitsequal to preserve shape invariants.Set a fixed random seed and an ideal simulator if you need repeatable sampling.
Installation#
Qiskit is included as an optional dependency of qml-hcs.
However, it is recommended to install it manually if you wish to test
a specific version or ensure compatibility with your preferred Qiskit release.
Choose the appropriate installation depending on your environment:
Qiskit 1.x (legacy Sampler API):
pip install "qiskit>=1.2,<2.0"
Qiskit 2.x (modern StatevectorSampler API):
pip install "qiskit>=2.0"
The backend automatically detects the available version of Qiskit and imports the correct primitive:
For Qiskit 1.x →
SamplerFor Qiskit 2.x →
StatevectorSampler(used as a drop-in replacement)
Ensure Python ≥ 3.9 and that your environment includes qiskit.primitives.
Version Compatibility#
Qiskit ≤1.x: uses the classic
qiskit.primitives.Sampler. Results are typically exposed asquasi_dists(probabilities).Qiskit ≥2.x: the adapter falls back to
qiskit.primitives.StatevectorSampler. Circuits are passed as a list ([qc]). The default circuit addsmeasure_all()to provide classical bits required by stricter validators. Results are read fromquasi_distswhen present or fromdata.meas['counts']otherwise.
Note
This backend is not strictly deterministic. Results depend on stochastic
sampling via Sampler. Determinism can only be
approximated with an ideal simulator, fixed seeds, and sufficiently large
shot counts. When config.shots is not provided, execution defaults to
1024 shots.
Performance & Scaling Notes#
Circuit depth: linear in qubit count (one
RYper wire + barrier).Sampling cost: dominated by the number of shots; empirical variance decreases roughly as \(O(1/\sqrt{N_{shots}})\).
Memory footprint: compact, since post-processing stores wire-wise aggregates, not full distributions.
Extension Pathways#
Custom ansatz: pass
circuit_builderto modify the circuit while keeping the backend contract unchanged.Sampler/backend choice: swap the underlying Sampler backend depending on your environment (e.g., Aer simulators, hardware providers that support Sampler).
Capability reporting: override
capabilities()to expose provider- specific features (the default advertises name/version, qubit count, shots/noise flags, batch support, andPARAMETER_SHIFT).
Interoperability & Testing#
Endianness handling: the implementation reverses big-endian bitstrings when aggregating counts, preserving the mapping between bit position and wire index.
No extra branch validation:
project_future()validates the input state and returns stacked branches directly; downstream checks are expected at higher layers.
Design Guarantees#
Enforced output–wire alignment (constructor validation).
Minimal, auditable default circuit (
RYper wire + barrier).Expectation-like signed averages computed from counts with explicit endianness handling.
Bounded multi-branch projections via smooth perturbations and
tanhsquashing.
API Reference overview#
Qiskit Backend Adapter#
Adapter for Qiskit-based quantum execution backends that conforms to the
QuantumBackend contract.
This wrapper uses Qiskit’s Sampler primitive to execute a (possibly
custom) parameterized circuit that encodes the last input x via RY
rotations. It returns an expectation-like vector per wire derived from
measured bitstring counts.
Example
>>> import numpy as np
>>> from qmlhc.core.backend import BackendConfig
>>> from qmlhc.backends.qiskit_backend import QiskitBackend
>>> cfg = BackendConfig(output_dim=3, shots=1024)
>>> be = QiskitBackend(cfg, num_qubits=3)
>>> be.encode(np.array([0.1, 0.2, 0.3], dtype=float))
>>> s_t = be.run()
>>> fut = be.project_future(s_t, branches=5)
>>> s_t.shape
(3,)
>>> fut.shape
(5, 3)
Note
This example demonstrates the standard Qiskit-based workflow within the unified
QuantumBackend API: initialize the backend, encode a numeric state, execute
the sampling run, and obtain future projections.
Because execution relies on the Sampler, the numerical
outputs are stochastic. While the individual expectation-like values in s_t
and fut vary across runs, their dimensional structure and bounded range
(within [-1, 1]) remain invariant. This behavior reflects the physical sampling
nature of quantum backends rather than a computational instability.
- class qmlhc.backends.qiskit_backend.QiskitBackend(config, num_qubits, circuit_builder=None)[source]#
Bases:
QuantumBackendWrap Qiskit primitives into the
QuantumBackendAPI.- Parameters:
config (BackendConfig) – Backend configuration (e.g.,
output_dim,shots).num_qubits (int) – Number of qubits / wires. Must equal
config.output_dim.circuit_builder (Callable[[np.ndarray], QuantumCircuit], optional) – Custom function that builds a circuit from the encoded input vector
x. If not provided, a default RY + barrier circuit is used.
- Raises:
ValueError – If
output_dimdoes not matchnum_qubits.
Notes
Execution is always shot-based; there is no analytic (deterministic) mode.
If
config.shotsis not provided, defaults to 1024.The default circuit applies one
RYper qubit and abarrier(no entanglement).Qiskit bitstrings are big-endian and are reversed when mapping bits → wires.
- capabilities()[source]#
Report merged capabilities (base + Qiskit-specific).
- Returns:
Capability dictionary including backend name/version, qubit count, shot/noise support, batching, and gradient method.
- Return type:
Capabilities
- project_future(s_t, branches=2)[source]#
Generate future state projections by applying smooth additive perturbations.
- Parameters:
s_t (np.ndarray) – Current state vector.
branches (int, optional) – Number of projected branches (K), by default 2.
- Returns:
Matrix of shape
(K, D)withtanh-stabilized perturbations.- Return type:
Array
- run(params=None)[source]#
Execute the circuit via Qiskit’s Sampler and return an expectation-like vector.
Notes
encode(x)must be called beforerun(); this is enforced by_require_input().Uses
shots = config.shots or 1024when running the sampler.Computes signed per-wire averages (‘1’→+1, ‘0’→−1) after endian correction.
Note
Wraps the circuit as
[qc]for Qiskit 2.x compatibility (accepted by 1.x as well).Reads results from
quasi_distswhen available, otherwise fromdata.meas['counts'].
- Parameters:
params (dict or None, optional) – Unused in this minimal adapter; reserved for future extensions.
- Returns:
Vector of shape
(D,)containing per-wire signed averages.- Return type:
Array
External and Internal Dependencies#
Third-Party Libraries
qiskit
Provides qiskit.QuantumCircuit and qiskit.primitives.Sampler
used to build/execute circuits and retrieve sampled results.
Documentation: https://qiskit.org/documentation/
Repository: Qiskit/qiskit
numpy
Used for vector operations and utilities (e.g., linspace, stack, tanh,
and array handling).
Documentation: https://numpy.org/doc/stable/
Repository: numpy/numpy
Python Standard Library
__future__ – forward references for annotations.
typing – typing hints for API clarity.
QML-HCS Internal Modules
qmlhc.core.backend
Defines qmlhc.core.backend.QuantumBackend and
qmlhc.core.backend.BackendConfig, which this adapter implements/extends.
qmlhc.core.types
Provides qmlhc.core.types.Array, qmlhc.core.types.Capabilities,
and qmlhc.core.types.GradientKind used in method signatures, returns,
and capability metadata.