Filtering...

dimacs

books/centaur/satlink/dimacs
other
(in-package "SATLINK")
other
(include-book "cnf")
other
(include-book "std/strings/decimal" :dir :system)
other
(include-book "std/strings/strnatless" :dir :system)
other
(include-book "std/strings/cat" :dir :system)
other
(include-book "std/io/base" :dir :system)
other
(include-book "std/typed-lists/character-listp" :dir :system)
other
(defsection dimacs
  :parents (satlink)
  :short "DIMACS format is a standard interface to SAT solvers."
  :long "<p>Many SAT solvers accept a common format for input and output that
is used in SAT solving competititons; <a href="http://www.satcompetition.org/2009/format-benchmarks2009.html">
this page</a> gives the competitions' official description.</p>

<p>The basic input format is as follows.  At the top you can have <i>comment
lines</i> that start with a @('c'), like this:</p>

@({
    c This line is a comment.
})

<p>Then comes the <i>problem line</i>, which starts with a @('p') and then says
how many variables and clauses there are.  For instance, here is a problem line
that says this is a CNF problem with 3 variables and 4 clauses:</p>

@({
    p cnf 3 4
})

<p>Finally the clauses are listed.  Each clause is represented as a list of
numbers like @('3') and @('-42').  A positive number like @('3') represents a
positive occurrence of variable 3.  A negative number like @('-42') represents
a negated occurrence of variable 42.</p>

<p>The number @('0') is treated in a special way: it is not a variable, but
instead marks the end of each clause.  This allows a single clause to be split
up over multiple lines.</p>


<h3>Input Format Questions and <i>Clean</i> Formulas</h3>

<p>If we could be sure the above document was the standard, we could very
easily convert from our @(see cnf) representation into it.  The only twist is
that 0 isn't a valid identifier in DIMACS format, but it is a valid identifier
in our representation.  To get around this, we can just add one to every
variable number throughout the problem.</p>

<p>However, the SAT competitions generally use a simplified version of the
DIMACS format.  For instance, the 2012 SAT competititon used the same <a
href='http://www.satcompetition.org/2011/rules.pdf'>rules</a> as the 2011
competititon and for previous years, and restrict the format so that the
solver may assume:</p>

<ul>

<li>Each variable, from 1 to the number of variables specified on the problem
line, is used at least once in some clause.</li>

<li>The clauses are distinct and may not simultaneously contain opposite
literals.</li>

<li>There are exactly the right number of clauses given in the problem
line.</li>

<li>Clauses are kept on the same line.</li>

</ul>

<p>It appears that the rules do not rule out empty clauses.</p>

<p><b>BOZO</b> Eventually, we would like to make sure we produce DIMACS files
that conform to these more stringent requirements, since otherwise a SAT solver
that believes it can make these assumptions may make a mistake.  We may
eventually add a cleaning phase to our SAT integration, to ensure that we only
call the SAT solver on "clean" formulas.</p>")
other
(defsection dimacs-export
  :parents (dimacs)
  :short "Writer to translate @(see cnf) formulas into DIMACS files."
  :long "<p>The basic idea here is that @('acc') is a character list with the
output we have printed in reverse order.  This means @('(cons char acc)') is
the same as printing a char, @('(str::revappend-chars str acc)') is the same as
printing @('str'), etc.  See @(see str::revappend-chars) for more background
about this basic approach.</p>"
  (define dimacs-write-lit
    ((lit litp) (acc character-listp))
    :returns (acc character-listp :hyp :guard)
    :inline t
    (b* ((negatedp (int= (lit->neg lit) 1)) (id+1 (+ 1 (lit->var lit)))
        (acc (if negatedp
            (cons #\- acc)
            acc)))
      (revappend-nat-to-dec-chars id+1 acc)))
  (define dimacs-write-clause
    ((clause lit-listp) (acc character-listp))
    :returns (acc character-listp :hyp :guard)
    (b* (((when (atom clause)) (cons #\
 (cons #\0 acc))) (acc (dimacs-write-lit (car clause)
            acc))
        (acc (cons #\  acc)))
      (dimacs-write-clause (cdr clause)
        acc)))
  (define dimacs-write-clauses
    ((clauses lit-list-listp) (acc character-listp))
    :returns (acc character-listp :hyp :guard)
    (b* (((when (atom clauses)) acc) (acc (dimacs-write-clause (car clauses)
            acc)))
      (dimacs-write-clauses (cdr clauses)
        acc)))
  (define dimacs-write-formula
    ((formula lit-list-listp))
    :returns (mv (dimacs-text stringp :hyp :guard)
      (max-index (equal max-index
          (max-index-formula formula))))
    (b* ((max-index (max-index-formula formula)) (dimacs-num-vars (+ 1 max-index))
        (acc nil)
        (acc (revappend-chars "p cnf " acc))
        (acc (revappend-nat-to-dec-chars dimacs-num-vars
            acc))
        (acc (cons #\  acc))
        (acc (revappend-nat-to-dec-chars (len formula)
            acc))
        (acc (cons #\
 acc))
        (acc (dimacs-write-clauses formula
            acc)))
      (mv (rchars-to-string acc)
        max-index)))
  (define dimacs-export
    ((formula lit-list-listp) &key
      (filename stringp)
      (state 'state))
    :returns (mv (successp "We can fail, e.g., due to file permissions."
        booleanp
        :rule-classes :type-prescription)
      (max-index "Maximum index used in the formula."
        (equal max-index
          (max-index-formula formula)))
      (state state-p1
        :hyp (force (state-p1 state))))
    (b* ((filename (mbe :logic (if (stringp filename)
             filename
             "")
           :exec filename)) ((mv channel state) (open-output-channel filename
            :character state))
        ((unless channel) (mv nil
            (max-index-formula formula)
            state))
        ((mv str max-index) (dimacs-write-formula formula))
        (state (princ$ str
            channel
            state))
        (state (close-output-channel channel
            state)))
      (mv t max-index state))))