@@ -1997,6 +1997,58 @@ def test_pyros_certain_params_ipopt_degrees_of_freedom(self):
19971997 )
19981998 self .assertEqual (m .x .value , 1 )
19991999
2000+ @parameterized .expand ([[True , 1 ], [True , 2 ], [False , 1 ], [False , 2 ]])
2001+ def test_two_stage_set_nonstatic_dr_robust_opt (self , use_discrete_set , dr_order ):
2002+ """
2003+ Test problems that are sensitive to the DR order efficiency.
2004+
2005+ If the efficiency is not switched off properly, then
2006+ PyROS may terminate prematurely with a(n inaccurate)
2007+ robust infeasibility status.
2008+ """
2009+ m = ConcreteModel ()
2010+ m .x = Var (bounds = [- 2 , 2 ], initialize = 0 )
2011+ m .z = Var (bounds = [- 10 , 10 ], initialize = 0 )
2012+ m .q = Param (initialize = 2 , mutable = True )
2013+ m .obj = Objective (expr = m .x + m .z , sense = maximize )
2014+ # when uncertainty set is discrete, the
2015+ # preprocessor should write out this constraint for
2016+ # each scenario as a first-stage constraint
2017+ # otherwise, coefficient matching constraint
2018+ # requires only the affine DR coefficient be nonzero
2019+ m .xz_con = Constraint (expr = m .z == m .q )
2020+
2021+ uncertainty_set = (
2022+ DiscreteScenarioSet ([[2 ], [3 ]]) if use_discrete_set else BoxSet ([[2 , 3 ]])
2023+ )
2024+ baron = SolverFactory ("baron" )
2025+ res = SolverFactory ("pyros" ).solve (
2026+ model = m ,
2027+ first_stage_variables = m .x ,
2028+ second_stage_variables = m .z ,
2029+ uncertain_params = m .q ,
2030+ uncertainty_set = uncertainty_set ,
2031+ local_solver = baron ,
2032+ global_solver = baron ,
2033+ solve_master_globally = True ,
2034+ bypass_local_separation = True ,
2035+ decision_rule_order = dr_order ,
2036+ objective_focus = "worst_case" ,
2037+ )
2038+ self .assertEqual (
2039+ # DR efficiency should have been switched off due to
2040+ # DR-dependent equalities, so robust optimal
2041+ # if the DR efficiency was not switched off, then
2042+ # robust infeasibililty would have been prematurely reported
2043+ res .pyros_termination_condition ,
2044+ pyrosTerminationCondition .robust_optimal ,
2045+ )
2046+ self .assertEqual (res .iterations , 1 )
2047+ # optimal solution evaluated under worst-case scenario
2048+ self .assertAlmostEqual (res .final_objective_value , 4 , places = 4 )
2049+ self .assertAlmostEqual (m .x .value , 2 , places = 4 )
2050+ self .assertAlmostEqual (m .z .value , 2 , places = 4 )
2051+
20002052
20012053@unittest .skipUnless (baron_available , "BARON not available" )
20022054class TestReformulateSecondStageEqualitiesDiscrete (unittest .TestCase ):
@@ -2145,12 +2197,9 @@ def test_two_stage_discrete_set_rank2_affine_dr(self):
21452197 res .pyros_termination_condition , pyrosTerminationCondition .robust_optimal
21462198 )
21472199 self .assertEqual (res .iterations , 1 )
2148- self .assertAlmostEqual (res .final_objective_value , 0 , places = 4 )
2149- # note: this solution is suboptimal (in the context of nonstatic DRs),
2150- # but follows from the current efficiency for DRs
2151- # (i.e. in first iteration, static DRs required)
2152- self .assertAlmostEqual (m .x .value , 0 , places = 4 )
2153- self .assertAlmostEqual (m .z .value , 0 , places = 4 )
2200+ self .assertAlmostEqual (res .final_objective_value , 2 , places = 4 )
2201+ self .assertAlmostEqual (m .x .value , 4 , places = 4 )
2202+ self .assertAlmostEqual (m .z .value , - 2 , places = 4 )
21542203
21552204 def test_two_stage_discrete_set_fullrank_affine_dr (self ):
21562205 m = self .build_two_stage_model ()
0 commit comments