sat_lab/
instance.rs

1use crate::{clause::Clause, literal::Literal};
2
3use bool_vec::{boolvec, BoolVec};
4
5#[cfg(feature = "rand")]
6use rand::{distributions::Standard, Rng, SeedableRng};
7
8use std::{fs, io::Write, path::Path};
9
10/// A SAT instance
11#[derive(Debug)]
12pub struct Instance {
13    pub vars: BoolVec,
14    clauses: Vec<Clause>,
15}
16
17impl Clone for Instance {
18    fn clone(&self) -> Self {
19        let mut bvec_clone = BoolVec::with_capacity(self.vars.capacity());
20        for origin in &self.vars {
21            bvec_clone.push(origin);
22        }
23
24        Self {
25            vars: bvec_clone,
26            clauses: self.clauses.clone(),
27        }
28    }
29}
30
31impl Instance {
32    /// Creates a new instance with the given variables and clauses.
33    pub fn new(vars: BoolVec, clauses: Vec<Clause>) -> Self {
34        Self { vars, clauses }
35    }
36
37    /// Creates a new instance with the given number of variables initialized at 0 and clauses.
38    pub fn with_clauses(n: usize, clauses: Vec<Clause>) -> Self {
39        Self {
40            vars: boolvec![false; n],
41            clauses,
42        }
43    }
44
45    /// Creates a new instance from a file in Conjunctive Normal Form.
46    /// Returns an error if the file is not in CNF or is malformed.
47    pub fn from_file<P>(path: P) -> std::io::Result<Self>
48    where
49        P: AsRef<Path>,
50    {
51        // TODO: Custom errors
52
53        let content = fs::read_to_string(path)?;
54
55        let mut lines = content.trim().lines().skip_while(|x| x.starts_with('c'));
56
57        let mut param_line = lines
58            .next()
59            .ok_or(std::io::ErrorKind::InvalidInput)?
60            .split_whitespace()
61            .skip(1);
62
63        let problem_type = param_line.next().ok_or(std::io::ErrorKind::InvalidInput)?;
64        if problem_type != "cnf" {
65            return Err(std::io::ErrorKind::InvalidInput.into());
66        }
67
68        let n = param_line
69            .next()
70            .ok_or(std::io::ErrorKind::InvalidInput)?
71            .parse()
72            .map_err(|_| std::io::ErrorKind::InvalidInput)?;
73        let m = param_line
74            .next()
75            .ok_or(std::io::ErrorKind::InvalidInput)?
76            .parse()
77            .map_err(|_| std::io::ErrorKind::InvalidInput)?;
78
79        let clauses = lines
80            .take(m)
81            .map(str::split_whitespace)
82            .map(|clause| {
83                clause
84                    .map(|x| x.parse())
85                    .take_while(|r| r.as_ref().map_or(false, |x| *x != 0))
86                    .map(|r| r.map(Literal::from_cnf))
87                    .collect::<Result<Clause, _>>()
88            })
89            .collect::<Result<_, _>>()
90            .map_err(|_| std::io::ErrorKind::InvalidInput)?;
91
92        Ok(Self {
93            vars: boolvec![false; n],
94            clauses,
95        })
96    }
97
98    /// Creates a new random instance with the given number of variables, clauses, and clause length.
99    #[cfg(feature = "rand")]
100    pub fn new_random<R: Rng + SeedableRng>(n: usize, m: usize, k: usize) -> Self {
101        let mut rng = R::from_entropy();
102
103        let mut chosen_indices = vec![];
104
105        let vars = BoolVec::from((&mut rng).sample_iter(Standard).take(n).collect::<Vec<_>>());
106        let clauses = (0..m)
107            .map(|_| {
108                let mut var_indices: Vec<usize> = vec![0; k];
109                let mut negates = boolvec![false; k];
110
111                for i in 0..k {
112                    let mut idx = rng.gen_range(0..n);
113                    while chosen_indices.contains(&idx) {
114                        idx = rng.gen_range(0..n);
115                    }
116                    chosen_indices.push(idx);
117
118                    var_indices[i] = idx;
119                    negates.set(i, rng.gen());
120                }
121                chosen_indices.clear();
122
123                Clause::from_indices(var_indices, &negates)
124            })
125            .collect();
126
127        Self { vars, clauses }
128    }
129
130    /// Save the instance to a file in Conjunctive Normal Form.
131    pub fn to_file<P>(&self, path: P) -> std::io::Result<()>
132    where
133        P: AsRef<Path>,
134    {
135        let mut file = fs::File::create(path)?;
136
137        writeln!(file, "p cnf {} {}", self.vars.len(), self.clauses.len())?;
138        for clause in &self.clauses {
139            for elem in clause.get_literals() {
140                write!(file, "{} ", elem.as_cnf())?;
141            }
142            writeln!(file, "0")?;
143        }
144
145        Ok(())
146    }
147
148    /// Randomly sample new variables
149    #[cfg(feature = "rand")]
150    pub fn sample_new_variables(&mut self) -> &BoolVec {
151        let mut rng = rand::thread_rng();
152        self.sample_new_variables_with(&mut rng)
153    }
154
155    /// Randomly sample new variables with a given RNG
156    #[cfg(feature = "rand")]
157    pub fn sample_new_variables_with<T: Rng>(&mut self, rng: &mut T) -> &BoolVec {
158        self.vars = BoolVec::from(
159            rng.sample_iter(Standard)
160                .take(self.vars.len())
161                .collect::<Vec<_>>(),
162        );
163
164        &self.vars
165    }
166
167    /// Returns the number of satisfied clauses
168    pub fn count_sat(&self) -> usize {
169        self.clauses
170            .iter()
171            .filter(|clause| clause.test_sat(&self.vars))
172            .count()
173    }
174
175    /// Returns true if all clauses are satisfied
176    pub fn is_sat(&self) -> bool {
177        self.count_sat() == self.clauses.len()
178    }
179
180    /// Returns the ratio of clauses to variables
181    pub fn clause_to_vars(&self) -> f32 {
182        self.clauses.len() as f32 / self.vars.len() as f32
183    }
184
185    /// Returns a reference to the clauses
186    pub fn get_clauses(&self) -> &Vec<Clause> {
187        &self.clauses
188    }
189}
190
191#[cfg(test)]
192mod tests {
193    #[test]
194    fn test() {}
195}