logicaffeine_proof/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3//! # Logicaffeine Proof Engine
4//!
5//! Core structures for proof representation and verification.
6//!
7//! This module defines the "Shape of Truth." A proof is not a boolean;
8//! it is a recursive tree of inference rules that can be inspected,
9//! transformed, and verified.
10//!
11//! ## Curry-Howard Correspondence
12//!
13//! The proof engine implements the propositions-as-types paradigm:
14//!
15//! - **A Proposition is a Type** — logical formulas correspond to types
16//! - **A Proof is a Program** — derivation trees are proof terms
17//! - **Verification is Type Checking** — the kernel validates proof terms
18//!
19//! ## Architecture Invariant
20//!
21//! This crate has **no dependency** on the language crate (Liskov boundary).
22//! The conversion from `LogicExpr` → [`ProofExpr`] lives in the language crate,
23//! ensuring the proof engine remains pure and reusable.
24
25pub mod certifier;
26pub mod engine;
27pub mod error;
28pub mod hints;
29pub mod unify;
30
31#[cfg(feature = "verification")]
32pub mod oracle;
33
34pub use engine::BackwardChainer;
35pub use error::ProofError;
36pub use hints::{suggest_hint, SocraticHint, SuggestedTactic};
37pub use unify::Substitution;
38
39use std::fmt;
40
41// =============================================================================
42// PROOF TERM - Owned representation of logical terms
43// =============================================================================
44
45/// Owned term representation for proof manipulation.
46///
47/// Decoupled from arena-allocated `Term<'a>` to allow proof trees to persist
48/// beyond the lifetime of the parsing arena.
49///
50/// # See Also
51///
52/// * [`ProofExpr`] - Expression-level representation containing `ProofTerm`s
53/// * [`unify::unify_terms`] - Unification algorithm for terms
54/// * [`unify::Substitution`] - Maps variables to terms
55/// * [`certifier::certify`] - Converts proof trees to kernel terms
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
57pub enum ProofTerm {
58    /// A constant symbol (e.g., "Socrates", "42")
59    Constant(String),
60
61    /// A variable symbol (e.g., "x", "y")
62    Variable(String),
63
64    /// A function application (e.g., "father(x)", "add(1, 2)")
65    Function(String, Vec<ProofTerm>),
66
67    /// A group/tuple of terms (e.g., "(x, y)")
68    Group(Vec<ProofTerm>),
69
70    /// Reference to a bound variable in a pattern context.
71    /// Distinct from Variable (unification var) and Constant (global name).
72    /// Prevents variable capture bugs during alpha-conversion.
73    BoundVarRef(String),
74}
75
76impl fmt::Display for ProofTerm {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        match self {
79            ProofTerm::Constant(s) => write!(f, "{}", s),
80            ProofTerm::Variable(s) => write!(f, "{}", s),
81            ProofTerm::Function(name, args) => {
82                write!(f, "{}(", name)?;
83                for (i, arg) in args.iter().enumerate() {
84                    if i > 0 {
85                        write!(f, ", ")?;
86                    }
87                    write!(f, "{}", arg)?;
88                }
89                write!(f, ")")
90            }
91            ProofTerm::Group(terms) => {
92                write!(f, "(")?;
93                for (i, t) in terms.iter().enumerate() {
94                    if i > 0 {
95                        write!(f, ", ")?;
96                    }
97                    write!(f, "{}", t)?;
98                }
99                write!(f, ")")
100            }
101            ProofTerm::BoundVarRef(s) => write!(f, "#{}", s),
102        }
103    }
104}
105
106// =============================================================================
107// PROOF EXPRESSION - Owned representation of logical expressions
108// =============================================================================
109
110/// Owned expression representation for proof manipulation.
111///
112/// Supports all `LogicExpr` variants to enable full language coverage.
113/// This is the "proposition" side of the Curry-Howard correspondence.
114///
115/// # See Also
116///
117/// * [`ProofTerm`] - Terms embedded within expressions (predicate arguments)
118/// * [`unify::unify_exprs`] - Expression-level unification with alpha-equivalence
119/// * [`unify::beta_reduce`] - Normalizes lambda applications
120/// * [`certifier::certify`] - Converts expressions to kernel types
121/// * [`DerivationTree`] - Proof trees conclude with a `ProofExpr`
122#[derive(Debug, Clone, PartialEq)]
123pub enum ProofExpr {
124    // --- Core FOL ---
125
126    /// Atomic predicate: P(t1, t2, ...)
127    Predicate {
128        name: String,
129        args: Vec<ProofTerm>,
130        world: Option<String>,
131    },
132
133    /// Identity: t1 = t2
134    Identity(ProofTerm, ProofTerm),
135
136    /// Propositional atom
137    Atom(String),
138
139    // --- Logical Connectives ---
140
141    /// Conjunction: P ∧ Q
142    And(Box<ProofExpr>, Box<ProofExpr>),
143
144    /// Disjunction: P ∨ Q
145    Or(Box<ProofExpr>, Box<ProofExpr>),
146
147    /// Implication: P → Q
148    Implies(Box<ProofExpr>, Box<ProofExpr>),
149
150    /// Biconditional: P ↔ Q
151    Iff(Box<ProofExpr>, Box<ProofExpr>),
152
153    /// Negation: ¬P
154    Not(Box<ProofExpr>),
155
156    // --- Quantifiers ---
157
158    /// Universal quantification: ∀x P(x)
159    ForAll {
160        variable: String,
161        body: Box<ProofExpr>,
162    },
163
164    /// Existential quantification: ∃x P(x)
165    Exists {
166        variable: String,
167        body: Box<ProofExpr>,
168    },
169
170    // --- Modal Logic ---
171
172    /// Modal operator: □P or ◇P (with world semantics)
173    Modal {
174        domain: String,
175        force: f32,
176        flavor: String,
177        body: Box<ProofExpr>,
178    },
179
180    // --- Temporal Logic ---
181
182    /// Temporal operator: Past(P), Future(P), Always(P), Eventually(P), Next(P)
183    Temporal {
184        operator: String,
185        body: Box<ProofExpr>,
186    },
187
188    /// Binary temporal operator: Until(P,Q), Release(P,Q), WeakUntil(P,Q)
189    TemporalBinary {
190        operator: String,
191        left: Box<ProofExpr>,
192        right: Box<ProofExpr>,
193    },
194
195    // --- Lambda Calculus (CIC extension) ---
196
197    /// Lambda abstraction: λx.P
198    Lambda {
199        variable: String,
200        body: Box<ProofExpr>,
201    },
202
203    /// Function application: (f x)
204    App(Box<ProofExpr>, Box<ProofExpr>),
205
206    // --- Event Semantics ---
207
208    /// Neo-Davidsonian event: ∃e(Verb(e) ∧ Agent(e,x) ∧ ...)
209    NeoEvent {
210        event_var: String,
211        verb: String,
212        roles: Vec<(String, ProofTerm)>,
213    },
214
215    // --- Peano / Inductive Types (CIC Extension) ---
216
217    /// Data Constructor: Zero, Succ(n), Cons(h, t), etc.
218    /// The building blocks of inductive types.
219    Ctor {
220        name: String,
221        args: Vec<ProofExpr>,
222    },
223
224    /// Pattern Matching: match n { Zero => A, Succ(k) => B }
225    /// Eliminates inductive types by case analysis.
226    Match {
227        scrutinee: Box<ProofExpr>,
228        arms: Vec<MatchArm>,
229    },
230
231    /// Recursive Function (Fixpoint): fix f. λn. ...
232    /// Defines recursive functions over inductive types.
233    Fixpoint {
234        name: String,
235        body: Box<ProofExpr>,
236    },
237
238    /// Typed Variable: n : Nat
239    /// Signals to the prover that induction may be applicable.
240    TypedVar {
241        name: String,
242        typename: String,
243    },
244
245    // --- Higher-Order Pattern Unification ---
246
247    /// Meta-variable (unification hole) to be solved during proof search.
248    /// Represents an unknown expression, typically a function or predicate.
249    /// Example: Hole("P") in ?P(x) = Body, to be solved as P = λx.Body
250    Hole(String),
251
252    /// Embedded term (lifts ProofTerm into ProofExpr context).
253    /// Used when a term appears where an expression is expected.
254    /// Example: App(Hole("P"), Term(BoundVarRef("x")))
255    Term(ProofTerm),
256
257    // --- Fallback ---
258
259    /// Unsupported construct (with description for debugging)
260    Unsupported(String),
261}
262
263// =============================================================================
264// MATCH ARM - A single case in pattern matching
265// =============================================================================
266
267/// A single arm in a match expression.
268/// Example: Succ(k) => Add(k, m)
269#[derive(Debug, Clone, PartialEq)]
270pub struct MatchArm {
271    /// The constructor being matched: "Zero", "Succ", "Cons", etc.
272    pub ctor: String,
273
274    /// Variable bindings for constructor arguments: ["k"] for Succ(k)
275    pub bindings: Vec<String>,
276
277    /// The expression to evaluate when this arm matches
278    pub body: ProofExpr,
279}
280
281impl fmt::Display for ProofExpr {
282    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283        match self {
284            ProofExpr::Predicate { name, args, world } => {
285                write!(f, "{}", name)?;
286                if !args.is_empty() {
287                    write!(f, "(")?;
288                    for (i, arg) in args.iter().enumerate() {
289                        if i > 0 {
290                            write!(f, ", ")?;
291                        }
292                        write!(f, "{}", arg)?;
293                    }
294                    write!(f, ")")?;
295                }
296                if let Some(w) = world {
297                    write!(f, " @{}", w)?;
298                }
299                Ok(())
300            }
301            ProofExpr::Identity(left, right) => write!(f, "{} = {}", left, right),
302            ProofExpr::Atom(s) => write!(f, "{}", s),
303            ProofExpr::And(left, right) => write!(f, "({} ∧ {})", left, right),
304            ProofExpr::Or(left, right) => write!(f, "({} ∨ {})", left, right),
305            ProofExpr::Implies(left, right) => write!(f, "({} → {})", left, right),
306            ProofExpr::Iff(left, right) => write!(f, "({} ↔ {})", left, right),
307            ProofExpr::Not(inner) => write!(f, "¬{}", inner),
308            ProofExpr::ForAll { variable, body } => write!(f, "∀{} {}", variable, body),
309            ProofExpr::Exists { variable, body } => write!(f, "∃{} {}", variable, body),
310            ProofExpr::Modal { domain, force, flavor, body } => {
311                write!(f, "□[{}/{}/{}]{}", domain, force, flavor, body)
312            }
313            ProofExpr::Temporal { operator, body } => write!(f, "{}({})", operator, body),
314            ProofExpr::TemporalBinary { operator, left, right } => {
315                write!(f, "TemporalBinary({}, {}, {})", operator, left, right)
316            }
317            ProofExpr::Lambda { variable, body } => write!(f, "λ{}.{}", variable, body),
318            ProofExpr::App(func, arg) => write!(f, "({} {})", func, arg),
319            ProofExpr::NeoEvent { event_var, verb, roles } => {
320                write!(f, "∃{}({}({})", event_var, verb, event_var)?;
321                for (role, term) in roles {
322                    write!(f, " ∧ {}({}, {})", role, event_var, term)?;
323                }
324                write!(f, ")")
325            }
326            ProofExpr::Ctor { name, args } => {
327                write!(f, "{}", name)?;
328                if !args.is_empty() {
329                    write!(f, "(")?;
330                    for (i, arg) in args.iter().enumerate() {
331                        if i > 0 {
332                            write!(f, ", ")?;
333                        }
334                        write!(f, "{}", arg)?;
335                    }
336                    write!(f, ")")?;
337                }
338                Ok(())
339            }
340            ProofExpr::Match { scrutinee, arms } => {
341                write!(f, "match {} {{ ", scrutinee)?;
342                for (i, arm) in arms.iter().enumerate() {
343                    if i > 0 {
344                        write!(f, ", ")?;
345                    }
346                    write!(f, "{}", arm.ctor)?;
347                    if !arm.bindings.is_empty() {
348                        write!(f, "({})", arm.bindings.join(", "))?;
349                    }
350                    write!(f, " => {}", arm.body)?;
351                }
352                write!(f, " }}")
353            }
354            ProofExpr::Fixpoint { name, body } => write!(f, "fix {}.{}", name, body),
355            ProofExpr::TypedVar { name, typename } => write!(f, "{}:{}", name, typename),
356            ProofExpr::Unsupported(desc) => write!(f, "⟨unsupported: {}⟩", desc),
357            ProofExpr::Hole(name) => write!(f, "?{}", name),
358            ProofExpr::Term(term) => write!(f, "{}", term),
359        }
360    }
361}
362
363// =============================================================================
364// INFERENCE RULE - The logical moves available to the prover
365// =============================================================================
366
367/// The "Lever" - The specific logical move made at each proof step.
368///
369/// This enum captures HOW we moved from premises to conclusion. Each variant
370/// corresponds to a different proof strategy or logical rule.
371///
372/// # See Also
373///
374/// * [`DerivationTree`] - Each node contains an `InferenceRule`
375/// * [`certifier::certify`] - Maps inference rules to kernel terms
376/// * [`hints::suggest_hint`] - Suggests applicable rules when stuck
377/// * [`BackwardChainer`] - The engine that selects and applies rules
378#[derive(Debug, Clone, PartialEq)]
379pub enum InferenceRule {
380    // --- Basic FOL ---
381
382    /// Direct match with a known fact in the Context/KnowledgeBase.
383    /// Logic: Γ, P ⊢ P
384    PremiseMatch,
385
386    /// Logic: P → Q, P ⊢ Q
387    ModusPonens,
388
389    /// Logic: ¬Q, P → Q ⊢ ¬P
390    ModusTollens,
391
392    /// Logic: P, Q ⊢ P ∧ Q
393    ConjunctionIntro,
394
395    /// Logic: P ∧ Q ⊢ P (or Q)
396    ConjunctionElim,
397
398    /// Logic: P ⊢ P ∨ Q
399    DisjunctionIntro,
400
401    /// Logic: P ∨ Q, P → R, Q → R ⊢ R
402    DisjunctionElim,
403
404    /// Logic: ¬¬P ⊢ P (and P ⊢ ¬¬P)
405    DoubleNegation,
406
407    // --- Quantifiers ---
408
409    /// Logic: ∀x P(x) ⊢ P(c)
410    /// Stores the specific term 'c' used to instantiate the universal.
411    UniversalInst(String),
412
413    /// Logic: Γ, x:T ⊢ P(x) implies Γ ⊢ ∀x:T. P(x)
414    /// Stores variable name and type name for Lambda construction.
415    UniversalIntro { variable: String, var_type: String },
416
417    /// Logic: P(w) ⊢ ∃x.P(x)
418    /// Carries the witness and its type for kernel certification.
419    ExistentialIntro {
420        witness: String,
421        witness_type: String,
422    },
423
424    // --- Modal Logic (World Moves) ---
425
426    /// Logic: □P (in w0), Accessible(w0, w1) ⊢ P (in w1)
427    /// "Necessity Elimination" / "Distribution"
428    ModalAccess,
429
430    /// Logic: If P is true in ALL accessible worlds ⊢ □P
431    /// "Necessity Introduction"
432    ModalGeneralization,
433
434    // --- Temporal Logic ---
435
436    /// Logic: t1 < t2, t2 < t3 ⊢ t1 < t3
437    TemporalTransitivity,
438
439    /// Logic: P(s₀), ∀s(P(s) → P(next(s))) ⊢ G(P)
440    /// Standard k-induction for hardware safety properties.
441    TemporalInduction,
442
443    /// Logic: G(P) ⊢ P ∧ X(G(P))
444    /// Fixpoint unfolding of Always.
445    TemporalUnfolding,
446
447    /// Logic: P(w) ⊢ F(P) for witness world w
448    /// Prove Eventually by exhibiting a reachable witness.
449    EventualityProgress,
450
451    /// Logic: Induction on trace length for P U Q
452    UntilInduction,
453
454    // --- Peano / Inductive Reasoning (CIC seed) ---
455
456    /// Logic: P(0), ∀k(P(k) → P(S(k))) ⊢ ∀n P(n)
457    /// Stores the variable name, its inductive type, and the step variable used.
458    StructuralInduction {
459        variable: String,  // "n" - the induction variable
460        ind_type: String,  // "Nat" - the inductive type
461        step_var: String,  // "k" - the predecessor variable (for IH matching)
462    },
463
464    // --- Equality ---
465
466    /// Leibniz's Law / Substitution of Equals
467    /// Logic: a = b, P(a) ⊢ P(b)
468    /// The equality proof is in `premise\[0\]`, the P(a) proof is in `premise\[1\]`.
469    /// Carries the original term and replacement term for certification.
470    Rewrite {
471        from: ProofTerm,
472        to: ProofTerm,
473    },
474
475    /// Symmetry of Equality: a = b ⊢ b = a
476    EqualitySymmetry,
477
478    /// Transitivity of Equality: a = b, b = c ⊢ a = c
479    EqualityTransitivity,
480
481    /// Reflexivity of Equality: a = a (after normalization)
482    /// Used when both sides of an identity reduce to the same normal form.
483    Reflexivity,
484
485    // --- Fallbacks ---
486
487    /// "The User Said So." Used for top-level axioms.
488    Axiom,
489
490    /// "The Machine Said So." (Z3 Oracle)
491    /// The string contains the solver's justification.
492    OracleVerification(String),
493
494    /// Proof by Contradiction (Reductio ad Absurdum)
495    /// Logic: Assume ¬C, derive P ∧ ¬P (contradiction), conclude C
496    /// Or: Assume P, derive Q ∧ ¬Q, conclude ¬P
497    ReductioAdAbsurdum,
498
499    /// Contradiction detected in premises: P and ¬P both hold
500    /// Logic: P, ¬P ⊢ ⊥ (ex falso quodlibet)
501    Contradiction,
502
503    // --- Quantifier Elimination ---
504
505    /// Existential Elimination (Skolemization in a proof context)
506    /// Logic: ∃x.P(x), [c fresh] P(c) ⊢ Goal implies ∃x.P(x) ⊢ Goal
507    /// The witness c must be fresh (not appearing in Goal).
508    ExistentialElim { witness: String },
509
510    /// Case Analysis (Tertium Non Datur / Law of Excluded Middle)
511    /// Logic: (P → ⊥), (¬P → ⊥) ⊢ ⊥
512    /// Used for self-referential paradoxes like the Barber Paradox.
513    CaseAnalysis { case_formula: String },
514}
515
516// =============================================================================
517// DERIVATION TREE - The recursive proof structure
518// =============================================================================
519
520/// The "Euclidean Structure" - A recursive tree representing the proof.
521///
522/// This is the object returned by the Prover. It allows the UI to render
523/// a step-by-step explanation (Natural Language Generation).
524///
525/// # Structure
526///
527/// Each node contains:
528/// - `conclusion`: The proposition proved at this step
529/// - `rule`: The logical rule applied (how we know the conclusion)
530/// - `premises`: Sub-proofs justifying the rule's requirements
531/// - `substitution`: Variable bindings from unification
532///
533/// # See Also
534///
535/// * [`BackwardChainer::prove`] - Creates derivation trees
536/// * [`InferenceRule`] - The rules that justify each step
537/// * [`ProofExpr`] - The conclusion type
538/// * [`certifier::certify`] - Converts trees to kernel terms
539/// * [`hints::suggest_hint`] - Suggests next steps when stuck
540#[derive(Debug, Clone)]
541pub struct DerivationTree {
542    /// The logical statement proved at this node.
543    pub conclusion: ProofExpr,
544
545    /// The rule applied to justify this conclusion.
546    pub rule: InferenceRule,
547
548    /// The sub-proofs that validate the requirements of the rule.
549    /// Example: ModusPonens will have 2 children (The Implication, The Antecedent).
550    pub premises: Vec<DerivationTree>,
551
552    /// The depth of the tree (used for complexity limits).
553    pub depth: usize,
554
555    /// Substitution applied at this step (for unification-based rules).
556    pub substitution: unify::Substitution,
557}
558
559impl DerivationTree {
560    /// Constructs a new Proof Node.
561    /// Automatically calculates depth based on children.
562    pub fn new(
563        conclusion: ProofExpr,
564        rule: InferenceRule,
565        premises: Vec<DerivationTree>,
566    ) -> Self {
567        let max_depth = premises.iter().map(|p| p.depth).max().unwrap_or(0);
568        Self {
569            conclusion,
570            rule,
571            premises,
572            depth: max_depth + 1,
573            substitution: unify::Substitution::new(),
574        }
575    }
576
577    /// A leaf node (usually a Premise, Axiom, or Oracle result).
578    pub fn leaf(conclusion: ProofExpr, rule: InferenceRule) -> Self {
579        Self::new(conclusion, rule, vec![])
580    }
581
582    /// Set the substitution for this derivation step.
583    pub fn with_substitution(mut self, subst: unify::Substitution) -> Self {
584        self.substitution = subst;
585        self
586    }
587
588    /// Renders the proof as a text-based tree (for debugging/CLI).
589    pub fn display_tree(&self) -> String {
590        self.display_recursive(0)
591    }
592
593    fn display_recursive(&self, indent: usize) -> String {
594        let prefix = "  ".repeat(indent);
595
596        let rule_name = match &self.rule {
597            InferenceRule::UniversalInst(var) => format!("UniversalInst({})", var),
598            InferenceRule::UniversalIntro { variable, var_type } => {
599                format!("UniversalIntro({}:{})", variable, var_type)
600            }
601            InferenceRule::ExistentialIntro { witness, witness_type } => {
602                format!("∃Intro({}:{})", witness, witness_type)
603            }
604            InferenceRule::StructuralInduction { variable, ind_type, step_var } => {
605                format!("Induction({}:{}, step={})", variable, ind_type, step_var)
606            }
607            InferenceRule::OracleVerification(s) => format!("Oracle({})", s),
608            InferenceRule::Rewrite { from, to } => format!("Rewrite({} → {})", from, to),
609            InferenceRule::EqualitySymmetry => "EqSymmetry".to_string(),
610            InferenceRule::EqualityTransitivity => "EqTransitivity".to_string(),
611            r => format!("{:?}", r),
612        };
613
614        let mut output = format!("{}└─ [{}] {}\n", prefix, rule_name, self.conclusion);
615
616        for premise in &self.premises {
617            output.push_str(&premise.display_recursive(indent + 1));
618        }
619        output
620    }
621}
622
623impl fmt::Display for DerivationTree {
624    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
625        write!(f, "{}", self.display_tree())
626    }
627}
628
629// =============================================================================
630// PROOF GOAL - The target state for backward chaining
631// =============================================================================
632
633/// The Goal State for the Backward Chainer.
634///
635/// Represents a "hole" in the proof that needs to be filled. During backward
636/// chaining, goals are decomposed into subgoals until they match known facts.
637///
638/// # Fields
639///
640/// * `target` - What we are trying to prove
641/// * `context` - Local assumptions (e.g., antecedents of implications we've entered)
642///
643/// # See Also
644///
645/// * [`BackwardChainer::prove`] - Takes a goal and produces a derivation tree
646/// * [`DerivationTree`] - The result of successfully proving a goal
647/// * [`ProofExpr`] - The target type
648#[derive(Debug, Clone)]
649pub struct ProofGoal {
650    /// What we are trying to prove right now.
651    pub target: ProofExpr,
652
653    /// The local assumptions available (e.g., inside an implication).
654    pub context: Vec<ProofExpr>,
655}
656
657impl ProofGoal {
658    pub fn new(target: ProofExpr) -> Self {
659        Self {
660            target,
661            context: Vec::new(),
662        }
663    }
664
665    pub fn with_context(target: ProofExpr, context: Vec<ProofExpr>) -> Self {
666        Self { target, context }
667    }
668}