Probabilistic Trigger Propagation¶
Per-sample P(lock) and P(envelope violation) for the merge trigger,
propagated analytically from the MIF-017 sensor noise model — no Monte-Carlo
sampling in the runtime path. Given a nominal kinematic trace and the
additive-Gaussian noise scales of the three scalar observables (phase-lock
error, reference error, axial separation), the propagation returns the
trigger's stated operating point: fire_probability,
abort_unsafe_probability, and hold_probability under the streaming
precedence (a violation at sample k beats a lock at sample k), replacing
bare thresholds with quantified false-fire and missed-window rates.
from scpn_mif_core import (
KinematicSafetySpec,
MeasurementNoiseSpec,
MergeWindowSpec,
dispatched_trigger_probabilities,
)
noise = MeasurementNoiseSpec(
phase_lock_error_sigma_rad=2.0e-3, # MIF-017 phase_lock_error_rad channel
reference_error_sigma_m=4.0e-4,
separation_sigma_m=3.0e-4,
)
trace = dispatched_trigger_probabilities(
MergeWindowSpec(phase_tolerance_rad=0.05, spatial_tolerance_m=0.01),
KinematicSafetySpec(tolerance_m=0.02),
noise,
phase_lock_errors_rad, # nominal per-sample observables
reference_errors_m,
separations_m,
)
print(trace.fire_probability, trace.abort_unsafe_probability, trace.hold_probability)
MeasurementNoiseSpec.from_noise_spec binds the scales directly to a MIF-017
NoiseSpec by channel name and fails closed when a channel is missing.
Model, stated exactly¶
- Noise enters as additive white Gaussian noise on the derived scalar observables — the linearised propagation. Dropout and timestamp jitter remain campaign-level MIF-017 concerns.
- The per-sample candidate probability is
Φ((φ_tol − φ_k)/σ_φ)·Φ((x_tol − x_k)/σ_x); P(lock by sample k) follows from an exact forward recursion over the consecutive-streak Markov states (exact for white noise — pinned against brute-force enumeration over all candidate outcome sequences). - Per-step envelope hazards use the one-step slack distribution
N(slack_k, σ_s²·(1 + c²)); the cumulative violation probability multiplies per-step survivals under a documented independence approximation (consecutive slacks share a sample's noise). The Monte-Carlo calibration test bounds the approximation against the deterministic MIF-017DegradedSensorStreamengine itself. σ = 0collapses every probability to the exact deterministic indicator, reproducing the monitor and certificate verdicts on the nominal trace.- The normal CDF is
erfc(−z/√2)/2, keeping full relative accuracy in both tails, so quoted false-fire rates stay meaningful at the 1e-9 level.
Backends¶
dispatched_trigger_probabilities follows bench/dispatch.toml
(kinematic.trigger_probability): the Rust kernel via a zero-copy column
boundary (read-only NumPy views in, per-sample probability columns out as
NumPy arrays) when the extension is available, with the pure-Python reference
as the guaranteed floor. Parity is bit-exact — both backends implement the
identical operation sequence, including a shared vendored fdlibm erfc
(kinematic/_erfc.py ↔ mif-kinematic/src/erfc.rs; the platform
implementations genuinely differ by an ulp on real inputs, so neither is
called), and every probability is asserted equal with no tolerance in
tests/unit/kinematic/test_trigger_probability_rust_parity.py.
Measurements live in bench/results/trigger_probability.json; the notes there
record that the per-sample tuple boundary was measured first and lost the
4096-sample group to object-conversion overhead before the boundary was
flipped to columns.
API¶
trigger_probability
¶
Per-sample P(lock) / P(envelope violation) for the merge trigger.
Given a nominal kinematic trace (the per-sample phase-lock error, reference-position error, and axial separation the monitor would see with perfect sensors) and the additive-Gaussian component of the MIF-017 sensor noise model, this module propagates measurement uncertainty through the MIF-003 merge-window decision law and the MIF-011 sampled safety envelope:
candidate_lock_probability— the probability a noisy sample satisfies both merge-window tolerances,Phi((phi_tol - phi_k)/sigma_phi)*Phi((x_tol - x_k)/sigma_x).lock_probability— P(sustained lock achieved by sample k), computed by an exact forward recursion over the consecutive-streak Markov states (exact for white per-sample noise; no sampling error).violation_probability— the per-step probability the measured separation breaks the envelope, using the one-step slack distributionN(slack_k, sigma_s^2*(1 + c^2)).- trace-level
fire_probability/abort_unsafe_probability/hold_probability— the trigger's stated operating point under the streaming precedence (a violation at sample k beats a lock at sample k), replacing bare thresholds with quantified false-fire and missed-window rates.
Model scope, stated exactly:
- Noise enters as additive white Gaussian noise on the derived scalar observables (phase-lock error, reference error, separation) — the linearised propagation named in the roadmap. Dropout and timestamp jitter stay campaign-level MIF-017 concerns and are not propagated here.
- The three observables carry independent noise channels; phase/reference noise is independent of separation noise, so the lock and violation processes factorise exactly.
- Consecutive one-step slacks share the separation noise of their common sample, so the cumulative violation probability multiplies per-step survivals under a documented independence approximation; the per-step hazards themselves are exact under the linearised model. The calibration tests bound the approximation error against the deterministic MIF-017 Monte-Carlo noise engine.
- Degenerate
sigma = 0channels reduce every probability to the exact deterministic indicator, reproducing the monitor and certificate verdicts bit-for-bit on the nominal trace.
The trace aggregates assume an armed, bank-feasible session: the arm and
bank-ready wires are deterministic gates that relabel the outcome (a
FIRE becomes ABORT_BANK_INFEASIBLE or a hold) without changing the
lock or violation probabilities.
MeasurementNoiseSpec(phase_lock_error_sigma_rad, reference_error_sigma_m, separation_sigma_m)
dataclass
¶
Additive-Gaussian sensor noise on the trigger's scalar observables.
Parameters¶
phase_lock_error_sigma_rad: Standard deviation of the measured phase-lock error, in radians. reference_error_sigma_m: Standard deviation of the measured reference-position error, in metres. separation_sigma_m: Standard deviation of the measured axial separation, in metres.
All sigmas must be finite and non-negative; a zero sigma declares that channel noiseless and collapses its probabilities to exact indicators.
__post_init__()
¶
Validate finite, non-negative noise scales.
from_noise_spec(noise, *, phase_channel='phase_lock_error_rad', reference_channel='reference_error_m', separation_channel='separation_m')
classmethod
¶
Build the spec from a MIF-017 per-channel Gaussian :class:NoiseSpec.
Parameters¶
noise: The MIF-017 stress-injection noise specification. phase_channel, reference_channel, separation_channel: Channel names to read the observable sigmas from.
Raises¶
ValueError If any named channel is absent — the mapping fails closed rather than silently assuming a noiseless channel.
TriggerProbabilitySample(sample_index, candidate_lock_probability, lock_at_sample_probability, lock_probability, violation_probability, cumulative_violation_probability, fire_at_sample_probability)
dataclass
¶
Per-sample propagated probabilities.
Attributes¶
sample_index:
Zero-based sample index, matching the streaming trigger.
candidate_lock_probability:
Probability the noisy sample satisfies both merge-window tolerances.
lock_at_sample_probability:
Probability the sustained lock is first achieved exactly here.
lock_probability:
Probability the sustained lock has been achieved by this sample.
violation_probability:
Probability this sample's envelope check trips (initial margin at
sample 0, one-step slack afterwards).
cumulative_violation_probability:
Probability any envelope check up to this sample tripped, under the
documented per-step independence approximation.
fire_at_sample_probability:
Probability the streaming trigger latches FIRE exactly here: the
lock arrives now and no envelope check through this sample tripped.
TriggerProbabilityTrace(samples, lock_probability, violation_probability, fire_probability, abort_unsafe_probability, hold_probability)
dataclass
¶
Trace-level propagated probabilities and the trigger operating point.
Attributes¶
samples:
Per-sample propagated probabilities.
lock_probability:
Probability the sustained lock is achieved anywhere on the trace.
violation_probability:
Probability any envelope check on the trace trips.
fire_probability:
Probability the streaming trigger fires (first lock strictly before
any violation, violation winning same-sample ties).
abort_unsafe_probability:
Probability the trigger latches ABORT_UNSAFE instead of firing.
hold_probability:
Probability the trace ends with neither a fire nor a violation.
propagate_trigger_probabilities(merge_window, safety, noise, phase_lock_errors_rad, reference_errors_m, separations_m)
¶
Propagate sensor noise through the merge-trigger decision law.
Parameters¶
merge_window: MIF-003 merge-window tolerances and debounce streak. safety: MIF-011 sampled safety envelope parameters. noise: Additive-Gaussian noise scales for the three scalar observables. phase_lock_errors_rad, reference_errors_m, separations_m: The nominal per-sample observables, equal-length one-dimensional arrays with at least one sample. Separations are folded to absolute values exactly as the certificate does.
Returns¶
TriggerProbabilityTrace Per-sample probabilities plus the trace-level operating point.
Raises¶
ValueError If any trace is empty, non-finite, or of unequal length.
trigger_probabilities_from_trace(trace, merge_window, safety, noise)
¶
Propagate sensor noise along an already-evaluated nominal trace.
Parameters¶
trace:
The nominal :class:MergeWindowTrace whose per-sample observables
(phase-lock error, reference error, separation) seed the propagation.
merge_window, safety, noise:
As for :func:propagate_trigger_probabilities.
Returns¶
TriggerProbabilityTrace Per-sample probabilities plus the trace-level operating point.