logicaffeine_proof/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3
4// Number-theory / cryptanalysis substrate — factoring, elliptic/isogeny, lattice, order-finding.
5// These are prover tools (the hardness lens behind isogeny/ait/oracle), not runtime crypto; they
6// depend only on `logicaffeine_base::numeric`.
7pub mod cyclotomic;
8pub mod elliptic;
9pub mod factor;
10pub mod fp2;
11pub mod hyperelliptic;
12pub mod lattice;
13pub mod period;
14
15pub mod arith;
16pub mod cdcl;
17pub mod cnf;
18pub mod dimacs;
19pub mod proof;
20pub mod proof_emit;
21pub mod complexity;
22pub mod ait;
23pub mod isogeny;
24pub mod coalgebra;
25pub mod groupoid;
26pub mod category_collapse;
27pub mod two_group;
28pub mod proof_rewrite;
29pub mod trace_determinism;
30pub mod progress_complex;
31pub mod cubical;
32pub mod kan_complex;
33pub mod two_type;
34pub mod eilenberg_maclane;
35pub mod postnikov;
36pub mod steenrod;
37pub mod pr;
38pub mod affine;
39pub mod affine_gfp;
40pub mod families;
41pub mod gf2;
42pub mod hypercube;
43pub mod census;
44pub mod cofactor;
45pub mod res_width;
46pub mod rup;
47pub mod sat;
48pub mod satcli;
49pub mod solve;
50pub mod xor_drat;
51pub mod xor_engine;
52pub mod bmc;
53pub mod cardinality;
54pub mod counting_principle;
55pub mod matching;
56pub mod ordering;
57pub mod parity_cardinality;
58pub mod pigeonhole;
59pub mod pseudo_boolean;
60pub mod symmetry;
61pub mod symmetry_detect;
62pub mod sym_certify;
63pub mod sym_dynamic;
64pub mod lyapunov;
65pub mod inprocess;
66pub mod sdcl;
67pub mod xorsat;
68pub mod lll;
69pub mod modp;
70pub mod modm;
71pub mod orbit_stability;
72pub mod polycalc;
73pub mod polycalc_gfp;
74pub mod polycalc_zm;
75pub mod sos;
76pub mod permgroup;
77pub mod sym_break;
78pub mod hornsat;
79pub mod twosat;
80pub mod interval_sched;
81pub mod register_alloc;
82pub mod optimize;
83pub mod certifier;
84pub mod counterexample;
85pub mod crush;
86pub mod decide;
87pub mod development;
88pub mod discrimination;
89pub mod lemma_index;
90pub mod engine;
91pub mod simp;
92pub mod formula;
93pub mod tactic;
94pub mod tactic_script;
95pub mod rule_search;
96pub mod linarith_solve;
97pub mod omega_solve;
98pub mod grounding;
99pub mod grid_solver;
100pub mod error;
101pub mod hints;
102pub mod unify;
103pub mod verify;
104
105#[cfg(feature = "verification")]
106pub mod oracle;
107#[cfg(feature = "verification")]
108mod modal_translation;
109
110pub use engine::BackwardChainer;
111pub use error::ProofError;
112pub use hints::{suggest_hint, SocraticHint, SuggestedTactic};
113pub use unify::Substitution;
114
115use std::fmt;
116
117// =============================================================================
118// PROOF TERM - Owned representation of logical terms
119// =============================================================================
120
121/// Owned term representation for proof manipulation.
122///
123/// Decoupled from arena-allocated `Term<'a>` to allow proof trees to persist
124/// beyond the lifetime of the parsing arena.
125///
126/// # See Also
127///
128/// * [`ProofExpr`] - Expression-level representation containing `ProofTerm`s
129/// * [`unify::unify_terms`] - Unification algorithm for terms
130/// * [`unify::Substitution`] - Maps variables to terms
131/// * [`certifier::certify`] - Converts proof trees to kernel terms
132#[derive(Debug, Clone, PartialEq, Eq, Hash)]
133pub enum ProofTerm {
134 /// A constant symbol (e.g., "Socrates", "42")
135 Constant(String),
136
137 /// A variable symbol (e.g., "x", "y")
138 Variable(String),
139
140 /// A function application (e.g., "father(x)", "add(1, 2)")
141 Function(String, Vec<ProofTerm>),
142
143 /// A group/tuple of terms (e.g., "(x, y)")
144 Group(Vec<ProofTerm>),
145
146 /// Reference to a bound variable in a pattern context.
147 /// Distinct from Variable (unification var) and Constant (global name).
148 /// Prevents variable capture bugs during alpha-conversion.
149 BoundVarRef(String),
150}
151
152impl fmt::Display for ProofTerm {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 match self {
155 ProofTerm::Constant(s) => write!(f, "{}", s),
156 ProofTerm::Variable(s) => write!(f, "{}", s),
157 ProofTerm::Function(name, args) => {
158 write!(f, "{}(", name)?;
159 for (i, arg) in args.iter().enumerate() {
160 if i > 0 {
161 write!(f, ", ")?;
162 }
163 write!(f, "{}", arg)?;
164 }
165 write!(f, ")")
166 }
167 ProofTerm::Group(terms) => {
168 write!(f, "(")?;
169 for (i, t) in terms.iter().enumerate() {
170 if i > 0 {
171 write!(f, ", ")?;
172 }
173 write!(f, "{}", t)?;
174 }
175 write!(f, ")")
176 }
177 ProofTerm::BoundVarRef(s) => write!(f, "#{}", s),
178 }
179 }
180}
181
182// =============================================================================
183// PROOF EXPRESSION - Owned representation of logical expressions
184// =============================================================================
185
186/// Owned expression representation for proof manipulation.
187///
188/// Supports all `LogicExpr` variants to enable full language coverage.
189/// This is the "proposition" side of the Curry-Howard correspondence.
190///
191/// # See Also
192///
193/// * [`ProofTerm`] - Terms embedded within expressions (predicate arguments)
194/// * [`unify::unify_exprs`] - Expression-level unification with alpha-equivalence
195/// * [`unify::beta_reduce`] - Normalizes lambda applications
196/// * [`certifier::certify`] - Converts expressions to kernel types
197/// * [`DerivationTree`] - Proof trees conclude with a `ProofExpr`
198#[derive(Debug, Clone, PartialEq)]
199pub enum ProofExpr {
200 // --- Core FOL ---
201
202 /// Atomic predicate: P(t1, t2, ...)
203 Predicate {
204 name: String,
205 args: Vec<ProofTerm>,
206 world: Option<String>,
207 },
208
209 /// Identity: t1 = t2
210 Identity(ProofTerm, ProofTerm),
211
212 /// Propositional atom
213 Atom(String),
214
215 // --- Logical Connectives ---
216
217 /// Conjunction: P ∧ Q
218 And(Box<ProofExpr>, Box<ProofExpr>),
219
220 /// Disjunction: P ∨ Q
221 Or(Box<ProofExpr>, Box<ProofExpr>),
222
223 /// Implication: P → Q
224 Implies(Box<ProofExpr>, Box<ProofExpr>),
225
226 /// Biconditional: P ↔ Q
227 Iff(Box<ProofExpr>, Box<ProofExpr>),
228
229 /// Negation: ¬P
230 Not(Box<ProofExpr>),
231
232 // --- Quantifiers ---
233
234 /// Universal quantification: ∀x P(x)
235 ForAll {
236 variable: String,
237 body: Box<ProofExpr>,
238 },
239
240 /// Existential quantification: ∃x P(x)
241 Exists {
242 variable: String,
243 body: Box<ProofExpr>,
244 },
245
246 // --- Modal Logic ---
247
248 /// Modal operator: □P or ◇P (with world semantics)
249 Modal {
250 domain: String,
251 force: f32,
252 flavor: String,
253 body: Box<ProofExpr>,
254 },
255
256 /// Counterfactual conditional: P □→ Q. Quantifies the consequent over the
257 /// closest antecedent-worlds (a similarity ordering), so it is strictly
258 /// distinct from material implication in both directions.
259 Counterfactual {
260 antecedent: Box<ProofExpr>,
261 consequent: Box<ProofExpr>,
262 },
263
264 // --- Temporal Logic ---
265
266 /// Temporal operator: Past(P), Future(P), Always(P), Eventually(P), Next(P)
267 Temporal {
268 operator: String,
269 body: Box<ProofExpr>,
270 },
271
272 /// Binary temporal operator: Until(P,Q), Release(P,Q), WeakUntil(P,Q)
273 TemporalBinary {
274 operator: String,
275 left: Box<ProofExpr>,
276 right: Box<ProofExpr>,
277 },
278
279 // --- Lambda Calculus (CIC extension) ---
280
281 /// Lambda abstraction: λx.P
282 Lambda {
283 variable: String,
284 body: Box<ProofExpr>,
285 },
286
287 /// Function application: (f x)
288 App(Box<ProofExpr>, Box<ProofExpr>),
289
290 // --- Event Semantics ---
291
292 /// Neo-Davidsonian event: ∃e(Verb(e) ∧ Agent(e,x) ∧ ...)
293 NeoEvent {
294 event_var: String,
295 verb: String,
296 roles: Vec<(String, ProofTerm)>,
297 },
298
299 // --- Peano / Inductive Types (CIC Extension) ---
300
301 /// Data Constructor: Zero, Succ(n), Cons(h, t), etc.
302 /// The building blocks of inductive types.
303 Ctor {
304 name: String,
305 args: Vec<ProofExpr>,
306 },
307
308 /// Pattern Matching: match n { Zero => A, Succ(k) => B }
309 /// Eliminates inductive types by case analysis.
310 Match {
311 scrutinee: Box<ProofExpr>,
312 arms: Vec<MatchArm>,
313 },
314
315 /// Recursive Function (Fixpoint): fix f. λn. ...
316 /// Defines recursive functions over inductive types.
317 Fixpoint {
318 name: String,
319 body: Box<ProofExpr>,
320 },
321
322 /// Typed Variable: n : Nat
323 /// Signals to the prover that induction may be applicable.
324 TypedVar {
325 name: String,
326 typename: String,
327 },
328
329 // --- Higher-Order Pattern Unification ---
330
331 /// Meta-variable (unification hole) to be solved during proof search.
332 /// Represents an unknown expression, typically a function or predicate.
333 /// Example: Hole("P") in ?P(x) = Body, to be solved as P = λx.Body
334 Hole(String),
335
336 /// Embedded term (lifts ProofTerm into ProofExpr context).
337 /// Used when a term appears where an expression is expected.
338 /// Example: App(Hole("P"), Term(BoundVarRef("x")))
339 Term(ProofTerm),
340
341 // --- Fallback ---
342
343 /// Unsupported construct (with description for debugging)
344 Unsupported(String),
345}
346
347// =============================================================================
348// MATCH ARM - A single case in pattern matching
349// =============================================================================
350
351/// A single arm in a match expression.
352/// Example: Succ(k) => Add(k, m)
353#[derive(Debug, Clone, PartialEq)]
354pub struct MatchArm {
355 /// The constructor being matched: "Zero", "Succ", "Cons", etc.
356 pub ctor: String,
357
358 /// Variable bindings for constructor arguments: ["k"] for Succ(k)
359 pub bindings: Vec<String>,
360
361 /// The expression to evaluate when this arm matches
362 pub body: ProofExpr,
363}
364
365impl fmt::Display for ProofExpr {
366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367 match self {
368 ProofExpr::Predicate { name, args, world } => {
369 write!(f, "{}", name)?;
370 if !args.is_empty() {
371 write!(f, "(")?;
372 for (i, arg) in args.iter().enumerate() {
373 if i > 0 {
374 write!(f, ", ")?;
375 }
376 write!(f, "{}", arg)?;
377 }
378 write!(f, ")")?;
379 }
380 if let Some(w) = world {
381 write!(f, " @{}", w)?;
382 }
383 Ok(())
384 }
385 ProofExpr::Identity(left, right) => write!(f, "{} = {}", left, right),
386 ProofExpr::Atom(s) => write!(f, "{}", s),
387 ProofExpr::And(left, right) => write!(f, "({} ∧ {})", left, right),
388 ProofExpr::Or(left, right) => write!(f, "({} ∨ {})", left, right),
389 ProofExpr::Implies(left, right) => write!(f, "({} → {})", left, right),
390 ProofExpr::Iff(left, right) => write!(f, "({} ↔ {})", left, right),
391 ProofExpr::Not(inner) => write!(f, "¬{}", inner),
392 ProofExpr::ForAll { variable, body } => write!(f, "∀{} {}", variable, body),
393 ProofExpr::Exists { variable, body } => write!(f, "∃{} {}", variable, body),
394 ProofExpr::Modal { domain, force, flavor, body } => {
395 write!(f, "□[{}/{}/{}]{}", domain, force, flavor, body)
396 }
397 ProofExpr::Counterfactual { antecedent, consequent } => {
398 write!(f, "({} □→ {})", antecedent, consequent)
399 }
400 ProofExpr::Temporal { operator, body } => write!(f, "{}({})", operator, body),
401 ProofExpr::TemporalBinary { operator, left, right } => {
402 write!(f, "TemporalBinary({}, {}, {})", operator, left, right)
403 }
404 ProofExpr::Lambda { variable, body } => write!(f, "λ{}.{}", variable, body),
405 ProofExpr::App(func, arg) => write!(f, "({} {})", func, arg),
406 ProofExpr::NeoEvent { event_var, verb, roles } => {
407 write!(f, "∃{}({}({})", event_var, verb, event_var)?;
408 for (role, term) in roles {
409 write!(f, " ∧ {}({}, {})", role, event_var, term)?;
410 }
411 write!(f, ")")
412 }
413 ProofExpr::Ctor { name, args } => {
414 write!(f, "{}", name)?;
415 if !args.is_empty() {
416 write!(f, "(")?;
417 for (i, arg) in args.iter().enumerate() {
418 if i > 0 {
419 write!(f, ", ")?;
420 }
421 write!(f, "{}", arg)?;
422 }
423 write!(f, ")")?;
424 }
425 Ok(())
426 }
427 ProofExpr::Match { scrutinee, arms } => {
428 write!(f, "match {} {{ ", scrutinee)?;
429 for (i, arm) in arms.iter().enumerate() {
430 if i > 0 {
431 write!(f, ", ")?;
432 }
433 write!(f, "{}", arm.ctor)?;
434 if !arm.bindings.is_empty() {
435 write!(f, "({})", arm.bindings.join(", "))?;
436 }
437 write!(f, " => {}", arm.body)?;
438 }
439 write!(f, " }}")
440 }
441 ProofExpr::Fixpoint { name, body } => write!(f, "fix {}.{}", name, body),
442 ProofExpr::TypedVar { name, typename } => write!(f, "{}:{}", name, typename),
443 ProofExpr::Unsupported(desc) => write!(f, "⟨unsupported: {}⟩", desc),
444 ProofExpr::Hole(name) => write!(f, "?{}", name),
445 ProofExpr::Term(term) => write!(f, "{}", term),
446 }
447 }
448}
449
450// =============================================================================
451// INFERENCE RULE - The logical moves available to the prover
452// =============================================================================
453
454/// The "Lever" - The specific logical move made at each proof step.
455///
456/// This enum captures HOW we moved from premises to conclusion. Each variant
457/// corresponds to a different proof strategy or logical rule.
458///
459/// # See Also
460///
461/// * [`DerivationTree`] - Each node contains an `InferenceRule`
462/// * [`certifier::certify`] - Maps inference rules to kernel terms
463/// * [`hints::suggest_hint`] - Suggests applicable rules when stuck
464/// * [`BackwardChainer`] - The engine that selects and applies rules
465///
466/// One constructor's case in a generic induction scheme
467/// ([`InferenceRule::InductionScheme`]). The derivation's premises align 1:1 with
468/// the cases, in the inductive's constructor-registration order.
469#[derive(Debug, Clone, PartialEq)]
470pub struct InductionCase {
471 /// The constructor this case eliminates (e.g. `"Succ"`, `"Node"`).
472 pub constructor: String,
473 /// The constructor's arguments, in order — each bound in the case body.
474 pub args: Vec<InductionArg>,
475}
476
477/// One argument of a constructor in an [`InductionCase`].
478#[derive(Debug, Clone, PartialEq)]
479pub struct InductionArg {
480 /// The name bound for this argument in the case body.
481 pub name: String,
482 /// Whether this argument is itself of the inductive type — a recursive
483 /// position, which therefore carries an induction hypothesis.
484 pub recursive: bool,
485}
486
487#[derive(Debug, Clone, PartialEq)]
488pub enum InferenceRule {
489 // --- Basic FOL ---
490
491 /// Direct match with a known fact in the Context/KnowledgeBase.
492 /// Logic: Γ, P ⊢ P
493 PremiseMatch,
494
495 /// Logic: P → Q, P ⊢ Q
496 ModusPonens,
497
498 /// Logic: ¬Q, P → Q ⊢ ¬P
499 ModusTollens,
500
501 /// Logic: P, Q ⊢ P ∧ Q
502 ConjunctionIntro,
503
504 /// Logic: P ∧ Q ⊢ P (or Q)
505 ConjunctionElim,
506
507 /// Logic: P ⊢ P ∨ Q
508 DisjunctionIntro,
509
510 /// Logic: P ∨ Q, P → R, Q → R ⊢ R
511 DisjunctionElim,
512
513 /// Logic: ⊥ ⊢ anything (ex falso quodlibet). The single premise concludes `False`.
514 ExFalso,
515
516 /// Logic: assume P, derive Q ⊢ P → Q (implication introduction / →I). The
517 /// single premise proves Q with P bound as a local hypothesis.
518 ImpliesIntro,
519
520 /// Logic: prove P → Q and Q → P ⊢ P ↔ Q (biconditional introduction / ↔I).
521 BicondIntro,
522
523 /// Logic: ¬¬P ⊢ P (and P ⊢ ¬¬P)
524 DoubleNegation,
525
526 /// Logic: classical reductio (proof by contradiction) — assume ¬G, derive ⊥ ⊢ G,
527 /// discharged through the `dne` axiom. The single premise concludes `False` with
528 /// ¬G bound as a local hypothesis.
529 ClassicalReductio,
530
531 // --- Quantifiers ---
532
533 /// Logic: ∀x P(x) ⊢ P(c)
534 /// Stores the specific term 'c' used to instantiate the universal.
535 UniversalInst(String),
536
537 /// Logic: ∀x P(x) ⊢ P(t) at an arbitrary witness TERM (a compound like
538 /// `add(a, Zero)`, not just a name). `UniversalInst` keeps the name-only
539 /// fast path; this carries the full term for instantiations that
540 /// `simp`/`crush` produce by matching.
541 UniversalInstTerm(ProofTerm),
542
543 /// Logic: Γ, x:T ⊢ P(x) implies Γ ⊢ ∀x:T. P(x)
544 /// Stores variable name and type name for Lambda construction.
545 UniversalIntro { variable: String, var_type: String },
546
547 /// Logic: P(w) ⊢ ∃x.P(x)
548 /// Carries the witness and its type for kernel certification.
549 ExistentialIntro {
550 witness: String,
551 witness_type: String,
552 },
553
554 // --- Modal Logic (World Moves) ---
555
556 /// Logic: □P (in w0), Accessible(w0, w1) ⊢ P (in w1)
557 /// "Necessity Elimination" / "Distribution"
558 ModalAccess,
559
560 /// Logic: If P is true in ALL accessible worlds ⊢ □P
561 /// "Necessity Introduction"
562 ModalGeneralization,
563
564 // --- Temporal Logic ---
565
566 /// Logic: t1 < t2, t2 < t3 ⊢ t1 < t3
567 TemporalTransitivity,
568
569 /// Logic: P(s₀), ∀s(P(s) → P(next(s))) ⊢ G(P)
570 /// Standard k-induction for hardware safety properties.
571 TemporalInduction,
572
573 /// Logic: G(P) ⊢ P ∧ X(G(P))
574 /// Fixpoint unfolding of Always.
575 TemporalUnfolding,
576
577 /// Logic: P(w) ⊢ F(P) for witness world w
578 /// Prove Eventually by exhibiting a reachable witness.
579 EventualityProgress,
580
581 /// Logic: Induction on trace length for P U Q
582 UntilInduction,
583
584 // --- Peano / Inductive Reasoning (CIC seed) ---
585
586 /// Logic: P(0), ∀k(P(k) → P(S(k))) ⊢ ∀n P(n)
587 /// Stores the variable name, its inductive type, and the step variable used.
588 StructuralInduction {
589 variable: String, // "n" - the induction variable
590 ind_type: String, // "Nat" - the inductive type
591 step_var: String, // "k" - the predecessor variable (for IH matching)
592 },
593
594 /// Logic: generic structural induction over ANY inductive type. Generalizes
595 /// [`InferenceRule::StructuralInduction`] (fixed to the nullary-base + unary-step
596 /// Nat shape) to an arbitrary constructor set — one premise per constructor, in
597 /// registration order, each recursive argument carrying its own induction
598 /// hypothesis. Certifies to a `Fix` over an N-ary `Match`: the dependent
599 /// eliminator the kernel re-checks for coverage, case types, and termination.
600 InductionScheme {
601 variable: String, // the induction variable, e.g. "t"
602 ind_type: String, // the inductive type, e.g. "Tree"
603 cases: Vec<InductionCase>, // one per constructor, in registration order
604 },
605
606 // --- Linear arithmetic (order) ---
607
608 /// `a ≤ b`, `b ≤ c` ⊢ `a ≤ c` over `Int`. The middle term is recovered from
609 /// the first premise's conclusion. Certifies to `le_trans a b c p₀ p₁`.
610 /// Inequalities are encoded as the Prop `Eq Bool (le a b) true`.
611 LeTrans,
612
613 /// `⊢ a ≤ a` over `Int`. Certifies to `le_refl a`.
614 LeRefl,
615
616 /// `a ≤ b`, `c ≤ d` ⊢ `a + c ≤ b + d` over `Int`. The four operands are read
617 /// from the conclusion `le(add a c, add b d) = true`; `premise[0]` proves
618 /// `a ≤ b`, `premise[1]` proves `c ≤ d`. Certifies to `le_add_mono a b c d p₀ p₁`.
619 LeAddMono,
620
621 /// Linear contradiction: `premise[0]` proves `le(m, n) = true` for ground `m > n`
622 /// (so `le m n ⇝ false`, the Prop is `Eq Bool false true`). Concludes `⊥` via the
623 /// `Bool` no-confusion discriminator. Lets contradictory bounds prove anything.
624 LinFalse,
625
626 /// `0 ≤ k`, `a ≤ b` ⊢ `k·a ≤ k·b` — scale an inequality by a non-negative `k`.
627 /// Operands from the conclusion `le(mul k a, mul k b) = true`; `premise[0]` proves
628 /// `0 ≤ k`, `premise[1]` proves `a ≤ b`. Certifies to `le_mul_nonneg k a b p₀ p₁`.
629 /// A Farkas-reconstruction primitive.
630 LeMulNonneg,
631
632 /// `a ≤ b` ⊢ `0 ≤ b - a` — move an inequality to one side. Operands from the
633 /// conclusion `le(0, sub b a) = true`; `premise[0]` proves `a ≤ b`. Certifies to
634 /// `le_sub a b p₀`. The Farkas "collect to a single side" primitive.
635 LeSub,
636
637 /// `a < b` ⊢ `(a + 1) ≤ b` — integer DISCRETENESS. Operands from the conclusion
638 /// `le(add a 1, b) = true`; `premise[0]` proves `a < b` (`lt(a,b) = true`).
639 /// Certifies to `lt_succ_le a b p₀`. This is the one step rational Fourier-Motzkin
640 /// lacks — the `omega` primitive that refutes strict systems the rational solver
641 /// reports satisfiable.
642 LtSuccLe,
643
644 /// `a < (b + 1)` ⊢ `a ≤ b` — the upper-side discreteness companion. Operands
645 /// from the conclusion `le(a, b) = true`; `premise[0]` proves `a < b+1`
646 /// (`lt(a, add b 1) = true`). Certifies to `lt_add1_le a b p₀`. Preferred over
647 /// `LtSuccLe` when the strict bound is already `b+1`, since it cancels the
648 /// constant instead of propagating it into the Farkas reconstruction.
649 LtAdd1Le,
650
651 // --- Equality ---
652
653 /// Leibniz's Law / Substitution of Equals
654 /// Logic: a = b, P(a) ⊢ P(b)
655 /// The equality proof is in `premise\[0\]`, the P(a) proof is in `premise\[1\]`.
656 /// Carries the original term and replacement term for certification.
657 Rewrite {
658 from: ProofTerm,
659 to: ProofTerm,
660 },
661
662 /// Symmetry of Equality: a = b ⊢ b = a
663 EqualitySymmetry,
664
665 /// Transitivity of Equality: a = b, b = c ⊢ a = c
666 EqualityTransitivity,
667
668 /// Reflexivity of Equality: a = a (after normalization)
669 /// Used when both sides of an identity reduce to the same normal form.
670 Reflexivity,
671
672 /// Arithmetic decision: an `Int` equality discharged by the proof-producing
673 /// arithmetic oracle ([`crate::arith::prove_int_eq`]) into a kernel-checked
674 /// proof (computation + the ring axioms). The conclusion is the `Identity`.
675 ArithDecision,
676
677 /// Proof by kernel evaluation: a closed decidable proposition (a ground
678 /// comparison or Bool/Nat equality) discharged via `native_decide`. The
679 /// leaf carries only the claim; certification re-runs the evaluator and
680 /// the kernel checks the resulting `of_decide_eq_true`/`ofReduceBool`
681 /// term, so a lying leaf is rejected.
682 NativeDecide,
683
684 // --- Fallbacks ---
685
686 /// "The User Said So." Used for top-level axioms.
687 Axiom,
688
689 /// "The Machine Said So." (Z3 Oracle)
690 /// The string contains the solver's justification.
691 OracleVerification(String),
692
693 /// Proof by Contradiction (Reductio ad Absurdum)
694 /// Logic: Assume ¬C, derive P ∧ ¬P (contradiction), conclude C
695 /// Or: Assume P, derive Q ∧ ¬Q, conclude ¬P
696 ReductioAdAbsurdum,
697
698 /// Contradiction detected in premises: P and ¬P both hold
699 /// Logic: P, ¬P ⊢ ⊥ (ex falso quodlibet)
700 Contradiction,
701
702 // --- Quantifier Elimination ---
703
704 /// Existential Elimination (Skolemization in a proof context)
705 /// Logic: ∃x.P(x), [c fresh] P(c) ⊢ Goal implies ∃x.P(x) ⊢ Goal
706 /// The witness c must be fresh (not appearing in Goal).
707 ExistentialElim { witness: String },
708
709 /// Case Analysis on a formula `C` whose two cases both reach absurdity.
710 /// Logic: (C → ⊥), (¬C → ⊥) ⊢ ⊥ — note this is the *intuitionistic* form
711 /// (build `¬C` and `¬¬C`, then apply), so certifying it needs no excluded
712 /// middle. Used for self-referential paradoxes like the Barber Paradox.
713 ///
714 /// `case_formula` carries the actual proposition `C` (not a rendered string)
715 /// so the certifier can build the case lambdas' parameter types and bind `C`
716 /// / `¬C` as local hypotheses in each branch.
717 CaseAnalysis { case_formula: Box<ProofExpr> },
718
719 /// Logic: `A ∨ B`, `A ⊢ C`, `B ⊢ C` ⊢ `C` — disjunction elimination to a common
720 /// conclusion (here always `⊥`, for the grounded-grid contradiction prover).
721 /// Premises: `[A∨B, left-branch (C assuming A), right-branch (C assuming B)]`.
722 /// Unlike `DisjunctionElim` (disjunctive syllogism, which needs a refuted
723 /// disjunct) this eliminates BOTH disjuncts by case analysis — the move a grid's
724 /// of-pair / either-or / closure clause needs. The disjuncts (and, when a disjunct
725 /// is a conjunction, each of its conjuncts) are bound as local hypotheses in the
726 /// respective branch, so a branch may reference them directly.
727 DisjunctionCases,
728}
729
730// =============================================================================
731// DERIVATION TREE - The recursive proof structure
732// =============================================================================
733
734/// The "Euclidean Structure" - A recursive tree representing the proof.
735///
736/// This is the object returned by the Prover. It allows the UI to render
737/// a step-by-step explanation (Natural Language Generation).
738///
739/// # Structure
740///
741/// Each node contains:
742/// - `conclusion`: The proposition proved at this step
743/// - `rule`: The logical rule applied (how we know the conclusion)
744/// - `premises`: Sub-proofs justifying the rule's requirements
745/// - `substitution`: Variable bindings from unification
746///
747/// # See Also
748///
749/// * [`BackwardChainer::prove`] - Creates derivation trees
750/// * [`InferenceRule`] - The rules that justify each step
751/// * [`ProofExpr`] - The conclusion type
752/// * [`certifier::certify`] - Converts trees to kernel terms
753/// * [`hints::suggest_hint`] - Suggests next steps when stuck
754#[derive(Debug, Clone)]
755pub struct DerivationTree {
756 /// The logical statement proved at this node.
757 pub conclusion: ProofExpr,
758
759 /// The rule applied to justify this conclusion.
760 pub rule: InferenceRule,
761
762 /// The sub-proofs that validate the requirements of the rule.
763 /// Example: ModusPonens will have 2 children (The Implication, The Antecedent).
764 pub premises: Vec<DerivationTree>,
765
766 /// The depth of the tree (used for complexity limits).
767 pub depth: usize,
768
769 /// Substitution applied at this step (for unification-based rules).
770 pub substitution: unify::Substitution,
771}
772
773impl DerivationTree {
774 /// Constructs a new Proof Node.
775 /// Automatically calculates depth based on children.
776 pub fn new(
777 conclusion: ProofExpr,
778 rule: InferenceRule,
779 premises: Vec<DerivationTree>,
780 ) -> Self {
781 let max_depth = premises.iter().map(|p| p.depth).max().unwrap_or(0);
782 Self {
783 conclusion,
784 rule,
785 premises,
786 depth: max_depth + 1,
787 substitution: unify::Substitution::new(),
788 }
789 }
790
791 /// A leaf node (usually a Premise, Axiom, or Oracle result).
792 pub fn leaf(conclusion: ProofExpr, rule: InferenceRule) -> Self {
793 Self::new(conclusion, rule, vec![])
794 }
795
796 /// Set the substitution for this derivation step.
797 pub fn with_substitution(mut self, subst: unify::Substitution) -> Self {
798 self.substitution = subst;
799 self
800 }
801
802 /// Renders the proof as a text-based tree (for debugging/CLI).
803 pub fn display_tree(&self) -> String {
804 self.display_recursive(0)
805 }
806
807 fn display_recursive(&self, indent: usize) -> String {
808 let prefix = " ".repeat(indent);
809
810 let rule_name = match &self.rule {
811 InferenceRule::UniversalInst(var) => format!("UniversalInst({})", var),
812 InferenceRule::UniversalInstTerm(term) => format!("UniversalInst({})", term),
813 InferenceRule::UniversalIntro { variable, var_type } => {
814 format!("UniversalIntro({}:{})", variable, var_type)
815 }
816 InferenceRule::ExistentialIntro { witness, witness_type } => {
817 format!("∃Intro({}:{})", witness, witness_type)
818 }
819 InferenceRule::StructuralInduction { variable, ind_type, step_var } => {
820 format!("Induction({}:{}, step={})", variable, ind_type, step_var)
821 }
822 InferenceRule::OracleVerification(s) => format!("Oracle({})", s),
823 InferenceRule::Rewrite { from, to } => format!("Rewrite({} → {})", from, to),
824 InferenceRule::EqualitySymmetry => "EqSymmetry".to_string(),
825 InferenceRule::EqualityTransitivity => "EqTransitivity".to_string(),
826 r => format!("{:?}", r),
827 };
828
829 let mut output = format!("{}└─ [{}] {}\n", prefix, rule_name, self.conclusion);
830
831 for premise in &self.premises {
832 output.push_str(&premise.display_recursive(indent + 1));
833 }
834 output
835 }
836}
837
838impl fmt::Display for DerivationTree {
839 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
840 write!(f, "{}", self.display_tree())
841 }
842}
843
844// =============================================================================
845// PROOF GOAL - The target state for backward chaining
846// =============================================================================
847
848/// The Goal State for the Backward Chainer.
849///
850/// Represents a "hole" in the proof that needs to be filled. During backward
851/// chaining, goals are decomposed into subgoals until they match known facts.
852///
853/// # Fields
854///
855/// * `target` - What we are trying to prove
856/// * `context` - Local assumptions (e.g., antecedents of implications we've entered)
857///
858/// # See Also
859///
860/// * [`BackwardChainer::prove`] - Takes a goal and produces a derivation tree
861/// * [`DerivationTree`] - The result of successfully proving a goal
862/// * [`ProofExpr`] - The target type
863#[derive(Debug, Clone)]
864pub struct ProofGoal {
865 /// What we are trying to prove right now.
866 pub target: ProofExpr,
867
868 /// The local assumptions available (e.g., inside an implication).
869 pub context: Vec<ProofExpr>,
870}
871
872impl ProofGoal {
873 pub fn new(target: ProofExpr) -> Self {
874 Self {
875 target,
876 context: Vec::new(),
877 }
878 }
879
880 pub fn with_context(target: ProofExpr, context: Vec<ProofExpr>) -> Self {
881 Self { target, context }
882 }
883}