{"data":{"kind":"file","path":"README.md","version_id":"rghg468lo2c554gu1e81k0uz","entry":{"name":"README.md","path":"README.md","is_directory":false,"size":6574,"modified_at":"2026-07-07T04:50:51.914000","content_hash":"add57813f84b3b8116a82e190de7302a08614291c6d7a9436f662b717d056067"},"entries":[],"content":"# multiperiod-dispatch\n\nMulti-period economic dispatch RL environment with ramp limits. Third environment in a power-systems vertical ([economic-dispatch](https://github.com/JWilksBooth/economic-dispatch) → [dcopf-grid-verifiers](https://github.com/JWilksBooth/dcopf-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.\n\n## Task\n\nGiven 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.\n\n## Ground truth validation\n\nTwo 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):\n\n| Metric (200 instances, seeds 0–199) | Result |\n|---|---|\n| Objective mismatches (>0.1% rel and >$1 abs) | **0 / 200** |\n| Worst relative objective gap | 8.8e-16 |\n| Ramp-blind merit-order dispatch infeasible | **149 / 200 (74%)** |\n| Median cost-of-ramps premium (constrained subset) | 5.4% |\n\nBoth 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](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).\n\n## Baseline results\n\n50 instances (seeds 0–49), 1 rollout each, July 2026:\n\n| Model | total | format | feasibility | optimality |\n|---|---|---|---|---|\n| claude-haiku-4-5 (8k tokens) | **0.242** | 0.980 | 0.200 | 0.135 |\n| claude-opus-4-8 (16k tokens) | **0.890** | 0.920 | 0.900 | 0.879 |\n\nThe weak model writes near-perfectly parseable schedules and only 1 in 5\nsurvives the ramp physics. The frontier model reaches 90% feasibility but its\noptimality-when-feasible is 0.977 (vs 0.998 on the congestion sibling) — the\neconomics of pre-positioning slow units is harder than it looks even when the\nconstraints are satisfied. Across the vertical the weak-model ladder is\n0.861 → 0.520 → 0.242 ([economic-dispatch](https://github.com/JWilksBooth/economic-dispatch)\n→ [dcopf-grid-verifiers](https://github.com/JWilksBooth/dcopf-grid-verifiers) → this).\n\n```bash\nvf-eval multiperiod-dispatch -p anthropic -m claude-haiku-4-5-20251001 -n 50 -r 1 --max-tokens 8000 --save-results\nvf-eval multiperiod-dispatch -p anthropic -m claude-opus-4-8 -n 50 -r 1 --max-tokens 16000 --save-results\n```\n\n## Example failure (claude-haiku-4-5, from the baseline run)\n\n> *\"...G2 ramp: 72.5 - 6.5 = 66 > 64.3 ... Can't meet ramp constraint. After multiple iterations, optimal solution: [emits schedule]\"*\n\nThe 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.\n\n## Rewards (weighted rubric)\n\n| Reward | Weight | Description |\n|---|---|---|\n| `reward_format` | 0.10 | Parseable T×G JSON matrix, finite values only |\n| `reward_feasibility` | 0.35 | Positive-proof check: per-period balance, unit bounds, all ramp transitions |\n| `reward_optimality` | 0.55 | Gated on feasibility; `exp(-5 × |relative cost gap|)` vs LP optimum |\n\nAnti-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.\n\n## Usage\n\n```bash\npip install -e .\n\n# validation harness (dual-formulation cross-check + attack gates)\nN_INSTANCES=200 python tests/test_validation.py\n\n# difficulty distribution / calibration sweep\npython calibration/measure.py --n 300\n\n# give frontier models room to reason — a bare command defaults to 4096 tokens\n# and frontier rollouts truncate, collapsing the score to ~0.5\nvf-eval multiperiod-dispatch -p anthropic -m <model> -n 50 -r 1 --max-tokens 16000\n```\n\n```python\nimport verifiers as vf\nenv = vf.load_environment(\"multiperiod-dispatch\", num_examples=300)\n# disjoint train/eval: load_environment(1000) + load_environment(200, seed_offset=1000)\n```\n\n## Instance generation\n\nTiered 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.\n\n## Roadmap\n\n- v0.2: startup costs + on/off commitment decisions (discrete on/off is delivered as the sibling env unit-commitment-lite (in build); this repo stays the continuous ramp-coupling rung)\n- Shipped siblings: [n1-contingency-dispatch](https://github.com/JWilksBooth/n1-contingency-dispatch) (N-1 security, 68% insecure), [nodal-pricing-lmp](https://github.com/JWilksBooth/nodal-pricing-lmp) (LMP pricing, 44% out-of-band)\n","encoding":"utf-8","truncated":false,"total_bytes":6574},"status":null}