Skip to content

Plan on the mean noise in the boiler, kiln and distillation oracles - #58

Merged
YannBerthelot merged 1 commit into
mainfrom
plant/noise-trio
Oct 1, 2026
Merged

YannBerthelot merged 1 commit into
mainfrom
plant/noise-trio

Conversation

@YannBerthelot

Copy link
Copy Markdown
Owner

Third step of the oracle audit. The three oracles that saw protocol seed 0's noise now plan on the noise mean, and each gets the optimiser budget it then needs.

The defect

boiler_drum, cement_kiln and distillation have plant noise: steam demand, raw-meal feed and feed composition, respectively. Their registry entries did not declare it in noise_fields, so plan_params left it on in the planner's copy of the params. These planners roll the simulator with a fixed PRNGKey(0). So on protocol seed 0, whose plant also draws from key 0, they knew the exact future noise, and on seeds 1-2 they planned against a noise path that never happened. The boiler's recorded gain was 0.93 on seed 0 and 38.8 and 31.6 on the others.

The fix

  1. Declare the noise (noise_fields) for all three, so the planners plan on the mean (certainty equivalence).
    • test_plant_noise_is_declared is new: every non-zero param ending in _std or _sigma must be declared.
    • Three tasks are listed as pending: glass furnace, reactor (its demand) and HVAC. Their CasADi oracles plan on their own models and never roll the simulator, so they cannot see the noise. Each gets declared when its task is next re-recorded.
  2. Give each optimiser the budget it needs once the noise is gone. Planning on the mean alone made the boiler a little better and distillation 15% worse; the optimisers were the bottleneck. Measured in budget sweeps on the protocol seeds:
plant before after new defaults cost per protocol seed
boiler_drum 23.8 4.67 (-80%) 1280 iterations, step decaying 0.15 to 0.005 about 160 s (was 12 s)
cement_kiln 1.33 0.0705 (-95%) 96 samples, 12 elites, 32 iterations, spread 0.1, return best sample about 190 s (was 32 s)
distillation 0.297 0.0125 (-96%) 80 iterations, step decaying 0.08 to 0.004 about 41 s (was 21 s)

Zero trips on every seed. More details:

  • Boiler. The curve is still falling: each doubling gains about half what the one before it did. A better-conditioned optimiser would probably beat more iterations, which is noted as future work. A longer horizon and a PID-rollout guide did not help.
  • Kiln. The search needed reshaping more than enlarging. At the same cost per step, the earlier 384 samples by 8 iterations scored 0.264, and 64 iterations add under 1%.
  • Distillation. It sits on its running-cost floor, which is seed 0's reflux and boilup at about 0.0122.

Floors (rho_floor only)

The NEA reference follows the re-measured oracle holds (scripts/measure_hold.py):

  • kiln: (4.89e-5 / 5e-4)^2 = 0.00956, from 0.468.
  • distillation: (7.64e-7/1e-4)^2 + (5.19e-7/1e-4)^2 = 8.5e-5, from 0.127.
  • boiler: (2.45e-3/2.67e-3)^2 + (0.0519/0.05)^2 = 1.92. That is up from 1.32, because the old pressure term came from the foresight seed's 0.028 bar.

Every e_floor stays. The boiler's 2.67 mm level floor is within 1.1x of the new 2.45 mm hold, and its pressure hold sits just above the 0.05 bar transmitter resolution. Both are inside the 1.5x rule, so the rewards are unchanged and the three versions are re-stamped in place.

The boiler planner divides its objective by rho_floor_tracking, so its hold was re-measured under the new value and is self-consistent: 1.9204 against the 1.9194 set. NEA against the new floors: boiler 0.995, kiln 0.987, distillation about 1.0.

Docs

The three PHYSICS.md files, env.py comments and factory docstrings, docs/baselines.md (the paragraph on how planners treat noise was wrong and is rewritten; protocol table), docs/reward-shaping.md (floor table, and the rho* rule now covers a floor kept within 1.5x), docs/roadmap.md, CHANGELOG, and the generated pages.

An adversarial review (four lenses, each finding checked by a skeptic) confirmed the noise plumbing, the floors and the re-stamps. It caught:

  • a test pattern that would have flagged the reactor's xenon absorption rate sigma_phi0 as noise;
  • a version note that wrongly said the distillation planner reads the floor;
  • several numbers quoted from the wrong sweep variant.

All are fixed here.

🤖 Generated with Claude Code

Their plant noise (steam demand, raw-meal feed, feed composition) was
not declared in noise_fields, so their planners, which roll the
simulator with a fixed key, saw the exact noise of protocol seed 0 and
a wrong path on every other seed. It is declared now, and a registry
test requires every non-zero noise amplitude to be declared (glass
furnace, reactor and HVAC pending: their CasADi oracles never roll the
simulator).

With the noise out of the plan, each optimiser gets the budget the
oracle audit measured it needs: boiler 1280 iterations with a step
decaying 0.15 -> 0.005; distillation 80 iterations decaying 0.08 ->
0.004; kiln 32 CEM iterations of 96 samples from a 0.1 spread,
returning the best sample. Protocol cost: boiler 23.8 -> 4.67, kiln
1.33 -> 0.0705, distillation 0.297 -> 0.0125, zero trips.

The NEA floors follow the re-measured holds (rho_floor only; every
e_floor stays): kiln 0.468 -> 0.00956, distillation 0.127 -> 8.5e-5,
boiler 1.32 -> 1.92 (its old value came from the seed-0 foresight).
The three versions are re-stamped in place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@YannBerthelot
YannBerthelot merged commit 0c66f5f into main Oct 1, 2026
6 checks passed
@YannBerthelot
YannBerthelot deleted the plant/noise-trio branch October 1, 2026 22:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant