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
//! Invoke an external SAT solver program which uses the DIMACS / MiniSAT
//! file format.

use {Instance, Assignment, Literal};
use solver::Solver;

use std::io::{self, Write, BufRead};
use std::process::Command;
use std::str::FromStr;

use tempfile;

/// A SAT solver which invokes an external program using the DIMACS / MiniSAT
/// file format.
pub struct Dimacs<CmdFactory> {
    cmd_factory: CmdFactory,
}

impl<CmdFactory> Dimacs<CmdFactory>
    where CmdFactory: Fn() -> Command,
{
    /// Create an instance of the DIMACS solver.
    ///
    /// The argument is a closure which should prepare a `std::process::Command`
    /// to invoke the solver program. The input and output filenames will be
    /// appended as additional arguments.
    ///
    /// ```ignore
    /// let s = Dimacs::new(|| {
    ///     let mut c = process::Command::new("minisat");
    ///     c.stdout(process::Stdio::null());
    ///     c
    /// });
    /// ```
    pub fn new(cmd_factory: CmdFactory) -> Dimacs<CmdFactory>
        where CmdFactory: Fn() -> Command,
    {
        Dimacs {
            cmd_factory: cmd_factory,
        }
    }

    /// Write an instance in DIMACS format.
    ///
    /// You don't need to call this directly as part of the solver workflow.
    /// It may be useful for debugging.
    pub fn write_instance<W>(&self, writer: &mut W, instance: &Instance)
        where W: Write,
    {
        write!(writer, "p cnf {} {}\n",
            instance.num_vars, instance.cnf_clauses.len()).unwrap();
        for c in &instance.cnf_clauses {
            for l in c {
                let n = (l.var + 1) as isize;
                write!(writer, "{} ", if l.negated { -n } else { n }).unwrap();
            }
            write!(writer, "0\n").unwrap();
        }
    }

    /// Read a solution in MiniSAT format.
    ///
    /// You don't need to call this directly as part of the solver workflow.
    /// It may be useful for debugging.
    pub fn read_solution<R>(&self, reader: &mut R, num_vars: usize) -> Option<Assignment>
        where R: BufRead,
    {
        let mut line = String::new();
        reader.read_line(&mut line).unwrap();
        if line == "UNSAT\n" {
            return None;
        }

        assert!(line == "SAT\n", "expected \"SAT\"");

        let mut assignment: Vec<_> = (0..num_vars).map(|i| Literal {
            var: i,
            negated: false,
        }).collect();

        loop {
            line.clear();
            if reader.read_line(&mut line).unwrap() == 0 {
                break;
            }

            for tok in line.split_whitespace() {
                let i = isize::from_str(tok).unwrap();
                if i == 0 {
                    continue;
                }

                if i < 0 {
                    assignment[(-i - 1) as usize].negated = true;
                }
            }
        }

        Some(Assignment {
            assignment: assignment,
        })
    }
}

impl<CmdFactory> Solver for Dimacs<CmdFactory>
    where CmdFactory: Fn() -> Command,
{
    fn solve(&self, instance: &Instance) -> Option<Assignment> {
        let mut in_file = tempfile::NamedTempFile::new().unwrap();
        let out_file = tempfile::NamedTempFile::new().unwrap();

        self.write_instance(&mut in_file, instance);

        let mut cmd = (self.cmd_factory)();

        // don't check the return code because minisat returns
        // non-zero on success :(
        let _ = cmd.arg(in_file.path())
           .arg(out_file.path())
           .spawn().unwrap().wait();

        let out_file = out_file.reopen().unwrap();
        let mut out_file = io::BufReader::new(out_file);

        self.read_solution(&mut out_file, instance.num_vars)
    }
}