def localVar(): """Two-term disjunction which declares a local variable y on one of the disjuncts, which is used in the objective function as well. Used to test that we will treat y as global in the transformations, despite where it is declared. """ # y appears in a global constraint and a single disjunct. m = ConcreteModel() m.x = Var(bounds=(0, 3)) m.disj1 = Disjunct() m.disj1.cons = Constraint(expr=m.x >= 1) m.disj2 = Disjunct() m.disj2.y = Var(bounds=(1, 3)) m.disj2.cons = Constraint(expr=m.x + m.disj2.y == 3) m.disjunction = Disjunction(expr=[m.disj1, m.disj2]) # This makes y global actually... But in disguise. m.objective = Objective(expr=m.x + m.disj2.y) return m
def generate_norm_inf_objective_function(model, setpoint_model, discrete_only=False): """ This function generates objective (PF-OA main problem) for minimum Norm Infinity distance to setpoint_model Norm-Infinity distance of (x,y) = \max_i |x_i - y_i| Parameters ---------- model: Pyomo model the model that needs new objective function setpoint_model: Pyomo model the model that provides the base point for us to calculate the distance discrete_only: Bool only optimize on distance between the discrete variables """ # skip objective_value variable and slack_var variables var_filter = (lambda v: v.is_integer()) if discrete_only \ else (lambda v: v.name != 'MindtPy_utils.objective_value' and 'MindtPy_utils.feas_opt.slack_var' not in v.name) model_vars = list(filter(var_filter, model.component_data_objects(Var))) setpoint_vars = list( filter(var_filter, setpoint_model.component_data_objects(Var))) assert len(model_vars) == len( setpoint_vars ), 'Trying to generate Norm Infinity objective function for models with different number of variables' model.MindtPy_utils.del_component('L_infinity_obj') obj_blk = model.MindtPy_utils.L_infinity_obj = Block() obj_blk.L_infinity_obj_var = Var(domain=Reals, bounds=(0, None)) obj_blk.abs_reform = ConstraintList() for v_model, v_setpoint in zip(model_vars, setpoint_vars): obj_blk.abs_reform.add( expr=v_model - v_setpoint.value >= -obj_blk.L_infinity_obj_var) obj_blk.abs_reform.add( expr=v_model - v_setpoint.value <= obj_blk.L_infinity_obj_var) return Objective(expr=obj_blk.L_infinity_obj_var)
def _generate_model(self): self.model = None self.model = ConcreteModel() model = self.model model._name = self.description model.x = Var(domain=RealInterval(bounds=(float('-inf'), None))) model.y = Var(bounds=(None, float('inf'))) model.obj = Objective(expr=model.x - model.y) model.c = ConstraintList() model.c.add(model.x >= -2) model.c.add(model.y <= 3) cdata = model.c.add((0, 1, 3)) assert cdata.lower == 0 assert cdata.upper == 3 assert cdata.body() == 1 assert not cdata.equality cdata = model.c.add((0, 2, 3)) assert cdata.lower == 0 assert cdata.upper == 3 assert cdata.body() == 2 assert not cdata.equality cdata = model.c.add((0, 1, None)) assert cdata.lower == 0 assert cdata.upper is None assert cdata.body() == 1 assert not cdata.equality cdata = model.c.add((None, 0, 1)) assert cdata.lower is None assert cdata.upper == 1 assert cdata.body() == 0 assert not cdata.equality cdata = model.c.add((1, 1)) assert cdata.lower == 1 assert cdata.upper == 1 assert cdata.body() == 1 assert cdata.equality
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 define_model(**kwds): model = ConcreteModel() model.x = Var(INDEX_SET1, INDEX_SET2, bounds=(-5, 4)) # domain variable model.Fx = Var(INDEX_SET1, INDEX_SET2) # range variable model.p = Param(INDEX_SET1, INDEX_SET2, initialize=1.0) model.obj = Objective(expr=sum_product(model.Fx), sense=kwds.pop('sense', maximize)) model.piecewise = Piecewise(INDEX_SET1, INDEX_SET2, model.Fx, model.x, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) #Fix the answer for testing purpose model.set_answer_constraint1 = Constraint(expr=model.x[1, 0] == -5.0) model.set_answer_constraint2 = Constraint(expr=model.x[2, 0] == -3.0) model.set_answer_constraint3 = Constraint(expr=model.x[3, 0] == -2.5) model.set_answer_constraint4 = Constraint(expr=model.x[4, 0] == -1.5) model.set_answer_constraint5 = Constraint(expr=model.x[5, 0] == 2.0) model.set_answer_constraint6 = Constraint(expr=model.x[6, 0] == 3.5) model.set_answer_constraint7 = Constraint(expr=model.x[7, 0] == 4.0) model.set_answer_constraint8 = Constraint(expr=model.x[1, 1] == -5.0) model.set_answer_constraint9 = Constraint(expr=model.x[2, 1] == -3.0) model.set_answer_constraint10 = Constraint(expr=model.x[3, 1] == -2.5) model.set_answer_constraint11 = Constraint(expr=model.x[4, 1] == -1.5) model.set_answer_constraint12 = Constraint(expr=model.x[5, 1] == 2.0) model.set_answer_constraint13 = Constraint(expr=model.x[6, 1] == 3.5) model.set_answer_constraint14 = Constraint(expr=model.x[7, 1] == 4.0) return model
def define_model(**kwds): model = ConcreteModel() model.x = Var(INDEX) # domain variable model.Fx = Var(INDEX) # range variable model.obj = Objective(expr=sum_product(model.Fx)+sum_product(model.x), sense=kwds.pop('sense',maximize)) model.piecewise = Piecewise(INDEX,model.Fx,model.x, pw_pts=DOMAIN_PTS, f_rule=F, unbounded_domain_var=True, **kwds) #Fix the answer for testing purposes model.set_answer_constraint1 = Constraint(expr= model.x[1] == 0.5) # Fx1 should solve to 0 model.set_answer_constraint2 = Constraint(expr= model.x[2] == 1.0) # model.set_answer_constraint3 = Constraint(expr= model.Fx[2] == 0.5) # model.set_answer_constraint4 = Constraint(expr= model.x[3] == 1.5) # Fx3 should solve to 1 model.set_answer_constraint5 = Constraint(expr= model.x[4] == 2.5) # Fx4 should solve to 1.5 return model
def solve_set_cover_mip(model, disj_needs_cover, solve_data, config): """Solve the set covering MIP to determine next configuration.""" m = model GDPopt = m.GDPopt_utils # number of disjuncts that still need to be covered num_needs_cover = sum(1 for disj_bool in disj_needs_cover if disj_bool) # number of disjuncts that have been covered num_covered = len(disj_needs_cover) - num_needs_cover # weights for the set covering problem weights = list((num_covered + 1 if disj_bool else 1) for disj_bool in disj_needs_cover) # Set up set covering objective if hasattr(GDPopt, "set_cover_obj"): del GDPopt.set_cover_obj GDPopt.set_cover_obj = Objective(expr=sum( weight * disj.binary_indicator_var for (weight, disj) in zip(weights, GDPopt.disjunct_list)), sense=maximize) mip_results = solve_linear_GDP(m.clone(), solve_data, config) if mip_results.feasible: config.logger.info('Solved set covering MIP') else: config.logger.info('Set covering problem is infeasible. ' 'Problem may have no more feasible ' 'disjunctive realizations.') if solve_data.mip_iteration <= 1: config.logger.warning('Set covering problem was infeasible. ' 'Check your linear and logical constraints ' 'for contradictions.') if solve_data.objective_sense == minimize: solve_data.LB = float('inf') else: solve_data.UB = float('-inf') return mip_results
def init_max_binaries(solve_data, config): """Initialize by maximizing binary variables and disjuncts. This function activates as many binary variables and disjucts as feasible. """ solve_data.mip_iteration += 1 linear_GDP = solve_data.linear_GDP.clone() config.logger.info("Generating initial linear GDP approximation by " "solving a subproblem that maximizes " "the sum of all binary and logical variables.") # Set up binary maximization objective linear_GDP.GDPopt_utils.objective.deactivate() binary_vars = (v for v in linear_GDP.component_data_objects( ctype=Var, descend_into=(Block, Disjunct)) if v.is_binary() and not v.fixed) linear_GDP.GDPopt_utils.max_binary_obj = Objective(expr=sum(binary_vars), sense=maximize) # Solve mip_results = solve_data.mip_solve_function(linear_GDP, solve_data, config) if mip_results.feasible: nlp_result = solve_data.nlp_solve_function(mip_results.var_values, solve_data, config) if nlp_result.feasible: solve_data.cut_generation_function(nlp_result, solve_data, config) solve_data.integer_cut_function(mip_results.var_values, solve_data, config, feasible=nlp_result.feasible) else: config.logger.info( "Linear relaxation for initialization was infeasible. " "Problem is infeasible.") return False
def generate_norm1_objective_function(model, setpoint_model, discrete_only=False): """This function generates objective (PF-OA main problem) for minimum Norm1 distance to setpoint_model. Norm1 distance of (x,y) = \sum_i |x_i - y_i|. Args: model (Pyomo model): the model that needs new objective function. setpoint_model (Pyomo model): the model that provides the base point for us to calculate the distance. discrete_only (bool, optional): whether only optimize on distance between the discrete variables. Defaults to False. Returns: Objective: the norm1 objective function """ # skip objective_value variable and slack_var variables var_filter = (lambda v: v.is_integer()) if discrete_only \ else (lambda v: v.name != 'MindtPy_utils.objective_value' and 'MindtPy_utils.feas_opt.slack_var' not in v.name) model_vars = list(filter(var_filter, model.MindtPy_utils.variable_list)) setpoint_vars = list( filter(var_filter, setpoint_model.MindtPy_utils.variable_list)) assert len(model_vars) == len( setpoint_vars), 'Trying to generate Norm1 objective function for models with different number of variables' model.MindtPy_utils.del_component('L1_obj') obj_blk = model.MindtPy_utils.L1_obj = Block() obj_blk.L1_obj_idx = RangeSet(len(model_vars)) obj_blk.L1_obj_var = Var( obj_blk.L1_obj_idx, domain=Reals, bounds=(0, None)) obj_blk.abs_reform = ConstraintList() for idx, v_model, v_setpoint in zip(obj_blk.L1_obj_idx, model_vars, setpoint_vars): obj_blk.abs_reform.add( expr=v_model - v_setpoint.value >= -obj_blk.L1_obj_var[idx]) obj_blk.abs_reform.add( expr=v_model - v_setpoint.value <= obj_blk.L1_obj_var[idx]) return Objective(expr=sum(obj_blk.L1_obj_var[idx] for idx in obj_blk.L1_obj_idx))
def add_cut(self,first=False): self._iter += 1 model = self._model self._wprod[self._iter] = self._compute_weight_weight_inner_product() if first is True: self._alphas[self._iter] = -( self._compute_objective_term() + (self._ph._rho/2.0)*self._wprod[self._iter] ) else: self._alphas[self._iter] = -(self._compute_objective_term()) + self._compute_xbar_weight_inner_product() if self._solved is True: if self._compute_convergence() is True: return True model.del_component('cuts') model.cuts = Set(initialize=sorted(self._alphas.keys())) model.del_component('beta') model.beta = Var(model.cuts,within=NonNegativeReals) model.del_component('beta_sum_one') model.beta_sum_one = Constraint(expr=sum_product(model.beta)==1) model.del_component('obj') model.obj = Objective(expr=sum(self._alphas[i]*model.beta[i] for i in model.cuts)) self._wbars[self._iter] = {} for stage in self._ph._scenario_tree._stages[:-1]: # all blended stages for tree_node in stage._tree_nodes: self._wbars[self._iter][tree_node._name] = copy.deepcopy(tree_node._wbars) block = getattr(model,tree_node._name) def _c_rule(block,i): lhs = sum(model.beta[k]*self._wbars[k][tree_node._name][block.id_to_var[i][0]][block.id_to_var[i][1]] for k in model.beta.index_set()) if not isinstance(lhs,ExpressionBase): return Constraint.Skip return lhs == 0 block.del_component('con') block.con = Constraint(block.var_index, rule=_c_rule) return False
def _generate_model(self): self.model = ConcreteModel() model = self.model model._name = self.description model.f = Var() model.x = Var(bounds=(1, 3)) model.fi = Param([1, 2, 3], mutable=True) model.fi[1] = 1.0 model.fi[2] = 2.0 model.fi[3] = 0.0 model.xi = Param([1, 2, 3], mutable=True) model.xi[1] = 1.0 model.xi[2] = 2.0 model.xi[3] = 3.0 model.p = Var(within=NonNegativeReals) model.n = Var(within=NonNegativeReals) model.lmbda = Var([1, 2, 3]) model.obj = Objective(expr=model.p + model.n) model.c1 = ConstraintList() model.c1.add((0.0, model.lmbda[1], 1.0)) model.c1.add((0.0, model.lmbda[2], 1.0)) model.c1.add(0.0 <= model.lmbda[3]) model.c2 = SOSConstraint(var=model.lmbda, index=[1, 2, 3], sos=2) model.c3 = Constraint(expr=sum_product(model.lmbda) == 1) model.c4 = Constraint( expr=model.f == sum_product(model.fi, model.lmbda)) model.c5 = Constraint( expr=model.x == sum_product(model.xi, model.lmbda)) model.x = 2.75 model.x.fixed = True # Make an empty SOSConstraint model.c6 = SOSConstraint(var=model.lmbda, index=[1, 2, 3], sos=2) model.c6.set_items([], []) assert len(list(model.c6.get_items())) == 0
def process_objective(solve_data, config, move_linear_objective=False, use_mcpp=True, updata_var_con_list=True): """Process model objective function. Check that the model has only 1 valid objective. If the objective is nonlinear, move it into the constraints. If no objective function exists, emit a warning and create a dummy objective. Parameters ---------- solve_data (GDPoptSolveData): solver environment data class config (ConfigBlock): solver configuration options move_linear_objective (bool): if True, move even linear objective functions to the constraints """ m = solve_data.working_model util_blk = getattr(m, solve_data.util_block_name) # Handle missing or multiple objectives active_objectives = list(m.component_data_objects( ctype=Objective, active=True, descend_into=True)) solve_data.results.problem.number_of_objectives = len(active_objectives) if len(active_objectives) == 0: config.logger.warning( 'Model has no active objectives. Adding dummy objective.') util_blk.dummy_objective = Objective(expr=1) main_obj = util_blk.dummy_objective elif len(active_objectives) > 1: raise ValueError('Model has multiple active objectives.') else: main_obj = active_objectives[0] solve_data.results.problem.sense = ProblemSense.minimize if main_obj.sense == 1 else ProblemSense.maximize solve_data.objective_sense = main_obj.sense # Move the objective to the constraints if it is nonlinear if main_obj.expr.polynomial_degree() not in (1, 0) \ or move_linear_objective: if move_linear_objective: config.logger.info("Moving objective to constraint set.") else: config.logger.info( "Objective is nonlinear. Moving it to constraint set.") util_blk.objective_value = Var(domain=Reals, initialize=0) if mcpp_available() and use_mcpp: mc_obj = McCormick(main_obj.expr) util_blk.objective_value.setub(mc_obj.upper()) util_blk.objective_value.setlb(mc_obj.lower()) else: # Use Pyomo's contrib.fbbt package lb, ub = compute_bounds_on_expr(main_obj.expr) if solve_data.results.problem.sense == ProblemSense.minimize: util_blk.objective_value.setlb(lb) else: util_blk.objective_value.setub(ub) if main_obj.sense == minimize: util_blk.objective_constr = Constraint( expr=util_blk.objective_value >= main_obj.expr) else: util_blk.objective_constr = Constraint( expr=util_blk.objective_value <= main_obj.expr) # Deactivate the original objective and add this new one. main_obj.deactivate() util_blk.objective = Objective( expr=util_blk.objective_value, sense=main_obj.sense) # Add the new variable and constraint to the working lists if main_obj.expr.polynomial_degree() not in (1, 0) or (move_linear_objective and updata_var_con_list): util_blk.variable_list.append(util_blk.objective_value) util_blk.continuous_variable_list.append(util_blk.objective_value) util_blk.constraint_list.append(util_blk.objective_constr) util_blk.objective_list.append(util_blk.objective) if util_blk.objective_constr.body.polynomial_degree() in (0, 1): util_blk.linear_constraint_list.append(util_blk.objective_constr) else: util_blk.nonlinear_constraint_list.append( util_blk.objective_constr)
def build_nonexclusive_model(): m = ConcreteModel() m.streams = RangeSet(25) m.x = Var(m.streams, bounds=(0, 50), initialize=5) m.stage1_split = Constraint(expr=m.x[1] == m.x[2] + m.x[4]) m.unit1 = Disjunction(expr=[ [ # Unit 1 m.x[2] == exp(m.x[3]) - 1, ], [ # No Unit 1 m.x[2] == 0, m.x[3] == 0 ] ]) m.unit2 = Disjunction(expr=[ [ # Unit 2 m.x[5] == log(m.x[4] + 1), ], [ # No Unit 2 m.x[4] == 0, m.x[5] == 0 ] ]) m.stage1_mix = Constraint(expr=m.x[3] + m.x[5] == m.x[6]) m.stage2_split = Constraint(expr=m.x[6] == sum(m.x[i] for i in (7, 9, 11, 13))) m.unit3 = Disjunction(expr=[ [ # Unit 3 m.x[8] == 2 * log(m.x[7]) + 3, m.x[7] >= 0.2, ], [ # No Unit 3 m.x[7] == 0, m.x[8] == 0 ] ]) m.unit4 = Disjunction(expr=[ [ # Unit 4 m.x[10] == 1.8 * log(m.x[9] + 4), ], [ # No Unit 4 m.x[9] == 0, m.x[10] == 0 ] ]) m.unit5 = Disjunction(expr=[ [ # Unit 5 m.x[12] == 1.2 * log(m.x[11]) + 2, m.x[11] >= 0.001, ], [ # No Unit 5 m.x[11] == 0, m.x[12] == 0 ] ]) m.unit6 = Disjunction(expr=[ [ # Unit 6 m.x[15] == sqrt(m.x[14] - 3) * m.x[23] + 1, m.x[14] >= 5, m.x[14] <= 20, ], [ # No Unit 6 m.x[14] == 0, m.x[15] == 0 ] ]) m.stage2_special_mix = Constraint(expr=m.x[14] == m.x[13] + m.x[23]) m.stage2_mix = Constraint(expr=sum(m.x[i] for i in (8, 10, 12, 15)) == m.x[16]) m.stage3_split = Constraint(expr=m.x[16] == sum(m.x[i] for i in (17, 19, 21))) m.unit7 = Disjunction(expr=[ [ # Unit 7 m.x[18] == m.x[17] * 0.9, ], [ # No Unit 7 m.x[17] == 0, m.x[18] == 0 ] ]) m.unit8 = Disjunction(expr=[ [ # Unit 8 m.x[20] == log(m.x[19] ** 1.5) + 2, m.x[19] >= 1, ], [ # No Unit 8 m.x[19] == 0, m.x[20] == 0 ] ]) m.unit9 = Disjunction(expr=[ [ # Unit 9 m.x[22] == log(m.x[21] + sqrt(m.x[21])) + 1, m.x[21] >= 4, ], [ # No Unit 9 m.x[21] == 0, m.x[22] == 0 ] ]) m.stage3_special_split = Constraint(expr=m.x[22] == m.x[23] + m.x[24]) m.stage3_mix = Constraint(expr=m.x[25] == sum(m.x[i] for i in (18, 20, 24))) m.obj = Objective(expr=-10 * m.x[25] + m.x[1]) return m
def build_model(): """ Base Model Optimal solution: Select units 1, 3, 8 Objective value -36.62 """ m = ConcreteModel() m.streams = RangeSet(25) m.x = Var(m.streams, bounds=(0, 50), initialize=5) m.stage1_split = Constraint(expr=m.x[1] == m.x[2] + m.x[4]) m.first_stage = Disjunction(expr=[ [ # Unit 1 m.x[2] == exp(m.x[3]) - 1, m.x[4] == 0, m.x[5] == 0 ], [ # Unit 2 m.x[5] == log(m.x[4] + 1), m.x[2] == 0, m.x[3] == 0 ] ]) m.stage1_mix = Constraint(expr=m.x[3] + m.x[5] == m.x[6]) m.stage2_split = Constraint(expr=m.x[6] == sum(m.x[i] for i in (7, 9, 11, 13))) m.second_stage = Disjunction(expr=[ [ # Unit 3 m.x[8] == 2 * log(m.x[7]) + 3, m.x[7] >= 0.2, ] + [m.x[i] == 0 for i in (9, 10, 11, 12, 14, 15)], [ # Unit 4 m.x[10] == 1.8 * log(m.x[9] + 4), ] + [m.x[i] == 0 for i in (7, 8, 11, 12, 14, 15)], [ # Unit 5 m.x[12] == 1.2 * log(m.x[11]) + 2, m.x[11] >= 0.001, ] + [m.x[i] == 0 for i in (7, 8, 9, 10, 14, 15)], [ # Unit 6 m.x[15] == sqrt(m.x[14] - 3) * m.x[23] + 1, m.x[14] >= 5, m.x[14] <= 20, ] + [m.x[i] == 0 for i in (7, 8, 9, 10, 11, 12)] ]) m.stage2_special_mix = Constraint(expr=m.x[14] == m.x[13] + m.x[23]) m.stage2_mix = Constraint(expr=sum(m.x[i] for i in (8, 10, 12, 15)) == m.x[16]) m.stage3_split = Constraint(expr=m.x[16] == sum(m.x[i] for i in (17, 19, 21))) m.third_stage = Disjunction(expr=[ [ # Unit 7 m.x[18] == m.x[17] * 0.9, ] + [m.x[i] == 0 for i in (19, 20, 21, 22)], [ # Unit 8 m.x[20] == log(m.x[19] ** 1.5) + 2, m.x[19] >= 1, ] + [m.x[i] == 0 for i in (17, 18, 21, 22)], [ # Unit 9 m.x[22] == log(m.x[21] + sqrt(m.x[21])) + 1, m.x[21] >= 4, ] + [m.x[i] == 0 for i in (17, 18, 19, 20)] ]) m.stage3_special_split = Constraint(expr=m.x[22] == m.x[23] + m.x[24]) m.stage3_mix = Constraint(expr=m.x[25] == sum(m.x[i] for i in (18, 20, 24))) m.obj = Objective(expr=-10 * m.x[25] + m.x[1]) return m
def model_is_valid(solve_data, config): """Validate that the model is solveable by GDPopt. Also preforms some preprocessing such as moving the objective to the constraints. """ m = solve_data.working_model GDPopt = m.GDPopt_utils # Handle LP/NLP being passed to the solver prob = solve_data.results.problem if (prob.number_of_binary_variables == 0 and prob.number_of_integer_variables == 0 and prob.number_of_disjunctions == 0): config.logger.info('Problem has no discrete decisions.') if len(GDPopt.working_nonlinear_constraints) > 0: config.logger.info( "Your model is an NLP (nonlinear program). " "Using NLP solver %s to solve." % config.nlp) SolverFactory(config.nlp).solve( solve_data.original_model, **config.nlp_options) return False else: config.logger.info( "Your model is an LP (linear program). " "Using LP solver %s to solve." % config.mip) SolverFactory(config.mip).solve( solve_data.original_model, **config.mip_options) return False # Handle missing or multiple objectives objs = list(m.component_data_objects( ctype=Objective, active=True, descend_into=True)) num_objs = len(objs) solve_data.results.problem.number_of_objectives = num_objs if num_objs == 0: config.logger.warning( 'Model has no active objectives. Adding dummy objective.') GDPopt.dummy_objective = Objective(expr=1) main_obj = GDPopt.dummy_objective elif num_objs > 1: raise ValueError('Model has multiple active objectives.') else: main_obj = objs[0] solve_data.working_objective_expr = main_obj.expr # Move the objective to the constraints # TODO only move the objective if nonlinear? GDPopt.objective_value = Var(domain=Reals, initialize=0) solve_data.objective_sense = main_obj.sense if main_obj.sense == minimize: GDPopt.objective_expr = Constraint( expr=GDPopt.objective_value >= main_obj.expr) solve_data.results.problem.sense = ProblemSense.minimize else: GDPopt.objective_expr = Constraint( expr=GDPopt.objective_value <= main_obj.expr) solve_data.results.problem.sense = ProblemSense.maximize main_obj.deactivate() GDPopt.objective = Objective( expr=GDPopt.objective_value, sense=main_obj.sense) # TODO if any continuous variables are multipled with binary ones, need # to do some kind of transformation (Glover?) or throw an error message return True
def solve_OA_master(solve_data, config): solve_data.mip_iter += 1 MindtPy = solve_data.mip.MindtPy_utils config.logger.info('MIP %s: Solve master problem.' % (solve_data.mip_iter, )) # Set up MILP for c in MindtPy.constraint_list: if c.body.polynomial_degree() not in (1, 0): c.deactivate() MindtPy.MindtPy_linear_cuts.activate() main_objective = next( solve_data.mip.component_data_objects(Objective, active=True)) main_objective.deactivate() sign_adjust = 1 if main_objective.sense == minimize else -1 MindtPy.del_component('MindtPy_oa_obj') if config.add_slack: MindtPy.del_component('MindtPy_penalty_expr') MindtPy.MindtPy_penalty_expr = Expression( expr=sign_adjust * config.OA_penalty_factor * sum(v for v in MindtPy.MindtPy_linear_cuts.slack_vars[...])) MindtPy.MindtPy_oa_obj = Objective(expr=main_objective.expr + MindtPy.MindtPy_penalty_expr, sense=main_objective.sense) else: MindtPy.MindtPy_oa_obj = Objective(expr=main_objective.expr, sense=main_objective.sense) # Deactivate extraneous IMPORT/EXPORT suffixes getattr(solve_data.mip, 'ipopt_zL_out', _DoNothing()).deactivate() getattr(solve_data.mip, 'ipopt_zU_out', _DoNothing()).deactivate() masteropt = SolverFactory(config.mip_solver) # determine if persistent solver is called. if isinstance(masteropt, PersistentSolver): masteropt.set_instance(solve_data.mip, symbolic_solver_labels=True) if config.single_tree: # Configuration of lazy callback lazyoa = masteropt._solver_model.register_callback( single_tree.LazyOACallback_cplex) # pass necessary data and parameters to lazyoa lazyoa.master_mip = solve_data.mip lazyoa.solve_data = solve_data lazyoa.config = config lazyoa.opt = masteropt masteropt._solver_model.set_warning_stream(None) masteropt._solver_model.set_log_stream(None) masteropt._solver_model.set_error_stream(None) masteropt.options['timelimit'] = config.time_limit master_mip_results = masteropt.solve( solve_data.mip, **config.mip_solver_args) # , tee=True) if master_mip_results.solver.termination_condition is tc.optimal: if config.single_tree: if main_objective.sense == minimize: solve_data.LB = max(master_mip_results.problem.lower_bound, solve_data.LB) solve_data.LB_progress.append(solve_data.LB) solve_data.UB = min(master_mip_results.problem.upper_bound, solve_data.UB) solve_data.UB_progress.append(solve_data.UB) elif master_mip_results.solver.termination_condition is tc.infeasibleOrUnbounded: # Linear solvers will sometimes tell me that it's infeasible or # unbounded during presolve, but fails to distinguish. We need to # resolve with a solver option flag on. master_mip_results, _ = distinguish_mip_infeasible_or_unbounded( solve_data.mip, config) return solve_data.mip, master_mip_results
def test_handle_termination_condition(self): """Test the outer approximation decomposition algorithm.""" model = SimpleMINLP() config = _get_MindtPy_config() solve_data = set_up_solve_data(model, config) with time_code(solve_data.timing, 'total', is_main_timer=True), \ create_utility_block(solve_data.working_model, 'MindtPy_utils', solve_data): MindtPy = solve_data.working_model.MindtPy_utils MindtPy = solve_data.working_model.MindtPy_utils setup_results_object(solve_data, config) process_objective( solve_data, config, move_linear_objective=(config.init_strategy == 'FP' or config.add_regularization is not None), use_mcpp=config.use_mcpp, update_var_con_list=config.add_regularization is None) feas = MindtPy.feas_opt = Block() feas.deactivate() feas.feas_constraints = ConstraintList( doc='Feasibility Problem Constraints') lin = MindtPy.cuts = Block() lin.deactivate() if config.feasibility_norm == 'L1' or config.feasibility_norm == 'L2': feas.nl_constraint_set = RangeSet( len(MindtPy.nonlinear_constraint_list), doc='Integer index set over the nonlinear constraints.') # Create slack variables for feasibility problem feas.slack_var = Var(feas.nl_constraint_set, domain=NonNegativeReals, initialize=1) else: feas.slack_var = Var(domain=NonNegativeReals, initialize=1) # no-good cuts exclude particular discrete decisions lin.no_good_cuts = ConstraintList(doc='no-good cuts') fixed_nlp = solve_data.working_model.clone() TransformationFactory('core.fix_integer_vars').apply_to(fixed_nlp) MindtPy_initialize_main(solve_data, config) # test handle_subproblem_other_termination termination_condition = tc.maxIterations config.add_no_good_cuts = True handle_subproblem_other_termination(fixed_nlp, termination_condition, solve_data, config) self.assertEqual( len(solve_data.mip.MindtPy_utils.cuts.no_good_cuts), 1) # test handle_main_other_conditions main_mip, main_mip_results = solve_main(solve_data, config) main_mip_results.solver.termination_condition = tc.infeasible handle_main_other_conditions(solve_data.mip, main_mip_results, solve_data, config) self.assertIs(solve_data.results.solver.termination_condition, tc.feasible) main_mip_results.solver.termination_condition = tc.unbounded handle_main_other_conditions(solve_data.mip, main_mip_results, solve_data, config) self.assertIn(main_mip.MindtPy_utils.objective_bound, main_mip.component_data_objects(ctype=Constraint)) main_mip.MindtPy_utils.del_component('objective_bound') main_mip_results.solver.termination_condition = tc.infeasibleOrUnbounded handle_main_other_conditions(solve_data.mip, main_mip_results, solve_data, config) self.assertIn(main_mip.MindtPy_utils.objective_bound, main_mip.component_data_objects(ctype=Constraint)) main_mip_results.solver.termination_condition = tc.maxTimeLimit handle_main_other_conditions(solve_data.mip, main_mip_results, solve_data, config) self.assertIs(solve_data.results.solver.termination_condition, tc.maxTimeLimit) main_mip_results.solver.termination_condition = tc.other main_mip_results.solution.status = SolutionStatus.feasible handle_main_other_conditions(solve_data.mip, main_mip_results, solve_data, config) for v1, v2 in zip( main_mip.MindtPy_utils.variable_list, solve_data.working_model.MindtPy_utils.variable_list): self.assertEqual(v1.value, v2.value) # test handle_feasibility_subproblem_tc feas_subproblem = solve_data.working_model.clone() add_feas_slacks(feas_subproblem, config) MindtPy = feas_subproblem.MindtPy_utils MindtPy.feas_opt.activate() if config.feasibility_norm == 'L1': MindtPy.feas_obj = Objective(expr=sum( s for s in MindtPy.feas_opt.slack_var[...]), sense=minimize) elif config.feasibility_norm == 'L2': MindtPy.feas_obj = Objective(expr=sum( s * s for s in MindtPy.feas_opt.slack_var[...]), sense=minimize) else: MindtPy.feas_obj = Objective(expr=MindtPy.feas_opt.slack_var, sense=minimize) handle_feasibility_subproblem_tc(tc.optimal, MindtPy, solve_data, config) handle_feasibility_subproblem_tc(tc.infeasible, MindtPy, solve_data, config) self.assertIs(solve_data.should_terminate, True) self.assertIs(solve_data.results.solver.status, SolverStatus.error) solve_data.should_terminate = False solve_data.results.solver.status = None handle_feasibility_subproblem_tc(tc.maxIterations, MindtPy, solve_data, config) self.assertIs(solve_data.should_terminate, True) self.assertIs(solve_data.results.solver.status, SolverStatus.error) solve_data.should_terminate = False solve_data.results.solver.status = None handle_feasibility_subproblem_tc(tc.solverFailure, MindtPy, solve_data, config) self.assertIs(solve_data.should_terminate, True) self.assertIs(solve_data.results.solver.status, SolverStatus.error) # test NLP subproblem infeasible solve_data.working_model.Y[1].value = 0 solve_data.working_model.Y[2].value = 0 solve_data.working_model.Y[3].value = 0 fixed_nlp, fixed_nlp_results = solve_subproblem(solve_data, config) solve_data.working_model.Y[1].value = None solve_data.working_model.Y[2].value = None solve_data.working_model.Y[3].value = None # test handle_nlp_subproblem_tc fixed_nlp_results.solver.termination_condition = tc.maxTimeLimit handle_nlp_subproblem_tc(fixed_nlp, fixed_nlp_results, solve_data, config) self.assertIs(solve_data.should_terminate, True) self.assertIs(solve_data.results.solver.termination_condition, tc.maxTimeLimit) fixed_nlp_results.solver.termination_condition = tc.maxEvaluations handle_nlp_subproblem_tc(fixed_nlp, fixed_nlp_results, solve_data, config) self.assertIs(solve_data.should_terminate, True) self.assertIs(solve_data.results.solver.termination_condition, tc.maxEvaluations) fixed_nlp_results.solver.termination_condition = tc.maxIterations handle_nlp_subproblem_tc(fixed_nlp, fixed_nlp_results, solve_data, config) self.assertIs(solve_data.should_terminate, True) self.assertIs(solve_data.results.solver.termination_condition, tc.maxEvaluations) # test handle_fp_main_tc config.init_strategy = 'FP' solve_data.fp_iter = 1 init_rNLP(solve_data, config) feas_main, feas_main_results = solve_main(solve_data, config, fp=True) feas_main_results.solver.termination_condition = tc.optimal fp_should_terminate = handle_fp_main_tc(feas_main_results, solve_data, config) self.assertIs(fp_should_terminate, False) feas_main_results.solver.termination_condition = tc.maxTimeLimit fp_should_terminate = handle_fp_main_tc(feas_main_results, solve_data, config) self.assertIs(fp_should_terminate, True) self.assertIs(solve_data.results.solver.termination_condition, tc.maxTimeLimit) feas_main_results.solver.termination_condition = tc.infeasible fp_should_terminate = handle_fp_main_tc(feas_main_results, solve_data, config) self.assertIs(fp_should_terminate, True) feas_main_results.solver.termination_condition = tc.unbounded fp_should_terminate = handle_fp_main_tc(feas_main_results, solve_data, config) self.assertIs(fp_should_terminate, True) feas_main_results.solver.termination_condition = tc.other feas_main_results.solution.status = SolutionStatus.feasible fp_should_terminate = handle_fp_main_tc(feas_main_results, solve_data, config) self.assertIs(fp_should_terminate, False) feas_main_results.solver.termination_condition = tc.solverFailure fp_should_terminate = handle_fp_main_tc(feas_main_results, solve_data, config) self.assertIs(fp_should_terminate, True) # test generate_norm_constraint fp_nlp = solve_data.working_model.clone() config.fp_main_norm = 'L1' generate_norm_constraint(fp_nlp, solve_data, config) self.assertIsNotNone( fp_nlp.MindtPy_utils.find_component('L1_norm_constraint')) config.fp_main_norm = 'L2' generate_norm_constraint(fp_nlp, solve_data, config) self.assertIsNotNone(fp_nlp.find_component('norm_constraint')) fp_nlp.del_component('norm_constraint') config.fp_main_norm = 'L_infinity' generate_norm_constraint(fp_nlp, solve_data, config) self.assertIsNotNone(fp_nlp.find_component('norm_constraint')) # test set_solver_options config.mip_solver = 'gams' config.threads = 1 opt = SolverFactory(config.mip_solver) set_solver_options(opt, solve_data, config, 'mip', regularization=False) config.mip_solver = 'gurobi' config.mip_regularization_solver = 'gurobi' config.regularization_mip_threads = 1 opt = SolverFactory(config.mip_solver) set_solver_options(opt, solve_data, config, 'mip', regularization=True) config.nlp_solver = 'gams' config.nlp_solver_args['solver'] = 'ipopt' set_solver_options(opt, solve_data, config, 'nlp', regularization=False) config.nlp_solver_args['solver'] = 'ipopth' set_solver_options(opt, solve_data, config, 'nlp', regularization=False) config.nlp_solver_args['solver'] = 'conopt' set_solver_options(opt, solve_data, config, 'nlp', regularization=False) config.nlp_solver_args['solver'] = 'msnlp' set_solver_options(opt, solve_data, config, 'nlp', regularization=False) config.nlp_solver_args['solver'] = 'baron' set_solver_options(opt, solve_data, config, 'nlp', regularization=False) # test algorithm_should_terminate solve_data.should_terminate = True solve_data.UB = float('inf') self.assertIs( algorithm_should_terminate(solve_data, config, check_cycling=False), True) self.assertIs(solve_data.results.solver.termination_condition, tc.noSolution) solve_data.UB = 100 self.assertIs( algorithm_should_terminate(solve_data, config, check_cycling=False), True) self.assertIs(solve_data.results.solver.termination_condition, tc.feasible) solve_data.objective_sense = maximize solve_data.LB = float('-inf') self.assertIs( algorithm_should_terminate(solve_data, config, check_cycling=False), True) self.assertIs(solve_data.results.solver.termination_condition, tc.noSolution) solve_data.LB = 100 self.assertIs( algorithm_should_terminate(solve_data, config, check_cycling=False), True) self.assertIs(solve_data.results.solver.termination_condition, tc.feasible)
def post_process_fme_constraints(self, m, solver_factory, tolerance=0): """Function that solves a sequence of LPs problems to check if constraints are implied by each other. Deletes any that are. Parameters ---------------- m: A model, already transformed with FME. Note that if constraints have been added, activated, or deactivated, we will check for redundancy against the whole active part of the model. If you call this straight after FME, you are only checking within the projected constraints, but otherwise it is up to the user. solver_factory: A SolverFactory object (constructed with a solver which can solve the continuous relaxation of the active constraints on the model. That is, if you had nonlinear constraints unrelated to the variables being projected, you need to either deactivate them or provide a solver which will do the right thing.) tolerance: Tolerance at which we decide a constraint is implied by the others. Default is 0, meaning we remove the constraint if the LP solve finds the constraint can be tight but not violated. Setting this to a small positive value would remove constraints more conservatively. Setting it to a negative value would result in a relaxed problem. """ # make sure m looks like what we expect if not hasattr(m, "_pyomo_contrib_fme_transformation"): raise RuntimeError("It looks like model %s has not been " "transformed with the " "fourier_motzkin_elimination transformation!" % m.name) transBlock = m._pyomo_contrib_fme_transformation constraints = transBlock.projected_constraints # relax integrality so that we can do this with LP solves. TransformationFactory('core.relax_integer_vars').apply_to( m, transform_deactivated_blocks=True) # deactivate any active objectives on the model, and save what we did so # we can undo it after. active_objs = [] for obj in m.component_data_objects(Objective, descend_into=True): if obj.active: active_objs.append(obj) obj.deactivate() # add placeholder for our own objective obj_name = unique_component_name(m, '_fme_post_process_obj') obj = Objective(expr=0) m.add_component(obj_name, obj) for i in constraints: # If someone wants us to ignore it and leave it in the model, we # can. if not constraints[i].active: continue # deactivate the constraint constraints[i].deactivate() m.del_component(obj) # make objective to maximize its infeasibility obj = Objective(expr=constraints[i].body - constraints[i].lower) m.add_component(obj_name, obj) results = solver_factory.solve(m) if results.solver.termination_condition == \ TerminationCondition.unbounded: obj_val = -float('inf') elif results.solver.termination_condition != \ TerminationCondition.optimal: raise RuntimeError( "Unsuccessful subproblem solve when checking" "constraint %s.\n\t" "Termination Condition: %s" % (constraints[i].name, results.solver.termination_condition)) else: obj_val = value(obj) # if we couldn't make it infeasible, it's useless if obj_val >= tolerance: m.del_component(constraints[i]) del constraints[i] else: constraints[i].activate() # clean up m.del_component(obj) for obj in active_objs: obj.activate() # undo relax integrality TransformationFactory('core.relax_integer_vars').apply_to(m, undo=True)
def _get_mock_model(self): model = ConcreteModel() model.X = Var(within=NonNegativeReals, initialize=1.5) model.Y = Var(within=Binary, initialize=0) model.O = Objective(expr=model.X * model.Y) return model
def device_scheduler( device_constraints: List[DataFrame], ems_constraints: DataFrame, commitment_quantities: List[Series], commitment_downwards_deviation_price: Union[List[Series], List[float]], commitment_upwards_deviation_price: Union[List[Series], List[float]], ) -> Tuple[List[Series], List[float]]: """Schedule devices given constraints on a device and EMS level, and given a list of commitments by the EMS. The commitments are assumed to be with regards to the flow of energy to the device (positive for consumption, negative for production). The solver minimises the costs of deviating from the commitments, and returns the costs per commitment. Device constraints are on a device level. Handled constraints (listed by column name): max: maximum stock assuming an initial stock of zero (e.g. in MWh or boxes) min: minimum stock assuming an initial stock of zero derivative max: maximum flow (e.g. in MW or boxes/h) derivative min: minimum flow derivative equals: exact amount of flow EMS constraints are on an EMS level. Handled constraints (listed by column name): derivative max: maximum flow derivative min: minimum flow Commitments are on an EMS level. Parameter explanations: commitment_quantities: amounts of flow specified in commitments (both previously ordered and newly requested) - e.g. in MW or boxes/h commitment_downwards_deviation_price: penalty for downwards deviations of the flow - e.g. in EUR/MW or EUR/(boxes/h) - either a single value (same value for each flow value) or a Series (different value for each flow value) commitment_upwards_deviation_price: penalty for upwards deviations of the flow All Series and DataFrames should have the same resolution. For now we pass in the various constraints and prices as separate variables, from which we make a MultiIndex DataFrame. Later we could pass in a MultiIndex DataFrame directly. """ # If the EMS has no devices, don't bother if len(device_constraints) == 0: return [], [] * len(commitment_quantities) # Check if commitments have the same time window and resolution as the constraints start = device_constraints[0].index.values[0] resolution = to_timedelta(device_constraints[0].index.freq) end = device_constraints[0].index.values[-1] + resolution if len(commitment_quantities) != 0: start_c = commitment_quantities[0].index.values[0] resolution_c = to_timedelta(commitment_quantities[0].index.freq) end_c = commitment_quantities[0].index.values[-1] + resolution if not (start_c == start and end_c == end): raise Exception( "Not implemented for different time windows.\n(%s,%s)\n(%s,%s)" % (start, end, start_c, end_c)) if resolution_c != resolution: raise Exception( "Not implemented for different resolutions.\n%s\n%s" % (resolution, resolution_c)) # Turn prices per commitment into prices per commitment flow if len(commitment_downwards_deviation_price) != 0: if all(not isinstance(price, Series) for price in commitment_downwards_deviation_price): commitment_downwards_deviation_price = [ initialize_series(price, start, end, resolution) for price in commitment_downwards_deviation_price ] if len(commitment_upwards_deviation_price) != 0: if all(not isinstance(price, Series) for price in commitment_upwards_deviation_price): commitment_upwards_deviation_price = [ initialize_series(price, start, end, resolution) for price in commitment_upwards_deviation_price ] # Determine appropriate overall bounds for power and price min_down_price = min(min(p) for p in commitment_downwards_deviation_price) max_down_price = max(max(p) for p in commitment_downwards_deviation_price) min_up_price = min(min(p) for p in commitment_upwards_deviation_price) max_up_price = max(max(p) for p in commitment_upwards_deviation_price) overall_min_price = min(min_down_price, min_up_price) overall_max_price = max(max_down_price, max_up_price) overall_min_power = min(ems_constraints["derivative min"]) overall_max_power = max(ems_constraints["derivative max"]) model = ConcreteModel() # Add indices for devices (d), datetimes (j) and commitments (c) model.d = RangeSet(0, len(device_constraints) - 1, doc="Set of devices") model.j = RangeSet(0, len(device_constraints[0].index.values) - 1, doc="Set of datetimes") model.c = RangeSet(0, len(commitment_quantities) - 1, doc="Set of commitments") # Add parameters def commitment_quantity_select(m, c, j): v = commitment_quantities[c].iloc[j] if isnan(v): # Discount this nan commitment by setting the prices to 0 commitment_downwards_deviation_price[c].iloc[j] = 0 commitment_upwards_deviation_price[c].iloc[j] = 0 return 0 else: return v def price_down_select(m, c, j): return commitment_downwards_deviation_price[c].iloc[j] def price_up_select(m, c, j): return commitment_upwards_deviation_price[c].iloc[j] def device_max_select(m, d, j): v = device_constraints[d]["max"].iloc[j] if isnan(v): return infinity else: return v def device_min_select(m, d, j): v = device_constraints[d]["min"].iloc[j] if isnan(v): return -infinity else: return v def device_derivative_max_select(m, d, j): max_v = device_constraints[d]["derivative max"].iloc[j] equal_v = device_constraints[d]["derivative equals"].iloc[j] if isnan(max_v) and isnan(equal_v): return infinity else: return nanmin([max_v]) def device_derivative_min_select(m, d, j): min_v = device_constraints[d]["derivative min"].iloc[j] equal_v = device_constraints[d]["derivative equals"].iloc[j] if isnan(min_v) and isnan(equal_v): return -infinity else: return nanmax([min_v]) def device_derivative_equal_select(m, d, j): min_v = device_constraints[d]["derivative min"].iloc[j] equal_v = device_constraints[d]["derivative equals"].iloc[j] if isnan(equal_v): return 0 else: return nanmax([equal_v]) def ems_derivative_max_select(m, j): v = ems_constraints["derivative max"].iloc[j] if isnan(v): return infinity else: return v def ems_derivative_min_select(m, j): v = ems_constraints["derivative min"].iloc[j] if isnan(v): return -infinity else: return v model.commitment_quantity = Param(model.c, model.j, initialize=commitment_quantity_select) model.up_price = Param(model.c, model.j, initialize=price_up_select) model.down_price = Param(model.c, model.j, initialize=price_down_select) model.device_max = Param(model.d, model.j, initialize=device_max_select) model.device_min = Param(model.d, model.j, initialize=device_min_select) model.device_derivative_max = Param( model.d, model.j, initialize=device_derivative_max_select) model.device_derivative_min = Param( model.d, model.j, initialize=device_derivative_min_select) model.device_derivative_equal = Param( model.d, model.j, initialize=device_derivative_equal_select) model.ems_derivative_max = Param(model.j, initialize=ems_derivative_max_select) model.ems_derivative_min = Param(model.j, initialize=ems_derivative_min_select) # Add variables model.power = Var( model.d, model.j, domain=Reals, initialize=0, bounds=(overall_min_power, overall_max_power), ) # Add constraints as a tuple of (lower bound, value, upper bound) def device_bounds(m, d, j): return ( m.device_min[d, j], sum(m.power[d, k] for k in range(0, j + 1)), m.device_max[d, j], ) def device_derivative_bounds(m, d, j): return ( m.device_derivative_min[d, j], m.power[d, j] - m.device_derivative_equal[d, j], m.device_derivative_max[d, j], ) def ems_derivative_bounds(m, j): return m.ems_derivative_min[j], sum( m.power[:, j]), m.ems_derivative_max[j] model.device_energy_bounds = Constraint(model.d, model.j, rule=device_bounds) model.device_power_bounds = Constraint(model.d, model.j, rule=device_derivative_bounds) model.ems_power_bounds = Constraint(model.j, rule=ems_derivative_bounds) # Add logical disjunction for deviations model.price = Var(model.c, model.j, initialize=0, bounds=(overall_min_price, overall_max_price)) def up_linker(b, c, d, j): # print("In up linker") m = b.model() ems_power_in_j = sum(m.power[d, j] for d in m.d) ems_power_deviation = ems_power_in_j - m.commitment_quantity[c, j] # try: # print(value(ems_power_deviation)) # except: # pass b.linker = Constraint(expr=m.price[c, j] == m.up_price[c, j]) b.constr = Constraint(expr=ems_power_deviation >= 0) b.BigM = Suffix(direction=Suffix.LOCAL) b.BigM[b.linker] = 10e5 return def down_linker(b, c, d, j): # print("In down linker") m = b.model() ems_power_in_j = sum(m.power[d, j] for d in m.d) ems_power_deviation = ems_power_in_j - m.commitment_quantity[c, j] # try: # print(value(ems_power_deviation)) # except: # pass b.linker = Constraint(expr=m.price[c, j] == m.down_price[c, j]) b.constr = Constraint(expr=ems_power_deviation <= 0) b.BigM = Suffix(direction=Suffix.LOCAL) b.BigM[b.linker] = 10e5 return # def zero_linker(b, c, d, j): # #print("In down linker") # m = b.model() # ems_power_in_j = sum(m.power[d, j] for d in m.d) # ems_power_deviation = ems_power_in_j - m.commitment_quantity[c, j] # #try: # #print(value(ems_power_deviation)) # #except: # #pass # b.linker = Constraint(expr=m.price[c, j] == 0) # b.constr = Constraint(expr=ems_power_deviation == 0) # #b.BigM = Suffix(direction=Suffix.LOCAL) # #b.BigM[b.linker] = 10e10 # return model.up_deviation = Disjunct(model.c, model.d, model.j, rule=up_linker) model.down_deviation = Disjunct(model.c, model.d, model.j, rule=down_linker) # model.zero_deviation = Disjunct(model.c, model.d, model.j, rule=zero_linker) def bind_prices(m, c, d, j): return [ model.up_deviation[c, d, j], model.down_deviation[c, d, j], # model.zero_deviation[c, d, j] ] model.up_or_down_deviation = Disjunction(model.c, model.d, model.j, rule=bind_prices, xor=True) # Add objective def cost_function(m): costs = 0 for j in m.j: for c in m.c: ems_power_in_j = sum(m.power[d, j] for d in m.d) ems_power_deviation = ems_power_in_j - m.commitment_quantity[c, j] costs += ems_power_deviation * m.price[c, j] return costs model.costs = Objective(rule=cost_function, sense=minimize) # def xfrm(m): # TransformationFactory('gdp.chull').apply_to(m) # model.xfrm = BuildAction(rule=xfrm) # Transform and solve xfrm = TransformationFactory("gdp.bigm") xfrm.apply_to(model) solver = SolverFactory( "cplex", executable="D:/CPLEX/Studio/cplex/bin/x64_win64/cplex") # solver.options['CPXchgprobtype'] = "CPXPROB_QP" # solver.options["solver"] = "CPXqpopt" solver.options["qpmethod"] = 1 solver.options["optimalitytarget"] = 3 # solver.options["acceptable_constr_viol_tol"] = 10 # solver.options['acceptable_tol'] = 1 # solver.options['acceptable_dual_inf_tol'] = 10 # solver.options['acceptable_compl_inf_tol'] = 10 results = solver.solve(model, tee=False) planned_costs = value(model.costs) planned_power_per_device = [] for d in model.d: planned_device_power = [ round(model.power[d, j].value, 3) for j in model.j ] planned_power_per_device.append( initialize_series(planned_device_power, start=start, end=end, resolution=resolution)) # model.display() # results.pprint() # model.down_deviation.pprint() # model.up_deviation.pprint() # model.power.pprint() # print(planned_power_per_device) # input() # Redo the cost calculation, because before the solver actually approximated the prices. def redo_cost_calculation(m): commitments_costs = [] for c in m.c: commitment_cost = 0 for j in m.j: ems_power_in_j = sum(m.power[d, j] for d in m.d) ems_power_deviation = ems_power_in_j - m.commitment_quantity[c, j] if value(ems_power_deviation) >= 0: commitment_cost += round( value(ems_power_deviation * m.up_price[c, j]), 3) else: commitment_cost += round( value(ems_power_deviation * m.down_price[c, j]), 3) commitments_costs.append(commitment_cost) return commitments_costs planned_costs_per_commitment = redo_cost_calculation(model) # print(planned_costs_per_commitment) return planned_power_per_device, planned_costs_per_commitment
# ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects # Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC # Under the terms of Contract DE-NA0003525 with National Technology and # Engineering Solutions of Sandia, LLC, the U.S. Government retains certain # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ from pyomo.core import ConcreteModel, Var, Objective, Constraint, maximize model = ConcreteModel() model.x = Var(bounds=(1,None)) model.y = Var(bounds=(1,None)) model.o = Objective(expr=model.x-model.x, sense=maximize) model.c = Constraint(expr=model.x+model.y >= 3)
def _apply_to_impl(self, instance, **kwds): config = self.CONFIG(kwds.pop('options', {})) config.set_value(kwds) targets = config.targets if targets is None: constraintDatas = instance.component_data_objects( Constraint, descend_into=True) else: constraintDatas = [] for t in targets: if isinstance(t, ComponentUID): cons = t.find_component(instance) if cons.is_indexed(): for i in cons: constraintDatas.append(cons[i]) else: constraintDatas.append(cons) else: # we know it's a constraint because that's all we let # through the config block validation. if t.is_indexed(): for i in t: constraintDatas.append(t[i]) else: constraintDatas.append(t) # deactivate the objective for o in instance.component_data_objects(Objective): o.deactivate() # create block where we can add slack variables safely xblockname = unique_component_name(instance, "_core_add_slack_variables") instance.add_component(xblockname, Block()) xblock = instance.component(xblockname) obj_expr = 0 for cons in constraintDatas: if (cons.lower is not None and cons.upper is not None) and \ value(cons.lower) > value(cons.upper): # this is a structural infeasibility so slacks aren't going to # help: raise RuntimeError("Lower bound exceeds upper bound in " "constraint %s" % cons.name) if not cons.active: continue cons_name = cons.getname(fully_qualified=True, name_buffer=NAME_BUFFER) if cons.lower is not None: # we add positive slack variable to body: # declare positive slack varName = "_slack_plus_" + cons_name posSlack = Var(within=NonNegativeReals) xblock.add_component(varName, posSlack) # add positive slack to body expression cons._body += posSlack # penalize slack in objective obj_expr += posSlack if cons.upper is not None: # we subtract a positive slack variable from the body: # declare slack varName = "_slack_minus_" + cons_name negSlack = Var(within=NonNegativeReals) xblock.add_component(varName, negSlack) # add negative slack to body expression cons._body -= negSlack # add slack to objective obj_expr += negSlack # make a new objective that minimizes sum of slack variables xblock._slack_objective = Objective(expr=obj_expr)
def _generate_model(self): self.model = ConcreteModel() model = self.model model._name = self.description model.s = Set(initialize=[1, 2]) model.x_unused = Var(within=Integers) model.x_unused.stale = False model.x_unused_initialy_stale = Var(within=Integers) model.x_unused_initialy_stale.stale = True model.X_unused = Var(model.s, within=Integers) model.X_unused_initialy_stale = Var(model.s, within=Integers) for i in model.s: model.X_unused[i].stale = False model.X_unused_initialy_stale[i].stale = True model.x = Var(within=RangeSet(None, None)) model.x.stale = False model.x_initialy_stale = Var(within=Integers) model.x_initialy_stale.stale = True model.X = Var(model.s, within=Integers) model.X_initialy_stale = Var(model.s, within=Integers) for i in model.s: model.X[i].stale = False model.X_initialy_stale[i].stale = True model.obj = Objective(expr= model.x + \ model.x_initialy_stale + \ sum_product(model.X) + \ sum_product(model.X_initialy_stale)) model.c = ConstraintList() model.c.add(model.x >= 1) model.c.add(model.x_initialy_stale >= 1) model.c.add(model.X[1] >= 0) model.c.add(model.X[2] >= 1) model.c.add(model.X_initialy_stale[1] >= 0) model.c.add(model.X_initialy_stale[2] >= 1) # Test that stale flags get set # on inactive blocks (where "inactive blocks" mean blocks # that do NOT follow a path of all active parent blocks # up to the top-level model) flat_model = model.clone() model.b = Block() model.B = Block(model.s) model.b.b = flat_model.clone() model.B[1].b = flat_model.clone() model.B[2].b = flat_model.clone() model.b.deactivate() model.B.deactivate() model.b.b.activate() model.B[1].b.activate() model.B[2].b.deactivate() assert model.b.active is False assert model.B[1].active is False assert model.B[1].active is False assert model.b.b.active is True assert model.B[1].b.active is True assert model.B[2].b.active is False
def _create_using(self, model, **kwds): """ Force all variables to lie in the nonnegative orthant. Required arguments: model The model to transform. Optional keyword arguments: pos_suffix The suffix applied to the 'positive' component of converted variables. Default is '_plus'. neg_suffix The suffix applied to the 'positive' component of converted variables. Default is '_minus'. """ # # Optional naming schemes # pos_suffix = kwds.pop("pos_suffix", "_plus") neg_suffix = kwds.pop("neg_suffix", "_minus") # # We first perform an abstract problem transformation. Then, if model # data is available, we instantiate the new model. If not, we construct # a mapping that can later be used to populate the new model. # nonneg = model.clone() components = collectAbstractComponents(nonneg) # Map from variable base names to a {index, rule} map constraint_rules = {} # Map from variable base names to a rule defining the domains for that # variable domain_rules = {} # Map from variable base names to its set of indices var_indices = {} # Map from fully qualified variable names to replacement expressions. # For now, it is actually a map from a variable name to a closure that # must later be evaulated with a model containing the replacement # variables. var_map = {} # # Get the constraints that enforce the bounds and domains of each # variable # for var_name in components["Var"]: var = nonneg.__getattribute__(var_name) # Individual bounds and domains orig_bounds = {} orig_domain = {} # New indices indices = set() # Map from constraint names to a constraint rule. constraints = {} # Map from variable indices to a domain domains = {} for ndx in var: # Fully qualified variable name vname = create_name(str(var_name), ndx) # We convert each index to a string to avoid difficult issues # regarding appending a suffix to tuples. # # If the index is None, this casts the index to a string, # which doesn't match up with how Pyomo treats None indices # internally. Replace with "" to be consistent. if ndx is None: v_ndx = "" else: v_ndx = str(ndx) # Get the variable bounds lb = value(var[ndx].lb) ub = value(var[ndx].ub) orig_bounds[ndx] = (lb, ub) # Get the variable domain if var[ndx].domain is not None: orig_domain[ndx] = var[ndx].domain else: orig_domain[ndx] = var.domain # Determine the replacement expression. Either a new single # variable with the same attributes, or a sum of two new # variables. # # If both the bounds and domain allow for negative values, # replace the variable with the sum of nonnegative ones. bounds_neg = (orig_bounds[ndx] == (None, None) or orig_bounds[ndx][0] is None or orig_bounds[ndx][0] < 0) domain_neg = (orig_domain[ndx] is None or orig_domain[ndx].bounds()[0] is None or orig_domain[ndx].bounds()[0] < 0) if bounds_neg and domain_neg: # Make two new variables. posVarSuffix = "%s%s" % (v_ndx, pos_suffix) negVarSuffix = "%s%s" % (v_ndx, neg_suffix) new_indices = (posVarSuffix, negVarSuffix) # Replace the original variable with a sum expression expr_dict = {posVarSuffix: 1, negVarSuffix: -1} else: # Add the new index. Lie if is 'None', since Pyomo treats # 'None' specially as a key. # # More lies: don't let a blank index exist. Replace it with # '_'. I don't actually have a justification for this other # than that allowing "" as a key will eventually almost # certainly lead to a strange bug. if v_ndx is None: t_ndx = "None" elif v_ndx == "": t_ndx = "_" else: t_ndx = v_ndx new_indices = (t_ndx, ) # Replace the original variable with a sum expression expr_dict = {t_ndx: 1} # Add the new indices for x in new_indices: indices.add(x) # Replace the original variable with an expression var_map[vname] = partial(self.sumRule, var_name, expr_dict) # Enforce bounds as constraints if orig_bounds[ndx] != (None, None): cname = "%s_%s" % (vname, "bounds") tmp = orig_bounds[ndx] constraints[cname] = partial(self.boundsConstraintRule, tmp[0], tmp[1], var_name, expr_dict) # Enforce the bounds of the domain as constraints if orig_domain[ndx] != None: cname = "%s_%s" % (vname, "domain_bounds") tmp = orig_domain[ndx].bounds() constraints[cname] = partial(self.boundsConstraintRule, tmp[0], tmp[1], var_name, expr_dict) # Domain will either be NonNegativeReals, NonNegativeIntegers, # or Binary. We consider Binary because some solvers may # optimize over binary variables. if var[ndx].is_continuous(): for x in new_indices: domains[x] = NonNegativeReals elif var[ndx].is_binary(): for x in new_indices: domains[x] = Binary elif var[ndx].is_integer(): for x in new_indices: domains[x] = NonNegativeIntegers else: logger.warning("Warning: domain '%s' not recognized, " "defaulting to 'NonNegativeReals'" % (var.domain, )) for x in new_indices: domains[x] = NonNegativeReals constraint_rules[var_name] = constraints domain_rules[var_name] = partial(self.exprMapRule, domains) var_indices[var_name] = indices # Remove all existing variables. toRemove = [] for (attr_name, attr) in nonneg.__dict__.items(): if isinstance(attr, Var): toRemove.append(attr_name) for attr_name in toRemove: nonneg.__delattr__(attr_name) # Add the sets defining the variables, then the variables for (k, v) in var_indices.items(): sname = "%s_indices" % k nonneg.__setattr__(sname, Set(initialize=v)) nonneg.__setattr__( k, Var(nonneg.__getattribute__(sname), domain=domain_rules[k], bounds=(0, None))) # Construct the model to get the variables and their indices # recognized in the model ##nonneg = nonneg.create() # Safe to evaluate the modifiedVars mapping for var in var_map: var_map[var] = var_map[var](nonneg) # Map from constraint base names to maps from indices to expressions constraintExprs = {} # # Convert all modified variables in all constraints in the original # problem # for conName in components["Constraint"]: con = nonneg.__getattribute__(conName) # Map from constraint indices to a corrected expression exprMap = {} for (ndx, cdata) in con._data.items(): lower = _walk_expr(cdata.lower, var_map) body = _walk_expr(cdata.body, var_map) upper = _walk_expr(cdata.upper, var_map) # Lie if ndx is None. Pyomo treats 'None' indices specially. if ndx is None: ndx = "None" # Cast indices to strings, otherwise tuples ruin everything exprMap[str(ndx)] = (lower, body, upper) # Add to list of expression maps constraintExprs[conName] = exprMap # Map from constraint base names to maps from indices to expressions objectiveExprs = {} # # Convert all modified variables in all objectives in the original # problem # for objName in components["Objective"]: obj = nonneg.__getattribute__(objName) # Map from objective indices to a corrected expression exprMap = {} for (ndx, odata) in obj._data.items(): exprMap[ndx] = _walk_expr(odata.expr, var_map) # Add to list of expression maps objectiveExprs[objName] = exprMap # Make the modified original constraints for (conName, ruleMap) in constraintExprs.items(): # Make the set of indices sname = conName + "_indices" _set = Set(initialize=ruleMap.keys()) nonneg.__setattr__(sname, _set) _set.construct() # Define the constraint _con = Constraint(nonneg.__getattribute__(sname), rule=partial(self.exprMapRule, ruleMap)) nonneg.__setattr__(conName, _con) _con.construct() # Make the bounds constraints for (varName, ruleMap) in constraint_rules.items(): conName = varName + "_constraints" # Make the set of indices sname = conName + "_indices" _set = Set(initialize=ruleMap.keys()) nonneg.__setattr__(sname, _set) _set.construct() # Define the constraint _con = Constraint(nonneg.__getattribute__(sname), rule=partial(self.delayedExprMapRule, ruleMap)) nonneg.__setattr__(conName, _con) _con.construct() # Make the objectives for (objName, ruleMap) in objectiveExprs.items(): # Make the set of indices sname = objName + "_indices" _set = Set(initialize=ruleMap.keys()) nonneg.__setattr__(sname, _set) _set.construct() # Define the constraint _obj = Objective(nonneg.__getattribute__(sname), rule=partial(self.exprMapRule, ruleMap)) nonneg.__setattr__(objName, _obj) _obj.construct() return nonneg
return model.x[t1, t2] == sum(model.y[t1, t2, i] * PIECEWISE_PTS[t1, t2][i] for i in xrange(len(PIECEWISE_PTS[t1, t2]))) def constraint2_rule(model, t1, t2): return model.Fx[t1, t2] == sum(model.y[t1, t2, i] * F(PIECEWISE_PTS[t1, t2][i]) for i in xrange(len(PIECEWISE_PTS[t1, t2]))) def constraint3_rule(model, t1, t2): return sum(model.y[t1, t2, j] for j in xrange(len(PIECEWISE_PTS[t1, t2]))) == 1 model.obj = Objective(expr=sum_product(model.Fx), sense=maximize) model.constraint1 = Constraint(INDEX_SET1, INDEX_SET2, rule=constraint1_rule) model.constraint2 = Constraint(INDEX_SET1, INDEX_SET2, rule=constraint2_rule) model.constraint3 = Constraint(INDEX_SET1, INDEX_SET2, rule=constraint3_rule) model.SOS_set_constraint = SOSConstraint(INDEX_SET1, INDEX_SET2, var=model.y, index=model.SOS_indices, sos=2) #Fix the answer for testing purposes model.set_answer_constraint1 = Constraint(expr=model.x[1, 1] == 2.5) model.set_answer_constraint2 = Constraint(expr=model.x[2, 1] == 1.5) model.set_answer_constraint3 = Constraint(expr=model.x[1, 2] == 2.5) model.set_answer_constraint4 = Constraint(expr=model.x[2, 2] == 1.5) model.set_answer_constraint5 = Constraint(expr=model.x[1, 3] == 2.5)
# ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects # Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC # Under the terms of Contract DE-NA0003525 with National Technology and # Engineering Solutions of Sandia, LLC, the U.S. Government retains certain # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ from pyomo.core import ConcreteModel, Var, Objective model = ConcreteModel() model.x = Var() model.y = Var() model.o = Objective(expr=model.x + model.y)
# Objective function: def obj_rule(m): c = len(workers) # Our primary objective is to minimize number of workers needed -> sum(c * m.needed[worker]) # We multiply by a constant 'c' to make sure that this part of the objective is most important. # Secondary objective is no_pref (avoiding scheduling 1 days in the weekends) return sum(m.no_pref[worker] for worker in workers) + sum(c * m.needed[worker] for worker in workers) # Now we can add the objective function to the model and set it to be minimized. # The objective now is to find a schedule minimizing the number of workers needed and once that is done, also reduce # the number of workers who have to work on Sundays but not on Saturdays. # The constant used to multiply needed workers makes sure that this is the primary objective, model.obj = Objective(rule=obj_rule, sense=minimize) # Now we can add the constraints that describe our food store. # We create a set of constraints on the model. model.constraints = ConstraintList() # 1. Constraint to make sure that all shifts are assigned and appropriate number of workers are working, for day in days: for shift in days_shifts[day]: if day != 'Sun' and shift in ['morning', 'evening']: # Weekdays and Saturday, morning and evenings have 2 workers. # Note that constraints are booleans! # Here we make sure that for each variable in works for a given worker, day, shift the sum of Binary values # is exactly 2. model.constraints.add(
def define_model(**kwds): model = ConcreteModel() model.x1 = Var(bounds=(-5, 4)) # domain variable model.x2 = Var(bounds=(-5, 4)) # domain variable model.x3 = Var(bounds=(-5, 4)) # domain variable model.x4 = Var(bounds=(-5, 4)) # domain variable model.x5 = Var(bounds=(-5, 4)) # domain variable model.x6 = Var(bounds=(-5, 4)) # domain variable model.x7 = Var(bounds=(-5, 4)) # domain variable model.Fx1 = Var() # range variable model.Fx2 = Var() # range variable model.Fx3 = Var() # range variable model.Fx4 = Var() # range variable model.Fx5 = Var() # range variable model.Fx6 = Var() # range variable model.Fx7 = Var() # range variable model.obj = Objective(expr=model.Fx1 + model.Fx2 + model.Fx3 + model.Fx4 + model.Fx5 + model.Fx6 + model.Fx7, sense=kwds.pop('sense', maximize)) model.piecewise1 = Piecewise(model.Fx1, model.x1, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise2 = Piecewise(model.Fx2, model.x2, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise3 = Piecewise(model.Fx3, model.x3, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise4 = Piecewise(model.Fx4, model.x4, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise5 = Piecewise(model.Fx5, model.x5, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise6 = Piecewise(model.Fx6, model.x6, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise7 = Piecewise(model.Fx7, model.x7, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) #Fix the answer for testing purposes model.set_answer_constraint1 = Constraint(expr=model.x1 == -5.0) model.set_answer_constraint2 = Constraint(expr=model.x2 == -3.0) model.set_answer_constraint3 = Constraint(expr=model.x3 == -2.5) model.set_answer_constraint4 = Constraint(expr=model.x4 == -1.5) model.set_answer_constraint5 = Constraint(expr=model.x5 == 2.0) model.set_answer_constraint6 = Constraint(expr=model.x6 == 3.5) model.set_answer_constraint7 = Constraint(expr=model.x7 == 4.0) return model
def get_mock_model(self): model = ConcreteModel() model.x = Var(within=Binary) model.con = Constraint(expr=model.x >= 1) model.obj = Objective(expr=model.x) return model
def criticalityCheck(self, x, y, z, rom_params, worstcase=False, M=[0.0]): model = self.model self.setVarValue(x=x, y=y, z=z) self.setBound(x, y, z, 1e10) self.deactiveExtraConObj() self.activateRomCons(x, rom_params) optGJH = SolverFactory('contrib.gjh') optGJH.solve(model, tee=False, symbolic_solver_labels=True) g, J, varlist, conlist = model._gjh_info l = ConcreteModel() l.v = Var(varlist, domain=Reals) for i in varlist: #dummy = model.find_component(i) l.v[i] = 0.0 l.v[i].setlb(-1.0) l.v[i].setub(1.0) if worstcase: if M.all() == 0.0: print( 'WARNING: worstcase criticality was requested but Jacobian error bound is zero' ) l.t = Var(range(0, self.ly), domain=Reals) for i in range(0, self.ly): l.t[i].setlb(-M[i]) l.t[i].setub(M[i]) def linConMaker(l, i): # i should be range(len(conlist) - 1) # because last element of conlist is the objective con_i = model.find_component(conlist[i]) isEquality = con_i.equality isROM = False if conlist[i][:7] == '.' + self.TRF.name + '.rom': isROM = True romIndex = int(filter(str.isdigit, conlist[i])) # This is very inefficient # Fix this later if these problems get big # This is the ith row of J times v Jv = sum(x[2] * l.v[varlist[x[1]]] for x in J if x[0] == i) if isEquality: if worstcase and isROM: return Jv + l.t[romIndex] == 0 else: return Jv == 0 else: lo = con_i.lower up = con_i.upper if lo is not None: level = lo.value - con_i.lslack() if up is not None: return (lo.value <= level + Jv <= up.value) else: return (lo.value <= level + Jv) elif up is not None: level = up.value - con_i.uslack() return (level + Jv <= up.value) else: raise Exception( "This constraint seems to be neither equality or inequality: " + conlist(i)) l.lincons = Constraint(range(len(conlist) - 1), rule=linConMaker) l.obj = Objective(expr=sum(x[1] * l.v[varlist[x[0]]] for x in g)) # Calculate gradient norm for scaling purposes gfnorm = sqrt(sum(x[1]**2 for x in g)) opt = SolverFactory(self.config.solver) opt.options.update(self.config.solver_options) #opt.options['halt_on_ampl_error'] = 'yes' #opt.options['max_iter'] = 5000 results = opt.solve(l, keepfiles=self.keepfiles, tee=self.stream_solver) if ((results.solver.status == SolverStatus.ok) and (results.solver.termination_condition == TerminationCondition.optimal)): l.solutions.load_from(results) if gfnorm > 1: return True, abs(l.obj()) / gfnorm else: return True, abs(l.obj()) else: print("Waring: Crticality check fails with solver Status: " + str(results.solver.status)) print("And Termination Conditions: " + str(results.solver.termination_condition)) return False, infinity