def _generate_model(self): self.model = ConcreteModel() model = self.model model._name = self.description model.s = Set(initialize=[1,2]) model.x = Var() model.y = Var() model.z = Var(bounds=(0,None)) model.obj = Objective(model.s, rule=inactive_index_LP_obj_rule) model.OBJ = Objective(expr=model.x+model.y) model.obj[1].deactivate() model.OBJ.deactivate() model.c1 = ConstraintList() model.c1.add(model.x<=1) # index=1 model.c1.add(model.x>=-1) # index=2 model.c1.add(model.y<=1) # index=3 model.c1.add(model.y>=-1) # index=4 model.c1[1].deactivate() model.c1[4].deactivate() model.c2 = Constraint(model.s, rule=inactive_index_LP_c2_rule) model.b = Block() model.b.c = Constraint(expr=model.z >= 2) model.B = Block(model.s) model.B[1].c = Constraint(expr=model.z >= 3) model.B[2].c = Constraint(expr=model.z >= 1) model.b.deactivate() model.B.deactivate() model.B[2].activate()
def EXTERNAL_initialize_for_admm(manager, scenario): if manager.get_option("verbose"): print("Initializing scenario %s for admm algorithm" % (scenario.name)) admm_block = Block(concrete=True) assert not hasattr(scenario._instance, ".admm") scenario._instance.add_component(".admm", admm_block) # Augment the objective with lagrangian and penalty terms # and weight the original objective by the scenario probability. # The langrangian and penalty terms will be computed after # the parameters are created. user_cost_expression = scenario._instance_cost_expression admm_block.cost_expression = Expression(initialize=\ scenario.probability * user_cost_expression) admm_block.lagrangian_expression = Expression(initialize=0.0) admm_block.penalty_expression = Expression(initialize=0.0) # these are used in the objective, they can be toggled # between the expression above or something else (e.g., 0.0) admm_block.cost_term = Expression(initialize=admm_block.cost_expression) admm_block.lagrangian_term = Expression( initialize=admm_block.lagrangian_expression) admm_block.penalty_term = Expression( initialize=admm_block.penalty_expression) objective_direction = 1 if manager.objective_sense == maximize: objective_direction = -1 scenario._instance_objective.expr = \ admm_block.cost_term + \ admm_block.lagrangian_term * objective_direction + \ admm_block.penalty_term * objective_direction # add objective parameters to admm block for tree_node in scenario.node_list[:-1]: assert not tree_node.is_leaf_node() node_block = Block(concrete=True) admm_block.add_component(tree_node.name, node_block) node_block.node_index_set = Set(ordered=True, initialize=sorted( tree_node._standard_variable_ids)) node_block.z = Param(node_block.node_index_set, initialize=0.0, mutable=True) node_block.y = Param(node_block.node_index_set, initialize=0.0, mutable=True) node_block.rho = Param(node_block.node_index_set, initialize=0.0, mutable=True) for id_ in node_block.node_index_set: varname, index = tree_node._variable_ids[id_] var = scenario._instance.find_component(varname)[index] admm_block.lagrangian_expression.expr += \ node_block.y[id_] * (var - node_block.z[id_]) admm_block.penalty_expression.expr += \ (node_block.rho[id_] / 2.0) * (var - node_block.z[id_])**2 # The objective has changed so flag this if necessary. if manager.preprocessor is not None: manager.preprocessor.objective_updated[scenario.name] = True
def test_nested_blocks(self): """Test with nested blocks.""" m = ConcreteModel() m.b = Block() m.inactive_b = Block() m.inactive_b.deactivate() m.b.x = Var() m.b.x2 = Var(domain=Binary) m.b.x3 = Var(domain=Integers) m.inactive_b.x = Var() m.b.c = Constraint(expr=m.b.x == m.b.x2) m.inactive_b.c = Constraint(expr=m.b.x == 1) m.inactive_b.c2 = Constraint(expr=m.inactive_b.x == 15) model_size = build_model_size_report(m) self.assertEqual(model_size.activated.variables, 2) self.assertEqual(model_size.overall.variables, 4) self.assertEqual(model_size.activated.binary_variables, 1) self.assertEqual(model_size.overall.binary_variables, 1) self.assertEqual(model_size.activated.integer_variables, 0) self.assertEqual(model_size.overall.integer_variables, 1) self.assertEqual(model_size.activated.constraints, 1) self.assertEqual(model_size.overall.constraints, 3) self.assertEqual(model_size.activated.disjuncts, 0) self.assertEqual(model_size.overall.disjuncts, 0) self.assertEqual(model_size.activated.disjunctions, 0) self.assertEqual(model_size.overall.disjunctions, 0)
def makeNetworkDisjunction(minimize=True): """ creates a GDP model with pyomo.network components """ m = ConcreteModel() m.feed = feed = Block() m.wkbx = wkbx = Block() m.dest = dest = Block() m.orange = orange = Disjunct() m.blue = blue = Disjunct() m.orange_or_blue = Disjunction(expr=[orange,blue]) blue.blue_box = blue_box = Block() feed.x = Var(bounds=(0,1)) wkbx.x = Var(bounds=(0,1)) dest.x = Var(bounds=(0,1)) wkbx.inlet = ntwk.Port(initialize={"x":wkbx.x}) wkbx.outlet = ntwk.Port(initialize={"x":wkbx.x}) feed.outlet = ntwk.Port(initialize={"x":feed.x}) dest.inlet = ntwk.Port(initialize={"x":dest.x}) blue_box.x = Var(bounds=(0,1)) blue_box.x_wkbx = Var(bounds=(0,1)) blue_box.x_dest = Var(bounds=(0,1)) blue_box.inlet_feed = ntwk.Port(initialize={"x":blue_box.x}) blue_box.outlet_wkbx = ntwk.Port(initialize={"x":blue_box.x}) blue_box.inlet_wkbx = ntwk.Port(initialize={"x":blue_box.x_wkbx}) blue_box.outlet_dest = ntwk.Port(initialize={"x":blue_box.x_dest}) blue_box.multiplier_constr = Constraint(expr=blue_box.x_dest == \ 2*blue_box.x_wkbx) # orange arcs orange.a1 = ntwk.Arc(source=feed.outlet, destination=wkbx.inlet) orange.a2 = ntwk.Arc(source=wkbx.outlet, destination=dest.inlet) # blue arcs blue.a1 = ntwk.Arc(source=feed.outlet, destination=blue_box.inlet_feed) blue.a2 = ntwk.Arc(source=blue_box.outlet_wkbx, destination=wkbx.inlet) blue.a3 = ntwk.Arc(source=wkbx.outlet, destination=blue_box.inlet_wkbx) blue.a4 = ntwk.Arc(source=blue_box.outlet_dest, destination=dest.inlet) # maximize/minimize "production" if minimize: m.obj = Objective(expr=m.dest.x) else: m.obj = Objective(expr=m.dest.x, sense=maximize) # create a completely fixed model feed.x.fix(0.42) return m
def test_getname_error(self): m = ConcreteModel() m.b = Block() m.b.v = Var() m.c = Block() self.assertRaises(RuntimeError, m.b.v.getname, fully_qualified=True, relative_to=m.c)
def _add_transformation_block(self, instance): # make a transformation block on instance to put transformed disjuncts # on transBlockName = unique_component_name(instance, '_pyomo_gdp_bigm_relaxation') transBlock = Block() instance.add_component(transBlockName, transBlock) transBlock.relaxedDisjuncts = Block(NonNegativeIntegers) transBlock.lbub = Set(initialize=['lb', 'ub']) return transBlock
def disj_rule(d, flag): m = d.model() if flag: d.b = Block() d.b.c = Constraint(expr=m.x == 0) d.add_component('b.c', Constraint(expr=m.y >= 9)) d.b.anotherblock = Block() d.b.anotherblock.c = Constraint(expr=m.y >= 11) d.bb = Block([1]) d.bb[1].c = Constraint(expr=m.x == 0) else: d.c = Constraint(expr=m.x >= 80)
def makeHierarchicalNested_DeclOrderMatchesInstantationOrder(): """Here, we put the disjunctive components on Blocks, but we do it in the same order that we declared the blocks, that is, on each block, decl order matches instantiation order.""" m = ConcreteModel() m.I = RangeSet(1, 4) m.x = Var(m.I, bounds=(-2, 6)) m.disjunct_block = Block() m.disjunction_block = Block() instantiate_hierarchical_nested_model(m) return m
def makeHierarchicalNested_DeclOrderOppositeInstantationOrder(): """Here, we declare the Blocks in the opposite order. This means that decl order will be *opposite* instantiation order, which means that we can break our targets preprocessing without even using targets if we are not correctly identifying what is nested in what!""" m = ConcreteModel() m.I = RangeSet(1, 4) m.x = Var(m.I, bounds=(-2, 6)) m.disjunction_block = Block() m.disjunct_block = Block() instantiate_hierarchical_nested_model(m) return m
def makeTwoTermDisj_IndexedConstraints(): m = ConcreteModel() m.s = Set(initialize=[1, 2]) m.a = Var(m.s) m.b = Block() def disj1_rule(disjunct): m = disjunct.model() def c_rule(d, s): return m.a[s] == 0 disjunct.c = Constraint(m.s, rule=c_rule) m.b.simpledisj1 = Disjunct(rule=disj1_rule) def disj2_rule(disjunct): m = disjunct.model() def c_rule(d, s): return m.a[s] <= 3 disjunct.c = Constraint(m.s, rule=c_rule) m.b.simpledisj2 = Disjunct(rule=disj2_rule) m.b.disjunction = Disjunction(expr=[m.b.simpledisj1, m.b.simpledisj2]) return m
def _apply_to(self, instance, **kwds): # TODO: This data should be stored differently. We cannot nest this transformation with itself if getattr(instance, 'bilinear_data_', None) is None: instance.bilinear_data_ = Block() instance.bilinear_data_.cache = {} instance.bilinear_data_.vlist = VarList() instance.bilinear_data_.vlist_boolean = [] instance.bilinear_data_.IDX = Set() instance.bilinear_data_.disjuncts_ = Disjunct( instance.bilinear_data_.IDX * [0, 1]) instance.bilinear_data_.disjunction_data = {} instance.bilinear_data_.o_expr = {} instance.bilinear_data_.c_body = {} # # Iterate over all blocks # for block in instance.block_data_objects( active=True, sort=SortComponents.deterministic): self._transformBlock(block, instance) # # WEH: I wish I had a DisjunctList and DisjunctionList object... # def rule(block, i): return instance.bilinear_data_.disjunction_data[i] instance.bilinear_data_.disjunction_ = Disjunction( instance.bilinear_data_.IDX, rule=rule)
def test_rules_with_None_in_set(self): def noarg_rule(b): b.args = () def onearg_rule(b, i): b.args = (i,) def twoarg_rule(b, i, j): b.args = (i,j) m = ConcreteModel() m.b1 = Block(rule=noarg_rule) self.assertEqual(m.b1.args, ()) m.b2 = Block([None], rule=onearg_rule) self.assertEqual(m.b2[None].args, (None,)) m.b3 = Block([(None,1)], rule=twoarg_rule) self.assertEqual(m.b3[None,1].args, ((None,1)))
def reformulate_integer_variables(model, config): integer_vars = list(v for v in model.component_data_objects( ctype=Var, descend_into=(Block, Disjunct)) if v.is_integer() and not v.fixed) if len(integer_vars) == 0: return # if no free integer variables, no reformulation needed. if config.reformulate_integer_vars_using is None: config.logger.warning( "Model contains unfixed integer variables. " "GDPopt will reformulate using base 2 binary variables " "by default. To specify a different method, see the " "reformulate_integer_vars_using configuration option.") config.reformulate_integer_vars_using = 'base2_binary' config.logger.info( "Reformulating integer variables using the %s strategy." % config.reformulate_integer_vars_using) # Set up reformulation block reform_block = model.GDPopt_utils.integer_reform = Block( doc="Holds variables and constraints for reformulating " "integer variables to binary variables.") reform_block.new_binary_var = Var( Any, domain=Binary, dense=False, doc="Binary variable with index (int_var.name, indx)") reform_block.integer_to_binary_constraint = Constraint( Any, doc="Equality constraints mapping the binary variable values " "to the integer variable value.") # check that variables are bounded and non-negative for int_var in integer_vars: if not (int_var.has_lb() and int_var.has_ub()): raise ValueError( "Integer variable %s is missing an " "upper or lower bound. LB: %s; UB: %s. " "GDPopt does not support unbounded integer variables." % (int_var.name, int_var.lb, int_var.ub)) if int_var.lb < 0: raise ValueError("Integer variable %s can be negative. " "GDPopt currently only supports positive integer " "variables." % (int_var.name)) # do the reformulation highest_power = floor(log(value(int_var.ub), 2)) var_name = int_var.name reform_block.integer_to_binary_constraint.add( var_name, expr=int_var == sum(reform_block.new_binary_var[var_name, pwr] * (2**pwr) for pwr in range(0, int(highest_power) + 1))) int_var.domain = NonNegativeReals config.logger.info("Reformulated %s integer variables using " "%s binary variables and %s constraints." % (len(integer_vars), len(reform_block.new_binary_var), len(reform_block.integer_to_binary_constraint)))
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|. 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 norm infinity 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 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 = ConcreteModel() model = self.model model._name = self.description model.b = Block() model.B = Block([1,2,3]) model.a = Param(initialize=1.0, mutable=True) model.b.x = Var(within=NonNegativeReals) model.B[1].x = Var(within=NonNegativeReals) model.obj = Objective(expr=model.b.x + 3.0*model.B[1].x) model.obj.deactivate() model.B[2].c = Constraint(expr=-model.B[1].x <= -model.a) model.B[2].obj = Objective(expr=model.b.x + 3.0*model.B[1].x + 2) model.B[3].c = Constraint(expr=2.0 <= model.b.x/model.a - model.B[1].x <= 10)
def create_submodel_hp_block(instance): """ Creates highpoint relaxation with the given specified model; does not include any submodel or block that is deactivated """ block = Block(concrete=True) # get the objective for the master problem for c in instance.component_objects(Objective, descend_into=False): block.add_component(c.name, Reference(c)) # get the variables of the model (if there are more submodels, then # extraneous variables may be added to the block) for c in instance.component_objects(Var, sort=True, descend_into=True, active=True): block.add_component(c.name, Reference(c)) # get the constraints from the main model for c in instance.component_objects(Constraint, sort=True, descend_into=True, active=True): block.add_component(c.name, Reference(c)) # deactivate the highpoint relaxation block.deactivate() return block
def add_affine_cuts(nlp_result, solve_data, config): with time_code(solve_data.timing, "affine cut generation"): m = solve_data.linear_GDP if config.calc_disjunctive_bounds: with time_code(solve_data.timing, "disjunctive variable bounding"): TransformationFactory( 'contrib.compute_disj_var_bounds').apply_to( m, solver=config.mip_solver if config.obbt_disjunctive_bounds else None) config.logger.info("Adding affine cuts.") GDPopt = m.GDPopt_utils counter = 0 for var, val in zip(GDPopt.variable_list, nlp_result.var_values): if val is not None and not var.fixed: var.value = val for constr in constraints_in_True_disjuncts(m, config): # Note: this includes constraints that are deactivated in the current model (linear_GDP) disjunctive_var_bounds = disjunctive_bounds(constr.parent_block()) if constr.body.polynomial_degree() in (1, 0): continue vars_in_constr = list(identify_variables(constr.body)) if any(var.value is None for var in vars_in_constr): continue # a variable has no values # mcpp stuff mc_eqn = mc(constr.body, disjunctive_var_bounds) # mc_eqn = mc(constr.body) ccSlope = mc_eqn.subcc() cvSlope = mc_eqn.subcv() ccStart = mc_eqn.concave() cvStart = mc_eqn.convex() ub_int = min( constr.upper, mc_eqn.upper()) if constr.has_ub() else mc_eqn.upper() lb_int = max( constr.lower, mc_eqn.lower()) if constr.has_lb() else mc_eqn.lower() parent_block = constr.parent_block() # Create a block on which to put outer approximation cuts. aff_utils = parent_block.component('GDPopt_aff') if aff_utils is None: aff_utils = parent_block.GDPopt_aff = Block( doc="Block holding affine constraints") aff_utils.GDPopt_aff_cons = ConstraintList() aff_cuts = aff_utils.GDPopt_aff_cons concave_cut = sum(ccSlope[var] * (var - var.value) for var in vars_in_constr) + ccStart >= lb_int convex_cut = sum(cvSlope[var] * (var - var.value) for var in vars_in_constr) + cvStart <= ub_int aff_cuts.add(expr=concave_cut) aff_cuts.add(expr=convex_cut) counter += 2 config.logger.info("Added %s affine cuts" % counter)
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 makeTwoTermDisj_IndexedConstraints(): """Single two-term disjunction with IndexedConstraints on both disjuncts. Does not bound the variables, so cannot be transformed by hull at all and requires specifying m values in bigm. """ m = ConcreteModel() m.s = Set(initialize=[1, 2]) m.a = Var(m.s) m.b = Block() def disj1_rule(disjunct): m = disjunct.model() def c_rule(d, s): return m.a[s] == 0 disjunct.c = Constraint(m.s, rule=c_rule) m.b.simpledisj1 = Disjunct(rule=disj1_rule) def disj2_rule(disjunct): m = disjunct.model() def c_rule(d, s): return m.a[s] <= 3 disjunct.c = Constraint(m.s, rule=c_rule) m.b.simpledisj2 = Disjunct(rule=disj2_rule) m.b.disjunction = Disjunction(expr=[m.b.simpledisj1, m.b.simpledisj2]) return m
def test_gdp_tree(self): m = ConcreteModel() m.x = Var() m.block = Block() m.block.d1 = Disjunct() m.block.d1.dd1 = Disjunct() m.disj1 = Disjunct() m.block.disjunction = Disjunction(expr=[m.block.d1, m.disj1]) m.block.d1.b = Block() m.block.d1.b.dd2 = Disjunct() m.block.d1.b.dd3 = Disjunct() m.block.d1.disjunction = Disjunction( expr=[m.block.d1.dd1, m.block.d1.b.dd2, m.block.d1.b.dd3]) m.block.d1.b.dd2.disjunction = Disjunction( expr=[[m.x >= 1], [m.x <= -1]]) targets = (m, ) knownBlocks = {} tree = get_gdp_tree(targets, m, knownBlocks) # check tree structure first vertices = tree.vertices self.assertEqual(len(vertices), 10) in_degrees = { m.block.d1: 1, m.block.disjunction: 0, m.disj1: 1, m.block.d1.disjunction: 1, m.block.d1.dd1: 1, m.block.d1.b.dd2: 1, m.block.d1.b.dd3: 1, m.block.d1.b.dd2.disjunction: 1, m.block.d1.b.dd2.disjunction.disjuncts[0]: 1, m.block.d1.b.dd2.disjunction.disjuncts[1]: 1 } for key, val in in_degrees.items(): self.assertEqual(tree.in_degree(key), val) # This should be deterministic, so we can just check the order topo_sort = [ m.block.disjunction, m.disj1, m.block.d1, m.block.d1.disjunction, m.block.d1.b.dd3, m.block.d1.b.dd2, m.block.d1.b.dd2.disjunction, m.block.d1.b.dd2.disjunction.disjuncts[1], m.block.d1.b.dd2.disjunction.disjuncts[0], m.block.d1.dd1 ] sort = tree.topological_sort() for i, node in enumerate(sort): self.assertIs(node, topo_sort[i])
def _add_relaxation_block(self, instance, name): # creates transformation block with a unique name based on name, adds it # to instance, and returns it. transBlockName = unique_component_name( instance, '_pyomo_gdp_cuttingplane_relaxation') transBlock = Block() instance.add_component(transBlockName, transBlock) return transBlockName, transBlock
def test_active_parent_block(self): m = ConcreteModel() m.d1 = Block() m.d1.sub1 = Disjunct() m.d1.sub2 = Disjunct() m.d1.disj = Disjunction(expr=[m.d1.sub1, m.d1.sub2]) with self.assertRaises(GDP_Error): TransformationFactory('gdp.reclassify').apply_to(m)
def _transfer_var_references(self, fromBlock, toBlock): disjunctList = toBlock.relaxedDisjuncts for idx, disjunctBlock in iteritems(fromBlock.relaxedDisjuncts): # move all the of the local var references newblock = disjunctList[len(disjunctList)] newblock.localVarReferences = Block() newblock.localVarReferences.transfer_attributes_from( disjunctBlock.localVarReferences)
def make_tiny_model_where_bounds_matter(self): m = ConcreteModel() m.b = Block() m.x = Var(bounds=(0, 15)) m.y = Var(bounds=(3, 5)) m.b.c = Constraint(expr=m.x + m.y <= 8) return m
def piecewise_nd(tri: qhull.Delaunay, values: List[float], input: List[SimpleVar] = None, output: SimpleVar = None, bound: str = 'eq', repn: str = 'cc', parent=None, **kw): """ 添加多维线性插值,values 必须是float类型的浮点数,不能是np的 tri (scipy.spatial.Delaunay): A triangulation over the discretized variable domain. Can be generated using a list of variables using the utility function :func:`util.generate_delaunay`. Required attributes: - points: An (npoints, D) shaped array listing the D-dimensional coordinates of the discretization points. - simplices: An (nsimplices, D+1) shaped array of integers specifying the D+1 indices of the points vector that define each simplex of the triangulation. values (numpy.array): An (npoints,) shaped array of the values of the piecewise function at each of coordinates in the triangulation points array. input: A D-length list of variables or expressions bound as the inputs of the piecewise function. output: The variable constrained to be the output of the piecewise linear function. bound (str): The type of bound to impose on the output expression. Can be one of: - 'lb': y <= f(x) - 'eq': y = f(x) - 'ub': y >= f(x) repn (str): The type of piecewise representation to use. Can be one of: - 'cc': convex combination """ if repn == "cc": pf = cc elif repn == "dlog": pf = dlog elif repn == "log": pf = log elif repn == "log_lp": pf = log_lp else: raise RuntimeError(f'{repn} 不支持!') _f = partial(pf, tri=tri, values=values, input=input, output=output, bound=bound, **kw) if parent is None: # raise RuntimeError("parent 不能为空,必须提前声明好,否则无法添加到model中") m = Block(rule=_f) else: m = parent _f(m) return m
def _transform_disjunct(self, obj, transBlock, bigM, arg_list, suffix_list): # deactivated -> either we've already transformed or user deactivated if not obj.active: if obj.indicator_var.is_fixed(): if value(obj.indicator_var) == 0: # The user cleanly deactivated the disjunct: there # is nothing for us to do here. return else: raise GDP_Error( "The disjunct '%s' is deactivated, but the " "indicator_var is fixed to %s. This makes no sense." % ( obj.name, value(obj.indicator_var) )) if obj._transformation_block is None: raise GDP_Error( "The disjunct '%s' is deactivated, but the " "indicator_var is not fixed and the disjunct does not " "appear to have been relaxed. This makes no sense. " "(If the intent is to deactivate the disjunct, fix its " "indicator_var to 0.)" % ( obj.name, )) if obj._transformation_block is not None: # we've transformed it, which means this is the second time it's # appearing in a Disjunction raise GDP_Error( "The disjunct '%s' has been transformed, but a disjunction " "it appears in has not. Putting the same disjunct in " "multiple disjunctions is not supported." % obj.name) # add reference to original disjunct on transformation block relaxedDisjuncts = transBlock.relaxedDisjuncts relaxationBlock = relaxedDisjuncts[len(relaxedDisjuncts)] # we will keep a map of constraints (hashable, ha!) to a tuple to # indicate where their m value came from, either (arg dict, key) if it # came from args, (Suffix, key) if it came from Suffixes, or (M_lower, # M_upper) if we calcualted it ourselves. I am keeping it here because I # want it to move with the disjunct transformation blocks in the case of # nested constraints, to make it easier to query. relaxationBlock.bigm_src = {} relaxationBlock.localVarReferences = Block() obj._transformation_block = weakref_ref(relaxationBlock) relaxationBlock._srcDisjunct = weakref_ref(obj) # This is crazy, but if the disjunction has been previously # relaxed, the disjunct *could* be deactivated. This is a big # deal for Hull, as it uses the component_objects / # component_data_objects generators. For BigM, that is OK, # because we never use those generators with active=True. I am # only noting it here for the future when someone (me?) is # comparing the two relaxations. # # Transform each component within this disjunct self._transform_block_components(obj, obj, bigM, arg_list, suffix_list) # deactivate disjunct to keep the writers happy obj._deactivate_without_fixing_indicator()
def test_is_child_of(self): m = ConcreteModel() m.b = Block() m.b.b_indexed = Block([1,2]) m.b_parallel = Block() knownBlocks = {} self.assertFalse(is_child_of(parent=m.b, child=m.b_parallel, knownBlocks=knownBlocks)) self.assertEqual(len(knownBlocks), 2) self.assertFalse(knownBlocks.get(m)) self.assertFalse(knownBlocks.get(m.b_parallel)) self.assertTrue(is_child_of(parent=m.b, child=m.b.b_indexed[1], knownBlocks=knownBlocks)) self.assertEqual(len(knownBlocks), 4) self.assertFalse(knownBlocks.get(m)) self.assertFalse(knownBlocks.get(m.b_parallel)) self.assertTrue(knownBlocks.get(m.b.b_indexed[1])) self.assertTrue(knownBlocks.get(m.b.b_indexed))
def _get_block_model(self): model = ConcreteModel() model.s = Set(initialize=[1, 2]) b = Block(concrete=True) b.s = Set(initialize=[1, 2]) b.x = Var() b.X = Var(model.s) model.b1 = b.clone() model.b2 = b.clone() model.b3 = b.clone() model.b4 = b.clone() model.B1 = Block(model.s, rule=lambda _, i: b.clone()) model.B2 = Block(model.s, rule=lambda _, i: b.clone()) model.B3 = Block(model.s, rule=lambda _, i: b.clone()) model.B4 = Block(model.s, rule=lambda _, i: b.clone()) model.FirstStageCost = Expression(expr=0.0) model.SecondStageCost = Expression(expr=0.0) model.obj = Objective(expr=0.0) return model
def _create_transformation_block(self, context): new_xfrm_block_name = unique_component_name(context, 'logic_to_linear') new_xfrm_block = Block(doc="Transformation objects for logic_to_linear") setattr(context, new_xfrm_block_name, new_xfrm_block) new_xfrm_block.transformed_constraints = ConstraintList() new_xfrm_block.augmented_vars = BooleanVarList() new_xfrm_block.augmented_vars_asbinary = VarList( domain=Binary) return new_xfrm_block
def _add_transformation_block(self, instance): # make a transformation block on instance where we will store # transformed components transBlockName = unique_component_name( instance, '_pyomo_gdp_hull_reformulation') transBlock = Block() instance.add_component(transBlockName, transBlock) transBlock.relaxedDisjuncts = Block(NonNegativeIntegers) transBlock.lbub = Set(initialize=['lb', 'ub', 'eq']) # We will store all of the disaggregation constraints for any # Disjunctions we transform onto this block here. transBlock.disaggregationConstraints = Constraint( NonNegativeIntegers, Any) # This will map from srcVar to a map of srcDisjunction to the # disaggregation constraint corresponding to srcDisjunction transBlock._disaggregationConstraintMap = ComponentMap() return transBlock