1use crate::literal::Lit;
13#[allow(unused_imports)]
14use crate::prelude::*;
15use smallvec::SmallVec;
16
17#[derive(Debug, Clone, Default)]
19pub struct HyperBinaryStats {
20 pub hbr_attempts: u64,
22 pub binary_clauses_found: usize,
24 pub unit_clauses_found: usize,
26 pub conflicts_found: usize,
28 pub literals_probed: u64,
30}
31
32impl HyperBinaryStats {
33 pub fn display(&self) {
35 println!("Hyper-Binary Resolution Statistics:");
36 println!(" HBR attempts: {}", self.hbr_attempts);
37 println!(" Binary clauses found: {}", self.binary_clauses_found);
38 println!(" Unit clauses found: {}", self.unit_clauses_found);
39 println!(" Conflicts found: {}", self.conflicts_found);
40 println!(" Literals probed: {}", self.literals_probed);
41 if self.hbr_attempts > 0 {
42 let success_rate = 100.0 * self.binary_clauses_found as f64 / self.hbr_attempts as f64;
43 println!(" Success rate: {:.1}%", success_rate);
44 }
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum HbrResult {
51 None,
53 BinaryClause {
55 probe: Lit,
57 implied: Lit,
59 },
60 UnitClause {
62 lit: Lit,
64 },
65 Conflict {
67 probe: Lit,
69 },
70}
71
72#[derive(Debug)]
77pub struct HyperBinaryResolver {
78 implications: Vec<SmallVec<[Lit; 4]>>,
80 seen: Vec<bool>,
82 queue: Vec<Lit>,
84 stats: HyperBinaryStats,
86}
87
88impl Default for HyperBinaryResolver {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94impl HyperBinaryResolver {
95 #[must_use]
97 pub fn new() -> Self {
98 Self {
99 implications: Vec::new(),
100 seen: Vec::new(),
101 queue: Vec::new(),
102 stats: HyperBinaryStats::default(),
103 }
104 }
105
106 pub fn resize(&mut self, num_vars: usize) {
108 self.implications.resize(num_vars * 2, SmallVec::new());
109 self.seen.resize(num_vars * 2, false);
110 }
111
112 pub fn add_binary_clause(&mut self, a: Lit, b: Lit) {
118 let not_a_idx = (!a).code() as usize;
119 let not_b_idx = (!b).code() as usize;
120
121 let max_idx = not_a_idx.max(not_b_idx);
123 if max_idx >= self.implications.len() {
124 self.implications.resize(max_idx + 1, SmallVec::new());
125 }
126
127 if !self.implications[not_a_idx].contains(&b) {
129 self.implications[not_a_idx].push(b);
130 }
131 if !self.implications[not_b_idx].contains(&a) {
132 self.implications[not_b_idx].push(a);
133 }
134 }
135
136 pub fn probe(&mut self, probe_lit: Lit) -> Vec<HbrResult> {
141 self.stats.hbr_attempts += 1;
142 self.stats.literals_probed += 1;
143
144 let mut results = Vec::new();
145
146 for seen in &mut self.seen {
148 *seen = false;
149 }
150 self.queue.clear();
151
152 self.queue.push(probe_lit);
154 self.seen[probe_lit.code() as usize] = true;
155
156 while let Some(lit) = self.queue.pop() {
158 let lit_idx = lit.code() as usize;
159
160 if lit_idx >= self.implications.len() {
161 continue;
162 }
163
164 for &implied in &self.implications[lit_idx] {
166 let impl_idx = implied.code() as usize;
167
168 if self.seen[(!implied).code() as usize] {
170 results.push(HbrResult::Conflict { probe: probe_lit });
171 self.stats.conflicts_found += 1;
172 return results;
173 }
174
175 if !self.seen[impl_idx] {
177 self.seen[impl_idx] = true;
178 self.queue.push(implied);
179
180 if implied != probe_lit {
182 results.push(HbrResult::BinaryClause {
183 probe: probe_lit,
184 implied,
185 });
186 self.stats.binary_clauses_found += 1;
187 }
188 }
189 }
190 }
191
192 results
193 }
194
195 pub fn double_probe(&mut self, lit: Lit) -> Vec<HbrResult> {
203 let mut results = Vec::new();
204
205 let pos_results = self.probe(lit);
207 let pos_conflict = pos_results
208 .iter()
209 .any(|r| matches!(r, HbrResult::Conflict { .. }));
210
211 let neg_results = self.probe(!lit);
213 let neg_conflict = neg_results
214 .iter()
215 .any(|r| matches!(r, HbrResult::Conflict { .. }));
216
217 if pos_conflict && neg_conflict {
219 results.push(HbrResult::Conflict { probe: lit });
221 self.stats.conflicts_found += 1;
222 } else if pos_conflict {
223 results.push(HbrResult::UnitClause { lit: !lit });
225 self.stats.unit_clauses_found += 1;
226 } else if neg_conflict {
227 results.push(HbrResult::UnitClause { lit });
229 self.stats.unit_clauses_found += 1;
230 } else {
231 let pos_implied: Vec<_> = pos_results
233 .iter()
234 .filter_map(|r| match r {
235 HbrResult::BinaryClause { implied, .. } => Some(*implied),
236 _ => None,
237 })
238 .collect();
239
240 let neg_implied: Vec<_> = neg_results
241 .iter()
242 .filter_map(|r| match r {
243 HbrResult::BinaryClause { implied, .. } => Some(*implied),
244 _ => None,
245 })
246 .collect();
247
248 for &implied in &pos_implied {
250 if neg_implied.contains(&implied) {
251 results.push(HbrResult::UnitClause { lit: implied });
252 self.stats.unit_clauses_found += 1;
253 }
254 }
255 }
256
257 results
258 }
259
260 #[must_use]
262 pub fn stats(&self) -> &HyperBinaryStats {
263 &self.stats
264 }
265
266 pub fn clear(&mut self) {
268 for implications in &mut self.implications {
269 implications.clear();
270 }
271 self.seen.clear();
272 self.queue.clear();
273 }
274
275 pub fn reset_stats(&mut self) {
277 self.stats = HyperBinaryStats::default();
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284 use crate::literal::Var;
285
286 #[test]
287 fn test_hyper_binary_resolver_creation() {
288 let resolver = HyperBinaryResolver::new();
289 assert_eq!(resolver.implications.len(), 0);
290 }
291
292 #[test]
293 fn test_add_binary_clause() {
294 let mut resolver = HyperBinaryResolver::new();
295 resolver.resize(10);
296
297 let a = Lit::pos(Var::new(0));
298 let b = Lit::pos(Var::new(1));
299
300 resolver.add_binary_clause(a, b);
301
302 let not_a_idx = (!a).code() as usize;
304 let not_b_idx = (!b).code() as usize;
305
306 assert!(resolver.implications[not_a_idx].contains(&b));
307 assert!(resolver.implications[not_b_idx].contains(&a));
308 }
309
310 #[test]
311 fn test_simple_probe() {
312 let mut resolver = HyperBinaryResolver::new();
313 resolver.resize(10);
314
315 let a = Lit::pos(Var::new(0));
317 let b = Lit::pos(Var::new(1));
318 let c = Lit::pos(Var::new(2));
319
320 resolver.add_binary_clause(a, b);
321 resolver.add_binary_clause(b, c);
322
323 let results = resolver.probe(!a);
325
326 assert!(!results.is_empty());
327 }
329
330 #[test]
331 fn test_conflict_detection() {
332 let mut resolver = HyperBinaryResolver::new();
333 resolver.resize(10);
334
335 let a = Lit::pos(Var::new(0));
337 let b = Lit::pos(Var::new(1));
338
339 resolver.add_binary_clause(!a, b);
341 resolver.add_binary_clause(!a, !b);
342
343 let results = resolver.probe(a);
344
345 assert!(
347 results
348 .iter()
349 .any(|r| matches!(r, HbrResult::Conflict { .. }))
350 );
351 }
352
353 #[test]
354 fn test_double_probe_unit() {
355 let mut resolver = HyperBinaryResolver::new();
356 resolver.resize(10);
357
358 let a = Lit::pos(Var::new(0));
359 let b = Lit::pos(Var::new(1));
360
361 resolver.add_binary_clause(a, b);
363 resolver.add_binary_clause(a, !b); let results = resolver.double_probe(a);
366
367 assert!(
369 results
370 .iter()
371 .any(|r| matches!(r, HbrResult::UnitClause { .. }))
372 );
373 }
374
375 #[test]
376 fn test_stats_tracking() {
377 let mut resolver = HyperBinaryResolver::new();
378 resolver.resize(10);
379
380 let a = Lit::pos(Var::new(0));
381 let b = Lit::pos(Var::new(1));
382
383 resolver.add_binary_clause(a, b);
384 resolver.probe(a);
385
386 assert_eq!(resolver.stats().hbr_attempts, 1);
387 assert_eq!(resolver.stats().literals_probed, 1);
388 }
389
390 #[test]
391 fn test_clear() {
392 let mut resolver = HyperBinaryResolver::new();
393 resolver.resize(10);
394
395 let a = Lit::pos(Var::new(0));
396 let b = Lit::pos(Var::new(1));
397
398 resolver.add_binary_clause(a, b);
399 resolver.clear();
400
401 assert!(resolver.implications.iter().all(|v| v.is_empty()));
402 }
403
404 #[test]
405 fn test_reset_stats() {
406 let mut resolver = HyperBinaryResolver::new();
407 resolver.resize(10);
408
409 resolver.probe(Lit::pos(Var::new(0)));
410 resolver.reset_stats();
411
412 assert_eq!(resolver.stats().hbr_attempts, 0);
413 }
414}