def test_identify_mutable_parameters_expr(self): # # Identify variables when there are duplicates # m = ConcreteModel() m.a = Param(initialize=1, mutable=True) m.b = Param(initialize=2, mutable=True) m.e = Expression(expr=3*m.a) m.E = Expression([0,1], initialize={0:3*m.a, 1:4*m.b}) self.assertEqual( list(identify_mutable_parameters(m.b+m.e)), [ m.b, m.a ] ) self.assertEqual( list(identify_mutable_parameters(m.E[0])), [ m.a ] ) self.assertEqual( list(identify_mutable_parameters(m.E[1])), [ m.b ] )
def test_identify_mutable_parameters_constants(self): # # ScalarParams and NumericConstants are not recognized # m = ConcreteModel() m.x = Var(initialize=1) m.x.fix() m.p = Param(initialize=2, mutable=False) m.p_m = Param(initialize=3, mutable=True) e1 = (m.x + m.p + NumericConstant(5)) self.assertEqual(list(identify_mutable_parameters(e1)), []) e2 = (5 * m.x + NumericConstant(3) * m.p_m + m.p == 0) mut_params = list(identify_mutable_parameters(e2)) self.assertEqual(len(mut_params), 1) self.assertIs(mut_params[0], m.p_m)
def replace_uncertain_bounds_with_constraints(model, uncertain_params): """ For variables of which the bounds are dependent on the parameters in the list `uncertain_params`, remove the bounds and add explicit variable bound inequality constraints. :param model: Model in which to make the bounds/constraint replacements :type model: class:`pyomo.core.base.PyomoModel.ConcreteModel` :param uncertain_params: List of uncertain model parameters :type uncertain_params: list """ uncertain_param_set = ComponentSet(uncertain_params) # component for explicit inequality constraints uncertain_var_bound_constrs = ConstraintList() model.add_component(unique_component_name(model, 'uncertain_var_bound_cons'), uncertain_var_bound_constrs) # get all variables in active objective and constraint expression(s) vars_in_cons = ComponentSet(get_vars_from_component(model, Constraint)) vars_in_obj = ComponentSet(get_vars_from_component(model, Objective)) for v in vars_in_cons | vars_in_obj: # get mutable parameters in variable bounds expressions ub = v.upper mutable_params_ub = ComponentSet(identify_mutable_parameters(ub)) lb = v.lower mutable_params_lb = ComponentSet(identify_mutable_parameters(lb)) # add explicit inequality constraint(s), remove variable bound(s) if mutable_params_ub & uncertain_param_set: if type(ub) is NPV_MinExpression: upper_bounds = ub.args else: upper_bounds = (ub,) for u_bnd in upper_bounds: uncertain_var_bound_constrs.add(v - u_bnd <= 0) v.setub(None) if mutable_params_lb & uncertain_param_set: if type(ub) is NPV_MaxExpression: lower_bounds = lb.args else: lower_bounds = (lb,) for l_bnd in lower_bounds: uncertain_var_bound_constrs.add(l_bnd - v <= 0) v.setlb(None)
def test_identify_duplicate_params(self): # # Identify mutable params when there are duplicates # m = ConcreteModel() m.a = Param(initialize=1, mutable=True) self.assertEqual(list(identify_mutable_parameters(2 * m.a + 2 * m.a)), [m.a])
def test_identify_duplicate_params(self): # # Identify mutable params when there are duplicates # m = ConcreteModel() m.a = Param(initialize=1, mutable=True) self.assertEqual( list(identify_mutable_parameters(2*m.a+2*m.a)), [ m.a ] )
def test_identify_mutable_parameters_logical_expr(self): # # Identify mutable params in logical expressions # m = ConcreteModel() m.a = Param(initialize=0, mutable=True) expr = m.a + 1 == 0 param_set = ComponentSet(identify_mutable_parameters(expr)) self.assertEqual(len(param_set), 1) self.assertIn(m.a, param_set)
def identify_objective_functions(model, objective): """ Identify the first and second-stage portions of an Objective expression, subject to user-provided variable partitioning and uncertain parameter choice. In doing so, the first and second-stage objective expressions are added to the model as `Expression` attributes. Parameters ---------- model : ConcreteModel Model of interest. objective : Objective Objective to be resolved into first and second-stage parts. """ expr_to_split = objective.expr has_args = hasattr(expr_to_split, "args") is_sum = isinstance(expr_to_split, SumExpression) # determine additive terms of the objective expression # additive terms are in accordance with user declaration if has_args and is_sum: obj_args = expr_to_split.args else: obj_args = [expr_to_split] # initialize first and second-stage cost expressions first_stage_cost_expr = 0 second_stage_cost_expr = 0 first_stage_var_set = ComponentSet(model.util.first_stage_variables) uncertain_param_set = ComponentSet(model.util.uncertain_params) for term in obj_args: non_first_stage_vars_in_term = ComponentSet( v for v in identify_variables(term) if v not in first_stage_var_set ) uncertain_params_in_term = ComponentSet( param for param in identify_mutable_parameters(term) if param in uncertain_param_set ) if non_first_stage_vars_in_term or uncertain_params_in_term: second_stage_cost_expr += term else: first_stage_cost_expr += term model.first_stage_objective = Expression(expr=first_stage_cost_expr) model.second_stage_objective = Expression(expr=second_stage_cost_expr)
def test_param_const_indexed(self): model = make_indexed_model() param_list = [model.eta] sens = SensitivityInterface(model, clone_model=False) sens.setup_sensitivity(param_list) block = sens.block param_const = block.paramConst param_var_map = ComponentMap( (param, var) for var, param, _, _ in block._sens_data_list) for con in param_const.values(): var_list = list(identify_variables(con.expr)) mut_param_list = list(identify_mutable_parameters(con.expr)) self.assertEqual(len(var_list), 1) self.assertEqual(len(mut_param_list), 1) self.assertIs(var_list[0], param_var_map[mut_param_list[0]]) self.assertEqual(con.body.to_string(), (var_list[0] - mut_param_list[0]).to_string())
def test_identify_mutable_parameters(self): m = ConcreteModel() m.I = RangeSet(3) m.a = Var(initialize=1) m.b = Var(m.I, initialize=1) # # There are no variables in expressions with only vars # self.assertEqual( list(identify_mutable_parameters(m.a)), [] ) self.assertEqual( list(identify_mutable_parameters(m.b[1])), [] ) self.assertEqual( list(identify_mutable_parameters(m.a+m.b[1])), [] ) self.assertEqual( list(identify_mutable_parameters(m.a**m.b[1])), [] ) self.assertEqual( list(identify_mutable_parameters( m.a**m.b[1] + m.b[2])), [] ) self.assertEqual( list(identify_mutable_parameters( m.a**m.b[1] + m.b[2]*m.b[3]*m.b[2])), [] )
def test_expression_replacement_ranged_inequality(self): model = make_model_with_ranged_inequalities() sens = SensitivityInterface(model, clone_model=False) sens._add_data_block() instance = sens.model_instance block = sens.block instance.x.fix() param_list = [instance.eta[1], instance.eta[2]] sens._add_sensitivity_data(param_list) orig_components = ( list(instance.component_data_objects(Constraint, active=True)) + list(instance.component_data_objects(Objective, active=True))) orig_expr = [con.expr for con in orig_components] # These will be modified to account for expected replacements expected_variables = ComponentMap( (con, ComponentSet(identify_variables(con.expr))) for con in orig_components) expected_parameters = ComponentMap( (con, ComponentSet(identify_mutable_parameters(con.expr))) for con in orig_components) # As constructed by the `setup_sensitivity` method: variable_sub_map = dict( (id(param), var) for var, param, list_idx, _ in block._sens_data_list if param_list[list_idx].ctype is Param) # Sanity check self.assertEqual(len(variable_sub_map), 2) # Map each param to the var that should replace it param_var_map = ComponentMap( (param, var) for var, param, _, _ in block._sens_data_list) # Remove parameters we expect to replace and add vars # we expect to replace with. for con in orig_components: for param in param_var_map: if param in expected_parameters[con]: expected_variables[con].add(param_var_map[param]) expected_parameters[con].remove(param) # We check that the new components (Constraints and Objectives) contain # the expected parameters and variables. replaced = sens._replace_parameters_in_constraints(variable_sub_map) # With ranged inequalities, we end up with more constraints than we # started with: self.assertEqual(len(block.constList), 3) for con in block.constList.values(): self.assertTrue(con.active) param_set = ComponentSet(identify_mutable_parameters(con.expr)) var_set = ComponentSet(identify_variables(con.expr)) orig_con = replaced[con] self.assertIsNot(orig_con, con) # Note that for ranged inequalities, it is not valid to check # that the two sets are equal as a mutable parameter could be # contained in only one "sub-inequality" self.assertIsSubset(param_set, expected_parameters[orig_con]) self.assertEqual(var_set, expected_variables[orig_con]) self.assertIs(block.cost.ctype, Objective) obj = block.cost param_set = ComponentSet(identify_mutable_parameters(obj.expr)) var_set = ComponentSet(identify_variables(obj.expr)) orig_obj = replaced[obj] self.assertIsNot(orig_obj, obj) self.assertEqual(param_set, expected_parameters[orig_obj]) self.assertEqual(var_set, expected_variables[orig_obj]) # Original components were deactivated but otherwise not altered for con, expr in zip(orig_components, orig_expr): self.assertFalse(con.active) #self.assertIs(con.expr, expr) # ^Why does this fail? self.assertEqual(con.expr.to_string(), expr.to_string())
def validate_kwarg_inputs(model, config): ''' Confirm kwarg inputs satisfy PyROS requirements. :param model: the deterministic model :param config: the config for this PyROS instance :return: ''' # === Check if model is ConcreteModel object if not isinstance(model, ConcreteModel): raise ValueError( "Model passed to PyROS solver must be a ConcreteModel object.") first_stage_variables = config.first_stage_variables second_stage_variables = config.second_stage_variables uncertain_params = config.uncertain_params # === Not currently supporting constraints of the form h(x, q) = 0 # (uncertain equality constraints in only first stage variables) # We have plans to support this in the future effective_first_stage_variables = first_stage_variables + second_stage_variables for c in model.component_data_objects(Constraint): if c.equality: variables_in_constraint = identify_variables(c.expr) params_in_constraint = identify_mutable_parameters(c.expr) if all(v in ComponentSet(effective_first_stage_variables) for v in variables_in_constraint) and \ any(q in ComponentSet(uncertain_params) for q in params_in_constraint): raise ValueError( "If any uncertain parameters participate " "in an equality constraint, a state variable must " "also participate. Offending constraint: %s " % c.name) if not config.first_stage_variables and not config.second_stage_variables: # Must have non-zero DOF raise ValueError("first_stage_variables and " "second_stage_variables cannot both be empty lists.") if ComponentSet(first_stage_variables) != ComponentSet( config.first_stage_variables): raise ValueError( "All elements in first_stage_variables must be Var members of the model object." ) if ComponentSet(second_stage_variables) != ComponentSet( config.second_stage_variables): raise ValueError( "All elements in second_stage_variables must be Var members of the model object." ) if any(v in ComponentSet(second_stage_variables) for v in ComponentSet(first_stage_variables)): raise ValueError( "No common elements allowed between first_stage_variables and second_stage_variables." ) if ComponentSet(uncertain_params) != ComponentSet(config.uncertain_params): raise ValueError( "uncertain_params must be mutable Param members of the model object." ) if not config.uncertainty_set: raise ValueError( "An UncertaintySet object must be provided to the PyROS solver.") non_mutable_params = [] for p in config.uncertain_params: if not (not p.is_constant() and p.is_fixed() and not p.is_potentially_variable()): non_mutable_params.append(p) if non_mutable_params: raise ValueError( "Param objects which are uncertain must have attribute mutable=True. " "Offending Params: %s" % [p.name for p in non_mutable_params]) # === Solvers provided check if not config.local_solver or not config.global_solver: raise ValueError( "User must designate both a local and global optimization solver via the local_solver" " and global_solver options.") # === Degrees of freedom provided check if len(config.first_stage_variables) + len( config.second_stage_variables) == 0: raise ValueError( "User must designate at least one first- and/or second-stage variable." ) # === Uncertain params provided check if len(config.uncertain_params) == 0: raise ValueError( "User must designate at least one uncertain parameter.") return
def test_identify_mutable_parameters_params(self): m = ConcreteModel() m.I = RangeSet(3) m.a = Param(initialize=1, mutable=True) m.b = Param(m.I, initialize=1, mutable=True) m.p = Var(initialize=1) m.x = ExternalFunction(library='foo.so', function='bar') # # Identify variables in various algebraic expressions # self.assertEqual(list(identify_mutable_parameters(m.a)), [m.a]) self.assertEqual(list(identify_mutable_parameters(m.b[1])), [m.b[1]]) self.assertEqual(list(identify_mutable_parameters(m.a + m.b[1])), [m.a, m.b[1]]) self.assertEqual(list(identify_mutable_parameters(m.a**m.b[1])), [m.a, m.b[1]]) self.assertEqual( list(identify_mutable_parameters(m.a**m.b[1] + m.b[2])), [m.a, m.b[1], m.b[2]]) self.assertEqual( list( identify_mutable_parameters(m.a**m.b[1] + m.b[2] * m.b[3] * m.b[2])), [m.a, m.b[1], m.b[2], m.b[3]]) self.assertEqual( list( identify_mutable_parameters(m.a**m.b[1] + m.b[2] / m.b[3] * m.b[2])), [m.a, m.b[1], m.b[2], m.b[3]]) # # Identify variables in the arguments to functions # self.assertEqual( list( identify_mutable_parameters( m.x(m.a, 'string_param', 1, []) * m.b[1])), [m.a, m.b[1]]) self.assertEqual( list( identify_mutable_parameters( m.x(m.p, 'string_param', 1, []) * m.b[1])), [m.b[1]]) self.assertEqual(list(identify_mutable_parameters(tanh(m.a) * m.b[1])), [m.a, m.b[1]]) self.assertEqual(list(identify_mutable_parameters(abs(m.a) * m.b[1])), [m.a, m.b[1]]) # # Check logic for allowing duplicates # self.assertEqual(list(identify_mutable_parameters(m.a**m.a + m.a)), [m.a])
def test_identify_params_numeric(self): # # There are no parameters in a constant expression # self.assertEqual(list(identify_mutable_parameters(5)), [])
def coefficient_matching(model, constraint, uncertain_params, config): ''' :param model: master problem model :param constraint: the constraint from the master problem model :param uncertain_params: the list of uncertain parameters :param first_stage_variables: the list of effective first-stage variables (includes ssv if decision_rule_order = 0) :return: True if the coefficient matching was successful, False if its proven robust_infeasible due to constraints of the form 1 == 0 ''' # === Returned flags successful_matching = True robust_infeasible = False # === Efficiency for q_LB = q_UB actual_uncertain_params = [] for i in range(len(uncertain_params)): if not is_certain_parameter(uncertain_param_index=i, config=config): actual_uncertain_params.append(uncertain_params[i]) # === Add coefficient matching constraint list if not hasattr(model, "coefficient_matching_constraints"): model.coefficient_matching_constraints = ConstraintList() if not hasattr(model, "swapped_constraints"): model.swapped_constraints = ConstraintList() variables_in_constraint = ComponentSet(identify_variables(constraint.expr)) params_in_constraint = ComponentSet(identify_mutable_parameters(constraint.expr)) first_stage_variables = model.util.first_stage_variables second_stage_variables = model.util.second_stage_variables # === Determine if we need to do DR expression/ssv substitution to # make h(x,z,q) == 0 into h(x,d,q) == 0 (which is just h(x,q) == 0) if all(v in ComponentSet(first_stage_variables) for v in variables_in_constraint) and \ any(q in ComponentSet(actual_uncertain_params) for q in params_in_constraint): # h(x, q) == 0 pass elif all(v in ComponentSet(first_stage_variables + second_stage_variables) for v in variables_in_constraint) and \ any(q in ComponentSet(actual_uncertain_params) for q in params_in_constraint): constraint = substitute_ssv_in_dr_constraints(model=model, constraint=constraint) variables_in_constraint = ComponentSet(identify_variables(constraint.expr)) params_in_constraint = ComponentSet(identify_mutable_parameters(constraint.expr)) else: pass if all(v in ComponentSet(first_stage_variables) for v in variables_in_constraint) and \ any(q in ComponentSet(actual_uncertain_params) for q in params_in_constraint): # Swap param objects for variable objects in this constraint model.param_set = [] for i in range(len(list(variables_in_constraint))): # Initialize Params to non-zero value due to standard_repn bug model.add_component("p_%s" % i, Param(initialize=1, mutable=True)) model.param_set.append(getattr(model, "p_%s" % i)) model.variable_set = [] for i in range(len(list(actual_uncertain_params))): model.add_component("x_%s" % i, Var(initialize=1)) model.variable_set.append(getattr(model, "x_%s" % i)) original_var_to_param_map = list(zip(list(variables_in_constraint), model.param_set)) original_param_to_vap_map = list(zip(list(actual_uncertain_params), model.variable_set)) var_to_param_substitution_map_forward = {} # Separation problem initialized to nominal uncertain parameter values for var, param in original_var_to_param_map: var_to_param_substitution_map_forward[id(var)] = param param_to_var_substitution_map_forward = {} # Separation problem initialized to nominal uncertain parameter values for param, var in original_param_to_vap_map: param_to_var_substitution_map_forward[id(param)] = var var_to_param_substitution_map_reverse = {} # Separation problem initialized to nominal uncertain parameter values for var, param in original_var_to_param_map: var_to_param_substitution_map_reverse[id(param)] = var param_to_var_substitution_map_reverse = {} # Separation problem initialized to nominal uncertain parameter values for param, var in original_param_to_vap_map: param_to_var_substitution_map_reverse[id(var)] = param model.swapped_constraints.add( replace_expressions( expr=replace_expressions(expr=constraint.lower, substitution_map=param_to_var_substitution_map_forward), substitution_map=var_to_param_substitution_map_forward) == replace_expressions( expr=replace_expressions(expr=constraint.body, substitution_map=param_to_var_substitution_map_forward), substitution_map=var_to_param_substitution_map_forward)) swapped = model.swapped_constraints[max(model.swapped_constraints.keys())] val = generate_standard_repn(swapped.body, compute_values=False) if val.constant is not None: if type(val.constant) not in native_types: temp_expr = replace_expressions(val.constant, substitution_map=var_to_param_substitution_map_reverse) if temp_expr.is_potentially_variable(): model.coefficient_matching_constraints.add(expr=temp_expr == 0) elif math.isclose(value(temp_expr), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL): pass else: successful_matching = False robust_infeasible = True elif math.isclose(value(val.constant), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL): pass else: successful_matching = False robust_infeasible = True if val.linear_coefs is not None: for coeff in val.linear_coefs: if type(coeff) not in native_types: temp_expr = replace_expressions(coeff, substitution_map=var_to_param_substitution_map_reverse) if temp_expr.is_potentially_variable(): model.coefficient_matching_constraints.add(expr=temp_expr == 0) elif math.isclose(value(temp_expr), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL): pass else: successful_matching = False robust_infeasible = True elif math.isclose(value(coeff), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL): pass else: successful_matching = False robust_infeasible = True if val.quadratic_coefs: for coeff in val.quadratic_coefs: if type(coeff) not in native_types: temp_expr = replace_expressions(coeff, substitution_map=var_to_param_substitution_map_reverse) if temp_expr.is_potentially_variable(): model.coefficient_matching_constraints.add(expr=temp_expr == 0) elif math.isclose(value(temp_expr), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL): pass else: successful_matching = False robust_infeasible = True elif math.isclose(value(coeff), 0, rel_tol=COEFF_MATCH_REL_TOL, abs_tol=COEFF_MATCH_ABS_TOL): pass else: successful_matching = False robust_infeasible = True if val.nonlinear_expr is not None: successful_matching = False robust_infeasible = False if successful_matching: model.util.h_x_q_constraints.add(constraint) for i in range(len(list(variables_in_constraint))): model.del_component("p_%s" % i) for i in range(len(list(params_in_constraint))): model.del_component("x_%s" % i) model.del_component("swapped_constraints") model.del_component("swapped_constraints_index") return successful_matching, robust_infeasible
def test_identify_mutable_parameters_params(self): m = ConcreteModel() m.I = RangeSet(3) m.a = Param(initialize=1, mutable=True) m.b = Param(m.I, initialize=1, mutable=True) m.p = Var(initialize=1) m.x = ExternalFunction(library='foo.so', function='bar') # # Identify variables in various algebraic expressions # self.assertEqual( list(identify_mutable_parameters(m.a)), [m.a] ) self.assertEqual( list(identify_mutable_parameters(m.b[1])), [m.b[1]] ) self.assertEqual( list(identify_mutable_parameters(m.a+m.b[1])), [ m.a, m.b[1] ] ) self.assertEqual( list(identify_mutable_parameters(m.a**m.b[1])), [ m.a, m.b[1] ] ) self.assertEqual( list(identify_mutable_parameters(m.a**m.b[1] + m.b[2])), [ m.a, m.b[1], m.b[2] ] ) self.assertEqual( list(identify_mutable_parameters( m.a**m.b[1] + m.b[2]*m.b[3]*m.b[2])), [ m.a, m.b[1], m.b[2], m.b[3] ] ) self.assertEqual( list(identify_mutable_parameters( m.a**m.b[1] + m.b[2]/m.b[3]*m.b[2])), [ m.a, m.b[1], m.b[2], m.b[3] ] ) # # Identify variables in the arguments to functions # self.assertEqual( list(identify_mutable_parameters( m.x(m.a, 'string_param', 1, [])*m.b[1] )), [ m.a, m.b[1] ] ) self.assertEqual( list(identify_mutable_parameters( m.x(m.p, 'string_param', 1, [])*m.b[1] )), [ m.b[1] ] ) self.assertEqual( list(identify_mutable_parameters( tanh(m.a)*m.b[1] )), [ m.a, m.b[1] ] ) self.assertEqual( list(identify_mutable_parameters( abs(m.a)*m.b[1] )), [ m.a, m.b[1] ] ) # # Check logic for allowing duplicates # self.assertEqual( list(identify_mutable_parameters(m.a**m.a + m.a)), [ m.a ] )
def identify_objective_functions(model, config): ''' Determine the objective first- and second-stage costs based on the user provided variable partition :param model: deterministic model :param config: config block :return: ''' m = model obj = [o for o in model.component_data_objects(Objective)] if len(obj) > 1: raise AttributeError("Deterministic model must only have 1 active objective!") if obj[0].sense != minimize: raise AttributeError("PyROS requires deterministic models to have an objective function with 'sense'=minimization. " "Please specify your objective function as minimization.") first_stage_terms = [] second_stage_terms = [] first_stage_cost_expr = 0 second_stage_cost_expr = 0 const_obj_expr = 0 if isinstance(obj[0].expr, Var): obj_to_parse = [obj[0].expr] else: obj_to_parse = obj[0].expr.args first_stage_variable_set = ComponentSet(model.util.first_stage_variables) second_stage_variable_set = ComponentSet(model.util.second_stage_variables) for term in obj_to_parse: vars_in_term = list(v for v in identify_variables(term)) first_stage_vars_in_term = list(v for v in vars_in_term if v in first_stage_variable_set) second_stage_vars_in_term = list(v for v in vars_in_term if v not in first_stage_variable_set) # By checking not in first_stage_variable_set, you pick up both ssv and state vars for v in first_stage_vars_in_term: if id(v) not in list(id(var) for var in first_stage_terms): first_stage_terms.append(v) for v in second_stage_vars_in_term: if id(v) not in list(id(var) for var in second_stage_terms): second_stage_terms.append(v) if first_stage_vars_in_term and second_stage_vars_in_term: second_stage_cost_expr += term elif first_stage_vars_in_term and not second_stage_vars_in_term: first_stage_cost_expr += term elif not first_stage_vars_in_term and second_stage_vars_in_term: second_stage_cost_expr += term elif not vars_in_term: const_obj_expr += term # convention to add constant objective term to first stage costs # IFF the const_obj_term does not contain an uncertain param! Else, it is second-stage cost mutable_params_in_const_term = identify_mutable_parameters(expr=const_obj_expr) if any(q in ComponentSet(model.util.uncertain_params) for q in mutable_params_in_const_term): m.first_stage_objective = Expression(expr=first_stage_cost_expr ) m.second_stage_objective = Expression(expr=second_stage_cost_expr + const_obj_expr) else: m.first_stage_objective = Expression(expr=first_stage_cost_expr + const_obj_expr) m.second_stage_objective = Expression(expr=second_stage_cost_expr) return
def test_identify_params_numeric(self): # # There are no parameters in a constant expression # self.assertEqual( list(identify_mutable_parameters(5)), [] )