code wiki / (root) / nx_eq_factor.nx

nx_eq_factor.nx source

↩ module page · 111 lines · 4859 B

1// nx_eq_factor.nx -- equality factoring (Bachmair-Ganzinger 1994). 2// 3// Per Vampire-displacement roadmap Phase 2. Standard inference rule 4// in superposition calculus, paired with paramodulation. Required 5// for completeness when an equational saturation fires. 6// 7// Standard formulation: 8// 9// C ∨ s ≃ t ∨ s' ≃ t' (two positive equality literals) 10// σ = mgu(s, s') 11// -------------------------- 12// (C ∨ s ≃ t ∨ t ≄ t')σ 13// 14// Reading: when two equality literals share a unifiable LHS, ONE of 15// them (here s ≃ t) is kept and the OTHER's RHS is asserted DIFFERENT 16// from the kept RHS (t ≄ t'). Future inference may resolve t≄t' 17// against an equality elsewhere; if t and t' really were equal the 18// resolvent is contradicted, otherwise the disequality survives. 19// 20// Ordering side conditions for full Bachmair-Ganzinger soundness are 21// the caller's responsibility (typically checked via KBO/LPO): 22// - s ≃ t is selected or maximal in C 23// - sσ ⊁ tσ 24// - (s ≃ t)σ ⊁ (s' ≃ t')σ 25// 26// API: caller picks the two equality-literal indices i and j; the 27// primitive validates polarity + equality head, unifies the LHS pair, 28// and emits the residual. 29 30// nx_safety_envelope: 31// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 32// sil_target: SIL1 33// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 34// verdict: NOT_YET_EVALUATED 35 36import "nx_syscalls.nx" 37import "nx_runtime.nx" 38import "nx_tier.nx" 39import "nx_result.nx" 40import "nx_unify.nx" 41import "nx_resolution.nx" 42 43// Build a NEG eq(t, t') literal from two terms. Used to construct 44// the disequality side of the factoring conclusion. 45func nx_eq_factor_make_diseq(t1: *Term, t2: *Term, eq_sym: nx_int) -> *Literal { 46 let args: *Term = (sys_mmap((2 * NX_TERM_BYTES) as i64)) as *Term 47 let a0: *Term = args 48 a0.kind = t1.kind; a0.sym = t1.sym; a0.n_args = t1.n_args; a0.args = t1.args 49 let a1: *Term = ((args as nx_int) + NX_TERM_BYTES) as *Term 50 a1.kind = t2.kind; a1.sym = t2.sym; a1.n_args = t2.n_args; a1.args = t2.args 51 let neq_atom: *Term = nx_term_app(eq_sym, 2, args) 52 return nx_lit_make(NX_LIT_NEG, neq_atom) 53} 54 55// Equality factoring entry. c_out is caller-allocated; populated 56// with the conclusion. Returns Result<n_lits, NX_ERR_*>: 57// NX_ERR_OUT_OF_RANGE -- bad index 58// NX_ERR_TAG_MISMATCH -- chosen literal isn't +eq(_, _) 59// NX_ERR_INVALID_INPUT -- i == j 60// underlying nx_unify err codes if LHS unification fails 61func nx_eq_factor(c: *Clause, i: nx_int, j: nx_int, 62 eq_sym: nx_int, c_out: *Clause) -> *NxResult { 63 if i < 0 { return nx_result_err(NX_ERR_OUT_OF_RANGE) } 64 if j < 0 { return nx_result_err(NX_ERR_OUT_OF_RANGE) } 65 if i == j { return nx_result_err(NX_ERR_INVALID_INPUT) } 66 if i >= c.n_lits { return nx_result_err(NX_ERR_OUT_OF_RANGE) } 67 if j >= c.n_lits { return nx_result_err(NX_ERR_OUT_OF_RANGE) } 68 69 let li: *Literal = nx_clause_lit_at(c, i) 70 let lj: *Literal = nx_clause_lit_at(c, j) 71 72 // Both must be positive equality literals. 73 if li.sign != NX_LIT_POS { return nx_result_err(NX_ERR_TAG_MISMATCH) } 74 if lj.sign != NX_LIT_POS { return nx_result_err(NX_ERR_TAG_MISMATCH) } 75 if li.atom.kind != NX_TERM_APP { return nx_result_err(NX_ERR_TAG_MISMATCH) } 76 if lj.atom.kind != NX_TERM_APP { return nx_result_err(NX_ERR_TAG_MISMATCH) } 77 if li.atom.sym != eq_sym { return nx_result_err(NX_ERR_TAG_MISMATCH) } 78 if lj.atom.sym != eq_sym { return nx_result_err(NX_ERR_TAG_MISMATCH) } 79 if li.atom.n_args != 2 { return nx_result_err(NX_ERR_TAG_MISMATCH) } 80 if lj.atom.n_args != 2 { return nx_result_err(NX_ERR_TAG_MISMATCH) } 81 82 // Pull s, t, s', t'. 83 let s_term: *Term = nx_term_arg(li.atom, 0) 84 let t_term: *Term = nx_term_arg(li.atom, 1) 85 let s2_term: *Term = nx_term_arg(lj.atom, 0) 86 let t2_term: *Term = nx_term_arg(lj.atom, 1) 87 88 // Unify s with s'. 89 let sigma: *Subst = nx_subst_new() 90 let r_un: *NxResult = nx_unify(s_term, s2_term, sigma) 91 if nx_result_is_err(r_un) == 1 { return r_un } 92 93 // Build conclusion: keep all literals of c except j, replace j 94 // with NEG eq(t, t') -- everything under sigma. 95 var k: nx_int = 0 96 while k < c.n_lits { 97 if k != j { 98 let lk: *Literal = nx_clause_lit_at(c, k) 99 let lk_sub: *Literal = nx_lit_apply_subst(lk, sigma) 100 let _r: *NxResult = nx_clause_add(c_out, lk_sub) 101 } 102 k = k + 1 103 } 104 // Append the new disequality t ≄ t' under sigma. 105 let t_sub: *Term = nx_subst_apply(t_term, sigma) 106 let t2_sub: *Term = nx_subst_apply(t2_term, sigma) 107 let diseq: *Literal = nx_eq_factor_make_diseq(t_sub, t2_sub, eq_sym) 108 let _r2: *NxResult = nx_clause_add(c_out, diseq) 109 110 return nx_result_ok(c_out.n_lits) 111}