code wiki / (root) / nx_dequant_iter_test.nx

nx_dequant_iter_test.nx source

↩ module page · 148 lines · 5916 B

1// nx_dequant_iter_test.nx -- smoke for nx_dequant_iter.nx. 2// 3// Builds a synthetic Q4_K super-block in memory with known scales, 4// mins, and nibble values. Tests: 5// A) Verdict gate 6// B) Iter init reads d/dmin/scales/mins correctly 7// C) nx_q4k_iter_value at known indices matches expected formula 8// D) nx_q4k_iter_subblock_dequant matches nx_gguf_dequant_q4_k 9// bit-exactly for all 256 values 10// E) Out-of-range index returns 0 (no crash) 11 12import "nx_syscalls.nx" 13import "nx_tier.nx" 14import "nx_le.nx" 15import "nx_gguf.nx" 16import "nx_gguf_load.nx" 17import "nx_dequant_iter.nx" 18 19func main() -> i64 { 20 // Verdict gate. 21 var vi: nx_int = 0 22 while vi < NX_Q4KI_N_VERDICTS { 23 if nx_q4ki_verdict_is_valid(vi) != 1 { return 5 + vi } 24 vi = vi + 1 25 } 26 27 // ----- Build a Q4_K super-block at byte offset 0 ----- 28 // 29 // Same layout as nx_gguf_load_q4k_test.nx fixture: 30 // d = 1.0 (f16 0x3C00) -> d_q10 = 1024 31 // dmin = 0.0 (f16 0x0000) -> dmin_q10 = 0 32 // sc[0..3] = {1, 1, 1, 1} packed in low 6 bits of bytes 0..3 = 0x01 33 // m[0..3] = {0, 0, 0, 0} packed in low 6 bits of bytes 4..7 = 0x00 34 // sc[4] = 2 via q[0]'s top-2 bits = 0 + q[8] low nibble = 2 35 // sc[5..7] = 0 / m[4..7] = 0 (all zero high-half packing) 36 // nibble byte 0 (sb 0, l 0): 0x32 -> q_lo = 2, q_hi = 3 37 // nibble byte 64 (sb 4, l 0): 0x32 -> q_lo = 2, q_hi = 3 38 let buf: *u8 = sys_mmap(512) 39 nx_le_write_u16(buf, 0, 0x3C00) 40 nx_le_write_u16(buf, 2, 0x0000) 41 buf[4]=0x01; buf[5]=0x01; buf[6]=0x01; buf[7]=0x01 42 buf[8]=0x00; buf[9]=0x00; buf[10]=0x00; buf[11]=0x00 43 buf[12]=0x02; buf[13]=0x00; buf[14]=0x00; buf[15]=0x00 44 // Zero 128 nibble bytes, then set the two test bytes. 45 var zi: nx_int = 0 46 while zi < 128 { 47 buf[16 + zi] = 0 48 zi = zi + 1 49 } 50 buf[16 + 0] = 0x32 51 buf[16 + 64] = 0x32 52 53 // ----- Init the iterator ----- 54 let it: *NxQ4KBlockIter = nx_q4k_iter_alloc() 55 let v_init: nx_int = nx_q4k_iter_init(buf, 0, it) 56 if v_init != NX_Q4KI_OK { return 20 + v_init } 57 58 // Verify header decode. d=1.0 -> Q24 = 2^24 = 16777216 (was Q10 1024). 59 if it.d_q10 != 16777216 { return 30 } 60 if it.dmin_q10 != 0 { return 31 } 61 if it.sc_0 != 1 { return 32 } 62 if it.sc_1 != 1 { return 33 } 63 if it.sc_2 != 1 { return 34 } 64 if it.sc_3 != 1 { return 35 } 65 if it.sc_4 != 2 { return 36 } 66 if it.sc_5 != 0 { return 37 } 67 if it.sc_6 != 0 { return 38 } 68 if it.sc_7 != 0 { return 39 } 69 if it.m_0 != 0 { return 40 } 70 if it.m_7 != 0 { return 41 } 71 72 // ----- Spot-check nx_q4k_iter_value at canonical indices ----- 73 // ggml layout + Q24 super-scale (d_q24 = 2^24 = 16777216): 74 // byte 0 = group 0, l=0 -> low nibble 2 -> sub-block 0, out[0] 75 // high nibble 3 -> sub-block 1, out[32] 76 // byte 64 = group 2, l=0 -> low nibble 2 -> sub-block 4, out[128] 77 // high nibble 3 -> sub-block 5 (sc=0) out[160]=0 78 // out[0] = (16777216*sc0=1) * 2 - 0 = 33554432 79 // out[32] = (16777216*sc1=1) * 3 - 0 = 50331648 80 // out[128] = (16777216*sc4=2) * 2 - 0 = 67108864 81 // out[160] = (16777216*sc5=0) * 3 - 0 = 0 82 if nx_q4k_iter_value(it, 0) != 33554432 { return 50 } 83 if nx_q4k_iter_value(it, 32) != 50331648 { return 51 } 84 if nx_q4k_iter_value(it, 1) != 0 { return 52 } 85 if nx_q4k_iter_value(it, 31) != 0 { return 53 } 86 if nx_q4k_iter_value(it, 128) != 67108864 { return 54 } 87 if nx_q4k_iter_value(it, 160) != 0 { return 55 } 88 if nx_q4k_iter_value(it, 255) != 0 { return 56 } 89 // Out-of-range 90 if nx_q4k_iter_value(it, -1) != 0 { return 57 } 91 if nx_q4k_iter_value(it, 256) != 0 { return 58 } 92 if nx_q4k_iter_value(it, 1000)!= 0 { return 59 } 93 94 // ----- Sub-block bulk dequant ----- 95 // ggml layout: sub-block sb draws from group g=sb/2, low nibble if 96 // sb even else high nibble; 32 outputs in order. 97 // sub-block 0 = group 0 low nibbles: sb0[0]=(Q24*1)*2=33554432, rest 0. 98 let sb0: *i64 = sys_mmap(32 * 8) as *i64 99 let v_sb0: nx_int = nx_q4k_iter_subblock_dequant(it, 0, sb0) 100 if v_sb0 != NX_Q4KI_OK { return 70 } 101 if sb0[0] != 33554432 { return 71 } 102 var i_sb0: nx_int = 1 103 while i_sb0 < 32 { 104 if sb0[i_sb0] != 0 { return 80 + i_sb0 } 105 i_sb0 = i_sb0 + 1 106 } 107 108 // sub-block 1 = group 0 HIGH nibbles: sb1[0]=(Q24*sc1=1)*3=50331648, rest 0. 109 let sb1: *i64 = sys_mmap(32 * 8) as *i64 110 let v_sb1: nx_int = nx_q4k_iter_subblock_dequant(it, 1, sb1) 111 if v_sb1 != NX_Q4KI_OK { return 90 } 112 if sb1[0] != 50331648 { return 91 } 113 var i_sb1: nx_int = 1 114 while i_sb1 < 32 { 115 if sb1[i_sb1] != 0 { return 92 } 116 i_sb1 = i_sb1 + 1 117 } 118 119 // sub-block 4 = group 2 low nibbles: sb4[0]=(Q24*sc4=2)*2=67108864. 120 let sb4: *i64 = sys_mmap(32 * 8) as *i64 121 let v_sb4: nx_int = nx_q4k_iter_subblock_dequant(it, 4, sb4) 122 if v_sb4 != NX_Q4KI_OK { return 200 } 123 if sb4[0] != 67108864 { return 201 } 124 var i_sb4: nx_int = 1 125 while i_sb4 < 32 { 126 if sb4[i_sb4] != 0 { return 202 } 127 i_sb4 = i_sb4 + 1 128 } 129 130 // Out-of-range sub-block. 131 let sb_bad: *i64 = sys_mmap(32 * 8) as *i64 132 let v_sbb: nx_int = nx_q4k_iter_subblock_dequant(it, 8, sb_bad) 133 if v_sbb != NX_Q4KI_ERR_BAD_IDX { return 210 } 134 135 // ----- KAT: cross-check iter output vs nx_gguf_dequant_q4_k ----- 136 // 137 // Run the existing whole-block dequant on the same bytes, then 138 // walk all 256 iter values, assert bit-exact match. 139 let full: *i64 = sys_mmap(256 * 8) as *i64 140 nx_gguf_dequant_q4_k(buf, 0, 256, full) 141 var kat_i: nx_int = 0 142 while kat_i < 256 { 143 if nx_q4k_iter_value(it, kat_i) != full[kat_i] { return 300 + kat_i / 32 } 144 kat_i = kat_i + 1 145 } 146 147 return 0 148}