Source code for nnf.pysat

import importlib
import typing as t

from nnf import And, Or, Var, config, true
from nnf.util import Model, Name

try:
    from pysat.solvers import Solver
except ImportError:
    available = False
    try:
        importlib.import_module("pysat")
    except ImportError:
        _wrong_pysat = False
    else:
        _wrong_pysat = True
else:
    #: Indicates whether the PySAT library is installed and available for use.
    available = True
    _wrong_pysat = False


__all__ = ("satisfiable", "solve", "models", "available")


def _encode_CNF(
    sentence: And[Or[Var]],
) -> t.Tuple[t.List[t.List[int]], t.Dict[int, Name]]:
    """Encode a CNF sentence into a list of lists of ints fit for PySAT."""

    if not sentence.is_CNF():
        raise ValueError("Sentence must be in CNF")

    decode = dict(enumerate(sentence.vars(), start=1))
    encode = {v: k for k, v in decode.items()}

    clauses = [
        [encode[var.name] if var.true else -encode[var.name] for var in clause]
        for clause in sentence
    ]

    return clauses, decode


def _solver_for(
    sentence: And[Or[Var]], name: t.Optional[str] = None
) -> t.Tuple["Solver", t.Dict[int, Name]]:
    """Return a Solver loaded with the sentence and a dictionary to decode it.

    Callers of this function MUST use the solver as a context manager or ensure
    that ``.delete()`` is called on it, to prevent memory leaks.
    """

    if not available:
        if _wrong_pysat:
            raise RuntimeError(
                "There is a `pysat` module, but it isn't PySAT. "
                "Did you pip install `pysat` instead of `python-sat`?"
            )
        raise RuntimeError(
            "`pysat` is not installed. Try `pip install python-sat`?"
        )

    if name is None:
        name = config.pysat_solver

    clauses, decode = _encode_CNF(sentence)
    solver = Solver(bootstrap_with=clauses, name=name)

    return solver, decode


[docs]def satisfiable(sentence: And[Or[Var]]) -> bool: """Return whether a CNF sentence is satisfiable.""" if sentence == true: # Feeding this to MapleSAT causes a segfault return True solver, _ = _solver_for(sentence) with solver: return solver.solve()
[docs]def solve(sentence: And[Or[Var]]) -> t.Optional[Model]: """Return a model of a CNF sentence, or ``None`` if unsatisfiable.""" if sentence == true: return {} solver, decode = _solver_for(sentence) with solver: if not solver.solve(): return None return {decode[abs(num)]: num > 0 for num in solver.get_model()}
[docs]def models(sentence: And[Or[Var]]) -> t.Iterator[Model]: """Yield all models of a CNF sentence.""" if sentence == true: yield {} return solver, decode = _solver_for(sentence) with solver: if not solver.solve(): return for model in solver.enum_models(): yield {decode[abs(num)]: num > 0 for num in model}