def _apply_to(self, instance, **kwds): # # Setup transformation data # tdata = instance._transformation_data['mpec.simple_disjunction'] tdata.compl_cuids = [] # # Iterate over the model finding Complementarity components # for complementarity in instance.component_objects(Complementarity, active=True, descend_into=(Block, Disjunct), sort=SortComponents.deterministic): block = complementarity.parent_block() for index in sorted(complementarity.keys()): _data = complementarity[index] if not _data.active: continue # _e1 = _data._canonical_expression(_data._args[0]) _e2 = _data._canonical_expression(_data._args[1]) if len(_e1)==3 and len(_e2) == 3 and (_e1[0] is None) + (_e1[2] is None) + (_e2[0] is None) + (_e2[2] is None) != 2: raise RuntimeError("Complementarity condition %s must have exactly two finite bounds" % _data.name) if len(_e1) == 3 and _e1[0] is None and _e1[2] is None: # # Swap _e1 and _e2. The ensures that # only e2 will be an unconstrained expression # _e1, _e2 = _e2, _e1 if _e2[0] is None and _e2[2] is None: if len(_e1) == 2: _data.c = Constraint(expr=_e1) else: _data.expr1 = Disjunct() _data.expr1.c0 = Constraint(expr= _e1[0] == _e1[1]) _data.expr1.c1 = Constraint(expr= _e2[1] >= 0) # _data.expr2 = Disjunct() _data.expr2.c0 = Constraint(expr= _e1[1] == _e1[2]) _data.expr2.c1 = Constraint(expr= _e2[1] <= 0) # _data.expr3 = Disjunct() _data.expr3.c0 = Constraint(expr= inequality(_e1[0], _e1[1], _e1[2])) _data.expr3.c1 = Constraint(expr= _e2[1] == 0) _data.complements = Disjunction(expr=(_data.expr1, _data.expr2, _data.expr3)) else: if _e1[0] is None: tmp1 = _e1[2] - _e1[1] else: tmp1 = _e1[1] - _e1[0] if _e2[0] is None: tmp2 = _e2[2] - _e2[1] else: tmp2 = _e2[1] - _e2[0] _data.expr1 = Disjunct() _data.expr1.c0 = Constraint(expr= tmp1 >= 0) _data.expr1.c1 = Constraint(expr= tmp2 == 0) # _data.expr2 = Disjunct() _data.expr2.c0 = Constraint(expr= tmp1 == 0) _data.expr2.c1 = Constraint(expr= tmp2 >= 0) # _data.complements = Disjunction(expr=(_data.expr1, _data.expr2)) tdata.compl_cuids.append( ComponentUID(complementarity) ) block.reclassify_component_type(complementarity, Block)
# Deactivate the old disjunctions / disjuncts # for i in ans.DISJUNCTIONS: disjunctions[i].deactivate() for d in disjunctions[i].disjuncts: d._deactivate_without_fixing_indicator() return ans if __name__ == '__main__': from pyomo.environ import ConcreteModel, Constraint, Var m = ConcreteModel() def _d(d, i): d.x = Var(range(i)) d.silly = Constraint(expr=d.indicator_var == i) m.d = Disjunct([1, 2], rule=_d) def _e(e, i): e.y = Var(range(2, i)) m.e = Disjunct([3, 4, 5], rule=_e) m.dd = Disjunction(expr=[m.d[1], m.d[2]]) m.ee = Disjunction(expr=[m.e[3], m.e[4], m.e[5]]) m.Z = apply_basic_step([m.dd, m.ee]) m.pprint()
def apply_basic_step(disjunctions_or_constraints): # # Basic steps only apply to XOR'd disjunctions # disjunctions = list(obj for obj in disjunctions_or_constraints if obj.ctype == Disjunction) constraints = list(obj for obj in disjunctions_or_constraints if obj.ctype == Constraint) for d in disjunctions: if not d.xor: raise ValueError( "Basic steps can only be applied to XOR'd disjunctions\n\t" "(raised by disjunction %s)" % (d.name, )) if not d.active: logger.warning("Warning: applying basic step to a previously " "deactivated disjunction (%s)" % (d.name, )) ans = Block(concrete=True) ans.DISJUNCTIONS = Set(initialize=range(len(disjunctions))) ans.INDEX = Set(dimen=len(disjunctions), initialize=_squish_singletons( itertools.product(*tuple( range(len(d.disjuncts)) for d in disjunctions)))) # # Form the individual disjuncts for the new basic step # ans.disjuncts = Disjunct(ans.INDEX) for idx in ans.INDEX: # # Each source disjunct will be copied (cloned) into its own # subblock # ans.disjuncts[idx].src = Block(ans.DISJUNCTIONS) for i in ans.DISJUNCTIONS: tmp = _clone_all_but_indicator_vars( disjunctions[i].disjuncts[idx[i] if isinstance(idx, tuple ) else idx]) for k, v in list(tmp.component_map().items()): if v.parent_block() is not tmp: # Skip indicator_var and binary_indicator_var continue tmp.del_component(k) ans.disjuncts[idx].src[i].add_component(k, v) # Copy in the constraints corresponding to the improper disjunctions ans.disjuncts[idx].improper_constraints = ConstraintList() for constr in constraints: if constr.is_indexed(): for indx in constr: ans.disjuncts[idx].improper_constraints.add( (constr[indx].lower, constr[indx].body, constr[indx].upper)) constr[indx].deactivate() # need this so that we can take an improper basic step with a # ConstraintData else: ans.disjuncts[idx].improper_constraints.add( (constr.lower, constr.body, constr.upper)) constr.deactivate() # # Link the new disjunct indicator_var's to the original # indicator_var's. Since only one of the new # NAME_BUFFER = {} ans.indicator_links = ConstraintList() for i in ans.DISJUNCTIONS: for j in range(len(disjunctions[i].disjuncts)): orig_var = disjunctions[i].disjuncts[j].indicator_var orig_binary_var = orig_var.get_associated_binary() ans.indicator_links.add(orig_binary_var == sum( ans.disjuncts[idx].binary_indicator_var for idx in ans.INDEX if (idx[i] if isinstance(idx, tuple) else idx) == j)) # and throw on a Reference to original on the block for v in (orig_var, orig_binary_var): name_base = v.getname(fully_qualified=True, name_buffer=NAME_BUFFER) ans.add_component(unique_component_name(ans, name_base), Reference(v)) # Form the new disjunction ans.disjunction = Disjunction(expr=[ans.disjuncts[i] for i in ans.INDEX]) # # Deactivate the old disjunctions / disjuncts # for i in ans.DISJUNCTIONS: disjunctions[i].deactivate() for d in disjunctions[i].disjuncts: d._deactivate_without_fixing_indicator() return ans
def apply_basic_step(disjunctions_or_constraints): # # Basic steps only apply to XOR'd disjunctions # disjunctions = list(obj for obj in disjunctions_or_constraints if obj.ctype == Disjunction) constraints = list(obj for obj in disjunctions_or_constraints if obj.ctype == Constraint) for d in disjunctions: if not d.xor: raise ValueError( "Basic steps can only be applied to XOR'd disjunctions\n\t" "(raised by disjunction %s)" % (d.name, )) if not d.active: logger.warning("Warning: applying basic step to a previously " "deactivated disjunction (%s)" % (d.name, )) ans = Block(concrete=True) ans.DISJUNCTIONS = Set(initialize=xrange(len(disjunctions))) ans.INDEX = Set(dimen=len(disjunctions), initialize=_squish_singletons( itertools.product(*tuple( xrange(len(d.disjuncts)) for d in disjunctions)))) # # Form the individual disjuncts for the new basic step # ans.disjuncts = Disjunct(ans.INDEX) for idx in ans.INDEX: # # Each source disjunct will be copied (cloned) into its own # subblock # ans.disjuncts[idx].src = Block(ans.DISJUNCTIONS) for i in ans.DISJUNCTIONS: tmp = _pseudo_clone( disjunctions[i].disjuncts[idx[i] if isinstance(idx, tuple ) else idx]) for k, v in list(iteritems(tmp.component_map())): if k == 'indicator_var': continue tmp.del_component(k) ans.disjuncts[idx].src[i].add_component(k, v) # Copy in the constraints corresponding to the improper disjunctions ans.disjuncts[idx].improper_constraints = ConstraintList() for constr in constraints: for indx in constr: ans.disjuncts[idx].improper_constraints.add( (constr[indx].lower, constr[indx].body, constr[indx].upper)) constr[indx].deactivate() # # Link the new disjunct indicator_var's to the original # indicator_var's. Since only one of the new # ans.indicator_links = ConstraintList() for i in ans.DISJUNCTIONS: for j in xrange(len(disjunctions[i].disjuncts)): ans.indicator_links.add( disjunctions[i].disjuncts[j].indicator_var == sum( ans.disjuncts[idx].indicator_var for idx in ans.INDEX if (idx[i] if isinstance(idx, tuple) else idx) == j)) # Form the new disjunction ans.disjunction = Disjunction(expr=[ans.disjuncts[i] for i in ans.INDEX]) # # Deactivate the old disjunctions / disjuncts # for i in ans.DISJUNCTIONS: disjunctions[i].deactivate() for d in disjunctions[i].disjuncts: d._deactivate_without_fixing_indicator() return ans