def _process_container(blk, config): if not hasattr(blk, '_induced_linearity_info'): blk._induced_linearity_info = Block() else: assert blk._induced_linearity_info.ctype == Block eff_discr_vars = detect_effectively_discrete_vars( blk, config.equality_tolerance) # TODO will need to go through this for each disjunct, since it does # not (should not) descend into Disjuncts. # Determine the valid values for the effectively discrete variables possible_var_values = determine_valid_values(blk, eff_discr_vars, config) # Collect find bilinear expressions that can be reformulated using # knowledge of effectively discrete variables bilinear_map = _bilinear_expressions(blk) # Relevant constraints are those with bilinear terms that involve # effectively_discrete_vars processed_pairs = ComponentSet() for v1, var_values in possible_var_values.items(): v1_pairs = bilinear_map.get(v1, ()) for v2, bilinear_constrs in v1_pairs.items(): if (v1, v2) in processed_pairs: continue _process_bilinear_constraints(blk, v1, v2, var_values, bilinear_constrs) processed_pairs.add((v2, v1))
def _generate_variables_in_constraints(constraints, include_fixed=False): known_vars = ComponentSet() for con in constraints: for var in identify_variables(con.expr, include_fixed=include_fixed): if var not in known_vars: known_vars.add(var) yield var
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 _get_expr_from_pyomo_repn(self, repn, max_degree=2): degree = repn.polynomial_degree() if degree is None or degree > max_degree: raise DegreeError( "CPLEXDirect does not support expressions of degree {0}.". format(degree)) referenced_vars = ComponentSet(repn.linear_vars) q_coefficients = [] q_variables1 = [] q_variables2 = [] for i, v in enumerate(repn.quadratic_vars): x, y = v q_coefficients.append(repn.quadratic_coefs[i]) q_variables1.append(self._pyomo_var_to_ndx_map[x]) q_variables2.append(self._pyomo_var_to_ndx_map[y]) referenced_vars.add(x) referenced_vars.add(y) return ( _CplexExpr( variables=[ self._pyomo_var_to_ndx_map[var] for var in repn.linear_vars ], coefficients=repn.linear_coefs, offset=repn.constant, q_variables1=q_variables1, q_variables2=q_variables2, q_coefficients=q_coefficients, ), referenced_vars, )
def _create_local_output_skeleton(model, sweep_params, outputs, num_samples): output_dict = {} output_dict["sweep_params"] = {} output_dict["outputs"] = {} sweep_param_objs = ComponentSet() # Store the inputs for sweep_param in sweep_params.values(): var = sweep_param.pyomo_object sweep_param_objs.add(var) output_dict["sweep_params"][ var.name] = _create_component_output_skeleton(var, num_samples) if outputs is None: # No outputs are specified, so every Var, Expression, and Objective on the model should be saved for pyo_obj in model.component_data_objects( (pyo.Var, pyo.Expression, pyo.Objective), active=True): # Only need to save this variable if it isn't one of the value in sweep_params if pyo_obj not in sweep_param_objs: output_dict["outputs"][ pyo_obj.name] = _create_component_output_skeleton( pyo_obj, num_samples) else: # Save only the outputs specified in the outputs dictionary for short_name, pyo_obj in outputs.items(): output_dict["outputs"][ short_name] = _create_component_output_skeleton( pyo_obj, num_samples) return output_dict
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
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 = 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 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 large_residuals_set(block, tol=1e-5, return_residual_values=False): """ Method to return a ComponentSet of all Constraint components with a residual greater than a given threshold which appear in a model. Args: block : model to be studied tol : residual threshold for inclusion in ComponentSet return_residual_values: boolean, if true return dictionary with residual values Returns: large_residual_set: A ComponentSet including all Constraint components with a residual greater than tol which appear in block (if return_residual_values is false) residual_values: dictionary with constraint as key and residual (float) as value (if return_residual_values is true) """ large_residuals_set = ComponentSet() if return_residual_values: residual_values = dict() for c in block.component_data_objects(ctype=Constraint, active=True, descend_into=True): r = 0.0 # residual # skip if no lower bound set if c.lower is None: r_temp = 0 else: r_temp = value(c.lower - c.body()) # update the residual if r_temp > r: r = r_temp # skip if no upper bound set if c.upper is None: r_temp = 0 else: r_temp = value(c.body() - c.upper) # update the residual if r_temp > r: r = r_temp # save residual if it is above threshold if r > tol: large_residuals_set.add(c) if return_residual_values: residual_values[c] = r if return_residual_values: return residual_values else: return large_residuals_set
def free_variables_in_active_equalities_set(blk): """ Return a set of variables that are contined in active equalities. """ vin = ComponentSet() for c in active_equalities(blk): for v in identify_variables(c.body): if not v.fixed: vin.add(v) return vin
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 _check_var_bounds(m: _BlockData, too_large: float): vars_without_bounds = ComponentSet() vars_with_large_bounds = ComponentSet() for v in m.component_data_objects(pyo.Var, descend_into=True): if v.is_fixed(): continue if v.lb is None or v.ub is None: vars_without_bounds.add(v) elif v.lb <= -too_large or v.ub >= too_large: vars_with_large_bounds.add(v) return vars_without_bounds, vars_with_large_bounds
def exitNode(self, node, values): node = super().exitNode(node, values) if node.__class__ is not EXPR.ExternalFunctionExpression: return node if id(node._fcn) not in self.efSet: return node # At this point we know this is an ExternalFunctionExpression # node that we want to replace with an auliliary variable (y) new_args = [] seen = ComponentSet() # TODO: support more than PythonCallbackFunctions assert isinstance(node._fcn, PythonCallbackFunction) # # Note: the first argument to PythonCallbackFunction is the # function ID. Since we are going to complain about constant # parameters, we need to skip the first argument when processing # the argument list. This is really not good: we should allow # for constant arguments to the functions, and we should relax # the restriction that the external functions implement the # PythonCallbackFunction API (that restriction leads unfortunate # things later; i.e., accessing the private _fcn attribute # below). for arg in values[1][1:]: if type(arg) in nonpyomo_leaf_types or arg.is_fixed(): # We currently do not allow constants or parameters for # the external functions. raise RuntimeError( "TrustRegion does not support black boxes with " "constant or parameter inputs\n\tExpression: %s" % (node, )) if arg.is_expression_type(): # All expressions (including simple linear expressions) # are replaced with a single auxiliary variable (and # eventually an additional constraint equating the # auxiliary variable to the original expression) _x = self.trf.x.add() _x.set_value(value(arg)) self.trf.conset.add(_x == arg) new_args.append(_x) else: # The only thing left is bare variables: check for duplicates. if arg in seen: raise RuntimeError( "TrustRegion does not support black boxes with " "duplicate input arguments\n\tExpression: %s" % (node, )) seen.add(arg) new_args.append(arg) _y = self.trf.y.add() self.trf.external_fcns.append(node) self.trf.exfn_xvars.append(new_args) return _y
def test_pop(self): cset = ComponentSet() self.assertEqual(len(cset), 0) with self.assertRaises(KeyError): cset.pop() v = variable() cset.add(v) self.assertTrue(v in cset) self.assertEqual(len(cset), 1) v_ = cset.pop() self.assertIs(v, v_) self.assertTrue(v not in cset) self.assertEqual(len(cset), 0)
def pass_edges(self, G, edges): """Call pass values for a list of edge indexes""" fixed_outputs = ComponentSet() edge_list = self.idx_to_edge(G) for ei in edges: arc = G.edges[edge_list[ei]]["arc"] for var in arc.src.iter_vars(expr_vars=True, fixed=False): fixed_outputs.add(var) var.fix() self.pass_values(arc, self.fixed_inputs()) for var in fixed_outputs: var.free() fixed_outputs.clear()
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 _get_disaggregated_vars(self, hull): disaggregatedVars = ComponentSet() hull_xform = TransformationFactory('gdp.hull') for disjunction in hull.component_data_objects( Disjunction, descend_into=(Disjunct, Block)): for disjunct in disjunction.disjuncts: if disjunct.transformation_block is not None: transBlock = disjunct.transformation_block() for v in transBlock.disaggregatedVars.\ component_data_objects(Var): disaggregatedVars.add(v) return disaggregatedVars
def set_add(self): cset = ComponentSet() self.assertEqual(len(cset), 0) for i, c in enumerate(self._components): self.assertTrue(c not in cset) cset.add(c) self.assertTrue(c in cset) self.assertEqual(len(cset), i + 1) self.assertEqual(len(cset), len(self._components)) for c in self._components: self.assertTrue(c in cset) cset.add(c) self.assertTrue(c in cset) self.assertEqual(len(cset), len(self._components))
def test_degenerate_solid_phase_model(self): m = make_degenerate_solid_phase_model() variables = list(m.component_data_objects(pyo.Var)) constraints = list(m.component_data_objects(pyo.Constraint)) igraph = IncidenceGraphInterface() var_dmp, con_dmp = igraph.dulmage_mendelsohn(variables, constraints) underconstrained_vars = ComponentSet(m.flow_comp.values()) underconstrained_vars.add(m.flow) underconstrained_cons = ComponentSet(m.flow_eqn.values()) self.assertEqual(len(var_dmp[0] + var_dmp[1]), len(underconstrained_vars)) for var in var_dmp[0] + var_dmp[1]: self.assertIn(var, underconstrained_vars) self.assertEqual(len(con_dmp[2]), len(underconstrained_cons)) for con in con_dmp[2]: self.assertIn(con, underconstrained_cons) overconstrained_cons = ComponentSet(m.holdup_eqn.values()) overconstrained_cons.add(m.density_eqn) overconstrained_cons.add(m.sum_eqn) overconstrained_vars = ComponentSet(m.x.values()) overconstrained_vars.add(m.rho) self.assertEqual(len(var_dmp[2]), len(overconstrained_vars)) for var in var_dmp[2]: self.assertIn(var, overconstrained_vars) self.assertEqual(len(con_dmp[0] + con_dmp[1]), len(overconstrained_cons)) for con in con_dmp[0] + con_dmp[1]: self.assertIn(con, overconstrained_cons)
def constraints_in_True_disjuncts(model, config): """Yield constraints in disjuncts where the indicator value is set or fixed to True.""" for constr in model.component_data_objects(Constraint): yield constr observed_disjuncts = ComponentSet() for disjctn in model.component_data_objects(Disjunction): # get all the disjuncts in the disjunction. Check which ones are True. for disj in disjctn.disjuncts: if disj in observed_disjuncts: continue observed_disjuncts.add(disj) if fabs(disj.indicator_var.value - 1) <= config.integer_tolerance: for constr in disj.component_data_objects(Constraint): yield constr
def pass_tear_direct(self, G, tears): """Pass values across all tears in the given tear set""" fixed_outputs = ComponentSet() edge_list = self.idx_to_edge(G) for tear in tears: # fix everything then call pass values arc = G.edges[edge_list[tear]]["arc"] for var in arc.src.iter_vars(expr_vars=True, fixed=False): fixed_outputs.add(var) var.fix() self.pass_values(arc, fixed_inputs=self.fixed_inputs()) for var in fixed_outputs: var.free() fixed_outputs.clear()
def test_pickle(self): c = ComponentSet() self.assertEqual(len(c), 0) cup = pickle.loads( pickle.dumps(c)) self.assertIsNot(cup, c) self.assertEqual(len(cup), 0) v = variable() c.add(v) self.assertEqual(len(c), 1) self.assertTrue(v in c) cup = pickle.loads( pickle.dumps(c)) vup = cup.pop() cup.add(vup) self.assertIsNot(cup, c) self.assertIsNot(vup, v) self.assertEqual(len(cup), 1) self.assertTrue(vup in cup) self.assertEqual(vup.parent, None) b = block() V = b.V = variable_list() b.V.append(v) b.c = c self.assertEqual(len(c), 1) self.assertTrue(v in c) self.assertIs(v.parent, b.V) self.assertIs(V.parent, b) self.assertIs(b.parent, None) bup = pickle.loads( pickle.dumps(b)) Vup = bup.V vup = Vup[0] cup = bup.c self.assertIsNot(cup, c) self.assertIsNot(vup, v) self.assertIsNot(Vup, V) self.assertIsNot(bup, b) self.assertEqual(len(cup), 1) self.assertTrue(vup in cup) self.assertIs(vup.parent, Vup) self.assertIs(Vup.parent, bup) self.assertIs(bup.parent, None) self.assertEqual(len(c), 1) self.assertTrue(v in c)
def _detect_fixed_variables(m): """Detect fixed variables due to constraints of form var = const.""" new_fixed_vars = ComponentSet() for constr in m.component_data_objects(ctype=Constraint, active=True, descend_into=True): if constr.equality and constr.body.polynomial_degree() == 1: repn = generate_standard_repn(constr.body) if len(repn.linear_vars) == 1 and repn.linear_coefs[0]: var = repn.linear_vars[0] coef = float(repn.linear_coefs[0]) const = repn.constant var_val = (value(constr.lower) - value(const)) / coef var.fix(var_val) new_fixed_vars.add(var) return new_fixed_vars
def vars_to_eliminate_list(x): if isinstance(x, (Var, _VarData)): if not x.is_indexed(): return ComponentSet([x]) ans = ComponentSet() for j in x.index_set(): ans.add(x[j]) return ans elif hasattr(x, '__iter__'): ans = ComponentSet() for i in x: ans.update(vars_to_eliminate_list(i)) return ans else: raise ValueError("Expected Var or list of Vars." "\n\tRecieved %s" % type(x))
def fixed_variables_only_in_inequalities(block): """ Method to return a ComponentSet of all fixed Var components which appear only within activated inequality Constraints in a model. Args: block : model to be studied Returns: A ComponentSet including all fixed Var components which appear only within activated inequality Constraints in block """ var_set = ComponentSet() for v in variables_only_in_inequalities(block): if v.fixed: var_set.add(v) return var_set
def variables_in_activated_inequalities_set(block): """ Method to return a ComponentSet of all Var components which appear within an inequality Constraint in a model. Args: block : model to be studied Returns: A ComponentSet including all Var components which appear within activated inequality Constraints in block """ var_set = ComponentSet() for c in activated_inequalities_generator(block): for v in identify_variables(c.body): var_set.add(v) return var_set
def total_blocks_set(block): """ Method to return a ComponentSet of all Block components in a model. Args: block : model to be studied Returns: A ComponentSet including all Block components in block (including block itself) """ total_blocks_set = ComponentSet( block.component_data_objects(ctype=Block, active=None, descend_into=True)) total_blocks_set.add(block) return total_blocks_set
def fixed_unused_variables_set(block): """ Method to return a ComponentSet of all fixed Var components which do not appear within any activated Constraint in a model. Args: block : model to be studied Returns: A ComponentSet including all fixed Var components which do not appear within any Constraints in block """ var_set = ComponentSet() for v in unused_variables_set(block): if v.fixed: var_set.add(v) return var_set
def active_variables_in_deactivated_blocks_set(block): """ Method to return a ComponentSet of any Var components which appear within an active Constraint but belong to a deacitvated Block in a model. Args: block : model to be studied Returns: A ComponentSet including any Var components which belong to a deacitvated Block but appear in an activate Constraint in block """ var_set = ComponentSet() block_set = activated_blocks_set(block) for v in variables_in_activated_constraints_set(block): if v.parent_block() not in block_set: var_set.add(v) return var_set
def variables_in_activated_constraints_set(block): """ Method to return a ComponentSet of all Var components which appear within a Constraint in a model. Args: block : model to be studied Returns: A ComponentSet including all Var components which appear within activated Constraints in block """ var_set = ComponentSet() for c in block.component_data_objects(ctype=Constraint, active=True, descend_into=True): for v in identify_variables(c.body): var_set.add(v) return var_set