New Module Index¶
This page preserves the historical March 2026 module notes and now routes the newer v0.10 public surfaces to their maintained guides. Use the guide pages below for current examples, evidence boundaries, and API contracts before reading the older module-by-module notes.
v0.10 public surfaces¶
| Surface | Primary module or command | Current guide | Evidence boundary |
|---|---|---|---|
| QRNG streaming and health reports | scpn_quantum_control.entropy.QRNGStream |
Quantum Random-Number Generation | Aer simulator entropy with FIPS/NIST health checks, not hardware RNG promotion. |
| ML-DSA-65 trigger authorisation | scpn_quantum_control.crypto.ml_dsa, PqcTriggerSigner |
Post-Quantum Trigger Signer | FIPS 204 vector-conformant, not FIPS-140 validated. |
| UltraScale+ HLS pulse artifact emission | scpn_quantum_control.codegen.ultrascale_hls and scripts/export_ultrascale_hls_artifact.py |
Pulse -> UltraScale+ HLS Codegen | Emits manifest-bound source artifacts for SC-NEUROCORE ingest; synthesis, timing closure, pinout, and hardware execution are outside this claim. |
| NV-centre ODMR magnetometry | scpn_quantum_control.sensing.nv_magnetometry_20T |
NV-Centre 20 T Magnetometry | Simulation and calibration contract; hardware calibration needs evidence. |
| FRC pulsed-shot scheduling | scpn_quantum_control.phase.frc_pulsed_qaoa |
FRC Pulsed-Shot QAOA | Control-grade surrogate unless physics-derived provenance is attached. |
| Realtime loop telemetry | scpn_quantum_control.realtime |
Realtime Runtime | Software-loop timing, not intra-shot QPU latency. |
| Kuramoto variants and acceleration | scpn_quantum_control.variants, scpn_quantum_control.accel |
Kuramoto Variants | Benchmark wording follows the local/isolated evidence classifier. |
| Studio federation | scpn-emit-studio-manifest, scpn_quantum_control.studio.evidence_bundle |
Studio Federation | Schema-A manifest plus schema-B bundles for existing ledgers and result packs; no new evidence promotion. |
Historical March 2026 module batch¶
11 modules added in the 2026-03-30 session, closing gaps identified by competitive analysis against QuSpin, quimb, Mitiq, MISTIQS, Tequila, and NetKet. 78 tests, all passing, zero skips.
Open-System Dynamics¶
phase/lindblad.py — Lindblad Master Equation Solver¶
Solves \(d\rho/dt = -i[H, \rho] + \sum_k (L_k \rho L_k^\dagger - \frac{1}{2}\{L_k^\dagger L_k, \rho\})\) for the Kuramoto-XY Hamiltonian with configurable amplitude damping (\(\gamma_\text{amp}\)) and dephasing (\(\gamma_\text{deph}\)) channels.
Why it matters: The quantum synchronisation community (Ameri et al. PRA 2015, Giorgi et al. PRA 2012) primarily uses QuTiP Lindblad dynamics. Without this module, our package was limited to unitary (closed-system) evolution.
from scpn_quantum_control.phase.lindblad import LindbladKuramotoSolver
import numpy as np
n = 4
K = 0.45 * np.exp(-0.3 * np.abs(np.subtract.outer(range(n), range(n))))
omega = np.linspace(0.8, 1.2, n)
solver = LindbladKuramotoSolver(n, K, omega, gamma_amp=0.05, gamma_deph=0.02)
result = solver.run(t_max=2.0, dt=0.05)
print(f"R: {result['R'][0]:.3f} → {result['R'][-1]:.3f}")
print(f"Purity: {result['purity'][0]:.3f} → {result['purity'][-1]:.3f}")
API:
| Function | Returns |
|---|---|
LindbladKuramotoSolver(n, K, omega, gamma_amp, gamma_deph) |
Solver instance |
.run(t_max, dt) |
{times, R, purity, rho_final} |
.order_parameter(rho) |
Kuramoto R from density matrix |
.purity(rho) |
\(\text{Tr}(\rho^2)\) |
Tests: 13 (purity preservation under unitary, purity decay under damping, R bounded, strong damping kills sync, density matrix positivity, matches unitary solver at zero dissipation)
phase/tensor_jump.py — Monte Carlo Wave Function Method¶
Stochastic simulation of open Kuramoto-XY using quantum jumps. Each trajectory evolves a pure state under the effective non-Hermitian Hamiltonian \(H_\text{eff} = H - \frac{i}{2}\sum_k L_k^\dagger L_k\), with random quantum jumps at rate \(dp = 1 - \langle\psi|\psi\rangle\).
Ensemble averaging over many trajectories recovers the density matrix. Scales better than full Lindblad for larger systems (state vector vs density matrix).
Based on Dalibard et al., PRL 68, 580 (1992) and the Tensor Jump Method (Causer et al., Nature Comms 2025).
from scpn_quantum_control.phase.tensor_jump import mcwf_ensemble
result = mcwf_ensemble(K, omega, gamma_amp=0.1, t_max=1.0, dt=0.05,
n_trajectories=50, seed=42)
print(f"R_mean(T) = {result['R_mean'][-1]:.3f} ± {result['R_std'][-1]:.3f}")
print(f"Total jumps: {result['total_jumps']}")
API:
| Function | Returns |
|---|---|
mcwf_trajectory(K, omega, gamma_amp, ...) |
{times, R, psi_final, n_jumps} |
mcwf_ensemble(K, omega, ..., n_trajectories) |
{times, R_mean, R_std, R_trajectories, total_jumps} |
Tests: 5 (single trajectory, R bounded, ensemble shape, no-damping norm, output keys)
phase/ancilla_lindblad.py — Single-Ancilla Open-System Circuit¶
Hardware-executable circuit for open-system dynamics using 1 ancilla qubit with repeated reset. Each dissipation step:
- Coherent Trotter evolution on system qubits
- Controlled-Ry from each system qubit to ancilla (\(\theta \propto \sqrt{\gamma \cdot dt}\))
- Reset ancilla
This implements amplitude damping without density matrix representation. Runs on IBM hardware with mid-circuit measurement and reset.
Based on Cattaneo et al., PRR 6, 043321 (2024).
from scpn_quantum_control.phase.ancilla_lindblad import (
AncillaCircuitStats, build_ancilla_lindblad_circuit, ancilla_circuit_stats
)
qc = build_ancilla_lindblad_circuit(K, omega, t=0.5, gamma=0.05,
n_dissipation_steps=5)
print(f"Qubits: {qc.num_qubits} ({qc.num_qubits-1} system + 1 ancilla)")
stats = ancilla_circuit_stats(K, omega)
print(f"CX gates: {stats['n_cx_gates']}, Resets: {stats['n_resets']}")
ancilla_circuit_stats(...) returns the typed AncillaCircuitStats envelope:
qubit counts, CX/reset counts, total gate estimate, dissipation-step count, and
the validated damping rate. The circuit and stats APIs use explicit float64
array contracts for K and omega.
Tests: module and contract suites cover circuit shape, reset scaling, finite-time damping angles, validation, stats consistency, and QASM-export workflow integration.
Exact Diagonalisation¶
analysis/magnetisation_sectors.py — U(1) Magnetisation Sectors¶
The XY interaction \(X_iX_j + Y_iY_j = 2(\sigma^+_i\sigma^-_j + \sigma^-_i\sigma^+_j)\) is a flip-flop: it swaps excitations but never creates or destroys them. Therefore the total magnetisation \(M = \sum_i Z_i\) is conserved. This decomposes the \(2^N\) Hilbert space into \(N+1\) sectors labelled by \(M\).
The largest sector (\(M=0\)) has dimension \(\binom{N}{N/2}\):
| N | Full dim | Z₂ sector | U(1) largest | Reduction |
|---|---|---|---|---|
| 12 | 4,096 | 2,048 | 924 | 4.4× |
| 16 | 65,536 | 32,768 | 12,870 | 5.1× |
| 18 | 262,144 | 131,072 | 48,620 | 5.4× |
| 20 | 1,048,576 | 524,288 | 184,756 | 5.7× |
For N=16: full ED needs 32 GB, U(1) sector needs 2.5 GB. 13× memory reduction.
from scpn_quantum_control.analysis.magnetisation_sectors import (
eigh_by_magnetisation, level_spacing_by_magnetisation, memory_estimate
)
# All sectors — exact spectrum
result = eigh_by_magnetisation(K, omega)
print(f"Ground: E={result['ground_energy']:.4f}, M={result['ground_sector']}")
# Level spacing within M=0 sector (avoids inter-sector artefact)
ls = level_spacing_by_magnetisation(K, omega, M=0)
print(f"r̄(M=0) = {ls['r_bar']:.3f} (Poisson=0.386, GOE=0.530)")
# Memory comparison
est = memory_estimate(16)
print(f"Full: {est['full_ed_mb']:.0f} MB, U(1): {est['u1_largest_mb']:.0f} MB")
API:
| Function | Returns |
|---|---|
basis_by_magnetisation(n) |
dict[M] → array of basis indices |
sector_dimensions(n) |
dict[M] → dimension |
eigh_by_magnetisation(K, omega, sectors) |
Full spectrum decomposed by M |
level_spacing_by_magnetisation(K, omega, M) |
r̄ within single sector |
memory_estimate(n) |
Comparison: full vs Z₂ vs U(1) |
Tests: 21 (partition, binomial match, eigenvalue exact match at n=4,6,8, level spacing, memory estimates, N=16/20 dimensions)
analysis/symmetry_sectors.py — Z₂ Parity Sector Decomposition¶
Exploits the Z₂ parity symmetry \(P = Z_1 \otimes \cdots \otimes Z_N\) to halve the Hilbert space for exact diagonalisation. At n=16, full ED needs 32 GB; with sectors, each sector needs 8 GB.
Inspired by QuSpin's symmetry handling (Weinberg & Bukov, SciPost 2017).
from scpn_quantum_control.analysis.symmetry_sectors import (
eigh_by_sector, level_spacing_by_sector, memory_estimate_mb
)
result = eigh_by_sector(K, omega)
print(f"Ground energy: {result['ground_energy']:.4f}")
print(f"Ground parity: {'even' if result['ground_parity'] == 0 else 'odd'}")
print(f"Memory (full): {memory_estimate_mb(16, False):.0f} MB")
print(f"Memory (sector): {memory_estimate_mb(16, True):.0f} MB")
# Sector-aware level spacing (avoids artefact of overlaying two spectra)
ls = level_spacing_by_sector(K, omega)
print(f"r̄ (even): {ls['r_bar_even']:.3f}, r̄ (odd): {ls['r_bar_odd']:.3f}")
Tests: 13 (partition correctness, sector dimensions, Hermiticity, eigenvalue match with full ED, level spacing bounded, memory estimates)
Tensor Network¶
phase/mps_evolution.py — MPS/DMRG via quimb¶
Matrix Product State backend for systems beyond ED limits (n=32-64). DMRG for ground state, TEBD for time evolution.
Currently nearest-neighbour couplings only (quimb SpinHam1D /
LocalHam1D limitation). Full-K inputs with non-adjacent couplings are
rejected by default; callers must pass allow_long_range_truncation=True
for the explicitly labelled nearest-neighbour diagnostic path.
Requires pip install quimb.
from scpn_quantum_control.phase.mps_evolution import dmrg_ground_state, tebd_evolution
# Ground state
gs = dmrg_ground_state(K, omega, bond_dim=64, max_sweeps=20)
print(f"DMRG energy: {gs['energy']:.4f}, converged: {gs['converged']}")
# Time evolution
dyn = tebd_evolution(K, omega, t_max=1.0, dt=0.05, bond_dim=64)
print(f"R(0) = {dyn['R'][0]:.3f}, R(T) = {dyn['R'][-1]:.3f}")
Tests: 10 (DMRG energy, bond dims, TEBD R bounded, output keys)
Error Mitigation¶
mitigation/mitiq_integration.py — Mitiq ZNE + DDD¶
Production-quality error mitigation via Mitiq (LaRose et al., Quantum 6, 774, 2022). Wraps Mitiq's ZNE (Richardson extrapolation) and DDD (digital dynamical decoupling) around our circuits.
Requires pip install mitiq.
from scpn_quantum_control.mitigation.mitiq_integration import zne_mitigated_expectation
from qiskit import QuantumCircuit
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
mitigated = zne_mitigated_expectation(qc, scale_factors=[1.0, 3.0, 5.0])
print(f"ZNE-mitigated ⟨Z⟩ = {mitigated:.4f}")
Tests: 5 (availability, returns float, bounded, identity circuit, custom executor)
Note: Mitiq 1.0 has a bug where from __future__ import annotations breaks
executor introspection. This module avoids the __future__ import as a workaround.
Variational Methods¶
phase/param_shift.py — Parameter-Shift Gradient Rule¶
Analytic gradient computation: \(\partial\langle H\rangle/\partial\theta_k = \frac{1}{2}[\langle H\rangle(\theta_k + \pi/2) - \langle H\rangle(\theta_k - \pi/2)]\).
No finite-difference error. Works on real hardware (only needs 2 circuit evaluations
per parameter). Replaces the finite-difference gradients in nqs_ansatz.py.
Based on Mitarai et al., PRA 98, 032309 (2018).
from scpn_quantum_control.phase.param_shift import vqe_with_param_shift
result = vqe_with_param_shift(cost_fn, n_params=10, learning_rate=0.1,
n_iterations=100, seed=42)
print(f"Energy: {result['energy']:.4f}")
Tests: 4 (quadratic gradient, VQE convergence, output keys, zero at minimum)
phase/nqs_ansatz.py — Neural Quantum State (RBM)¶
Restricted Boltzmann Machine wavefunction for variational ground state search. \(\log\psi(\sigma) = \sum_i a_i \sigma_i + \sum_j \log\cosh(\sum_i W_{ji}\sigma_i + b_j)\).
Pure numpy, no JAX/torch. Exact mode for n ≤ 12 (all \(2^n\) configurations).
This path uses central finite-difference gradients and rejects n_samples
instead of silently ignoring requested sampling budgets. Returned metadata
records sampling_mode, n_samples_used, and gradient_method. For
production at larger scales or sampled VMC, use NetKet.
Based on Carleo & Troyer, Science 355, 602 (2017).
from scpn_quantum_control.phase.nqs_ansatz import vmc_ground_state
result = vmc_ground_state(K, omega, n_iterations=200, seed=42)
print(f"VMC energy: {result['energy']:.4f}, params: {result['n_params']}")
Tests: RBM amplitude invariants, normalisation, parameter counting,
reproducibility, variational energy checks, output metadata, explicit
n_samples rejection, and large-n rejection.
Circuit Compilation¶
phase/xy_compiler.py — XY-Optimised Gate Decomposition¶
Domain-specific compiler for the XX+YY interaction. Each coupling term \(e^{-iK_{ij}t(X_iX_j + Y_iY_j)}\) decomposes into 2 CNOT + 1 Rx, which is more efficient than generic PauliEvolutionGate Trotter decomposition.
Inspired by MISTIQS domain-specific TFIM compiler.
from scpn_quantum_control.phase.xy_compiler import compile_xy_trotter, depth_comparison
qc = compile_xy_trotter(K, omega, t=0.1, reps=5, order=2)
print(f"Depth: {qc.depth()}, gates: {qc.size()}")
cmp = depth_comparison(K, omega, t=0.1, reps=5)
print(f"Generic: {cmp['generic_depth']}, Optimised: {cmp['optimised_depth']}")
print(f"Reduction: {cmp['reduction_pct']}%")
Tests: 4 (circuit creation, order 2 ≥ order 1, depth comparison, unitarity)
hardware/circuit_export.py — Multi-Platform Export¶
Export Kuramoto-XY circuits to OpenQASM, Quil (Rigetti), and Cirq formats.
from scpn_quantum_control.hardware.circuit_export import export_all
result = export_all(K, omega, t=0.1, reps=5)
print(f"QASM length: {len(result['qasm3'])} chars")
print(f"Quil length: {len(result['quil'])} chars")
print(f"Depth: {result['depth']}, gates: {result['gate_count']}")
# Save for Rigetti
with open("kuramoto.quil", "w") as f:
f.write(result["quil"])
Tests: 4 (QASM string, Quil string, export_all keys, measurements present)
Infrastructure¶
phase/backend_selector.py — Automatic Backend Selection¶
Auto-selects the best simulation backend based on system size, available RAM, installed packages, and whether open-system dynamics are needed.
Inspired by Maestro (Qoro, arXiv:2512.04216).
from scpn_quantum_control.phase.backend_selector import recommend_backend, auto_solve
# Recommendation only
rec = recommend_backend(n=16, ram_gb=32.0)
print(f"Backend: {rec['backend']}, Memory: {rec['memory_mb']:.0f} MB")
# Auto-solve (selects backend and runs)
result = auto_solve(K, omega)
print(f"Used: {result['backend_used']}, E₀ = {result['result']['ground_energy']:.4f}")
| System size | Backend selected |
|---|---|
| n ≤ 14 | exact_diag (numpy eigh) |
| n = 15-16 | sector_ed (Z₂ parity) |
| n = 17-64 | mps_dmrg (quimb, if installed) |
| n > 64 | hardware (IBM) |
| Open system, n ≤ 12 | lindblad_scipy |
Tests: 6 (small/medium/large/open/huge system selection, auto_solve runs)
Batch 3 — Scalability & Ecosystem Integration (March 2026)¶
8 modules closing capability gaps identified by analysis of 20+ competing projects (QuSpin, quimb, Stim, NetKet, TensorCircuit, OpenFermion, TorchQuantum, Maestro, cotengra). 53 tests, all passing.
Sparse Hamiltonian¶
bridge/sparse_hamiltonian.py — CSC Sparse XY Hamiltonian¶
The XY Hamiltonian has \(O(n^2 \cdot 2^n)\) non-zero elements in a \(2^N \times 2^N\)
matrix — less than 1% fill for \(n \geq 10\). Sparse storage (scipy CSC) +
ARPACK eigensolver (eigsh) enables exact diagonalisation at scales impossible
with dense matrices.
| n | Dense (MB) | Sparse (MB) | Reduction |
|---|---|---|---|
| 12 | 134 | 12 | 11× |
| 14 | 2,147 | 50 | 43× |
| 16 | 32,768 | 200 | 164× |
| 18 | 524,288 | 800 | 655× |
Combined with U(1) sectors: sparse eigsh on \(C(20,10) = 184{,}756\) with \(\sim 10^6\) non-zeros → feasible on a 32 GB workstation.
Inspired by QuSpin's sparse Hamiltonian construction.
from scpn_quantum_control.bridge.sparse_hamiltonian import (
build_sparse_hamiltonian, sparse_eigsh, sparsity_stats
)
# Full sparse Hamiltonian
H = build_sparse_hamiltonian(K, omega)
print(f"Shape: {H.shape}, NNZ: {H.nnz}, Fill: {H.nnz/H.shape[0]**2:.4%}")
# Sparse eigenvalues (ARPACK — k smallest)
result = sparse_eigsh(K, omega, k=10)
print(f"Ground energy: {result['eigvals'][0]:.6f}")
# Within U(1) sector
result_m0 = sparse_eigsh(K, omega, k=10, M=0)
print(f"M=0 ground: {result_m0['eigvals'][0]:.6f}")
# Memory estimate
stats = sparsity_stats(16, K)
print(f"Sparse: {stats['memory_sparse_mb']:.0f} MB vs Dense: {stats['memory_dense_mb']:.0f} MB")
API:
| Function | Returns |
|---|---|
build_sparse_hamiltonian(K, omega) |
scipy.sparse.csc_matrix |
build_sparse_sector_hamiltonian(K, omega, M) |
(csc_matrix, indices) within U(1) sector |
sparse_eigsh(K, omega, k, which, M) |
{eigvals, eigvecs, nnz, dim, method} |
sparsity_stats(n, K) |
{dim, nnz_estimate, fill_pct, memory_sparse_mb, memory_dense_mb} |
Tests: 14 (shape, Hermiticity, matches dense at n=4/6, nnz bounds, sector match, ground energy match, ARPACK feasible at n=8, dense fallback, sparsity stats)
JAX Acceleration¶
phase/jax_nqs.py — JAX-Accelerated RBM Wavefunction¶
Replaces the numpy finite-difference gradients in nqs_ansatz.py with JAX
jit + grad for automatic differentiation. This exact-enumeration research
path has no committed isolated speed benchmark and no NumPy fallback.
Inspired by NetKet (Vicentini et al., SoftwareX 2022).
Requires: pip install jax jaxlib
from scpn_quantum_control.phase.jax_nqs import jax_vmc_ground_state
result = jax_vmc_ground_state(K, omega, n_iterations=200, learning_rate=0.01, seed=42)
print(f"JAX VMC energy: {result['energy']:.4f}")
print(f"Parameters: {result['n_params']}")
API:
| Function | Returns |
|---|---|
is_jax_available() |
bool |
jax_rbm_energy(params, H, n) |
JAX scalar (differentiable) |
jax_vmc_ground_state(K, omega, ...) |
{energy, energy_history, params, n_params} |
Tests: Requires JAX installation — included in batch 3 test file as conditional.
For validated 2 <= N <= 6 exact-reference evidence, environment provenance,
and the no-advantage boundary, use
jax_nqs_baseline_product.
Multi-Backend Dispatch¶
backend_dispatch.py — Runtime Backend Selection¶
Switch between numpy, JAX, and PyTorch for array operations at runtime.
Inspired by TensorCircuit's tc.set_backend().
from scpn_quantum_control.backend_dispatch import set_backend, get_backend, available_backends
print(available_backends()) # ['numpy', 'jax', 'torch']
set_backend("jax") # all array ops now use JAX
set_backend("numpy") # back to numpy
API:
| Function | Returns |
|---|---|
set_backend(name) |
Sets global backend ("numpy", "jax", "torch") |
get_backend() |
Current backend name |
get_array_module() |
The active array module (np, jnp, or torch) |
to_numpy(arr) |
Convert any backend array to numpy |
from_numpy(arr) |
Convert numpy to current backend |
available_backends() |
List installed backends |
Tests: 6 (default numpy, set/get, available list, to/from numpy, array module)
Plugin Architecture¶
hardware/plugin_registry.py — Extensible Backend Registry¶
Register and discover hardware backends at runtime. Supports lazy loading of built-in backends (Qiskit, PennyLane, Cirq) and registration of custom backends.
Inspired by OpenFermion's plugin architecture.
from scpn_quantum_control.hardware.plugin_registry import registry
# List and use built-in backends
print(registry.available_backends()) # ['qiskit', 'pennylane', 'cirq']
runner = registry.get_runner("pennylane", K, omega, device="default.qubit")
result = runner.run_trotter(t=0.5, reps=3)
# Register a custom backend
@registry.register("my_simulator")
class MyRunner:
def __init__(self, K, omega, **kwargs): ...
def run_trotter(self, t, reps): ...
API:
| Method | Description |
|---|---|
registry.list_backends() |
All registered + lazy-loadable names |
registry.available_backends() |
Only actually importable backends |
registry.is_available(name) |
Check single backend |
registry.get_runner(name, K, omega, **kw) |
Get instantiated runner |
@registry.register(name) |
Decorator for custom backends |
Tests: 6 (list, availability, get runner, custom registration, error on unknown, subset check)
GPU Batch Evaluation¶
phase/gpu_batch_vqe.py — Parallel VQE Parameter Scan¶
Evaluate multiple VQE parameter sets in batch. CPU baseline with NumPy;
use_gpu=True requests a PyTorch/CUDA execution path and raises if CUDA is
not available rather than silently falling back.
Inspired by TorchQuantum (MIT HAN Lab).
from scpn_quantum_control.phase.gpu_batch_vqe import batch_vqe_scan
result = batch_vqe_scan(K, omega, n_samples=100, seed=42)
print(f"Best energy: {result['best_energy']:.4f}")
print(f"Scanned {result['n_samples']} parameter sets")
API:
| Function | Returns |
|---|---|
batch_energy_numpy(H, param_sets, ansatz_fn) |
energies array |
batch_energy_torch(H, param_sets, ansatz_fn, device) |
energies array (GPU) |
batch_vqe_scan(K, omega, n_samples, seed, use_gpu) |
{energies, params, best_energy, best_params, n_samples, backend, ansatz_family, optimizer, hardware_claim} |
The built-in scan is a product-Ry random-parameter statevector expectation diagnostic, not a gradient-optimised or hardware-executed VQE.
Tests: batch energy, VQE scan, output keys, backend contract, input validation.
Translation Symmetry¶
analysis/translation_symmetry.py — Cyclic Translation for Periodic Chains¶
When frequencies are homogeneous (\(\omega_i = \omega\)) AND the coupling matrix is circulant (\(K_{ij} = K(|i-j| \bmod N)\)), the cyclic shift operator \(T\) commutes with \(H\). Eigenstates then carry definite crystal momentum \(k = 2\pi m/N\).
Combined with U(1), this gives \(N \times (N+1)\) sectors. For \(n=16\): full 65,536 → \(k=0\) sector of \(M=0\): ~805 states → 80× reduction.
Only applicable to homogeneous systems. For heterogeneous \(\omega\) (the SCPN case), translation is broken — use U(1) sectors instead.
Inspired by QuSpin's full symmetry chain.
from scpn_quantum_control.analysis.translation_symmetry import (
is_translation_invariant, eigh_with_translation
)
# Check if system is translation-invariant
K_ring = ... # circulant coupling
omega_uniform = np.ones(n) * 1.0
print(is_translation_invariant(K_ring, omega_uniform)) # True
# Diagonalise in k=0 sector
result = eigh_with_translation(K_ring, omega_uniform, momentum=0)
print(f"k=0 sector: {result['dim']} states, ground={result['eigvals'][0]:.4f}")
Tests: 6 (TI detection, non-TI detection, sector dimensions, k=0 eigenvalues, heterogeneous raises, ground energy within full spectrum)
Contraction Optimisation¶
phase/contraction_optimiser.py — Tensor Contraction Path Finder¶
Optimal einsum contraction paths using cotengra (if available), with
numpy fallback. Drop-in replacement for np.einsum.
Inspired by quimb's cotengra integration.
from scpn_quantum_control.phase.contraction_optimiser import contract, benchmark_contraction
# Drop-in replacement for np.einsum
C = contract("ij,jk->ik", A, B)
# Benchmark
result = benchmark_contraction("ij,jk,kl->il", A, B, C, n_repeats=10)
print(f"Naive: {result['naive_ms']:.1f} ms, Optimised: {result['optimised_ms']:.1f} ms")
print(f"Speedup: {result['speedup']}×")
API:
| Function | Returns |
|---|---|
is_cotengra_available() |
bool |
optimal_contraction_path(subscripts, *operands) |
(path, info) |
contract(subscripts, *operands) |
Result array (optimised) |
benchmark_contraction(subscripts, *operands, n_repeats) |
{naive_ms, optimised_ms, speedup} |
Tests: 4 (matches einsum, path info, benchmark, availability check)
Rust Engine Expansion¶
3 new PyO3 functions added to scpn_quantum_engine, bringing the total to 18.
All wired into Python modules as automatic fast paths with Python fallback.
build_sparse_xy_hamiltonian — 80× faster sparse construction¶
Returns COO triplets (rows, cols, vals) for scipy.sparse.csc_matrix.
Same bitwise flip-flop as build_xy_hamiltonian_dense but outputs sparse format
instead of dense matrix. Eliminates the Python for k in range(2^n) bottleneck.
import scpn_quantum_engine as eng
rows, cols, vals = eng.build_sparse_xy_hamiltonian(K.ravel(), omega, n)
# Then: scipy.sparse.csc_matrix((vals, (rows, cols)), shape=(2**n, 2**n))
Wired into: bridge/sparse_hamiltonian.py — called automatically, Python fallback if engine not installed.
Measured: 0.024 ms (Rust) vs 1.9 ms (Python) at n=8 → 80× speedup
Tests: 6 (triplets returned, matches dense Rust, matches Python dense, Hermitian, NNZ bounds, eigenvalue match)
magnetisation_labels — 97× faster popcount¶
Returns array of magnetisation M for all 2^N basis states using hardware
count_ones() instruction. M = N − 2×popcount(k).
Wired into: analysis/magnetisation_sectors.py::basis_by_magnetisation() — called automatically.
Measured: 0.001 ms (Rust) vs 0.11 ms (Python) at n=8 → 97× speedup
Tests: 6 (n=2 explicit, n=4 range, all-up/all-down, matches Python, sum=0, popcount consistency)
order_param_from_statevector — 851× faster order parameter¶
Computes Kuramoto order parameter R from complex state vector via bitwise Pauli expectations. Critical inner loop in MCWF trajectories.
Wired into: phase/tensor_jump.py::_order_param_vec() — called automatically.
Measured: 0.008 ms (Rust) vs 6.47 ms (Python) at n=8 → 851× speedup
Tests: 5 (all-up state, bounded, matches Python exactly, n=8 performance <1ms, deterministic)
Complete Rust Engine¶
| Function | Category | Speedup |
|---|---|---|
build_xy_hamiltonian_dense |
Hamiltonian | ~111× (L=4) → parity (L=12) |
build_sparse_xy_hamiltonian |
Hamiltonian | 80× |
order_param_from_statevector |
Order param | 851× |
otoc_from_eigendecomp |
OTOC | 264× |
lanczos_b_coefficients |
Krylov | 27× |
magnetisation_labels |
Symmetry | 97× |
all_xy_expectations |
Pauli | 6.2× |
state_order_param_sparse |
Order param | — |
expectation_pauli_fast |
Pauli | — |
pec_sample_parallel |
PEC (rayon) | — |
brute_mpc |
MPC (rayon) | — |
dla_dimension |
DLA (rayon) | — |
mc_xy_simulate |
Monte Carlo | — |
kuramoto_trajectory |
Kuramoto ODE | — |
kuramoto_euler |
Kuramoto ODE | — |
order_parameter |
Classical R | — |
build_knm |
K matrix | — |
pec_coefficients |
PEC | — |