From dfac0039f45cc274627cfe48b06759ab3443efcc Mon Sep 17 00:00:00 2001 From: "David L. Woodruff" Date: Sun, 15 Dec 2024 09:15:01 -0800 Subject: [PATCH] add a test and a little doc for the scenario lp writer extension --- doc/src/extensions.rst | 16 +++++++++++++ examples/generic_cylinders.bash | 2 +- mpisppy/tests/test_ef_ph.py | 40 +++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/doc/src/extensions.rst b/doc/src/extensions.rst index e1bf5bc38..1c0df836e 100644 --- a/doc/src/extensions.rst +++ b/doc/src/extensions.rst @@ -15,6 +15,13 @@ extension can be included in some applications, but not others. There are a number of extensions, particularly for PH, that are provided with ``mpi-sppy`` and they provide examples that can be used for the creation of more. Extensions can be found in ``mpisppy.extensions``. +Note that some things (e.g. some xhatters) can be used as a cylinder +or as an extension. A few other things (e.g., cross scenario cuts) need +both an extension and a cylinder. + +Many extensions are supported in :ref:`generic_cylinders`. The rest of +this help file describes extensions released with mpisppy along with +some hints for including them in your own cylinders driver program. Multiple Extensions ------------------- @@ -266,3 +273,12 @@ If some variables have zero probability in all scenarios, then you will need to ``do_not_check_variable_probabilities`` to True in the options for ``spbase``. This will result in skipping the checks for all variable probabilities! So you might want to set this to False to verify that the probabilities sum to one only for the Vars you expect before setting it to True. + +Scenario_lpwriter +----------------- + +This extension writes an lp file with the model and json file with (a) list(s) of +scenario tree node names and nonanticaptive variables for each scenario before +the iteration zero solve of PH or APH. Note that for two-stage problems, all +json files will be the same. See ``mpisppy.generic_cylinders.py`` +for an example of use. diff --git a/examples/generic_cylinders.bash b/examples/generic_cylinders.bash index 5b9ae6b8b..f9f1569cd 100644 --- a/examples/generic_cylinders.bash +++ b/examples/generic_cylinders.bash @@ -20,7 +20,7 @@ echo "^^^ unpickle the sizes bundles and write the lp and nonant files ^^^" cd sizes python ../../mpisppy/generic_cylinders.py --module-name sizes --num-scens 10 --default-rho 1 --solver-name ${SOLVER} --max-iterations 0 --scenario-lpfiles --unpickle-bundles-dir sizes_pickles --scenarios-per-bundle 5 cd .. -echo "xxx also write a test?" +echo "xxxx Early exit. xxxx" exit echo "^^^ pickle the scenarios ^^^" diff --git a/mpisppy/tests/test_ef_ph.py b/mpisppy/tests/test_ef_ph.py index 8a1154688..76c81eadb 100644 --- a/mpisppy/tests/test_ef_ph.py +++ b/mpisppy/tests/test_ef_ph.py @@ -15,6 +15,7 @@ import os import glob +import json import unittest import pandas as pd import pyomo.environ as pyo @@ -379,6 +380,45 @@ def test_xhat_extension(self): xhatobj1 = round_pos_sig(ph.extobject._xhat_looper_obj_final, 1) self.assertEqual(xhatobj1, 200000) + + @unittest.skipIf(not pyo.SolverFactory('glpk').available(), + "glpk is not available") + def test_scenario_lpwriter_extension(self): + print("test scenarip_lpwriter") + from mpisppy.extensions.scenario_lpfiles import Scenario_lpfiles + options = self._copy_of_base_options() + options["iter0_solver_options"] = {"mipgap": 0.1} + options["PHIterLimit"] = 0 + options["solver_name"] = "glpk" + options["tee_rank0_solves"] = True + + ph = mpisppy.opt.ph.PH( + options, + self.all3_scenario_names, + scenario_creator, + scenario_denouement, + scenario_creator_kwargs={"scenario_count": 3}, + extensions=Scenario_lpfiles, + ) + conv, basic_obj, tbound = ph.ph_main() + # The idea is to detect a change in Pyomo's writing of lp files + with open("Scenario1_nonants.json", "r") as jfile: + nonants_by_node = json.load(jfile) + vname = nonants_by_node["ROOT"][0] # first name in the file + gotit = False + with open("Scenario1.lp", 'r') as lpfile: + for line in lpfile: + if vname in line: + gotit = True + break + assert gotit, f"The first nonant in Scenario1_nonants.json ({vname}) not found in Scenario1.lp" + print(" deleting Scenario*.p and Scenario*_nonants.json") + for fn in glob.glob("Scenario*.lp"): + os.remove(fn) + for fn in glob.glob("Scenario*_nonants.json"): + os.remove(fn) + + @unittest.skipIf(not solver_available, "no solver is available") def test_wtracker_extension(self):