def _transform_constraintData(self, logical_constraint, new_varlists, transBlocks): # first find all the relevant BooleanVars and associate a binary (if # they don't have one already) for bool_vardata in identify_variables(logical_constraint.expr): if bool_vardata.ctype is BooleanVar: self._transform_boolean_varData(bool_vardata, new_varlists) # now create a transformation block on the constraint's parent block (if # we don't have one already) parent_block = logical_constraint.parent_block() xfrm_block = transBlocks.get(parent_block) if xfrm_block is None: xfrm_block = self._create_transformation_block(parent_block) transBlocks[parent_block] = xfrm_block new_constrlist = xfrm_block.transformed_constraints new_boolvarlist = xfrm_block.augmented_vars new_varlist = xfrm_block.augmented_vars_asbinary old_boolvarlist_length = len(new_boolvarlist) indicator_map = ComponentMap() cnf_statements = to_cnf(logical_constraint.body, new_boolvarlist, indicator_map) logical_constraint.deactivate() # Associate new Boolean vars to new binary variables num_new = len(new_boolvarlist) - old_boolvarlist_length list_o_vars = list(new_boolvarlist.values()) if num_new: for bool_vardata in list_o_vars[-num_new:]: new_binary_vardata = new_varlist.add() bool_vardata.associate_binary_var(new_binary_vardata) # Add constraints associated with each CNF statement for cnf_statement in cnf_statements: for linear_constraint in _cnf_to_linear_constraint_list( cnf_statement): new_constrlist.add(expr=linear_constraint) # Add bigM associated with special atoms # Note: this ad-hoc reformulation may be revisited for tightness in the # future. old_varlist_length = len(new_varlist) for indicator_var, special_atom in indicator_map.items(): for linear_constraint in _cnf_to_linear_constraint_list( special_atom, indicator_var, new_varlist): new_constrlist.add(expr=linear_constraint) # Previous step may have added auxiliary binaries. Associate augmented # Booleans to them. num_new = len(new_varlist) - old_varlist_length list_o_vars = list(new_varlist.values()) if num_new: for binary_vardata in list_o_vars[-num_new:]: new_bool_vardata = new_boolvarlist.add() new_bool_vardata.associate_binary_var(binary_vardata)
def test_cnf(self): m = ConcreteModel() m.Y1 = BooleanVar() m.Y2 = BooleanVar() implication = implies(m.Y1, m.Y2) x = to_cnf(implication)[0] _check_equivalent(self, implication, x) atleast_expr = atleast(1, m.Y1, m.Y2) x = to_cnf(atleast_expr)[0] self.assertIs(atleast_expr, x) # should be no change nestedatleast = implies(m.Y1, atleast_expr) m.extraY = BooleanVarList() indicator_map = ComponentMap() x = to_cnf(nestedatleast, m.extraY, indicator_map) self.assertEqual(str(x[0]), "extraY[1] ∨ ~Y1") self.assertIs(indicator_map[m.extraY[1]], atleast_expr)
def _process_logical_constraints_in_logical_context(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_constrlist = new_xfrm_block.transformed_constraints = ConstraintList() new_boolvarlist = new_xfrm_block.augmented_vars = BooleanVarList() new_varlist = new_xfrm_block.augmented_vars_asbinary = VarList( domain=Binary) indicator_map = ComponentMap() cnf_statements = [] # Convert all logical constraints to CNF for logical_constraint in context.component_data_objects( ctype=LogicalConstraint, active=True): cnf_statements.extend( to_cnf(logical_constraint.body, new_boolvarlist, indicator_map)) logical_constraint.deactivate() # Associate new Boolean vars to new binary variables for bool_vardata in new_boolvarlist.values(): new_binary_vardata = new_varlist.add() bool_vardata.associate_binary_var(new_binary_vardata) # Add constraints associated with each CNF statement for cnf_statement in cnf_statements: for linear_constraint in _cnf_to_linear_constraint_list(cnf_statement): new_constrlist.add(expr=linear_constraint) # Add bigM associated with special atoms # Note: this ad-hoc reformulation may be revisited for tightness in the future. old_varlist_length = len(new_varlist) for indicator_var, special_atom in indicator_map.items(): for linear_constraint in _cnf_to_linear_constraint_list( special_atom, indicator_var, new_varlist): new_constrlist.add(expr=linear_constraint) # Previous step may have added auxiliary binaries. Associate augmented Booleans to them. num_new = len(new_varlist) - old_varlist_length list_o_vars = list(new_varlist.values()) if num_new: for binary_vardata in list_o_vars[-num_new:]: new_bool_vardata = new_boolvarlist.add() new_bool_vardata.associate_binary_var(binary_vardata) # If added components were not used, remove them. # Note: it is ok to simply delete the index_set for these components, because by # default, a new set object is generated for each [Thing]List. if len(new_constrlist) == 0: new_xfrm_block.del_component(new_constrlist.index_set()) new_xfrm_block.del_component(new_constrlist) if len(new_boolvarlist) == 0: new_xfrm_block.del_component(new_boolvarlist.index_set()) new_xfrm_block.del_component(new_boolvarlist) if len(new_varlist) == 0: new_xfrm_block.del_component(new_varlist.index_set()) new_xfrm_block.del_component(new_varlist) # If block was entirely unused, remove it if all( len(l) == 0 for l in (new_constrlist, new_boolvarlist, new_varlist)): context.del_component(new_xfrm_block)