1use crate::literal::Lit;
4#[allow(unused_imports)]
5use crate::prelude::*;
6use smallvec::SmallVec;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct ClauseId(pub u32);
11
12impl ClauseId {
13 pub const NULL: Self = Self(u32::MAX);
15
16 #[must_use]
18 pub const fn new(id: u32) -> Self {
19 Self(id)
20 }
21
22 #[must_use]
24 pub const fn is_null(self) -> bool {
25 self.0 == u32::MAX
26 }
27
28 #[must_use]
30 pub const fn index(self) -> usize {
31 self.0 as usize
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
37pub enum ClauseTier {
38 Local = 3,
40 Mid = 2,
42 Core = 1,
44}
45
46#[derive(Debug, Clone)]
50#[repr(align(64))]
51pub struct Clause {
52 pub lits: SmallVec<[Lit; 4]>,
54 pub learned: bool,
56 pub activity: f64,
58 pub lbd: u32,
60 pub deleted: bool,
62 pub tier: ClauseTier,
64 pub usage_count: u32,
66}
67
68impl Clause {
69 #[must_use]
71 pub fn new(lits: impl IntoIterator<Item = Lit>, learned: bool) -> Self {
72 Self {
73 lits: lits.into_iter().collect(),
74 learned,
75 activity: 0.0,
76 lbd: 0,
77 deleted: false,
78 tier: ClauseTier::Local, usage_count: 0,
80 }
81 }
82
83 #[must_use]
85 pub fn original(lits: impl IntoIterator<Item = Lit>) -> Self {
86 Self::new(lits, false)
87 }
88
89 #[must_use]
91 pub fn learned(lits: impl IntoIterator<Item = Lit>) -> Self {
92 Self::new(lits, true)
93 }
94
95 #[must_use]
97 pub fn len(&self) -> usize {
98 self.lits.len()
99 }
100
101 #[must_use]
103 pub fn is_empty(&self) -> bool {
104 self.lits.is_empty()
105 }
106
107 #[must_use]
109 pub fn is_unit(&self) -> bool {
110 self.lits.len() == 1
111 }
112
113 #[must_use]
115 pub fn is_binary(&self) -> bool {
116 self.lits.len() == 2
117 }
118
119 #[must_use]
121 pub fn unit_lit(&self) -> Option<Lit> {
122 if self.is_unit() {
123 Some(self.lits[0])
124 } else {
125 None
126 }
127 }
128
129 pub fn swap(&mut self, i: usize, j: usize) {
131 self.lits.swap(i, j);
132 }
133
134 pub fn record_usage(&mut self) {
136 self.usage_count += 1;
137
138 if self.usage_count >= 3 && self.tier == ClauseTier::Local {
140 self.tier = ClauseTier::Mid;
141 }
142 else if (self.usage_count >= 10 || self.lbd <= 2) && self.tier == ClauseTier::Mid {
144 self.tier = ClauseTier::Core;
145 }
146 }
147
148 pub fn promote_to_core(&mut self) {
150 self.tier = ClauseTier::Core;
151 }
152
153 pub fn normalize(&mut self) -> bool {
156 if self.lits.is_empty() {
157 return false;
158 }
159
160 self.lits.sort_unstable_by_key(|lit| lit.code());
162
163 let mut write_idx = 0;
165 let mut prev_lit = self.lits[0];
166
167 for read_idx in 1..self.lits.len() {
168 let curr_lit = self.lits[read_idx];
169
170 if curr_lit == prev_lit.negate() {
172 return true;
173 }
174
175 if curr_lit != prev_lit {
177 write_idx += 1;
178 self.lits[write_idx] = curr_lit;
179 prev_lit = curr_lit;
180 }
181 }
182
183 self.lits.truncate(write_idx + 1);
185 false
186 }
187
188 #[must_use]
191 pub fn subsumes(&self, other: &Clause) -> bool {
192 if self.lits.len() > other.lits.len() {
193 return false;
194 }
195
196 let mut i = 0;
198 let mut j = 0;
199
200 while i < self.lits.len() && j < other.lits.len() {
201 if self.lits[i] == other.lits[j] {
202 i += 1;
203 j += 1;
204 } else if self.lits[i].code() < other.lits[j].code() {
205 return false;
207 } else {
208 j += 1;
209 }
210 }
211
212 i == self.lits.len()
213 }
214
215 #[must_use]
218 pub fn self_subsuming_resolvent(&self, other: &Clause) -> Option<Lit> {
219 if self.lits.len() >= other.lits.len() {
220 return None;
221 }
222
223 let mut diff_lit = None;
224 let mut matches = 0;
225
226 for &other_lit in &other.lits {
227 if self.lits.contains(&other_lit) {
228 matches += 1;
229 } else if self.lits.contains(&other_lit.negate()) {
230 if diff_lit.is_some() {
231 return None; }
233 diff_lit = Some(other_lit);
234 }
235 }
236
237 if matches == self.lits.len() - 1 && diff_lit.is_some() {
240 diff_lit
241 } else {
242 None
243 }
244 }
245}
246
247#[derive(Debug, Clone, Default)]
249pub struct ClauseDatabaseStats {
250 pub tier_counts: [usize; 3], pub total_lbd: u64,
254 pub lbd_count: usize,
256 pub size_distribution: [usize; 10], pub promotions: usize,
260 pub demotions: usize,
262}
263
264impl ClauseDatabaseStats {
265 #[must_use]
267 pub fn avg_lbd(&self) -> f64 {
268 if self.lbd_count == 0 {
269 0.0
270 } else {
271 self.total_lbd as f64 / self.lbd_count as f64
272 }
273 }
274
275 pub fn display(&self) {
277 println!("Clause Database Statistics:");
278 println!(" Tier distribution:");
279 println!(" Core: {}", self.tier_counts[0]);
280 println!(" Mid: {}", self.tier_counts[1]);
281 println!(" Local: {}", self.tier_counts[2]);
282 println!(" Average LBD: {:.2}", self.avg_lbd());
283 println!(" Size distribution:");
284 for (i, &count) in self.size_distribution.iter().enumerate() {
285 if count > 0 {
286 let size = if i < 9 {
287 format!("{}", i + 2)
288 } else {
289 "10+".to_string()
290 };
291 println!(" {} literals: {}", size, count);
292 }
293 }
294 println!(
295 " Promotions: {}, Demotions: {}",
296 self.promotions, self.demotions
297 );
298 }
299}
300
301#[derive(Debug)]
303pub struct ClauseDatabase {
304 clauses: Vec<Clause>,
306 num_original: usize,
308 num_learned: usize,
310 free_list: Vec<ClauseId>,
312 stats: ClauseDatabaseStats,
314}
315
316impl Default for ClauseDatabase {
317 fn default() -> Self {
318 Self::new()
319 }
320}
321
322impl ClauseDatabase {
323 #[must_use]
325 pub fn new() -> Self {
326 Self {
327 clauses: Vec::new(),
328 num_original: 0,
329 num_learned: 0,
330 free_list: Vec::new(),
331 stats: ClauseDatabaseStats::default(),
332 }
333 }
334
335 #[must_use]
337 pub fn stats(&self) -> &ClauseDatabaseStats {
338 &self.stats
339 }
340
341 fn update_stats_add(&mut self, clause: &Clause) {
343 if clause.learned {
344 let tier_idx = match clause.tier {
346 ClauseTier::Core => 0,
347 ClauseTier::Mid => 1,
348 ClauseTier::Local => 2,
349 };
350 self.stats.tier_counts[tier_idx] += 1;
351
352 if clause.lbd > 0 {
354 self.stats.total_lbd += clause.lbd as u64;
355 self.stats.lbd_count += 1;
356 }
357 }
358
359 if clause.len() >= 2 {
361 let size_idx = if clause.len() >= 12 {
362 9 } else {
364 clause.len() - 2
365 };
366 self.stats.size_distribution[size_idx] += 1;
367 }
368 }
369
370 fn update_stats_remove(&mut self, clause: &Clause) {
372 if clause.learned {
373 let tier_idx = match clause.tier {
375 ClauseTier::Core => 0,
376 ClauseTier::Mid => 1,
377 ClauseTier::Local => 2,
378 };
379 if self.stats.tier_counts[tier_idx] > 0 {
380 self.stats.tier_counts[tier_idx] -= 1;
381 }
382
383 if clause.lbd > 0 && self.stats.lbd_count > 0 {
385 self.stats.total_lbd = self.stats.total_lbd.saturating_sub(clause.lbd as u64);
386 self.stats.lbd_count -= 1;
387 }
388 }
389
390 if clause.len() >= 2 {
392 let size_idx = if clause.len() >= 12 {
393 9 } else {
395 clause.len() - 2
396 };
397 if self.stats.size_distribution[size_idx] > 0 {
398 self.stats.size_distribution[size_idx] -= 1;
399 }
400 }
401 }
402
403 pub fn add(&mut self, clause: Clause) -> ClauseId {
407 self.update_stats_add(&clause);
409
410 if let Some(id) = self.free_list.pop() {
412 if let Some(slot) = self.clauses.get_mut(id.index()) {
414 *slot = clause.clone();
415 if clause.learned {
416 self.num_learned += 1;
417 } else {
418 self.num_original += 1;
419 }
420 return id;
421 }
422 }
423
424 let id = ClauseId::new(self.clauses.len() as u32);
426 if clause.learned {
427 self.num_learned += 1;
428 } else {
429 self.num_original += 1;
430 }
431 self.clauses.push(clause);
432 id
433 }
434
435 pub fn add_original(&mut self, lits: impl IntoIterator<Item = Lit>) -> ClauseId {
437 self.add(Clause::original(lits))
438 }
439
440 pub fn add_learned(&mut self, lits: impl IntoIterator<Item = Lit>) -> ClauseId {
442 self.add(Clause::learned(lits))
443 }
444
445 #[must_use]
447 pub fn get(&self, id: ClauseId) -> Option<&Clause> {
448 self.clauses.get(id.index())
449 }
450
451 pub fn get_mut(&mut self, id: ClauseId) -> Option<&mut Clause> {
453 self.clauses.get_mut(id.index())
454 }
455
456 pub fn remove(&mut self, id: ClauseId) {
460 if let Some(clause) = self.clauses.get_mut(id.index())
461 && !clause.deleted
462 {
463 let clause_copy = clause.clone();
465
466 clause.deleted = true;
467 if clause.learned {
468 self.num_learned -= 1;
469 } else {
470 self.num_original -= 1;
471 }
472 self.free_list.push(id);
474
475 self.update_stats_remove(&clause_copy);
477 }
478 }
479
480 pub fn compact(&mut self) {
484 const MAX_FREE_LIST_SIZE: usize = 1000;
486
487 if self.free_list.len() > MAX_FREE_LIST_SIZE {
488 self.free_list
490 .drain(0..self.free_list.len() - MAX_FREE_LIST_SIZE);
491 }
492 }
493
494 #[must_use]
496 pub fn len(&self) -> usize {
497 self.num_original + self.num_learned
498 }
499
500 #[must_use]
502 pub fn is_empty(&self) -> bool {
503 self.len() == 0
504 }
505
506 #[must_use]
508 pub fn num_original(&self) -> usize {
509 self.num_original
510 }
511
512 #[must_use]
514 pub fn num_learned(&self) -> usize {
515 self.num_learned
516 }
517
518 pub fn iter_ids(&self) -> impl Iterator<Item = ClauseId> + '_ {
520 self.clauses
521 .iter()
522 .enumerate()
523 .filter(|(_, c)| !c.deleted)
524 .map(|(i, _)| ClauseId::new(i as u32))
525 }
526
527 pub fn bump_activity(&mut self, id: ClauseId, increment: f64) {
529 if let Some(clause) = self.get_mut(id) {
530 clause.activity += increment;
531 }
532 }
533
534 pub fn decay_activity(&mut self, factor: f64) {
536 for clause in &mut self.clauses {
537 if !clause.deleted {
538 clause.activity *= factor;
539 }
540 }
541 }
542}
543
544#[cfg(test)]
545mod tests {
546 use super::*;
547 use crate::literal::Var;
548
549 #[test]
550 fn test_clause_creation() {
551 let lits = vec![
552 Lit::pos(Var::new(0)),
553 Lit::neg(Var::new(1)),
554 Lit::pos(Var::new(2)),
555 ];
556 let clause = Clause::original(lits.clone());
557
558 assert_eq!(clause.len(), 3);
559 assert!(!clause.is_unit());
560 assert!(!clause.is_binary());
561 assert!(!clause.learned);
562 }
563
564 #[test]
565 fn test_clause_database() {
566 let mut db = ClauseDatabase::new();
567
568 let c1 = db.add_original([Lit::pos(Var::new(0)), Lit::neg(Var::new(1))]);
569 let _c2 = db.add_learned([Lit::pos(Var::new(2))]);
570
571 assert_eq!(db.len(), 2);
572 assert_eq!(db.num_original(), 1);
573 assert_eq!(db.num_learned(), 1);
574
575 db.remove(c1);
576 assert_eq!(db.len(), 1);
577 assert_eq!(db.num_original(), 0);
578 }
579
580 #[test]
581 fn test_clause_normalize() {
582 let mut clause = Clause::original([
583 Lit::pos(Var::new(2)),
584 Lit::pos(Var::new(0)),
585 Lit::pos(Var::new(2)), Lit::pos(Var::new(1)),
587 ]);
588
589 let is_tautology = clause.normalize();
590 assert!(!is_tautology);
591 assert_eq!(clause.len(), 3); assert_eq!(clause.lits[0], Lit::pos(Var::new(0)));
594 assert_eq!(clause.lits[1], Lit::pos(Var::new(1)));
595 assert_eq!(clause.lits[2], Lit::pos(Var::new(2)));
596 }
597
598 #[test]
599 fn test_clause_normalize_tautology() {
600 let mut clause = Clause::original([
601 Lit::pos(Var::new(0)),
602 Lit::neg(Var::new(0)), Lit::pos(Var::new(1)),
604 ]);
605
606 let is_tautology = clause.normalize();
607 assert!(is_tautology);
608 }
609
610 #[test]
611 fn test_clause_subsumes() {
612 let mut c1 = Clause::original([Lit::pos(Var::new(0)), Lit::pos(Var::new(1))]);
613 let mut c2 = Clause::original([
614 Lit::pos(Var::new(0)),
615 Lit::pos(Var::new(1)),
616 Lit::pos(Var::new(2)),
617 ]);
618
619 c1.normalize();
620 c2.normalize();
621
622 assert!(c1.subsumes(&c2)); assert!(!c2.subsumes(&c1)); }
625
626 #[test]
627 fn test_clause_self_subsuming_resolvent() {
628 let mut c1 = Clause::original([Lit::pos(Var::new(0)), Lit::pos(Var::new(1))]);
631 let mut c2 = Clause::original([
632 Lit::neg(Var::new(0)),
633 Lit::pos(Var::new(1)),
634 Lit::pos(Var::new(2)),
635 ]);
636
637 c1.normalize();
638 c2.normalize();
639
640 if let Some(lit_to_remove) = c1.self_subsuming_resolvent(&c2) {
641 assert_eq!(lit_to_remove, Lit::neg(Var::new(0)));
642 } else {
643 panic!("Expected self-subsuming resolvent");
644 }
645 }
646}