0

Multiperiod Dispatch

Fresh

Multi-period economic dispatch RL environment with ramp limits (UC-lite): cheap units are slow, fast units are expensive, so per-period merit-order...

Type
RL Env
Publisher
Jwilksbooth
Runtime
single-turn
License
mit
Size
v0.1.2
Published
Jul 2026

Cite

Notes

Only stored in your browser.

multiperiod-dispatch

Multi-period economic dispatch RL environment with ramp limits (unit-commitment-lite). Third environment in a power-systems vertical (economic-dispatchdcopf-grid-verifiers → this). The physics axis here is time: unit output changes between consecutive periods are limited by ramp rates, and the fleet is drawn with real merit-stack texture — cheap units are slow, fast units are expensive. Per-period merit-order reasoning is therefore provably infeasible on a measured 74% of instances: the cheap baseload cannot climb out of the valley into the peak fast enough, so cost-optimal scheduling must anticipate the climb periods ahead of time or pay expensive fast units to carry the peak.

Task

Given 5–8 consecutive periods, a duck-curve-shaped load profile, and 3–5 committed units (linear cost, [Pmin, Pmax], per-period ramp limit), output the full schedule as JSON: {"dispatch_mw": [[unit MWs period 1], [period 2], ...]} such that every period balances, every unit respects its range, and no unit's output changes faster than its ramp limit.

Ground truth validation

Two independently assembled LP formulations must agree (unit-major assembly solved with HiGHS dual simplex vs a separately written time-major assembly solved with HiGHS interior point):

Metric (200 instances, seeds 0–199)Result
Objective mismatches (>0.1% rel and >$1 abs)0 / 200
Worst relative objective gap8.8e-16
Ramp-blind merit-order dispatch infeasible149 / 200 (74%)
Median cost-of-ramps premium (constrained subset)5.4%

Both formulations share scipy/HiGHS, so this is cross-implementation validation of the constraint assembly (the dominant error source), not independent-solver validation — stated plainly. See CALIBRATION.md for how the load-profile design was iterated against measured difficulty (a naive gradual climb produced ramp infeasibility without economic cost — the duck-curve construction fixes exactly that).

Baseline results

50 instances (seeds 0–49), 1 rollout each, July 2026:

Modeltotalformatfeasibilityoptimality
claude-haiku-4-5 (8k tokens)0.2420.9800.2000.135
claude-opus-4-8 (16k tokens)0.8900.9200.9000.879

The weak model writes near-perfectly parseable schedules and only 1 in 5 survives the ramp physics. The frontier model reaches 90% feasibility but its optimality-when-feasible is 0.977 (vs 0.998 on the congestion sibling) — the economics of pre-positioning slow units is harder than it looks even when the constraints are satisfied. Across the vertical the weak-model ladder is 0.861 → 0.520 → 0.242 (economic-dispatchdcopf-grid-verifiers → this).

vf-eval multiperiod-dispatch -p anthropic -m claude-haiku-4-5-20251001 -n 50 -r 1 --max-tokens 8000 --save-results
vf-eval multiperiod-dispatch -p anthropic -m claude-opus-4-8 -n 50 -r 1 --max-tokens 16000 --save-results

Example failure (claude-haiku-4-5, from the baseline run)

"...G2 ramp: 72.5 - 6.5 = 66 > 64.3 ... Can't meet ramp constraint. After multiple iterations, optimal solution: [emits schedule]"

The model correctly computes that a ramp limit is violated — then submits a schedule anyway, one that also puts G3 at 6.8 MW, below its minimum. Feasibility reward: 0. The ramp coupling across periods is precisely the reasoning per-period merit order cannot shortcut.

Rewards (weighted rubric)

RewardWeightDescription
reward_format0.10Parseable T×G JSON matrix, finite values only
reward_feasibility0.35Positive-proof check: per-period balance, unit bounds, all ramp transitions
reward_optimality0.55Gated on feasibility; `exp(-5 ×

Anti-reward-hacking carried over from the sibling environments and regression-tested by 7 attack gates in tests/test_validation.py: the ramp-blind cheap schedule scores 0 (feasibility gate); NaN/Infinity literals and oversized integers (including CPython's >4300-digit limit and deeply-nested-bracket payloads) are rejected at parse; and tolerance abuse (riding the ±0.5 MW grading tolerance to beat the LP optimum) is settled at a penalty price of 2×·T·(max unit cost) — scaled by the horizon T because a single within-tolerance violation can buy an advantageous position that pays off across every remaining period — with imbalance priced symmetrically so over-generation is as costly as under-service.

Usage

pip install -e .

# validation harness (dual-formulation cross-check + attack gates)
N_INSTANCES=200 python tests/test_validation.py

# difficulty distribution / calibration sweep
python calibration/measure.py --n 300

# give frontier models room to reason — a bare command defaults to 4096 tokens
# and frontier rollouts truncate, collapsing the score to ~0.5
vf-eval multiperiod-dispatch -p anthropic -m <model> -n 50 -r 1 --max-tokens 16000
import verifiers as vf
env = vf.load_environment("multiperiod-dispatch", num_examples=300)
# disjoint train/eval: load_environment(1000) + load_environment(200, seed_offset=1000)

Instance generation

Tiered fleet (40% baseload $15–30/MWh with 5–15%/period ramps, 40% mid-merit $35–60 at 15–35%, 20% peakers $70–130 at 50–100%; at least one fast unit guaranteed). Load profiles are duck-curve shaped in explicit phases — valley plateau, steep 1–2 period climb, peak plateau, shoulder decline — with the climb rate constructed at 55–95% of the drawn fleet's aggregate ramp capability, so instances are physically followable by construction (draw rejection ~5%, no survivor bias) while merit-blind allocation frequently is not. 25% of instances are near-flat "mild days" (the easy tier). Fully deterministic per seed; ~10² instances/sec generation.

Roadmap

  • v0.2: startup costs + on/off commitment decisions (true UC, MILP ground truth)
  • Sibling roadmap: N-1 contingency screening; LMP/nodal pricing verified against LP duals