1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Interface for defining and solving SAT problems.
//!
//!
//! The [Boolean satisfiability][] problem (SAT for short) asks, for a given
//! Boolean formula, whether there exists an assignment of values (true or false) to
//! the formula's variables such that the formula evaluates to true.
//!
//! SAT is [NP-complete][], which implies two things:
//!
//! 1. A large number of important problems (e.g. in program analysis, circuit design, or
//!    logistical planning) may be seen as instances of SAT.
//!
//! 2. It is believed that no algorithm exists which efficiently solves all instances
//!    of SAT.
//!
//! The observation (1) is significant in spite of (2) because there exist
//! algorithms (such as [DPLL][]) which efficiently solve the SAT instances one encounters
//! *in practice*.
//!
//! This crate allows the user to formulate instances of SAT and to solve them using
//! off-the-shelf SAT solvers.
//!
//! ```ignore
//! // Create a SAT instance.
//! let mut i = sat::Instance::new();
//! let x = i.fresh_var();
//! let y = i.fresh_var();
//! let z = i.fresh_var();
//! i.assert_any(&[x, z]);        //     (x OR z)
//! i.assert_any(&[!x, !y, !z]);  // AND (!x OR !y OR !z)
//! i.assert_any(&[y]);           // AND (y = TRUE)
//!
//! // Use the external program `minisat` as a solver.
//! let s = sat::solver::Dimacs::new(|| Command::new("minisat"));
//!
//! // Solve the instance and check that it meets our requirements.
//! let a = s.solve(&i).unwrap();
//! assert!(a.get(x) || a.get(z));
//! assert!(!a.get(x) || !a.get(y) || !a.get(z));
//! assert!(a.get(y));
//!
//! // Add a clause which makes the instance impossible to satisfy,
//! // and check that the solver recognizes as much.
//! i.assert_any(&[!y]);
//! assert!(s.solve(&i).is_none());
//! ```
//!
//! For a more elaborate example, see `examples/petersen.rs` which produces a 3-coloring
//! of the [Petersen graph][].
//!
//! [Boolean satisfiability]: https://en.wikipedia.org/wiki/Boolean_satisfiability_problem
//! [NP-complete]: https://en.wikipedia.org/wiki/NP-completeness
//! [DPLL]: https://en.wikipedia.org/wiki/DPLL_algorithm
//! [Petersen graph]: https://en.wikipedia.org/wiki/Petersen_graph

use std::iter::IntoIterator;
use std::ops;

extern crate tempfile;

pub mod solver;

/// An instance of the SAT problem.
pub struct Instance {
    num_vars: usize,
    cnf_clauses: Vec<Vec<Literal>>,
}

/// A literal; a variable or negated variable.
///
/// Literals support the `!` (negation) operator.
#[derive(Copy, Clone)]
pub struct Literal {
    var: usize,
    negated: bool,
}

/// An assignment of truth values to variables.
///
/// This is the output of a successful solve.
pub struct Assignment {
    assignment: Vec<Literal>,
}

impl Instance {
    /// Create a new, empty SAT instance.
    pub fn new() -> Instance {
        Instance {
            num_vars: 0,
            cnf_clauses: vec![],
        }
    }

    /// Create a fresh variable.
    pub fn fresh_var(&mut self) -> Literal {
        let v = self.num_vars;
        self.num_vars += 1;
        Literal {
            var: v,
            negated: false,
        }
    }

    /// Assert that at least one of the provided literals must
    /// evaluate to true.
    ///
    /// This is a CNF (conjunctive normal form) constraint, which
    /// is the basic type of constraint in most solvers.
    pub fn assert_any<'a, I>(&mut self, lits: I)
        where I: IntoIterator<Item = &'a Literal>
    {
        let lits = lits.into_iter();
        self.cnf_clauses.push(lits.cloned().collect());
    }
}

impl ops::Not for Literal {
    type Output = Literal;

    fn not(self) -> Literal {
        Literal {
            negated: !self.negated,
            ..self
        }
    }
}

impl Assignment {
    /// Get the value assigned to a literal.
    pub fn get(&self, lit: Literal) -> bool {
        lit.negated ^ (!self.assignment[lit.var].negated)
    }
}