code wiki / (root) / nx_q4k_to_f32_test.nx

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}