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 — API Reference¶
API Reference¶
For first-path user workflows, start with the
Stable Facades API. It is the mkdocstrings reference
for public facades such as scpn_quantum_control.kuramoto_core. The lower-level
sections below are advanced module references for maintainers, extension authors,
and researchers who need direct subsystem access.
Reference Levels¶
| Level | Start here | Use when |
|---|---|---|
| Stable facade | Stable Facades API | Building notebooks, tutorials, cross-repository integrations, or user-facing workflows. |
| Workflow guide | Kuramoto Core Facade | Compiling arbitrary K_nm/omega problems without depending on low-level module layout. |
| Source-validation register | Paper 0 Validation Register | Inspecting generated Paper 0 source-accounting specs, fixtures, and validation modules under their explicit claim boundary. |
| Runtime contract | QPU Data Artifact, Pipeline Runtime Contract | Exchanging persisted QPU results or compute-unit metadata. |
| Advanced module reference | This page and Auto-Generated Module Index | Auditing, extending, or debugging subsystem internals. |
stable facades¶
kuramoto_core¶
build_kuramoto_problem(K_nm, omega, metadata=None) -> KuramotoProblem
compile_hamiltonian(problem) -> SparsePauliOp
compile_dense_hamiltonian(problem) -> np.ndarray
compile_trotter_circuit(problem, time, trotter_steps=10, trotter_order=1) -> QuantumCircuit
measure_order_parameter(problem, statevector) -> tuple[float, float]
Validate an arbitrary symmetric Kuramoto coupling problem, attach serialisable metadata, and compile the common Hamiltonian/circuit objects used by simulator, witness, and hardware workflows. See Kuramoto Core Facade for the workflow page.
paper0¶
from scpn_quantum_control.paper0 import validate_upde_fixture
from scpn_quantum_control.paper0.spec_loader import load_upde_validation_spec
The paper0 package exposes the completed source-accounting register for Paper
0. Generated validation modules preserve ledger-bounded source spans, component
labels, fixture summaries, spec bundles, and claim boundaries. These modules are
documentation and regression infrastructure for source ingestion; they do not
convert Paper 0 source statements into measured hardware evidence or external
scientific validation. See Paper 0 Validation Register.
Advanced Module Reference¶
The following sections expose lower-level modules directly. Prefer the stable facades above unless you specifically need a bridge adapter, solver class, or subsystem implementation detail.
bridge¶
knm_hamiltonian¶
Convert Knm coupling matrix and natural frequencies to XY Hamiltonian as Qiskit SparsePauliOp.
H = -\sum_{i<j} K_{ij}(X_iX_j + Y_iY_j) - \sum_i \omega_i Z_i$. Use when you need the
SparsePauliOp for circuit construction. For dense matrix operations, use knm_to_dense_matrix.
Build dense XY Hamiltonian matrix (shape 2^n × 2^n, complex). Uses Rust bitwise flip-flop
construction when scpn_quantum_engine is installed (10-50× faster than Qiskit for n≤10),
falls back to knm_to_hamiltonian(...).to_matrix(). Preferred for eigendecomposition, OTOC,
entanglement entropy, Krylov complexity, and any code that needs a numpy matrix.
Build sparse XY/XXZ Hamiltonian matrix in CSC format for large-scale memory-efficient simulation.
XXZ Hamiltonian with anisotropy \(\Delta\): H = -\sum_{i<j} K_{ij}(X_iX_j + Y_iY_j + \Delta Z_iZ_j) - \sum_i \omega_i Z_i$.
At \(\Delta=0\): XY model (standard Kuramoto mapping). At \(\Delta=1\): isotropic Heisenberg.
Build physics-informed ansatz: CZ entanglement only between pairs where K[i,j] > threshold.
Canonical {nm}$ coupling matrix from Paper 27 with exponential decay, calibration anchors, and cross-hierarchy boosts.
build_kuramoto_ring(n: int, coupling: float = 1.0, omega: np.ndarray | None = None) -> tuple[np.ndarray, np.ndarray]
(K, omega).
phase_artifact¶
LockSignatureArtifact(source_layer, target_layer, plv, mean_lag)
LayerStateArtifact(R, psi, lock_signatures={})
UPDEPhaseArtifact(layers, cross_layer_alignment, stability_proxy, regime_id, metadata={})
Helpers:
UPDEPhaseArtifact.to_dict() -> dict
UPDEPhaseArtifact.to_json(indent=2) -> str
UPDEPhaseArtifact.from_dict(payload) -> UPDEPhaseArtifact
UPDEPhaseArtifact.from_json(payload) -> UPDEPhaseArtifact
orchestrator_adapter¶
PhaseOrchestratorAdapter.from_orchestrator_state(state, metadata=None) -> UPDEPhaseArtifact
PhaseOrchestratorAdapter.to_orchestrator_payload(artifact) -> dict
PhaseOrchestratorAdapter.to_scpn_control_telemetry(artifact) -> dict
PhaseOrchestratorAdapter.build_knm_from_binding_spec(binding_spec, zero_diagonal=False) -> np.ndarray
PhaseOrchestratorAdapter.build_omega_from_binding_spec(binding_spec, default_omega=1.0) -> np.ndarray
control_plasma_knm¶
Compatibility bridge for scpn-control plasma-native Knm update.
build_knm_plasma(mode="baseline", L=8, K_base=0.30, zeta_uniform=0.0, ..., repo_src=None) -> np.ndarray
build_knm_plasma_spec(...) -> dict # {K, zeta, layer_names}
build_knm_plasma_from_config(R0, a, B0, Ip, n_e, ..., repo_src=None) -> np.ndarray
plasma_omega(L=8, repo_src=None) -> np.ndarray
sc_to_quantum¶
probability_to_angle(p: float) -> float # p -> 2*arcsin(sqrt(p))
angle_to_probability(theta: float) -> float # theta -> sin^2(theta/2)
bitstream_to_statevector(bits: np.ndarray) -> np.ndarray
measurement_to_bitstream(counts: dict, length: int) -> np.ndarray
spn_to_qcircuit¶
qsnn¶
dynamic_coupling.DynamicCouplingEngine¶
DynamicCouplingEngine(n_qubits, initial_K, omega, learning_rate=0.1, decay_rate=0.05)
.step(dt: float) -> dict # K_updated, correlation_matrix, statevector
.run_coevolution(steps, dt) -> list[dict]
qlif.QuantumLIFNeuron¶
QuantumLIFNeuron(v_rest=-70.0, v_threshold=-55.0, tau_mem=10.0, dt=1.0, n_shots=100)
.step(input_current: float) -> int # 0 or 1
.get_circuit() -> QuantumCircuit
.reset()
qsynapse.QuantumSynapse¶
QuantumSynapse(weight: float, w_min: float = 0.0, w_max: float = 1.0)
.apply(circuit, pre_qubit, post_qubit)
.effective_weight() -> float
qstdp.QuantumSTDP¶
QuantumSTDP(learning_rate: float = 0.01, shift: float = pi/2)
.update(synapse, pre_measured, post_measured)
qlayer.QuantumDenseLayer¶
QuantumDenseLayer(n_neurons: int, n_inputs: int, weights: np.ndarray | None = None, spike_threshold: float = 0.5)
.forward(input_values: np.ndarray) -> np.ndarray # spike array (0/1)
.get_weights() -> np.ndarray # (n_neurons, n_inputs)
training.QSNNTrainer¶
QSNNTrainer(layer: QuantumDenseLayer, lr: float = 0.01)
.parameter_shift_gradient(inputs, target) -> np.ndarray
.train_epoch(X, y) -> float # mean loss
.train(X, y, epochs=10) -> list[float] # loss history
phase¶
structured_ansatz¶
build_structured_ansatz(coupling_matrix, reps=2, entanglement_gate="cz", threshold=1e-6) -> QuantumCircuit
lindblad_engine.LindbladSyncEngine¶
LindbladSyncEngine(K, omega, gamma=0.1)
.evolve(t_max, n_steps=100, method="trajectory", initial_state=None, n_traj=20, observables=None) -> dict
xy_kuramoto.QuantumKuramotoSolver¶
QuantumKuramotoSolver(
n_oscillators,
K_coupling,
omega_natural,
trotter_order=None,
evolution_config=None,
)
.build_hamiltonian() -> SparsePauliOp
.evolve(time, trotter_steps=None) -> QuantumCircuit
.measure_order_parameter(statevector) -> tuple[float, float] # (R, psi)
.run(t_max: float, dt: float, trotter_per_step: int | None = None) -> dict # times, R
TrotterEvolutionConfig carries the default Trotter order and repetition
counts. run() uses exact labelled time boundaries: when t_max is not an
integer multiple of dt, the final interval is shortened so the state evolution
and returned times[-1] both end at t_max.
trotter_upde.QuantumUPDESolver¶
QuantumUPDESolver(n_layers=16, knm=None, omega=None)
.build_hamiltonian() -> SparsePauliOp
.step(dt, shots=10000) -> dict # per_layer_R, global_R
.run(n_steps, dt, shots=10000) -> dict
phase_vqe.PhaseVQE¶
PhaseVQE(K: np.ndarray, omega: np.ndarray, ansatz_reps: int = 2, threshold: float = 0.01)
.solve(optimizer="COBYLA", maxiter=200, seed: int | None = None) -> dict
pulse_shaping (added April 2026)¶
PMP-optimal ICI pulse sequences (Liu et al. 2023) and the unified \((\alpha,\beta)\)-hypergeometric pulse family (Ventura Meinersen et al., arXiv:2504.08031). All compute paths are Rust-accelerated:
| Function | Speedup vs Python |
|---|---|
hypergeometric_envelope_batch (10k points) |
44× |
ici_three_level_evolution_batch (2k points) |
1,665× |
ici_mixing_angle_batch |
parity-checked |
from scpn_quantum_control.phase.pulse_shaping import (
build_ici_pulse,
build_hypergeometric_pulse,
ici_three_level_evolution,
hypergeometric_envelope,
build_trotter_pulse_schedule,
)
# ICI pulse with Lindblad decay simulation
pulse = build_ici_pulse(t_total=2.0, omega_0=20.0, gamma_decay=0.05, n_points=500)
populations = ici_three_level_evolution(pulse) # shape (500, 3) — P_g, P_e, P_s
# (α,β)-hypergeometric envelope (STIRAP-optimal: α=β=0.5)
hpulse = build_hypergeometric_pulse(t_total=1.0, omega_0=10.0, alpha=0.5, beta=0.5)
# Build a complete Trotter pulse schedule from a coupling matrix
schedule = build_trotter_pulse_schedule(n_qubits=4, k_matrix=K, t_step=0.1)
hardware¶
qubit_mapper.dynq_initial_layout (added April 2026)¶
Topology-agnostic qubit placement via Louvain community detection on a calibration-weighted QPU graph. Implements the DynQ method (Liu et al., arXiv:2601.19635). Quality scoring is Rust-accelerated.
from scpn_quantum_control.hardware.qubit_mapper import dynq_initial_layout
result = dynq_initial_layout(
gate_errors={(i, j): error_rate for ...},
circuit_width=4,
readout_errors={i: error_rate for ...},
seed=42,
)
result.initial_layout # list[int] — physical qubits to use
result.selected_region # ExecutionRegion — quality_score, connectivity, etc.
See dynq_qubit_mapping.md for the full
theory and Qiskit transpiler integration recipe.
mitigation (highlights — see also mitigation_api.md)¶
symmetry_decay — GUESS (added April 2026)¶
Physics-informed zero-noise extrapolation using the conserved \(\sum Z_i\) as the guide observable for the XY Hamiltonian. Used as both the noise calibration and the headline scientific result of the April 2026 ibm_kingston Phase 1 campaign.
from scpn_quantum_control.mitigation.symmetry_decay import (
learn_symmetry_decay,
guess_extrapolate,
xy_magnetisation_ideal,
)
n = 4
s_ideal = xy_magnetisation_ideal(n, "ground")
model = learn_symmetry_decay(
ideal_symmetry_value=s_ideal,
noisy_symmetry_values=[3.92, 3.65, 3.10], # at noise scales 1, 3, 5
noise_scales=[1, 3, 5],
)
result = guess_extrapolate(target_noisy_value=0.45,
symmetry_noisy_value=3.92,
decay_model=model)
result.mitigated_value # corrected target observable
See symmetry_decay_guess.md for the full
theory and a worked Phase 1 example.
control¶
topological_optimizer.TopologicalCouplingOptimizer¶
TopologicalCouplingOptimizer(n_qubits, initial_K, omega, learning_rate=0.05, dt=1.0)
.step(n_samples=5) -> dict # K_updated, p_h1_current, gradient_norm
.optimize(steps=10, n_samples=3) -> list[dict]
hardware_topological_optimizer.HardwareTopologicalOptimizer¶
Hardware-in-the-loop variant using real IBM QPU measurement counts for topological optimization.
qaoa_mpc.QAOA_MPC¶
QAOA_MPC(B_matrix, target_state, horizon, p_layers=2)
.build_cost_hamiltonian() -> SparsePauliOp
.optimize() -> np.ndarray # action sequence
vqls_gs.VQLS_GradShafranov¶
VQLS_GradShafranov(grid_size=4, source_profile=None)
.discretize() -> tuple[np.ndarray, np.ndarray] # (A, b)
.build_ansatz(n_qubits, reps=2) -> QuantumCircuit
.solve() -> np.ndarray # psi profile
qpetri.QuantumPetriNet¶
QuantumPetriNet(n_places, n_transitions, W_in, W_out, thresholds=None)
.encode_marking(marking) -> QuantumCircuit
.step(marking, shots=1000) -> np.ndarray # new marking
q_disruption.QuantumDisruptionClassifier¶
QuantumDisruptionClassifier(n_features=11, n_layers=3)
.predict(features: np.ndarray) -> float # risk score [0, 1]
.train(X, y, epochs=10, lr=0.01)
q_disruption_iter¶
ITERFeatureSpec() # 11 ITER disruption features with min/max ranges
normalize_iter_features(raw: np.ndarray) -> np.ndarray # min-max to [0, 1]
generate_synthetic_iter_data(n_samples, disruption_fraction=0.3, allow_synthetic=True) -> (X, y)
from_fusion_core_shot(
shot_data: dict,
allow_center_defaults=False,
allow_density_proxy=False,
) -> (features, label, warnings)
DisruptionBenchmark(n_train=100, n_test=50, allow_synthetic=True).run(epochs=10) -> dict
applications¶
eeg_classification¶
eeg_plv_to_vqe(plv_matrix, natural_frequencies, reps=2, threshold=0.1) -> EEGVQEResult
eeg_quantum_kernel(state_a, state_b) -> float
mitigation¶
compound_mitigation¶
Combines CPDR with Z2 Symmetry Verification for order-of-magnitude noise reduction.
zne¶
zne_extrapolate(noise_scales: list[int], expectation_values: list[float], order: int = 1) -> ZNEResult
dd¶
insert_dd_sequence(circuit: QuantumCircuit, idle_qubits: list[int], sequence: DDSequence = DDSequence.XY4) -> QuantumCircuit
hardware¶
fast_classical¶
High-performance sparse evolution engine. Bypasses circuit overhead; supports N=20 systems.
runner.HardwareRunner¶
HardwareRunner(...)
.connect()
.transpile(circuit) -> QuantumCircuit
.run_sampler(circuits, shots=10000, name="experiment") -> list[JobResult]
The default IBM instance CRN is read via SCPNConfig.ibm_instance
(see config below) when the [config] extra is installed, falling
back to SCPN_IBM_CRN and then the legacy SCPN_IBM_INSTANCE
environment variable otherwise.
backends — plugin registry¶
from scpn_quantum_control.hardware import (
describe_backend, get_backend, list_backends, list_quantum_backends,
register_backend, discover_backends,
)
list_backends() # ['qiskit_ibm', 'pennylane', ...]
backend = get_backend("qiskit_ibm") # BackendProtocol instance
backend.name # 'qiskit_ibm'
backend.is_available() # True iff installed + importable
descriptor = describe_backend("qiskit_ibm")
descriptor.provider # 'ibm_quantum'
descriptor.can_submit # True
descriptor.submit_requires_approval # True
list_quantum_backends() # sorted QuantumBackendDescriptor list
# Third-party plugin — register via entry_points in pyproject.toml
# [project.entry-points."scpn_quantum_control.backends"]
# acme_trapped_ion = "acme_plugin:AcmeBackend"
discover_backends() # scan entry_points group
Every registered backend exposes name: str and is_available() ->
bool. Discovery is lazy; one broken plugin never blocks the rest.
async_runner.AsyncHardwareRunner¶
import asyncio
from scpn_quantum_control.hardware import AsyncHardwareRunner
async def main():
async_runner = AsyncHardwareRunner([runner_a, runner_b], max_concurrent=2)
handles = await async_runner.submit_batch_async(
[circuits_a, circuits_b], shots=4096, name="dla_parity",
)
results = await async_runner.wait_all_async(handles)
asyncio.run(main())
Fans out sampler.run(...) calls across one or more
HardwareRunner instances via asyncio.to_thread. Bounded by an
asyncio.Semaphore; round-robin across runners. Legacy sync
HardwareRunner remains unchanged.
provenance.capture_provenance¶
from scpn_quantum_control.hardware.provenance import capture_provenance
prov = capture_provenance() # returns a JSON-serialisable dict
# prov["git"]["commit"], prov["runtime"]["python"], etc.
Records git commit + branch + dirty flag, Python / package /
scpn_quantum_engine versions, hostname (hashed when
SCPN_ANONYMOUS_HOSTNAME=1, also available via
SCPNConfig.anonymous_hostname), and UTC timestamp.
qec¶
biological_surface_code.BiologicalSurfaceCode¶
Native topological error correction code mapped directly to the hierarchical SCPN coupling graph.
K must be a finite square symmetric zero-diagonal coupling matrix.
threshold must be finite and non-negative; edges with
abs(K[i, j]) >= threshold are data qubits.
biological_surface_code.BiologicalMWPMDecoder¶
Minimum Weight Perfect Matching decoder using biological coupling strengths as distance metrics.
syndrome_x must be a one-dimensional binary vector with length equal
to the number of X stabilizers. Because this graph decoder does not
model rough boundaries, every connected component must have even
syndrome parity; odd component parity raises ValueError instead of
silently discarding an unmatched defect.
control_qec.ControlQEC¶
ControlQEC(distance=3)
.protect_signal(circuit) -> QuantumCircuit
.decode_syndrome(syndrome) -> np.ndarray # correction
config — unified runtime configuration¶
Available via the [config] extra (pydantic-settings).
SCPNConfig¶
from scpn_quantum_control.config import SCPNConfig, get_config, reload_config
cfg = get_config() # process-wide singleton
cfg.anonymous_hostname # bool — SCPN_ANONYMOUS_HOSTNAME
cfg.ibm_instance # str — SCPN_IBM_CRN, fallback SCPN_IBM_INSTANCE
cfg.ibm_backend # str — SCPN_IBM_BACKEND
cfg.ibm_channel # str — "ibm_cloud" | "ibm_quantum"
cfg.ibm_shots # int — default shot count
cfg.gpu_enable # bool — SCPN_GPU_ENABLE
cfg.jax_disable # bool — SCPN_JAX_DISABLE
cfg.result_dir # Path — results directory
cfg.figure_dir # Path — figure output directory
cfg.log_level # "DEBUG"|"INFO"|"WARNING"|"ERROR"|"CRITICAL"
cfg.log_format # "console" | "json"
# Tests that mutate os.environ must call reload_config():
monkeypatch.setenv("SCPN_IBM_SHOTS", "1024")
cfg = reload_config() # discards cached instance
Layered sources, highest priority first:
- Explicit kwargs:
SCPNConfig(ibm_shots=8192). SCPN_*-prefixed environment variables.- A
.envfile in the current working directory. - Pydantic-declared defaults.
logging_setup — structlog bootstrap¶
Available via the [logging] extra (structlog).
from scpn_quantum_control.logging_setup import configure_logging, get_logger
configure_logging() # once, at application start
log = get_logger(__name__)
log.info("submitted_job", backend="ibm_kingston", shots=4096)
configure_logging(level=None, format=None, force=False):
leveldefaults toSCPNConfig.log_level.formatdefaults toSCPNConfig.log_format; non-TTY stderr auto-downgradesconsole→json.force=Truereconfigures even if the same arguments have been applied before.
Stdlib logging.getLogger(...).info(...) calls route through the
same structlog processor chain — ISO-UTC timestamps, log-level tag,
stack info, context-var merging.
accel — multi-language dispatcher¶
The accel/ package exposes compute functions through a
Rust → Julia → Python chain. Rust is the default when the optional
scpn-quantum-engine wheel is installed. Julia kicks in via the
[julia] extra. Python is the always-available correctness floor.
from scpn_quantum_control.accel import (
order_parameter, last_tier_used, available_tiers,
MultiLangDispatcher, dispatch,
)
r = order_parameter(theta) # Rust (default) → Julia → Python
last_tier_used() # 'rust' | 'julia' | 'python' | None
available_tiers() # ['rust', 'julia'] on a full install
# Generic name-based dispatch
r = dispatch("order_parameter", theta)
Custom chain for a new compute function:
from scpn_quantum_control.accel import MultiLangDispatcher
disp = MultiLangDispatcher([
("rust", _rust_impl), # ImportError falls through
("julia", _julia_impl), # RuntimeError falls through
("python", _python_impl), # floor — MUST be last
])
result = disp(arg)
disp.last_tier # which tier served the call
See docs/pipeline_performance.md §"Multi-language accel chain" for
wall-time measurements and docs/language_policy.md for the
ordering rules.