Skip to content

perf: scale solver objective 1e4; hard SOC bounds for in-range starts#90

Open
andig wants to merge 1 commit into
model/tight-big-mfrom
model/scale-objective-hard-soc
Open

perf: scale solver objective 1e4; hard SOC bounds for in-range starts#90
andig wants to merge 1 commit into
model/tight-big-mfrom
model/scale-objective-hard-soc

Conversation

@andig

@andig andig commented Jul 16, 2026

Copy link
Copy Markdown
Member

Stacked on #88 (base model/tight-big-m; merge that first). Independent of #89.

Two changes, validated together against the full golden suite:

1. Solver-facing objective ×1e4 (one line). The optimum sits at ~0.5–18 currency units with tie-break coefficients down at 1e-8; CBC's absolute tolerances (integrality 1e-6, cutoff increment 1e-5) can't separate near-tie nodes, so the search wanders the alternate-optima plateau. Scaling lets the tolerances bite. get_clean_objective_value recomputes from variable values, so reported numbers stay unscaled.

2. Hard SOC bounds when s_min ≤ s_initial ≤ s_max. The soft-penalty construction (2 penalty columns + 2 rows per battery per time step) is provably dead weight for in-range starts: the penalty (0.27/Wh) exceeds any achievable gain (<3e-4/Wh) by three orders of magnitude, so penalties are zero at every optimum and [s_min, s_max] can live on the variable bounds. The soft path stays for out-of-range starts (015/018 exercise it).

Full-suite benchmark (bundled CBC, single-thread, app-faithful build, median of 3)

case main #88 (base) this PR
009 0nd / 160it / 0.08s 0 / 121 / 0.09s 0 / 129 / 0.08s
016 2 / 1256 / 0.18s 0 / 148 / 0.09s 2 / 189 / 0.08s
017 4 / 97 / 0.09s 0 / 98 / 0.06s 0 / 0 / 0.04s
018 8 / 546 / 0.09s 2 / 266 / 0.09s 2 / 261 / 0.09s
020 86 / 5233 / 1.02s 18 / 1442 / 0.62s 24 / 1714 / 0.61s
021 0 / 15 / 0.14s 10 / 220 / 0.38s 0 / 0 / 0.09s
others ≤0.05s, 0 nodes unchanged unchanged
TOTAL 1.93s 1.70s 1.36s

Every case is equal-or-better than main — notably this heals the base branch's 021 drift (10 nodes → root-solved, faster than main ever was), and side effects each change shows alone (SOC bounds alone push 009 to 770 iterations) cancel under scaling. Stable across reruns.

Equivalence: full pulp objectives (÷1e4) match the base branch to ≤3e-6 absolute, 016/017/020/021 exact. The 013/019 golden objective_values land on different points of the same optimal plateau (019 returns to main's original value); optimum unchanged.

🤖 Generated with Claude Code

Two changes that compound with the tightened big-Ms underneath:

Objective scaling: the optimum sits at ~0.5-18 currency units with
tie-break coefficients down at 1e-8, so CBC's absolute tolerances
(integrality 1e-6, cutoff increment 1e-5) cannot separate near-tie
nodes and the search wanders the alternate-optima plateau. Scaling the
solver-facing objective by 1e4 lets those tolerances bite; reported
values are recomputed from variable values and stay unscaled.

Hard SOC bounds: when s_initial lies within [s_min, s_max], the soft
penalty construction (2 penalty columns + 2 rows per battery per step)
is dead weight — the penalty (0.27/Wh) exceeds any achievable gain
(<3e-4/Wh) by three orders of magnitude, so the penalties are zero at
every optimum and [s_min, s_max] can be variable bounds instead. The
soft path remains for out-of-range starts (cases 015/018).

Full suite (bundled CBC, single-thread, median of 3): total
1.70s -> 1.36s vs the big-M base, 1.93s -> 1.36s vs main; every case
equal or better than main (021 root-solves again: 0 nodes/0.09s,
healing the base branch's 10-node drift; 017 root-solves; 016
1256 -> 189 iterations). Full objectives match the base to <= 3e-6.
Golden objective_value for 013/019 re-lands on other plateau points
(019 back to main's original value) — optimum unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@andig

andig commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

@ekkea found this during model optimization when porting CBC to Go.

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