def test_solve(sentence: nnf.NNF): solution = sentence.solve() if solution is None: assert not sentence.satisfiable() else: assert sentence.satisfiable() assert sentence.satisfied_by(solution)
def test_simplify_preserves_meaning(sentence: nnf.NNF, merge_nodes): simple = sentence.simplify(merge_nodes) assert sentence.equivalent(simple) for model in sentence.models(): assert simple.satisfied_by(model) for model in simple.models(): assert sentence.condition(model).simplify(merge_nodes) == nnf.true
def test_models_deterministic_sanity(sentence: nnf.NNF): """Running _models_deterministic() on a non-deterministic decomposable sentence may return duplicate models but should not return unsatisfying models and should return each satisfying model at least once. """ assert model_set(sentence._models_decomposable()) == model_set( sentence._models_deterministic())
def test_simplify_solves_DNNF_satisfiability(sentence: nnf.NNF, merge_nodes): if sentence.satisfiable(): event("Sentence is satisfiable") assert sentence.simplify(merge_nodes) != nnf.false else: event("Sentence is not satisfiable") assert sentence.simplify(merge_nodes) == nnf.false
def test_models(sentence: nnf.NNF): real_models = [ model for model in all_models(sentence.vars()) if sentence.satisfied_by(model) ] models = list(sentence.models()) assert len(real_models) == len(models) assert model_set(real_models) == model_set(models)
def test_implies(a: nnf.NNF, b: nnf.NNF): if a.implies(b): event("Implication") for model in a.models(): assert b.condition(model).valid() else: event("No implication") assert any(not b.condition(model).valid() for model in a.models())
def test_to_MODS(sentence: nnf.NNF): assume(len(sentence.vars()) <= 5) mods = sentence.to_MODS() assert mods.is_MODS() assert mods.is_DNF() assert mods.is_DNF(strict=True) assert mods.smooth() assert isinstance(mods, Or) assert mods.model_count() == len(mods.children)
def test_validity(sentence: nnf.NNF): if sentence.valid(): event("Valid sentence") assert all( sentence.satisfied_by(model) for model in nnf.all_models(sentence.vars())) else: event("Invalid sentence") assert any(not sentence.satisfied_by(model) for model in nnf.all_models(sentence.vars()))
def test_simplify_merges_internal_nodes(sentence: nnf.NNF): if any( any(type(node) == type(child) for child in node.children) for node in sentence.walk() if isinstance(node, nnf.Internal)): event("Sentence contained immediately mergeable nodes") # Nodes may also be merged after intermediate nodes are removed for node in sentence.simplify().walk(): if isinstance(node, nnf.Internal): for child in node.children: assert type(node) != type(child)
def test_simplify_eliminates_bools(sentence: nnf.NNF, merge_nodes): assume(sentence != nnf.true and sentence != nnf.false) if any(node == nnf.true or node == nnf.false for node in sentence.walk()): event("Sentence contained booleans originally") sentence = sentence.simplify(merge_nodes) if sentence == nnf.true or sentence == nnf.false: event("Sentence simplified to boolean") else: for node in sentence.walk(): assert node != nnf.true and node != nnf.false
def test_DNNF_sat_strategies(sentence: nnf.NNF, merge_nodes): sat = sentence.satisfiable() if sat: assert sentence.simplify(merge_nodes) != nnf.false assert amc.SAT(sentence) event("Sentence satisfiable") else: assert sentence.simplify(merge_nodes) == nnf.false assert not amc.SAT(sentence) event("Sentence not satisfiable")
def test_smoothing(sentence: nnf.NNF): if not sentence.smooth(): event("Sentence not smooth yet") smoothed = sentence.make_smooth() assert type(sentence) is type(smoothed) assert smoothed.smooth() assert sentence.equivalent(smoothed) assert smoothed.make_smooth() == smoothed else: event("Sentence already smooth") assert sentence.make_smooth() == sentence
def test_implicates_implicants_negation_rule(sentence: nnf.NNF): """Any implicate is also a negated implicant of the negated sentence. .implicates() gives some implicates, and .implicants() gives all implicants. So sentence.negate().implicants().negate() gives all implicates, and sentence.negate().implicates().negate() gives some implicants. """ assume(sentence.size() <= 30) assert (sentence.negate().implicants().negate().children >= sentence.implicates().children) assert (sentence.negate().implicates().negate().children <= sentence.implicants().children)
def test_pairwise(sentence: nnf.NNF): new = sentence.make_pairwise() assert new.equivalent(sentence) if new not in {nnf.true, nnf.false}: assert all( len(node.children) == 2 for node in new.walk() if isinstance(node, nnf.Internal))
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
def test_tseitin(sentence: nnf.NNF): # Assumption to reduce the time in testing assume(sentence.size() <= 10) T = tseitin.to_CNF(sentence) assert T.is_CNF() assert T.is_CNF(strict=True) assert tseitin.to_CNF(T) == T assert T.forget_aux().equivalent(sentence) models = list(complete_models(T.models(), sentence.vars() | T.vars())) for mt in models: assert sentence.satisfied_by(mt) assert len(models) == sentence.model_count()
def test_dimacs_cnf_serialize_accepts_only_cnf(sentence: nnf.NNF): if sentence.is_CNF(): event("CNF sentence") dimacs.dumps(sentence, mode='cnf') else: event("Not CNF sentence") with pytest.raises(dimacs.EncodeError): dimacs.dumps(sentence, mode='cnf')
def test_equivalent(sentence: nnf.NNF): assert sentence.equivalent(sentence) assert sentence.equivalent(sentence | nnf.false) assert sentence.equivalent(sentence & (nnf.Var('A') | ~nnf.Var('A'))) if sentence.satisfiable(): assert not sentence.equivalent(sentence & nnf.false) assert not sentence.equivalent(sentence & nnf.Var('A')) else: assert sentence.equivalent(sentence & nnf.false) assert sentence.equivalent(sentence & nnf.Var('A'))
def dump(obj: NNF, fp: t.TextIO, *, num_variables: t.Optional[int] = None, var_labels: t.Optional[t.Dict[Name, int]] = None, comment_header: t.Optional[str] = None, mode: str = 'sat') -> None: """Dump a sentence into an open file in a DIMACS format. Variable names have to be integers. If the variables in the sentence you want to dump are not integers, you can pass a ``var_labels`` dictionary to map names to integers. :param obj: The sentence to dump. :param fp: The open file. :param num_variables: Override the number of variables, in case there are variables that don't appear in the sentence. :param var_labels: A dictionary mapping variable names to integers, to rename non-integer variables. :param comment_header: A comment to include at the top of the file. May include newlines. :param mode: Either ``'sat'`` to dump in the general SAT format, or ``'cnf'`` to dump in the specialized CNF format. """ if num_variables is None: if var_labels is None: names = t.cast("t.FrozenSet[int]", obj.vars()) for name in names: if not isinstance(name, int) or name <= 0: raise EncodeError( "{!r} is not an integer > 0. Try supplying a " "var_labels dictionary.".format(name)) num_vars = max(names, default=0) # type: int else: num_vars = max(var_labels.values(), default=0) else: num_vars = num_variables if mode == 'sat': _dump_sat(obj, fp, num_variables=num_vars, var_labels=var_labels, comment_header=comment_header) elif mode == 'cnf': _dump_cnf(obj, fp, num_variables=num_vars, var_labels=var_labels, comment_header=comment_header)
def test_forget(sentence: nnf.NNF): # Assumption to reduce the time in testing assume(sentence.size() <= 15) # Test that forgetting a backbone variable doesn't change the theory T = sentence & Var('added_var') assert sentence.equivalent(T.forget({'added_var'})) # Test the tseitin projection assert sentence.equivalent(sentence.to_CNF().forget_aux()) # Test that models of a projected theory are consistent with the original names = list(sentence.vars())[:2] T = sentence.forget(names) assert not any([v in T.vars() for v in names]) for m in T.models(): assert sentence.condition(m).satisfiable()
def print_stats(sentence: NNF) -> None: if sentence.is_CNF(): print("Sentence is in CNF.") if sentence.decomposable(): print("Sentence is decomposable.") if sentence.smooth(): print("Sentence is smooth.") print("Variables: {}".format(len(sentence.vars()))) print("Size: {}".format(sentence.size())) if sentence.is_CNF(): print("Clauses: {}".format(len(sentence))) # type: ignore sizes = {len(clause) for clause in sentence} # type: ignore low, high = min(sizes), max(sizes) if low == high: print("Clause size: {}".format(low)) else: print("Clause size: {}-{}".format(low, high))
def test_implicates(sentence: nnf.NNF): implicates = sentence.implicates() assert implicates.equivalent(sentence) assert implicates.is_CNF(strict=True) assert not any(a.children < b.children for a in implicates.children for b in implicates.children)
def test_is_MODS(sentence: nnf.NNF): assert sentence.is_MODS()
def test_implicants(sentence: nnf.NNF): implicants = sentence.implicants() assert implicants.equivalent(sentence) assert implicants.is_DNF() assert not any(a.children < b.children for a in implicants.children for b in implicants.children)
def test_pickling(sentence: nnf.NNF): new = pickle.loads(pickle.dumps(sentence)) assert sentence == new assert sentence is not new assert sentence.object_count() == new.object_count()
def test_project(sentence: nnf.NNF): # Test that we get the same as projecting and forgetting assume(len(sentence.vars()) > 3) vars1 = list(sentence.vars())[:2] vars2 = list(sentence.vars())[2:] assert sentence.forget(vars1).equivalent(sentence.project(vars2))
def test_iff(a: nnf.NNF, b: nnf.NNF): c = operators.iff(a, b) for model in nnf.all_models(c.vars()): assert ((a.satisfied_by(model) == b.satisfied_by(model) ) == c.satisfied_by(model))
def test_implied_by(a: nnf.NNF, b: nnf.NNF): c = operators.implied_by(a, b) for model in nnf.all_models(c.vars()): assert ((b.satisfied_by(model) and not a.satisfied_by(model)) != c.satisfied_by(model))
def test_nor(a: nnf.NNF, b: nnf.NNF): c = operators.nor(a, b) for model in nnf.all_models(c.vars()): assert ((a.satisfied_by(model) or b.satisfied_by(model)) != c.satisfied_by(model))
def test_implicants_idempotent(sentence: nnf.NNF): assume(len(sentence.vars()) <= 6) implicants = sentence.implicants() implicates = sentence.implicates() assert implicants.implicants() == implicants assert implicates.implicants() == implicants