Пример #1
0
 def count_solutions(self, lits=[]):
     if lits:
         T = And(self.constraints + lits)
     else:
         T = And(self.constraints)
     return dsharp.compile(T.to_CNF(),
                           executable='bin/dsharp').model_count()
Пример #2
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)
Пример #3
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
Пример #4
0
    def process_theory(node: NNF):

        if node.is_CNF():
            return node

        res = []

        if isinstance(node, Var):
            res.append(node)

        assert isinstance(node, Internal)

        if len(node.children) == 1:
            [child] = node.children
            res.append(process_node(child))

        else:

            left, right = node.children

            if isinstance(node, And):
                left_to_cnf = distribute(left)
                right_to_cnf = distribute(right)
                return And({left_to_cnf, right_to_cnf})

            elif isinstance(node, Or):
                left_to_cnf = distribute(left)
                right_to_cnf = distribute(right)
                return merge_cnf(left_to_cnf, right_to_cnf)

        return res
Пример #5
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))
Пример #6
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
Пример #7
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]
Пример #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)
Пример #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)
Пример #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)
Пример #11
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)})})
Пример #12
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)})})
Пример #13
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)
Пример #14
0
    def count_solutions(self, lits=[]):
        if lits:
            T = And(self.constraints + lits)
        else:
            T = And(self.constraints)
        if not T.satisfiable():
            return 0

        return dsharp.compile(T.to_CNF(), executable='bin/dsharp', smooth=True).model_count()
Пример #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
Пример #16
0
    def merge_cnf(left: NNF, right: NNF):

        res = []
        left_clauses = [left] if isinstance(
            left, Var) else [clause for clause in left.children]
        right_clauses = [right] if isinstance(
            right, Var) else [clause for clause in right.children]

        if len(left_clauses) > len(right_clauses):
            for index, clause in enumerate(left_clauses):
                res.append((clause, right_clauses[index % len(right_clauses)]))
        else:
            for index, clause in enumerate(right_clauses):
                res.append((clause, left_clauses[index % len(left_clauses)]))

        return And(res)
Пример #17
0
def count_solutions(base_formula, lits=[]):
    """Counts the number of solutions to a given formula."""
    def _nnfify(lit):
        if type(lit).__name__ == "CustomNNF":
            assert lit.typ == 'not', "Literal must be a variable or negated variable."
            return ~(lit.args[0].args[0])
        else:
            return lit._var

    T = base_formula
    if lits:
        T = T & And([_nnfify(l) for l in lits])

    if not T.satisfiable():
        return 0

    return dsharp.compile(T.to_CNF(), smooth=True).model_count()
Пример #18
0
def test_mark_deterministic():
    s = And()
    t = And()

    assert not s.marked_deterministic()
    assert not t.marked_deterministic()

    s.mark_deterministic()

    assert s.marked_deterministic()
    assert not t.marked_deterministic()

    t.mark_deterministic()

    assert s.marked_deterministic()
    assert t.marked_deterministic()

    del s

    assert t.marked_deterministic()
Пример #19
0
    def exactly_one(self, inputs: list) -> NNF:
        """
        Exactly one variable can be true of the input

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

        Returns
        -------
        nnf.NNF: And(at_most_one, at_least_one)

        """
        at_most_one = _ConstraintBuilder.at_most_one(self, inputs)
        at_least_one = _ConstraintBuilder.at_least_one(self, inputs)

        if not (at_most_one and at_least_one):
            raise ValueError
        return And({at_most_one, at_least_one})
Пример #20
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)
Пример #21
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)
Пример #22
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})})
Пример #23
0
def CNF(draw):
    sentence = And(draw(st.frozensets(clauses())))
    return sentence
Пример #24
0
def models(draw):
    return And(
        Var(name, draw(st.booleans()))
        for name in range(1, draw(st.integers(min_value=1, max_value=9))))
Пример #25
0
 def solve(self):
     return And(self.constraints).solve()
Пример #26
0
 def is_satisfiable(self):
     return And(self.constraints).satisfiable()
Пример #27
0
 def negate(self):
     return And(self.constraints).negate()
Пример #28
0
 def valid(self):
     return And(self.constraints).valid()
Пример #29
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))
Пример #30
0
    def build(self, propositions) -> 'NNF':
        """Builds a SAT constraint from a ConstraintBuilder instance.

        To handle a user using the groupby feature, the partition helper
        function is used to partition a constraint's inputs.
        We then apply the SAT constraint over each partitioned set of inputs.

        Note
        ----
        There is unique handling for the implies all constraint
        where a user could have the following cases,
        1) implies_all used on a class or bound method:
            For this case, we can have inputs (list of dictionaries)
            and left or right attributes, which must be validated
            by utils/unpack_variables. We set left and right as
            empty lists if they're None to merging the propositional
            variables simple in _ConstraintBuilder.implies_all

        2) implies_all used for direct constraint addition:
            If called as a function, the user must provide both
            a left and right side of the implication. This is
            ensured in bauhaus/core.constraint.constraint_by_function.
            No positional arguments are allowed to be passed based
            on the function definition of core/constraint.implies_all,
            so inputs will be empty for this case.

        Arguments
        ---------
        propositions : defaultdict(weakref.WeakValueDictionary)
            Stores instances in the form [classname] -> [instance_id: object]

        Returns
        -------
        constraint : nnf.NNF
            A built NNF constraint

        """
        if self._constraint is _ConstraintBuilder.implies_all:
            left_vars = unpack(self._left, propositions) if self._left else []
            right_vars = unpack(self._right,
                                propositions) if self._right else []
            if not self._func:
                inputs = []
            else:
                # retrieve dictionary of inputs
                inputs = self.get_implication_inputs(propositions)
                if not any(inputs.values()) and not right_vars:
                    raise ValueError(
                        f"The '{self}' cannot be built"
                        " as it is decorating a class and"
                        " the right implication variables are not"
                        " provided. If it is decorating a method,"
                        " ensure that the method's return is"
                        " valid for bauhaus or for the nnf library."
                        " Check your decorator signature and set"
                        " the 'right' keyword argument to such a value.")

            constraints = []
            for input_set in self.partition(inputs):
                constraints.append(
                    self._constraint(self, input_set, left_vars, right_vars))
            return And(constraints)

        inputs = self.get_inputs(propositions)
        if not inputs:
            raise ValueError(inputs)

        constraints = []
        for input_set in self.partition(inputs):
            if self._constraint is _ConstraintBuilder.at_most_k:
                constraints.append(self._constraint(self, input_set,
                                                    k=self._k))
            else:
                constraints.append(self._constraint(self, input_set))
        return And(constraints)