Streaming distributed optical Monte-Carlo with converging sketches and bounded output - #126
Streaming distributed optical Monte-Carlo with converging sketches and bounded output#126Areustle wants to merge 4 commits into
Conversation
Pull the per-event half of RegionGeom.mcintegral into a module-level pure function (geo factor after the separation cut + full weighted contribution after the trigger cut) and have mcintegral call it, keeping only the reduction. Byte-identical output, locked by a regression test whose oracle is a verbatim copy of the pre-refactor math. This gives the streaming sketch and the one-shot integral a single shared definition of the integrand. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New Simulation.Streaming model (batch_size, reservoir_size, rel_unc_target, min_thrown, max_walltime_s with 0 = disabled, reservoir_weighting), same pattern as cherenkov_quadrature: every field defaulted so existing config files load unchanged. Example block added to sample_input_file.toml; defaults, overrides, TOML backward-compat, and validation covered in tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two mergeable, order-independent summaries for the distributed streaming MC: - MomentSketch: additive accumulators (n_thrown, n_valid, s1, s2, s1_geo, n_pass) reproducing mcintegral's outputs exactly -- the integral is a mean of per-event contributions times config-only constants, so batching loses no precision. Variance keeps the legacy quirk: ddof=1 over valid events, divided by total thrown. - WeightedReservoir: Efraimidis-Spirakis bottom-k sample with keys kept in LOG domain (log(u)/w). The direct u**(1/w) underflows to 0 at the ~1e-5 contribution weights this simulation produces, collapsing the sample; the log form is order-equivalent, underflow-free, and scale-invariant. Merging is top-k of the union, so partial reservoirs tree-reduce in any order. Tests: bit-exact single-batch equality vs a real RegionGeom.mcintegral, merge exactness/associativity, KS-uniformity under equal weights, importance bias under contribution weights, and validation error branches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nuspacesim stream <config> [count]: throw events in batches across a process-based dask cluster, merge each batch's sketch + reservoir into running totals behind a live rich panel, and stop on the first of: relative-uncertainty target (with min_thrown warmup), thrown-count backstop (count, default infinity), wall-clock limit, or a spacebar press (graceful: the partial result finalizes normally, STRMSTOP=user_interrupt). Only the weighted reservoir is materialized; sketch results land in the legacy OMCINT/OMCINTGO/ONEVPASS/OMCINTUN header keys plus STRM* provenance. - batch.py run_batch: per-worker cached engines; per-batch RNG from SeedSequence(entropy, spawn_key=(batch_idx,)) so batch i is identical regardless of scheduling -- the reservoir is reproducible even though float-sum merge order isn't. - Parallelism moves up to the batch level: EAS.__call__ gains serial=True (drives CphotAng.run directly, no nested cluster) and BackgroundCluster accepts LocalCluster kwargs (threads_per_worker=1 keeps the legacy global np.random seeding race-free; silence_logs quiets worker teardown noise). - Batch size: --batch-size N for a fixed reproducible schedule, else _AdaptiveBatch grows toward the throughput knee (B/dt signal, EWMA, settle on diminishing gains) and holds. - CLI overrides: --rel-unc, --reservoir-size, --batch-size; --monospectrum/ --powerspectrum/cloud flags mirror `run`. Validated against the one-shot compute(): optical MC integral agrees within 1.22 sigma at 200k thrown (pure MC noise); in-process worker tests lock the run_batch determinism contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
c7f1a1d to
1415cf4
Compare
Regression check against current
|
Summary
Adds a streaming, distributed optical Monte-Carlo path beside the existing one-shot
compute(), exposed as a newnuspacesim streamcommand. Events are thrown in batches across a dask cluster; the MC integral accumulates as an exact additive sketch with live statistics, the run stops when it reaches a target precision, and only a bounded weighted sample of events is written to disk.Motivated by three limits of the fixed-N path: memory grows with
thrown_events(every valid event is retained), the integral's precision is only known after the run completes, and a long run gives no feedback while in flight.compute()and the radio/ToO paths are untouched; this is purely additive.Why the integral streams exactly
Every optical quantity
RegionGeom.mcintegralreports is a linear functional of a per-event contribution scaled by config-only constants:np.sum,np.var,np.count_nonzeroover the events, divided by total thrown.mcnormis config-derived, and bothspec_normandsum_spec_weightsare closed-form constants (the latter despite its name — it is not a sum over the drawn sample). There is therefore no hidden global normalization coupling batches, and the moment accumulators(n_thrown, n_valid, s1, s2, s1_geo, n_pass)merge additively with no loss of precision versus a one-shot run.Validated: streaming vs
compute()optical integral agree within 1.22σ at 200k thrown (ratio 1.017 — pure MC noise).What's in it
simulation/streaming/sketch.pyMomentSketch— exact additive integral accumulatorsimulation/streaming/reservoir.pyWeightedReservoir— Efraimidis–Spirakis bottom-k samplesimulation/streaming/batch.pyrun_batch— the per-batch worker unitsimulation/streaming/driver.pycompute_streaming— sliding-window dask loop, live panel, stop criteriageometry/region_geometry.pymc_contribution()extracted (shared with the one-shot integral)Stop criteria (first to trip): relative-uncertainty target with a
min_thrownwarmup, thrown-count backstop (count, default infinity), wall-clock limit, or a spacebar press — which ends the run early but gracefully, finalizing the partial result rather than aborting like Ctrl-C.Batch sizing adapts to the throughput knee by default;
--batch-size Nfixes it for a reproducible schedule.Notes for review
log(u)/w, not the textbooku**(1/w). Optical contributions are ~1e-5, so1/wis ~1e5 and the direct form underflows to0.0for nearly every event, collapsing the sample. The log form is order-equivalent, underflow-free, and invariant to global weight rescaling. This was a real bug caught by the end-to-end test.EAS.__call__gainsserial=Trueso the optical kernel runs serially inside a worker (a nested cluster there would be pathological); the cluster usesthreads_per_worker=1so the per-batch seeding of the legacy globalnp.randomis race-free.SeedSequence(entropy, spawn_key=(batch_idx,)), so batch i yields the same events and keys regardless of scheduling. The materialized reservoir is reproducible; only the float-sum order in the sketch varies (harmlessly). Adaptive batch sizing is timing-dependent and therefore not bit-reproducible — use--batch-sizewhen that matters.mc_contributionextraction is regression-locked: its test oracle is a verbatim copy of the pre-refactormcintegralbody, asserting bit-exact equality.RegionGeomToOdivides by a fixed time grid — a different streaming mode). Threading explicit RNG generators through the physics stages is also deferred.Verification
RegionGeom.mcintegralis bit-exact on the linear terms; reservoir merge is order-independent, uniform-weight marginals pass a KS test.🤖 Generated with Claude Code