From 39de7df92818da0d2dd5fd4ae47af2f35ef944ce Mon Sep 17 00:00:00 2001 From: "David L. Woodruff" Date: Thu, 21 Nov 2024 18:28:09 -0800 Subject: [PATCH] working tests; still need a GAMS test --- .../farmer/AMPL/AMPLpy_examples/AAAreadme.txt | 1 - .../farmer/AMPL/AMPLpy_examples/diet_model.py | 162 ------------------ .../AMPLpy_examples/efficient_frontier.py | 91 ---------- .../AMPL/AMPLpy_examples/first_example.py | 81 --------- .../AMPL/AMPLpy_examples/models/diet.dat | 31 ---- .../AMPL/AMPLpy_examples/models/diet.mod | 18 -- .../AMPL/AMPLpy_examples/options_example.py | 51 ------ .../AMPL/AMPLpy_examples/tracking_model.py | 91 ---------- examples/farmer/GAMS/modify.py | 61 ------- examples/farmer/farmer_ampl_agnostic.py | 9 +- mpisppy/agnostic/agnostic_cylinders.py | 47 +++-- mpisppy/tests/test_agnostic.py | 134 +++++++++------ 12 files changed, 115 insertions(+), 662 deletions(-) delete mode 100644 examples/farmer/AMPL/AMPLpy_examples/AAAreadme.txt delete mode 100644 examples/farmer/AMPL/AMPLpy_examples/diet_model.py delete mode 100644 examples/farmer/AMPL/AMPLpy_examples/efficient_frontier.py delete mode 100644 examples/farmer/AMPL/AMPLpy_examples/first_example.py delete mode 100644 examples/farmer/AMPL/AMPLpy_examples/models/diet.dat delete mode 100644 examples/farmer/AMPL/AMPLpy_examples/models/diet.mod delete mode 100644 examples/farmer/AMPL/AMPLpy_examples/options_example.py delete mode 100644 examples/farmer/AMPL/AMPLpy_examples/tracking_model.py delete mode 100644 examples/farmer/GAMS/modify.py diff --git a/examples/farmer/AMPL/AMPLpy_examples/AAAreadme.txt b/examples/farmer/AMPL/AMPLpy_examples/AAAreadme.txt deleted file mode 100644 index 64dbf942c..000000000 --- a/examples/farmer/AMPL/AMPLpy_examples/AAAreadme.txt +++ /dev/null @@ -1 +0,0 @@ -Examples from AMPL diff --git a/examples/farmer/AMPL/AMPLpy_examples/diet_model.py b/examples/farmer/AMPL/AMPLpy_examples/diet_model.py deleted file mode 100644 index 8f45f4d14..000000000 --- a/examples/farmer/AMPL/AMPLpy_examples/diet_model.py +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import sys -import os -import pandas as pd # for pandas.DataFrame objects (https://pandas.pydata.org/) -import numpy as np # for numpy.matrix objects (https://numpy.org/) - - -def prepare_data(): - food_df = pd.DataFrame( - [ - ("BEEF", 3.59, 2, 10), - ("CHK", 2.59, 2, 10), - ("FISH", 2.29, 2, 10), - ("HAM", 2.89, 2, 10), - ("MCH", 1.89, 2, 10), - ("MTL", 1.99, 2, 10), - ("SPG", 1.99, 2, 10), - ("TUR", 2.49, 2, 10), - ], - columns=["FOOD", "cost", "f_min", "f_max"], - ).set_index("FOOD") - - # Create a pandas.DataFrame with data for n_min, n_max - nutr_df = pd.DataFrame( - [ - ("A", 700, 20000), - ("C", 700, 20000), - ("B1", 700, 20000), - ("B2", 700, 20000), - ("NA", 0, 50000), - ("CAL", 16000, 24000), - ], - columns=["NUTR", "n_min", "n_max"], - ).set_index("NUTR") - - amt_df = pd.DataFrame( - np.array( - [ - [60, 8, 8, 40, 15, 70, 25, 60], - [20, 0, 10, 40, 35, 30, 50, 20], - [10, 20, 15, 35, 15, 15, 25, 15], - [15, 20, 10, 10, 15, 15, 15, 10], - [928, 2180, 945, 278, 1182, 896, 1329, 1397], - [295, 770, 440, 430, 315, 400, 379, 450], - ] - ), - columns=food_df.index.to_list(), - index=nutr_df.index.to_list(), - ) - return food_df, nutr_df, amt_df - - -def main(argc, argv): - # You can install amplpy with "python -m pip install amplpy" - from amplpy import AMPL - - os.chdir(os.path.dirname(__file__) or os.curdir) - - """ - # If you are not using amplpy.modules, and the AMPL installation directory - # is not in the system search path, add it as follows: - from amplpy import add_to_path - add_to_path(r"full path to the AMPL installation directory") - """ - - # Create an AMPL instance - ampl = AMPL() - - # Set the solver to use - solver = argv[1] if argc > 1 else "highs" - ampl.set_option("solver", solver) - - ampl.eval( - r""" - set NUTR; - set FOOD; - - param cost {FOOD} > 0; - param f_min {FOOD} >= 0; - param f_max {j in FOOD} >= f_min[j]; - - param n_min {NUTR} >= 0; - param n_max {i in NUTR} >= n_min[i]; - - param amt {NUTR,FOOD} >= 0; - - var Buy {j in FOOD} >= f_min[j], <= f_max[j]; - - minimize Total_Cost: - sum {j in FOOD} cost[j] * Buy[j]; - - subject to Diet {i in NUTR}: - n_min[i] <= sum {j in FOOD} amt[i,j] * Buy[j] <= n_max[i]; - """ - ) - - # Load the data from pandas.DataFrame objects: - food_df, nutr_df, amt_df = prepare_data() - # 1. Send the data from "amt_df" to AMPL and initialize the indexing set "FOOD" - ampl.set_data(food_df, "FOOD") - # 2. Send the data from "nutr_df" to AMPL and initialize the indexing set "NUTR" - ampl.set_data(nutr_df, "NUTR") - # 3. Set the values for the parameter "amt" using "amt_df" - ampl.get_parameter("amt").set_values(amt_df) - - # Solve - ampl.solve() - - # Get objective entity by AMPL name - totalcost = ampl.get_objective("Total_Cost") - # Print it - print("Objective is:", totalcost.value()) - - # Reassign data - specific instances - cost = ampl.get_parameter("cost") - cost.set_values({"BEEF": 5.01, "HAM": 4.55}) - print("Increased costs of beef and ham.") - - # Resolve and display objective - ampl.solve() - assert ampl.solve_result == "solved" - print("New objective value:", totalcost.value()) - - # Reassign data - all instances - cost.set_values( - { - "BEEF": 3, - "CHK": 5, - "FISH": 5, - "HAM": 6, - "MCH": 1, - "MTL": 2, - "SPG": 5.01, - "TUR": 4.55, - } - ) - - print("Updated all costs.") - - # Resolve and display objective - ampl.solve() - assert ampl.solve_result == "solved" - print("New objective value:", totalcost.value()) - - # Get the values of the variable Buy in a pandas.DataFrame object - df = ampl.get_variable("Buy").get_values().to_pandas() - # Print them - print(df) - - # Get the values of an expression into a pandas.DataFrame object - df2 = ampl.get_data("{j in FOOD} 100*Buy[j]/Buy[j].ub").to_pandas() - # Print them - print(df2) - - -if __name__ == "__main__": - try: - main(len(sys.argv), sys.argv) - except Exception as e: - print(e) - raise diff --git a/examples/farmer/AMPL/AMPLpy_examples/efficient_frontier.py b/examples/farmer/AMPL/AMPLpy_examples/efficient_frontier.py deleted file mode 100644 index bcd79208c..000000000 --- a/examples/farmer/AMPL/AMPLpy_examples/efficient_frontier.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import sys -import os - - -def main(argc, argv): - # You can install amplpy with "python -m pip install amplpy" - from amplpy import AMPL - - os.chdir(os.path.dirname(__file__) or os.curdir) - model_directory = os.path.join(os.curdir, "models", "qpmv") - - """ - # If you are not using amplpy.modules, and the AMPL installation directory - # is not in the system search path, add it as follows: - from amplpy import add_to_path - add_to_path(r"full path to the AMPL installation directory") - """ - - # Create an AMPL instance - ampl = AMPL() - - # Number of steps of the efficient frontier - steps = 10 - - ampl.set_option("reset_initial_guesses", True) - ampl.set_option("send_statuses", False) - ampl.set_option("solver", "cplex") - - # Load the AMPL model from file - ampl.read(os.path.join(model_directory, "qpmv.mod")) - ampl.read(os.path.join(model_directory, "qpmvbit.run")) - - # Set tables directory (parameter used in the script above) - ampl.get_parameter("data_dir").set(model_directory) - # Read tables - ampl.read_table("assetstable") - ampl.read_table("astrets") - - portfolio_return = ampl.getVariable("portret") - average_return = ampl.get_parameter("averret") - target_return = ampl.get_parameter("targetret") - variance = ampl.get_objective("cst") - - # Relax the integrality - ampl.set_option("relax_integrality", True) - # Solve the problem - ampl.solve() - # Calibrate the efficient frontier range - minret = portfolio_return.value() - maxret = ampl.get_value("max {s in stockall} averret[s]") - stepsize = (maxret - minret) / steps - returns = [None] * steps - variances = [None] * steps - for i in range(steps): - print(f"Solving for return = {maxret - i * stepsize:g}") - # Set target return to the desired point - target_return.set(maxret - i * stepsize) - ampl.eval("let stockopall:={};let stockrun:=stockall;") - # Relax integrality - ampl.set_option("relax_integrality", True) - ampl.solve() - print(f"QP result = {variance.value():g}") - # Adjust included stocks - ampl.eval("let stockrun:={i in stockrun:weights[i]>0};") - ampl.eval("let stockopall:={i in stockrun:weights[i]>0.5};") - # Set integrality back - ampl.set_option("relax_integrality", False) - # Solve the problem - ampl.solve() - # Check if the problem was solved successfully - if ampl.solve_result != "solved": - raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") - print(f"QMIP result = {variance.value():g}") - # Store data of corrent frontier point - returns[i] = maxret - (i - 1) * stepsize - variances[i] = variance.value() - - # Display efficient frontier points - print("RETURN VARIANCE") - for i in range(steps): - print(f"{returns[i]:-6f} {variances[i]:-6f}") - - -if __name__ == "__main__": - try: - main(len(sys.argv), sys.argv) - except Exception as e: - print(e) - raise diff --git a/examples/farmer/AMPL/AMPLpy_examples/first_example.py b/examples/farmer/AMPL/AMPLpy_examples/first_example.py deleted file mode 100644 index d5acf4405..000000000 --- a/examples/farmer/AMPL/AMPLpy_examples/first_example.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import sys -import os - - -def main(argc, argv): - # You can install amplpy with "python -m pip install amplpy" - from amplpy import AMPL - - os.chdir(os.path.dirname(__file__) or os.curdir) - model_directory = os.path.join(os.curdir, "models") - - """ - # If you are not using amplpy.modules, and the AMPL installation directory - # is not in the system search path, add it as follows: - from amplpy import add_to_path - add_to_path(r"full path to the AMPL installation directory") - """ - - # Create an AMPL instance - ampl = AMPL() - - # Set the solver to use - solver = argv[1] if argc > 1 else "highs" - ampl.set_option("solver", solver) - - # Read the model and data files. - ampl.read(os.path.join(model_directory, "diet.mod")) - ampl.read_data(os.path.join(model_directory, "diet.dat")) - - # Solve - ampl.solve() - if ampl.solve_result != "solved": - raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") - - # Get objective entity by AMPL name - totalcost = ampl.get_objective("Total_Cost") - # Print it - print("Objective is:", totalcost.value()) - - # Reassign data - specific instances - cost = ampl.get_parameter("cost") - cost.set_values({"BEEF": 5.01, "HAM": 4.55}) - print("Increased costs of beef and ham.") - - # Resolve and display objective - ampl.solve() - if ampl.solve_result != "solved": - raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") - print("New objective value:", totalcost.value()) - - # Reassign data - all instances - elements = [3, 5, 5, 6, 1, 2, 5.01, 4.55] - cost.set_values(elements) - print("Updated all costs.") - - # Resolve and display objective - ampl.solve() - if ampl.solve_result != "solved": - raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") - print("New objective value:", totalcost.value()) - - # Get the values of the variable Buy in a dataframe object - buy = ampl.get_variable("Buy") - df = buy.get_values() - # Print as pandas dataframe - print(df.to_pandas()) - - # Get the values of an expression into a DataFrame object - df2 = ampl.get_data("{j in FOOD} 100*Buy[j]/Buy[j].ub") - # Print as pandas dataframe - print(df2.to_pandas()) - - -if __name__ == "__main__": - try: - main(len(sys.argv), sys.argv) - except Exception as e: - print(e) - raise diff --git a/examples/farmer/AMPL/AMPLpy_examples/models/diet.dat b/examples/farmer/AMPL/AMPLpy_examples/models/diet.dat deleted file mode 100644 index 1ed333eb9..000000000 --- a/examples/farmer/AMPL/AMPLpy_examples/models/diet.dat +++ /dev/null @@ -1,31 +0,0 @@ -data; - -set NUTR := A B1 B2 C ; -set FOOD := BEEF CHK FISH HAM MCH MTL SPG TUR ; - -param: cost f_min f_max := - BEEF 3.19 0 100 - CHK 2.59 0 100 - FISH 2.29 0 100 - HAM 2.89 0 100 - MCH 1.89 0 100 - MTL 1.99 0 100 - SPG 1.99 0 100 - TUR 2.49 0 100 ; - -param: n_min n_max := - A 700 10000 - C 700 10000 - B1 700 10000 - B2 700 10000 ; - -param amt (tr): - A C B1 B2 := - BEEF 60 20 10 15 - CHK 8 0 20 20 - FISH 8 10 15 10 - HAM 40 40 35 10 - MCH 15 35 15 15 - MTL 70 30 15 15 - SPG 25 50 25 15 - TUR 60 20 15 10 ; diff --git a/examples/farmer/AMPL/AMPLpy_examples/models/diet.mod b/examples/farmer/AMPL/AMPLpy_examples/models/diet.mod deleted file mode 100644 index bed2074cb..000000000 --- a/examples/farmer/AMPL/AMPLpy_examples/models/diet.mod +++ /dev/null @@ -1,18 +0,0 @@ -set NUTR; -set FOOD; - -param cost {FOOD} > 0; -param f_min {FOOD} >= 0; -param f_max {j in FOOD} >= f_min[j]; - -param n_min {NUTR} >= 0; -param n_max {i in NUTR} >= n_min[i]; - -param amt {NUTR,FOOD} >= 0; - -var Buy {j in FOOD} >= f_min[j], <= f_max[j]; - -minimize Total_Cost: sum {j in FOOD} cost[j] * Buy[j]; - -subject to Diet {i in NUTR}: - n_min[i] <= sum {j in FOOD} amt[i,j] * Buy[j] <= n_max[i]; diff --git a/examples/farmer/AMPL/AMPLpy_examples/options_example.py b/examples/farmer/AMPL/AMPLpy_examples/options_example.py deleted file mode 100644 index 15e653031..000000000 --- a/examples/farmer/AMPL/AMPLpy_examples/options_example.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import sys -import os - - -def main(argc, argv): - # You can install amplpy with "python -m pip install amplpy" - from amplpy import AMPL - - os.chdir(os.path.dirname(__file__) or os.curdir) - - """ - # If you are not using amplpy.modules, and the AMPL installation directory - # is not in the system search path, add it as follows: - from amplpy import add_to_path - add_to_path(r"full path to the AMPL installation directory") - """ - - # Create an AMPL instance - ampl = AMPL() - - # Get the value of the option presolve and print - presolve = ampl.get_option("presolve") - print("AMPL presolve is", presolve) - - # Set the value to false (maps to 0) - ampl.set_option("presolve", False) - - # Get the value of the option presolve and print - presolve = ampl.get_option("presolve") - print("AMPL presolve is now", presolve) - - # Check whether an option with a specified name - # exists - value = ampl.get_option("solver") - if value is not None: - print("Option solver exists and has value:", value) - - # Check again, this time failing - value = ampl.get_option("s_o_l_v_e_r") - if value is None: - print("Option s_o_l_v_e_r does not exist!") - - -if __name__ == "__main__": - try: - main(len(sys.argv), sys.argv) - except Exception as e: - print(e) - raise diff --git a/examples/farmer/AMPL/AMPLpy_examples/tracking_model.py b/examples/farmer/AMPL/AMPLpy_examples/tracking_model.py deleted file mode 100644 index 9153c1cc6..000000000 --- a/examples/farmer/AMPL/AMPLpy_examples/tracking_model.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import sys -import os - - -def main(argc, argv): - # You can install amplpy with "python -m pip install amplpy" - from amplpy import AMPL - - os.chdir(os.path.dirname(__file__) or os.curdir) - model_directory = os.path.join(os.curdir, "models", "tracking") - - """ - # If you are not using amplpy.modules, and the AMPL installation directory - # is not in the system search path, add it as follows: - from amplpy import add_to_path - add_to_path(r"full path to the AMPL installation directory") - """ - - # Create an AMPL instance - ampl = AMPL() - - # Set the solver to use - solver = argv[1] if argc > 1 else "highs" - ampl.set_option("solver", solver) - - # Load the AMPL model from file - ampl.read(os.path.join(model_directory, "tracking.mod")) - # Read data - ampl.read_data(os.path.join(model_directory, "tracking.dat")) - # Read table declarations - ampl.read(os.path.join(model_directory, "trackingbit.run")) - # Set tables directory (parameter used in the script above) - ampl.get_parameter("data_dir").set(model_directory) - # Read tables - ampl.read_table("assets") - ampl.read_table("indret") - ampl.read_table("returns") - - hold = ampl.get_variable("hold") - ifinuniverse = ampl.get_parameter("ifinuniverse") - - # Relax the integrality - ampl.set_option("relax_integrality", True) - - # Solve the problem - ampl.solve() - if ampl.solve_result != "solved": - raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") - - objectives = list(obj for name, obj in ampl.get_objectives()) - assert objectives[0].value() == ampl.get_objective("cst").value() - print("QP objective value", ampl.get_objective("cst").value()) - - lowcutoff = 0.04 - highcutoff = 0.1 - - # Get the variable representing the (relaxed) solution vector - holdvalues = hold.get_values().to_list() - to_hold = [] - # For each asset, if it was held by more than the highcutoff, - # forces it in the model, if less than lowcutoff, forces it out - for _, value in holdvalues: - if value < lowcutoff: - to_hold.append(0) - elif value > highcutoff: - to_hold.append(2) - else: - to_hold.append(1) - # uses those values for the parameter ifinuniverse, which controls - # which stock is included or not in the solution - ifinuniverse.set_values(to_hold) - - # Get back to the integer problem - ampl.set_option("relax_integrality", False) - - # Solve the (integer) problem - ampl.solve() - if ampl.solve_result != "solved": - raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") - - print("QMIP objective value", ampl.get_objective("cst").value()) - - -if __name__ == "__main__": - try: - main(len(sys.argv), sys.argv) - except Exception as e: - print(e) - raise diff --git a/examples/farmer/GAMS/modify.py b/examples/farmer/GAMS/modify.py deleted file mode 100644 index 0b30c34f4..000000000 --- a/examples/farmer/GAMS/modify.py +++ /dev/null @@ -1,61 +0,0 @@ -from farmer_augmented import * - -cp = ws.add_checkpoint() - -mi = cp.add_modelinstance() - -model.run(checkpoint=cp) - -crop = mi.sync_db.add_set("crop", 1, "crop type") - -y = mi.sync_db.add_parameter_dc("yield", [crop,], "yield") -rho = mi.sync_db.add_parameter_dc("rho", [crop,], "ph rho") -ph_W = mi.sync_db.add_parameter_dc("ph_W", [crop,], "ph weight") -xbar = mi.sync_db.add_parameter_dc("xbar", [crop,], "ph average") - -W_on = mi.sync_db.add_parameter("W_on", 0, "activate w term") -prox_on = mi.sync_db.add_parameter("prox_on", 0, "activate prox term") - -mi.instantiate("simple min negprofit using nlp", - [ - gams.GamsModifier(rho), - gams.GamsModifier(ph_W), - gams.GamsModifier(xbar), - gams.GamsModifier(y), - gams.GamsModifier(W_on), - gams.GamsModifier(prox_on), - ], -) - -prox_on.add_record().value = 1.0 -W_on.add_record().value = 1.0 - -crop_ext = model.out_db.get_set("crop") -for c in crop_ext: - name = c.key(0) - ph_W.add_record(name).value = 50 - xbar.add_record(name).value = 100 - rho.add_record(name).value = 10 - y.add_record(name).value = 42 - -mi.solve(output=sys.stdout) - -prox_on.find_record().value = 1.0 -W_on.find_record().value = 1.0 - -crop_ext = model.out_db.get_set("crop") -for c in crop_ext: - name = c.key(0) - ph_W.find_record(name).value = 500 - xbar.find_record(name).value = 200 - rho.find_record(name).value = 10 - -mi.solve(output=sys.stdout) - -###prox_on.find_record().value = 0.0 -###W_on.find_record().value = 0.0 -prox_on.find_record().set_value(0.0) -W_on.find_record().set_value(0.0) - -mi.solve(output=sys.stdout) -print(f"{prox_on.find_record() =}") diff --git a/examples/farmer/farmer_ampl_agnostic.py b/examples/farmer/farmer_ampl_agnostic.py index 6513e5d9a..c3576d2e9 100644 --- a/examples/farmer/farmer_ampl_agnostic.py +++ b/examples/farmer/farmer_ampl_agnostic.py @@ -102,7 +102,12 @@ def inparser_adder(cfg): #========= def kw_creator(cfg): # creates keywords for scenario creator - return farmer.kw_creator(cfg) + kwargs = {"use_integer": cfg.get('farmer_with_integers', False), + "crops_multiplier": cfg.get('crops_multiplier', 1), + "num_scens" : cfg.get('num_scens', None), + } + return kwargs + # This is not needed for PH def sample_tree_scen_creator(sname, stage, sample_branching_factors, seed, @@ -243,7 +248,7 @@ def solve_one(Ag, s, solve_keyword_args, gripe, tee): gs = gd["scenario"] # guest scenario handle #### start debugging - if True: # global_rank == 0: + if False: # True: # global_rank == 0: try: WParamDatas = list(gs.get_parameter("W").instances()) print(f" ^^^ in _solve_one {WParamDatas =} {global_rank =}") diff --git a/mpisppy/agnostic/agnostic_cylinders.py b/mpisppy/agnostic/agnostic_cylinders.py index 2c664556d..7e54108c7 100644 --- a/mpisppy/agnostic/agnostic_cylinders.py +++ b/mpisppy/agnostic/agnostic_cylinders.py @@ -14,8 +14,7 @@ import mpisppy.agnostic.agnostic as agnostic import mpisppy.utils.sputils as sputils - -def _parse_args(m): +def _setup_args(m): # m is the model file module # NOTE: try to avoid adding features here that are not supported for agnostic cfg = config.Config() @@ -54,23 +53,23 @@ def _parse_args(m): cfg.lagrangian_args() cfg.lagranger_args() cfg.xhatshuffle_args() + return cfg +def _parse_args(m): + # m is the model file module + cfg = _setup_args(m) cfg.parse_command_line("agnostic cylinders") return cfg - -if __name__ == "__main__": - if len(sys.argv) == 1: - print("need the python model file module name (no .py)") - print("usage, e.g.: python -m mpi4py agnostic_cylinders.py --module-name farmer4agnostic" --help) - quit() - - model_fname = sys.argv[2] - - module = sputils.module_name_to_module(model_fname) - - cfg = _parse_args(module) - +def main(model_fname, module, cfg): + """ main is outside __main__ for testing + + Args: + model_fname (str): the module name from the command line) + module (Python module): from model_fname (redundant) + cfg (Pyomo config object): parsed arguments + """ + supported_guests = {"Pyomo", "AMPL", "GAMS"} # special hack to support bundles if hasattr(module, "bundle_hack"): @@ -87,7 +86,7 @@ def _parse_args(m): Ag = agnostic.Agnostic(pg, cfg) elif cfg.guest_language == "AMPL": assert cfg.ampl_model_file is not None, "If the guest language is AMPL, you need ampl-model-file" - from ampl_guest import AMPL_guest + from mpisppy.agnostic.ampl_guest import AMPL_guest guest = AMPL_guest(model_fname, cfg.ampl_model_file) Ag = agnostic.Agnostic(guest, cfg) @@ -147,4 +146,18 @@ def _parse_args(m): wheel.write_first_stage_solution(f'{cfg.solution_base_name}.npy', first_stage_solution_writer=sputils.first_stage_nonant_npy_serializer) wheel.write_tree_solution(f'{cfg.solution_base_name}') - + + +if __name__ == "__main__": + if len(sys.argv) == 1: + print("need the python model file module name (no .py)") + print("usage, e.g.: python -m mpi4py agnostic_cylinders.py --module-name farmer4agnostic" --help) + quit() + + model_fname = sys.argv[2] + + module = sputils.module_name_to_module(model_fname) + + cfg = _parse_args(module) + + main(model_fname, module, cfg) diff --git a/mpisppy/tests/test_agnostic.py b/mpisppy/tests/test_agnostic.py index 6115e3deb..a00771799 100644 --- a/mpisppy/tests/test_agnostic.py +++ b/mpisppy/tests/test_agnostic.py @@ -9,20 +9,25 @@ from mpisppy.tests.utils import get_solver, round_pos_sig import mpisppy.utils.config as config import mpisppy.agnostic.agnostic as agnostic +import mpisppy.agnostic.agnostic_cylinders as agnostic_cylinders +import mpisppy.utils.sputils as sputils sys.path.insert(0, "../../examples/farmer") import farmer_pyomo_agnostic import farmer_ampl_agnostic -import farmer_gurobipy_agnostic +try: + import farmer_gurobipy_agnostic + have_gurobipy = True +except: + have_gurobipy = False - -__version__ = 0.1 +__version__ = 0.2 solver_available, solver_name, persistent_available, persistent_solver_name = ( get_solver() ) -# NOTE Gurobi is hardwired for the AMPL test, so don't install it on github +# NOTE Gurobi is hardwired for the AMPL and GAMS tests, so don't install it on github # (and, if you have gurobi installed the ampl test will fail) @@ -60,60 +65,57 @@ def _get_ph_base_options(): # ***************************************************************************** -# class Test_Agnostic_pyomo(unittest.TestCase): -# -# def test_agnostic_pyomo_constructor(self): -# cfg = _farmer_cfg() -# Ag = agnostic.Agnostic(farmer_pyomo_agnostic, cfg) -# -# -# def test_agnostic_pyomo_scenario_creator(self): -# cfg = _farmer_cfg() -# Ag = agnostic.Agnostic(farmer_pyomo_agnostic, cfg) -# s0 = Ag.scenario_creator("scen0") -# s2 = Ag.scenario_creator("scen2") -# -# -# def test_agnostic_pyomo_PH_constructor(self): -# cfg = _farmer_cfg() -# Ag = agnostic.Agnostic(farmer_pyomo_agnostic, cfg) -# s1 = Ag.scenario_creator("scen1") # average case -# phoptions = _get_ph_base_options() -# ph = mpisppy.opt.ph.PH( -# phoptions, -# farmer_pyomo_agnostic.scenario_names_creator(num_scens=3), -# Ag.scenario_creator, -# farmer_pyomo_agnostic.scenario_denouement, -# scenario_creator_kwargs=None, # agnostic.py takes care of this -# extensions=None -# ) -# -# @unittest.skipIf(not solver_available, -# "no solver is available") -# def test_agnostic_pyomo_PH(self): -# cfg = _farmer_cfg() -# Ag = agnostic.Agnostic(farmer_pyomo_agnostic, cfg) -# s1 = Ag.scenario_creator("scen1") # average case -# phoptions = _get_ph_base_options() -# phoptions["Ag"] = Ag # this is critical -# scennames = farmer_pyomo_agnostic.scenario_names_creator(num_scens=3) -# ph = mpisppy.opt.ph.PH( -# phoptions, -# scennames, -# Ag.scenario_creator, -# farmer_pyomo_agnostic.scenario_denouement, -# scenario_creator_kwargs=None, # agnostic.py takes care of this -# extensions=None -# ) -# conv, obj, tbound = ph.ph_main() -# self.assertAlmostEqual(-115405.5555, tbound, places=2) -# self.assertAlmostEqual(-110433.4007, obj, places=2) +class Test_Agnostic_pyomo(unittest.TestCase): + + def test_agnostic_pyomo_constructor(self): + cfg = _farmer_cfg() + Ag = agnostic.Agnostic(farmer_pyomo_agnostic, cfg) + + + def test_agnostic_pyomo_scenario_creator(self): + cfg = _farmer_cfg() + Ag = agnostic.Agnostic(farmer_pyomo_agnostic, cfg) + s0 = Ag.scenario_creator("scen0") + s2 = Ag.scenario_creator("scen2") + + + def test_agnostic_pyomo_PH_constructor(self): + cfg = _farmer_cfg() + Ag = agnostic.Agnostic(farmer_pyomo_agnostic, cfg) + s1 = Ag.scenario_creator("scen1") # average case + phoptions = _get_ph_base_options() + ph = mpisppy.opt.ph.PH( + phoptions, + farmer_pyomo_agnostic.scenario_names_creator(num_scens=3), + Ag.scenario_creator, + farmer_pyomo_agnostic.scenario_denouement, + scenario_creator_kwargs=None, # agnostic.py takes care of this + extensions=None + ) + + @unittest.skipIf(not solver_available, + "no solver is available") + def test_agnostic_pyomo_PH(self): + cfg = _farmer_cfg() + Ag = agnostic.Agnostic(farmer_pyomo_agnostic, cfg) + s1 = Ag.scenario_creator("scen1") # average case + phoptions = _get_ph_base_options() + phoptions["Ag"] = Ag # this is critical + scennames = farmer_pyomo_agnostic.scenario_names_creator(num_scens=3) + ph = mpisppy.opt.ph.PH( + phoptions, + scennames, + Ag.scenario_creator, + farmer_pyomo_agnostic.scenario_denouement, + scenario_creator_kwargs=None, # agnostic.py takes care of this + extensions=None + ) + conv, obj, tbound = ph.ph_main() + self.assertAlmostEqual(-115405.5555, tbound, places=2) + self.assertAlmostEqual(-110433.4007, obj, places=2) class Test_Agnostic_AMPL(unittest.TestCase): - # HEY (Sept 2023), when we go to a more generic cylinders for - # agnostic, move the model file name to cfg and remove the model - # file from the test directory TBD def test_agnostic_AMPL_constructor(self): cfg = _farmer_cfg() Ag = agnostic.Agnostic(farmer_ampl_agnostic, cfg) @@ -153,7 +155,27 @@ def test_agnostic_ampl_PH(self): self.assertAlmostEqual(-115405.5555, tbound, 2, message) self.assertAlmostEqual(-110433.4007, obj, 2, message) - + def test_agnostic_cylinders_ampl(self): + # just make sure PH runs + # TBD: Nov 2024 + # this test (or the code) needs work: why does it stop after 3 iterations + # why are spbase and phbase initialized at the end + model_fname = "mpisppy.agnostic.examples.farmer_ampl_model" + module = sputils.module_name_to_module(model_fname) + cfg = agnostic_cylinders._setup_args(module) + cfg.module_name = model_fname + cfg.default_rho = 1 + cfg.num_scens = 3 + cfg.solver_name= "gurobi" + cfg.guest_language = "AMPL" + cfg.max_iterations = 5 + cfg.ampl_model_file = "../agnostic/examples/farmer.mod" + agnostic_cylinders.main(model_fname, module, cfg) + + +print("*********** hack out gurobipy ***********") +have_gurobipy = False +@unittest.skipIf(not have_gurobipy, "skipping gurobipy") class Test_Agnostic_gurobipy(unittest.TestCase): def test_agnostic_gurobipy_constructor(self): cfg = _farmer_cfg()