Skip to content

Latest commit

 

History

History
529 lines (393 loc) · 18.2 KB

File metadata and controls

529 lines (393 loc) · 18.2 KB

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

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. 17 modules (April 2026: added qubit_mapper.py for DynQ topology-agnostic placement) covering IBM superconducting, trapped ion, PennyLane, Cirq, GPU acceleration, and circuit cutting.

Hardware evidence status:

Device Family Campaign Highlight
ibm_fez Heron r2, 156 q Legacy March 2026 baseline artifacts Artifact-backed Bell/QKD/VQE/ZNE/UPDE observations; quote only rows named in the hardware ledger.
ibm_kingston Heron r2, 156 q April 2026 Phase 1, 342 circuits Promoted raw-count DLA parity dataset: peak $+17.48,%$ at depth 6, reproduced by scripts/run_dla_parity_suite.py.

V2, frontier, queued-job, placeholder, and aggregate-only IBM outputs are not promoted unless the hardware ledger names raw counts, private retrieval map, analysis code, and review status.

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

  1. Account: https://quantum.cloud.ibm.com
  2. Credentials (use ibm_cloud channel, NOT deprecated ibm_quantum):
    export IBM_QUANTUM_TOKEN="your-token-here"
    export IBM_QUANTUM_CRN="your-crn-instance-id"
  3. Install IBM runtime:
    pip install -e ".[ibm]"

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

transpiled = runner.transpile(circuit, optimization_level=3)

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

Qiskit-Aer is imported lazily when the noise-model function is called. Importing scpn_quantum_control.hardware does not require a working local Aer installation unless a simulator noise model is actually built.

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:

d(theta_i)/dt = omega_i + sum_j K[i,j] * sin(theta_j - theta_i)

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.


PennyLane Adapter (pennylane_adapter.py)

PennyLaneRunner exposes the same Kuramoto-XY Hamiltonian through any PennyLane-compatible device. run_trotter() evaluates the energy after Trotterized time evolution and reconstructs the Kuramoto order parameter from local transverse expectations:

theta_i = atan2(<Y_i>, <X_i>)
R = |mean_i exp(i theta_i)|

run_vqe() uses the optimized hardware-efficient ansatz for both the energy objective and the post-optimization observable pass. The returned order_parameter is therefore measured from the final ansatz via the same X/Y phase reconstruction; it is not a sentinel, simulator-only statevector value, or unmeasured placeholder.


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:

result = runner.run_vqe(ansatz_depth=1, maxiter=5, seed=42)

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, allow_proxy_basis=True)
model = trapped_ion_noise_model()

Representative target: all-to-all QCCD-style trapped-ion devices. The helper emits a CX-basis proxy for MS/RXX-style entangling operations, records that proxy in circuit metadata, and is not a vendor-native IonQ or Quantinuum compiler path.

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:

R = (1/N) × |sum_i (<X_i> + i×<Y_i>)|

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 serialisation
  • test_noise_model.py — NoiseModel construction, error rates, parameter overrides
  • test_classical.py — Kuramoto reference, exact diag, evolution parity
  • test_experiments.py — All 19 experiment definitions, circuit validity
  • test_pennylane_adapter.py — PennyLane Trotter, VQE, device selection
  • test_cirq_adapter.py — Cirq Trotter, simulator parity
  • test_circuit_cutting.py — Partitioning, recombination, overhead bounds
  • test_qcvv.py — RB, gate set tomography, fidelity extraction

DynQ Topology-Agnostic Qubit Placement

scpn_quantum_control.hardware.qubit_mapper (added April 2026) implements the DynQ method (Liu et al., arXiv:2601.19635) for selecting an execution region on a heavy-hex device based on the live calibration data. The QPU is modelled as a graph weighted by inverse two-qubit gate errors, and Louvain community detection partitions it into high-fidelity sub-regions; the sub-region with the best composite quality score is chosen for the circuit. Quality scoring is Rust-accelerated.

See dynq_qubit_mapping.md for the full theory, the dynq_initial_layout() API, and a Qiskit transpiler integration recipe.

GUESS Symmetry-Decay Error Mitigation

scpn_quantum_control.mitigation.symmetry_decay (added April 2026) provides physics-informed zero-noise extrapolation specifically for the XY Hamiltonian, using its conserved $\sum Z_i$ as the guide observable. GUESS is the recommended default for any SCPN Kuramoto-XY hardware run because the symmetry observable is measured for free in the standard Z-basis read-out, so the mitigation adds zero shot overhead.

See symmetry_decay_guess.md for the full theory, API, and a worked example calibrating $\alpha$ from the Phase 1 ibm_kingston dataset.

Phase 1 Campaign Protocol (April 2026)

The Phase 1 campaign is recorded in .coordination/IBM_CAMPAIGN_STATE.md and IBM_EXECUTION_LOG.md, and the analysis is reproduced by scripts/analyse_phase1_dla_parity.py. The four sub-phases progressively increased the per-point repetition count from 2 to 21 to drive the per-depth uncertainty below the 5 % asymmetry signal:

Sub-phase Circuits Wall time Reps per (depth, sector) point
Pipe cleaner 2 ~0.1 s sanity check
Phase 1 (A/B/C) 42 44.1 s 2
Phase 1.5 (D/E) 72 56.7 s +4 → 6
Phase 2 exhaust (F/G/H/I) 138 97.5 s +6 → 12
Phase 2.5 final burn (J) 90 65.1 s +9 → 21 (at the 5 strongest depths)
Total $n=4$ 342 ~264 s wall up to 21

Headline result: $+10.8,%$ mean asymmetry for depths $\ge 4$, peak $+17.48,%$ at depth 6, Welch combined $p \ll 10^{-16}$.

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
dynq_initial_layout (156-qubit graph, 5-qubit circuit) < 100 ms
learn_symmetry_decay (5 noise scales, Rust) < 1 µs
guess_extrapolate_batch (1,000 observables, Rust) < 50 µs
hypergeometric_envelope (10,000 points, Rust) 2.6 ms
ici_three_level_evolution (2,000 points, Rust) 0.04 ms