feat: a spec reports how its optimum moves with a parameter, which for a fixed decision is a Benders cut - #749
Closed
FBumann wants to merge 11 commits into
Conversation
…r a fixed decision is a Benders cut Spec.sensitivity(*names) adds a reported expression <name>_sensitivity for each named parameter: the objective's own rate in it, plus dual(c) times the rate of rhs - lhs for each row that reads it. named.md now defines dual(c) as the rate at which the optimal objective rises with the right side of c. That is what specsolve returns. It gives an equality a sign, and it makes the rate the same expression under either sense. The old wording made a <= dual non-negative under minimize, which specsolve does not return. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
Documentation build overview
60 files changed ·
|
This was referenced Sep 27, 2026
FBumann
added this pull request to stack #754
September 28, 2026 10:56
…ec-analysis-features-zy7x4n-sensitivity Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
…ec-analysis-features-zy7x4n-sensitivity Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
…ec-analysis-features-zy7x4n-sensitivity Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
…ec-analysis-features-zy7x4n-sensitivity Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
…ec-analysis-features-zy7x4n-sensitivity Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
…ec-analysis-features-zy7x4n-sensitivity Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
…ec-analysis-features-zy7x4n-sensitivity Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
…ec-analysis-features-zy7x4n-sensitivity Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
…ec-analysis-features-zy7x4n-sensitivity named.md takes the dual definition as #751 merged it, and keeps the paragraph on spec.sensitivity after it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
Contributor
Author
Note The following content was generated by AI. Superseded by #826, which carries this diff onto #824 (the port of #748). It also refuses a given parameter with a message that names it as given. Generated by Claude Code |
FBumann
pushed a commit
that referenced
this pull request
Oct 2, 2026
…ich for a fixed decision is a Benders cut spec.sensitivity(*names) reports <name>_sensitivity, the rise of the optimal objective per unit of a parameter, written over dual(). It carries arithmetic, sum(over=) and named expressions from a row down to the parameter, and refuses a bound, a divisor, a masked row read pointwise, and any other operator on the path. Ported from #749 onto spec.fix. A given parameter is refused with a message that names it as given. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XjTvqu5LubaVCgWeKogCqj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
The following content was generated by AI.
What this changes
spec.sensitivity(*names)reports<name>_sensitivity, the rise of the optimal objective per unit of a parameter, written overdual(). It depends on #751, which definesdual(c)as the rate in the right side ofc. Stacked on #748.The dual it reads, evidence, rules, gates
The dual it reads
The formula is
Σ_c dual(c) · ∂(rhs − lhs)/∂q, plus the objective's own∂f/∂q. It is one expression whatever the comparator and the sense, because #751 definesdual(c)as the rate at which the optimal objective rises with the right side ofc. That is what every specsolve sink returns; fluxopt/specsolve#1774 pins it. This PR carries the samenamed.mdhunk as #751. Once #751 merges, the hunk is already onmain.Evidence: finite differences on HiGHS
A scratch venv with this branch and specsolve
main. The model is specsolve's Benders monolith,fix('cap'), thensensitivity('cap', 'load'), atcap = [60, 90], with a step of1e-3:cap[wind]cap[gas]load[0..3]cap,balanceas==cap, undermaximizeof the negated costUnder the old wording of
dual(c)the derivation writesinvest - sum(dual(capacity) * avail, …), which gives 112.5 forcap[wind]against a true 67.5.The derived expressions:
What it carries, and what it refuses
sum(over=)and named expressions, from the row down to the parameter. On the way it multiplies by each factor, and at the parameter it sums back over every dimension the parameter does not carry.shift,at,sum(by=),sum_backor a case between the row and the parameter; the parameter as a divisor; a sum over a dimension the factor outside it also carries.where:that reads the parameter pointwise, because the absent dual would take the whole sum with it (a parameter cannot say that a missing row is absent, so a decision supplied as data brings back the rows its mask deleted #750 names the reverse case).where:masks and assumptions that read the parameter are steps the rate does not see. The docstring says so.Guards
on-the-left,divideda-bounda-masked-row-read-pointwisea-sum-over-a-dim-the-factor-outside-carriesa-divisorGates
pixi run lint: pass.pixi run test: 1834 passed.docs-buildandcompile-texwere not run in the session (network). CI runs both.Not done
shift,at,sum(by=)and cases on the path. The transpose of each is an operator the language has (sum(by=)is the adjoint ofat), exceptsum_back, whose adjoint would besum_forward#49'ssum_forward.Why
A Benders loop needs the cut slope, and a modeller reads the marginal value of capacity or load. Both are this derivative, and a driver that writes it by hand has to get the dual's sign right; the evidence above shows how easily that goes wrong.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS