Skip to content

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-017 DegradedSensorStream engine itself.
  • σ = 0 collapses 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.pymif-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 distribution N(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 = 0 channels 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.