nx_q4k_to_f32_test.nx source
↩ module page · 97 lines · 3098 B
1// nx_q4k_to_f32_test.nx -- smoke for nx_q4k_to_f32.nx.
2//
3// Synthetic Q4_K block (same as nx_gguf_load_q4k_test):
4// d = 1.0 (f16 0x3C00 -> f32 0x3F800000)
5// dmin = 0 (f16 0x0000 -> f32 0)
6// sc[0..3] = 1, sc[4] = 2, sc[5..7] = 0
7// m[0..7] = 0
8// nibble byte 0 -> low nibble 2, high nibble 3
9// nibble byte 64 -> low nibble 2, high nibble 3
10//
11// ggml layout (verified bit-exact vs ggml-quants.c): 32-byte groups
12// g=0..3; group g feeds sub-block 2g (low nibbles) + 2g+1 (high nibbles).
13// byte 0 is group 0, l=0 -> low -> sub-block 0, out[0];
14// high -> sub-block 1, out[32]
15// byte 64 is group 2, l=0 -> low -> sub-block 4, out[128];
16// high -> sub-block 5 (sc[5]=0) -> out[160]=0
17//
18// Expected f32 outputs (EXACT — all operands are exact small ints):
19// out[0] = 1 * sc[0]=1 * 2 - 0 = 2.0 (f32 0x40000000)
20// out[32] = 1 * sc[1]=1 * 3 - 0 = 3.0 (f32 0x40400000)
21// out[128] = 1 * sc[4]=2 * 2 - 0 = 4.0 (f32 0x40800000)
22// out[160] = 1 * sc[5]=0 * 3 - 0 = 0.0 (sc[5] is zero)
23// everywhere else: 0.0 (f32 0)
24
25import "nx_syscalls.nx"
26import "nx_tier.nx"
27import "nx_le.nx"
28import "nx_gguf.nx"
29import "nx_f32.nx"
30import "nx_f32_cvt.nx"
31import "nx_q4k_to_f32.nx"
32
33func main() -> i64 {
34 // Verdict gate
35 var vi: nx_int = 0
36 while vi < NX_Q4KF_N_VERDICTS {
37 if nx_q4kf_verdict_is_valid(vi) != 1 { return 5 + vi }
38 vi = vi + 1
39 }
40
41 let buf: *u8 = sys_mmap(256)
42 // d = 1.0 (f16)
43 nx_le_write_u16(buf, 0, 0x3C00)
44 // dmin = 0.0 (f16)
45 nx_le_write_u16(buf, 2, 0x0000)
46 // scales/mins (12 bytes): sc[0..3]=1, m[0..3]=0, sc[4]=2 high-half
47 buf[4] = 0x01
48 buf[5] = 0x01
49 buf[6] = 0x01
50 buf[7] = 0x01
51 buf[8] = 0x00
52 buf[9] = 0x00
53 buf[10] = 0x00
54 buf[11] = 0x00
55 buf[12] = 0x02 // sc[4] low nibble
56 buf[13] = 0x00
57 buf[14] = 0x00
58 buf[15] = 0x00
59 // Nibble bytes (128): zero all, then set bytes 0 and 64.
60 var zi: nx_int = 0
61 while zi < 128 {
62 buf[16 + zi] = 0
63 zi = zi + 1
64 }
65 buf[16 + 0] = 0x32
66 buf[16 + 64] = 0x32
67
68 let out: *i64 = sys_mmap(256 * 8) as *i64
69
70 let v: nx_int = nx_q4k_block_to_f32(buf, 0, 256, out)
71 if v != NX_Q4KF_OK { return 10 + v }
72
73 // Spot-check non-zero outputs (all exact f32 small ints).
74 if out[0] != 0x40000000 { return 20 } // 2.0 (sub-block 0, low nibble of byte 0)
75 if out[32] != 0x40400000 { return 21 } // 3.0 (sub-block 1, high nibble of byte 0)
76 if out[128] != 0x40800000 { return 22 } // 4.0 (sub-block 4, low nibble of byte 64)
77 if out[160] != 0 { return 23 } // 0.0 (sub-block 5 has sc=0)
78
79 // All zero positions. Nonzero live only at {0, 32, 128}.
80 var z: nx_int = 1
81 while z < 32 {
82 if out[z] != 0 { return 30 + z }
83 z = z + 1
84 }
85 var w: nx_int = 33
86 while w < 128 {
87 if out[w] != 0 { return 100 }
88 w = w + 1
89 }
90 var u: nx_int = 129
91 while u < 256 {
92 if out[u] != 0 { return 200 }
93 u = u + 1
94 }
95
96 return 0
97}