Skip to content

Surrogates Python API

The full surrogate library lives in linac_gen.surrogates. Five small modules cover training, persistence, registration, comparison, and the per-element surrogate class itself.

Training

The one-shot orchestrator:

train_surrogate_for_element(...) signature

Arg Type Required Notes
element FieldMapElement source whose fitted_matrix(ref) provides ground truth
ref_template reference particle cloned per sample; only w_kin is varied
n_samples int LHS sample count
ref_w_kin_range (lo, hi) MeV scope on incoming kinetic energy
param_ranges dict[str, (lo, hi)] element attributes to sweep
out_dir path where to save weights.pt + metadata.json
hidden_dims tuple of int (128,128,128) MLP hidden layer sizes
activation "silu"/"tanh"/"relu" "silu" smooth = better Jacobians (M7-friendly)
epochs int 200 training epochs
lr float 1e-3 Adam learning rate
batch_size int 256 minibatch size
val_frac float 0.2 hold-out split for val MAPE
seed int 42 LHS + train RNG seed
lattice_hash str "unknown" recorded in metadata
element_key str / None None defaults to element.name
verbose bool False progress prints
n_workers int / None None ≥2 enables multiprocessing.spawn; output bit-identical to serial for fixed seed

Building blocks (use if you need finer control)

Loading and discovery

The runtime registry

The envelope hook in tracking/envelope.py:_fitted_matrix_slice_at looks up surrogates by element name on every matrix call. Register to engage, unregister to disengage.

The registry is process-global state. In a long-running process that runs many comparisons, clear between runs to avoid stale surrogates from a previous lattice contaminating the current one.

Comparing envelopes

CompareReport fields

Attribute Type Meaning
s ndarray arc length (mm)
sigma_baseline / sigma_surrogate dict[str, ndarray] keys "x", "y", "phi", "w"
end_of_line dict[str, float] sigma_{k}_base, sigma_{k}_surr, rel_diff_{k}
wall_baseline_s / wall_surrogate_s float seconds
scope_ok bool True if every query was in scope (OOD silently falls back)
surrogate_names list[str] names engaged this run
notes list[str] warnings (e.g. "no surrogate engaged")
speedup() method wall_baseline_s / wall_surrogate_s
worst_rel_diff() method max of the four end-of-line rel.diffs
summary_text() method multi-line CLI-friendly summary

compare_envelope preserves the global registry state — whatever was registered before the call is restored after, so it's safe to call in a long-running session.

Comparing multi-particle runs

The MP analogue of compare_envelope runs the multi-particle tracker twice — a pure-RK4 baseline (registry temporarily cleared, MP engagement off), then the hybrid linear-anchor + RK4-residual path with the surrogates registered — and diffs the two:

CompareMpReport mirrors CompareReport but for MP quantities: s, sigma_baseline / sigma_surrogate (keys "x", "y", "z"), emit_baseline / emit_surrogate (keys "nx", "ny"), transmission_baseline / transmission_surrogate, end_of_line rel-diffs (σ_x/σ_y/σ_z/ε_nx/ε_ny/transmission), wall times, residual_n_steps, plus the same speedup() / worst_rel_diff() / summary_text() helpers. Like the envelope version, compare_mp restores the whole registry + MP-engagement state afterwards, and each surrogate's own residual_n_steps setting is restored too.

Drop-in inference

When you've already registered surrogates, normal HELIX code paths (envelope solver, matcher, parameter scans) pick them up automatically — no changes to your scripts needed.

For ad-hoc inference outside the envelope solver, call surr.fitted_matrix(ref) directly:

Out-of-scope handling

A surrogate is queried with an input vector [w_kin, beta, gamma, *param_values]. Each component must lie within the metadata's scope bounds; otherwise:

Programmatic check before calling:

Cross-references

CLI · Continue to Training guide →