Repeated calibration
Fit independent instrument datasets with one compiled regression model. Each instrument can have a different number of readings. Stable IDs keep each fit's random stream tied to its instrument, so reordering the batch does not change any instrument's result.
This does not pool instruments: every fit is independent and learns nothing from
its neighbours. Use examples/site_effects.py when group estimates should share
information. Chunked batch dispatch currently retains inputs and fits; see the
roadmap for bounded streaming.
Run it with python examples/repeated_calibration.py from the repository root. The blocks below are
cells of that one file and share its state, so they assume the blocks above
them have run.
Three instruments with different sample counts
rng = np.random.default_rng(42)
datasets = []
ids = []
for i, rows in enumerate([40, 75, 120]):
x = np.linspace(-2, 2, rows)
y = 0.1 * i + (1.0 + 0.05 * i) * x + rng.normal(0, 0.2, rows)
ids.append(f"instrument-{i}")
datasets.append({"x": x, "y": y})
for name, dataset in zip(ids, datasets):
print(f"{name}: {len(dataset['x'])} readings")
One compiled model, many datasets
calibration_model() comes from examples/instrument_calibration.py: an
offset, a gain and a noise scale, fitted to y ~ Normal(offset + gain * x, noise).
Compiling once and calling sample_batch builds the graph structure once and
reuses it for every dataset. Each fit still validates its own binding and lays
out its own evaluator buffers, because those depend on the data's shapes: what
is saved is the model construction, not the per-fit setup.
errors="collect" keeps one bad dataset from losing the whole batch: the failing
ID lands in batch.errors and the rest still return fits.
Fit the batch
batch = calibration_model().sample_batch(
datasets,
ids=ids,
chains=4,
warmup=1000,
draws=1000,
threads=2,
seed=42,
errors="collect",
show_progress=False,
)
for name in batch.ids:
print(f"===== {name}")
if name in batch.errors:
print(batch.errors[name])
else:
print(batch.get(name).summary())
===== instrument-0
4 chains × 1000 draws per chain
Parameter mean std hdi_3% hdi_97% ess_bulk ess_tail r_hat mcse_mean
──────────────────────────────────────────────────────────────────────────────────────────────
offset 0.0076 0.0272 -0.0438 0.0583 3438 2690 1.0009 0.000468
gain 1.0161 0.0232 0.9726 1.0613 3943 2641 1.0024 0.000371
noise 0.1720 0.0213 0.1324 0.2108 3238 2606 1.0012 0.000380
──────────────────────────────────────────────────────────────────────────────────────────────
Mean accept rate: 0.93 │ Divergences: 0
===== instrument-1
4 chains × 1000 draws per chain
Parameter mean std hdi_3% hdi_97% ess_bulk ess_tail r_hat mcse_mean
──────────────────────────────────────────────────────────────────────────────────────────────
offset 0.0737 0.0177 0.0405 0.1078 4043 2687 1.0009 0.000280
gain 1.0202 0.0150 0.9902 1.0465 3903 2972 1.0011 0.000241
noise 0.1513 0.0127 0.1290 0.1760 3685 2957 1.0009 0.000211
──────────────────────────────────────────────────────────────────────────────────────────────
Mean accept rate: 0.91 │ Divergences: 0
===== instrument-2
4 chains × 1000 draws per chain
Parameter mean std hdi_3% hdi_97% ess_bulk ess_tail r_hat mcse_mean
──────────────────────────────────────────────────────────────────────────────────────────────
offset 0.2031 0.0182 0.1694 0.2368 3871 2985 0.9999 0.000293
gain 1.1013 0.0157 1.0723 1.1305 3923 3110 1.0004 0.000250
noise 0.2012 0.0131 0.1777 0.2259 3533 2869 1.0000 0.000224
──────────────────────────────────────────────────────────────────────────────────────────────
Mean accept rate: 0.92 │ Divergences: 0
Reading the result
The true offsets are 0.0, 0.1 and 0.2 and the true gains 1.00, 1.05 and 1.10. The instrument with 40 readings has the widest intervals, as it should: fewer readings, less information, and no borrowing from the other two.
Interval width does not fall monotonically with sample size here. Instrument 2 has 120 readings against instrument 1's 75 and still reports slightly wider intervals. The counts are fixed at 40, 75 and 120; the noise scale is not, and instrument 2's came out higher on this simulated draw. Width falls with the count and rises with the noise, so a larger instrument can still be the less precisely measured one.
Check r_hat and ess_bulk per instrument before comparing them. A batch
reports diagnostics per fit for exactly this reason -- one instrument can fail
to converge while the others are fine.