1use crate::error::IrError;
61use crate::expr::TLExpr;
62use crate::term::Term;
63use crate::unification::{rename_vars, unify_term_list, Substitution};
64use serde::{Deserialize, Serialize};
65use std::collections::{HashSet, VecDeque};
66
67#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
69pub struct Literal {
70 pub atom: TLExpr,
72 pub polarity: bool,
74}
75
76impl Literal {
77 pub fn positive(atom: TLExpr) -> Self {
79 Literal {
80 atom,
81 polarity: true,
82 }
83 }
84
85 pub fn negative(atom: TLExpr) -> Self {
87 Literal {
88 atom,
89 polarity: false,
90 }
91 }
92
93 pub fn negate(&self) -> Self {
95 Literal {
96 atom: self.atom.clone(),
97 polarity: !self.polarity,
98 }
99 }
100
101 pub fn is_complementary(&self, other: &Literal) -> bool {
105 self.atom == other.atom && self.polarity != other.polarity
106 }
107
108 pub fn try_unify(&self, other: &Literal) -> Option<Substitution> {
128 if self.polarity == other.polarity {
130 return None;
131 }
132
133 self.try_unify_atoms(&other.atom)
135 }
136
137 fn try_unify_atoms(&self, other_atom: &TLExpr) -> Option<Substitution> {
141 match (&self.atom, other_atom) {
142 (
143 TLExpr::Pred {
144 name: n1,
145 args: args1,
146 },
147 TLExpr::Pred {
148 name: n2,
149 args: args2,
150 },
151 ) => {
152 if n1 != n2 {
154 return None;
155 }
156
157 if args1.len() != args2.len() {
159 return None;
160 }
161
162 let pairs: Vec<(Term, Term)> = args1
164 .iter()
165 .zip(args2.iter())
166 .map(|(t1, t2)| (t1.clone(), t2.clone()))
167 .collect();
168
169 unify_term_list(&pairs).ok()
170 }
171 _ => None,
172 }
173 }
174
175 pub fn apply_substitution(&self, subst: &Substitution) -> Literal {
179 let new_atom = self.apply_subst_to_expr(&self.atom, subst);
180 Literal {
181 atom: new_atom,
182 polarity: self.polarity,
183 }
184 }
185
186 fn apply_subst_to_expr(&self, expr: &TLExpr, subst: &Substitution) -> TLExpr {
188 match expr {
189 TLExpr::Pred { name, args } => {
190 let new_args = args.iter().map(|term| subst.apply(term)).collect();
191 TLExpr::Pred {
192 name: name.clone(),
193 args: new_args,
194 }
195 }
196 _ => expr.clone(),
198 }
199 }
200
201 pub fn is_positive(&self) -> bool {
203 self.polarity
204 }
205
206 pub fn is_negative(&self) -> bool {
208 !self.polarity
209 }
210
211 pub fn free_vars(&self) -> HashSet<String> {
213 self.atom.free_vars()
214 }
215}
216
217#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
224pub struct Clause {
225 pub literals: Vec<Literal>,
227}
228
229impl Clause {
230 pub fn from_literals(literals: Vec<Literal>) -> Self {
232 let mut unique_lits: Vec<Literal> = literals.into_iter().collect();
234 unique_lits.sort_by(|a, b| {
235 let a_str = format!("{:?}", a);
236 let b_str = format!("{:?}", b);
237 a_str.cmp(&b_str)
238 });
239 unique_lits.dedup();
240
241 Clause {
242 literals: unique_lits,
243 }
244 }
245
246 pub fn empty() -> Self {
248 Clause { literals: vec![] }
249 }
250
251 pub fn unit(literal: Literal) -> Self {
253 Clause {
254 literals: vec![literal],
255 }
256 }
257
258 pub fn is_empty(&self) -> bool {
260 self.literals.is_empty()
261 }
262
263 pub fn is_unit(&self) -> bool {
265 self.literals.len() == 1
266 }
267
268 pub fn is_horn(&self) -> bool {
270 self.literals.iter().filter(|l| l.is_positive()).count() <= 1
271 }
272
273 pub fn len(&self) -> usize {
275 self.literals.len()
276 }
277
278 pub fn is_len_zero(&self) -> bool {
280 self.literals.is_empty()
281 }
282
283 pub fn free_vars(&self) -> HashSet<String> {
285 self.literals
286 .iter()
287 .flat_map(|lit| lit.free_vars())
288 .collect()
289 }
290
291 pub fn subsumes(&self, other: &Clause) -> bool {
321 if self.is_empty() {
323 return other.is_empty();
324 }
325
326 if self.literals.len() > other.literals.len() {
328 return false;
329 }
330
331 self.try_subsumption_matching(other).is_some()
333 }
334
335 fn try_subsumption_matching(&self, other: &Clause) -> Option<Substitution> {
339 static mut SUBSUME_COUNTER: usize = 0;
341 let counter = unsafe {
342 SUBSUME_COUNTER += 1;
343 SUBSUME_COUNTER
344 };
345
346 let renamed_self = self.rename_variables(&format!("_s{}", counter));
347 let renamed_vars: HashSet<String> = renamed_self.free_vars();
348
349 let mut subst = Substitution::empty();
351
352 for self_lit in &renamed_self.literals {
353 let mut found_match = false;
355
356 for other_lit in &other.literals {
357 if self_lit.polarity != other_lit.polarity {
359 continue;
360 }
361
362 if let Some(lit_mgu) = self_lit.try_one_way_match(&other_lit.atom, &renamed_vars) {
364 if let Ok(()) = subst.try_extend(&lit_mgu) {
366 found_match = true;
367 break;
368 }
369 }
370 }
371
372 if !found_match {
373 return None; }
375 }
376
377 Some(subst)
378 }
379}
380
381impl Literal {
382 fn try_one_way_match(
387 &self,
388 other_atom: &TLExpr,
389 allowed_vars: &HashSet<String>,
390 ) -> Option<Substitution> {
391 match (&self.atom, other_atom) {
392 (
393 TLExpr::Pred {
394 name: n1,
395 args: args1,
396 },
397 TLExpr::Pred {
398 name: n2,
399 args: args2,
400 },
401 ) => {
402 if n1 != n2 {
404 return None;
405 }
406
407 if args1.len() != args2.len() {
409 return None;
410 }
411
412 let mut subst = Substitution::empty();
414
415 for (t1, t2) in args1.iter().zip(args2.iter()) {
416 if !try_one_way_match_terms(t1, t2, allowed_vars, &mut subst) {
417 return None;
418 }
419 }
420
421 Some(subst)
422 }
423 _ => None,
424 }
425 }
426}
427
428fn try_one_way_match_terms(
430 t1: &Term,
431 t2: &Term,
432 allowed_vars: &HashSet<String>,
433 subst: &mut Substitution,
434) -> bool {
435 let t1_subst = subst.apply(t1);
437
438 match (&t1_subst, t2) {
439 (Term::Const(c1), Term::Const(c2)) => c1 == c2,
441
442 (Term::Var(v1), Term::Var(v2)) => v1 == v2,
444
445 (Term::Var(v1), _) if allowed_vars.contains(v1) => {
447 let after_subst = subst.apply(&t1_subst);
449 if after_subst != t1_subst {
450 &after_subst == t2
452 } else {
453 subst.bind(v1.clone(), t2.clone());
455 true
456 }
457 }
458
459 (Term::Var(_), _) => false,
461
462 (_, Term::Var(_)) => false,
464
465 (
467 Term::Typed {
468 value: inner1,
469 type_annotation: ty1,
470 },
471 Term::Typed {
472 value: inner2,
473 type_annotation: ty2,
474 },
475 ) => {
476 if ty1 != ty2 {
477 return false;
478 }
479 try_one_way_match_terms(inner1, inner2, allowed_vars, subst)
480 }
481
482 (Term::Typed { value, .. }, other) | (other, Term::Typed { value, .. }) => {
484 try_one_way_match_terms(value, other, allowed_vars, subst)
485 }
486 }
487}
488
489impl Clause {
490 pub fn is_tautology(&self) -> bool {
492 for i in 0..self.literals.len() {
493 for j in (i + 1)..self.literals.len() {
494 if self.literals[i].is_complementary(&self.literals[j]) {
495 return true;
496 }
497 }
498 }
499 false
500 }
501
502 pub fn apply_substitution(&self, subst: &Substitution) -> Clause {
506 let new_literals = self
507 .literals
508 .iter()
509 .map(|lit| lit.apply_substitution(subst))
510 .collect();
511 Clause::from_literals(new_literals)
512 }
513
514 pub fn rename_variables(&self, suffix: &str) -> Clause {
533 let renamed_literals = self
534 .literals
535 .iter()
536 .map(|lit| self.rename_literal(lit, suffix))
537 .collect();
538 Clause::from_literals(renamed_literals)
539 }
540
541 fn rename_literal(&self, lit: &Literal, suffix: &str) -> Literal {
543 match &lit.atom {
544 TLExpr::Pred { name, args } => {
545 let renamed_args = args.iter().map(|term| rename_vars(term, suffix)).collect();
546 Literal {
547 atom: TLExpr::Pred {
548 name: name.clone(),
549 args: renamed_args,
550 },
551 polarity: lit.polarity,
552 }
553 }
554 _ => lit.clone(),
555 }
556 }
557}
558
559#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
561pub enum ProofResult {
562 Unsatisfiable {
564 steps: usize,
566 derivation: Vec<ResolutionStep>,
568 },
569 Satisfiable,
571 Saturated {
573 clauses_generated: usize,
575 },
576 ResourceLimitReached {
578 steps_attempted: usize,
580 },
581}
582
583impl ProofResult {
584 pub fn is_unsatisfiable(&self) -> bool {
586 matches!(self, ProofResult::Unsatisfiable { .. })
587 }
588
589 pub fn is_satisfiable(&self) -> bool {
591 matches!(self, ProofResult::Satisfiable)
592 }
593
594 pub fn steps(&self) -> usize {
596 match self {
597 ProofResult::Unsatisfiable { steps, .. } => *steps,
598 ProofResult::ResourceLimitReached { steps_attempted } => *steps_attempted,
599 ProofResult::Saturated { clauses_generated } => *clauses_generated,
600 ProofResult::Satisfiable => 0,
601 }
602 }
603}
604
605#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
607pub struct ResolutionStep {
608 pub parent1: Clause,
610 pub parent2: Clause,
612 pub resolvent: Clause,
614 pub resolved_literal: Literal,
616}
617
618#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
620pub enum ResolutionStrategy {
621 Saturation {
623 max_clauses: usize,
625 },
626 SetOfSupport {
628 max_steps: usize,
630 },
631 UnitResolution {
633 max_steps: usize,
635 },
636 Linear {
638 max_depth: usize,
640 },
641}
642
643pub struct ResolutionProver {
645 clauses: Vec<Clause>,
647 strategy: ResolutionStrategy,
649 pub stats: ProverStats,
651}
652
653#[derive(Clone, Debug, Default, Serialize, Deserialize)]
655pub struct ProverStats {
656 pub clauses_generated: usize,
658 pub resolution_steps: usize,
660 pub tautologies_removed: usize,
662 pub clauses_subsumed: usize,
664 pub empty_clause_found: bool,
666}
667
668impl ResolutionProver {
669 pub fn new() -> Self {
671 ResolutionProver {
672 clauses: Vec::new(),
673 strategy: ResolutionStrategy::Saturation { max_clauses: 10000 },
674 stats: ProverStats::default(),
675 }
676 }
677
678 pub fn with_strategy(strategy: ResolutionStrategy) -> Self {
680 ResolutionProver {
681 clauses: Vec::new(),
682 strategy,
683 stats: ProverStats::default(),
684 }
685 }
686
687 pub fn add_clause(&mut self, clause: Clause) {
689 if !clause.is_tautology() {
691 self.clauses.push(clause);
692 } else {
693 self.stats.tautologies_removed += 1;
694 }
695 }
696
697 pub fn add_clauses(&mut self, clauses: Vec<Clause>) {
699 for clause in clauses {
700 self.add_clause(clause);
701 }
702 }
703
704 pub fn reset(&mut self) {
706 self.clauses.clear();
707 self.stats = ProverStats::default();
708 }
709
710 fn resolve(&self, c1: &Clause, c2: &Clause) -> Vec<(Clause, Literal)> {
717 let mut resolvents = Vec::new();
718
719 for lit1 in &c1.literals {
721 for lit2 in &c2.literals {
722 if lit1.is_complementary(lit2) {
723 let mut new_literals = Vec::new();
725
726 for lit in &c1.literals {
728 if lit != lit1 {
729 new_literals.push(lit.clone());
730 }
731 }
732
733 for lit in &c2.literals {
735 if lit != lit2 {
736 new_literals.push(lit.clone());
737 }
738 }
739
740 let resolvent = Clause::from_literals(new_literals);
741 resolvents.push((resolvent, lit1.clone()));
742 }
743 }
744 }
745
746 resolvents
747 }
748
749 pub fn resolve_first_order(&self, c1: &Clause, c2: &Clause) -> Vec<(Clause, Literal)> {
775 static mut RENAME_COUNTER: usize = 0;
778 let counter = unsafe {
779 RENAME_COUNTER += 1;
780 RENAME_COUNTER
781 };
782
783 let c1_renamed = c1.rename_variables(&format!("_c1_{}", counter));
785 let c2_renamed = c2.rename_variables(&format!("_c2_{}", counter));
786
787 let mut resolvents = Vec::new();
788
789 for lit1 in &c1_renamed.literals {
791 for lit2 in &c2_renamed.literals {
792 if let Some(mgu) = lit1.try_unify(lit2) {
794 let mut new_literals = Vec::new();
796
797 for lit in &c1_renamed.literals {
799 if lit != lit1 {
800 new_literals.push(lit.apply_substitution(&mgu));
801 }
802 }
803
804 for lit in &c2_renamed.literals {
806 if lit != lit2 {
807 new_literals.push(lit.apply_substitution(&mgu));
808 }
809 }
810
811 let resolvent = Clause::from_literals(new_literals);
812 let orig_lit = lit1.clone(); resolvents.push((resolvent, orig_lit));
815 }
816 }
817 }
818
819 resolvents
820 }
821
822 fn is_subsumed(&self, clause: &Clause, clause_set: &[Clause]) -> bool {
824 clause_set.iter().any(|c| c.subsumes(clause))
825 }
826
827 pub fn prove(&mut self) -> ProofResult {
829 match &self.strategy {
830 ResolutionStrategy::Saturation { max_clauses } => self.prove_saturation(*max_clauses),
831 ResolutionStrategy::SetOfSupport { max_steps } => self.prove_set_of_support(*max_steps),
832 ResolutionStrategy::UnitResolution { max_steps } => {
833 self.prove_unit_resolution(*max_steps)
834 }
835 ResolutionStrategy::Linear { max_depth } => self.prove_linear(*max_depth),
836 }
837 }
838
839 fn prove_saturation(&mut self, max_clauses: usize) -> ProofResult {
841 let mut clause_set: Vec<Clause> = self.clauses.clone();
842 let mut derivation = Vec::new();
843 let mut steps = 0;
844
845 if clause_set.iter().any(|c| c.is_empty()) {
847 self.stats.empty_clause_found = true;
848 return ProofResult::Unsatisfiable {
849 steps: 0,
850 derivation: vec![],
851 };
852 }
853
854 loop {
855 let current_clauses: Vec<Clause> = clause_set.clone();
856 let mut new_clauses = Vec::new();
857
858 for i in 0..current_clauses.len() {
860 for j in (i + 1)..current_clauses.len() {
861 let resolvents = self.resolve(¤t_clauses[i], ¤t_clauses[j]);
862
863 for (resolvent, resolved_lit) in resolvents {
864 steps += 1;
865 self.stats.resolution_steps += 1;
866
867 if resolvent.is_tautology() {
869 self.stats.tautologies_removed += 1;
870 continue;
871 }
872
873 if resolvent.is_empty() {
875 self.stats.empty_clause_found = true;
876 derivation.push(ResolutionStep {
877 parent1: current_clauses[i].clone(),
878 parent2: current_clauses[j].clone(),
879 resolvent: resolvent.clone(),
880 resolved_literal: resolved_lit,
881 });
882 return ProofResult::Unsatisfiable { steps, derivation };
883 }
884
885 if self.is_subsumed(&resolvent, ¤t_clauses) {
887 self.stats.clauses_subsumed += 1;
888 continue;
889 }
890
891 if !clause_set.contains(&resolvent) && !new_clauses.contains(&resolvent) {
893 new_clauses.push(resolvent.clone());
894 derivation.push(ResolutionStep {
895 parent1: current_clauses[i].clone(),
896 parent2: current_clauses[j].clone(),
897 resolvent,
898 resolved_literal: resolved_lit,
899 });
900 }
901 }
902 }
903 }
904
905 if new_clauses.is_empty() {
907 return ProofResult::Saturated {
908 clauses_generated: clause_set.len(),
909 };
910 }
911
912 for clause in new_clauses {
914 clause_set.push(clause);
915 self.stats.clauses_generated += 1;
916
917 if clause_set.len() >= max_clauses {
918 return ProofResult::ResourceLimitReached {
919 steps_attempted: steps,
920 };
921 }
922 }
923 }
924 }
925
926 fn prove_set_of_support(&mut self, max_steps: usize) -> ProofResult {
928 if self.clauses.is_empty() {
930 return ProofResult::Satisfiable;
931 }
932
933 let support = self.clauses.pop().unwrap();
934 let mut sos: VecDeque<Clause> = VecDeque::new();
935 sos.push_back(support);
936
937 let usable: Vec<Clause> = self.clauses.clone();
938 let mut derivation = Vec::new();
939 let mut steps = 0;
940
941 while let Some(current) = sos.pop_front() {
942 if steps >= max_steps {
943 return ProofResult::ResourceLimitReached {
944 steps_attempted: steps,
945 };
946 }
947
948 if current.is_empty() {
949 self.stats.empty_clause_found = true;
950 return ProofResult::Unsatisfiable { steps, derivation };
951 }
952
953 for usable_clause in &usable {
955 let resolvents = self.resolve(¤t, usable_clause);
956
957 for (resolvent, resolved_lit) in resolvents {
958 steps += 1;
959 self.stats.resolution_steps += 1;
960
961 if resolvent.is_tautology() {
962 self.stats.tautologies_removed += 1;
963 continue;
964 }
965
966 if resolvent.is_empty() {
967 self.stats.empty_clause_found = true;
968 derivation.push(ResolutionStep {
969 parent1: current.clone(),
970 parent2: usable_clause.clone(),
971 resolvent: resolvent.clone(),
972 resolved_literal: resolved_lit,
973 });
974 return ProofResult::Unsatisfiable { steps, derivation };
975 }
976
977 sos.push_back(resolvent.clone());
978 self.stats.clauses_generated += 1;
979 derivation.push(ResolutionStep {
980 parent1: current.clone(),
981 parent2: usable_clause.clone(),
982 resolvent,
983 resolved_literal: resolved_lit,
984 });
985 }
986 }
987 }
988
989 ProofResult::Satisfiable
990 }
991
992 fn prove_unit_resolution(&mut self, max_steps: usize) -> ProofResult {
994 let mut clauses = self.clauses.clone();
995 let mut derivation = Vec::new();
996 let mut steps = 0;
997
998 loop {
999 if steps >= max_steps {
1000 return ProofResult::ResourceLimitReached {
1001 steps_attempted: steps,
1002 };
1003 }
1004
1005 let unit_clauses: Vec<Clause> =
1007 clauses.iter().filter(|c| c.is_unit()).cloned().collect();
1008
1009 if unit_clauses.is_empty() {
1010 return ProofResult::Satisfiable;
1011 }
1012
1013 let mut new_clauses = Vec::new();
1014 let mut found_new = false;
1015
1016 for unit in &unit_clauses {
1018 for clause in &clauses {
1019 if clause.is_unit() && clause == unit {
1020 continue; }
1022
1023 let resolvents = self.resolve(unit, clause);
1024
1025 for (resolvent, resolved_lit) in resolvents {
1026 steps += 1;
1027 self.stats.resolution_steps += 1;
1028
1029 if resolvent.is_tautology() {
1030 self.stats.tautologies_removed += 1;
1031 continue;
1032 }
1033
1034 if resolvent.is_empty() {
1035 self.stats.empty_clause_found = true;
1036 derivation.push(ResolutionStep {
1037 parent1: unit.clone(),
1038 parent2: clause.clone(),
1039 resolvent: resolvent.clone(),
1040 resolved_literal: resolved_lit,
1041 });
1042 return ProofResult::Unsatisfiable { steps, derivation };
1043 }
1044
1045 if !clauses.contains(&resolvent) && !new_clauses.contains(&resolvent) {
1046 new_clauses.push(resolvent.clone());
1047 found_new = true;
1048 self.stats.clauses_generated += 1;
1049 derivation.push(ResolutionStep {
1050 parent1: unit.clone(),
1051 parent2: clause.clone(),
1052 resolvent,
1053 resolved_literal: resolved_lit,
1054 });
1055 }
1056 }
1057 }
1058 }
1059
1060 if !found_new {
1061 return ProofResult::Satisfiable;
1062 }
1063
1064 clauses.extend(new_clauses);
1065 }
1066 }
1067
1068 fn prove_linear(&mut self, max_depth: usize) -> ProofResult {
1070 if self.clauses.is_empty() {
1072 return ProofResult::Satisfiable;
1073 }
1074
1075 let start = self.clauses[0].clone();
1076 let mut current = start.clone();
1077 let mut depth = 0;
1078 let mut derivation = Vec::new();
1079
1080 while depth < max_depth {
1081 if current.is_empty() {
1082 self.stats.empty_clause_found = true;
1083 return ProofResult::Unsatisfiable {
1084 steps: depth,
1085 derivation,
1086 };
1087 }
1088
1089 let mut resolved = false;
1091 for other in &self.clauses[1..] {
1092 let resolvents = self.resolve(¤t, other);
1093
1094 if let Some((resolvent, resolved_lit)) = resolvents.first() {
1095 if !resolvent.is_tautology() {
1096 current = resolvent.clone();
1097 depth += 1;
1098 self.stats.resolution_steps += 1;
1099 self.stats.clauses_generated += 1;
1100 derivation.push(ResolutionStep {
1101 parent1: current.clone(),
1102 parent2: other.clone(),
1103 resolvent: resolvent.clone(),
1104 resolved_literal: resolved_lit.clone(),
1105 });
1106 resolved = true;
1107 break;
1108 }
1109 }
1110 }
1111
1112 if !resolved {
1113 return ProofResult::Satisfiable;
1114 }
1115 }
1116
1117 ProofResult::ResourceLimitReached {
1118 steps_attempted: depth,
1119 }
1120 }
1121}
1122
1123impl Default for ResolutionProver {
1124 fn default() -> Self {
1125 Self::new()
1126 }
1127}
1128
1129pub fn to_cnf(expr: &TLExpr) -> Result<Vec<Clause>, IrError> {
1134 match expr {
1143 TLExpr::And(left, right) => {
1144 let mut clauses = to_cnf(left)?;
1145 clauses.extend(to_cnf(right)?);
1146 Ok(clauses)
1147 }
1148 TLExpr::Or(left, right) => {
1149 let left_clauses = to_cnf(left)?;
1151 let right_clauses = to_cnf(right)?;
1152
1153 if left_clauses.len() == 1 && right_clauses.len() == 1 {
1154 let mut literals = left_clauses[0].literals.clone();
1156 literals.extend(right_clauses[0].literals.clone());
1157 Ok(vec![Clause::from_literals(literals)])
1158 } else {
1159 let mut result = left_clauses;
1162 result.extend(right_clauses);
1163 Ok(result)
1164 }
1165 }
1166 TLExpr::Not(inner) => {
1167 match &**inner {
1168 TLExpr::Pred { .. } => {
1169 Ok(vec![Clause::unit(Literal::negative((**inner).clone()))])
1171 }
1172 _ => {
1173 Err(IrError::ConstraintViolation {
1175 message: "Complex negation not supported in simplified CNF conversion"
1176 .to_string(),
1177 })
1178 }
1179 }
1180 }
1181 TLExpr::Pred { .. } => {
1182 Ok(vec![Clause::unit(Literal::positive(expr.clone()))])
1184 }
1185 _ => Err(IrError::ConstraintViolation {
1186 message: format!(
1187 "Expression type not supported in CNF conversion: {:?}",
1188 expr
1189 ),
1190 }),
1191 }
1192}
1193
1194#[cfg(test)]
1195mod tests {
1196 use super::*;
1197
1198 fn p() -> TLExpr {
1199 TLExpr::pred("P", vec![])
1200 }
1201
1202 fn q() -> TLExpr {
1203 TLExpr::pred("Q", vec![])
1204 }
1205
1206 fn r() -> TLExpr {
1207 TLExpr::pred("R", vec![])
1208 }
1209
1210 #[test]
1211 fn test_literal_creation() {
1212 let lit_pos = Literal::positive(p());
1213 assert!(lit_pos.is_positive());
1214 assert!(!lit_pos.is_negative());
1215
1216 let lit_neg = Literal::negative(p());
1217 assert!(!lit_neg.is_positive());
1218 assert!(lit_neg.is_negative());
1219 }
1220
1221 #[test]
1222 fn test_literal_complementary() {
1223 let lit_pos = Literal::positive(p());
1224 let lit_neg = Literal::negative(p());
1225
1226 assert!(lit_pos.is_complementary(&lit_neg));
1227 assert!(lit_neg.is_complementary(&lit_pos));
1228 assert!(!lit_pos.is_complementary(&lit_pos));
1229 }
1230
1231 #[test]
1232 fn test_clause_empty() {
1233 let clause = Clause::empty();
1234 assert!(clause.is_empty());
1235 assert_eq!(clause.len(), 0);
1236 }
1237
1238 #[test]
1239 fn test_clause_unit() {
1240 let clause = Clause::unit(Literal::positive(p()));
1241 assert!(clause.is_unit());
1242 assert_eq!(clause.len(), 1);
1243 }
1244
1245 #[test]
1246 fn test_clause_tautology() {
1247 let clause = Clause::from_literals(vec![Literal::positive(p()), Literal::negative(p())]);
1249 assert!(clause.is_tautology());
1250 }
1251
1252 #[test]
1253 fn test_resolution_basic() {
1254 let mut prover = ResolutionProver::new();
1256 prover.add_clause(Clause::unit(Literal::positive(p())));
1257 prover.add_clause(Clause::unit(Literal::negative(p())));
1258
1259 let result = prover.prove();
1260 assert!(result.is_unsatisfiable());
1261 }
1262
1263 #[test]
1264 fn test_resolution_modus_ponens() {
1265 let mut prover = ResolutionProver::new();
1269 prover.add_clause(Clause::unit(Literal::positive(p())));
1270 prover.add_clause(Clause::from_literals(vec![
1271 Literal::negative(p()),
1272 Literal::positive(q()),
1273 ]));
1274 prover.add_clause(Clause::unit(Literal::negative(q())));
1276
1277 let result = prover.prove();
1278 assert!(result.is_unsatisfiable());
1279 }
1280
1281 #[test]
1282 fn test_resolution_satisfiable() {
1283 let mut prover = ResolutionProver::new();
1285 prover.add_clause(Clause::unit(Literal::positive(p())));
1286 prover.add_clause(Clause::unit(Literal::positive(q())));
1287
1288 let result = prover.prove();
1289 assert!(!result.is_unsatisfiable());
1291 }
1292
1293 #[test]
1294 fn test_cnf_conversion_and() {
1295 let expr = TLExpr::and(p(), q());
1297 let clauses = to_cnf(&expr).unwrap();
1298
1299 assert_eq!(clauses.len(), 2);
1300 assert!(clauses.iter().all(|c| c.is_unit()));
1301 }
1302
1303 #[test]
1304 fn test_cnf_conversion_or() {
1305 let expr = TLExpr::or(p(), q());
1307 let clauses = to_cnf(&expr).unwrap();
1308
1309 assert_eq!(clauses.len(), 1);
1310 assert_eq!(clauses[0].len(), 2);
1311 }
1312
1313 #[test]
1314 fn test_resolution_strategy_unit() {
1315 let mut prover =
1317 ResolutionProver::with_strategy(ResolutionStrategy::UnitResolution { max_steps: 100 });
1318
1319 prover.add_clause(Clause::unit(Literal::positive(p())));
1320 prover.add_clause(Clause::unit(Literal::negative(p())));
1321
1322 let result = prover.prove();
1323 assert!(result.is_unsatisfiable());
1324 }
1325
1326 #[test]
1327 fn test_resolution_three_clauses() {
1328 let mut prover = ResolutionProver::new();
1330
1331 prover.add_clause(Clause::from_literals(vec![
1332 Literal::positive(p()),
1333 Literal::positive(q()),
1334 ]));
1335 prover.add_clause(Clause::from_literals(vec![
1336 Literal::negative(p()),
1337 Literal::positive(r()),
1338 ]));
1339 prover.add_clause(Clause::unit(Literal::negative(q())));
1340 prover.add_clause(Clause::unit(Literal::negative(r())));
1341
1342 let result = prover.prove();
1343 assert!(result.is_unsatisfiable());
1344 }
1345
1346 #[test]
1347 fn test_horn_clause_detection() {
1348 let clause = Clause::from_literals(vec![
1350 Literal::negative(p()),
1351 Literal::negative(q()),
1352 Literal::positive(r()),
1353 ]);
1354 assert!(clause.is_horn());
1355
1356 let non_horn = Clause::from_literals(vec![Literal::positive(p()), Literal::positive(q())]);
1358 assert!(!non_horn.is_horn());
1359 }
1360
1361 #[test]
1362 fn test_prover_stats() {
1363 let mut prover = ResolutionProver::new();
1364 prover.add_clause(Clause::unit(Literal::positive(p())));
1365 prover.add_clause(Clause::unit(Literal::negative(p())));
1366
1367 let result = prover.prove();
1368
1369 assert!(prover.stats.empty_clause_found);
1370 assert!(prover.stats.resolution_steps > 0);
1371 assert!(result.is_unsatisfiable());
1372 }
1373
1374 #[test]
1377 fn test_literal_unification_ground() {
1378 let p_a = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1380 let not_p_a = Literal::negative(TLExpr::pred("P", vec![Term::constant("a")]));
1381
1382 let mgu = p_a.try_unify(¬_p_a);
1383 assert!(mgu.is_some());
1384 assert!(mgu.unwrap().is_empty());
1385 }
1386
1387 #[test]
1388 fn test_literal_unification_variable() {
1389 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1391 let not_p_a = Literal::negative(TLExpr::pred("P", vec![Term::constant("a")]));
1392
1393 let mgu = p_x.try_unify(¬_p_a);
1394 assert!(mgu.is_some());
1395
1396 let mgu = mgu.unwrap();
1397 assert_eq!(mgu.len(), 1);
1398 assert_eq!(mgu.apply(&Term::var("x")), Term::constant("a"));
1399 }
1400
1401 #[test]
1402 fn test_literal_unification_fails_diff_names() {
1403 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1405 let not_q_x = Literal::negative(TLExpr::pred("Q", vec![Term::var("x")]));
1406
1407 let mgu = p_x.try_unify(¬_q_x);
1408 assert!(mgu.is_none());
1409 }
1410
1411 #[test]
1412 fn test_literal_unification_fails_same_polarity() {
1413 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1415 let p_a = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1416
1417 let mgu = p_x.try_unify(&p_a);
1418 assert!(mgu.is_none());
1419 }
1420
1421 #[test]
1422 fn test_literal_apply_substitution() {
1423 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1425 let mut subst = Substitution::empty();
1426 subst.bind("x".to_string(), Term::constant("a"));
1427
1428 let p_a = p_x.apply_substitution(&subst);
1429 let expected = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1430
1431 assert_eq!(p_a.atom, expected.atom);
1432 assert_eq!(p_a.polarity, expected.polarity);
1433 }
1434
1435 #[test]
1436 fn test_clause_rename_variables() {
1437 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1439 let q_x = Literal::positive(TLExpr::pred("Q", vec![Term::var("x")]));
1440 let clause = Clause::from_literals(vec![p_x, q_x]);
1441
1442 let renamed = clause.rename_variables("1");
1443
1444 let vars = renamed.free_vars();
1446 assert!(vars.contains("x_1"));
1447 assert!(!vars.contains("x"));
1448 }
1449
1450 #[test]
1451 fn test_clause_apply_substitution() {
1452 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1454 let q_y = Literal::positive(TLExpr::pred("Q", vec![Term::var("y")]));
1455 let clause = Clause::from_literals(vec![p_x, q_y]);
1456
1457 let mut subst = Substitution::empty();
1458 subst.bind("x".to_string(), Term::constant("a"));
1459 subst.bind("y".to_string(), Term::constant("b"));
1460
1461 let result = clause.apply_substitution(&subst);
1462
1463 assert!(result.free_vars().is_empty());
1465 }
1466
1467 #[test]
1468 fn test_first_order_resolution_basic() {
1469 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1471 let not_p_a = Literal::negative(TLExpr::pred("P", vec![Term::constant("a")]));
1472
1473 let c1 = Clause::unit(p_x);
1474 let c2 = Clause::unit(not_p_a);
1475
1476 let prover = ResolutionProver::new();
1477 let resolvents = prover.resolve_first_order(&c1, &c2);
1478
1479 assert_eq!(resolvents.len(), 1);
1480 assert!(resolvents[0].0.is_empty()); }
1482
1483 #[test]
1484 fn test_first_order_resolution_complex() {
1485 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1487 let q_x = Literal::positive(TLExpr::pred("Q", vec![Term::var("x")]));
1488 let c1 = Clause::from_literals(vec![p_x, q_x]);
1489
1490 let not_p_a = Literal::negative(TLExpr::pred("P", vec![Term::constant("a")]));
1491 let r_a = Literal::positive(TLExpr::pred("R", vec![Term::constant("a")]));
1492 let c2 = Clause::from_literals(vec![not_p_a, r_a]);
1493
1494 let prover = ResolutionProver::new();
1495 let resolvents = prover.resolve_first_order(&c1, &c2);
1496
1497 assert_eq!(resolvents.len(), 1);
1498 let resolvent = &resolvents[0].0;
1499
1500 assert_eq!(resolvent.len(), 2);
1502
1503 assert!(resolvent.free_vars().is_empty());
1505 }
1506
1507 #[test]
1508 fn test_first_order_resolution_multiple_vars() {
1509 let p_xy = Literal::positive(TLExpr::pred("P", vec![Term::var("x"), Term::var("y")]));
1511 let not_p_ab = Literal::negative(TLExpr::pred(
1512 "P",
1513 vec![Term::constant("a"), Term::constant("b")],
1514 ));
1515
1516 let c1 = Clause::unit(p_xy);
1517 let c2 = Clause::unit(not_p_ab);
1518
1519 let prover = ResolutionProver::new();
1520 let resolvents = prover.resolve_first_order(&c1, &c2);
1521
1522 assert_eq!(resolvents.len(), 1);
1523 assert!(resolvents[0].0.is_empty());
1524 }
1525
1526 #[test]
1527 fn test_first_order_resolution_standardizing_apart() {
1528 let p_x1 = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1532 let not_p_x2 = Literal::negative(TLExpr::pred("P", vec![Term::var("x")]));
1533
1534 let c1 = Clause::unit(p_x1);
1535 let c2 = Clause::unit(not_p_x2);
1536
1537 let prover = ResolutionProver::new();
1538 let resolvents = prover.resolve_first_order(&c1, &c2);
1539
1540 assert_eq!(resolvents.len(), 1);
1542 assert!(resolvents[0].0.is_empty());
1543 }
1544
1545 #[test]
1546 fn test_first_order_resolution_no_unifier() {
1547 let p_a = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1549 let not_p_b = Literal::negative(TLExpr::pred("P", vec![Term::constant("b")]));
1550
1551 let c1 = Clause::unit(p_a);
1552 let c2 = Clause::unit(not_p_b);
1553
1554 let prover = ResolutionProver::new();
1555 let resolvents = prover.resolve_first_order(&c1, &c2);
1556
1557 assert_eq!(resolvents.len(), 0);
1559 }
1560
1561 #[test]
1564 fn test_subsumption_identical() {
1565 let p_a = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1567 let c = Clause::unit(p_a);
1568
1569 assert!(c.subsumes(&c));
1570 }
1571
1572 #[test]
1573 fn test_subsumption_variable_to_constant() {
1574 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1576 let p_a = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1577
1578 let c_general = Clause::unit(p_x);
1579 let c_specific = Clause::unit(p_a);
1580
1581 assert!(c_general.subsumes(&c_specific));
1582 }
1583
1584 #[test]
1585 fn test_subsumption_not_reverse() {
1586 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1588 let p_a = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1589
1590 let c_general = Clause::unit(p_x);
1591 let c_specific = Clause::unit(p_a);
1592
1593 assert!(!c_specific.subsumes(&c_general));
1594 }
1595
1596 #[test]
1597 fn test_subsumption_smaller_clause() {
1598 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1600 let p_a = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1601 let q_a = Literal::positive(TLExpr::pred("Q", vec![Term::constant("a")]));
1602
1603 let c1 = Clause::unit(p_x);
1604 let c2 = Clause::from_literals(vec![p_a, q_a]);
1605
1606 assert!(c1.subsumes(&c2));
1607 }
1608
1609 #[test]
1610 fn test_subsumption_multiple_literals() {
1611 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1613 let q_x = Literal::positive(TLExpr::pred("Q", vec![Term::var("x")]));
1614 let c1 = Clause::from_literals(vec![p_x, q_x]);
1615
1616 let p_a = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1617 let q_a = Literal::positive(TLExpr::pred("Q", vec![Term::constant("a")]));
1618 let r_a = Literal::positive(TLExpr::pred("R", vec![Term::constant("a")]));
1619 let c2 = Clause::from_literals(vec![p_a, q_a, r_a]);
1620
1621 assert!(c1.subsumes(&c2));
1622 }
1623
1624 #[test]
1625 fn test_subsumption_fails_different_pred() {
1626 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1628 let q_a = Literal::positive(TLExpr::pred("Q", vec![Term::constant("a")]));
1629
1630 let c1 = Clause::unit(p_x);
1631 let c2 = Clause::unit(q_a);
1632
1633 assert!(!c1.subsumes(&c2));
1634 }
1635
1636 #[test]
1637 fn test_subsumption_fails_too_many_literals() {
1638 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1640 let q_x = Literal::positive(TLExpr::pred("Q", vec![Term::var("x")]));
1641 let r_x = Literal::positive(TLExpr::pred("R", vec![Term::var("x")]));
1642 let c1 = Clause::from_literals(vec![p_x, q_x, r_x]);
1643
1644 let p_a = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1645 let q_a = Literal::positive(TLExpr::pred("Q", vec![Term::constant("a")]));
1646 let c2 = Clause::from_literals(vec![p_a, q_a]);
1647
1648 assert!(!c1.subsumes(&c2));
1649 }
1650
1651 #[test]
1652 fn test_subsumption_empty_clause() {
1653 let empty = Clause::empty();
1655 let p_a = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1656 let non_empty = Clause::unit(p_a);
1657
1658 assert!(empty.subsumes(&empty));
1659 assert!(!empty.subsumes(&non_empty));
1660 assert!(!non_empty.subsumes(&empty));
1661 }
1662
1663 #[test]
1664 fn test_subsumption_polarity_matters() {
1665 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1667 let not_p_a = Literal::negative(TLExpr::pred("P", vec![Term::constant("a")]));
1668
1669 let c1 = Clause::unit(p_x);
1670 let c2 = Clause::unit(not_p_a);
1671
1672 assert!(!c1.subsumes(&c2));
1673 }
1674
1675 #[test]
1676 fn test_subsumption_two_variables() {
1677 let p_xy = Literal::positive(TLExpr::pred("P", vec![Term::var("x"), Term::var("y")]));
1679 let p_ab = Literal::positive(TLExpr::pred(
1680 "P",
1681 vec![Term::constant("a"), Term::constant("b")],
1682 ));
1683
1684 let c1 = Clause::unit(p_xy);
1685 let c2 = Clause::unit(p_ab);
1686
1687 assert!(c1.subsumes(&c2));
1688 }
1689
1690 #[test]
1691 fn test_subsumption_in_prover() {
1692 let mut prover = ResolutionProver::new();
1694
1695 let p_x = Literal::positive(TLExpr::pred("P", vec![Term::var("x")]));
1697 let q_x = Literal::positive(TLExpr::pred("Q", vec![Term::var("x")]));
1698 prover.add_clause(Clause::from_literals(vec![p_x, q_x]));
1699
1700 let p_a = Literal::positive(TLExpr::pred("P", vec![Term::constant("a")]));
1702 let q_a = Literal::positive(TLExpr::pred("Q", vec![Term::constant("a")]));
1703 let r_a = Literal::positive(TLExpr::pred("R", vec![Term::constant("a")]));
1704 let subsumed_clause = Clause::from_literals(vec![p_a, q_a, r_a]);
1705
1706 assert!(prover.clauses[0].subsumes(&subsumed_clause));
1708 }
1709}