Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/create-experiment-resolves-record.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hashintel/petrinaut": patch
---

`createExperiment` resolves with the experiment record, which now carries the scenario it compiled (`ExperimentRecord.scenario`), and no longer selects it.
4 changes: 2 additions & 2 deletions libs/@hashintel/petrinaut-core/src/ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ export const petrinautDocSummaries: Record<PetrinautDocName, string> = {
scenarios:
"Named simulation configurations authored through the scenario form: Variables exposed as scenario parameters, parameter overrides, per-place initial state blocks, running and switching scenarios, the expression language, scenarios stored per place or as code by files, the AI or earlier versions.",
"ad-hoc-scenarios":
"Inline initial state + parameters without saving a scenario: the shared form (scenario.<name> variables, fixed/dynamic/swept-count rows chosen from the row gutter's menu, shared columns, phantom row, place totals, live type checking), its three surfaces (quick simulation, experiments, scenario creation and editing with Scenario Parameter toggles), Sweep selections with generated adhoc_* parameter names, saved scenarios shown in run mode.",
"Inline initial state + parameters without saving a scenario: the shared form (scenario.<name> variables, fixed/dynamic/swept-count rows chosen from the row gutter's menu, shared columns, phantom row, place totals, live type checking), its three surfaces (quick simulation, experiments, scenario creation and editing with Scenario Parameter toggles), interval selections — Sweep or Optimize by setting — with generated adhoc_* parameter names, saved scenarios shown in run mode.",
experiments:
"Monte Carlo batches: configuration (runs, seed, dt, max time, scenario), parameter sweeps, constraints (parameter and state, pass threshold), optimizing a sweep from its Parameters card (in-browser optimizer, steps, Stop), lifecycle/statuses, cancel/remove, header columns (Steps, Steps clear), metric charts, the Constraints and Sensitivity analysis cards, the steps table, Objective by step, compute backend, active-experiments popover.",
"Monte Carlo batches: configuration (runs, seed, dt, max time, scenario), parameter sweeps, constraints (parameter and state, pass threshold), Optimize toggles and an Objective section (metric, direction, steps) at creation, the drawer opening already optimizing, Stop on the Parameters card, one study per experiment, lifecycle/statuses, cancel/remove, header columns (Steps, Steps clear), metric charts, the Constraints and Sensitivity analysis cards, the steps table, Objective by step, compute backend, active-experiments popover.",
"actual-mode":
"Actual mode: host-provided live execution view, Brunch stream URL route, read-only extension-free net, current limits.",
preview:
Expand Down
5 changes: 3 additions & 2 deletions libs/@hashintel/petrinaut/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ yarn workspace @hashintel/petrinaut dev
```

The **Simulate / SimulateView / Run Supply Chain optimization (synthetic
optimizer)** story creates a parameter sweep and drives it with an internal
fake optimizer, so it does not require the Python service or Docker.
optimizer)** story creates a parameter sweep with its study, as the Create
Experiment drawer's Optimize does, and drives it with an internal fake
optimizer, so it does not require the Python service or Docker.

## Host-owned interactive AI tools

Expand Down
14 changes: 7 additions & 7 deletions libs/@hashintel/petrinaut/docs/ad-hoc-scenarios.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Use an ad-hoc scenario for one-off runs and quick exploration. When you want to
The same form appears in three places:

1. **Quick simulation** -- in the [Simulation Settings](simulation.md#simulation-settings) tab, with "No scenario" selected, the panel's two columns are the form's own tables: **Variables** above **Parameters** on the left, **Initial state** -- token counts and values -- on the right, no separate dialog. A quiet **Clear** button next to the Initial state title resets your entries. The next simulation run uses what you defined. Any [compile error](#errors) appears in the settings panel's error banner.
2. **Experiments** -- in the [create-experiment drawer](experiments.md#creating-an-experiment), choosing "No scenario" shows the form inside the Scenario section. The experiment's runs start from the state you defined, and the experiments table shows "Ad-hoc scenario" in its Scenario column. With [Parameter sweeps](experiments.md#parameter-sweeps) enabled, every numeric value carries a **Sweep** toggle (see below).
2. **Experiments** -- in the [create-experiment drawer](experiments.md#creating-an-experiment), choosing "No scenario" shows the form inside the Scenario section. The experiment's runs start from the state you defined, and the experiments table shows "Ad-hoc scenario" in its Scenario column. With [Parameter sweeps](experiments.md#parameter-sweeps) enabled, every numeric value carries an interval toggle (see below).
3. **Scenario creation** -- [creating or editing a scenario](scenarios.md#creating-a-scenario) uses the same form with a **Scenario Parameter** toggle on each top-level Variable; see [Saving a scenario from the form](#saving-a-scenario-from-the-form).

## The form
Expand All @@ -22,7 +22,7 @@ The form has up to three sections. Variables come first -- parameter overrides m

In the experiment drawer each section collapses: click the chevron in its header, or focus the header and press Left to collapse and Right to expand. Place headers inside Initial state collapse the same way everywhere, and a collapsed place shows a one-line summary of its rows and token total. In the quick-simulation embedding, places start collapsed.

Every value in the form is an expression. A first click selects a value; a second click, a double-click, or Enter opens the editor in place: a code input with completion and type checking at exactly the cell's position, the value's path (for example `Space › item 0 › x`) above it, and -- in the experiment drawer with sweeps enabled -- the Sweep control below it. Expressions may use your Variables (`scenario.<name>`), net parameters (`parameters.<name>`), and arithmetic -- the same [expression language](scenarios.md#expression-language) scenarios use. Press Enter, Escape, or click elsewhere to close the editor. Escape closes only the innermost thing that is open -- a completion list, a bound edit, the editor itself -- and never the drawer or dialog around the form; close those from their own buttons. Closing tidies a valid expression's formatting (spacing, redundant parentheses) without changing its meaning. A value may also be left **empty**: an empty cell reads as its type's neutral value -- 0 for numbers, `false` for booleans, `""` for text, the nil UUID -- shown grayed in the cell, and it is never an error. An empty dynamic-row count means 1 token; an empty place count means 0.
Every value in the form is an expression. A first click selects a value; a second click, a double-click, or Enter opens the editor in place: a code input with completion and type checking at exactly the cell's position, the value's path (for example `Space › item 0 › x`) above it, and -- in the experiment drawer with sweeps enabled -- the interval toggle below it. Expressions may use your Variables (`scenario.<name>`), net parameters (`parameters.<name>`), and arithmetic -- the same [expression language](scenarios.md#expression-language) scenarios use. Press Enter, Escape, or click elsewhere to close the editor. Escape closes only the innermost thing that is open -- a completion list, a bound edit, the editor itself -- and never the drawer or dialog around the form; close those from their own buttons. Closing tidies a valid expression's formatting (spacing, redundant parentheses) without changing its meaning. A value may also be left **empty**: an empty cell reads as its type's neutral value -- 0 for numbers, `false` for booleans, `""` for text, the nil UUID -- shown grayed in the cell, and it is never an error. An empty dynamic-row count means 1 token; an empty place count means 0.

Opening a value with Enter or a second click selects its whole content, so typing replaces it. Opening by typing keeps the caret right after what you typed.

Expand All @@ -32,7 +32,7 @@ Every table in the form is a keyboard grid: arrow keys move between cells, phant

The walk does not stop at a table's edge: moving down from a table's last row continues to the next part of the form -- a section header, a place header, the next table -- and moving up continues backwards the same way. Collapsed sections are skipped.

The whole form has one undo history: Cmd/Ctrl+Z undoes and Shift+Cmd/Ctrl+Z (or Ctrl+Y) redoes any edit -- a changed value, an added or deleted row, a shared column, a Sweep toggle. Typing in one value counts as a single step, however long the pause; editing another value starts the next step. Redo restores exactly the state you undid from. An open text editor keeps its own text-level undo until you close it.
The whole form has one undo history: Cmd/Ctrl+Z undoes and Shift+Cmd/Ctrl+Z (or Ctrl+Y) redoes any edit -- a changed value, an added or deleted row, a shared column, an interval toggle. Typing in one value counts as a single step, however long the pause; editing another value starts the next step. Redo restores exactly the state you undid from. An open text editor keeps its own text-level undo until you close it.

### Connections around the focused value

Expand All @@ -48,7 +48,7 @@ A place with a [token type](petri-net-extensions.md#typed-vs-untyped-places) is

- **Fixed** (`#1`, `#2`, ...) -- the row emits exactly one token.
- **Dynamic** (`i`, blue) -- the row emits many tokens: a quiet strip above the cells shows `×` and the row's **count expression**, and each cell is evaluated once per token with `i` running from `0` to `count - 1` (`count` is also available). The gutter's tooltip shows the row number.
- **Swept count** (`i`, purple; experiments with sweeps only) -- a dynamic row whose count is a swept parameter: the strip shows the count's bounds, `× 0 … 12`.
- **Swept count** or **Optimized count** (`i`, purple; experiments with sweeps only, the word following the interval toggle's) -- a dynamic row whose count is a swept parameter: the strip shows the count's bounds, `× 0 … 12`.

Changing a row's kind never loses anything: its count (bounds included) is restored when you change back. The dimmed trailing row is a **phantom row**, and its cells follow the same selection model as every other cell: a first click selects one, and a second click (or Enter, or the row's `+` gutter) materializes a new fixed row. Remove a row from its gutter: the menu offers **Delete row**, and the Delete key removes it directly. In fixed rows, `i` is the row's position in the list and `count` is `1`.

Expand All @@ -68,9 +68,9 @@ A dynamic row's **count** may read the place's variables too, as long as their v

Every expression is type-checked as you work. The open editor marks problems inline; a closed value with a problem underlines in red and shows the message when you hover it. Cells inherit their type from the token type's field; declared types exist on Variables and counts only. Structural rules (duplicate names, bounds that do not resolve, sweeping a text field) surface the same way, on the value they belong to.

## Sweep selections (experiments only)
## Interval selections (experiments)

In the create-experiment drawer, with [Parameter sweeps](experiments.md#parameter-sweeps) enabled, every numeric value slot -- cells, counts, variables, shared columns, and net parameters -- carries a labeled **Sweep** toggle, purple while on: under the open cell editor, and on the row for Variables and Parameters. Turning it on replaces the expression with **Min** and **Max** cells; a sweep declares an interval and nothing else, so there is no Scale or Step. Each bound is an expression cell with the same selection model as the rest of the form -- select it, press Enter (or click again) to edit, Enter or Escape to leave; Escape from a selected cell closes the editor. Turning Sweep off restores the expression you had, and the bounds are remembered too. A swept value shows its bounds (`0 … 12`) on a purple slot. Boolean and text values offer no toggle, and changing a swept Variable to boolean turns its Sweep off; a cell muted by a shared column does not count. A row's gutter menu offers **Swept count** for a dynamic row's count.
In the create-experiment drawer, with [Parameter sweeps](experiments.md#parameter-sweeps) enabled, every numeric value slot -- cells, counts, variables, shared columns, and net parameters -- carries a labeled interval toggle, purple while on: under the open cell editor, and on the row for Variables and Parameters. It reads **Sweep**, or **Optimize** when the [in-browser optimizer](experiments.md#optimizing-a-sweep) is on; the word is the same on every toggle of the form, and both mean the same thing. Turning it on replaces the expression with **Min** and **Max** cells; an interval declares nothing else, so there is no Scale or Step. Each bound is an expression cell with the same selection model as the rest of the form -- select it, press Enter (or click again) to edit, Enter or Escape to leave; Escape from a selected cell closes the editor. Turning the toggle off restores the expression you had, and the bounds are remembered too. A selected value shows its bounds (`0 … 12`) on a purple slot. Boolean and text values offer no toggle, and changing a selected Variable to boolean turns its toggle off; a cell muted by a shared column does not count. A row's gutter menu offers **Swept count** or **Optimized count** for a dynamic row's count, to match.

Each selection becomes a swept parameter of the experiment with a deterministic name, shown in the sweep navigator under the value's path (`Space › item 0 › x`):

Expand All @@ -80,7 +80,7 @@ Each selection becomes a swept parameter of the experiment with a deterministic
- `adhoc_var_net_<name>` -- a top-level Variable; place-scoped variables use the place's name as the scope.
- `adhoc_param_<variable_name>` -- a net parameter override.

Bounds must resolve to constants, integer values need integer bounds, and the maximum must exceed the minimum; a value that does not run shows its problem on the bound, and the drawer's footer names it. The experiment then behaves like any [parameter sweep](experiments.md#parameter-sweeps): the initial state compiles at the navigator's selection, parameter overrides follow each run's draw.
Bounds must resolve to constants, integer values need integer bounds, and the maximum must exceed the minimum; a value that does not run shows its problem on the bound, and the drawer's footer names it. The experiment then behaves like any [parameter sweep](experiments.md#parameter-sweeps): the initial state compiles at the navigator's selection, parameter overrides follow each run's draw. Under **Optimize**, the study searches the generated parameters like any others; only [Constraints](experiments.md#constraints) need a saved scenario.

A saved scenario shown through the form in the experiment drawer offers the same toggle on each numeric scenario parameter row.

Expand Down
6 changes: 3 additions & 3 deletions libs/@hashintel/petrinaut/docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The SIR model with two policy levers and a cost account, built as the model to o
- **GPU-ready modelling** -- untyped places and rates that read only parameters, so an experiment measuring the **Infected** place's token count (**Built-in › Place tokens**) runs on the GPU backend as shipped. The model's expression metrics — **Total cost**, **Infected**, **Attack rate** — compile to the GPU too.
- Two further metrics -- **Infected** (the wave's curve, dying out or growing) and **Attack rate** (share of the population infected so far).

**Suggested initial state:** pick **Winter wave** and create an experiment over it with a max time of 60, **Sweep** on `vaccination_coverage` (0 to 0.9) and `contact_reduction` (0 to 0.8) and a **Total cost** metric; then press **Optimize** on the sweep's Parameters card and minimize **Total cost**: the Surface shows a valley along the epidemic threshold and the steps settle around a coverage of 0.45 and a contact reduction of 0.4 (see [Optimizing a sweep](experiments.md#optimizing-a-sweep)). To watch a single run instead, press Play and select the **Infected** metric in the timeline.
**Suggested initial state:** create an experiment on **Winter wave** with a max time of 60 and **Total cost** as a metric, flip **Optimize** on `vaccination_coverage` (0 to 0.9) and `contact_reduction` (0 to 0.8), choose **Minimize**, and the experiment opens searching: the Surface shows a valley along the epidemic threshold and the steps settle around a coverage of 0.45 and a contact reduction of 0.4 (see [Optimizing a sweep](experiments.md#optimizing-a-sweep)). To watch a single run instead, press Play and select the **Infected** metric in the timeline.

**Key concepts:** [stochastic firing](petri-net-extensions.md#stochastic-rate), [parameters](petri-net-extensions.md#global-parameters), [scenarios](scenarios.md), [optimization objectives](useful-patterns.md#optimization-objectives-metrics-that-read-parameters), [parameter sweeps](experiments.md#parameter-sweeps).

Expand All @@ -48,7 +48,7 @@ A small service system: customers arrive, wait, are served by a limited staff po
- Rate parameters (`arrival_rate`, `begin_rate`, `service_rate`) a sweep can range over: the **Morning Rush** scenario exposes `arrival_rate` and `service_rate` as scenario parameters wired straight to the net's rates, so a two-parameter sweep explores under- and over-staffed regimes.
- A conserved staff pool (**FreeStaff** + **Serving** always totals the staff count).

**Suggested initial state:** pick **Morning Rush** and create an experiment measuring **Waiting** — then sweep `arrival_rate` against `service_rate` and watch the queue-explosion boundary appear on the surface plot.
**Suggested initial state:** pick **Morning Rush** and create an experiment measuring **Waiting** — flip the interval toggle on `arrival_rate` and `service_rate` (**Sweep** with the optimizer off, so the surface is yours to explore) and watch the queue-explosion boundary appear on the surface plot.

## Drone Patrol

Expand All @@ -62,7 +62,7 @@ A typed fleet of drones cycling between the hangar and the air: launch, drain ba
- **Token-reading rates**: launch tempo scales with the candidate drone's battery, and returns become more likely as the battery falls.
- The **Standard Patrol** scenario exposes `launch_rate` and `drain_rate` for sweeping.

**Suggested initial state:** pick **Standard Patrol** and create an experiment measuring **Airborne**; on the GPU, sweeping `launch_rate` against `drain_rate` streams the fleet's equilibrium as a distribution.
**Suggested initial state:** pick **Standard Patrol** and create an experiment measuring **Airborne**; on the GPU, with the interval toggle on `launch_rate` and `drain_rate` (**Sweep** with the optimizer off), the fleet's equilibrium streams as a distribution.

## Supply Chain with Disruption

Expand Down
Loading
Loading