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}