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) or Future(P)
183    Temporal {
184        operator: String,
185        body: Box<ProofExpr>,
186    },
187
188    // --- Lambda Calculus (CIC extension) ---
189
190    /// Lambda abstraction: λx.P
191    Lambda {
192        variable: String,
193        body: Box<ProofExpr>,
194    },
195
196    /// Function application: (f x)
197    App(Box<ProofExpr>, Box<ProofExpr>),
198
199    // --- Event Semantics ---
200
201    /// Neo-Davidsonian event: ∃e(Verb(e) ∧ Agent(e,x) ∧ ...)
202    NeoEvent {
203        event_var: String,
204        verb: String,
205        roles: Vec<(String, ProofTerm)>,
206    },
207
208    // --- Peano / Inductive Types (CIC Extension) ---
209
210    /// Data Constructor: Zero, Succ(n), Cons(h, t), etc.
211    /// The building blocks of inductive types.
212    Ctor {
213        name: String,
214        args: Vec<ProofExpr>,
215    },
216
217    /// Pattern Matching: match n { Zero => A, Succ(k) => B }
218    /// Eliminates inductive types by case analysis.
219    Match {
220        scrutinee: Box<ProofExpr>,
221        arms: Vec<MatchArm>,
222    },
223
224    /// Recursive Function (Fixpoint): fix f. λn. ...
225    /// Defines recursive functions over inductive types.
226    Fixpoint {
227        name: String,
228        body: Box<ProofExpr>,
229    },
230
231    /// Typed Variable: n : Nat
232    /// Signals to the prover that induction may be applicable.
233    TypedVar {
234        name: String,
235        typename: String,
236    },
237
238    // --- Higher-Order Pattern Unification ---
239
240    /// Meta-variable (unification hole) to be solved during proof search.
241    /// Represents an unknown expression, typically a function or predicate.
242    /// Example: Hole("P") in ?P(x) = Body, to be solved as P = λx.Body
243    Hole(String),
244
245    /// Embedded term (lifts ProofTerm into ProofExpr context).
246    /// Used when a term appears where an expression is expected.
247    /// Example: App(Hole("P"), Term(BoundVarRef("x")))
248    Term(ProofTerm),
249
250    // --- Fallback ---
251
252    /// Unsupported construct (with description for debugging)
253    Unsupported(String),
254}
255
256// =============================================================================
257// MATCH ARM - A single case in pattern matching
258// =============================================================================
259
260/// A single arm in a match expression.
261/// Example: Succ(k) => Add(k, m)
262#[derive(Debug, Clone, PartialEq)]
263pub struct MatchArm {
264    /// The constructor being matched: "Zero", "Succ", "Cons", etc.
265    pub ctor: String,
266
267    /// Variable bindings for constructor arguments: ["k"] for Succ(k)
268    pub bindings: Vec<String>,
269
270    /// The expression to evaluate when this arm matches
271    pub body: ProofExpr,
272}
273
274impl fmt::Display for ProofExpr {
275    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276        match self {
277            ProofExpr::Predicate { name, args, world } => {
278                write!(f, "{}", name)?;
279                if !args.is_empty() {
280                    write!(f, "(")?;
281                    for (i, arg) in args.iter().enumerate() {
282                        if i > 0 {
283                            write!(f, ", ")?;
284                        }
285                        write!(f, "{}", arg)?;
286                    }
287                    write!(f, ")")?;
288                }
289                if let Some(w) = world {
290                    write!(f, " @{}", w)?;
291                }
292                Ok(())
293            }
294            ProofExpr::Identity(left, right) => write!(f, "{} = {}", left, right),
295            ProofExpr::Atom(s) => write!(f, "{}", s),
296            ProofExpr::And(left, right) => write!(f, "({} ∧ {})", left, right),
297            ProofExpr::Or(left, right) => write!(f, "({} ∨ {})", left, right),
298            ProofExpr::Implies(left, right) => write!(f, "({} → {})", left, right),
299            ProofExpr::Iff(left, right) => write!(f, "({} ↔ {})", left, right),
300            ProofExpr::Not(inner) => write!(f, "¬{}", inner),
301            ProofExpr::ForAll { variable, body } => write!(f, "∀{} {}", variable, body),
302            ProofExpr::Exists { variable, body } => write!(f, "∃{} {}", variable, body),
303            ProofExpr::Modal { domain, force, flavor, body } => {
304                write!(f, "□[{}/{}/{}]{}", domain, force, flavor, body)
305            }
306            ProofExpr::Temporal { operator, body } => write!(f, "{}({})", operator, body),
307            ProofExpr::Lambda { variable, body } => write!(f, "λ{}.{}", variable, body),
308            ProofExpr::App(func, arg) => write!(f, "({} {})", func, arg),
309            ProofExpr::NeoEvent { event_var, verb, roles } => {
310                write!(f, "∃{}({}({})", event_var, verb, event_var)?;
311                for (role, term) in roles {
312                    write!(f, " ∧ {}({}, {})", role, event_var, term)?;
313                }
314                write!(f, ")")
315            }
316            ProofExpr::Ctor { name, args } => {
317                write!(f, "{}", name)?;
318                if !args.is_empty() {
319                    write!(f, "(")?;
320                    for (i, arg) in args.iter().enumerate() {
321                        if i > 0 {
322                            write!(f, ", ")?;
323                        }
324                        write!(f, "{}", arg)?;
325                    }
326                    write!(f, ")")?;
327                }
328                Ok(())
329            }
330            ProofExpr::Match { scrutinee, arms } => {
331                write!(f, "match {} {{ ", scrutinee)?;
332                for (i, arm) in arms.iter().enumerate() {
333                    if i > 0 {
334                        write!(f, ", ")?;
335                    }
336                    write!(f, "{}", arm.ctor)?;
337                    if !arm.bindings.is_empty() {
338                        write!(f, "({})", arm.bindings.join(", "))?;
339                    }
340                    write!(f, " => {}", arm.body)?;
341                }
342                write!(f, " }}")
343            }
344            ProofExpr::Fixpoint { name, body } => write!(f, "fix {}.{}", name, body),
345            ProofExpr::TypedVar { name, typename } => write!(f, "{}:{}", name, typename),
346            ProofExpr::Unsupported(desc) => write!(f, "⟨unsupported: {}⟩", desc),
347            ProofExpr::Hole(name) => write!(f, "?{}", name),
348            ProofExpr::Term(term) => write!(f, "{}", term),
349        }
350    }
351}
352
353// =============================================================================
354// INFERENCE RULE - The logical moves available to the prover
355// =============================================================================
356
357/// The "Lever" - The specific logical move made at each proof step.
358///
359/// This enum captures HOW we moved from premises to conclusion. Each variant
360/// corresponds to a different proof strategy or logical rule.
361///
362/// # See Also
363///
364/// * [`DerivationTree`] - Each node contains an `InferenceRule`
365/// * [`certifier::certify`] - Maps inference rules to kernel terms
366/// * [`hints::suggest_hint`] - Suggests applicable rules when stuck
367/// * [`BackwardChainer`] - The engine that selects and applies rules
368#[derive(Debug, Clone, PartialEq)]
369pub enum InferenceRule {
370    // --- Basic FOL ---
371
372    /// Direct match with a known fact in the Context/KnowledgeBase.
373    /// Logic: Γ, P ⊢ P
374    PremiseMatch,
375
376    /// Logic: P → Q, P ⊢ Q
377    ModusPonens,
378
379    /// Logic: ¬Q, P → Q ⊢ ¬P
380    ModusTollens,
381
382    /// Logic: P, Q ⊢ P ∧ Q
383    ConjunctionIntro,
384
385    /// Logic: P ∧ Q ⊢ P (or Q)
386    ConjunctionElim,
387
388    /// Logic: P ⊢ P ∨ Q
389    DisjunctionIntro,
390
391    /// Logic: P ∨ Q, P → R, Q → R ⊢ R
392    DisjunctionElim,
393
394    /// Logic: ¬¬P ⊢ P (and P ⊢ ¬¬P)
395    DoubleNegation,
396
397    // --- Quantifiers ---
398
399    /// Logic: ∀x P(x) ⊢ P(c)
400    /// Stores the specific term 'c' used to instantiate the universal.
401    UniversalInst(String),
402
403    /// Logic: Γ, x:T ⊢ P(x) implies Γ ⊢ ∀x:T. P(x)
404    /// Stores variable name and type name for Lambda construction.
405    UniversalIntro { variable: String, var_type: String },
406
407    /// Logic: P(w) ⊢ ∃x.P(x)
408    /// Carries the witness and its type for kernel certification.
409    ExistentialIntro {
410        witness: String,
411        witness_type: String,
412    },
413
414    // --- Modal Logic (World Moves) ---
415
416    /// Logic: □P (in w0), Accessible(w0, w1) ⊢ P (in w1)
417    /// "Necessity Elimination" / "Distribution"
418    ModalAccess,
419
420    /// Logic: If P is true in ALL accessible worlds ⊢ □P
421    /// "Necessity Introduction"
422    ModalGeneralization,
423
424    // --- Temporal Logic ---
425
426    /// Logic: t1 < t2, t2 < t3 ⊢ t1 < t3
427    TemporalTransitivity,
428
429    // --- Peano / Inductive Reasoning (CIC seed) ---
430
431    /// Logic: P(0), ∀k(P(k) → P(S(k))) ⊢ ∀n P(n)
432    /// Stores the variable name, its inductive type, and the step variable used.
433    StructuralInduction {
434        variable: String,  // "n" - the induction variable
435        ind_type: String,  // "Nat" - the inductive type
436        step_var: String,  // "k" - the predecessor variable (for IH matching)
437    },
438
439    // --- Equality ---
440
441    /// Leibniz's Law / Substitution of Equals
442    /// Logic: a = b, P(a) ⊢ P(b)
443    /// The equality proof is in `premise\[0\]`, the P(a) proof is in `premise\[1\]`.
444    /// Carries the original term and replacement term for certification.
445    Rewrite {
446        from: ProofTerm,
447        to: ProofTerm,
448    },
449
450    /// Symmetry of Equality: a = b ⊢ b = a
451    EqualitySymmetry,
452
453    /// Transitivity of Equality: a = b, b = c ⊢ a = c
454    EqualityTransitivity,
455
456    /// Reflexivity of Equality: a = a (after normalization)
457    /// Used when both sides of an identity reduce to the same normal form.
458    Reflexivity,
459
460    // --- Fallbacks ---
461
462    /// "The User Said So." Used for top-level axioms.
463    Axiom,
464
465    /// "The Machine Said So." (Z3 Oracle)
466    /// The string contains the solver's justification.
467    OracleVerification(String),
468
469    /// Proof by Contradiction (Reductio ad Absurdum)
470    /// Logic: Assume ¬C, derive P ∧ ¬P (contradiction), conclude C
471    /// Or: Assume P, derive Q ∧ ¬Q, conclude ¬P
472    ReductioAdAbsurdum,
473
474    /// Contradiction detected in premises: P and ¬P both hold
475    /// Logic: P, ¬P ⊢ ⊥ (ex falso quodlibet)
476    Contradiction,
477
478    // --- Quantifier Elimination ---
479
480    /// Existential Elimination (Skolemization in a proof context)
481    /// Logic: ∃x.P(x), [c fresh] P(c) ⊢ Goal implies ∃x.P(x) ⊢ Goal
482    /// The witness c must be fresh (not appearing in Goal).
483    ExistentialElim { witness: String },
484
485    /// Case Analysis (Tertium Non Datur / Law of Excluded Middle)
486    /// Logic: (P → ⊥), (¬P → ⊥) ⊢ ⊥
487    /// Used for self-referential paradoxes like the Barber Paradox.
488    CaseAnalysis { case_formula: String },
489}
490
491// =============================================================================
492// DERIVATION TREE - The recursive proof structure
493// =============================================================================
494
495/// The "Euclidean Structure" - A recursive tree representing the proof.
496///
497/// This is the object returned by the Prover. It allows the UI to render
498/// a step-by-step explanation (Natural Language Generation).
499///
500/// # Structure
501///
502/// Each node contains:
503/// - `conclusion`: The proposition proved at this step
504/// - `rule`: The logical rule applied (how we know the conclusion)
505/// - `premises`: Sub-proofs justifying the rule's requirements
506/// - `substitution`: Variable bindings from unification
507///
508/// # See Also
509///
510/// * [`BackwardChainer::prove`] - Creates derivation trees
511/// * [`InferenceRule`] - The rules that justify each step
512/// * [`ProofExpr`] - The conclusion type
513/// * [`certifier::certify`] - Converts trees to kernel terms
514/// * [`hints::suggest_hint`] - Suggests next steps when stuck
515#[derive(Debug, Clone)]
516pub struct DerivationTree {
517    /// The logical statement proved at this node.
518    pub conclusion: ProofExpr,
519
520    /// The rule applied to justify this conclusion.
521    pub rule: InferenceRule,
522
523    /// The sub-proofs that validate the requirements of the rule.
524    /// Example: ModusPonens will have 2 children (The Implication, The Antecedent).
525    pub premises: Vec<DerivationTree>,
526
527    /// The depth of the tree (used for complexity limits).
528    pub depth: usize,
529
530    /// Substitution applied at this step (for unification-based rules).
531    pub substitution: unify::Substitution,
532}
533
534impl DerivationTree {
535    /// Constructs a new Proof Node.
536    /// Automatically calculates depth based on children.
537    pub fn new(
538        conclusion: ProofExpr,
539        rule: InferenceRule,
540        premises: Vec<DerivationTree>,
541    ) -> Self {
542        let max_depth = premises.iter().map(|p| p.depth).max().unwrap_or(0);
543        Self {
544            conclusion,
545            rule,
546            premises,
547            depth: max_depth + 1,
548            substitution: unify::Substitution::new(),
549        }
550    }
551
552    /// A leaf node (usually a Premise, Axiom, or Oracle result).
553    pub fn leaf(conclusion: ProofExpr, rule: InferenceRule) -> Self {
554        Self::new(conclusion, rule, vec![])
555    }
556
557    /// Set the substitution for this derivation step.
558    pub fn with_substitution(mut self, subst: unify::Substitution) -> Self {
559        self.substitution = subst;
560        self
561    }
562
563    /// Renders the proof as a text-based tree (for debugging/CLI).
564    pub fn display_tree(&self) -> String {
565        self.display_recursive(0)
566    }
567
568    fn display_recursive(&self, indent: usize) -> String {
569        let prefix = "  ".repeat(indent);
570
571        let rule_name = match &self.rule {
572            InferenceRule::UniversalInst(var) => format!("UniversalInst({})", var),
573            InferenceRule::UniversalIntro { variable, var_type } => {
574                format!("UniversalIntro({}:{})", variable, var_type)
575            }
576            InferenceRule::ExistentialIntro { witness, witness_type } => {
577                format!("∃Intro({}:{})", witness, witness_type)
578            }
579            InferenceRule::StructuralInduction { variable, ind_type, step_var } => {
580                format!("Induction({}:{}, step={})", variable, ind_type, step_var)
581            }
582            InferenceRule::OracleVerification(s) => format!("Oracle({})", s),
583            InferenceRule::Rewrite { from, to } => format!("Rewrite({} → {})", from, to),
584            InferenceRule::EqualitySymmetry => "EqSymmetry".to_string(),
585            InferenceRule::EqualityTransitivity => "EqTransitivity".to_string(),
586            r => format!("{:?}", r),
587        };
588
589        let mut output = format!("{}└─ [{}] {}\n", prefix, rule_name, self.conclusion);
590
591        for premise in &self.premises {
592            output.push_str(&premise.display_recursive(indent + 1));
593        }
594        output
595    }
596}
597
598impl fmt::Display for DerivationTree {
599    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
600        write!(f, "{}", self.display_tree())
601    }
602}
603
604// =============================================================================
605// PROOF GOAL - The target state for backward chaining
606// =============================================================================
607
608/// The Goal State for the Backward Chainer.
609///
610/// Represents a "hole" in the proof that needs to be filled. During backward
611/// chaining, goals are decomposed into subgoals until they match known facts.
612///
613/// # Fields
614///
615/// * `target` - What we are trying to prove
616/// * `context` - Local assumptions (e.g., antecedents of implications we've entered)
617///
618/// # See Also
619///
620/// * [`BackwardChainer::prove`] - Takes a goal and produces a derivation tree
621/// * [`DerivationTree`] - The result of successfully proving a goal
622/// * [`ProofExpr`] - The target type
623#[derive(Debug, Clone)]
624pub struct ProofGoal {
625    /// What we are trying to prove right now.
626    pub target: ProofExpr,
627
628    /// The local assumptions available (e.g., inside an implication).
629    pub context: Vec<ProofExpr>,
630}
631
632impl ProofGoal {
633    pub fn new(target: ProofExpr) -> Self {
634        Self {
635            target,
636            context: Vec::new(),
637        }
638    }
639
640    pub fn with_context(target: ProofExpr, context: Vec<ProofExpr>) -> Self {
641        Self { target, context }
642    }
643}