SPDX-License-Identifier: AGPL-3.0-or-later¶
Commercial license available¶
© Concepts 1996–2026 Miroslav Šotek. All rights reserved.¶
© Code 2020–2026 Miroslav Šotek. All rights reserved.¶
ORCID: 0009-0009-3560-0851¶
Contact: www.anulum.li | protoscience@anulum.li¶
scpn-quantum-control — Hardware Execution Guide¶
Hardware Execution Guide¶
The hardware package provides the full stack from circuit compilation
to QPU execution, noise modelling, classical reference computation, and
multi-backend support. 16 modules covering IBM superconducting, trapped
ion, PennyLane, Cirq, GPU acceleration, and circuit cutting.
Architecture¶
Experiment Definition (experiments.py)
│
├── HardwareRunner (runner.py)
│ ├── connect() → IBM Runtime / AerSimulator
│ ├── transpile() → native gate set
│ ├── run_circuit() → JobResult
│ └── run_with_zne() → ZNE-mitigated result
│
├── Noise Model (noise_model.py)
│ └── heron_r2_noise_model() → NoiseModel (thermal + depolarizing)
│
├── Classical Reference (classical.py)
│ ├── classical_kuramoto_reference() → Euler integration
│ ├── classical_exact_diag() → full eigendecomposition
│ ├── classical_exact_evolution() → matrix expm
│ └── classical_brute_mpc() → brute-force MPC
│
├── Multi-Backend
│ ├── PennyLane (pennylane_adapter.py)
│ ├── Cirq (cirq_adapter.py)
│ ├── Trapped Ion (trapped_ion.py)
│ ├── GPU (gpu_accel.py, jax_accel.py)
│ └── Plugin Registry (plugin_registry.py)
│
└── Circuit Tools
├── Circuit Cutting (circuit_cutting.py, cutting_runner.py)
├── QASM Export (qasm_export.py)
├── Circuit Export (circuit_export.py)
└── QCVV (qcvv.py)
Prerequisites¶
IBM Quantum¶
- Account: https://quantum.cloud.ibm.com
- Credentials (use
ibm_cloudchannel, NOT deprecatedibm_quantum): - Install IBM runtime:
HardwareRunner (runner.py)¶
The primary execution interface. Handles authentication, backend selection, transpilation, job submission, and result collection.
Connection¶
from scpn_quantum_control.hardware import HardwareRunner
# Real hardware
runner = HardwareRunner(use_simulator=False)
runner.connect() # Authenticates with IBM_QUANTUM_TOKEN env var
# Local simulator (default)
runner = HardwareRunner(use_simulator=True, results_dir="results/")
runner.connect()
When use_simulator=True, uses AerSimulator with the Heron r2 noise
model for realistic local testing without QPU budget consumption.
Transpilation¶
Uses Qiskit's preset pass manager with Heron r2 target. Optimization level 3 performs heavy gate cancellation and routing.
Execution¶
result = runner.run_circuit(
circuit,
experiment_name="kuramoto_4osc",
shots=10000,
)
# result: JobResult with counts, wall_time_s, metadata
ZNE Execution¶
result = runner.run_with_zne(
circuit,
experiment_name="kuramoto_zne",
noise_scales=[1, 3, 5],
shots=10000,
)
Internally calls gate_fold_circuit from the mitigation package.
JobResult¶
| Field | Type | Description |
|---|---|---|
job_id |
str | IBM job ID or "simulator" |
backend_name |
str | Backend identifier |
experiment_name |
str | User-specified experiment name |
counts |
dict or None | Measurement counts |
expectation_values |
ndarray or None | Computed expectations |
metadata |
dict | Arbitrary metadata |
wall_time_s |
float | Total execution time |
timestamp |
str | ISO timestamp |
Results are serialised to JSON via to_dict() and saved to results_dir.
Noise Model (noise_model.py)¶
IBM Heron r2 calibration (ibm_fez, February 2026 median):
| Parameter | Value | Description |
|---|---|---|
| T1 | 300 us | Longitudinal relaxation |
| T2 | 200 us | Transverse relaxation |
| CZ error | 0.5% | Two-qubit gate error rate |
| Readout error | 0.2% | Measurement error rate |
| Single-gate time | 0.06 us | SX/X/RZ duration |
| Two-gate time | 0.66 us | CZ/ECR duration |
heron_r2_noise_model(t1_us, t2_us, cz_error, readout_error)¶
Constructs a Qiskit-Aer NoiseModel:
- Single-qubit gates: thermal relaxation only
- Two-qubit gates (ECR/CZ): thermal relaxation + depolarizing
- Readout: symmetric bit-flip error
from scpn_quantum_control.hardware import heron_r2_noise_model
model = heron_r2_noise_model()
# Use with AerSimulator:
from qiskit_aer import AerSimulator
backend = AerSimulator(noise_model=model)
Classical Reference (classical.py)¶
Exact classical computations for hardware experiment comparison. Every quantum result should be compared against these references.
classical_kuramoto_reference(n_osc, t_max, dt, K=None, omega=None)¶
Euler integration of the classical Kuramoto model:
Returns {times, theta, R} — phase trajectories and order parameter.
Rust acceleration: scpn_quantum_engine.kuramoto_euler() at 33x
speedup for n >= 8.
classical_exact_diag(n, K=None, omega=None)¶
Full eigendecomposition of the XY Hamiltonian. Returns eigenvalues, eigenvectors, ground state, and ground energy.
For n <= 14: direct dense diagonalisation via numpy.linalg.eigh.
For n > 14: sparse ARPACK via scipy.sparse.linalg.eigsh.
classical_exact_evolution(n, t_max, dt, K=None, omega=None)¶
Matrix exponential evolution: psi(t+dt) = exp(-iHdt) psi(t).
Returns time series of R(t) and energy E(t) for direct comparison with Trotter evolution on quantum hardware.
classical_brute_mpc(K, omega, horizon, theta_init)¶
Brute-force model predictive control: enumerate all 2^horizon action sequences and select the one maximising R(t_final).
Rust acceleration: scpn_quantum_engine.brute_mpc() with rayon
parallel enumeration at 5-50x speedup.
Experiments (experiments.py)¶
Pre-defined experiment configurations for systematic QPU characterisation.
ALL_EXPERIMENTS¶
Registry of all 19 experiment functions:
| Experiment | Qubits | Description |
|---|---|---|
kuramoto_4osc |
4 | Basic Trotter evolution, R(t) |
kuramoto_4osc_trotter2 |
4 | Suzuki-Trotter 2nd order |
kuramoto_4osc_zne |
4 | ZNE-mitigated Kuramoto |
kuramoto_8osc |
8 | 8-qubit Kuramoto dynamics |
kuramoto_8osc_zne |
8 | ZNE-mitigated 8-qubit |
vqe_4q |
4 | VQE ground state search |
vqe_8q |
8 | VQE with physics-informed ansatz |
vqe_8q_hardware |
8 | VQE targeting real QPU |
vqe_landscape |
4 | Energy landscape scan |
qaoa_mpc_4 |
4 | QAOA-based MPC |
upde_16_snapshot |
16 | Full 16-qubit UPDE state snapshot |
upde_16_dd |
16 | UPDE with dynamical decoupling |
noise_baseline |
4 | Noise characterisation baseline |
ansatz_comparison_hw |
4 | Compare ansatz architectures |
sync_threshold |
4 | Synchronisation threshold detection |
decoherence_scaling |
4 | Depth vs fidelity scaling |
zne_higher_order |
4 | Higher-order ZNE extrapolation |
bell_test_4q |
4 | CHSH Bell test on hardware |
correlator_4q |
4 | XY correlator measurement |
Each experiment function returns a dict with circuit, shots,
n_qubits, and experiment-specific metadata.
QPU Budget¶
Free tier: 10 minutes/month on ibm_fez (Heron r2, 156 qubits).
| Experiment | Circuits | Shots | QPU Seconds |
|---|---|---|---|
| kuramoto_4osc (1 step) | 3 | 10k | ~15 |
| vqe_4q (100 COBYLA iter) | ~100 | 10k | ~15 |
| qaoa_mpc_4 (p=1) | ~30 | 10k | ~100 |
| upde_16 snapshot | 3 | 20k | ~60 |
Multi-Backend Support¶
PennyLane Adapter (pennylane_adapter.py)¶
from scpn_quantum_control.hardware.pennylane_adapter import PennyLaneRunner
runner = PennyLaneRunner(K, omega, device="default.qubit")
result = runner.run_trotter(t=0.5, reps=2)
# result: PennyLaneResult(energy, order_parameter, n_qubits, device_name, statevector)
VQE via PennyLane optimisers:
Requires: pip install pennylane
Cirq Adapter (cirq_adapter.py)¶
from scpn_quantum_control.hardware.cirq_adapter import CirqRunner
runner = CirqRunner(K, omega)
result = runner.run_trotter(t=0.5, reps=2)
Enables targeting Google Sycamore/Weber QPUs via Cirq.
Requires: pip install cirq-core
Trapped Ion (trapped_ion.py)¶
from scpn_quantum_control.hardware import transpile_for_trapped_ion, trapped_ion_noise_model
ion_circuit = transpile_for_trapped_ion(circuit, n_qubits=4)
model = trapped_ion_noise_model()
Target: IonQ Forte / Quantinuum H-series. Native gate set: MS (Molmer-Sorensen), Rz, Ry.
GPU Acceleration (gpu_accel.py)¶
cuQuantum integration for large-scale statevector simulation. Falls back to CPU when CUDA is not available.
JAX Acceleration (jax_accel.py)¶
JAX-based compilation for VQE parameter optimisation. Enables automatic differentiation of quantum circuits.
Plugin Registry (plugin_registry.py)¶
Dynamic backend registration. Third-party backends register via:
from scpn_quantum_control.hardware.plugin_registry import register_backend
register_backend("my_backend", MyBackendClass)
Circuit Tools¶
Circuit Cutting (circuit_cutting.py, cutting_runner.py)¶
Decomposes large circuits (> available qubits) into subcircuits connected by classical communication. Enables running n-qubit circuits on n/2-qubit hardware with polynomial overhead.
from scpn_quantum_control.hardware.circuit_cutting import partition_circuit
from scpn_quantum_control.hardware.cutting_runner import CuttingRunner
subcircuits = partition_circuit(circuit, max_partition_size=8)
runner = CuttingRunner(backend)
result = runner.run_partitioned(subcircuits)
QASM Export (qasm_export.py)¶
Export circuits to OpenQASM 2.0/3.0 for platform-independent storage and submission to third-party systems.
Circuit Export (circuit_export.py)¶
Export circuits to JSON, LaTeX (Qiskit drawer), and SVG formats for documentation and publication.
QCVV (qcvv.py)¶
Quantum Characterisation, Verification, and Validation protocols. Randomised benchmarking and gate set tomography for hardware qualification.
Decoherence Reference¶
| Depth Range | Expected Error | Recommendation |
|---|---|---|
| < 50 | < 5% | Publishable as-is |
| 50-150 | 5-15% | Publishable with error bars |
| 150-250 | 15-25% | Apply ZNE mitigation |
| 250-400 | 25-40% | Qualitative trends only |
| > 400 | > 40% | Do not trust individual values |
Native Gate Set (Heron r2)¶
| Gate | Description | Duration |
|---|---|---|
| CZ | Two-qubit entangling (native) | 0.66 us |
| RZ(theta) | Z rotation (virtual) | 0 us |
| SX | sqrt(X) | 0.06 us |
| X | Pauli-X | 0.06 us |
| ID | Identity (delay) | 0.06 us |
Transpilation from Qiskit standard gates increases depth. Typical expansion: 1 CNOT → 2 SX + 1 CZ + RZ gates.
Rust Acceleration¶
The classical.py module transparently uses Rust via scpn_quantum_engine
when available:
| Python Function | Rust Function | Speedup | Method |
|---|---|---|---|
classical_kuramoto_reference |
kuramoto_euler, kuramoto_trajectory |
33x | rayon parallel Euler steps |
_expectation_pauli |
expectation_pauli_fast |
3-10x | Bitwise Pauli ops |
classical_brute_mpc |
brute_mpc |
5-50x | rayon parallel 2^horizon enumeration |
_state_order_param |
state_order_param_sparse |
2-5x | SIMD-friendly inner loop |
_order_parameter (Floquet) |
all_xy_expectations |
5-20x | Batch bitwise, single FFI call |
All Rust functions accept split real/imaginary arrays (no complex64 across FFI). Python fallback always available when the Rust crate is not installed.
Interpreting Results¶
Order parameter R from qubit expectation values:
where <X_i> = 2×P(|0>)_x - 1 from X-basis measurement. Requires 3
measurement bases (X, Y, Z) for full reconstruction.
Compare hw_R against exact_R (from classical_kuramoto_reference or
classical_exact_evolution) to quantify hardware error.
Testing¶
72 tests across 8 test files:
test_runner.py— HardwareRunner lifecycle, simulator mode, job serialisationtest_noise_model.py— NoiseModel construction, error rates, parameter overridestest_classical.py— Kuramoto reference, exact diag, evolution paritytest_experiments.py— All 19 experiment definitions, circuit validitytest_pennylane_adapter.py— PennyLane Trotter, VQE, device selectiontest_cirq_adapter.py— Cirq Trotter, simulator paritytest_circuit_cutting.py— Partitioning, recombination, overhead boundstest_qcvv.py— RB, gate set tomography, fidelity extraction
Pipeline Performance¶
Measured on ML350 Gen8 (128 GB RAM, Xeon E5-2620v2):
| Operation | System | Wall Time |
|---|---|---|
HardwareRunner.connect (simulator) |
— | 50 ms |
runner.transpile (opt level 3) |
4 qubits | 120 ms |
runner.run_circuit (simulator) |
4 qubits, 10k shots | 800 ms |
classical_kuramoto_reference (Rust) |
8 oscillators | 0.3 ms |
classical_kuramoto_reference (Python) |
8 oscillators | 12 ms |
classical_exact_diag |
8 qubits | 15 ms |
classical_exact_evolution |
8 qubits | 120 ms |
heron_r2_noise_model |
— | 5 ms |
PennyLaneRunner.run_trotter |
3 qubits | 50 ms |
partition_circuit |
16 → 2×8 qubits | 25 ms |