def test_npv_abs(self): m = ConcreteModel() m.p1 = Param(mutable=True) m.p2 = Param(mutable=True) m.x = Var() e1 = abs(m.p1) e2 = replace_expressions(e1, {id(m.p1): m.p2}) e3 = replace_expressions(e1, {id(m.p1): m.x}) self.assertTrue(compare_expressions(e2, abs(m.p2))) self.assertTrue(compare_expressions(e3, abs(m.x)))
def test_npv_div(self): m = ConcreteModel() m.p1 = Param(mutable=True) m.p2 = Param(mutable=True) m.x = Var() e1 = m.p1/3 e2 = replace_expressions(e1, {id(m.p1): m.p2}) e3 = replace_expressions(e1, {id(m.p1): m.x}) self.assertTrue(compare_expressions(e2, m.p2/3)) self.assertTrue(compare_expressions(e3, DivisionExpression([m.x, 3])))
def test_npv_unary(self): m = ConcreteModel() m.p1 = Param(mutable=True) m.p2 = Param(mutable=True) m.x = Var(initialize=0) e1 = sin(m.p1) e2 = replace_expressions(e1, {id(m.p1): m.p2}) e3 = replace_expressions(e1, {id(m.p1): m.x}) self.assertTrue(compare_expressions(e2, sin(m.p2))) self.assertTrue(compare_expressions(e3, sin(m.x)))
def test_npv_product(self): m = ConcreteModel() m.p1 = Param(mutable=True) m.p2 = Param(mutable=True) m.x = Var() e1 = m.p1*3 e2 = replace_expressions(e1, {id(m.p1): m.p2}) e3 = replace_expressions(e1, {id(m.p1): m.x}) self.assertTrue(compare_expressions(e2, m.p2*3)) self.assertTrue(compare_expressions(e3, ProductExpression([m.x, 3])))
def test_npv_negation(self): m = ConcreteModel() m.p1 = Param(mutable=True) m.p2 = Param(mutable=True) m.x = Var() e1 = -m.p1 e2 = replace_expressions(e1, {id(m.p1): m.p2}) e3 = replace_expressions(e1, {id(m.p1): m.x}) self.assertTrue(compare_expressions(e2, -m.p2)) self.assertTrue(compare_expressions(e3, NegationExpression([m.x])))
def test_npv_sum(self): m = ConcreteModel() m.p1 = Param(mutable=True) m.p2 = Param(mutable=True) m.x = Var() e1 = m.p1 + 2 e2 = replace_expressions(e1, {id(m.p1): m.p2}) e3 = replace_expressions(e1, {id(m.p1): m.x}) self.assertTrue(compare_expressions(e2, m.p2 + 2)) self.assertTrue(compare_expressions(e3, m.x + 2))
def test_replace_expressions_with_monomial_term(self): M = ConcreteModel() M.x = Var() e = 2.0 * M.x substitution_map = {id(M.x): 3.0 * M.x} new_e = replace_expressions(e, substitution_map=substitution_map) self.assertEqual('6.0*x', str(new_e))
def test_replace_expressions_with_monomial_term(self): M = ConcreteModel() M.x = Var() e = 2.0*M.x substitution_map = {id(M.x): 3.0*M.x} new_e = replace_expressions(e, substitution_map=substitution_map) self.assertEqual('6.0*x', str(new_e))
def construct_separation_problem(self, sense=maximize): m = ConcreteModel() uncparam = self._uncparam[0] index = uncparam.index_set() # Create inner problem variables # TODO: use setattr to give uncparam same name as in model m.uncparam = Var(index) # collect current coefficient values expr = self._rule(m.uncparam, compute_values=True) # construct objective with current coefficient values m.obj = Objective(expr=expr, sense=sense) # construct constraints from uncertainty set uncset = self._uncset[0] m.cons = ConstraintList() substitution_map = {id(uncparam[i]): m.uncparam[i] for i in index} if not uncset.is_lib(): for c in uncset.component_data_objects(Constraint): m.cons.add( (c.lower, replace_expressions(c.body, substitution_map), c.upper)) else: for cons in uncset.generate_cons_from_lib(m.uncparam): m.cons.add(cons) return m
def _apply_to(self, instance): cons = self.get_uncertain_components(instance) objs = self.get_uncertain_components(instance, component=Objective) smap = {} for c in cons: param = collect_uncparam(c) for i in param: smap[id(param[i])] = param[i].nominal body_nominal = replace_expressions(c.body, smap) c.set_value((c.lower, body_nominal, c.upper)) for o in objs: param = collect_uncparam(o) for i in param: smap[id(param[i])] = param[i].nominal expr_nominal = replace_expressions(o.expr, smap) o.expr = expr_nominal
def substitute_ssv_in_dr_constraints(model, constraint): ''' Generate the standard_repn for the dr constraints. Generate new expression with replace_expression to ignore the ssv component. Then, replace_expression with substitution_map between ssv and the new expression. Deactivate or del_component the original dr equation. Then, return modified model and do coefficient matching as normal. :param model: the working_model :param constraint: an equality constraint from the working model identified to be of the form h(x,z,q) = 0. :return: ''' dr_eqns = model.util.decision_rule_eqns fsv = ComponentSet(model.util.first_stage_variables) if not hasattr(model, "dr_substituted_constraints"): model.dr_substituted_constraints = ConstraintList() for eqn in dr_eqns: repn = generate_standard_repn(eqn.body, compute_values=False) new_expression = 0 map_linear_coeff_to_var = [x for x in zip(repn.linear_coefs, repn.linear_vars) if x[1] in ComponentSet(fsv)] map_quad_coeff_to_var = [x for x in zip(repn.quadratic_coefs, repn.quadratic_vars) if x[1] in ComponentSet(fsv)] if repn.linear_coefs: for coeff, var in map_linear_coeff_to_var: new_expression += coeff * var if repn.quadratic_coefs: for coeff, var in map_quad_coeff_to_var: new_expression += coeff * var[0] * var[1] # var here is a 2-tuple model.no_ssv_dr_expr = Expression(expr=new_expression) substitution_map = {} substitution_map[id(repn.linear_vars[-1])] = model.no_ssv_dr_expr.expr model.dr_substituted_constraints.add( replace_expressions(expr=constraint.lower, substitution_map=substitution_map) == replace_expressions(expr=constraint.body, substitution_map=substitution_map)) # === Delete the original constraint model.del_component(constraint.name) model.del_component("no_ssv_dr_expr") return model.dr_substituted_constraints[max(model.dr_substituted_constraints.keys())]
def _apply_to(self, instance): cons = self.get_uncertain_components(instance) objs = self.get_uncertain_components(instance, component=Objective) smap = {} for c in cons: param = collect_uncparam(c) for i in param: if param[i].nominal is None: raise RuntimeError("Uncertain parameter '{}' does not have" " a nominal value.".format( param[i].name)) smap[id(param[i])] = param[i].nominal body_nominal = replace_expressions(c.body, smap) c.set_value((c.lower, body_nominal, c.upper)) for o in objs: param = collect_uncparam(o) for i in param: smap[id(param[i])] = param[i].nominal expr_nominal = replace_expressions(o.expr, smap) o.expr = expr_nominal
def _apply_to(self, instance): for c in chain( self.get_adjustable_components(instance), self.get_adjustable_components(instance, component=Objective)): # Collect adjustable var adjvar = collect_adjustable(c) # Get regular var if adjvar.name not in self._adjvar_dict: var = Var(adjvar.index_set(), bounds=adjvar._bounds_init_value) setattr(instance, adjvar.name + '_nominal', var) self._adjvar_dict[adjvar.name] = var for i in adjvar: var[i].fixed = adjvar[i].fixed var[i].setlb(adjvar[i].lb) var[i].setub(adjvar[i].ub) var[i].value = adjvar[i].value else: var = self._adjvar_dict[adjvar.name] # Construct substitution map sub_map = {id(adjvar[i]): var[i] for i in adjvar} # Replace AdjustableVar with Var if c.ctype is Objective: e_new = replace_expressions(c.expr, substitution_map=sub_map) c_new = Objective(expr=e_new, sense=c.sense) else: e_new = replace_expressions(c.body, substitution_map=sub_map) if c.equality: c_new = Constraint(expr=e_new == c.upper) else: c_new = Constraint( expr=inequality(c.lower, e_new, c.upper)) setattr(instance, c.name + '_nominal', c_new) self._cons_dict[c.name] = (c, c_new) c.deactivate()
def construct_master_feasibility_problem(model_data, config): """ Construct a slack-variable based master feasibility model. Initialize all model variables appropriately, and scale slack variables as well. Parameters ---------- model_data : MasterProblemData Master problem data. config : ConfigDict PyROS solver config. Returns ------- model : ConcreteModel Slack variable model. """ model = model_data.master_model.clone() for obj in model.component_data_objects(Objective): obj.deactivate() iteration = model_data.iteration # first stage vars are already initialized appropriately. # initialize second-stage DOF variables using DR equation expressions if model.scenarios[iteration, 0].util.second_stage_variables: for blk in model.scenarios[iteration, :]: for eq in blk.util.decision_rule_eqns: vars_in_dr_eq = ComponentSet(identify_variables(eq.body)) ssv_set = ComponentSet(blk.util.second_stage_variables) # get second-stage var in DR eqn. should only be one var ssv_in_dr_eq = [ var for var in vars_in_dr_eq if var in ssv_set ][0] # update var value for initialization # fine since DR eqns are f(d) - z == 0 (not z - f(d) == 0) ssv_in_dr_eq.set_value(0) ssv_in_dr_eq.set_value(eq.body) # initialize state vars to previous master solution values if iteration != 0: stvar_map = get_state_vars(model, [iteration, iteration - 1]) for current, prev in zip(stvar_map[iteration], stvar_map[iteration - 1]): current.set_value(prev) # constraints to which slacks should be added # (all the constraints for the current iteration, except the DR eqns) targets = [] for blk in model.scenarios[iteration, :]: if blk.util.second_stage_variables: dr_eqs = blk.util.decision_rule_eqns else: dr_eqs = list() targets.extend([ con for con in blk.component_data_objects( Constraint, active=True, descend_into=True) if con not in dr_eqs ]) # retain original constraint exprs (for slack initialization and scaling) pre_slack_con_exprs = ComponentMap([(con, con.body) for con in targets]) # add slack variables and objective # inequalities g(v) <= b become g(v) -- s^-<= b # equalities h(v) == b become h(v) -- s^- + s^+ == b TransformationFactory("core.add_slack_variables").apply_to(model, targets=targets) slack_vars = ComponentSet( model._core_add_slack_variables.component_data_objects( Var, descend_into=True)) # initialize and scale slack variables for con in pre_slack_con_exprs: # obtain slack vars in updated constraints # and their coefficients (+/-1) in the constraint expression repn = generate_standard_repn(con.body) slack_var_coef_map = ComponentMap() for idx in range(len(repn.linear_vars)): var = repn.linear_vars[idx] if var in slack_vars: slack_var_coef_map[var] = repn.linear_coefs[idx] slack_substitution_map = dict() for slack_var in slack_var_coef_map: # coefficient determines whether the slack is a +ve or -ve slack if slack_var_coef_map[slack_var] == -1: con_slack = max(0, value(pre_slack_con_exprs[con])) else: con_slack = max(0, -value(pre_slack_con_exprs[con])) # initialize slack var, evaluate scaling coefficient scaling_coeff = 1 slack_var.set_value(con_slack) # update expression replacement map slack_substitution_map[id(slack_var)] = (scaling_coeff * slack_var) # finally, scale slack(s) con.set_value(( replace_expressions(con.lower, slack_substitution_map), replace_expressions(con.body, slack_substitution_map), replace_expressions(con.upper, slack_substitution_map), )) return model
def generate_structured_model(self): """ Using the community map and the original model used to create this community map, we will create structured_model, which will be based on the original model but will place variables, constraints, and objectives into or outside of various blocks (communities) based on the community map. Returns ------- structured_model: Block a Pyomo model that reflects the nature of the community map """ # Initialize a new model (structured_model) which will contain variables and constraints in blocks based on # their respective communities within the CommunityMap structured_model = ConcreteModel() # Create N blocks (where N is the number of communities found within the model) structured_model.b = Block([0, len(self.community_map) - 1, 1]) # values given for (start, stop, step) # Initialize a ComponentMap that will map a variable from the model (for example, old_model.x1) used to # create the CommunityMap to a list of variables in various blocks that were created based on this # variable (for example, [structured_model.b[0].x1, structured_model.b[3].x1]) blocked_variable_map = ComponentMap() # Example key-value pair -> {original_model.x1 : [structured_model.b[0].x1, structured_model.b[3].x1]} # TODO - Consider changing structure of the next two for loops to be more efficient (maybe loop through # constraints and add variables as you go) (but note that disconnected variables would be # missed with this strategy) # First loop through community_map to add all the variables to structured_model before we add constraints # that use those variables for community_key, community in self.community_map.items(): _, variables_in_community = community # Loop through all of the variables (from the original model) in the given community for stored_variable in variables_in_community: # Construct a new_variable whose attributes are determined by querying the variable from the # original model new_variable = Var(domain=stored_variable.domain, bounds=stored_variable.bounds) # Add this new_variable to its block/community and name it using the string of the variable from the # original model structured_model.b[community_key].add_component( str(stored_variable), new_variable) # Since there could be multiple variables 'x1' (such as # structured_model.b[0].x1, structured_model.b[3].x1, etc), we need to create equality constraints # for all of the variables 'x1' within structured_model (this is the purpose of blocked_variable_map) # Here we update blocked_variable_map to keep track of what equality constraints need to be made variable_in_new_model = structured_model.find_component( new_variable) blocked_variable_map[ stored_variable] = blocked_variable_map.get( stored_variable, []) + [variable_in_new_model] # Now that we have all of our variables within the model, we will initialize a dictionary that used to # replace variables within constraints to other variables (in our case, this will convert variables from the # original model into variables from the new model (structured_model)) replace_variables_in_expression_map = dict() # Loop through community_map again, this time to add constraints (with replaced variables) for community_key, community in self.community_map.items(): constraints_in_community, _ = community # Loop through all of the constraints (from the original model) in the given community for stored_constraint in constraints_in_community: # Now, loop through all of the variables within the given constraint expression for variable_in_stored_constraint in identify_variables( stored_constraint.expr): # Loop through each of the "blocked" variables that a variable is mapped to and update # replace_variables_in_expression_map if a variable has a "blocked" form in the given community # What this means is that if we are looping through constraints in community 0, then it would be # best to change a variable x1 into b[0].x1 as opposed to b[2].x1 or b[5].x1 (assuming all of these # blocked versions of the variable x1 exist (which depends on the community map)) variable_in_current_block = False for blocked_variable in blocked_variable_map[ variable_in_stored_constraint]: if 'b[%d]' % community_key in str(blocked_variable): # Update replace_variables_in_expression_map accordingly replace_variables_in_expression_map[ id(variable_in_stored_constraint )] = blocked_variable variable_in_current_block = True if not variable_in_current_block: # Create a version of the given variable outside of blocks then add it to # replace_variables_in_expression_map new_variable = Var( domain=variable_in_stored_constraint.domain, bounds=variable_in_stored_constraint.bounds) # Add the new variable just as we did above (but now it is not in any blocks) structured_model.add_component( str(variable_in_stored_constraint), new_variable) # Update blocked_variable_map to keep track of what equality constraints need to be made variable_in_new_model = structured_model.find_component( new_variable) blocked_variable_map[ variable_in_stored_constraint] = blocked_variable_map.get( variable_in_stored_constraint, []) + [variable_in_new_model] # Update replace_variables_in_expression_map accordingly replace_variables_in_expression_map[ id(variable_in_stored_constraint )] = variable_in_new_model # TODO - Is there a better way to check whether something is actually an objective? (as done below) # Check to see whether 'stored_constraint' is actually an objective (since constraints and objectives # grouped together) if self.with_objective and isinstance( stored_constraint, (_GeneralObjectiveData, Objective)): # If the constraint is actually an objective, we add it to the block as an objective new_objective = Objective(expr=replace_expressions( stored_constraint.expr, replace_variables_in_expression_map)) structured_model.b[community_key].add_component( str(stored_constraint), new_objective) else: # Construct a constraint based on the expression within stored_constraint and the dict we have # created for the purpose of replacing the variables within the constraint expression new_constraint = Constraint(expr=replace_expressions( stored_constraint.expr, replace_variables_in_expression_map)) # Add this new constraint to the corresponding community/block with its name as the string of the # constraint from the original model structured_model.b[community_key].add_component( str(stored_constraint), new_constraint) # If with_objective was set to False, that means we might have missed an objective function within the # original model if not self.with_objective: # Construct a new dictionary for replacing the variables (replace_variables_in_objective_map) which will # be specific to the variables in the objective function, since there is the possibility that the # objective contains variables we have not yet seen (and thus not yet added to our new model) for objective_function in self.model.component_data_objects( ctype=Objective, active=self.use_only_active_components, descend_into=True): for variable_in_objective in identify_variables( objective_function): # Add all of the variables in the objective function (not within any blocks) # Check to make sure a form of the variable has not already been made outside of the blocks if structured_model.find_component( str(variable_in_objective)) is None: new_variable = Var(domain=variable_in_objective.domain, bounds=variable_in_objective.bounds) structured_model.add_component( str(variable_in_objective), new_variable) # Again we update blocked_variable_map to keep track of what # equality constraints need to be made variable_in_new_model = structured_model.find_component( new_variable) blocked_variable_map[ variable_in_objective] = blocked_variable_map.get( variable_in_objective, []) + [variable_in_new_model] # Update the dictionary that we will use to replace the variables replace_variables_in_expression_map[id( variable_in_objective)] = variable_in_new_model else: for version_of_variable in blocked_variable_map[ variable_in_objective]: if 'b[' not in str(version_of_variable): replace_variables_in_expression_map[ id(variable_in_objective )] = version_of_variable # Now we will construct a new objective function based on the one from the original model and then # add it to the new model just as we have done before new_objective = Objective(expr=replace_expressions( objective_function.expr, replace_variables_in_expression_map)) structured_model.add_component(str(objective_function), new_objective) # Now, we need to create equality constraints for all of the different "versions" of a variable (such # as x1, b[0].x1, b[2].x2, etc.) # Create a constraint list for the equality constraints structured_model.equality_constraint_list = ConstraintList( doc="Equality Constraints for the different " "forms of a given variable") # Loop through blocked_variable_map and create constraints accordingly for variable, duplicate_variables in blocked_variable_map.items(): # variable -> variable from the original model # duplicate_variables -> list of variables in the new model # Create a list of all the possible equality constraints that need to be made equalities_to_make = combinations(duplicate_variables, 2) # Loop through the list of two-variable tuples and create an equality constraint for those two variables for variable_1, variable_2 in equalities_to_make: structured_model.equality_constraint_list.add( expr=variable_1 == variable_2) # Return 'structured_model', which is essentially identical to the original model but now has all of the # variables, constraints, and objectives placed into blocks based on the nature of the CommunityMap return structured_model
def _apply_to(self, instance): for c in chain( self.get_adjustable_components(instance), self.get_adjustable_components(instance, component=Objective)): # Collect adjustable vars and uncparams adjvar = collect_adjustable(c) if id(adjvar) not in self._adjvars: self._adjvars[id(adjvar)] = adjvar # Create variables for LDR coefficients for i in adjvar: for u in adjvar[i].uncparams: parent = u.parent_component() if (adjvar.name, parent.name) not in self._coef_dict: coef = Var(adjvar.index_set(), parent.index_set()) coef_name = adjvar.name + '_' + parent.name + '_coef' setattr(instance, coef_name, coef) self._coef_dict[adjvar.name, parent.name] = coef # Create substitution map def coef(u): return self._coef_dict[adjvar.name, u.parent_component().name] def gen_index(u): if hasattr(u, 'index'): yield u.index() else: for i in u: yield i sub_map = { id(adjvar[i]): sum(u.parent_component()[j] * coef(u)[i, j] for u in adjvar[i].uncparams for j in gen_index(u)) for i in adjvar } self._expr_dict[adjvar.name] = sub_map # Replace AdjustableVar by LDR # Objectives if c.ctype is Objective: e_new = replace_expressions(c.expr, substitution_map=sub_map) c_new = Objective(expr=e_new, sense=c.sense) setattr(instance, c.name + '_ldr', c_new) # Constraints elif c.ctype is Constraint: e_new = replace_expressions(c.body, substitution_map=sub_map) if c.equality: repn = self.generate_repn_param(instance, e_new) c_new = ConstraintList() setattr(instance, c.name + '_ldr', c_new) # Check if repn.constant is an expression cons = repn.constant if cons.__class__ in nonpyomo_leaf_types: if cons != 0: raise ValueError("Can't reformulate constraint {} " "with numeric constant " "{}".format(c.name, cons)) elif cons.is_potentially_variable(): c_new.add(cons == 0) else: raise ValueError("Can't reformulate constraint {} with" " constant " "{}".format(c.name, cons)) # Add constraints for each uncparam for coef in repn.linear_coefs: c_new.add(coef == 0) for coef in repn.quadratic_coefs: c_new.add(coef == 0) else: def c_rule(x): return (c.lower, e_new, c.upper) c_new = Constraint(rule=c_rule) setattr(instance, c.name + '_ldr', c_new) c.deactivate() # Add constraints for bounds on AdjustableVar for name, sub_map in self._expr_dict.items(): adjvar = instance.find_component(name) cl = ConstraintList() setattr(instance, adjvar.name + '_bounds', cl) for i in adjvar: if adjvar[i].has_lb(): cl.add(adjvar[i].lb <= sub_map[id(adjvar[i])]) if adjvar[i].has_ub(): cl.add(adjvar[i].ub >= sub_map[id(adjvar[i])])
def _reformulate(self, c, param, uncset, counterpart, initialize_wolfe=False): """ Reformulate an uncertain constraint or objective c: Constraint or Objective param: UncParam uncset: UncSet counterpart: Block """ # Only proceed if uncertainty set is WarpedGPSet from rogp.util.numpy import _pyomo_to_np, _to_np_obj_array repn = self.generate_repn_param(c) assert repn.is_linear(), ("Only constraints which are linear in " "the uncertain parameters are valid for " "uncertainty set WarpedGPSet.") # Constraint may contain subset of elements of UncParam index_set = [p.index() for p in repn.linear_vars] x = [[xi] for xi in repn.linear_coefs] x = _to_np_obj_array(x) # Set up Block for Wolfe duality constraints b = counterpart # Set up extra variables b.y = Var(param.index_set()) # Set bounds for extra variables based on UncParam bounds for i in param: lb, ub = param[i].lb, param[i].ub b.y[i].setlb(lb) b.y[i].setub(ub) if ((lb is not None and lb is not float('-inf')) and (ub is not None and ub is not float('inf'))): b.y[i].value = (ub + lb) / 2 y = _pyomo_to_np(b.y, ind=index_set) # Setup dual vars based on sense if c.ctype is Constraint: if c.has_ub() and not c.has_lb(): b.u = Var(within=NonPositiveReals) elif not c.has_ub() and c.has_lb(): b.u = Var(within=NonNegativeReals) else: raise RuntimeError( "Uncertain constraints with 'WarpedGPSet' " "currently only support either an upper or a " "lower bound, not both.") elif c.ctype is Objective: if c.sense is maximize: b.u = Var(within=NonPositiveReals) else: b.u = Var(within=NonNegativeReals) u = _pyomo_to_np(b.u) # Primal constraint sub_map = {id(param[i]): b.y[i] for i in param} if c.ctype is Constraint: e_new = replace_expressions(c.body, substitution_map=sub_map) b.primal = Constraint(rule=lambda x: (c.lower, e_new, c.upper)) else: e_new = replace_expressions(c.expr, substitution_map=sub_map) b.primal = Objective(expr=e_new, sense=c.sense) # Calculate matrices gp = uncset.gp var = uncset.var if type(var) is dict: z = [var[i] for i in index_set] z = _to_np_obj_array(z) else: var = var[0] assert var.index_set() == param.index_set(), ( "Index set of `UncParam` and `var` in `WarpedGPSet` " "should be the same. Alternatively use " "var = {index: [list of vars]}") z = _pyomo_to_np(var, ind=index_set) Sig = gp.predict_cov_latent(z) mu = gp.predict_mu_latent(z) dHinv = 1 / gp.warp_deriv(y) dHinv = np.diag(dHinv[:, 0]) hz = gp.warp(y) LHS = np.matmul(Sig, dHinv) LHS = np.matmul(LHS, x) RHS = LHS LHS = LHS + 2 * u * (hz - mu) # Add stationarity condition b.stationarity = ConstraintList() for lhs in np.nditer(LHS, ['refs_ok']): b.stationarity.add(lhs.item() == 0) RHS = np.matmul(dHinv, RHS) rhs = np.matmul(x.T, RHS)[0, 0] lhs = 4 * u[0, 0]**2 * uncset.F # Set consistent initial value for u (helps convergence) if initialize_wolfe: u0 = np.sqrt(rhs() / 4 / uncset.F) if u[0, 0].ub == 0: u[0, 0].value = -u0 elif u[0, 0].lb == 0: u[0, 0].value = u0 # Dual variable constraint b.dual = Constraint(expr=lhs == rhs)
def SDM(self, model_name): ''' Algorithm 2 in Boland et al. (with small tweaks) ''' mip = self.local_subproblems[model_name] qp = self.local_QP_subproblems[model_name] # Set the QP dual weights to the correct values If we are bundling, we # initialize the QP dual weights to be the dual weights associated with # the first scenario in the bundle (this should be okay, because each # scenario in the bundle has the same dual weights, analytically--maybe # a numerical problem). arb_scen_mip = self.local_scenarios[mip.scen_list[0]] \ if self.bundling else mip for (node_name, ix) in arb_scen_mip._nonant_indexes: qp._Ws[node_name, ix]._value = \ arb_scen_mip._Ws[node_name, ix].value alpha = self.FW_options['FW_weight'] # Algorithm 3 line 6 xt = { ndn_i: (1 - alpha) * pyo.value(arb_scen_mip._xbars[ndn_i]) + alpha * pyo.value(xvar) for ndn_i, xvar in arb_scen_mip._nonant_indexes.items() } for itr in range(self.FW_options['FW_iter_limit']): # Algorithm 2 line 4 mip_source = mip.scen_list if self.bundling else [model_name] for scenario_name in mip_source: scen_mip = self.local_scenarios[scenario_name] for ndn_i, nonant in scen_mip._nonant_indexes.items(): x_source = xt[ndn_i] if itr==0 \ else nonant._value scen_mip._Ws[ndn_i]._value = ( qp._Ws[ndn_i]._value + scen_mip._PHrho[ndn_i]._value * (x_source - scen_mip._xbars[ndn_i]._value)) # Algorithm 2 line 5 if (sputils.is_persistent(mip._solver_plugin)): mip_obj = find_active_objective(mip, True) mip._solver_plugin.set_objective(mip_obj) mip_results = mip._solver_plugin.solve(mip) self._check_solve(mip_results, model_name + ' (MIP)') # Algorithm 2 lines 6--8 obj = find_active_objective(mip, True) if (itr == 0): dual_bound = pyo.value(obj) # Algorithm 2 line 9 (compute \Gamma^t) val0 = pyo.value(obj) new = replace_expressions(obj.expr, mip.mip_to_qp) val1 = pyo.value(new) obj.expr = replace_expressions(new, qp.qp_to_mip) if abs(val0) > 1e-9: stop_check = (val1 - val0) / abs( val0) # \Gamma^t in Boland, but normalized else: stop_check = val1 - val0 # \Gamma^t in Boland stop_check_tol = self.FW_options["stop_check_tol"]\ if "stop_check_tol" in self.FW_options else 1e-4 if (self.is_minimizing and stop_check < -stop_check_tol): print( 'Warning (fwph): convergence quantity Gamma^t = ' '{sc:.2e} (should be non-negative)'.format(sc=stop_check)) print('Try decreasing the MIP gap tolerance and re-solving') elif (not self.is_minimizing and stop_check > stop_check_tol): print( 'Warning (fwph): convergence quantity Gamma^t = ' '{sc:.2e} (should be non-positive)'.format(sc=stop_check)) print('Try decreasing the MIP gap tolerance and re-solving') self._add_QP_column(model_name) if (sputils.is_persistent(qp._QP_solver_plugin)): qp_obj = find_active_objective(qp, True) qp._QP_solver_plugin.set_objective(qp_obj) qp_results = qp._QP_solver_plugin.solve(qp) self._check_solve(qp_results, model_name + ' (QP)') if (stop_check < self.FW_options['FW_conv_thresh']): break # Re-set the mip._Ws so that the QP objective # is correct in the next major iteration mip_source = mip.scen_list if self.bundling else [model_name] for scenario_name in mip_source: scen_mip = self.local_scenarios[scenario_name] for (node_name, ix) in scen_mip._nonant_indexes: scen_mip._Ws[node_name, ix]._value = \ qp._Ws[node_name, ix]._value return dual_bound
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 _parse_sol(self): solve_vars = self._writer.get_ordered_vars() solve_cons = self._writer.get_ordered_cons() results = Results() f = open(self._filename + '.sol', 'r') all_lines = list(f.readlines()) f.close() termination_line = all_lines[1] if 'Optimal Solution Found' in termination_line: results.termination_condition = TerminationCondition.optimal elif 'Problem may be infeasible' in termination_line: results.termination_condition = TerminationCondition.infeasible elif 'problem might be unbounded' in termination_line: results.termination_condition = TerminationCondition.unbounded elif 'Maximum Number of Iterations Exceeded' in termination_line: results.termination_condition = TerminationCondition.maxIterations elif 'Maximum CPU Time Exceeded' in termination_line: results.termination_condition = TerminationCondition.maxTimeLimit else: results.termination_condition = TerminationCondition.unknown n_cons = len(solve_cons) n_vars = len(solve_vars) dual_lines = all_lines[12:12 + n_cons] primal_lines = all_lines[12 + n_cons:12 + n_cons + n_vars] rc_upper_info_line = all_lines[12 + n_cons + n_vars + 1] assert rc_upper_info_line.startswith('suffix') n_rc_upper = int(rc_upper_info_line.split()[2]) assert 'ipopt_zU_out' in all_lines[12 + n_cons + n_vars + 2] upper_rc_lines = all_lines[12 + n_cons + n_vars + 3:12 + n_cons + n_vars + 3 + n_rc_upper] rc_lower_info_line = all_lines[12 + n_cons + n_vars + 3 + n_rc_upper] assert rc_lower_info_line.startswith('suffix') n_rc_lower = int(rc_lower_info_line.split()[2]) assert 'ipopt_zL_out' in all_lines[12 + n_cons + n_vars + 3 + n_rc_upper + 1] lower_rc_lines = all_lines[12 + n_cons + n_vars + 3 + n_rc_upper + 2:12 + n_cons + n_vars + 3 + n_rc_upper + 2 + n_rc_lower] self._dual_sol = dict() self._primal_sol = ComponentMap() self._reduced_costs = ComponentMap() for ndx, dual in enumerate(dual_lines): dual = float(dual) con = solve_cons[ndx] self._dual_sol[con] = dual for ndx, primal in enumerate(primal_lines): primal = float(primal) var = solve_vars[ndx] self._primal_sol[var] = primal for rcu_line in upper_rc_lines: split_line = rcu_line.split() var_ndx = int(split_line[0]) rcu = float(split_line[1]) var = solve_vars[var_ndx] self._reduced_costs[var] = rcu for rcl_line in lower_rc_lines: split_line = rcl_line.split() var_ndx = int(split_line[0]) rcl = float(split_line[1]) var = solve_vars[var_ndx] if var in self._reduced_costs: if abs(rcl) > abs(self._reduced_costs[var]): self._reduced_costs[var] = rcl else: self._reduced_costs[var] = rcl for var in solve_vars: if var not in self._reduced_costs: self._reduced_costs[var] = 0 if results.termination_condition == TerminationCondition.optimal and self.config.load_solution: for v, val in self._primal_sol.items(): v.set_value(val, skip_validation=True) if self._writer.get_active_objective() is None: results.best_feasible_objective = None else: results.best_feasible_objective = value( self._writer.get_active_objective().expr) elif results.termination_condition == TerminationCondition.optimal: if self._writer.get_active_objective() is None: results.best_feasible_objective = None else: obj_expr_evaluated = replace_expressions( self._writer.get_active_objective().expr, substitution_map={ id(v): val for v, val in self._primal_sol.items() }, descend_into_named_expressions=True, remove_named_expressions=True) results.best_feasible_objective = value(obj_expr_evaluated) elif self.config.load_solution: raise RuntimeError( 'A feasible solution was not found, so no solution can be loaded.' 'Please set opt.config.load_solution=False and check ' 'results.termination_condition and ' 'resutls.best_feasible_objective before loading a solution.') results.solution_loader = PersistentSolutionLoader(solver=self) return results
def copy_relaxation_with_local_data(rel, old_var_to_new_var_map): """ This function copies a relaxation object with new variables. Note that only what can be set through the set_input and build methods are copied. For example, piecewise partitioning points are not copied. Parameters ---------- rel: coramin.relaxations.relaxations_base.BaseRelaxationData The relaxation to be copied old_var_to_new_var_map: dict Map from the original variable id to the new variable Returns ------- rel: coramin.relaxations.relaxations_base.BaseRelaxationData The copy of rel with new variables """ if isinstance(rel, PWXSquaredRelaxationData): new_x = old_var_to_new_var_map[id(rel.get_rhs_vars()[0])] new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())] new_rel = PWXSquaredRelaxation(concrete=True) new_rel.set_input(x=new_x, aux_var=new_aux_var, pw_repn=rel._pw_repn, use_linear_relaxation=rel.use_linear_relaxation, relaxation_side=rel.relaxation_side) elif isinstance(rel, PWArctanRelaxationData): new_x = old_var_to_new_var_map[id(rel.get_rhs_vars()[0])] new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())] new_rel = PWArctanRelaxation(concrete=True) new_rel.set_input(x=new_x, aux_var=new_aux_var, pw_repn=rel._pw_repn, relaxation_side=rel.relaxation_side, use_linear_relaxation=rel.use_linear_relaxation) elif isinstance(rel, PWSinRelaxationData): new_x = old_var_to_new_var_map[id(rel.get_rhs_vars()[0])] new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())] new_rel = PWSinRelaxation(concrete=True) new_rel.set_input(x=new_x, aux_var=new_aux_var, pw_repn=rel._pw_repn, relaxation_side=rel.relaxation_side, use_linear_relaxation=rel.use_linear_relaxation) elif isinstance(rel, PWCosRelaxationData): new_x = old_var_to_new_var_map[id(rel.get_rhs_vars()[0])] new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())] new_rel = PWCosRelaxation(concrete=True) new_rel.set_input(x=new_x, aux_var=new_aux_var, pw_repn=rel._pw_repn, relaxation_side=rel.relaxation_side, use_linear_relaxation=rel.use_linear_relaxation) elif isinstance(rel, PWUnivariateRelaxationData): new_x = old_var_to_new_var_map[id(rel.get_rhs_vars()[0])] new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())] new_f_x_expr = replace_expressions( rel.get_rhs_expr(), substitution_map=old_var_to_new_var_map, remove_named_expressions=True) new_rel = PWUnivariateRelaxation(concrete=True) if rel.is_rhs_convex(): shape = FunctionShape.CONVEX elif rel.is_rhs_concave(): shape = FunctionShape.CONCAVE else: shape = FunctionShape.UNKNOWN new_rel.set_input(x=new_x, aux_var=new_aux_var, shape=shape, f_x_expr=new_f_x_expr, pw_repn=rel._pw_repn, relaxation_side=rel.relaxation_side, use_linear_relaxation=rel.use_linear_relaxation) elif isinstance(rel, PWMcCormickRelaxationData): rhs_vars = rel.get_rhs_vars() old_x1 = rhs_vars[0] old_x2 = rhs_vars[1] new_x1 = old_var_to_new_var_map[id(old_x1)] new_x2 = old_var_to_new_var_map[id(old_x2)] new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())] new_rel = PWMcCormickRelaxation(concrete=True) new_rel.set_input(x1=new_x1, x2=new_x2, aux_var=new_aux_var, relaxation_side=rel.relaxation_side) elif isinstance(rel, MultivariateRelaxationData): new_aux_var = old_var_to_new_var_map[id(rel.get_aux_var())] if rel.is_rhs_convex(): shape = FunctionShape.CONVEX elif rel.is_rhs_concave(): shape = FunctionShape.CONCAVE else: shape = FunctionShape.UNKNOWN new_f_x_expr = replace_expressions( rel.get_rhs_expr(), substitution_map=old_var_to_new_var_map, remove_named_expressions=True) new_rel = MultivariateRelaxation(concrete=True) new_rel.set_input(aux_var=new_aux_var, shape=shape, f_x_expr=new_f_x_expr, use_linear_relaxation=rel.use_linear_relaxation) else: raise ValueError('Unrecognized relaxation: {0}'.format(str(type(rel)))) return new_rel