@@ -2186,6 +2186,50 @@ def test_pyros_intersection_aux_vars(self):
21862186 self .assertAlmostEqual (m .x1 .value , 1 )
21872187 self .assertAlmostEqual (m .x2 .value , 1 )
21882188
2189+ @unittest .skipUnless (ipopt_available , "IPOPT is not available." )
2190+ def test_pyros_dr_interface_ordering (self ):
2191+ """
2192+ Test PyROS DR interface returns coefficients in expected order.
2193+ """
2194+ m = ConcreteModel ()
2195+ m .q1 = Param (initialize = 0.5 , mutable = True )
2196+ m .q2 = Param (initialize = 0.5 , mutable = True )
2197+ m .x1 = Var (bounds = [1 , 1 ])
2198+ m .x2 = Var (bounds = [m .q1 , m .q1 ])
2199+ x3_bound_expr = 3 + m .q1 * m .q2
2200+ m .x3 = Var (bounds = [x3_bound_expr , x3_bound_expr ])
2201+ m .obj = Objective (expr = m .x1 + m .x2 + m .x3 + m .q2 )
2202+ res = SolverFactory ("pyros" ).solve (
2203+ model = m ,
2204+ first_stage_variables = [],
2205+ second_stage_variables = [m .x1 , m .x2 , m .x3 ],
2206+ uncertain_params = [m .q1 , m .q2 ],
2207+ uncertainty_set = BoxSet ([[0 , 1 ]] * 2 ),
2208+ local_solver = "ipopt" ,
2209+ global_solver = "ipopt" ,
2210+ decision_rule_order = 2 ,
2211+ )
2212+ self .assertEqual (
2213+ res .pyros_termination_condition ,
2214+ pyrosTerminationCondition .robust_feasible ,
2215+ )
2216+ self .assertEqual (m .x1 .value , 1 )
2217+ self .assertAlmostEqual (m .x2 .value , 0.5 ) # nominal realization
2218+ self .assertAlmostEqual (m .x3 .value , 3.25 )
2219+ # nominal objective
2220+ self .assertAlmostEqual (res .final_objective_value , 5.25 )
2221+ # test DR coefficients: easily inferred from the
2222+ # variable bound expressions
2223+ np .testing .assert_allclose (res .decision_rule_coeffs ["static" ], [1 , 0 , 3 ])
2224+ np .testing .assert_allclose (
2225+ res .decision_rule_coeffs ["affine" ],
2226+ [[0 , 0 ], [1 , 0 ], [0 , 0 ]],
2227+ )
2228+ np .testing .assert_allclose (
2229+ res .decision_rule_coeffs ["quadratic" ],
2230+ [[[0 , 0 ], [0 , 0 ]]] * 2 + [[[0 , 0.5 ], [0.5 , 0 ]]],
2231+ )
2232+
21892233
21902234@unittest .skipUnless (ipopt_available , "IPOPT not available." )
21912235class TestPyROSSeparationPriorityOrder (unittest .TestCase ):
@@ -2540,6 +2584,10 @@ def test_two_stage_discrete_set_rank2_affine_dr(self):
25402584 self .assertAlmostEqual (res .final_objective_value , 2 , places = 4 )
25412585 self .assertAlmostEqual (m .x .value , 4 , places = 4 )
25422586 self .assertAlmostEqual (m .z .value , - 2 , places = 4 )
2587+ # optimal DR can be computed analytically
2588+ np .testing .assert_allclose (res .decision_rule_coeffs ["static" ], [- 10 / 3 ])
2589+ np .testing .assert_allclose (res .decision_rule_coeffs ["affine" ], [[2 / 3 ]])
2590+ self .assertIsNone (res .decision_rule_coeffs ["quadratic" ])
25432591
25442592 def test_two_stage_discrete_set_fullrank_affine_dr (self ):
25452593 m = self .build_two_stage_model ()
@@ -2568,6 +2616,10 @@ def test_two_stage_discrete_set_fullrank_affine_dr(self):
25682616 # variables must be 0
25692617 self .assertAlmostEqual (m .x .value , 0 , places = 4 )
25702618 self .assertAlmostEqual (m .z .value , 0 , places = 4 )
2619+ # optimal DR can be calculated analytically
2620+ np .testing .assert_allclose (res .decision_rule_coeffs ["static" ], [0 ])
2621+ np .testing .assert_allclose (res .decision_rule_coeffs ["affine" ], [[0 ]])
2622+ self .assertIsNone (res .decision_rule_coeffs ["quadratic" ])
25712623
25722624
25732625@unittest .skipUnless (ipopt_available , "IPOPT not available." )
0 commit comments