drup

A Verified DRUP Proof Checker

As the title suggests, a verified implementation of a checker for propositional unsatisfiability proofs in the DRUP format that is produced by many solvers. The core of the checker is written in Why3, which is extracted to OCaml, compiled natively, and exported as a C library with Python bindings.

  • The checker also supports RAT clauses, so DRAT proofs are accepted.
  • The current implementation is not optimized, and will be considerably slower than DRAT-trim on large proofs (see performance below).
  • Accordingly, the frontend does not accept proofs in binary format.

Installation

If you use a recent Linux distribution on x86_64, you should be able to install the compiled wheel from PyPI:

$ pip install drup

Otherwise, you need to have OCaml (>= 4.12), Ctypes (>=0.20), Why3 (>= 1.5.1), and Dune (>=2.9.3) installed. The most straightforward way to install these is to use opam, which is available in most package systems, and then install Why3 and Dune (a sufficiently recent version of OCaml should already be installed with Opam):

$ opam install why3 dune

If you do not intend to check the verification of the library or develop it further, then you do not need to install Why3's IDE or any of the solvers that it supports.

Once OCaml and Why3 are installed, make sure that Python build is installed:

$ pip install build

Then, clone this repository, build, and install the package:

$ git clone https://github.com/cmu-transparency/verified_rup.git
$ cd verified_rup
$ python -m build
$ pip install dist/*.whl

Usage

Command line interface

The package provides a command line interface for checking proofs stored in files:

$ drup --help
usage: drup [-h] [-d] [-v] dimacs drup

Checks DRUP & DRAT proofs against DIMACS source. Returns 0 if the proof is valid, -1 if not, or a negative error code if the input is invalid.

positional arguments:
  dimacs            Path to a DIMACS CNF formula
  drup              Path to a DRUP/DRAT proof

optional arguments:
  -h, --help        show this help message and exit
  -d, --derivation  Check each step, ignore absence of empty clause
  -v, --verbose     Print detailed information about failed checks

For more information visit https://github.com/cmu-transparency/verified_rup

As a Python module

See the documentation for details of the API. The primary function is drup.check_proof, or alternatively, drup.check_derivation to check each step of the proof, ignoring the absence of an empty clause). There are corresponding convenience functions check_proof_from_strings and check_proof_from_files, similarly for check_derivation.

The following example uses CNFgen to generate a PHP instance, and PySAT to solve it and generate a DRUP proof. To illustrate the verbose output given for failed checks, only the first ten clauses of the proof are checked against the proof.

import drup
import cnfgen
from pysat.formula import CNF
from pysat.solvers import Solver

dimacs = cnfgen.BinaryPigeonholePrinciple(4, 3).dimacs()
cnf = CNF(from_string=dimacs).clauses
g4 = Solver(name='g4', with_proof=True, bootstrap_with=cnf)
g4.solve()
proof = [[int(l) for l in c.split(' ')[:-1]] for c in g4.get_proof()]

drup.check_proof(cnf[:10], proof, verbose=True)

This gives a CheckerResult object with the following information:

CheckerResult(
    Outcome.INVALID, 
    [], 
    RupInfo([4, 6, 7, 8], [-4, -6, -7, -8, 3, 5]), 
    RatInfo(
        [4, 6, 7, 8], 
        [-4, -3], 
        RupInfo([6, 7, 8, -3], [-6, -7, -8, 5, 3, -4])
    )
)

The RupInfo component relates that RUP verification failed on the first clause of the proof, 4 6 7 8 0, after propagating the literals -4, -6, -7, -8, 3, 5, and failing to find more opposites to propagate. Likewise, the RatInfo component relates that RAT verification on this clause failed when checking the pivot on clause -4 -3 0. The resolvent of these clauses, 6 7 8 -3 0, failed RUP verification after propagating -6 -7 -8 5 3 -4.

Performance

At present, the implementation of RUP checking is not optimized, and drop lines are ignored. Unit propagation does not take advantage of watched literals, and does not use mutable data structures. Nonetheless, the performance compares well to that of DRAT-trim on small proofs (<200 variables, a few hundred clauses).

We measure this on random unsatisfiable instances generated by the procedure described in [1]. To evaluate the performance of DRAT-trim without the overhead of creating and tearing down a new process for each instance, we compiled it into a library with the same check_from_strings interface as the C library, and called it using ctypes. In the table below, each configuration is run on 10,000 instances, with proofs generated by Glucose 4.

# vars # clauses (avg) pf len (avg) drup (sec, avg) drat-trim (sec, avg)
25 147.7 7.3 0.001 0.085
50 280.5 14.2 0.006 0.179
75 413.5 26.3 0.022 0.217
100 548.2 40.6 0.068 0.172
150 811.8 102.7 0.407 0.326
200 1079.5 227.9 1.916 0.292

References

[1] Daniel Selsam, Matthew Lamm, Benedikt Bünz, Percy Liang, Leonardo de Moura, David L. Dill. Learning a SAT Solver from Single-Bit Supervision. International Conference on Learning Representations (ICLR), 2019.

Verification

The verification can be examined by running src/librupchecker/rup_pure.mlw in Why3, or by checking the Why3 session in src/librupchecker/rup_pure/why3session.xml. The proof was developed using Why3 1.5.1, Alt-Ergo 2.4.0, Z3 4.8.6, and CVC4 1.8. Verification has not been attempted with earlier versions of Why3 or the provers.

The primary contract on the proof checker is as follows:

requires { no_redundant_clauses cnf /\ no_trivial_clauses cnf }
requires { no_redundant_clauses pf /\ no_trivial_clauses pf }
ensures { match result with
            | Valid -> valid_proof cnf pf
            | _ -> proof_failure orig result
            end }

The bindings used by this library take care of removing redundant and trivial clauses. The valid_proof predicate is a straightforward translation of DRAT certification requirements. A proof_failure result provides additional assurances about the verbose output of the checker.

predicate proof_failure (cnf : cnf) (result : result) =
    match result with
    | Valid -> false
    | InvalidEmpty steps rup_info -> rup_failure (steps ++ cnf) rup_info
    | InvalidStep steps rup_info rat_info -> 
        rup_failure (steps ++ cnf) rup_info /\ rat_failure (steps ++ cnf) rat_info
    end

An InvalidEmpty result indicates all of the listed steps are valid, but the empty clause was not derived, as witnessed by the provided rup_info. This is only returned when all of the steps in the proof are valid except an empty clause at the end. The rup_info component ensures that the empty clause is not RUP, and that the unit chain used to conclude this is exhaustive.

predicate rup_failure (cnf : cnf) (info : rup_info) =
    not (rup cnf info.rup_clause) /\
    match info.rup_clause with
    | Nil -> 
        let cnf' = bcp cnf info.chain in
        is_unit_chain cnf info.chain /\ 
        forall chain' . is_unit_chain cnf' chain' -> 
                        forall c . mem c (bcp cnf' chain') <-> mem c cnf'
    | Cons _ _ -> 
        let cnf' = bcp (cnf ++ (negate_clause info.rup_clause)) info.chain in
        is_unit_chain (cnf ++ (negate_clause info.rup_clause)) info.chain /\ 
        forall chain' . is_unit_chain cnf' chain' -> 
                        forall c . mem c (bcp cnf' chain') <-> mem c cnf'
    end

An InvalidStep result indicates that the steps are valid up to some non-empty step. The next step in the certificate following steps is invalid, as witnessed by the provided rup_info and rat_info. In addition to the assurances on rup_info described above, rat_info provides that the identified pivot clause is not RUP.

predicate rat_failure (cnf : cnf) (info : rat_info) =
    not (rat cnf info.rat_clause) /\
    match info.rat_clause with
    | Nil -> false
    | Cons l _ ->
        mem info.pivot_clause (pivot_clauses cnf l) /\
        info.pivot_info.rup_clause = resolve info.rat_clause info.pivot_clause l /\
        rup_failure cnf info.pivot_info
    end

The derivation checker provides a similar contract, but rather than ensuring valid_proof on success, it provides valid_derivation.

predicate valid_derivation (cnf : cnf) (pf : proof) =
    match pf with
    | Nil -> true
    | Cons c cs -> (rup cnf c \/ rat cnf c) /\ valid_derivation (Cons c cnf) cs
    end

This is the same condition as valid_proof, but in the Nil case, the checker does not require that the empty clause is not RUP.

Acknowledgements

Many thanks to Frank Pfenning, Joseph Reeves, and Marijn Huele for the ongoing insightful discussions that led to this project.

  1"""
  2.. include:: ../../README.md
  3"""
  4
  5__version__ = '1.2.0'
  6
  7import os
  8import sys
  9import ctypes
 10
 11_basedir = os.path.abspath(os.path.dirname(__file__))
 12_checker = ctypes.cdll.LoadLibrary(os.path.join(_basedir, "libdrupchecker.so"))
 13
 14class _Lit_struct(ctypes.Structure):
 15  _fields_=[("var", ctypes.c_int), ("sign", ctypes.c_int), ("root", ctypes.c_void_p)]
 16
 17  def __str__(self):
 18    if self.sign <= 0:
 19      return f"-{self.var}"
 20    else:
 21      return f"{self.var}"
 22
 23class _Clause_struct(ctypes.Structure):
 24  _fields_=[("lits", ctypes.POINTER(_Lit_struct)), ("len", ctypes.c_int), ("root", ctypes.c_void_p)]
 25
 26  def __str__(self):
 27    r = " ".join([str(self.lits[i]) for i in range(self.len)])
 28    spacer = " " if self.len > 0 else ""
 29    return f"{r}{spacer}0"
 30
 31class _Cnf_struct(ctypes.Structure):
 32  _fields_=[("clauses", ctypes.POINTER(_Clause_struct)), ("len", ctypes.c_int), ("root", ctypes.c_void_p)]
 33
 34  def __str__(self):
 35    cs = "\n".join([str(self.clauses[i]) for i in range(self.len)])
 36    return f"{cs}"
 37
 38class _Chain_struct(ctypes.Structure):
 39  _fields_=[("lits", ctypes.POINTER(_Lit_struct)), ("len", ctypes.c_int), ("root", ctypes.c_void_p)]
 40
 41  def __str__(self):
 42    ls = " ".join([str(self.lits[i]) for i in range(self.len)])
 43    return f"<{ls}>"
 44
 45class _Rupinfo_struct(ctypes.Structure):
 46  _fields_=[("clause", _Clause_struct), ("chain", _Chain_struct), ("root", ctypes.c_void_p)]
 47
 48  def __str__(self):
 49    rup_clause = str(self.clause)
 50    rup_chain = str(self.chain)
 51    return f"_Rupinfo_struct({rup_clause}, {rup_chain})"
 52
 53class _Ratinfo_struct(ctypes.Structure):
 54  _fields_=[("clause", _Clause_struct), ("pivot_clause", _Clause_struct), ("rup_info", _Rupinfo_struct), ("root", ctypes.c_void_p)]
 55
 56  def __str__(self):
 57    rat_clause = str(self.clause)
 58    pivot_clause = str(self.pivot_clause)
 59    rup_info = str(self.rup_info)
 60    return f"_Ratinfo_struct({rat_clause}, {pivot_clause}, {rup_info})"
 61
 62class _Result_struct(ctypes.Structure):
 63  _fields_=[("valid", ctypes.c_int), ("steps", _Cnf_struct), ("rup_info", _Rupinfo_struct), ("rat_info", _Ratinfo_struct), ("root", ctypes.c_void_p)]
 64
 65  def __str__(self):
 66    steps = ", ".join(str(self.steps.clauses[i]) for i in range(self.steps.len))
 67    rup = str(self.rup_info)
 68    rat = str(self.rat_info)
 69    return f"_Result_struct({self.valid}, [{steps}], {rup}, {rat})"
 70
 71_checker.check.argtypes = [ctypes.POINTER(_Cnf_struct), ctypes.POINTER(_Cnf_struct)]
 72_checker.check.restype = ctypes.POINTER(_Result_struct)
 73_checker.check_derivation.argtypes = [ctypes.POINTER(_Cnf_struct), ctypes.POINTER(_Cnf_struct)]
 74_checker.check_derivation.restype = ctypes.POINTER(_Result_struct)
 75_checker.check_fast.argtypes = [ctypes.POINTER(_Cnf_struct), ctypes.POINTER(_Cnf_struct)]
 76_checker.check_fast.restype = ctypes.POINTER(_Result_struct)
 77_checker.check_derivation_fast.argtypes = [ctypes.POINTER(_Cnf_struct), ctypes.POINTER(_Cnf_struct)]
 78_checker.check_derivation_fast.restype = ctypes.POINTER(_Result_struct)
 79_checker.free_rup_info.argtypes = [ctypes.POINTER(_Rupinfo_struct)]
 80_checker.free_rat_info.argtypes = [ctypes.POINTER(_Ratinfo_struct)]
 81_checker.free_result.argtypes = [ctypes.POINTER(_Result_struct)]
 82
 83import drup.wrappers
 84
 85Lit = drup.wrappers.Lit
 86'''
 87A signed integer representing a literal.
 88'''
 89
 90Clause = drup.wrappers.Clause
 91'''
 92A sequence of literals.
 93'''
 94
 95Chain = drup.wrappers.Chain
 96'''
 97A sequence of clauses.
 98'''
 99
100Cnf = drup.wrappers.Cnf
101'''
102A sequence of literals.
103'''
104
105Proof = drup.wrappers.Proof
106'''
107A sequence of clauses.
108'''
109
110Outcome = drup.wrappers.Outcome
111RupInfo = drup.wrappers.RupInfo
112RatInfo = drup.wrappers.RatInfo
113CheckerResult = drup.wrappers.CheckerResult
114check_proof = drup.wrappers.check_proof
115check_proof_from_files = drup.wrappers.check_proof_from_files
116check_proof_from_strings = drup.wrappers.check_proof_from_strings
117check_derivation = drup.wrappers.check_derivation
118check_derivation_from_files = drup.wrappers.check_derivation_from_files
119check_derivation_from_strings = drup.wrappers.check_derivation_from_strings
120
121__all__ = [
122  'Lit',
123  'Clause',
124  'Chain',
125  'Cnf',
126  'Proof',
127  'Outcome',
128  'RupInfo',
129  'RatInfo',
130  'CheckerResult',
131  'check_proof',
132  'check_proof_from_files',
133  'check_proof_from_strings',
134  'check_derivation',
135  'check_derivation_from_files',
136  'check_derivation_from_strings',
137]

API Documentation

Lit = <class 'int'>

A signed integer representing a literal.

Clause = typing.Iterable[int]

A sequence of literals.

Chain = typing.Iterable[int]

A sequence of clauses.

Cnf = typing.Iterable[typing.Iterable[int]]

A sequence of literals.

Proof = typing.Iterable[typing.Iterable[int]]

A sequence of clauses.

class Outcome(enum.Enum):

An enumeration.

VALID = <Outcome.VALID: 1>
INVALID = <Outcome.INVALID: 2>
Inherited Members
enum.Enum
name
value
class RupInfo:
29class RupInfo:
30
31  '''
32  Information on a failed RUP check.
33
34  **Attributes**:
35    
36    clause (`Clause`): The clause that failed the RUP check.
37
38    chain (`Chain`): A sequence of unit literals that failed to
39      derive an empty clause via propagation, with no further 
40      opportunities to propagate.
41  '''
42
43  def __init__(self, clause : Clause, chain : Chain):
44    self.clause = clause
45    self.chain = chain
46
47  def __str__(self):
48    return f"RupInfo({str(self.clause)}, {str(self.chain)})"

Information on a failed RUP check.

Attributes:

clause (Clause): The clause that failed the RUP check.

chain (Chain): A sequence of unit literals that failed to derive an empty clause via propagation, with no further opportunities to propagate.

RupInfo(clause: Iterable[int], chain: Iterable[int])
43  def __init__(self, clause : Clause, chain : Chain):
44    self.clause = clause
45    self.chain = chain
class RatInfo:
50class RatInfo:
51
52  '''
53  Information on a failed RAT check.
54
55  **Attributes**:
56    clause (`Clause`): The clause that failed the RAT check.
57
58    pivot_clause (`Clause`): A pivot clause that failed a RUP check.
59
60    rup_info (`RupInfo`): Information on the failed RUP check.
61  '''
62
63  def __init__(self, clause : Clause, pivot_clause : Clause, rup_info : RupInfo):
64    self.clause = clause
65    self.pivot_clause = pivot_clause
66    self.rup_info = rup_info
67
68  def __str__(self):
69    return f"RatInfo({str(self.clause)}, {str(self.pivot_clause)}, {str(self.rup_info)})"

Information on a failed RAT check.

Attributes: clause (Clause): The clause that failed the RAT check.

pivot_clause (Clause): A pivot clause that failed a RUP check.

rup_info (RupInfo): Information on the failed RUP check.

RatInfo( clause: Iterable[int], pivot_clause: Iterable[int], rup_info: drup.RupInfo)
63  def __init__(self, clause : Clause, pivot_clause : Clause, rup_info : RupInfo):
64    self.clause = clause
65    self.pivot_clause = pivot_clause
66    self.rup_info = rup_info
class CheckerResult:
 71class CheckerResult:
 72
 73  '''
 74  Result of a proof check.
 75
 76  **Attributes**:
 77
 78    outcome (`Outcome`): The outcome of the check. If the check
 79      succeeded, this will be Outcome.VALID. If the check failed,
 80      this will be Outcome.INVALID.
 81
 82    steps (`Optional[Cnf]`): Completed proof steps prior to an invalid step, 
 83      if the proof was invalid.
 84
 85    rup_info (`Optional[RupInfo]`): Information on a failed RUP check,
 86      if the proof was invalid.
 87
 88    rat_info (`Optional[RatInfo]`): Information on a failed RAT check,
 89      if the proof was invalid. The RAT clause in this object will
 90      be the same as the RUP clause in `rup_info`.
 91  '''
 92
 93  def __init__(self, outcome : Outcome, steps : Optional[Cnf], rup_info : Optional[RupInfo], rat_info : Optional[RatInfo]):
 94    self.outcome = outcome
 95    self.steps = steps
 96    self.rup_info = rup_info
 97    self.rat_info = rat_info
 98
 99  def __str__(self):
100    if self.outcome == Outcome.VALID:
101      return f"CheckerResult({self.outcome})"
102    else:
103      if self.steps is not None and self.rup_info is not None and self.rat_info is not None:
104        steps = " ".join(str(step) for step in self.steps)
105        rup = str(self.rup_info)
106        rat = str(self.rat_info)
107        return f"CheckerResult({self.outcome}, [{steps}], {rup}, {rat})"
108      else:
109        return f"CheckerResult({self.outcome})"

Result of a proof check.

Attributes:

outcome (Outcome): The outcome of the check. If the check succeeded, this will be Outcome.VALID. If the check failed, this will be Outcome.INVALID.

steps (Optional[Cnf]): Completed proof steps prior to an invalid step, if the proof was invalid.

rup_info (Optional[RupInfo]): Information on a failed RUP check, if the proof was invalid.

rat_info (Optional[RatInfo]): Information on a failed RAT check, if the proof was invalid. The RAT clause in this object will be the same as the RUP clause in rup_info.

CheckerResult( outcome: drup.Outcome, steps: Optional[Iterable[Iterable[int]]], rup_info: Optional[drup.RupInfo], rat_info: Optional[drup.RatInfo])
93  def __init__(self, outcome : Outcome, steps : Optional[Cnf], rup_info : Optional[RupInfo], rat_info : Optional[RatInfo]):
94    self.outcome = outcome
95    self.steps = steps
96    self.rup_info = rup_info
97    self.rat_info = rat_info
def check_proof( formula: Iterable[Iterable[int]], proof: Iterable[Iterable[int]], verbose: bool = False) -> drup.CheckerResult:
267def check_proof(formula : Cnf, proof : Proof, verbose : bool = False) -> CheckerResult:
268  """Check a sequence of RUP and RAT clauses against a CNF. Inputs are Python iterables
269  of clauses, where each clause is an iterable of signed Python ints.
270
271  **Args:**
272    formula (`Cnf`): Cnf as an iterable of clauses.
273
274    proof (`Proof`): Iterable of RUP or RAT clauses.
275    
276    verbose (bool, optional): Return detailed information
277      if the check fails. Defaults to False.
278
279  **Returns:**
280    `CheckerResult`: CheckerResult struct representing the result of the check.
281  
282  **Raises:**
283    ValueError: If the formula or proof cannot be formatted.
284  """
285  return _check_proof_from_structs(_cnf_to_struct(formula), _cnf_to_struct(proof), verbose)

Check a sequence of RUP and RAT clauses against a CNF. Inputs are Python iterables of clauses, where each clause is an iterable of signed Python ints.

Args: formula (Cnf): Cnf as an iterable of clauses.

proof (Proof): Iterable of RUP or RAT clauses.

verbose (bool, optional): Return detailed information if the check fails. Defaults to False.

Returns: CheckerResult: CheckerResult struct representing the result of the check.

Raises: ValueError: If the formula or proof cannot be formatted.

def check_proof_from_files( formula_file: str, proof_file: str, verbose: bool = False) -> drup.CheckerResult:
318def check_proof_from_files(formula_file : str, proof_file : str, verbose : bool = False) -> CheckerResult:
319  """Check a sequence of RUP and RAT clauses against a CNF.
320
321  **Args:**
322    formula_file (str): Path to a file containing a CNF in DIMACS format.
323    proof_file (str): Path to a file containing a sequence of RUP or RAT clauses.
324    verbose (bool, optional): Return detailed information
325      if the check fails. Defaults to False.
326
327  **Returns:**
328    `CheckerResult`: CheckerResult struct representing the result of the check.
329
330  **Raises:**
331    ValueError: If the formula or proof cannot be parsed or formatted.
332    FileNotFoundError: If the formula or proof file cannot be found.
333  """
334  with open(formula_file, 'r') as f:
335    formula = f.read()
336  with open(proof_file, 'r') as f:
337    proof = f.read()
338
339  return check_proof_from_strings(formula, proof, verbose)

Check a sequence of RUP and RAT clauses against a CNF.

Args: formula_file (str): Path to a file containing a CNF in DIMACS format. proof_file (str): Path to a file containing a sequence of RUP or RAT clauses. verbose (bool, optional): Return detailed information if the check fails. Defaults to False.

Returns: CheckerResult: CheckerResult struct representing the result of the check.

Raises: ValueError: If the formula or proof cannot be parsed or formatted. FileNotFoundError: If the formula or proof file cannot be found.

def check_proof_from_strings( formula: str, proof: str, verbose: bool = False) -> drup.CheckerResult:
287def check_proof_from_strings(formula : str, proof : str, verbose : bool = False) -> CheckerResult:
288  """Check a sequence of RUP and RAT clauses against a CNF.
289
290  **Args:**
291    formula (str): Cnf as a string in DIMACS format. The header
292      is ignored if present.
293    proof (str): Sequence of RUP or RAT clauses format.
294    verbose (bool, optional): Return detailed information
295      if the check fails. Defaults to False.
296  
297  **Returns:**
298    `CheckerResult`: CheckerResult struct representing the result of the check.
299
300  **Raises:**
301    ValueError: If the formula or proof cannot be parsed or formatted.
302  """
303  try:
304    formula = re.sub(' +', ' ', formula)
305    if '\n' in formula and formula.split('\n')[0].strip().startswith('p'):
306      formula = '\n'.join(formula.split('\n')[1:])
307    formula = _read_clauses(formula)
308  except:
309    raise ValueError("Error parsing formula. Check that the input is properly formatted.")
310  try:
311    proof = re.sub(' +', ' ', proof)
312    proof = _read_clauses(proof)
313  except:
314    raise ValueError("Error parsing proof. Check that the input is properly formatted.")
315
316  return check_proof(formula, proof, verbose)

Check a sequence of RUP and RAT clauses against a CNF.

Args: formula (str): Cnf as a string in DIMACS format. The header is ignored if present. proof (str): Sequence of RUP or RAT clauses format. verbose (bool, optional): Return detailed information if the check fails. Defaults to False.

Returns: CheckerResult: CheckerResult struct representing the result of the check.

Raises: ValueError: If the formula or proof cannot be parsed or formatted.

def check_derivation( formula: Iterable[Iterable[int]], derivation: Iterable[Iterable[int]], verbose: bool = False) -> drup.CheckerResult:
402def check_derivation(formula : Cnf, derivation : Proof, verbose : bool = False) -> CheckerResult:
403  """Check a sequence of RUP and RAT clauses against a CNF. Inputs are Python iterables
404  of clauses, where each clause is an iterable of signed Python ints.
405
406  **Args:**
407    formula (`Cnf`): Cnf as an iterable of clauses.
408
409    derivation (`Proof`): Iterable of RUP or RAT clauses.
410    
411    verbose (bool, optional): Return detailed information
412      if the check fails. Defaults to False.
413
414  **Returns:**
415    `CheckerResult`: CheckerResult struct representing the result of the check.
416      If each step in the derivation is either RUP or RAT, then the result will
417      be Outcome.VALID. Otherwise, the result will be Outcome.INVALID.
418      The derivation does not need to contain the empty clause.
419  
420  **Raises:**
421    ValueError: If the formula or derivation cannot be formatted.
422  """
423  return _check_derivation_from_structs(_cnf_to_struct(formula), _cnf_to_struct(derivation), verbose)

Check a sequence of RUP and RAT clauses against a CNF. Inputs are Python iterables of clauses, where each clause is an iterable of signed Python ints.

Args: formula (Cnf): Cnf as an iterable of clauses.

derivation (Proof): Iterable of RUP or RAT clauses.

verbose (bool, optional): Return detailed information if the check fails. Defaults to False.

Returns: CheckerResult: CheckerResult struct representing the result of the check. If each step in the derivation is either RUP or RAT, then the result will be Outcome.VALID. Otherwise, the result will be Outcome.INVALID. The derivation does not need to contain the empty clause.

Raises: ValueError: If the formula or derivation cannot be formatted.

def check_derivation_from_files( formula_file: str, derivation_file: str, verbose: bool = False) -> drup.CheckerResult:
461def check_derivation_from_files(formula_file : str, derivation_file : str, verbose : bool = False) -> CheckerResult:
462  """Check a sequence of RUP and RAT clauses against a CNF.
463
464  **Args:**
465    
466    formula_file (str): Path to a file containing a CNF in DIMACS format.
467    
468    derivation_file (str): Path to a file containing a sequence of RUP or RAT clauses.
469    
470    verbose (bool, optional): Return detailed information
471      if the check fails. Defaults to False.
472
473  **Returns:**
474    `CheckerResult`: CheckerResult struct representing the result of the check.
475      If each step in the derivation is either RUP or RAT, then the result will
476      be Outcome.VALID. Otherwise, the result will be Outcome.INVALID.
477      The derivation does not need to contain the empty clause.
478
479  **Raises:**
480    ValueError: If the formula or proof cannot be parsed or formatted.
481    FileNotFoundError: If the formula or proof file cannot be found.
482  """
483  with open(formula_file, 'r') as f:
484    formula = f.read()
485  with open(derivation_file, 'r') as f:
486    derivation = f.read()
487
488  return check_derivation_from_strings(formula, derivation, verbose)

Check a sequence of RUP and RAT clauses against a CNF.

Args:

formula_file (str): Path to a file containing a CNF in DIMACS format.

derivation_file (str): Path to a file containing a sequence of RUP or RAT clauses.

verbose (bool, optional): Return detailed information if the check fails. Defaults to False.

Returns: CheckerResult: CheckerResult struct representing the result of the check. If each step in the derivation is either RUP or RAT, then the result will be Outcome.VALID. Otherwise, the result will be Outcome.INVALID. The derivation does not need to contain the empty clause.

Raises: ValueError: If the formula or proof cannot be parsed or formatted. FileNotFoundError: If the formula or proof file cannot be found.

def check_derivation_from_strings( formula: str, derivation: str, verbose: bool = False) -> drup.CheckerResult:
425def check_derivation_from_strings(formula : str, derivation : str, verbose : bool = False) -> CheckerResult:
426  """Check a sequence of RUP and RAT clauses against a CNF.
427
428  **Args:**
429    formula (str): Cnf as a string in DIMACS format. The header
430      is ignored if present.
431    
432    proof (str): Sequence of RUP or RAT clauses format.
433    
434    verbose (bool, optional): Return detailed information
435      if the check fails. Defaults to False.
436  
437  **Returns:**
438    `CheckerResult`: CheckerResult struct representing the result of the check.
439      If each step in the derivation is either RUP or RAT, then the result will
440      be Outcome.VALID. Otherwise, the result will be Outcome.INVALID.
441      The derivation does not need to contain the empty clause.
442
443  **Raises:**
444    ValueError: If the formula or proof cannot be parsed or formatted.
445  """
446  try:
447    formula = re.sub(' +', ' ', formula)
448    if '\n' in formula and formula.split('\n')[0].strip().startswith('p'):
449      formula = '\n'.join(formula.split('\n')[1:])
450    formula = _read_clauses(formula)
451  except:
452    raise ValueError("Error parsing formula. Check that the input is properly formatted.")
453  try:
454    derivation = re.sub(' +', ' ', derivation)
455    derivation = _read_clauses(derivation)
456  except:
457    raise ValueError("Error parsing derivation. Check that the input is properly formatted.")
458
459  return check_derivation(formula, derivation, verbose)

Check a sequence of RUP and RAT clauses against a CNF.

Args: formula (str): Cnf as a string in DIMACS format. The header is ignored if present.

proof (str): Sequence of RUP or RAT clauses format.

verbose (bool, optional): Return detailed information if the check fails. Defaults to False.

Returns: CheckerResult: CheckerResult struct representing the result of the check. If each step in the derivation is either RUP or RAT, then the result will be Outcome.VALID. Otherwise, the result will be Outcome.INVALID. The derivation does not need to contain the empty clause.

Raises: ValueError: If the formula or proof cannot be parsed or formatted.