|
42 | 42 | from pyomo.contrib.pyros.util import get_vars_from_component
|
43 | 43 | from pyomo.contrib.pyros.util import identify_objective_functions
|
44 | 44 | from pyomo.common.collections import Bunch
|
| 45 | +from pyomo.repn.plugins import nl_writer as pyomo_nl_writer |
45 | 46 | import time
|
46 | 47 | import math
|
47 | 48 | from pyomo.contrib.pyros.util import time_code
|
|
68 | 69 | from pyomo.common.dependencies import numpy as np, numpy_available
|
69 | 70 | from pyomo.common.dependencies import scipy as sp, scipy_available
|
70 | 71 | from pyomo.environ import maximize as pyo_max
|
71 |
| -from pyomo.common.errors import ApplicationError |
| 72 | +from pyomo.common.errors import ApplicationError, InfeasibleConstraintException |
72 | 73 | from pyomo.opt import (
|
73 | 74 | SolverResults,
|
74 | 75 | SolverStatus,
|
@@ -4616,6 +4617,76 @@ def test_discrete_separation_subsolver_error(self):
|
4616 | 4617 | ),
|
4617 | 4618 | )
|
4618 | 4619 |
|
| 4620 | + @unittest.skipUnless(ipopt_available, "IPOPT is not available.") |
| 4621 | + def test_pyros_nl_writer_tol(self): |
| 4622 | + """ |
| 4623 | + Test PyROS subsolver call routine behavior |
| 4624 | + with respect to the NL writer tolerance is as |
| 4625 | + expected. |
| 4626 | + """ |
| 4627 | + m = ConcreteModel() |
| 4628 | + m.q = Param(initialize=1, mutable=True) |
| 4629 | + m.x1 = Var(initialize=1, bounds=(0, 1)) |
| 4630 | + m.x2 = Var(initialize=2, bounds=(0, m.q)) |
| 4631 | + m.obj = Objective(expr=m.x1 + m.x2) |
| 4632 | + |
| 4633 | + # fixed just inside the PyROS-specified NL writer tolerance. |
| 4634 | + m.x1.fix(m.x1.upper + 9.9e-5) |
| 4635 | + |
| 4636 | + current_nl_writer_tol = pyomo_nl_writer.TOL |
| 4637 | + ipopt_solver = SolverFactory("ipopt") |
| 4638 | + pyros_solver = SolverFactory("pyros") |
| 4639 | + |
| 4640 | + pyros_solver.solve( |
| 4641 | + model=m, |
| 4642 | + first_stage_variables=[m.x1], |
| 4643 | + second_stage_variables=[m.x2], |
| 4644 | + uncertain_params=[m.q], |
| 4645 | + uncertainty_set=BoxSet([[0, 1]]), |
| 4646 | + local_solver=ipopt_solver, |
| 4647 | + global_solver=ipopt_solver, |
| 4648 | + decision_rule_order=0, |
| 4649 | + solve_master_globally=False, |
| 4650 | + bypass_global_separation=True, |
| 4651 | + ) |
| 4652 | + |
| 4653 | + self.assertEqual( |
| 4654 | + pyomo_nl_writer.TOL, |
| 4655 | + current_nl_writer_tol, |
| 4656 | + msg="Pyomo NL writer tolerance not restored as expected.", |
| 4657 | + ) |
| 4658 | + |
| 4659 | + # fixed just outside the PyROS-specified NL writer tolerance. |
| 4660 | + # this should be exceptional. |
| 4661 | + m.x1.fix(m.x1.upper + 1.01e-4) |
| 4662 | + |
| 4663 | + err_msg = ( |
| 4664 | + "model contains a trivially infeasible variable.*x1" |
| 4665 | + ".*fixed.*outside bounds" |
| 4666 | + ) |
| 4667 | + with self.assertRaisesRegex(InfeasibleConstraintException, err_msg): |
| 4668 | + pyros_solver.solve( |
| 4669 | + model=m, |
| 4670 | + first_stage_variables=[m.x1], |
| 4671 | + second_stage_variables=[m.x2], |
| 4672 | + uncertain_params=[m.q], |
| 4673 | + uncertainty_set=BoxSet([[0, 1]]), |
| 4674 | + local_solver=ipopt_solver, |
| 4675 | + global_solver=ipopt_solver, |
| 4676 | + decision_rule_order=0, |
| 4677 | + solve_master_globally=False, |
| 4678 | + bypass_global_separation=True, |
| 4679 | + ) |
| 4680 | + |
| 4681 | + self.assertEqual( |
| 4682 | + pyomo_nl_writer.TOL, |
| 4683 | + current_nl_writer_tol, |
| 4684 | + msg=( |
| 4685 | + "Pyomo NL writer tolerance not restored as expected " |
| 4686 | + "after exceptional test." |
| 4687 | + ), |
| 4688 | + ) |
| 4689 | + |
4619 | 4690 | @unittest.skipUnless(
|
4620 | 4691 | baron_license_is_valid, "Global NLP solver is not available and licensed."
|
4621 | 4692 | )
|
|
0 commit comments