1pub mod encode;
16pub use encode::q4_k::{encode_block_q4_k, encode_row_q4_k};
17pub use encode::q5_k::{encode_block_q5_k, encode_row_q5_k};
18pub use encode::q6_k::{encode_block_q6_k, encode_row_q6_k, probe_q6_k_group};
19pub use encode::{encode_block_q8_0, encode_row_q8_0};
20
21pub mod iq4_xs_q8;
22pub mod ternary;
24pub use iq4_xs_q8::{dot_iq4_xs_q8_k, dot_iq4_xs_q8_k_scalar};
25pub mod iq_tables;
26#[cfg(test)]
30mod iq_tier_goldens;
31pub mod repack;
32
33pub use repack::{
34 batch_gemm_is_accelerated, gemm_q4_0x4_group, gemm_q4_0x4_group_x4, gemm_q4_0x4_group_x4_on,
35 gemm_q4_kx8_group, gemm_q4_kx8_group_x4, gemm_q4_kx8_group_x4_on, gemm_q5_kx8_group,
36 gemm_q5_kx8_group_x4, gemm_q5_kx8_group_x4_on, gemm_q6_kx8_group, gemm_q6_kx8_group_x4,
37 gemm_q6_kx8_group_x4_on, gemm_q8_0x4_group, gemm_q8_0x4_group_x4, gemm_q8_0x4_group_x4_on,
38 gemv_q4_0x4_group, gemv_q4_kx8_group, gemv_q4_kx8_q8_k, gemv_q5_kx8_group, gemv_q5_kx8_q8_k,
39 gemv_q6_kx8_group, gemv_q6_kx8_q8_k, gemv_q8_0x4_group, gemv_q8_0x4_q8_0,
40 interleaved_gemm_is_accelerated, make_block_q4_0x4, make_block_q4_kx8, make_block_q5_kx8,
41 make_block_q6_kx8, make_block_q8_0x4, pack_q4_0_matrix_x4, pack_q4_k_matrix_x8,
42 pack_q5_k_matrix_x8, pack_q6_k_matrix_x8, pack_q8_0_matrix_x4, preferred_interleave,
43 prepare_q8_acts_x4, prepare_q8_k_acts_x4, q4_0x4_gemm_uses_acts_x4, q4_0x4_interleave,
44 q4_kx8_gemm_uses_acts_x4, q4_kx8_interleave, q5_kx8_gemm_uses_acts_x4, q5_kx8_interleave,
45 q6_kx8_gemm_uses_acts_x4, q6_kx8_interleave, q8_0x4_gemm_uses_acts_x4, q8_0x4_interleave,
46 AccelX4, Q8ActsX4, Q8KActsX4, Q4_0X4_BLOCK_BYTES, Q4_0X4_GEMM_NC, Q4_0X4_INTERLEAVE,
47 Q4_0X4_NROWS, Q4_KX8_BLOCK_BYTES, Q4_KX8_GEMM_NC, Q4_KX8_NROWS, Q5_KX8_BLOCK_BYTES,
48 Q5_KX8_GEMM_NC, Q5_KX8_NROWS, Q6_KX8_BLOCK_BYTES, Q6_KX8_GEMM_NC, Q6_KX8_NROWS, Q8K_ACTS_X4_NC,
49 Q8_0X4_BLOCK_BYTES, Q8_0X4_GEMM_NC, Q8_0X4_INTERLEAVE, Q8_0X4_NROWS,
50};
51
52use half::f16;
53
54pub const Q8_0_BLOCK_BYTES: usize = 34;
56pub const Q8_0_BLOCK_ELEMS: usize = 32;
57
58pub const Q4_0_BLOCK_BYTES: usize = 18;
60pub const Q4_0_BLOCK_ELEMS: usize = 32;
61
62pub const Q4_1_BLOCK_BYTES: usize = 20;
67pub const Q4_1_BLOCK_ELEMS: usize = 32;
68
69pub const Q5_0_BLOCK_BYTES: usize = 22;
73pub const Q5_0_BLOCK_ELEMS: usize = 32;
74
75pub const Q5_1_BLOCK_BYTES: usize = 24;
79pub const Q5_1_BLOCK_ELEMS: usize = 32;
80
81pub const Q8_1_BLOCK_BYTES: usize = 36;
87pub const Q8_1_BLOCK_ELEMS: usize = 32;
88
89pub const TURBO4_KV_GROUP: usize = 32;
91pub const TURBO4_KV_BLOCK_BYTES: usize = 18;
92
93pub const FP8_KV_GROUP: usize = 32;
96pub const FP8_KV_BLOCK_BYTES: usize = 34;
97
98pub fn pack_turbo4_kv_blocks(x: &[f32]) -> Vec<u8> {
100 assert_eq!(x.len() % TURBO4_KV_GROUP, 0);
101 let n_blocks = x.len() / TURBO4_KV_GROUP;
102 let mut out = vec![0u8; n_blocks * TURBO4_KV_BLOCK_BYTES];
103 for b in 0..n_blocks {
104 let chunk = &x[b * TURBO4_KV_GROUP..(b + 1) * TURBO4_KV_GROUP];
105 let amax = chunk.iter().fold(0f32, |m, &v| m.max(v.abs()));
106 let scale = if amax > 0.0 { amax / 7.0 } else { 0.0 };
107 let inv = if scale > 0.0 { 1.0 / scale } else { 0.0 };
108 let bits = f16::from_f32(scale).to_le_bytes();
109 let dst = &mut out[b * TURBO4_KV_BLOCK_BYTES..(b + 1) * TURBO4_KV_BLOCK_BYTES];
110 dst[0] = bits[0];
111 dst[1] = bits[1];
112 for i in 0..16 {
113 let q0 = (chunk[i * 2] * inv).round().clamp(-8.0, 7.0) as i8;
114 let q1 = (chunk[i * 2 + 1] * inv).round().clamp(-8.0, 7.0) as i8;
115 dst[2 + i] = ((q0 as u8) & 0x0f) | (((q1 as u8) & 0x0f) << 4);
116 }
117 }
118 out
119}
120
121pub fn unpack_turbo4_kv_blocks(bytes: &[u8]) -> Result<Vec<f32>, QuantError> {
123 if !bytes.len().is_multiple_of(TURBO4_KV_BLOCK_BYTES) {
124 return Err(QuantError::Misaligned(bytes.len(), TURBO4_KV_BLOCK_BYTES));
125 }
126 let n_blocks = bytes.len() / TURBO4_KV_BLOCK_BYTES;
127 let mut out = Vec::with_capacity(n_blocks * TURBO4_KV_GROUP);
128 for b in 0..n_blocks {
129 let block = &bytes[b * TURBO4_KV_BLOCK_BYTES..(b + 1) * TURBO4_KV_BLOCK_BYTES];
130 let scale = f16::from_le_bytes([block[0], block[1]]).to_f32();
131 for i in 0..16 {
132 let byte = block[2 + i];
133 let q0 = ((byte & 0x0f) as i8) << 4 >> 4;
134 let q1 = ((byte >> 4) as i8) << 4 >> 4;
135 out.push(q0 as f32 * scale);
136 out.push(q1 as f32 * scale);
137 }
138 }
139 Ok(out)
140}
141
142pub fn pack_fp8_kv_blocks(x: &[f32]) -> Vec<u8> {
144 quantize_q8_0(x)
146}
147
148pub fn unpack_fp8_kv_blocks(bytes: &[u8]) -> Result<Vec<f32>, QuantError> {
150 dequant_q8_0(bytes)
151}
152
153pub const Q4_K_BLOCK_BYTES: usize = 144;
158pub const Q4_K_BLOCK_ELEMS: usize = 256;
159const Q4_K_SCALE_BYTES: usize = 12;
160
161pub const Q5_K_BLOCK_BYTES: usize = 176;
167pub const Q5_K_BLOCK_ELEMS: usize = 256;
168
169pub const Q6_K_BLOCK_BYTES: usize = 210;
174pub const Q6_K_BLOCK_ELEMS: usize = 256;
175
176pub const Q2_K_BLOCK_BYTES: usize = 84;
186pub const Q2_K_BLOCK_ELEMS: usize = 256;
187const Q2_K_SCALE_BYTES: usize = 16;
188
189pub const Q3_K_BLOCK_BYTES: usize = 110;
198pub const Q3_K_BLOCK_ELEMS: usize = 256;
199const Q3_K_SCALE_BYTES: usize = 12;
200
201#[derive(Debug, thiserror::Error)]
202pub enum QuantError {
203 #[error("buffer length {0} is not a multiple of the block size {1}")]
204 Misaligned(usize, usize),
205 #[error("MXFP4 packed buffer is {0} bytes but scales buffer implies {1} bytes ({1} = scales.len() * MXFP4_GROUP_SIZE / 2)")]
206 Mxfp4RowMismatch(usize, usize),
207}
208
209pub fn dequant_bf16(src: &[u8]) -> Result<Vec<f32>, QuantError> {
218 if !src.len().is_multiple_of(2) {
219 return Err(QuantError::Misaligned(src.len(), 2));
220 }
221 Ok(src
222 .as_chunks::<2>()
223 .0
224 .iter()
225 .map(|c| f32::from_bits((u16::from_le_bytes([c[0], c[1]]) as u32) << 16))
226 .collect())
227}
228
229pub fn dequant_f16(src: &[u8]) -> Result<Vec<f32>, QuantError> {
236 if !src.len().is_multiple_of(2) {
237 return Err(QuantError::Misaligned(src.len(), 2));
238 }
239 Ok(src
240 .as_chunks::<2>()
241 .0
242 .iter()
243 .map(|c| f16::from_le_bytes([c[0], c[1]]).to_f32())
244 .collect())
245}
246
247pub fn dequant_q8_0(src: &[u8]) -> Result<Vec<f32>, QuantError> {
249 if !src.len().is_multiple_of(Q8_0_BLOCK_BYTES) {
250 return Err(QuantError::Misaligned(src.len(), Q8_0_BLOCK_BYTES));
251 }
252 let n_blocks = src.len() / Q8_0_BLOCK_BYTES;
253 let mut out = Vec::with_capacity(n_blocks * Q8_0_BLOCK_ELEMS);
254 for b in 0..n_blocks {
255 let block = &src[b * Q8_0_BLOCK_BYTES..(b + 1) * Q8_0_BLOCK_BYTES];
256 let scale = f16::from_le_bytes([block[0], block[1]]).to_f32();
257 for i in 0..Q8_0_BLOCK_ELEMS {
258 let q = block[2 + i] as i8;
259 out.push(q as f32 * scale);
260 }
261 }
262 Ok(out)
263}
264
265pub fn dequant_q4_0(src: &[u8]) -> Result<Vec<f32>, QuantError> {
269 if !src.len().is_multiple_of(Q4_0_BLOCK_BYTES) {
270 return Err(QuantError::Misaligned(src.len(), Q4_0_BLOCK_BYTES));
271 }
272 let n_blocks = src.len() / Q4_0_BLOCK_BYTES;
273 let mut out = vec![0f32; n_blocks * Q4_0_BLOCK_ELEMS];
274 for b in 0..n_blocks {
275 let block = &src[b * Q4_0_BLOCK_BYTES..(b + 1) * Q4_0_BLOCK_BYTES];
276 let scale = f16::from_le_bytes([block[0], block[1]]).to_f32();
277 let nibbles = &block[2..18];
278 let base = b * Q4_0_BLOCK_ELEMS;
279 for i in 0..16 {
280 let byte = nibbles[i];
281 let lo = (byte & 0x0F) as i32 - 8;
282 let hi = ((byte >> 4) & 0x0F) as i32 - 8;
283 out[base + i] = lo as f32 * scale;
284 out[base + i + 16] = hi as f32 * scale;
285 }
286 }
287 Ok(out)
288}
289
290fn q4_k_scale_min(j: usize, scales: &[u8; Q4_K_SCALE_BYTES]) -> (u8, u8) {
299 if j < 4 {
300 (scales[j] & 63, scales[j + 4] & 63)
301 } else {
302 (
303 (scales[j + 4] & 0x0F) | ((scales[j - 4] >> 6) << 4),
304 (scales[j + 4] >> 4) | ((scales[j] >> 6) << 4),
305 )
306 }
307}
308
309pub fn dequant_q4_k(src: &[u8]) -> Result<Vec<f32>, QuantError> {
312 if !src.len().is_multiple_of(Q4_K_BLOCK_BYTES) {
313 return Err(QuantError::Misaligned(src.len(), Q4_K_BLOCK_BYTES));
314 }
315 let n_blocks = src.len() / Q4_K_BLOCK_BYTES;
316 let mut out = Vec::with_capacity(n_blocks * Q4_K_BLOCK_ELEMS);
317 for block in src.as_chunks::<Q4_K_BLOCK_BYTES>().0 {
318 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
319 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
320 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
321 let qs = &block[16..144];
322
323 let mut is = 0usize;
324 let mut q_off = 0usize;
325 for _ in 0..4 {
326 let (sc1, m1) = q4_k_scale_min(is, &scales);
327 let (sc2, m2) = q4_k_scale_min(is + 1, &scales);
328 let (d1, min1) = (d * sc1 as f32, dmin * m1 as f32);
329 let (d2, min2) = (d * sc2 as f32, dmin * m2 as f32);
330 for l in 0..32 {
331 out.push(d1 * (qs[q_off + l] & 0x0F) as f32 - min1);
332 }
333 for l in 0..32 {
334 out.push(d2 * (qs[q_off + l] >> 4) as f32 - min2);
335 }
336 q_off += 32;
337 is += 2;
338 }
339 }
340 Ok(out)
341}
342
343pub fn dot_q4_k_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
348 #[cfg(target_arch = "x86_64")]
349 {
350 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
351 return unsafe { simd_x86::dot_q4_k_f32_avx2(row_bytes, x) };
352 }
353 }
354 #[cfg(target_arch = "aarch64")]
355 {
356 if std::arch::is_aarch64_feature_detected!("neon") {
357 return unsafe { simd_aarch64::dot_q4_k_f32_neon(row_bytes, x) };
358 }
359 }
360 dot_q4_k_f32_scalar(row_bytes, x)
361}
362
363pub fn dot_q4_k_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
364 debug_assert_eq!(row_bytes.len() % Q4_K_BLOCK_BYTES, 0);
365 let mut acc = 0f32;
366 let mut base = 0usize;
367 for block in row_bytes.as_chunks::<Q4_K_BLOCK_BYTES>().0 {
368 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
369 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
370 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
371 let qs = &block[16..144];
372
373 let mut is = 0usize;
374 let mut q_off = 0usize;
375 for _ in 0..4 {
376 let (sc1, m1) = q4_k_scale_min(is, &scales);
377 let (sc2, m2) = q4_k_scale_min(is + 1, &scales);
378 let (d1, min1) = (d * sc1 as f32, dmin * m1 as f32);
379 let (d2, min2) = (d * sc2 as f32, dmin * m2 as f32);
380 for l in 0..32 {
381 acc += (d1 * (qs[q_off + l] & 0x0F) as f32 - min1) * x[base + l];
382 }
383 for l in 0..32 {
384 acc += (d2 * (qs[q_off + l] >> 4) as f32 - min2) * x[base + 32 + l];
385 }
386 q_off += 32;
387 base += 64;
388 is += 2;
389 }
390 }
391 acc
392}
393
394pub fn dequant_q5_k(src: &[u8]) -> Result<Vec<f32>, QuantError> {
402 if !src.len().is_multiple_of(Q5_K_BLOCK_BYTES) {
403 return Err(QuantError::Misaligned(src.len(), Q5_K_BLOCK_BYTES));
404 }
405 let n_blocks = src.len() / Q5_K_BLOCK_BYTES;
406 let mut out = Vec::with_capacity(n_blocks * Q5_K_BLOCK_ELEMS);
407 for block in src.as_chunks::<Q5_K_BLOCK_BYTES>().0 {
408 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
409 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
410 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
411 let qh = &block[16..48];
412 let qs = &block[48..176];
413
414 let mut is = 0usize;
415 let (mut u1, mut u2) = (1u8, 2u8);
416 for oi in 0..4 {
417 let (sc1, m1) = q4_k_scale_min(is, &scales);
418 let (sc2, m2) = q4_k_scale_min(is + 1, &scales);
419 let (d1, min1) = (d * sc1 as f32, dmin * m1 as f32);
420 let (d2, min2) = (d * sc2 as f32, dmin * m2 as f32);
421 let ql = &qs[oi * 32..oi * 32 + 32];
422 for l in 0..32 {
423 let hi = if qh[l] & u1 != 0 { 16 } else { 0 };
424 out.push(d1 * ((ql[l] & 0x0F) + hi) as f32 - min1);
425 }
426 for l in 0..32 {
427 let hi = if qh[l] & u2 != 0 { 16 } else { 0 };
428 out.push(d2 * ((ql[l] >> 4) + hi) as f32 - min2);
429 }
430 is += 2;
431 u1 <<= 2;
432 u2 <<= 2;
433 }
434 }
435 Ok(out)
436}
437
438pub fn dot_q5_k_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
443 #[cfg(target_arch = "x86_64")]
444 {
445 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
446 return unsafe { simd_x86::dot_q5_k_f32_avx2(row_bytes, x) };
447 }
448 }
449 #[cfg(target_arch = "aarch64")]
450 {
451 if std::arch::is_aarch64_feature_detected!("neon") {
452 return unsafe { simd_aarch64::dot_q5_k_f32_neon(row_bytes, x) };
453 }
454 }
455 dot_q5_k_f32_scalar(row_bytes, x)
456}
457
458pub fn dot_q5_k_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
459 debug_assert_eq!(row_bytes.len() % Q5_K_BLOCK_BYTES, 0);
460 let mut acc = 0f32;
461 let mut base = 0usize;
462 for block in row_bytes.as_chunks::<Q5_K_BLOCK_BYTES>().0 {
463 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
464 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
465 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
466 let qh = &block[16..48];
467 let qs = &block[48..176];
468
469 let mut is = 0usize;
470 let (mut u1, mut u2) = (1u8, 2u8);
471 for oi in 0..4 {
472 let (sc1, m1) = q4_k_scale_min(is, &scales);
473 let (sc2, m2) = q4_k_scale_min(is + 1, &scales);
474 let (d1, min1) = (d * sc1 as f32, dmin * m1 as f32);
475 let (d2, min2) = (d * sc2 as f32, dmin * m2 as f32);
476 let ql = &qs[oi * 32..oi * 32 + 32];
477 for l in 0..32 {
478 let hi = if qh[l] & u1 != 0 { 16 } else { 0 };
479 acc += (d1 * ((ql[l] & 0x0F) + hi) as f32 - min1) * x[base + l];
480 }
481 for l in 0..32 {
482 let hi = if qh[l] & u2 != 0 { 16 } else { 0 };
483 acc += (d2 * ((ql[l] >> 4) + hi) as f32 - min2) * x[base + 32 + l];
484 }
485 base += 64;
486 is += 2;
487 u1 <<= 2;
488 u2 <<= 2;
489 }
490 }
491 acc
492}
493
494pub fn dequant_q6_k(src: &[u8]) -> Result<Vec<f32>, QuantError> {
497 if !src.len().is_multiple_of(Q6_K_BLOCK_BYTES) {
498 return Err(QuantError::Misaligned(src.len(), Q6_K_BLOCK_BYTES));
499 }
500 let n_blocks = src.len() / Q6_K_BLOCK_BYTES;
501 let mut out = vec![0f32; n_blocks * Q6_K_BLOCK_ELEMS];
502 for (b, block) in src.as_chunks::<Q6_K_BLOCK_BYTES>().0.iter().enumerate() {
503 let ql_full = &block[0..128];
504 let qh_full = &block[128..192];
505 let sc_full = &block[192..208];
506 let d = f16::from_le_bytes([block[208], block[209]]).to_f32();
507 let out_base = b * Q6_K_BLOCK_ELEMS;
508
509 for half in 0..2 {
510 let ql = &ql_full[half * 64..half * 64 + 64];
511 let qh = &qh_full[half * 32..half * 32 + 32];
512 let sc = &sc_full[half * 8..half * 8 + 8];
513 let y = &mut out[out_base + half * 128..out_base + half * 128 + 128];
514
515 for l in 0..32 {
516 let is = l / 16;
517 let q1 = ((ql[l] & 0x0F) | ((qh[l] & 3) << 4)) as i8 - 32;
518 let q2 = ((ql[l + 32] & 0x0F) | (((qh[l] >> 2) & 3) << 4)) as i8 - 32;
519 let q3 = ((ql[l] >> 4) | (((qh[l] >> 4) & 3) << 4)) as i8 - 32;
520 let q4 = ((ql[l + 32] >> 4) | (((qh[l] >> 6) & 3) << 4)) as i8 - 32;
521 y[l] = d * (sc[is] as i8 as f32) * (q1 as f32);
522 y[l + 32] = d * (sc[is + 2] as i8 as f32) * (q2 as f32);
523 y[l + 64] = d * (sc[is + 4] as i8 as f32) * (q3 as f32);
524 y[l + 96] = d * (sc[is + 6] as i8 as f32) * (q4 as f32);
525 }
526 }
527 }
528 Ok(out)
529}
530
531pub fn dot_q6_k_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
536 #[cfg(target_arch = "x86_64")]
537 {
538 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
539 return unsafe { simd_x86::dot_q6_k_f32_avx2(row_bytes, x) };
540 }
541 }
542 #[cfg(target_arch = "aarch64")]
543 {
544 if std::arch::is_aarch64_feature_detected!("neon") {
545 return unsafe { simd_aarch64::dot_q6_k_f32_neon(row_bytes, x) };
546 }
547 }
548 dot_q6_k_f32_scalar(row_bytes, x)
549}
550
551pub fn dot_q6_k_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
552 debug_assert_eq!(row_bytes.len() % Q6_K_BLOCK_BYTES, 0);
553 let mut acc = 0f32;
554 let mut x_base = 0usize;
555 for block in row_bytes.as_chunks::<Q6_K_BLOCK_BYTES>().0 {
556 let ql_full = &block[0..128];
557 let qh_full = &block[128..192];
558 let sc_full = &block[192..208];
559 let d = f16::from_le_bytes([block[208], block[209]]).to_f32();
560
561 for half in 0..2 {
562 let ql = &ql_full[half * 64..half * 64 + 64];
563 let qh = &qh_full[half * 32..half * 32 + 32];
564 let sc = &sc_full[half * 8..half * 8 + 8];
565 let xh = &x[x_base..x_base + 128];
566
567 for l in 0..32 {
568 let is = l / 16;
569 let q1 = ((ql[l] & 0x0F) | ((qh[l] & 3) << 4)) as i8 - 32;
570 let q2 = ((ql[l + 32] & 0x0F) | (((qh[l] >> 2) & 3) << 4)) as i8 - 32;
571 let q3 = ((ql[l] >> 4) | (((qh[l] >> 4) & 3) << 4)) as i8 - 32;
572 let q4 = ((ql[l + 32] >> 4) | (((qh[l] >> 6) & 3) << 4)) as i8 - 32;
573 acc += d * (sc[is] as i8 as f32) * (q1 as f32) * xh[l];
574 acc += d * (sc[is + 2] as i8 as f32) * (q2 as f32) * xh[l + 32];
575 acc += d * (sc[is + 4] as i8 as f32) * (q3 as f32) * xh[l + 64];
576 acc += d * (sc[is + 6] as i8 as f32) * (q4 as f32) * xh[l + 96];
577 }
578 x_base += 128;
579 }
580 }
581 acc
582}
583
584pub fn quantize_q8_0(src: &[f32]) -> Vec<u8> {
602 let mut out = Vec::with_capacity(src.len().div_ceil(Q8_0_BLOCK_ELEMS) * Q8_0_BLOCK_BYTES);
603 for chunk in src.chunks(Q8_0_BLOCK_ELEMS) {
604 let mut block = [0f32; Q8_0_BLOCK_ELEMS];
605 block[..chunk.len()].copy_from_slice(chunk);
606 encode::encode_block_q8_0(&block, &mut out);
607 }
608 out
609}
610
611pub fn dot_q8_0_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
625 #[cfg(target_arch = "x86_64")]
626 {
627 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
628 return unsafe { simd_x86::dot_q8_0_f32_avx2(row_bytes, x) };
629 }
630 }
631 #[cfg(target_arch = "aarch64")]
632 {
633 if std::arch::is_aarch64_feature_detected!("neon") {
634 return unsafe { simd_aarch64::dot_q8_0_f32_neon(row_bytes, x) };
635 }
636 }
637 dot_q8_0_f32_scalar(row_bytes, x)
638}
639
640pub fn dot_q8_0_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
641 debug_assert_eq!(row_bytes.len() % Q8_0_BLOCK_BYTES, 0);
642 debug_assert_eq!(
643 row_bytes.len() / Q8_0_BLOCK_BYTES * Q8_0_BLOCK_ELEMS,
644 x.len()
645 );
646 let mut acc = 0f32;
647 for (b, block) in row_bytes
648 .as_chunks::<Q8_0_BLOCK_BYTES>()
649 .0
650 .iter()
651 .enumerate()
652 {
653 let scale = f16::from_le_bytes([block[0], block[1]]).to_f32();
654 let base = b * Q8_0_BLOCK_ELEMS;
655 let mut block_acc = 0f32;
656 for i in 0..Q8_0_BLOCK_ELEMS {
657 let q = block[2 + i] as i8;
658 block_acc += (q as f32) * x[base + i];
659 }
660 acc += block_acc * scale;
661 }
662 acc
663}
664
665#[derive(Clone, Debug)]
674pub struct Q8Activations {
675 pub q: Vec<i8>,
677 pub d: Vec<f32>,
679}
680
681impl Q8Activations {
682 pub fn n_blocks(&self) -> usize {
683 self.d.len()
684 }
685}
686
687#[derive(Clone, Debug)]
690pub struct Q8KActivations {
691 pub q: Vec<i8>,
692 pub d: Vec<f32>,
693 pub bsums: Vec<i16>,
695}
696
697impl Q8KActivations {
698 pub fn n_blocks(&self) -> usize {
699 self.d.len()
700 }
701}
702
703pub fn quantize_activations_q8_k(x: &[f32]) -> Q8KActivations {
707 debug_assert_eq!(x.len() % Q4_K_BLOCK_ELEMS, 0);
708 let n_blocks = x.len() / Q4_K_BLOCK_ELEMS;
709 let mut q = vec![0i8; n_blocks * Q4_K_BLOCK_ELEMS];
710 let mut d = vec![0f32; n_blocks];
711 let mut bsums = vec![0i16; n_blocks * 16];
712 let quant_one =
713 |(q_slot, d_slot, bsum_slot, chunk): (&mut [i8], &mut f32, &mut [i16], &[f32])| {
714 let amax = chunk.iter().fold(0f32, |m, &v| m.max(v.abs()));
715 let scale = amax / 127.0;
716 let inv = if scale > 0.0 { 1.0 / scale } else { 0.0 };
717 *d_slot = scale;
718 for (i, &v) in chunk.iter().enumerate() {
719 let qi = (v * inv).round();
720 q_slot[i] = qi.clamp(-127.0, 127.0) as i8;
721 }
722 for (slot, group) in bsum_slot.iter_mut().zip(q_slot.as_chunks::<16>().0) {
723 *slot = group.iter().map(|&q| q as i32).sum::<i32>() as i16;
724 }
725 };
726 for (b, chunk) in x.as_chunks::<Q4_K_BLOCK_ELEMS>().0.iter().enumerate() {
732 quant_one((
733 &mut q[b * Q4_K_BLOCK_ELEMS..(b + 1) * Q4_K_BLOCK_ELEMS],
734 &mut d[b],
735 &mut bsums[b * 16..(b + 1) * 16],
736 chunk,
737 ));
738 }
739 Q8KActivations { q, d, bsums }
740}
741
742pub fn quantize_activations_q8(x: &[f32]) -> Q8Activations {
746 debug_assert_eq!(x.len() % Q8_0_BLOCK_ELEMS, 0);
747 let n_blocks = x.len() / Q8_0_BLOCK_ELEMS;
748 let mut q = vec![0i8; n_blocks * Q8_0_BLOCK_ELEMS];
749 let mut d = vec![0f32; n_blocks];
750 let quant_one = |(q_slot, d_slot, chunk): (&mut [i8], &mut f32, &[f32])| {
751 let amax = chunk.iter().fold(0f32, |m, &v| m.max(v.abs()));
752 let scale = amax / 127.0;
753 let inv = if scale > 0.0 { 1.0 / scale } else { 0.0 };
754 *d_slot = scale;
755 for (i, &v) in chunk.iter().enumerate() {
756 let qi = (v * inv).round();
758 q_slot[i] = qi.clamp(-127.0, 127.0) as i8;
759 }
760 };
761 for (b, chunk) in x.as_chunks::<Q8_0_BLOCK_ELEMS>().0.iter().enumerate() {
765 quant_one((
766 &mut q[b * Q8_0_BLOCK_ELEMS..(b + 1) * Q8_0_BLOCK_ELEMS],
767 &mut d[b],
768 chunk,
769 ));
770 }
771 Q8Activations { q, d }
772}
773
774pub fn dot_q8_0_q8(row_bytes: &[u8], act: &Q8Activations) -> f32 {
779 #[cfg(target_arch = "x86_64")]
780 {
781 if is_x86_feature_detected!("avx2") {
782 return unsafe { simd_x86::dot_q8_0_q8_avx2(row_bytes, act) };
783 }
784 }
785 #[cfg(target_arch = "aarch64")]
786 {
787 if std::arch::is_aarch64_feature_detected!("dotprod") {
788 return unsafe { simd_aarch64::dot_q8_0_q8_neon_sdot(row_bytes, act) };
789 }
790 if std::arch::is_aarch64_feature_detected!("neon") {
791 return unsafe { simd_aarch64::dot_q8_0_q8_neon(row_bytes, act) };
792 }
793 }
794 dot_q8_0_q8_scalar(row_bytes, act)
795}
796
797pub fn dot_q8_0_q8_scalar(row_bytes: &[u8], act: &Q8Activations) -> f32 {
798 debug_assert_eq!(row_bytes.len() % Q8_0_BLOCK_BYTES, 0);
799 let n_blocks = row_bytes.len() / Q8_0_BLOCK_BYTES;
800 debug_assert_eq!(n_blocks, act.n_blocks());
801 let mut acc = 0f32;
802 for (b, block) in row_bytes
803 .as_chunks::<Q8_0_BLOCK_BYTES>()
804 .0
805 .iter()
806 .enumerate()
807 {
808 let dw = f16::from_le_bytes([block[0], block[1]]).to_f32();
809 let base = b * Q8_0_BLOCK_ELEMS;
810 let mut isum = 0i32;
811 for i in 0..Q8_0_BLOCK_ELEMS {
812 let qw = block[2 + i] as i8 as i32;
813 let qa = act.q[base + i] as i32;
814 isum += qw * qa;
815 }
816 acc += dw * act.d[b] * isum as f32;
817 }
818 acc
819}
820
821pub fn dot_q4_0_q8(row_bytes: &[u8], act: &Q8Activations) -> f32 {
825 #[cfg(target_arch = "x86_64")]
826 {
827 if is_x86_feature_detected!("avx2") {
828 return unsafe { simd_x86::dot_q4_0_q8_avx2(row_bytes, act) };
829 }
830 }
831 #[cfg(target_arch = "aarch64")]
832 {
833 if std::arch::is_aarch64_feature_detected!("dotprod") {
834 return unsafe { simd_aarch64::dot_q4_0_q8_neon_sdot(row_bytes, act) };
835 }
836 if std::arch::is_aarch64_feature_detected!("neon") {
837 return unsafe { simd_aarch64::dot_q4_0_q8_neon(row_bytes, act) };
838 }
839 }
840 dot_q4_0_q8_scalar(row_bytes, act)
841}
842
843pub fn dot_q4_0_q8_2row(row0: &[u8], row1: &[u8], act: &Q8Activations) -> (f32, f32) {
846 #[cfg(target_arch = "aarch64")]
847 {
848 if std::arch::is_aarch64_feature_detected!("dotprod")
849 && row0.len() == row1.len()
850 && row0.len().is_multiple_of(Q4_0_BLOCK_BYTES)
851 {
852 return unsafe { simd_aarch64::dot_q4_0_q8_neon_sdot_2row(row0, row1, act) };
853 }
854 }
855 (dot_q4_0_q8(row0, act), dot_q4_0_q8(row1, act))
856}
857
858pub fn dot_q4_0_q8_scalar(row_bytes: &[u8], act: &Q8Activations) -> f32 {
859 debug_assert_eq!(row_bytes.len() % Q4_0_BLOCK_BYTES, 0);
860 let n_blocks = row_bytes.len() / Q4_0_BLOCK_BYTES;
861 debug_assert_eq!(n_blocks, act.n_blocks());
862 let mut acc = 0f32;
863 for (b, block) in row_bytes
864 .as_chunks::<Q4_0_BLOCK_BYTES>()
865 .0
866 .iter()
867 .enumerate()
868 {
869 let dw = f16::from_le_bytes([block[0], block[1]]).to_f32();
870 let base = b * Q4_0_BLOCK_ELEMS;
871 let mut isum = 0i32;
872 for i in 0..16 {
873 let qs = block[2 + i];
874 let q0 = (qs & 0x0F) as i32 - 8;
875 let q1 = (qs >> 4) as i32 - 8;
876 isum += q0 * act.q[base + i] as i32;
877 isum += q1 * act.q[base + 16 + i] as i32;
878 }
879 acc += dw * act.d[b] * isum as f32;
880 }
881 acc
882}
883
884pub fn dot_q4_k_q8(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
887 #[cfg(target_arch = "x86_64")]
888 {
889 if is_x86_feature_detected!("avx2") {
890 return unsafe { simd_x86::dot_q4_k_q8_avx2(row_bytes, act) };
891 }
892 }
893 #[cfg(target_arch = "aarch64")]
894 {
895 if std::arch::is_aarch64_feature_detected!("i8mm") {
896 return unsafe { simd_aarch64::dot_q4_k_q8_neon_i8mm(row_bytes, act) };
897 }
898 if std::arch::is_aarch64_feature_detected!("dotprod") {
899 return unsafe { simd_aarch64::dot_q4_k_q8_neon_sdot(row_bytes, act) };
900 }
901 if std::arch::is_aarch64_feature_detected!("neon") {
902 return unsafe { simd_aarch64::dot_q4_k_q8_neon(row_bytes, act) };
903 }
904 }
905 dot_q4_k_q8_scalar(row_bytes, act)
906}
907
908pub fn dot_q4_k_q8_scalar(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
909 debug_assert_eq!(row_bytes.len() % Q4_K_BLOCK_BYTES, 0);
910 let n_blocks = row_bytes.len() / Q4_K_BLOCK_BYTES;
911 debug_assert_eq!(n_blocks, act.n_blocks());
912 let mut acc = 0f32;
913 for (b, block) in row_bytes
914 .as_chunks::<Q4_K_BLOCK_BYTES>()
915 .0
916 .iter()
917 .enumerate()
918 {
919 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
920 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
921 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
922 let qs = &block[16..144];
923 let da = act.d[b];
924 let q8 = &act.q[b * Q4_K_BLOCK_ELEMS..(b + 1) * Q4_K_BLOCK_ELEMS];
925 let bsums = &act.bsums[b * 16..(b + 1) * 16];
926
927 let mut sum_min = 0i32;
928 for i in 0..8 {
929 let (_, m) = q4_k_scale_min(i, &scales);
930 sum_min += m as i32 * (bsums[2 * i] as i32 + bsums[2 * i + 1] as i32);
931 }
932 acc -= dmin * da * sum_min as f32;
933
934 let mut q_off = 0usize;
935 let mut base = 0usize;
936 let mut is = 0usize;
937 for _ in 0..4 {
938 let (sc1, _) = q4_k_scale_min(is, &scales);
939 let (sc2, _) = q4_k_scale_min(is + 1, &scales);
940 let mut isum1 = 0i32;
941 let mut isum2 = 0i32;
942 for l in 0..32 {
943 isum1 += (qs[q_off + l] & 0x0F) as i32 * q8[base + l] as i32;
944 }
945 for l in 0..32 {
946 isum2 += (qs[q_off + l] >> 4) as i32 * q8[base + 32 + l] as i32;
947 }
948 acc += d * da * (sc1 as f32 * isum1 as f32 + sc2 as f32 * isum2 as f32);
949 q_off += 32;
950 base += 64;
951 is += 2;
952 }
953 }
954 acc
955}
956
957pub fn dot_q5_k_q8(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
960 #[cfg(target_arch = "aarch64")]
961 {
962 if std::arch::is_aarch64_feature_detected!("dotprod") {
963 return unsafe { simd_aarch64::dot_q5_k_q8_neon_sdot(row_bytes, act) };
964 }
965 if std::arch::is_aarch64_feature_detected!("neon") {
966 return unsafe { simd_aarch64::dot_q5_k_q8_neon(row_bytes, act) };
967 }
968 }
969 dot_q5_k_q8_scalar(row_bytes, act)
970}
971
972pub fn dot_q5_k_q8_scalar(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
973 debug_assert_eq!(row_bytes.len() % Q5_K_BLOCK_BYTES, 0);
974 let n_blocks = row_bytes.len() / Q5_K_BLOCK_BYTES;
975 debug_assert_eq!(n_blocks, act.n_blocks());
976 let mut acc = 0f32;
977 for (b, block) in row_bytes
978 .as_chunks::<Q5_K_BLOCK_BYTES>()
979 .0
980 .iter()
981 .enumerate()
982 {
983 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
984 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
985 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
986 let qh = &block[16..48];
987 let qs = &block[48..176];
988 let da = act.d[b];
989 let q8 = &act.q[b * Q5_K_BLOCK_ELEMS..(b + 1) * Q5_K_BLOCK_ELEMS];
990 let bsums = &act.bsums[b * 16..(b + 1) * 16];
991
992 let mut sum_min = 0i32;
993 for i in 0..8 {
994 let (_, m) = q4_k_scale_min(i, &scales);
995 sum_min += m as i32 * (bsums[2 * i] as i32 + bsums[2 * i + 1] as i32);
996 }
997 acc -= dmin * da * sum_min as f32;
998
999 let mut q_off = 0usize;
1000 let mut base = 0usize;
1001 let mut is = 0usize;
1002 let (mut u1, mut u2) = (1u8, 2u8);
1003 for _ in 0..4 {
1004 let (sc1, _) = q4_k_scale_min(is, &scales);
1005 let (sc2, _) = q4_k_scale_min(is + 1, &scales);
1006 let mut isum1 = 0i32;
1007 let mut isum2 = 0i32;
1008 for l in 0..32 {
1009 let hi = if qh[l] & u1 != 0 { 16 } else { 0 };
1010 isum1 += ((qs[q_off + l] & 0x0F) + hi) as i32 * q8[base + l] as i32;
1011 }
1012 for l in 0..32 {
1013 let hi = if qh[l] & u2 != 0 { 16 } else { 0 };
1014 isum2 += ((qs[q_off + l] >> 4) + hi) as i32 * q8[base + 32 + l] as i32;
1015 }
1016 acc += d * da * (sc1 as f32 * isum1 as f32 + sc2 as f32 * isum2 as f32);
1017 q_off += 32;
1018 base += 64;
1019 is += 2;
1020 u1 <<= 2;
1021 u2 <<= 2;
1022 }
1023 }
1024 acc
1025}
1026
1027pub const Q5_K_GEMM_NC: usize = 4;
1031pub const Q6_K_GEMM_NC: usize = 4;
1032
1033pub fn gemm_q5_k_q8_row(row_bytes: &[u8], acts: &[Q8KActivations], out: &mut [f32]) {
1039 assert_eq!(out.len(), acts.len());
1040 if acts.is_empty() {
1041 return;
1042 }
1043 #[cfg(target_arch = "aarch64")]
1044 {
1045 if acts.len() <= Q5_K_GEMM_NC && std::arch::is_aarch64_feature_detected!("dotprod") {
1046 unsafe {
1047 simd_aarch64::gemm_q5_k_q8_neon_sdot(row_bytes, acts, out);
1048 }
1049 return;
1050 }
1051 }
1052 gemm_q5_k_q8_row_scalar(row_bytes, acts, out);
1053}
1054
1055pub fn gemm_q5_k_q8_row_scalar(row_bytes: &[u8], acts: &[Q8KActivations], out: &mut [f32]) {
1056 debug_assert_eq!(row_bytes.len() % Q5_K_BLOCK_BYTES, 0);
1057 out.fill(0.0);
1058 let n_blocks = row_bytes.len() / Q5_K_BLOCK_BYTES;
1059 for act in acts {
1060 debug_assert_eq!(n_blocks, act.n_blocks());
1061 }
1062 for (b, block) in row_bytes
1063 .as_chunks::<Q5_K_BLOCK_BYTES>()
1064 .0
1065 .iter()
1066 .enumerate()
1067 {
1068 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
1069 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
1070 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
1071 let qh = &block[16..48];
1072 let qs = &block[48..176];
1073 let mut mins = [0u8; 8];
1074 let mut sc_only = [0u8; 8];
1075 for i in 0..8 {
1076 let (s, m) = q4_k_scale_min(i, &scales);
1077 sc_only[i] = s;
1078 mins[i] = m;
1079 }
1080 for (j, act) in acts.iter().enumerate() {
1081 let da = act.d[b];
1082 let q8 = &act.q[b * Q5_K_BLOCK_ELEMS..(b + 1) * Q5_K_BLOCK_ELEMS];
1083 let bsums = &act.bsums[b * 16..(b + 1) * 16];
1084 let mut sum_min = 0i32;
1085 for i in 0..8 {
1086 sum_min += mins[i] as i32 * (bsums[2 * i] as i32 + bsums[2 * i + 1] as i32);
1087 }
1088 out[j] -= dmin * da * sum_min as f32;
1089
1090 let mut q_off = 0usize;
1091 let mut base = 0usize;
1092 let mut is = 0usize;
1093 let (mut u1, mut u2) = (1u8, 2u8);
1094 for _ in 0..4 {
1095 let sc1 = sc_only[is];
1096 let sc2 = sc_only[is + 1];
1097 let mut isum1 = 0i32;
1098 let mut isum2 = 0i32;
1099 for l in 0..32 {
1100 let hi = if qh[l] & u1 != 0 { 16 } else { 0 };
1101 isum1 += ((qs[q_off + l] & 0x0F) + hi) as i32 * q8[base + l] as i32;
1102 }
1103 for l in 0..32 {
1104 let hi = if qh[l] & u2 != 0 { 16 } else { 0 };
1105 isum2 += ((qs[q_off + l] >> 4) + hi) as i32 * q8[base + 32 + l] as i32;
1106 }
1107 out[j] += d * da * (sc1 as f32 * isum1 as f32 + sc2 as f32 * isum2 as f32);
1108 q_off += 32;
1109 base += 64;
1110 is += 2;
1111 u1 <<= 2;
1112 u2 <<= 2;
1113 }
1114 }
1115 }
1116}
1117
1118pub fn gemm_q6_k_q8_row(row_bytes: &[u8], acts: &[Q8KActivations], out: &mut [f32]) {
1120 assert_eq!(out.len(), acts.len());
1121 if acts.is_empty() {
1122 return;
1123 }
1124 #[cfg(target_arch = "aarch64")]
1125 {
1126 if acts.len() <= Q6_K_GEMM_NC && std::arch::is_aarch64_feature_detected!("dotprod") {
1127 unsafe {
1128 simd_aarch64::gemm_q6_k_q8_neon_sdot(row_bytes, acts, out);
1129 }
1130 return;
1131 }
1132 }
1133 gemm_q6_k_q8_row_scalar(row_bytes, acts, out);
1134}
1135
1136pub fn gemm_q6_k_q8_row_scalar(row_bytes: &[u8], acts: &[Q8KActivations], out: &mut [f32]) {
1137 out.fill(0.0);
1138 for (j, act) in acts.iter().enumerate() {
1139 out[j] = dot_q6_k_q8_scalar(row_bytes, act);
1140 }
1141}
1142
1143pub fn dot_q6_k_q8(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
1146 #[cfg(target_arch = "aarch64")]
1147 {
1148 if std::arch::is_aarch64_feature_detected!("dotprod") {
1149 return unsafe { simd_aarch64::dot_q6_k_q8_neon_sdot(row_bytes, act) };
1150 }
1151 }
1152 dot_q6_k_q8_scalar(row_bytes, act)
1153}
1154
1155pub fn dot_q6_k_q8_scalar(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
1156 debug_assert_eq!(row_bytes.len() % Q6_K_BLOCK_BYTES, 0);
1157 let n_blocks = row_bytes.len() / Q6_K_BLOCK_BYTES;
1158 debug_assert_eq!(n_blocks, act.n_blocks());
1159 debug_assert_eq!(Q6_K_BLOCK_ELEMS, Q4_K_BLOCK_ELEMS);
1161 let mut acc = 0f32;
1162 for (b, block) in row_bytes
1163 .as_chunks::<Q6_K_BLOCK_BYTES>()
1164 .0
1165 .iter()
1166 .enumerate()
1167 {
1168 let ql_full = &block[0..128];
1169 let qh_full = &block[128..192];
1170 let sc_full = &block[192..208];
1171 let d = f16::from_le_bytes([block[208], block[209]]).to_f32();
1172 let da = act.d[b];
1173 let q8 = &act.q[b * Q6_K_BLOCK_ELEMS..(b + 1) * Q6_K_BLOCK_ELEMS];
1174 let mut isum = 0i32;
1175
1176 for half in 0..2 {
1177 let ql = &ql_full[half * 64..half * 64 + 64];
1178 let qh = &qh_full[half * 32..half * 32 + 32];
1179 let sc = &sc_full[half * 8..half * 8 + 8];
1180 let q8h = &q8[half * 128..half * 128 + 128];
1181 for l in 0..32 {
1182 let is = l / 16;
1183 let q1 = ((ql[l] & 0x0F) | ((qh[l] & 3) << 4)) as i8 as i32 - 32;
1184 let q2 = ((ql[l + 32] & 0x0F) | (((qh[l] >> 2) & 3) << 4)) as i8 as i32 - 32;
1185 let q3 = ((ql[l] >> 4) | (((qh[l] >> 4) & 3) << 4)) as i8 as i32 - 32;
1186 let q4 = ((ql[l + 32] >> 4) | (((qh[l] >> 6) & 3) << 4)) as i8 as i32 - 32;
1187 isum += (sc[is] as i8 as i32) * q1 * (q8h[l] as i32);
1188 isum += (sc[is + 2] as i8 as i32) * q2 * (q8h[l + 32] as i32);
1189 isum += (sc[is + 4] as i8 as i32) * q3 * (q8h[l + 64] as i32);
1190 isum += (sc[is + 6] as i8 as i32) * q4 * (q8h[l + 96] as i32);
1191 }
1192 }
1193 acc += d * da * isum as f32;
1194 }
1195 acc
1196}
1197
1198#[cfg(target_arch = "x86_64")]
1199mod simd_x86 {
1200 use super::{
1201 e8m0_scale, q3_k_unpack_scales, q4_k_scale_min, q5_fifth_bits, Q8Activations,
1202 Q8KActivations, IQ4_NL_BLOCK_BYTES, IQ4_NL_BLOCK_ELEMS, IQ4_XS_BLOCK_BYTES, KVALUES_IQ4NL,
1203 MXFP4_GROUP_SIZE, Q2_K_BLOCK_BYTES, Q2_K_SCALE_BYTES, Q3_K_BLOCK_BYTES, Q3_K_SCALE_BYTES,
1204 Q4_0_BLOCK_BYTES, Q4_0_BLOCK_ELEMS, Q4_1_BLOCK_BYTES, Q4_1_BLOCK_ELEMS, Q4_K_BLOCK_BYTES,
1205 Q4_K_BLOCK_ELEMS, Q4_K_SCALE_BYTES, Q5_0_BLOCK_BYTES, Q5_0_BLOCK_ELEMS, Q5_1_BLOCK_BYTES,
1206 Q5_1_BLOCK_ELEMS, Q5_K_BLOCK_BYTES, Q6_K_BLOCK_BYTES, Q6_K_BLOCK_ELEMS, Q8_0_BLOCK_BYTES,
1207 Q8_0_BLOCK_ELEMS, Q8_1_BLOCK_BYTES, Q8_1_BLOCK_ELEMS,
1208 };
1209 use half::f16;
1210 use std::arch::x86_64::*;
1211
1212 #[target_feature(enable = "avx2,fma")]
1222 pub unsafe fn dot_q8_0_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
1223 debug_assert_eq!(row_bytes.len() % Q8_0_BLOCK_BYTES, 0);
1224 debug_assert_eq!(
1225 row_bytes.len() / Q8_0_BLOCK_BYTES * Q8_0_BLOCK_ELEMS,
1226 x.len()
1227 );
1228 let mut acc = 0f32;
1229 for (b, block) in row_bytes
1230 .as_chunks::<Q8_0_BLOCK_BYTES>()
1231 .0
1232 .iter()
1233 .enumerate()
1234 {
1235 let scale = f16::from_le_bytes([block[0], block[1]]).to_f32();
1236 let base = b * Q8_0_BLOCK_ELEMS;
1237 let qs = &block[2..34];
1238
1239 let mut block_acc = _mm256_setzero_ps();
1240 for g in 0..4 {
1241 let raw8 = _mm_loadl_epi64(qs.as_ptr().add(g * 8) as *const __m128i);
1242 let i32x8 = _mm256_cvtepi8_epi32(raw8);
1243 let f32x8 = _mm256_cvtepi32_ps(i32x8);
1244 let xv = _mm256_loadu_ps(x.as_ptr().add(base + g * 8));
1245 block_acc = _mm256_fmadd_ps(f32x8, xv, block_acc);
1246 }
1247 acc += hsum256_ps(block_acc) * scale;
1248 }
1249 acc
1250 }
1251
1252 #[target_feature(enable = "avx2")]
1258 pub unsafe fn dot_q8_0_q8_avx2(row_bytes: &[u8], act: &Q8Activations) -> f32 {
1259 debug_assert_eq!(row_bytes.len() % Q8_0_BLOCK_BYTES, 0);
1260 debug_assert_eq!(row_bytes.len() / Q8_0_BLOCK_BYTES, act.n_blocks());
1261 let mut acc = 0f32;
1262 for (b, block) in row_bytes
1263 .as_chunks::<Q8_0_BLOCK_BYTES>()
1264 .0
1265 .iter()
1266 .enumerate()
1267 {
1268 let dw = f16::from_le_bytes([block[0], block[1]]).to_f32();
1269 let base = b * Q8_0_BLOCK_ELEMS;
1270 let w = _mm256_loadu_si256(block.as_ptr().add(2) as *const __m256i);
1271 let a = _mm256_loadu_si256(act.q.as_ptr().add(base) as *const __m256i);
1272 let w_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(w));
1273 let w_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(w, 1));
1274 let a_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(a));
1275 let a_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(a, 1));
1276 let prod =
1277 _mm256_add_epi32(_mm256_madd_epi16(w_lo, a_lo), _mm256_madd_epi16(w_hi, a_hi));
1278 let hi128 = _mm256_extracti128_si256(prod, 1);
1280 let lo128 = _mm256_castsi256_si128(prod);
1281 let mut sum128 = _mm_add_epi32(lo128, hi128);
1282 sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, 0b01_00_11_10));
1283 sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, 0b00_00_00_01));
1284 let isum = _mm_cvtsi128_si32(sum128);
1285 acc += dw * act.d[b] * isum as f32;
1286 }
1287 acc
1288 }
1289
1290 #[target_feature(enable = "avx2")]
1294 pub unsafe fn dot_q4_0_q8_avx2(row_bytes: &[u8], act: &Q8Activations) -> f32 {
1295 debug_assert_eq!(row_bytes.len() % Q4_0_BLOCK_BYTES, 0);
1296 debug_assert_eq!(row_bytes.len() / Q4_0_BLOCK_BYTES, act.n_blocks());
1297 let low_mask = _mm_set1_epi8(0x0F);
1298 let bias = _mm_set1_epi8(8);
1299 let mut acc = 0f32;
1300 for (b, block) in row_bytes
1301 .as_chunks::<Q4_0_BLOCK_BYTES>()
1302 .0
1303 .iter()
1304 .enumerate()
1305 {
1306 let dw = f16::from_le_bytes([block[0], block[1]]).to_f32();
1307 let base = b * Q4_0_BLOCK_ELEMS;
1308 let qs = _mm_loadu_si128(block.as_ptr().add(2) as *const __m128i);
1309 let lo = _mm_sub_epi8(_mm_and_si128(qs, low_mask), bias);
1310 let hi = _mm_sub_epi8(_mm_and_si128(_mm_srli_epi16(qs, 4), low_mask), bias);
1311 let w = _mm256_set_m128i(hi, lo);
1313 let a = _mm256_loadu_si256(act.q.as_ptr().add(base) as *const __m256i);
1314 let w_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(w));
1315 let w_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(w, 1));
1316 let a_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(a));
1317 let a_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(a, 1));
1318 let prod =
1319 _mm256_add_epi32(_mm256_madd_epi16(w_lo, a_lo), _mm256_madd_epi16(w_hi, a_hi));
1320 let hi128 = _mm256_extracti128_si256(prod, 1);
1321 let lo128 = _mm256_castsi256_si128(prod);
1322 let mut sum128 = _mm_add_epi32(lo128, hi128);
1323 sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, 0b01_00_11_10));
1324 sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, 0b00_00_00_01));
1325 let isum = _mm_cvtsi128_si32(sum128);
1326 acc += dw * act.d[b] * isum as f32;
1327 }
1328 acc
1329 }
1330
1331 #[target_feature(enable = "avx2")]
1333 pub unsafe fn dot_q4_k_q8_avx2(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
1334 debug_assert_eq!(row_bytes.len() % Q4_K_BLOCK_BYTES, 0);
1335 debug_assert_eq!(row_bytes.len() / Q4_K_BLOCK_BYTES, act.n_blocks());
1336 let low_mask = _mm256_set1_epi8(0x0F_u8 as i8);
1337 let mut acc = 0f32;
1338 for (b, block) in row_bytes
1339 .as_chunks::<Q4_K_BLOCK_BYTES>()
1340 .0
1341 .iter()
1342 .enumerate()
1343 {
1344 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
1345 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
1346 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
1347 let qs = &block[16..144];
1348 let da = act.d[b];
1349 let q8 = act.q.as_ptr().add(b * Q4_K_BLOCK_ELEMS);
1350 let bsums = &act.bsums[b * 16..(b + 1) * 16];
1351
1352 let mut sum_min = 0i32;
1353 for i in 0..8 {
1354 let (_, m) = q4_k_scale_min(i, &scales);
1355 sum_min += m as i32 * (bsums[2 * i] as i32 + bsums[2 * i + 1] as i32);
1356 }
1357 acc -= dmin * da * sum_min as f32;
1358
1359 let mut q_off = 0usize;
1360 let mut base = 0usize;
1361 let mut is = 0usize;
1362 for _ in 0..4 {
1363 let (sc1, _) = q4_k_scale_min(is, &scales);
1364 let (sc2, _) = q4_k_scale_min(is + 1, &scales);
1365 let packed = _mm256_loadu_si256(qs.as_ptr().add(q_off) as *const __m256i);
1366 let lo = _mm256_and_si256(packed, low_mask);
1367 let hi = _mm256_and_si256(_mm256_srli_epi16(packed, 4), low_mask);
1368 let a0 = _mm256_loadu_si256(q8.add(base) as *const __m256i);
1369 let a1 = _mm256_loadu_si256(q8.add(base + 32) as *const __m256i);
1370 let isum1 = madd_i8_avx2(lo, a0);
1371 let isum2 = madd_i8_avx2(hi, a1);
1372 acc += d * da * (sc1 as f32 * isum1 as f32 + sc2 as f32 * isum2 as f32);
1373 q_off += 32;
1374 base += 64;
1375 is += 2;
1376 }
1377 }
1378 acc
1379 }
1380
1381 #[target_feature(enable = "avx2")]
1382 unsafe fn madd_i8_avx2(w: __m256i, a: __m256i) -> i32 {
1383 let w_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(w));
1384 let w_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(w, 1));
1385 let a_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(a));
1386 let a_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(a, 1));
1387 let prod = _mm256_add_epi32(_mm256_madd_epi16(w_lo, a_lo), _mm256_madd_epi16(w_hi, a_hi));
1388 let hi128 = _mm256_extracti128_si256(prod, 1);
1389 let lo128 = _mm256_castsi256_si128(prod);
1390 let mut sum128 = _mm_add_epi32(lo128, hi128);
1391 sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, 0b01_00_11_10));
1392 sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, 0b00_00_00_01));
1393 _mm_cvtsi128_si32(sum128)
1394 }
1395
1396 #[target_feature(enable = "avx2,fma")]
1405 pub unsafe fn dot_q4_0_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
1406 debug_assert_eq!(row_bytes.len() % Q4_0_BLOCK_BYTES, 0);
1407 let bias = _mm_set1_epi8(8);
1408 let low_mask = _mm_set1_epi8(0x0F);
1409
1410 let mut acc = 0f32;
1411 for (b, block) in row_bytes
1412 .as_chunks::<Q4_0_BLOCK_BYTES>()
1413 .0
1414 .iter()
1415 .enumerate()
1416 {
1417 let scale = f16::from_le_bytes([block[0], block[1]]).to_f32();
1418 let base = b * Q4_0_BLOCK_ELEMS;
1419 let nibbles = _mm_loadu_si128(block.as_ptr().add(2) as *const __m128i);
1420
1421 let lo_nibbles = _mm_sub_epi8(_mm_and_si128(nibbles, low_mask), bias);
1422 let hi_nibbles =
1423 _mm_sub_epi8(_mm_and_si128(_mm_srli_epi16(nibbles, 4), low_mask), bias);
1424
1425 let mut block_acc = _mm256_setzero_ps();
1426 for (group_idx, half) in [
1428 (0usize, lo_nibbles),
1429 (1usize, _mm_srli_si128(lo_nibbles, 8)),
1430 (2usize, hi_nibbles),
1431 (3usize, _mm_srli_si128(hi_nibbles, 8)),
1432 ] {
1433 let i32x8 = _mm256_cvtepi8_epi32(half);
1434 let f32x8 = _mm256_cvtepi32_ps(i32x8);
1435 let elem_base = base + group_idx * 8;
1436 let xv = _mm256_loadu_ps(x.as_ptr().add(elem_base));
1437 block_acc = _mm256_fmadd_ps(f32x8, xv, block_acc);
1438 }
1439 acc += hsum256_ps(block_acc) * scale;
1440 }
1441 acc
1442 }
1443
1444 #[inline]
1445 #[target_feature(enable = "avx2")]
1446 unsafe fn hsum256_ps(v: __m256) -> f32 {
1447 let hi = _mm256_extractf128_ps(v, 1);
1448 let lo = _mm256_castps256_ps128(v);
1449 let sum128 = _mm_add_ps(hi, lo);
1450 let shuf = _mm_movehdup_ps(sum128);
1451 let sums = _mm_add_ps(sum128, shuf);
1452 let shuf2 = _mm_movehl_ps(shuf, sums);
1453 let sums2 = _mm_add_ss(sums, shuf2);
1454 _mm_cvtss_f32(sums2)
1455 }
1456
1457 #[inline]
1471 #[target_feature(enable = "avx2,fma")]
1472 unsafe fn fma_affine8(
1473 part: __m128i,
1474 d: f32,
1475 min: f32,
1476 x: &[f32],
1477 x_base: usize,
1478 acc: __m256,
1479 ) -> __m256 {
1480 let i32x8 = _mm256_cvtepu8_epi32(part);
1481 let f32x8 = _mm256_cvtepi32_ps(i32x8);
1482 let weight = _mm256_fmsub_ps(f32x8, _mm256_set1_ps(d), _mm256_set1_ps(min));
1483 let xv = _mm256_loadu_ps(x.as_ptr().add(x_base));
1484 _mm256_fmadd_ps(weight, xv, acc)
1485 }
1486
1487 #[target_feature(enable = "avx2,fma")]
1498 pub unsafe fn dot_q4_k_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
1499 debug_assert_eq!(row_bytes.len() % Q4_K_BLOCK_BYTES, 0);
1500 let low_mask = _mm_set1_epi8(0x0F);
1501 let mut acc = 0f32;
1502 let mut x_base = 0usize;
1503 for block in row_bytes.as_chunks::<Q4_K_BLOCK_BYTES>().0 {
1504 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
1505 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
1506 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
1507 let qs = &block[16..144];
1508
1509 let mut is = 0usize;
1510 let mut q_off = 0usize;
1511 for _ in 0..4 {
1512 let (sc1, m1) = q4_k_scale_min(is, &scales);
1513 let (sc2, m2) = q4_k_scale_min(is + 1, &scales);
1514 let d1 = d * sc1 as f32;
1515 let min1 = dmin * m1 as f32;
1516 let d2 = d * sc2 as f32;
1517 let min2 = dmin * m2 as f32;
1518
1519 let mut lo_acc = _mm256_setzero_ps();
1520 let mut hi_acc = _mm256_setzero_ps();
1521 for g in 0..2 {
1522 let raw16 = _mm_loadu_si128(qs.as_ptr().add(q_off + g * 16) as *const __m128i);
1523 let lo_nib = _mm_and_si128(raw16, low_mask);
1524 let hi_nib = _mm_and_si128(_mm_srli_epi16(raw16, 4), low_mask);
1525
1526 for (part_idx, part) in
1527 [lo_nib, _mm_srli_si128(lo_nib, 8)].into_iter().enumerate()
1528 {
1529 lo_acc =
1530 fma_affine8(part, d1, min1, x, x_base + g * 16 + part_idx * 8, lo_acc);
1531 }
1532 for (part_idx, part) in
1533 [hi_nib, _mm_srli_si128(hi_nib, 8)].into_iter().enumerate()
1534 {
1535 hi_acc = fma_affine8(
1536 part,
1537 d2,
1538 min2,
1539 x,
1540 x_base + 32 + g * 16 + part_idx * 8,
1541 hi_acc,
1542 );
1543 }
1544 }
1545 acc += hsum256_ps(lo_acc) + hsum256_ps(hi_acc);
1546 q_off += 32;
1547 x_base += 64;
1548 is += 2;
1549 }
1550 }
1551 acc
1552 }
1553
1554 #[target_feature(enable = "avx2,fma")]
1567 pub unsafe fn dot_q5_k_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
1568 debug_assert_eq!(row_bytes.len() % Q5_K_BLOCK_BYTES, 0);
1569 let low_mask = _mm_set1_epi8(0x0F);
1570 let zero = _mm_setzero_si128();
1571 let sixteen = _mm_set1_epi8(16);
1572 let mut acc = 0f32;
1573 let mut x_base = 0usize;
1574 for block in row_bytes.as_chunks::<Q5_K_BLOCK_BYTES>().0 {
1575 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
1576 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
1577 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
1578 let qh = &block[16..48];
1579 let qs = &block[48..176];
1580
1581 let mut is = 0usize;
1582 let (mut u1, mut u2) = (1u8, 2u8);
1583 for _oi in 0..4 {
1584 let (sc1, m1) = q4_k_scale_min(is, &scales);
1585 let (sc2, m2) = q4_k_scale_min(is + 1, &scales);
1586 let d1 = d * sc1 as f32;
1587 let min1 = dmin * m1 as f32;
1588 let d2 = d * sc2 as f32;
1589 let min2 = dmin * m2 as f32;
1590 let ql = &qs[is / 2 * 32..is / 2 * 32 + 32];
1591 let u1_vec = _mm_set1_epi8(u1 as i8);
1592 let u2_vec = _mm_set1_epi8(u2 as i8);
1593
1594 let mut lo_acc = _mm256_setzero_ps();
1595 let mut hi_acc = _mm256_setzero_ps();
1596 for g in 0..2 {
1597 let raw16 = _mm_loadu_si128(ql.as_ptr().add(g * 16) as *const __m128i);
1598 let qh16 = _mm_loadu_si128(qh.as_ptr().add(g * 16) as *const __m128i);
1599
1600 let lo_nib = _mm_and_si128(raw16, low_mask);
1601 let hi_nib = _mm_and_si128(_mm_srli_epi16(raw16, 4), low_mask);
1602
1603 let is_zero1 = _mm_cmpeq_epi8(_mm_and_si128(qh16, u1_vec), zero);
1604 let hi_bit1 = _mm_andnot_si128(is_zero1, sixteen);
1605 let is_zero2 = _mm_cmpeq_epi8(_mm_and_si128(qh16, u2_vec), zero);
1606 let hi_bit2 = _mm_andnot_si128(is_zero2, sixteen);
1607
1608 let lo_full = _mm_or_si128(lo_nib, hi_bit1);
1609 let hi_full = _mm_or_si128(hi_nib, hi_bit2);
1610
1611 for (part_idx, part) in [lo_full, _mm_srli_si128(lo_full, 8)]
1612 .into_iter()
1613 .enumerate()
1614 {
1615 lo_acc =
1616 fma_affine8(part, d1, min1, x, x_base + g * 16 + part_idx * 8, lo_acc);
1617 }
1618 for (part_idx, part) in [hi_full, _mm_srli_si128(hi_full, 8)]
1619 .into_iter()
1620 .enumerate()
1621 {
1622 hi_acc = fma_affine8(
1623 part,
1624 d2,
1625 min2,
1626 x,
1627 x_base + 32 + g * 16 + part_idx * 8,
1628 hi_acc,
1629 );
1630 }
1631 }
1632 acc += hsum256_ps(lo_acc) + hsum256_ps(hi_acc);
1633 x_base += 64;
1634 is += 2;
1635 u1 <<= 2;
1636 u2 <<= 2;
1637 }
1638 }
1639 acc
1640 }
1641
1642 #[inline]
1663 #[target_feature(enable = "avx2,fma")]
1664 #[allow(clippy::too_many_arguments)]
1665 unsafe fn q6_k_group_avx2<const QH_SHIFT: i32, const HI_NIBBLE: bool>(
1666 ql: &[u8],
1667 ql_off: usize,
1668 qh: &[u8],
1669 sc: &[u8],
1670 sc_base: usize,
1671 d: f32,
1672 x: &[f32],
1673 x_base: usize,
1674 out_off: usize,
1675 low_mask: __m128i,
1676 two_bit_mask: __m128i,
1677 bias: __m256,
1678 ) -> f32 {
1679 let mut acc = 0f32;
1680 for sub in 0..2usize {
1681 let byte_off = sub * 16;
1682 let ql_raw = _mm_loadu_si128(ql.as_ptr().add(ql_off + byte_off) as *const __m128i);
1683 let qh_raw = _mm_loadu_si128(qh.as_ptr().add(byte_off) as *const __m128i);
1684
1685 let nib = if HI_NIBBLE {
1686 _mm_and_si128(_mm_srli_epi16(ql_raw, 4), low_mask)
1687 } else {
1688 _mm_and_si128(ql_raw, low_mask)
1689 };
1690 let qh_field = _mm_and_si128(_mm_srli_epi16(qh_raw, QH_SHIFT), two_bit_mask);
1691 let raw6 = _mm_or_si128(nib, _mm_slli_epi16(qh_field, 4));
1692
1693 let scale = d * (sc[sc_base + sub] as i8) as f32;
1694 let elem_base = x_base + out_off + sub * 16;
1695 for (part_idx, part) in [raw6, _mm_srli_si128(raw6, 8)].into_iter().enumerate() {
1696 let i32x8 = _mm256_cvtepu8_epi32(part);
1697 let f32x8 = _mm256_sub_ps(_mm256_cvtepi32_ps(i32x8), bias);
1698 let xv = _mm256_loadu_ps(x.as_ptr().add(elem_base + part_idx * 8));
1699 let weighted = _mm256_mul_ps(f32x8, _mm256_set1_ps(scale));
1700 acc += hsum256_ps(_mm256_mul_ps(weighted, xv));
1701 }
1702 }
1703 acc
1704 }
1705
1706 #[target_feature(enable = "avx2,fma")]
1712 pub unsafe fn dot_q6_k_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
1713 debug_assert_eq!(row_bytes.len() % Q6_K_BLOCK_BYTES, 0);
1714 debug_assert_eq!(
1715 row_bytes.len() / Q6_K_BLOCK_BYTES * Q6_K_BLOCK_ELEMS,
1716 x.len()
1717 );
1718 let low_mask = _mm_set1_epi8(0x0F);
1719 let two_bit_mask = _mm_set1_epi8(0x03);
1720 let bias = _mm256_set1_ps(32.0);
1721
1722 let mut acc = 0f32;
1723 let mut x_base = 0usize;
1724 for block in row_bytes.as_chunks::<Q6_K_BLOCK_BYTES>().0 {
1725 let ql_full = &block[0..128];
1726 let qh_full = &block[128..192];
1727 let sc_full = &block[192..208];
1728 let d = f16::from_le_bytes([block[208], block[209]]).to_f32();
1729
1730 for half in 0..2 {
1731 let ql = &ql_full[half * 64..half * 64 + 64];
1732 let qh = &qh_full[half * 32..half * 32 + 32];
1733 let sc = &sc_full[half * 8..half * 8 + 8];
1734 let half_base = x_base + half * 128;
1735
1736 acc += q6_k_group_avx2::<0, false>(
1737 ql,
1738 0,
1739 qh,
1740 sc,
1741 0,
1742 d,
1743 x,
1744 half_base,
1745 0,
1746 low_mask,
1747 two_bit_mask,
1748 bias,
1749 );
1750 acc += q6_k_group_avx2::<2, false>(
1751 ql,
1752 32,
1753 qh,
1754 sc,
1755 2,
1756 d,
1757 x,
1758 half_base,
1759 32,
1760 low_mask,
1761 two_bit_mask,
1762 bias,
1763 );
1764 acc += q6_k_group_avx2::<4, true>(
1765 ql,
1766 0,
1767 qh,
1768 sc,
1769 4,
1770 d,
1771 x,
1772 half_base,
1773 64,
1774 low_mask,
1775 two_bit_mask,
1776 bias,
1777 );
1778 acc += q6_k_group_avx2::<6, true>(
1779 ql,
1780 32,
1781 qh,
1782 sc,
1783 6,
1784 d,
1785 x,
1786 half_base,
1787 96,
1788 low_mask,
1789 two_bit_mask,
1790 bias,
1791 );
1792 }
1793 x_base += Q6_K_BLOCK_ELEMS;
1794 }
1795 acc
1796 }
1797
1798 #[inline]
1807 #[target_feature(enable = "avx2,fma")]
1808 unsafe fn mxfp4_nibbles_to_f32x8(nib: __m128i) -> __m256 {
1809 let sign_bit = _mm_and_si128(nib, _mm_set1_epi8(0x8));
1810 let e = _mm_and_si128(_mm_srli_epi16(nib, 1), _mm_set1_epi8(0x3));
1811 let m = _mm_and_si128(nib, _mm_set1_epi8(0x1));
1812
1813 let pow2_table = _mm_setr_epi8(1, 1, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1814 let bias_table = _mm_setr_epi8(0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1815 let pow2_u8 = _mm_shuffle_epi8(pow2_table, e);
1816 let bias_u8 = _mm_shuffle_epi8(bias_table, e);
1817
1818 let pow2_f = _mm256_cvtepi32_ps(_mm256_cvtepu8_epi32(pow2_u8));
1819 let bias_f = _mm256_cvtepi32_ps(_mm256_cvtepu8_epi32(bias_u8));
1820 let m_f = _mm256_cvtepi32_ps(_mm256_cvtepu8_epi32(m));
1821 let sign_f = _mm256_cvtepi32_ps(_mm256_cvtepu8_epi32(sign_bit));
1822
1823 let magnitude = _mm256_mul_ps(pow2_f, _mm256_fmadd_ps(m_f, _mm256_set1_ps(0.5), bias_f));
1825 let sign_mul = _mm256_fnmadd_ps(sign_f, _mm256_set1_ps(0.25), _mm256_set1_ps(1.0));
1826 _mm256_mul_ps(magnitude, sign_mul)
1827 }
1828
1829 pub unsafe fn dot_mxfp4_row_f32_avx2(packed: &[u8], scales: &[u8], x: &[f32]) -> f32 {
1839 debug_assert_eq!(packed.len(), scales.len() * (MXFP4_GROUP_SIZE / 2));
1840 let low_mask = _mm_set1_epi8(0x0F);
1841 let mut acc = 0f32;
1842 let mut x_base = 0usize;
1843 for (g, &e_byte) in scales.iter().enumerate() {
1844 let d = e8m0_scale(e_byte);
1845 let group = &packed[g * 16..(g + 1) * 16];
1846 let bytes = _mm_loadu_si128(group.as_ptr() as *const __m128i);
1847 let lo_nib = _mm_and_si128(bytes, low_mask);
1848 let hi_nib = _mm_and_si128(_mm_srli_epi16(bytes, 4), low_mask);
1849
1850 let mut block_acc = _mm256_setzero_ps();
1851 for (half_idx, nib) in [
1852 (0usize, lo_nib),
1853 (1usize, _mm_srli_si128(lo_nib, 8)),
1854 (2usize, hi_nib),
1855 (3usize, _mm_srli_si128(hi_nib, 8)),
1856 ] {
1857 let vals = mxfp4_nibbles_to_f32x8(nib);
1858 let elem_base = x_base + half_idx * 8;
1859 let xv = _mm256_loadu_ps(x.as_ptr().add(elem_base));
1860 block_acc = _mm256_fmadd_ps(vals, xv, block_acc);
1861 }
1862 acc += hsum256_ps(block_acc) * d;
1863 x_base += MXFP4_GROUP_SIZE;
1864 }
1865 acc
1866 }
1867
1868 #[target_feature(enable = "avx2,fma")]
1874 pub unsafe fn dot_q8_1_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
1875 debug_assert_eq!(row_bytes.len() % Q8_1_BLOCK_BYTES, 0);
1876 let mut acc = 0f32;
1877 for (b, block) in row_bytes
1878 .as_chunks::<Q8_1_BLOCK_BYTES>()
1879 .0
1880 .iter()
1881 .enumerate()
1882 {
1883 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
1884 let base = b * Q8_1_BLOCK_ELEMS;
1885 let qs = &block[4..36];
1886
1887 let mut block_acc = _mm256_setzero_ps();
1888 for g in 0..4 {
1889 let raw8 = _mm_loadl_epi64(qs.as_ptr().add(g * 8) as *const __m128i);
1890 let i32x8 = _mm256_cvtepi8_epi32(raw8);
1891 let f32x8 = _mm256_cvtepi32_ps(i32x8);
1892 let xv = _mm256_loadu_ps(x.as_ptr().add(base + g * 8));
1893 block_acc = _mm256_fmadd_ps(f32x8, xv, block_acc);
1894 }
1895 acc += hsum256_ps(block_acc) * d;
1896 }
1897 acc
1898 }
1899
1900 #[target_feature(enable = "avx2,fma")]
1906 pub unsafe fn dot_q4_1_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
1907 debug_assert_eq!(row_bytes.len() % Q4_1_BLOCK_BYTES, 0);
1908 let low_mask = _mm_set1_epi8(0x0F);
1909 let mut acc = 0f32;
1910 for (b, block) in row_bytes
1911 .as_chunks::<Q4_1_BLOCK_BYTES>()
1912 .0
1913 .iter()
1914 .enumerate()
1915 {
1916 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
1917 let m = f16::from_le_bytes([block[2], block[3]]).to_f32();
1918 let base = b * Q4_1_BLOCK_ELEMS;
1919 let nibbles = _mm_loadu_si128(block.as_ptr().add(4) as *const __m128i);
1920
1921 let lo_nibbles = _mm_and_si128(nibbles, low_mask);
1922 let hi_nibbles = _mm_and_si128(_mm_srli_epi16(nibbles, 4), low_mask);
1923
1924 let mut lo_acc = _mm256_setzero_ps();
1925 let mut hi_acc = _mm256_setzero_ps();
1926 for (part_idx, part) in [lo_nibbles, _mm_srli_si128(lo_nibbles, 8)]
1927 .into_iter()
1928 .enumerate()
1929 {
1930 lo_acc = fma_affine8(part, d, -m, x, base + part_idx * 8, lo_acc);
1931 }
1932 for (part_idx, part) in [hi_nibbles, _mm_srli_si128(hi_nibbles, 8)]
1933 .into_iter()
1934 .enumerate()
1935 {
1936 hi_acc = fma_affine8(part, d, -m, x, base + 16 + part_idx * 8, hi_acc);
1937 }
1938 acc += hsum256_ps(lo_acc) + hsum256_ps(hi_acc);
1939 }
1940 acc
1941 }
1942
1943 #[target_feature(enable = "avx2,fma")]
1955 pub unsafe fn dot_q5_0_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
1956 debug_assert_eq!(row_bytes.len() % Q5_0_BLOCK_BYTES, 0);
1957 let mut acc = 0f32;
1958 for (b, block) in row_bytes
1959 .as_chunks::<Q5_0_BLOCK_BYTES>()
1960 .0
1961 .iter()
1962 .enumerate()
1963 {
1964 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
1965 let qh = u32::from_le_bytes(block[2..6].try_into().unwrap());
1966 let qs = &block[6..22];
1967 let base = b * Q5_0_BLOCK_ELEMS;
1968
1969 let mut vals = [0i8; 32];
1970 for j in 0..16 {
1971 let (xh_0, xh_1) = q5_fifth_bits(qh, j);
1972 vals[j] = (((qs[j] & 0x0F) | xh_0) as i32 - 16) as i8;
1973 vals[j + 16] = (((qs[j] >> 4) | xh_1) as i32 - 16) as i8;
1974 }
1975
1976 let mut block_acc = _mm256_setzero_ps();
1977 for g in 0..4 {
1978 let raw8 = _mm_loadl_epi64(vals.as_ptr().add(g * 8) as *const __m128i);
1979 let i32x8 = _mm256_cvtepi8_epi32(raw8);
1980 let f32x8 = _mm256_cvtepi32_ps(i32x8);
1981 let xv = _mm256_loadu_ps(x.as_ptr().add(base + g * 8));
1982 block_acc = _mm256_fmadd_ps(f32x8, xv, block_acc);
1983 }
1984 acc += hsum256_ps(block_acc) * d;
1985 }
1986 acc
1987 }
1988
1989 #[target_feature(enable = "avx2,fma")]
1995 pub unsafe fn dot_q5_1_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
1996 debug_assert_eq!(row_bytes.len() % Q5_1_BLOCK_BYTES, 0);
1997 let mut acc = 0f32;
1998 for (b, block) in row_bytes
1999 .as_chunks::<Q5_1_BLOCK_BYTES>()
2000 .0
2001 .iter()
2002 .enumerate()
2003 {
2004 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
2005 let m = f16::from_le_bytes([block[2], block[3]]).to_f32();
2006 let qh = u32::from_le_bytes(block[4..8].try_into().unwrap());
2007 let qs = &block[8..24];
2008 let base = b * Q5_1_BLOCK_ELEMS;
2009
2010 let mut vals = [0u8; 32];
2011 for j in 0..16 {
2012 let (xh_0, xh_1) = q5_fifth_bits(qh, j);
2013 vals[j] = (qs[j] & 0x0F) | xh_0;
2014 vals[j + 16] = (qs[j] >> 4) | xh_1;
2015 }
2016
2017 let mut block_acc = _mm256_setzero_ps();
2018 for g in 0..4 {
2019 let raw8 = _mm_loadl_epi64(vals.as_ptr().add(g * 8) as *const __m128i);
2020 let i32x8 = _mm256_cvtepu8_epi32(raw8);
2021 let f32x8 = _mm256_cvtepi32_ps(i32x8);
2022 let weight = _mm256_fmadd_ps(f32x8, _mm256_set1_ps(d), _mm256_set1_ps(m));
2023 let xv = _mm256_loadu_ps(x.as_ptr().add(base + g * 8));
2024 block_acc = _mm256_fmadd_ps(weight, xv, block_acc);
2025 }
2026 acc += hsum256_ps(block_acc);
2027 }
2028 acc
2029 }
2030
2031 #[target_feature(enable = "avx2,fma")]
2047 pub unsafe fn dot_q2_k_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
2048 debug_assert_eq!(row_bytes.len() % Q2_K_BLOCK_BYTES, 0);
2049 let two_bit_mask = _mm_set1_epi8(3);
2050 let mut acc = 0f32;
2051 let mut x_base = 0usize;
2052
2053 macro_rules! q2_k_sub_block {
2054 ($shift:literal, $q:expr, $scales:expr, $is:expr, $d:expr, $dmin:expr, $x:expr, $x_base:expr, $acc:expr) => {{
2055 let sc1 = $scales[$is];
2056 $is += 1;
2057 let dl1 = $d * (sc1 & 0x0F) as f32;
2058 let ml1 = $dmin * (sc1 >> 4) as f32;
2059 let sc2 = $scales[$is];
2060 $is += 1;
2061 let dl2 = $d * (sc2 & 0x0F) as f32;
2062 let ml2 = $dmin * (sc2 >> 4) as f32;
2063
2064 let lo16 = _mm_loadu_si128($q.as_ptr() as *const __m128i);
2065 let hi16 = _mm_loadu_si128($q.as_ptr().add(16) as *const __m128i);
2066 let lo2 = _mm_and_si128(_mm_srli_epi16(lo16, $shift), two_bit_mask);
2067 let hi2 = _mm_and_si128(_mm_srli_epi16(hi16, $shift), two_bit_mask);
2068
2069 let mut lo_acc = _mm256_setzero_ps();
2070 let mut hi_acc = _mm256_setzero_ps();
2071 for (part_idx, part) in [lo2, _mm_srli_si128(lo2, 8)].into_iter().enumerate() {
2072 lo_acc = fma_affine8(part, dl1, ml1, $x, $x_base + part_idx * 8, lo_acc);
2073 }
2074 for (part_idx, part) in [hi2, _mm_srli_si128(hi2, 8)].into_iter().enumerate() {
2075 hi_acc = fma_affine8(part, dl2, ml2, $x, $x_base + 16 + part_idx * 8, hi_acc);
2076 }
2077 $acc += hsum256_ps(lo_acc) + hsum256_ps(hi_acc);
2078 $x_base += 32;
2079 }};
2080 }
2081
2082 for block in row_bytes.as_chunks::<Q2_K_BLOCK_BYTES>().0 {
2083 let scales: &[u8; Q2_K_SCALE_BYTES] = block[0..16].try_into().unwrap();
2084 let qs = &block[16..80];
2085 let d = f16::from_le_bytes([block[80], block[81]]).to_f32();
2086 let dmin = f16::from_le_bytes([block[82], block[83]]).to_f32();
2087
2088 let mut is = 0usize;
2089 for n in 0..2 {
2090 let q = &qs[n * 32..n * 32 + 32];
2091 q2_k_sub_block!(0, q, scales, is, d, dmin, x, x_base, acc);
2092 q2_k_sub_block!(2, q, scales, is, d, dmin, x, x_base, acc);
2093 q2_k_sub_block!(4, q, scales, is, d, dmin, x, x_base, acc);
2094 q2_k_sub_block!(6, q, scales, is, d, dmin, x, x_base, acc);
2095 }
2096 }
2097 acc
2098 }
2099
2100 #[target_feature(enable = "avx2,fma")]
2115 pub unsafe fn dot_q3_k_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
2116 debug_assert_eq!(row_bytes.len() % Q3_K_BLOCK_BYTES, 0);
2117 let two_bit_mask = _mm_set1_epi8(3);
2118 let zero = _mm_setzero_si128();
2119 let four = _mm_set1_epi8(4);
2120 let mut acc = 0f32;
2121 let mut x_base = 0usize;
2122
2123 macro_rules! q3_k_sub_block {
2124 ($shift:literal, $q:expr, $hmask:expr, $m_vec:expr, $dl1:expr, $dl2:expr, $x:expr, $x_base:expr, $acc:expr) => {{
2125 let lo16 = _mm_loadu_si128($q.as_ptr() as *const __m128i);
2126 let hi16 = _mm_loadu_si128($q.as_ptr().add(16) as *const __m128i);
2127 let lo2 = _mm_and_si128(_mm_srli_epi16(lo16, $shift), two_bit_mask);
2128 let hi2 = _mm_and_si128(_mm_srli_epi16(hi16, $shift), two_bit_mask);
2129
2130 let hmask_lo = _mm_loadu_si128($hmask.as_ptr() as *const __m128i);
2131 let hmask_hi = _mm_loadu_si128($hmask.as_ptr().add(16) as *const __m128i);
2132 let bit_clear_lo = _mm_cmpeq_epi8(_mm_and_si128(hmask_lo, $m_vec), zero);
2136 let bit_clear_hi = _mm_cmpeq_epi8(_mm_and_si128(hmask_hi, $m_vec), zero);
2137 let bias_lo = _mm_and_si128(bit_clear_lo, four);
2138 let bias_hi = _mm_and_si128(bit_clear_hi, four);
2139 let raw_lo = _mm_sub_epi8(lo2, bias_lo);
2140 let raw_hi = _mm_sub_epi8(hi2, bias_hi);
2141
2142 let mut lo_acc = _mm256_setzero_ps();
2143 let mut hi_acc = _mm256_setzero_ps();
2144 for (part_idx, part) in [raw_lo, _mm_srli_si128(raw_lo, 8)].into_iter().enumerate()
2145 {
2146 let i32x8 = _mm256_cvtepi8_epi32(part);
2147 let f32x8 = _mm256_cvtepi32_ps(i32x8);
2148 let xv = _mm256_loadu_ps($x.as_ptr().add($x_base + part_idx * 8));
2149 lo_acc = _mm256_fmadd_ps(f32x8, xv, lo_acc);
2150 }
2151 for (part_idx, part) in [raw_hi, _mm_srli_si128(raw_hi, 8)].into_iter().enumerate()
2152 {
2153 let i32x8 = _mm256_cvtepi8_epi32(part);
2154 let f32x8 = _mm256_cvtepi32_ps(i32x8);
2155 let xv = _mm256_loadu_ps($x.as_ptr().add($x_base + 16 + part_idx * 8));
2156 hi_acc = _mm256_fmadd_ps(f32x8, xv, hi_acc);
2157 }
2158 $acc += hsum256_ps(lo_acc) * $dl1 + hsum256_ps(hi_acc) * $dl2;
2159 $x_base += 32;
2160 }};
2161 }
2162
2163 for block in row_bytes.as_chunks::<Q3_K_BLOCK_BYTES>().0 {
2164 let hmask = &block[0..32];
2165 let qs = &block[32..96];
2166 let scales_raw: &[u8; Q3_K_SCALE_BYTES] = block[96..108].try_into().unwrap();
2167 let d_all = f16::from_le_bytes([block[108], block[109]]).to_f32();
2168 let scales = q3_k_unpack_scales(scales_raw);
2169
2170 let mut is = 0usize;
2171 let mut m = 1u8;
2172 for n in 0..2 {
2173 let q = &qs[n * 32..n * 32 + 32];
2174 for shift in [0u32, 2, 4, 6] {
2175 let dl1 = d_all * (scales[is] as f32 - 32.0);
2176 let dl2 = d_all * (scales[is + 1] as f32 - 32.0);
2177 is += 2;
2178 let m_vec = _mm_set1_epi8(m as i8);
2179 match shift {
2180 0 => q3_k_sub_block!(0, q, hmask, m_vec, dl1, dl2, x, x_base, acc),
2181 2 => q3_k_sub_block!(2, q, hmask, m_vec, dl1, dl2, x, x_base, acc),
2182 4 => q3_k_sub_block!(4, q, hmask, m_vec, dl1, dl2, x, x_base, acc),
2183 6 => q3_k_sub_block!(6, q, hmask, m_vec, dl1, dl2, x, x_base, acc),
2184 _ => unreachable!(),
2185 }
2186 m <<= 1;
2187 }
2188 }
2189 }
2190 acc
2191 }
2192
2193 #[target_feature(enable = "avx2,fma")]
2201 pub unsafe fn dot_iq4_nl_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
2202 debug_assert_eq!(row_bytes.len() % IQ4_NL_BLOCK_BYTES, 0);
2203 let low_mask = _mm_set1_epi8(0x0F);
2204 let codebook = _mm_loadu_si128(KVALUES_IQ4NL.as_ptr() as *const __m128i);
2205 let mut acc = 0f32;
2206 let mut x_base = 0usize;
2207 for block in row_bytes.as_chunks::<IQ4_NL_BLOCK_BYTES>().0 {
2208 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
2209 let qs = &block[2..18];
2210 let bytes = _mm_loadu_si128(qs.as_ptr() as *const __m128i);
2211 let lo_idx = _mm_and_si128(bytes, low_mask);
2212 let hi_idx = _mm_and_si128(_mm_srli_epi16(bytes, 4), low_mask);
2213 let lo_vals = _mm_shuffle_epi8(codebook, lo_idx);
2214 let hi_vals = _mm_shuffle_epi8(codebook, hi_idx);
2215
2216 let mut block_acc = _mm256_setzero_ps();
2217 for (half_idx, vals) in [
2218 (0usize, lo_vals),
2219 (1usize, _mm_srli_si128(lo_vals, 8)),
2220 (2usize, hi_vals),
2221 (3usize, _mm_srli_si128(hi_vals, 8)),
2222 ] {
2223 let i32x8 = _mm256_cvtepi8_epi32(vals);
2224 let f32x8 = _mm256_cvtepi32_ps(i32x8);
2225 let xv = _mm256_loadu_ps(x.as_ptr().add(x_base + half_idx * 8));
2226 block_acc = _mm256_fmadd_ps(f32x8, xv, block_acc);
2227 }
2228 acc += hsum256_ps(block_acc) * d;
2229 x_base += IQ4_NL_BLOCK_ELEMS;
2230 }
2231 acc
2232 }
2233
2234 #[target_feature(enable = "avx2,fma")]
2240 pub unsafe fn dot_iq4_xs_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
2241 debug_assert_eq!(row_bytes.len() % IQ4_XS_BLOCK_BYTES, 0);
2242 let low_mask = _mm_set1_epi8(0x0F);
2243 let codebook = _mm_loadu_si128(KVALUES_IQ4NL.as_ptr() as *const __m128i);
2244 let mut acc = 0f32;
2245 let mut x_base = 0usize;
2246 for block in row_bytes.as_chunks::<IQ4_XS_BLOCK_BYTES>().0 {
2247 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
2248 let scales_h = u16::from_le_bytes([block[2], block[3]]);
2249 let scales_l = &block[4..8];
2250 let qs = &block[8..136];
2251
2252 for ib in 0..8 {
2253 let ls = ((scales_l[ib / 2] >> (4 * (ib % 2))) & 0xf)
2254 | (((scales_h >> (2 * ib)) & 3) as u8) << 4;
2255 let dl = d * (ls as f32 - 32.0);
2256 let sub = &qs[ib * 16..ib * 16 + 16];
2257 let bytes = _mm_loadu_si128(sub.as_ptr() as *const __m128i);
2258 let lo_idx = _mm_and_si128(bytes, low_mask);
2259 let hi_idx = _mm_and_si128(_mm_srli_epi16(bytes, 4), low_mask);
2260 let lo_vals = _mm_shuffle_epi8(codebook, lo_idx);
2261 let hi_vals = _mm_shuffle_epi8(codebook, hi_idx);
2262
2263 let mut sub_acc = _mm256_setzero_ps();
2264 for (half_idx, vals) in [
2265 (0usize, lo_vals),
2266 (1usize, _mm_srli_si128(lo_vals, 8)),
2267 (2usize, hi_vals),
2268 (3usize, _mm_srli_si128(hi_vals, 8)),
2269 ] {
2270 let i32x8 = _mm256_cvtepi8_epi32(vals);
2271 let f32x8 = _mm256_cvtepi32_ps(i32x8);
2272 let xv = _mm256_loadu_ps(x.as_ptr().add(x_base + half_idx * 8));
2273 sub_acc = _mm256_fmadd_ps(f32x8, xv, sub_acc);
2274 }
2275 acc += hsum256_ps(sub_acc) * dl;
2276 x_base += 32;
2277 }
2278 }
2279 acc
2280 }
2281
2282 #[inline]
2290 #[target_feature(enable = "avx2", enable = "fma")]
2291 unsafe fn iq_grid_row_signed_f32(row_le: u64, signs: u8) -> __m256 {
2292 let mags = _mm256_cvtepi32_ps(_mm256_cvtepu8_epi32(_mm_set_epi64x(0, row_le as i64)));
2293 let bit_mask = _mm256_setr_epi32(1, 2, 4, 8, 16, 32, 64, 128);
2294 let bits = _mm256_and_si256(_mm256_set1_epi32(signs as i32), bit_mask);
2295 let neg = _mm256_cmpeq_epi32(bits, bit_mask);
2296 let sign_bit = _mm256_and_si256(neg, _mm256_set1_epi32(0x8000_0000_u32 as i32));
2297 _mm256_xor_ps(mags, _mm256_castsi256_ps(sign_bit))
2298 }
2299
2300 #[target_feature(enable = "avx2", enable = "fma")]
2307 pub unsafe fn dot_iq1_s_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
2308 debug_assert_eq!(row_bytes.len() % crate::IQ1_S_BLOCK_BYTES, 0);
2309 let mut acc = _mm256_setzero_ps();
2310 let mut x_base = 0usize;
2311 for block in row_bytes.as_chunks::<{ crate::IQ1_S_BLOCK_BYTES }>().0 {
2312 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
2313 let qs = &block[2..34];
2314 let qh = &block[34..50];
2315 for ib in 0..8 {
2316 let h = u16::from_le_bytes([qh[2 * ib], qh[2 * ib + 1]]);
2317 let dl = d * (2.0 * ((h >> 12) & 7) as f32 + 1.0);
2318 let delta = if h & 0x8000 != 0 {
2319 -crate::IQ1S_DELTA
2320 } else {
2321 crate::IQ1S_DELTA
2322 };
2323 let dl_v = _mm256_set1_ps(dl);
2324 let delta_v = _mm256_set1_ps(delta);
2325 for l in 0..4 {
2326 let idx = qs[4 * ib + l] as usize | ((((h >> (3 * l)) & 7) as usize) << 8);
2327 let row = crate::iq_tables::IQ1S_GRID[idx];
2328 let g = _mm256_cvtepi32_ps(_mm256_cvtepi8_epi32(_mm_set_epi64x(0, row as i64)));
2329 let vals = _mm256_mul_ps(dl_v, _mm256_add_ps(g, delta_v));
2330 let xv = _mm256_loadu_ps(x.as_ptr().add(x_base));
2331 acc = _mm256_fmadd_ps(vals, xv, acc);
2332 x_base += 8;
2333 }
2334 }
2335 }
2336 hsum256_ps(acc)
2337 }
2338
2339 #[target_feature(enable = "avx2", enable = "fma")]
2344 pub unsafe fn dot_iq2_xxs_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
2345 debug_assert_eq!(row_bytes.len() % crate::IQ2_XXS_BLOCK_BYTES, 0);
2346 let mut acc = _mm256_setzero_ps();
2347 let mut x_base = 0usize;
2348 for block in row_bytes.as_chunks::<{ crate::IQ2_XXS_BLOCK_BYTES }>().0 {
2349 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
2350 for ib32 in 0..8 {
2351 let g0 = u16::from_le_bytes([block[2 + 8 * ib32], block[3 + 8 * ib32]]);
2352 let g1 = u16::from_le_bytes([block[4 + 8 * ib32], block[5 + 8 * ib32]]);
2353 let g2 = u16::from_le_bytes([block[6 + 8 * ib32], block[7 + 8 * ib32]]);
2354 let g3 = u16::from_le_bytes([block[8 + 8 * ib32], block[9 + 8 * ib32]]);
2355 let aux32_1 = g2 as u32 | ((g3 as u32) << 16);
2356 let db = _mm256_set1_ps(d * (0.5 + (aux32_1 >> 28) as f32) * 0.25);
2357 let aux8 = [
2358 (g0 & 0xFF) as usize,
2359 (g0 >> 8) as usize,
2360 (g1 & 0xFF) as usize,
2361 (g1 >> 8) as usize,
2362 ];
2363 for (l, &code) in aux8.iter().enumerate() {
2364 let signs =
2365 crate::iq_tables::KSIGNS_IQ2XS[((aux32_1 >> (7 * l)) & 127) as usize];
2366 let vals = iq_grid_row_signed_f32(crate::iq_tables::IQ2XXS_GRID[code], signs);
2367 let xv = _mm256_loadu_ps(x.as_ptr().add(x_base));
2368 acc = _mm256_fmadd_ps(_mm256_mul_ps(db, vals), xv, acc);
2369 x_base += 8;
2370 }
2371 }
2372 }
2373 hsum256_ps(acc)
2374 }
2375
2376 #[target_feature(enable = "avx2", enable = "fma")]
2381 pub unsafe fn dot_iq3_xxs_f32_avx2(row_bytes: &[u8], x: &[f32]) -> f32 {
2382 debug_assert_eq!(row_bytes.len() % crate::IQ3_XXS_BLOCK_BYTES, 0);
2383 let mut acc = _mm256_setzero_ps();
2384 let mut x_base = 0usize;
2385 for block in row_bytes.as_chunks::<{ crate::IQ3_XXS_BLOCK_BYTES }>().0 {
2386 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
2387 let qs = &block[2..66];
2388 let sas = &block[66..98];
2389 for ib32 in 0..8 {
2390 let aux32 = u32::from_le_bytes([
2391 sas[4 * ib32],
2392 sas[4 * ib32 + 1],
2393 sas[4 * ib32 + 2],
2394 sas[4 * ib32 + 3],
2395 ]);
2396 let db = _mm256_set1_ps(d * (0.5 + (aux32 >> 28) as f32) * 0.5);
2397 for l in 0..4 {
2398 let signs = crate::iq_tables::KSIGNS_IQ2XS[((aux32 >> (7 * l)) & 127) as usize];
2399 let r1 = crate::iq_tables::IQ3XXS_GRID[qs[8 * ib32 + 2 * l] as usize];
2400 let r2 = crate::iq_tables::IQ3XXS_GRID[qs[8 * ib32 + 2 * l + 1] as usize];
2401 let row = (r1 as u64) | ((r2 as u64) << 32);
2402 let vals = iq_grid_row_signed_f32(row, signs);
2403 let xv = _mm256_loadu_ps(x.as_ptr().add(x_base));
2404 acc = _mm256_fmadd_ps(_mm256_mul_ps(db, vals), xv, acc);
2405 x_base += 8;
2406 }
2407 }
2408 }
2409 hsum256_ps(acc)
2410 }
2411}
2412
2413#[cfg(target_arch = "aarch64")]
2426mod simd_aarch64 {
2427 use super::{
2428 e8m0_scale, q3_k_unpack_scales, q4_k_scale_min, q5_fifth_bits, Q8Activations,
2429 Q8KActivations, IQ4_NL_BLOCK_BYTES, IQ4_NL_BLOCK_ELEMS, IQ4_XS_BLOCK_BYTES, KVALUES_IQ4NL,
2430 MXFP4_GROUP_SIZE, Q2_K_BLOCK_BYTES, Q2_K_SCALE_BYTES, Q3_K_BLOCK_BYTES, Q3_K_SCALE_BYTES,
2431 Q4_0_BLOCK_BYTES, Q4_0_BLOCK_ELEMS, Q4_1_BLOCK_BYTES, Q4_1_BLOCK_ELEMS, Q4_K_BLOCK_BYTES,
2432 Q4_K_BLOCK_ELEMS, Q4_K_SCALE_BYTES, Q5_0_BLOCK_BYTES, Q5_0_BLOCK_ELEMS, Q5_1_BLOCK_BYTES,
2433 Q5_1_BLOCK_ELEMS, Q5_K_BLOCK_BYTES, Q5_K_BLOCK_ELEMS, Q6_K_BLOCK_BYTES, Q6_K_BLOCK_ELEMS,
2434 Q8_0_BLOCK_BYTES, Q8_0_BLOCK_ELEMS, Q8_1_BLOCK_BYTES, Q8_1_BLOCK_ELEMS,
2435 };
2436 use half::f16;
2437 use std::arch::aarch64::*;
2438
2439 #[target_feature(enable = "neon")]
2452 pub unsafe fn dot_q8_0_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
2453 debug_assert_eq!(row_bytes.len() % Q8_0_BLOCK_BYTES, 0);
2454 debug_assert_eq!(
2455 row_bytes.len() / Q8_0_BLOCK_BYTES * Q8_0_BLOCK_ELEMS,
2456 x.len()
2457 );
2458 let mut acc = 0f32;
2459 for (b, block) in row_bytes
2460 .as_chunks::<Q8_0_BLOCK_BYTES>()
2461 .0
2462 .iter()
2463 .enumerate()
2464 {
2465 let scale = f16::from_le_bytes([block[0], block[1]]).to_f32();
2466 let base = b * Q8_0_BLOCK_ELEMS;
2467 let qs = &block[2..34];
2468
2469 let mut block_acc = vdupq_n_f32(0.0);
2470 for g in 0..2 {
2471 let raw16 = vld1q_s8(qs.as_ptr().add(g * 16) as *const i8);
2472 let lo16 = vmovl_s8(vget_low_s8(raw16));
2473 let hi16 = vmovl_s8(vget_high_s8(raw16));
2474 for (half_idx, half16) in [lo16, hi16].into_iter().enumerate() {
2475 let lo32 = vmovl_s16(vget_low_s16(half16));
2476 let hi32 = vmovl_s16(vget_high_s16(half16));
2477 let f_lo = vcvtq_f32_s32(lo32);
2478 let f_hi = vcvtq_f32_s32(hi32);
2479 let elem_base = base + g * 16 + half_idx * 8;
2480 let x_lo = vld1q_f32(x.as_ptr().add(elem_base));
2481 let x_hi = vld1q_f32(x.as_ptr().add(elem_base + 4));
2482 block_acc = vfmaq_f32(block_acc, f_lo, x_lo);
2483 block_acc = vfmaq_f32(block_acc, f_hi, x_hi);
2484 }
2485 }
2486 acc += vaddvq_f32(block_acc) * scale;
2487 }
2488 acc
2489 }
2490
2491 #[target_feature(enable = "neon")]
2494 pub unsafe fn dot_q8_0_q8_neon(row_bytes: &[u8], act: &Q8Activations) -> f32 {
2495 debug_assert_eq!(row_bytes.len() % Q8_0_BLOCK_BYTES, 0);
2496 debug_assert_eq!(row_bytes.len() / Q8_0_BLOCK_BYTES, act.n_blocks());
2497 let mut acc = 0f32;
2498 for (b, block) in row_bytes
2499 .as_chunks::<Q8_0_BLOCK_BYTES>()
2500 .0
2501 .iter()
2502 .enumerate()
2503 {
2504 let dw = f16::from_le_bytes([block[0], block[1]]).to_f32();
2505 let base = b * Q8_0_BLOCK_ELEMS;
2506 let mut isum = vdupq_n_s32(0);
2507 for g in 0..2 {
2508 let w = vld1q_s8(block.as_ptr().add(2 + g * 16) as *const i8);
2509 let a = vld1q_s8(act.q.as_ptr().add(base + g * 16));
2510 let prod_lo = vmull_s8(vget_low_s8(w), vget_low_s8(a));
2511 let prod_hi = vmull_s8(vget_high_s8(w), vget_high_s8(a));
2512 isum = vpadalq_s16(isum, prod_lo);
2513 isum = vpadalq_s16(isum, prod_hi);
2514 }
2515 acc += dw * act.d[b] * vaddvq_s32(isum) as f32;
2516 }
2517 acc
2518 }
2519
2520 #[target_feature(enable = "neon,dotprod")]
2522 unsafe fn neon_sdot(mut acc: int32x4_t, a: int8x16_t, b: int8x16_t) -> int32x4_t {
2523 std::arch::asm!(
2524 "sdot {acc:v}.4s, {a:v}.16b, {b:v}.16b",
2525 acc = inout(vreg) acc,
2526 a = in(vreg) a,
2527 b = in(vreg) b,
2528 options(pure, nomem, nostack),
2529 );
2530 acc
2531 }
2532
2533 #[target_feature(enable = "neon,dotprod")]
2536 pub unsafe fn dot_q8_0_q8_neon_sdot(row_bytes: &[u8], act: &Q8Activations) -> f32 {
2537 debug_assert_eq!(row_bytes.len() % Q8_0_BLOCK_BYTES, 0);
2538 debug_assert_eq!(row_bytes.len() / Q8_0_BLOCK_BYTES, act.n_blocks());
2539 let nb = row_bytes.len() / Q8_0_BLOCK_BYTES;
2540 let mut sumv0 = vdupq_n_f32(0.0);
2541 let mut sumv1 = vdupq_n_f32(0.0);
2542 let mut b = 0usize;
2543 while b + 1 < nb {
2544 let block0 = row_bytes.as_ptr().add(b * Q8_0_BLOCK_BYTES);
2545 let block1 = row_bytes.as_ptr().add((b + 1) * Q8_0_BLOCK_BYTES);
2546 let dw0 = f16::from_le_bytes([*block0, *block0.add(1)]).to_f32();
2547 let dw1 = f16::from_le_bytes([*block1, *block1.add(1)]).to_f32();
2548 let base0 = b * Q8_0_BLOCK_ELEMS;
2549 let base1 = (b + 1) * Q8_0_BLOCK_ELEMS;
2550 let mut isum0 = vdupq_n_s32(0);
2551 let mut isum1 = vdupq_n_s32(0);
2552 for g in 0..2 {
2553 let w0 = vld1q_s8(block0.add(2 + g * 16) as *const i8);
2554 let w1 = vld1q_s8(block1.add(2 + g * 16) as *const i8);
2555 let a0 = vld1q_s8(act.q.as_ptr().add(base0 + g * 16));
2556 let a1 = vld1q_s8(act.q.as_ptr().add(base1 + g * 16));
2557 isum0 = neon_sdot(isum0, w0, a0);
2558 isum1 = neon_sdot(isum1, w1, a1);
2559 }
2560 sumv0 = vmlaq_n_f32(sumv0, vcvtq_f32_s32(isum0), dw0 * act.d[b]);
2561 sumv1 = vmlaq_n_f32(sumv1, vcvtq_f32_s32(isum1), dw1 * act.d[b + 1]);
2562 b += 2;
2563 }
2564 let mut acc = vaddvq_f32(sumv0) + vaddvq_f32(sumv1);
2565 if b < nb {
2566 let block = row_bytes.as_ptr().add(b * Q8_0_BLOCK_BYTES);
2567 let dw = f16::from_le_bytes([*block, *block.add(1)]).to_f32();
2568 let base = b * Q8_0_BLOCK_ELEMS;
2569 let mut isum = vdupq_n_s32(0);
2570 for g in 0..2 {
2571 let w = vld1q_s8(block.add(2 + g * 16) as *const i8);
2572 let a = vld1q_s8(act.q.as_ptr().add(base + g * 16));
2573 isum = neon_sdot(isum, w, a);
2574 }
2575 acc += dw * act.d[b] * vaddvq_s32(isum) as f32;
2576 }
2577 acc
2578 }
2579
2580 #[target_feature(enable = "neon")]
2584 pub unsafe fn dot_q4_0_q8_neon(row_bytes: &[u8], act: &Q8Activations) -> f32 {
2585 debug_assert_eq!(row_bytes.len() % Q4_0_BLOCK_BYTES, 0);
2586 debug_assert_eq!(row_bytes.len() / Q4_0_BLOCK_BYTES, act.n_blocks());
2587 let bias = vdupq_n_s8(8);
2588 let low_mask = vdupq_n_u8(0x0F);
2589 let mut acc = 0f32;
2590 for (b, block) in row_bytes
2591 .as_chunks::<Q4_0_BLOCK_BYTES>()
2592 .0
2593 .iter()
2594 .enumerate()
2595 {
2596 let dw = f16::from_le_bytes([block[0], block[1]]).to_f32();
2597 let base = b * Q4_0_BLOCK_ELEMS;
2598 let nibbles = vld1q_u8(block.as_ptr().add(2));
2599 let lo = vsubq_s8(vreinterpretq_s8_u8(vandq_u8(nibbles, low_mask)), bias);
2600 let hi = vsubq_s8(vreinterpretq_s8_u8(vshrq_n_u8(nibbles, 4)), bias);
2601 let mut isum = vdupq_n_s32(0);
2602 let a0 = vld1q_s8(act.q.as_ptr().add(base));
2604 let a1 = vld1q_s8(act.q.as_ptr().add(base + 16));
2605 let p0_lo = vmull_s8(vget_low_s8(lo), vget_low_s8(a0));
2606 let p0_hi = vmull_s8(vget_high_s8(lo), vget_high_s8(a0));
2607 let p1_lo = vmull_s8(vget_low_s8(hi), vget_low_s8(a1));
2608 let p1_hi = vmull_s8(vget_high_s8(hi), vget_high_s8(a1));
2609 isum = vpadalq_s16(isum, p0_lo);
2610 isum = vpadalq_s16(isum, p0_hi);
2611 isum = vpadalq_s16(isum, p1_lo);
2612 isum = vpadalq_s16(isum, p1_hi);
2613 acc += dw * act.d[b] * vaddvq_s32(isum) as f32;
2614 }
2615 acc
2616 }
2617
2618 #[target_feature(enable = "neon,dotprod")]
2620 pub unsafe fn dot_q4_0_q8_neon_sdot_2row(
2621 row0: &[u8],
2622 row1: &[u8],
2623 act: &Q8Activations,
2624 ) -> (f32, f32) {
2625 debug_assert_eq!(row0.len(), row1.len());
2626 debug_assert_eq!(row0.len() % Q4_0_BLOCK_BYTES, 0);
2627 let bias = vdupq_n_s8(8);
2628 let low_mask = vdupq_n_u8(0x0F);
2629 let nb = row0.len() / Q4_0_BLOCK_BYTES;
2630 let mut sum0 = vdupq_n_f32(0.0);
2631 let mut sum1 = vdupq_n_f32(0.0);
2632 for b in 0..nb {
2633 let p0 = row0.as_ptr().add(b * Q4_0_BLOCK_BYTES);
2634 let p1 = row1.as_ptr().add(b * Q4_0_BLOCK_BYTES);
2635 let dw0 = f16::from_le_bytes([*p0, *p0.add(1)]).to_f32();
2636 let dw1 = f16::from_le_bytes([*p1, *p1.add(1)]).to_f32();
2637 let base = b * Q4_0_BLOCK_ELEMS;
2638 let a_lo = vld1q_s8(act.q.as_ptr().add(base));
2639 let a_hi = vld1q_s8(act.q.as_ptr().add(base + 16));
2640 let nib0 = vld1q_u8(p0.add(2));
2641 let nib1 = vld1q_u8(p1.add(2));
2642 let lo0 = vsubq_s8(vreinterpretq_s8_u8(vandq_u8(nib0, low_mask)), bias);
2643 let hi0 = vsubq_s8(vreinterpretq_s8_u8(vshrq_n_u8(nib0, 4)), bias);
2644 let lo1 = vsubq_s8(vreinterpretq_s8_u8(vandq_u8(nib1, low_mask)), bias);
2645 let hi1 = vsubq_s8(vreinterpretq_s8_u8(vshrq_n_u8(nib1, 4)), bias);
2646 let mut is0 = neon_sdot(vdupq_n_s32(0), lo0, a_lo);
2647 is0 = neon_sdot(is0, hi0, a_hi);
2648 let mut is1 = neon_sdot(vdupq_n_s32(0), lo1, a_lo);
2649 is1 = neon_sdot(is1, hi1, a_hi);
2650 let scale = act.d[b];
2651 sum0 = vmlaq_n_f32(sum0, vcvtq_f32_s32(is0), dw0 * scale);
2652 sum1 = vmlaq_n_f32(sum1, vcvtq_f32_s32(is1), dw1 * scale);
2653 }
2654 (vaddvq_f32(sum0), vaddvq_f32(sum1))
2655 }
2656
2657 #[target_feature(enable = "neon,dotprod")]
2659 pub unsafe fn dot_q4_0_q8_neon_sdot(row_bytes: &[u8], act: &Q8Activations) -> f32 {
2660 debug_assert_eq!(row_bytes.len() % Q4_0_BLOCK_BYTES, 0);
2661 debug_assert_eq!(row_bytes.len() / Q4_0_BLOCK_BYTES, act.n_blocks());
2662 let bias = vdupq_n_s8(8);
2663 let low_mask = vdupq_n_u8(0x0F);
2664 let nb = row_bytes.len() / Q4_0_BLOCK_BYTES;
2665 let mut sumv0 = vdupq_n_f32(0.0);
2666 let mut sumv1 = vdupq_n_f32(0.0);
2667 let mut b = 0usize;
2668 while b + 1 < nb {
2669 let block0 = row_bytes.as_ptr().add(b * Q4_0_BLOCK_BYTES);
2670 let block1 = row_bytes.as_ptr().add((b + 1) * Q4_0_BLOCK_BYTES);
2671 let dw0 = f16::from_le_bytes([*block0, *block0.add(1)]).to_f32();
2672 let dw1 = f16::from_le_bytes([*block1, *block1.add(1)]).to_f32();
2673 let base0 = b * Q4_0_BLOCK_ELEMS;
2674 let base1 = (b + 1) * Q4_0_BLOCK_ELEMS;
2675 let nib0 = vld1q_u8(block0.add(2));
2676 let nib1 = vld1q_u8(block1.add(2));
2677 let lo0 = vsubq_s8(vreinterpretq_s8_u8(vandq_u8(nib0, low_mask)), bias);
2678 let hi0 = vsubq_s8(vreinterpretq_s8_u8(vshrq_n_u8(nib0, 4)), bias);
2679 let lo1 = vsubq_s8(vreinterpretq_s8_u8(vandq_u8(nib1, low_mask)), bias);
2680 let hi1 = vsubq_s8(vreinterpretq_s8_u8(vshrq_n_u8(nib1, 4)), bias);
2681 let mut isum0 = neon_sdot(vdupq_n_s32(0), lo0, vld1q_s8(act.q.as_ptr().add(base0)));
2682 isum0 = neon_sdot(isum0, hi0, vld1q_s8(act.q.as_ptr().add(base0 + 16)));
2683 let mut isum1 = neon_sdot(vdupq_n_s32(0), lo1, vld1q_s8(act.q.as_ptr().add(base1)));
2684 isum1 = neon_sdot(isum1, hi1, vld1q_s8(act.q.as_ptr().add(base1 + 16)));
2685 sumv0 = vmlaq_n_f32(sumv0, vcvtq_f32_s32(isum0), dw0 * act.d[b]);
2686 sumv1 = vmlaq_n_f32(sumv1, vcvtq_f32_s32(isum1), dw1 * act.d[b + 1]);
2687 b += 2;
2688 }
2689 let mut acc = vaddvq_f32(sumv0) + vaddvq_f32(sumv1);
2690 if b < nb {
2691 let block = &row_bytes[b * Q4_0_BLOCK_BYTES..(b + 1) * Q4_0_BLOCK_BYTES];
2692 let dw = f16::from_le_bytes([block[0], block[1]]).to_f32();
2693 let base = b * Q4_0_BLOCK_ELEMS;
2694 let nibbles = vld1q_u8(block.as_ptr().add(2));
2695 let lo = vsubq_s8(vreinterpretq_s8_u8(vandq_u8(nibbles, low_mask)), bias);
2696 let hi = vsubq_s8(vreinterpretq_s8_u8(vshrq_n_u8(nibbles, 4)), bias);
2697 let mut isum = neon_sdot(vdupq_n_s32(0), lo, vld1q_s8(act.q.as_ptr().add(base)));
2698 isum = neon_sdot(isum, hi, vld1q_s8(act.q.as_ptr().add(base + 16)));
2699 acc += dw * act.d[b] * vaddvq_s32(isum) as f32;
2700 }
2701 acc
2702 }
2703
2704 #[target_feature(enable = "neon")]
2705 unsafe fn neon_i8_dot_widen(mut isum: int32x4_t, w: int8x16_t, a: int8x16_t) -> int32x4_t {
2706 let prod_lo = vmull_s8(vget_low_s8(w), vget_low_s8(a));
2707 let prod_hi = vmull_s8(vget_high_s8(w), vget_high_s8(a));
2708 isum = vpadalq_s16(isum, prod_lo);
2709 vpadalq_s16(isum, prod_hi)
2710 }
2711
2712 #[target_feature(enable = "neon")]
2714 pub unsafe fn dot_q4_k_q8_neon(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
2715 debug_assert_eq!(row_bytes.len() % Q4_K_BLOCK_BYTES, 0);
2716 debug_assert_eq!(row_bytes.len() / Q4_K_BLOCK_BYTES, act.n_blocks());
2717 let low_mask = vdupq_n_u8(0x0F);
2718 let mut acc = 0f32;
2719 for (b, block) in row_bytes
2720 .as_chunks::<Q4_K_BLOCK_BYTES>()
2721 .0
2722 .iter()
2723 .enumerate()
2724 {
2725 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
2726 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
2727 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
2728 let qs = &block[16..144];
2729 let da = act.d[b];
2730 let q8 = act.q.as_ptr().add(b * Q4_K_BLOCK_ELEMS);
2731 let bsums = &act.bsums[b * 16..(b + 1) * 16];
2732
2733 let mut sum_min = 0i32;
2734 for i in 0..8 {
2735 let (_, m) = q4_k_scale_min(i, &scales);
2736 sum_min += m as i32 * (bsums[2 * i] as i32 + bsums[2 * i + 1] as i32);
2737 }
2738 acc -= dmin * da * sum_min as f32;
2739
2740 let mut q_off = 0usize;
2741 let mut base = 0usize;
2742 let mut is = 0usize;
2743 for _ in 0..4 {
2744 let (sc1, _) = q4_k_scale_min(is, &scales);
2745 let (sc2, _) = q4_k_scale_min(is + 1, &scales);
2746 let mut isum1 = vdupq_n_s32(0);
2747 let mut isum2 = vdupq_n_s32(0);
2748 for g in 0..2 {
2749 let packed = vld1q_u8(qs.as_ptr().add(q_off + g * 16));
2750 let lo = vreinterpretq_s8_u8(vandq_u8(packed, low_mask));
2751 let hi = vreinterpretq_s8_u8(vshrq_n_u8(packed, 4));
2752 let a0 = vld1q_s8(q8.add(base + g * 16));
2753 let a1 = vld1q_s8(q8.add(base + 32 + g * 16));
2754 isum1 = neon_i8_dot_widen(isum1, lo, a0);
2755 isum2 = neon_i8_dot_widen(isum2, hi, a1);
2756 }
2757 acc += d
2758 * da
2759 * (sc1 as f32 * vaddvq_s32(isum1) as f32
2760 + sc2 as f32 * vaddvq_s32(isum2) as f32);
2761 q_off += 32;
2762 base += 64;
2763 is += 2;
2764 }
2765 }
2766 acc
2767 }
2768
2769 #[target_feature(enable = "neon,i8mm")]
2775 pub unsafe fn dot_q4_k_q8_neon_i8mm(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
2776 dot_q4_k_q8_neon_sdot(row_bytes, act)
2777 }
2778
2779 #[target_feature(enable = "neon,dotprod")]
2781 pub unsafe fn dot_q4_k_q8_neon_sdot(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
2782 debug_assert_eq!(row_bytes.len() % Q4_K_BLOCK_BYTES, 0);
2783 debug_assert_eq!(row_bytes.len() / Q4_K_BLOCK_BYTES, act.n_blocks());
2784 let low_mask = vdupq_n_u8(0x0F);
2785 let mut acc = 0f32;
2786 for (b, block) in row_bytes
2787 .as_chunks::<Q4_K_BLOCK_BYTES>()
2788 .0
2789 .iter()
2790 .enumerate()
2791 {
2792 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
2793 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
2794 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
2795 let qs = &block[16..144];
2796 let da = act.d[b];
2797 let q8 = act.q.as_ptr().add(b * Q4_K_BLOCK_ELEMS);
2798 let bsums = &act.bsums[b * 16..(b + 1) * 16];
2799
2800 let mut sum_min = 0i32;
2801 for i in 0..8 {
2802 let (_, m) = q4_k_scale_min(i, &scales);
2803 sum_min += m as i32 * (bsums[2 * i] as i32 + bsums[2 * i + 1] as i32);
2804 }
2805 acc -= dmin * da * sum_min as f32;
2806
2807 let mut q_off = 0usize;
2808 let mut base = 0usize;
2809 let mut is = 0usize;
2810 for _ in 0..4 {
2811 let (sc1, _) = q4_k_scale_min(is, &scales);
2812 let (sc2, _) = q4_k_scale_min(is + 1, &scales);
2813 let mut isum1 = vdupq_n_s32(0);
2814 let mut isum2 = vdupq_n_s32(0);
2815 for g in 0..2 {
2816 let packed = vld1q_u8(qs.as_ptr().add(q_off + g * 16));
2817 let lo = vreinterpretq_s8_u8(vandq_u8(packed, low_mask));
2818 let hi = vreinterpretq_s8_u8(vshrq_n_u8(packed, 4));
2819 let a0 = vld1q_s8(q8.add(base + g * 16));
2820 let a1 = vld1q_s8(q8.add(base + 32 + g * 16));
2821 isum1 = neon_sdot(isum1, lo, a0);
2822 isum2 = neon_sdot(isum2, hi, a1);
2823 }
2824 acc += d
2825 * da
2826 * (sc1 as f32 * vaddvq_s32(isum1) as f32
2827 + sc2 as f32 * vaddvq_s32(isum2) as f32);
2828 q_off += 32;
2829 base += 64;
2830 is += 2;
2831 }
2832 }
2833 acc
2834 }
2835
2836 #[target_feature(enable = "neon")]
2838 pub unsafe fn dot_q5_k_q8_neon(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
2839 debug_assert_eq!(row_bytes.len() % Q5_K_BLOCK_BYTES, 0);
2840 debug_assert_eq!(row_bytes.len() / Q5_K_BLOCK_BYTES, act.n_blocks());
2841 let low_mask = vdupq_n_u8(0x0F);
2842 let sixteen = vdupq_n_u8(16);
2843 let mut acc = 0f32;
2844 for (b, block) in row_bytes
2845 .as_chunks::<Q5_K_BLOCK_BYTES>()
2846 .0
2847 .iter()
2848 .enumerate()
2849 {
2850 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
2851 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
2852 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
2853 let qh = block.as_ptr().add(16);
2854 let qs = &block[48..176];
2855 let da = act.d[b];
2856 let q8 = act.q.as_ptr().add(b * Q5_K_BLOCK_ELEMS);
2857 let bsums = &act.bsums[b * 16..(b + 1) * 16];
2858
2859 let mut sum_min = 0i32;
2860 for i in 0..8 {
2861 let (_, m) = q4_k_scale_min(i, &scales);
2862 sum_min += m as i32 * (bsums[2 * i] as i32 + bsums[2 * i + 1] as i32);
2863 }
2864 acc -= dmin * da * sum_min as f32;
2865
2866 let mut q_off = 0usize;
2867 let mut base = 0usize;
2868 let mut is = 0usize;
2869 let (mut u1, mut u2) = (1u8, 2u8);
2870 for _ in 0..4 {
2871 let (sc1, _) = q4_k_scale_min(is, &scales);
2872 let (sc2, _) = q4_k_scale_min(is + 1, &scales);
2873 let mut isum1 = vdupq_n_s32(0);
2874 let mut isum2 = vdupq_n_s32(0);
2875 let u1_vec = vdupq_n_u8(u1);
2876 let u2_vec = vdupq_n_u8(u2);
2877 for g in 0..2 {
2878 let packed = vld1q_u8(qs.as_ptr().add(q_off + g * 16));
2879 let qh16 = vld1q_u8(qh.add(g * 16));
2880 let lo_nib = vandq_u8(packed, low_mask);
2881 let hi_nib = vshrq_n_u8(packed, 4);
2882 let hi_bit1 = vandq_u8(vtstq_u8(qh16, u1_vec), sixteen);
2883 let hi_bit2 = vandq_u8(vtstq_u8(qh16, u2_vec), sixteen);
2884 let lo = vreinterpretq_s8_u8(vorrq_u8(lo_nib, hi_bit1));
2885 let hi = vreinterpretq_s8_u8(vorrq_u8(hi_nib, hi_bit2));
2886 let a0 = vld1q_s8(q8.add(base + g * 16));
2887 let a1 = vld1q_s8(q8.add(base + 32 + g * 16));
2888 isum1 = neon_i8_dot_widen(isum1, lo, a0);
2889 isum2 = neon_i8_dot_widen(isum2, hi, a1);
2890 }
2891 acc += d
2892 * da
2893 * (sc1 as f32 * vaddvq_s32(isum1) as f32
2894 + sc2 as f32 * vaddvq_s32(isum2) as f32);
2895 q_off += 32;
2896 base += 64;
2897 is += 2;
2898 u1 <<= 2;
2899 u2 <<= 2;
2900 }
2901 }
2902 acc
2903 }
2904
2905 #[target_feature(enable = "neon,dotprod")]
2907 pub unsafe fn dot_q5_k_q8_neon_sdot(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
2908 debug_assert_eq!(row_bytes.len() % Q5_K_BLOCK_BYTES, 0);
2909 debug_assert_eq!(row_bytes.len() / Q5_K_BLOCK_BYTES, act.n_blocks());
2910 let low_mask = vdupq_n_u8(0x0F);
2911 let sixteen = vdupq_n_u8(16);
2912 let mut acc = 0f32;
2913 for (b, block) in row_bytes
2914 .as_chunks::<Q5_K_BLOCK_BYTES>()
2915 .0
2916 .iter()
2917 .enumerate()
2918 {
2919 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
2920 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
2921 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
2922 let qh = block.as_ptr().add(16);
2923 let qs = &block[48..176];
2924 let da = act.d[b];
2925 let q8 = act.q.as_ptr().add(b * Q5_K_BLOCK_ELEMS);
2926 let bsums = &act.bsums[b * 16..(b + 1) * 16];
2927
2928 let mut sum_min = 0i32;
2929 for i in 0..8 {
2930 let (_, m) = q4_k_scale_min(i, &scales);
2931 sum_min += m as i32 * (bsums[2 * i] as i32 + bsums[2 * i + 1] as i32);
2932 }
2933 acc -= dmin * da * sum_min as f32;
2934
2935 let mut q_off = 0usize;
2936 let mut base = 0usize;
2937 let mut is = 0usize;
2938 let (mut u1, mut u2) = (1u8, 2u8);
2939 for _ in 0..4 {
2940 let (sc1, _) = q4_k_scale_min(is, &scales);
2941 let (sc2, _) = q4_k_scale_min(is + 1, &scales);
2942 let mut isum1 = vdupq_n_s32(0);
2943 let mut isum2 = vdupq_n_s32(0);
2944 let u1_vec = vdupq_n_u8(u1);
2945 let u2_vec = vdupq_n_u8(u2);
2946 for g in 0..2 {
2947 let packed = vld1q_u8(qs.as_ptr().add(q_off + g * 16));
2948 let qh16 = vld1q_u8(qh.add(g * 16));
2949 let lo_nib = vandq_u8(packed, low_mask);
2950 let hi_nib = vshrq_n_u8(packed, 4);
2951 let hi_bit1 = vandq_u8(vtstq_u8(qh16, u1_vec), sixteen);
2952 let hi_bit2 = vandq_u8(vtstq_u8(qh16, u2_vec), sixteen);
2953 let lo = vreinterpretq_s8_u8(vorrq_u8(lo_nib, hi_bit1));
2954 let hi = vreinterpretq_s8_u8(vorrq_u8(hi_nib, hi_bit2));
2955 let a0 = vld1q_s8(q8.add(base + g * 16));
2956 let a1 = vld1q_s8(q8.add(base + 32 + g * 16));
2957 isum1 = neon_sdot(isum1, lo, a0);
2958 isum2 = neon_sdot(isum2, hi, a1);
2959 }
2960 acc += d
2961 * da
2962 * (sc1 as f32 * vaddvq_s32(isum1) as f32
2963 + sc2 as f32 * vaddvq_s32(isum2) as f32);
2964 q_off += 32;
2965 base += 64;
2966 is += 2;
2967 u1 <<= 2;
2968 u2 <<= 2;
2969 }
2970 }
2971 acc
2972 }
2973
2974 #[target_feature(enable = "neon,dotprod")]
2976 pub unsafe fn gemm_q5_k_q8_neon_sdot(
2977 row_bytes: &[u8],
2978 acts: &[Q8KActivations],
2979 out: &mut [f32],
2980 ) {
2981 debug_assert_eq!(out.len(), acts.len());
2982 debug_assert!(acts.len() <= super::Q5_K_GEMM_NC);
2983 out.fill(0.0);
2984 if acts.is_empty() {
2985 return;
2986 }
2987 let low_mask = vdupq_n_u8(0x0F);
2988 let sixteen = vdupq_n_u8(16);
2989 let n = acts.len();
2990 for (b, block) in row_bytes
2991 .as_chunks::<Q5_K_BLOCK_BYTES>()
2992 .0
2993 .iter()
2994 .enumerate()
2995 {
2996 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
2997 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
2998 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
2999 let qh = block.as_ptr().add(16);
3000 let qs = &block[48..176];
3001 let mut mins = [0u8; 8];
3002 let mut sc_only = [0u8; 8];
3003 for i in 0..8 {
3004 let (s, m) = q4_k_scale_min(i, &scales);
3005 sc_only[i] = s;
3006 mins[i] = m;
3007 }
3008 for j in 0..n {
3009 let act = &acts[j];
3010 let da = act.d[b];
3011 let bsums = &act.bsums[b * 16..(b + 1) * 16];
3012 let mut sum_min = 0i32;
3013 for i in 0..8 {
3014 sum_min += mins[i] as i32 * (bsums[2 * i] as i32 + bsums[2 * i + 1] as i32);
3015 }
3016 out[j] -= dmin * da * sum_min as f32;
3017 }
3018 let mut q_off = 0usize;
3019 let mut base = 0usize;
3020 let mut is = 0usize;
3021 let (mut u1, mut u2) = (1u8, 2u8);
3022 for _ in 0..4 {
3023 let sc1 = sc_only[is];
3024 let sc2 = sc_only[is + 1];
3025 let u1_vec = vdupq_n_u8(u1);
3026 let u2_vec = vdupq_n_u8(u2);
3027 let mut lo_cols = [vreinterpretq_s8_u8(vdupq_n_u8(0)); 2];
3029 let mut hi_cols = [vreinterpretq_s8_u8(vdupq_n_u8(0)); 2];
3030 for g in 0..2 {
3031 let packed = vld1q_u8(qs.as_ptr().add(q_off + g * 16));
3032 let qh16 = vld1q_u8(qh.add(g * 16));
3033 let lo_nib = vandq_u8(packed, low_mask);
3034 let hi_nib = vshrq_n_u8(packed, 4);
3035 let hi_bit1 = vandq_u8(vtstq_u8(qh16, u1_vec), sixteen);
3036 let hi_bit2 = vandq_u8(vtstq_u8(qh16, u2_vec), sixteen);
3037 lo_cols[g] = vreinterpretq_s8_u8(vorrq_u8(lo_nib, hi_bit1));
3038 hi_cols[g] = vreinterpretq_s8_u8(vorrq_u8(hi_nib, hi_bit2));
3039 }
3040 for j in 0..n {
3041 let q8 = acts[j].q.as_ptr().add(b * Q5_K_BLOCK_ELEMS);
3042 let da = acts[j].d[b];
3043 let mut isum1 = vdupq_n_s32(0);
3044 let mut isum2 = vdupq_n_s32(0);
3045 for g in 0..2 {
3046 let a0 = vld1q_s8(q8.add(base + g * 16));
3047 let a1 = vld1q_s8(q8.add(base + 32 + g * 16));
3048 isum1 = neon_sdot(isum1, lo_cols[g], a0);
3049 isum2 = neon_sdot(isum2, hi_cols[g], a1);
3050 }
3051 out[j] += d
3052 * da
3053 * (sc1 as f32 * vaddvq_s32(isum1) as f32
3054 + sc2 as f32 * vaddvq_s32(isum2) as f32);
3055 }
3056 q_off += 32;
3057 base += 64;
3058 is += 2;
3059 u1 <<= 2;
3060 u2 <<= 2;
3061 }
3062 }
3063 }
3064
3065 #[target_feature(enable = "neon,dotprod")]
3068 pub unsafe fn gemm_q6_k_q8_neon_sdot(
3069 row_bytes: &[u8],
3070 acts: &[Q8KActivations],
3071 out: &mut [f32],
3072 ) {
3073 debug_assert_eq!(out.len(), acts.len());
3074 debug_assert!(acts.len() <= super::Q6_K_GEMM_NC);
3075 out.fill(0.0);
3076 let n = acts.len();
3077 if n == 0 {
3078 return;
3079 }
3080 let m4b = vdupq_n_u8(0x0F);
3081 let mone = vdupq_n_u8(3);
3082 for (b, block) in row_bytes
3083 .as_chunks::<Q6_K_BLOCK_BYTES>()
3084 .0
3085 .iter()
3086 .enumerate()
3087 {
3088 let d_all = f16::from_le_bytes([block[208], block[209]]).to_f32();
3089 let ql = block.as_ptr();
3090 let qh = block.as_ptr().add(128);
3091 let scale = block.as_ptr().add(192) as *const i8;
3092 let scales = vld1q_s8(scale);
3093 let q6scales0 = vmovl_s8(vget_low_s8(scales));
3094 let q6scales1 = vmovl_s8(vget_high_s8(scales));
3095
3096 let mut isum_mins = [0i32; 4];
3097 let mut isums = [0i32; 4];
3098 for j in 0..n {
3099 let bsums = acts[j].bsums.as_ptr().add(b * 16);
3100 let q8sums0 = vld1q_s16(bsums);
3101 let q8sums1 = vld1q_s16(bsums.add(8));
3102 let prod = vaddq_s32(
3103 vaddq_s32(
3104 vmull_s16(vget_low_s16(q8sums0), vget_low_s16(q6scales0)),
3105 vmull_s16(vget_high_s16(q8sums0), vget_high_s16(q6scales0)),
3106 ),
3107 vaddq_s32(
3108 vmull_s16(vget_low_s16(q8sums1), vget_low_s16(q6scales1)),
3109 vmull_s16(vget_high_s16(q8sums1), vget_high_s16(q6scales1)),
3110 ),
3111 );
3112 isum_mins[j] = vaddvq_s32(prod);
3113 }
3114
3115 for half in 0..2usize {
3116 let q6 = ql.add(half * 64);
3117 let qhp = qh.add(half * 32);
3118 let sc = scale.add(half * 8);
3119 let act_off = half * 128;
3120
3121 let qh0 = vld1q_u8(qhp);
3122 let qh1 = vld1q_u8(qhp.add(16));
3123 let q6_0 = vld1q_u8(q6);
3124 let q6_1 = vld1q_u8(q6.add(16));
3125 let q6_2 = vld1q_u8(q6.add(32));
3126 let q6_3 = vld1q_u8(q6.add(48));
3127
3128 let h0 = vshlq_n_u8(vandq_u8(mone, qh0), 4);
3129 let h1 = vshlq_n_u8(vandq_u8(mone, qh1), 4);
3130 let mut shifted = vshrq_n_u8(qh0, 2);
3131 let h2 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3132 shifted = vshrq_n_u8(qh1, 2);
3133 let h3 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3134 let wb0 = vreinterpretq_s8_u8(vorrq_u8(vandq_u8(q6_0, m4b), h0));
3135 let wb1 = vreinterpretq_s8_u8(vorrq_u8(vandq_u8(q6_1, m4b), h1));
3136 let wb2 = vreinterpretq_s8_u8(vorrq_u8(vandq_u8(q6_2, m4b), h2));
3137 let wb3 = vreinterpretq_s8_u8(vorrq_u8(vandq_u8(q6_3, m4b), h3));
3138 let sc0 = *sc.add(0) as i32;
3139 let sc1 = *sc.add(1) as i32;
3140 let sc2 = *sc.add(2) as i32;
3141 let sc3 = *sc.add(3) as i32;
3142 let z = vdupq_n_s32(0);
3143 for j in 0..n {
3144 let q8p = acts[j].q.as_ptr().add(b * Q6_K_BLOCK_ELEMS + act_off);
3145 isums[j] += vaddvq_s32(neon_sdot(z, wb0, vld1q_s8(q8p))) * sc0
3146 + vaddvq_s32(neon_sdot(z, wb1, vld1q_s8(q8p.add(16)))) * sc1
3147 + vaddvq_s32(neon_sdot(z, wb2, vld1q_s8(q8p.add(32)))) * sc2
3148 + vaddvq_s32(neon_sdot(z, wb3, vld1q_s8(q8p.add(48)))) * sc3;
3149 }
3150
3151 shifted = vshrq_n_u8(qh0, 4);
3152 let h0 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3153 shifted = vshrq_n_u8(qh1, 4);
3154 let h1 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3155 shifted = vshrq_n_u8(qh0, 6);
3156 let h2 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3157 shifted = vshrq_n_u8(qh1, 6);
3158 let h3 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3159 let wb0 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_0, 4), h0));
3160 let wb1 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_1, 4), h1));
3161 let wb2 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_2, 4), h2));
3162 let wb3 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_3, 4), h3));
3163 let sc0 = *sc.add(4) as i32;
3164 let sc1 = *sc.add(5) as i32;
3165 let sc2 = *sc.add(6) as i32;
3166 let sc3 = *sc.add(7) as i32;
3167 for j in 0..n {
3168 let q8p = acts[j].q.as_ptr().add(b * Q6_K_BLOCK_ELEMS + act_off + 64);
3169 isums[j] += vaddvq_s32(neon_sdot(z, wb0, vld1q_s8(q8p))) * sc0
3170 + vaddvq_s32(neon_sdot(z, wb1, vld1q_s8(q8p.add(16)))) * sc1
3171 + vaddvq_s32(neon_sdot(z, wb2, vld1q_s8(q8p.add(32)))) * sc2
3172 + vaddvq_s32(neon_sdot(z, wb3, vld1q_s8(q8p.add(48)))) * sc3;
3173 }
3174 }
3175 for j in 0..n {
3176 out[j] += d_all * acts[j].d[b] * (isums[j] - 32 * isum_mins[j]) as f32;
3177 }
3178 }
3179 }
3180
3181 #[target_feature(enable = "neon,dotprod")]
3185 pub unsafe fn dot_q6_k_q8_neon_sdot(row_bytes: &[u8], act: &Q8KActivations) -> f32 {
3186 debug_assert_eq!(row_bytes.len() % Q6_K_BLOCK_BYTES, 0);
3187 debug_assert_eq!(row_bytes.len() / Q6_K_BLOCK_BYTES, act.n_blocks());
3188 let m4b = vdupq_n_u8(0x0F);
3189 let mone = vdupq_n_u8(3);
3190 let mut acc = 0f32;
3191 for (b, block) in row_bytes
3192 .as_chunks::<Q6_K_BLOCK_BYTES>()
3193 .0
3194 .iter()
3195 .enumerate()
3196 {
3197 let d_all = f16::from_le_bytes([block[208], block[209]]).to_f32();
3198 let da = act.d[b];
3199 let ql = block.as_ptr();
3200 let qh = block.as_ptr().add(128);
3201 let scale = block.as_ptr().add(192) as *const i8;
3202 let q8 = act.q.as_ptr().add(b * Q6_K_BLOCK_ELEMS);
3203 let bsums = act.bsums.as_ptr().add(b * 16);
3204
3205 let scales = vld1q_s8(scale);
3206 let q6scales0 = vmovl_s8(vget_low_s8(scales));
3207 let q6scales1 = vmovl_s8(vget_high_s8(scales));
3208 let q8sums0 = vld1q_s16(bsums);
3209 let q8sums1 = vld1q_s16(bsums.add(8));
3210 let prod = vaddq_s32(
3211 vaddq_s32(
3212 vmull_s16(vget_low_s16(q8sums0), vget_low_s16(q6scales0)),
3213 vmull_s16(vget_high_s16(q8sums0), vget_high_s16(q6scales0)),
3214 ),
3215 vaddq_s32(
3216 vmull_s16(vget_low_s16(q8sums1), vget_low_s16(q6scales1)),
3217 vmull_s16(vget_high_s16(q8sums1), vget_high_s16(q6scales1)),
3218 ),
3219 );
3220 let isum_mins = vaddvq_s32(prod);
3221 let mut isum = 0i32;
3222 let mut q6 = ql;
3223 let mut qhp = qh;
3224 let mut q8p = q8;
3225 let mut sc = scale;
3226 for _ in 0..2 {
3227 let qh0 = vld1q_u8(qhp);
3228 let qh1 = vld1q_u8(qhp.add(16));
3229 qhp = qhp.add(32);
3230 let q6_0 = vld1q_u8(q6);
3231 let q6_1 = vld1q_u8(q6.add(16));
3232 let q6_2 = vld1q_u8(q6.add(32));
3233 let q6_3 = vld1q_u8(q6.add(48));
3234 q6 = q6.add(64);
3235 let q8_0 = vld1q_s8(q8p);
3236 let q8_1 = vld1q_s8(q8p.add(16));
3237 let q8_2 = vld1q_s8(q8p.add(32));
3238 let q8_3 = vld1q_s8(q8p.add(48));
3239 q8p = q8p.add(64);
3240
3241 let h0 = vshlq_n_u8(vandq_u8(mone, qh0), 4);
3242 let h1 = vshlq_n_u8(vandq_u8(mone, qh1), 4);
3243 let mut shifted = vshrq_n_u8(qh0, 2);
3244 let h2 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3245 shifted = vshrq_n_u8(qh1, 2);
3246 let h3 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3247
3248 let b0 = vreinterpretq_s8_u8(vorrq_u8(vandq_u8(q6_0, m4b), h0));
3249 let b1 = vreinterpretq_s8_u8(vorrq_u8(vandq_u8(q6_1, m4b), h1));
3250 let b2 = vreinterpretq_s8_u8(vorrq_u8(vandq_u8(q6_2, m4b), h2));
3251 let b3 = vreinterpretq_s8_u8(vorrq_u8(vandq_u8(q6_3, m4b), h3));
3252 let z = vdupq_n_s32(0);
3253 isum += vaddvq_s32(neon_sdot(z, b0, q8_0)) * (*sc.add(0) as i32)
3254 + vaddvq_s32(neon_sdot(z, b1, q8_1)) * (*sc.add(1) as i32)
3255 + vaddvq_s32(neon_sdot(z, b2, q8_2)) * (*sc.add(2) as i32)
3256 + vaddvq_s32(neon_sdot(z, b3, q8_3)) * (*sc.add(3) as i32);
3257 sc = sc.add(4);
3258
3259 let q8_0 = vld1q_s8(q8p);
3260 let q8_1 = vld1q_s8(q8p.add(16));
3261 let q8_2 = vld1q_s8(q8p.add(32));
3262 let q8_3 = vld1q_s8(q8p.add(48));
3263 q8p = q8p.add(64);
3264 shifted = vshrq_n_u8(qh0, 4);
3265 let h0 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3266 shifted = vshrq_n_u8(qh1, 4);
3267 let h1 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3268 shifted = vshrq_n_u8(qh0, 6);
3269 let h2 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3270 shifted = vshrq_n_u8(qh1, 6);
3271 let h3 = vshlq_n_u8(vandq_u8(mone, shifted), 4);
3272 let b0 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_0, 4), h0));
3273 let b1 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_1, 4), h1));
3274 let b2 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_2, 4), h2));
3275 let b3 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_3, 4), h3));
3276 isum += vaddvq_s32(neon_sdot(z, b0, q8_0)) * (*sc.add(0) as i32)
3277 + vaddvq_s32(neon_sdot(z, b1, q8_1)) * (*sc.add(1) as i32)
3278 + vaddvq_s32(neon_sdot(z, b2, q8_2)) * (*sc.add(2) as i32)
3279 + vaddvq_s32(neon_sdot(z, b3, q8_3)) * (*sc.add(3) as i32);
3280 sc = sc.add(4);
3281 }
3282 acc += d_all * da * (isum - 32 * isum_mins) as f32;
3283 }
3284 acc
3285 }
3286
3287 #[target_feature(enable = "neon")]
3296 pub unsafe fn dot_q4_0_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
3297 debug_assert_eq!(row_bytes.len() % Q4_0_BLOCK_BYTES, 0);
3298 let bias = vdupq_n_s16(8);
3299 let low_mask = vdupq_n_u8(0x0F);
3300
3301 let mut acc = 0f32;
3302 for (b, block) in row_bytes
3303 .as_chunks::<Q4_0_BLOCK_BYTES>()
3304 .0
3305 .iter()
3306 .enumerate()
3307 {
3308 let scale = f16::from_le_bytes([block[0], block[1]]).to_f32();
3309 let base = b * Q4_0_BLOCK_ELEMS;
3310 let nibbles = vld1q_u8(block.as_ptr().add(2));
3311
3312 let lo_nibbles = vandq_u8(nibbles, low_mask); let hi_nibbles = vshrq_n_u8(nibbles, 4); let mut block_acc = vdupq_n_f32(0.0);
3316 for (group_idx, nib_u8) in [lo_nibbles, hi_nibbles].into_iter().enumerate() {
3317 let lo16 = vsubq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(nib_u8))), bias);
3318 let hi16 = vsubq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_high_u8(nib_u8))), bias);
3319 for (half_idx, half16) in [lo16, hi16].into_iter().enumerate() {
3320 let lo32 = vmovl_s16(vget_low_s16(half16));
3321 let hi32 = vmovl_s16(vget_high_s16(half16));
3322 let f_lo = vcvtq_f32_s32(lo32);
3323 let f_hi = vcvtq_f32_s32(hi32);
3324 let elem_base = base + group_idx * 16 + half_idx * 8;
3325 let x_lo = vld1q_f32(x.as_ptr().add(elem_base));
3326 let x_hi = vld1q_f32(x.as_ptr().add(elem_base + 4));
3327 block_acc = vfmaq_f32(block_acc, f_lo, x_lo);
3328 block_acc = vfmaq_f32(block_acc, f_hi, x_hi);
3329 }
3330 }
3331 acc += vaddvq_f32(block_acc) * scale;
3332 }
3333 acc
3334 }
3335
3336 #[inline]
3342 #[target_feature(enable = "neon")]
3343 unsafe fn widen_u8x16_to_f32_quads(
3344 v: uint8x16_t,
3345 ) -> (float32x4_t, float32x4_t, float32x4_t, float32x4_t) {
3346 let u16_lo = vmovl_u8(vget_low_u8(v)); let u16_hi = vmovl_u8(vget_high_u8(v)); (
3349 vcvtq_f32_u32(vmovl_u16(vget_low_u16(u16_lo))), vcvtq_f32_u32(vmovl_u16(vget_high_u16(u16_lo))), vcvtq_f32_u32(vmovl_u16(vget_low_u16(u16_hi))), vcvtq_f32_u32(vmovl_u16(vget_high_u16(u16_hi))), )
3354 }
3355
3356 #[inline]
3363 #[target_feature(enable = "neon")]
3364 unsafe fn fma_affine16(
3365 quads: (float32x4_t, float32x4_t, float32x4_t, float32x4_t),
3366 d: f32,
3367 min_vec: float32x4_t,
3368 x: &[f32],
3369 x_base: usize,
3370 mut acc: float32x4_t,
3371 ) -> float32x4_t {
3372 let (q0, q1, q2, q3) = quads;
3373 let mut i = 0usize;
3374 for q in [q0, q1, q2, q3] {
3375 let w = vsubq_f32(vmulq_n_f32(q, d), min_vec);
3376 let xv = vld1q_f32(x.as_ptr().add(x_base + i));
3377 acc = vfmaq_f32(acc, w, xv);
3378 i += 4;
3379 }
3380 acc
3381 }
3382
3383 #[target_feature(enable = "neon")]
3392 pub unsafe fn dot_q4_k_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
3393 debug_assert_eq!(row_bytes.len() % Q4_K_BLOCK_BYTES, 0);
3394 let low_mask = vdupq_n_u8(0x0F);
3395 let mut acc = 0f32;
3396 let mut x_base = 0usize;
3397 for block in row_bytes.as_chunks::<Q4_K_BLOCK_BYTES>().0 {
3398 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
3399 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
3400 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
3401 let qs = &block[16..144];
3402
3403 let mut vec_acc = vdupq_n_f32(0.0);
3406 let mut is = 0usize;
3407 let mut q_off = 0usize;
3408 for _ in 0..4 {
3409 let (sc1, m1) = q4_k_scale_min(is, &scales);
3410 let (sc2, m2) = q4_k_scale_min(is + 1, &scales);
3411 let d1 = d * sc1 as f32;
3412 let min1_vec = vdupq_n_f32(dmin * m1 as f32);
3413 let d2 = d * sc2 as f32;
3414 let min2_vec = vdupq_n_f32(dmin * m2 as f32);
3415
3416 for g in 0..2 {
3417 let raw16 = vld1q_u8(qs.as_ptr().add(q_off + g * 16));
3418 let lo_nib = vandq_u8(raw16, low_mask);
3419 let hi_nib = vshrq_n_u8(raw16, 4);
3420 vec_acc = fma_affine16(
3421 widen_u8x16_to_f32_quads(lo_nib),
3422 d1,
3423 min1_vec,
3424 x,
3425 x_base + g * 16,
3426 vec_acc,
3427 );
3428 vec_acc = fma_affine16(
3429 widen_u8x16_to_f32_quads(hi_nib),
3430 d2,
3431 min2_vec,
3432 x,
3433 x_base + 32 + g * 16,
3434 vec_acc,
3435 );
3436 }
3437 q_off += 32;
3438 x_base += 64;
3439 is += 2;
3440 }
3441 acc += vaddvq_f32(vec_acc);
3442 }
3443 acc
3444 }
3445
3446 #[target_feature(enable = "neon")]
3456 pub unsafe fn dot_q5_k_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
3457 debug_assert_eq!(row_bytes.len() % Q5_K_BLOCK_BYTES, 0);
3458 let low_mask = vdupq_n_u8(0x0F);
3459 let sixteen = vdupq_n_u8(16);
3460 let mut acc = 0f32;
3461 let mut x_base = 0usize;
3462 for block in row_bytes.as_chunks::<Q5_K_BLOCK_BYTES>().0 {
3463 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
3464 let dmin = f16::from_le_bytes([block[2], block[3]]).to_f32();
3465 let scales: [u8; Q4_K_SCALE_BYTES] = block[4..16].try_into().unwrap();
3466 let qh = &block[16..48];
3467 let qs = &block[48..176];
3468
3469 let mut is = 0usize;
3470 let (mut u1, mut u2) = (1u8, 2u8);
3471 for oi in 0..4 {
3472 let (sc1, m1) = q4_k_scale_min(is, &scales);
3473 let (sc2, m2) = q4_k_scale_min(is + 1, &scales);
3474 let d1 = d * sc1 as f32;
3475 let min1_vec = vdupq_n_f32(dmin * m1 as f32);
3476 let d2 = d * sc2 as f32;
3477 let min2_vec = vdupq_n_f32(dmin * m2 as f32);
3478 let ql = &qs[oi * 32..oi * 32 + 32];
3479 let u1_vec = vdupq_n_u8(u1);
3480 let u2_vec = vdupq_n_u8(u2);
3481
3482 let mut lo_acc = vdupq_n_f32(0.0);
3483 let mut hi_acc = vdupq_n_f32(0.0);
3484 for g in 0..2 {
3485 let raw16 = vld1q_u8(ql.as_ptr().add(g * 16));
3486 let qh16 = vld1q_u8(qh.as_ptr().add(g * 16));
3487
3488 let lo_nib = vandq_u8(raw16, low_mask);
3489 let hi_nib = vshrq_n_u8(raw16, 4);
3490 let hi_bit1 = vandq_u8(vtstq_u8(qh16, u1_vec), sixteen);
3491 let hi_bit2 = vandq_u8(vtstq_u8(qh16, u2_vec), sixteen);
3492
3493 lo_acc = fma_affine16(
3494 widen_u8x16_to_f32_quads(vorrq_u8(lo_nib, hi_bit1)),
3495 d1,
3496 min1_vec,
3497 x,
3498 x_base + g * 16,
3499 lo_acc,
3500 );
3501 hi_acc = fma_affine16(
3502 widen_u8x16_to_f32_quads(vorrq_u8(hi_nib, hi_bit2)),
3503 d2,
3504 min2_vec,
3505 x,
3506 x_base + 32 + g * 16,
3507 hi_acc,
3508 );
3509 }
3510 acc += vaddvq_f32(lo_acc) + vaddvq_f32(hi_acc);
3511 x_base += 64;
3512 is += 2;
3513 u1 <<= 2;
3514 u2 <<= 2;
3515 }
3516 }
3517 acc
3518 }
3519
3520 #[inline]
3528 #[target_feature(enable = "neon")]
3529 unsafe fn widen_u8x16_centered_to_f32_quads(
3530 v: uint8x16_t,
3531 bias16: int16x8_t,
3532 ) -> (float32x4_t, float32x4_t, float32x4_t, float32x4_t) {
3533 let s16_lo = vsubq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(v))), bias16);
3534 let s16_hi = vsubq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_high_u8(v))), bias16);
3535 (
3536 vcvtq_f32_s32(vmovl_s16(vget_low_s16(s16_lo))),
3537 vcvtq_f32_s32(vmovl_s16(vget_high_s16(s16_lo))),
3538 vcvtq_f32_s32(vmovl_s16(vget_low_s16(s16_hi))),
3539 vcvtq_f32_s32(vmovl_s16(vget_high_s16(s16_hi))),
3540 )
3541 }
3542
3543 #[inline]
3550 #[target_feature(enable = "neon")]
3551 unsafe fn fma_scaled16(
3552 quads: (float32x4_t, float32x4_t, float32x4_t, float32x4_t),
3553 scale: f32,
3554 x: &[f32],
3555 x_base: usize,
3556 mut acc: float32x4_t,
3557 ) -> float32x4_t {
3558 let (q0, q1, q2, q3) = quads;
3559 let mut i = 0usize;
3560 for q in [q0, q1, q2, q3] {
3561 let xv = vld1q_f32(x.as_ptr().add(x_base + i));
3562 acc = vfmaq_f32(acc, vmulq_n_f32(q, scale), xv);
3563 i += 4;
3564 }
3565 acc
3566 }
3567
3568 #[inline]
3580 #[target_feature(enable = "neon")]
3581 #[allow(clippy::too_many_arguments)]
3582 unsafe fn q6_k_group<const QH_SHIFT: i32, const HI_NIBBLE: bool>(
3583 ql: &[u8],
3584 ql_off: usize,
3585 qh: &[u8],
3586 sc: &[u8],
3587 sc_base: usize,
3588 d: f32,
3589 x: &[f32],
3590 x_base: usize,
3591 out_off: usize,
3592 low_mask: uint8x16_t,
3593 two_bit_mask: uint8x16_t,
3594 bias16: int16x8_t,
3595 ) -> f32 {
3596 let mut acc = 0f32;
3597 for sub in 0..2usize {
3598 let byte_off = sub * 16;
3599 let ql_raw = vld1q_u8(ql.as_ptr().add(ql_off + byte_off));
3600 let qh_raw = vld1q_u8(qh.as_ptr().add(byte_off));
3601
3602 let nib = if HI_NIBBLE {
3603 vshrq_n_u8::<4>(ql_raw)
3604 } else {
3605 vandq_u8(ql_raw, low_mask)
3606 };
3607 let qh_field = vandq_u8(vshrq_n_u8::<QH_SHIFT>(qh_raw), two_bit_mask);
3615 let raw6 = vorrq_u8(nib, vshlq_n_u8::<4>(qh_field));
3616
3617 let scale = d * (sc[sc_base + sub] as i8) as f32;
3618 let quads = widen_u8x16_centered_to_f32_quads(raw6, bias16);
3619 let acc_vec = fma_scaled16(
3620 quads,
3621 scale,
3622 x,
3623 x_base + out_off + sub * 16,
3624 vdupq_n_f32(0.0),
3625 );
3626 acc += vaddvq_f32(acc_vec);
3627 }
3628 acc
3629 }
3630
3631 #[inline]
3637 #[target_feature(enable = "neon")]
3638 #[allow(clippy::too_many_arguments)]
3639 unsafe fn q6_k_group_q1(
3640 ql: &[u8],
3641 qh: &[u8],
3642 sc: &[u8],
3643 d: f32,
3644 x: &[f32],
3645 x_base: usize,
3646 low_mask: uint8x16_t,
3647 two_bit_mask: uint8x16_t,
3648 bias16: int16x8_t,
3649 ) -> f32 {
3650 let mut acc = 0f32;
3651 #[allow(clippy::needless_range_loop)]
3655 for sub in 0..2usize {
3656 let byte_off = sub * 16;
3657 let ql_raw = vld1q_u8(ql.as_ptr().add(byte_off));
3658 let qh_raw = vld1q_u8(qh.as_ptr().add(byte_off));
3659
3660 let nib = vandq_u8(ql_raw, low_mask);
3661 let qh_field = vandq_u8(qh_raw, two_bit_mask);
3662 let raw6 = vorrq_u8(nib, vshlq_n_u8::<4>(qh_field));
3663
3664 let scale = d * (sc[sub] as i8) as f32;
3665 let quads = widen_u8x16_centered_to_f32_quads(raw6, bias16);
3666 let acc_vec = fma_scaled16(quads, scale, x, x_base + sub * 16, vdupq_n_f32(0.0));
3667 acc += vaddvq_f32(acc_vec);
3668 }
3669 acc
3670 }
3671
3672 #[target_feature(enable = "neon")]
3678 pub unsafe fn dot_q6_k_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
3679 debug_assert_eq!(row_bytes.len() % Q6_K_BLOCK_BYTES, 0);
3680 debug_assert_eq!(
3681 row_bytes.len() / Q6_K_BLOCK_BYTES * Q6_K_BLOCK_ELEMS,
3682 x.len()
3683 );
3684 let low_mask = vdupq_n_u8(0x0F);
3685 let two_bit_mask = vdupq_n_u8(0x03);
3686 let bias16 = vdupq_n_s16(32);
3687
3688 let mut acc = 0f32;
3689 let mut x_base = 0usize;
3690 for block in row_bytes.as_chunks::<Q6_K_BLOCK_BYTES>().0 {
3691 let ql_full = &block[0..128];
3692 let qh_full = &block[128..192];
3693 let sc_full = &block[192..208];
3694 let d = f16::from_le_bytes([block[208], block[209]]).to_f32();
3695
3696 for half in 0..2 {
3697 let ql = &ql_full[half * 64..half * 64 + 64];
3698 let qh = &qh_full[half * 32..half * 32 + 32];
3699 let sc = &sc_full[half * 8..half * 8 + 8];
3700 let half_base = x_base + half * 128;
3701
3702 acc += q6_k_group_q1(ql, qh, sc, d, x, half_base, low_mask, two_bit_mask, bias16);
3704 acc += q6_k_group::<2, false>(
3706 ql,
3707 32,
3708 qh,
3709 sc,
3710 2,
3711 d,
3712 x,
3713 half_base,
3714 32,
3715 low_mask,
3716 two_bit_mask,
3717 bias16,
3718 );
3719 acc += q6_k_group::<4, true>(
3721 ql,
3722 0,
3723 qh,
3724 sc,
3725 4,
3726 d,
3727 x,
3728 half_base,
3729 64,
3730 low_mask,
3731 two_bit_mask,
3732 bias16,
3733 );
3734 acc += q6_k_group::<6, true>(
3736 ql,
3737 32,
3738 qh,
3739 sc,
3740 6,
3741 d,
3742 x,
3743 half_base,
3744 96,
3745 low_mask,
3746 two_bit_mask,
3747 bias16,
3748 );
3749 }
3750 x_base += Q6_K_BLOCK_ELEMS;
3751 }
3752 acc
3753 }
3754
3755 #[inline]
3772 #[target_feature(enable = "neon")]
3773 unsafe fn mxfp4_nibbles_to_f32_quads(
3774 nib: uint8x16_t,
3775 ) -> (float32x4_t, float32x4_t, float32x4_t, float32x4_t) {
3776 let sign_bit = vandq_u8(nib, vdupq_n_u8(0x8));
3777 let e = vandq_u8(vshrq_n_u8(nib, 1), vdupq_n_u8(0x3));
3778 let m = vandq_u8(nib, vdupq_n_u8(0x1));
3779
3780 let pow2_table: [u8; 16] = [1, 1, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
3781 let bias_table: [u8; 16] = [0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
3782 let pow2_u8 = vqtbl1q_u8(vld1q_u8(pow2_table.as_ptr()), e);
3783 let bias_u8 = vqtbl1q_u8(vld1q_u8(bias_table.as_ptr()), e);
3784
3785 let (p0, p1, p2, p3) = widen_u8x16_to_f32_quads(pow2_u8);
3786 let (b0, b1, b2, b3) = widen_u8x16_to_f32_quads(bias_u8);
3787 let (m0, m1, m2, m3) = widen_u8x16_to_f32_quads(m);
3788 let (s0, s1, s2, s3) = widen_u8x16_to_f32_quads(sign_bit);
3789
3790 let half = vdupq_n_f32(0.5);
3791 let quarter = vdupq_n_f32(0.25);
3792 let one = vdupq_n_f32(1.0);
3793
3794 let decode = |p: float32x4_t, b: float32x4_t, m: float32x4_t, s: float32x4_t| {
3795 let magnitude = vmulq_f32(p, vfmaq_f32(b, m, half)); let sign_mul = vfmsq_f32(one, s, quarter); vmulq_f32(magnitude, sign_mul)
3798 };
3799
3800 (
3801 decode(p0, b0, m0, s0),
3802 decode(p1, b1, m1, s1),
3803 decode(p2, b2, m2, s2),
3804 decode(p3, b3, m3, s3),
3805 )
3806 }
3807
3808 #[target_feature(enable = "neon")]
3818 pub unsafe fn dot_mxfp4_row_f32_neon(packed: &[u8], scales: &[u8], x: &[f32]) -> f32 {
3819 debug_assert_eq!(packed.len(), scales.len() * (MXFP4_GROUP_SIZE / 2));
3820 let low_mask = vdupq_n_u8(0x0F);
3821 let mut acc = 0f32;
3822 let mut x_base = 0usize;
3823 for (g, &e_byte) in scales.iter().enumerate() {
3824 let d = e8m0_scale(e_byte);
3825 let group = &packed[g * 16..(g + 1) * 16];
3826 let bytes = vld1q_u8(group.as_ptr());
3827 let lo_nib = vandq_u8(bytes, low_mask);
3828 let hi_nib = vshrq_n_u8(bytes, 4);
3829
3830 let mut block_acc = vdupq_n_f32(0.0);
3831 for (half_idx, nib) in [lo_nib, hi_nib].into_iter().enumerate() {
3832 let (v0, v1, v2, v3) = mxfp4_nibbles_to_f32_quads(nib);
3833 let elem_base = x_base + half_idx * 16;
3834 for (i, v) in [v0, v1, v2, v3].into_iter().enumerate() {
3835 let xv = vld1q_f32(x.as_ptr().add(elem_base + i * 4));
3836 block_acc = vfmaq_f32(block_acc, v, xv);
3837 }
3838 }
3839 acc += vaddvq_f32(block_acc) * d;
3840 x_base += MXFP4_GROUP_SIZE;
3841 }
3842 acc
3843 }
3844
3845 #[target_feature(enable = "neon")]
3849 pub unsafe fn dot_q8_1_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
3850 debug_assert_eq!(row_bytes.len() % Q8_1_BLOCK_BYTES, 0);
3851 let mut acc = 0f32;
3852 for (b, block) in row_bytes
3853 .as_chunks::<Q8_1_BLOCK_BYTES>()
3854 .0
3855 .iter()
3856 .enumerate()
3857 {
3858 let scale = f16::from_le_bytes([block[0], block[1]]).to_f32();
3859 let base = b * Q8_1_BLOCK_ELEMS;
3860 let qs = &block[4..36];
3861
3862 let mut block_acc = vdupq_n_f32(0.0);
3863 for g in 0..2 {
3864 let raw16 = vld1q_s8(qs.as_ptr().add(g * 16) as *const i8);
3865 let lo16 = vmovl_s8(vget_low_s8(raw16));
3866 let hi16 = vmovl_s8(vget_high_s8(raw16));
3867 for (half_idx, half16) in [lo16, hi16].into_iter().enumerate() {
3868 let lo32 = vmovl_s16(vget_low_s16(half16));
3869 let hi32 = vmovl_s16(vget_high_s16(half16));
3870 let f_lo = vcvtq_f32_s32(lo32);
3871 let f_hi = vcvtq_f32_s32(hi32);
3872 let elem_base = base + g * 16 + half_idx * 8;
3873 let x_lo = vld1q_f32(x.as_ptr().add(elem_base));
3874 let x_hi = vld1q_f32(x.as_ptr().add(elem_base + 4));
3875 block_acc = vfmaq_f32(block_acc, f_lo, x_lo);
3876 block_acc = vfmaq_f32(block_acc, f_hi, x_hi);
3877 }
3878 }
3879 acc += vaddvq_f32(block_acc) * scale;
3880 }
3881 acc
3882 }
3883
3884 #[target_feature(enable = "neon")]
3890 pub unsafe fn dot_q4_1_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
3891 debug_assert_eq!(row_bytes.len() % Q4_1_BLOCK_BYTES, 0);
3892 let low_mask = vdupq_n_u8(0x0F);
3893
3894 let mut acc = 0f32;
3895 for (b, block) in row_bytes
3896 .as_chunks::<Q4_1_BLOCK_BYTES>()
3897 .0
3898 .iter()
3899 .enumerate()
3900 {
3901 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
3902 let m = f16::from_le_bytes([block[2], block[3]]).to_f32();
3903 let base = b * Q4_1_BLOCK_ELEMS;
3904 let nibbles = vld1q_u8(block.as_ptr().add(4));
3905
3906 let lo_nibbles = vandq_u8(nibbles, low_mask); let hi_nibbles = vshrq_n_u8(nibbles, 4); let mut block_acc = vdupq_n_f32(0.0);
3910 for (group_idx, nib_u8) in [lo_nibbles, hi_nibbles].into_iter().enumerate() {
3911 let lo16 = vmovl_u8(vget_low_u8(nib_u8));
3912 let hi16 = vmovl_u8(vget_high_u8(nib_u8));
3913 for (half_idx, half16) in [lo16, hi16].into_iter().enumerate() {
3914 let lo32 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(half16)));
3915 let hi32 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(half16)));
3916 let elem_base = base + group_idx * 16 + half_idx * 8;
3917 let x_lo = vld1q_f32(x.as_ptr().add(elem_base));
3918 let x_hi = vld1q_f32(x.as_ptr().add(elem_base + 4));
3919 let w_lo = vfmaq_n_f32(vdupq_n_f32(m), lo32, d);
3920 let w_hi = vfmaq_n_f32(vdupq_n_f32(m), hi32, d);
3921 block_acc = vfmaq_f32(block_acc, w_lo, x_lo);
3922 block_acc = vfmaq_f32(block_acc, w_hi, x_hi);
3923 }
3924 }
3925 acc += vaddvq_f32(block_acc);
3926 }
3927 acc
3928 }
3929
3930 #[target_feature(enable = "neon")]
3936 pub unsafe fn dot_q5_0_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
3937 debug_assert_eq!(row_bytes.len() % Q5_0_BLOCK_BYTES, 0);
3938 let mut acc = 0f32;
3939 for (b, block) in row_bytes
3940 .as_chunks::<Q5_0_BLOCK_BYTES>()
3941 .0
3942 .iter()
3943 .enumerate()
3944 {
3945 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
3946 let qh = u32::from_le_bytes(block[2..6].try_into().unwrap());
3947 let qs = &block[6..22];
3948 let base = b * Q5_0_BLOCK_ELEMS;
3949
3950 let mut vals = [0i8; 32];
3951 for j in 0..16 {
3952 let (xh_0, xh_1) = q5_fifth_bits(qh, j);
3953 vals[j] = (((qs[j] & 0x0F) | xh_0) as i32 - 16) as i8;
3954 vals[j + 16] = (((qs[j] >> 4) | xh_1) as i32 - 16) as i8;
3955 }
3956
3957 let mut block_acc = vdupq_n_f32(0.0);
3958 for g in 0..2 {
3959 let raw16 = vld1q_s8(vals.as_ptr().add(g * 16));
3960 let lo16 = vmovl_s8(vget_low_s8(raw16));
3961 let hi16 = vmovl_s8(vget_high_s8(raw16));
3962 for (half_idx, half16) in [lo16, hi16].into_iter().enumerate() {
3963 let lo32 = vcvtq_f32_s32(vmovl_s16(vget_low_s16(half16)));
3964 let hi32 = vcvtq_f32_s32(vmovl_s16(vget_high_s16(half16)));
3965 let elem_base = base + g * 16 + half_idx * 8;
3966 let x_lo = vld1q_f32(x.as_ptr().add(elem_base));
3967 let x_hi = vld1q_f32(x.as_ptr().add(elem_base + 4));
3968 block_acc = vfmaq_f32(block_acc, lo32, x_lo);
3969 block_acc = vfmaq_f32(block_acc, hi32, x_hi);
3970 }
3971 }
3972 acc += vaddvq_f32(block_acc) * d;
3973 }
3974 acc
3975 }
3976
3977 #[target_feature(enable = "neon")]
3981 pub unsafe fn dot_q5_1_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
3982 debug_assert_eq!(row_bytes.len() % Q5_1_BLOCK_BYTES, 0);
3983 let mut acc = 0f32;
3984 for (b, block) in row_bytes
3985 .as_chunks::<Q5_1_BLOCK_BYTES>()
3986 .0
3987 .iter()
3988 .enumerate()
3989 {
3990 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
3991 let m = f16::from_le_bytes([block[2], block[3]]).to_f32();
3992 let qh = u32::from_le_bytes(block[4..8].try_into().unwrap());
3993 let qs = &block[8..24];
3994 let base = b * Q5_1_BLOCK_ELEMS;
3995
3996 let mut vals = [0u8; 32];
3997 for j in 0..16 {
3998 let (xh_0, xh_1) = q5_fifth_bits(qh, j);
3999 vals[j] = (qs[j] & 0x0F) | xh_0;
4000 vals[j + 16] = (qs[j] >> 4) | xh_1;
4001 }
4002
4003 let mut block_acc = vdupq_n_f32(0.0);
4004 for g in 0..2 {
4005 let raw16 = vld1q_u8(vals.as_ptr().add(g * 16));
4006 let lo16 = vmovl_u8(vget_low_u8(raw16));
4007 let hi16 = vmovl_u8(vget_high_u8(raw16));
4008 for (half_idx, half16) in [lo16, hi16].into_iter().enumerate() {
4009 let lo32 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(half16)));
4010 let hi32 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(half16)));
4011 let elem_base = base + g * 16 + half_idx * 8;
4012 let x_lo = vld1q_f32(x.as_ptr().add(elem_base));
4013 let x_hi = vld1q_f32(x.as_ptr().add(elem_base + 4));
4014 let w_lo = vfmaq_n_f32(vdupq_n_f32(m), lo32, d);
4015 let w_hi = vfmaq_n_f32(vdupq_n_f32(m), hi32, d);
4016 block_acc = vfmaq_f32(block_acc, w_lo, x_lo);
4017 block_acc = vfmaq_f32(block_acc, w_hi, x_hi);
4018 }
4019 }
4020 acc += vaddvq_f32(block_acc);
4021 }
4022 acc
4023 }
4024
4025 #[target_feature(enable = "neon")]
4034 pub unsafe fn dot_q2_k_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
4035 debug_assert_eq!(row_bytes.len() % Q2_K_BLOCK_BYTES, 0);
4036 let two_bit_mask = vdupq_n_u8(3);
4037 let mut acc = 0f32;
4038 let mut x_base = 0usize;
4039
4040 macro_rules! shr2 {
4047 (0, $v:expr) => {
4048 vandq_u8($v, two_bit_mask)
4049 };
4050 ($shift:literal, $v:expr) => {
4051 vandq_u8(vshrq_n_u8($v, $shift), two_bit_mask)
4052 };
4053 }
4054
4055 macro_rules! q2_k_sub_block {
4056 ($shift:tt, $q:expr, $scales:expr, $is:expr, $d:expr, $dmin:expr, $x:expr, $x_base:expr, $acc:expr) => {{
4057 let sc1 = $scales[$is];
4058 $is += 1;
4059 let dl1 = $d * (sc1 & 0x0F) as f32;
4060 let min1_vec = vdupq_n_f32($dmin * (sc1 >> 4) as f32);
4061 let sc2 = $scales[$is];
4062 $is += 1;
4063 let dl2 = $d * (sc2 & 0x0F) as f32;
4064 let min2_vec = vdupq_n_f32($dmin * (sc2 >> 4) as f32);
4065
4066 let lo16 = vld1q_u8($q.as_ptr());
4067 let hi16 = vld1q_u8($q.as_ptr().add(16));
4068 let lo2 = shr2!($shift, lo16);
4069 let hi2 = shr2!($shift, hi16);
4070
4071 let lo_acc = fma_affine16(
4072 widen_u8x16_to_f32_quads(lo2),
4073 dl1,
4074 min1_vec,
4075 $x,
4076 $x_base,
4077 vdupq_n_f32(0.0),
4078 );
4079 let hi_acc = fma_affine16(
4080 widen_u8x16_to_f32_quads(hi2),
4081 dl2,
4082 min2_vec,
4083 $x,
4084 $x_base + 16,
4085 vdupq_n_f32(0.0),
4086 );
4087 $acc += vaddvq_f32(lo_acc) + vaddvq_f32(hi_acc);
4088 $x_base += 32;
4089 }};
4090 }
4091
4092 for block in row_bytes.as_chunks::<Q2_K_BLOCK_BYTES>().0 {
4093 let scales: &[u8; Q2_K_SCALE_BYTES] = block[0..16].try_into().unwrap();
4094 let qs = &block[16..80];
4095 let d = f16::from_le_bytes([block[80], block[81]]).to_f32();
4096 let dmin = f16::from_le_bytes([block[82], block[83]]).to_f32();
4097
4098 let mut is = 0usize;
4099 for n in 0..2 {
4100 let q = &qs[n * 32..n * 32 + 32];
4101 q2_k_sub_block!(0, q, scales, is, d, dmin, x, x_base, acc);
4102 q2_k_sub_block!(2, q, scales, is, d, dmin, x, x_base, acc);
4103 q2_k_sub_block!(4, q, scales, is, d, dmin, x, x_base, acc);
4104 q2_k_sub_block!(6, q, scales, is, d, dmin, x, x_base, acc);
4105 }
4106 }
4107 acc
4108 }
4109
4110 #[target_feature(enable = "neon")]
4120 pub unsafe fn dot_q3_k_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
4121 debug_assert_eq!(row_bytes.len() % Q3_K_BLOCK_BYTES, 0);
4122 let two_bit_mask = vdupq_n_u8(3);
4123 let four = vdupq_n_u8(4);
4124 let mut acc = 0f32;
4125 let mut x_base = 0usize;
4126
4127 macro_rules! shr2 {
4130 (0, $v:expr) => {
4131 vandq_u8($v, two_bit_mask)
4132 };
4133 ($shift:literal, $v:expr) => {
4134 vandq_u8(vshrq_n_u8($v, $shift), two_bit_mask)
4135 };
4136 }
4137
4138 macro_rules! q3_k_sub_block {
4139 ($shift:tt, $q:expr, $hmask:expr, $m_vec:expr, $dl1:expr, $dl2:expr, $x:expr, $x_base:expr, $acc:expr) => {{
4140 let lo16 = vld1q_u8($q.as_ptr());
4141 let hi16 = vld1q_u8($q.as_ptr().add(16));
4142 let lo2 = shr2!($shift, lo16);
4143 let hi2 = shr2!($shift, hi16);
4144
4145 let hmask_lo = vld1q_u8($hmask.as_ptr());
4146 let hmask_hi = vld1q_u8($hmask.as_ptr().add(16));
4147 let bit_clear_lo = vmvnq_u8(vtstq_u8(hmask_lo, $m_vec));
4152 let bit_clear_hi = vmvnq_u8(vtstq_u8(hmask_hi, $m_vec));
4153 let bias_lo = vandq_u8(bit_clear_lo, four);
4154 let bias_hi = vandq_u8(bit_clear_hi, four);
4155
4156 let raw_lo_i16_lo = vsubq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(lo2))), {
4157 vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(bias_lo)))
4158 });
4159 let raw_lo_i16_hi =
4160 vsubq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_high_u8(lo2))), {
4161 vreinterpretq_s16_u16(vmovl_u8(vget_high_u8(bias_lo)))
4162 });
4163 let raw_hi_i16_lo = vsubq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(hi2))), {
4164 vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(bias_hi)))
4165 });
4166 let raw_hi_i16_hi =
4167 vsubq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_high_u8(hi2))), {
4168 vreinterpretq_s16_u16(vmovl_u8(vget_high_u8(bias_hi)))
4169 });
4170
4171 let mut lo_acc = vdupq_n_f32(0.0);
4172 let mut hi_acc = vdupq_n_f32(0.0);
4173 for (i, half16) in [raw_lo_i16_lo, raw_lo_i16_hi].into_iter().enumerate() {
4174 let lo32 = vcvtq_f32_s32(vmovl_s16(vget_low_s16(half16)));
4175 let hi32 = vcvtq_f32_s32(vmovl_s16(vget_high_s16(half16)));
4176 let elem_base = $x_base + i * 8;
4177 let x_lo = vld1q_f32($x.as_ptr().add(elem_base));
4178 let x_hi = vld1q_f32($x.as_ptr().add(elem_base + 4));
4179 lo_acc = vfmaq_f32(lo_acc, lo32, x_lo);
4180 lo_acc = vfmaq_f32(lo_acc, hi32, x_hi);
4181 }
4182 for (i, half16) in [raw_hi_i16_lo, raw_hi_i16_hi].into_iter().enumerate() {
4183 let lo32 = vcvtq_f32_s32(vmovl_s16(vget_low_s16(half16)));
4184 let hi32 = vcvtq_f32_s32(vmovl_s16(vget_high_s16(half16)));
4185 let elem_base = $x_base + 16 + i * 8;
4186 let x_lo = vld1q_f32($x.as_ptr().add(elem_base));
4187 let x_hi = vld1q_f32($x.as_ptr().add(elem_base + 4));
4188 hi_acc = vfmaq_f32(hi_acc, lo32, x_lo);
4189 hi_acc = vfmaq_f32(hi_acc, hi32, x_hi);
4190 }
4191 $acc += vaddvq_f32(lo_acc) * $dl1 + vaddvq_f32(hi_acc) * $dl2;
4192 $x_base += 32;
4193 }};
4194 }
4195
4196 for block in row_bytes.as_chunks::<Q3_K_BLOCK_BYTES>().0 {
4197 let hmask = &block[0..32];
4198 let qs = &block[32..96];
4199 let scales_raw: &[u8; Q3_K_SCALE_BYTES] = block[96..108].try_into().unwrap();
4200 let d_all = f16::from_le_bytes([block[108], block[109]]).to_f32();
4201 let scales = q3_k_unpack_scales(scales_raw);
4202
4203 let mut is = 0usize;
4204 let mut m = 1u8;
4205 for n in 0..2 {
4206 let q = &qs[n * 32..n * 32 + 32];
4207 for shift in [0u32, 2, 4, 6] {
4208 let dl1 = d_all * (scales[is] as f32 - 32.0);
4209 let dl2 = d_all * (scales[is + 1] as f32 - 32.0);
4210 is += 2;
4211 let m_vec = vdupq_n_u8(m);
4212 match shift {
4213 0 => q3_k_sub_block!(0, q, hmask, m_vec, dl1, dl2, x, x_base, acc),
4214 2 => q3_k_sub_block!(2, q, hmask, m_vec, dl1, dl2, x, x_base, acc),
4215 4 => q3_k_sub_block!(4, q, hmask, m_vec, dl1, dl2, x, x_base, acc),
4216 6 => q3_k_sub_block!(6, q, hmask, m_vec, dl1, dl2, x, x_base, acc),
4217 _ => unreachable!(),
4218 }
4219 m <<= 1;
4220 }
4221 }
4222 }
4223 acc
4224 }
4225
4226 #[target_feature(enable = "neon")]
4235 pub unsafe fn dot_iq4_nl_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
4236 debug_assert_eq!(row_bytes.len() % IQ4_NL_BLOCK_BYTES, 0);
4237 let low_mask = vdupq_n_u8(0x0F);
4238 let codebook = vld1q_s8(KVALUES_IQ4NL.as_ptr());
4239 let mut acc = 0f32;
4240 let mut x_base = 0usize;
4241 for block in row_bytes.as_chunks::<IQ4_NL_BLOCK_BYTES>().0 {
4242 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4243 let qs = &block[2..18];
4244 let bytes = vld1q_u8(qs.as_ptr());
4245 let lo_idx = vandq_u8(bytes, low_mask);
4246 let hi_idx = vshrq_n_u8(bytes, 4);
4247 let lo_vals = vqtbl1q_s8(codebook, lo_idx);
4248 let hi_vals = vqtbl1q_s8(codebook, hi_idx);
4249
4250 let mut block_acc = vdupq_n_f32(0.0);
4251 for (half_idx, vals) in [lo_vals, hi_vals].into_iter().enumerate() {
4252 let lo16 = vmovl_s8(vget_low_s8(vals));
4253 let hi16 = vmovl_s8(vget_high_s8(vals));
4254 for (i, half16) in [lo16, hi16].into_iter().enumerate() {
4255 let lo32 = vcvtq_f32_s32(vmovl_s16(vget_low_s16(half16)));
4256 let hi32 = vcvtq_f32_s32(vmovl_s16(vget_high_s16(half16)));
4257 let elem_base = x_base + half_idx * 16 + i * 8;
4258 let x_lo = vld1q_f32(x.as_ptr().add(elem_base));
4259 let x_hi = vld1q_f32(x.as_ptr().add(elem_base + 4));
4260 block_acc = vfmaq_f32(block_acc, lo32, x_lo);
4261 block_acc = vfmaq_f32(block_acc, hi32, x_hi);
4262 }
4263 }
4264 acc += vaddvq_f32(block_acc) * d;
4265 x_base += IQ4_NL_BLOCK_ELEMS;
4266 }
4267 acc
4268 }
4269
4270 #[target_feature(enable = "neon")]
4275 pub unsafe fn dot_iq4_xs_f32_neon(row_bytes: &[u8], x: &[f32]) -> f32 {
4276 debug_assert_eq!(row_bytes.len() % IQ4_XS_BLOCK_BYTES, 0);
4277 let low_mask = vdupq_n_u8(0x0F);
4278 let codebook = vld1q_s8(KVALUES_IQ4NL.as_ptr());
4279 let mut acc = 0f32;
4280 let mut x_base = 0usize;
4281 for block in row_bytes.as_chunks::<IQ4_XS_BLOCK_BYTES>().0 {
4282 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4283 let scales_h = u16::from_le_bytes([block[2], block[3]]);
4284 let scales_l = &block[4..8];
4285 let qs = &block[8..136];
4286
4287 for ib in 0..8 {
4288 let ls = ((scales_l[ib / 2] >> (4 * (ib % 2))) & 0xf)
4289 | (((scales_h >> (2 * ib)) & 3) as u8) << 4;
4290 let dl = d * (ls as f32 - 32.0);
4291 let sub = &qs[ib * 16..ib * 16 + 16];
4292 let bytes = vld1q_u8(sub.as_ptr());
4293 let lo_idx = vandq_u8(bytes, low_mask);
4294 let hi_idx = vshrq_n_u8(bytes, 4);
4295 let lo_vals = vqtbl1q_s8(codebook, lo_idx);
4296 let hi_vals = vqtbl1q_s8(codebook, hi_idx);
4297
4298 let mut sub_acc = vdupq_n_f32(0.0);
4299 for (half_idx, vals) in [lo_vals, hi_vals].into_iter().enumerate() {
4300 let lo16 = vmovl_s8(vget_low_s8(vals));
4301 let hi16 = vmovl_s8(vget_high_s8(vals));
4302 for (i, half16) in [lo16, hi16].into_iter().enumerate() {
4303 let lo32 = vcvtq_f32_s32(vmovl_s16(vget_low_s16(half16)));
4304 let hi32 = vcvtq_f32_s32(vmovl_s16(vget_high_s16(half16)));
4305 let elem_base = x_base + half_idx * 16 + i * 8;
4306 let x_lo = vld1q_f32(x.as_ptr().add(elem_base));
4307 let x_hi = vld1q_f32(x.as_ptr().add(elem_base + 4));
4308 sub_acc = vfmaq_f32(sub_acc, lo32, x_lo);
4309 sub_acc = vfmaq_f32(sub_acc, hi32, x_hi);
4310 }
4311 }
4312 acc += vaddvq_f32(sub_acc) * dl;
4313 x_base += 32;
4314 }
4315 }
4316 acc
4317 }
4318}
4319
4320pub fn dot_q4_0_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
4324 #[cfg(target_arch = "x86_64")]
4325 {
4326 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
4327 return unsafe { simd_x86::dot_q4_0_f32_avx2(row_bytes, x) };
4328 }
4329 }
4330 #[cfg(target_arch = "aarch64")]
4331 {
4332 if std::arch::is_aarch64_feature_detected!("neon") {
4333 return unsafe { simd_aarch64::dot_q4_0_f32_neon(row_bytes, x) };
4334 }
4335 }
4336 dot_q4_0_f32_scalar(row_bytes, x)
4337}
4338
4339pub fn dot_q4_0_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
4340 debug_assert_eq!(row_bytes.len() % Q4_0_BLOCK_BYTES, 0);
4341 let mut acc = 0f32;
4342 for (b, block) in row_bytes
4343 .as_chunks::<Q4_0_BLOCK_BYTES>()
4344 .0
4345 .iter()
4346 .enumerate()
4347 {
4348 let scale = f16::from_le_bytes([block[0], block[1]]).to_f32();
4349 let nibbles = &block[2..18];
4350 let base = b * Q4_0_BLOCK_ELEMS;
4351 let mut block_acc = 0f32;
4352 for i in 0..16 {
4353 let byte = nibbles[i];
4354 let lo = (byte & 0x0F) as i32 - 8;
4355 let hi = ((byte >> 4) & 0x0F) as i32 - 8;
4356 block_acc += (lo as f32) * x[base + i];
4357 block_acc += (hi as f32) * x[base + i + 16];
4358 }
4359 acc += block_acc * scale;
4360 }
4361 acc
4362}
4363
4364pub fn dequant_q4_1(src: &[u8]) -> Result<Vec<f32>, QuantError> {
4368 if !src.len().is_multiple_of(Q4_1_BLOCK_BYTES) {
4369 return Err(QuantError::Misaligned(src.len(), Q4_1_BLOCK_BYTES));
4370 }
4371 let n_blocks = src.len() / Q4_1_BLOCK_BYTES;
4372 let mut out = vec![0f32; n_blocks * Q4_1_BLOCK_ELEMS];
4373 for (b, block) in src.as_chunks::<Q4_1_BLOCK_BYTES>().0.iter().enumerate() {
4374 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4375 let m = f16::from_le_bytes([block[2], block[3]]).to_f32();
4376 let nibbles = &block[4..20];
4377 let base = b * Q4_1_BLOCK_ELEMS;
4378 for i in 0..16 {
4379 let byte = nibbles[i];
4380 out[base + i] = (byte & 0x0F) as f32 * d + m;
4381 out[base + i + 16] = (byte >> 4) as f32 * d + m;
4382 }
4383 }
4384 Ok(out)
4385}
4386
4387pub fn dot_q4_1_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
4390 #[cfg(target_arch = "x86_64")]
4391 {
4392 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
4393 return unsafe { simd_x86::dot_q4_1_f32_avx2(row_bytes, x) };
4394 }
4395 }
4396 #[cfg(target_arch = "aarch64")]
4397 {
4398 if std::arch::is_aarch64_feature_detected!("neon") {
4399 return unsafe { simd_aarch64::dot_q4_1_f32_neon(row_bytes, x) };
4400 }
4401 }
4402 dot_q4_1_f32_scalar(row_bytes, x)
4403}
4404
4405pub fn dot_q4_1_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
4406 debug_assert_eq!(row_bytes.len() % Q4_1_BLOCK_BYTES, 0);
4407 let mut acc = 0f32;
4408 for (b, block) in row_bytes
4409 .as_chunks::<Q4_1_BLOCK_BYTES>()
4410 .0
4411 .iter()
4412 .enumerate()
4413 {
4414 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4415 let m = f16::from_le_bytes([block[2], block[3]]).to_f32();
4416 let nibbles = &block[4..20];
4417 let base = b * Q4_1_BLOCK_ELEMS;
4418 for i in 0..16 {
4419 let byte = nibbles[i];
4420 acc += ((byte & 0x0F) as f32 * d + m) * x[base + i];
4421 acc += ((byte >> 4) as f32 * d + m) * x[base + i + 16];
4422 }
4423 }
4424 acc
4425}
4426
4427#[inline]
4433fn q5_fifth_bits(qh: u32, j: usize) -> (u8, u8) {
4434 let xh_0 = ((qh >> j) << 4) as u8 & 0x10;
4435 let xh_1 = (qh >> (j + 12)) as u8 & 0x10;
4436 (xh_0, xh_1)
4437}
4438
4439pub fn dequant_q5_0(src: &[u8]) -> Result<Vec<f32>, QuantError> {
4443 if !src.len().is_multiple_of(Q5_0_BLOCK_BYTES) {
4444 return Err(QuantError::Misaligned(src.len(), Q5_0_BLOCK_BYTES));
4445 }
4446 let n_blocks = src.len() / Q5_0_BLOCK_BYTES;
4447 let mut out = vec![0f32; n_blocks * Q5_0_BLOCK_ELEMS];
4448 for (b, block) in src.as_chunks::<Q5_0_BLOCK_BYTES>().0.iter().enumerate() {
4449 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4450 let qh = u32::from_le_bytes(block[2..6].try_into().unwrap());
4451 let qs = &block[6..22];
4452 let base = b * Q5_0_BLOCK_ELEMS;
4453 for j in 0..16 {
4454 let (xh_0, xh_1) = q5_fifth_bits(qh, j);
4455 let x0 = ((qs[j] & 0x0F) | xh_0) as i32 - 16;
4456 let x1 = ((qs[j] >> 4) | xh_1) as i32 - 16;
4457 out[base + j] = x0 as f32 * d;
4458 out[base + j + 16] = x1 as f32 * d;
4459 }
4460 }
4461 Ok(out)
4462}
4463
4464pub fn dot_q5_0_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
4467 #[cfg(target_arch = "x86_64")]
4468 {
4469 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
4470 return unsafe { simd_x86::dot_q5_0_f32_avx2(row_bytes, x) };
4471 }
4472 }
4473 #[cfg(target_arch = "aarch64")]
4474 {
4475 if std::arch::is_aarch64_feature_detected!("neon") {
4476 return unsafe { simd_aarch64::dot_q5_0_f32_neon(row_bytes, x) };
4477 }
4478 }
4479 dot_q5_0_f32_scalar(row_bytes, x)
4480}
4481
4482pub fn dot_q5_0_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
4483 debug_assert_eq!(row_bytes.len() % Q5_0_BLOCK_BYTES, 0);
4484 let mut acc = 0f32;
4485 for (b, block) in row_bytes
4486 .as_chunks::<Q5_0_BLOCK_BYTES>()
4487 .0
4488 .iter()
4489 .enumerate()
4490 {
4491 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4492 let qh = u32::from_le_bytes(block[2..6].try_into().unwrap());
4493 let qs = &block[6..22];
4494 let base = b * Q5_0_BLOCK_ELEMS;
4495 for j in 0..16 {
4496 let (xh_0, xh_1) = q5_fifth_bits(qh, j);
4497 let x0 = ((qs[j] & 0x0F) | xh_0) as i32 - 16;
4498 let x1 = ((qs[j] >> 4) | xh_1) as i32 - 16;
4499 acc += (x0 as f32 * d) * x[base + j];
4500 acc += (x1 as f32 * d) * x[base + j + 16];
4501 }
4502 }
4503 acc
4504}
4505
4506pub fn dequant_q5_1(src: &[u8]) -> Result<Vec<f32>, QuantError> {
4510 if !src.len().is_multiple_of(Q5_1_BLOCK_BYTES) {
4511 return Err(QuantError::Misaligned(src.len(), Q5_1_BLOCK_BYTES));
4512 }
4513 let n_blocks = src.len() / Q5_1_BLOCK_BYTES;
4514 let mut out = vec![0f32; n_blocks * Q5_1_BLOCK_ELEMS];
4515 for (b, block) in src.as_chunks::<Q5_1_BLOCK_BYTES>().0.iter().enumerate() {
4516 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4517 let m = f16::from_le_bytes([block[2], block[3]]).to_f32();
4518 let qh = u32::from_le_bytes(block[4..8].try_into().unwrap());
4519 let qs = &block[8..24];
4520 let base = b * Q5_1_BLOCK_ELEMS;
4521 for j in 0..16 {
4522 let (xh_0, xh_1) = q5_fifth_bits(qh, j);
4523 let x0 = (qs[j] & 0x0F) | xh_0;
4524 let x1 = (qs[j] >> 4) | xh_1;
4525 out[base + j] = x0 as f32 * d + m;
4526 out[base + j + 16] = x1 as f32 * d + m;
4527 }
4528 }
4529 Ok(out)
4530}
4531
4532pub fn dot_q5_1_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
4535 #[cfg(target_arch = "x86_64")]
4536 {
4537 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
4538 return unsafe { simd_x86::dot_q5_1_f32_avx2(row_bytes, x) };
4539 }
4540 }
4541 #[cfg(target_arch = "aarch64")]
4542 {
4543 if std::arch::is_aarch64_feature_detected!("neon") {
4544 return unsafe { simd_aarch64::dot_q5_1_f32_neon(row_bytes, x) };
4545 }
4546 }
4547 dot_q5_1_f32_scalar(row_bytes, x)
4548}
4549
4550pub fn dot_q5_1_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
4551 debug_assert_eq!(row_bytes.len() % Q5_1_BLOCK_BYTES, 0);
4552 let mut acc = 0f32;
4553 for (b, block) in row_bytes
4554 .as_chunks::<Q5_1_BLOCK_BYTES>()
4555 .0
4556 .iter()
4557 .enumerate()
4558 {
4559 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4560 let m = f16::from_le_bytes([block[2], block[3]]).to_f32();
4561 let qh = u32::from_le_bytes(block[4..8].try_into().unwrap());
4562 let qs = &block[8..24];
4563 let base = b * Q5_1_BLOCK_ELEMS;
4564 for j in 0..16 {
4565 let (xh_0, xh_1) = q5_fifth_bits(qh, j);
4566 let x0 = (qs[j] & 0x0F) | xh_0;
4567 let x1 = (qs[j] >> 4) | xh_1;
4568 acc += (x0 as f32 * d + m) * x[base + j];
4569 acc += (x1 as f32 * d + m) * x[base + j + 16];
4570 }
4571 }
4572 acc
4573}
4574
4575pub fn dequant_q8_1(src: &[u8]) -> Result<Vec<f32>, QuantError> {
4581 if !src.len().is_multiple_of(Q8_1_BLOCK_BYTES) {
4582 return Err(QuantError::Misaligned(src.len(), Q8_1_BLOCK_BYTES));
4583 }
4584 let n_blocks = src.len() / Q8_1_BLOCK_BYTES;
4585 let mut out = Vec::with_capacity(n_blocks * Q8_1_BLOCK_ELEMS);
4586 for block in src.as_chunks::<Q8_1_BLOCK_BYTES>().0 {
4587 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4588 for i in 0..Q8_1_BLOCK_ELEMS {
4589 let q = block[4 + i] as i8;
4590 out.push(q as f32 * d);
4591 }
4592 }
4593 Ok(out)
4594}
4595
4596pub fn dot_q8_1_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
4602 #[cfg(target_arch = "x86_64")]
4603 {
4604 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
4605 return unsafe { simd_x86::dot_q8_1_f32_avx2(row_bytes, x) };
4606 }
4607 }
4608 #[cfg(target_arch = "aarch64")]
4609 {
4610 if std::arch::is_aarch64_feature_detected!("neon") {
4611 return unsafe { simd_aarch64::dot_q8_1_f32_neon(row_bytes, x) };
4612 }
4613 }
4614 dot_q8_1_f32_scalar(row_bytes, x)
4615}
4616
4617pub fn dot_q8_1_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
4618 debug_assert_eq!(row_bytes.len() % Q8_1_BLOCK_BYTES, 0);
4619 let mut acc = 0f32;
4620 for (b, block) in row_bytes
4621 .as_chunks::<Q8_1_BLOCK_BYTES>()
4622 .0
4623 .iter()
4624 .enumerate()
4625 {
4626 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4627 let base = b * Q8_1_BLOCK_ELEMS;
4628 let mut block_acc = 0f32;
4629 for i in 0..Q8_1_BLOCK_ELEMS {
4630 let q = block[4 + i] as i8;
4631 block_acc += (q as f32) * x[base + i];
4632 }
4633 acc += block_acc * d;
4634 }
4635 acc
4636}
4637
4638pub fn dequant_q2_k(src: &[u8]) -> Result<Vec<f32>, QuantError> {
4646 if !src.len().is_multiple_of(Q2_K_BLOCK_BYTES) {
4647 return Err(QuantError::Misaligned(src.len(), Q2_K_BLOCK_BYTES));
4648 }
4649 let n_blocks = src.len() / Q2_K_BLOCK_BYTES;
4650 let mut out = Vec::with_capacity(n_blocks * Q2_K_BLOCK_ELEMS);
4651 for block in src.as_chunks::<Q2_K_BLOCK_BYTES>().0 {
4652 let scales: &[u8; Q2_K_SCALE_BYTES] = block[0..16].try_into().unwrap();
4653 let qs = &block[16..80];
4654 let d = f16::from_le_bytes([block[80], block[81]]).to_f32();
4655 let dmin = f16::from_le_bytes([block[82], block[83]]).to_f32();
4656
4657 let mut is = 0usize;
4658 for n in 0..2 {
4659 let q = &qs[n * 32..n * 32 + 32];
4660 let mut shift = 0u32;
4661 for _j in 0..4 {
4662 let sc1 = scales[is];
4663 is += 1;
4664 let (dl1, ml1) = (d * (sc1 & 0x0F) as f32, dmin * (sc1 >> 4) as f32);
4665 for &byte in &q[0..16] {
4666 let raw = (byte >> shift) & 3;
4667 out.push(dl1 * raw as f32 - ml1);
4668 }
4669
4670 let sc2 = scales[is];
4671 is += 1;
4672 let (dl2, ml2) = (d * (sc2 & 0x0F) as f32, dmin * (sc2 >> 4) as f32);
4673 for &byte in &q[16..32] {
4674 let raw = (byte >> shift) & 3;
4675 out.push(dl2 * raw as f32 - ml2);
4676 }
4677 shift += 2;
4678 }
4679 }
4680 }
4681 Ok(out)
4682}
4683
4684pub fn dot_q2_k_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
4687 #[cfg(target_arch = "x86_64")]
4688 {
4689 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
4690 return unsafe { simd_x86::dot_q2_k_f32_avx2(row_bytes, x) };
4691 }
4692 }
4693 #[cfg(target_arch = "aarch64")]
4694 {
4695 if std::arch::is_aarch64_feature_detected!("neon") {
4696 return unsafe { simd_aarch64::dot_q2_k_f32_neon(row_bytes, x) };
4697 }
4698 }
4699 dot_q2_k_f32_scalar(row_bytes, x)
4700}
4701
4702pub fn dot_q2_k_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
4703 debug_assert_eq!(row_bytes.len() % Q2_K_BLOCK_BYTES, 0);
4704 let mut acc = 0f32;
4705 let mut x_base = 0usize;
4706 for block in row_bytes.as_chunks::<Q2_K_BLOCK_BYTES>().0 {
4707 let scales: &[u8; Q2_K_SCALE_BYTES] = block[0..16].try_into().unwrap();
4708 let qs = &block[16..80];
4709 let d = f16::from_le_bytes([block[80], block[81]]).to_f32();
4710 let dmin = f16::from_le_bytes([block[82], block[83]]).to_f32();
4711
4712 let mut is = 0usize;
4713 for n in 0..2 {
4714 let q = &qs[n * 32..n * 32 + 32];
4715 let mut shift = 0u32;
4716 for _j in 0..4 {
4717 let sc1 = scales[is];
4718 is += 1;
4719 let (dl1, ml1) = (d * (sc1 & 0x0F) as f32, dmin * (sc1 >> 4) as f32);
4720 for l in 0..16 {
4721 let raw = (q[l] >> shift) & 3;
4722 acc += (dl1 * raw as f32 - ml1) * x[x_base + l];
4723 }
4724
4725 let sc2 = scales[is];
4726 is += 1;
4727 let (dl2, ml2) = (d * (sc2 & 0x0F) as f32, dmin * (sc2 >> 4) as f32);
4728 for l in 0..16 {
4729 let raw = (q[l + 16] >> shift) & 3;
4730 acc += (dl2 * raw as f32 - ml2) * x[x_base + l + 16];
4731 }
4732 shift += 2;
4733 x_base += 32;
4734 }
4735 }
4736 }
4737 acc
4738}
4739
4740fn q3_k_unpack_scales(raw: &[u8; Q3_K_SCALE_BYTES]) -> [i8; 16] {
4750 const KMASK1: u8 = 0x03;
4751 const KMASK2: u8 = 0x0F;
4752 let mut out = [0u8; 16];
4753 for j in 0..4 {
4754 let (a0, a1, tmp) = (raw[j], raw[4 + j], raw[8 + j]);
4755 out[j] = (a0 & KMASK2) | ((tmp & KMASK1) << 4);
4760 out[4 + j] = (a1 & KMASK2) | (((tmp >> 2) & KMASK1) << 4);
4761 out[8 + j] = (a0 >> 4) | (((tmp >> 4) & KMASK1) << 4);
4762 out[12 + j] = (a1 >> 4) | (((tmp >> 6) & KMASK1) << 4);
4763 }
4764 out.map(|b| b as i8)
4768}
4769
4770pub fn dequant_q3_k(src: &[u8]) -> Result<Vec<f32>, QuantError> {
4778 if !src.len().is_multiple_of(Q3_K_BLOCK_BYTES) {
4779 return Err(QuantError::Misaligned(src.len(), Q3_K_BLOCK_BYTES));
4780 }
4781 let n_blocks = src.len() / Q3_K_BLOCK_BYTES;
4782 let mut out = Vec::with_capacity(n_blocks * Q3_K_BLOCK_ELEMS);
4783 for block in src.as_chunks::<Q3_K_BLOCK_BYTES>().0 {
4784 let hmask = &block[0..32];
4785 let qs = &block[32..96];
4786 let scales_raw: &[u8; Q3_K_SCALE_BYTES] = block[96..108].try_into().unwrap();
4787 let d_all = f16::from_le_bytes([block[108], block[109]]).to_f32();
4788 let scales = q3_k_unpack_scales(scales_raw);
4789
4790 let mut is = 0usize;
4791 let mut m = 1u8;
4792 for n in 0..2 {
4793 let q = &qs[n * 32..n * 32 + 32];
4794 let mut shift = 0u32;
4795 for _j in 0..4 {
4796 let dl1 = d_all * (scales[is] as f32 - 32.0);
4797 is += 1;
4798 for l in 0..16 {
4799 let raw = ((q[l] >> shift) & 3) as i32;
4800 let bias = if hmask[l] & m != 0 { 0 } else { 4 };
4801 out.push(dl1 * (raw - bias) as f32);
4802 }
4803
4804 let dl2 = d_all * (scales[is] as f32 - 32.0);
4805 is += 1;
4806 for l in 0..16 {
4807 let raw = ((q[l + 16] >> shift) & 3) as i32;
4808 let bias = if hmask[l + 16] & m != 0 { 0 } else { 4 };
4809 out.push(dl2 * (raw - bias) as f32);
4810 }
4811 shift += 2;
4812 m <<= 1;
4813 }
4814 }
4815 }
4816 Ok(out)
4817}
4818
4819pub fn dot_q3_k_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
4822 #[cfg(target_arch = "x86_64")]
4823 {
4824 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
4825 return unsafe { simd_x86::dot_q3_k_f32_avx2(row_bytes, x) };
4826 }
4827 }
4828 #[cfg(target_arch = "aarch64")]
4829 {
4830 if std::arch::is_aarch64_feature_detected!("neon") {
4831 return unsafe { simd_aarch64::dot_q3_k_f32_neon(row_bytes, x) };
4832 }
4833 }
4834 dot_q3_k_f32_scalar(row_bytes, x)
4835}
4836
4837pub fn dot_q3_k_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
4838 debug_assert_eq!(row_bytes.len() % Q3_K_BLOCK_BYTES, 0);
4839 let mut acc = 0f32;
4840 let mut x_base = 0usize;
4841 for block in row_bytes.as_chunks::<Q3_K_BLOCK_BYTES>().0 {
4842 let hmask = &block[0..32];
4843 let qs = &block[32..96];
4844 let scales_raw: &[u8; Q3_K_SCALE_BYTES] = block[96..108].try_into().unwrap();
4845 let d_all = f16::from_le_bytes([block[108], block[109]]).to_f32();
4846 let scales = q3_k_unpack_scales(scales_raw);
4847
4848 let mut is = 0usize;
4849 let mut m = 1u8;
4850 for n in 0..2 {
4851 let q = &qs[n * 32..n * 32 + 32];
4852 let mut shift = 0u32;
4853 for _j in 0..4 {
4854 let dl1 = d_all * (scales[is] as f32 - 32.0);
4855 is += 1;
4856 for l in 0..16 {
4857 let raw = ((q[l] >> shift) & 3) as i32;
4858 let bias = if hmask[l] & m != 0 { 0 } else { 4 };
4859 acc += (dl1 * (raw - bias) as f32) * x[x_base + l];
4860 }
4861
4862 let dl2 = d_all * (scales[is] as f32 - 32.0);
4863 is += 1;
4864 for l in 0..16 {
4865 let raw = ((q[l + 16] >> shift) & 3) as i32;
4866 let bias = if hmask[l + 16] & m != 0 { 0 } else { 4 };
4867 acc += (dl2 * (raw - bias) as f32) * x[x_base + l + 16];
4868 }
4869 shift += 2;
4870 m <<= 1;
4871 x_base += 32;
4872 }
4873 }
4874 }
4875 acc
4876}
4877
4878pub const IQ4_NL_BLOCK_BYTES: usize = 18;
4879pub const IQ4_NL_BLOCK_ELEMS: usize = 32;
4880pub const IQ4_XS_BLOCK_BYTES: usize = 136;
4881pub const IQ4_XS_BLOCK_ELEMS: usize = 256;
4882
4883pub(crate) const KVALUES_IQ4NL: [i8; 16] = [
4888 -127, -104, -83, -65, -49, -35, -22, -10, 1, 13, 25, 38, 53, 69, 89, 113,
4889];
4890
4891pub fn dequant_iq4_nl(src: &[u8]) -> Result<Vec<f32>, QuantError> {
4892 if !src.len().is_multiple_of(IQ4_NL_BLOCK_BYTES) {
4893 return Err(QuantError::Misaligned(src.len(), IQ4_NL_BLOCK_BYTES));
4894 }
4895 let n_blocks = src.len() / IQ4_NL_BLOCK_BYTES;
4896 let mut out = Vec::with_capacity(n_blocks * IQ4_NL_BLOCK_ELEMS);
4897 for block in src.as_chunks::<IQ4_NL_BLOCK_BYTES>().0 {
4898 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4899 let qs = &block[2..18];
4900 let mut lo = [0f32; 16];
4901 let mut hi = [0f32; 16];
4902 for (j, &byte) in qs.iter().enumerate() {
4903 lo[j] = d * KVALUES_IQ4NL[(byte & 0xf) as usize] as f32;
4904 hi[j] = d * KVALUES_IQ4NL[(byte >> 4) as usize] as f32;
4905 }
4906 out.extend_from_slice(&lo);
4907 out.extend_from_slice(&hi);
4908 }
4909 Ok(out)
4910}
4911
4912pub fn dot_iq4_nl_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
4915 #[cfg(target_arch = "x86_64")]
4916 {
4917 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
4918 return unsafe { simd_x86::dot_iq4_nl_f32_avx2(row_bytes, x) };
4919 }
4920 }
4921 #[cfg(target_arch = "aarch64")]
4922 {
4923 if std::arch::is_aarch64_feature_detected!("neon") {
4924 return unsafe { simd_aarch64::dot_iq4_nl_f32_neon(row_bytes, x) };
4925 }
4926 }
4927 dot_iq4_nl_f32_scalar(row_bytes, x)
4928}
4929
4930pub fn dot_iq4_nl_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
4931 debug_assert_eq!(row_bytes.len() % IQ4_NL_BLOCK_BYTES, 0);
4932 let mut acc = 0f32;
4933 let mut x_base = 0usize;
4934 for block in row_bytes.as_chunks::<IQ4_NL_BLOCK_BYTES>().0 {
4935 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4936 let qs = &block[2..18];
4937 for (j, &byte) in qs.iter().enumerate() {
4938 acc += (d * KVALUES_IQ4NL[(byte & 0xf) as usize] as f32) * x[x_base + j];
4939 acc += (d * KVALUES_IQ4NL[(byte >> 4) as usize] as f32) * x[x_base + 16 + j];
4940 }
4941 x_base += IQ4_NL_BLOCK_ELEMS;
4942 }
4943 acc
4944}
4945
4946pub fn dequant_iq4_xs(src: &[u8]) -> Result<Vec<f32>, QuantError> {
4947 if !src.len().is_multiple_of(IQ4_XS_BLOCK_BYTES) {
4948 return Err(QuantError::Misaligned(src.len(), IQ4_XS_BLOCK_BYTES));
4949 }
4950 let n_blocks = src.len() / IQ4_XS_BLOCK_BYTES;
4951 let mut out = Vec::with_capacity(n_blocks * IQ4_XS_BLOCK_ELEMS);
4952 for block in src.as_chunks::<IQ4_XS_BLOCK_BYTES>().0 {
4953 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
4954 let scales_h = u16::from_le_bytes([block[2], block[3]]);
4955 let scales_l = &block[4..8];
4956 let qs = &block[8..136];
4957
4958 for ib in 0..8 {
4959 let ls = ((scales_l[ib / 2] >> (4 * (ib % 2))) & 0xf)
4960 | (((scales_h >> (2 * ib)) & 3) as u8) << 4;
4961 let dl = d * (ls as f32 - 32.0);
4962 let sub = &qs[ib * 16..ib * 16 + 16];
4963 let mut lo = [0f32; 16];
4964 let mut hi = [0f32; 16];
4965 for (j, &byte) in sub.iter().enumerate() {
4966 lo[j] = dl * KVALUES_IQ4NL[(byte & 0xf) as usize] as f32;
4967 hi[j] = dl * KVALUES_IQ4NL[(byte >> 4) as usize] as f32;
4968 }
4969 out.extend_from_slice(&lo);
4970 out.extend_from_slice(&hi);
4971 }
4972 }
4973 Ok(out)
4974}
4975
4976pub fn dot_iq4_xs_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
4979 #[cfg(target_arch = "x86_64")]
4980 {
4981 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
4982 return unsafe { simd_x86::dot_iq4_xs_f32_avx2(row_bytes, x) };
4983 }
4984 }
4985 #[cfg(target_arch = "aarch64")]
4986 {
4987 if std::arch::is_aarch64_feature_detected!("neon") {
4988 return unsafe { simd_aarch64::dot_iq4_xs_f32_neon(row_bytes, x) };
4989 }
4990 }
4991 dot_iq4_xs_f32_scalar(row_bytes, x)
4992}
4993
4994pub fn dot_iq4_xs_f32_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
4995 debug_assert_eq!(row_bytes.len() % IQ4_XS_BLOCK_BYTES, 0);
4996 let mut acc = 0f32;
4997 let mut x_base = 0usize;
4998 for block in row_bytes.as_chunks::<IQ4_XS_BLOCK_BYTES>().0 {
4999 let d = f16::from_le_bytes([block[0], block[1]]).to_f32();
5000 let scales_h = u16::from_le_bytes([block[2], block[3]]);
5001 let scales_l = &block[4..8];
5002 let qs = &block[8..136];
5003
5004 for ib in 0..8 {
5005 let ls = ((scales_l[ib / 2] >> (4 * (ib % 2))) & 0xf)
5006 | (((scales_h >> (2 * ib)) & 3) as u8) << 4;
5007 let dl = d * (ls as f32 - 32.0);
5008 let sub = &qs[ib * 16..ib * 16 + 16];
5009 for (j, &byte) in sub.iter().enumerate() {
5010 acc += (dl * KVALUES_IQ4NL[(byte & 0xf) as usize] as f32) * x[x_base + j];
5011 acc += (dl * KVALUES_IQ4NL[(byte >> 4) as usize] as f32) * x[x_base + 16 + j];
5012 }
5013 x_base += 32;
5014 }
5015 }
5016 acc
5017}
5018
5019pub const MXFP4_GROUP_SIZE: usize = 32;
5024
5025const KVALUES_MXFP4: [f32; 16] = [
5034 0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, -0.0, -0.5, -1.0, -1.5, -2.0, -3.0, -4.0, -6.0,
5035];
5036
5037fn e8m0_scale(e: u8) -> f32 {
5047 if e == 0 {
5048 f32::from_bits(0x0040_0000)
5049 } else {
5050 f32::from_bits((e as u32) << 23)
5051 }
5052}
5053
5054pub fn dequant_mxfp4_row(packed: &[u8], scales: &[u8]) -> Result<Vec<f32>, QuantError> {
5067 let expected_packed_len = scales.len() * (MXFP4_GROUP_SIZE / 2);
5068 if packed.len() != expected_packed_len {
5069 return Err(QuantError::Mxfp4RowMismatch(
5070 packed.len(),
5071 expected_packed_len,
5072 ));
5073 }
5074 let mut out = Vec::with_capacity(scales.len() * MXFP4_GROUP_SIZE);
5075 for (g, &e) in scales.iter().enumerate() {
5076 let d = e8m0_scale(e);
5077 let group = &packed[g * (MXFP4_GROUP_SIZE / 2)..(g + 1) * (MXFP4_GROUP_SIZE / 2)];
5078 let mut lo = [0f32; MXFP4_GROUP_SIZE / 2];
5079 let mut hi = [0f32; MXFP4_GROUP_SIZE / 2];
5080 for (j, &byte) in group.iter().enumerate() {
5081 lo[j] = d * KVALUES_MXFP4[(byte & 0xf) as usize];
5082 hi[j] = d * KVALUES_MXFP4[(byte >> 4) as usize];
5083 }
5084 out.extend_from_slice(&lo);
5085 out.extend_from_slice(&hi);
5086 }
5087 Ok(out)
5088}
5089
5090pub fn dot_mxfp4_row_f32(packed: &[u8], scales: &[u8], x: &[f32]) -> f32 {
5098 #[cfg(target_arch = "x86_64")]
5099 {
5100 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
5101 return unsafe { simd_x86::dot_mxfp4_row_f32_avx2(packed, scales, x) };
5102 }
5103 }
5104 #[cfg(target_arch = "aarch64")]
5105 {
5106 if std::arch::is_aarch64_feature_detected!("neon") {
5107 return unsafe { simd_aarch64::dot_mxfp4_row_f32_neon(packed, scales, x) };
5108 }
5109 }
5110 dot_mxfp4_row_f32_scalar(packed, scales, x)
5111}
5112
5113pub fn dot_mxfp4_row_f32_scalar(packed: &[u8], scales: &[u8], x: &[f32]) -> f32 {
5114 debug_assert_eq!(packed.len(), scales.len() * (MXFP4_GROUP_SIZE / 2));
5115 let mut acc = 0f32;
5116 let mut x_base = 0usize;
5117 for (g, &e) in scales.iter().enumerate() {
5118 let d = e8m0_scale(e);
5119 let group = &packed[g * (MXFP4_GROUP_SIZE / 2)..(g + 1) * (MXFP4_GROUP_SIZE / 2)];
5120 for (j, &byte) in group.iter().enumerate() {
5121 acc += (d * KVALUES_MXFP4[(byte & 0xf) as usize]) * x[x_base + j];
5122 acc += (d * KVALUES_MXFP4[(byte >> 4) as usize]) * x[x_base + MXFP4_GROUP_SIZE / 2 + j];
5123 }
5124 x_base += MXFP4_GROUP_SIZE;
5125 }
5126 acc
5127}
5128
5129pub const IQ1_S_BLOCK_BYTES: usize = 50;
5158pub const IQ1_S_BLOCK_ELEMS: usize = 256;
5159pub const IQ1_M_BLOCK_BYTES: usize = 56;
5163pub const IQ1_M_BLOCK_ELEMS: usize = 256;
5164pub const IQ2_XXS_BLOCK_BYTES: usize = 66;
5167pub const IQ2_XXS_BLOCK_ELEMS: usize = 256;
5168pub const IQ2_XS_BLOCK_BYTES: usize = 74;
5171pub const IQ2_XS_BLOCK_ELEMS: usize = 256;
5172pub const IQ2_S_BLOCK_BYTES: usize = 82;
5175pub const IQ2_S_BLOCK_ELEMS: usize = 256;
5176pub const IQ3_XXS_BLOCK_BYTES: usize = 98;
5179pub const IQ3_XXS_BLOCK_ELEMS: usize = 256;
5180pub const IQ3_S_BLOCK_BYTES: usize = 110;
5184pub const IQ3_S_BLOCK_ELEMS: usize = 256;
5185
5186const IQ1S_DELTA: f32 = 0.125;
5192
5193#[inline]
5198fn iq_sign(signs: u8, j: usize) -> f32 {
5199 if signs & iq_tables::KMASK_IQ2XS[j] != 0 {
5200 -1.0
5201 } else {
5202 1.0
5203 }
5204}
5205
5206#[inline]
5207fn read_f16(bytes: &[u8]) -> f32 {
5208 f16::from_le_bytes([bytes[0], bytes[1]]).to_f32()
5209}
5210
5211#[inline]
5214fn for_each_iq1_s(block: &[u8], mut emit: impl FnMut(usize, f32)) {
5215 let d = read_f16(block);
5216 let qs = &block[2..34];
5217 let qh = &block[34..50];
5218 let mut idx = 0usize;
5219 for ib in 0..8 {
5220 let h = u16::from_le_bytes([qh[2 * ib], qh[2 * ib + 1]]);
5221 let dl = d * (2.0 * ((h >> 12) & 7) as f32 + 1.0);
5222 let delta = if h & 0x8000 != 0 {
5223 -IQ1S_DELTA
5224 } else {
5225 IQ1S_DELTA
5226 };
5227 for l in 0..4 {
5228 let grid_index = qs[4 * ib + l] as usize | ((((h >> (3 * l)) & 7) as usize) << 8);
5229 let row = iq_tables::IQ1S_GRID[grid_index];
5230 for j in 0..8 {
5231 let v = ((row >> (8 * j)) & 0xFF) as u8 as i8;
5232 emit(idx, dl * (v as f32 + delta));
5233 idx += 1;
5234 }
5235 }
5236 }
5237}
5238
5239#[inline]
5241fn for_each_iq2_xxs(block: &[u8], mut emit: impl FnMut(usize, f32)) {
5242 let d = read_f16(block);
5243 let qs: Vec<u16> = block[2..66]
5244 .as_chunks::<2>()
5245 .0
5246 .iter()
5247 .map(|c| u16::from_le_bytes([c[0], c[1]]))
5248 .collect();
5249 let mut idx = 0usize;
5250 for ib32 in 0..8 {
5251 let g = &qs[4 * ib32..4 * ib32 + 4];
5252 let aux32_1 = g[2] as u32 | ((g[3] as u32) << 16);
5253 let db = d * (0.5 + (aux32_1 >> 28) as f32) * 0.25;
5254 let aux8 = [
5255 (g[0] & 0xFF) as usize,
5256 (g[0] >> 8) as usize,
5257 (g[1] & 0xFF) as usize,
5258 (g[1] >> 8) as usize,
5259 ];
5260 for (l, &code) in aux8.iter().enumerate() {
5261 let row = iq_tables::IQ2XXS_GRID[code];
5262 let signs = iq_tables::KSIGNS_IQ2XS[((aux32_1 >> (7 * l)) & 127) as usize];
5263 for j in 0..8 {
5264 let mag = ((row >> (8 * j)) & 0xFF) as f32;
5265 emit(idx, db * mag * iq_sign(signs, j));
5266 idx += 1;
5267 }
5268 }
5269 }
5270}
5271
5272#[inline]
5274fn for_each_iq3_xxs(block: &[u8], mut emit: impl FnMut(usize, f32)) {
5275 let d = read_f16(block);
5276 let qs = &block[2..66];
5277 let sas = &block[66..98];
5278 let mut idx = 0usize;
5279 for ib32 in 0..8 {
5280 let aux32 = u32::from_le_bytes([
5281 sas[4 * ib32],
5282 sas[4 * ib32 + 1],
5283 sas[4 * ib32 + 2],
5284 sas[4 * ib32 + 3],
5285 ]);
5286 let db = d * (0.5 + (aux32 >> 28) as f32) * 0.5;
5287 for l in 0..4 {
5288 let signs = iq_tables::KSIGNS_IQ2XS[((aux32 >> (7 * l)) & 127) as usize];
5289 let g1 = iq_tables::IQ3XXS_GRID[qs[8 * ib32 + 2 * l] as usize];
5290 let g2 = iq_tables::IQ3XXS_GRID[qs[8 * ib32 + 2 * l + 1] as usize];
5291 for j in 0..4 {
5292 emit(
5293 idx + j,
5294 db * ((g1 >> (8 * j)) & 0xFF) as f32 * iq_sign(signs, j),
5295 );
5296 }
5297 for j in 0..4 {
5298 emit(
5299 idx + 4 + j,
5300 db * ((g2 >> (8 * j)) & 0xFF) as f32 * iq_sign(signs, j + 4),
5301 );
5302 }
5303 idx += 8;
5304 }
5305 }
5306}
5307
5308#[inline]
5317fn for_each_iq2_xs(block: &[u8], mut emit: impl FnMut(usize, f32)) {
5318 let d = read_f16(block);
5319 let qs = &block[2..66];
5320 let scales = &block[66..74];
5321 let mut idx = 0usize;
5322 for ib32 in 0..8 {
5323 let db = [
5324 d * (0.5 + (scales[ib32] & 0xF) as f32) * 0.25,
5325 d * (0.5 + (scales[ib32] >> 4) as f32) * 0.25,
5326 ];
5327 for l in 0..4 {
5328 let code = u16::from_le_bytes([qs[8 * ib32 + 2 * l], qs[8 * ib32 + 2 * l + 1]]);
5329 let row = iq_tables::IQ2XS_GRID[(code & 511) as usize];
5330 let signs = iq_tables::KSIGNS_IQ2XS[(code >> 9) as usize];
5331 for j in 0..8 {
5332 let mag = ((row >> (8 * j)) & 0xFF) as f32;
5333 emit(idx, db[l / 2] * mag * iq_sign(signs, j));
5334 idx += 1;
5335 }
5336 }
5337 }
5338}
5339
5340#[inline]
5352fn for_each_iq2_s(block: &[u8], mut emit: impl FnMut(usize, f32)) {
5353 let d = read_f16(block);
5354 let qs = &block[2..34];
5355 let sign_bytes = &block[34..66];
5356 let qh = &block[66..74];
5357 let scales = &block[74..82];
5358 let mut idx = 0usize;
5359 for ib32 in 0..8 {
5360 let db = [
5361 d * (0.5 + (scales[ib32] & 0xF) as f32) * 0.25,
5362 d * (0.5 + (scales[ib32] >> 4) as f32) * 0.25,
5363 ];
5364 for l in 0..4 {
5365 let hi = ((qh[ib32] as usize) << (8 - 2 * l)) & 0x300;
5366 let row = iq_tables::IQ2S_GRID[qs[4 * ib32 + l] as usize | hi];
5367 let signs = sign_bytes[4 * ib32 + l];
5368 for j in 0..8 {
5369 let mag = ((row >> (8 * j)) & 0xFF) as f32;
5370 emit(idx, db[l / 2] * mag * iq_sign(signs, j));
5371 idx += 1;
5372 }
5373 }
5374 }
5375}
5376
5377#[inline]
5392fn for_each_iq3_s(block: &[u8], mut emit: impl FnMut(usize, f32)) {
5393 let d = read_f16(block);
5394 let qs = &block[2..66];
5395 let qh = &block[66..74];
5396 let sign_bytes = &block[74..106];
5397 let scales = &block[106..110];
5398 let mut idx = 0usize;
5399 for ib32 in 0..8 {
5400 let nibble = if ib32 % 2 == 0 {
5401 scales[ib32 / 2] & 0xF
5402 } else {
5403 scales[ib32 / 2] >> 4
5404 };
5405 let db = d * (1.0 + 2.0 * nibble as f32);
5406 for l in 0..4 {
5407 let h = qh[ib32] as usize;
5411 let i1 = qs[8 * ib32 + 2 * l] as usize | ((h << (8 - 2 * l)) & 256);
5412 let i2 = qs[8 * ib32 + 2 * l + 1] as usize | ((h << (7 - 2 * l)) & 256);
5413 let g1 = iq_tables::IQ3S_GRID[i1];
5414 let g2 = iq_tables::IQ3S_GRID[i2];
5415 let signs = sign_bytes[4 * ib32 + l];
5416 for j in 0..4 {
5417 emit(
5418 idx + j,
5419 db * ((g1 >> (8 * j)) & 0xFF) as f32 * iq_sign(signs, j),
5420 );
5421 }
5422 for j in 0..4 {
5423 emit(
5424 idx + 4 + j,
5425 db * ((g2 >> (8 * j)) & 0xFF) as f32 * iq_sign(signs, j + 4),
5426 );
5427 }
5428 idx += 8;
5429 }
5430 }
5431}
5432
5433#[inline]
5447fn for_each_iq1_m(block: &[u8], mut emit: impl FnMut(usize, f32)) {
5448 let qs = &block[0..32];
5449 let qh = &block[32..48];
5450 let scales = &block[48..56];
5451 let sc: [u16; 4] =
5452 std::array::from_fn(|k| u16::from_le_bytes([scales[2 * k], scales[2 * k + 1]]));
5453 let d = f16::from_bits(
5455 (sc[0] >> 12) | ((sc[1] >> 8) & 0x00F0) | ((sc[2] >> 4) & 0x0F00) | (sc[3] & 0xF000),
5456 )
5457 .to_f32();
5458 let mut idx = 0usize;
5459 for ib in 0..8 {
5460 let shift = 6 * (ib % 2);
5461 let dl = [
5462 d * (2.0 * ((sc[ib / 2] >> shift) & 7) as f32 + 1.0),
5463 d * (2.0 * ((sc[ib / 2] >> (shift + 3)) & 7) as f32 + 1.0),
5464 ];
5465 let (h0, h1) = (qh[2 * ib] as usize, qh[2 * ib + 1] as usize);
5466 let grid_idx = [
5469 qs[4 * ib] as usize | ((h0 << 8) & 0x700),
5470 qs[4 * ib + 1] as usize | ((h0 << 4) & 0x700),
5471 qs[4 * ib + 2] as usize | ((h1 << 8) & 0x700),
5472 qs[4 * ib + 3] as usize | ((h1 << 4) & 0x700),
5473 ];
5474 let delta = [
5475 if h0 & 0x08 != 0 {
5476 -IQ1S_DELTA
5477 } else {
5478 IQ1S_DELTA
5479 },
5480 if h0 & 0x80 != 0 {
5481 -IQ1S_DELTA
5482 } else {
5483 IQ1S_DELTA
5484 },
5485 if h1 & 0x08 != 0 {
5486 -IQ1S_DELTA
5487 } else {
5488 IQ1S_DELTA
5489 },
5490 if h1 & 0x80 != 0 {
5491 -IQ1S_DELTA
5492 } else {
5493 IQ1S_DELTA
5494 },
5495 ];
5496 for l in 0..4 {
5497 let row = iq_tables::IQ1S_GRID[grid_idx[l]];
5498 for j in 0..8 {
5499 let v = ((row >> (8 * j)) & 0xFF) as u8 as i8;
5500 emit(idx, dl[l / 2] * (v as f32 + delta[l]));
5501 idx += 1;
5502 }
5503 }
5504 }
5505}
5506
5507macro_rules! iq_dequant_and_dot {
5508 ($dequant:ident, $dot_scalar:ident, $walk:ident, $bytes:ident, $elems:ident) => {
5509 pub fn $dequant(src: &[u8]) -> Result<Vec<f32>, QuantError> {
5510 if !src.len().is_multiple_of($bytes) {
5511 return Err(QuantError::Misaligned(src.len(), $bytes));
5512 }
5513 let n_blocks = src.len() / $bytes;
5514 let mut out = vec![0f32; n_blocks * $elems];
5515 for (b, block) in src.chunks_exact($bytes).enumerate() {
5516 let base = b * $elems;
5517 $walk(block, |i, v| out[base + i] = v);
5518 }
5519 Ok(out)
5520 }
5521
5522 pub fn $dot_scalar(row_bytes: &[u8], x: &[f32]) -> f32 {
5523 debug_assert_eq!(row_bytes.len() % $bytes, 0);
5524 let mut acc = 0f32;
5525 let mut x_base = 0usize;
5526 for block in row_bytes.chunks_exact($bytes) {
5527 $walk(block, |i, v| acc += v * x[x_base + i]);
5528 x_base += $elems;
5529 }
5530 acc
5531 }
5532 };
5533}
5534
5535macro_rules! iq_dispatch {
5541 ($dot:ident, $dot_scalar:ident, $avx2:ident) => {
5542 pub fn $dot(row_bytes: &[u8], x: &[f32]) -> f32 {
5543 #[cfg(target_arch = "x86_64")]
5544 {
5545 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
5546 return unsafe { simd_x86::$avx2(row_bytes, x) };
5547 }
5548 }
5549 $dot_scalar(row_bytes, x)
5550 }
5551 };
5552}
5553
5554iq_dispatch!(dot_iq1_s_f32, dot_iq1_s_f32_scalar, dot_iq1_s_f32_avx2);
5555iq_dispatch!(
5556 dot_iq2_xxs_f32,
5557 dot_iq2_xxs_f32_scalar,
5558 dot_iq2_xxs_f32_avx2
5559);
5560iq_dispatch!(
5561 dot_iq3_xxs_f32,
5562 dot_iq3_xxs_f32_scalar,
5563 dot_iq3_xxs_f32_avx2
5564);
5565
5566pub fn dot_iq2_xs_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
5574 dot_iq2_xs_f32_scalar(row_bytes, x)
5575}
5576
5577pub fn dot_iq2_s_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
5578 dot_iq2_s_f32_scalar(row_bytes, x)
5579}
5580
5581pub fn dot_iq3_s_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
5582 dot_iq3_s_f32_scalar(row_bytes, x)
5583}
5584
5585pub fn dot_iq1_m_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
5586 dot_iq1_m_f32_scalar(row_bytes, x)
5587}
5588
5589pub fn dot_mxfp4_gguf_f32(row_bytes: &[u8], x: &[f32]) -> f32 {
5593 dot_mxfp4_gguf_f32_scalar(row_bytes, x)
5594}
5595
5596iq_dequant_and_dot!(
5597 dequant_iq1_s,
5598 dot_iq1_s_f32_scalar,
5599 for_each_iq1_s,
5600 IQ1_S_BLOCK_BYTES,
5601 IQ1_S_BLOCK_ELEMS
5602);
5603iq_dequant_and_dot!(
5604 dequant_iq2_xxs,
5605 dot_iq2_xxs_f32_scalar,
5606 for_each_iq2_xxs,
5607 IQ2_XXS_BLOCK_BYTES,
5608 IQ2_XXS_BLOCK_ELEMS
5609);
5610iq_dequant_and_dot!(
5611 dequant_iq3_xxs,
5612 dot_iq3_xxs_f32_scalar,
5613 for_each_iq3_xxs,
5614 IQ3_XXS_BLOCK_BYTES,
5615 IQ3_XXS_BLOCK_ELEMS
5616);
5617iq_dequant_and_dot!(
5618 dequant_iq2_xs,
5619 dot_iq2_xs_f32_scalar,
5620 for_each_iq2_xs,
5621 IQ2_XS_BLOCK_BYTES,
5622 IQ2_XS_BLOCK_ELEMS
5623);
5624iq_dequant_and_dot!(
5625 dequant_iq2_s,
5626 dot_iq2_s_f32_scalar,
5627 for_each_iq2_s,
5628 IQ2_S_BLOCK_BYTES,
5629 IQ2_S_BLOCK_ELEMS
5630);
5631iq_dequant_and_dot!(
5632 dequant_iq3_s,
5633 dot_iq3_s_f32_scalar,
5634 for_each_iq3_s,
5635 IQ3_S_BLOCK_BYTES,
5636 IQ3_S_BLOCK_ELEMS
5637);
5638iq_dequant_and_dot!(
5639 dequant_iq1_m,
5640 dot_iq1_m_f32_scalar,
5641 for_each_iq1_m,
5642 IQ1_M_BLOCK_BYTES,
5643 IQ1_M_BLOCK_ELEMS
5644);
5645
5646pub const MXFP4_GGUF_BLOCK_BYTES: usize = 17;
5657pub const MXFP4_GGUF_BLOCK_ELEMS: usize = 32;
5658
5659#[inline]
5662fn for_each_mxfp4_gguf(block: &[u8], mut emit: impl FnMut(usize, f32)) {
5663 let d = e8m0_scale(block[0]);
5664 for (j, &byte) in block[1..17].iter().enumerate() {
5665 emit(j, d * KVALUES_MXFP4[(byte & 0x0F) as usize]);
5666 emit(j + 16, d * KVALUES_MXFP4[(byte >> 4) as usize]);
5667 }
5668}
5669
5670iq_dequant_and_dot!(
5671 dequant_mxfp4_gguf,
5672 dot_mxfp4_gguf_f32_scalar,
5673 for_each_mxfp4_gguf,
5674 MXFP4_GGUF_BLOCK_BYTES,
5675 MXFP4_GGUF_BLOCK_ELEMS
5676);
5677
5678#[cfg(test)]
5679mod tests {
5680 use super::*;
5681
5682 #[test]
5683 fn turbo4_kv_blocks_roundtrip_reasonable() {
5684 let x: Vec<f32> = (0..64).map(|i| (i as f32 * 0.17).sin() * 2.0).collect();
5685 let packed = pack_turbo4_kv_blocks(&x);
5686 assert_eq!(packed.len(), 2 * TURBO4_KV_BLOCK_BYTES);
5687 let y = unpack_turbo4_kv_blocks(&packed).unwrap();
5688 assert_eq!(y.len(), 64);
5689 let mut err = 0.0f32;
5690 for (a, b) in x.iter().zip(y.iter()) {
5691 err += (a - b).abs();
5692 }
5693 err /= x.len() as f32;
5694 assert!(err < 0.2, "mean abs err {err}");
5695 }
5696
5697 #[test]
5698 fn q8_0_roundtrip_is_within_quantization_error() {
5699 let original: Vec<f32> = (0..32).map(|i| (i as f32 - 16.0) * 0.37).collect();
5700 let packed = quantize_q8_0(&original);
5701 assert_eq!(packed.len(), Q8_0_BLOCK_BYTES);
5702 let restored = dequant_q8_0(&packed).unwrap();
5703 assert_eq!(restored.len(), 32);
5704 for (a, b) in original.iter().zip(restored.iter()) {
5705 assert!((a - b).abs() < 0.1, "a={a} b={b}");
5706 }
5707 }
5708
5709 #[test]
5710 fn quantize_activations_q8_reconstructs_within_quant_error() {
5711 let x: Vec<f32> = (0..64)
5712 .map(|i| ((i as f32) * 0.13 - 4.0).sin() * 3.0)
5713 .collect();
5714 let act = quantize_activations_q8(&x);
5715 assert_eq!(act.n_blocks(), 2);
5716 assert_eq!(act.q.len(), 64);
5717 for (b, chunk) in x.as_chunks::<32>().0.iter().enumerate() {
5718 let amax = chunk.iter().fold(0f32, |m, &v| m.max(v.abs()));
5719 let tol = amax / 127.0 + 1e-6;
5720 for (i, &v) in chunk.iter().enumerate() {
5721 let recon = act.q[b * 32 + i] as f32 * act.d[b];
5722 assert!((recon - v).abs() <= tol, "b={b} i={i} v={v} recon={recon}");
5723 }
5724 }
5725 }
5726
5727 #[test]
5728 fn quantize_activations_q8_handles_all_zero_block() {
5729 let act = quantize_activations_q8(&[0f32; 32]);
5730 assert_eq!(act.d[0], 0.0);
5731 assert!(act.q.iter().all(|&q| q == 0));
5732 }
5733
5734 #[test]
5735 fn quantize_activations_q8_parallel_matches_serial() {
5736 let x: Vec<f32> = (0..512)
5737 .map(|i| ((i as f32) * 0.07 - 8.0).sin() * 2.5)
5738 .collect();
5739 let got = quantize_activations_q8(&x);
5740 let n_blocks = x.len() / Q8_0_BLOCK_ELEMS;
5741 let mut q = vec![0i8; n_blocks * Q8_0_BLOCK_ELEMS];
5742 let mut d = vec![0f32; n_blocks];
5743 for (b, chunk) in x.as_chunks::<Q8_0_BLOCK_ELEMS>().0.iter().enumerate() {
5744 let amax = chunk.iter().fold(0f32, |m, &v| m.max(v.abs()));
5745 let scale = amax / 127.0;
5746 let inv = if scale > 0.0 { 1.0 / scale } else { 0.0 };
5747 d[b] = scale;
5748 let base = b * Q8_0_BLOCK_ELEMS;
5749 for (i, &v) in chunk.iter().enumerate() {
5750 let qi = (v * inv).round();
5751 q[base + i] = qi.clamp(-127.0, 127.0) as i8;
5752 }
5753 }
5754 assert_eq!(got.q, q);
5755 assert_eq!(got.d, d);
5756 }
5757
5758 #[test]
5759 fn quantize_activations_q8_k_parallel_matches_serial() {
5760 let x: Vec<f32> = (0..1024)
5761 .map(|i| ((i as f32) * 0.05 - 12.0).cos() * 1.7)
5762 .collect();
5763 let got = quantize_activations_q8_k(&x);
5764 let n_blocks = x.len() / Q4_K_BLOCK_ELEMS;
5765 let mut q = vec![0i8; n_blocks * Q4_K_BLOCK_ELEMS];
5766 let mut d = vec![0f32; n_blocks];
5767 let mut bsums = vec![0i16; n_blocks * 16];
5768 for (b, chunk) in x.as_chunks::<Q4_K_BLOCK_ELEMS>().0.iter().enumerate() {
5769 let amax = chunk.iter().fold(0f32, |m, &v| m.max(v.abs()));
5770 let scale = amax / 127.0;
5771 let inv = if scale > 0.0 { 1.0 / scale } else { 0.0 };
5772 d[b] = scale;
5773 let base = b * Q4_K_BLOCK_ELEMS;
5774 for (i, &v) in chunk.iter().enumerate() {
5775 let qi = (v * inv).round();
5776 q[base + i] = qi.clamp(-127.0, 127.0) as i8;
5777 }
5778 let bsum_base = b * 16;
5779 for g in 0..16 {
5780 let mut s = 0i32;
5781 let off = base + g * 16;
5782 for i in 0..16 {
5783 s += q[off + i] as i32;
5784 }
5785 bsums[bsum_base + g] = s as i16;
5786 }
5787 }
5788 assert_eq!(got.q, q);
5789 assert_eq!(got.d, d);
5790 assert_eq!(got.bsums, bsums);
5791 }
5792
5793 #[test]
5794 fn dot_q4_k_q8_matches_scalar_and_tracks_float_dot() {
5795 let n_blocks = 3;
5796 let cols = n_blocks * Q4_K_BLOCK_ELEMS;
5797 let x: Vec<f32> = (0..cols)
5798 .map(|i| ((i as f32) * 0.017 - 2.1).sin() * 1.8)
5799 .collect();
5800 let mut weights = Vec::with_capacity(n_blocks * Q4_K_BLOCK_BYTES);
5804 for b in 0..n_blocks {
5805 weights.extend_from_slice(&f16::from_f32(0.05 + b as f32 * 0.01).to_le_bytes());
5806 weights.extend_from_slice(&f16::from_f32(0.01 + b as f32 * 0.002).to_le_bytes());
5807 for i in 0..12u8 {
5809 weights.push(20 + i.wrapping_mul(3));
5810 }
5811 for i in 0..128u8 {
5812 weights.push(i.wrapping_mul(17).wrapping_add(b as u8));
5813 }
5814 }
5815 let act = quantize_activations_q8_k(&x);
5816 let dispatched = dot_q4_k_q8(&weights, &act);
5817 let scalar = dot_q4_k_q8_scalar(&weights, &act);
5818 assert_eq!(dispatched, scalar, "dispatch must match scalar");
5819 let float_dot = dot_q4_k_f32(&weights, &x);
5820 let err = (dispatched - float_dot).abs();
5821 let scale = float_dot.abs().max(1.0);
5822 assert!(
5823 err / scale < 0.05,
5824 "int-dot vs f32 relative err {err}/{scale} too large (int={dispatched} f32={float_dot})"
5825 );
5826 }
5827
5828 #[test]
5829 #[cfg(target_arch = "aarch64")]
5830 fn dot_q4_k_q8_i8mm_matches_scalar_when_available() {
5831 if !std::arch::is_aarch64_feature_detected!("i8mm") {
5832 return;
5833 }
5834 let n_blocks = 3;
5835 let cols = n_blocks * Q4_K_BLOCK_ELEMS;
5836 let x: Vec<f32> = (0..cols)
5837 .map(|i| ((i as f32) * 0.017 - 2.1).sin() * 1.8)
5838 .collect();
5839 let mut weights = Vec::with_capacity(n_blocks * Q4_K_BLOCK_BYTES);
5840 for b in 0..n_blocks {
5841 weights.extend_from_slice(&f16::from_f32(0.05 + b as f32 * 0.01).to_le_bytes());
5842 weights.extend_from_slice(&f16::from_f32(0.01 + b as f32 * 0.002).to_le_bytes());
5843 for i in 0..12u8 {
5844 weights.push(20 + i.wrapping_mul(3));
5845 }
5846 for i in 0..128u8 {
5847 weights.push(i.wrapping_mul(17).wrapping_add(b as u8));
5848 }
5849 }
5850 let act = quantize_activations_q8_k(&x);
5851 let scalar = dot_q4_k_q8_scalar(&weights, &act);
5852 let i8mm = unsafe { simd_aarch64::dot_q4_k_q8_neon_i8mm(&weights, &act) };
5853 assert_eq!(i8mm, scalar, "i8mm must match scalar");
5854 let dispatched = dot_q4_k_q8(&weights, &act);
5855 assert_eq!(
5856 dispatched, scalar,
5857 "dispatch must match scalar on i8mm host"
5858 );
5859 }
5860
5861 #[test]
5862 fn dot_q5_k_q8_matches_scalar_and_tracks_float_dot() {
5863 let x: Vec<f32> = (0..Q5_K_BLOCK_ELEMS)
5864 .map(|i| ((i as f32) * 0.013 - 1.7).sin() * 1.5)
5865 .collect();
5866 let act = quantize_activations_q8_k(&x);
5867 let dispatched = dot_q5_k_q8(&Q5_K_TEST_BLOCK, &act);
5868 let scalar = dot_q5_k_q8_scalar(&Q5_K_TEST_BLOCK, &act);
5869 assert_eq!(dispatched, scalar, "dispatch must match scalar");
5870 let float_dot = dot_q5_k_f32(&Q5_K_TEST_BLOCK, &x);
5871 let err = (dispatched - float_dot).abs();
5872 let scale = float_dot.abs().max(1.0);
5873 assert!(
5874 err / scale < 0.05,
5875 "Q5_K int-dot vs f32 relative err {err}/{scale} (int={dispatched} f32={float_dot})"
5876 );
5877 }
5878
5879 #[test]
5880 fn gemm_q5_k_q8_row_matches_per_act_dots() {
5881 let acts: Vec<_> = (0..Q5_K_GEMM_NC)
5882 .map(|j| {
5883 let x: Vec<f32> = (0..Q5_K_BLOCK_ELEMS)
5884 .map(|i| ((i as f32) * 0.013 - 1.7 + j as f32).sin() * 1.5)
5885 .collect();
5886 quantize_activations_q8_k(&x)
5887 })
5888 .collect();
5889 let mut out = vec![0f32; acts.len()];
5890 gemm_q5_k_q8_row(&Q5_K_TEST_BLOCK, &acts, &mut out);
5891 for (j, act) in acts.iter().enumerate() {
5892 let want = dot_q5_k_q8(&Q5_K_TEST_BLOCK, act);
5893 let err = (out[j] - want).abs();
5894 assert!(
5895 err < 1e-4,
5896 "act {j}: gemm {got} vs dot {want}",
5897 got = out[j]
5898 );
5899 }
5900 }
5901
5902 #[test]
5903 fn gemm_q6_k_q8_row_matches_per_act_dots() {
5904 let acts: Vec<_> = (0..Q6_K_GEMM_NC)
5905 .map(|j| {
5906 let x: Vec<f32> = (0..Q6_K_BLOCK_ELEMS)
5907 .map(|i| ((i as f32) * 0.011 - 0.9 + j as f32).cos() * 1.9)
5908 .collect();
5909 quantize_activations_q8_k(&x)
5910 })
5911 .collect();
5912 let mut out = vec![0f32; acts.len()];
5913 gemm_q6_k_q8_row(&Q6_K_TEST_BLOCK, &acts, &mut out);
5914 for (j, act) in acts.iter().enumerate() {
5915 let want = dot_q6_k_q8(&Q6_K_TEST_BLOCK, act);
5916 let err = (out[j] - want).abs();
5917 assert!(
5918 err < 1e-3,
5919 "act {j}: gemm {got} vs dot {want}",
5920 got = out[j]
5921 );
5922 }
5923 }
5924
5925 #[test]
5926 fn dot_q6_k_q8_matches_scalar_and_tracks_float_dot() {
5927 let x: Vec<f32> = (0..Q6_K_BLOCK_ELEMS)
5928 .map(|i| ((i as f32) * 0.011 - 0.9).cos() * 1.9)
5929 .collect();
5930 let act = quantize_activations_q8_k(&x);
5931 let dispatched = dot_q6_k_q8(&Q6_K_TEST_BLOCK, &act);
5932 let scalar = dot_q6_k_q8_scalar(&Q6_K_TEST_BLOCK, &act);
5933 assert_eq!(dispatched, scalar, "dispatch must match scalar");
5934 let float_dot = dot_q6_k_f32(&Q6_K_TEST_BLOCK, &x);
5935 let err = (dispatched - float_dot).abs();
5936 let scale = float_dot.abs().max(1.0);
5937 assert!(
5938 err / scale < 0.05,
5939 "Q6_K int-dot vs f32 relative err {err}/{scale} (int={dispatched} f32={float_dot})"
5940 );
5941 }
5942
5943 #[test]
5944 fn dot_q8_0_q8_dispatch_matches_scalar_and_float_dot() {
5945 let n_blocks = 5;
5948 let cols = n_blocks * Q8_0_BLOCK_ELEMS;
5949 let x: Vec<f32> = (0..cols)
5950 .map(|i| ((i as f32) * 0.019 - 1.3).cos() * 2.7)
5951 .collect();
5952
5953 let mut weights = Vec::with_capacity(n_blocks * Q8_0_BLOCK_BYTES);
5954 for b in 0..n_blocks {
5955 weights.extend_from_slice(&f16::from_f32(0.021 + b as f32 * 0.004).to_le_bytes());
5956 for i in 0..Q8_0_BLOCK_ELEMS {
5957 weights.push(((i as i32 * 7 + b as i32 * 3) % 255 - 127) as i8 as u8);
5958 }
5959 }
5960
5961 let act = quantize_activations_q8(&x);
5962 let dispatched = dot_q8_0_q8(&weights, &act);
5963 let scalar = dot_q8_0_q8_scalar(&weights, &act);
5964 assert_eq!(
5965 dispatched.to_bits(),
5966 scalar.to_bits(),
5967 "SIMD int dot must match scalar int dot bit-for-bit"
5968 );
5969
5970 let float_dot = dot_q8_0_f32(&weights, &x);
5971 let rel = (dispatched - float_dot).abs() / float_dot.abs().max(1e-6);
5974 assert!(
5975 rel < 0.02,
5976 "int dot {dispatched} vs float {float_dot} rel={rel}"
5977 );
5978 }
5979
5980 #[test]
5981 fn dot_q4_0_q8_dispatch_matches_scalar_and_float_dot() {
5982 let n_blocks = 5;
5983 let cols = n_blocks * Q4_0_BLOCK_ELEMS;
5984 let x: Vec<f32> = (0..cols)
5985 .map(|i| ((i as f32) * 0.019 - 1.3).cos() * 2.7)
5986 .collect();
5987
5988 let mut weights = Vec::with_capacity(n_blocks * Q4_0_BLOCK_BYTES);
5989 for b in 0..n_blocks {
5990 weights.extend_from_slice(&f16::from_f32(0.021 + b as f32 * 0.004).to_le_bytes());
5991 for i in 0..16 {
5992 weights.push(((i as u32 * 13 + b as u32 * 7) % 256) as u8);
5993 }
5994 }
5995
5996 let act = quantize_activations_q8(&x);
5997 let dispatched = dot_q4_0_q8(&weights, &act);
5998 let scalar = dot_q4_0_q8_scalar(&weights, &act);
5999 assert_eq!(
6000 dispatched.to_bits(),
6001 scalar.to_bits(),
6002 "SIMD Q4_0 int dot must match scalar bit-for-bit"
6003 );
6004
6005 let float_dot = dot_q4_0_f32(&weights, &x);
6006 let rel = (dispatched - float_dot).abs() / float_dot.abs().max(1e-6);
6007 assert!(
6008 rel < 0.03,
6009 "Q4_0 int dot {dispatched} vs float {float_dot} rel={rel}"
6010 );
6011 }
6012
6013 #[test]
6014 fn q4_0_zero_nibble_maps_to_negative_bias() {
6015 let mut block = Vec::new();
6017 block.extend_from_slice(&f16::from_f32(1.0).to_le_bytes());
6018 block.extend_from_slice(&[0u8; 16]); let out = dequant_q4_0(&block).unwrap();
6020 assert_eq!(out.len(), 32);
6021 assert!(out.iter().all(|&v| v == -8.0));
6022 }
6023
6024 #[test]
6025 fn rejects_misaligned_buffers() {
6026 let bad = vec![0u8; 5];
6027 assert!(dequant_q8_0(&bad).is_err());
6028 assert!(dequant_q4_0(&bad).is_err());
6029 }
6030
6031 #[test]
6032 fn q4_1_affine_nibble_maps_to_scale_plus_min() {
6033 let mut block = Vec::new();
6036 block.extend_from_slice(&f16::from_f32(2.0).to_le_bytes());
6037 block.extend_from_slice(&f16::from_f32(5.0).to_le_bytes());
6038 block.extend_from_slice(&[0x11u8; 16]); let out = dequant_q4_1(&block).unwrap();
6040 assert_eq!(out.len(), 32);
6041 assert!(out.iter().all(|&v| (v - 7.0).abs() < 1e-6));
6042 }
6043
6044 #[test]
6045 fn q5_0_fifth_bit_extends_range_past_a_plain_nibble() {
6046 let mut block = Vec::new();
6055 block.extend_from_slice(&f16::from_f32(1.0).to_le_bytes());
6056 let mut qh = [0u8; 4];
6057 qh[0] |= 1 << 0; block.extend_from_slice(&qh);
6059 let mut qs = [0u8; 16];
6060 qs[0] = 0x0F; block.extend_from_slice(&qs);
6062 let out = dequant_q5_0(&block).unwrap();
6063 assert_eq!(out.len(), 32);
6064 assert_eq!(out[0], 15.0);
6066 assert_eq!(out[1], -16.0);
6068 }
6069
6070 #[test]
6071 fn q5_1_fifth_bit_without_bias_subtraction() {
6072 let mut block = Vec::new();
6073 block.extend_from_slice(&f16::from_f32(1.0).to_le_bytes());
6074 block.extend_from_slice(&f16::from_f32(0.0).to_le_bytes());
6075 let mut qh = [0u8; 4];
6076 qh[0] |= 1 << 0;
6077 block.extend_from_slice(&qh);
6078 let mut qs = [0u8; 16];
6079 qs[0] = 0x0F;
6080 block.extend_from_slice(&qs);
6081 let out = dequant_q5_1(&block).unwrap();
6082 assert_eq!(out.len(), 32);
6083 assert_eq!(out[0], 31.0);
6085 assert_eq!(out[1], 0.0);
6086 }
6087
6088 #[test]
6089 fn q8_1_matches_q8_0_math_ignoring_the_extra_sum_field() {
6090 let mut block = Vec::new();
6091 block.extend_from_slice(&f16::from_f32(0.5).to_le_bytes());
6092 block.extend_from_slice(&f16::from_f32(999.0).to_le_bytes()); let qs: Vec<i8> = (0..32).map(|i| i - 16).collect();
6094 block.extend_from_slice(&i8_to_u8_bytes(&qs));
6095 let out = dequant_q8_1(&block).unwrap();
6096 assert_eq!(out.len(), 32);
6097 for (i, &v) in out.iter().enumerate() {
6098 assert_eq!(v, (i as f32 - 16.0) * 0.5);
6099 }
6100 }
6101
6102 fn i8_to_u8_bytes(src: &[i8]) -> Vec<u8> {
6106 src.iter().map(|&b| b as u8).collect()
6107 }
6108
6109 #[test]
6110 fn legacy_formats_fused_dot_matches_dequant_then_dot() {
6111 let x: Vec<f32> = (0..32).map(|i| ((i as f32) * 0.07).sin()).collect();
6112
6113 let mut q4_1 = Vec::new();
6114 q4_1.extend_from_slice(&f16::from_f32(0.3).to_le_bytes());
6115 q4_1.extend_from_slice(&f16::from_f32(-1.2).to_le_bytes());
6116 q4_1.extend_from_slice(
6117 &(0..16)
6118 .map(|i| (i as u8) | ((15 - i as u8) << 4))
6119 .collect::<Vec<u8>>(),
6120 );
6121 let expected: f32 = dequant_q4_1(&q4_1)
6122 .unwrap()
6123 .iter()
6124 .zip(x.iter())
6125 .map(|(a, b)| a * b)
6126 .sum();
6127 let fused = dot_q4_1_f32(&q4_1, &x);
6128 assert!(
6129 (fused - expected).abs() < 1e-3,
6130 "Q4_1: fused={fused} expected={expected}"
6131 );
6132
6133 let mut q5_0 = Vec::new();
6134 q5_0.extend_from_slice(&f16::from_f32(0.4).to_le_bytes());
6135 q5_0.extend_from_slice(&[0xA5, 0x3C, 0x00, 0xFF]);
6136 q5_0.extend_from_slice(
6137 &(0..16)
6138 .map(|i| (i as u8) | ((15 - i as u8) << 4))
6139 .collect::<Vec<u8>>(),
6140 );
6141 let expected: f32 = dequant_q5_0(&q5_0)
6142 .unwrap()
6143 .iter()
6144 .zip(x.iter())
6145 .map(|(a, b)| a * b)
6146 .sum();
6147 let fused = dot_q5_0_f32(&q5_0, &x);
6148 assert!(
6149 (fused - expected).abs() < 1e-3,
6150 "Q5_0: fused={fused} expected={expected}"
6151 );
6152
6153 let mut q5_1 = Vec::new();
6154 q5_1.extend_from_slice(&f16::from_f32(0.2).to_le_bytes());
6155 q5_1.extend_from_slice(&f16::from_f32(0.9).to_le_bytes());
6156 q5_1.extend_from_slice(&[0x12, 0x34, 0x56, 0x78]);
6157 q5_1.extend_from_slice(
6158 &(0..16)
6159 .map(|i| (i as u8) | ((15 - i as u8) << 4))
6160 .collect::<Vec<u8>>(),
6161 );
6162 let expected: f32 = dequant_q5_1(&q5_1)
6163 .unwrap()
6164 .iter()
6165 .zip(x.iter())
6166 .map(|(a, b)| a * b)
6167 .sum();
6168 let fused = dot_q5_1_f32(&q5_1, &x);
6169 assert!(
6170 (fused - expected).abs() < 1e-3,
6171 "Q5_1: fused={fused} expected={expected}"
6172 );
6173
6174 let mut q8_1 = Vec::new();
6175 q8_1.extend_from_slice(&f16::from_f32(0.6).to_le_bytes());
6176 q8_1.extend_from_slice(&f16::from_f32(0.0).to_le_bytes());
6177 let qs: Vec<i8> = (0..32).map(|i| ((i * 7) % 61) as i8 - 30).collect();
6178 q8_1.extend_from_slice(&i8_to_u8_bytes(&qs));
6179 let expected: f32 = dequant_q8_1(&q8_1)
6180 .unwrap()
6181 .iter()
6182 .zip(x.iter())
6183 .map(|(a, b)| a * b)
6184 .sum();
6185 let fused = dot_q8_1_f32(&q8_1, &x);
6186 assert!(
6187 (fused - expected).abs() < 1e-3,
6188 "Q8_1: fused={fused} expected={expected}"
6189 );
6190 }
6191
6192 #[test]
6193 fn legacy_formats_reject_misaligned_buffers() {
6194 let bad = vec![0u8; 5];
6195 assert!(dequant_q4_1(&bad).is_err());
6196 assert!(dequant_q5_0(&bad).is_err());
6197 assert!(dequant_q5_1(&bad).is_err());
6198 assert!(dequant_q8_1(&bad).is_err());
6199 }
6200
6201 #[test]
6202 fn bf16_widening_is_exact_for_round_values() {
6203 for v in [0.0f32, 1.0, -1.0, 2.5, -0.5, 100.0, -100.0] {
6207 let bf16_bits = (v.to_bits() >> 16) as u16;
6208 let bytes = bf16_bits.to_le_bytes();
6209 let restored = dequant_bf16(&bytes).unwrap();
6210 assert_eq!(restored, vec![v], "bf16 round-trip mismatch for {v}");
6211 }
6212 }
6213
6214 #[test]
6215 fn bf16_widening_matches_hand_computed_bits() {
6216 let bytes = 0x3F80u16.to_le_bytes();
6219 let out = dequant_bf16(&bytes).unwrap();
6220 assert_eq!(out, vec![1.0f32]);
6221 assert_eq!(out[0].to_bits(), 0x3F800000);
6222 }
6223
6224 #[test]
6225 fn bf16_rejects_odd_length_buffers() {
6226 let bad = vec![0u8; 3];
6227 assert!(dequant_bf16(&bad).is_err());
6228 }
6229
6230 #[test]
6231 fn f16_widening_is_exact_and_covers_the_special_values() {
6232 let subnormal = f16::from_bits(0x0001); let cases: Vec<f16> = [0.0f32, -0.0, 1.0, -1.0, 2.5, -0.5, 65504.0, -65504.0]
6237 .iter()
6238 .map(|&v| f16::from_f32(v))
6239 .chain(std::iter::once(subnormal))
6240 .collect();
6241 let bytes: Vec<u8> = cases.iter().flat_map(|h| h.to_le_bytes()).collect();
6242 let out = dequant_f16(&bytes).unwrap();
6243 assert_eq!(out.len(), cases.len());
6244 for (got, want) in out.iter().zip(cases.iter()) {
6245 assert_eq!(got.to_bits(), want.to_f32().to_bits());
6246 }
6247 assert_eq!(out[8], 2f32.powi(-24));
6248
6249 let inf = f16::INFINITY.to_le_bytes();
6251 assert!(dequant_f16(&inf).unwrap()[0].is_infinite());
6252 }
6253
6254 #[test]
6255 fn f16_rejects_odd_length_buffers() {
6256 let bad = vec![0u8; 5];
6257 assert!(dequant_f16(&bad).is_err());
6258 }
6259
6260 #[test]
6261 fn fused_q8_0_dot_matches_dequant_then_dot() {
6262 let original: Vec<f32> = (0..32).map(|i| (i as f32 - 16.0) * 0.37).collect();
6263 let packed = quantize_q8_0(&original);
6264 let x: Vec<f32> = (0..32).map(|i| (i as f32) * 0.01 - 0.16).collect();
6265
6266 let dequanted = dequant_q8_0(&packed).unwrap();
6267 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
6268
6269 let fused = dot_q8_0_f32(&packed, &x);
6270 assert!(
6271 (fused - expected).abs() < 1e-3,
6272 "fused={fused} expected={expected}"
6273 );
6274 }
6275
6276 #[test]
6277 fn dispatched_dot_matches_scalar_reference_across_many_blocks() {
6278 let n_blocks = 5;
6283 let original: Vec<f32> = (0..n_blocks * 32)
6284 .map(|i| ((i as f32) - (n_blocks * 16) as f32) * 0.29)
6285 .collect();
6286 let packed = quantize_q8_0(&original);
6287 let x: Vec<f32> = (0..n_blocks * 32)
6288 .map(|i| ((i as f32) * 0.013).sin())
6289 .collect();
6290
6291 let dispatched = dot_q8_0_f32(&packed, &x);
6292 let scalar = dot_q8_0_f32_scalar(&packed, &x);
6293 assert!(
6294 (dispatched - scalar).abs() < 1e-2,
6295 "dispatched={dispatched} scalar={scalar} (should match regardless of which SIMD path the host CPU takes)"
6296 );
6297 }
6298
6299 #[cfg(target_arch = "x86_64")]
6300 #[test]
6301 fn avx2_kernel_matches_scalar_directly_when_available() {
6302 if !is_x86_feature_detected!("avx2") || !is_x86_feature_detected!("fma") {
6303 eprintln!("skipping: host CPU lacks AVX2/FMA");
6304 return;
6305 }
6306 let n_blocks = 8;
6307 let original: Vec<f32> = (0..n_blocks * 32)
6308 .map(|i| ((i % 37) as f32 - 18.0) * 0.11)
6309 .collect();
6310 let packed = quantize_q8_0(&original);
6311 let x: Vec<f32> = (0..n_blocks * 32)
6312 .map(|i| ((i as f32) * 0.07).cos())
6313 .collect();
6314
6315 let simd = unsafe { simd_x86::dot_q8_0_f32_avx2(&packed, &x) };
6316 let scalar = dot_q8_0_f32_scalar(&packed, &x);
6317 assert!(
6318 (simd - scalar).abs() < 1e-2,
6319 "AVX2 kernel diverged from scalar: simd={simd} scalar={scalar}"
6320 );
6321 }
6322
6323 #[cfg(target_arch = "x86_64")]
6324 #[test]
6325 fn avx2_q4_0_kernel_matches_scalar_directly_when_available() {
6326 if !is_x86_feature_detected!("avx2") || !is_x86_feature_detected!("fma") {
6327 eprintln!("skipping: host CPU lacks AVX2/FMA");
6328 return;
6329 }
6330 let n_blocks = 6;
6335 let mut packed = Vec::new();
6336 for b in 0..n_blocks {
6337 packed.extend_from_slice(&half::f16::from_f32(0.05 + b as f32 * 0.01).to_le_bytes());
6338 for i in 0..16u8 {
6339 let lo = (i + b as u8) % 16;
6340 let hi = (15 - i + b as u8) % 16;
6341 packed.push(lo | (hi << 4));
6342 }
6343 }
6344 let x: Vec<f32> = (0..n_blocks * 32)
6345 .map(|i| ((i as f32) * 0.09).sin())
6346 .collect();
6347
6348 let simd = unsafe { simd_x86::dot_q4_0_f32_avx2(&packed, &x) };
6349 let scalar = dot_q4_0_f32_scalar(&packed, &x);
6350 assert!(
6351 (simd - scalar).abs() < 1e-2,
6352 "AVX2 Q4_0 kernel diverged from scalar: simd={simd} scalar={scalar}"
6353 );
6354 }
6355
6356 #[cfg(target_arch = "aarch64")]
6357 #[test]
6358 fn neon_kernel_matches_scalar_directly_when_available() {
6359 if !std::arch::is_aarch64_feature_detected!("neon") {
6360 eprintln!("skipping: host CPU lacks NEON (unexpected on real aarch64 hardware)");
6361 return;
6362 }
6363 let n_blocks = 8;
6364 let original: Vec<f32> = (0..n_blocks * 32)
6365 .map(|i| ((i % 37) as f32 - 18.0) * 0.11)
6366 .collect();
6367 let packed = quantize_q8_0(&original);
6368 let x: Vec<f32> = (0..n_blocks * 32)
6369 .map(|i| ((i as f32) * 0.07).cos())
6370 .collect();
6371
6372 let simd = unsafe { simd_aarch64::dot_q8_0_f32_neon(&packed, &x) };
6373 let scalar = dot_q8_0_f32_scalar(&packed, &x);
6374 assert!(
6375 (simd - scalar).abs() < 1e-2,
6376 "NEON kernel diverged from scalar: simd={simd} scalar={scalar}"
6377 );
6378 }
6379
6380 #[cfg(target_arch = "aarch64")]
6381 #[test]
6382 fn neon_q4_0_kernel_matches_scalar_directly_when_available() {
6383 if !std::arch::is_aarch64_feature_detected!("neon") {
6384 eprintln!("skipping: host CPU lacks NEON (unexpected on real aarch64 hardware)");
6385 return;
6386 }
6387 let n_blocks = 6;
6392 let mut packed = Vec::new();
6393 for b in 0..n_blocks {
6394 packed.extend_from_slice(&half::f16::from_f32(0.05 + b as f32 * 0.01).to_le_bytes());
6395 for i in 0..16u8 {
6396 let lo = (i + b as u8) % 16;
6397 let hi = (15 - i + b as u8) % 16;
6398 packed.push(lo | (hi << 4));
6399 }
6400 }
6401 let x: Vec<f32> = (0..n_blocks * 32)
6402 .map(|i| ((i as f32) * 0.09).sin())
6403 .collect();
6404
6405 let simd = unsafe { simd_aarch64::dot_q4_0_f32_neon(&packed, &x) };
6406 let scalar = dot_q4_0_f32_scalar(&packed, &x);
6407 assert!(
6408 (simd - scalar).abs() < 1e-2,
6409 "NEON Q4_0 kernel diverged from scalar: simd={simd} scalar={scalar}"
6410 );
6411 }
6412
6413 #[test]
6414 fn dispatched_q4_0_matches_scalar_reference() {
6415 let n_blocks = 4;
6416 let mut packed = Vec::new();
6417 for b in 0..n_blocks {
6418 packed.extend_from_slice(&half::f16::from_f32(0.2).to_le_bytes());
6419 for i in 0..16u8 {
6420 packed.push((i % 16) | (((15 - i + b as u8) % 16) << 4));
6421 }
6422 }
6423 let x: Vec<f32> = (0..n_blocks * 32)
6424 .map(|i| (i as f32) * 0.02 - 1.0)
6425 .collect();
6426
6427 let dispatched = dot_q4_0_f32(&packed, &x);
6428 let scalar = dot_q4_0_f32_scalar(&packed, &x);
6429 assert!(
6430 (dispatched - scalar).abs() < 1e-2,
6431 "dispatched={dispatched} scalar={scalar}"
6432 );
6433 }
6434
6435 #[test]
6436 fn fused_q4_0_dot_matches_dequant_then_dot() {
6437 let mut block = Vec::new();
6438 block.extend_from_slice(&f16::from_f32(1.0).to_le_bytes());
6439 block.extend_from_slice(&[0x12u8; 16]); let x: Vec<f32> = (0..32).map(|i| (i as f32) * 0.1).collect();
6441
6442 let dequanted = dequant_q4_0(&block).unwrap();
6443 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
6444 let fused = dot_q4_0_f32(&block, &x);
6445 assert!(
6446 (fused - expected).abs() < 1e-3,
6447 "fused={fused} expected={expected}"
6448 );
6449 }
6450
6451 const Q4_K_TEST_BLOCK: [u8; 144] = [
6457 0x66, 0x2a, 0x66, 0x2a, 0x02, 0x02, 0x02, 0x02, 0x4f, 0x4b, 0x10, 0x12, 0x42, 0xe4, 0xc1,
6458 0xb2, 0x64, 0xa8, 0x70, 0x2d, 0x6a, 0xa6, 0x76, 0x79, 0xa6, 0xf7, 0x5a, 0xda, 0x37, 0x87,
6459 0x38, 0xd5, 0xf9, 0xfa, 0xc2, 0x98, 0x33, 0x94, 0x48, 0x59, 0x46, 0x73, 0xb2, 0x3b, 0x28,
6460 0x18, 0x2e, 0x02, 0xe4, 0x5d, 0x86, 0xa9, 0x93, 0x39, 0x51, 0x75, 0x5f, 0xb6, 0xac, 0x0a,
6461 0x17, 0x35, 0x8d, 0xf7, 0x97, 0x7a, 0x95, 0xf5, 0x51, 0xc9, 0xdd, 0xb8, 0xdf, 0x7a, 0x69,
6462 0xdb, 0xcb, 0xfe, 0xa6, 0xf0, 0x69, 0xf6, 0xf2, 0xc6, 0xad, 0xb4, 0x68, 0x9f, 0xad, 0x7f,
6463 0xd6, 0x40, 0x8f, 0x14, 0xca, 0xdb, 0xa9, 0x7d, 0x89, 0xb6, 0xad, 0x96, 0xa9, 0x69, 0x96,
6464 0xaa, 0x98, 0x79, 0x06, 0x9a, 0x86, 0x74, 0xff, 0xde, 0x8e, 0xf0, 0xf0, 0x3f, 0xcd, 0xdd,
6465 0x7d, 0x7f, 0x0c, 0x3d, 0x0e, 0x7f, 0x88, 0x8f, 0xf7, 0x95, 0x83, 0x13, 0x11, 0x85, 0x55,
6466 0x0c, 0x5c, 0x7b, 0x9e, 0x51, 0x48, 0x69, 0x67, 0x1e,
6467 ];
6468 const Q4_K_GOLDEN: [f32; 256] = [
6469 -0.349915, 0.0499878, -0.749817, 0.549866, 0.249939, -0.149963, -0.149963, 0.149963,
6470 -0.149963, -0.0499878, 0.249939, 0.249939, -0.0499878, -0.0499878, 0.0499878, -0.249939,
6471 0.149963, 0.249939, -0.549866, 0.0499878, -0.44989, -0.349915, 0.0499878, 0.149963,
6472 -0.149963, -0.44989, -0.549866, 0.349915, 0.0499878, 0.0499878, 0.649841, -0.549866,
6473 0.0499878, 0.44989, 0.149963, -0.349915, 0.0499878, 0.44989, 0.149963, 0.149963, 0.44989,
6474 0.949768, -0.0499878, 0.749817, -0.249939, 0.249939, -0.249939, 0.749817, 0.949768,
6475 0.949768, 0.649841, 0.349915, -0.249939, 0.349915, -0.149963, -0.0499878, -0.149963,
6476 0.149963, 0.549866, -0.249939, -0.349915, -0.44989, -0.349915, -0.549866, -0.399902,
6477 0.499878, -0.199951, 0.0999756, -0.499878, 0.0999756, -0.699829, -0.299927, 0.699829,
6478 -0.199951, 0.399902, 0.199951, -0.0999756, -0.299927, 0.499878, -0.0999756, -0.0999756,
6479 0.199951, -0.299927, -0.299927, -0.699829, 0.0999756, 0.499878, 0.0, 0.699829, 0.199951,
6480 0.0999756, 0.299927, 0.299927, 0.599854, -0.199951, -0.799805, 0.499878, -0.399902,
6481 -0.0999756, 0.0999756, 0.0, -0.599854, -0.399902, -0.199951, -0.399902, 0.199951,
6482 0.0999756, -0.89978, -0.799805, -0.599854, -0.0999756, 0.599854, 0.0, -0.199951, 0.0,
6483 0.599854, -0.399902, 0.299927, 0.399902, 0.199951, 0.399902, -0.199951, -0.299927,
6484 0.399902, 0.299927, 0.599854, 0.0999756, 0.599854, -0.0999756, -0.399902, -0.799805,
6485 -0.399902, 0.299927, -0.599854, -0.199951, 0.499878, 0.299927, 0.499878, -0.399902,
6486 -0.999756, 0.499878, -0.599854, 0.0, 0.0999756, -0.0999756, 0.299927, -0.0999756,
6487 -0.399902, 0.299927, -0.399902, -0.0999756, -0.0999756, -0.399902, 0.0, -0.199951,
6488 -0.0999756, -0.399902, 0.0, -0.399902, -0.599854, -0.299927, 1.49963, 1.49963, 0.89978,
6489 0.499878, 0.699829, -0.299927, 0.299927, 0.499878, -0.0999756, 1.09973, -0.699829,
6490 0.0999756, -1.29968, 0.89978, 1.09973, 0.499878, -0.0999756, 0.0999756, 0.699829, 0.499878,
6491 0.299927, 0.499878, -0.299927, 0.299927, 0.499878, 0.299927, -0.0999756, -1.49963,
6492 0.299927, 0.0999756, -0.0999756, 0.149963, 0.0999756, 0.0999756, -0.599854, -0.599854,
6493 0.149963, 0.0499878, 0.0499878, 0.0499878, 0.149963, 0.0, 0.0499878, 0.0999756, 0.149963,
6494 -0.199951, 0.149963, -0.249939, -0.349915, -0.44989, -0.44989, -0.549866, -0.349915,
6495 -0.349915, 0.0, 0.0, -0.0499878, 0.0999756, -0.549866, -0.199951, -0.149963, -0.249939,
6496 0.0999756, 0.949768, 0.749817, 0.249939, 0.949768, 0.949768, -0.249939, 0.649841, 0.749817,
6497 0.149963, 0.149963, -0.549866, -0.249939, -0.549866, 0.149963, 0.249939, 0.249939,
6498 0.949768, 0.349915, 0.249939, -0.44989, -0.44989, 0.249939, -0.0499878, -0.549866,
6499 -0.0499878, 0.149963, 0.349915, -0.0499878, -0.149963, 0.0499878, 0.0499878, -0.44989,
6500 ];
6501
6502 #[rustfmt::skip]
6504 const Q5_K_TEST_BLOCK: [u8; 176] = [
6505 0x66, 0x2a, 0x66, 0x2a, 0x01, 0x01, 0x01, 0x01, 0x4f, 0x4b, 0x10, 0x12, 0x41, 0xe2, 0xc1,
6506 0xb1, 0x72, 0x2f, 0x20, 0x07, 0x31, 0x0c, 0x38, 0xb3, 0x9c, 0xb8, 0xad, 0x2f, 0x9a, 0xea,
6507 0x17, 0xd0, 0xee, 0x93, 0x9e, 0x3e, 0x74, 0xbb, 0x28, 0x18, 0x39, 0x25, 0xb6, 0x09, 0x18,
6508 0x29, 0x1c, 0x1d, 0x29, 0x41, 0x40, 0x0a, 0x74, 0x7d, 0xfd, 0x21, 0xdd, 0x6d, 0x45, 0x73,
6509 0x0e, 0x1e, 0xc0, 0x4a, 0xfc, 0xf3, 0x8e, 0x24, 0x6b, 0x34, 0x7d, 0xbe, 0x94, 0xde, 0x59,
6510 0x7a, 0x35, 0x30, 0x36, 0x0a, 0xf9, 0x4a, 0x9b, 0xa2, 0x26, 0x21, 0xa2, 0xfa, 0xdf, 0x4b,
6511 0x29, 0x64, 0x6f, 0xbb, 0xca, 0x0f, 0x3c, 0xda, 0x20, 0xf4, 0x93, 0x86, 0xab, 0x6e, 0xb9,
6512 0xe5, 0xd5, 0xa0, 0x82, 0xd6, 0x41, 0xff, 0x12, 0xbc, 0x34, 0xbb, 0xab, 0xb8, 0x20, 0x2f,
6513 0xbb, 0x5f, 0x0c, 0x10, 0xcf, 0x49, 0xc5, 0x86, 0x5c, 0xdf, 0xff, 0x78, 0x44, 0x26, 0x3b,
6514 0xc2, 0x23, 0x3d, 0x2b, 0xe9, 0x00, 0x12, 0xf8, 0xea, 0xe2, 0x9e, 0x5e, 0x50, 0x20, 0x9f,
6515 0x9d, 0x8d, 0x7d, 0x7f, 0xcc, 0x1d, 0x0e, 0x13, 0xf8, 0xc2, 0xf1, 0x3d, 0x08, 0x2f, 0x23,
6516 0x13, 0xac, 0x0d, 0xa7, 0xe7, 0x20, 0xa3, 0x90, 0xb7, 0xc8, 0x28,
6517 ];
6518 const Q5_K_GOLDEN: [f32; 256] = [
6519 -0.299927, 0.0999756, -0.749817, 0.549866, 0.249939, -0.0999756, -0.0999756, 0.0999756,
6520 -0.0999756, -0.0999756, 0.299927, 0.199951, -0.0499878, -0.0499878, 0.0499878, -0.249939,
6521 -0.149963, 0.199951, -0.0499878, -0.549866, -0.199951, 0.249939, -0.0999756, -0.0499878,
6522 0.249939, 0.749817, -0.299927, 0.549866, -0.499878, 0.0499878, -0.44989, 0.549866,
6523 0.349915, 0.44989, -0.349915, 0.249939, -0.199951, -0.199951, 0.199951, 0.349915,
6524 0.0999756, -0.249939, -0.349915, 0.599854, 0.249939, 0.299927, 0.849792, -0.349915,
6525 0.999756, 0.999756, 0.649841, 0.349915, -0.249939, 0.399902, -0.199951, 0.0, -0.0999756,
6526 0.0999756, 0.499878, -0.199951, -0.399902, -0.399902, -0.399902, -0.549866, -0.349915,
6527 0.499878, -0.249939, 0.0999756, -0.499878, 0.0499878, -0.699829, -0.299927, 0.749817,
6528 -0.249939, 0.44989, 0.199951, -0.0499878, -0.249939, 0.499878, -0.0499878, 0.599854,
6529 -0.299927, 0.0, 0.199951, 0.149963, -0.499878, -0.249939, -0.0999756, -0.349915, 0.249939,
6530 0.249939, -0.799805, -0.699829, -0.499878, 0.0499878, 0.749817, -0.149963, 0.0999756,
6531 -0.44989, -0.399902, -0.799805, 0.0, 0.399902, -0.149963, 0.549866, 0.0999756, 0.0,
6532 0.199951, 0.199951, 0.44989, -0.299927, -0.89978, 0.0499878, -0.249939, 0.0, 0.649841,
6533 -0.44989, 0.299927, 0.399902, 0.199951, 0.44989, -0.199951, -0.249939, 0.399902, 0.299927,
6534 0.549866, 0.0999756, 0.649841, -0.0999756, -0.399902, -0.799805, -0.44989, 0.349915,
6535 -0.599854, -0.199951, 0.549866, 0.349915, 0.549866, -0.399902, -0.999756, 0.549866,
6536 -0.549866, 0.0499878, 0.0999756, -0.399902, 0.549866, 0.549866, 0.199951, 0.0, 0.0999756,
6537 -0.44989, -0.0999756, -0.0499878, -0.349915, 0.349915, -0.549866, -0.199951, -0.89978,
6538 0.199951, 0.299927, 0.199951, 1.19971, 0.399902, -0.399902, 1.09973, -0.399902, 0.299927,
6539 0.299927, -0.399902, 0.599854, 0.0999756, 0.199951, -0.299927, 0.499878, -0.299927,
6540 -0.699829, 0.599854, -0.199951, 0.0, 0.799805, 0.499878, 0.299927, 0.399902, -0.299927,
6541 0.299927, 0.399902, 0.299927, -0.0999756, -1.49963, 0.199951, 0.0, -0.0999756, 0.299927,
6542 0.0999756, 0.0999756, -0.599854, -0.599854, 0.149963, 0.0499878, 0.0499878, 0.0499878,
6543 0.149963, 0.0, 0.0499878, 0.0999756, 0.349915, -0.199951, 0.299927, 0.249939, 0.0499878,
6544 -0.199951, 0.149963, 0.349915, -0.44989, 0.0, 0.0499878, -0.249939, -0.249939, -0.599854,
6545 -0.44989, -0.599854, -0.249939, -0.199951, -0.199951, 0.149963, -0.0999756, -0.299927,
6546 -0.299927, -0.44989, -0.0999756, -0.0999756, 0.649841, 0.599854, 0.599854, 0.849792,
6547 -0.499878, 0.249939, 0.299927, 0.199951, 0.849792, 0.999756, 0.399902, 0.249939, -0.44989,
6548 -0.44989, 0.299927, -0.0499878, -0.549866, -0.0499878, 0.149963, 0.349915, -0.0499878,
6549 -0.0999756, 0.0, 0.0499878, -0.44989,
6550 ];
6551
6552 #[test]
6553 fn q5_k_dequant_matches_independent_python_reference() {
6554 let got = dequant_q5_k(&Q5_K_TEST_BLOCK).unwrap();
6555 assert_eq!(got.len(), Q5_K_GOLDEN.len());
6556 for (i, (a, b)) in got.iter().zip(Q5_K_GOLDEN.iter()).enumerate() {
6557 assert!(
6558 (a - b).abs() < 1e-3,
6559 "Q5_K element {i}: rust={a} python={b}"
6560 );
6561 }
6562 }
6563
6564 #[test]
6565 fn q5_k_fused_dot_matches_dequant_then_dot() {
6566 let dequanted = dequant_q5_k(&Q5_K_TEST_BLOCK).unwrap();
6567 let x: Vec<f32> = (0..256).map(|i| ((i as f32) * 0.023).sin()).collect();
6568 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
6569 let fused = dot_q5_k_f32(&Q5_K_TEST_BLOCK, &x);
6570 assert!(
6571 (fused - expected).abs() < 1e-2,
6572 "fused={fused} expected={expected}"
6573 );
6574 }
6575
6576 #[test]
6577 fn q5_k_rejects_misaligned_buffers() {
6578 let bad = vec![0u8; 5];
6579 assert!(dequant_q5_k(&bad).is_err());
6580 }
6581
6582 const Q6_K_TEST_BLOCK: [u8; 210] = [
6583 0xe0, 0xa5, 0x40, 0x5c, 0x8d, 0x3a, 0x0a, 0x26, 0xfb, 0x4b, 0x6e, 0x9a, 0xdf, 0x3e, 0xa3,
6584 0xc4, 0xf8, 0x2b, 0x1d, 0x95, 0x76, 0x7d, 0x3b, 0xcd, 0xfd, 0xef, 0xc2, 0x0b, 0x07, 0x63,
6585 0x29, 0xfb, 0x81, 0x57, 0xbe, 0xbe, 0x06, 0xf7, 0x3a, 0x92, 0xc4, 0x43, 0xff, 0xad, 0xac,
6586 0x7e, 0x0f, 0x00, 0x2a, 0x4f, 0xf0, 0xf8, 0xa9, 0xfa, 0x3c, 0x90, 0x6d, 0x73, 0x2d, 0x5a,
6587 0xe6, 0xc6, 0x46, 0xf2, 0x0d, 0x55, 0x4c, 0x25, 0x38, 0x71, 0x2b, 0x35, 0x38, 0x82, 0x16,
6588 0x37, 0x5f, 0x32, 0x61, 0x02, 0xdd, 0x2f, 0x6f, 0x7b, 0x1f, 0xb4, 0x1a, 0x1b, 0x3e, 0x4f,
6589 0x11, 0xa3, 0x17, 0x40, 0x5a, 0x5f, 0x76, 0xcd, 0x19, 0x27, 0x9b, 0xc7, 0xc8, 0xf7, 0xf7,
6590 0xee, 0xf4, 0x86, 0xd9, 0xfd, 0xa7, 0xfe, 0x9e, 0xac, 0x70, 0x53, 0x5b, 0x76, 0xfb, 0x39,
6591 0xf8, 0x4b, 0x98, 0xfe, 0xd0, 0x06, 0x21, 0x4c, 0x4d, 0xbe, 0x10, 0x2b, 0x06, 0x65, 0xc9,
6592 0x5e, 0xf9, 0x95, 0x72, 0xae, 0x99, 0xd9, 0x7e, 0x15, 0xbd, 0x5e, 0x6d, 0xe8, 0x25, 0x8a,
6593 0xd5, 0x99, 0xc6, 0x6b, 0x69, 0xc7, 0x84, 0xc6, 0xa4, 0xf7, 0xb9, 0x6d, 0x68, 0x45, 0x0e,
6594 0x65, 0x69, 0xeb, 0xe6, 0xeb, 0xe9, 0x28, 0xa6, 0xb9, 0x96, 0xf2, 0xe8, 0xa7, 0x9b, 0x6e,
6595 0x79, 0x8a, 0x68, 0x65, 0x59, 0x98, 0x8b, 0x44, 0x41, 0x98, 0x9a, 0x56, 0x01, 0x01, 0x01,
6596 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x1f, 0x25,
6597 ];
6598 const Q6_K_GOLDEN: [f32; 256] = [
6599 -0.320068, 0.100021, -0.640137, 0.56012, 0.260056, -0.120026, -0.120026, 0.120026,
6600 -0.100021, -0.100021, 0.28006, 0.200043, -0.0200043, -0.0400085, 0.0600128, -0.240051,
6601 -0.160034, 0.220047, -0.0600128, -0.540115, -0.200043, 0.260056, -0.100021, -0.0600128,
6602 0.260056, 0.620132, -0.28006, 0.540115, -0.500107, 0.0600128, -0.460098, 0.540115,
6603 0.340073, 0.460098, -0.360077, 0.28006, -0.200043, -0.180038, 0.200043, 0.360077,
6604 0.0800171, -0.260056, -0.340073, 0.580124, 0.240051, 0.28006, 0.620132, -0.320068, 1.04022,
6605 1.24026, 0.640137, 0.320068, -0.28006, 0.400085, -0.160034, 0.0, -0.120026, 0.120026,
6606 0.520111, -0.240051, -0.400085, -0.400085, -0.400085, -0.56012, -0.360077, 0.520111,
6607 -0.240051, 0.100021, -0.480103, 0.0600128, -0.640137, -0.28006, 0.620132, -0.240051,
6608 0.440094, 0.180038, -0.0600128, -0.260056, 0.520111, -0.0800171, 0.620132, -0.28006,
6609 0.0200043, 0.180038, 0.14003, -0.500107, -0.260056, -0.0800171, -0.340073, 0.28006,
6610 0.240051, -0.640137, -0.640137, -0.520111, 0.0400085, 0.620132, -0.160034, 0.100021,
6611 -0.42009, -0.42009, -0.640137, -0.0200043, 0.380081, -0.14003, 0.56012, 0.0800171,
6612 -0.0200043, 0.200043, 0.200043, 0.460098, -0.320068, -0.640137, 0.0400085, -0.240051,
6613 -0.0200043, 0.620132, -0.440094, 0.300064, 0.380081, 0.180038, 0.440094, -0.180038,
6614 -0.28006, 0.42009, 0.28006, 0.56012, 0.0800171, 0.620132, -0.120026, -0.440094, -0.800171,
6615 -0.440094, 0.320068, -0.600128, -0.200043, 0.840179, 0.320068, 0.720154, -0.400085,
6616 -1.00021, 0.600128, -0.56012, 0.0400085, 0.0800171, -0.380081, 0.620132, 0.620132,
6617 0.220047, -0.0200043, 0.0800171, -0.440094, -0.100021, -0.0400085, -0.340073, 0.340073,
6618 -0.580124, -0.180038, -0.640137, 0.200043, 0.300064, 0.240051, 1.16025, 0.360077,
6619 -0.360077, 1.08023, -0.360077, 0.320068, 0.28006, -0.360077, 0.56012, 0.160034, 0.240051,
6620 -0.28006, 0.520111, -0.360077, -0.720154, 0.56012, -0.160034, 0.0, 0.760162, 0.440094,
6621 0.240051, 0.440094, -0.28006, 0.320068, 0.440094, 0.320068, -0.0800171, -1.28027, 0.240051,
6622 0.0400085, -0.160034, 0.320068, 0.100021, 0.0800171, -0.600128, -0.580124, 0.14003,
6623 0.0400085, 0.0600128, 0.0600128, 0.160034, 0.0200043, 0.0600128, 0.100021, 0.380081,
6624 -0.200043, 0.320068, 0.260056, 0.0400085, -0.200043, 0.14003, 0.340073, -0.42009,
6625 0.0200043, 0.0200043, -0.260056, -0.240051, -0.620132, -0.440094, -0.620132, -0.240051,
6626 -0.220047, -0.220047, 0.14003, -0.0800171, -0.300064, -0.28006, -0.460098, -0.0800171,
6627 -0.0800171, 0.620132, 0.620132, 0.600128, 0.620132, -0.480103, 0.260056, 0.300064,
6628 0.200043, 0.620132, 1.00021, 0.400085, 0.28006, -0.440094, -0.440094, 0.28006, -0.0400085,
6629 -0.520111, -0.0400085, 0.160034, 0.360077, -0.0400085, -0.120026, 0.0, 0.0800171,
6630 -0.480103,
6631 ];
6632
6633 const Q6_K_SIGNED_SCALES_TEST_BLOCK: [u8; 210] = [
6640 0xe0, 0xa5, 0x40, 0x5c, 0x8d, 0x3a, 0x0a, 0x26, 0xfb, 0x4b, 0x6e, 0x9a, 0xdf, 0x3e, 0xa3,
6641 0xc4, 0x18, 0xe5, 0xf3, 0x7b, 0x9a, 0x93, 0xd5, 0x43, 0x13, 0x20, 0x4e, 0xf5, 0xf9, 0xad,
6642 0xe7, 0x05, 0x81, 0x57, 0xbe, 0xbe, 0x06, 0xf7, 0x3a, 0x92, 0xc4, 0x43, 0xff, 0xad, 0xac,
6643 0x7e, 0x0f, 0x00, 0xe6, 0xc0, 0x10, 0x08, 0x67, 0x16, 0xd4, 0x70, 0xa3, 0x9d, 0xe3, 0xb6,
6644 0x2a, 0x4a, 0xca, 0x0e, 0x0d, 0x55, 0x4c, 0x25, 0x38, 0x71, 0x2b, 0x35, 0x38, 0x82, 0x16,
6645 0x37, 0x5f, 0x32, 0x61, 0x02, 0x33, 0xe1, 0xa1, 0x95, 0xf1, 0x5c, 0xf6, 0xf5, 0xd2, 0xc1,
6646 0xff, 0x6d, 0xf9, 0xcf, 0xb6, 0xb1, 0x76, 0xcd, 0x19, 0x27, 0x9b, 0xc7, 0xc8, 0xf7, 0xf7,
6647 0xee, 0xf4, 0x86, 0xd9, 0xfd, 0xa7, 0xfe, 0x72, 0x64, 0x90, 0xbd, 0xb5, 0x9a, 0x15, 0xd7,
6648 0x18, 0xc5, 0x78, 0x12, 0x3f, 0x0a, 0xef, 0xc4, 0x4d, 0xbe, 0x10, 0x2b, 0x06, 0x65, 0xc9,
6649 0x5e, 0xf9, 0x95, 0x72, 0xae, 0x99, 0xd9, 0x7e, 0x15, 0x42, 0xa1, 0x96, 0x17, 0xda, 0x75,
6650 0x2a, 0x6a, 0x39, 0x94, 0x96, 0x38, 0x7b, 0x39, 0x5b, 0x08, 0xb9, 0x6d, 0x68, 0x45, 0x0e,
6651 0x65, 0x69, 0xeb, 0xe6, 0xeb, 0xe9, 0x28, 0xa6, 0xb9, 0x96, 0xf2, 0x17, 0x58, 0x68, 0x91,
6652 0x86, 0x75, 0x97, 0x9a, 0xa6, 0x67, 0x74, 0xbb, 0xbe, 0xa7, 0x65, 0xa9, 0x01, 0xff, 0x01,
6653 0xfe, 0x01, 0xff, 0x01, 0xff, 0x02, 0xff, 0x02, 0xfe, 0x01, 0xff, 0x01, 0xfe, 0x1f, 0x25,
6654 ];
6655 const Q6_K_SIGNED_SCALES_GOLDEN: [f32; 256] = [
6656 -0.320068, 0.100021, -0.640137, 0.56012, 0.260056, -0.120026, -0.120026, 0.120026,
6657 -0.100021, -0.100021, 0.28006, 0.200043, -0.0200043, -0.0400085, 0.0600128, -0.240051,
6658 -0.160034, 0.220047, -0.0600128, -0.540115, -0.200043, 0.260056, -0.100021, -0.0600128,
6659 0.260056, 0.640137, -0.28006, 0.540115, -0.500107, 0.0600128, -0.460098, 0.540115,
6660 0.340073, 0.460098, -0.360077, 0.28006, -0.200043, -0.180038, 0.200043, 0.360077,
6661 0.0800171, -0.260056, -0.340073, 0.580124, 0.240051, 0.28006, 0.620132, -0.320068, 1.04022,
6662 1.28027, 0.640137, 0.320068, -0.28006, 0.400085, -0.160034, -0.0, -0.120026, 0.120026,
6663 0.520111, -0.240051, -0.400085, -0.400085, -0.400085, -0.56012, -0.360077, 0.520111,
6664 -0.240051, 0.100021, -0.480103, 0.0600128, -0.640137, -0.28006, 0.620132, -0.240051,
6665 0.440094, 0.180038, -0.0600128, -0.260056, 0.520111, -0.0800171, 0.620132, -0.28006,
6666 0.0200043, 0.180038, 0.14003, -0.500107, -0.260056, -0.0800171, -0.340073, 0.28006,
6667 0.240051, -0.620132, -0.620132, -0.520111, 0.0400085, 0.640137, -0.160034, 0.100021,
6668 -0.42009, -0.42009, -0.640137, -0.0200043, 0.380081, -0.14003, 0.56012, 0.0800171,
6669 -0.0200043, 0.200043, 0.200043, 0.460098, -0.320068, -0.640137, 0.0400085, -0.240051,
6670 -0.0200043, 0.640137, -0.440094, 0.300064, 0.380081, 0.180038, 0.440094, -0.180038,
6671 -0.28006, 0.42009, 0.28006, 0.56012, 0.0800171, 0.640137, -0.120026, -0.440094, -0.800171,
6672 -0.440094, 0.320068, -0.600128, -0.200043, 0.840179, 0.320068, 0.720154, -0.400085,
6673 -1.00021, 0.600128, -0.56012, 0.0400085, 0.0800171, -0.380081, 0.620132, 0.620132,
6674 0.220047, -0.0200043, 0.0800171, -0.440094, -0.100021, -0.0400085, -0.340073, 0.340073,
6675 -0.580124, -0.180038, -0.620132, 0.200043, 0.300064, 0.240051, 1.16025, 0.360077,
6676 -0.360077, 1.08023, -0.360077, 0.320068, 0.28006, -0.360077, 0.56012, 0.160034, 0.240051,
6677 -0.28006, 0.520111, -0.360077, -0.720154, 0.56012, -0.160034, -0.0, 0.760162, 0.440094,
6678 0.240051, 0.440094, -0.28006, 0.320068, 0.440094, 0.320068, -0.0800171, -1.24026, 0.240051,
6679 0.0400085, -0.160034, 0.320068, 0.100021, 0.0800171, -0.600128, -0.580124, 0.14003,
6680 0.0400085, 0.0600128, 0.0600128, 0.160034, 0.0200043, 0.0600128, 0.100021, 0.380081,
6681 -0.200043, 0.320068, 0.260056, 0.0400085, -0.200043, 0.14003, 0.340073, -0.42009,
6682 0.0200043, 0.0200043, -0.260056, -0.240051, -0.620132, -0.440094, -0.620132, -0.240051,
6683 -0.220047, -0.220047, 0.14003, -0.0800171, -0.300064, -0.28006, -0.460098, -0.0800171,
6684 -0.0800171, 0.620132, 0.620132, 0.600128, 0.620132, -0.480103, 0.260056, 0.300064,
6685 0.200043, 0.620132, 1.00021, 0.400085, 0.28006, -0.440094, -0.440094, 0.28006, -0.0400085,
6686 -0.520111, -0.0400085, 0.160034, 0.360077, -0.0400085, -0.120026, -0.0, 0.0800171,
6687 -0.480103,
6688 ];
6689
6690 #[test]
6691 fn q4_k_dequant_matches_independent_python_reference() {
6692 let got = dequant_q4_k(&Q4_K_TEST_BLOCK).unwrap();
6693 assert_eq!(got.len(), Q4_K_GOLDEN.len());
6694 for (i, (a, b)) in got.iter().zip(Q4_K_GOLDEN.iter()).enumerate() {
6695 assert!(
6696 (a - b).abs() < 1e-3,
6697 "Q4_K element {i}: rust={a} python={b}"
6698 );
6699 }
6700 }
6701
6702 #[test]
6703 fn q4_k_fused_dot_matches_dequant_then_dot() {
6704 let dequanted = dequant_q4_k(&Q4_K_TEST_BLOCK).unwrap();
6705 let x: Vec<f32> = (0..256).map(|i| ((i as f32) * 0.017).sin()).collect();
6706 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
6707 let fused = dot_q4_k_f32(&Q4_K_TEST_BLOCK, &x);
6708 assert!(
6709 (fused - expected).abs() < 1e-2,
6710 "fused={fused} expected={expected}"
6711 );
6712 }
6713
6714 #[test]
6715 fn q6_k_dequant_matches_independent_python_reference() {
6716 let got = dequant_q6_k(&Q6_K_TEST_BLOCK).unwrap();
6717 assert_eq!(got.len(), Q6_K_GOLDEN.len());
6718 for (i, (a, b)) in got.iter().zip(Q6_K_GOLDEN.iter()).enumerate() {
6719 assert!(
6720 (a - b).abs() < 1e-3,
6721 "Q6_K element {i}: rust={a} python={b}"
6722 );
6723 }
6724 }
6725
6726 #[test]
6727 fn q6_k_fused_dot_matches_dequant_then_dot() {
6728 let dequanted = dequant_q6_k(&Q6_K_TEST_BLOCK).unwrap();
6729 let x: Vec<f32> = (0..256).map(|i| ((i as f32) * 0.021).cos()).collect();
6730 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
6731 let fused = dot_q6_k_f32(&Q6_K_TEST_BLOCK, &x);
6732 assert!(
6733 (fused - expected).abs() < 1e-2,
6734 "fused={fused} expected={expected}"
6735 );
6736 }
6737
6738 const IQ1_S_TEST_BLOCK: [u8; 50] = [
6745 0x0a, 0x2f, 0xfa, 0x06, 0x1e, 0x37, 0x6f, 0xe3, 0x62, 0xd0, 0xb6, 0xa4, 0x25, 0xae, 0x76,
6746 0x14, 0x72, 0x5b, 0xfa, 0x05, 0xd1, 0xf1, 0x2a, 0x4c, 0xad, 0x29, 0xae, 0xf4, 0xcf, 0x0c,
6747 0x96, 0x51, 0x58, 0x03, 0x6d, 0xd3, 0x10, 0x92, 0x70, 0xff, 0x61, 0x58, 0xc8, 0x30, 0x25,
6748 0x64, 0x49, 0x85, 0xc0, 0x24,
6749 ];
6750 const IQ1_S_GOLDEN: [f32; 256] = [
6751 1.05861, 1.05861, 1.05861, -0.15123, -0.15123, -0.15123, -1.36107, 1.05861, -1.36107,
6752 -1.36107, 1.05861, -0.15123, -1.36107, -0.15123, 1.05861, -0.15123, -0.15123, -0.15123,
6753 -1.36107, -0.15123, -0.15123, -0.15123, 1.05861, -0.15123, -1.36107, -0.15123, -1.36107,
6754 -0.15123, -0.15123, -0.15123, -0.15123, -1.36107, -0.371201, 0.288712, -0.0412445,
6755 0.288712, -0.0412445, -0.0412445, -0.371201, -0.371201, 0.288712, -0.0412445, -0.0412445,
6756 -0.0412445, -0.0412445, -0.0412445, -0.371201, -0.0412445, -0.0412445, -0.371201,
6757 -0.0412445, -0.0412445, -0.0412445, -0.0412445, -0.371201, -0.371201, 0.288712, 0.288712,
6758 0.288712, 0.288712, -0.371201, -0.371201, 0.288712, -0.371201, 1.44356, 1.44356, 1.44356,
6759 -1.856, 1.44356, 1.44356, -1.856, -1.856, -1.856, 1.44356, -0.206223, -1.856, -1.856,
6760 -0.206223, -0.206223, 1.44356, 1.44356, -0.206223, -0.206223, -0.206223, -0.206223,
6761 -0.206223, 1.44356, -0.206223, -0.206223, 1.44356, -1.856, 1.44356, -0.206223, -0.206223,
6762 1.44356, 1.44356, 0.15123, 1.36107, 1.36107, 1.36107, 1.36107, 0.15123, 0.15123, -1.05861,
6763 0.15123, 0.15123, -1.05861, 1.36107, 0.15123, 0.15123, 0.15123, 0.15123, 1.36107, 1.36107,
6764 -1.05861, 1.36107, 1.36107, 0.15123, 0.15123, -1.05861, 0.15123, 1.36107, -1.05861,
6765 -1.05861, -1.05861, 1.36107, 0.15123, 0.15123, 0.866135, 0.0962372, -0.67366, 0.0962372,
6766 0.866135, -0.67366, 0.0962372, -0.67366, 0.866135, 0.0962372, 0.0962372, 0.866135,
6767 0.866135, -0.67366, 0.0962372, -0.67366, -0.67366, 0.866135, 0.0962372, 0.0962372,
6768 -0.67366, 0.0962372, 0.0962372, 0.0962372, 0.866135, -0.67366, 0.0962372, 0.866135,
6769 0.0962372, -0.67366, 0.0962372, -0.67366, 1.60854, 0.178726, 1.60854, 0.178726, 0.178726,
6770 0.178726, 1.60854, 0.178726, 1.60854, 0.178726, -1.25108, 1.60854, 1.60854, 0.178726,
6771 0.178726, 0.178726, 0.178726, 0.178726, 1.60854, 1.60854, 0.178726, 1.60854, -1.25108,
6772 -1.25108, -1.25108, -1.25108, 0.178726, -1.25108, 1.60854, 0.178726, 1.60854, -1.25108,
6773 0.0962372, -0.123734, -0.0137482, -0.0137482, 0.0962372, 0.0962372, -0.0137482, -0.123734,
6774 -0.123734, 0.0962372, -0.123734, 0.0962372, 0.0962372, -0.123734, 0.0962372, -0.123734,
6775 -0.0137482, 0.0962372, -0.0137482, -0.0137482, 0.0962372, -0.123734, -0.123734, 0.0962372,
6776 -0.123734, -0.0137482, -0.0137482, 0.0962372, -0.123734, -0.0137482, 0.0962372, -0.123734,
6777 0.618668, 0.618668, -0.481186, 0.618668, -0.481186, 0.618668, -0.481186, -0.481186,
6778 -0.481186, 0.0687408, 0.618668, 0.0687408, -0.481186, 0.0687408, -0.481186, -0.481186,
6779 0.0687408, 0.0687408, 0.618668, 0.618668, 0.618668, 0.618668, -0.481186, 0.0687408,
6780 0.618668, 0.0687408, -0.481186, 0.0687408, -0.481186, 0.0687408, 0.618668, -0.481186,
6781 ];
6782
6783 const IQ2_XXS_TEST_BLOCK: [u8; 66] = [
6784 0x29, 0x30, 0xd9, 0x33, 0x95, 0x4c, 0x08, 0x1e, 0xad, 0x79, 0x49, 0xf2, 0x8d, 0x5f, 0x93,
6785 0xea, 0x78, 0x18, 0x98, 0xb9, 0x94, 0x14, 0xad, 0xce, 0xca, 0x1d, 0xab, 0x81, 0x53, 0x4a,
6786 0x68, 0xd0, 0x59, 0x96, 0x36, 0x5d, 0xbe, 0x20, 0xc4, 0xff, 0xe4, 0x2c, 0xcd, 0x2f, 0x4f,
6787 0x4f, 0x67, 0x53, 0xc6, 0xd5, 0xa2, 0xfb, 0xc7, 0xf3, 0xe2, 0x6b, 0xf1, 0x99, 0x23, 0x1e,
6788 0x2d, 0x5e, 0x8c, 0x78, 0xc2, 0x31,
6789 ];
6790 const IQ2_XXS_GOLDEN: [f32; 256] = [
6791 1.95007, 1.95007, 1.95007, -6.09398, 6.09398, 1.95007, 1.95007, -10.4816, 1.95007, 1.95007,
6792 -1.95007, -10.4816, -6.09398, -6.09398, 1.95007, 1.95007, 6.09398, 6.09398, -1.95007,
6793 10.4816, -6.09398, -1.95007, 1.95007, -6.09398, -1.95007, 1.95007, -1.95007, -6.09398,
6794 1.95007, 1.95007, -6.09398, 1.95007, -0.390015, -1.2188, 0.390015, 0.390015, -0.390015,
6795 0.390015, 1.2188, -0.390015, -0.390015, 0.390015, -0.390015, 0.390015, -0.390015, 1.2188,
6796 -1.2188, 2.09633, -0.390015, -0.390015, 2.09633, 1.2188, 0.390015, -0.390015, -0.390015,
6797 1.2188, -0.390015, -2.09633, 1.2188, 0.390015, 1.2188, 1.2188, -1.2188, -0.390015,
6798 -0.390015, 2.09633, -0.390015, -1.2188, 2.09633, -0.390015, 0.390015, 1.2188, -0.390015,
6799 0.390015, -1.2188, -2.09633, -0.390015, 1.2188, 1.2188, 1.2188, -0.390015, -0.390015,
6800 0.390015, -2.09633, 1.2188, -0.390015, 0.390015, 1.2188, 2.09633, -0.390015, -2.09633,
6801 -2.09633, 0.390015, -0.390015, -0.390015, -0.390015, 13.2767, 2.47009, 2.47009, -13.2767,
6802 7.71904, -13.2767, -2.47009, -7.71904, 2.47009, 2.47009, 13.2767, 2.47009, 2.47009,
6803 -13.2767, 13.2767, -2.47009, -2.47009, -2.47009, -13.2767, 2.47009, 7.71904, -2.47009,
6804 -7.71904, -2.47009, 2.47009, -2.47009, 7.71904, 2.47009, -2.47009, -2.47009, 7.71904,
6805 -2.47009, 0.650024, 0.650024, -2.03133, 0.650024, 3.49388, 2.03133, -0.650024, 0.650024,
6806 -2.03133, -3.49388, -0.650024, -2.03133, -0.650024, -2.03133, -2.03133, -0.650024,
6807 -0.650024, -0.650024, 0.650024, 0.650024, -0.650024, 3.49388, 2.03133, -2.03133, -2.03133,
6808 -0.650024, -0.650024, 0.650024, 0.650024, -2.03133, -0.650024, -0.650024, -10.9692,
6809 -10.9692, -3.51013, 3.51013, 3.51013, -10.9692, -18.867, -10.9692, 3.51013, -18.867,
6810 -3.51013, 3.51013, 10.9692, -10.9692, 3.51013, -3.51013, -3.51013, 3.51013, 10.9692,
6811 -18.867, -3.51013, 3.51013, 10.9692, -3.51013, 3.51013, -3.51013, -10.9692, -18.867,
6812 3.51013, -3.51013, 10.9692, 3.51013, 2.47009, -2.47009, 2.47009, 2.47009, 13.2767,
6813 -7.71904, -2.47009, -7.71904, -7.71904, -13.2767, -2.47009, 2.47009, -7.71904, 2.47009,
6814 -13.2767, -13.2767, -2.47009, 13.2767, -13.2767, 7.71904, 2.47009, 2.47009, -13.2767,
6815 -7.71904, -13.2767, -2.47009, -13.2767, -2.47009, 2.47009, 7.71904, -7.71904, -13.2767,
6816 2.84386, 0.910034, -4.89143, -0.910034, 0.910034, 2.84386, 0.910034, 0.910034, -4.89143,
6817 0.910034, 4.89143, 0.910034, -4.89143, -0.910034, -0.910034, 0.910034, -0.910034, 0.910034,
6818 0.910034, -0.910034, 2.84386, 2.84386, 0.910034, 0.910034, 0.910034, -0.910034, -0.910034,
6819 -4.89143, 0.910034, 2.84386, 2.84386, -0.910034,
6820 ];
6821
6822 const IQ3_XXS_TEST_BLOCK: [u8; 98] = [
6823 0x71, 0x31, 0x16, 0x0a, 0x79, 0x04, 0x5d, 0x87, 0xae, 0x2a, 0x4a, 0x43, 0xfd, 0x02, 0xba,
6824 0x6c, 0x10, 0x42, 0x80, 0xe5, 0x1d, 0x08, 0x22, 0xcb, 0x21, 0x54, 0xf9, 0xaa, 0x8e, 0xc2,
6825 0xf2, 0x34, 0x66, 0x1e, 0x2a, 0xef, 0x19, 0xae, 0x48, 0x47, 0x29, 0xa0, 0x72, 0xd1, 0x31,
6826 0xc0, 0x65, 0x49, 0xde, 0x79, 0x32, 0xe6, 0x4d, 0xb6, 0x55, 0x3f, 0x4d, 0xf1, 0x18, 0xbb,
6827 0x18, 0x59, 0x4c, 0x31, 0xa3, 0xb2, 0x34, 0xdd, 0xf6, 0x4a, 0x91, 0x51, 0x3f, 0x3e, 0x40,
6828 0x69, 0xad, 0xbf, 0x1a, 0xd0, 0x05, 0xfb, 0xbe, 0x8b, 0x0b, 0xdd, 0xdf, 0x7d, 0x94, 0x74,
6829 0x92, 0x3e, 0xff, 0x04, 0x2a, 0xc4, 0xea, 0xc9,
6830 ];
6831 const IQ3_XXS_GOLDEN: [f32; 256] = [
6832 1.5304, 23.7211, -4.59119, 1.5304, -10.7128, -23.7211, 1.5304, -1.5304, 7.65198, -7.65198,
6833 7.65198, -7.65198, -10.7128, -4.59119, 1.5304, 1.5304, -4.59119, -23.7211, 16.8344,
6834 -4.59119, -13.7736, 23.7211, -16.8344, -7.65198, -13.7736, -1.5304, -1.5304, 13.7736,
6835 -23.7211, 10.7128, -13.7736, -1.5304, -3.57092, 1.19031, 5.95154, 3.57092, -18.4498,
6836 10.7128, 1.19031, 3.57092, -5.95154, -1.19031, 13.0934, 18.4498, 10.7128, -1.19031,
6837 1.19031, -1.19031, -18.4498, 1.19031, -8.33215, -10.7128, -13.0934, -1.19031, -3.57092,
6838 5.95154, -3.57092, 5.95154, 3.57092, 1.19031, -10.7128, -8.33215, -1.19031, 3.57092,
6839 3.91101, 60.6207, 27.3771, 19.5551, 35.1991, 35.1991, -35.1991, -50.8431, 11.733, -27.3771,
6840 19.5551, 3.91101, -11.733, 27.3771, -3.91101, -3.91101, -43.0211, 60.6207, -19.5551,
6841 3.91101, -50.8431, -19.5551, 11.733, 43.0211, -60.6207, 43.0211, -19.5551, -3.91101,
6842 -11.733, -27.3771, -27.3771, 11.733, 5.27136, -68.5277, 36.8995, -81.7061, -68.5277,
6843 36.8995, 68.5277, -36.8995, 26.3568, 15.8141, 5.27136, 36.8995, 57.985, -5.27136, 81.7061,
6844 -47.4423, -5.27136, -47.4423, -15.8141, 81.7061, -47.4423, 68.5277, 68.5277, 5.27136,
6845 26.3568, 26.3568, 5.27136, -26.3568, -36.8995, 36.8995, -26.3568, -5.27136, 71.1634,
6846 -32.1383, -41.3207, -4.59119, -22.9559, -32.1383, 4.59119, -71.1634, -41.3207, -4.59119,
6847 -22.9559, 4.59119, -41.3207, 4.59119, 4.59119, 41.3207, 4.59119, -22.9559, -13.7736,
6848 -13.7736, 13.7736, -13.7736, 13.7736, 13.7736, 32.1383, 13.7736, 41.3207, -4.59119,
6849 13.7736, -13.7736, -32.1383, -32.1383, -39.5352, -33.1586, -7.65198, -12.7533, -17.8546,
6850 28.0573, -17.8546, 28.0573, -12.7533, -17.8546, 28.0573, -2.55066, -17.8546, -22.9559,
6851 -28.0573, 22.9559, -2.55066, 12.7533, 2.55066, 12.7533, -12.7533, 12.7533, -7.65198,
6852 -7.65198, 22.9559, 33.1586, -2.55066, 33.1586, 12.7533, -12.7533, 12.7533, 12.7533,
6853 0.85022, -1.87048, 1.87048, 0.170044, -1.87048, 0.170044, 1.87048, 2.21057, -0.85022,
6854 0.510132, -0.85022, -0.510132, -2.63568, -1.19031, -0.85022, 1.5304, 2.21057, 1.5304,
6855 -1.19031, -0.510132, -1.19031, -0.85022, -0.170044, -0.510132, -0.85022, -0.510132,
6856 -0.85022, 0.510132, 2.21057, -0.85022, 0.510132, 2.63568, 21.2555, -4.2511, 21.2555,
6857 -4.2511, 46.7621, -38.2599, 29.7577, -38.2599, 21.2555, 4.2511, 21.2555, -4.2511, 4.2511,
6858 46.7621, 38.2599, -12.7533, -4.2511, -12.7533, 21.2555, -12.7533, 21.2555, -29.7577,
6859 46.7621, 4.2511, -65.892, -38.2599, -38.2599, -29.7577, 29.7577, 46.7621, -4.2511,
6860 -38.2599,
6861 ];
6862
6863 #[test]
6864 fn iq1_s_dequant_matches_independent_python_reference() {
6865 let got = dequant_iq1_s(&IQ1_S_TEST_BLOCK).unwrap();
6866 assert_eq!(got.len(), IQ1_S_GOLDEN.len());
6867 for (i, (a, b)) in got.iter().zip(IQ1_S_GOLDEN.iter()).enumerate() {
6868 assert!(
6869 (a - b).abs() < 1e-3,
6870 "IQ1_S element {i}: rust={a} python={b}"
6871 );
6872 }
6873 }
6874
6875 #[test]
6876 fn iq2_xxs_dequant_matches_independent_python_reference() {
6877 let got = dequant_iq2_xxs(&IQ2_XXS_TEST_BLOCK).unwrap();
6878 assert_eq!(got.len(), IQ2_XXS_GOLDEN.len());
6879 for (i, (a, b)) in got.iter().zip(IQ2_XXS_GOLDEN.iter()).enumerate() {
6880 assert!(
6881 (a - b).abs() < 1e-3,
6882 "IQ2_XXS element {i}: rust={a} python={b}"
6883 );
6884 }
6885 }
6886
6887 #[test]
6888 fn iq3_xxs_dequant_matches_independent_python_reference() {
6889 let got = dequant_iq3_xxs(&IQ3_XXS_TEST_BLOCK).unwrap();
6890 assert_eq!(got.len(), IQ3_XXS_GOLDEN.len());
6891 for (i, (a, b)) in got.iter().zip(IQ3_XXS_GOLDEN.iter()).enumerate() {
6892 assert!(
6893 (a - b).abs() < 1e-3,
6894 "IQ3_XXS element {i}: rust={a} python={b}"
6895 );
6896 }
6897 }
6898
6899 #[test]
6900 fn iq_lowbit_fused_dots_match_dequant_then_dot() {
6901 type DequantFn = fn(&[u8]) -> Result<Vec<f32>, QuantError>;
6902 type DotFn = fn(&[u8], &[f32]) -> f32;
6903 let x: Vec<f32> = (0..1024).map(|i| ((i as f32) * 0.027).sin()).collect();
6904 let cases: [(&[u8], usize, DequantFn, DotFn); 3] = [
6905 (&IQ1_S_TEST_BLOCK, 4, dequant_iq1_s, dot_iq1_s_f32),
6906 (&IQ2_XXS_TEST_BLOCK, 4, dequant_iq2_xxs, dot_iq2_xxs_f32),
6907 (&IQ3_XXS_TEST_BLOCK, 4, dequant_iq3_xxs, dot_iq3_xxs_f32),
6908 ];
6909 for (block, n, dequant, dot) in cases {
6910 let packed = repeat_block(block, n);
6911 let dequanted = dequant(&packed).unwrap();
6912 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
6913 let fused = dot(&packed, &x[..dequanted.len()]);
6914 assert!(
6915 (fused - expected).abs() < 1e-2,
6916 "fused={fused} expected={expected}"
6917 );
6918 }
6919 }
6920
6921 #[cfg(target_arch = "x86_64")]
6926 #[test]
6927 fn avx2_iq_kernels_match_scalar_directly_on_random_blocks() {
6928 if !(is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma")) {
6929 eprintln!("skipping: host CPU lacks AVX2+FMA");
6930 return;
6931 }
6932 type ScalarFn = fn(&[u8], &[f32]) -> f32;
6933 type Avx2Fn = unsafe fn(&[u8], &[f32]) -> f32;
6934 let cases: [(&str, usize, ScalarFn, Avx2Fn); 3] = [
6935 (
6936 "iq1_s",
6937 IQ1_S_BLOCK_BYTES,
6938 dot_iq1_s_f32_scalar,
6939 simd_x86::dot_iq1_s_f32_avx2,
6940 ),
6941 (
6942 "iq2_xxs",
6943 IQ2_XXS_BLOCK_BYTES,
6944 dot_iq2_xxs_f32_scalar,
6945 simd_x86::dot_iq2_xxs_f32_avx2,
6946 ),
6947 (
6948 "iq3_xxs",
6949 IQ3_XXS_BLOCK_BYTES,
6950 dot_iq3_xxs_f32_scalar,
6951 simd_x86::dot_iq3_xxs_f32_avx2,
6952 ),
6953 ];
6954 for (name, block_bytes, scalar, avx2) in cases {
6955 for trial in 0..16u32 {
6956 let n_blocks = 3;
6957 let mut bytes =
6958 pseudo_random_bytes(trial.wrapping_mul(97) + 5, n_blocks * block_bytes);
6959 for b in 0..n_blocks {
6960 let d = half::f16::from_f32(0.05 + 0.01 * trial as f32).to_le_bytes();
6962 bytes[b * block_bytes] = d[0];
6963 bytes[b * block_bytes + 1] = d[1];
6964 }
6965 let x: Vec<f32> = (0..n_blocks * 256)
6966 .map(|i| ((i as f32) * 0.017 + trial as f32).sin())
6967 .collect();
6968 let s = scalar(&bytes, &x);
6969 let v = unsafe { avx2(&bytes, &x) };
6970 let tol = 2e-3_f32.max(s.abs() * 1e-3);
6977 assert!(
6978 (s - v).abs() < tol,
6979 "{name} trial {trial}: scalar={s} avx2={v}"
6980 );
6981 }
6982 }
6983 }
6984
6985 #[cfg(target_arch = "x86_64")]
6990 fn pseudo_random_bytes(seed: u32, len: usize) -> Vec<u8> {
6991 let mut state = seed.wrapping_mul(2654435761).wrapping_add(1);
6992 (0..len)
6993 .map(|_| {
6994 state = state.wrapping_mul(1664525).wrapping_add(1013904223);
6995 (state >> 16) as u8
6996 })
6997 .collect()
6998 }
6999
7000 #[test]
7001 fn iq_lowbit_dequant_rejects_misaligned_buffers() {
7002 let bad = vec![0u8; 7];
7003 assert!(dequant_iq1_s(&bad).is_err());
7004 assert!(dequant_iq2_xxs(&bad).is_err());
7005 assert!(dequant_iq3_xxs(&bad).is_err());
7006 assert!(dequant_iq2_xs(&bad).is_err());
7007 assert!(dequant_iq2_s(&bad).is_err());
7008 assert!(dequant_iq3_s(&bad).is_err());
7009 assert!(dequant_iq1_m(&bad).is_err());
7010 }
7011
7012 #[test]
7026 fn iq_tier_dequant_matches_real_ggml_exactly() {
7027 type DequantFn = fn(&[u8]) -> Result<Vec<f32>, QuantError>;
7028 let cases: [(&str, &[u8], &[f32], DequantFn); 4] = [
7029 (
7030 "IQ2_XS",
7031 &iq_tier_goldens::IQ2_XS_TEST_BLOCKS,
7032 &iq_tier_goldens::IQ2_XS_GOLDEN,
7033 dequant_iq2_xs,
7034 ),
7035 (
7036 "IQ2_S",
7037 &iq_tier_goldens::IQ2_S_TEST_BLOCKS,
7038 &iq_tier_goldens::IQ2_S_GOLDEN,
7039 dequant_iq2_s,
7040 ),
7041 (
7042 "IQ3_S",
7043 &iq_tier_goldens::IQ3_S_TEST_BLOCKS,
7044 &iq_tier_goldens::IQ3_S_GOLDEN,
7045 dequant_iq3_s,
7046 ),
7047 (
7048 "IQ1_M",
7049 &iq_tier_goldens::IQ1_M_TEST_BLOCKS,
7050 &iq_tier_goldens::IQ1_M_GOLDEN,
7051 dequant_iq1_m,
7052 ),
7053 ];
7054 for (name, blocks, golden, dequant) in cases {
7055 let got = dequant(blocks).unwrap();
7056 assert_eq!(got.len(), golden.len(), "{name}: element count");
7057 for (i, (a, b)) in got.iter().zip(golden.iter()).enumerate() {
7058 assert_eq!(
7059 a.to_bits(),
7060 b.to_bits(),
7061 "{name} element {i} (block {}, offset {}): rust={a} ggml={b}",
7062 i / 256,
7063 i % 256
7064 );
7065 }
7066 }
7067 }
7068
7069 #[test]
7079 fn iq_tier_all_ones_block_reaches_the_maximum_grid_index() {
7080 let d = f16::from_le_bytes([
7084 iq_tier_goldens::IQ2_XS_TEST_BLOCKS[0],
7085 iq_tier_goldens::IQ2_XS_TEST_BLOCKS[1],
7086 ])
7087 .to_f32();
7088 let mag = (iq_tables::IQ2XS_GRID[511] & 0xFF) as f32;
7089 assert_eq!(
7090 iq_tier_goldens::IQ2_XS_GOLDEN[0],
7091 -(d * (0.5 + 15.0) * 0.25) * mag
7092 );
7093
7094 let d = f16::from_le_bytes([
7097 iq_tier_goldens::IQ2_S_TEST_BLOCKS[0],
7098 iq_tier_goldens::IQ2_S_TEST_BLOCKS[1],
7099 ])
7100 .to_f32();
7101 let mag = (iq_tables::IQ2S_GRID[1023] & 0xFF) as f32;
7102 assert_eq!(
7103 iq_tier_goldens::IQ2_S_GOLDEN[0],
7104 -(d * (0.5 + 15.0) * 0.25) * mag
7105 );
7106
7107 let d = f16::from_le_bytes([
7110 iq_tier_goldens::IQ3_S_TEST_BLOCKS[0],
7111 iq_tier_goldens::IQ3_S_TEST_BLOCKS[1],
7112 ])
7113 .to_f32();
7114 let mag = (iq_tables::IQ3S_GRID[511] & 0xFF) as f32;
7115 assert_eq!(iq_tier_goldens::IQ3_S_GOLDEN[0], -(d * 31.0) * mag);
7116
7117 let sc: [u16; 4] = std::array::from_fn(|k| {
7123 u16::from_le_bytes([
7124 iq_tier_goldens::IQ1_M_TEST_BLOCKS[48 + 2 * k],
7125 iq_tier_goldens::IQ1_M_TEST_BLOCKS[48 + 2 * k + 1],
7126 ])
7127 });
7128 let d = f16::from_bits(
7129 (sc[0] >> 12) | ((sc[1] >> 8) & 0x00F0) | ((sc[2] >> 4) & 0x0F00) | (sc[3] & 0xF000),
7130 )
7131 .to_f32();
7132 let v = (iq_tables::IQ1S_GRID[2047] & 0xFF) as u8 as i8;
7133 assert_eq!(
7134 iq_tier_goldens::IQ1_M_GOLDEN[0],
7135 d * 15.0 * (v as f32 - IQ1S_DELTA)
7136 );
7137 }
7138
7139 #[test]
7145 fn iq_tier_fused_dots_match_dequant_then_dot() {
7146 type DequantFn = fn(&[u8]) -> Result<Vec<f32>, QuantError>;
7147 type DotFn = fn(&[u8], &[f32]) -> f32;
7148 let x: Vec<f32> = (0..1024).map(|i| ((i as f32) * 0.031).cos()).collect();
7149 let cases: [(&str, &[u8], DequantFn, DotFn); 4] = [
7150 (
7151 "IQ2_XS",
7152 &iq_tier_goldens::IQ2_XS_TEST_BLOCKS,
7153 dequant_iq2_xs,
7154 dot_iq2_xs_f32,
7155 ),
7156 (
7157 "IQ2_S",
7158 &iq_tier_goldens::IQ2_S_TEST_BLOCKS,
7159 dequant_iq2_s,
7160 dot_iq2_s_f32,
7161 ),
7162 (
7163 "IQ3_S",
7164 &iq_tier_goldens::IQ3_S_TEST_BLOCKS,
7165 dequant_iq3_s,
7166 dot_iq3_s_f32,
7167 ),
7168 (
7169 "IQ1_M",
7170 &iq_tier_goldens::IQ1_M_TEST_BLOCKS,
7171 dequant_iq1_m,
7172 dot_iq1_m_f32,
7173 ),
7174 ];
7175 for (name, blocks, dequant, dot) in cases {
7176 let dequanted = dequant(blocks).unwrap();
7177 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
7178 let fused = dot(blocks, &x[..dequanted.len()]);
7179 assert!(
7180 (fused - expected).abs() <= expected.abs() * 1e-5 + 1e-3,
7181 "{name}: fused={fused} expected={expected}"
7182 );
7183 }
7184 }
7185
7186 const MXFP4_GGUF_TEST_BLOCKS: [u8; 68] = [
7192 0x79, 0xb4, 0x8d, 0xe2, 0x62, 0x5d, 0xbb, 0x9d, 0x54, 0xe6, 0xdb, 0x94, 0x59, 0x7d, 0x28,
7193 0xf9, 0x79, 0x7a, 0xfc, 0xc1, 0xfa, 0x1e, 0x53, 0x5b, 0x0e, 0xc2, 0x5a, 0x2f, 0x0c, 0x82,
7194 0x4d, 0xcb, 0x11, 0x28, 0x7b, 0x7c, 0xb6, 0x45, 0xe0, 0xb0, 0x52, 0x40, 0x51, 0xec, 0x30,
7195 0x1a, 0xd2, 0x17, 0xf3, 0xbb, 0xfc, 0x7c, 0x8f, 0xf0, 0x67, 0x83, 0x88, 0x9d, 0x79, 0xdb,
7196 0xf4, 0x45, 0x29, 0x78, 0xe6, 0xf4, 0x99, 0xea,
7197 ];
7198 const MXFP4_GGUF_GOLDEN: [f32; 128] = [
7199 0.03125, -0.046875, 0.015625, 0.015625, -0.046875, -0.0234375, -0.046875, 0.03125, 0.0625,
7200 -0.0234375, 0.03125, -0.0078125, -0.046875, 0.0, -0.0078125, -0.0078125, -0.0234375, 0.0,
7201 -0.0625, 0.0625, 0.046875, -0.0234375, -0.0078125, 0.046875, -0.0625, -0.046875,
7202 -0.0078125, 0.046875, 0.09375, 0.015625, -0.09375, 0.09375, -0.0625, 0.015625, -0.03125,
7203 -0.125, 0.046875, -0.046875, -0.125, 0.03125, -0.03125, -0.1875, -0.0625, 0.03125,
7204 -0.09375, -0.046875, 0.015625, 0.0, -0.1875, -0.0625, -0.1875, 0.015625, 0.09375, 0.09375,
7205 0.0, -0.0625, 0.09375, 0.03125, 0.0, 0.0, 0.0625, -0.0625, 0.015625, 0.03125, -0.125, 0.25,
7206 0.1875, 0.0, 0.0, 0.0625, 0.0, 0.03125, -0.125, 0.0, -0.0625, 0.0625, 0.375, 0.09375,
7207 -0.09375, -0.125, 0.375, -0.09375, 0.125, -0.25, -0.09375, 0.1875, 0.125, 0.1875, -0.25,
7208 0.09375, 0.03125, -0.1875, 0.03125, -0.375, -0.09375, -0.375, -0.75, 0.0, 0.75, 0.1875,
7209 0.0, -0.375, -0.0625, -0.1875, 0.25, 0.375, -0.0625, 0.0, 0.5, 0.25, -0.0625, -0.125, 0.0,
7210 -0.75, 0.5, 0.0, 0.0, -0.0625, 0.75, -0.375, -0.75, 0.25, 0.125, 0.75, -0.5, -0.75,
7211 -0.0625, -0.5,
7212 ];
7213
7214 #[test]
7215 fn mxfp4_gguf_dequant_matches_independent_python_reference() {
7216 let got = dequant_mxfp4_gguf(&MXFP4_GGUF_TEST_BLOCKS).unwrap();
7217 assert_eq!(got.len(), MXFP4_GGUF_GOLDEN.len());
7218 for (i, (a, b)) in got.iter().zip(MXFP4_GGUF_GOLDEN.iter()).enumerate() {
7219 assert!(
7220 (a - b).abs() < 1e-3,
7221 "MXFP4-GGUF element {i}: rust={a} python={b}"
7222 );
7223 }
7224 }
7225
7226 #[test]
7227 fn mxfp4_gguf_fused_dot_matches_dequant_then_dot() {
7228 let dequanted = dequant_mxfp4_gguf(&MXFP4_GGUF_TEST_BLOCKS).unwrap();
7229 let x: Vec<f32> = (0..128).map(|i| ((i as f32) * 0.031).cos()).collect();
7230 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
7231 let fused = dot_mxfp4_gguf_f32(&MXFP4_GGUF_TEST_BLOCKS, &x);
7232 assert!(
7233 (fused - expected).abs() < 1e-2,
7234 "fused={fused} expected={expected}"
7235 );
7236 }
7237
7238 #[test]
7243 fn mxfp4_gguf_block_form_agrees_with_two_buffer_form() {
7244 let mut packed = Vec::new();
7245 let mut scales = Vec::new();
7246 for block in MXFP4_GGUF_TEST_BLOCKS
7247 .as_chunks::<MXFP4_GGUF_BLOCK_BYTES>()
7248 .0
7249 {
7250 scales.push(block[0]);
7251 packed.extend_from_slice(&block[1..17]);
7252 }
7253 let x: Vec<f32> = (0..128).map(|i| ((i as f32) * 0.019).sin()).collect();
7254 let a = dot_mxfp4_gguf_f32(&MXFP4_GGUF_TEST_BLOCKS, &x);
7255 let b = dot_mxfp4_row_f32(&packed, &scales, &x);
7256 assert!((a - b).abs() < 1e-4, "block={a} two-buffer={b}");
7257 }
7258
7259 const Q6_K_SIGNED_TEST_BLOCK: [u8; 210] = [
7268 0x10, 0x5b, 0x5f, 0x45, 0x4a, 0xa0, 0x3f, 0x10, 0xf2, 0x7f, 0xdd, 0xf5, 0x25, 0x03, 0xc3,
7269 0x12, 0x74, 0xe1, 0x4e, 0x42, 0xf1, 0x04, 0xe1, 0xad, 0xc6, 0x55, 0x59, 0x4b, 0x5a, 0xfc,
7270 0xf5, 0x3f, 0xc5, 0x0b, 0xac, 0x7b, 0x4c, 0xd4, 0x19, 0xa6, 0x27, 0xdd, 0xf4, 0x7d, 0x9c,
7271 0xfc, 0x03, 0xd2, 0x5f, 0xe3, 0xff, 0x9c, 0xa6, 0x74, 0xa0, 0xe1, 0xbe, 0xf0, 0x26, 0xdb,
7272 0x4b, 0x23, 0xa0, 0xbc, 0xb1, 0x94, 0xd7, 0x7e, 0xcf, 0xf7, 0x97, 0xb4, 0xac, 0x1f, 0xb1,
7273 0x9f, 0xb7, 0xbe, 0xa3, 0xb5, 0xd2, 0xd4, 0x6d, 0x9c, 0x3d, 0xf3, 0x5f, 0x0e, 0x64, 0xbf,
7274 0x54, 0x40, 0xc8, 0xef, 0x9d, 0xc3, 0xf3, 0x4c, 0xb0, 0xf8, 0x54, 0xcf, 0xf3, 0x12, 0xcc,
7275 0x2f, 0x0c, 0xee, 0xab, 0x5d, 0x8d, 0x0b, 0x19, 0xb2, 0x99, 0xbd, 0x4a, 0xec, 0x04, 0xb3,
7276 0xf6, 0xc1, 0xb9, 0xf8, 0x1d, 0xfe, 0x51, 0xea, 0x99, 0xe5, 0x75, 0x5b, 0x98, 0x28, 0x05,
7277 0x18, 0x8a, 0x9f, 0xda, 0xb7, 0xb6, 0xe5, 0x5b, 0x3a, 0x52, 0x49, 0xcc, 0x72, 0xff, 0x61,
7278 0x91, 0x95, 0xa2, 0xa1, 0x5d, 0xd5, 0xc4, 0x7d, 0xb1, 0x0b, 0xda, 0xa9, 0xa2, 0x97, 0x1e,
7279 0x7e, 0xe9, 0xa2, 0xd6, 0xdd, 0x0e, 0x94, 0x21, 0xa4, 0x67, 0x92, 0xad, 0x46, 0xab, 0xe1,
7280 0xe2, 0x3b, 0x21, 0x69, 0x2a, 0x1e, 0xd3, 0xea, 0xa4, 0xdf, 0xa6, 0xd2, 0xff, 0x01, 0xfe,
7281 0xff, 0x01, 0xff, 0x01, 0x01, 0x02, 0xff, 0xff, 0x01, 0xfe, 0x02, 0x01, 0xff, 0x1f, 0x25,
7282 ];
7283 const Q6_K_SIGNED_GOLDEN: [f32; 256] = [
7284 0.320068, 0.100021, 0.0200043, -0.42009, 0.440094, 0.640137, 0.0200043, 0.640137,
7285 -0.0400085, -0.620132, -0.260056, -0.42009, -0.100021, 0.260056, -0.380081, -0.0400085,
7286 0.0800171, -0.300064, -0.360077, 0.0400085, 0.340073, -0.240051, -0.300064, -0.0600128,
7287 0.120026, -0.220047, -0.14003, -0.100021, -0.440094, -0.0800171, -0.220047, 0.620132,
7288 -0.200043, 0.200043, 0.160034, -0.440094, -0.480103, -0.160034, 0.28006, -0.240051,
7289 -0.28006, -1.16025, -0.160034, 0.120026, 0.160034, 0.160034, -0.120026, -0.0800171,
7290 0.340073, -0.0600128, -0.620132, 0.400085, -0.440094, 0.56012, 0.640137, 0.300064,
7291 0.360077, 0.640137, -0.440094, 0.100021, 0.100021, -0.380081, 0.640137, -0.240051,
7292 -0.300064, 0.100021, 0.42009, -0.240051, -0.240051, 0.200043, -0.580124, -0.300064,
7293 -0.340073, -0.180038, -0.0600128, 0.620132, 0.360077, 0.0, -0.0800171, 0.340073, 0.180038,
7294 0.360077, 0.56012, -0.400085, -0.620132, -0.0, 0.0400085, 0.120026, -0.240051, -0.100021,
7295 0.220047, 0.240051, 0.540115, -0.620132, -0.620132, 0.580124, 0.240051, 0.320068,
7296 -0.120026, -0.180038, 0.0800171, -0.380081, -0.620132, -0.440094, 0.0400085, 0.260056,
7297 0.620132, 0.14003, 0.180038, 0.620132, -0.320068, -0.380081, -0.220047, -0.0400085,
7298 0.620132, -0.14003, 0.520111, -0.180038, 0.200043, 0.28006, 0.220047, 0.300064, -0.28006,
7299 0.580124, 0.400085, -0.28006, 0.200043, -0.42009, 0.0400085, -0.480103, 0.28006, 1.20026,
7300 0.600128, 0.28006, -0.360077, 0.160034, 0.480103, -0.0400085, 0.0400085, -0.680145,
7301 -0.360077, -0.720154, 0.760162, 0.200043, 0.28006, -0.0800171, -0.580124, 0.0800171,
7302 -0.260056, -0.380081, 0.0200043, 0.0400085, -0.0800171, -0.300064, -0.400085, -0.0,
7303 0.480103, -0.620132, -0.260056, -0.0600128, -0.0600128, -0.240051, 0.640137, 0.160034,
7304 -0.400085, -0.620132, -0.0600128, 0.600128, 0.0800171, -0.620132, -0.56012, 0.0400085,
7305 0.42009, 0.0600128, 0.0600128, 0.42009, 0.500107, -0.28006, 0.180038, -0.380081, -0.440094,
7306 0.240051, -0.56012, 0.0600128, 0.120026, 0.340073, -0.460098, 0.160034, -0.0600128,
7307 0.600128, -0.300064, -0.440094, 0.200043, -0.360077, -0.520111, 0.360077, 0.160034,
7308 -1.24026, -0.360077, -0.440094, 0.240051, 0.600128, 0.840179, 0.28006, -0.440094,
7309 -0.440094, -0.400085, 0.200043, 0.520111, -0.760162, 0.240051, 0.360077, 0.120026, 1.24026,
7310 0.200043, 0.0, 0.240051, -0.200043, -0.440094, 0.160034, 0.480103, -0.0800171, 0.360077,
7311 -0.160034, 0.620132, 0.0800171, 0.220047, 0.300064, -0.540115, -0.0800171, 0.620132,
7312 0.0200043, 0.56012, 0.360077, -0.640137, 0.28006, -0.440094, 0.100021, -0.160034, 0.0,
7313 -0.0200043, 0.100021, -0.180038, -0.540115, -0.400085, 0.360077, 0.640137, 0.100021,
7314 0.340073, 0.400085, -0.540115, -0.620132, -0.0200043, -0.620132, -0.100021, -0.600128,
7315 ];
7316
7317 #[test]
7318 fn q6_k_signed_scale_dequant_matches_independent_python_reference() {
7319 let got = dequant_q6_k(&Q6_K_SIGNED_TEST_BLOCK).unwrap();
7320 assert_eq!(got.len(), Q6_K_SIGNED_GOLDEN.len());
7321 for (i, (a, b)) in got.iter().zip(Q6_K_SIGNED_GOLDEN.iter()).enumerate() {
7322 assert!(
7323 (a - b).abs() < 1e-3,
7324 "Q6_K signed-scale element {i}: rust={a} python={b}"
7325 );
7326 }
7327 }
7328
7329 #[test]
7330 fn q6_k_signed_scale_fused_dot_matches_dequant_then_dot() {
7331 let dequanted = dequant_q6_k(&Q6_K_SIGNED_TEST_BLOCK).unwrap();
7332 let x: Vec<f32> = (0..256).map(|i| ((i as f32) * 0.023).sin()).collect();
7333 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
7334 let fused = dot_q6_k_f32(&Q6_K_SIGNED_TEST_BLOCK, &x);
7335 assert!(
7336 (fused - expected).abs() < 1e-2,
7337 "fused={fused} expected={expected}"
7338 );
7339 }
7340
7341 #[test]
7342 fn dispatched_q6_k_matches_scalar_on_signed_scales() {
7343 let n_blocks = 4;
7348 let packed = repeat_block(&Q6_K_SIGNED_TEST_BLOCK, n_blocks);
7349 let x: Vec<f32> = (0..256 * n_blocks)
7350 .map(|i| ((i as f32) * 0.019).sin())
7351 .collect();
7352 let dispatched = dot_q6_k_f32(&packed, &x);
7353 let scalar = dot_q6_k_f32_scalar(&packed, &x);
7354 assert!(
7355 (dispatched - scalar).abs() < 1e-1,
7356 "dispatched={dispatched} scalar={scalar}"
7357 );
7358 }
7359
7360 #[test]
7361 fn q6_k_dequant_matches_python_reference_with_negative_scales() {
7362 let got = dequant_q6_k(&Q6_K_SIGNED_SCALES_TEST_BLOCK).unwrap();
7367 assert_eq!(got.len(), Q6_K_SIGNED_SCALES_GOLDEN.len());
7368 for (i, (a, b)) in got.iter().zip(Q6_K_SIGNED_SCALES_GOLDEN.iter()).enumerate() {
7369 assert!(
7370 (a - b).abs() < 1e-3,
7371 "Q6_K signed-scale element {i}: rust={a} python={b}"
7372 );
7373 }
7374 }
7375
7376 #[test]
7377 fn q6_k_fused_dot_matches_dequant_then_dot_with_negative_scales() {
7378 let dequanted = dequant_q6_k(&Q6_K_SIGNED_SCALES_TEST_BLOCK).unwrap();
7379 let x: Vec<f32> = (0..256).map(|i| ((i as f32) * 0.023).sin()).collect();
7380 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
7381 let fused = dot_q6_k_f32(&Q6_K_SIGNED_SCALES_TEST_BLOCK, &x);
7382 assert!(
7383 (fused - expected).abs() < 1e-2,
7384 "fused={fused} expected={expected}"
7385 );
7386 }
7387
7388 #[test]
7389 fn q6_k_scalar_dot_matches_python_reference_with_negative_scales() {
7390 let x: Vec<f32> = (0..256).map(|i| ((i as f32) * 0.023).sin()).collect();
7395 let expected: f32 = Q6_K_SIGNED_SCALES_GOLDEN
7396 .iter()
7397 .zip(x.iter())
7398 .map(|(a, b)| a * b)
7399 .sum();
7400 let scalar = dot_q6_k_f32_scalar(&Q6_K_SIGNED_SCALES_TEST_BLOCK, &x);
7401 assert!(
7402 (scalar - expected).abs() < 1e-2,
7403 "scalar={scalar} expected={expected}"
7404 );
7405 }
7406
7407 #[test]
7408 fn q4_k_and_q6_k_reject_misaligned_buffers() {
7409 let bad = vec![0u8; 5];
7410 assert!(dequant_q4_k(&bad).is_err());
7411 assert!(dequant_q6_k(&bad).is_err());
7412 }
7413
7414 const Q2_K_TEST_BLOCK: [u8; 84] = [
7421 0x92, 0x32, 0xc9, 0x0e, 0x0f, 0xf8, 0x10, 0xf0, 0xd1, 0x82, 0xca, 0x81, 0x7f, 0x11, 0xdb,
7422 0xff, 0x78, 0xf8, 0xab, 0xc5, 0x60, 0x0c, 0xc0, 0xbc, 0xa6, 0x52, 0x56, 0x1b, 0xc0, 0x36,
7423 0x6b, 0x6e, 0xbb, 0x53, 0x32, 0x90, 0x0a, 0x41, 0x67, 0x97, 0x48, 0x76, 0x86, 0x23, 0xd5,
7424 0x8e, 0x9e, 0x02, 0xc1, 0x1b, 0xea, 0x9c, 0xb7, 0x55, 0xc3, 0x1b, 0xf4, 0x59, 0xc6, 0xef,
7425 0x11, 0x61, 0xbc, 0x54, 0xd7, 0x8a, 0x6d, 0xed, 0x9e, 0xe7, 0x48, 0x69, 0x8e, 0x3a, 0x30,
7426 0x6c, 0xd8, 0xdc, 0x85, 0xc1, 0xec, 0x35, 0x14, 0x32,
7427 ];
7428 const Q2_K_GOLDEN: [f32; 256] = [
7429 -1.70947, -1.70947, 0.51123, -0.969238, -1.70947, -1.70947, -1.70947, -1.70947, -0.229004,
7430 -0.229004, -0.229004, 0.51123, -1.70947, -0.229004, 0.51123, -0.229004, 1.65088, 1.65088,
7431 0.910645, -0.569824, 0.910645, 0.17041, 1.65088, 1.65088, -0.569824, 0.910645, 0.910645,
7432 1.65088, 0.17041, 0.910645, 0.910645, 0.910645, 4.38281, 4.38281, 4.38281, 1.05176,
7433 -2.2793, 7.71387, -2.2793, 7.71387, 1.05176, -2.2793, 1.05176, 4.38281, -2.2793, 1.05176,
7434 4.38281, 7.71387, 10.3633, 0.0, 0.0, 0.0, 10.3633, 0.0, 5.18164, 5.18164, 10.3633, 5.18164,
7435 5.18164, 0.0, 5.18164, 15.5449, 15.5449, 0.0, 16.6553, 16.6553, 11.1035, 0.0, 11.1035, 0.0,
7436 0.0, 16.6553, 11.1035, 5.55176, 5.55176, 5.55176, 0.0, 16.6553, 11.1035, 11.1035, 6.03369,
7437 0.111816, 6.03369, 0.111816, -2.84912, -2.84912, 3.07275, 0.111816, -2.84912, 6.03369,
7438 -2.84912, 3.07275, 0.111816, -2.84912, 0.111816, -2.84912, -0.189941, -0.189941, -0.189941,
7439 -0.189941, -0.189941, -0.189941, -0.189941, -0.189941, -0.189941, -0.189941, -0.189941,
7440 -0.189941, -0.189941, -0.189941, -0.189941, -0.189941, -2.84912, -2.84912, -2.84912,
7441 -2.84912, -2.84912, -2.84912, -2.84912, -2.84912, -2.84912, -2.84912, -2.84912, -2.84912,
7442 -2.84912, -2.84912, -2.84912, -2.84912, -2.09912, -1.35889, -1.729, -2.46924, -1.35889,
7443 -2.09912, -1.35889, -1.35889, -2.46924, -2.09912, -1.729, -1.35889, -2.09912, -2.09912,
7444 -2.46924, -2.46924, 0.701172, -0.0390625, -0.779297, -0.779297, -0.0390625, 0.701172,
7445 -1.51953, -0.779297, -0.0390625, -0.0390625, -1.51953, -1.51953, -1.51953, -1.51953,
7446 -0.779297, -0.779297, -2.2793, 5.12305, 5.12305, 8.82422, 1.42188, 1.42188, -2.2793,
7447 5.12305, 1.42188, 5.12305, 1.42188, 8.82422, -2.2793, -2.2793, 8.82422, 1.42188, -1.14941,
7448 -0.779297, -0.40918, -0.40918, -0.40918, -1.14941, -0.779297, -0.779297, -0.40918,
7449 -0.779297, -1.51953, -0.40918, -0.779297, -0.40918, -1.14941, -1.51953, -1.32959, 4.22217,
7450 9.77393, 4.22217, 15.3257, 4.22217, -1.32959, 4.22217, 15.3257, 4.22217, -1.32959, 9.77393,
7451 4.22217, 9.77393, 15.3257, 4.22217, 0.180176, -0.189941, 0.550293, 0.550293, 0.180176,
7452 0.550293, -0.189941, 0.550293, -0.189941, 0.92041, 0.92041, 0.550293, 0.180176, 0.180176,
7453 -0.189941, -0.189941, 9.74463, -2.46924, 9.74463, 5.67334, 5.67334, 1.60205, 9.74463,
7454 -2.46924, 9.74463, 1.60205, 9.74463, 9.74463, -2.46924, 1.60205, 5.67334, 1.60205, 13.8062,
7455 8.25439, 2.70264, 13.8062, 8.25439, 13.8062, 2.70264, 2.70264, 8.25439, -2.84912, -2.84912,
7456 2.70264, 13.8062, 13.8062, 8.25439, 13.8062,
7457 ];
7458
7459 const Q3_K_TEST_BLOCK: [u8; 110] = [
7460 0x56, 0xf2, 0xb4, 0x2b, 0xd5, 0x6f, 0x51, 0x71, 0x3c, 0x0a, 0xb9, 0x1d, 0xd0, 0xb9, 0x3b,
7461 0xb3, 0x0f, 0xff, 0x8c, 0xb2, 0x83, 0x3a, 0x3d, 0x24, 0xb1, 0x12, 0x56, 0xe3, 0x23, 0x54,
7462 0xf2, 0xfa, 0x7f, 0xdf, 0x31, 0xe1, 0x18, 0x26, 0x6e, 0xcd, 0x5b, 0x38, 0xee, 0xbd, 0x9f,
7463 0x8c, 0x57, 0x47, 0x0b, 0x11, 0xcb, 0xfb, 0xb4, 0x83, 0xa0, 0x4e, 0x0b, 0xd4, 0xa7, 0x85,
7464 0xe0, 0x60, 0xf3, 0xb3, 0xe3, 0x95, 0x43, 0xc6, 0x05, 0x05, 0x77, 0x53, 0xed, 0x23, 0xcc,
7465 0x6a, 0x0e, 0x89, 0xa1, 0x79, 0x85, 0xf6, 0x6e, 0x5a, 0x23, 0x63, 0xbe, 0x53, 0xfa, 0xa2,
7466 0x2b, 0xe9, 0xcd, 0xce, 0xf8, 0x3d, 0x6f, 0xd0, 0x42, 0x6e, 0x3b, 0x7f, 0x23, 0x26, 0xd3,
7467 0xb9, 0x18, 0xbf, 0xa4, 0x34,
7468 ];
7469 const Q3_K_GOLDEN: [f32; 256] = [
7470 -8.99121, -8.99121, -26.9736, 8.99121, 0.0, 17.9824, 17.9824, 8.99121, -8.99121, -35.9648,
7471 17.9824, 8.99121, -8.99121, 0.0, 26.9736, 26.9736, -13.9219, -4.64062, 4.64062, 4.64062,
7472 -0.0, 4.64062, -0.0, 9.28125, -13.9219, 18.5625, 4.64062, -4.64062, -0.0, 18.5625, 4.64062,
7473 4.64062, -26.1035, -26.1035, 34.8047, -0.0, 17.4023, -8.70117, 8.70117, 8.70117, 17.4023,
7474 -17.4023, 8.70117, 8.70117, 8.70117, 8.70117, -8.70117, -8.70117, 17.4023, 0.0, -17.4023,
7475 17.4023, 8.70117, 0.0, -34.8047, -8.70117, -17.4023, 8.70117, 8.70117, 8.70117, 0.0,
7476 -34.8047, 0.0, 0.0, -18.2725, 18.2725, -18.2725, 12.1816, -6.09082, -12.1816, 12.1816,
7477 24.3633, -6.09082, 6.09082, 12.1816, -18.2725, 18.2725, 24.3633, 18.2725, 24.3633, 0.0,
7478 4.35059, 0.0, -4.35059, -4.35059, -17.4023, 8.70117, 0.0, -17.4023, -13.0518, 8.70117,
7479 -17.4023, -8.70117, 8.70117, -4.35059, -4.35059, -2.61035, -0.870117, -3.48047, 2.61035,
7480 -3.48047, 0.0, -2.61035, -0.870117, 0.870117, 0.0, 2.61035, 1.74023, -1.74023, 1.74023,
7481 0.870117, -2.61035, 0.0, 0.0, 19.1426, -6.38086, -12.7617, 12.7617, 12.7617, -19.1426,
7482 -25.5234, -6.38086, -12.7617, -12.7617, -6.38086, -19.1426, -6.38086, 12.7617, -8.70117,
7483 -2.90039, -8.70117, 5.80078, -2.90039, 8.70117, -8.70117, -8.70117, -2.90039, 2.90039,
7484 -0.0, -5.80078, -5.80078, -2.90039, -2.90039, -2.90039, -25.2334, 16.8223, -16.8223,
7485 16.8223, -8.41113, 25.2334, 16.8223, -8.41113, 16.8223, 16.8223, 25.2334, -25.2334,
7486 -25.2334, 16.8223, 0.0, 8.41113, 13.9219, -3.48047, -0.0, -3.48047, 10.4414, -3.48047,
7487 10.4414, -0.0, -10.4414, 13.9219, -10.4414, 6.96094, 3.48047, -6.96094, -0.0, -6.96094,
7488 -19.1426, 6.38086, -6.38086, 12.7617, -25.5234, 0.0, 19.1426, 0.0, 12.7617, -25.5234,
7489 -12.7617, 12.7617, 19.1426, -6.38086, 12.7617, 19.1426, 11.0215, 5.51074, -22.043, -22.043,
7490 0.0, 0.0, 16.5322, 5.51074, -11.0215, -11.0215, -22.043, -11.0215, 0.0, -22.043, -11.0215,
7491 -5.51074, -8.12109, 6.09082, -4.06055, -6.09082, -4.06055, -4.06055, -2.03027, -6.09082,
7492 -2.03027, -4.06055, 4.06055, 4.06055, -8.12109, 0.0, 6.09082, 6.09082, 8.70117, -17.4023,
7493 -8.70117, 8.70117, -0.0, 34.8047, 26.1035, 26.1035, 8.70117, 34.8047, -26.1035, 26.1035,
7494 -0.0, -17.4023, 17.4023, -8.70117, -1.16016, 1.74023, 0.580078, 0.580078, 0.0, -1.74023,
7495 -1.16016, -1.74023, 1.74023, -1.16016, -2.32031, 1.74023, -0.580078, -0.580078, 1.74023,
7496 0.0,
7497 ];
7498
7499 #[test]
7500 fn q2_k_dequant_matches_independent_python_reference() {
7501 let got = dequant_q2_k(&Q2_K_TEST_BLOCK).unwrap();
7502 assert_eq!(got.len(), Q2_K_GOLDEN.len());
7503 for (i, (a, b)) in got.iter().zip(Q2_K_GOLDEN.iter()).enumerate() {
7504 assert!(
7505 (a - b).abs() < 1e-3,
7506 "Q2_K element {i}: rust={a} python={b}"
7507 );
7508 }
7509 }
7510
7511 #[test]
7512 fn q2_k_fused_dot_matches_dequant_then_dot() {
7513 let dequanted = dequant_q2_k(&Q2_K_TEST_BLOCK).unwrap();
7514 let x: Vec<f32> = (0..256).map(|i| ((i as f32) * 0.019).sin()).collect();
7515 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
7516 let fused = dot_q2_k_f32(&Q2_K_TEST_BLOCK, &x);
7517 assert!(
7518 (fused - expected).abs() < 1e-1,
7519 "fused={fused} expected={expected}"
7520 );
7521 }
7522
7523 #[test]
7524 fn q3_k_dequant_matches_independent_python_reference() {
7525 let got = dequant_q3_k(&Q3_K_TEST_BLOCK).unwrap();
7526 assert_eq!(got.len(), Q3_K_GOLDEN.len());
7527 for (i, (a, b)) in got.iter().zip(Q3_K_GOLDEN.iter()).enumerate() {
7528 assert!(
7529 (a - b).abs() < 1e-3,
7530 "Q3_K element {i}: rust={a} python={b}"
7531 );
7532 }
7533 }
7534
7535 #[test]
7536 fn q3_k_fused_dot_matches_dequant_then_dot() {
7537 let dequanted = dequant_q3_k(&Q3_K_TEST_BLOCK).unwrap();
7538 let x: Vec<f32> = (0..256).map(|i| ((i as f32) * 0.023).cos()).collect();
7539 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
7540 let fused = dot_q3_k_f32(&Q3_K_TEST_BLOCK, &x);
7541 assert!(
7542 (fused - expected).abs() < 1e-1,
7543 "fused={fused} expected={expected}"
7544 );
7545 }
7546
7547 #[test]
7548 fn q2_k_and_q3_k_reject_misaligned_buffers() {
7549 let bad = vec![0u8; 5];
7550 assert!(dequant_q2_k(&bad).is_err());
7551 assert!(dequant_q3_k(&bad).is_err());
7552 }
7553
7554 const IQ4_NL_TEST_BLOCK: [u8; 18] = [
7560 0xf6, 0x34, 0x3c, 0x7f, 0x90, 0x6a, 0xdc, 0x0f, 0x77, 0xfc, 0xb9, 0x1c, 0xdf, 0x74, 0xe0,
7561 0x40, 0x5d, 0xf3,
7562 ];
7563 const IQ4_NL_GOLDEN: [f32; 32] = [
7564 16.4331, 35.0366, -39.3774, 7.75146, 16.4331, 35.0366, -3.10059, 16.4331, 4.03076, 16.4331,
7565 35.0366, -15.1929, -39.3774, -39.3774, 21.394, -20.1538, -20.1538, -3.10059, 4.03076,
7566 -6.82129, 21.394, -39.3774, -3.10059, 35.0366, 11.7822, -32.2461, 21.394, -3.10059,
7567 27.5952, -15.1929, -10.8521, 35.0366,
7568 ];
7569
7570 const IQ4_XS_TEST_BLOCK: [u8; 136] = [
7571 0x5c, 0x33, 0xb4, 0x39, 0xd1, 0x64, 0x97, 0x82, 0xcb, 0xbd, 0x88, 0x95, 0xf3, 0x60, 0x2a,
7572 0xb5, 0xe7, 0x24, 0xd3, 0xee, 0xfe, 0x71, 0x13, 0xbe, 0x70, 0x84, 0x48, 0x79, 0x7b, 0x3e,
7573 0xf0, 0x55, 0xdc, 0xb2, 0xb2, 0xde, 0x32, 0xa1, 0x5b, 0x02, 0x01, 0xdc, 0x2a, 0xbb, 0xf7,
7574 0x0b, 0x8a, 0x88, 0xdd, 0x0b, 0x02, 0x7e, 0x5e, 0x76, 0x87, 0x30, 0x1e, 0x1c, 0xcf, 0x48,
7575 0xd7, 0x61, 0xf3, 0x51, 0x52, 0x17, 0x98, 0x0a, 0x87, 0xcf, 0x02, 0x91, 0xc8, 0xee, 0xc0,
7576 0x91, 0x69, 0x2a, 0x4f, 0x64, 0x68, 0xa7, 0xb2, 0xe6, 0x98, 0x21, 0x81, 0x75, 0x53, 0x2a,
7577 0x8d, 0x12, 0xae, 0xe0, 0xea, 0x0c, 0x75, 0xff, 0x22, 0x5e, 0x25, 0x19, 0xda, 0x2e, 0x51,
7578 0x4e, 0x81, 0xdc, 0x0e, 0x78, 0x86, 0xd7, 0x58, 0xb5, 0xb7, 0xf6, 0x45, 0xa9, 0x0a, 0x83,
7579 0xfd, 0x2a, 0x12, 0x7d, 0xf0, 0x12, 0x97, 0xe2, 0xfe, 0xf4, 0xd0, 0xa2, 0x11, 0x14, 0x78,
7580 0xdb,
7581 ];
7582 const IQ4_XS_GOLDEN: [f32; 256] = [
7583 -270.917, -491.928, -7.12939, 249.529, 463.411, 905.433, -178.235, 249.529, 71.2939,
7584 349.34, 463.411, -634.516, -634.516, 741.457, 463.411, -634.516, -377.858, -270.917,
7585 -7.12939, -92.6821, -805.622, 156.847, 591.74, -270.917, -634.516, 591.74, -491.928,
7586 -634.516, -805.622, 71.2939, 741.457, -270.917, 87.6226, 33.8071, -0.689941, -8.96924,
7587 -26.2178, -61.4048, 87.6226, 24.1479, -36.5669, 57.2651, 57.2651, -61.4048, 57.2651,
7588 71.7539, -26.2178, 57.2651, 6.89941, -0.689941, 33.8071, 6.89941, 6.89941, 44.8462,
7589 -77.9634, 24.1479, -47.606, -26.2178, -26.2178, -47.606, 44.8462, -17.2485, 24.1479,
7590 87.6226, -478.359, 243.779, 114.99, 174.785, -45.9961, 174.785, 114.99, 4.59961, 317.373,
7591 174.785, -381.768, 409.365, 409.365, -101.191, -45.9961, -584.15, -584.15, 317.373,
7592 -381.768, 174.785, 519.756, -584.15, 4.59961, 4.59961, 317.373, -584.15, -584.15, -45.9961,
7593 -160.986, -45.9961, 4.59961, -298.975, 122.81, 73.1338, 155.927, 1.37988, -13.7988,
7594 -143.508, -89.6924, -143.508, -114.53, -13.7988, 1.37988, 34.4971, -13.7988, 155.927,
7595 -114.53, -143.508, -143.508, -143.508, 73.1338, -67.6143, 95.2119, -30.3574, 155.927,
7596 -48.2959, -48.2959, -143.508, 17.9385, -175.245, 1.37988, 73.1338, -175.245, 17.9385,
7597 -2.06982, -184.214, 262.868, 215.262, -26.9077, -51.7456, -233.89, 101.421, -2.06982,
7598 20.6982, 171.795, 45.5361, -2.06982, 215.262, 215.262, 72.4438, -109.701, -184.214,
7599 -109.701, -26.9077, 45.5361, 171.795, 101.421, 45.5361, 45.5361, -51.7456, -78.6533,
7600 -184.214, -26.9077, 171.795, -2.06982, 20.6982, -134.539, 51.7456, 142.818, -171.795,
7601 184.214, -262.868, 51.7456, 109.701, -72.4438, 233.89, -171.795, 184.214, -72.4438,
7602 26.9077, 51.7456, 184.214, -72.4438, -171.795, 2.06982, -215.262, 51.7456, 184.214,
7603 184.214, -262.868, -20.6982, 233.89, -171.795, -72.4438, -171.795, -215.262, 142.818,
7604 -171.795, -430.523, 368.429, -430.523, 219.401, 368.429, 4.13965, -91.0723, -41.3965,
7605 4.13965, -144.888, -41.3965, -91.0723, -144.888, 53.8154, 103.491, -269.077, -144.888,
7606 -202.843, 4.13965, 285.636, -525.735, -41.3965, 4.13965, 285.636, -144.888, 157.307,
7607 157.307, 467.78, -202.843, 103.491, -525.735, 4.13965, -380.848, -137.988, 458.121,
7608 -380.848, 700.98, 458.121, 55.1953, 458.121, -491.238, 270.457, 700.98, 458.121, 574.031,
7609 270.457, -5.51953, -209.742, -623.707, 458.121, 574.031, 55.1953, -623.707, 574.031,
7610 -71.7539, -491.238, -623.707, -623.707, -380.848, -137.988, 574.031, 574.031, 55.1953,
7611 -380.848,
7612 ];
7613
7614 #[test]
7615 fn iq4_nl_dequant_matches_independent_python_reference() {
7616 let got = dequant_iq4_nl(&IQ4_NL_TEST_BLOCK).unwrap();
7617 assert_eq!(got.len(), IQ4_NL_GOLDEN.len());
7618 for (i, (a, b)) in got.iter().zip(IQ4_NL_GOLDEN.iter()).enumerate() {
7619 assert!(
7620 (a - b).abs() < 1e-2,
7621 "IQ4_NL element {i}: rust={a} python={b}"
7622 );
7623 }
7624 }
7625
7626 #[test]
7627 fn iq4_nl_fused_dot_matches_dequant_then_dot() {
7628 let dequanted = dequant_iq4_nl(&IQ4_NL_TEST_BLOCK).unwrap();
7629 let x: Vec<f32> = (0..32).map(|i| ((i as f32) * 0.019).sin()).collect();
7630 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
7631 let fused = dot_iq4_nl_f32(&IQ4_NL_TEST_BLOCK, &x);
7632 assert!(
7633 (fused - expected).abs() < 1e-1,
7634 "fused={fused} expected={expected}"
7635 );
7636 }
7637
7638 #[test]
7639 fn iq4_xs_dequant_matches_independent_python_reference() {
7640 let got = dequant_iq4_xs(&IQ4_XS_TEST_BLOCK).unwrap();
7641 assert_eq!(got.len(), IQ4_XS_GOLDEN.len());
7642 for (i, (a, b)) in got.iter().zip(IQ4_XS_GOLDEN.iter()).enumerate() {
7643 assert!(
7644 (a - b).abs() < 1e-1,
7645 "IQ4_XS element {i}: rust={a} python={b}"
7646 );
7647 }
7648 }
7649
7650 #[test]
7651 fn iq4_xs_fused_dot_matches_dequant_then_dot() {
7652 let dequanted = dequant_iq4_xs(&IQ4_XS_TEST_BLOCK).unwrap();
7653 let x: Vec<f32> = (0..256).map(|i| ((i as f32) * 0.023).cos()).collect();
7654 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
7655 let fused = dot_iq4_xs_f32(&IQ4_XS_TEST_BLOCK, &x);
7656 assert!(
7657 (fused - expected).abs() < 1e-1,
7658 "fused={fused} expected={expected}"
7659 );
7660 }
7661
7662 #[test]
7663 fn iq4_nl_and_iq4_xs_reject_misaligned_buffers() {
7664 let bad = vec![0u8; 5];
7665 assert!(dequant_iq4_nl(&bad).is_err());
7666 assert!(dequant_iq4_xs(&bad).is_err());
7667 }
7668
7669 const MXFP4_TEST_PACKED: [u8; 32] = [
7674 0xaa, 0xf9, 0x12, 0xda, 0x04, 0xac, 0xce, 0x2d, 0xbf, 0x4c, 0xc3, 0x06, 0x67, 0x59, 0xd1,
7675 0xa3, 0xea, 0xf1, 0x8f, 0x5d, 0xe5, 0xe6, 0x9e, 0x77, 0x73, 0x9c, 0x6f, 0x14, 0x5f, 0x1f,
7676 0xd9, 0x5e,
7677 ];
7678 const MXFP4_TEST_SCALES: [u8; 2] = [0x00, 0x82];
7679 const MXFP4_GOLDEN: [f32; 64] = [
7680 -5.87747e-39,
7681 -2.93874e-39,
7682 5.87747e-39,
7683 -5.87747e-39,
7684 1.17549e-38,
7685 -1.17549e-38,
7686 -2.35099e-38,
7687 -1.76324e-38,
7688 -3.52648e-38,
7689 -1.17549e-38,
7690 8.81621e-39,
7691 2.35099e-38,
7692 3.52648e-38,
7693 -2.93874e-39,
7694 2.93874e-39,
7695 8.81621e-39,
7696 -5.87747e-39,
7697 -3.52648e-38,
7698 2.93874e-39,
7699 -1.76324e-38,
7700 0.0,
7701 -5.87747e-39,
7702 -1.17549e-38,
7703 5.87747e-39,
7704 -8.81621e-39,
7705 1.17549e-38,
7706 -1.17549e-38,
7707 0.0,
7708 2.35099e-38,
7709 1.76324e-38,
7710 -1.76324e-38,
7711 -5.87747e-39,
7712 -8.0,
7713 4.0,
7714 -48.0,
7715 -24.0,
7716 24.0,
7717 32.0,
7718 -32.0,
7719 48.0,
7720 12.0,
7721 -16.0,
7722 -48.0,
7723 16.0,
7724 -48.0,
7725 -48.0,
7726 -4.0,
7727 -32.0,
7728 -32.0,
7729 -48.0,
7730 -0.0,
7731 24.0,
7732 -32.0,
7733 -32.0,
7734 -4.0,
7735 48.0,
7736 48.0,
7737 -4.0,
7738 32.0,
7739 4.0,
7740 24.0,
7741 4.0,
7742 -24.0,
7743 24.0,
7744 ];
7745
7746 #[test]
7747 fn mxfp4_dequant_matches_independent_python_reference() {
7748 let got = dequant_mxfp4_row(&MXFP4_TEST_PACKED, &MXFP4_TEST_SCALES).unwrap();
7749 assert_eq!(got.len(), MXFP4_GOLDEN.len());
7750 for (i, (a, b)) in got.iter().zip(MXFP4_GOLDEN.iter()).enumerate() {
7751 let tol = 1e-38f32.max(b.abs() * 1e-3);
7752 assert!(
7753 (a - b).abs() < tol,
7754 "MXFP4 element {i}: rust={a} python={b}"
7755 );
7756 }
7757 }
7758
7759 #[test]
7760 fn mxfp4_fused_dot_matches_dequant_then_dot() {
7761 let dequanted = dequant_mxfp4_row(&MXFP4_TEST_PACKED, &MXFP4_TEST_SCALES).unwrap();
7762 let x: Vec<f32> = (0..64).map(|i| ((i as f32) * 0.037).sin()).collect();
7763 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
7764 let fused = dot_mxfp4_row_f32(&MXFP4_TEST_PACKED, &MXFP4_TEST_SCALES, &x);
7765 assert!(
7766 (fused - expected).abs() < 1e-3,
7767 "fused={fused} expected={expected}"
7768 );
7769 }
7770
7771 #[test]
7772 fn mxfp4_scale_byte_zero_and_max_match_the_e8m0_formula() {
7773 assert!((e8m0_scale(0) - 2f32.powi(-127)).abs() < 1e-45);
7776 assert_eq!(e8m0_scale(127), 1.0);
7777 assert_eq!(e8m0_scale(128), 2.0);
7778 }
7779
7780 #[test]
7781 fn mxfp4_simd_dispatch_matches_scalar_across_every_possible_packed_byte_value() {
7782 let packed: Vec<u8> = (0..=255u8).collect();
7793 let n_groups = packed.len() / (MXFP4_GROUP_SIZE / 2);
7794 let scales: Vec<u8> = (0..n_groups).map(|i| ((i * 17 + 3) % 180) as u8).collect();
7800 let x: Vec<f32> = (0..n_groups * MXFP4_GROUP_SIZE)
7801 .map(|i| ((i as f32) * 0.013).cos())
7802 .collect();
7803
7804 let scalar = dot_mxfp4_row_f32_scalar(&packed, &scales, &x);
7805 let dispatched = dot_mxfp4_row_f32(&packed, &scales, &x);
7806 assert!(
7807 (scalar - dispatched).abs() < scalar.abs() * 1e-3 + 1e-3,
7808 "scalar={scalar} dispatched (SIMD)={dispatched}"
7809 );
7810
7811 #[cfg(target_arch = "aarch64")]
7812 {
7813 let neon = unsafe { simd_aarch64::dot_mxfp4_row_f32_neon(&packed, &scales, &x) };
7814 assert!(
7815 (scalar - neon).abs() < scalar.abs() * 1e-3 + 1e-3,
7816 "scalar={scalar} neon={neon}"
7817 );
7818 }
7819 }
7820
7821 #[test]
7822 fn mxfp4_rejects_a_packed_scales_length_mismatch() {
7823 let bad_packed = vec![0u8; 15]; let scales = [0u8; 1];
7825 assert!(matches!(
7826 dequant_mxfp4_row(&bad_packed, &scales),
7827 Err(QuantError::Mxfp4RowMismatch(15, 16))
7828 ));
7829 }
7830
7831 fn repeat_block(block: &[u8], n: usize) -> Vec<u8> {
7834 block
7835 .iter()
7836 .copied()
7837 .cycle()
7838 .take(block.len() * n)
7839 .collect()
7840 }
7841
7842 #[test]
7843 fn dispatched_q4_k_matches_scalar_reference_across_many_blocks() {
7844 let n_blocks = 4;
7845 let packed = repeat_block(&Q4_K_TEST_BLOCK, n_blocks);
7846 let x: Vec<f32> = (0..256 * n_blocks)
7847 .map(|i| ((i as f32) * 0.013).sin())
7848 .collect();
7849 let dispatched = dot_q4_k_f32(&packed, &x);
7850 let scalar = dot_q4_k_f32_scalar(&packed, &x);
7851 assert!(
7852 (dispatched - scalar).abs() < 1e-1,
7853 "dispatched={dispatched} scalar={scalar}"
7854 );
7855 }
7856
7857 #[test]
7858 fn dispatched_q5_k_matches_scalar_reference_across_many_blocks() {
7859 let n_blocks = 4;
7860 let packed = repeat_block(&Q5_K_TEST_BLOCK, n_blocks);
7861 let x: Vec<f32> = (0..256 * n_blocks)
7862 .map(|i| ((i as f32) * 0.011).cos())
7863 .collect();
7864 let dispatched = dot_q5_k_f32(&packed, &x);
7865 let scalar = dot_q5_k_f32_scalar(&packed, &x);
7866 assert!(
7867 (dispatched - scalar).abs() < 1e-1,
7868 "dispatched={dispatched} scalar={scalar}"
7869 );
7870 }
7871
7872 #[test]
7873 fn dispatched_q6_k_matches_scalar_reference_across_many_blocks() {
7874 let n_blocks = 4;
7875 let packed = repeat_block(&Q6_K_TEST_BLOCK, n_blocks);
7876 let x: Vec<f32> = (0..256 * n_blocks)
7877 .map(|i| ((i as f32) * 0.019).sin())
7878 .collect();
7879 let dispatched = dot_q6_k_f32(&packed, &x);
7880 let scalar = dot_q6_k_f32_scalar(&packed, &x);
7881 assert!(
7882 (dispatched - scalar).abs() < 1e-1,
7883 "dispatched={dispatched} scalar={scalar}"
7884 );
7885 }
7886
7887 #[test]
7888 fn dispatched_q6_k_matches_scalar_reference_with_negative_scales() {
7889 let n_blocks = 4;
7894 let packed = repeat_block(&Q6_K_SIGNED_SCALES_TEST_BLOCK, n_blocks);
7895 let x: Vec<f32> = (0..256 * n_blocks)
7896 .map(|i| ((i as f32) * 0.019).sin())
7897 .collect();
7898 let dispatched = dot_q6_k_f32(&packed, &x);
7899 let scalar = dot_q6_k_f32_scalar(&packed, &x);
7900 assert!(
7901 (dispatched - scalar).abs() < 1e-1,
7902 "dispatched={dispatched} scalar={scalar}"
7903 );
7904 }
7905
7906 #[cfg(target_arch = "aarch64")]
7907 #[test]
7908 fn neon_q4_k_kernel_matches_scalar_directly_when_available() {
7909 if !std::arch::is_aarch64_feature_detected!("neon") {
7910 eprintln!("skipping: host CPU lacks NEON");
7911 return;
7912 }
7913 let n_blocks = 4;
7914 let packed = repeat_block(&Q4_K_TEST_BLOCK, n_blocks);
7915 let x: Vec<f32> = (0..256 * n_blocks)
7916 .map(|i| ((i as f32) * 0.037).cos())
7917 .collect();
7918 let simd = unsafe { simd_aarch64::dot_q4_k_f32_neon(&packed, &x) };
7919 let scalar = dot_q4_k_f32_scalar(&packed, &x);
7920 assert!(
7921 (simd - scalar).abs() < 1e-1,
7922 "NEON Q4_K kernel diverged from scalar: simd={simd} scalar={scalar}"
7923 );
7924 }
7925
7926 #[cfg(target_arch = "aarch64")]
7927 #[test]
7928 fn neon_q5_k_q8_kernel_matches_scalar_directly_when_available() {
7929 if !std::arch::is_aarch64_feature_detected!("neon") {
7930 eprintln!("skipping: host CPU lacks NEON");
7931 return;
7932 }
7933 let n_blocks = 4;
7934 let packed = repeat_block(&Q5_K_TEST_BLOCK, n_blocks);
7935 let x: Vec<f32> = (0..256 * n_blocks)
7936 .map(|i| ((i as f32) * 0.029).sin())
7937 .collect();
7938 let act = quantize_activations_q8_k(&x);
7939 let dispatched = dot_q5_k_q8(&packed, &act);
7940 let scalar = dot_q5_k_q8_scalar(&packed, &act);
7941 assert_eq!(
7942 dispatched,
7943 scalar,
7944 "Q5_K×Q8_K dispatch must match scalar (dotprod={})",
7945 std::arch::is_aarch64_feature_detected!("dotprod")
7946 );
7947 if std::arch::is_aarch64_feature_detected!("dotprod") {
7948 let sdot = unsafe { simd_aarch64::dot_q5_k_q8_neon_sdot(&packed, &act) };
7949 assert_eq!(sdot, scalar, "NEON SDOT Q5_K×Q8_K diverged from scalar");
7950 }
7951 if std::arch::is_aarch64_feature_detected!("neon") {
7952 let neon = unsafe { simd_aarch64::dot_q5_k_q8_neon(&packed, &act) };
7953 assert_eq!(neon, scalar, "NEON widen Q5_K×Q8_K diverged from scalar");
7954 }
7955 }
7956
7957 #[cfg(target_arch = "aarch64")]
7958 #[test]
7959 fn neon_q5_k_kernel_matches_scalar_directly_when_available() {
7960 if !std::arch::is_aarch64_feature_detected!("neon") {
7961 eprintln!("skipping: host CPU lacks NEON");
7962 return;
7963 }
7964 let n_blocks = 4;
7965 let packed = repeat_block(&Q5_K_TEST_BLOCK, n_blocks);
7966 let x: Vec<f32> = (0..256 * n_blocks)
7967 .map(|i| ((i as f32) * 0.029).sin())
7968 .collect();
7969 let simd = unsafe { simd_aarch64::dot_q5_k_f32_neon(&packed, &x) };
7970 let scalar = dot_q5_k_f32_scalar(&packed, &x);
7971 assert!(
7972 (simd - scalar).abs() < 1e-1,
7973 "NEON Q5_K kernel diverged from scalar: simd={simd} scalar={scalar}"
7974 );
7975 }
7976
7977 #[cfg(target_arch = "aarch64")]
7978 #[test]
7979 fn neon_q6_k_kernel_matches_scalar_directly_when_available() {
7980 if !std::arch::is_aarch64_feature_detected!("neon") {
7981 eprintln!("skipping: host CPU lacks NEON");
7982 return;
7983 }
7984 let n_blocks = 4;
7985 let packed = repeat_block(&Q6_K_TEST_BLOCK, n_blocks);
7986 let x: Vec<f32> = (0..256 * n_blocks)
7987 .map(|i| ((i as f32) * 0.041).cos())
7988 .collect();
7989 let simd = unsafe { simd_aarch64::dot_q6_k_f32_neon(&packed, &x) };
7990 let scalar = dot_q6_k_f32_scalar(&packed, &x);
7991 assert!(
7992 (simd - scalar).abs() < 1e-1,
7993 "NEON Q6_K kernel diverged from scalar: simd={simd} scalar={scalar}"
7994 );
7995 }
7996
7997 #[cfg(target_arch = "aarch64")]
7998 #[test]
7999 fn neon_q6_k_kernel_matches_scalar_directly_on_negative_scales() {
8000 if !std::arch::is_aarch64_feature_detected!("neon") {
8001 eprintln!("skipping: host CPU lacks NEON");
8002 return;
8003 }
8004 let n_blocks = 4;
8005 let packed = repeat_block(&Q6_K_SIGNED_SCALES_TEST_BLOCK, n_blocks);
8006 let x: Vec<f32> = (0..256 * n_blocks)
8007 .map(|i| ((i as f32) * 0.041).cos())
8008 .collect();
8009 let simd = unsafe { simd_aarch64::dot_q6_k_f32_neon(&packed, &x) };
8010 let scalar = dot_q6_k_f32_scalar(&packed, &x);
8011 assert!(
8012 (simd - scalar).abs() < 1e-1,
8013 "NEON Q6_K kernel diverged from scalar on negative scales: simd={simd} scalar={scalar}"
8014 );
8015 }
8016
8017 #[test]
8018 fn q4_k_scalar_matches_independent_python_reference_via_dispatch_entrypoint() {
8019 let x: Vec<f32> = (0..256).map(|i| ((i as f32) * 0.017).sin()).collect();
8026 let dequanted = dequant_q4_k(&Q4_K_TEST_BLOCK).unwrap();
8027 let expected: f32 = dequanted.iter().zip(x.iter()).map(|(a, b)| a * b).sum();
8028 let dispatched = dot_q4_k_f32(&Q4_K_TEST_BLOCK, &x);
8029 assert!((dispatched - expected).abs() < 1e-2);
8030 }
8031
8032 fn q4_1_test_block() -> Vec<u8> {
8035 let mut b = Vec::new();
8036 b.extend_from_slice(&f16::from_f32(0.3).to_le_bytes());
8037 b.extend_from_slice(&f16::from_f32(-1.2).to_le_bytes());
8038 b.extend_from_slice(
8039 &(0..16)
8040 .map(|i| (i as u8) | ((15 - i as u8) << 4))
8041 .collect::<Vec<u8>>(),
8042 );
8043 b
8044 }
8045
8046 fn q5_0_test_block() -> Vec<u8> {
8047 let mut b = Vec::new();
8048 b.extend_from_slice(&f16::from_f32(0.4).to_le_bytes());
8049 b.extend_from_slice(&[0xA5, 0x3C, 0x00, 0xFF]);
8050 b.extend_from_slice(
8051 &(0..16)
8052 .map(|i| (i as u8) | ((15 - i as u8) << 4))
8053 .collect::<Vec<u8>>(),
8054 );
8055 b
8056 }
8057
8058 fn q5_1_test_block() -> Vec<u8> {
8059 let mut b = Vec::new();
8060 b.extend_from_slice(&f16::from_f32(0.2).to_le_bytes());
8061 b.extend_from_slice(&f16::from_f32(0.9).to_le_bytes());
8062 b.extend_from_slice(&[0x12, 0x34, 0x56, 0x78]);
8063 b.extend_from_slice(
8064 &(0..16)
8065 .map(|i| (i as u8) | ((15 - i as u8) << 4))
8066 .collect::<Vec<u8>>(),
8067 );
8068 b
8069 }
8070
8071 fn q8_1_test_block() -> Vec<u8> {
8072 let mut b = Vec::new();
8073 b.extend_from_slice(&f16::from_f32(0.6).to_le_bytes());
8074 b.extend_from_slice(&f16::from_f32(0.0).to_le_bytes());
8075 let qs: Vec<i8> = (0..32).map(|i| ((i * 7) % 61) as i8 - 30).collect();
8076 b.extend_from_slice(&i8_to_u8_bytes(&qs));
8077 b
8078 }
8079
8080 #[test]
8081 fn dispatched_matches_scalar_for_the_8_newly_simd_formats_across_many_blocks() {
8082 let n_blocks = 4;
8083
8084 let q4_1 = repeat_block(&q4_1_test_block(), n_blocks);
8085 let x32 = |seed: f32| -> Vec<f32> {
8086 (0..32 * n_blocks)
8087 .map(|i| ((i as f32) * seed).sin())
8088 .collect()
8089 };
8090 let x = x32(0.031);
8091 assert!((dot_q4_1_f32(&q4_1, &x) - dot_q4_1_f32_scalar(&q4_1, &x)).abs() < 1e-1);
8092
8093 let q5_0 = repeat_block(&q5_0_test_block(), n_blocks);
8094 let x = x32(0.037);
8095 assert!((dot_q5_0_f32(&q5_0, &x) - dot_q5_0_f32_scalar(&q5_0, &x)).abs() < 1e-1);
8096
8097 let q5_1 = repeat_block(&q5_1_test_block(), n_blocks);
8098 let x = x32(0.041);
8099 assert!((dot_q5_1_f32(&q5_1, &x) - dot_q5_1_f32_scalar(&q5_1, &x)).abs() < 1e-1);
8100
8101 let q8_1 = repeat_block(&q8_1_test_block(), n_blocks);
8102 let x = x32(0.043);
8103 assert!((dot_q8_1_f32(&q8_1, &x) - dot_q8_1_f32_scalar(&q8_1, &x)).abs() < 1e-1);
8104
8105 let q2_k = repeat_block(&Q2_K_TEST_BLOCK, n_blocks);
8106 let x256 = |seed: f32| -> Vec<f32> {
8107 (0..256 * n_blocks)
8108 .map(|i| ((i as f32) * seed).cos())
8109 .collect()
8110 };
8111 let x = x256(0.013);
8112 assert!((dot_q2_k_f32(&q2_k, &x) - dot_q2_k_f32_scalar(&q2_k, &x)).abs() < 1e-1);
8113
8114 let q3_k = repeat_block(&Q3_K_TEST_BLOCK, n_blocks);
8115 let x = x256(0.017);
8116 assert!((dot_q3_k_f32(&q3_k, &x) - dot_q3_k_f32_scalar(&q3_k, &x)).abs() < 1e-1);
8117
8118 let iq4_nl = repeat_block(&IQ4_NL_TEST_BLOCK, n_blocks);
8119 let x = x32(0.019);
8120 assert!((dot_iq4_nl_f32(&iq4_nl, &x) - dot_iq4_nl_f32_scalar(&iq4_nl, &x)).abs() < 1e-1);
8121
8122 let iq4_xs = repeat_block(&IQ4_XS_TEST_BLOCK, n_blocks);
8123 let x = x256(0.023);
8124 assert!((dot_iq4_xs_f32(&iq4_xs, &x) - dot_iq4_xs_f32_scalar(&iq4_xs, &x)).abs() < 1e-1);
8125 }
8126
8127 #[cfg(target_arch = "aarch64")]
8128 #[test]
8129 fn neon_kernels_match_scalar_directly_for_the_8_newly_simd_formats() {
8130 if !std::arch::is_aarch64_feature_detected!("neon") {
8131 eprintln!("skipping: host CPU lacks NEON");
8132 return;
8133 }
8134 let n_blocks = 4;
8135 let x32 = |seed: f32| -> Vec<f32> {
8136 (0..32 * n_blocks)
8137 .map(|i| ((i as f32) * seed).sin())
8138 .collect()
8139 };
8140 let x256 = |seed: f32| -> Vec<f32> {
8141 (0..256 * n_blocks)
8142 .map(|i| ((i as f32) * seed).cos())
8143 .collect()
8144 };
8145
8146 let q4_1 = repeat_block(&q4_1_test_block(), n_blocks);
8147 let x = x32(0.031);
8148 let simd = unsafe { simd_aarch64::dot_q4_1_f32_neon(&q4_1, &x) };
8149 assert!((simd - dot_q4_1_f32_scalar(&q4_1, &x)).abs() < 1e-1);
8150
8151 let q5_0 = repeat_block(&q5_0_test_block(), n_blocks);
8152 let x = x32(0.037);
8153 let simd = unsafe { simd_aarch64::dot_q5_0_f32_neon(&q5_0, &x) };
8154 assert!((simd - dot_q5_0_f32_scalar(&q5_0, &x)).abs() < 1e-1);
8155
8156 let q5_1 = repeat_block(&q5_1_test_block(), n_blocks);
8157 let x = x32(0.041);
8158 let simd = unsafe { simd_aarch64::dot_q5_1_f32_neon(&q5_1, &x) };
8159 assert!((simd - dot_q5_1_f32_scalar(&q5_1, &x)).abs() < 1e-1);
8160
8161 let q8_1 = repeat_block(&q8_1_test_block(), n_blocks);
8162 let x = x32(0.043);
8163 let simd = unsafe { simd_aarch64::dot_q8_1_f32_neon(&q8_1, &x) };
8164 assert!((simd - dot_q8_1_f32_scalar(&q8_1, &x)).abs() < 1e-1);
8165
8166 let q2_k = repeat_block(&Q2_K_TEST_BLOCK, n_blocks);
8167 let x = x256(0.013);
8168 let simd = unsafe { simd_aarch64::dot_q2_k_f32_neon(&q2_k, &x) };
8169 assert!((simd - dot_q2_k_f32_scalar(&q2_k, &x)).abs() < 1e-1);
8170
8171 let q3_k = repeat_block(&Q3_K_TEST_BLOCK, n_blocks);
8172 let x = x256(0.017);
8173 let simd = unsafe { simd_aarch64::dot_q3_k_f32_neon(&q3_k, &x) };
8174 assert!((simd - dot_q3_k_f32_scalar(&q3_k, &x)).abs() < 1e-1);
8175
8176 let iq4_nl = repeat_block(&IQ4_NL_TEST_BLOCK, n_blocks);
8177 let x = x32(0.019);
8178 let simd = unsafe { simd_aarch64::dot_iq4_nl_f32_neon(&iq4_nl, &x) };
8179 assert!((simd - dot_iq4_nl_f32_scalar(&iq4_nl, &x)).abs() < 1e-1);
8180
8181 let iq4_xs = repeat_block(&IQ4_XS_TEST_BLOCK, n_blocks);
8182 let x = x256(0.023);
8183 let simd = unsafe { simd_aarch64::dot_iq4_xs_f32_neon(&iq4_xs, &x) };
8184 assert!((simd - dot_iq4_xs_f32_scalar(&iq4_xs, &x)).abs() < 1e-1);
8185 }
8186
8187 #[cfg(target_arch = "x86_64")]
8188 #[test]
8189 fn avx2_kernels_match_scalar_directly_for_the_8_newly_simd_formats() {
8190 if !(is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma")) {
8191 eprintln!("skipping: host CPU lacks AVX2+FMA");
8192 return;
8193 }
8194 let n_blocks = 4;
8195 let x32 = |seed: f32| -> Vec<f32> {
8196 (0..32 * n_blocks)
8197 .map(|i| ((i as f32) * seed).sin())
8198 .collect()
8199 };
8200 let x256 = |seed: f32| -> Vec<f32> {
8201 (0..256 * n_blocks)
8202 .map(|i| ((i as f32) * seed).cos())
8203 .collect()
8204 };
8205
8206 let q4_1 = repeat_block(&q4_1_test_block(), n_blocks);
8207 let x = x32(0.031);
8208 let simd = unsafe { simd_x86::dot_q4_1_f32_avx2(&q4_1, &x) };
8209 assert!((simd - dot_q4_1_f32_scalar(&q4_1, &x)).abs() < 1e-1);
8210
8211 let q5_0 = repeat_block(&q5_0_test_block(), n_blocks);
8212 let x = x32(0.037);
8213 let simd = unsafe { simd_x86::dot_q5_0_f32_avx2(&q5_0, &x) };
8214 assert!((simd - dot_q5_0_f32_scalar(&q5_0, &x)).abs() < 1e-1);
8215
8216 let q5_1 = repeat_block(&q5_1_test_block(), n_blocks);
8217 let x = x32(0.041);
8218 let simd = unsafe { simd_x86::dot_q5_1_f32_avx2(&q5_1, &x) };
8219 assert!((simd - dot_q5_1_f32_scalar(&q5_1, &x)).abs() < 1e-1);
8220
8221 let q8_1 = repeat_block(&q8_1_test_block(), n_blocks);
8222 let x = x32(0.043);
8223 let simd = unsafe { simd_x86::dot_q8_1_f32_avx2(&q8_1, &x) };
8224 assert!((simd - dot_q8_1_f32_scalar(&q8_1, &x)).abs() < 1e-1);
8225
8226 let q2_k = repeat_block(&Q2_K_TEST_BLOCK, n_blocks);
8227 let x = x256(0.013);
8228 let simd = unsafe { simd_x86::dot_q2_k_f32_avx2(&q2_k, &x) };
8229 assert!((simd - dot_q2_k_f32_scalar(&q2_k, &x)).abs() < 1e-1);
8230
8231 let q3_k = repeat_block(&Q3_K_TEST_BLOCK, n_blocks);
8232 let x = x256(0.017);
8233 let simd = unsafe { simd_x86::dot_q3_k_f32_avx2(&q3_k, &x) };
8234 assert!((simd - dot_q3_k_f32_scalar(&q3_k, &x)).abs() < 1e-1);
8235
8236 let iq4_nl = repeat_block(&IQ4_NL_TEST_BLOCK, n_blocks);
8237 let x = x32(0.019);
8238 let simd = unsafe { simd_x86::dot_iq4_nl_f32_avx2(&iq4_nl, &x) };
8239 assert!((simd - dot_iq4_nl_f32_scalar(&iq4_nl, &x)).abs() < 1e-1);
8240
8241 let iq4_xs = repeat_block(&IQ4_XS_TEST_BLOCK, n_blocks);
8242 let x = x256(0.023);
8243 let simd = unsafe { simd_x86::dot_iq4_xs_f32_avx2(&iq4_xs, &x) };
8244 assert!((simd - dot_iq4_xs_f32_scalar(&iq4_xs, &x)).abs() < 1e-1);
8245 }
8246}