Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
""" SAT-Solvers via DIMACS Files
Sage supports calling SAT solvers using the popular DIMACS format. This module implements infrastructure to make it easy to add new such interfaces and some example interfaces.
Currently, interfaces to **RSat** and **Glucose** are included by default.
.. note::
Our SAT solver interfaces are 1-based, i.e., literals start at 1. This is consistent with the popular DIMACS format for SAT solving but not with Pythion's 0-based convention. However, this also allows to construct clauses using simple integers.
AUTHORS:
- Martin Albrecht (2012): first version
Classes and Methods ------------------- """ ############################################################################## # Copyright (C) 2012 Martin Albrecht <martinralbrecht@googlemail.com> # Distributed under the terms of the GNU General Public License (GPL) # The full text of the GPL is available at: # http://www.gnu.org/licenses/ ##############################################################################
""" Generic DIMACS Solver.
.. note::
Usually, users won't have to use this class directly but some class which inherits from this class.
.. automethod:: __init__ .. automethod:: __call__ """
""" Construct a new generic DIMACS solver.
INPUT:
- ``command`` - a named format string with the command to run. The string must contain {input} and may contain {output} if the solvers writes the solution to an output file. For example "sat-solver {input}" is a valid command. If ``None`` then the class variable ``command`` is used. (default: ``None``)
- ``filename`` - a filename to write clauses to in DIMACS format, must be writable. If ``None`` a temporary filename is chosen automatically. (default: ``None``)
- ``verbosity`` - a verbosity level, where zero means silent and anything else means verbose output. (default: ``0``)
- ``**kwds`` - accepted for compatibility with other solves, ignored.
TESTS::
sage: from sage.sat.solvers.dimacs import DIMACS sage: DIMACS() DIMACS Solver: '' """
else:
""" TESTS::
sage: from sage.sat.solvers.dimacs import DIMACS sage: DIMACS(command="iliketurtles {input}") DIMACS Solver: 'iliketurtles {input}' """
""" TESTS::
sage: from sage.sat.solvers.dimacs import DIMACS sage: d = DIMACS(command="iliketurtles {input}") sage: del d """
""" Return a *new* variable.
INPUT:
- ``decision`` - accepted for compatibility with other solvers, ignored.
EXAMPLES::
sage: from sage.sat.solvers.dimacs import DIMACS sage: solver = DIMACS() sage: solver.var() 1 """
""" Return the number of variables.
EXAMPLES::
sage: from sage.sat.solvers.dimacs import DIMACS sage: solver = DIMACS() sage: solver.var() 1 sage: solver.var(decision=True) 2 sage: solver.nvars() 2 """
""" Add a new clause to set of clauses.
INPUT:
- ``lits`` - a tuple of integers != 0
.. note::
If any element ``e`` in ``lits`` has ``abs(e)`` greater than the number of variables generated so far, then new variables are created automatically.
EXAMPLES::
sage: from sage.sat.solvers.dimacs import DIMACS sage: solver = DIMACS() sage: solver.var() 1 sage: solver.var(decision=True) 2 sage: solver.add_clause( (1, -2 , 3) ) sage: solver DIMACS Solver: '' """
""" Write DIMACS file.
INPUT:
- ``filename`` - if ``None`` default filename specified at initialization is used for writing to (default: ``None``)
EXAMPLES::
sage: from sage.sat.solvers.dimacs import DIMACS sage: fn = tmp_filename() sage: solver = DIMACS(filename=fn) sage: solver.add_clause( (1, -2 , 3) ) sage: _ = solver.write() sage: for line in open(fn).readlines(): ....: print(line) p cnf 3 1 1 -2 3 0
sage: from sage.sat.solvers.dimacs import DIMACS sage: fn = tmp_filename() sage: solver = DIMACS() sage: solver.add_clause( (1, -2 , 3) ) sage: _ = solver.write(fn) sage: for line in open(fn).readlines(): ....: print(line) p cnf 3 1 1 -2 3 0 """
""" Return original clauses.
INPUT:
- ``filename`` - if not ``None`` clauses are written to ``filename`` in DIMACS format (default: ``None``)
OUTPUT:
If ``filename`` is ``None`` then a list of ``lits, is_xor, rhs`` tuples is returned, where ``lits`` is a tuple of literals, ``is_xor`` is always ``False`` and ``rhs`` is always ``None``.
If ``filename`` points to a writable file, then the list of original clauses is written to that file in DIMACS format.
EXAMPLES::
sage: from sage.sat.solvers.dimacs import DIMACS sage: fn = tmp_filename() sage: solver = DIMACS() sage: solver.add_clause( (1, 2, 3) ) sage: solver.clauses() [((1, 2, 3), False, None)]
sage: solver.add_clause( (1, 2, -3) ) sage: solver.clauses(fn) sage: print(open(fn).read()) p cnf 3 2 1 2 3 0 1 2 -3 0 <BLANKLINE> """ else:
continue
def render_dimacs(clauses, filename, nlits): """ Produce DIMACS file ``filename`` from ``clauses``.
INPUT:
- ``clauses`` - a list of clauses, either in simple format as a list of literals or in extended format for CryptoMiniSat: a tuple of literals, ``is_xor`` and ``rhs``.
- ``filename`` - the file to write to
- ``nlits -- the number of literals appearing in ``clauses``
EXAMPLES::
sage: from sage.sat.solvers.dimacs import DIMACS sage: fn = tmp_filename() sage: solver = DIMACS() sage: solver.add_clause( (1, 2, -3) ) sage: DIMACS.render_dimacs(solver.clauses(), fn, solver.nvars()) sage: print(open(fn).read()) p cnf 3 1 1 2 -3 0 <BLANKLINE>
This is equivalent to::
sage: solver.clauses(fn) sage: print(open(fn).read()) p cnf 3 1 1 2 -3 0 <BLANKLINE>
This function also accepts a "simple" format::
sage: DIMACS.render_dimacs([ (1,2), (1,2,-3) ], fn, 3) sage: print(open(fn).read()) p cnf 3 2 1 2 0 1 2 -3 0 <BLANKLINE> """ else:
closing = lits[-1] if rhs else -lits[-1] fh.write("x" + " ".join(map(str, lits[:-1])) + " %d 0\n"%closing) else:
""" Run 'command' and collect output.
INPUT:
- ``assumptions`` - ignored, accepted for compatibility with other solvers (default: ``None``)
TESTS:
This class is not meant to be called directly::
sage: from sage.sat.solvers.dimacs import DIMACS sage: fn = tmp_filename() sage: solver = DIMACS(filename=fn) sage: solver.add_clause( (1, -2 , 3) ) sage: solver() Traceback (most recent call last): ... ValueError: No SAT solver command selected. """ raise NotImplementedError("Assumptions are not supported for DIMACS based solvers.")
if "{output}" in command: output_filename = tmp_filename() command = command.format(input=self._headname, output=output_filename)
args = shlex.split(command)
try: process = subprocess.Popen(args, stdout=subprocess.PIPE) except OSError: raise OSError("Could run '%s', perhaps you need to add your SAT solver to $PATH?"%(" ".join(args)))
try: while process.poll() is None: for line in iter(process.stdout.readline,''): if get_verbose() or self._verbosity: print(line) sys.stdout.flush() self._output.append(line) sleep(0.1) if output_filename: self._output.extend(open(output_filename).readlines()) except BaseException: process.kill() raise
""" An instance of the RSat solver.
For information on RSat see: http://reasoning.cs.ucla.edu/rsat/ """
""" Solve this instance.
INPUT:
- ``assumptions`` - ignored, accepted for compatibility with other solvers (default: ``None``)
OUTPUT:
- If this instance is SAT: A tuple of length ``nvars()+1`` where the ``i``-th entry holds an assignment for the ``i``-th variables (the ``0``-th entry is always ``None``).
- If this instance is UNSAT: ``False``
EXAMPLES::
sage: from sage.sat.boolean_polynomials import solve as solve_sat sage: F,s = mq.SR(1,1,1,4,gf2=True,polybori=True).polynomial_system() sage: solve_sat(F, solver=sage.sat.solvers.RSat) # optional - RSat """ DIMACS.__call__(self)
s = [None] + [False for _ in range(self.nvars())] for line in self._output: if line.startswith("c"): continue if line.startswith("s"): if "UNSAT" in line: return False if line.startswith("v"): lits = map(int, line[2:-2].strip().split(" ")) for e in lits: s[abs(e)] = e>0 return tuple(s)
""" An instance of the Glucose solver.
For information on Glucose see: http://www.lri.fr/~simon/?page=glucose """
""" Solve this instance.
INPUT:
- ``assumptions`` - ignored, accepted for compatibility with other solvers (default: ``None``)
OUTPUT:
- If this instance is SAT: A tuple of length ``nvars()+1`` where the ``i``-th entry holds an assignment for the ``i``-th variables (the ``0``-th entry is always ``None``).
- If this instance is UNSAT: ``False``
EXAMPLES::
sage: from sage.sat.boolean_polynomials import solve as solve_sat sage: F,s = mq.SR(1,1,1,4,gf2=True,polybori=True).polynomial_system() sage: solve_sat(F, solver=sage.sat.solvers.Glucose) # optional - Glucose """ DIMACS.__call__(self)
for line in self._output: if line.startswith("c"): continue if line.startswith("s"): if "UNSAT" in line: return False try: s = map(int, line[:-2].strip().split(" ")) s = (None,) + tuple(e>0 for e in s) return s except ValueError: pass return False |