oxiz_sat/
recursive_minimization.rs1use crate::clause::ClauseDatabase;
10use crate::literal::Lit;
11#[allow(unused_imports)]
12use crate::prelude::*;
13use crate::trail::Reason;
14use smallvec::SmallVec;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18enum LitStatus {
19 Redundant,
21 Essential,
23 Unknown,
25}
26
27#[derive(Debug, Default, Clone)]
29pub struct RecursiveMinStats {
30 pub clauses_minimized: u64,
32 pub literals_removed: u64,
34 pub recursive_checks: u64,
36}
37
38pub struct RecursiveMinimizer {
40 status: Vec<LitStatus>,
42 stack: Vec<Lit>,
44 stats: RecursiveMinStats,
46}
47
48impl RecursiveMinimizer {
49 pub fn new(num_vars: usize) -> Self {
51 Self {
52 status: vec![LitStatus::Unknown; num_vars * 2],
53 stack: Vec::new(),
54 stats: RecursiveMinStats::default(),
55 }
56 }
57
58 pub fn resize(&mut self, num_vars: usize) {
60 self.status.resize(num_vars * 2, LitStatus::Unknown);
61 }
62
63 #[must_use]
65 pub fn stats(&self) -> &RecursiveMinStats {
66 &self.stats
67 }
68
69 pub fn minimize(
73 &mut self,
74 clause: &[Lit],
75 clauses: &ClauseDatabase,
76 reasons: &[Reason],
77 level: &[u32],
78 current_level: u32,
79 ) -> SmallVec<[Lit; 8]> {
80 for status in &mut self.status {
82 *status = LitStatus::Unknown;
83 }
84
85 let mut abstract_level = 0u32;
87 for &lit in clause {
88 let var = lit.var();
89 if level[var.index()] == current_level {
90 self.status[lit.index()] = LitStatus::Essential;
91 abstract_level |= 1 << (level[var.index()] & 31);
92 }
93 }
94
95 let mut result = SmallVec::new();
97 let mut removed = 0;
98
99 for &lit in clause {
100 let var = lit.var();
101
102 if level[var.index()] == 0 {
104 result.push(lit);
105 continue;
106 }
107
108 if self.status[lit.index()] == LitStatus::Essential {
110 result.push(lit);
111 continue;
112 }
113
114 if self.is_redundant(lit, clauses, reasons, level, abstract_level, current_level) {
116 self.status[lit.index()] = LitStatus::Redundant;
117 removed += 1;
118 self.stats.literals_removed += 1;
119 } else {
120 self.status[lit.index()] = LitStatus::Essential;
121 result.push(lit);
122 }
123 }
124
125 if removed > 0 {
126 self.stats.clauses_minimized += 1;
127 }
128
129 result
130 }
131
132 fn is_redundant(
134 &mut self,
135 lit: Lit,
136 clauses: &ClauseDatabase,
137 reasons: &[Reason],
138 level: &[u32],
139 abstract_level: u32,
140 _current_level: u32,
141 ) -> bool {
142 self.stats.recursive_checks += 1;
143
144 self.stack.clear();
145 self.stack.push(lit);
146
147 while let Some(current_lit) = self.stack.pop() {
148 let var = current_lit.var();
149
150 match self.status[current_lit.index()] {
152 LitStatus::Redundant => continue,
153 LitStatus::Essential => return false,
154 LitStatus::Unknown => {}
155 }
156
157 let reason = &reasons[var.index()];
159
160 match reason {
161 Reason::Decision => {
162 return false;
164 }
165 Reason::Theory => {
166 return false;
168 }
169 Reason::Propagation(clause_id) => {
170 let Some(clause) = clauses.get(*clause_id) else {
172 return false;
173 };
174
175 for &reason_lit in &clause.lits {
176 if reason_lit.var() == var {
178 continue;
179 }
180
181 let reason_var = reason_lit.var();
182 let reason_level = level[reason_var.index()];
183
184 if reason_level == 0 {
185 continue;
187 }
188
189 if (abstract_level & (1 << (reason_level & 31))) == 0 {
191 return false;
192 }
193
194 if self.status[reason_lit.index()] == LitStatus::Unknown {
196 self.stack.push(reason_lit);
197 }
198 }
199 }
200 }
201
202 self.status[current_lit.index()] = LitStatus::Redundant;
204 }
205
206 true
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use crate::clause::ClauseDatabase;
214 use crate::literal::{Lit, Var};
215 use crate::trail::Reason;
216
217 #[test]
218 fn test_recursive_minimizer_creation() {
219 let minimizer = RecursiveMinimizer::new(10);
220 assert_eq!(minimizer.status.len(), 20); }
222
223 #[test]
224 fn test_recursive_minimizer_resize() {
225 let mut minimizer = RecursiveMinimizer::new(10);
226 minimizer.resize(20);
227 assert_eq!(minimizer.status.len(), 40);
228 }
229
230 #[test]
231 fn test_recursive_minimizer_stats() {
232 let minimizer = RecursiveMinimizer::new(10);
233 let stats = minimizer.stats();
234 assert_eq!(stats.clauses_minimized, 0);
235 assert_eq!(stats.literals_removed, 0);
236 assert_eq!(stats.recursive_checks, 0);
237 }
238
239 #[test]
240 fn test_minimize_empty_clause() {
241 let mut minimizer = RecursiveMinimizer::new(10);
242 let db = ClauseDatabase::new();
243 let reasons = vec![Reason::Decision; 10];
244 let level = vec![0; 10];
245
246 let clause: Vec<Lit> = vec![];
247 let result = minimizer.minimize(&clause, &db, &reasons, &level, 0);
248 assert_eq!(result.len(), 0);
249 }
250
251 #[test]
252 fn test_minimize_unit_clause() {
253 let mut minimizer = RecursiveMinimizer::new(10);
254 let db = ClauseDatabase::new();
255 let reasons = vec![Reason::Decision; 10];
256 let level = vec![0; 10];
257
258 let clause = vec![Lit::pos(Var::new(0))];
259 let result = minimizer.minimize(&clause, &db, &reasons, &level, 0);
260 assert_eq!(result.len(), 1);
261 }
262}