oxiz_nlsat/
inprocessing.rs1use crate::clause::{Clause, ClauseId};
11use crate::types::Literal;
12use rustc_hash::FxHashSet;
13use std::collections::HashMap;
14
15#[derive(Debug, Clone, Default)]
17pub struct InprocessStats {
18 pub subsumed_clauses: u64,
20 pub strengthened_clauses: u64,
22 pub literals_removed: u64,
24 pub inprocess_calls: u64,
26}
27
28#[derive(Debug, Clone)]
30pub struct InprocessConfig {
31 pub enable_subsumption: bool,
33 pub enable_strengthening: bool,
35 pub max_subsumption_size: usize,
37 pub inprocess_interval: u64,
39}
40
41impl Default for InprocessConfig {
42 fn default() -> Self {
43 Self {
44 enable_subsumption: true,
45 enable_strengthening: true,
46 max_subsumption_size: 30,
47 inprocess_interval: 5000,
48 }
49 }
50}
51
52pub struct Inprocessor {
54 config: InprocessConfig,
55 stats: InprocessStats,
56 literal_watch: HashMap<Literal, Vec<ClauseId>>,
58}
59
60impl Inprocessor {
61 pub fn new() -> Self {
63 Self::with_config(InprocessConfig::default())
64 }
65
66 pub fn with_config(config: InprocessConfig) -> Self {
68 Self {
69 config,
70 stats: InprocessStats::default(),
71 literal_watch: HashMap::new(),
72 }
73 }
74
75 pub fn stats(&self) -> &InprocessStats {
77 &self.stats
78 }
79
80 pub fn config(&self) -> &InprocessConfig {
82 &self.config
83 }
84
85 pub fn subsumes(clause_c: &[Literal], clause_d: &[Literal]) -> bool {
89 if clause_c.len() > clause_d.len() {
90 return false;
91 }
92
93 let d_set: FxHashSet<_> = clause_d.iter().copied().collect();
94
95 clause_c.iter().all(|&lit| d_set.contains(&lit))
96 }
97
98 pub fn self_subsumes(clause_c: &[Literal], clause_d: &[Literal]) -> Option<Literal> {
106 if clause_c.len() > clause_d.len() {
107 return None;
108 }
109
110 let d_set: FxHashSet<_> = clause_d.iter().copied().collect();
111
112 for &lit_c in clause_c {
114 let neg_lit_c = lit_c.negate();
115
116 if d_set.contains(&neg_lit_c) {
117 let all_match = clause_c
119 .iter()
120 .filter(|&&lit| lit != lit_c)
121 .all(|&lit| d_set.contains(&lit));
122
123 if all_match {
124 return Some(neg_lit_c); }
126 }
127 }
128
129 None
130 }
131
132 pub fn find_subsumptions(&mut self, clauses: &[Clause]) -> Vec<(ClauseId, ClauseId)> {
136 if !self.config.enable_subsumption {
137 return Vec::new();
138 }
139
140 let mut subsumptions = Vec::new();
141
142 self.literal_watch.clear();
144 for clause in clauses {
145 if clause.len() > self.config.max_subsumption_size {
146 continue;
147 }
148
149 for &lit in clause.literals() {
150 self.literal_watch.entry(lit).or_default().push(clause.id());
151 }
152 }
153
154 for i in 0..clauses.len() {
156 let clause_c = &clauses[i];
157
158 if clause_c.len() > self.config.max_subsumption_size {
159 continue;
160 }
161
162 if let Some(&first_lit) = clause_c.literals().first()
165 && let Some(candidates) = self.literal_watch.get(&first_lit)
166 {
167 for &candidate_id in candidates {
168 if candidate_id == clause_c.id() {
169 continue; }
171
172 if let Some(clause_d) = clauses.iter().find(|c| c.id() == candidate_id)
173 && Self::subsumes(clause_c.literals(), clause_d.literals())
174 {
175 subsumptions.push((clause_d.id(), clause_c.id()));
176 self.stats.subsumed_clauses += 1;
177 }
178 }
179 }
180 }
181
182 subsumptions
183 }
184
185 pub fn find_strengthenings(&mut self, clauses: &[Clause]) -> Vec<(ClauseId, Literal)> {
189 if !self.config.enable_strengthening {
190 return Vec::new();
191 }
192
193 let mut strengthenings = Vec::new();
194
195 for i in 0..clauses.len() {
196 for j in 0..clauses.len() {
197 if i == j {
198 continue;
199 }
200
201 let clause_c = &clauses[i];
202 let clause_d = &clauses[j];
203
204 if clause_c.len() >= clause_d.len() {
205 continue;
206 }
207
208 if clause_c.len() > self.config.max_subsumption_size
209 || clause_d.len() > self.config.max_subsumption_size
210 {
211 continue;
212 }
213
214 if let Some(lit_to_remove) =
215 Self::self_subsumes(clause_c.literals(), clause_d.literals())
216 {
217 strengthenings.push((clause_d.id(), lit_to_remove));
218 self.stats.strengthened_clauses += 1;
219 self.stats.literals_removed += 1;
220 }
221 }
222 }
223
224 strengthenings
225 }
226
227 pub fn inprocess(&mut self, conflicts: u64) -> bool {
231 self.stats.inprocess_calls += 1;
232
233 conflicts.is_multiple_of(self.config.inprocess_interval)
235 }
236}
237
238impl Default for Inprocessor {
239 fn default() -> Self {
240 Self::new()
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247 use crate::types::BoolVar;
248
249 fn make_literal(var: BoolVar, sign: bool) -> Literal {
250 Literal::new(var, sign)
251 }
252
253 #[test]
254 fn test_subsumption_basic() {
255 let c = vec![make_literal(0, true)];
257 let d = vec![make_literal(0, true), make_literal(1, true)];
258
259 assert!(Inprocessor::subsumes(&c, &d));
260 assert!(!Inprocessor::subsumes(&d, &c));
261 }
262
263 #[test]
264 fn test_subsumption_same_size() {
265 let c = vec![make_literal(0, true), make_literal(1, true)];
267 let d = vec![make_literal(0, true), make_literal(1, true)];
268
269 assert!(Inprocessor::subsumes(&c, &d));
270 }
271
272 #[test]
273 fn test_subsumption_different_literals() {
274 let c = vec![make_literal(0, true), make_literal(1, true)];
276 let d = vec![make_literal(0, true), make_literal(2, true)];
277
278 assert!(!Inprocessor::subsumes(&c, &d));
279 }
280
281 #[test]
282 fn test_self_subsumption() {
283 let c = vec![make_literal(0, true), make_literal(1, true)];
287 let d = vec![make_literal(0, true), make_literal(1, false)];
288
289 assert_eq!(
290 Inprocessor::self_subsumes(&c, &d),
291 Some(make_literal(1, false))
292 );
293 }
294
295 #[test]
296 fn test_self_subsumption_none() {
297 let c = vec![make_literal(0, true), make_literal(1, true)];
298 let d = vec![make_literal(0, true), make_literal(2, true)];
299
300 assert_eq!(Inprocessor::self_subsumes(&c, &d), None);
301 }
302
303 #[test]
304 fn test_inprocessor_new() {
305 let inproc = Inprocessor::new();
306 assert_eq!(inproc.stats().subsumed_clauses, 0);
307 assert_eq!(inproc.stats().strengthened_clauses, 0);
308 }
309
310 #[test]
311 fn test_inprocess_interval() {
312 let mut inproc = Inprocessor::new();
313 assert!(inproc.inprocess(5000));
314 assert!(!inproc.inprocess(5001));
315 assert!(inproc.inprocess(10000));
316 }
317}