Inference & Deployment¶
Inference in ZeroProofML v0.4 uses strict SCM semantics: no stochastic thresholds and explicit ⊥ outputs for singular inputs.
Runtime Rules¶
- Use a fixed
τ_inferto decide when denominators are treated as singular (|Q| < τ_infer⇒ ⊥). - When you know the training-time margin
τ_train(typicallyτ_train > τ_infer), you can detect the training–inference gap regionτ_infer ≤ |Q| < τ_trainand treat it as numerically risky at deployment time (e.g., log it, trigger fallbacks, or tightenτ_infer). - No gradient policies are applied; forward behaviour matches the strict SCM decode rule used by
zeroproof.inference.mode.strict_inference. - Encode ⊥ as
nanwhen bridging to IEEE-754 viautils.ieee_bridge.to_ieee.
Exporting Models¶
- TorchScript (deprecated in recent PyTorch): use
zeroproof.inference.script_module(model)(wrapstorch.jit.script) if you still rely on TorchScript; otherwise prefer ONNX. - ONNX: use
zeroproof.inference.export_onnx_model(...)orzeroproof.inference.export_bundle(...). - Checkpoints: saved via
SCMTrainer.save_checkpoint, compatible with both SCM-only and projective graphs.
Bundle validation¶
export_bundle(...) writes a minimal bundle:
model.onnxmetadata.json(includestau_infer, optionaltau_train, and the strict output contract)
The strict inference output contract is versioned via strict_inference_schema_version in metadata.json.
Validate a bundle before shipping:
from zeroproof.inference import validate_bundle
validate_bundle("path/to/bundle_dir")
Reference deployment (robotics RR IK)¶
An end-to-end reference path (train → bundle → strict inference → fallback → report) is provided as:
python scripts/reference_robotics_deployment.py --device cpu --epochs 2 --n-samples 6000
It writes a self-contained run directory under results/reference_deploy_robotics/ including bundle/ and VALIDATION_REPORT.md.
Pattern: strict gate + direction head (censoring/regimes)¶
For 3-way censoring problems (below / in-range / above), you can combine:
1) strict bottom gating via |Q| < τ_infer; and
2) a 2-class direction head to disambiguate which censored regime applies when the sample is bottom.
import torch
from zeroproof.inference import decode_strict_censored_3way
# Given projective head outputs (P, Q) and optional direction logits.
decoded, bottom_mask, class_id = decode_strict_censored_3way(
P.squeeze(-1),
Q.squeeze(-1),
tau_infer=1e-6,
direction_logits=dir_logits, # shape (B, 2); omit to fall back to sign(P)
)
Monitoring + fallbacks¶
At deployment time, treat masks as authoritative and log them explicitly:
from zeroproof.inference import StrictInferenceMonitor, reject_on_gap
decoded, bottom_mask, gap_mask = wrapped(x) # strict outputs
decoded_safe, accept_mask = reject_on_gap(decoded, bottom_mask, gap_mask)
mon = StrictInferenceMonitor(bundle_id="my_bundle")
mon.update(bottom_mask, gap_mask)
rates = mon.rates()
Choosing τ_infer (post-hoc sweep)¶
If your strict gate is of the form |Q| < τ_infer, you can evaluate safety trade-offs post-hoc by sweeping τ_infer over cached |Q| values:
from zeroproof.metrics import tau_infer_sweep_from_q_abs
# q_abs: cached |Q|, is_in_range: boolean ground-truth mask
curves = tau_infer_sweep_from_q_abs(q_abs, is_in_range=is_in_range, taus=[1e-6, 1e-5, 1e-4])
Use write_tau_infer_sweep(...) to save both a JSON payload and a compact Markdown report.
Typical trade-off: increasing τ_infer reduces false in-range predictions on truly censored points, but can increase false censoring on truly in-range points.
Named operating points¶
These are named deployment presets for selecting thresholds + fallback behavior. They are not magic values — use a post-hoc sweep (or a held-out calibration set) to set the actual τ_infer/τ_train for your domain.
safety_first¶
Goal: minimize unsafe accepts by rejecting both strict-bottom and gap-region samples.
from zeroproof.inference import InferenceConfig, reject_on_gap, safe_sentinel, strict_inference
cfg = InferenceConfig(tau_infer=1e-4, tau_train=1e-3)
decoded, bottom_mask, gap_mask = strict_inference(P, Q, config=cfg)
decoded, accept_mask = reject_on_gap(decoded, bottom_mask, gap_mask)
decoded = safe_sentinel(decoded, ~accept_mask, sentinel=float("nan"))
Trade-off: lower false-finite-on-censored (or “unsafe accept”) at the cost of higher rejection / false-censoring rates.
direction_aware¶
Goal: when bottoms happen, keep the censored direction meaningful by using an auxiliary direction head.
from zeroproof.inference import decode_strict_censored_3way
decoded, bottom_mask, class_id = decode_strict_censored_3way(
P.squeeze(-1),
Q.squeeze(-1),
tau_infer=1e-6,
direction_logits=dir_logits, # (B, 2)
)
Trade-off: adds an extra head (and training/eval complexity), but produces more actionable outputs on censored / singular inputs.
accuracy_first¶
Goal: maximize coverage by keeping τ_infer tight and treating the gap region as “monitor-only”.
from zeroproof.inference import StrictInferenceMonitor, reject_on_bottom
decoded, bottom_mask, gap_mask = wrapped(x)
decoded, accept_mask = reject_on_bottom(decoded, bottom_mask)
mon = StrictInferenceMonitor(bundle_id="my_bundle")
mon.update(bottom_mask, gap_mask) # log rates; do not reject gap by default
Trade-off: higher coverage and in-range accuracy when things are well-behaved, but more risk exposure near the training–inference gap unless you monitor and gate carefully.
Safety Checklist¶
- Validate coverage on a held-out set using
losses.coverage.coveragebefore shipping. - Monitor the rate of ⊥ outputs in production; aggressive rejection loss during training usually lowers this.
- For robotics or control, ensure sign consistency loss was active so orientation of infinities is preserved.