Closed-Loop Control Analysis¶
SPDX-License-Identifier: AGPL-3.0-or-later
scpn_quantum_control.control.closed_loop_analysis turns the response of the
measurement-feedback synchronisation controller
(control.realtime_feedback.RealtimeSyncFeedbackController) into a
control-theoretic verdict and gates any non-simulation execution behind an
explicit policy.
The controller measures a finite-shot order-parameter estimate each round and adjusts the coupling scale toward a set-point. This module reads the resulting trajectory — the exact statevector order parameter as the controlled output, the sampled estimate as the feedback signal — and reports how well the loop tracked the set-point.
It is a local software-in-the-loop assessment: not provider-prepared dynamic-circuit evidence and not live closed-loop QPU evidence. Hardware execution is refused fail-closed without an explicit live ticket.
Response classification¶
analyse_closed_loop_response returns a ResponseClass and a
ControlPerformance:
| verdict | meaning |
|---|---|
converged |
the response settles inside the tolerance band with small steady-state error |
limit_cycle |
the response oscillates persistently around the set-point without settling |
diverged |
the steady-state error is worse than the early-window error |
unsettled |
still transient at the end of the horizon |
ControlPerformance carries the steady-state error, settling round, overshoot
(relative to the initial gap), integral absolute error, late-window oscillation
amplitude, and trailing error sign-change count.
from scpn_quantum_control.control import analyse_closed_loop_response
verdict, performance = analyse_closed_loop_response(response, target=0.75, tolerance=0.05)
Execution policy¶
evaluate_closed_loop_policy is fail-closed: it authorises a simulation run by
default, and authorises hardware only when the policy sets allow_hardware,
carries a non-empty live_ticket, and the requested backend is on the
allow-list. The round budget is enforced in both modes.
from scpn_quantum_control.control import ClosedLoopExecutionPolicy, evaluate_closed_loop_policy
policy = ClosedLoopExecutionPolicy() # simulation-only
decision = evaluate_closed_loop_policy(policy, requested_rounds=32)
decision.mode # ExecutionMode.SIMULATION
Replayable run¶
run_closed_loop_control runs the controller under the policy, classifies the
response, and returns a deterministic ClosedLoopControlEvidence record (same
seed → same trajectory) with a claim boundary in its provenance.
from scpn_quantum_control.control import run_closed_loop_control
evidence = run_closed_loop_control(controller, n_rounds=32, seed=0)
evidence.classification # ResponseClass verdict
evidence.performance # control-theoretic metrics
evidence.decision.mode # simulation unless a live ticket authorises hardware
Latency budget¶
measure_closed_loop_latency_budget adds a no-submit latency gate around the
same controller contract. By default it measures each local feedback round with
time.perf_counter_ns; CI and replay fixtures may pass
observed_round_latencies_s to validate a stored profile without relying on
workstation timing.
from scpn_quantum_control.control import (
ClosedLoopLatencyBudget,
measure_closed_loop_latency_budget,
)
budget = ClosedLoopLatencyBudget(
max_round_latency_s=0.050,
p95_round_latency_s=0.040,
p99_round_latency_s=0.045,
max_total_latency_s=1.500,
)
latency = measure_closed_loop_latency_budget(controller, 32, budget=budget, seed=0)
latency.passes
latency.to_dict()
The report records per-round samples, max/p95/p99/total latency, policy authorisation, response classification, and blockers. A failed policy decision or latency-budget breach is evidence, not a provider submission.
Publication package¶
build_closed_loop_publication_package creates a structured scaffold for the
future paper/control campaign. It separates three evidence classes:
| evidence class | status boundary |
|---|---|
software_in_loop_simulation |
available from the local controller and latency gate |
provider_prepared_dynamic_circuit |
placeholder until backend capability and provider-preparation artefacts exist |
live_closed_loop_qpu |
blocked until a live ticket, allow-listed backend, raw counts, calibration snapshot, job IDs, and replay harness exist |
from scpn_quantum_control.control import build_closed_loop_publication_package
package = build_closed_loop_publication_package(latency_report=latency)
package.to_dict()
package.to_markdown()
Claim boundary¶
The evidence is software-in-the-loop only: the controlled output is the exact statevector order parameter and the feedback signal is the finite-shot estimate. The latency gate measures local controller/simulator wall-clock time only. It is not provider-prepared dynamic-circuit evidence and not live closed-loop QPU evidence.