code wiki / (root) / nx_ot_replay.nx

nx_ot_replay.nx source

↩ module page · 281 lines · 10287 B

1// nx_ot_replay.nx -- OpenTheory replay engine. 2// 3// Per user 2026-05-15: "world class". Per the comparison-engine 4// commit's named improvement for library_size LOSE_BIG vs HOL Light. 5// 6// OpenTheory is the kernel-independent proof-exchange format from 7// gilith.com/opentheory/article.html. HOL kernels (HOL Light, HOL4, 8// ProofPower, Isabelle/HOL) export their proofs as a list of stack 9// machine instructions; any HOL-compatible kernel can replay them 10// and re-derive each theorem natively. 11// 12// THIS ENGINE: a stack-based interpreter that executes OpenTheory- 13// style instructions. Each opcode that produces a Theorem dispatches 14// to a v2 kernel emitter, so every replayed theorem becomes a real 15// kernel-checked chain in our K2Chain. No third-party trust: the 16// source is the input, OUR kernel is the verifier. 17// 18// Every successful `thm` opcode registers an entry in the supplied 19// LemmaLib with NX_LEMMA_PROVED_NATIVE_REPLAYED status. This is the 20// honest framing per [[feedback-no-third-party-trust-native-or-nothing]]: 21// the proof BODY was replayed natively; the SOURCE is preserved as 22// provenance metadata. 23// 24// What this commit ships: 25// - Stack machine + 8 opcodes (num, term, axiom, refl, mp, assume, 26// imp_intro, thm) 27// - Programmatic replay (caller assembles an instruction array) 28// Named follow-up: 29// - .ot file parser (read ASCII OpenTheory format from file) 30// - Full opcode coverage (absTerm, appTerm, defineConst, subst, 31// deductAntisym, eqMp, etc. -- ~30 total) 32 33// nx_safety_envelope: 34// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 35// sil_target: SIL1 36// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 37// verdict: NOT_YET_EVALUATED 38 39import "nx_lemma_import.nx" 40 41// ===== Sealed opcode enum ========================================== 42const NX_OT_OP_NUM: nx_int = 1 43const NX_OT_OP_TERM: nx_int = 2 44const NX_OT_OP_AXIOM: nx_int = 3 45const NX_OT_OP_REFL: nx_int = 4 46const NX_OT_OP_MP: nx_int = 5 47const NX_OT_OP_ASSUME: nx_int = 6 48const NX_OT_OP_IMP_INTRO: nx_int = 7 49const NX_OT_OP_AND_INTRO: nx_int = 8 50const NX_OT_OP_THM: nx_int = 9 51const NX_OT_OP_NAME: nx_int = 10 52 53// ===== One instruction ============================================= 54// Caller-built array. arg_term and arg_str unused for opcodes that 55// only need the integer arg. 56struct OTInstr { 57 op: nx_int, 58 arg_int: nx_int, 59 arg_term: *Term, 60 arg_str: *u8, 61 arg_str_len: nx_int, 62} 63const NX_OT_INSTR_BYTES: nx_int = 40 64 65func nx_ot_instr_at(prog: *OTInstr, i: nx_int) -> *OTInstr { 66 return ((prog as nx_int) + (i * NX_OT_INSTR_BYTES)) as *OTInstr 67} 68 69// ===== Stack value ================================================= 70// kind = 1 (number), 2 (term), 3 (chain-index of a thm), 4 (name). 71struct OTValue { 72 kind: nx_int, 73 n: nx_int, 74 t: *Term, 75 name: *u8, 76 name_len: nx_int, 77} 78const NX_OT_VAL_BYTES: nx_int = 40 79 80const NX_OT_VAL_NUM: nx_int = 1 81const NX_OT_VAL_TERM: nx_int = 2 82const NX_OT_VAL_THM: nx_int = 3 83const NX_OT_VAL_NAME: nx_int = 4 84 85const NX_OT_STACK_CAP: nx_int = 256 86 87struct OTStack { 88 entries: *OTValue, 89 n: nx_int, 90 cap: nx_int, 91} 92const NX_OT_STACK_BYTES: nx_int = 24 93 94func nx_ot_stack_new() -> *OTStack { 95 let s: *OTStack = (sys_mmap(NX_OT_STACK_BYTES as i64)) as *OTStack 96 s.entries = (sys_mmap((NX_OT_STACK_CAP * NX_OT_VAL_BYTES) as i64)) as *OTValue 97 s.n = 0 98 s.cap = NX_OT_STACK_CAP 99 return s 100} 101 102func nx_ot_stack_at(s: *OTStack, i: nx_int) -> *OTValue { 103 return ((s.entries as nx_int) + (i * NX_OT_VAL_BYTES)) as *OTValue 104} 105 106func nx_ot_stack_push_num(s: *OTStack, n: nx_int) -> nx_int { 107 if s.n >= s.cap { return 0 - 1 } 108 let v: *OTValue = nx_ot_stack_at(s, s.n) 109 v.kind = NX_OT_VAL_NUM 110 v.n = n 111 s.n = s.n + 1 112 return s.n 113} 114 115func nx_ot_stack_push_term(s: *OTStack, t: *Term) -> nx_int { 116 if s.n >= s.cap { return 0 - 1 } 117 let v: *OTValue = nx_ot_stack_at(s, s.n) 118 v.kind = NX_OT_VAL_TERM 119 v.t = t 120 s.n = s.n + 1 121 return s.n 122} 123 124func nx_ot_stack_push_thm(s: *OTStack, chain_idx: nx_int) -> nx_int { 125 if s.n >= s.cap { return 0 - 1 } 126 let v: *OTValue = nx_ot_stack_at(s, s.n) 127 v.kind = NX_OT_VAL_THM 128 v.n = chain_idx 129 s.n = s.n + 1 130 return s.n 131} 132 133func nx_ot_stack_push_name(s: *OTStack, name: *u8, name_len: nx_int) -> nx_int { 134 if s.n >= s.cap { return 0 - 1 } 135 let v: *OTValue = nx_ot_stack_at(s, s.n) 136 v.kind = NX_OT_VAL_NAME 137 v.name = name 138 v.name_len = name_len 139 s.n = s.n + 1 140 return s.n 141} 142 143func nx_ot_stack_pop(s: *OTStack) -> *OTValue { 144 if s.n <= 0 { return 0 as *OTValue } 145 s.n = s.n - 1 146 return nx_ot_stack_at(s, s.n) 147} 148 149// ===== Engine: replay one instruction ============================== 150// Returns 0 on success, negative error code on failure. 151const NX_OT_OK: nx_int = 0 152const NX_OT_ERR_STACK: nx_int = -1 153const NX_OT_ERR_BAD_OP: nx_int = -2 154const NX_OT_ERR_KIND: nx_int = -3 155const NX_OT_ERR_KERNEL: nx_int = -4 156 157func nx_ot_step( 158 instr: *OTInstr, 159 s: *OTStack, 160 ch: *K2Chain, 161 lib: *LemmaLib 162) -> nx_int { 163 if instr.op == NX_OT_OP_NUM { 164 let r: nx_int = nx_ot_stack_push_num(s, instr.arg_int) 165 if r < 0 { return NX_OT_ERR_STACK } 166 return NX_OT_OK 167 } 168 if instr.op == NX_OT_OP_TERM { 169 let r: nx_int = nx_ot_stack_push_term(s, instr.arg_term) 170 if r < 0 { return NX_OT_ERR_STACK } 171 return NX_OT_OK 172 } 173 if instr.op == NX_OT_OP_NAME { 174 let r: nx_int = nx_ot_stack_push_name(s, instr.arg_str, instr.arg_str_len) 175 if r < 0 { return NX_OT_ERR_STACK } 176 return NX_OT_OK 177 } 178 if instr.op == NX_OT_OP_AXIOM { 179 // Pop top term -> emit axiom -> push thm. 180 let v: *OTValue = nx_ot_stack_pop(s) 181 if (v as nx_int) == 0 { return NX_OT_ERR_STACK } 182 if v.kind != NX_OT_VAL_TERM { return NX_OT_ERR_KIND } 183 let idx: nx_int = nx_k2_axiom(ch, v.t) 184 if idx < 0 { return NX_OT_ERR_KERNEL } 185 let _r: nx_int = nx_ot_stack_push_thm(s, idx) 186 return NX_OT_OK 187 } 188 if instr.op == NX_OT_OP_REFL { 189 let v: *OTValue = nx_ot_stack_pop(s) 190 if (v as nx_int) == 0 { return NX_OT_ERR_STACK } 191 if v.kind != NX_OT_VAL_TERM { return NX_OT_ERR_KIND } 192 let idx: nx_int = nx_k2_refl(ch, v.t) 193 if idx < 0 { return NX_OT_ERR_KERNEL } 194 let _r: nx_int = nx_ot_stack_push_thm(s, idx) 195 return NX_OT_OK 196 } 197 if instr.op == NX_OT_OP_ASSUME { 198 let v: *OTValue = nx_ot_stack_pop(s) 199 if (v as nx_int) == 0 { return NX_OT_ERR_STACK } 200 if v.kind != NX_OT_VAL_TERM { return NX_OT_ERR_KIND } 201 let idx: nx_int = nx_k2_assume(ch, v.t) 202 if idx < 0 { return NX_OT_ERR_KERNEL } 203 let _r: nx_int = nx_ot_stack_push_thm(s, idx) 204 return NX_OT_OK 205 } 206 if instr.op == NX_OT_OP_MP { 207 // Stack: ..., (P=>Q)_thm, P_thm -- pop P_thm then imp_thm 208 let v_a: *OTValue = nx_ot_stack_pop(s) 209 if (v_a as nx_int) == 0 { return NX_OT_ERR_STACK } 210 let v_imp: *OTValue = nx_ot_stack_pop(s) 211 if (v_imp as nx_int) == 0 { return NX_OT_ERR_STACK } 212 if v_a.kind != NX_OT_VAL_THM { return NX_OT_ERR_KIND } 213 if v_imp.kind != NX_OT_VAL_THM { return NX_OT_ERR_KIND } 214 let idx: nx_int = nx_k2_modus_ponens(ch, v_imp.n, v_a.n) 215 if idx < 0 { return NX_OT_ERR_KERNEL } 216 let _r: nx_int = nx_ot_stack_push_thm(s, idx) 217 return NX_OT_OK 218 } 219 if instr.op == NX_OT_OP_IMP_INTRO { 220 // Stack: ..., assumption_thm, body_thm -- pop body then assume 221 let v_b: *OTValue = nx_ot_stack_pop(s) 222 if (v_b as nx_int) == 0 { return NX_OT_ERR_STACK } 223 let v_a: *OTValue = nx_ot_stack_pop(s) 224 if (v_a as nx_int) == 0 { return NX_OT_ERR_STACK } 225 if v_b.kind != NX_OT_VAL_THM { return NX_OT_ERR_KIND } 226 if v_a.kind != NX_OT_VAL_THM { return NX_OT_ERR_KIND } 227 let idx: nx_int = nx_k2_imp_intro(ch, v_a.n, v_b.n) 228 if idx < 0 { return NX_OT_ERR_KERNEL } 229 let _r: nx_int = nx_ot_stack_push_thm(s, idx) 230 return NX_OT_OK 231 } 232 if instr.op == NX_OT_OP_AND_INTRO { 233 let v_b: *OTValue = nx_ot_stack_pop(s) 234 if (v_b as nx_int) == 0 { return NX_OT_ERR_STACK } 235 let v_a: *OTValue = nx_ot_stack_pop(s) 236 if (v_a as nx_int) == 0 { return NX_OT_ERR_STACK } 237 if v_b.kind != NX_OT_VAL_THM { return NX_OT_ERR_KIND } 238 if v_a.kind != NX_OT_VAL_THM { return NX_OT_ERR_KIND } 239 let idx: nx_int = nx_k2_and_intro(ch, v_a.n, v_b.n) 240 if idx < 0 { return NX_OT_ERR_KERNEL } 241 let _r: nx_int = nx_ot_stack_push_thm(s, idx) 242 return NX_OT_OK 243 } 244 if instr.op == NX_OT_OP_THM { 245 // Stack: ..., name, thm. Pop both; register in lib with 246 // PROVED_NATIVE_REPLAYED status + provenance = HOL_LIGHT (the 247 // canonical OpenTheory source). 248 let v_thm: *OTValue = nx_ot_stack_pop(s) 249 if (v_thm as nx_int) == 0 { return NX_OT_ERR_STACK } 250 if v_thm.kind != NX_OT_VAL_THM { return NX_OT_ERR_KIND } 251 let v_name: *OTValue = nx_ot_stack_pop(s) 252 if (v_name as nx_int) == 0 { return NX_OT_ERR_STACK } 253 if v_name.kind != NX_OT_VAL_NAME { return NX_OT_ERR_KIND } 254 let thm: *K2Thm = nx_k2_at(ch, v_thm.n) 255 let _i: nx_int = nx_lemma_import( 256 lib, ch, NX_LEMMA_SRC_HOL_LIGHT, 257 v_name.name, v_name.name_len, 258 thm.stmt, NX_LEMMA_PROVED_NATIVE_REPLAYED) 259 return NX_OT_OK 260 } 261 return NX_OT_ERR_BAD_OP 262} 263 264// Replay an entire program. Returns # of `thm` opcodes successfully 265// committed (= number of new lemmas added to lib), or negative on 266// any kernel-rejection (which means the source proof was malformed -- 267// fail loud per the no-third-party-trust cardinal). 268func nx_ot_replay( 269 prog: *OTInstr, n_instr: nx_int, 270 ch: *K2Chain, lib: *LemmaLib 271) -> nx_int { 272 let s: *OTStack = nx_ot_stack_new() 273 let lib_n_before: nx_int = lib.n 274 var i: nx_int = 0 275 while i < n_instr { 276 let r: nx_int = nx_ot_step(nx_ot_instr_at(prog, i), s, ch, lib) 277 if r != NX_OT_OK { return r } 278 i = i + 1 279 } 280 return lib.n - lib_n_before 281}