def _build_equality_set(m): """Construct an equality set map. Maps all variables to the set of variables that are linked to them by equality. Mapping takes place using id(). That is, if you have x = y, then you would have id(x) -> ComponentSet([x, y]) and id(y) -> ComponentSet([x, y]) in the mapping. """ #: dict: map of var UID to the set of all equality-linked var UIDs eq_var_map = ComponentMap() relevant_vars = ComponentSet() for constr in m.component_data_objects(ctype=Constraint, active=True, descend_into=True): # Check to make sure the constraint is of form v1 - v2 == 0 if (value(constr.lower) == 0 and value(constr.upper) == 0 and constr.body.polynomial_degree() == 1): repn = generate_canonical_repn(constr.body) # only take the variables with nonzero coefficients vars_ = [v for i, v in enumerate(repn.variables) if repn.linear[i]] if (len(vars_) == 2 and sorted(l for l in repn.linear if l) == [-1, 1]): # this is an a == b constraint. v1 = vars_[0] v2 = vars_[1] set1 = eq_var_map.get(v1, ComponentSet([v1])) set2 = eq_var_map.get(v2, ComponentSet([v2])) relevant_vars.update([v1, v2]) set1.update(set2) # set1 is now the union for v in set1: eq_var_map[v] = set1 return eq_var_map, relevant_vars
def test_induced_linearity_case2(self): m = ConcreteModel() m.x = Var([0], bounds=(-3, 8)) m.y = Var(RangeSet(4), domain=Binary) m.z = Var(domain=Integers, bounds=(-1, 2)) m.constr = Constraint( expr=m.x[0] == m.y[1] + 2 * m.y[2] + m.y[3] + 2 * m.y[4] + m.z) m.logical = ConstraintList() m.logical.add(expr=m.y[1] + m.y[2] == 1) m.logical.add(expr=m.y[3] + m.y[4] == 1) m.logical.add(expr=m.y[2] + m.y[4] <= 1) m.b = Var(bounds=(-2, 7)) m.c = Var() m.bilinear = Constraint( expr=(m.x[0] - 3) * (m.b + 2) - (m.c + 4) * m.b + exp(m.b ** 2) * m.x[0] <= m.c) TransformationFactory('contrib.induced_linearity').apply_to(m) xfrmed_blk = m._induced_linearity_info.x0_b_bilinear self.assertSetEqual( set(xfrmed_blk.valid_values), set([1, 2, 3, 4, 5])) select_one_repn = generate_standard_repn( xfrmed_blk.select_one_value.body) self.assertEqual( ComponentSet(select_one_repn.linear_vars), ComponentSet(xfrmed_blk.x_active[i] for i in xfrmed_blk.valid_values))
def test_log(self): m = pe.ConcreteModel() m.x = pe.Var(bounds=(1, 2)) m.y = pe.Var(bounds=(1, 2)) m.z = pe.Var() m.w = pe.Var() m.c = pe.Constraint(expr=pe.log(m.x * m.y) + m.z == 0) m.c2 = pe.Constraint(expr=m.w - 3 * pe.log(m.x * m.y) == 0) rel = coramin.relaxations.relax(m) self.assertTrue(hasattr(rel, 'aux_cons')) self.assertTrue(hasattr(rel, 'aux_vars')) self.assertEqual(len(rel.aux_cons), 2) self.assertEqual(len(rel.aux_vars), 2) self.assertAlmostEqual(rel.aux_vars[1].lb, 1) self.assertAlmostEqual(rel.aux_vars[1].ub, 4) self.assertAlmostEqual(rel.aux_vars[2].lb, math.log(1)) self.assertAlmostEqual(rel.aux_vars[2].ub, math.log(4)) self.assertEqual(rel.aux_cons[1].lower, 0) self.assertEqual(rel.aux_cons[1].upper, 0) ders = reverse_sd(rel.aux_cons[1].body) self.assertEqual(ders[rel.z], 1) self.assertEqual(ders[rel.aux_vars[2]], 1) self.assertEqual(len(list(identify_variables(rel.aux_cons[1].body))), 2) self.assertEqual(rel.aux_cons[2].lower, 0) self.assertEqual(rel.aux_cons[2].upper, 0) ders = reverse_sd(rel.aux_cons[2].body) self.assertEqual(ders[rel.w], 1) self.assertEqual(ders[rel.aux_vars[2]], -3) self.assertEqual(len(list(identify_variables(rel.aux_cons[2].body))), 2) self.assertTrue(hasattr(rel, 'relaxations')) self.assertTrue(hasattr(rel.relaxations, 'rel0')) self.assertTrue( isinstance(rel.relaxations.rel0, coramin.relaxations.PWMcCormickRelaxation)) self.assertIn(rel.x, ComponentSet(rel.relaxations.rel0.get_rhs_vars())) self.assertIn(rel.y, ComponentSet(rel.relaxations.rel0.get_rhs_vars())) self.assertEqual(id(rel.aux_vars[1]), id(rel.relaxations.rel0.get_aux_var())) self.assertTrue(hasattr(rel.relaxations, 'rel1')) self.assertTrue( isinstance(rel.relaxations.rel1, coramin.relaxations.PWUnivariateRelaxation)) self.assertIn(rel.aux_vars[1], ComponentSet(rel.relaxations.rel1.get_rhs_vars())) self.assertEqual(id(rel.aux_vars[2]), id(rel.relaxations.rel1.get_aux_var())) self.assertFalse(rel.relaxations.rel1.is_rhs_convex()) self.assertTrue(rel.relaxations.rel1.is_rhs_concave()) self.assertFalse(hasattr(rel.relaxations, 'rel2'))
def test_var_aggregate(self): """Test for transitivity in a variable equality set.""" m = self.build_model() TransformationFactory('contrib.aggregate_vars').apply_to(m) z_to_vars = m._var_aggregator_info.z_to_vars var_to_z = m._var_aggregator_info.var_to_z z = m._var_aggregator_info.z self.assertEqual(z_to_vars[z[1]], ComponentSet([m.v3, m.v4])) self.assertEqual(z_to_vars[z[2]], ComponentSet([m.x[1], m.x[2], m.x[3], m.x[4]])) self.assertEqual(z_to_vars[z[3]], ComponentSet([m.y[1], m.y[2]])) self.assertIs(var_to_z[m.v3], z[1]) self.assertIs(var_to_z[m.v4], z[1]) self.assertIs(var_to_z[m.x[1]], z[2]) self.assertIs(var_to_z[m.x[2]], z[2]) self.assertIs(var_to_z[m.x[3]], z[2]) self.assertIs(var_to_z[m.x[4]], z[2]) self.assertIs(var_to_z[m.y[1]], z[3]) self.assertIs(var_to_z[m.y[2]], z[3]) self.assertEqual(z[1].value, 2) self.assertEqual(z[1].lb, 2) self.assertEqual(z[1].ub, 4) self.assertEqual(z[3].value, 3.5)
def test_product1(self): m = pe.ConcreteModel() m.x = pe.Var(bounds=(-1, 1)) m.y = pe.Var(bounds=(-1, 1)) m.z = pe.Var() m.c = pe.Constraint(expr=m.z - m.x * m.y == 0) rel = coramin.relaxations.relax(m) self.assertTrue(hasattr(rel, 'aux_cons')) self.assertTrue(hasattr(rel, 'aux_vars')) self.assertEqual(len(rel.aux_cons), 1) self.assertEqual(len(rel.aux_vars), 1) self.assertAlmostEqual(rel.aux_vars[1].lb, -1) self.assertAlmostEqual(rel.aux_vars[1].ub, 1) self.assertEqual(rel.aux_cons[1].lower, 0) self.assertEqual(rel.aux_cons[1].upper, 0) ders = reverse_sd(rel.aux_cons[1].body) self.assertEqual(ders[rel.z], 1) self.assertEqual(ders[rel.aux_vars[1]], -1) self.assertEqual(len(list(identify_variables(rel.aux_cons[1].body))), 2) self.assertTrue(hasattr(rel, 'relaxations')) self.assertTrue(hasattr(rel.relaxations, 'rel0')) self.assertTrue( isinstance(rel.relaxations.rel0, coramin.relaxations.PWMcCormickRelaxation)) self.assertIn(rel.x, ComponentSet(rel.relaxations.rel0.get_rhs_vars())) self.assertIn(rel.y, ComponentSet(rel.relaxations.rel0.get_rhs_vars())) self.assertEqual(id(rel.aux_vars[1]), id(rel.relaxations.rel0.get_aux_var()))
def _apply_to(self, instance, **kwds): config = self.CONFIG(kwds) if config.tmp and not hasattr(instance, '_tmp_propagate_fixed'): instance._tmp_propagate_fixed = ComponentSet() eq_var_map, relevant_vars = _build_equality_set(instance) #: ComponentSet: The set of all fixed variables fixed_vars = ComponentSet((v for v in relevant_vars if v.fixed)) newly_fixed = _detect_fixed_variables(instance) if config.tmp: instance._tmp_propagate_fixed.update(newly_fixed) fixed_vars.update(newly_fixed) processed = ComponentSet() # Go through each fixed variable to propagate the 'fixed' status to all # equality-linked variabes. for v1 in fixed_vars: # If we have already processed the variable, skip it. if v1 in processed: continue eq_set = eq_var_map.get(v1, ComponentSet([v1])) for v2 in eq_set: if (v2.fixed and value(v1) != value(v2)): raise ValueError( 'Variables {} and {} have conflicting fixed ' 'values of {} and {}, but are linked by ' 'equality constraints.'.format(v1.name, v2.name, value(v1), value(v2))) elif not v2.fixed: v2.fix(value(v1)) if config.tmp: instance._tmp_propagate_fixed.add(v2) # Add all variables in the equality set to the set of processed # variables. processed.update(eq_set)
def run_order(self, G, order, function, ignore=None, use_guesses=False): """ Run computations in the order provided by calling the function Arguments --------- G A networkx graph corresponding to order order The order in which to run each node in the graph function The function to be called on each block/node ignore Edge indexes to ignore when passing values use_guesses If True, will check the guesses dict when fixing free variables before calling function """ fixed_inputs = self.fixed_inputs() fixed_outputs = ComponentSet() edge_map = self.edge_to_idx(G) guesses = self.options["guesses"] default = self.options["default_guess"] for lev in order: for unit in lev: if unit not in fixed_inputs: fixed_inputs[unit] = ComponentSet() fixed_ins = fixed_inputs[unit] # make sure all inputs are fixed for port in unit.component_data_objects(Port): if not len(port.sources()): continue if use_guesses and port in guesses: self.load_guesses(guesses, port, fixed_ins) self.load_values(port, default, fixed_ins, use_guesses) function(unit) # free the inputs that were not already fixed for var in fixed_ins: var.free() fixed_ins.clear() # pass the values downstream for all outlet ports for port in unit.component_data_objects(Port): dests = port.dests() if not len(dests): continue for var in port.iter_vars(expr_vars=True, fixed=False): fixed_outputs.add(var) var.fix() for arc in dests: arc_map = self.arc_to_edge(G) if edge_map[arc_map[arc]] not in ignore: self.pass_values(arc, fixed_inputs) for var in fixed_outputs: var.free() fixed_outputs.clear()
def test_len(self): cset = ComponentSet() self.assertEqual(len(cset), 0) cset.update(self._components) self.assertEqual(len(cset), len(self._components)) cset = ComponentSet(self._components) self.assertEqual(len(cset), len(self._components)) self.assertTrue(len(self._components) > 0)
def add_integer_cut(var_values, target_model, solve_data, config, feasible=False): """Add an integer cut to the target GDP model.""" with time_code(solve_data.timing, 'integer cut generation'): m = target_model GDPopt = m.GDPopt_utils var_value_is_one = ComponentSet() var_value_is_zero = ComponentSet() for var, val in zip(GDPopt.variable_list, var_values): if not var.is_binary(): continue if var.fixed: if val is not None and var.value != val: # val needs to be None or match var.value. Otherwise, we have a # contradiction. raise ValueError( "Fixed variable %s has value %s != " "provided value of %s." % (var.name, var.value, val)) val = var.value if not config.force_subproblem_nlp: # Skip indicator variables # TODO we should implement this as a check among Disjuncts instead if not (var.local_name == 'indicator_var' and var.parent_block().type() == Disjunct): continue if fabs(val - 1) <= config.integer_tolerance: var_value_is_one.add(var) elif fabs(val) <= config.integer_tolerance: var_value_is_zero.add(var) else: raise ValueError( 'Binary %s = %s is not 0 or 1' % (var.name, val)) if not (var_value_is_one or var_value_is_zero): # if no remaining binary variables, then terminate algorithm. config.logger.info( 'Adding integer cut to a model without discrete variables. ' 'Model is now infeasible.') if solve_data.objective_sense == minimize: solve_data.LB = float('inf') else: solve_data.UB = float('-inf') return False int_cut = (sum(1 - v for v in var_value_is_one) + sum(v for v in var_value_is_zero)) >= 1 if not feasible: config.logger.info('Adding integer cut') GDPopt.integer_cuts.add(expr=int_cut) else: backtracking_enabled = ( "disabled" if GDPopt.no_backtracking.active else "allowed") config.logger.info( 'Registering explored configuration. ' 'Backtracking is currently %s.' % backtracking_enabled) GDPopt.no_backtracking.add(expr=int_cut)
def _apply_to(self, instance, tmp=False): """Apply the transformation. Args: instance (Block): the block on which to search for x == y constraints. Note that variables may be located anywhere in the model. tmp (bool, optional): Whether the bound modifications will be temporary Returns: None """ if tmp and not hasattr(instance, '_tmp_propagate_original_bounds'): instance._tmp_propagate_original_bounds = Suffix( direction=Suffix.LOCAL) eq_var_map, relevant_vars = _build_equality_set(instance) processed = ComponentSet() # Go through each variable in an equality set to propagate the variable # bounds to all equality-linked variables. for var in relevant_vars: # If we have already processed the variable, skip it. if var in processed: continue var_equality_set = eq_var_map.get(var, ComponentSet([var])) #: variable lower bounds in the equality set lbs = [v.lb for v in var_equality_set if v.has_lb()] max_lb = max(lbs) if len(lbs) > 0 else None #: variable upper bounds in the equality set ubs = [v.ub for v in var_equality_set if v.has_ub()] min_ub = min(ubs) if len(ubs) > 0 else None # Check for error due to bound cross-over if max_lb is not None and min_ub is not None and max_lb > min_ub: # the lower bound is above the upper bound. Raise a ValueError. # get variable with the highest lower bound v1 = next(v for v in var_equality_set if v.lb == max_lb) # get variable with the lowest upper bound v2 = next(v for v in var_equality_set if v.ub == min_ub) raise ValueError( 'Variable {} has a lower bound {} ' ' > the upper bound {} of variable {}, ' 'but they are linked by equality constraints.' .format(v1.name, value(v1.lb), value(v2.ub), v2.name)) for v in var_equality_set: if tmp: # TODO warn if overwriting instance._tmp_propagate_original_bounds[v] = ( v.lb, v.ub) v.setlb(max_lb) v.setub(min_ub) processed.update(var_equality_set)
def test_isdisjoint(self): cset1 = ComponentSet() cset2 = ComponentSet() self.assertTrue(cset1.isdisjoint(cset2)) self.assertTrue(cset2.isdisjoint(cset1)) v = variable() cset1.add(v) self.assertTrue(cset1.isdisjoint(cset2)) self.assertTrue(cset2.isdisjoint(cset1)) cset2.add(v) self.assertFalse(cset1.isdisjoint(cset2)) self.assertFalse(cset2.isdisjoint(cset1))
def _setup_subproblem(b, master_vars, relax_subproblem_cons): # first get the objective and turn it into a constraint master_vars = ComponentSet(master_vars) objs = list( b.component_data_objects(pe.Objective, descend_into=False, active=True)) if len(objs) != 1: raise ValueError('Subproblem must have exactly one objective') orig_obj = objs[0] orig_obj_expr = orig_obj.expr b.del_component(orig_obj) b._z = pe.Var(bounds=(0, None)) b.objective = pe.Objective(expr=b._z) b.dual = pe.Suffix(direction=pe.Suffix.IMPORT) b._eta = pe.Var() b.aux_cons = pe.ConstraintList() for c in list( b.component_data_objects(pe.Constraint, descend_into=True, active=True, sort=True)): if not relax_subproblem_cons: c_vars = ComponentSet( identify_variables(c.body, include_fixed=False)) if not _any_common_elements(master_vars, c_vars): continue if c.equality: body = c.body rhs = pe.value(c.lower) body -= rhs b.aux_cons.add(body - b._z <= 0) b.aux_cons.add(-body - b._z <= 0) _del_con(c) else: body = c.body lower = pe.value(c.lower) upper = pe.value(c.upper) if upper is not None: body_upper = body - upper - b._z b.aux_cons.add(body_upper <= 0) if lower is not None: body_lower = body - lower body_lower = -body_lower body_lower -= b._z b.aux_cons.add(body_lower <= 0) _del_con(c) b.obj_con = pe.Constraint(expr=orig_obj_expr - b._eta - b._z <= 0)
def _apply_to(self, instance, tmp=False): """Apply the transformation. Args: instance (Block): the block on which to search for x == y constraints. Note that variables may be located anywhere in the model. tmp (bool, optional): Whether the variable fixing will be temporary Returns: None Raises: ValueError: if two fixed variables x = y have different values. """ if tmp and not hasattr(instance, '_tmp_propagate_fixed'): instance._tmp_propagate_fixed = ComponentSet() eq_var_map, relevant_vars = _build_equality_set(instance) #: ComponentSet: The set of all fixed variables fixed_vars = ComponentSet((v for v in relevant_vars if v.fixed)) newly_fixed = _detect_fixed_variables(instance) if tmp: instance._tmp_propagate_fixed.update(newly_fixed) fixed_vars.update(newly_fixed) processed = ComponentSet() # Go through each fixed variable to propagate the 'fixed' status to all # equality-linked variabes. for v1 in fixed_vars: # If we have already processed the variable, skip it. if v1 in processed: continue eq_set = eq_var_map.get(v1, ComponentSet([v1])) for v2 in eq_set: if (v2.fixed and value(v1) != value(v2)): raise ValueError( 'Variables {} and {} have conflicting fixed ' 'values of {} and {}, but are linked by ' 'equality constraints.' .format(v1.name, v2.name, value(v1), value(v2))) elif not v2.fixed: v2.fix(value(v1)) if tmp: instance._tmp_propagate_fixed.add(v2) # Add all variables in the equality set to the set of processed # variables. processed.update(eq_set)
def add_integer_cut(var_values, solve_data, config, feasible=False): """Add an integer cut to the linear GDP model.""" m = solve_data.linear_GDP GDPopt = m.GDPopt_utils var_value_is_one = ComponentSet() var_value_is_zero = ComponentSet() for var, val in zip(GDPopt.working_var_list, var_values): if not var.is_binary(): continue if var.fixed: if val is not None and var.value != val: # val needs to be None or match var.value. Otherwise, we have a # contradiction. raise ValueError( "Fixed variable %s has value %s != " "provided value of %s." % (var.name, var.value, val)) val = var.value # TODO we can also add a check to skip binary variables that are not an # indicator_var on disjuncts. if fabs(val - 1) <= config.integer_tolerance: var_value_is_one.add(var) elif fabs(val) <= config.integer_tolerance: var_value_is_zero.add(var) else: raise ValueError( 'Binary %s = %s is not 0 or 1' % (var.name, val)) if not (var_value_is_one or var_value_is_zero): # if no remaining binary variables, then terminate algorithm. config.logger.info( 'Adding integer cut to a model without binary variables. ' 'Model is now infeasible.') if solve_data.objective_sense == minimize: solve_data.LB = float('inf') else: solve_data.UB = float('-inf') return False int_cut = (sum(1 - v for v in var_value_is_one) + sum(v for v in var_value_is_zero)) >= 1 if not feasible: config.logger.info('Adding integer cut') GDPopt.integer_cuts.add(expr=int_cut) else: backtracking_enabled = ( "disabled" if GDPopt.no_backtracking.active else "allowed") config.logger.info( 'Registering explored configuration. ' 'Backtracking is currently %s.' % backtracking_enabled) GDPopt.no_backtracking.add(expr=int_cut)
def _process_activated_container(blk): """Process a container object, returning the new components found.""" new_fixed_true_disjuncts = ComponentSet( disj for disj in blk.component_data_objects(Disjunct, active=True) if disj.indicator_var.value == 1 and disj.indicator_var.fixed) new_activated_disjunctions = ComponentSet( blk.component_data_objects(Disjunction, active=True)) new_activated_disjuncts = ComponentSet( disj for disjtn in new_activated_disjunctions for disj in _activated_disjuncts_in_disjunction(disjtn)) new_activated_constraints = ComponentSet( blk.component_data_objects(Constraint, active=True)) return (new_activated_disjunctions, new_fixed_true_disjuncts, new_activated_disjuncts, new_activated_constraints)
def filter_variables_from_solution(candidate_variables_at_relaxation_solution, tolerance=1e-6): """ This function takes a set of candidate variables for OBBT and filters out the variables that are at their bounds in the provided solution to the relaxation. See Gleixner, Ambros M., et al. "Three enhancements for optimization-based bound tightening." Journal of Global Optimization 67.4 (2017): 731-757. for details on why this works. The basic idea is that if x = xl is feasible for the relaxation that will be used for OBBT, then minimizing x subject to that relaxation is guaranteed to result in an optimal solution of x* = xl. This function simply loops through candidate_variables_at_relaxation_solution and specifies which variables should be minimized and which variables should be maximized with OBBT. Parameters ---------- candidate_variables_at_relaxation_solution: iterable of _GeneralVarData This should be an iterable of the variables which are candidates for OBBT. The values of the variables should be feasible for the relaxation that would be used to perform OBBT on the variables. tolerance: float A float greater than or equal to zero. If the value of the variable is within tolerance of its lower bound, then that variable is filtered from the set of variables that should be minimized for OBBT. The same is true for upper bounds and variables that should be maximized. Returns ------- vars_to_minimize: ComponentSet of _GeneralVarData variables that should be considered for minimization vars_to_maximize: ComponentSet of _GeneralVarData variables that should be considered for maximization """ candidate_vars = ComponentSet(candidate_variables_at_relaxation_solution) vars_to_minimize = ComponentSet() vars_to_maximize = ComponentSet() for v in candidate_vars: if (not v.has_lb()) or (v.value - v.lb > tolerance): vars_to_minimize.add(v) if (not v.has_ub()) or (v.ub - v.value > tolerance): vars_to_maximize.add(v) return vars_to_minimize, vars_to_maximize
def _set_input(self, relaxation_side=RelaxationSide.BOTH, persistent_solvers=None, use_linear_relaxation=True, large_eval_tol=math.inf): self._oa_points = list() self._saved_oa_points = list() self._persistent_solvers = persistent_solvers if self._persistent_solvers is None: self._persistent_solvers = ComponentSet() if not isinstance(self._persistent_solvers, Iterable): self._persistent_solvers = ComponentSet([self._persistent_solvers]) else: self._persistent_solvers = ComponentSet(self._persistent_solvers) self._relaxation_side = relaxation_side assert self._relaxation_side in RelaxationSide self.use_linear_relaxation = use_linear_relaxation self.large_eval_tol = large_eval_tol
def test_misc_set_ops(self): v1 = variable() cset1 = ComponentSet([v1]) v2 = variable() cset2 = ComponentSet([v2]) cset3 = ComponentSet([v1, v2]) empty = ComponentSet([]) self.assertEqual(cset1 | cset2, cset3) self.assertEqual((cset1 | cset2) - cset3, empty) self.assertEqual(cset1 ^ cset2, cset3) self.assertEqual(cset1 ^ cset3, cset2) self.assertEqual(cset2 ^ cset3, cset1) self.assertEqual(cset1 & cset2, empty) self.assertEqual(cset1 & cset3, cset1) self.assertEqual(cset2 & cset3, cset2)
def create_hull_model(self): m = ConcreteModel() m.p = Var([1, 2], bounds=(0, 10)) m.time1 = Disjunction(expr=[m.p[1] >= 1, m.p[1] == 0]) m.on = Disjunct() m.on.above_min = Constraint(expr=m.p[2] >= 1) m.on.ramping = Constraint(expr=m.p[2] - m.p[1] <= 3) m.on.on_before = Constraint(expr=m.p[1] >= 1) m.startup = Disjunct() m.startup.startup_limit = Constraint(expr=(1, m.p[2], 2)) m.startup.off_before = Constraint(expr=m.p[1] == 0) m.off = Disjunct() m.off.off = Constraint(expr=m.p[2] == 0) m.time2 = Disjunction(expr=[m.on, m.startup, m.off]) m.obj = Objective(expr=m.p[1] + m.p[2]) hull = TransformationFactory('gdp.hull') hull.apply_to(m) disaggregatedVars = ComponentSet([ hull.get_disaggregated_var(m.p[1], m.time1.disjuncts[0]), hull.get_disaggregated_var(m.p[1], m.time1.disjuncts[1]), hull.get_disaggregated_var(m.p[1], m.on), hull.get_disaggregated_var(m.p[2], m.on), hull.get_disaggregated_var(m.p[1], m.startup), hull.get_disaggregated_var(m.p[2], m.startup), hull.get_disaggregated_var(m.p[1], m.off), hull.get_disaggregated_var(m.p[2], m.off) ]) return m, disaggregatedVars
def _set_instance(self, model, kwds={}): if not isinstance(model, (Model, IBlockStorage)): msg = "The problem instance supplied to the {0} plugin " \ "'_presolve' method must be of type 'Model'".format(type(self)) raise ValueError(msg) self._pyomo_model = model self._symbolic_solver_labels = kwds.pop('symbolic_solver_labels', self._symbolic_solver_labels) self._skip_trivial_constraints = kwds.pop( 'skip_trivial_constraints', self._skip_trivial_constraints) self._output_fixed_variable_bounds = kwds.pop( 'output_fixed_variable_bounds', self._output_fixed_variable_bounds) self._pyomo_var_to_solver_var_map = ComponentMap() self._pyomo_con_to_solver_con_map = ComponentMap() self._vars_referenced_by_con = ComponentMap() self._vars_referenced_by_obj = ComponentSet() self._referenced_variables = ComponentMap() self._objective_label = None self._objective = None self._symbol_map = SymbolMap() if self._symbolic_solver_labels: self._labeler = TextLabeler() else: self._labeler = NumericLabeler('x')
def _set_objective(self, obj): if self._objective is not None: for var in self._vars_referenced_by_obj: self._referenced_variables[var] -= 1 self._vars_referenced_by_obj = ComponentSet() self._objective = None if obj.active is False: raise ValueError('Cannot add inactive objective to solver.') if obj.sense == pyomo.core.kernel.minimize: sense = self._gurobipy.GRB.MINIMIZE elif obj.sense == pyomo.core.kernel.maximize: sense = self._gurobipy.GRB.MAXIMIZE else: raise ValueError('Objective sense is not recognized: {0}'.format( obj.sense)) gurobi_expr, referenced_vars = self._get_expr_from_pyomo_expr( obj.expr, self._max_obj_degree) for var in referenced_vars: self._referenced_variables[var] += 1 self._solver_model.setObjective(gurobi_expr, sense=sense) self._objective = obj self._vars_referenced_by_obj = referenced_vars
def _add_sos_constraint(self, con): if not con.active: return None conname = self._symbol_map.getSymbol(con, self._labeler) level = con.level if level == 1: sos_type = self._solver_model.SOS.type.SOS1 elif level == 2: sos_type = self._solver_model.SOS.type.SOS2 else: raise ValueError("Solver does not support SOS " "level {0} constraints".format(level)) cplex_vars = [] weights = [] self._vars_referenced_by_con[con] = ComponentSet() if hasattr(con, 'get_items'): # aml sos constraint sos_items = list(con.get_items()) else: # kernel sos constraint sos_items = list(con.items()) for v, w in sos_items: self._vars_referenced_by_con[con].add(v) cplex_vars.append(self._pyomo_var_to_solver_var_map[v]) self._referenced_variables[v] += 1 weights.append(w) self._solver_model.SOS.add(type=sos_type, SOS=[cplex_vars, weights], name=conname) self._pyomo_con_to_solver_con_map[con] = conname self._solver_con_to_pyomo_con_map[conname] = con
def _add_sos_constraint(self, con): if not con.active: return None conname = self._symbol_map.getSymbol(con, self._labeler) level = con.level if level not in [1, 2]: raise ValueError("Solver does not support SOS " "level {0} constraints".format(level)) xpress_vars = [] weights = [] self._vars_referenced_by_con[con] = ComponentSet() if hasattr(con, 'get_items'): # aml sos constraint sos_items = list(con.get_items()) else: # kernel sos constraint sos_items = list(con.items()) for v, w in sos_items: self._vars_referenced_by_con[con].add(v) xpress_vars.append(self._pyomo_var_to_solver_var_map[v]) self._referenced_variables[v] += 1 weights.append(w) xpress_con = self._xpress.sos(xpress_vars, weights, level, conname) self._solver_model.addSOS(xpress_con) self._pyomo_con_to_solver_con_map[con] = xpress_con self._solver_con_to_pyomo_con_map[xpress_con] = con
def _get_expr_from_pyomo_repn(self, repn, max_degree=2): referenced_vars = ComponentSet() degree = repn.polynomial_degree() if (degree is None) or (degree > max_degree): raise DegreeError( 'XpressDirect does not support expressions of degree {0}.'. format(degree)) # NOTE: xpress's python interface only allows for expresions # with native numeric types. Others, like numpy.float64, # will cause an exception when constructing expressions if len(repn.linear_vars) > 0: referenced_vars.update(repn.linear_vars) new_expr = self._xpress.Sum( float(coef) * self._pyomo_var_to_solver_var_map[var] for coef, var in zip(repn.linear_coefs, repn.linear_vars)) else: new_expr = 0.0 for coef, (x, y) in zip(repn.quadratic_coefs, repn.quadratic_vars): new_expr += float(coef) * self._pyomo_var_to_solver_var_map[ x] * self._pyomo_var_to_solver_var_map[y] referenced_vars.add(x) referenced_vars.add(y) new_expr += repn.constant return new_expr, referenced_vars
def _build_equality_set(model): """Construct an equality set map. Maps all variables to the set of variables that are linked to them by equality. Mapping takes place using id(). That is, if you have x = y, then you would have id(x) -> ComponentSet([x, y]) and id(y) -> ComponentSet([x, y]) in the mapping. """ # Map of variables to their equality set (ComponentSet) eq_var_map = ComponentMap() # Loop through all the active constraints in the model for constraint in model.component_data_objects(ctype=Constraint, active=True, descend_into=True): eq_linked_vars = _get_equality_linked_variables(constraint) if not eq_linked_vars: continue # if we get an empty tuple, skip to next constraint. v1, v2 = eq_linked_vars set1 = eq_var_map.get(v1, ComponentSet((v1, v2))) set2 = eq_var_map.get(v2, (v2, )) # if set1 and set2 are equivalent, skip to next constraint. if set1 is set2: continue # add all elements of set2 to set 1 set1.update(set2) # Update all elements to point to set 1 for v in set1: eq_var_map[v] = set1 return eq_var_map
def _get_expr_from_pyomo_repn(self, repn, max_degree=2): referenced_vars = ComponentSet() degree = repn.polynomial_degree() if (degree is None) or (degree > max_degree): raise DegreeError( 'GurobiDirect does not support expressions of degree {0}.'. format(degree)) if len(repn.linear_vars) > 0: referenced_vars.update(repn.linear_vars) new_expr = self._gurobipy.LinExpr(repn.linear_coefs, [ self._pyomo_var_to_solver_var_map[i] for i in repn.linear_vars ]) else: new_expr = 0.0 for i, v in enumerate(repn.quadratic_vars): x, y = v new_expr += repn.quadratic_coefs[ i] * self._pyomo_var_to_solver_var_map[ x] * self._pyomo_var_to_solver_var_map[y] referenced_vars.add(x) referenced_vars.add(y) new_expr += repn.constant return new_expr, referenced_vars
def _transformDisjunctionData(self, disjunction): # the sum of all the indicator variable values of disjuncts in the # disjunction logical_sum = sum( value(disj.indicator_var) for disj in disjunction.disjuncts) # Check that the disjunctions are not being fixed to an infeasible # realization. if disjunction.xor and not logical_sum == 1: # for XOR disjunctions, the sum of all disjunct values should be 1 raise GDP_Error( "Disjunction %s violated. " "Expected 1 disjunct to be active, but %s were active." % (disjunction.name, logical_sum)) elif not logical_sum >= 1: # for non-XOR disjunctions, the sum of all disjunct values should # be at least 1 raise GDP_Error("Disjunction %s violated. " "Expected at least 1 disjunct to be active, " "but none were active.") else: # disjunction is in feasible realization. Deactivate it. disjunction.deactivate() # Process the disjuncts associatd with the disjunction that have not # already been transformed. for disj in ComponentSet( disjunction.disjuncts) - self._transformedDisjuncts: self._transformDisjunctData(disj) # Update the set of transformed disjuncts with those from this # disjunction self._transformedDisjuncts.update(disjunction.disjuncts)
def test_clear(self): cset = ComponentSet() self.assertEqual(len(cset), 0) cset.update(self._components) self.assertEqual(len(cset), len(self._components)) cset.clear() self.assertEqual(len(cset), 0)
def _set_objective(self, obj): if self._objective is not None: for var in self._vars_referenced_by_obj: self._referenced_variables[var] -= 1 self._vars_referenced_by_obj = ComponentSet() self._objective = None if obj.active is False: raise ValueError('Cannot add inactive objective to solver.') if obj.sense == minimize: self._solver_model.putobjsense(self._mosek.objsense.minimize) elif obj.sense == maximize: self._solver_model.putobjsense(self._mosek.objsense.maximize) else: raise ValueError('Objective sense is not recognized: {0}'.format( obj.sense)) mosek_expr, referenced_vars = self._get_expr_from_pyomo_expr( obj.expr, self._max_obj_degree) for var in referenced_vars: self._referenced_variables[var] += 1 for i, j in enumerate(mosek_expr[1]): self._solver_model.putcj(j, mosek_expr[0][i]) self._solver_model.putqobj(mosek_expr[4], mosek_expr[5], mosek_expr[3]) self._solver_model.putcfix(mosek_expr[2]) self._objective = obj self._vars_referenced_by_obj = referenced_vars
def _get_expr_from_pyomo_repn(self, repn, max_degree=2): referenced_vars = ComponentSet() degree = repn.polynomial_degree() if (degree is None) or (degree > max_degree): raise DegreeError( 'Mosek does not support expressions of degree {0}.'.format( degree)) # if len(repn.linear_vars) > 0: referenced_vars.update(repn.linear_vars) indexes = [] [ indexes.append(self._pyomo_var_to_solver_var_map[i]) for i in repn.linear_vars ] new_expr = [list(repn.linear_coefs), indexes, repn.constant] qsubi = [] qsubj = [] qval = [] for i, v in enumerate(repn.quadratic_vars): x, y = v qsubj.append(self._pyomo_var_to_solver_var_map[x]) qsubi.append(self._pyomo_var_to_solver_var_map[y]) qval.append(repn.quadratic_coefs[i] * ((qsubi == qsubj) + 1)) referenced_vars.add(x) referenced_vars.add(y) new_expr.extend([qval, qsubi, qsubj]) return new_expr, referenced_vars