Digital twin plugin (twin)¶
A digital twin of a lab is the engine model of it, fitted to the lab's measurements, with an honest statement of what it can
predict. The twin plugin is the project layer on top of the fitting plugin: a folder layout, the multi-condition fit,
validation against held-out conditions with credible bands, a planner for the next measurement, a what-if explorer and drift tracking.
It is Python (ultrafast_twin, NumPy only) plus one command, ultrafast twin.
Examples: build-the-twin and explore-the-twin.
Project layout¶
lab/
twin.json the project: system, run, instrument, sigma, conditions, parameters
system.ufs what you believe the lab is
data/*.csv one file per condition (the instrument's reading)
fits/<name>/ fit.json, fitted-system.ufs, fitted-run.json, fitted-conditions.json (latest.txt names the last fit)
reports/ validation.json, validation.md
goals.json optional: checks a good twin must pass
twin.json keys: system (path), run (run spec with the instrument), instrument (name of the instrument that reads the data),
sigma (noise as a fraction of the peak), conditions[] (name, variables, set, data, heldOut), parameters[] (paths with
initial, min, max, optionally a dependence on a condition variable), fit (solver options). A condition sets values
("set": {"pulse/energyJ": 1.2e-6}) in the system or the run for that measurement. The dependences, surfaces and networks come from the
fitting plugin.
Commands¶
ultrafast twin init lab [--template synthetic-lab | synthetic-lab-saturating] [--force]
ultrafast twin fit lab [--name NAME]
ultrafast twin validate lab [--samples 12] [--json]
ultrafast twin predict lab --var energyJ=2e-6 --set pulse/energyJ=2e-6
ultrafast twin design lab --variable energyJ --values 0,0.5e-6,1e-6,2e-6 --set-key pulse/energyJ # or --placement
ultrafast twin track lab --data day1.csv --data day2.csv --var energyJ=1.2e-6 --process filter.centerNm=0.3
ultrafast twin check lab [--goals goals.json] | --benchmark
ultrafast twin starts python -m ultrafast_twin. It uses ULTRAFAST_PYTHON as the interpreter (else python3, or python on Windows):
point it at a Python that has NumPy and the ultrafast package (in a source checkout also set PYTHONPATH=python/src).
ultrafast twin serve (the MCP server for agents) is not implemented; it exits with code 2.
Python¶
import ultrafast as uf
uf.plugins.load_python_plugins()
import ultrafast_twin as tw
proj = tw.init("lab", "synthetic-lab") # or tw.TwinProject("lab") for an existing project
twin = proj.fit("fit") # the multi-condition fit; returns a Twin
report = twin.validate() # verdict, per-condition chi2 and coverage, flags; writes reports/
p = twin.predict(variables={"energyJ": 2e-6}, set={"pulse/energyJ": 2e-6}, n_samples=12) # x, y, y_sigma (1 sigma band), scalars
w = twin.whatif({"1 uJ": {...}, "3 uJ": {...}}) # paired draws: the difference has a proper uncertainty
d = twin.design(ultrafast_twin.design.energy_candidates(twin, "energyJ", [...], set_key="pulse/energyJ"))
trk = twin.tracker(parameters=["filter.centerNm"], process_sigma={"filter.centerNm": 0.3}); trk.update("day3.csv", variables=..., set=...)
Validation rules¶
Stated in ultrafast_twin/validate.py, not tuned. rt = max(1, reduced chi² of the training conditions). A held-out condition is
flagged when its chi² per point exceeds 3 rt or fewer than 80 % of its points lie within 2 sigma of the credible band combined with
the stated noise; marginal above 1.5 rt. The verdict is unvalidated (no held-out condition), fits-training-only (any held-out
condition flagged), marginal or ok. Credible bands come from draws of the fit covariance (the Jacobian-based one of the fitting
plugin), so they cover the parameters the data constrain and nothing else: a model that is wrong is not made right by a band.
Planner, what-if, tracking¶
- Information gain of a candidate measurement:
0.5 (logdet(F + J^T J / sigma^2) - logdet F)for the fit's information matrixF(covariance inverse) and the candidate's JacobianJ(finite differences through the engine).picks > 1selects greedily, each pick updating the covariance. Candidates: values of a condition variable, or the instrument at each element (placement_candidates). - What-if simulates each variant with the same parameter draws, so
deltaSigmais the sigma of the paired difference. - Drift tracker: an ensemble Kalman filter on the chosen parameters. Each measurement adds random-walk process noise (
process_sigma, in the parameter's unit, default the fitted sigma timesdrift) and inflation, then updates from the recorded file through the engine.driftedlists parameters that have moved more thanalarm(3) fitted sigma from the fit.
Goal files and the benchmark¶
goals.json lists checks an agent or a CI job can run (ultrafast twin check lab): validationVerdict, parameter,
parameterSigma, heldOutChi2, coverage, prediction. ultrafast twin check --benchmark runs three design tasks whose answers follow from the
construction of the synthetic lab (placement; a phase proportional to the energy is invisible at zero energy; saturation is flagged).
What was measured (S4 notes)¶
init+fiton the synthetic lab: 1.6 s, 135 runs. Recovered centre 1033.008 nm (truth 1033), FWHM 8.497 nm (8.5), slope 2.9998 rad/µJ (3.0).- Credible-band coverage inside 2 sigma: 94-98 % on all seven conditions.
- The saturating lab: held-out chi² per point 11.9 and 180: verdict
fits-training-only. - Information on a nonlinear-phase slope is not monotonic in energy: when the fringes become finer than the spectrometer resolution (12 rad at 4 µJ against 0.2 nm), the linearised information drops. A zero-energy shot pins the filter parameters the slope is correlated with and shrinks its uncertainty most in the synthetic lab.
- A tracker needs the expected drift in physical units: at the fitted sigma (0.006 nm) a 0.5 nm shift was followed only 17 % in three
steps; with
process_sigma0.3 nm it followed in one update.
Limits and not built¶
- The MCP server for agents (T6: eight workspace-scoped tools, user expressions refused, time and memory budgets) is designed (design record) and not built.
- The only project template is the synthetic spectral lab; a spatial (wavefront) template does not exist.
- Gradients through the engine are finite differences (one run per parameter and condition); the network transform needed 1050 runs in its test.