def test_misc(): a, b, c = map(exprvar, 'abc') a0 = exprvar('a', 0) b0 = exprvar('b', 0) assert expr("a * b * c").equivalent(a * b * c) assert expr("a + b + c").equivalent(a + b + c) assert expr("a * (b + c)").equivalent(a * (b + c)) assert expr("a + (b * c)").equivalent(a + b * c) assert expr("Or()").is_zero() assert expr("a[0] + b[0]").equivalent(a0 + b0)
def test_misc(): a, b, c = map(exprvar, 'abc') a0 = exprvar('a', 0) b0 = exprvar('b', 0) assert expr("a & b & c").equivalent(a & b & c) assert expr("a ^ b ^ c").equivalent(a ^ b ^ c) assert expr("a | b | c").equivalent(a | b | c) assert expr("a & (b | c)").equivalent(a & (b | c)) assert expr("a | (b & c)").equivalent(a | b & c) assert expr("Or()").is_zero() assert expr("a[0] | b[0]").equivalent(a0 | b0)
def test_not_at_most_one_not_equivalent(self): self.function = expr.And(self.pos, expr.exprvar("b"), ~expr.Or(*self.neg)) at_most = expr.exprvar("at_most") cardinality = constraint.at_most_one(self.function.support, ~at_most) sat = expr.And(self.function, at_most, *cardinality).to_cnf().satisfy_one() self.assertIsNotNone(sat) self.assertEqual(sat.get(at_most), 1)
def test_misc(): a, b, c = map(exprvar, 'abc') assert expr("a & b & c").equivalent(a & b & c) assert expr("a ^ b ^ c").equivalent(a ^ b ^ c) assert expr("a | b | c").equivalent(a | b | c) assert expr("a & (b | c)").equivalent(a & (b | c)) assert expr("a | (b & c)").equivalent(a | b & c) assert expr("Or()").is_zero() a_0 = exprvar('a', 0) b_a = exprvar(('a', 'b')) a_0_1 = exprvar('a', (0, 1)) b_a_0_1 = exprvar(('a', 'b'), (0, 1)) assert expr("a[0] | b.a | a[0,1] | b.a[0,1]").equivalent(a_0 | b_a | a_0_1 | b_a_0_1)
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 test_not_at_most_one(self): self.function = expr.And(self.pos, expr.exprvar("b"), ~expr.Or(*self.neg)) cardinality = constraint.at_most_one(self.function.support) sat = expr.And(self.function, *cardinality).to_cnf().satisfy_one() self.assertIsNone(sat)
def bdd2expr(bdd, conj=False): """Convert a binary decision diagram into an expression. This function will always return an expression in two-level form. If *conj* is ``False``, return a sum of products (SOP). Otherwise, return a product of sums (POS). For example:: >>> a, b = map(bddvar, 'ab') >>> bdd2expr(~a | b) Or(~a, And(a, b)) """ if conj: outer, inner = (And, Or) paths = _iter_all_paths(bdd.node, BDDNODEZERO) else: outer, inner = (Or, And) paths = _iter_all_paths(bdd.node, BDDNODEONE) terms = list() for path in paths: expr_point = {exprvar(v.names, v.indices): val for v, val in _path2point(path).items()} terms.append(boolfunc.point2term(expr_point, conj)) return outer(*[inner(*term) for term in terms])
def edge_expr(edge_nodes, k, expr_var_names): """ Get BDD expression from each set of nodes for any given edge; e.g., first edge 0-->1, i.e., 00 --> 01 r = ~x1 & ~x2 & ~y1 & y2 """ # Each BDD variable gets a unique id (w/in the pairs of nodes for the edge) var_id = 0 # For each node of edge, convert each bit to a BDD variable in an expression r = None for n in edge_nodes: node_bin = "{0:0b}".format(n) node_bin = "0" * (k - len(node_bin)) + node_bin node_bin_list = list(node_bin) for node_var_str in node_bin_list: # Give each variable a unique name, "v[unique num]" uniq_var_id = expr_var_names[var_id] var_id += 1 # Create variable from bit node_var = exprvar(uniq_var_id) if node_var_str == "0": node_var = ~node_var # Update the edge relation if r is None: r = node_var else: r &= node_var return r
def espresso_tts(*tts): """Return a tuple of expressions optimized using Espresso.""" for f in tts: if not isinstance(f, TruthTable): raise ValueError("expected a TruthTable instance") support = frozenset.union(*[f.support for f in tts]) inputs = sorted(support) ninputs = len(inputs) noutputs = len(tts) cover = set() for i, point in enumerate(boolfunc.iter_points(inputs)): invec = [2 if point[v] else 1 for v in inputs] outvec = list() for f in tts: val = f.pcdata[i] if val == PC_ZERO: outvec.append(0) elif val == PC_ONE: outvec.append(1) elif val == PC_DC: outvec.append(2) else: raise ValueError("expected truth table entry in {0, 1, -}") cover.add((tuple(invec), tuple(outvec))) set_config(**CONFIG) cover = espresso(ninputs, noutputs, cover, intype=FTYPE|DTYPE|RTYPE) inputs = [exprvar(v.names, v.indices) for v in inputs] return _cover2exprs(inputs, noutputs, cover)
def bdd2expr(bdd, conj=False): """Convert a binary decision diagram into an expression. This function will always return an expression in two-level form. If *conj* is ``False``, return a sum of products (SOP). Otherwise, return a product of sums (POS). For example:: >>> a, b = map(bddvar, 'ab') >>> bdd2expr(~a | b) Or(~a, And(a, b)) """ if conj: outer, inner = (And, Or) paths = _iter_all_paths(bdd.node, BDDNODEZERO) else: outer, inner = (Or, And) paths = _iter_all_paths(bdd.node, BDDNODEONE) terms = list() for path in paths: expr_point = { exprvar(v.names, v.indices): val for v, val in _path2point(path).items() } terms.append(boolfunc.point2term(expr_point, conj)) return outer(*[inner(*term) for term in terms])
def gen_vars(size): """Generates expression variables for every cell in the grid and returns them as rows and columns""" chars = AZ9[:size] rows_vars = tuple( tuple(exprvar("".join(p)) for p in product(char, chars)) for char in chars) return rows_vars, list(zip(*rows_vars))
def test_not_equals_higher(self): equals = expr.exprvar("equals") cardinality = cardnet.equals(self.function.support, len(self.pos) + 1, ~equals) sat = expr.And(self.function, equals, *cardinality).to_cnf().satisfy_one() self.assertIsNotNone(sat) self.assertEqual(sat.get(equals), 1)
def test_at_least_one_equivalent(self): at_least = expr.exprvar("at_least") cardinality = constraint.at_least_one(self.function.support, at_least) sat = expr.And(self.function, at_least, *cardinality).to_cnf().satisfy_one() self.assertIsNotNone(sat) self.assertEqual(sat.get(at_least), 1)
def test_not_at_most_is_not_equivalent(self): at_most = expr.exprvar("at_most") cardinality = cardnet.at_most(self.function.support, len(self.pos) - 1, ~at_most) sat = expr.And(self.function, at_most, *cardinality).to_cnf().satisfy_one() self.assertIsNotNone(sat) self.assertEqual(sat.get(at_most), 1)
def test_not_equals_one_not_equivalent(self): self.function = ~expr.Or(*self.neg) equals = expr.exprvar("equals") cardinality = constraint.equals_one(self.function.support, ~equals) sat = expr.And(self.function, equals, *cardinality).to_cnf().satisfy_one() self.assertIsNotNone(sat) self.assertEqual(sat.get(equals), 1)
def truthtable2expr(tt, conj=False): """Convert a truth table into an expression.""" if conj: outer, inner = (And, Or) nums = tt.pcdata.iter_zeros() else: outer, inner = (Or, And) nums = tt.pcdata.iter_ones() inputs = [exprvar(v.names, v.indices) for v in tt.inputs] terms = [boolfunc.num2term(num, inputs, conj) for num in nums] return outer(*[inner(*term) for term in terms])
def _parse_position_variable(self, variable): (_literal, kind, *names) = variable.names (i, j, *indices) = variable.indices if tuple(names) == ("constant",): if kind == "positive": return (i, j, True) elif kind == "negated": return (i, j, False) else: var = expr.exprvar(tuple(names), tuple(indices)) if kind == "positive": return (i, j, var) elif kind == "negated": return (i, j, ~var) raise ValueError("Unparseable variable ({})".format(variable))
def bdd2expr(bdd, conj=False): """Convert a binary decision diagram into an expression.""" if conj: outer, inner = (And, Or) paths = _iter_all_paths(bdd.node, BDDNODEZERO) else: outer, inner = (Or, And) paths = _iter_all_paths(bdd.node, BDDNODEONE) terms = list() for path in paths: expr_point = {exprvar(v.names, v.indices): val for v, val in path2point(path).items()} terms.append(boolfunc.point2term(expr_point, conj)) return outer(*[inner(*term) for term in terms])
def bitvec(name, *slices): """Return a BitVector with an arbitrary number of slices. Parameters ---------- name : str slices : (int or (int, int)) An int N means a slice from [0:N] A tuple (M, N) means a slice from [M:N] """ if slices: return bfarray.exprvars(name, *slices) else: return expr.exprvar(name)
def bitvec(name, *dims): """Return a new array of given dimensions, filled with Expressions. Parameters ---------- name : str dims : (int or (int, int)) An int N means a slice from [0:N] A tuple (M, N) means a slice from [M:N] """ if dims: return bfarray.exprvars(name, *dims) else: return expr.exprvar(name)
def test_at_least_one_not_equivalent(self, function): equivalent = expr.exprvar("equivalent") cardinality_a = constraint.at_least_one(function.support, ~equivalent) sat_a = expr.And(function, ~equivalent, *cardinality_a).to_cnf().satisfy_one() cardinality_b = constraint.at_least(function.support, 1, ~equivalent) sat_b = expr.And(function, ~equivalent, *cardinality_b).to_cnf().satisfy_one() self.assertEqual(sat_a is None, sat_b is None) if sat_a or sat_b: self.assertEqual(sat_a.get(equivalent), sat_b.get(equivalent))
def espresso_tts(*tts): """Return a tuple of expressions optimized using Espresso. The variadic *tts* argument is a sequence of truth tables. For example:: >>> from pyeda.boolalg.bfarray import exprvars >>> from pyeda.boolalg.table import truthtable >>> X = exprvars('x', 4) >>> f1 = truthtable(X, "0000011111------") >>> f2 = truthtable(X, "0001111100------") >>> f1m, f2m = espresso_tts(f1, f2) >>> f1m.equivalent(X[3] | X[0] & X[2] | X[1] & X[2]) True >>> f2m.equivalent(X[2] | X[0] & X[1]) True """ for f in tts: if not isinstance(f, TruthTable): raise ValueError("expected a TruthTable instance") support = frozenset.union(*[f.support for f in tts]) inputs = sorted(support) ninputs = len(inputs) noutputs = len(tts) cover = set() for i, point in enumerate(boolfunc.iter_points(inputs)): invec = [2 if point[v] else 1 for v in inputs] outvec = list() for f in tts: val = f.pcdata[i] if val == PC_ZERO: outvec.append(0) elif val == PC_ONE: outvec.append(1) elif val == PC_DC: outvec.append(2) else: raise ValueError("expected truth table entry in {0, 1, -}") cover.add((tuple(invec), tuple(outvec))) set_config(**CONFIG) cover = espresso(ninputs, noutputs, cover, intype=FTYPE | DTYPE | RTYPE) inputs = [exprvar(v.names, v.indices) for v in inputs] return _cover2exprs(inputs, noutputs, cover)
def espresso_tts(*tts): """Return a tuple of expressions optimized using Espresso. The variadic *tts* argument is a sequence of truth tables. For example:: >>> from pyeda.boolalg.bfarray import exprvars >>> from pyeda.boolalg.table import truthtable >>> X = exprvars('x', 4) >>> f1 = truthtable(X, "0000011111------") >>> f2 = truthtable(X, "0001111100------") >>> f1m, f2m = espresso_tts(f1, f2) >>> f1m.equivalent(X[3] | X[0] & X[2] | X[1] & X[2]) True >>> f2m.equivalent(X[2] | X[0] & X[1]) True """ for f in tts: if not isinstance(f, TruthTable): raise ValueError("expected a TruthTable instance") support = frozenset.union(*[f.support for f in tts]) inputs = sorted(support) ninputs = len(inputs) noutputs = len(tts) cover = set() for i, point in enumerate(boolfunc.iter_points(inputs)): invec = [2 if point[v] else 1 for v in inputs] outvec = list() for f in tts: val = f.pcdata[i] if val == PC_ZERO: outvec.append(0) elif val == PC_ONE: outvec.append(1) elif val == PC_DC: outvec.append(2) else: raise ValueError("expected truth table entry in {0, 1, -}") cover.add((tuple(invec), tuple(outvec))) set_config(**CONFIG) cover = espresso(ninputs, noutputs, cover, intype=FTYPE|DTYPE|RTYPE) inputs = [exprvar(v.names, v.indices) for v in inputs] return _cover2exprs(inputs, noutputs, cover)
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 _bitvec(name, slices, indices): """Return a BitVector with an arbitrary number of slices. NOTE: This is a recursive helper function for 'bitvec'. Do not invoke this function directly. """ fst, rst = slices[0], slices[1:] if rst: items = [_bitvec(name, rst, indices + (i, )) for i in range(fst.start, fst.stop)] return Slicer(items, fst.start) else: vs = [exprvar(name, indices + (i, )) for i in range(fst.start, fst.stop)] return BitVector(vs, fst.start)
def test_qbf_forall_cardnet(self): equals = expr.exprvar("equals") cardinality = synth.constraint.equals(self.pos + self.neg, len(self.neg), equals) function = expr.Or(*self.neg, *(~n for n in self.neg), simplify=False) solver = self.get_solver() solver.exists([equals]) solver.forall(self.neg) solver.add(function) for constraint in cardinality: solver.add(constraint) solver.add(equals) sat = solver.solve([equals]) self.assertIsNotNone(sat) self.assertTrue(sat.get(equals))
def literals(draw): variables = tuple(expr.exprvar(x) for x in "abcdef") variable = draw(st.sampled_from(variables)) negated = draw(st.booleans()) if negated: return ~variable return variable
def _inactive_switch(self, i, j): assert 1 <= i <= self.m assert 1 <= j <= self.n return expr.exprvar(("switch", "inactive"), (i, j))
def switch_var(i, j): assert 1 <= i <= self.m assert 1 <= j <= self.n return expr.exprvar("switch", (i, j, count))
def position_var(i, j, rnd): assert 1 <= i <= self.m assert 1 <= j <= self.n return expr.exprvar("reachable", (i, j, rnd, count))
def _path_var(self, i, j): assert 1 <= i <= self.m assert 1 <= j <= self.n return expr.exprvar("path", (i, j))
def _negative_path_var(self, i, j): assert 1 <= i <= self.m assert 1 <= j <= self.n return expr.exprvar(("path", "negative"), (i, j))
def _vector_to_vars(vector): for (num, inp) in enumerate(vector): if inp == table.PC_ONE: yield expr.exprvar("input", num) elif inp == table.PC_ZERO: yield ~expr.exprvar("input", num)
def _next_aux(name=None, index=None): count = next(Cardnet._counter) names = ("cardnet", name) if name else "cardnet" indices = (index, count) if index else count return expr.exprvar(names, indices)
def setUp(self): self.pos = tuple(expr.exprvar(p) for p in "abc") self.neg = tuple(expr.exprvar(p) for p in "uvwxyz") self.function = expr.And(*self.pos, ~expr.Or(*self.neg))
def path_var(i, j): assert 1 <= i <= self.m assert 1 <= j <= self.n return expr.exprvar("path", (i, j, count))
def a(i): return expr.exprvar("sinz", (i, count))
def _position_unreachable(self, i, j, rnd): assert 1 <= i <= self.m assert 1 <= j <= self.n return expr.exprvar("unreachable", (i, j, rnd))
def setUp(self): super().setUp() self.pos = tuple(expr.exprvar(p) for p in "abcdef") self.neg = tuple(expr.exprvar(p) for p in "uvwxyz")