Mutable scenario probabilities (issue #797) - #799
Open
DLWoodruff wants to merge 6 commits into
Open
Conversation
Design proposal for representing scenario probabilities as opt-in mutable Pyomo Params so EF probabilities can be updated between persistent-solver solves without rebuilding the model. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…zation Mutable probability is a full-EF feature and bundles never set the flag, so requiring sum-to-1 lets the mutable path drop the objective divisor entirely. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rding - set_objective re-extracts mutable Params on appsi_highs; appsi auto-tracks param/objective changes on next solve (legacy persistent requires the push). - appsi_highs is not recognized as persistent by mpi-sppy today; detection must be extended to APPSI / pyomo.contrib.solver (phase 0 prerequisite). - reuse_instance is an explicit argument to solve_extensive_form. - Replace "spike" jargon with "experiment". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lets an ExtensiveForm's scenario probabilities be updated in place and re-solved on a persistent solver without rebuilding the model -- the rolling-horizon / probability-sensitivity use case from the issue. Phase 0 (detection): - Add sputils.has_persistent_solve_api(): recognizes both the legacy PersistentSolver and the APPSI / pyomo.contrib.solver interfaces (e.g. appsi_highs) by the set_instance/set_objective/load_vars trio. Kept separate from is_persistent() on purpose: broadening is_persistent would break PH/FWPH call sites that call update_var/add_var/add_constraint, which the APPSI legacy wrapper does not expose. - ExtensiveForm.solve_extensive_form now uses it in place of the old '"persistent" in solver_name' and is_persistent() checks, so appsi_highs finally takes the persistent path (previously it did not). Phase 1 (feature): - _create_EF_from_scen_dict(mutable_probability=False): when True, store probabilities as a mutable Param (_mpisppy_model.prob) referenced by the objective, require the probabilities to sum to 1, and drop the divisor (design option B). Rejected for bundles (sum < 1). - ExtensiveForm gains mutable_probability (kwarg or option key), set_scenario_probabilities() (validate-then-apply, transactional; re-pushes the objective to a persistent solver), and a reuse_instance argument to solve_extensive_form() to skip set_instance on re-solves. Verified end-to-end on farmer against a rebuild oracle: machine-precision agreement across a probability sweep with set_instance called once, for both appsi_highs (auto-tracks) and gurobi_persistent (needs the set_objective re-push). Tests: Test_mutable_probability in test_ef_ph.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e 2) Add SPBase.set_scenario_probabilities(prob_map, check_sum=True, reset_ph_duals=True): updates _mpisppy_probability on local scenarios and forces prob_coeff to be recomputed (new force= flag on _compute_unconditional_node_probabilities bypasses the compute-once short-circuit). Two-stage only; multistage raises NotImplementedError. reset_ph_duals (default True) zeroes the PH multipliers W: at a converged PH consensus, xbar is weight-independent, so re-solving with new probabilities but stale W falsely reports convergence at the old optimum. Zeroing W breaks the consensus so a re-solve tracks the new probabilities. Tests (Test_mutable_probability_ph in test_ef_ph.py): prob_coeff refresh, guards, fresh-PH-vs-EF-oracle match, in-place reuse match, and the reset_ph_duals=False stuck-consensus case. Full file 38 passed/1 skipped; ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e 4) Add a probability-sensitivity example and user docs for the mutable scenario probability feature. - examples/farmer/farmer_prob_sensitivity.py: builds a farmer EF once with mutable_probability=True, then sweeps the weight on one scenario, re-solving with reuse_instance=True so a persistent solver keeps its loaded instance across the sweep. Reports objective and first-stage acreage per vector. - doc/src/mutable_probability.rst: user docs for the EF and PH paths, the sum-to-1 requirement, persistent-solver reuse, and the reset_ph_duals consensus caveat; linked from index.rst after ef.rst. - design doc: mark phase 4 done; record the deliberate omission of a generic_cylinders --mutable-probability flag (no EF path to consume it; a sweep is a driver loop, so the example script is the CLI exposure). Verified: example runs on farmer with gurobi_persistent; docs build with sphinx and autodoc resolves both set_scenario_probabilities methods; ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #799 +/- ##
==========================================
+ Coverage 76.18% 76.21% +0.02%
==========================================
Files 169 169
Lines 22224 22287 +63
==========================================
+ Hits 16932 16986 +54
- Misses 5292 5301 +9 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Collaborator
|
Since you included PH -- I'm guessing just disabling prox is not a good thing, as you could still converge immediately for some problems. But could we do something less extreme than resetting W, and just do a partial reset by taking a convex combination of the converged re-normalized or re-projected W and 0? Or, we can disable prox and keep the re-normalized or re-projected W's for the first iteration. |
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.
Mutable scenario probabilities (issue #797)
Closes #797.
Today each scenario's probability is folded into the Extensive Form (EF)
objective as a floating-point constant when the model is built, so changing a
probability requires rebuilding the objective and, for a persistent solver,
re-loading the instance. This PR adds an opt-in
mutable_probabilitymode thatstores each probability as a mutable Pyomo
Paramin the objective, so theprobability vector can be updated in place and a persistent solver re-solved
cheaply — the motivating use case in the issue (a rolling-horizon loop that
re-weights a fixed scenario set between solves), and probability-sensitivity
studies.
What's in this PR
sputils.has_persistent_solve_apirecognizesthe APPSI /
pyomo.contrib.solverinterface (e.g.appsi_highs, thesolver in the issue) as persistent for the EF workflow, in addition to legacy
PersistentSolver. Kept separate fromis_persistent()on purpose: APPSI'sLegacySolverlacks theupdate_var/add_var/add_constraintmethods thatthe PH/FWPH sites call behind
is_persistent, so broadening that would breakthem.
mutable_probabilityoption onExtensiveForm/sputils._create_EF_from_scen_dictbuilds the objective against a mutableParam;ExtensiveForm.set_scenario_probabilities(prob_map)validates andapplies a new vector (transactionally) and re-pushes the objective;
solve_extensive_form(reuse_instance=True)skipsset_instanceso thepersistent solver keeps its loaded instance across a sweep.
SPBase.set_scenario_probabilities(prob_map, check_sum=True, reset_ph_duals=True)updates_mpisppy_probability, forcesprob_coefftobe recomputed, re-applies any variable-probability overrides, and (by
default) zeroes the PH multipliers
W. Two-stage for now.doc/src/mutable_probability.rstand a runnableexamples/farmer/farmer_prob_sensitivity.pythat sweeps the weight on onescenario and reuses the persistent instance across the sweep.
Normalization: require sum-to-1 (option B)
On the mutable path the supplied probabilities must sum to 1 (within
1e-9);there is no re-normalization, so the objective stays a plain probability-weighted
sum with no division node. The existing float path — including the normalization
divisor that scenario bundles rely on — is untouched, because the mutable
flag is opt-in and bundles never set it (requesting
mutable_probabilityfor abundle raises). Partial mappings are allowed: omitted scenarios keep their
current probability as long as the full vector still sums to 1. A rejected call
leaves the model unchanged.
PH warm-start caveat (why
reset_ph_dualsdefaults to True)At a converged PH solution every scenario sits at the same nonanticipative
point, so the probability-weighted
xbaris independent of the weights.Re-solving from there with new probabilities but stale
Wleavesxbarunmoved and PH reports immediate (false) convergence at the old solution.
Zeroing
Wbreaks that consensus so PH re-converges for the new probabilities.Covered by a regression test that shows
reset_ph_duals=Falsestays stuck.Cross-solver behavior (verified)
appsi_highs) auto-tracks the objective change on the nextsolve(); the explicitset_objectivere-push is redundant but harmless.gurobi_persistent) does not auto-track, so there-push is required —
set_scenario_probabilitiesdoes it.Verified end-to-end on the farmer example against a rebuild oracle to machine
precision across a probability sweep, with
set_instancecalled once, for bothappsi_highsandgurobi_persistent.Tests
Test_mutable_probabilityandTest_mutable_probability_phinmpisppy/tests/test_ef_ph.py(EF-vs-rebuild-oracle across a sweep, singleset_instance, guards/transactionality, PHprob_coeffrefresh,fresh-PH-vs-EF-oracle, in-place reuse, and the stuck-consensus case). Full file
passes;
ruffclean; docs build.Design and follow-up
doc/designs/mutable_scenario_probabilities_design.mdrecords the fullrationale. A follow-up (design §9 phase 3) will extend
SPBase.set_scenario_probabilitiesto multistage node probabilities(
ScenarioNode.cond_prob) and the variable-probability interaction; amultistage node list currently raises
NotImplementedError.