Example #1
0
def test_tseitin_required_detection():
    assert a.to_CNF() == And({Or({a})})
    assert And().to_CNF() == And()
    assert Or().to_CNF() == And({Or()})
    assert (a | b).to_CNF() == And({a | b})
    assert And({a | b, b | c}).to_CNF() == And({a | b, b | c})
    assert And({And({Or({And({~a})})})}).to_CNF() == And({Or({~a})})
Example #2
0
    def process_required(node: NNF) -> None:
        """For nodes that have to be satisfied.

        This lets us perform some optimizations.
        """
        if isinstance(node, Var):
            clauses.append(Or({node}))
            return

        assert isinstance(node, Internal)

        if len(node.children) == 1:
            [child] = node.children
            process_required(child)

        elif isinstance(node, Or):
            children = {process_node(c) for c in node.children}
            if any(~var in children for var in children):
                return
            clauses.append(Or(children))

        elif isinstance(node, And):
            for child in node.children:
                process_required(child)

        else:
            raise TypeError(node)
Example #3
0
def _parse_cnf(tokens: t.Iterable[str]) -> And[Or[Var]]:
    clauses = set()  # type: t.Set[Or[Var]]
    clause = set()  # type: t.Set[Var]
    for token in tokens:
        if token == '0':
            clauses.add(Or(clause))
            clause = set()
        elif token == '%':
            # Some example files end with:
            # 0
            # %
            # 0
            # I don't know why.
            break
        elif token.startswith('-'):
            clause.add(Var(_parse_int(token[1:]), False))
        else:
            clause.add(Var(_parse_int(token)))
    if clause:
        # A file may or may not end with a 0
        # Adding an empty clause is not desirable
        clauses.add(Or(clause))
    sentence = And(clauses)
    NNF._is_CNF_loose.set(sentence, True)
    return sentence
Example #4
0
def test_dimacs_cnf_serialize():
    sample_input = """c Example CNF format file
c
p cnf 4 3
1 3 -4 0
4 0 2
-3
"""
    assert dimacs.loads(sample_input) == And(
        {Or({Var(1), Var(3), ~Var(4)}),
         Or({Var(4)}),
         Or({Var(2), ~Var(3)})})
Example #5
0
def test_dimacs_sat_serialize():
    # http://www.domagoj-babic.com/uploads/ResearchProjects/Spear/dimacs-cnf.pdf
    sample_input = """c Sample SAT format
c
p sat 4
(*(+(1 3 -4)
   +(4)
   +(2 3)))
"""
    assert dimacs.loads(sample_input) == And(
        {Or({Var(1), Var(3), ~Var(4)}),
         Or({Var(4)}),
         Or({Var(2), Var(3)})})
Example #6
0
def load(fp: t.TextIO,
         var_labels: t.Optional[t.Dict[int, Name]] = None) -> NNF:
    """Load a sentence from an open file.

    An optional ``var_labels`` dictionary can map integers to other names.
    """
    def decode_name(num: int) -> Name:
        if var_labels is not None:
            return var_labels[num]
        return num

    fmt, nodecount, edges, varcount = fp.readline().split()
    node_specs = dict(enumerate(line.split() for line in fp))
    assert fmt == 'nnf'
    nodes = {}  # type: t.Dict[int, NNF]
    for num, spec in node_specs.items():
        if spec[0] == 'L':
            if spec[1].startswith('-'):
                nodes[num] = Var(decode_name(int(spec[1][1:])), False)
            else:
                nodes[num] = Var(decode_name(int(spec[1])))
        elif spec[0] == 'A':
            nodes[num] = And(nodes[int(n)] for n in spec[2:])
        elif spec[0] == 'O':
            nodes[num] = Or(nodes[int(n)] for n in spec[3:])
        else:
            raise ValueError("Can't parse line {}: {}".format(num, spec))
    if int(nodecount) == 0:
        raise ValueError("The sentence doesn't have any nodes.")
    return nodes[int(nodecount) - 1]
Example #7
0
def _parse_sat(tokens: 't.Deque[str]') -> NNF:
    cur = tokens.popleft()
    if cur == '(':
        content = _parse_sat(tokens)
        close = tokens.popleft()
        if close != ')':
            raise DecodeError(
                "Expected closing paren, found {!r}".format(close))
        return content
    elif cur == '-':
        content = _parse_sat(tokens)
        if not isinstance(content, Var):
            raise DecodeError(
                "Only variables can be negated, not {!r}".format(content))
        return ~content
    elif cur == '*(':
        children = []
        while tokens[0] != ')':
            children.append(_parse_sat(tokens))
        tokens.popleft()
        if children:
            return And(children)
        else:
            return true
    elif cur == '+(':
        children = []
        while tokens[0] != ')':
            children.append(_parse_sat(tokens))
        tokens.popleft()
        if children:
            return Or(children)
        else:
            return false
    else:
        return Var(_parse_int(cur))
Example #8
0
def test_is_DNF_examples():
    assert Or().is_DNF()
    assert Or().is_DNF(strict=True)
    assert Or({And()}).is_DNF()
    assert Or({And()}).is_DNF(strict=True)
    assert Or({And({a, ~b})}).is_DNF()
    assert Or({And({a, ~b})}).is_DNF(strict=True)
    assert Or({And({a, ~b}), And({c, ~c})}).is_DNF()
    assert not Or({And({a, ~b}), And({c, ~c})}).is_DNF(strict=True)
Example #9
0
def test_is_CNF_examples():
    assert And().is_CNF()
    assert And().is_CNF(strict=True)
    assert And({Or()}).is_CNF()
    assert And({Or()}).is_CNF(strict=True)
    assert And({Or({a, ~b})}).is_CNF()
    assert And({Or({a, ~b})}).is_CNF(strict=True)
    assert And({Or({a, ~b}), Or({c, ~c})}).is_CNF()
    assert not And({Or({a, ~b}), Or({c, ~c})}).is_CNF(strict=True)
Example #10
0
 def test_dsharp_compile_converting_names(sentence: And[Or[Var]]):
     sentence = And(
         Or(Var(str(var.name), var.true) for var in clause)
         for clause in sentence)
     compiled = dsharp.compile(sentence)
     assert all(isinstance(name, str) for name in compiled.vars())
     if sentence.satisfiable():
         assert sentence.equivalent(compiled)
Example #11
0
def test_implicates_implicants_negation_rule_example():
    """These failed an old version of the previous test. See issue #3."""
    sentence = Or({And({~Var(1), Var(2)}), And({~Var(3), Var(1)})})
    assert (sentence.negate().implicants().negate().children >=
            sentence.implicates().children)
    assert (sentence.negate().implicates().negate().children <=
            sentence.implicants().children)
Example #12
0
    def process_node(node: NNF) -> Var:

        if isinstance(node, Var):
            return node

        assert isinstance(node, Internal)

        children = {process_node(c) for c in node.children}

        if len(children) == 1:
            [child] = children
            return child

        aux = Var.aux()

        if any(~var in children for var in children):
            if isinstance(node, And):
                clauses.append(Or({~aux}))
            else:
                clauses.append(Or({aux}))

        elif isinstance(node, And):
            clauses.append(Or({~c for c in children} | {aux}))
            for c in children:
                clauses.append(Or({~aux, c}))

        elif isinstance(node, Or):
            clauses.append(Or(children | {~aux}))
            for c in children:
                clauses.append(Or({~c, aux}))

        else:
            raise TypeError(node)

        return aux
Example #13
0
    def check_correct(self):
        var = []
        ret_string = ""
        chars = list(self.characters.keys())
        sentence_vals = {x: False for x in chars}

        chars.remove('weapon')
        chars.remove('location')

        for char in chars:
            var.append(Var(char))

        weapon = self.characters['weapon'].goals['murderweapon'].points
        sus_weapon = self.characters['weapon'].goals['suspectedweapon'].points
        location = self.characters['location'].goals['murderlocation'].points
        sus_location = self.characters['location'].goals['suspectedlocation'].points

        for char in chars:
            if self.characters[char].goals['issuspect'].points == 1:
                sus_killer = char
            if self.characters[char].goals['iskiller'].points == 1:
                killer = char

        w = Var('weapon')
        l = Var('location')

        sentence = And({w, l, Or(tuple(var))})

        if killer == sus_killer:
            sentence_vals[killer] = True
            ret_string += "You got the killer!\n"
        else:
            ret_string += "The killer got away!\n"

        if weapon == sus_weapon:
            sentence_vals['weapon'] = True
            ret_string += "You correctly identified the weapon!\n"
        else:
            ret_string += "You didn't identify the murder weapon correctly.\n"

        if location == sus_location:
            sentence_vals['location'] = True
            ret_string += "You deduced the correct location!\n\n"
        else:
            ret_string += "The murder took place in another location.\n\n"

        if sentence.satisfied_by(sentence_vals):
            ret_string += "Congratulations, you solved the mystery!\n"
        else:
            ret_string += "Unfortunately, you didn't solve the mystery completely.\n"

        return ret_string
Example #14
0
    def implies_all(self, inputs: dict, left: list, right: list) -> NNF:
        """All left variables imply all right variables.

        Arguments
        ---------
        inputs: dict
        left : list[nnf.Var]
        right: list[nnf.Var]

        Returns
        -------
        nnf.NNF: And(Or(~left_i, right_j))

        """
        clauses = []

        # constraint created by function
        if not inputs:
            if left and right:
                left_vars = list(map(lambda var: ~var, left))
                clauses = list(
                    map(lambda clause: Or(clause), product(left_vars, right)))
                return And(clauses)

        assert isinstance(inputs, dict)

        # constraint from decorator
        for key, value in inputs.items():
            left_vars = left + [key]
            right_vars = right + value
            negated_left = list(map(lambda var: ~var, left_vars))
            res = list(
                map(lambda clause: Or(clause), product(negated_left,
                                                       right_vars)))
            clauses.extend(res)
            self.add_to_instance_constraints(tuple(left_vars), res)
        return And(clauses)
Example #15
0
 def reduce_(node: NNF) -> NNF:
     if isinstance(node, Or):
         best = add_neut
         candidates = []  # type: t.List[NNF]
         for child in node.children:
             value = eval_(child)
             if value > best:  # type: ignore
                 best = value
                 candidates = [child]
             elif value == best:
                 candidates.append(child)
         return Or(reduce_(candidate) for candidate in candidates)
     elif isinstance(node, And):
         return And(reduce_(child) for child in node.children)
     else:
         return node
Example #16
0
    def none_of(self, inputs: list) -> NNF:
        """None of the inputs are true.

        Arguments
        ---------
        inputs : list[nnf.Var]

        Returns
        -------
        theory : nnf.NNF
            And(~a, ~b) for all a,b in input

        """
        if not inputs:
            raise ValueError(f"Inputs are empty for {self}")

        return Or(inputs).negate()
Example #17
0
    def at_least_one(self, inputs: list) -> NNF:
        """At least one of the inputs are true.

        This is equivalent to a disjunction across all variables

        Arguments
        ---------
        inputs : list[nnf.Var]

        Returns
        -------
        nnf.NNF: Or(inputs)
            Disjunction across all variables.

        """
        if not inputs:
            raise ValueError(f"Inputs are empty for {self}")

        return Or(inputs)
Example #18
0
    def at_most_k(self, inputs: list, k: int) -> NNF:
        """ At most k variables can be true.

        Arguments
        ---------
        inputs : list[nnf.Var]
        k : int

        Returns
        -------
        nnf.NNF

        """
        if not 1 <= k <= len(inputs):
            raise ValueError(f"The provided k={k} is greater"
                             " than the number of propositional"
                             f" variables (i.e. {len(inputs)} variables)"
                             f" for {self}.")
        elif k == 1:
            return _ConstraintBuilder.at_most_one(inputs)
        if k >= len(inputs):
            warnings.warn(f"The provided k={k} for building the at most K"
                          " constraint is greater than or equal to"
                          f" the number of variables, which is {len(inputs)}."
                          f" We're setting k = {len(inputs) - 1} as a result.")
            k = len(inputs) - 1

        clauses = set()  # avoid adding duplicate clauses
        inputs = list(map(lambda var: ~var, inputs))
        # combinations from choosing k from n inputs for 1 <= k <n
        chosen = list(combinations(inputs, k))
        for combo in chosen:
            combo = list(combo)
            excludes_combo = [x for x in inputs if x not in combo]
            for x in excludes_combo:
                clause = Or(combo + [x])
                clauses.add(clause)
                self.add_to_instance_constraints(tuple(combo), clause)
        return And(clauses)
Example #19
0
    def at_most_one(self, inputs: list) -> NNF:
        """At most one of the inputs are true.

        Arguments
        ---------
        inputs : list[nnf.Var]

        Returns
        -------
        theory : nnf.NNF
            And(Or(~a, ~b)) for all a,b in input

        """
        if not inputs:
            raise ValueError(f"Inputs are empty for {self}")

        clauses = []
        for var in inputs:
            # negate variables that aren't the current var
            excludes_var = [~x for x in inputs if x != var]
            clause = list(map(lambda c: Or(c), product([~var], excludes_var)))
            clauses.extend(clause)
            self.add_to_instance_constraints(str(var), clause)
        return And(clauses)
Example #20
0
    sample_input = """c Sample SAT format
c
p sat 4
(*(+(1 3 -4)
   +(4)
   +(2 3)))
"""
    assert dimacs.loads(sample_input) == And(
        {Or({Var(1), Var(3), ~Var(4)}),
         Or({Var(4)}),
         Or({Var(2), Var(3)})})


@pytest.mark.parametrize(
    'serialized, sentence',
    [('p sat 2\n(+((1)+((2))))', Or({Var(1), Or({Var(2)})}))])
def test_dimacs_sat_weird_input(serialized: str, sentence: nnf.NNF):
    assert dimacs.loads(serialized) == sentence


def test_dimacs_cnf_serialize():
    sample_input = """c Example CNF format file
c
p cnf 4 3
1 3 -4 0
4 0 2
-3
"""
    assert dimacs.loads(sample_input) == And(
        {Or({Var(1), Var(3), ~Var(4)}),
         Or({Var(4)}),
Example #21
0
def encode_circuit_search(original_theory, num_gates, models=[]):

    ########
    # Vars #
    ########

    # Inputs to the circuit are the variables of the input theory
    inputs = [Var(v) for v in original_theory.vars()]
    if models:
        input_clones = clone_varset(models, inputs, inputs)
    else:
        input_clones = {}

    # Gates are either & | or ~
    gates = []
    gate_modalities = {}
    for i in range(num_gates):
        gates.append(Var(Gate(i)))
        gate_modalities[gates[-1]] = {}
        for m in ['and', 'or', 'not']:
            gate_modalities[gates[-1]][m] = Var(GateType(gates[-1], m))

    if models:
        gate_clones = clone_varset(models, inputs, gates)
    else:
        gate_clones = {}

    # Single (arbitrary) gate is the circuit output
    output = gates[0]

    # Connections between inputs/gates to gates, and transitivity
    connections = {}
    unconnections = {}
    for src in inputs + gates[1:]:
        connections[src] = {}
        for dst in gates:
            if src != dst:
                connections[src][dst] = Var(Connection(src, dst))
                if dst not in unconnections:
                    unconnections[dst] = {}
                unconnections[dst][src] = connections[src][dst]
    C = connections # convenience

    orders = {}
    for src in gates:
        orders[src] = {}
        for dst in gates:
            orders[src][dst] = Var(Order(src,dst))



    ###############
    # Constraints #
    ###############

    ''' add decorators for simplifying adding constraints (i.e. a conjunct)'''
    conjuncts = []

    # Orderings (to forbid cycles in the circuit)
    for g1 in gates:
        # Connection implies orders
        for src in connections:
            for dst in connections[src]:
                if isinstance(src.name, Gate) and isinstance(dst.name, Gate):
                    conjuncts.append(~connections[src][dst] | orders[src][dst])
        # Can't order before yourself
        conjuncts.append(~orders[g1][g1])
        # Transitive closure
        for g2 in gates:
            for g3 in gates:
                conjuncts.append(~orders[g1][g2] | ~orders[g2][g3] | orders[g1][g3])

    if FORCE_TREE:
        # At max one outgoing connection on a gate
        for src in gates[1:]:
            for dst1 in connections[src]:
                for dst2 in connections[src]:
                    if dst1 != dst2:
                        conjuncts.append(~connections[src][dst1] | ~connections[src][dst2])

    # Every gate has at least one input
    for dst in unconnections:
        conjuncts.append(Or(unconnections[dst].values()))

    # Every gate has at most two inputs and negation gates have at most one
    for j in gates:
        for i1 in inputs+gates[1:]:
            for i2 in inputs+gates[1:]:
                if not unique([j,i1,i2]):
                    continue
                conjuncts.append(~gate_modalities[j]['not'] | ~C[i1][j] | ~C[i2][j])
                for i3 in inputs+gates[1:]:
                    if not unique([j,i1,i2,i3]):
                        continue
                    conjuncts.append(~C[i1][j] | ~C[i2][j] | ~C[i3][j])

    # Every gate has exactly one modality
    for g in gates:
        # At least one
        conjuncts.append(Or(gate_modalities[g].values()))

        # At most one
        for m1 in ['and', 'or', 'not']:
            remaining = set(['and', 'or', 'not']) - set([m1])
            conjuncts.append(Or([~gate_modalities[g][m2] for m2 in remaining]))

    # Re-usable theories
    notneg_cache = {}
    def notneg(src, dst, cloned_src=None):
        if not cloned_src:
            cloned_src = src
        if (cloned_src,dst) not in notneg_cache:
            notneg_cache[(cloned_src,dst)] = ~C[src][dst] | cloned_src
        return notneg_cache[(cloned_src,dst)]

    notpos_cache = {}
    def notpos(src, dst, cloned_src=None):
        if not cloned_src:
            cloned_src = src
        if (cloned_src,dst) not in notpos_cache:
            notpos_cache[(cloned_src,dst)] = ~C[src][dst] | ~cloned_src
        return notpos_cache[(cloned_src,dst)]

    # Implement the gates
    for g in gates:

        ins = [i for i in inputs+gates[1:] if i != g]

        conjuncts.append(~gate_modalities[g]['and'] | iff(g, And([notneg(src,g) for src in ins])))

        t = Or([notpos(src,g).negate() for src in ins])
        conjuncts.append(~gate_modalities[g]['or'] | iff(g, t))

        conjuncts.append(~gate_modalities[g]['not'] | iff(g, t.negate()))

        for m in models:
            bitvec = model_to_bitvec(m, inputs)
            orig_mapping = {**{input_clones[bitvec][i]: i for i in inputs if i != g},
                            **{gate_clones[bitvec][i]: i for i in gates[1:] if i != g}}
            ins = orig_mapping.keys()

            conjuncts.append(~gate_modalities[g]['and'] | iff(gate_clones[bitvec][g],
                     And([notneg(orig_mapping[src],g,src) for src in ins])))

            t = Or([notpos(orig_mapping[src],g,src).negate() for src in ins])
            conjuncts.append(~gate_modalities[g]['or'] | iff(gate_clones[bitvec][g], t))

            conjuncts.append(~gate_modalities[g]['not'] | iff(gate_clones[bitvec][g], t.negate()))


    # Finally, lock in the models
    if models:
        for m in models:
            bitvec = model_to_bitvec(m, inputs)
            for var in m:
                if m[var]:
                    conjuncts.append(input_clones[bitvec][Var(var)])
                else:
                    conjuncts.append(~input_clones[bitvec][Var(var)])
            if original_theory.satisfied_by(m):
                conjuncts.append(gate_clones[bitvec][output])
            else:
                conjuncts.append(~gate_clones[bitvec][output])
    else:
        for model in all_models(original_theory.vars()):

            t = false # negating the conjunction because of the implication: flips the signs
            for var,val in model.items():
                if val:
                    t |= ~Var(var)
                else:
                    t |= Var(var)
            if original_theory.satisfied_by(model):
                t |= output
            else:
                t |= ~output
            conjuncts.append(t)


    versions = {}
    example = {}
    for c in conjuncts:
        cn = c.simplify().to_CNF()
        stats = "(%d / %d / %d) > (%d / %d / %d)" % (c.simplify().size(), c.simplify().height(), len(c.simplify().vars()),
                                                     cn.size(), cn.height(), len(cn.vars()))
        example[stats] = str(c.simplify())
        versions[stats] = versions.get(stats, 0) + 1
    print("Conjunct stats:")
    for k in versions:
        print("\n - (%d) %s: %s" % (versions[k], k, example[k]))

    T = And(conjuncts)
    return T.simplify(), inputs, gates, output, connections, unconnections, gate_modalities
Example #22
0
def DNF(draw):
    return Or(draw(st.frozensets(terms())))
Example #23
0
def clauses(draw):
    return Or(Var(name, draw(st.booleans())) for name in draw(st.sets(names)))
Example #24
0
    def C(S):
        if S == candidates:
            return true

        return Or(C_child(i, S) for i in candidates - S)
Example #25
0
def MODS(draw):
    num = draw(st.integers(min_value=1, max_value=9))
    amount = draw(st.integers(min_value=0, max_value=10))
    return Or(
        And(Var(name, draw(st.booleans())) for name in range(1, num))
        for _ in range(amount))
Example #26
0
def test_hyp(sentence: nnf.Or):
    assume(len(sentence.children) != 0)
    assume(sentence.decomposable())
    assert sentence.satisfiable()
    assert sentence.vars() <= set(range(1, 9))
Example #27
0
def test_MODS_satisfiable(sentence: nnf.Or):
    if len(sentence.children) > 0:
        assert sentence.satisfiable()
    else:
        assert not sentence.satisfiable()
Example #28
0
def test_MODS(sentence: nnf.Or):
    assert sentence.smooth()
    assert sentence.flat()
    assert sentence.decomposable()
    assert sentence.simply_conjunct()
Example #29
0
def distribute(node: NNF):

    if isinstance(node, Var):
        return node

    elif isinstance(node, And):

        # And(Var(A), Var(B)) -> And(Var(A), Var(B))
        if all([isinstance(child, Var) for child in node.children]):
            return node

        else:
            left, right = node.children
            return And({distribute(left), distribute(right)})

    elif isinstance(node, Or):

        # Or(Var(A), Var(B)) -> Or(Var(A), Var(B))
        if all([isinstance(child, Var) for child in node.children]):
            return node

        else:
            left, right = node.children

            # Or(And(A,B), And(C, D)) -> And(Or(A,C), Or(A,D), Or(B,C), Or(B,D))
            if all([isinstance(child, And) for child in node.children]):
                res = []

                for c in left.children:
                    for d in right.children:
                        res.append(Or({c, d}))
                return And(res)

            # Either Or(And(A,B), C) or Or(C, And(A,B))
            # where C can be Var or Or
            elif any([isinstance(child, And) for child in node.children]):

                if isinstance(left, And):
                    clause1, clause2 = left.children
                    distributed_left = Or(
                        {distribute(clause1),
                         distribute(right)})
                    distributed_right = Or(
                        {distribute(clause2),
                         distribute(right)})

                    return And({
                        distribute(distributed_left),
                        distribute(distributed_right)
                    })

                else:
                    clause1, clause2 = right.children
                    distributed_left = Or(
                        {distribute(clause1),
                         distribute(left)})
                    distributed_right = Or(
                        {distribute(clause2),
                         distribute(left)})

                    return And({
                        distribute(distributed_left),
                        distribute(distributed_right)
                    })

            # Or(Or(A, B), Or(D, C))
            else:
                left, right = node.children
                return Or({distribute(left), distribute(right)})
Example #30
0
 def C(S):
     if len(S) < k:
         return Or(C_child(i, S) for i in candidates - S)
     return And(~Var((i, j)) for i in candidates - S for j in candidates - S
                if i != j)