@@ -1997,6 +1997,58 @@ def test_pyros_certain_params_ipopt_degrees_of_freedom(self):
1997
1997
)
1998
1998
self .assertEqual (m .x .value , 1 )
1999
1999
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
+
2000
2052
2001
2053
@unittest .skipUnless (baron_available , "BARON not available" )
2002
2054
class TestReformulateSecondStageEqualitiesDiscrete (unittest .TestCase ):
@@ -2145,12 +2197,9 @@ def test_two_stage_discrete_set_rank2_affine_dr(self):
2145
2197
res .pyros_termination_condition , pyrosTerminationCondition .robust_optimal
2146
2198
)
2147
2199
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 )
2154
2203
2155
2204
def test_two_stage_discrete_set_fullrank_affine_dr (self ):
2156
2205
m = self .build_two_stage_model ()
0 commit comments