code wiki / (root) / nx_q6_k_to_f32.nx

nx_q6_k_to_f32.nx source

↩ module page · 116 lines · 4888 B

1// nx_q6_k_to_f32.nx -- bits-up Q6_K dequantization to IEEE 754 binary32. 2// 3// Q6_K super-block layout (per ggml v3 spec; clean-room). 4// 210 bytes per super-block of 256 values: 5// bytes [0..128] ql 128 bytes, 4-bit packed (low/high nibble) 6// bytes [128..192] qh 64 bytes, 2-bit packed (4 pairs per byte) 7// bytes [192..208] scales 16 i8 sub-block scales 8// bytes [208..210] d f16 super-scale 9// 10// Dequant (per ggml dequantize_row_q6_K): 11// Process super-block in 2 chunks of 128 values each (c in 0..1). 12// Each chunk consumes 64 bytes of ql, 32 bytes of qh, 8 scales. 13// Inside a chunk, l loops 0..31, producing 4 output positions per l: 14// y[c*128 + l ] = d * sc[c*8 + (l/16) + 0] * q1 15// y[c*128 + l + 32] = d * sc[c*8 + (l/16) + 2] * q2 16// y[c*128 + l + 64] = d * sc[c*8 + (l/16) + 4] * q3 17// y[c*128 + l + 96] = d * sc[c*8 + (l/16) + 6] * q4 18// where: 19// q1 = (ql[c*64 + l ] & 0x0F) | ((qh[c*32 + l] >> 0) & 3) << 4 - 32 20// q2 = (ql[c*64 + l + 32] & 0x0F) | ((qh[c*32 + l] >> 2) & 3) << 4 - 32 21// q3 = (ql[c*64 + l ] >> 4 ) | ((qh[c*32 + l] >> 4) & 3) << 4 - 32 22// q4 = (ql[c*64 + l + 32] >> 4 ) | ((qh[c*32 + l] >> 6) & 3) << 4 - 32 23// 24// (Rewrite 2026-05-21 after live-fire on real Qwen2.5 showed the 25// naive per-position algorithm doesn't match ggml's interleaving.) 26// 27// genealogy_id: ggml_q6_k_canon 28// lineage_id: substrate_q6_k_to_f32_v1 29 30import "nx_syscalls.nx" 31import "nx_tier.nx" 32import "nx_le.nx" 33import "nx_f32.nx" 34import "nx_f32_cvt.nx" 35 36const NX_Q6_K_OK: nx_int = 0 37const NX_Q6_K_VPB: i64 = 256 // values per super-block 38const NX_Q6_K_BPB: i64 = 210 // bytes per super-block 39 40func _q6k_emit(d_f32: i64, scale_u8: i64, 41 q_low: i64, qh_byte: i64, qh_shift: i64, 42 out_f32: *i64, out_idx: i64, take: i64) -> nx_int { 43 if out_idx >= take { return 0 } 44 var scale_i: i64 = scale_u8 45 if scale_i >= 128 { scale_i = scale_i - 256 } 46 let scale_f32: i64 = __f32_mul(d_f32, __f32_from_i64(scale_i)) // HW cvtsi2ss+mulss (2026-07-08) 47 let q_high: i64 = (qh_byte >> qh_shift) & 0x03 48 let q6: i64 = q_low | (q_high << 4) 49 let s: i64 = q6 - 32 50 out_f32[out_idx] = __f32_mul(scale_f32, __f32_from_i64(s)) // HW cvtsi2ss+mulss (2026-07-08) 51 return 0 52} 53 54func _nx_q6_k_block_to_f32(buf: *u8, base_off: i64, take: i64, 55 out_f32: *i64) -> nx_int { 56 let ql_off: i64 = base_off + 0 57 let qh_off: i64 = base_off + 128 58 let scales_off: i64 = base_off + 192 59 let d_off: i64 = base_off + 208 60 61 let d_f16: i64 = nx_le_read_u16(buf, d_off) 62 let d_f32: i64 = nx_f16_to_f32(d_f16) 63 64 // Process 2 chunks of 128 values each (c=0 covers values 0..127, 65 // c=1 covers 128..255). 66 var c: i64 = 0 67 while c < 2 { 68 let ql_base: i64 = ql_off + c * 64 69 let qh_base: i64 = qh_off + c * 32 70 let sc_base: i64 = scales_off + c * 8 71 let y_base: i64 = c * 128 72 73 var l: i64 = 0 74 while l < 32 { 75 let is: i64 = l / 16 // 0 or 1 -- which scale subgroup 76 77 let ql0: i64 = nx_le_read_u8(buf, ql_base + l) 78 let ql1: i64 = nx_le_read_u8(buf, ql_base + l + 32) 79 let qh: i64 = nx_le_read_u8(buf, qh_base + l) 80 81 // q1 = (ql0 & 0xF) + bits01<<4 -32, scale sc[is + 0] 82 // q2 = (ql1 & 0xF) + bits23<<4 -32, scale sc[is + 2] 83 // q3 = (ql0 >> 4) + bits45<<4 -32, scale sc[is + 4] 84 // q4 = (ql1 >> 4) + bits67<<4 -32, scale sc[is + 6] 85 let sc1: i64 = nx_le_read_u8(buf, sc_base + is + 0) 86 let sc2: i64 = nx_le_read_u8(buf, sc_base + is + 2) 87 let sc3: i64 = nx_le_read_u8(buf, sc_base + is + 4) 88 let sc4: i64 = nx_le_read_u8(buf, sc_base + is + 6) 89 90 _q6k_emit(d_f32, sc1, ql0 & 0x0F, qh, 0, out_f32, y_base + l + 0, take) 91 _q6k_emit(d_f32, sc2, ql1 & 0x0F, qh, 2, out_f32, y_base + l + 32, take) 92 _q6k_emit(d_f32, sc3, (ql0 >> 4) & 0x0F, qh, 4, out_f32, y_base + l + 64, take) 93 _q6k_emit(d_f32, sc4, (ql1 >> 4) & 0x0F, qh, 6, out_f32, y_base + l + 96, take) 94 95 l = l + 1 96 } 97 c = c + 1 98 } 99 return NX_Q6_K_OK 100} 101 102func nx_q6_k_to_f32(buf: *u8, base_off: i64, n_values: i64, 103 out_f32: *i64) -> nx_int { 104 let n_super: i64 = (n_values + NX_Q6_K_VPB - 1) / NX_Q6_K_VPB 105 var sb: i64 = 0 106 while sb < n_super { 107 let super_off: i64 = base_off + sb * NX_Q6_K_BPB 108 let block_n: i64 = n_values - sb * NX_Q6_K_VPB 109 var take: i64 = block_n 110 if take > NX_Q6_K_VPB { take = NX_Q6_K_VPB } 111 _nx_q6_k_block_to_f32(buf, super_off, take, 112 ((out_f32 as i64) + sb * NX_Q6_K_VPB * 8) as *i64) 113 sb = sb + 1 114 } 115 return NX_Q6_K_OK 116}