code wiki / (root) / nx_fmb_test.nx

nx_fmb_test.nx source

↩ module page · 103 lines · 4558 B

1// nx_fmb_test.nx -- Finite Model Building smoke. 2 3import "nx_syscalls.nx" 4import "nx_runtime.nx" 5import "nx_tier.nx" 6import "nx_result.nx" 7import "nx_unify.nx" 8import "nx_resolution.nx" 9import "nx_sat_solver.nx" 10import "nx_fmb.nx" 11 12const SYM_P: nx_int = 200 13const SYM_Q: nx_int = 201 14const VAR_X: nx_int = 0 15 16func mk_p_var(p_sym: nx_int, var_id: nx_int) -> *Term { 17 let arg: *Term = (sys_mmap(NX_TERM_BYTES as i64)) as *Term 18 arg.kind = NX_TERM_VAR; arg.sym = var_id; arg.n_args = 0; arg.args = 0 as *Term 19 return nx_term_app(p_sym, 1, arg) 20} 21 22func place(arr: *Clause, i: nx_int, src: *Clause) { 23 let dest: *Clause = ((arr as nx_int) + (i * NX_CLAUSE_BYTES)) as *Clause 24 dest.n_lits = src.n_lits 25 dest.lits = src.lits 26} 27 28func main() -> nx_exit { 29 println("=== Finite Model Building smoke ===" as *u8) 30 var fails: nx_int = 0 31 32 // ---------- Test 1: satisfiable single-clause set -------------- 33 // {p(X)} -- this is satisfiable: any model where p(d) is true for 34 // each domain element d. Domain size 1 should suffice. 35 let c1: *Clause = nx_clause_new() 36 let _r1: *NxResult = nx_clause_add(c1, nx_lit_make(NX_LIT_POS, mk_p_var(SYM_P, VAR_X))) 37 38 let in1: *Clause = (sys_mmap((4 * NX_CLAUSE_BYTES) as i64)) as *Clause 39 place(in1, 0, c1) 40 41 let size1: *nx_int = sys_mmap(8) as *nx_int 42 let v1: nx_int = nx_fmb_search(in1, 1, 5, size1) 43 print(" 1. {p(X)} -> verdict=" as *u8); print(nx_fmb_verdict_name(v1)); print(" size=" as *u8); print_i64(size1[0]); println("" as *u8) 44 if v1 == NX_FMB_MODEL_FOUND { 45 if size1[0] == 1 { println(" model at domain size 1 PASS" as *u8) } 46 else { println(" unexpected size FAIL" as *u8); fails = fails + 1 } 47 } else { println(" expected MODEL_FOUND FAIL" as *u8); fails = fails + 1 } 48 49 // ---------- Test 2: unsat clause set ------------------------ 50 // {p(X)}, {~p(X)} -- ground at any size produces propositional 51 // p(d) and ~p(d), which is UNSAT. 52 let c2a: *Clause = nx_clause_new() 53 let _r2a: *NxResult = nx_clause_add(c2a, nx_lit_make(NX_LIT_POS, mk_p_var(SYM_P, VAR_X))) 54 let c2b: *Clause = nx_clause_new() 55 let _r2b: *NxResult = nx_clause_add(c2b, nx_lit_make(NX_LIT_NEG, mk_p_var(SYM_P, VAR_X))) 56 57 let in2: *Clause = (sys_mmap((4 * NX_CLAUSE_BYTES) as i64)) as *Clause 58 place(in2, 0, c2a) 59 place(in2, 1, c2b) 60 61 let size2: *nx_int = sys_mmap(8) as *nx_int 62 let v2: nx_int = nx_fmb_search(in2, 2, 5, size2) 63 print(" 2. {p(X)}, {~p(X)} -> " as *u8); print(nx_fmb_verdict_name(v2)); println("" as *u8) 64 if v2 == NX_FMB_NO_MODEL { 65 println(" correctly identified as unsatisfiable PASS" as *u8) 66 } else { println(" expected NO_MODEL FAIL" as *u8); fails = fails + 1 } 67 68 // ---------- Test 3: requires domain size >= 2 ---------------- 69 // {p(X) | q(X)}, {~p(X)}, {~q(X)} but with fresh vars per clause 70 // is unsat at size 1. Skip -- our subst mechanism uses one 71 // var-id space across clauses, so this would behave differently. 72 // Test 3 instead: model exists trivially. 73 let c3: *Clause = nx_clause_new() 74 let _r3a: *NxResult = nx_clause_add(c3, nx_lit_make(NX_LIT_POS, mk_p_var(SYM_P, VAR_X))) 75 let _r3b: *NxResult = nx_clause_add(c3, nx_lit_make(NX_LIT_POS, mk_p_var(SYM_Q, VAR_X))) 76 77 let in3: *Clause = (sys_mmap((4 * NX_CLAUSE_BYTES) as i64)) as *Clause 78 place(in3, 0, c3) 79 80 let size3: *nx_int = sys_mmap(8) as *nx_int 81 let v3: nx_int = nx_fmb_search(in3, 1, 3, size3) 82 print(" 3. {p(X) | q(X)} -> " as *u8); print(nx_fmb_verdict_name(v3)); print(" size=" as *u8); print_i64(size3[0]); println("" as *u8) 83 if v3 == NX_FMB_MODEL_FOUND { 84 println(" disjunction satisfiable PASS" as *u8) 85 } else { println(" expected MODEL_FOUND FAIL" as *u8); fails = fails + 1 } 86 87 // ---------- Test 4: empty clause set is trivially SAT -------- 88 let in4: *Clause = (sys_mmap((4 * NX_CLAUSE_BYTES) as i64)) as *Clause 89 let size4: *nx_int = sys_mmap(8) as *nx_int 90 let v4: nx_int = nx_fmb_search(in4, 0, 3, size4) 91 print(" 4. {} (empty input) -> " as *u8); print(nx_fmb_verdict_name(v4)); println("" as *u8) 92 if v4 == NX_FMB_MODEL_FOUND { 93 println(" trivially satisfiable PASS" as *u8) 94 } else { println(" expected MODEL_FOUND FAIL" as *u8); fails = fails + 1 } 95 96 println("" as *u8) 97 if fails == 0 { 98 println("=== ALL 4 FMB tests PASS ===" as *u8) 99 return 0 100 } 101 print("=== " as *u8); print_i64(fails); println(" tests FAILED ===" as *u8) 102 return 1 103}