code wiki / (root) / nx_proof_log.nx

nx_proof_log.nx source

↩ module page · 121 lines · 4803 B

1// nx_proof_log.nx -- inference history tracking. 2// 3// Side table recording, per derived clause, the inference rule + parent 4// clause indices. Foundation for TSTP-format proof certificate 5// emission required by CASC submissions (LOSE -> WIN on the 6// "proof certificate output" axis of the honest CASC verdict). 7// 8// API: 9// nx_proof_log_new(cap) -> *ProofLog 10// nx_proof_log_add(log, rule, parent_a, parent_b) -> entry_idx 11// nx_proof_log_walk_unsat(log, empty_idx, visited[]) -> count 12// (transitive parent walk from the empty clause back to inputs -- 13// the "minimal proof support" set) 14// 15// Caller threading is the saturation loop's responsibility. A clause 16// index in `log` corresponds 1:1 to its index in the clause set the 17// loop manages. Input clauses get NX_PROOF_RULE_INPUT with parents -1. 18 19// nx_safety_envelope: 20// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 21// sil_target: SIL1 22// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 23// verdict: NOT_YET_EVALUATED 24 25import "nx_syscalls.nx" 26import "nx_runtime.nx" 27import "nx_tier.nx" 28import "nx_result.nx" 29 30// Sealed rule enum. 31const NX_PROOF_RULE_INPUT: nx_int = 0 32const NX_PROOF_RULE_RES: nx_int = 1 // binary resolution 33const NX_PROOF_RULE_FACTOR: nx_int = 2 34const NX_PROOF_RULE_PARAMOD: nx_int = 3 35const NX_PROOF_RULE_DEMOD: nx_int = 4 36const NX_PROOF_RULE_HYPERRES: nx_int = 5 // N-electron hyperresolution 37const NX_PROOF_RULE_EQ_FACTOR: nx_int = 6 38const NX_PROOF_RULE_INST_GEN: nx_int = 7 39const NX_PROOF_RULE_DEDUP: nx_int = 8 // literal dedup (preprocessing) 40const NX_PROOF_RULE_AVATAR_SPLIT: nx_int = 9 41 42func nx_proof_rule_name(r: nx_int) -> *u8 { 43 if r == NX_PROOF_RULE_INPUT { return "input" as *u8 } 44 if r == NX_PROOF_RULE_RES { return "resolution" as *u8 } 45 if r == NX_PROOF_RULE_FACTOR { return "factoring" as *u8 } 46 if r == NX_PROOF_RULE_PARAMOD { return "paramodulation" as *u8 } 47 if r == NX_PROOF_RULE_DEMOD { return "demodulation" as *u8 } 48 if r == NX_PROOF_RULE_HYPERRES { return "hyperresolution" as *u8 } 49 if r == NX_PROOF_RULE_EQ_FACTOR { return "equality_factoring" as *u8 } 50 if r == NX_PROOF_RULE_INST_GEN { return "instance_generation" as *u8 } 51 if r == NX_PROOF_RULE_DEDUP { return "literal_dedup" as *u8 } 52 if r == NX_PROOF_RULE_AVATAR_SPLIT { return "avatar_split" as *u8 } 53 return "?" as *u8 54} 55 56struct ProofEntry { 57 rule: nx_int, 58 parent_a: nx_int, // -1 if not applicable (e.g. INPUT) 59 parent_b: nx_int, // -1 if unary inference 60} 61const NX_PROOF_ENTRY_BYTES: nx_int = 24 62 63struct ProofLog { 64 entries: *ProofEntry, 65 n: nx_int, 66 cap: nx_int, 67} 68const NX_PROOF_LOG_BYTES: nx_int = 24 69 70func nx_proof_log_new(cap: nx_int) -> *ProofLog { 71 let log: *ProofLog = (sys_mmap(NX_PROOF_LOG_BYTES as i64)) as *ProofLog 72 log.entries = (sys_mmap((cap * NX_PROOF_ENTRY_BYTES) as i64)) as *ProofEntry 73 log.n = 0 74 log.cap = cap 75 return log 76} 77 78// Append an entry; returns its index in the log or -1 on overflow. 79func nx_proof_log_add(log: *ProofLog, rule: nx_int, 80 parent_a: nx_int, parent_b: nx_int) -> nx_int { 81 if log.n >= log.cap { return 0 - 1 } 82 let slot: *ProofEntry = ((log.entries as nx_int) + (log.n * NX_PROOF_ENTRY_BYTES)) as *ProofEntry 83 slot.rule = rule 84 slot.parent_a = parent_a 85 slot.parent_b = parent_b 86 let idx: nx_int = log.n 87 log.n = log.n + 1 88 return idx 89} 90 91func nx_proof_log_get(log: *ProofLog, i: nx_int) -> *ProofEntry { 92 if i < 0 { return 0 as *ProofEntry } 93 if i >= log.n { return 0 as *ProofEntry } 94 return ((log.entries as nx_int) + (i * NX_PROOF_ENTRY_BYTES)) as *ProofEntry 95} 96 97// Walk transitively from `start_idx` upward through parent_a/parent_b, 98// marking every ancestor in `visited[]` (caller-allocated bitfield 99// sized at least log.cap). Returns the count of distinct ancestors 100// visited. Used to compute the "proof support" -- the minimal set of 101// input clauses + intermediate derivations needed to justify the empty 102// clause. 103func nx_proof_walk_ancestors(log: *ProofLog, start_idx: nx_int, 104 visited: *nx_int) -> nx_int { 105 if start_idx < 0 { return 0 } 106 if start_idx >= log.n { return 0 } 107 if visited[start_idx] == 1 { return 0 } 108 visited[start_idx] = 1 109 110 let e: *ProofEntry = nx_proof_log_get(log, start_idx) 111 var count: nx_int = 1 112 if (e as nx_int) != 0 { 113 if e.parent_a >= 0 { 114 count = count + nx_proof_walk_ancestors(log, e.parent_a, visited) 115 } 116 if e.parent_b >= 0 { 117 count = count + nx_proof_walk_ancestors(log, e.parent_b, visited) 118 } 119 } 120 return count 121}