def _assert_some_negative_path_connected(self, path_var=None): path_var = self._negative_path_var if path_var is None else path_var start_left = [path_var(i, 1) for i in range(1, self.m + 1)] end_right = [path_var(i, self.n) for i in range(1, self.m + 1)] yield from constraint.at_most(start_left, 1) yield from constraint.at_most(end_right, 1) if self.n == 1: # covered by `_assert_negative_path_exists_if_function_false()` pass elif self.m == 1: for j in range(1, self.n): yield expr.Implies(path_var(1, j + 1), path_var(1, j)).to_cnf() else: for i in range(1, self.m + 1): column_offset = [ i_ for i_ in (i - 1, i, i + 1) if 1 <= i_ <= self.m ] for (j, j_off) in ((1, 2), (self.n, self.n - 1)): elements = [path_var(i_, j_off) for i_ in column_offset] equals_one = self._next_aux("equals_one") yield from constraint.cardnet.equals( elements, 1, equals_one) yield expr.Implies(path_var(i, j), equals_one).to_cnf() for j in range(2, self.n): elements = [ path_var(i_, j_) for (i_, j_) in self._adjacent_8(i, j) ] equals_two = self._next_aux("equals_two") yield from constraint.equals(elements, 2, equals_two) yield expr.Implies(path_var(i, j), equals_two).to_cnf()
def _assert_some_path_connected(self, path_var=None): path_var = self._path_var if path_var is None else path_var start_at_top = [path_var(1, j) for j in range(1, self.n + 1)] end_at_bottom = [path_var(self.m, j) for j in range(1, self.n + 1)] yield from constraint.at_most(start_at_top, 1) yield from constraint.at_most(end_at_bottom, 1) if self.m == 1: # covered by `_assert_path_exists_if_function_true()` pass elif self.n == 1: for i in range(1, self.m): yield expr.Implies(path_var(i + 1, 1), path_var(i, 1)).to_cnf() else: for j in range(1, self.n + 1): yield expr.Implies(path_var(1, j), path_var(2, j)).to_cnf() yield expr.Implies(path_var(self.m, j), path_var(self.m - 1, j)).to_cnf() for i in range(2, self.m): for j in range(1, self.n + 1): elements = [ path_var(i_, j_) for (i_, j_) in self._adjacent_4(i, j) ] equals_two = self._next_aux("equals_two") yield from constraint.equals(elements, 2, equals_two) yield expr.Implies(path_var(i, j), equals_two).to_cnf()
def _assert_positive_path(self, switch_var=None, position_var=None): switch_var = self._active_switch if switch_var is None else switch_var position_var = self._position_reachable if position_var is None \ else position_var if self.m == 1 or self.n == 1: upper = self._upper_path_bound() for i in range(1, self.m + 1): for j in range(1, self.n + 1): yield expr.Implies(position_var(i, j, upper), switch_var(i, j)).to_cnf() if self.n == 1: for i in range(1, self.m): yield expr.Implies(position_var(i + 1, 1, upper), position_var(i, 1, upper)).to_cnf() else: for i in range(1, self.m + 1): for j in range(1, self.n + 1): reachable = position_var(i, j, 0) if i == 1: yield reachable else: yield ~reachable for rnd in range(1, self._upper_path_bound() + 1): for i in range(1, self.m + 1): for j in range(1, self.n + 1): reachable = position_var(i, j, rnd) elements = [position_var(i_, j_, rnd - 1) & \ switch_var(i_, j_) for (i_, j_) in self._adjacent_4(i, j)] elements.append(position_var(i, j, rnd - 1)) yield expr.Implies(reachable, expr.Or(*elements)).to_cnf()
def at_most_one(inputs, equivalent=None): assert inputs, "inputs must not be empty" count = next(_counter) def a(i): return expr.exprvar("sinz", (i, count)) def clauses(variables): length = len(variables) (first, rest, last) = (variables[0], variables[1:-1], variables[-1]) yield expr.Or(~first, a(1)) yield expr.Or(~last, ~a(length - 1)) for (i, x) in zip(range(2, length), rest): yield expr.Or(~x, a(i)) yield expr.Or(~a(i - 1), a(i)) yield expr.Or(~x, ~a(i - 1)) if equivalent is None: yield from clauses(tuple(inputs)) else: auxiliaries = list() for clause in clauses(tuple(inputs)): aux = expr.exprvar(("sinz", "eq"), next(_counter)) yield expr.Implies(aux, clause).to_cnf() yield expr.Implies(clause, aux).to_cnf() auxiliaries.append(aux) constraint = expr.And(*auxiliaries) yield expr.Implies(constraint, equivalent).to_cnf() yield expr.Implies(equivalent, constraint).to_cnf()
def at_least_one(inputs, equivalent=None): assert inputs, "inputs must not be empty" constraint = expr.Or(*inputs) if equivalent is None: yield constraint else: yield expr.Implies(equivalent, constraint).to_cnf() yield expr.Implies(constraint, equivalent).to_cnf()
def at_most(inputs, p, equivalent=None): assert inputs, "inputs must not be empty" (variables, clauses) = Cardnet().cardnet(inputs, p) condition = ~variables[p] yield from clauses if equivalent is None: yield condition else: yield expr.Implies(equivalent, condition).to_cnf() yield expr.Implies(condition, equivalent).to_cnf()
def _assert_lattice_on_negative_path(self): def _set(i, j, inp): return self._literal_at_position_is(i, j, inp) & ~inp def _negated(i, j, inp): return self._literal_at_position_is(i, j, ~inp) & inp for i in range(1, self.m + 1): for j in range(1, self.n + 1): auxiliaries = list() for inp in self._inputs_plus(): aux = self._next_aux("on_neg_path") on_neg_path = _set(i, j, inp) | _negated(i, j, inp) auxiliaries.append(aux) yield expr.Implies(aux, on_neg_path).to_cnf() yield expr.Implies(self._negative_path_var(i, j), expr.Or(*auxiliaries)).to_cnf()
def _assert_lattice_on_negative_path(self, assignment, path_var): for i in range(1, self.m + 1): for j in range(1, self.n + 1): elements = list( self._literal_at_position_is(i, j, inp) for inp in self._input_literals() if inp.compose(assignment).simplify().is_zero()) assert elements, "list of literal variables must not be empty" yield expr.Implies(path_var(i, j), expr.Or(*elements)).to_cnf()
def convert_to_pyeda(term): """Converts a term object to a PyEDA expression. Args: term (Term): A term object. Returns: expr: The corresponding PyEDA expression. """ if term.operator is None: return expr.exprvar(escape_var_name(term.operands[0]), None) elif term.operator == "NEG": return expr.Not(convert_to_pyeda(term.operands[0]), simplify=False) elif term.operator == "AND": return expr.And( *[convert_to_pyeda(operand) for operand in term.operands], simplify=False) elif term.operator == "OR": return expr.Or( *[convert_to_pyeda(operand) for operand in term.operands], simplify=False) elif term.operator == "IMP": return expr.Implies(convert_to_pyeda(term.operands[0]), convert_to_pyeda(term.operands[0]), simplify=False) elif term.operator == "EQV": return expr.Equal( *[convert_to_pyeda(operand) for operand in term.operands], simplify=False) elif term.operator == "ADD": return expr.exprvar( "___ADD___".join([ escape_var_name(operand.operands[0]) for operand in term.operands ]), None)
def _assert_path_exists_if_function_false(self): elements = (self._active_switch(self.m, j) & \ self._position_reachable(self.m, j, self._upper_path_bound()) for j in range(1, self.n + 1)) yield expr.Implies(~self.function, expr.Or(*elements)).to_cnf()
def _assert_negative_path_exists_if_function_true(self): elements = (self._inactive_switch(i, self.n) & \ self._position_unreachable(i, self.n, self._upper_path_bound()) for i in range(1, self.m + 1)) yield expr.Implies(self.function, expr.Or(*elements)).to_cnf()
def _assert_path_exists_if_function_true(self): elements = (self._path_var(self.m, j) for j in range(1, self.n + 1)) yield expr.Implies(self.function, expr.Or(*elements)).to_cnf()
def _assert_negative_path_exists_if_function_false(self): elements = (self._negative_path_var(i, self.n) for i in range(1, self.m + 1)) yield expr.Implies(~self.function, expr.Or(*elements)).to_cnf()