VSS: Value of the Stochastic Solution report (--vss, two-stage)#791
Draft
DLWoodruff wants to merge 5 commits into
Draft
VSS: Value of the Stochastic Solution report (--vss, two-stage)#791DLWoodruff wants to merge 5 commits into
DLWoodruff wants to merge 5 commits into
Conversation
Add doc/designs/vss_design.md: a post-run --vss report in generic_cylinders that computes VSS = EEV - RP. Reuses shipped machinery (average_scenario_creator for the mean-value solve; Xhat_Eval.evaluate for the honest cross-scenario EEV; wheel/EF for RP), mirroring the do_mmw post-run report structure. Covers the minimization/maximization sign convention, the EF-exact vs decomposition-bracketed RP, the infeasible-mean-value-solution case, and a prominent warning that EEV re-solves every scenario and can roughly double run time. Two-stage first; multistage plumbing (ef_xhat_nonants) noted as future. Closes the pysp_but_not_mpisppy.md A7 gap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-stage) generic_cylinders --vss prints RP / EV / EEV / VSS after an --EF or a decomposition run. Reuses shipped machinery: average_scenario_creator for the mean-value solve (x_bar) and Xhat_Eval.evaluate for the honest cross-scenario EEV; RP is exact from the EF or the incumbent (with a BestOuterBound/BestInnerBound bracket) from the wheel. Structure mirrors do_mmw: new mpisppy/generic/vss.py (vss_prep + do_vss), one config flag, two call sites in generic_cylinders. - Sign-aware: VSS = EEV-RP (min) or RP-EEV (max), always reported >= 0. - Decomposition run reports a conservative point value plus the VSS bracket implied by the unclosed optimality gap. - Infeasible mean-value first stage => EEV = VSS = +inf, naming the offending scenarios (MPI-safe: solves with need_solution=False so a clean per-scenario infeasibility does not deadlock the collectives). - Two-stage only; fails fast at setup (before the solve) on multistage, proper bundles, ADMM, --cvar, or a missing average_scenario_creator. Tests: mpisppy/tests/test_vss.py (guards, sign/bracket helpers, EF min/max e2e, decomposition bracket, infeasible EEV), wired into run_coverage.bash and test_pr_and_main.yml. Docs: doc/src/vss.rst (+ index toctree); pysp_but_not_mpisppy.md A7 marked addressed for two-stage. Design in doc/designs/vss_design.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The unit-tests job installs no solver, so test_vss's solver-gated e2e cases (the ones asserting the actual RP/EV/EEV/VSS numbers on farmer) would skip there. Add a dedicated step in the cplex/xpress regression job -- mirroring test_feasible_xhat -- so the numerical results are actually checked in CI. The farmer-3 cases are LPs with a unique, solver-independent optimum, well within community-cplex size limits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gression job Like test_vss, test_jensens was only in the no-solver unit-tests job, so its solver-gated end-to-end cases (farmer-3 average-scenario bound, sizes-3 integer-guard paths) skipped in CI. Add a dedicated regression-job step so they actually run under cplex/xpress. Both models are small and within community-cplex limits; the file is green locally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #791 +/- ##
==========================================
+ Coverage 76.16% 76.23% +0.06%
==========================================
Files 169 170 +1
Lines 22224 22373 +149
==========================================
+ Hits 16928 17057 +129
- Misses 5296 5316 +20 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
… solves The EV and EEV solves were passing no solver options, so they ran at the solver's default mipgap -- inconsistent with the run that produced RP, and uncontrollable. Resolve solver name + options via solver_specification (EF_solver_options after --EF, else solver_options) and apply them to both the mean-value solve and the Xhat_Eval EEV pass. Also lets --vss work on an --EF run that sets only --EF-solver-name. Because VSS = EEV - RP is a difference of two optimized values, mipgap matters; document that the VSS solves inherit the run's solver-options string and that a loose gap makes a small VSS unreliable. Also reconcile the cost docs: drop the "roughly doubles run time on large/ integer models" claim (backwards -- fixing the first stage decouples the scenarios and makes each EEV solve easier; for decomposition EEV is ~one iteration's worth). State honestly that the added time is model-dependent and can be significant for some problems. Adds test_ef_solver_options_threaded; updates _compute_eev's signature and its infeasible-path test caller. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
What
Adds a Value of the Stochastic Solution (VSS) report to
generic_cylinders. Passing--vssprintsRP / EV / EEV / VSSat theend of an
--EFor a decomposition run. Two-stage, opt-in, report-only(it never changes the solution). Closes the two-stage part of the
pysp_but_not_mpisppy.md§A7 gap (PySP'sef_vss.py).Example (farmer, 3 scenarios):
How
Mirrors the
do_mmwpost-run report and reuses shipped machinery — nonew spoke/hub code:
average_scenario_creator(the Jensen's-bound contract) → the mean-valuesolve, giving
EVand the first-stagex_bar;Xhat_Eval.evaluate→ the honest cross-scenarioEEV(fixx_bar,solve every scenario);
RPis exact fromef.get_objective_value(), or the incumbent(
BestInnerBound) with aBestOuterBound/BestInnerBoundbracket fromthe wheel.
New
mpisppy/generic/vss.py(vss_prep+do_vss), one config flag, twocall sites in
generic_cylinders.py.Behavior details
VSS = EEV − RP(min) orRP − EEV(max), alwaysreported ≥ 0.
the VSS bracket implied by any unclosed optimality gap.
and
solver_optionsviasolver_specification—EF_solver_optionsafter
--EF, elsesolver_options— so all three numbers are solvedconsistently and any
mipgapin that string applies. SinceVSS = EEV − RPis a difference of two optimized values, a loose gap makes a small VSS
unreliable (documented). (Also lets
--vsswork on an--EFrun thatsets only
--EF-solver-name.)EEV = VSS = +inf, naming the offending scenarios. MPI-safe: solves withneed_solution=Falseso a clean per-scenario infeasibility does notdeadlock the collectives.
ADMM,
--cvar, or a missingaverage_scenario_creator.Cost
EEVre-solves every scenario once with the first stage fixed. Fixing thefirst stage decouples the scenarios and makes each solve easier than the
original, so the added wall-clock is model-dependent — usually a fraction
of the run (for a decomposition run, ~one iteration's worth), occasionally
significant with very many scenarios or expensive recourse. Off by default.
Tests
mpisppy/tests/test_vss.py: setup guards, sign/bracket helpers, EFminimize and maximize end-to-end (asserting the actual RP/EV/EEV/VSS
numbers), solver-options threading, the decomposition bracket, and the
infeasible-EEV path. Wired into
run_coverage.bashand a dedicated step inthe solver-equipped
regressionCI job so the numerical assertions rununder cplex/xpress (not just the no-solver unit job).
Drive-by:
test_jensens.pyhad the same wiring gap (only in theno-solver job); added a
regression-job step so its solver-gated e2e casesrun in CI too.
Docs
doc/src/vss.rst(in the toctree; explains VSS, the honest cost note, thesolver-options/mipgap reuse, the EF-exact vs bracketed-RP distinction, and
infeasibility);
doc/designs/vss_design.md(design);pysp_but_not_mpisppy.md§A7 marked addressed for two-stage.Scope / follow-ups
Two-stage only. Multistage VSS (the expected-value tree via
ef_xhat_nonants) and EVPI are noted as future work in the design doc. V1threads the solver-options string (not the separate
EF_mipgap/*_iter*_mipgapknobs or a*_solver_options_file).🤖 Generated with Claude Code