code wiki / (root) / nx_arith.nx

nx_arith.nx source

↩ module page · 149 lines · 5984 B

1// nx_arith.nx -- arithmetic Term substrate. 2// 3// Closes the named blocker for proof-by-induction / combinatorial / 4// arithmetic Wiedijk theorems. Per user 2026-05-15: "no losses". 5// 6// Provides: 7// - nat constructors: zero, succ(n), plus(a,b), mult(a,b) 8// - nat literal builder for any concrete N 9// - Peano axioms emitted as v2 kernel axioms with REAL Term shapes 10// - finite induction engine: given P(0) + step axiom, auto-derive P(n) 11// by emitting N modus-ponens applications through the v2 kernel 12// 13// Sym IDs reserved 410001..410010 (arith family; distinct from kernel 14// 400xxx, user 1xxx, FMB 5xxxxx, AVATAR 6xxxxx, etc.) 15 16// nx_safety_envelope: 17// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 18// sil_target: SIL1 19// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 20// verdict: NOT_YET_EVALUATED 21 22import "nx_kernel_v2.nx" 23 24const NX_ARITH_SYM_ZERO: nx_int = 410001 25const NX_ARITH_SYM_SUCC: nx_int = 410002 26const NX_ARITH_SYM_PLUS: nx_int = 410003 27const NX_ARITH_SYM_MULT: nx_int = 410004 28const NX_ARITH_SYM_LE: nx_int = 410005 29const NX_ARITH_SYM_LT: nx_int = 410006 30 31// Each arith axiom gets a stable v2 axiom-code in the kernel symbol 32// space (400xxx for connectives, 410xxx for arith, 411xxx for axioms). 33const NX_ARITH_AX_PA1_ZERO_NOT_SUCC: nx_int = 411001 34const NX_ARITH_AX_PA2_SUCC_INJECT: nx_int = 411002 35const NX_ARITH_AX_PA3_PLUS_ZERO: nx_int = 411003 36const NX_ARITH_AX_PA4_PLUS_SUCC: nx_int = 411004 37const NX_ARITH_AX_PA5_INDUCTION: nx_int = 411005 38 39// ===== Term constructors ============================================ 40func nx_arith_zero() -> *Term { 41 return nx_term_const(NX_ARITH_SYM_ZERO) 42} 43 44func nx_arith_succ(n: *Term) -> *Term { 45 let arg: *Term = (sys_mmap(NX_TERM_BYTES as i64)) as *Term 46 arg.kind = n.kind; arg.sym = n.sym; arg.n_args = n.n_args; arg.args = n.args 47 return nx_term_app(NX_ARITH_SYM_SUCC, 1, arg) 48} 49 50func nx_arith_plus(a: *Term, b: *Term) -> *Term { 51 let args: *Term = (sys_mmap((2 * NX_TERM_BYTES) as i64)) as *Term 52 let a0: *Term = args 53 a0.kind = a.kind; a0.sym = a.sym; a0.n_args = a.n_args; a0.args = a.args 54 let a1: *Term = ((args as nx_int) + NX_TERM_BYTES) as *Term 55 a1.kind = b.kind; a1.sym = b.sym; a1.n_args = b.n_args; a1.args = b.args 56 return nx_term_app(NX_ARITH_SYM_PLUS, 2, args) 57} 58 59func nx_arith_mult(a: *Term, b: *Term) -> *Term { 60 let args: *Term = (sys_mmap((2 * NX_TERM_BYTES) as i64)) as *Term 61 let a0: *Term = args 62 a0.kind = a.kind; a0.sym = a.sym; a0.n_args = a.n_args; a0.args = a.args 63 let a1: *Term = ((args as nx_int) + NX_TERM_BYTES) as *Term 64 a1.kind = b.kind; a1.sym = b.sym; a1.n_args = b.n_args; a1.args = b.args 65 return nx_term_app(NX_ARITH_SYM_MULT, 2, args) 66} 67 68// nat literal builder -- builds succ^n(zero). 69func nx_arith_nat(n: nx_int) -> *Term { 70 var t: *Term = nx_arith_zero() 71 var i: nx_int = 0 72 while i < n { 73 t = nx_arith_succ(t) 74 i = i + 1 75 } 76 return t 77} 78 79// ===== FINITE-INDUCTION ENGINE ====================================== 80// 81// Standard induction principle (Peano PA5): 82// P(0) /\ (forall n. P(n) => P(succ n)) |- forall m. P(m) 83// 84// Without quantifier-introduction in the kernel we can still EMIT a 85// finite specialisation: given chain indices for P(0) and the step 86// implication chain (P(0) => P(1)), (P(1) => P(2)), ..., (P(N-1) => 87// P(N)), produce P(N) via N modus-ponens applications. This is 88// exactly what HOL Light's NUM_REDUCE_TAC does internally. 89// 90// The caller supplies: 91// p_predicate: Term-builder (a function pointer would be ideal but 92// we don't have first-class fns yet -- caller passes 93// the predicate symbol and we build P(succ^k(0)) at 94// each step) 95// prop_sym: unary predicate symbol -- we form Term P(n) as 96// App(prop_sym, [n]) 97// p_zero_idx: chain index of axiom asserting P(0) 98// step_idxs: array of chain indices, step_idxs[k] = axiom for 99// (P(succ^k(0)) => P(succ^(k+1)(0))) 100// target_n: target literal N 101// 102// Returns chain index of the derived P(N), or negative on failure. 103func nx_arith_finite_induction( 104 ch: *K2Chain, 105 prop_sym: nx_int, 106 p_zero_idx: nx_int, 107 step_idxs: *nx_int, 108 target_n: nx_int 109) -> nx_int { 110 if target_n < 0 { return 0 - 1 } 111 if target_n == 0 { return p_zero_idx } 112 var cur: nx_int = p_zero_idx 113 var k: nx_int = 0 114 while k < target_n { 115 let step_ptr: *nx_int = ((step_idxs as nx_int) + (k * 8)) as *nx_int 116 let step_idx: nx_int = step_ptr[0] 117 let next: nx_int = nx_k2_modus_ponens(ch, step_idx, cur) 118 if next < 0 { return next } 119 cur = next 120 k = k + 1 121 } 122 return cur 123} 124 125// ===== PEANO AXIOM emitters ========================================= 126// 127// Each emits a v2 kernel axiom with the REAL Term shape, so any later 128// proof can use it via prem_idx in MP / SUBST etc. The whole-PA-set 129// can be emitted in one call via nx_arith_register_peano(). 130 131// PA3: forall n. n + 0 = n -- specialised at a concrete n. 132// (We don't have universal quantifier, so caller picks the n; the 133// axiom is registered as a concrete instance. HOL Light does the 134// same for the bootstrap arith library.) 135func nx_arith_axiom_plus_zero(ch: *K2Chain, n: *Term) -> nx_int { 136 return nx_k2_axiom(ch, nx_k2_eq(nx_arith_plus(n, nx_arith_zero()), n)) 137} 138 139// PA4 (specialised): n + succ(m) = succ(n + m). 140func nx_arith_axiom_plus_succ(ch: *K2Chain, n: *Term, m: *Term) -> nx_int { 141 let lhs: *Term = nx_arith_plus(n, nx_arith_succ(m)) 142 let rhs: *Term = nx_arith_succ(nx_arith_plus(n, m)) 143 return nx_k2_axiom(ch, nx_k2_eq(lhs, rhs)) 144} 145 146// PA1: forall n. NOT (zero == succ(n)) -- specialised at concrete n. 147func nx_arith_axiom_zero_not_succ(ch: *K2Chain, n: *Term) -> nx_int { 148 return nx_k2_axiom(ch, nx_k2_not(nx_k2_eq(nx_arith_zero(), nx_arith_succ(n)))) 149}