Skip to content

FE-1263: Add lazily-computed parameter sweeps to Petrinaut experiments - #9093

Draft
kube wants to merge 2 commits into
mainfrom
cf/fe-1263-experiment-parameter-ranges
Draft

FE-1263: Add lazily-computed parameter sweeps to Petrinaut experiments#9093
kube wants to merge 2 commits into
mainfrom
cf/fe-1263-experiment-parameter-ranges

Conversation

@kube

@kube kube commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

🌟 What is the purpose of this PR?

Experiments currently force a single fixed value per scenario parameter. This prototypes the "inputs as ranges" direction from the Experiments & metrics RFC (FE-1045 §4.4): scenario parameters can be swept across a range of values, expanding into a grid of parameter combinations — and the grid is computed lazily, driven by what the user looks at. A seed pass gives every combination one run for an instant overview; after that, the combinations matching the currently-viewed parameter selection accumulate runs in progressively larger batches (1 → 10 → 50 → 100 → 500 → 1000 …) up to the requested run count. The results drawer's parameter navigator (toggle + slider per ranged parameter) doubles as the scheduler's focus, so exploring the metric space is instant (histogram merging) while compute concentrates exactly where the user is looking.

🔗 Related links

🔍 What does this change?

  • Create-experiment drawer: each non-boolean scenario parameter gets a Fixed / Range selector. Range mode takes min, max, and the number of evenly spaced values (endpoints included; integer parameters round and reject ranges that collapse onto duplicate values), with a live preview and a N combinations × R runs = T total runs summary — a budget, not an upfront cost. Hard cap of 200 combinations, warning above 50.
  • parameter-grid.ts (new): range expansion, cartesian-grid construction, exact histogram merging (mergeMetricFramesAcrossCells), the run ladder (getNextRunTarget: 1, 10, 50, 100, 500, 1000, then ×5/×2), and pickNextRefinementCell (random among the combinations with the fewest runs, keeping merged distributions an evenly-weighted mixture).
  • ExperimentsProvider: two orchestrations behind one interface. Range-less experiments keep the existing eager single-batch behaviour. Sweeps get a lazy scheduler: seed pass over all combinations (1 run each, always completes), then focus-driven refinement through a concurrency-capped worker pool. Batches accumulate per combination — batch seeds derive from the experiment seed plus the combination's accumulated run count, keeping streams distinct while combinations at equal progress stay paired (common random numbers). Moving the selection interrupts out-of-view batches (partial runs discarded, accumulated runs kept) with a 250 ms debounce so slider drags don't thrash workers; closing the drawer pauses refinement; each combination stops at the requested run count; a failing combination stops the sweep.
  • View drawer: the parameter navigator's selection is reported to the provider as the run focus (setExperimentRunFocus). Charts merge accumulated + live in-flight frames of the matching combinations, a feedback line shows N combinations in view · X of Y runs accumulated (adding runs… / at target), and the Summary shows accumulated runs against the total budget. New Idle status (badge + docs) for sweeps whose refinement is paused; Cancel now also applies to idle sweeps and permanently stops accumulation.
  • Types: CreateExperimentInput.scenarioParameterValues takes fixed-or-range inputs; ExperimentRecord gains parameterAxes and per-combination cells (with runsCompleted and inFlightMetricFrames); the context gains setExperimentRunFocus. Changeset included (minor).
  • Docs (experiments.md) describe the lazy scheduling, the Idle status, and the navigator; story fixtures and a "Parameter sweep" Storybook story included.

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

  • modifies an npm-publishable library and I have added a changeset file(s)

📜 Does this require a change to the docs?

The changes in this PR:

  • require changes to docs which are made as part of this PR

🕸️ Does this require a change to the Turbo Graph?

The changes in this PR:

  • do not affect the execution graph

⚠️ Known issues

  • This is a prototype: experiments remain runtime-only (no persistence). Memory grows with combinations × frames × histogram bins as combinations accumulate runs — the 200-combination cap bounds it; the RFC's mergeable sketches / time-axis downsampling would be the durable answer.
  • An interrupted batch discards its partial runs (batches are atomic so accumulated distributions stay well-defined); the largest ladder steps (500) waste the most work when abandoned mid-batch.
  • Each batch spawns a fresh Web Worker (SDCPN + compiled artifacts are re-transferred). Fine at these batch sizes; a worker-reuse pool would remove the overhead.
  • Merging is exact for distribution-output metrics (the only kind the experiments UI creates). Scalar-output metric frames (API-only path) merge via a run-sample-weighted mean, which is exact for mean aggregation and approximate for the rest.
  • Pre-existing, unrelated: the static Storybook fixture stories select a row on click but never mount the view drawer in that story wrapper; the runnable stories open it fine.

🐾 Next steps

  • Sampling (LHS/Sobol) + sensitivity analysis instead of dense grids for many-parameter sweeps (RFC §4.4).
  • Persistence of experiment definitions/results.
  • Worker reuse across batches; adaptive stopping (target precision) instead of a fixed run budget (RFC §4.7).

🛡 What tests cover this?

  • parameter-grid.test.ts: range expansion, cartesian-grid order, histogram-merge semantics, the run ladder (climbing, clamping, saturation, ×5/×2 extension), and least-runs-first refinement picking.
  • provider.test.tsx: all pre-existing single-batch tests unchanged and passing, plus lazy-sweep tests — seed pass through a single-worker pool with per-combination parameterValues, idle-when-unviewed, focus-driven ladder batches with the expected runCount/seed per batch, exact accumulation of merged distributions, saturation stopping, unpinned levelling, in-flight batch interruption on focus change (partials discarded, worker terminated, compute redirected), and permanent stop on cancel.

❓ How to test this?

  1. yarn workspace @hashintel/petrinaut dev and open the Simulate / SimulateView / Run SIR experiment story.
  2. Create an experiment against the Seasonal Flu scenario; switch infected_ratio to Range (0.01–0.05 × 3 values), set Runs to e.g. 1000 and Max time to 30, add a place-tokens metric, and Run.
  3. Watch the seed pass appear almost immediately (1 run per combination), then runs accumulate for the viewed selection — the Parameters section shows "X of Y runs accumulated — adding runs…".
  4. Pin infected_ratio and drag the slider: compute redirects to the pinned value (the previous one stops), and the pinned distribution sharpens the longer you stay. Unpinned, all values are sampled evenly.
  5. Close the drawer: the experiment shows Idle in the list and stops computing; reopen it to resume. Each combination stops at the requested run count.

📹 Demo

The Simulate / SimulateView / Parameter sweep Storybook story demonstrates the navigator on a completed 5 × 2 sweep with synthetic data; the Run SIR experiment story exercises the live lazy scheduler.

🤖 Generated with Claude Code

@kube kube self-assigned this Jul 24, 2026
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview, Comment Jul 24, 2026 2:31am
petrinaut Ready Ready Preview, Comment Jul 24, 2026 2:31am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Preview Jul 24, 2026 2:31am

@github-actions github-actions Bot added area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team area/apps > hash.design Affects the `hash.design` design site (app) labels Jul 24, 2026
@kube kube changed the title FE-1263: Add parameter ranges to Petrinaut experiments with a sweep-results navigator FE-1263: Add lazily-computed parameter sweeps to Petrinaut experiments Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps > hash.design Affects the `hash.design` design site (app) area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

1 participant