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}