1use crate::error::{ProofError, ProofResult};
32use crate::unify::{
33 apply_subst_to_expr, apply_subst_to_term, beta_reduce, compose_substitutions, unify_exprs,
34 unify_pattern, unify_terms, Substitution,
35};
36use crate::{DerivationTree, InferenceRule, ProofExpr, ProofGoal, ProofTerm};
37
38const DEFAULT_MAX_DEPTH: usize = 100;
40
41const DEFAULT_STEP_BUDGET: usize = 100_000;
49
50pub struct BackwardChainer {
55 knowledge_base: Vec<ProofExpr>,
57
58 max_depth: usize,
60
61 var_counter: usize,
63
64 eliminated_existentials: Vec<ProofExpr>,
72
73 active_goals: Vec<String>,
85
86 steps: usize,
90
91 step_budget: usize,
93}
94
95fn term_to_expr(term: &ProofTerm) -> ProofExpr {
103 match term {
104 ProofTerm::Constant(s) => ProofExpr::Atom(s.clone()),
105 ProofTerm::Variable(s) => ProofExpr::Atom(s.clone()),
106 ProofTerm::BoundVarRef(s) => ProofExpr::Atom(s.clone()),
107 ProofTerm::Function(name, args) => {
108 if matches!(name.as_str(), "Zero" | "Succ" | "Nil" | "Cons") {
110 ProofExpr::Ctor {
111 name: name.clone(),
112 args: args.iter().map(term_to_expr).collect(),
113 }
114 } else {
115 ProofExpr::Predicate {
117 name: name.clone(),
118 args: args.clone(),
119 world: None,
120 }
121 }
122 }
123 ProofTerm::Group(terms) => {
124 if terms.len() == 1 {
126 term_to_expr(&terms[0])
127 } else {
128 ProofExpr::Predicate {
130 name: "Group".into(),
131 args: terms.clone(),
132 world: None,
133 }
134 }
135 }
136 }
137}
138
139fn is_falsum(expr: &ProofExpr) -> bool {
142 matches!(expr, ProofExpr::Atom(s) if s == "⊥" || s == "False" || s == "false")
143}
144
145fn flatten_conjuncts(expr: &ProofExpr) -> Vec<ProofExpr> {
148 match expr {
149 ProofExpr::And(left, right) => {
150 let mut conjuncts = flatten_conjuncts(left);
151 conjuncts.extend(flatten_conjuncts(right));
152 conjuncts
153 }
154 other => vec![other.clone()],
155 }
156}
157
158fn build_conjunction_tree(
164 ante: &ProofExpr,
165 subst: &Substitution,
166 proofs: &mut std::vec::IntoIter<DerivationTree>,
167) -> DerivationTree {
168 match ante {
169 ProofExpr::And(left, right) => {
170 let lt = build_conjunction_tree(left, subst, proofs);
171 let rt = build_conjunction_tree(right, subst, proofs);
172 DerivationTree::new(
173 apply_subst_to_expr(ante, subst),
174 InferenceRule::ConjunctionIntro,
175 vec![lt, rt],
176 )
177 }
178 _ => proofs
179 .next()
180 .expect("flatten_conjuncts yields exactly one proof per non-And leaf"),
181 }
182}
183
184fn is_atomic_fact(expr: &ProofExpr) -> bool {
187 matches!(
188 expr,
189 ProofExpr::Predicate { .. } | ProofExpr::Identity(_, _) | ProofExpr::Atom(_)
190 )
191}
192
193struct CertRule {
207 source: ProofExpr,
208 binder: Option<String>,
209 ante: ProofExpr,
210 cons: ProofExpr,
211}
212
213fn cert_is_rule(e: &ProofExpr) -> bool {
214 matches!(e, ProofExpr::Implies(..))
215 || matches!(e, ProofExpr::ForAll { body, .. }
216 if matches!(body.as_ref(), ProofExpr::Implies(..)))
217}
218
219fn cert_extract_rules(all: &[ProofExpr]) -> Vec<CertRule> {
220 let mut rules = Vec::new();
221 for e in all {
222 match e {
223 ProofExpr::Implies(a, c) => rules.push(CertRule {
224 source: e.clone(),
225 binder: None,
226 ante: (**a).clone(),
227 cons: (**c).clone(),
228 }),
229 ProofExpr::ForAll { variable, body } => {
230 if let ProofExpr::Implies(a, c) = body.as_ref() {
231 rules.push(CertRule {
232 source: e.clone(),
233 binder: Some(variable.clone()),
234 ante: (**a).clone(),
235 cons: (**c).clone(),
236 });
237 }
238 }
239 _ => {}
240 }
241 }
242 rules
243}
244
245fn cert_seed_facts(all: &[ProofExpr]) -> Vec<(ProofExpr, DerivationTree)> {
247 let mut known: Vec<(ProofExpr, DerivationTree)> = Vec::new();
248 for e in all {
249 if cert_is_rule(e) {
250 continue;
251 }
252 if !known.iter().any(|(p, _)| exprs_structurally_equal(p, e)) {
253 known.push((
254 e.clone(),
255 DerivationTree::leaf(e.clone(), InferenceRule::PremiseMatch),
256 ));
257 }
258 }
259 known
260}
261
262fn cert_eq_path_proof(
269 a: &ProofTerm,
270 c: &ProofTerm,
271 adj: &std::collections::HashMap<String, Vec<(ProofTerm, DerivationTree)>>,
272) -> Option<DerivationTree> {
273 fn term_key(t: &ProofTerm) -> Option<String> {
274 term_skey(t)
275 }
276 let a_key = term_key(a)?;
277 let c_key = term_key(c)?;
278 if a_key == c_key {
279 return None;
280 }
281 let start_proof = DerivationTree::leaf(
283 ProofExpr::Identity(a.clone(), a.clone()),
284 InferenceRule::Reflexivity,
285 );
286 let mut queue: std::collections::VecDeque<(String, ProofTerm, DerivationTree)> =
287 std::collections::VecDeque::new();
288 queue.push_back((a_key.clone(), a.clone(), start_proof));
289 let mut visited: std::collections::HashSet<String> = std::collections::HashSet::new();
290 visited.insert(a_key.clone());
291
292 while let Some((cur_key, cur_term, acc_proof)) = queue.pop_front() {
293 if cur_key == c_key {
294 return Some(acc_proof);
298 }
299 let Some(edges) = adj.get(&cur_key) else {
300 continue;
301 };
302 for (next_term, edge_tree) in edges {
303 let Some(next_key) = term_key(next_term) else {
304 continue;
305 };
306 if visited.contains(&next_key) {
307 continue;
308 }
309 visited.insert(next_key.clone());
310 let new_concl = ProofExpr::Identity(a.clone(), next_term.clone());
313 let step = if cur_key == a_key {
314 edge_tree.clone()
316 } else {
317 DerivationTree::new(
318 new_concl,
319 InferenceRule::Rewrite {
320 from: cur_term.clone(),
321 to: next_term.clone(),
322 },
323 vec![edge_tree.clone(), acc_proof.clone()],
324 )
325 };
326 queue.push_back((next_key, next_term.clone(), step));
327 }
328 }
329 None
330}
331
332fn cert_equality_close(known: &[(ProofExpr, DerivationTree)]) -> Option<DerivationTree> {
338 use std::collections::HashMap;
339 let mut adj: HashMap<String, Vec<(ProofTerm, DerivationTree)>> = HashMap::new();
342 fn key(t: &ProofTerm) -> Option<String> {
343 match t {
344 ProofTerm::Constant(s) | ProofTerm::Variable(s) | ProofTerm::BoundVarRef(s) => {
345 Some(s.clone())
346 }
347 _ => None,
348 }
349 }
350 for (prop, tree) in known {
351 if let ProofExpr::Identity(l, r) = prop {
352 let (Some(lk), Some(rk)) = (key(l), key(r)) else {
353 continue;
354 };
355 if lk == rk {
356 continue;
357 }
358 adj.entry(lk).or_default().push((r.clone(), tree.clone()));
359 let sym = DerivationTree::new(
360 ProofExpr::Identity(r.clone(), l.clone()),
361 InferenceRule::EqualitySymmetry,
362 vec![tree.clone()],
363 );
364 adj.entry(rk).or_default().push((l.clone(), sym));
365 }
366 }
367 if adj.is_empty() {
368 return None;
369 }
370
371 for (prop, neg_tree) in known {
372 let ProofExpr::Not(inner) = prop else {
373 continue;
374 };
375 let ProofExpr::Identity(x, y) = inner.as_ref() else {
376 continue;
377 };
378 if let Some(eq_proof) = cert_eq_path_proof(x, y, &adj) {
380 return Some(DerivationTree::new(
381 ProofExpr::Atom("⊥".into()),
382 InferenceRule::Contradiction,
383 vec![eq_proof, neg_tree.clone()],
384 ));
385 }
386 }
387 None
388}
389
390fn term_skey(t: &ProofTerm) -> Option<String> {
394 match t {
395 ProofTerm::Constant(s) | ProofTerm::Variable(s) | ProofTerm::BoundVarRef(s) => {
396 Some(s.clone())
397 }
398 ProofTerm::Function(f, args) => {
399 let mut parts = Vec::with_capacity(args.len());
400 for a in args {
401 parts.push(term_skey(a)?);
402 }
403 Some(format!("{}({})", f, parts.join(",")))
404 }
405 ProofTerm::Group(args) => {
406 let mut parts = Vec::with_capacity(args.len());
407 for a in args {
408 parts.push(term_skey(a)?);
409 }
410 Some(format!("({})", parts.join(",")))
411 }
412 _ => None,
413 }
414}
415
416fn collect_subterms(t: &ProofTerm, out: &mut Vec<ProofTerm>) {
418 out.push(t.clone());
419 if let ProofTerm::Function(_, args) | ProofTerm::Group(args) = t {
420 for a in args {
421 collect_subterms(a, out);
422 }
423 }
424}
425
426fn build_congruence_proof(
433 head: &str,
434 xs: &[ProofTerm],
435 ys: &[ProofTerm],
436 arg_proofs: &[Option<DerivationTree>],
437) -> DerivationTree {
438 let f = |args: &[ProofTerm]| ProofTerm::Function(head.to_string(), args.to_vec());
439 let mut cur: Vec<ProofTerm> = xs.to_vec();
440 let mut acc = DerivationTree::leaf(
441 ProofExpr::Identity(f(xs), f(xs)),
442 InferenceRule::Reflexivity,
443 );
444 for i in 0..xs.len() {
445 let Some(arg_proof) = &arg_proofs[i] else {
446 continue;
447 };
448 let mut next = cur.clone();
449 next[i] = ys[i].clone();
450 let concl = ProofExpr::Identity(f(xs), f(&next));
451 acc = DerivationTree::new(
452 concl,
453 InferenceRule::Rewrite {
454 from: xs[i].clone(),
455 to: ys[i].clone(),
456 },
457 vec![arg_proof.clone(), acc],
458 );
459 cur = next;
460 }
461 acc
462}
463
464pub(crate) fn cert_congruence_path(
472 lhs: &ProofTerm,
473 rhs: &ProofTerm,
474 hyps: &[(ProofExpr, DerivationTree)],
475) -> Option<DerivationTree> {
476 use std::collections::HashMap;
477 let mut adj: HashMap<String, Vec<(ProofTerm, DerivationTree)>> = HashMap::new();
478
479 for (prop, tree) in hyps {
481 let ProofExpr::Identity(l, r) = prop else {
482 continue;
483 };
484 let (Some(lk), Some(rk)) = (term_skey(l), term_skey(r)) else {
485 continue;
486 };
487 if lk == rk {
488 continue;
489 }
490 adj.entry(lk).or_default().push((r.clone(), tree.clone()));
491 let sym = DerivationTree::new(
492 ProofExpr::Identity(r.clone(), l.clone()),
493 InferenceRule::EqualitySymmetry,
494 vec![tree.clone()],
495 );
496 adj.entry(rk).or_default().push((l.clone(), sym));
497 }
498
499 let mut universe: Vec<ProofTerm> = Vec::new();
501 for (prop, _) in hyps {
502 if let ProofExpr::Identity(l, r) = prop {
503 collect_subterms(l, &mut universe);
504 collect_subterms(r, &mut universe);
505 }
506 }
507 collect_subterms(lhs, &mut universe);
508 collect_subterms(rhs, &mut universe);
509 let funcs: Vec<ProofTerm> = {
510 let mut seen = std::collections::HashSet::new();
511 universe
512 .into_iter()
513 .filter(|t| matches!(t, ProofTerm::Function(..)))
514 .filter(|t| term_skey(t).is_some_and(|k| seen.insert(k)))
515 .collect()
516 };
517
518 loop {
520 let mut added = false;
521 for i in 0..funcs.len() {
522 for j in 0..funcs.len() {
523 if i == j {
524 continue;
525 }
526 let (ProofTerm::Function(fi, xs), ProofTerm::Function(fj, ys)) =
527 (&funcs[i], &funcs[j])
528 else {
529 continue;
530 };
531 if fi != fj || xs.len() != ys.len() {
532 continue;
533 }
534 let (ki, kj) = (term_skey(&funcs[i]).unwrap(), term_skey(&funcs[j]).unwrap());
535 let already = adj.get(&ki).is_some_and(|es| {
536 es.iter().any(|(t, _)| term_skey(t).as_deref() == Some(kj.as_str()))
537 });
538 if already {
539 continue;
540 }
541 let mut arg_proofs: Vec<Option<DerivationTree>> = Vec::with_capacity(xs.len());
542 let mut ok = true;
543 for (x, y) in xs.iter().zip(ys.iter()) {
544 if term_skey(x) == term_skey(y) {
545 arg_proofs.push(None);
546 } else if let Some(p) = cert_eq_path_proof(x, y, &adj) {
547 arg_proofs.push(Some(p));
548 } else {
549 ok = false;
550 break;
551 }
552 }
553 if !ok {
554 continue;
555 }
556 let cong = build_congruence_proof(fi, xs, ys, &arg_proofs);
557 let sym = DerivationTree::new(
558 ProofExpr::Identity(funcs[j].clone(), funcs[i].clone()),
559 InferenceRule::EqualitySymmetry,
560 vec![cong.clone()],
561 );
562 adj.entry(ki).or_default().push((funcs[j].clone(), cong));
563 adj.entry(kj).or_default().push((funcs[i].clone(), sym));
564 added = true;
565 }
566 }
567 if !added {
568 break;
569 }
570 }
571
572 cert_eq_path_proof(lhs, rhs, &adj)
573}
574
575pub(crate) fn le_eq(a: ProofTerm, b: ProofTerm) -> ProofExpr {
577 ProofExpr::Identity(
578 ProofTerm::Function("le".to_string(), vec![a, b]),
579 ProofTerm::Constant("true".to_string()),
580 )
581}
582
583pub(crate) fn as_le_pair(e: &ProofExpr) -> Option<(ProofTerm, ProofTerm)> {
585 if let ProofExpr::Identity(lhs, rhs) = e {
586 if let (ProofTerm::Function(name, args), ProofTerm::Constant(t)) = (lhs, rhs) {
587 if name == "le" && args.len() == 2 && t == "true" {
588 return Some((args[0].clone(), args[1].clone()));
589 }
590 }
591 }
592 None
593}
594
595fn as_int_literal(t: &ProofTerm) -> Option<i64> {
597 match t {
598 ProofTerm::Constant(s) => s.parse::<i64>().ok(),
599 _ => None,
600 }
601}
602
603fn resolve_term(t: &ProofTerm, subst: &Substitution) -> ProofTerm {
608 let mut cur = t.clone();
609 for _ in 0..256 {
610 let next = apply_subst_to_term(&cur, subst);
611 if next == cur {
612 break;
613 }
614 cur = next;
615 }
616 cur
617}
618
619fn is_ground_term(t: &ProofTerm) -> bool {
621 match t {
622 ProofTerm::Variable(_) | ProofTerm::BoundVarRef(_) => false,
623 ProofTerm::Constant(_) => true,
624 ProofTerm::Function(_, args) | ProofTerm::Group(args) => args.iter().all(is_ground_term),
625 }
626}
627
628fn ground_witness(var: &str, subst: &Substitution) -> Option<ProofTerm> {
632 let resolved = resolve_term(subst.get(var)?, subst);
633 is_ground_term(&resolved).then_some(resolved)
634}
635
636fn collect_vars_ordered_term(t: &ProofTerm, acc: &mut Vec<String>) {
640 match t {
641 ProofTerm::Variable(v) => {
642 if !acc.iter().any(|x| x == v) {
643 acc.push(v.clone());
644 }
645 }
646 ProofTerm::Function(_, args) | ProofTerm::Group(args) => {
647 for a in args {
648 collect_vars_ordered_term(a, acc);
649 }
650 }
651 ProofTerm::Constant(_) | ProofTerm::BoundVarRef(_) => {}
652 }
653}
654
655fn collect_vars_ordered_expr(e: &ProofExpr, acc: &mut Vec<String>) {
659 match e {
660 ProofExpr::Predicate { args, .. } => {
661 for a in args {
662 collect_vars_ordered_term(a, acc);
663 }
664 }
665 ProofExpr::Identity(l, r) => {
666 collect_vars_ordered_term(l, acc);
667 collect_vars_ordered_term(r, acc);
668 }
669 ProofExpr::Term(t) => collect_vars_ordered_term(t, acc),
670 ProofExpr::NeoEvent { roles, .. } => {
671 for (_, t) in roles {
672 collect_vars_ordered_term(t, acc);
673 }
674 }
675 ProofExpr::Ctor { args, .. } => {
676 for a in args {
677 collect_vars_ordered_expr(a, acc);
678 }
679 }
680 ProofExpr::Not(p)
681 | ProofExpr::ForAll { body: p, .. }
682 | ProofExpr::Exists { body: p, .. }
683 | ProofExpr::Modal { body: p, .. }
684 | ProofExpr::Temporal { body: p, .. }
685 | ProofExpr::Lambda { body: p, .. }
686 | ProofExpr::Fixpoint { body: p, .. } => collect_vars_ordered_expr(p, acc),
687 ProofExpr::And(l, r)
688 | ProofExpr::Or(l, r)
689 | ProofExpr::Implies(l, r)
690 | ProofExpr::Iff(l, r)
691 | ProofExpr::App(l, r)
692 | ProofExpr::TemporalBinary { left: l, right: r, .. } => {
693 collect_vars_ordered_expr(l, acc);
694 collect_vars_ordered_expr(r, acc);
695 }
696 ProofExpr::Counterfactual { antecedent, consequent } => {
697 collect_vars_ordered_expr(antecedent, acc);
698 collect_vars_ordered_expr(consequent, acc);
699 }
700 ProofExpr::Match { scrutinee, arms } => {
701 collect_vars_ordered_expr(scrutinee, acc);
702 for arm in arms {
703 collect_vars_ordered_expr(&arm.body, acc);
704 }
705 }
706 ProofExpr::Atom(_)
707 | ProofExpr::TypedVar { .. }
708 | ProofExpr::Hole(_)
709 | ProofExpr::Unsupported(_) => {}
710 }
711}
712
713fn expr_mentions_var(expr: &ProofExpr, var: &str) -> bool {
715 let mut vs = Vec::new();
716 collect_vars_ordered_expr(expr, &mut vs);
717 vs.iter().any(|v| v == var)
718}
719
720fn instantiation_witnesses(
728 bound_vars: &[String],
729 subst: &Substitution,
730 matrix: &ProofExpr,
731) -> Option<Vec<ProofTerm>> {
732 let fallback = bound_vars.iter().find_map(|v| ground_witness(v, subst));
733 bound_vars
734 .iter()
735 .map(|v| {
736 ground_witness(v, subst).or_else(|| {
737 if expr_mentions_var(matrix, v) {
738 None
739 } else {
740 fallback.clone()
741 }
742 })
743 })
744 .collect()
745}
746
747fn rewrite_const_to_var_term(t: &ProofTerm, eigen: &str, var: &str) -> ProofTerm {
751 match t {
752 ProofTerm::Constant(c) if c == eigen => ProofTerm::Variable(var.to_string()),
753 ProofTerm::Function(n, args) => ProofTerm::Function(
754 n.clone(),
755 args.iter().map(|a| rewrite_const_to_var_term(a, eigen, var)).collect(),
756 ),
757 ProofTerm::Group(args) => {
758 ProofTerm::Group(args.iter().map(|a| rewrite_const_to_var_term(a, eigen, var)).collect())
759 }
760 other => other.clone(),
761 }
762}
763
764fn rewrite_const_to_var_expr(e: &ProofExpr, eigen: &str, var: &str) -> ProofExpr {
766 let re = |x: &ProofExpr| Box::new(rewrite_const_to_var_expr(x, eigen, var));
767 let rt = |x: &ProofTerm| rewrite_const_to_var_term(x, eigen, var);
768 match e {
769 ProofExpr::Predicate { name, args, world } => ProofExpr::Predicate {
770 name: name.clone(),
771 args: args.iter().map(rt).collect(),
772 world: world.clone(),
773 },
774 ProofExpr::Identity(l, r) => ProofExpr::Identity(rt(l), rt(r)),
775 ProofExpr::Atom(s) => ProofExpr::Atom(s.clone()),
776 ProofExpr::And(l, r) => ProofExpr::And(re(l), re(r)),
777 ProofExpr::Or(l, r) => ProofExpr::Or(re(l), re(r)),
778 ProofExpr::Implies(l, r) => ProofExpr::Implies(re(l), re(r)),
779 ProofExpr::Iff(l, r) => ProofExpr::Iff(re(l), re(r)),
780 ProofExpr::Not(p) => ProofExpr::Not(re(p)),
781 ProofExpr::ForAll { variable, body } => {
782 ProofExpr::ForAll { variable: variable.clone(), body: re(body) }
783 }
784 ProofExpr::Exists { variable, body } => {
785 ProofExpr::Exists { variable: variable.clone(), body: re(body) }
786 }
787 ProofExpr::Modal { domain, force, flavor, body } => ProofExpr::Modal {
788 domain: domain.clone(),
789 force: *force,
790 flavor: flavor.clone(),
791 body: re(body),
792 },
793 ProofExpr::Counterfactual { antecedent, consequent } => ProofExpr::Counterfactual {
794 antecedent: re(antecedent),
795 consequent: re(consequent),
796 },
797 ProofExpr::Temporal { operator, body } => {
798 ProofExpr::Temporal { operator: operator.clone(), body: re(body) }
799 }
800 ProofExpr::TemporalBinary { operator, left, right } => ProofExpr::TemporalBinary {
801 operator: operator.clone(),
802 left: re(left),
803 right: re(right),
804 },
805 ProofExpr::Lambda { variable, body } => {
806 ProofExpr::Lambda { variable: variable.clone(), body: re(body) }
807 }
808 ProofExpr::App(l, r) => ProofExpr::App(re(l), re(r)),
809 ProofExpr::NeoEvent { event_var, verb, roles } => ProofExpr::NeoEvent {
810 event_var: event_var.clone(),
811 verb: verb.clone(),
812 roles: roles.iter().map(|(role, t)| (role.clone(), rt(t))).collect(),
813 },
814 ProofExpr::Ctor { name, args } => ProofExpr::Ctor {
815 name: name.clone(),
816 args: args.iter().map(|a| rewrite_const_to_var_expr(a, eigen, var)).collect(),
817 },
818 ProofExpr::Match { scrutinee, arms } => ProofExpr::Match {
819 scrutinee: re(scrutinee),
820 arms: arms
821 .iter()
822 .map(|arm| crate::MatchArm {
823 ctor: arm.ctor.clone(),
824 bindings: arm.bindings.clone(),
825 body: rewrite_const_to_var_expr(&arm.body, eigen, var),
826 })
827 .collect(),
828 },
829 ProofExpr::Fixpoint { name, body } => {
830 ProofExpr::Fixpoint { name: name.clone(), body: re(body) }
831 }
832 ProofExpr::TypedVar { name, typename } => {
833 ProofExpr::TypedVar { name: name.clone(), typename: typename.clone() }
834 }
835 ProofExpr::Hole(s) => ProofExpr::Hole(s.clone()),
836 ProofExpr::Term(t) => ProofExpr::Term(rt(t)),
837 ProofExpr::Unsupported(s) => ProofExpr::Unsupported(s.clone()),
838 }
839}
840
841fn rewrite_tree_const_to_var(tree: &DerivationTree, eigen: &str, var: &str) -> DerivationTree {
846 let rule = match &tree.rule {
847 InferenceRule::UniversalInst(s) if s == eigen => InferenceRule::UniversalInst(var.to_string()),
848 InferenceRule::ExistentialIntro { witness, witness_type } if witness == eigen => {
849 InferenceRule::ExistentialIntro {
850 witness: var.to_string(),
851 witness_type: witness_type.clone(),
852 }
853 }
854 InferenceRule::ExistentialElim { witness } if witness == eigen => {
855 InferenceRule::ExistentialElim { witness: var.to_string() }
856 }
857 InferenceRule::Rewrite { from, to } => InferenceRule::Rewrite {
858 from: rewrite_const_to_var_term(from, eigen, var),
859 to: rewrite_const_to_var_term(to, eigen, var),
860 },
861 InferenceRule::CaseAnalysis { case_formula } => InferenceRule::CaseAnalysis {
862 case_formula: Box::new(rewrite_const_to_var_expr(case_formula, eigen, var)),
863 },
864 other => other.clone(),
865 };
866 DerivationTree {
867 conclusion: rewrite_const_to_var_expr(&tree.conclusion, eigen, var),
868 rule,
869 premises: tree.premises.iter().map(|p| rewrite_tree_const_to_var(p, eigen, var)).collect(),
870 depth: tree.depth,
871 substitution: tree
872 .substitution
873 .iter()
874 .map(|(k, v)| (k.clone(), rewrite_const_to_var_term(v, eigen, var)))
875 .collect(),
876 }
877}
878
879fn extract_conjunct(
888 conj: &DerivationTree,
889 target: &ProofExpr,
890) -> Option<(DerivationTree, Substitution)> {
891 if let ProofExpr::And(l, r) = &conj.conclusion {
892 let left =
893 DerivationTree::new((**l).clone(), InferenceRule::ConjunctionElim, vec![conj.clone()]);
894 if let Some(found) = extract_conjunct(&left, target) {
895 return Some(found);
896 }
897 let right =
898 DerivationTree::new((**r).clone(), InferenceRule::ConjunctionElim, vec![conj.clone()]);
899 extract_conjunct(&right, target)
900 } else if let Ok(subst) = unify_exprs(target, &conj.conclusion) {
901 Some((conj.clone(), subst))
902 } else {
903 None
904 }
905}
906
907fn cert_le_path(
911 a: &ProofTerm,
912 b: &ProofTerm,
913 adj: &std::collections::HashMap<String, Vec<(ProofTerm, DerivationTree)>>,
914) -> Option<DerivationTree> {
915 let a_key = term_skey(a)?;
916 let b_key = term_skey(b)?;
917 if a_key == b_key {
918 return Some(DerivationTree::leaf(
919 le_eq(a.clone(), a.clone()),
920 InferenceRule::LeRefl,
921 ));
922 }
923 let mut queue: std::collections::VecDeque<(String, Option<DerivationTree>)> =
924 std::collections::VecDeque::new();
925 queue.push_back((a_key.clone(), None));
926 let mut visited: std::collections::HashSet<String> = std::collections::HashSet::new();
927 visited.insert(a_key);
928 while let Some((cur_key, acc)) = queue.pop_front() {
929 let Some(edges) = adj.get(&cur_key) else {
930 continue;
931 };
932 for (next_term, edge_tree) in edges {
933 let Some(next_key) = term_skey(next_term) else {
934 continue;
935 };
936 if visited.contains(&next_key) {
937 continue;
938 }
939 visited.insert(next_key.clone());
940 let step = match &acc {
942 None => edge_tree.clone(), Some(acc_proof) => DerivationTree::new(
944 le_eq(a.clone(), next_term.clone()),
945 InferenceRule::LeTrans,
946 vec![acc_proof.clone(), edge_tree.clone()],
947 ),
948 };
949 if next_key == b_key {
950 return Some(step);
951 }
952 queue.push_back((next_key, Some(step)));
953 }
954 }
955 None
956}
957
958fn cert_linarith_close(known: &[(ProofExpr, DerivationTree)]) -> Option<DerivationTree> {
963 use std::collections::HashMap;
964 let mut adj: HashMap<String, Vec<(ProofTerm, DerivationTree)>> = HashMap::new();
965 let mut grounds: Vec<(i64, ProofTerm)> = Vec::new();
966 for (prop, tree) in known {
967 if let Some((x, y)) = as_le_pair(prop) {
968 if let Some(xk) = term_skey(&x) {
969 adj.entry(xk).or_default().push((y.clone(), tree.clone()));
970 }
971 for t in [&x, &y] {
972 if let Some(v) = as_int_literal(t) {
973 grounds.push((v, t.clone()));
974 }
975 }
976 }
977 }
978 if adj.is_empty() {
979 return None;
980 }
981 for (mv, m) in &grounds {
982 for (nv, n) in &grounds {
983 if mv > nv {
984 if let Some(le_proof) = cert_le_path(m, n, &adj) {
985 return Some(DerivationTree::new(
986 ProofExpr::Atom("⊥".into()),
987 InferenceRule::LinFalse,
988 vec![le_proof],
989 ));
990 }
991 }
992 }
993 }
994 None
995}
996
997pub(crate) fn cert_farkas(known: &[(ProofExpr, DerivationTree)]) -> Option<DerivationTree> {
1006 use crate::linarith_solve::{combine, find_farkas, parse_lin};
1007
1008 let mut le_hyps: Vec<(ProofTerm, ProofTerm, DerivationTree)> = Vec::new();
1009 let mut constraints = Vec::new();
1010 for (prop, tree) in known {
1011 if let Some((l, r)) = as_le_pair(prop) {
1012 if let (Some(ll), Some(rl)) = (parse_lin(&l), parse_lin(&r)) {
1013 constraints.push(ll.sub(&rl)); le_hyps.push((l, r, tree.clone()));
1015 }
1016 }
1017 }
1018 if le_hyps.is_empty() {
1019 return None;
1020 }
1021 let multipliers = find_farkas(&constraints)?;
1022 let combo = combine(&constraints, &multipliers);
1023 if !combo.is_const() || combo.constant <= 0 {
1024 return None;
1025 }
1026 let d = combo.constant; let pc = |n: i64| ProofTerm::Constant(n.to_string());
1029 let pf = |op: &str, x: ProofTerm, y: ProofTerm| ProofTerm::Function(op.to_string(), vec![x, y]);
1030
1031 let mut scaled: Vec<DerivationTree> = Vec::new();
1033 for (&i, &lam) in &multipliers {
1034 if lam <= 0 {
1035 continue;
1036 }
1037 let (l, r, hyp_tree) = &le_hyps[i];
1038 let diff = pf("add", r.clone(), pf("mul", pc(-1), l.clone()));
1040 let sub_i = DerivationTree::new(
1041 le_eq(pc(0), diff.clone()),
1042 InferenceRule::LeSub,
1043 vec![hyp_tree.clone()],
1044 );
1045 let ground = DerivationTree::leaf(le_eq(pc(0), pc(lam)), InferenceRule::Reflexivity);
1046 scaled.push(DerivationTree::new(
1047 le_eq(pf("mul", pc(lam), pc(0)), pf("mul", pc(lam), diff)),
1048 InferenceRule::LeMulNonneg,
1049 vec![ground, sub_i],
1050 ));
1051 }
1052 let summed = scaled.into_iter().reduce(|acc, s| {
1054 let (al, ar) = as_le_pair(&acc.conclusion).expect("scaled is an le-fact");
1055 let (sl, sr) = as_le_pair(&s.conclusion).expect("scaled is an le-fact");
1056 DerivationTree::new(
1057 le_eq(pf("add", al, sl), pf("add", ar, sr)),
1058 InferenceRule::LeAddMono,
1059 vec![acc, s],
1060 )
1061 })?;
1062 let (big_l, big_r) = as_le_pair(&summed.conclusion)?;
1063
1064 let rw1 = DerivationTree::new(
1066 le_eq(big_l.clone(), pc(-d)),
1067 InferenceRule::Rewrite { from: big_r.clone(), to: pc(-d) },
1068 vec![
1069 DerivationTree::leaf(
1070 ProofExpr::Identity(big_r, pc(-d)),
1071 InferenceRule::ArithDecision,
1072 ),
1073 summed,
1074 ],
1075 );
1076 let rw2 = DerivationTree::new(
1077 le_eq(pc(0), pc(-d)),
1078 InferenceRule::Rewrite { from: big_l.clone(), to: pc(0) },
1079 vec![
1080 DerivationTree::leaf(
1081 ProofExpr::Identity(big_l, pc(0)),
1082 InferenceRule::ArithDecision,
1083 ),
1084 rw1,
1085 ],
1086 );
1087 Some(DerivationTree::new(
1088 ProofExpr::Atom("⊥".into()),
1089 InferenceRule::LinFalse,
1090 vec![rw2],
1091 ))
1092}
1093
1094fn cert_close(known: &[(ProofExpr, DerivationTree)]) -> Option<DerivationTree> {
1096 for (prop, neg_tree) in known {
1097 if let ProofExpr::Not(inner) = prop {
1098 for (other, pos_tree) in known {
1099 if exprs_structurally_equal(other, inner) {
1100 return Some(DerivationTree::new(
1101 ProofExpr::Atom("⊥".into()),
1102 InferenceRule::Contradiction,
1103 vec![pos_tree.clone(), neg_tree.clone()],
1104 ));
1105 }
1106 }
1107 }
1108 }
1109 None
1110}
1111
1112fn cert_atoms_of(expr: &ProofExpr) -> Vec<ProofExpr> {
1114 match expr {
1115 ProofExpr::And(l, r) => {
1116 let mut v = cert_atoms_of(l);
1117 v.extend(cert_atoms_of(r));
1118 v
1119 }
1120 ProofExpr::Not(inner) => cert_atoms_of(inner),
1121 other => vec![other.clone()],
1122 }
1123}
1124
1125fn cert_is_ground(expr: &ProofExpr) -> bool {
1127 fn term_ground(t: &ProofTerm) -> bool {
1128 match t {
1129 ProofTerm::Variable(_) | ProofTerm::BoundVarRef(_) => false,
1130 ProofTerm::Function(_, args) | ProofTerm::Group(args) => args.iter().all(term_ground),
1131 ProofTerm::Constant(_) => true,
1132 }
1133 }
1134 match expr {
1135 ProofExpr::Predicate { args, .. } => args.iter().all(term_ground),
1136 ProofExpr::Not(inner) => cert_is_ground(inner),
1137 ProofExpr::And(l, r) | ProofExpr::Or(l, r) | ProofExpr::Implies(l, r) => {
1138 cert_is_ground(l) && cert_is_ground(r)
1139 }
1140 ProofExpr::Atom(_) => true,
1141 _ => false,
1142 }
1143}
1144
1145fn cert_collect_constants(known: &[(ProofExpr, DerivationTree)]) -> Vec<String> {
1147 fn from_term(t: &ProofTerm, out: &mut Vec<String>) {
1148 match t {
1149 ProofTerm::Constant(c) => {
1150 if !out.contains(c) {
1151 out.push(c.clone());
1152 }
1153 }
1154 ProofTerm::Function(_, args) | ProofTerm::Group(args) => {
1155 for a in args {
1156 from_term(a, out);
1157 }
1158 }
1159 _ => {}
1160 }
1161 }
1162 fn from_expr(e: &ProofExpr, out: &mut Vec<String>) {
1163 match e {
1164 ProofExpr::Predicate { args, .. } => {
1165 for a in args {
1166 from_term(a, out);
1167 }
1168 }
1169 ProofExpr::Not(i) => from_expr(i, out),
1170 ProofExpr::And(l, r) | ProofExpr::Or(l, r) | ProofExpr::Implies(l, r) => {
1171 from_expr(l, out);
1172 from_expr(r, out);
1173 }
1174 _ => {}
1175 }
1176 }
1177 let mut out = Vec::new();
1178 for (p, _) in known {
1179 from_expr(p, &mut out);
1180 }
1181 out
1182}
1183
1184fn cert_rule_substs(rule: &CertRule, known: &[(ProofExpr, DerivationTree)]) -> Vec<Substitution> {
1188 match &rule.binder {
1189 None => vec![Substitution::new()],
1190 Some(v) => {
1191 let mut substs: Vec<Substitution> = Vec::new();
1192 for atom in cert_atoms_of(&rule.ante) {
1193 for (fact, _) in known {
1194 if let Ok(s) = unify_exprs(fact, &atom) {
1195 if s.contains_key(v) && !substs.iter().any(|e| e == &s) {
1196 substs.push(s);
1197 }
1198 }
1199 }
1200 }
1201 substs
1202 }
1203 }
1204}
1205
1206fn cert_prove_ante(
1210 ante: &ProofExpr,
1211 subst: &Substitution,
1212 known: &[(ProofExpr, DerivationTree)],
1213) -> Option<DerivationTree> {
1214 match ante {
1215 ProofExpr::And(l, r) => {
1216 let lt = cert_prove_ante(l, subst, known)?;
1217 let rt = cert_prove_ante(r, subst, known)?;
1218 let li = apply_subst_to_expr(l, subst);
1219 let ri = apply_subst_to_expr(r, subst);
1220 Some(DerivationTree::new(
1221 ProofExpr::And(Box::new(li), Box::new(ri)),
1222 InferenceRule::ConjunctionIntro,
1223 vec![lt, rt],
1224 ))
1225 }
1226 _ => {
1227 let goal = apply_subst_to_expr(ante, subst);
1228 known
1229 .iter()
1230 .find(|(p, _)| exprs_structurally_equal(p, &goal))
1231 .map(|(_, t)| t.clone())
1232 }
1233 }
1234}
1235
1236fn cert_neg(e: &ProofExpr) -> ProofExpr {
1238 match e {
1239 ProofExpr::Not(inner) => (**inner).clone(),
1240 _ => ProofExpr::Not(Box::new(e.clone())),
1241 }
1242}
1243
1244fn cert_lookup(
1246 known: &[(ProofExpr, DerivationTree)],
1247 target: &ProofExpr,
1248) -> Option<DerivationTree> {
1249 known
1250 .iter()
1251 .find(|(p, _)| exprs_structurally_equal(p, target))
1252 .map(|(_, t)| t.clone())
1253}
1254
1255fn cert_rule_impl_tree(
1258 rule: &CertRule,
1259 subst: &Substitution,
1260 cons_inst: &ProofExpr,
1261) -> Option<DerivationTree> {
1262 match &rule.binder {
1263 None => Some(DerivationTree::leaf(
1264 rule.source.clone(),
1265 InferenceRule::PremiseMatch,
1266 )),
1267 Some(v) => {
1268 let witness = match subst.get(v) {
1269 Some(ProofTerm::Constant(c)) | Some(ProofTerm::Variable(c)) => c.clone(),
1270 _ => return None,
1271 };
1272 let ante_inst = apply_subst_to_expr(&rule.ante, subst);
1273 Some(DerivationTree::new(
1274 ProofExpr::Implies(Box::new(ante_inst), Box::new(cons_inst.clone())),
1275 InferenceRule::UniversalInst(witness),
1276 vec![DerivationTree::leaf(
1277 rule.source.clone(),
1278 InferenceRule::PremiseMatch,
1279 )],
1280 ))
1281 }
1282 }
1283}
1284
1285fn cert_refute(d: &ProofExpr, known: &[(ProofExpr, DerivationTree)]) -> Option<DerivationTree> {
1296 if let Some(t) = cert_lookup(known, &cert_neg(d)) {
1298 return Some(t);
1299 }
1300 match d {
1301 ProofExpr::And(l, r) => {
1304 for (side, _other_first) in [(l.as_ref(), true), (r.as_ref(), false)] {
1305 if let Some(neg_side) = cert_refute(side, known) {
1306 let assume = DerivationTree::leaf(d.clone(), InferenceRule::PremiseMatch);
1307 let elim = DerivationTree::new(
1308 side.clone(),
1309 InferenceRule::ConjunctionElim,
1310 vec![assume.clone()],
1311 );
1312 let contra = DerivationTree::new(
1313 ProofExpr::Atom("⊥".into()),
1314 InferenceRule::Contradiction,
1315 vec![elim, neg_side],
1316 );
1317 return Some(DerivationTree::new(
1318 cert_neg(d),
1319 InferenceRule::ReductioAdAbsurdum,
1320 vec![assume, contra],
1321 ));
1322 }
1323 }
1324 None
1325 }
1326 ProofExpr::Not(x) => {
1328 let x_tree = cert_lookup(known, x)?;
1329 let assume = DerivationTree::leaf(d.clone(), InferenceRule::PremiseMatch);
1330 let contra = DerivationTree::new(
1331 ProofExpr::Atom("⊥".into()),
1332 InferenceRule::Contradiction,
1333 vec![x_tree, assume.clone()],
1334 );
1335 Some(DerivationTree::new(
1336 cert_neg(d),
1337 InferenceRule::ReductioAdAbsurdum,
1338 vec![assume, contra],
1339 ))
1340 }
1341 _ => None,
1342 }
1343}
1344
1345fn cert_peel_disjunction(
1346 or_tree: &DerivationTree,
1347 known: &[(ProofExpr, DerivationTree)],
1348) -> Option<(ProofExpr, DerivationTree)> {
1349 let ProofExpr::Or(l, r) = &or_tree.conclusion else {
1350 return None;
1351 };
1352 let (l, r) = ((**l).clone(), (**r).clone());
1353 if let Some(neg_l) = cert_refute(&l, known) {
1354 let elim = DerivationTree::new(
1355 r.clone(),
1356 InferenceRule::DisjunctionElim,
1357 vec![or_tree.clone(), neg_l],
1358 );
1359 return Some(cert_peel_disjunction(&elim, known).unwrap_or((r, elim)));
1360 }
1361 if let Some(neg_r) = cert_refute(&r, known) {
1362 let elim = DerivationTree::new(
1363 l.clone(),
1364 InferenceRule::DisjunctionElim,
1365 vec![or_tree.clone(), neg_r],
1366 );
1367 return Some(cert_peel_disjunction(&elim, known).unwrap_or((l, elim)));
1368 }
1369 None
1370}
1371
1372fn cert_saturate(
1387 rules: &[CertRule],
1388 mut known: Vec<(ProofExpr, DerivationTree)>,
1389) -> Vec<(ProofExpr, DerivationTree)> {
1390 let max_rounds = 64;
1391 let mut push_new =
1392 |known: &mut Vec<(ProofExpr, DerivationTree)>, fact: ProofExpr, tree: DerivationTree| -> bool {
1393 if known.iter().any(|(p, _)| exprs_structurally_equal(p, &fact)) {
1394 false
1395 } else {
1396 known.push((fact, tree));
1397 true
1398 }
1399 };
1400 for _ in 0..max_rounds {
1401 let snapshot = known.clone();
1402 let mut added = false;
1403
1404 for rule in rules {
1406 for subst in cert_rule_substs(rule, &snapshot) {
1407 let cons_inst = apply_subst_to_expr(&rule.cons, &subst);
1408 if known.iter().any(|(p, _)| exprs_structurally_equal(p, &cons_inst)) {
1409 continue;
1410 }
1411 let Some(ante_proof) = cert_prove_ante(&rule.ante, &subst, &snapshot) else {
1412 continue;
1413 };
1414 let Some(impl_tree) = cert_rule_impl_tree(rule, &subst, &cons_inst) else {
1415 continue;
1416 };
1417 let mp = DerivationTree::new(
1418 cons_inst.clone(),
1419 InferenceRule::ModusPonens,
1420 vec![impl_tree, ante_proof],
1421 );
1422 added |= push_new(&mut known, cons_inst, mp);
1423 }
1424 }
1425
1426 for rule in rules {
1430 if rule.binder.is_some() {
1431 continue;
1432 }
1433 let neg_cons = cert_neg(&rule.cons);
1434 let Some(nc_tree) = cert_lookup(&snapshot, &neg_cons) else {
1435 continue;
1436 };
1437 let neg_ante = cert_neg(&rule.ante);
1438 if cert_lookup(&known, &neg_ante).is_some() {
1439 continue;
1440 }
1441 let assume = DerivationTree::leaf(rule.ante.clone(), InferenceRule::PremiseMatch);
1442 let rule_tree =
1443 DerivationTree::leaf(rule.source.clone(), InferenceRule::PremiseMatch);
1444 let mp = DerivationTree::new(
1445 rule.cons.clone(),
1446 InferenceRule::ModusPonens,
1447 vec![rule_tree, assume.clone()],
1448 );
1449 let contra = DerivationTree::new(
1450 ProofExpr::Atom("⊥".into()),
1451 InferenceRule::Contradiction,
1452 vec![mp, nc_tree],
1453 );
1454 let tree = DerivationTree::new(
1455 neg_ante.clone(),
1456 InferenceRule::ReductioAdAbsurdum,
1457 vec![assume, contra],
1458 );
1459 added |= push_new(&mut known, neg_ante, tree);
1460 }
1461
1462 for (p, or_tree) in &snapshot {
1464 if !matches!(p, ProofExpr::Or(..)) {
1465 continue;
1466 }
1467 if let Some((lit, tree)) = cert_peel_disjunction(or_tree, &snapshot) {
1468 added |= push_new(&mut known, lit, tree);
1469 }
1470 }
1471
1472 for (p, neg_tree) in &snapshot {
1476 let ProofExpr::Not(inner) = p else { continue };
1477 let ProofExpr::And(a, b) = inner.as_ref() else {
1478 continue;
1479 };
1480 for (known_is_left, known_side, other) in
1481 [(true, a.as_ref(), b.as_ref()), (false, b.as_ref(), a.as_ref())]
1482 {
1483 let Some(side_proof) = cert_prove_ante(known_side, &Substitution::new(), &snapshot)
1484 else {
1485 continue;
1486 };
1487 let neg_other = cert_neg(other);
1488 if cert_lookup(&known, &neg_other).is_some() {
1489 continue;
1490 }
1491 let assume = DerivationTree::leaf(other.clone(), InferenceRule::PremiseMatch);
1492 let (l_proof, r_proof) = if known_is_left {
1493 (side_proof.clone(), assume.clone())
1494 } else {
1495 (assume.clone(), side_proof.clone())
1496 };
1497 let conj_intro = DerivationTree::new(
1498 (**inner).clone(),
1499 InferenceRule::ConjunctionIntro,
1500 vec![l_proof, r_proof],
1501 );
1502 let contra = DerivationTree::new(
1503 ProofExpr::Atom("⊥".into()),
1504 InferenceRule::Contradiction,
1505 vec![conj_intro, neg_tree.clone()],
1506 );
1507 let tree = DerivationTree::new(
1508 neg_other.clone(),
1509 InferenceRule::ReductioAdAbsurdum,
1510 vec![assume, contra],
1511 );
1512 added |= push_new(&mut known, neg_other, tree);
1513 }
1514 }
1515
1516 for (p, and_tree) in &snapshot {
1521 let ProofExpr::And(a, b) = p else { continue };
1522 for side in [a.as_ref(), b.as_ref()] {
1523 let elim = DerivationTree::new(
1524 side.clone(),
1525 InferenceRule::ConjunctionElim,
1526 vec![and_tree.clone()],
1527 );
1528 added |= push_new(&mut known, side.clone(), elim);
1529 }
1530 }
1531
1532 if !added {
1533 break;
1534 }
1535 }
1536 known
1537}
1538
1539fn cert_candidates(known: &[(ProofExpr, DerivationTree)], rules: &[CertRule]) -> Vec<ProofExpr> {
1542 let consts = cert_collect_constants(known);
1543 let mut cands: Vec<ProofExpr> = Vec::new();
1544 let mut push = |c: ProofExpr, cands: &mut Vec<ProofExpr>| {
1545 if cert_is_ground(&c) && !cands.iter().any(|e| exprs_structurally_equal(e, &c)) {
1546 cands.push(c);
1547 }
1548 };
1549 for rule in rules {
1550 let atoms: Vec<ProofExpr> = cert_atoms_of(&rule.cons)
1551 .into_iter()
1552 .chain(cert_atoms_of(&rule.ante))
1553 .collect();
1554 match &rule.binder {
1555 None => {
1556 for a in atoms {
1557 push(a, &mut cands);
1558 }
1559 }
1560 Some(v) => {
1561 for k in &consts {
1562 let mut s = Substitution::new();
1563 s.insert(v.clone(), ProofTerm::Constant(k.clone()));
1564 for a in &atoms {
1565 push(apply_subst_to_expr(a, &s), &mut cands);
1566 }
1567 }
1568 }
1569 }
1570 }
1571 cands
1572}
1573
1574fn cert_derive_falsum(
1577 rules: &[CertRule],
1578 seed: &[(ProofExpr, DerivationTree)],
1579 depth: usize,
1580 fresh: &mut u32,
1581) -> Option<DerivationTree> {
1582 if let Some((idx, exist_tree)) = seed.iter().enumerate().find_map(|(i, (p, t))| {
1586 if matches!(p, ProofExpr::Exists { .. }) {
1587 Some((i, t.clone()))
1588 } else {
1589 None
1590 }
1591 }) {
1592 let (variable, body) = match &seed[idx].0 {
1593 ProofExpr::Exists { variable, body } => (variable.clone(), body.as_ref().clone()),
1594 _ => unreachable!(),
1595 };
1596 let c = format!("__sk{}", *fresh);
1597 *fresh += 1;
1598 let mut subst = Substitution::new();
1599 subst.insert(variable, ProofTerm::Constant(c.clone()));
1600 let p_c = apply_subst_to_expr(&body, &subst);
1601
1602 let mut seed2: Vec<(ProofExpr, DerivationTree)> = seed
1603 .iter()
1604 .enumerate()
1605 .filter(|(j, _)| *j != idx)
1606 .map(|(_, x)| x.clone())
1607 .collect();
1608 seed2.push((
1609 p_c.clone(),
1610 DerivationTree::leaf(p_c, InferenceRule::PremiseMatch),
1611 ));
1612
1613 return cert_derive_falsum(rules, &seed2, depth, fresh).map(|body_proof| {
1614 DerivationTree::new(
1615 ProofExpr::Atom("⊥".into()),
1616 InferenceRule::ExistentialElim { witness: c },
1617 vec![exist_tree, body_proof],
1618 )
1619 });
1620 }
1621
1622 let known = cert_saturate(rules, seed.to_vec());
1623 if let Some(t) = cert_close(&known) {
1624 return Some(t);
1625 }
1626 if let Some(t) = cert_equality_close(&known) {
1627 return Some(t);
1628 }
1629 if let Some(t) = cert_linarith_close(&known) {
1630 return Some(t);
1631 }
1632 if let Some(t) = cert_farkas(&known) {
1633 return Some(t);
1634 }
1635 if let Some(t) = crate::omega_solve::omega_close(&known) {
1639 return Some(t);
1640 }
1641 if depth == 0 {
1642 return None;
1643 }
1644 let known_has = |x: &ProofExpr, k: &[(ProofExpr, DerivationTree)]| {
1657 k.iter().any(|(q, _)| exprs_structurally_equal(q, x))
1658 };
1659 let seed_with = |d: &ProofExpr| -> Vec<(ProofExpr, DerivationTree)> {
1660 let mut s = seed.to_vec();
1661 for conj in flatten_conjuncts(d) {
1662 if !s.iter().any(|(q, _)| exprs_structurally_equal(q, &conj)) {
1663 s.push((conj.clone(), DerivationTree::leaf(conj, InferenceRule::PremiseMatch)));
1664 }
1665 }
1666 s
1667 };
1668 let established = |x: &ProofExpr, k: &[(ProofExpr, DerivationTree)]| {
1669 flatten_conjuncts(x).iter().all(|c| known_has(c, k))
1670 };
1671 if let Some((p, tree_or)) = known.iter().find_map(|(p, t)| match p {
1679 ProofExpr::Or(a, b) if !(established(a, &known) || established(b, &known)) => {
1680 Some((p.clone(), t.clone()))
1681 }
1682 _ => None,
1683 }) {
1684 let ProofExpr::Or(a, b) = &p else { unreachable!() };
1685 if let (Some(pa), Some(pb)) = (
1686 cert_derive_falsum(rules, &seed_with(a), depth - 1, fresh),
1687 cert_derive_falsum(rules, &seed_with(b), depth - 1, fresh),
1688 ) {
1689 return Some(DerivationTree::new(
1690 ProofExpr::Atom("⊥".into()),
1691 InferenceRule::DisjunctionCases,
1692 vec![tree_or, pa, pb],
1693 ));
1694 }
1695 return None;
1699 }
1700 for c in cert_candidates(&known, rules) {
1701 let not_c = ProofExpr::Not(Box::new(c.clone()));
1702 if known
1704 .iter()
1705 .any(|(p, _)| exprs_structurally_equal(p, &c) || exprs_structurally_equal(p, ¬_c))
1706 {
1707 continue;
1708 }
1709 let mut seed_pos = seed.to_vec();
1710 seed_pos.push((c.clone(), DerivationTree::leaf(c.clone(), InferenceRule::PremiseMatch)));
1711 let mut seed_neg = seed.to_vec();
1712 seed_neg.push((
1713 not_c.clone(),
1714 DerivationTree::leaf(not_c.clone(), InferenceRule::PremiseMatch),
1715 ));
1716 if let (Some(p), Some(n)) = (
1717 cert_derive_falsum(rules, &seed_pos, depth - 1, fresh),
1718 cert_derive_falsum(rules, &seed_neg, depth - 1, fresh),
1719 ) {
1720 return Some(DerivationTree::new(
1721 ProofExpr::Atom("⊥".into()),
1722 InferenceRule::CaseAnalysis {
1723 case_formula: Box::new(c.clone()),
1724 },
1725 vec![p, n],
1726 ));
1727 }
1728 }
1729 None
1730}
1731
1732fn exprs_structurally_equal(left: &ProofExpr, right: &ProofExpr) -> bool {
1736 match (left, right) {
1737 (ProofExpr::Atom(a), ProofExpr::Atom(b)) => a == b,
1738
1739 (ProofExpr::Ctor { name: n1, args: a1 }, ProofExpr::Ctor { name: n2, args: a2 }) => {
1740 n1 == n2 && a1.len() == a2.len() && a1.iter().zip(a2).all(|(x, y)| exprs_structurally_equal(x, y))
1741 }
1742
1743 (
1744 ProofExpr::Predicate { name: n1, args: a1, .. },
1745 ProofExpr::Predicate { name: n2, args: a2, .. },
1746 ) => n1 == n2 && a1.len() == a2.len() && a1.iter().zip(a2).all(|(x, y)| terms_structurally_equal(x, y)),
1747
1748 (ProofExpr::Identity(l1, r1), ProofExpr::Identity(l2, r2)) => {
1749 terms_structurally_equal(l1, l2) && terms_structurally_equal(r1, r2)
1750 }
1751
1752 (ProofExpr::And(l1, r1), ProofExpr::And(l2, r2))
1753 | (ProofExpr::Or(l1, r1), ProofExpr::Or(l2, r2))
1754 | (ProofExpr::Implies(l1, r1), ProofExpr::Implies(l2, r2))
1755 | (ProofExpr::Iff(l1, r1), ProofExpr::Iff(l2, r2)) => {
1756 exprs_structurally_equal(l1, l2) && exprs_structurally_equal(r1, r2)
1757 }
1758
1759 (ProofExpr::Not(a), ProofExpr::Not(b)) => exprs_structurally_equal(a, b),
1760
1761 (
1762 ProofExpr::ForAll { variable: v1, body: b1 },
1763 ProofExpr::ForAll { variable: v2, body: b2 },
1764 )
1765 | (
1766 ProofExpr::Exists { variable: v1, body: b1 },
1767 ProofExpr::Exists { variable: v2, body: b2 },
1768 ) => v1 == v2 && exprs_structurally_equal(b1, b2),
1769
1770 (
1771 ProofExpr::Lambda { variable: v1, body: b1 },
1772 ProofExpr::Lambda { variable: v2, body: b2 },
1773 ) => v1 == v2 && exprs_structurally_equal(b1, b2),
1774
1775 (ProofExpr::App(f1, a1), ProofExpr::App(f2, a2)) => {
1776 exprs_structurally_equal(f1, f2) && exprs_structurally_equal(a1, a2)
1777 }
1778
1779 (
1780 ProofExpr::TypedVar { name: n1, typename: t1 },
1781 ProofExpr::TypedVar { name: n2, typename: t2 },
1782 ) => n1 == n2 && t1 == t2,
1783
1784 (
1785 ProofExpr::Fixpoint { name: n1, body: b1 },
1786 ProofExpr::Fixpoint { name: n2, body: b2 },
1787 ) => n1 == n2 && exprs_structurally_equal(b1, b2),
1788
1789 _ => false,
1790 }
1791}
1792
1793fn terms_structurally_equal(left: &ProofTerm, right: &ProofTerm) -> bool {
1795 match (left, right) {
1796 (ProofTerm::Constant(a), ProofTerm::Constant(b)) => a == b,
1797 (ProofTerm::Variable(a), ProofTerm::Variable(b)) => a == b,
1798 (ProofTerm::BoundVarRef(a), ProofTerm::BoundVarRef(b)) => a == b,
1799 (ProofTerm::Function(n1, a1), ProofTerm::Function(n2, a2)) => {
1800 n1 == n2 && a1.len() == a2.len() && a1.iter().zip(a2).all(|(x, y)| terms_structurally_equal(x, y))
1801 }
1802 (ProofTerm::Group(a1), ProofTerm::Group(a2)) => {
1803 a1.len() == a2.len() && a1.iter().zip(a2).all(|(x, y)| terms_structurally_equal(x, y))
1804 }
1805 _ => false,
1806 }
1807}
1808
1809impl BackwardChainer {
1810 pub fn new() -> Self {
1812 Self {
1813 knowledge_base: Vec::new(),
1814 max_depth: DEFAULT_MAX_DEPTH,
1815 var_counter: 0,
1816 eliminated_existentials: Vec::new(),
1817 active_goals: Vec::new(),
1818 steps: 0,
1819 step_budget: DEFAULT_STEP_BUDGET,
1820 }
1821 }
1822
1823 pub fn set_max_depth(&mut self, depth: usize) {
1825 self.max_depth = depth;
1826 }
1827
1828 pub fn set_step_budget(&mut self, budget: usize) {
1831 self.step_budget = budget;
1832 }
1833
1834 fn charge_budget(&mut self) -> ProofResult<()> {
1840 self.steps += 1;
1841 if self.steps > self.step_budget {
1842 Err(ProofError::DepthExceeded)
1843 } else {
1844 Ok(())
1845 }
1846 }
1847
1848 pub fn knowledge_base(&self) -> &[ProofExpr] {
1850 &self.knowledge_base
1851 }
1852
1853 pub fn add_axiom(&mut self, expr: ProofExpr) {
1857 let abstracted = self.abstract_all_events(&expr);
1859 let simplified = self.simplify_definite_description_conjunction(&abstracted);
1861 self.knowledge_base.push(simplified);
1862 }
1863
1864 pub fn prove(&mut self, goal: ProofExpr) -> ProofResult<DerivationTree> {
1870 self.unify_definite_descriptions();
1874
1875 let abstracted_goal = self.abstract_events_only(&goal);
1878 let normalized_goal = self.simplify_definite_description_conjunction(&abstracted_goal);
1880 self.prove_goal(ProofGoal::new(normalized_goal), 0)
1884 }
1885
1886 pub fn prove_with_goal(&mut self, goal: ProofGoal) -> ProofResult<DerivationTree> {
1891 self.unify_definite_descriptions();
1892
1893 let abstracted_target = self.abstract_events_only(&goal.target);
1895 let normalized_target = self.simplify_definite_description_conjunction(&abstracted_target);
1896
1897 let normalized_context: Vec<ProofExpr> = goal
1899 .context
1900 .iter()
1901 .map(|expr| {
1902 let abstracted = self.abstract_events_only(expr);
1903 self.simplify_definite_description_conjunction(&abstracted)
1904 })
1905 .collect();
1906
1907 let normalized_goal = ProofGoal::with_context(normalized_target, normalized_context);
1908 self.prove_goal(normalized_goal, 0)
1909 }
1910
1911 fn unify_definite_descriptions(&mut self) {
1920 let mut definite_descs: std::collections::HashMap<String, Vec<(usize, String, ProofExpr)>> = std::collections::HashMap::new();
1922
1923 for (idx, axiom) in self.knowledge_base.iter().enumerate() {
1924 if let Some((pred_name, var_name, property)) = self.extract_definite_description(axiom) {
1925 definite_descs.entry(pred_name).or_default().push((idx, var_name, property));
1926 }
1927 }
1928
1929 let mut anaphor_bindings: Vec<(String, ProofTerm)> = Vec::new();
1934
1935 for (pred_name, descs) in definite_descs {
1937 if descs.is_empty() {
1938 continue;
1939 }
1940
1941 let skolem_name = format!("the_{}", pred_name);
1943 let skolem_const = ProofTerm::Constant(skolem_name.clone());
1944
1945 let defining_fact = ProofExpr::Predicate {
1947 name: pred_name.clone(),
1948 args: vec![skolem_const.clone()],
1949 world: None,
1950 };
1951 self.knowledge_base.push(defining_fact);
1952
1953 let uniqueness = ProofExpr::ForAll {
1957 variable: "_u".to_string(),
1958 body: Box::new(ProofExpr::Implies(
1959 Box::new(ProofExpr::Predicate {
1960 name: pred_name.clone(),
1961 args: vec![ProofTerm::Variable("_u".to_string())],
1962 world: None,
1963 }),
1964 Box::new(ProofExpr::Identity(
1965 ProofTerm::Variable("_u".to_string()),
1966 skolem_const.clone(),
1967 )),
1968 )),
1969 };
1970 self.knowledge_base.push(uniqueness);
1971
1972 let mut indices_to_remove: Vec<usize> = Vec::new();
1974 for (idx, var_name, property) in descs {
1975 anaphor_bindings.push((var_name.clone(), skolem_const.clone()));
1976 let substituted = self.substitute_term_in_expr(
1978 &property,
1979 &ProofTerm::Variable(var_name),
1980 &skolem_const,
1981 );
1982 let normalized = self.normalize_for_proof(&substituted);
1984 self.knowledge_base.push(normalized);
1985 indices_to_remove.push(idx);
1986 }
1987
1988 indices_to_remove.sort_unstable_by(|a, b| b.cmp(a));
1990 for idx in indices_to_remove {
1991 self.knowledge_base.remove(idx);
1992 }
1993 }
1994
1995 if !anaphor_bindings.is_empty() {
1999 let kb = std::mem::take(&mut self.knowledge_base);
2000 self.knowledge_base = kb
2001 .into_iter()
2002 .map(|axiom| {
2003 let mut bound = axiom;
2004 for (var_name, skolem) in &anaphor_bindings {
2005 bound = self.substitute_free_var_in_expr(&bound, var_name, skolem);
2006 }
2007 bound
2008 })
2009 .collect();
2010 }
2011 }
2012
2013 fn normalize_for_proof(&self, expr: &ProofExpr) -> ProofExpr {
2017 match expr {
2018 ProofExpr::ForAll { variable, body } => {
2019 if let ProofExpr::Not(inner) = body.as_ref() {
2021 if let ProofExpr::And(left, right) = inner.as_ref() {
2022 return ProofExpr::ForAll {
2023 variable: variable.clone(),
2024 body: Box::new(ProofExpr::Implies(
2025 Box::new(self.normalize_for_proof(left)),
2026 Box::new(ProofExpr::Not(Box::new(self.normalize_for_proof(right)))),
2027 )),
2028 };
2029 }
2030 }
2031 ProofExpr::ForAll {
2032 variable: variable.clone(),
2033 body: Box::new(self.normalize_for_proof(body)),
2034 }
2035 }
2036 ProofExpr::And(left, right) => ProofExpr::And(
2037 Box::new(self.normalize_for_proof(left)),
2038 Box::new(self.normalize_for_proof(right)),
2039 ),
2040 ProofExpr::Or(left, right) => ProofExpr::Or(
2041 Box::new(self.normalize_for_proof(left)),
2042 Box::new(self.normalize_for_proof(right)),
2043 ),
2044 ProofExpr::Implies(left, right) => ProofExpr::Implies(
2045 Box::new(self.normalize_for_proof(left)),
2046 Box::new(self.normalize_for_proof(right)),
2047 ),
2048 ProofExpr::Not(inner) => ProofExpr::Not(Box::new(self.normalize_for_proof(inner))),
2049 ProofExpr::Exists { variable, body } => ProofExpr::Exists {
2050 variable: variable.clone(),
2051 body: Box::new(self.normalize_for_proof(body)),
2052 },
2053 other => other.clone(),
2054 }
2055 }
2056
2057 fn extract_definite_description(&self, expr: &ProofExpr) -> Option<(String, String, ProofExpr)> {
2062 let (var, body) = match expr {
2064 ProofExpr::Exists { variable, body } => (variable.clone(), body.as_ref()),
2065 _ => return None,
2066 };
2067
2068 let (defining_part, property) = match body {
2070 ProofExpr::And(left, right) => (left.as_ref(), right.as_ref().clone()),
2071 _ => return None,
2072 };
2073
2074 let (type_pred, uniqueness) = match defining_part {
2076 ProofExpr::And(left, right) => (left.as_ref(), right.as_ref()),
2077 _ => return None,
2078 };
2079
2080 let pred_name = match type_pred {
2082 ProofExpr::Predicate { name, args, .. } if args.len() == 1 => {
2083 if let ProofTerm::Variable(v) = &args[0] {
2085 if v == &var {
2086 name.clone()
2087 } else {
2088 return None;
2089 }
2090 } else {
2091 return None;
2092 }
2093 }
2094 _ => return None,
2095 };
2096
2097 match uniqueness {
2099 ProofExpr::ForAll { variable: _, body: inner_body } => {
2100 match inner_body.as_ref() {
2101 ProofExpr::Implies(ante, cons) => {
2102 if let ProofExpr::Predicate { name, .. } = ante.as_ref() {
2104 if name != &pred_name {
2105 return None;
2106 }
2107 } else {
2108 return None;
2109 }
2110 if !matches!(cons.as_ref(), ProofExpr::Identity(_, _)) {
2112 return None;
2113 }
2114 }
2115 _ => return None,
2116 }
2117 }
2118 _ => return None,
2119 }
2120
2121 Some((pred_name, var, property))
2122 }
2123
2124 fn loop_key(goal: &ProofGoal) -> String {
2132 use std::hash::{Hash, Hasher};
2133 let mut order: Vec<String> = Vec::new();
2134 collect_vars_ordered_expr(&goal.target, &mut order);
2135 let mut subst = Substitution::new();
2136 for (i, v) in order.iter().enumerate() {
2137 subst.insert(v.clone(), ProofTerm::Constant(format!("\u{27ea}{i}\u{27eb}")));
2138 }
2139 let canon = apply_subst_to_expr(&goal.target, &subst);
2140 let mut hasher = std::collections::hash_map::DefaultHasher::new();
2141 goal.context.len().hash(&mut hasher);
2142 for c in &goal.context {
2143 format!("{c:?}").hash(&mut hasher);
2144 }
2145 format!("{canon:?}#{:x}", hasher.finish())
2146 }
2147
2148 fn prove_goal(&mut self, goal: ProofGoal, depth: usize) -> ProofResult<DerivationTree> {
2149 if depth == 0 {
2154 self.steps = 0;
2155 }
2156 self.charge_budget()?;
2157
2158 if depth > self.max_depth {
2160 return Err(ProofError::DepthExceeded);
2161 }
2162
2163 let key = Self::loop_key(&goal);
2170
2171 if self.active_goals.contains(&key) {
2178 return Err(ProofError::NoProofFound);
2179 }
2180 self.active_goals.push(key);
2181 let result = self.prove_goal_inner(goal, depth);
2182 self.active_goals.pop();
2183 result
2184 }
2185
2186 fn prove_goal_inner(&mut self, goal: ProofGoal, depth: usize) -> ProofResult<DerivationTree> {
2187 if depth > self.max_depth {
2189 return Err(ProofError::DepthExceeded);
2190 }
2191
2192 if let Some((_, typename)) = self.find_typed_var(&goal.target) {
2196 let is_known_inductive = matches!(typename.as_str(), "Nat" | "List");
2199
2200 if let Some(tree) = self.try_structural_induction(&goal, depth)? {
2201 return Ok(tree);
2202 }
2203
2204 if is_known_inductive {
2207 return Err(ProofError::NoProofFound);
2208 }
2209 }
2211
2212 if is_falsum(&goal.target) {
2217 if let Some(tree) = self.find_certifiable_contradiction(&goal.context, depth)? {
2221 return Ok(tree);
2222 }
2223 if let Some(tree) = self.find_contradiction(&goal.context, depth)? {
2224 return Ok(tree);
2225 }
2226 return Err(ProofError::NoProofFound);
2227 }
2228
2229 if let Some(tree) = self.try_reflexivity(&goal)? {
2232 return Ok(tree);
2233 }
2234
2235 if let Some(tree) = self.try_arithmetic(&goal)? {
2239 return Ok(tree);
2240 }
2241
2242 if let Some(tree) = self.try_linarith(&goal, depth)? {
2246 return Ok(tree);
2247 }
2248
2249 if let Some(tree) = self.try_match_fact(&goal)? {
2251 return Ok(tree);
2252 }
2253
2254 if matches!(goal.target, ProofExpr::Not(_)) {
2263 if let Some(tree) = self.try_modus_tollens(&goal, depth)? {
2264 return Ok(tree);
2265 }
2266 let is_double_neg = matches!(&goal.target, ProofExpr::Not(inner) if matches!(inner.as_ref(), ProofExpr::Not(_)));
2269 if !is_double_neg {
2270 if let Some(tree) = self.try_reductio_ad_absurdum(&goal, depth)? {
2271 return Ok(tree);
2272 }
2273 }
2274 }
2275
2276 if let Some(tree) = self.try_intro_rules(&goal, depth)? {
2278 return Ok(tree);
2279 }
2280
2281 if let Some(tree) = self.try_backward_chain(&goal, depth)? {
2283 return Ok(tree);
2284 }
2285
2286 if let Some(tree) = self.try_modus_tollens(&goal, depth)? {
2288 return Ok(tree);
2289 }
2290
2291 if let Some(tree) = self.try_universal_inst(&goal, depth)? {
2293 return Ok(tree);
2294 }
2295
2296 if let Some(tree) = self.try_existential_intro(&goal, depth)? {
2298 return Ok(tree);
2299 }
2300
2301 if let Some(tree) = self.try_disjunction_elimination(&goal, depth)? {
2303 return Ok(tree);
2304 }
2305
2306 if let Some(tree) = self.try_reductio_ad_absurdum(&goal, depth)? {
2309 return Ok(tree);
2310 }
2311
2312 if let Some(tree) = self.try_existential_elimination(&goal, depth)? {
2315 return Ok(tree);
2316 }
2317
2318 if let Some(tree) = self.try_equality_rewrite(&goal, depth)? {
2320 return Ok(tree);
2321 }
2322
2323 if depth == 0 && !is_falsum(&goal.target) {
2328 if let Some(false_tree) = self.find_certifiable_contradiction(&goal.context, depth)? {
2329 return Ok(DerivationTree::new(
2330 goal.target.clone(),
2331 InferenceRule::ExFalso,
2332 vec![false_tree],
2333 ));
2334 }
2335 }
2336
2337 if depth == 0 && !is_falsum(&goal.target) && !matches!(goal.target, ProofExpr::Not(_)) {
2341 let mut ext = goal.context.clone();
2342 ext.push(ProofExpr::Not(Box::new(goal.target.clone())));
2343 if let Some(false_tree) = self.find_certifiable_contradiction(&ext, depth)? {
2344 return Ok(DerivationTree::new(
2345 goal.target.clone(),
2346 InferenceRule::ClassicalReductio,
2347 vec![false_tree],
2348 ));
2349 }
2350 }
2351
2352 #[cfg(feature = "verification")]
2360 if depth == 0 {
2361 if let Some(tree) = self.try_oracle_fallback(&goal)? {
2362 return Ok(tree);
2363 }
2364 }
2365
2366 Err(ProofError::NoProofFound)
2368 }
2369
2370 fn try_reflexivity(&self, goal: &ProofGoal) -> ProofResult<Option<DerivationTree>> {
2379 if let ProofExpr::Identity(left_term, right_term) = &goal.target {
2380 let left_expr = term_to_expr(left_term);
2382 let right_expr = term_to_expr(right_term);
2383
2384 let left_normal = beta_reduce(&left_expr);
2386 let right_normal = beta_reduce(&right_expr);
2387
2388 if exprs_structurally_equal(&left_normal, &right_normal) {
2390 return Ok(Some(DerivationTree::leaf(
2391 goal.target.clone(),
2392 InferenceRule::Reflexivity,
2393 )));
2394 }
2395 }
2396 Ok(None)
2397 }
2398
2399 fn try_arithmetic(&self, goal: &ProofGoal) -> ProofResult<Option<DerivationTree>> {
2407 if let ProofExpr::Identity(left_term, right_term) = &goal.target {
2408 let is_arith = |t: &ProofTerm| {
2414 matches!(
2415 t,
2416 ProofTerm::Function(n, _)
2417 if matches!(n.as_str(), "add" | "sub" | "mul" | "div" | "mod")
2418 )
2419 };
2420 if !is_arith(left_term) && !is_arith(right_term) {
2421 return Ok(None);
2422 }
2423 let (kl, kr) = match (
2424 crate::certifier::proof_term_to_kernel_term(left_term),
2425 crate::certifier::proof_term_to_kernel_term(right_term),
2426 ) {
2427 (Ok(a), Ok(b)) => (a, b),
2428 _ => return Ok(None),
2429 };
2430
2431 let mut ctx = logicaffeine_kernel::Context::new();
2432 logicaffeine_kernel::prelude::StandardLibrary::register(&mut ctx);
2433
2434 if crate::arith::prove_int_eq(&ctx, &kl, &kr).is_some() {
2435 return Ok(Some(DerivationTree::leaf(
2436 goal.target.clone(),
2437 InferenceRule::ArithDecision,
2438 )));
2439 }
2440 }
2441 Ok(None)
2442 }
2443
2444 fn try_match_fact(&self, goal: &ProofGoal) -> ProofResult<Option<DerivationTree>> {
2450 for fact in goal.context.iter().chain(self.knowledge_base.iter()) {
2452 if let Ok(subst) = unify_exprs(&goal.target, fact) {
2453 return Ok(Some(
2454 DerivationTree::leaf(goal.target.clone(), InferenceRule::PremiseMatch)
2455 .with_substitution(subst),
2456 ));
2457 }
2458 }
2459 for fact in goal.context.iter().chain(self.knowledge_base.iter()) {
2465 if matches!(fact, ProofExpr::And(_, _)) {
2466 let leaf = DerivationTree::leaf(fact.clone(), InferenceRule::PremiseMatch);
2467 if let Some((tree, subst)) = extract_conjunct(&leaf, &goal.target) {
2468 return Ok(Some(tree.with_substitution(subst)));
2469 }
2470 }
2471 }
2472 Ok(None)
2473 }
2474
2475 fn try_intro_rules(
2481 &mut self,
2482 goal: &ProofGoal,
2483 depth: usize,
2484 ) -> ProofResult<Option<DerivationTree>> {
2485 match &goal.target {
2486 ProofExpr::And(left, right) => {
2488 let left_goal = ProofGoal::with_context((**left).clone(), goal.context.clone());
2489 let right_goal = ProofGoal::with_context((**right).clone(), goal.context.clone());
2490
2491 if let (Ok(left_proof), Ok(right_proof)) = (
2493 self.prove_goal(left_goal, depth + 1),
2494 self.prove_goal(right_goal, depth + 1),
2495 ) {
2496 return Ok(Some(DerivationTree::new(
2497 goal.target.clone(),
2498 InferenceRule::ConjunctionIntro,
2499 vec![left_proof, right_proof],
2500 )));
2501 }
2502 }
2503
2504 ProofExpr::Or(left, right) => {
2506 let left_goal = ProofGoal::with_context((**left).clone(), goal.context.clone());
2508 if let Ok(left_proof) = self.prove_goal(left_goal, depth + 1) {
2509 return Ok(Some(DerivationTree::new(
2510 goal.target.clone(),
2511 InferenceRule::DisjunctionIntro,
2512 vec![left_proof],
2513 )));
2514 }
2515
2516 let right_goal = ProofGoal::with_context((**right).clone(), goal.context.clone());
2518 if let Ok(right_proof) = self.prove_goal(right_goal, depth + 1) {
2519 return Ok(Some(DerivationTree::new(
2520 goal.target.clone(),
2521 InferenceRule::DisjunctionIntro,
2522 vec![right_proof],
2523 )));
2524 }
2525 }
2526
2527 ProofExpr::Iff(left, right) => {
2531 let pq = ProofExpr::Implies(left.clone(), right.clone());
2532 let qp = ProofExpr::Implies(right.clone(), left.clone());
2533 let pq_goal = ProofGoal::with_context(pq, goal.context.clone());
2534 let qp_goal = ProofGoal::with_context(qp, goal.context.clone());
2535 if let (Ok(pq_proof), Ok(qp_proof)) = (
2536 self.prove_goal(pq_goal, depth + 1),
2537 self.prove_goal(qp_goal, depth + 1),
2538 ) {
2539 return Ok(Some(DerivationTree::new(
2540 goal.target.clone(),
2541 InferenceRule::BicondIntro,
2542 vec![pq_proof, qp_proof],
2543 )));
2544 }
2545 }
2546
2547 ProofExpr::ForAll { variable, body } => {
2557 let eigen = self.fresh_eigenconstant();
2558 let mut subst = Substitution::new();
2559 subst.insert(variable.clone(), ProofTerm::Constant(eigen.clone()));
2560 let body_expr = apply_subst_to_expr(body, &subst);
2561 let body_ctx: Vec<ProofExpr> =
2562 goal.context.iter().map(|c| apply_subst_to_expr(c, &subst)).collect();
2563 let body_goal = ProofGoal::with_context(body_expr, body_ctx);
2564 if let Ok(body_proof) = self.prove_goal(body_goal, depth + 1) {
2565 let generalized = rewrite_tree_const_to_var(&body_proof, &eigen, variable);
2566 return Ok(Some(DerivationTree::new(
2567 goal.target.clone(),
2568 InferenceRule::UniversalIntro {
2569 variable: variable.clone(),
2570 var_type: "Entity".to_string(),
2571 },
2572 vec![generalized],
2573 )));
2574 }
2575 }
2576
2577 ProofExpr::Implies(ant, con) => {
2581 let mut ext = goal.context.clone();
2582 ext.push((**ant).clone());
2583 let con_goal = ProofGoal::with_context((**con).clone(), ext);
2584 if let Ok(con_proof) = self.prove_goal(con_goal, depth + 1) {
2585 return Ok(Some(DerivationTree::new(
2586 goal.target.clone(),
2587 InferenceRule::ImpliesIntro,
2588 vec![con_proof],
2589 )));
2590 }
2591 }
2592
2593 ProofExpr::Not(inner) => {
2595 if let ProofExpr::Not(core) = &**inner {
2596 let core_goal = ProofGoal::with_context((**core).clone(), goal.context.clone());
2597 if let Ok(core_proof) = self.prove_goal(core_goal, depth + 1) {
2598 return Ok(Some(DerivationTree::new(
2599 goal.target.clone(),
2600 InferenceRule::DoubleNegation,
2601 vec![core_proof],
2602 )));
2603 }
2604 }
2605 }
2606
2607 _ => {}
2608 }
2609
2610 Ok(None)
2611 }
2612
2613 fn try_backward_chain(
2619 &mut self,
2620 goal: &ProofGoal,
2621 depth: usize,
2622 ) -> ProofResult<Option<DerivationTree>> {
2623 struct Candidate {
2633 impl_expr: ProofExpr,
2634 antecedent: ProofExpr,
2635 subst: Substitution,
2636 score: usize,
2637 }
2638 let kb_implications: Vec<ProofExpr> = self
2639 .knowledge_base
2640 .iter()
2641 .filter(|e| matches!(e, ProofExpr::Implies(_, _)))
2642 .cloned()
2643 .collect();
2644 let mut candidates: Vec<Candidate> = Vec::new();
2645 for impl_expr in kb_implications {
2646 let renamed = self.rename_variables(&impl_expr);
2647 if let ProofExpr::Implies(ant, con) = renamed {
2648 if let Ok(subst) = unify_exprs(&goal.target, &con) {
2649 let antecedent = apply_subst_to_expr(&ant, &subst);
2650 let score = self.antecedent_pinnability(&antecedent, &goal.context);
2651 candidates.push(Candidate { impl_expr, antecedent, subst, score });
2652 }
2653 }
2654 }
2655 candidates.sort_by(|a, b| b.score.cmp(&a.score));
2657
2658 for cand in candidates {
2659 let ant_goal = ProofGoal::with_context(cand.antecedent, goal.context.clone());
2660 if let Ok(ant_proof) = self.prove_goal(ant_goal, depth + 1) {
2661 let impl_leaf =
2662 DerivationTree::leaf(cand.impl_expr.clone(), InferenceRule::PremiseMatch);
2663 return Ok(Some(
2664 DerivationTree::new(
2665 goal.target.clone(),
2666 InferenceRule::ModusPonens,
2667 vec![impl_leaf, ant_proof],
2668 )
2669 .with_substitution(cand.subst),
2670 ));
2671 }
2672 }
2673
2674 Ok(None)
2675 }
2676
2677 fn antecedent_pinnability(&self, ant: &ProofExpr, context: &[ProofExpr]) -> usize {
2682 flatten_conjuncts(ant)
2683 .iter()
2684 .filter(|c| self.subgoal_pinnable(c, context))
2685 .count()
2686 }
2687
2688 fn rule_relevance(&mut self, rule: &ProofExpr, goal: &ProofGoal) -> usize {
2697 let renamed = self.rename_variables(rule);
2698 let mut matrix: &ProofExpr = &renamed;
2699 while let ProofExpr::ForAll { body, .. } = matrix {
2700 matrix = body.as_ref();
2701 }
2702 match matrix {
2703 ProofExpr::Implies(ant, con) => {
2704 if let Ok(subst) = unify_exprs(&goal.target, con) {
2705 let ant_inst = apply_subst_to_expr(ant, &subst);
2706 2 + self.antecedent_pinnability(&ant_inst, &goal.context)
2709 } else {
2710 0
2711 }
2712 }
2713 other => {
2714 if unify_exprs(&goal.target, other).is_ok() {
2717 1000
2718 } else {
2719 0
2720 }
2721 }
2722 }
2723 }
2724
2725 fn try_modus_tollens(
2736 &mut self,
2737 goal: &ProofGoal,
2738 depth: usize,
2739 ) -> ProofResult<Option<DerivationTree>> {
2740 let inner_goal = match &goal.target {
2742 ProofExpr::Not(inner) => (**inner).clone(),
2743 _ => return Ok(None),
2744 };
2745
2746 let implications: Vec<ProofExpr> = self
2748 .knowledge_base
2749 .iter()
2750 .chain(goal.context.iter())
2751 .flat_map(|expr| {
2752 match expr {
2753 ProofExpr::Implies(_, _) => vec![expr.clone()],
2754 ProofExpr::ForAll { body, .. } => {
2755 if let ProofExpr::Implies(_, _) = body.as_ref() {
2757 vec![*body.clone()]
2758 } else {
2759 vec![]
2760 }
2761 }
2762 _ => vec![],
2763 }
2764 })
2765 .collect();
2766
2767 let negations: Vec<ProofExpr> = self
2769 .knowledge_base
2770 .iter()
2771 .chain(goal.context.iter())
2772 .filter_map(|expr| {
2773 if let ProofExpr::Not(inner) = expr {
2774 Some((**inner).clone())
2775 } else {
2776 None
2777 }
2778 })
2779 .collect();
2780
2781 for impl_expr in &implications {
2783 if let ProofExpr::Implies(antecedent, consequent) = impl_expr {
2784 if let Ok(subst) = unify_exprs(&inner_goal, antecedent) {
2786 let q = apply_subst_to_expr(consequent, &subst);
2788
2789 for negated in &negations {
2791 if exprs_structurally_equal(negated, &q) {
2792 let impl_leaf = DerivationTree::leaf(
2794 impl_expr.clone(),
2795 InferenceRule::PremiseMatch,
2796 );
2797 let neg_q_leaf = DerivationTree::leaf(
2798 ProofExpr::Not(Box::new(q.clone())),
2799 InferenceRule::PremiseMatch,
2800 );
2801
2802 return Ok(Some(
2803 DerivationTree::new(
2804 goal.target.clone(),
2805 InferenceRule::ModusTollens,
2806 vec![impl_leaf, neg_q_leaf],
2807 )
2808 .with_substitution(subst),
2809 ));
2810 }
2811 }
2812
2813 let neg_q_goal = ProofGoal::with_context(
2815 ProofExpr::Not(Box::new(q.clone())),
2816 goal.context.clone(),
2817 );
2818
2819 if let Ok(neg_q_proof) = self.prove_goal(neg_q_goal, depth + 1) {
2820 let impl_leaf = DerivationTree::leaf(
2822 impl_expr.clone(),
2823 InferenceRule::PremiseMatch,
2824 );
2825
2826 return Ok(Some(
2827 DerivationTree::new(
2828 goal.target.clone(),
2829 InferenceRule::ModusTollens,
2830 vec![impl_leaf, neg_q_proof],
2831 )
2832 .with_substitution(subst),
2833 ));
2834 }
2835 }
2836 }
2837 }
2838
2839 Ok(None)
2840 }
2841
2842 fn try_universal_inst(
2848 &mut self,
2849 goal: &ProofGoal,
2850 depth: usize,
2851 ) -> ProofResult<Option<DerivationTree>> {
2852 let universal_exprs: Vec<ProofExpr> = self
2858 .knowledge_base
2859 .iter()
2860 .filter(|expr| matches!(expr, ProofExpr::ForAll { .. }))
2861 .cloned()
2862 .collect();
2863 let mut scored: Vec<(usize, ProofExpr)> = universal_exprs
2864 .into_iter()
2865 .map(|u| (self.rule_relevance(&u, goal), u))
2866 .collect();
2867 scored.sort_by(|a, b| b.0.cmp(&a.0));
2868 let universals: Vec<ProofExpr> = scored.into_iter().map(|(_, u)| u).collect();
2869
2870 for forall_expr in universals {
2871 let renamed = self.rename_variables(&forall_expr);
2876 let mut body: &ProofExpr = &renamed;
2877 let mut bound_vars: Vec<String> = Vec::new();
2878 while let ProofExpr::ForAll { variable, body: inner } = body {
2879 bound_vars.push(variable.clone());
2880 body = inner.as_ref();
2881 }
2882 if bound_vars.is_empty() {
2883 continue;
2884 }
2885
2886 if let Ok(subst) = unify_exprs(&goal.target, body) {
2890 if let Some(witnesses) = instantiation_witnesses(&bound_vars, &subst, body) {
2894 let mut inst_node =
2895 DerivationTree::leaf(forall_expr.clone(), InferenceRule::PremiseMatch);
2896 for witness in &witnesses {
2897 inst_node = DerivationTree::new(
2898 goal.target.clone(),
2899 InferenceRule::UniversalInst(format!("{}", witness)),
2900 vec![inst_node],
2901 );
2902 }
2903 return Ok(Some(inst_node.with_substitution(subst)));
2904 }
2905 }
2906
2907 if let ProofExpr::Implies(ant, con) = body {
2912 if let Ok(subst) = unify_exprs(&goal.target, &**con) {
2913 let ant_inst = apply_subst_to_expr(&**ant, &subst);
2914 if let Ok((ant_proof, final_subst)) =
2915 self.prove_rule_antecedent(&ant_inst, &subst, &goal.context, depth + 1)
2916 {
2917 let witnesses =
2922 match instantiation_witnesses(&bound_vars, &final_subst, body) {
2923 Some(w) => w,
2924 None => continue,
2925 };
2926 let instantiated = apply_subst_to_expr(body, &final_subst);
2932 let mut inst_node =
2933 DerivationTree::leaf(forall_expr.clone(), InferenceRule::PremiseMatch);
2934 for witness in &witnesses {
2935 inst_node = DerivationTree::new(
2936 instantiated.clone(),
2937 InferenceRule::UniversalInst(format!("{}", witness)),
2938 vec![inst_node],
2939 );
2940 }
2941 return Ok(Some(
2942 DerivationTree::new(
2943 goal.target.clone(),
2944 InferenceRule::ModusPonens,
2945 vec![inst_node, ant_proof],
2946 )
2947 .with_substitution(final_subst),
2948 ));
2949 }
2950 }
2951 }
2952 }
2953
2954 Ok(None)
2955 }
2956
2957 fn prove_rule_antecedent(
2962 &mut self,
2963 antecedent: &ProofExpr,
2964 subst: &Substitution,
2965 context: &[ProofExpr],
2966 depth: usize,
2967 ) -> ProofResult<(DerivationTree, Substitution)> {
2968 let conjuncts = flatten_conjuncts(antecedent);
2969 let (proofs, final_subst) = self.solve_subgoals(&conjuncts, subst, context, depth)?;
2970 let tree = if proofs.len() == 1 {
2971 proofs.into_iter().next().expect("exactly one antecedent proof")
2972 } else {
2973 let mut proofs = proofs.into_iter();
2980 build_conjunction_tree(antecedent, &final_subst, &mut proofs)
2981 };
2982 Ok((tree, final_subst))
2983 }
2984
2985 fn subgoal_pinnable(&self, g: &ProofExpr, context: &[ProofExpr]) -> bool {
2990 context.iter().chain(self.knowledge_base.iter()).any(|fact| {
2991 if is_atomic_fact(fact) {
2992 unify_exprs(g, fact).is_ok()
2993 } else if matches!(fact, ProofExpr::And(_, _)) {
2994 flatten_conjuncts(fact).iter().any(|c| unify_exprs(g, c).is_ok())
2995 } else {
2996 false
2997 }
2998 })
2999 }
3000
3001 fn select_subgoal(
3007 &self,
3008 subgoals: &[ProofExpr],
3009 subst: &Substitution,
3010 context: &[ProofExpr],
3011 ) -> usize {
3012 let mut best = 0;
3013 let mut best_score = u8::MAX;
3014 for (i, sg) in subgoals.iter().enumerate() {
3015 let g = apply_subst_to_expr(sg, subst);
3016 let score = if matches!(g, ProofExpr::And(_, _)) {
3017 3
3018 } else if self.collect_variables(&g).is_empty() {
3019 0
3020 } else if self.subgoal_pinnable(&g, context) {
3021 1
3022 } else {
3023 2
3024 };
3025 if score < best_score {
3026 best_score = score;
3027 best = i;
3028 if score == 0 {
3029 break;
3030 }
3031 }
3032 }
3033 best
3034 }
3035
3036 fn solve_subgoals(
3042 &mut self,
3043 subgoals: &[ProofExpr],
3044 subst: &Substitution,
3045 context: &[ProofExpr],
3046 depth: usize,
3047 ) -> ProofResult<(Vec<DerivationTree>, Substitution)> {
3048 self.charge_budget()?;
3049 if depth > self.max_depth {
3050 return Err(ProofError::DepthExceeded);
3051 }
3052 if subgoals.is_empty() {
3053 return Ok((Vec::new(), subst.clone()));
3054 }
3055 let head = apply_subst_to_expr(&subgoals[0], subst);
3057 if matches!(head, ProofExpr::And(_, _)) {
3058 let mut expanded = flatten_conjuncts(&head);
3059 expanded.extend_from_slice(&subgoals[1..]);
3060 return self.solve_subgoals(&expanded, subst, context, depth);
3061 }
3062
3063 let idx = self.select_subgoal(subgoals, subst, context);
3072 let first = &subgoals[idx];
3073 let rest: Vec<ProofExpr> = subgoals
3074 .iter()
3075 .enumerate()
3076 .filter(|(i, _)| *i != idx)
3077 .map(|(_, e)| e.clone())
3078 .collect();
3079 let g = apply_subst_to_expr(first, subst);
3080
3081 if self.collect_variables(&g).is_empty() {
3087 let proof =
3088 self.prove_goal(ProofGoal::with_context(g.clone(), context.to_vec()), depth + 1)?;
3089 let (mut proofs, out) = self.solve_subgoals(&rest, subst, context, depth)?;
3090 proofs.insert(idx, proof);
3091 return Ok((proofs, out));
3092 }
3093
3094 let facts: Vec<ProofExpr> = context
3098 .iter()
3099 .chain(self.knowledge_base.iter())
3100 .filter(|e| is_atomic_fact(e) || matches!(e, ProofExpr::And(_, _)))
3101 .cloned()
3102 .collect();
3103 for fact in &facts {
3104 let matched: Option<(DerivationTree, Substitution)> =
3105 if matches!(fact, ProofExpr::And(_, _)) {
3106 let src = DerivationTree::leaf(fact.clone(), InferenceRule::PremiseMatch);
3107 extract_conjunct(&src, &g)
3108 } else if is_atomic_fact(fact) {
3109 unify_exprs(&g, fact).ok().map(|delta| {
3110 let leaf = DerivationTree::leaf(
3111 apply_subst_to_expr(&g, &delta),
3112 InferenceRule::PremiseMatch,
3113 );
3114 (leaf, delta)
3115 })
3116 } else {
3117 None
3118 };
3119 if let Some((leaf, delta)) = matched {
3120 let combined = compose_substitutions(subst.clone(), delta.clone());
3121 if let Ok((mut proofs, out)) =
3122 self.solve_subgoals(&rest, &combined, context, depth)
3123 {
3124 proofs.insert(idx, leaf.with_substitution(delta));
3125 return Ok((proofs, out));
3126 }
3127 }
3128 }
3129
3130 let rules: Vec<ProofExpr> = self
3133 .knowledge_base
3134 .iter()
3135 .filter(|e| matches!(e, ProofExpr::ForAll { .. } | ProofExpr::Implies(_, _)))
3136 .cloned()
3137 .collect();
3138 for rule in &rules {
3139 let renamed = self.rename_variables(rule);
3140 let mut matrix: &ProofExpr = &renamed;
3141 while let ProofExpr::ForAll { body, .. } = matrix {
3142 matrix = body.as_ref();
3143 }
3144 if let ProofExpr::Implies(ant, con) = matrix {
3145 if let Ok(delta) = unify_exprs(&g, &**con) {
3146 let combined = compose_substitutions(subst.clone(), delta.clone());
3147 let ant_inst = apply_subst_to_expr(&**ant, &combined);
3148 if let Ok((ant_proof, s_ant)) =
3149 self.prove_rule_antecedent(&ant_inst, &combined, context, depth + 1)
3150 {
3151 let mut bound_vars = Vec::new();
3156 let mut peel: &ProofExpr = &renamed;
3157 while let ProofExpr::ForAll { variable, body } = peel {
3158 bound_vars.push(variable.clone());
3159 peel = body;
3160 }
3161 let witnesses = match instantiation_witnesses(&bound_vars, &s_ant, matrix) {
3162 Some(w) => w,
3163 None => continue,
3164 };
3165 let resolved = apply_subst_to_expr(&g, &s_ant);
3166 let instantiated = apply_subst_to_expr(matrix, &s_ant);
3167 let mut inst_node =
3168 DerivationTree::leaf(rule.clone(), InferenceRule::PremiseMatch);
3169 for witness in &witnesses {
3170 inst_node = DerivationTree::new(
3171 instantiated.clone(),
3172 InferenceRule::UniversalInst(format!("{}", witness)),
3173 vec![inst_node],
3174 );
3175 }
3176 let mp = DerivationTree::new(
3177 resolved,
3178 InferenceRule::ModusPonens,
3179 vec![inst_node, ant_proof],
3180 );
3181 if let Ok((mut proofs, out)) =
3182 self.solve_subgoals(&rest, &s_ant, context, depth)
3183 {
3184 proofs.insert(idx, mp);
3185 return Ok((proofs, out));
3186 }
3187 }
3188 }
3189 } else if let Ok(delta) = unify_exprs(&g, matrix) {
3190 let combined = compose_substitutions(subst.clone(), delta.clone());
3196 let resolved = apply_subst_to_expr(&g, &combined);
3197 let mut vars = Vec::new();
3201 let mut orig_body: &ProofExpr = rule;
3202 while let ProofExpr::ForAll { variable, body } = orig_body {
3203 vars.push(variable.clone());
3204 orig_body = body;
3205 }
3206 if let Ok(wsubst) = unify_exprs(orig_body, &resolved) {
3207 let witnesses = match instantiation_witnesses(&vars, &wsubst, orig_body) {
3208 Some(w) => w,
3209 None => continue,
3210 };
3211 let mut node =
3212 DerivationTree::leaf(rule.clone(), InferenceRule::PremiseMatch);
3213 for witness in &witnesses {
3214 node = DerivationTree::new(
3215 resolved.clone(),
3216 InferenceRule::UniversalInst(format!("{}", witness)),
3217 vec![node],
3218 );
3219 }
3220 if let Ok((mut proofs, out)) =
3221 self.solve_subgoals(&rest, &combined, context, depth)
3222 {
3223 proofs.insert(idx, node);
3224 return Ok((proofs, out));
3225 }
3226 }
3227 }
3228 }
3229
3230 Err(ProofError::NoProofFound)
3231 }
3232
3233 fn try_existential_intro(
3239 &mut self,
3240 goal: &ProofGoal,
3241 depth: usize,
3242 ) -> ProofResult<Option<DerivationTree>> {
3243 if let ProofExpr::Exists { variable, body } = &goal.target {
3244 let witnesses = self.collect_witnesses();
3247
3248 for witness in witnesses {
3249 let mut subst = Substitution::new();
3251 subst.insert(variable.clone(), witness.clone());
3252
3253 let instantiated = apply_subst_to_expr(body, &subst);
3255
3256 let inst_goal = ProofGoal::with_context(instantiated, goal.context.clone());
3258
3259 if let Ok(body_proof) = self.prove_goal(inst_goal, depth + 1) {
3260 let witness_str = format!("{}", witness);
3261 let witness_type = extract_type_from_exists_body(body)
3266 .unwrap_or_else(|| "Entity".to_string());
3267 return Ok(Some(DerivationTree::new(
3268 goal.target.clone(),
3269 InferenceRule::ExistentialIntro {
3270 witness: witness_str,
3271 witness_type,
3272 },
3273 vec![body_proof],
3274 )));
3275 }
3276 }
3277 }
3278
3279 Ok(None)
3280 }
3281
3282 fn try_disjunction_elimination(
3292 &mut self,
3293 goal: &ProofGoal,
3294 depth: usize,
3295 ) -> ProofResult<Option<DerivationTree>> {
3296 let disjunctions: Vec<(ProofExpr, ProofExpr, ProofExpr)> = self
3298 .knowledge_base
3299 .iter()
3300 .chain(goal.context.iter())
3301 .filter_map(|expr| {
3302 if let ProofExpr::Or(left, right) = expr {
3303 Some((expr.clone(), (**left).clone(), (**right).clone()))
3304 } else {
3305 None
3306 }
3307 })
3308 .collect();
3309
3310 let negations: Vec<ProofExpr> = self
3312 .knowledge_base
3313 .iter()
3314 .chain(goal.context.iter())
3315 .filter_map(|expr| {
3316 if let ProofExpr::Not(inner) = expr {
3317 Some((**inner).clone())
3318 } else {
3319 None
3320 }
3321 })
3322 .collect();
3323
3324 for (disj_expr, left, right) in &disjunctions {
3326 for negated in &negations {
3328 if exprs_structurally_equal(negated, left) {
3329 if let Ok(subst) = unify_exprs(&goal.target, right) {
3332 let disj_leaf = DerivationTree::leaf(
3333 disj_expr.clone(),
3334 InferenceRule::PremiseMatch,
3335 );
3336 let neg_leaf = DerivationTree::leaf(
3337 ProofExpr::Not(Box::new(left.clone())),
3338 InferenceRule::PremiseMatch,
3339 );
3340 return Ok(Some(
3341 DerivationTree::new(
3342 goal.target.clone(),
3343 InferenceRule::DisjunctionElim,
3344 vec![disj_leaf, neg_leaf],
3345 )
3346 .with_substitution(subst),
3347 ));
3348 }
3349 }
3350
3351 if exprs_structurally_equal(negated, right) {
3353 if let Ok(subst) = unify_exprs(&goal.target, left) {
3356 let disj_leaf = DerivationTree::leaf(
3357 disj_expr.clone(),
3358 InferenceRule::PremiseMatch,
3359 );
3360 let neg_leaf = DerivationTree::leaf(
3361 ProofExpr::Not(Box::new(right.clone())),
3362 InferenceRule::PremiseMatch,
3363 );
3364 return Ok(Some(
3365 DerivationTree::new(
3366 goal.target.clone(),
3367 InferenceRule::DisjunctionElim,
3368 vec![disj_leaf, neg_leaf],
3369 )
3370 .with_substitution(subst),
3371 ));
3372 }
3373 }
3374 }
3375 }
3376
3377 if depth < self.max_depth && !matches!(goal.target, ProofExpr::Or(..)) {
3388 for negated in &negations {
3389 if exprs_structurally_equal(negated, &goal.target) {
3390 continue;
3391 }
3392 for orient_left in [true, false] {
3393 let disjunction = if orient_left {
3394 ProofExpr::Or(
3395 Box::new(negated.clone()),
3396 Box::new(goal.target.clone()),
3397 )
3398 } else {
3399 ProofExpr::Or(
3400 Box::new(goal.target.clone()),
3401 Box::new(negated.clone()),
3402 )
3403 };
3404 if disjunctions
3406 .iter()
3407 .any(|(d, _, _)| exprs_structurally_equal(d, &disjunction))
3408 {
3409 continue;
3410 }
3411 let disj_goal =
3412 ProofGoal::with_context(disjunction.clone(), goal.context.clone());
3413 let disj_proof = self
3420 .try_match_fact(&disj_goal)?
3421 .map(Ok)
3422 .or_else(|| self.try_universal_inst(&disj_goal, depth + 1).transpose())
3423 .transpose()?;
3424 if let Some(disj_proof) = disj_proof {
3425 let neg_leaf = DerivationTree::leaf(
3426 ProofExpr::Not(Box::new(negated.clone())),
3427 InferenceRule::PremiseMatch,
3428 );
3429 return Ok(Some(DerivationTree::new(
3430 goal.target.clone(),
3431 InferenceRule::DisjunctionElim,
3432 vec![disj_proof, neg_leaf],
3433 )));
3434 }
3435 }
3436 }
3437 }
3438
3439 if depth < self.max_depth && !matches!(goal.target, ProofExpr::Or(..)) {
3448 let disjunctive_rules: Vec<(ProofExpr, ProofExpr)> = self
3449 .knowledge_base
3450 .iter()
3451 .chain(goal.context.iter())
3452 .filter_map(|e| match e {
3453 ProofExpr::ForAll { body, .. } => match body.as_ref() {
3454 ProofExpr::Implies(_, cons) => match cons.as_ref() {
3455 ProofExpr::Or(l, r) => Some(((**l).clone(), (**r).clone())),
3456 _ => None,
3457 },
3458 _ => None,
3459 },
3460 ProofExpr::Implies(_, cons) => match cons.as_ref() {
3461 ProofExpr::Or(l, r) => Some(((**l).clone(), (**r).clone())),
3462 _ => None,
3463 },
3464 ProofExpr::Or(l, r) => Some(((**l).clone(), (**r).clone())),
3465 _ => None,
3466 })
3467 .collect();
3468
3469 for (d_left, d_right) in &disjunctive_rules {
3470 for goal_is_right in [true, false] {
3473 let (goal_template, other_template) = if goal_is_right {
3474 (d_right, d_left)
3475 } else {
3476 (d_left, d_right)
3477 };
3478 let Ok(subst) = unify_exprs(&goal.target, goal_template) else {
3479 continue;
3480 };
3481 let other = apply_subst_to_expr(other_template, &subst);
3482 if self.contains_quantifier(&other) {
3483 continue;
3484 }
3485 let goal_disjunct = apply_subst_to_expr(goal_template, &subst);
3489 let disjunction = if goal_is_right {
3490 ProofExpr::Or(Box::new(other.clone()), Box::new(goal_disjunct))
3491 } else {
3492 ProofExpr::Or(Box::new(goal_disjunct), Box::new(other.clone()))
3493 };
3494 let disj_goal =
3495 ProofGoal::with_context(disjunction.clone(), goal.context.clone());
3496 let disj_proof = match self.try_match_fact(&disj_goal)? {
3497 Some(p) => Some(p),
3498 None => match self.try_universal_inst(&disj_goal, depth + 1)? {
3499 Some(p) => Some(p),
3500 None => self.try_backward_chain(&disj_goal, depth + 1)?,
3506 },
3507 };
3508 let Some(disj_proof) = disj_proof else {
3509 continue;
3510 };
3511 let neg_goal = ProofGoal::with_context(
3513 ProofExpr::Not(Box::new(other.clone())),
3514 goal.context.clone(),
3515 );
3516 if let Ok(neg_proof) = self.prove_goal(neg_goal, depth + 1) {
3517 return Ok(Some(DerivationTree::new(
3518 goal.target.clone(),
3519 InferenceRule::DisjunctionElim,
3520 vec![disj_proof, neg_proof],
3521 )));
3522 }
3523 }
3524 }
3525 }
3526
3527 if depth < self.max_depth {
3534 for (disj_expr, left, right) in &disjunctions {
3535 let decided = goal.context.iter().any(|c| {
3536 exprs_structurally_equal(c, left) || exprs_structurally_equal(c, right)
3537 });
3538 if decided {
3539 continue;
3540 }
3541 let mut left_ctx = goal.context.clone();
3542 left_ctx.push(left.clone());
3543 let left_branch = match self.prove_goal(
3544 ProofGoal::with_context(goal.target.clone(), left_ctx),
3545 depth + 1,
3546 ) {
3547 Ok(t) => t,
3548 Err(_) => continue,
3549 };
3550 let mut right_ctx = goal.context.clone();
3551 right_ctx.push(right.clone());
3552 let right_branch = match self.prove_goal(
3553 ProofGoal::with_context(goal.target.clone(), right_ctx),
3554 depth + 1,
3555 ) {
3556 Ok(t) => t,
3557 Err(_) => continue,
3558 };
3559 let disj_leaf =
3560 DerivationTree::leaf(disj_expr.clone(), InferenceRule::PremiseMatch);
3561 return Ok(Some(DerivationTree::new(
3562 goal.target.clone(),
3563 InferenceRule::DisjunctionCases,
3564 vec![disj_leaf, left_branch, right_branch],
3565 )));
3566 }
3567 }
3568
3569 Ok(None)
3570 }
3571
3572 fn try_reductio_ad_absurdum(
3584 &mut self,
3585 goal: &ProofGoal,
3586 depth: usize,
3587 ) -> ProofResult<Option<DerivationTree>> {
3588 let assumed = match &goal.target {
3590 ProofExpr::Not(inner) => (**inner).clone(),
3591 _ => return Ok(None),
3592 };
3593
3594 if depth > 5 {
3596 return Ok(None);
3597 }
3598
3599 if let ProofExpr::Exists { .. } = &assumed {
3602 return self.try_existence_negation_proof(&goal, &assumed, depth);
3603 }
3604
3605 if self.contains_quantifier(&assumed) {
3608 return Ok(None);
3609 }
3610
3611 let mut extended_context = goal.context.clone();
3613 extended_context.push(assumed.clone());
3614
3615 let skolemized = self.skolemize_existential(&assumed);
3617 for sk in &skolemized {
3618 extended_context.push(sk.clone());
3619 }
3620
3621 if let Some(contradiction_proof) =
3626 self.find_certifiable_contradiction(&extended_context, depth)?
3627 {
3628 let assumption_leaf =
3629 DerivationTree::leaf(assumed.clone(), InferenceRule::PremiseMatch);
3630 return Ok(Some(DerivationTree::new(
3631 goal.target.clone(),
3632 InferenceRule::ReductioAdAbsurdum,
3633 vec![assumption_leaf, contradiction_proof],
3634 )));
3635 }
3636
3637 if let Some(contradiction_proof) = self.find_contradiction(&extended_context, depth)? {
3640 let assumption_leaf = DerivationTree::leaf(
3642 assumed.clone(),
3643 InferenceRule::PremiseMatch,
3644 );
3645
3646 return Ok(Some(DerivationTree::new(
3647 goal.target.clone(),
3648 InferenceRule::ReductioAdAbsurdum,
3649 vec![assumption_leaf, contradiction_proof],
3650 )));
3651 }
3652
3653 Ok(None)
3654 }
3655
3656 fn try_existence_negation_proof(
3668 &mut self,
3669 goal: &ProofGoal,
3670 assumed_existence: &ProofExpr,
3671 depth: usize,
3672 ) -> ProofResult<Option<DerivationTree>> {
3673 let witness_facts = self.skolemize_existential(assumed_existence);
3675
3676 if witness_facts.is_empty() {
3677 return Ok(None);
3678 }
3679
3680 let mut extended_context = goal.context.clone();
3682 extended_context.push(assumed_existence.clone());
3683
3684 for fact in &witness_facts {
3686 let abstracted = self.abstract_all_events(fact);
3687 if !extended_context.contains(&abstracted) {
3688 extended_context.push(abstracted);
3689 }
3690 if !extended_context.contains(fact) {
3691 extended_context.push(fact.clone());
3692 }
3693 }
3694
3695 let mut skolem_constants = self.extract_skolem_constants(&witness_facts);
3697
3698 let kb_skolemized = self.skolemize_kb_existentials();
3703 for fact in &kb_skolemized {
3704 let abstracted = self.abstract_all_events(fact);
3705 if !extended_context.contains(&abstracted) {
3706 extended_context.push(abstracted);
3707 }
3708 if !extended_context.contains(fact) {
3709 extended_context.push(fact.clone());
3710 }
3711 }
3712
3713 let kb_skolems = self.extract_skolem_constants(&kb_skolemized);
3715 for sk in kb_skolems {
3716 if !skolem_constants.contains(&sk) {
3717 skolem_constants.push(sk);
3718 }
3719 }
3720
3721 for expr in &self.knowledge_base {
3724 self.collect_unified_constants(expr, &mut skolem_constants);
3725 }
3726
3727 let instantiated = self.instantiate_universals_with_constants(
3729 &extended_context,
3730 &skolem_constants,
3731 );
3732 for inst in &instantiated {
3733 let abstracted = self.abstract_all_events(inst);
3734 if !extended_context.contains(&abstracted) {
3735 extended_context.push(abstracted);
3736 }
3737 }
3738
3739 let kb_instantiated = self.instantiate_kb_universals_with_constants(&skolem_constants);
3741 for inst in &kb_instantiated {
3742 let abstracted = self.abstract_all_events(inst);
3743 if !extended_context.contains(&abstracted) {
3744 extended_context.push(abstracted);
3745 }
3746 }
3747
3748 let derived_equalities = self.derive_equalities_from_uniqueness_constraints(
3753 &extended_context,
3754 &skolem_constants,
3755 );
3756
3757 for eq in &derived_equalities {
3759 if !extended_context.contains(eq) {
3760 extended_context.push(eq.clone());
3761 }
3762 }
3763
3764 let unified_context = self.apply_equalities_to_context(&extended_context, &derived_equalities);
3767
3768 if let Some(contradiction_proof) = self.find_contradiction(&unified_context, depth)? {
3770 let assumption_leaf = DerivationTree::leaf(
3771 assumed_existence.clone(),
3772 InferenceRule::PremiseMatch,
3773 );
3774
3775 return Ok(Some(DerivationTree::new(
3776 goal.target.clone(),
3777 InferenceRule::ReductioAdAbsurdum,
3778 vec![assumption_leaf, contradiction_proof],
3779 )));
3780 }
3781
3782 if let Some(case_proof) = self.try_case_analysis_contradiction(&unified_context, &skolem_constants, depth)? {
3784 let assumption_leaf = DerivationTree::leaf(
3785 assumed_existence.clone(),
3786 InferenceRule::PremiseMatch,
3787 );
3788
3789 return Ok(Some(DerivationTree::new(
3790 goal.target.clone(),
3791 InferenceRule::ReductioAdAbsurdum,
3792 vec![assumption_leaf, case_proof],
3793 )));
3794 }
3795
3796 Ok(None)
3797 }
3798
3799 fn skolemize_kb_existentials(&mut self) -> Vec<ProofExpr> {
3805 let mut results = Vec::new();
3806
3807 for expr in &self.knowledge_base.clone() {
3808 if let ProofExpr::Exists { .. } = expr {
3809 let skolemized = self.skolemize_existential(expr);
3810 results.extend(skolemized);
3811 }
3812 }
3813
3814 results
3815 }
3816
3817 fn derive_equalities_from_uniqueness_constraints(
3829 &self,
3830 context: &[ProofExpr],
3831 skolem_constants: &[String],
3832 ) -> Vec<ProofExpr> {
3833 let mut equalities = Vec::new();
3834
3835 let uniqueness_constraints = self.extract_uniqueness_constraints(context);
3838
3839 for skolem in skolem_constants {
3842 for (predicate_name, unique_entity) in &uniqueness_constraints {
3843 let skolem_term = ProofTerm::Constant(skolem.clone());
3845 let skolem_satisfies_predicate = context.iter().any(|expr| {
3846 self.predicate_matches(expr, predicate_name, &skolem_term)
3847 });
3848
3849 if skolem_satisfies_predicate {
3850 let equality = ProofExpr::Identity(
3852 skolem_term.clone(),
3853 unique_entity.clone(),
3854 );
3855 if !equalities.contains(&equality) {
3856 equalities.push(equality);
3857 }
3858
3859 let sym_equality = ProofExpr::Identity(
3861 unique_entity.clone(),
3862 skolem_term.clone(),
3863 );
3864 if !equalities.contains(&sym_equality) {
3865 equalities.push(sym_equality);
3866 }
3867 }
3868 }
3869 }
3870
3871 let mut transitive_equalities = Vec::new();
3873 for eq1 in &equalities {
3874 if let ProofExpr::Identity(t1, t2) = eq1 {
3875 for eq2 in &equalities {
3876 if let ProofExpr::Identity(t3, t4) = eq2 {
3877 if t1 == t3 && t2 != t4 {
3879 let trans_eq = ProofExpr::Identity(t2.clone(), t4.clone());
3880 if !equalities.contains(&trans_eq) && !transitive_equalities.contains(&trans_eq) {
3881 transitive_equalities.push(trans_eq);
3882 }
3883 }
3884 if t1 == t4 && t2 != t3 {
3886 let trans_eq = ProofExpr::Identity(t2.clone(), t3.clone());
3887 if !equalities.contains(&trans_eq) && !transitive_equalities.contains(&trans_eq) {
3888 transitive_equalities.push(trans_eq);
3889 }
3890 }
3891 }
3892 }
3893 }
3894 }
3895 equalities.extend(transitive_equalities);
3896
3897 equalities
3898 }
3899
3900 fn extract_uniqueness_constraints(&self, context: &[ProofExpr]) -> Vec<(String, ProofTerm)> {
3905 let mut constraints = Vec::new();
3906
3907 for expr in context.iter().chain(self.knowledge_base.iter()) {
3908 self.extract_uniqueness_from_expr(expr, &mut constraints);
3909 }
3910
3911 constraints
3912 }
3913
3914 fn extract_uniqueness_from_expr(&self, expr: &ProofExpr, constraints: &mut Vec<(String, ProofTerm)>) {
3916 match expr {
3917 ProofExpr::ForAll { variable, body } => {
3919 if let ProofExpr::Implies(ante, cons) = body.as_ref() {
3920 if let ProofExpr::Identity(left, right) = cons.as_ref() {
3921 let var_term = ProofTerm::Variable(variable.clone());
3923 if left == &var_term {
3924 if let Some(pred_name) = self.extract_unary_predicate_name(ante, variable) {
3926 constraints.push((pred_name, right.clone()));
3928 }
3929 } else if right == &var_term {
3930 if let Some(pred_name) = self.extract_unary_predicate_name(ante, variable) {
3932 constraints.push((pred_name, left.clone()));
3933 }
3934 }
3935 }
3936 }
3937 self.extract_uniqueness_from_expr(body, constraints);
3939 }
3940
3941 ProofExpr::And(left, right) => {
3943 self.extract_uniqueness_from_expr(left, constraints);
3944 self.extract_uniqueness_from_expr(right, constraints);
3945 }
3946
3947 ProofExpr::Exists { body, .. } => {
3949 self.extract_uniqueness_from_expr(body, constraints);
3950 }
3951
3952 _ => {}
3953 }
3954 }
3955
3956 fn extract_unary_predicate_name(&self, expr: &ProofExpr, var: &str) -> Option<String> {
3960 match expr {
3961 ProofExpr::Predicate { name, args, .. } => {
3962 if args.len() == 1 {
3963 if let ProofTerm::Variable(v) = &args[0] {
3964 if v == var {
3965 return Some(name.clone());
3966 }
3967 }
3968 }
3969 None
3970 }
3971 _ => None,
3972 }
3973 }
3974
3975 fn predicate_matches(&self, expr: &ProofExpr, pred_name: &str, term: &ProofTerm) -> bool {
3977 match expr {
3978 ProofExpr::Predicate { name, args, .. } => {
3979 name == pred_name && args.len() == 1 && &args[0] == term
3980 }
3981 _ => false,
3982 }
3983 }
3984
3985 fn apply_equalities_to_context(
3990 &self,
3991 context: &[ProofExpr],
3992 equalities: &[ProofExpr],
3993 ) -> Vec<ProofExpr> {
3994 if equalities.is_empty() {
3995 return context.to_vec();
3996 }
3997
3998 let mut substitutions: Vec<(&ProofTerm, &ProofTerm)> = Vec::new();
4001 for eq in equalities {
4002 if let ProofExpr::Identity(t1, t2) = eq {
4003 if let ProofTerm::Constant(c) = t1 {
4005 if c.starts_with("sk_") {
4006 substitutions.push((t2, t1)); continue;
4008 }
4009 }
4010 if let ProofTerm::Constant(c) = t2 {
4011 if c.starts_with("sk_") {
4012 substitutions.push((t1, t2)); continue;
4014 }
4015 }
4016 substitutions.push((t2, t1));
4018 }
4019 }
4020
4021 let mut unified_context = Vec::new();
4023 for expr in context {
4024 let mut unified = expr.clone();
4025 for (from, to) in &substitutions {
4026 unified = self.substitute_term_in_expr(&unified, from, to);
4027 }
4028 let abstracted = self.abstract_all_events(&unified);
4030 if !unified_context.contains(&unified) {
4031 unified_context.push(unified);
4032 }
4033 if !unified_context.contains(&abstracted) {
4034 unified_context.push(abstracted);
4035 }
4036 }
4037
4038 for expr in context {
4041 if let ProofExpr::ForAll { variable, body } = expr {
4042 if let ProofExpr::Implies(_, _) = body.as_ref() {
4043 for (from, to) in &substitutions {
4045 if let ProofTerm::Constant(c) = to {
4046 if c.starts_with("sk_") {
4047 let mut subst = Substitution::new();
4049 subst.insert(variable.clone(), (*to).clone());
4050 let instantiated = apply_subst_to_expr(body, &subst);
4051 let abstracted = self.abstract_all_events(&instantiated);
4052 if !unified_context.contains(&abstracted) {
4053 unified_context.push(abstracted);
4054 }
4055 }
4056 }
4057 }
4058 }
4059 }
4060 }
4061
4062 unified_context
4063 }
4064
4065 fn extract_skolem_constants(&self, exprs: &[ProofExpr]) -> Vec<String> {
4067 let mut constants = Vec::new();
4068 for expr in exprs {
4069 self.collect_skolem_constants_from_expr(expr, &mut constants);
4070 }
4071 constants.sort();
4072 constants.dedup();
4073 constants
4074 }
4075
4076 fn collect_skolem_constants_from_expr(&self, expr: &ProofExpr, constants: &mut Vec<String>) {
4078 match expr {
4079 ProofExpr::Predicate { args, .. } => {
4080 for arg in args {
4081 self.collect_skolem_constants_from_term(arg, constants);
4082 }
4083 }
4084 ProofExpr::And(l, r) | ProofExpr::Or(l, r) | ProofExpr::Implies(l, r) | ProofExpr::Iff(l, r) => {
4085 self.collect_skolem_constants_from_expr(l, constants);
4086 self.collect_skolem_constants_from_expr(r, constants);
4087 }
4088 ProofExpr::Not(inner) => {
4089 self.collect_skolem_constants_from_expr(inner, constants);
4090 }
4091 ProofExpr::Identity(l, r) => {
4092 self.collect_skolem_constants_from_term(l, constants);
4093 self.collect_skolem_constants_from_term(r, constants);
4094 }
4095 ProofExpr::NeoEvent { roles, .. } => {
4096 for (_, term) in roles {
4097 self.collect_skolem_constants_from_term(term, constants);
4098 }
4099 }
4100 _ => {}
4101 }
4102 }
4103
4104 fn collect_unified_constants(&self, expr: &ProofExpr, constants: &mut Vec<String>) {
4108 match expr {
4109 ProofExpr::Predicate { args, .. } => {
4110 for arg in args {
4111 if let ProofTerm::Constant(name) = arg {
4112 if name.starts_with("the_") && !constants.contains(name) {
4113 constants.push(name.clone());
4114 }
4115 }
4116 }
4117 }
4118 ProofExpr::And(left, right) | ProofExpr::Or(left, right) |
4119 ProofExpr::Implies(left, right) | ProofExpr::Iff(left, right) => {
4120 self.collect_unified_constants(left, constants);
4121 self.collect_unified_constants(right, constants);
4122 }
4123 ProofExpr::Not(inner) => self.collect_unified_constants(inner, constants),
4124 ProofExpr::ForAll { body, .. } | ProofExpr::Exists { body, .. } => {
4125 self.collect_unified_constants(body, constants);
4126 }
4127 ProofExpr::Identity(t1, t2) => {
4128 if let ProofTerm::Constant(name) = t1 {
4129 if name.starts_with("the_") && !constants.contains(name) {
4130 constants.push(name.clone());
4131 }
4132 }
4133 if let ProofTerm::Constant(name) = t2 {
4134 if name.starts_with("the_") && !constants.contains(name) {
4135 constants.push(name.clone());
4136 }
4137 }
4138 }
4139 _ => {}
4140 }
4141 }
4142
4143 fn collect_skolem_constants_from_term(&self, term: &ProofTerm, constants: &mut Vec<String>) {
4144 match term {
4145 ProofTerm::Constant(name) if name.starts_with("sk_") => {
4146 constants.push(name.clone());
4147 }
4148 ProofTerm::Function(_, args) => {
4149 for arg in args {
4150 self.collect_skolem_constants_from_term(arg, constants);
4151 }
4152 }
4153 ProofTerm::Group(terms) => {
4154 for t in terms {
4155 self.collect_skolem_constants_from_term(t, constants);
4156 }
4157 }
4158 _ => {}
4159 }
4160 }
4161
4162 fn instantiate_universals_with_constants(
4164 &self,
4165 context: &[ProofExpr],
4166 constants: &[String],
4167 ) -> Vec<ProofExpr> {
4168 let mut results = Vec::new();
4169
4170 for expr in context {
4171 if let ProofExpr::ForAll { variable, body } = expr {
4172 for constant in constants {
4173 let mut subst = Substitution::new();
4174 subst.insert(variable.clone(), ProofTerm::Constant(constant.clone()));
4175 let instantiated = apply_subst_to_expr(body, &subst);
4176 results.push(instantiated);
4177 }
4178 }
4179 }
4180
4181 results
4182 }
4183
4184 fn instantiate_kb_universals_with_constants(&self, constants: &[String]) -> Vec<ProofExpr> {
4186 let mut results = Vec::new();
4187
4188 for expr in &self.knowledge_base {
4189 if let ProofExpr::ForAll { variable, body } = expr {
4190 for constant in constants {
4191 let mut subst = Substitution::new();
4192 subst.insert(variable.clone(), ProofTerm::Constant(constant.clone()));
4193 let instantiated = apply_subst_to_expr(body, &subst);
4194 results.push(instantiated);
4195 }
4196 }
4197 }
4198
4199 results
4200 }
4201
4202 fn try_case_analysis_contradiction(
4214 &mut self,
4215 context: &[ProofExpr],
4216 skolem_constants: &[String],
4217 depth: usize,
4218 ) -> ProofResult<Option<DerivationTree>> {
4219 let candidates = self.find_case_split_candidates(context, skolem_constants);
4222
4223 for candidate in candidates {
4224 let mut context_with_pos = context.to_vec();
4226 if !context_with_pos.contains(&candidate) {
4227 context_with_pos.push(candidate.clone());
4228 }
4229
4230 let negated = ProofExpr::Not(Box::new(candidate.clone()));
4232 let mut context_with_neg = context.to_vec();
4233 if !context_with_neg.contains(&negated) {
4234 context_with_neg.push(negated.clone());
4235 }
4236
4237 let case1_contradiction = self.find_contradiction(&context_with_pos, depth)?;
4239 let case2_contradiction = self.find_contradiction(&context_with_neg, depth)?;
4240
4241 if let (Some(case1_proof), Some(case2_proof)) = (case1_contradiction, case2_contradiction) {
4243 let case1_tree = DerivationTree::new(
4245 ProofExpr::Atom("⊥".into()),
4246 InferenceRule::PremiseMatch,
4247 vec![case1_proof],
4248 );
4249 let case2_tree = DerivationTree::new(
4250 ProofExpr::Atom("⊥".into()),
4251 InferenceRule::PremiseMatch,
4252 vec![case2_proof],
4253 );
4254
4255 return Ok(Some(DerivationTree::new(
4256 ProofExpr::Atom("⊥".into()),
4257 InferenceRule::CaseAnalysis {
4258 case_formula: Box::new(candidate.clone()),
4259 },
4260 vec![case1_tree, case2_tree],
4261 )));
4262 }
4263 }
4264
4265 Ok(None)
4266 }
4267
4268 fn find_case_split_candidates(
4274 &self,
4275 context: &[ProofExpr],
4276 skolem_constants: &[String],
4277 ) -> Vec<ProofExpr> {
4278 let mut candidates = Vec::new();
4279
4280 for expr in context {
4282 if let ProofExpr::Predicate { name, args, world } = expr {
4283 if args.len() == 2 {
4285 if let (ProofTerm::Constant(c1), ProofTerm::Constant(c2)) = (&args[0], &args[1]) {
4286 if c1 == c2 && skolem_constants.contains(c1) {
4287 candidates.push(expr.clone());
4288 }
4289 }
4290 }
4291 }
4292 }
4293
4294 let implications: Vec<(ProofExpr, ProofExpr)> = context.iter()
4297 .chain(self.knowledge_base.iter())
4298 .filter_map(|e| {
4299 if let ProofExpr::Implies(ante, cons) = e {
4300 Some(((**ante).clone(), (**cons).clone()))
4301 } else {
4302 None
4303 }
4304 })
4305 .collect();
4306
4307 for (ante, cons) in &implications {
4308 if let ProofExpr::Not(inner) = cons {
4310 if exprs_structurally_equal(ante, inner) {
4311 let neg_ante = ProofExpr::Not(Box::new(ante.clone()));
4313 for (a2, c2) in &implications {
4314 if exprs_structurally_equal(a2, &neg_ante) && exprs_structurally_equal(c2, ante) {
4315 if !candidates.contains(ante) {
4317 candidates.push(ante.clone());
4318 }
4319 }
4320 }
4321 }
4322 }
4323 }
4324
4325 for const_name in skolem_constants {
4328 for expr in context.iter().chain(self.knowledge_base.iter()) {
4330 if let ProofExpr::Implies(ante, cons) = expr {
4331 self.extract_predicate_template(cons, const_name, &mut candidates);
4333 }
4334 }
4335 }
4336
4337 candidates
4338 }
4339
4340 fn extract_predicate_template(
4342 &self,
4343 expr: &ProofExpr,
4344 skolem: &str,
4345 candidates: &mut Vec<ProofExpr>,
4346 ) {
4347 match expr {
4348 ProofExpr::Predicate { name, args, world } if args.len() == 2 => {
4349 let self_ref = ProofExpr::Predicate {
4351 name: name.clone(),
4352 args: vec![
4353 ProofTerm::Constant(skolem.to_string()),
4354 ProofTerm::Constant(skolem.to_string()),
4355 ],
4356 world: world.clone(),
4357 };
4358 if !candidates.contains(&self_ref) {
4359 candidates.push(self_ref);
4360 }
4361 }
4362 ProofExpr::Not(inner) => {
4363 self.extract_predicate_template(inner, skolem, candidates);
4364 }
4365 ProofExpr::NeoEvent { verb, .. } => {
4366 let self_ref = ProofExpr::Predicate {
4368 name: verb.to_lowercase(),
4369 args: vec![
4370 ProofTerm::Constant(skolem.to_string()),
4371 ProofTerm::Constant(skolem.to_string()),
4372 ],
4373 world: None,
4374 };
4375 if !candidates.contains(&self_ref) {
4376 candidates.push(self_ref);
4377 }
4378 }
4379 _ => {}
4380 }
4381 }
4382
4383 fn try_existential_elimination(
4395 &mut self,
4396 goal: &ProofGoal,
4397 depth: usize,
4398 ) -> ProofResult<Option<DerivationTree>> {
4399 if depth > 8 {
4401 return Ok(None);
4402 }
4403
4404 let existentials: Vec<ProofExpr> = self.knowledge_base.iter()
4406 .chain(goal.context.iter())
4407 .filter(|e| matches!(e, ProofExpr::Exists { .. }))
4408 .cloned()
4409 .collect();
4410
4411 if existentials.is_empty() {
4412 return Ok(None);
4413 }
4414
4415 for exist_expr in existentials {
4417 if self
4421 .eliminated_existentials
4422 .iter()
4423 .any(|e| exprs_structurally_equal(e, &exist_expr))
4424 {
4425 continue;
4426 }
4427
4428 let Some((witness_const, witness_facts)) =
4432 self.skolemize_existential_with_const(&exist_expr)
4433 else {
4434 continue;
4435 };
4436
4437 if witness_facts.is_empty() {
4438 continue;
4439 }
4440
4441 let abstracted_facts: Vec<ProofExpr> = witness_facts.iter()
4443 .map(|f| self.abstract_all_events(f))
4444 .collect();
4445
4446 let mut extended_context = goal.context.clone();
4448 for fact in &abstracted_facts {
4449 if !extended_context.contains(fact) {
4450 extended_context.push(fact.clone());
4451 }
4452 }
4453
4454 for fact in &witness_facts {
4456 if !extended_context.contains(fact) {
4457 extended_context.push(fact.clone());
4458 }
4459 }
4460
4461 let extended_goal = ProofGoal::with_context(goal.target.clone(), extended_context);
4463
4464 self.eliminated_existentials.push(exist_expr.clone());
4467 let inner_result = self.prove_goal(extended_goal, depth + 1);
4468 self.eliminated_existentials.pop();
4469
4470 if let Ok(inner_proof) = inner_result {
4471 let existential_premise = DerivationTree::leaf(
4477 exist_expr.clone(),
4478 InferenceRule::PremiseMatch,
4479 );
4480
4481 return Ok(Some(DerivationTree::new(
4482 goal.target.clone(),
4483 InferenceRule::ExistentialElim { witness: witness_const },
4484 vec![existential_premise, inner_proof],
4485 )));
4486 }
4487 }
4488
4489 Ok(None)
4490 }
4491
4492 fn contains_quantifier(&self, expr: &ProofExpr) -> bool {
4494 match expr {
4495 ProofExpr::ForAll { .. } | ProofExpr::Exists { .. } => true,
4496 ProofExpr::And(l, r)
4497 | ProofExpr::Or(l, r)
4498 | ProofExpr::Implies(l, r)
4499 | ProofExpr::Iff(l, r) => self.contains_quantifier(l) || self.contains_quantifier(r),
4500 ProofExpr::Not(inner) => self.contains_quantifier(inner),
4501 _ => false,
4502 }
4503 }
4504
4505 fn skolemize_existential(&mut self, expr: &ProofExpr) -> Vec<ProofExpr> {
4511 let mut results = Vec::new();
4512
4513 if let ProofExpr::Exists { variable, body } = expr {
4514 let skolem = format!("sk_{}", self.fresh_var());
4516
4517 let mut subst = Substitution::new();
4519 subst.insert(variable.clone(), ProofTerm::Constant(skolem.clone()));
4520
4521 let instantiated = apply_subst_to_expr(body, &subst);
4522
4523 self.flatten_conjunction(&instantiated, &mut results);
4525
4526 let mut i = 0;
4528 while i < results.len() {
4529 if let ProofExpr::Exists { .. } = &results[i] {
4530 let nested = results.remove(i);
4531 let nested_skolem = self.skolemize_existential(&nested);
4532 results.extend(nested_skolem);
4533 } else {
4534 i += 1;
4535 }
4536 }
4537 }
4538
4539 results
4540 }
4541
4542 fn skolemize_existential_with_const(&mut self, expr: &ProofExpr) -> Option<(String, Vec<ProofExpr>)> {
4553 if let ProofExpr::Exists { variable, body } = expr {
4554 let skolem = format!("sk_{}", self.fresh_var());
4555 let mut subst = Substitution::new();
4556 subst.insert(variable.clone(), ProofTerm::Constant(skolem.clone()));
4557 let instantiated = apply_subst_to_expr(body, &subst);
4558 let mut results = Vec::new();
4559 self.flatten_conjunction(&instantiated, &mut results);
4560 Some((skolem, results))
4561 } else {
4562 None
4563 }
4564 }
4565
4566 fn flatten_conjunction(&self, expr: &ProofExpr, results: &mut Vec<ProofExpr>) {
4568 match expr {
4569 ProofExpr::And(left, right) => {
4570 self.flatten_conjunction(left, results);
4571 self.flatten_conjunction(right, results);
4572 }
4573 other => results.push(other.clone()),
4574 }
4575 }
4576
4577 fn is_tautological_identity(&self, expr: &ProofExpr) -> bool {
4584 if let ProofExpr::Predicate { name, args, .. } = expr {
4585 args.len() == 1 && matches!(
4586 &args[0],
4587 ProofTerm::Constant(c) | ProofTerm::BoundVarRef(c) | ProofTerm::Variable(c) if c == name
4588 )
4589 } else {
4590 false
4591 }
4592 }
4593
4594 fn simplify_definite_description_conjunction(&self, expr: &ProofExpr) -> ProofExpr {
4597 match expr {
4598 ProofExpr::And(left, right) => {
4599 let left_simplified = self.simplify_definite_description_conjunction(left);
4601 let right_simplified = self.simplify_definite_description_conjunction(right);
4602
4603 if self.is_tautological_identity(&left_simplified) {
4605 return right_simplified;
4606 }
4607 if self.is_tautological_identity(&right_simplified) {
4608 return left_simplified;
4609 }
4610
4611 ProofExpr::And(
4612 Box::new(left_simplified),
4613 Box::new(right_simplified),
4614 )
4615 }
4616 ProofExpr::Or(left, right) => ProofExpr::Or(
4617 Box::new(self.simplify_definite_description_conjunction(left)),
4618 Box::new(self.simplify_definite_description_conjunction(right)),
4619 ),
4620 ProofExpr::Implies(left, right) => ProofExpr::Implies(
4621 Box::new(self.simplify_definite_description_conjunction(left)),
4622 Box::new(self.simplify_definite_description_conjunction(right)),
4623 ),
4624 ProofExpr::Iff(left, right) => ProofExpr::Iff(
4625 Box::new(self.simplify_definite_description_conjunction(left)),
4626 Box::new(self.simplify_definite_description_conjunction(right)),
4627 ),
4628 ProofExpr::Not(inner) => ProofExpr::Not(
4629 Box::new(self.simplify_definite_description_conjunction(inner)),
4630 ),
4631 ProofExpr::ForAll { variable, body } => ProofExpr::ForAll {
4632 variable: variable.clone(),
4633 body: Box::new(self.simplify_definite_description_conjunction(body)),
4634 },
4635 ProofExpr::Exists { variable, body } => ProofExpr::Exists {
4636 variable: variable.clone(),
4637 body: Box::new(self.simplify_definite_description_conjunction(body)),
4638 },
4639 ProofExpr::Temporal { operator, body } => ProofExpr::Temporal {
4640 operator: operator.clone(),
4641 body: Box::new(self.simplify_definite_description_conjunction(body)),
4642 },
4643 ProofExpr::TemporalBinary { operator, left, right } => ProofExpr::TemporalBinary {
4644 operator: operator.clone(),
4645 left: Box::new(self.simplify_definite_description_conjunction(left)),
4646 right: Box::new(self.simplify_definite_description_conjunction(right)),
4647 },
4648 _ => expr.clone(),
4649 }
4650 }
4651
4652 fn abstract_event_to_predicate(&self, expr: &ProofExpr) -> Option<ProofExpr> {
4663 match expr {
4664 ProofExpr::NeoEvent { verb, roles, .. } => {
4666 let agent = roles.iter()
4668 .find(|(role, _)| role == "Agent")
4669 .map(|(_, term)| term.clone());
4670
4671 let theme = roles.iter()
4672 .find(|(role, _)| role == "Theme" || role == "Patient")
4673 .map(|(_, term)| term.clone());
4674
4675 let mut args = Vec::new();
4677 if let Some(a) = agent {
4678 args.push(a);
4679 }
4680 if let Some(t) = theme {
4681 args.push(t);
4682 }
4683
4684 let pred_name = verb.to_lowercase();
4686
4687 Some(ProofExpr::Predicate {
4688 name: pred_name,
4689 args,
4690 world: None,
4691 })
4692 }
4693
4694 ProofExpr::Exists { variable, body } => {
4696 if !self.is_event_variable(variable) {
4698 return None;
4699 }
4700
4701 if let Some(abstracted) = self.abstract_event_to_predicate(body) {
4703 return Some(abstracted);
4704 }
4705
4706 if let Some(abstracted) = self.abstract_event_conjunction(variable, body) {
4709 return Some(abstracted);
4710 }
4711
4712 None
4713 }
4714
4715 _ => None,
4716 }
4717 }
4718
4719 fn abstract_event_conjunction(&self, event_var: &str, body: &ProofExpr) -> Option<ProofExpr> {
4723 let mut components = Vec::new();
4725 self.flatten_conjunction(body, &mut components);
4726
4727 let mut verb_name: Option<String> = None;
4729 let mut agent: Option<ProofTerm> = None;
4730 let mut theme: Option<ProofTerm> = None;
4731
4732 for comp in &components {
4733 if let ProofExpr::Predicate { name, args, .. } = comp {
4734 let first_is_event = args.first().map_or(false, |arg| {
4736 matches!(arg, ProofTerm::Variable(v) | ProofTerm::BoundVarRef(v) if v == event_var)
4737 });
4738
4739 if !first_is_event && args.len() == 1 {
4740 if let Some(ProofTerm::Variable(v) | ProofTerm::BoundVarRef(v)) = args.first() {
4742 if v == event_var {
4743 verb_name = Some(name.clone());
4744 continue;
4745 }
4746 }
4747 }
4748
4749 if first_is_event {
4750 match name.as_str() {
4751 "Agent" if args.len() == 2 => {
4752 agent = Some(args[1].clone());
4753 }
4754 "Theme" | "Patient" if args.len() == 2 => {
4755 theme = Some(args[1].clone());
4756 }
4757 _ if args.len() == 1 && verb_name.is_none() => {
4758 verb_name = Some(name.clone());
4760 }
4761 _ => {}
4762 }
4763 }
4764 }
4765 }
4766
4767 if let Some(verb) = verb_name {
4769 let mut args = Vec::new();
4770 if let Some(a) = agent {
4771 args.push(a);
4772 }
4773 if let Some(t) = theme {
4774 args.push(t);
4775 }
4776
4777 return Some(ProofExpr::Predicate {
4778 name: verb.to_lowercase(),
4779 args,
4780 world: None,
4781 });
4782 }
4783
4784 None
4785 }
4786
4787 fn is_event_variable(&self, var: &str) -> bool {
4791 var == "e" || var.starts_with("e_") ||
4792 (var.starts_with('e') && var.len() == 2 && var.chars().nth(1).map_or(false, |c| c.is_ascii_digit()))
4793 }
4794
4795 pub(crate) fn abstract_all_events(&self, expr: &ProofExpr) -> ProofExpr {
4802 if let Some(abstracted) = self.abstract_event_to_predicate(expr) {
4804 return abstracted;
4805 }
4806
4807 match expr {
4809 ProofExpr::And(left, right) => ProofExpr::And(
4810 Box::new(self.abstract_all_events(left)),
4811 Box::new(self.abstract_all_events(right)),
4812 ),
4813 ProofExpr::Or(left, right) => ProofExpr::Or(
4814 Box::new(self.abstract_all_events(left)),
4815 Box::new(self.abstract_all_events(right)),
4816 ),
4817 ProofExpr::Implies(left, right) => ProofExpr::Implies(
4818 Box::new(self.abstract_all_events(left)),
4819 Box::new(self.abstract_all_events(right)),
4820 ),
4821 ProofExpr::Iff(left, right) => ProofExpr::Iff(
4822 Box::new(self.abstract_all_events(left)),
4823 Box::new(self.abstract_all_events(right)),
4824 ),
4825 ProofExpr::Not(inner) => {
4826 if let ProofExpr::Exists { variable, body } = inner.as_ref() {
4830 return ProofExpr::ForAll {
4831 variable: variable.clone(),
4832 body: Box::new(self.abstract_all_events(&ProofExpr::Not(body.clone()))),
4833 };
4834 }
4835 ProofExpr::Not(Box::new(self.abstract_all_events(inner)))
4838 }
4839 ProofExpr::ForAll { variable, body } => {
4840 if let ProofExpr::Not(inner) = body.as_ref() {
4843 if let ProofExpr::And(left, right) = inner.as_ref() {
4844 return ProofExpr::ForAll {
4845 variable: variable.clone(),
4846 body: Box::new(ProofExpr::Implies(
4847 Box::new(self.abstract_all_events(left)),
4848 Box::new(self.abstract_all_events(&ProofExpr::Not(right.clone()))),
4849 )),
4850 };
4851 }
4852 }
4853 ProofExpr::ForAll {
4854 variable: variable.clone(),
4855 body: Box::new(self.abstract_all_events(body)),
4856 }
4857 }
4858 ProofExpr::Exists { variable, body } => {
4859 if self.is_event_variable(variable) {
4861 if let Some(abstracted) = self.abstract_event_to_predicate(body) {
4862 return abstracted;
4863 }
4864 }
4865 ProofExpr::Exists {
4867 variable: variable.clone(),
4868 body: Box::new(self.abstract_all_events(body)),
4869 }
4870 }
4871 other => other.clone(),
4873 }
4874 }
4875
4876 fn abstract_events_only(&self, expr: &ProofExpr) -> ProofExpr {
4881 if let Some(abstracted) = self.abstract_event_to_predicate(expr) {
4883 return abstracted;
4884 }
4885
4886 match expr {
4888 ProofExpr::And(left, right) => ProofExpr::And(
4889 Box::new(self.abstract_events_only(left)),
4890 Box::new(self.abstract_events_only(right)),
4891 ),
4892 ProofExpr::Or(left, right) => ProofExpr::Or(
4893 Box::new(self.abstract_events_only(left)),
4894 Box::new(self.abstract_events_only(right)),
4895 ),
4896 ProofExpr::Implies(left, right) => ProofExpr::Implies(
4897 Box::new(self.abstract_events_only(left)),
4898 Box::new(self.abstract_events_only(right)),
4899 ),
4900 ProofExpr::Iff(left, right) => ProofExpr::Iff(
4901 Box::new(self.abstract_events_only(left)),
4902 Box::new(self.abstract_events_only(right)),
4903 ),
4904 ProofExpr::Not(inner) => {
4905 ProofExpr::Not(Box::new(self.abstract_events_only(inner)))
4907 }
4908 ProofExpr::ForAll { variable, body } => ProofExpr::ForAll {
4909 variable: variable.clone(),
4910 body: Box::new(self.abstract_events_only(body)),
4911 },
4912 ProofExpr::Exists { variable, body } => {
4913 if self.is_event_variable(variable) {
4915 if let Some(abstracted) = self.abstract_event_to_predicate(body) {
4916 return abstracted;
4917 }
4918 }
4919 ProofExpr::Exists {
4921 variable: variable.clone(),
4922 body: Box::new(self.abstract_events_only(body)),
4923 }
4924 }
4925 other => other.clone(),
4927 }
4928 }
4929
4930 fn find_certifiable_contradiction(
4946 &self,
4947 context: &[ProofExpr],
4948 _depth: usize,
4949 ) -> ProofResult<Option<DerivationTree>> {
4950 let all: Vec<ProofExpr> = self
4951 .knowledge_base
4952 .iter()
4953 .chain(context.iter())
4954 .cloned()
4955 .collect();
4956 let rules = cert_extract_rules(&all);
4957 let seed = cert_seed_facts(&all);
4958 let mut fresh = 0u32;
4962 Ok(cert_derive_falsum(&rules, &seed, 6, &mut fresh))
4963 }
4964
4965 fn find_contradiction(
4966 &mut self,
4967 context: &[ProofExpr],
4968 depth: usize,
4969 ) -> ProofResult<Option<DerivationTree>> {
4970 let all_exprs: Vec<ProofExpr> = self.knowledge_base.iter()
4972 .chain(context.iter())
4973 .cloned()
4974 .collect();
4975
4976 for expr in &all_exprs {
4978 if let ProofExpr::Not(inner) = expr {
4979 for other in &all_exprs {
4981 if exprs_structurally_equal(other, inner) {
4982 let pos_leaf = DerivationTree::leaf(
4984 (**inner).clone(),
4985 InferenceRule::PremiseMatch,
4986 );
4987 let neg_leaf = DerivationTree::leaf(
4988 expr.clone(),
4989 InferenceRule::PremiseMatch,
4990 );
4991 return Ok(Some(DerivationTree::new(
4992 ProofExpr::Atom("⊥".into()),
4993 InferenceRule::Contradiction,
4994 vec![pos_leaf, neg_leaf],
4995 )));
4996 }
4997 }
4998 }
4999 }
5000
5001 let mut implications: Vec<(ProofExpr, ProofExpr)> = Vec::new();
5006 for e in &all_exprs {
5007 if let ProofExpr::Implies(ante, cons) = e {
5008 implications.push(((**ante).clone(), (**cons).clone()));
5009 }
5010 if let ProofExpr::ForAll { body, .. } = e {
5012 if let ProofExpr::Implies(ante, cons) = body.as_ref() {
5013 implications.push(((**ante).clone(), (**cons).clone()));
5014 }
5015 }
5016 }
5017
5018 for fact in context {
5020 let mut derivable_consequences: Vec<ProofExpr> = Vec::new();
5022
5023 for (ante, cons) in &implications {
5024 if let Ok(subst) = unify_exprs(fact, ante) {
5026 let instantiated_cons = apply_subst_to_expr(cons, &subst);
5027 derivable_consequences.push(instantiated_cons);
5028 }
5029
5030 if let ProofExpr::And(left, right) = ante {
5032 if let Some(subst) = self.try_match_conjunction_antecedent(
5034 left, right, &all_exprs
5035 ) {
5036 let instantiated_cons = apply_subst_to_expr(cons, &subst);
5037 if !derivable_consequences.contains(&instantiated_cons) {
5038 derivable_consequences.push(instantiated_cons);
5039 }
5040 }
5041 }
5042 }
5043
5044 for cons in &derivable_consequences {
5046 if let ProofExpr::Not(inner) = cons {
5048 if exprs_structurally_equal(inner, fact) {
5049 let pos_leaf = DerivationTree::leaf(
5052 fact.clone(),
5053 InferenceRule::PremiseMatch,
5054 );
5055 let neg_leaf = DerivationTree::leaf(
5056 cons.clone(),
5057 InferenceRule::ModusPonens,
5058 );
5059 return Ok(Some(DerivationTree::new(
5060 ProofExpr::Atom("⊥".into()),
5061 InferenceRule::Contradiction,
5062 vec![pos_leaf, neg_leaf],
5063 )));
5064 }
5065 }
5066
5067 for other in context {
5069 if std::ptr::eq(fact as *const _, other as *const _) {
5070 continue; }
5072 if let ProofExpr::Not(inner) = cons {
5074 if exprs_structurally_equal(inner, other) {
5075 let pos_leaf = DerivationTree::leaf(
5076 other.clone(),
5077 InferenceRule::PremiseMatch,
5078 );
5079 let neg_leaf = DerivationTree::leaf(
5080 cons.clone(),
5081 InferenceRule::ModusPonens,
5082 );
5083 return Ok(Some(DerivationTree::new(
5084 ProofExpr::Atom("⊥".into()),
5085 InferenceRule::Contradiction,
5086 vec![pos_leaf, neg_leaf],
5087 )));
5088 }
5089 }
5090 if let ProofExpr::Not(inner_other) = other {
5092 if exprs_structurally_equal(inner_other, cons) {
5093 let pos_leaf = DerivationTree::leaf(
5094 cons.clone(),
5095 InferenceRule::ModusPonens,
5096 );
5097 let neg_leaf = DerivationTree::leaf(
5098 other.clone(),
5099 InferenceRule::PremiseMatch,
5100 );
5101 return Ok(Some(DerivationTree::new(
5102 ProofExpr::Atom("⊥".into()),
5103 InferenceRule::Contradiction,
5104 vec![pos_leaf, neg_leaf],
5105 )));
5106 }
5107 }
5108 }
5109 }
5110
5111 for i in 0..derivable_consequences.len() {
5113 for j in (i + 1)..derivable_consequences.len() {
5114 let cons1 = &derivable_consequences[i];
5115 let cons2 = &derivable_consequences[j];
5116
5117 if let ProofExpr::Not(inner1) = cons1 {
5119 if exprs_structurally_equal(inner1, cons2) {
5120 let pos_leaf = DerivationTree::leaf(
5122 cons2.clone(),
5123 InferenceRule::ModusPonens,
5124 );
5125 let neg_leaf = DerivationTree::leaf(
5126 cons1.clone(),
5127 InferenceRule::ModusPonens,
5128 );
5129 return Ok(Some(DerivationTree::new(
5130 ProofExpr::Atom("⊥".into()),
5131 InferenceRule::Contradiction,
5132 vec![pos_leaf, neg_leaf],
5133 )));
5134 }
5135 }
5136 if let ProofExpr::Not(inner2) = cons2 {
5137 if exprs_structurally_equal(inner2, cons1) {
5138 let pos_leaf = DerivationTree::leaf(
5140 cons1.clone(),
5141 InferenceRule::ModusPonens,
5142 );
5143 let neg_leaf = DerivationTree::leaf(
5144 cons2.clone(),
5145 InferenceRule::ModusPonens,
5146 );
5147 return Ok(Some(DerivationTree::new(
5148 ProofExpr::Atom("⊥".into()),
5149 InferenceRule::Contradiction,
5150 vec![pos_leaf, neg_leaf],
5151 )));
5152 }
5153 }
5154 }
5155 }
5156 }
5157
5158 if let Some(proof) = self.find_self_referential_contradiction(context, depth)? {
5160 return Ok(Some(proof));
5161 }
5162
5163 Ok(None)
5164 }
5165
5166 fn try_match_conjunction_antecedent(
5171 &self,
5172 left: &ProofExpr,
5173 right: &ProofExpr,
5174 facts: &[ProofExpr],
5175 ) -> Option<Substitution> {
5176 for fact1 in facts {
5178 if let Ok(subst1) = unify_exprs(fact1, left) {
5179 let instantiated_right = apply_subst_to_expr(right, &subst1);
5181 for fact2 in facts {
5183 if let Ok(subst2) = unify_exprs(fact2, &instantiated_right) {
5184 let mut combined = subst1.clone();
5186 for (k, v) in subst2.iter() {
5187 combined.insert(k.clone(), v.clone());
5188 }
5189 return Some(combined);
5190 }
5191 }
5192 }
5193 }
5194 for fact1 in facts {
5196 if let Ok(subst1) = unify_exprs(fact1, right) {
5197 let instantiated_left = apply_subst_to_expr(left, &subst1);
5198 for fact2 in facts {
5199 if let Ok(subst2) = unify_exprs(fact2, &instantiated_left) {
5200 let mut combined = subst1.clone();
5201 for (k, v) in subst2.iter() {
5202 combined.insert(k.clone(), v.clone());
5203 }
5204 return Some(combined);
5205 }
5206 }
5207 }
5208 }
5209 None
5210 }
5211
5212 fn find_self_referential_contradiction(
5220 &mut self,
5221 context: &[ProofExpr],
5222 _depth: usize,
5223 ) -> ProofResult<Option<DerivationTree>> {
5224 let all_exprs: Vec<ProofExpr> = self.knowledge_base.iter()
5226 .chain(context.iter())
5227 .cloned()
5228 .collect();
5229
5230 for expr1 in &all_exprs {
5233 if let ProofExpr::ForAll { variable: var1, body: body1 } = expr1 {
5234 if let ProofExpr::Implies(ante1, cons1) = body1.as_ref() {
5235 for expr2 in &all_exprs {
5236 if std::ptr::eq(expr1, expr2) {
5237 continue; }
5239 if let ProofExpr::ForAll { variable: var2, body: body2 } = expr2 {
5240 if let ProofExpr::Implies(ante2, cons2) = body2.as_ref() {
5241 if let ProofExpr::Not(neg_cons2) = cons2.as_ref() {
5243 let witnesses = self.extract_constants_from_expr(cons1);
5249
5250 for witness_name in &witnesses {
5251 let witness = ProofTerm::Constant(witness_name.clone());
5252
5253 let mut subst1 = Substitution::new();
5255 subst1.insert(var1.clone(), witness.clone());
5256 let ante1_inst = apply_subst_to_expr(ante1, &subst1);
5257 let cons1_inst = apply_subst_to_expr(cons1, &subst1);
5258
5259 let mut subst2 = Substitution::new();
5260 subst2.insert(var2.clone(), witness.clone());
5261 let ante2_inst = apply_subst_to_expr(ante2, &subst2);
5262 let cons2_inst = apply_subst_to_expr(cons2, &subst2);
5263
5264 if let ProofExpr::Not(inner2) = &cons2_inst {
5267 if exprs_structurally_equal(&cons1_inst, inner2) {
5268 if self.are_complements(&ante1_inst, &ante2_inst) {
5280 let pos_leaf = DerivationTree::leaf(
5284 cons1_inst.clone(),
5285 InferenceRule::ModusPonens,
5286 );
5287 let neg_leaf = DerivationTree::leaf(
5288 cons2_inst,
5289 InferenceRule::ModusPonens,
5290 );
5291 return Ok(Some(DerivationTree::new(
5292 ProofExpr::Atom("⊥".into()),
5293 InferenceRule::Contradiction,
5294 vec![pos_leaf, neg_leaf],
5295 )));
5296 }
5297 }
5298 }
5299 }
5300 }
5301 }
5302 }
5303 }
5304 }
5305 }
5306 }
5307
5308 Ok(None)
5309 }
5310
5311 fn are_complements(&self, expr1: &ProofExpr, expr2: &ProofExpr) -> bool {
5313 if let ProofExpr::Not(inner1) = expr1 {
5315 if exprs_structurally_equal(inner1, expr2) {
5316 return true;
5317 }
5318 }
5319 if let ProofExpr::Not(inner2) = expr2 {
5321 if exprs_structurally_equal(inner2, expr1) {
5322 return true;
5323 }
5324 }
5325 false
5326 }
5327
5328 fn extract_constants_from_expr(&self, expr: &ProofExpr) -> Vec<String> {
5330 let mut constants = Vec::new();
5331 self.extract_constants_recursive(expr, &mut constants);
5332 constants
5333 }
5334
5335 fn extract_constants_recursive(&self, expr: &ProofExpr, constants: &mut Vec<String>) {
5336 match expr {
5337 ProofExpr::Predicate { args, .. } => {
5338 for arg in args {
5339 self.extract_constants_from_term_recursive(arg, constants);
5340 }
5341 }
5342 ProofExpr::Identity(l, r) => {
5343 self.extract_constants_from_term_recursive(l, constants);
5344 self.extract_constants_from_term_recursive(r, constants);
5345 }
5346 ProofExpr::And(l, r)
5347 | ProofExpr::Or(l, r)
5348 | ProofExpr::Implies(l, r)
5349 | ProofExpr::Iff(l, r) => {
5350 self.extract_constants_recursive(l, constants);
5351 self.extract_constants_recursive(r, constants);
5352 }
5353 ProofExpr::Not(inner) => {
5354 self.extract_constants_recursive(inner, constants);
5355 }
5356 ProofExpr::ForAll { body, .. } | ProofExpr::Exists { body, .. } => {
5357 self.extract_constants_recursive(body, constants);
5358 }
5359 _ => {}
5360 }
5361 }
5362
5363 fn extract_constants_from_term_recursive(&self, term: &ProofTerm, constants: &mut Vec<String>) {
5364 match term {
5365 ProofTerm::Constant(name) => {
5366 if !constants.contains(name) {
5367 constants.push(name.clone());
5368 }
5369 }
5370 ProofTerm::Function(_, args) => {
5371 for arg in args {
5372 self.extract_constants_from_term_recursive(arg, constants);
5373 }
5374 }
5375 ProofTerm::Group(terms) => {
5376 for t in terms {
5377 self.extract_constants_from_term_recursive(t, constants);
5378 }
5379 }
5380 _ => {}
5381 }
5382 }
5383
5384 fn try_equality_rewrite(
5393 &mut self,
5394 goal: &ProofGoal,
5395 depth: usize,
5396 ) -> ProofResult<Option<DerivationTree>> {
5397 let equalities: Vec<(ProofTerm, ProofTerm)> = self
5399 .knowledge_base
5400 .iter()
5401 .chain(goal.context.iter())
5402 .filter_map(|expr| {
5403 if let ProofExpr::Identity(l, r) = expr {
5404 Some((l.clone(), r.clone()))
5405 } else {
5406 None
5407 }
5408 })
5409 .collect();
5410
5411 if equalities.is_empty() {
5412 return Ok(None);
5413 }
5414
5415 if let ProofExpr::Identity(goal_l, goal_r) = &goal.target {
5417 if let Some(tree) = self.try_equality_symmetry(goal_l, goal_r, &equalities, depth)? {
5419 return Ok(Some(tree));
5420 }
5421
5422 if let Some(tree) = self.try_equality_transitivity(goal_l, goal_r, &equalities, depth)? {
5424 return Ok(Some(tree));
5425 }
5426
5427 if depth + 3 < self.max_depth {
5430 if let Some(tree) = self.try_equational_identity_rewrite(goal, goal_l, goal_r, depth)? {
5431 return Ok(Some(tree));
5432 }
5433 }
5434
5435 if let Some(tree) = self.try_congruence(goal)? {
5438 return Ok(Some(tree));
5439 }
5440
5441 return Ok(None);
5442 }
5443
5444 for (eq_from, eq_to) in &equalities {
5446 if let Some(tree) = self.try_rewrite_with_equality(
5448 goal, eq_from, eq_to, depth,
5449 )? {
5450 return Ok(Some(tree));
5451 }
5452
5453 if let Some(tree) = self.try_rewrite_with_equality(
5455 goal, eq_to, eq_from, depth,
5456 )? {
5457 return Ok(Some(tree));
5458 }
5459 }
5460
5461 Ok(None)
5462 }
5463
5464 fn try_rewrite_with_equality(
5466 &mut self,
5467 goal: &ProofGoal,
5468 from: &ProofTerm,
5469 to: &ProofTerm,
5470 depth: usize,
5471 ) -> ProofResult<Option<DerivationTree>> {
5472 let source_goal = self.substitute_term_in_expr(&goal.target, to, from);
5475
5476 if source_goal == goal.target {
5478 return Ok(None);
5479 }
5480
5481 let source_proof_goal = ProofGoal::with_context(source_goal.clone(), goal.context.clone());
5483 if let Ok(source_proof) = self.prove_goal(source_proof_goal, depth + 1) {
5484 let equality = ProofExpr::Identity(from.clone(), to.clone());
5486 let eq_proof_goal = ProofGoal::with_context(equality.clone(), goal.context.clone());
5487
5488 if let Ok(eq_proof) = self.prove_goal(eq_proof_goal, depth + 1) {
5489 return Ok(Some(DerivationTree::new(
5490 goal.target.clone(),
5491 InferenceRule::Rewrite {
5492 from: from.clone(),
5493 to: to.clone(),
5494 },
5495 vec![eq_proof, source_proof],
5496 )));
5497 }
5498 }
5499
5500 Ok(None)
5501 }
5502
5503 fn try_equality_symmetry(
5505 &mut self,
5506 goal_l: &ProofTerm,
5507 goal_r: &ProofTerm,
5508 equalities: &[(ProofTerm, ProofTerm)],
5509 _depth: usize,
5510 ) -> ProofResult<Option<DerivationTree>> {
5511 for (eq_l, eq_r) in equalities {
5513 if eq_l == goal_r && eq_r == goal_l {
5514 let source = ProofExpr::Identity(goal_r.clone(), goal_l.clone());
5516 return Ok(Some(DerivationTree::new(
5517 ProofExpr::Identity(goal_l.clone(), goal_r.clone()),
5518 InferenceRule::EqualitySymmetry,
5519 vec![DerivationTree::leaf(source, InferenceRule::PremiseMatch)],
5520 )));
5521 }
5522 }
5523 Ok(None)
5524 }
5525
5526 fn try_congruence(&mut self, goal: &ProofGoal) -> ProofResult<Option<DerivationTree>> {
5531 let ProofExpr::Identity(lhs, rhs) = &goal.target else {
5532 return Ok(None);
5533 };
5534 let hyps: Vec<(ProofExpr, DerivationTree)> = self
5535 .knowledge_base
5536 .iter()
5537 .chain(goal.context.iter())
5538 .filter_map(|e| match e {
5539 ProofExpr::Identity(l, r) => Some((
5540 e.clone(),
5541 DerivationTree::leaf(
5542 ProofExpr::Identity(l.clone(), r.clone()),
5543 InferenceRule::PremiseMatch,
5544 ),
5545 )),
5546 _ => None,
5547 })
5548 .collect();
5549 if hyps.is_empty() {
5550 return Ok(None);
5551 }
5552 Ok(cert_congruence_path(lhs, rhs, &hyps))
5553 }
5554
5555 fn try_linarith(&mut self, goal: &ProofGoal, depth: usize) -> ProofResult<Option<DerivationTree>> {
5560 let Some((ga, gb)) = as_le_pair(&goal.target) else {
5561 return Ok(None);
5562 };
5563 if let (Some(av), Some(bv)) = (as_int_literal(&ga), as_int_literal(&gb)) {
5567 return Ok((av <= bv)
5568 .then(|| DerivationTree::leaf(goal.target.clone(), InferenceRule::Reflexivity)));
5569 }
5570 if let (ProofTerm::Function(fl, la), ProofTerm::Function(fr, ra)) = (&ga, &gb) {
5573 if fl == "add" && fr == "add" && la.len() == 2 && ra.len() == 2 {
5574 let g0 =
5575 ProofGoal::with_context(le_eq(la[0].clone(), ra[0].clone()), goal.context.clone());
5576 let g1 =
5577 ProofGoal::with_context(le_eq(la[1].clone(), ra[1].clone()), goal.context.clone());
5578 if let (Ok(p0), Ok(p1)) =
5579 (self.prove_goal(g0, depth + 1), self.prove_goal(g1, depth + 1))
5580 {
5581 return Ok(Some(DerivationTree::new(
5582 goal.target.clone(),
5583 InferenceRule::LeAddMono,
5584 vec![p0, p1],
5585 )));
5586 }
5587 }
5588 }
5589 let mut adj: std::collections::HashMap<String, Vec<(ProofTerm, DerivationTree)>> =
5590 std::collections::HashMap::new();
5591 for e in goal.context.iter().chain(self.knowledge_base.iter()) {
5592 if let Some((x, y)) = as_le_pair(e) {
5593 if let Some(xk) = term_skey(&x) {
5594 let proof = DerivationTree::leaf(e.clone(), InferenceRule::PremiseMatch);
5595 adj.entry(xk).or_default().push((y, proof));
5596 }
5597 }
5598 }
5599 Ok(cert_le_path(&ga, &gb, &adj))
5600 }
5601
5602 fn try_equality_transitivity(
5604 &mut self,
5605 goal_l: &ProofTerm,
5606 goal_r: &ProofTerm,
5607 equalities: &[(ProofTerm, ProofTerm)],
5608 _depth: usize,
5609 ) -> ProofResult<Option<DerivationTree>> {
5610 for (eq1_l, eq1_r) in equalities {
5612 if eq1_l == goal_l {
5613 for (eq2_l, eq2_r) in equalities {
5615 if eq2_l == eq1_r && eq2_r == goal_r {
5616 let premise1 = ProofExpr::Identity(eq1_l.clone(), eq1_r.clone());
5618 let premise2 = ProofExpr::Identity(eq2_l.clone(), eq2_r.clone());
5619 return Ok(Some(DerivationTree::new(
5620 ProofExpr::Identity(goal_l.clone(), goal_r.clone()),
5621 InferenceRule::EqualityTransitivity,
5622 vec![
5623 DerivationTree::leaf(premise1, InferenceRule::PremiseMatch),
5624 DerivationTree::leaf(premise2, InferenceRule::PremiseMatch),
5625 ],
5626 )));
5627 }
5628 }
5629 }
5630 }
5631 Ok(None)
5632 }
5633
5634 fn try_equational_identity_rewrite(
5639 &mut self,
5640 goal: &ProofGoal,
5641 goal_l: &ProofTerm,
5642 goal_r: &ProofTerm,
5643 depth: usize,
5644 ) -> ProofResult<Option<DerivationTree>> {
5645 if let (
5648 ProofTerm::Function(name_l, args_l),
5649 ProofTerm::Function(name_r, args_r),
5650 ) = (goal_l, goal_r)
5651 {
5652 if name_l == name_r && args_l.len() == args_r.len() {
5653 let mut arg_proofs: Vec<Option<DerivationTree>> = Vec::new();
5656 let mut all_ok = true;
5657 for (arg_l, arg_r) in args_l.iter().zip(args_r.iter()) {
5658 if arg_l == arg_r {
5659 arg_proofs.push(None);
5660 continue;
5661 }
5662 let arg_goal_expr = ProofExpr::Identity(arg_l.clone(), arg_r.clone());
5663 let arg_goal = ProofGoal::with_context(arg_goal_expr, goal.context.clone());
5664 match self.prove_goal(arg_goal, depth + 1) {
5665 Ok(proof) => arg_proofs.push(Some(proof)),
5666 Err(_) => {
5667 all_ok = false;
5668 break;
5669 }
5670 }
5671 }
5672 if all_ok {
5673 return Ok(Some(build_congruence_proof(
5677 name_l, args_l, args_r, &arg_proofs,
5678 )));
5679 }
5680 }
5681 }
5682 let axioms: Vec<(ProofTerm, ProofTerm)> = self
5684 .knowledge_base
5685 .iter()
5686 .filter_map(|e| {
5687 if let ProofExpr::Identity(l, r) = e {
5688 Some((l.clone(), r.clone()))
5689 } else {
5690 None
5691 }
5692 })
5693 .collect();
5694
5695 for (axiom_l, axiom_r) in &axioms {
5697 let mut var_map = std::collections::HashMap::new();
5699 let renamed_l = self.rename_term_vars_with_map(axiom_l, &mut var_map);
5700 let renamed_r = self.rename_term_vars_with_map(axiom_r, &mut var_map);
5701
5702 if let Ok(subst) = unify_terms(&renamed_l, goal_l) {
5706 let rewritten = self.apply_subst_to_term(&renamed_r, &subst);
5708
5709 if terms_structurally_equal(&rewritten, goal_r) {
5711 let axiom_expr = ProofExpr::Identity(axiom_l.clone(), axiom_r.clone());
5713 return Ok(Some(DerivationTree::new(
5714 goal.target.clone(),
5715 InferenceRule::Rewrite {
5716 from: goal_l.clone(),
5717 to: rewritten,
5718 },
5719 vec![DerivationTree::leaf(axiom_expr, InferenceRule::PremiseMatch)],
5720 )));
5721 }
5722
5723 let new_goal_expr = ProofExpr::Identity(rewritten.clone(), goal_r.clone());
5725 let new_goal = ProofGoal::with_context(new_goal_expr.clone(), goal.context.clone());
5726
5727 if let Ok(sub_proof) = self.prove_goal(new_goal, depth + 1) {
5729 let axiom_expr = ProofExpr::Identity(axiom_l.clone(), axiom_r.clone());
5731 return Ok(Some(DerivationTree::new(
5732 goal.target.clone(),
5733 InferenceRule::Rewrite {
5734 from: goal_l.clone(),
5735 to: rewritten,
5736 },
5737 vec![
5738 DerivationTree::leaf(axiom_expr, InferenceRule::PremiseMatch),
5739 sub_proof,
5740 ],
5741 )));
5742 }
5743 }
5744 }
5745
5746 Ok(None)
5747 }
5748
5749 fn rename_term_vars(&mut self, term: &ProofTerm) -> ProofTerm {
5751 let mut var_map = std::collections::HashMap::new();
5752 self.rename_term_vars_with_map(term, &mut var_map)
5753 }
5754
5755 fn rename_term_vars_with_map(
5756 &mut self,
5757 term: &ProofTerm,
5758 var_map: &mut std::collections::HashMap<String, String>,
5759 ) -> ProofTerm {
5760 match term {
5761 ProofTerm::Variable(name) => {
5762 if let Some(fresh) = var_map.get(name) {
5764 ProofTerm::Variable(fresh.clone())
5765 } else {
5766 let fresh = format!("_v{}", self.var_counter);
5768 self.var_counter += 1;
5769 var_map.insert(name.clone(), fresh.clone());
5770 ProofTerm::Variable(fresh)
5771 }
5772 }
5773 ProofTerm::Function(name, args) => {
5774 ProofTerm::Function(
5775 name.clone(),
5776 args.iter().map(|a| self.rename_term_vars_with_map(a, var_map)).collect(),
5777 )
5778 }
5779 ProofTerm::Group(terms) => {
5780 ProofTerm::Group(
5781 terms.iter().map(|t| self.rename_term_vars_with_map(t, var_map)).collect(),
5782 )
5783 }
5784 other => other.clone(),
5785 }
5786 }
5787
5788 fn apply_subst_to_term(&self, term: &ProofTerm, subst: &Substitution) -> ProofTerm {
5790 match term {
5791 ProofTerm::Variable(name) => {
5792 if let Some(replacement) = subst.get(name) {
5793 replacement.clone()
5794 } else {
5795 term.clone()
5796 }
5797 }
5798 ProofTerm::Function(name, args) => {
5799 ProofTerm::Function(
5800 name.clone(),
5801 args.iter().map(|a| self.apply_subst_to_term(a, subst)).collect(),
5802 )
5803 }
5804 ProofTerm::Group(terms) => {
5805 ProofTerm::Group(terms.iter().map(|t| self.apply_subst_to_term(t, subst)).collect())
5806 }
5807 other => other.clone(),
5808 }
5809 }
5810
5811 fn substitute_free_var_in_expr(
5817 &self,
5818 expr: &ProofExpr,
5819 var_name: &str,
5820 to: &ProofTerm,
5821 ) -> ProofExpr {
5822 fn in_term(term: &ProofTerm, var_name: &str, to: &ProofTerm) -> ProofTerm {
5823 match term {
5824 ProofTerm::Variable(v) if v == var_name => to.clone(),
5825 ProofTerm::Function(name, args) => ProofTerm::Function(
5826 name.clone(),
5827 args.iter().map(|a| in_term(a, var_name, to)).collect(),
5828 ),
5829 ProofTerm::Group(terms) => ProofTerm::Group(
5830 terms.iter().map(|t| in_term(t, var_name, to)).collect(),
5831 ),
5832 other => other.clone(),
5833 }
5834 }
5835
5836 match expr {
5837 ProofExpr::Predicate { name, args, world } => ProofExpr::Predicate {
5838 name: name.clone(),
5839 args: args.iter().map(|a| in_term(a, var_name, to)).collect(),
5840 world: world.clone(),
5841 },
5842 ProofExpr::Identity(l, r) => ProofExpr::Identity(
5843 in_term(l, var_name, to),
5844 in_term(r, var_name, to),
5845 ),
5846 ProofExpr::NeoEvent { event_var, verb, roles } => ProofExpr::NeoEvent {
5847 event_var: event_var.clone(),
5848 verb: verb.clone(),
5849 roles: roles
5850 .iter()
5851 .map(|(role, t)| (role.clone(), in_term(t, var_name, to)))
5852 .collect(),
5853 },
5854 ProofExpr::And(l, r) => ProofExpr::And(
5855 Box::new(self.substitute_free_var_in_expr(l, var_name, to)),
5856 Box::new(self.substitute_free_var_in_expr(r, var_name, to)),
5857 ),
5858 ProofExpr::Or(l, r) => ProofExpr::Or(
5859 Box::new(self.substitute_free_var_in_expr(l, var_name, to)),
5860 Box::new(self.substitute_free_var_in_expr(r, var_name, to)),
5861 ),
5862 ProofExpr::Implies(l, r) => ProofExpr::Implies(
5863 Box::new(self.substitute_free_var_in_expr(l, var_name, to)),
5864 Box::new(self.substitute_free_var_in_expr(r, var_name, to)),
5865 ),
5866 ProofExpr::Iff(l, r) => ProofExpr::Iff(
5867 Box::new(self.substitute_free_var_in_expr(l, var_name, to)),
5868 Box::new(self.substitute_free_var_in_expr(r, var_name, to)),
5869 ),
5870 ProofExpr::Not(inner) => ProofExpr::Not(Box::new(
5871 self.substitute_free_var_in_expr(inner, var_name, to),
5872 )),
5873 ProofExpr::ForAll { variable, body } if variable != var_name => ProofExpr::ForAll {
5874 variable: variable.clone(),
5875 body: Box::new(self.substitute_free_var_in_expr(body, var_name, to)),
5876 },
5877 ProofExpr::Exists { variable, body } if variable != var_name => ProofExpr::Exists {
5878 variable: variable.clone(),
5879 body: Box::new(self.substitute_free_var_in_expr(body, var_name, to)),
5880 },
5881 ProofExpr::Lambda { variable, body } if variable != var_name => ProofExpr::Lambda {
5882 variable: variable.clone(),
5883 body: Box::new(self.substitute_free_var_in_expr(body, var_name, to)),
5884 },
5885 ProofExpr::Modal { domain, force, flavor, body } => ProofExpr::Modal {
5886 domain: domain.clone(),
5887 force: *force,
5888 flavor: flavor.clone(),
5889 body: Box::new(self.substitute_free_var_in_expr(body, var_name, to)),
5890 },
5891 ProofExpr::Temporal { operator, body } => ProofExpr::Temporal {
5892 operator: operator.clone(),
5893 body: Box::new(self.substitute_free_var_in_expr(body, var_name, to)),
5894 },
5895 ProofExpr::TemporalBinary { operator, left, right } => ProofExpr::TemporalBinary {
5896 operator: operator.clone(),
5897 left: Box::new(self.substitute_free_var_in_expr(left, var_name, to)),
5898 right: Box::new(self.substitute_free_var_in_expr(right, var_name, to)),
5899 },
5900 other => other.clone(),
5902 }
5903 }
5904
5905 fn substitute_term_in_expr(
5906 &self,
5907 expr: &ProofExpr,
5908 from: &ProofTerm,
5909 to: &ProofTerm,
5910 ) -> ProofExpr {
5911 match expr {
5912 ProofExpr::Predicate { name, args, world } => {
5913 let new_args: Vec<_> = args
5914 .iter()
5915 .map(|arg| self.substitute_in_term(arg, from, to))
5916 .collect();
5917 ProofExpr::Predicate {
5918 name: name.clone(),
5919 args: new_args,
5920 world: world.clone(),
5921 }
5922 }
5923 ProofExpr::Identity(l, r) => ProofExpr::Identity(
5924 self.substitute_in_term(l, from, to),
5925 self.substitute_in_term(r, from, to),
5926 ),
5927 ProofExpr::And(l, r) => ProofExpr::And(
5928 Box::new(self.substitute_term_in_expr(l, from, to)),
5929 Box::new(self.substitute_term_in_expr(r, from, to)),
5930 ),
5931 ProofExpr::Or(l, r) => ProofExpr::Or(
5932 Box::new(self.substitute_term_in_expr(l, from, to)),
5933 Box::new(self.substitute_term_in_expr(r, from, to)),
5934 ),
5935 ProofExpr::Implies(l, r) => ProofExpr::Implies(
5936 Box::new(self.substitute_term_in_expr(l, from, to)),
5937 Box::new(self.substitute_term_in_expr(r, from, to)),
5938 ),
5939 ProofExpr::Not(inner) => {
5940 ProofExpr::Not(Box::new(self.substitute_term_in_expr(inner, from, to)))
5941 }
5942 ProofExpr::ForAll { variable, body } => ProofExpr::ForAll {
5943 variable: variable.clone(),
5944 body: Box::new(self.substitute_term_in_expr(body, from, to)),
5945 },
5946 ProofExpr::Exists { variable, body } => ProofExpr::Exists {
5947 variable: variable.clone(),
5948 body: Box::new(self.substitute_term_in_expr(body, from, to)),
5949 },
5950 other => other.clone(),
5952 }
5953 }
5954
5955 fn substitute_in_term(
5957 &self,
5958 term: &ProofTerm,
5959 from: &ProofTerm,
5960 to: &ProofTerm,
5961 ) -> ProofTerm {
5962 if term == from {
5963 return to.clone();
5964 }
5965 match term {
5966 ProofTerm::Function(name, args) => {
5967 let new_args: Vec<_> = args
5968 .iter()
5969 .map(|arg| self.substitute_in_term(arg, from, to))
5970 .collect();
5971 ProofTerm::Function(name.clone(), new_args)
5972 }
5973 ProofTerm::Group(terms) => {
5974 let new_terms: Vec<_> = terms
5975 .iter()
5976 .map(|t| self.substitute_in_term(t, from, to))
5977 .collect();
5978 ProofTerm::Group(new_terms)
5979 }
5980 other => other.clone(),
5981 }
5982 }
5983
5984 fn try_structural_induction(
5998 &mut self,
5999 goal: &ProofGoal,
6000 depth: usize,
6001 ) -> ProofResult<Option<DerivationTree>> {
6002 if let Some((var_name, typename)) = self.find_typed_var(&goal.target) {
6004 if let Some(motive) = self.try_infer_motive(&goal.target, &var_name) {
6006 match typename.as_str() {
6007 "Nat" => {
6008 if let Ok(Some(proof)) =
6009 self.try_nat_induction_with_motive(goal, &var_name, &motive, depth)
6010 {
6011 return Ok(Some(proof));
6012 }
6013 }
6014 "List" => {
6015 }
6017 _ => {}
6018 }
6019 }
6020
6021 match typename.as_str() {
6023 "Nat" => self.try_nat_induction(goal, &var_name, depth),
6024 "List" => self.try_list_induction(goal, &var_name, depth),
6025 _ => Ok(None), }
6027 } else {
6028 Ok(None)
6029 }
6030 }
6031
6032 fn try_infer_motive(&self, goal: &ProofExpr, var_name: &str) -> Option<ProofExpr> {
6037 let motive_hole = ProofExpr::Hole("Motive".to_string());
6039 let pattern = ProofExpr::App(
6040 Box::new(motive_hole),
6041 Box::new(ProofExpr::Term(ProofTerm::BoundVarRef(var_name.to_string()))),
6042 );
6043
6044 let body = self.convert_typed_var_to_variable(goal, var_name);
6046
6047 match unify_pattern(&pattern, &body) {
6049 Ok(solution) => solution.get("Motive").cloned(),
6050 Err(_) => None,
6051 }
6052 }
6053
6054 fn convert_typed_var_to_variable(&self, expr: &ProofExpr, var_name: &str) -> ProofExpr {
6059 match expr {
6060 ProofExpr::TypedVar { name, .. } if name == var_name => {
6061 ProofExpr::Atom(name.clone())
6063 }
6064 ProofExpr::Identity(l, r) => ProofExpr::Identity(
6065 self.convert_typed_var_in_term(l, var_name),
6066 self.convert_typed_var_in_term(r, var_name),
6067 ),
6068 ProofExpr::Predicate { name, args, world } => ProofExpr::Predicate {
6069 name: name.clone(),
6070 args: args
6071 .iter()
6072 .map(|a| self.convert_typed_var_in_term(a, var_name))
6073 .collect(),
6074 world: world.clone(),
6075 },
6076 ProofExpr::And(l, r) => ProofExpr::And(
6077 Box::new(self.convert_typed_var_to_variable(l, var_name)),
6078 Box::new(self.convert_typed_var_to_variable(r, var_name)),
6079 ),
6080 ProofExpr::Or(l, r) => ProofExpr::Or(
6081 Box::new(self.convert_typed_var_to_variable(l, var_name)),
6082 Box::new(self.convert_typed_var_to_variable(r, var_name)),
6083 ),
6084 ProofExpr::Not(inner) => {
6085 ProofExpr::Not(Box::new(self.convert_typed_var_to_variable(inner, var_name)))
6086 }
6087 _ => expr.clone(),
6088 }
6089 }
6090
6091 fn convert_typed_var_in_term(&self, term: &ProofTerm, var_name: &str) -> ProofTerm {
6093 match term {
6094 ProofTerm::Variable(v) => {
6095 if v == var_name || v.starts_with(&format!("{}:", var_name)) {
6097 ProofTerm::Variable(var_name.to_string())
6098 } else {
6099 term.clone()
6100 }
6101 }
6102 ProofTerm::Function(name, args) => ProofTerm::Function(
6103 name.clone(),
6104 args.iter()
6105 .map(|a| self.convert_typed_var_in_term(a, var_name))
6106 .collect(),
6107 ),
6108 ProofTerm::Group(terms) => ProofTerm::Group(
6109 terms
6110 .iter()
6111 .map(|t| self.convert_typed_var_in_term(t, var_name))
6112 .collect(),
6113 ),
6114 _ => term.clone(),
6115 }
6116 }
6117
6118 fn try_nat_induction_with_motive(
6126 &mut self,
6127 goal: &ProofGoal,
6128 var_name: &str,
6129 motive: &ProofExpr,
6130 depth: usize,
6131 ) -> ProofResult<Option<DerivationTree>> {
6132 let zero_ctor = ProofExpr::Ctor {
6135 name: "Zero".into(),
6136 args: vec![],
6137 };
6138 let base_goal_expr = beta_reduce(&ProofExpr::App(
6139 Box::new(motive.clone()),
6140 Box::new(zero_ctor),
6141 ));
6142
6143 let base_goal = ProofGoal::with_context(base_goal_expr, goal.context.clone());
6144 let base_proof = match self.prove_goal(base_goal, depth + 1) {
6145 Ok(proof) => proof,
6146 Err(_) => return Ok(None),
6147 };
6148
6149 let fresh_k = self.fresh_var();
6151 let k_var = ProofExpr::Atom(fresh_k.clone());
6152
6153 let ih = beta_reduce(&ProofExpr::App(
6155 Box::new(motive.clone()),
6156 Box::new(k_var.clone()),
6157 ));
6158
6159 let succ_k = ProofExpr::Ctor {
6161 name: "Succ".into(),
6162 args: vec![k_var],
6163 };
6164 let step_goal_expr = beta_reduce(&ProofExpr::App(
6165 Box::new(motive.clone()),
6166 Box::new(succ_k),
6167 ));
6168
6169 let mut step_context = goal.context.clone();
6171 step_context.push(ih.clone());
6172
6173 let step_goal = ProofGoal::with_context(step_goal_expr, step_context);
6174 let step_proof = match self.try_step_case_with_equational_reasoning(&step_goal, &ih, depth)
6175 {
6176 Ok(proof) => proof,
6177 Err(_) => return Ok(None),
6178 };
6179
6180 Ok(Some(DerivationTree::new(
6181 goal.target.clone(),
6182 InferenceRule::StructuralInduction {
6183 variable: var_name.to_string(),
6184 ind_type: "Nat".to_string(),
6185 step_var: fresh_k,
6186 },
6187 vec![base_proof, step_proof],
6188 )))
6189 }
6190
6191 fn try_nat_induction(
6196 &mut self,
6197 goal: &ProofGoal,
6198 var_name: &str,
6199 depth: usize,
6200 ) -> ProofResult<Option<DerivationTree>> {
6201 let zero = ProofExpr::Ctor {
6203 name: "Zero".into(),
6204 args: vec![],
6205 };
6206
6207 let base_goal_expr = self.substitute_typed_var(&goal.target, var_name, &zero);
6209 let base_goal = ProofGoal::with_context(base_goal_expr, goal.context.clone());
6210
6211 let base_proof = match self.prove_goal(base_goal, depth + 1) {
6213 Ok(proof) => proof,
6214 Err(_) => return Ok(None), };
6216
6217 let fresh_k = self.fresh_var();
6219
6220 let k_var = ProofExpr::Atom(fresh_k.clone());
6222
6223 let succ_k = ProofExpr::Ctor {
6225 name: "Succ".into(),
6226 args: vec![k_var.clone()],
6227 };
6228
6229 let ih = self.substitute_typed_var(&goal.target, var_name, &k_var);
6231
6232 let step_goal_expr = self.substitute_typed_var(&goal.target, var_name, &succ_k);
6234
6235 let mut step_context = goal.context.clone();
6237 step_context.push(ih.clone());
6238
6239 let step_goal = ProofGoal::with_context(step_goal_expr, step_context);
6240
6241 let step_proof = match self.try_step_case_with_equational_reasoning(&step_goal, &ih, depth)
6243 {
6244 Ok(proof) => proof,
6245 Err(_) => return Ok(None), };
6247
6248 Ok(Some(DerivationTree::new(
6250 goal.target.clone(),
6251 InferenceRule::StructuralInduction {
6252 variable: var_name.to_string(),
6253 ind_type: "Nat".to_string(),
6254 step_var: fresh_k,
6255 },
6256 vec![base_proof, step_proof],
6257 )))
6258 }
6259
6260 fn try_list_induction(
6265 &mut self,
6266 goal: &ProofGoal,
6267 var_name: &str,
6268 depth: usize,
6269 ) -> ProofResult<Option<DerivationTree>> {
6270 let nil = ProofExpr::Ctor {
6272 name: "Nil".into(),
6273 args: vec![],
6274 };
6275
6276 let base_goal_expr = self.substitute_typed_var(&goal.target, var_name, &nil);
6278 let base_goal = ProofGoal::with_context(base_goal_expr, goal.context.clone());
6279
6280 let base_proof = match self.prove_goal(base_goal, depth + 1) {
6282 Ok(proof) => proof,
6283 Err(_) => return Ok(None),
6284 };
6285
6286 let fresh_h = self.fresh_var();
6288 let fresh_t = self.fresh_var();
6289
6290 let h_var = ProofExpr::Atom(fresh_h);
6291 let t_var = ProofExpr::Atom(fresh_t.clone());
6292
6293 let cons_ht = ProofExpr::Ctor {
6294 name: "Cons".into(),
6295 args: vec![h_var, t_var.clone()],
6296 };
6297
6298 let ih = self.substitute_typed_var(&goal.target, var_name, &t_var);
6300
6301 let step_goal_expr = self.substitute_typed_var(&goal.target, var_name, &cons_ht);
6303
6304 let mut step_context = goal.context.clone();
6305 step_context.push(ih.clone());
6306
6307 let step_goal = ProofGoal::with_context(step_goal_expr, step_context);
6308
6309 let step_proof = match self.try_step_case_with_equational_reasoning(&step_goal, &ih, depth)
6310 {
6311 Ok(proof) => proof,
6312 Err(_) => return Ok(None),
6313 };
6314
6315 Ok(Some(DerivationTree::new(
6316 goal.target.clone(),
6317 InferenceRule::StructuralInduction {
6318 variable: var_name.to_string(),
6319 ind_type: "List".to_string(),
6320 step_var: fresh_t,
6321 },
6322 vec![base_proof, step_proof],
6323 )))
6324 }
6325
6326 fn try_step_case_with_equational_reasoning(
6333 &mut self,
6334 goal: &ProofGoal,
6335 ih: &ProofExpr,
6336 depth: usize,
6337 ) -> ProofResult<DerivationTree> {
6338 if let Ok(proof) = self.prove_goal(goal.clone(), depth + 1) {
6340 return Ok(proof);
6341 }
6342
6343 if let ProofExpr::Identity(lhs, rhs) = &goal.target {
6345 if let Some(proof) = self.try_equational_proof(goal, lhs, rhs, ih, depth)? {
6347 return Ok(proof);
6348 }
6349 }
6350
6351 Err(ProofError::NoProofFound)
6352 }
6353
6354 fn try_equational_proof(
6362 &mut self,
6363 goal: &ProofGoal,
6364 lhs: &ProofTerm,
6365 rhs: &ProofTerm,
6366 ih: &ProofExpr,
6367 _depth: usize,
6368 ) -> ProofResult<Option<DerivationTree>> {
6369 let equations: Vec<ProofExpr> = self
6371 .knowledge_base
6372 .iter()
6373 .filter(|e| matches!(e, ProofExpr::Identity(_, _)))
6374 .cloned()
6375 .collect();
6376
6377 for eq_axiom in &equations {
6379 if let ProofExpr::Identity(_, _) = &eq_axiom {
6380 let renamed_axiom = self.rename_variables(&eq_axiom);
6382 if let ProofExpr::Identity(renamed_lhs, renamed_rhs) = renamed_axiom {
6383 if let Ok(subst) = unify_terms(&renamed_lhs, lhs) {
6387 let rewritten = self.apply_subst_to_term_with(&renamed_rhs, &subst);
6390
6391 if self.terms_equal_with_ih(&rewritten, rhs, ih) {
6393 let axiom_leaf =
6395 DerivationTree::leaf(eq_axiom.clone(), InferenceRule::PremiseMatch);
6396
6397 let ih_leaf =
6398 DerivationTree::leaf(ih.clone(), InferenceRule::PremiseMatch);
6399
6400 return Ok(Some(DerivationTree::new(
6401 goal.target.clone(),
6402 InferenceRule::PremiseMatch, vec![axiom_leaf, ih_leaf],
6404 )));
6405 }
6406 }
6407 }
6408 }
6409 }
6410
6411 Ok(None)
6412 }
6413
6414 fn terms_equal_with_ih(&self, t1: &ProofTerm, t2: &ProofTerm, ih: &ProofExpr) -> bool {
6416 if t1 == t2 {
6418 return true;
6419 }
6420
6421 if let ProofExpr::Identity(ih_lhs, ih_rhs) = ih {
6423 let t1_with_ih = self.rewrite_term_with_equation(t1, ih_lhs, ih_rhs);
6425 if &t1_with_ih == t2 {
6426 return true;
6427 }
6428
6429 let t2_with_ih = self.rewrite_term_with_equation(t2, ih_rhs, ih_lhs);
6431 if t1 == &t2_with_ih {
6432 return true;
6433 }
6434 }
6435
6436 false
6437 }
6438
6439 fn rewrite_term_with_equation(
6441 &self,
6442 term: &ProofTerm,
6443 from: &ProofTerm,
6444 to: &ProofTerm,
6445 ) -> ProofTerm {
6446 if term == from {
6448 return to.clone();
6449 }
6450
6451 match term {
6453 ProofTerm::Function(name, args) => {
6454 let new_args: Vec<ProofTerm> = args
6455 .iter()
6456 .map(|a| self.rewrite_term_with_equation(a, from, to))
6457 .collect();
6458 ProofTerm::Function(name.clone(), new_args)
6459 }
6460 ProofTerm::Group(terms) => {
6461 let new_terms: Vec<ProofTerm> = terms
6462 .iter()
6463 .map(|t| self.rewrite_term_with_equation(t, from, to))
6464 .collect();
6465 ProofTerm::Group(new_terms)
6466 }
6467 _ => term.clone(),
6468 }
6469 }
6470
6471 fn apply_subst_to_term_with(&self, term: &ProofTerm, subst: &Substitution) -> ProofTerm {
6473 match term {
6474 ProofTerm::Variable(v) => subst.get(v).cloned().unwrap_or_else(|| term.clone()),
6475 ProofTerm::Function(name, args) => ProofTerm::Function(
6476 name.clone(),
6477 args.iter()
6478 .map(|a| self.apply_subst_to_term_with(a, subst))
6479 .collect(),
6480 ),
6481 ProofTerm::Group(terms) => ProofTerm::Group(
6482 terms
6483 .iter()
6484 .map(|t| self.apply_subst_to_term_with(t, subst))
6485 .collect(),
6486 ),
6487 ProofTerm::Constant(_) => term.clone(),
6488 ProofTerm::BoundVarRef(_) => term.clone(),
6489 }
6490 }
6491
6492 fn find_typed_var(&self, expr: &ProofExpr) -> Option<(String, String)> {
6494 match expr {
6495 ProofExpr::TypedVar { name, typename } => Some((name.clone(), typename.clone())),
6496 ProofExpr::Identity(l, r) => {
6497 self.find_typed_var_in_term(l).or_else(|| self.find_typed_var_in_term(r))
6498 }
6499 ProofExpr::Predicate { args, .. } => {
6500 for arg in args {
6501 if let Some(tv) = self.find_typed_var_in_term(arg) {
6502 return Some(tv);
6503 }
6504 }
6505 None
6506 }
6507 ProofExpr::And(l, r)
6508 | ProofExpr::Or(l, r)
6509 | ProofExpr::Implies(l, r)
6510 | ProofExpr::Iff(l, r) => self.find_typed_var(l).or_else(|| self.find_typed_var(r)),
6511 ProofExpr::Not(inner) => self.find_typed_var(inner),
6512 ProofExpr::ForAll { body, .. } | ProofExpr::Exists { body, .. } => {
6513 self.find_typed_var(body)
6514 }
6515 _ => None,
6516 }
6517 }
6518
6519 fn find_typed_var_in_term(&self, term: &ProofTerm) -> Option<(String, String)> {
6521 match term {
6522 ProofTerm::Variable(v) => {
6523 if v.contains(':') {
6527 let parts: Vec<&str> = v.splitn(2, ':').collect();
6528 if parts.len() == 2 {
6529 return Some((parts[0].to_string(), parts[1].to_string()));
6530 }
6531 }
6532 None
6533 }
6534 ProofTerm::Function(_, args) => {
6535 for arg in args {
6536 if let Some(tv) = self.find_typed_var_in_term(arg) {
6537 return Some(tv);
6538 }
6539 }
6540 None
6541 }
6542 ProofTerm::Group(terms) => {
6543 for t in terms {
6544 if let Some(tv) = self.find_typed_var_in_term(t) {
6545 return Some(tv);
6546 }
6547 }
6548 None
6549 }
6550 ProofTerm::Constant(_) => None,
6551 ProofTerm::BoundVarRef(_) => None, }
6553 }
6554
6555 fn substitute_typed_var(
6557 &self,
6558 expr: &ProofExpr,
6559 var_name: &str,
6560 replacement: &ProofExpr,
6561 ) -> ProofExpr {
6562 match expr {
6563 ProofExpr::TypedVar { name, .. } if name == var_name => replacement.clone(),
6564 ProofExpr::Identity(l, r) => {
6565 let new_l = self.substitute_typed_var_in_term(l, var_name, replacement);
6566 let new_r = self.substitute_typed_var_in_term(r, var_name, replacement);
6567 ProofExpr::Identity(new_l, new_r)
6568 }
6569 ProofExpr::Predicate { name, args, world } => {
6570 let new_args: Vec<ProofTerm> = args
6571 .iter()
6572 .map(|a| self.substitute_typed_var_in_term(a, var_name, replacement))
6573 .collect();
6574 ProofExpr::Predicate {
6575 name: name.clone(),
6576 args: new_args,
6577 world: world.clone(),
6578 }
6579 }
6580 ProofExpr::And(l, r) => ProofExpr::And(
6581 Box::new(self.substitute_typed_var(l, var_name, replacement)),
6582 Box::new(self.substitute_typed_var(r, var_name, replacement)),
6583 ),
6584 ProofExpr::Or(l, r) => ProofExpr::Or(
6585 Box::new(self.substitute_typed_var(l, var_name, replacement)),
6586 Box::new(self.substitute_typed_var(r, var_name, replacement)),
6587 ),
6588 ProofExpr::Implies(l, r) => ProofExpr::Implies(
6589 Box::new(self.substitute_typed_var(l, var_name, replacement)),
6590 Box::new(self.substitute_typed_var(r, var_name, replacement)),
6591 ),
6592 ProofExpr::Iff(l, r) => ProofExpr::Iff(
6593 Box::new(self.substitute_typed_var(l, var_name, replacement)),
6594 Box::new(self.substitute_typed_var(r, var_name, replacement)),
6595 ),
6596 ProofExpr::Not(inner) => {
6597 ProofExpr::Not(Box::new(self.substitute_typed_var(inner, var_name, replacement)))
6598 }
6599 ProofExpr::ForAll { variable, body } => ProofExpr::ForAll {
6600 variable: variable.clone(),
6601 body: Box::new(self.substitute_typed_var(body, var_name, replacement)),
6602 },
6603 ProofExpr::Exists { variable, body } => ProofExpr::Exists {
6604 variable: variable.clone(),
6605 body: Box::new(self.substitute_typed_var(body, var_name, replacement)),
6606 },
6607 _ => expr.clone(),
6608 }
6609 }
6610
6611 fn substitute_typed_var_in_term(
6613 &self,
6614 term: &ProofTerm,
6615 var_name: &str,
6616 replacement: &ProofExpr,
6617 ) -> ProofTerm {
6618 match term {
6619 ProofTerm::Variable(v) => {
6620 if v == var_name || v.starts_with(&format!("{}:", var_name)) {
6622 self.expr_to_term(replacement)
6623 } else {
6624 term.clone()
6625 }
6626 }
6627 ProofTerm::Function(name, args) => ProofTerm::Function(
6628 name.clone(),
6629 args.iter()
6630 .map(|a| self.substitute_typed_var_in_term(a, var_name, replacement))
6631 .collect(),
6632 ),
6633 ProofTerm::Group(terms) => ProofTerm::Group(
6634 terms
6635 .iter()
6636 .map(|t| self.substitute_typed_var_in_term(t, var_name, replacement))
6637 .collect(),
6638 ),
6639 ProofTerm::Constant(_) => term.clone(),
6640 ProofTerm::BoundVarRef(_) => term.clone(),
6641 }
6642 }
6643
6644 fn expr_to_term(&self, expr: &ProofExpr) -> ProofTerm {
6646 match expr {
6647 ProofExpr::Atom(s) => ProofTerm::Variable(s.clone()),
6648 ProofExpr::Ctor { name, args } => {
6649 ProofTerm::Function(name.clone(), args.iter().map(|a| self.expr_to_term(a)).collect())
6650 }
6651 ProofExpr::TypedVar { name, .. } => ProofTerm::Variable(name.clone()),
6652 _ => ProofTerm::Constant(format!("{}", expr)),
6653 }
6654 }
6655
6656 fn fresh_var(&mut self) -> String {
6662 self.var_counter += 1;
6663 format!("_G{}", self.var_counter)
6664 }
6665
6666 fn fresh_eigenconstant(&mut self) -> String {
6671 self.var_counter += 1;
6672 format!("__eigen{}", self.var_counter)
6673 }
6674
6675 fn rename_variables(&mut self, expr: &ProofExpr) -> ProofExpr {
6677 let vars = self.collect_variables(expr);
6678 let mut subst = Substitution::new();
6679
6680 for var in vars {
6681 let fresh = self.fresh_var();
6682 subst.insert(var, ProofTerm::Variable(fresh));
6683 }
6684
6685 apply_subst_to_expr(expr, &subst)
6686 }
6687
6688 fn collect_variables(&self, expr: &ProofExpr) -> Vec<String> {
6690 let mut vars = Vec::new();
6691 self.collect_variables_recursive(expr, &mut vars);
6692 vars
6693 }
6694
6695 fn collect_variables_recursive(&self, expr: &ProofExpr, vars: &mut Vec<String>) {
6696 match expr {
6697 ProofExpr::Predicate { args, .. } => {
6698 for arg in args {
6699 self.collect_term_variables(arg, vars);
6700 }
6701 }
6702 ProofExpr::Identity(l, r) => {
6703 self.collect_term_variables(l, vars);
6704 self.collect_term_variables(r, vars);
6705 }
6706 ProofExpr::And(l, r)
6707 | ProofExpr::Or(l, r)
6708 | ProofExpr::Implies(l, r)
6709 | ProofExpr::Iff(l, r) => {
6710 self.collect_variables_recursive(l, vars);
6711 self.collect_variables_recursive(r, vars);
6712 }
6713 ProofExpr::Not(inner) => self.collect_variables_recursive(inner, vars),
6714 ProofExpr::ForAll { variable, body } | ProofExpr::Exists { variable, body } => {
6715 if !vars.contains(variable) {
6716 vars.push(variable.clone());
6717 }
6718 self.collect_variables_recursive(body, vars);
6719 }
6720 ProofExpr::Lambda { variable, body } => {
6721 if !vars.contains(variable) {
6722 vars.push(variable.clone());
6723 }
6724 self.collect_variables_recursive(body, vars);
6725 }
6726 ProofExpr::App(f, a) => {
6727 self.collect_variables_recursive(f, vars);
6728 self.collect_variables_recursive(a, vars);
6729 }
6730 ProofExpr::NeoEvent { roles, .. } => {
6731 for (_, term) in roles {
6732 self.collect_term_variables(term, vars);
6733 }
6734 }
6735 _ => {}
6736 }
6737 }
6738
6739 fn collect_term_variables(&self, term: &ProofTerm, vars: &mut Vec<String>) {
6740 match term {
6741 ProofTerm::Variable(v) => {
6742 if !vars.contains(v) {
6743 vars.push(v.clone());
6744 }
6745 }
6746 ProofTerm::Function(_, args) => {
6747 for arg in args {
6748 self.collect_term_variables(arg, vars);
6749 }
6750 }
6751 ProofTerm::Group(terms) => {
6752 for t in terms {
6753 self.collect_term_variables(t, vars);
6754 }
6755 }
6756 ProofTerm::Constant(_) => {}
6757 ProofTerm::BoundVarRef(_) => {} }
6759 }
6760
6761 fn collect_witnesses(&self) -> Vec<ProofTerm> {
6763 let mut witnesses = Vec::new();
6764
6765 for expr in &self.knowledge_base {
6766 self.collect_constants_from_expr(expr, &mut witnesses);
6767 }
6768
6769 witnesses
6770 }
6771
6772 fn collect_constants_from_expr(&self, expr: &ProofExpr, constants: &mut Vec<ProofTerm>) {
6773 match expr {
6774 ProofExpr::Predicate { args, .. } => {
6775 for arg in args {
6776 self.collect_constants_from_term(arg, constants);
6777 }
6778 }
6779 ProofExpr::Identity(l, r) => {
6780 self.collect_constants_from_term(l, constants);
6781 self.collect_constants_from_term(r, constants);
6782 }
6783 ProofExpr::And(l, r)
6784 | ProofExpr::Or(l, r)
6785 | ProofExpr::Implies(l, r)
6786 | ProofExpr::Iff(l, r) => {
6787 self.collect_constants_from_expr(l, constants);
6788 self.collect_constants_from_expr(r, constants);
6789 }
6790 ProofExpr::Not(inner) => self.collect_constants_from_expr(inner, constants),
6791 ProofExpr::ForAll { body, .. } | ProofExpr::Exists { body, .. } => {
6792 self.collect_constants_from_expr(body, constants);
6793 }
6794 ProofExpr::NeoEvent { roles, .. } => {
6795 for (_, term) in roles {
6796 self.collect_constants_from_term(term, constants);
6797 }
6798 }
6799 _ => {}
6800 }
6801 }
6802
6803 fn collect_constants_from_term(&self, term: &ProofTerm, constants: &mut Vec<ProofTerm>) {
6804 match term {
6805 ProofTerm::Constant(_) => {
6806 if !constants.contains(term) {
6807 constants.push(term.clone());
6808 }
6809 }
6810 ProofTerm::Function(_, args) => {
6811 if !constants.contains(term) {
6813 constants.push(term.clone());
6814 }
6815 for arg in args {
6816 self.collect_constants_from_term(arg, constants);
6817 }
6818 }
6819 ProofTerm::Group(terms) => {
6820 for t in terms {
6821 self.collect_constants_from_term(t, constants);
6822 }
6823 }
6824 ProofTerm::Variable(_) => {}
6825 ProofTerm::BoundVarRef(_) => {} }
6827 }
6828
6829 #[cfg(feature = "verification")]
6838 fn try_oracle_fallback(&self, goal: &ProofGoal) -> ProofResult<Option<DerivationTree>> {
6839 crate::oracle::try_oracle(goal, &self.knowledge_base)
6840 }
6841}
6842
6843fn extract_type_from_exists_body(body: &ProofExpr) -> Option<String> {
6853 match body {
6854 ProofExpr::TypedVar { typename, .. } => Some(typename.clone()),
6856
6857 ProofExpr::And(l, r) => {
6859 extract_type_from_exists_body(l).or_else(|| extract_type_from_exists_body(r))
6860 }
6861
6862 ProofExpr::Or(l, r) => {
6864 extract_type_from_exists_body(l).or_else(|| extract_type_from_exists_body(r))
6865 }
6866
6867 ProofExpr::Exists { body, .. } | ProofExpr::ForAll { body, .. } => {
6869 extract_type_from_exists_body(body)
6870 }
6871
6872 _ => None,
6874 }
6875}
6876
6877impl Default for BackwardChainer {
6878 fn default() -> Self {
6879 Self::new()
6880 }
6881}