Formulas
SAT solvers operate on formulas in conjunctive normal form (CNF). The Varisat library comes with types to efficiently store CNF formulas. This is independent from loading a formula into the solver. This is useful for writing custom code that processes CNF formulas. It also simplifies writing code that generates a formula to either directly solve it or alternatively write it to a file.
Literals and Variables
To build formulas in conjunctive normal form, we need to start with variables
and literals. For this Varisat provides the types Var
and Lit
.
A variable is identified by an integer. The convention used for input and output of variables follows the DIMACS CNF format and is 1-based. A literal is a variable or a negated variable. In the DIMACS format negative literals are represented by negative integers.
Internally variables are numbered starting with zero. This matches the array indexing convention used by Rust. The 0-based number of a variable is called index, while the 1-based number is called dimacs. Literals are also represented by a non-negative number called the code. A literal's code is the corresponding variable's index shifted to the left by one. The least significant bit is set if the literal is negative.
The Var
and Lit
types come with methods to convert between the
representations. Note that Lit::index
returns the corresponding variable's
index, not the literal's code.
# #![allow(unused_variables)] #fn main() { # extern crate varisat; use varisat::{Var, Lit}; let x = Var::from_index(0); assert_eq!(x, Var::from_dimacs(1)); assert_eq!(x.index(), 0); assert_eq!(x.to_dimacs(), 1); assert!(Lit::from_dimacs(2).is_positive()); assert!(Lit::from_dimacs(-3).is_negative()); assert_eq!(Lit::positive(x), x.lit(true)); assert_eq!(Lit::negative(x), x.lit(false)); assert_eq!(x.positive(), Lit::from_var(x, true)); assert_eq!(x.negative(), Lit::from_var(x, false)); assert_eq!(x.positive(), !x.negative()); assert_eq!(Lit::from_index(6, true).code(), 12); assert_eq!(Lit::from_index(6, false).code(), 13); assert_eq!(Lit::from_code(13).var(), Var::from_dimacs(7)); assert_eq!(Lit::from_code(13).index(), 6); #}
When formatting variables or literals the DIMACS convention is used.
# #![allow(unused_variables)] #fn main() { # extern crate varisat; # use varisat::{Var, Lit}; let fmt_display = format!("{}, {}", Var::from_dimacs(5), Lit::from_dimacs(-1)); let fmt_debug = format!("{:?}, {:?}", Var::from_dimacs(5), Lit::from_dimacs(-1)); assert_eq!(fmt_display, "5, -1"); assert_eq!(fmt_debug, "5, -1"); #}
Formulas
A CNF formula is a conjunction of clauses and a clause a disjunction of literals. This means we can represent a formula as a set of sets of literals.
By arbitrarily ordering the elements of the sets, we could use a
Vec<Vec<Lit>>
to represent a formula. Such a representation has too much
memory overhead per clause though. Each clause requires a separate allocation,
which also hurts cache locality when iterating over the clauses of a large
formula.
Instead Varisat provides the CnfFormula
type, which stores all literals in a
single Vec
. When iterating over a CnfFormula
the clauses can be accessed as
slices.
The add_clause
method of the ExtendFormula
trait allows adding new clauses
to a CnfFormula
.
# #![allow(unused_variables)] #fn main() { # extern crate varisat; # use varisat::{Var, Lit}; use varisat::{CnfFormula, ExtendFormula}; let mut formula = CnfFormula::new(); let (a, b, c) = (Lit::from_dimacs(1), Lit::from_dimacs(2), Lit::from_dimacs(3)); formula.add_clause(&[a, b, c]); formula.add_clause(&[!a, !b]); formula.add_clause(&[!b, !c]); formula.add_clause(&[!a, !c]); assert_eq!(formula.iter().last().unwrap(), &[!a, !c]); #}
New Variables and Literals
Often we don't care about the specific indices of variables. In that case,
instead of manually computing indices, we can dynamically ask for new unused
variables. This functionality is also also provided by the ExtendFormula
trait.
# #![allow(unused_variables)] #fn main() { # extern crate varisat; # use varisat::{CnfFormula, ExtendFormula, Lit, Var}; let mut formula = CnfFormula::new(); let a = formula.new_var().negative(); let b = formula.new_lit(); let (c, d, e) = formula.new_lits(); let f: Vec<Lit> = formula.new_lit_iter(10).collect(); formula.add_clause(&[a, b, c, d, e]); formula.add_clause(&f); assert_eq!(formula.var_count(), 15); #}
Parsing and Writing Formulas
Varisat provides routines for parsing and writing Formulas in the DIMACS CNF format.
# #![allow(unused_variables)] #fn main() { # extern crate varisat; # use varisat::{Var, Lit}; # use varisat::ExtendFormula; use varisat::dimacs::{DimacsParser, write_dimacs}; let input = b"p cnf 3 2\n1 2 3 0\n-1 -3 0\n"; let implements_read = &input[..]; let mut formula = DimacsParser::parse(implements_read).expect("parse error"); formula.add_clause(&[Lit::from_dimacs(2)]); let mut implements_write = vec![]; write_dimacs(&mut implements_write, &formula); assert_eq!(implements_write, b"p cnf 3 3\n1 2 3 0\n-1 -3 0\n2 0\n"); #}