use super::Qwen35CudaModel;
use crate::gguf::forward_qwen35::{
causal_conv1d, delta_rule_recurrence, gated_rmsnorm, l2_norm_per_head, silu, softplus,
Qwen35Model, Qwen35OwnedDeltaNetLayer, Qwen35OwnedLayer,
};
use trueno_gpu::driver::GpuBuffer;
const MODEL_PATH: &str = "/home/noah/models/Qwen3.5-0.8B-Q4_K_M.gguf";
const TOL: f32 = 1e-3;
const LAYER_TOL: f32 = 2e-2;
const KV_TOL: f32 = TOL;
const COSINE_FLOOR: f32 = 0.996;
const LOGITS_BUDGET: f32 = 7e-2;
const LAYER_BUDGET: f32 = 5e-2;
const PROMPT: [u32; 3] = [9707, 11, 1879];
const LONG_PROMPT: [u32; 6] = [9707, 11, 1879, 0, 2610, 525];
fn max_abs(v: &[f32]) -> f32 {
v.iter().fold(0.0f32, |m, x| m.max(x.abs()))
}
fn rel_linf(got: &[f32], want: &[f32]) -> f32 {
assert_eq!(got.len(), want.len(), "rel_linf: length mismatch");
let scale = max_abs(want);
assert!(scale > 0.0, "rel_linf: the reference is all zeros");
got.iter()
.zip(want)
.fold(0.0f32, |m, (g, w)| m.max((g - w).abs()))
/ scale
}
fn assert_rel_linf(got: &[f32], want: &[f32], tol: f32, what: &str) {
assert_eq!(got.len(), want.len(), "{what}: length mismatch");
let scale = max_abs(want);
assert!(
scale > 0.0,
"{what}: the reference is all zeros — the comparison would pass on anything"
);
let (mut worst, mut at) = (0.0f32, 0usize);
for (i, (g, w)) in got.iter().zip(want).enumerate() {
let d = (g - w).abs();
if d > worst {
worst = d;
at = i;
}
}
assert!(
worst <= tol * scale,
"{what}: relative L-inf {:.3e} (abs {:.3e} at [{at}], scale {:.3e}) exceeds {tol:.0e}; \
cpu={:.6} gpu={:.6}",
worst / scale,
worst,
scale,
want[at],
got[at],
);
}
fn cosine(a: &[f32], b: &[f32]) -> f32 {
assert_eq!(a.len(), b.len(), "cosine: length mismatch");
let (mut dot, mut na, mut nb) = (0.0f64, 0.0f64, 0.0f64);
for (x, y) in a.iter().zip(b) {
dot += f64::from(*x) * f64::from(*y);
na += f64::from(*x) * f64::from(*x);
nb += f64::from(*y) * f64::from(*y);
}
assert!(
na > 0.0 && nb > 0.0,
"cosine: a zero vector — the comparison would be undefined"
);
(dot / (na.sqrt() * nb.sqrt())) as f32
}
fn exact_gemv(tensor: &crate::gguf::quantized::OwnedQuantizedTensor, x: &[f32]) -> Vec<f32> {
use crate::gguf::types::{GGUF_TYPE_Q4_K, GGUF_TYPE_Q5_K, GGUF_TYPE_Q6_K, GGUF_TYPE_Q8_0};
let w = match tensor.qtype {
GGUF_TYPE_Q4_K => crate::quantize::dequantize_q4_k(&tensor.data),
GGUF_TYPE_Q5_K => crate::quantize::dequantize_q5_k(&tensor.data),
GGUF_TYPE_Q6_K => crate::quantize::dequantize_q6_k(&tensor.data),
GGUF_TYPE_Q8_0 => crate::quantize::dequantize_q8_0(&tensor.data),
other => panic!("exact_gemv: no dequantizer for GGML type {other}"),
}
.expect("dequantize the projection");
assert_eq!(
x.len(),
tensor.in_dim,
"exact_gemv: the input is not in_dim wide"
);
assert!(
w.len() >= tensor.out_dim * tensor.in_dim,
"exact_gemv: the dequantized weight is short of out_dim x in_dim"
);
(0..tensor.out_dim)
.map(|r| {
let row = &w[r * tensor.in_dim..(r + 1) * tensor.in_dim];
row.iter()
.zip(x)
.fold(0.0f64, |s, (wv, xv)| s + f64::from(*wv) * f64::from(*xv)) as f32
})
.collect()
}
fn assert_forward_parity(got: &[f32], want: &[f32], budget: f32, what: &str) -> (f32, f32) {
let cos = cosine(got, want);
let linf = rel_linf(got, want);
assert_eq!(
crate::gguf::ops::argmax(got),
crate::gguf::ops::argmax(want),
"{what}: the GPU and the CPU must pick the same index (gpu {} vs cpu {}, cosine \
{cos:.6}, relative L-inf {linf:.3e})",
crate::gguf::ops::argmax(got),
crate::gguf::ops::argmax(want),
);
assert!(
cos >= COSINE_FLOOR,
"{what}: cosine {cos:.6} is below the measured floor {COSINE_FLOOR} — the two sides \
point in different directions, which GEMV rounding does not do"
);
assert!(
linf <= budget,
"{what}: relative L-inf {linf:.3e} exceeds the measured budget {budget:.0e}"
);
(cos, linf)
}
macro_rules! qwen35_cuda_fixture_or_skip {
() => {{
if !std::path::Path::new(MODEL_PATH).exists() {
eprintln!("SKIP: {MODEL_PATH} is absent");
return;
}
crate::cuda_executor_or_skip!(0)
}};
}
fn load_cpu_model(mapped: &crate::gguf::MappedGGUFModel) -> crate::gguf::OwnedQuantizedModel {
Qwen35Model::create_base_model(&mapped.model, mapped.data()).expect("base model")
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_deltanet_layers_match_cpu_on_the_real_file() {
let executor = qwen35_cuda_fixture_or_skip!();
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH).expect("map the GGUF");
let base = load_cpu_model(&mapped);
let qwen =
Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data()).expect("qwen35");
let deltanet_layers = qwen
.layers
.iter()
.filter(|l| matches!(l, Qwen35OwnedLayer::DeltaNet(_)))
.count();
assert!(
deltanet_layers > 0,
"the fixture must carry Gated DeltaNet layers, else this test proves nothing"
);
let mut gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the CUDA model");
gpu.pin_reference_gemv();
let hidden_dim = qwen.base.config.hidden_dim;
let mut cpu_state = qwen.new_state(PROMPT.len() + 1);
let mut normed = vec![0.0f32; hidden_dim];
let mut post_normed = vec![0.0f32; hidden_dim];
let mut compared = 0usize;
for (pos, &token) in PROMPT.iter().enumerate() {
let mut hidden = qwen.base.token_embedding()
[(token as usize) * hidden_dim..(token as usize + 1) * hidden_dim]
.to_vec();
for (il, layer) in qwen.layers.iter().enumerate() {
match layer {
Qwen35OwnedLayer::DeltaNet(d) => {
gpu.upload_layer(il, &cpu_state.conv_states[il], &cpu_state.ssm_states[il])
.expect("seed the device state");
let dev = GpuBuffer::from_host(gpu.executor_mut().context(), &hidden)
.expect("upload hidden");
qwen.forward_deltanet(
d,
&mut hidden,
&mut cpu_state,
il,
pos,
&mut normed,
&mut post_normed,
)
.expect("cpu deltanet");
gpu.forward_deltanet_layer(il, &dev).expect("gpu deltanet");
gpu.executor_mut().sync_stream().expect("sync");
let mut got = vec![0.0f32; hidden_dim];
dev.copy_to_host(&mut got).expect("download hidden");
assert_rel_linf(
&got,
&hidden,
LAYER_TOL,
&format!("pos {pos} layer {il} hidden"),
);
let (conv, ssm) = gpu.download_layer(il).expect("download state");
assert_rel_linf(
&conv,
&cpu_state.conv_states[il],
TOL,
&format!("pos {pos} layer {il} conv window"),
);
assert_rel_linf(
&ssm,
&cpu_state.ssm_states[il],
LAYER_TOL,
&format!("pos {pos} layer {il} ssm state"),
);
compared += 1;
},
Qwen35OwnedLayer::Attention(a) => {
qwen.forward_attention(
a,
&mut hidden,
&mut cpu_state,
il,
pos,
&mut normed,
&mut post_normed,
)
.expect("cpu attention");
},
}
}
cpu_state.kv_cache.advance();
}
assert_eq!(
compared,
deltanet_layers * PROMPT.len(),
"every DeltaNet layer must be compared at every position"
);
}
#[test]
#[serial_test::serial]
#[allow(clippy::too_many_lines)]
fn qwen35_cuda_gdn_ops_match_cpu_given_the_same_inputs() {
let executor = qwen35_cuda_fixture_or_skip!();
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH).expect("map the GGUF");
let base = load_cpu_model(&mapped);
let qwen =
Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data()).expect("qwen35");
let il = qwen
.layers
.iter()
.position(|l| matches!(l, Qwen35OwnedLayer::DeltaNet(_)))
.expect("a DeltaNet layer");
let Qwen35OwnedLayer::DeltaNet(d) = &qwen.layers[il] else {
unreachable!("just matched")
};
let mut gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the CUDA model");
gpu.pin_reference_gemv();
let hidden_dim = qwen.base.config.hidden_dim;
let eps = qwen.base.config.eps;
let k_dim = qwen.head_k_dim * qwen.num_k_heads;
let v_dim = qwen.head_v_dim * qwen.num_v_heads;
let conv_dim = k_dim * 2 + v_dim;
let mut cpu_state = qwen.new_state(PROMPT.len() + 1);
let mut normed = vec![0.0f32; hidden_dim];
let mut post_normed = vec![0.0f32; hidden_dim];
for (pos, &token) in PROMPT.iter().enumerate() {
let conv0 = cpu_state.conv_states[il].clone();
let ssm0 = cpu_state.ssm_states[il].clone();
assert_eq!(
pos > 0,
max_abs(&ssm0) > 0.0,
"pos {pos}: the recurrent state must be non-zero after the first token"
);
let mut hidden = qwen.base.token_embedding()
[(token as usize) * hidden_dim..(token as usize + 1) * hidden_dim]
.to_vec();
gpu.upload_layer(il, &conv0, &ssm0).expect("seed");
let dev =
GpuBuffer::from_host(gpu.executor_mut().context(), &hidden).expect("upload hidden");
gpu.forward_deltanet_layer(il, &dev).expect("gpu deltanet");
let conv_in_g = gpu.dump_stage("conv_in");
let conv_out_g = gpu.dump_stage("conv_out");
let alpha_g = gpu.dump_stage("alpha_raw");
let beta_raw_g = gpu.dump_stage("beta_raw");
let dt_g = gpu.dump_stage("dt");
let beta_g = gpu.dump_stage("beta");
let gate_g = gpu.dump_stage("gate");
let out_h_g = gpu.dump_stage("out_h");
let ssm_out_in_g = gpu.dump_stage("ssm_out_in");
let (conv_state_g, ssm_state_g) = gpu.download_layer(il).expect("download state");
let mut conv_state_c = conv0.clone();
let mut conv_out_c = vec![0.0f32; conv_dim];
causal_conv1d(
&conv_in_g,
&mut conv_state_c[..],
&d.ssm_conv1d_weight,
qwen.conv_kernel,
conv_dim,
&mut conv_out_c,
);
for x in &mut conv_out_c {
*x = silu(*x);
}
l2_norm_per_head(&mut conv_out_c[0..k_dim], qwen.head_k_dim, eps);
l2_norm_per_head(&mut conv_out_c[k_dim..k_dim * 2], qwen.head_k_dim, eps);
assert_rel_linf(
&conv_out_g,
&conv_out_c,
TOL,
&format!("pos {pos}: conv1d+SiLU+per-head L2"),
);
assert_rel_linf(
&conv_state_g,
&conv_state_c,
TOL,
&format!("pos {pos}: conv window after the shift"),
);
let dt_c: Vec<f32> = alpha_g
.iter()
.enumerate()
.map(|(i, a)| softplus(a + d.ssm_dt_bias[i]) * d.ssm_a[i])
.collect();
let beta_c: Vec<f32> = beta_raw_g
.iter()
.map(|b| 1.0 / (1.0 + (-b).exp()))
.collect();
assert_rel_linf(&dt_g, &dt_c, TOL, &format!("pos {pos}: dt gate"));
assert_rel_linf(&beta_g, &beta_c, TOL, &format!("pos {pos}: beta gate"));
let mut ssm_state_c = ssm0.clone();
let mut out_h_c = vec![0.0f32; v_dim];
delta_rule_recurrence(
&conv_out_g[0..k_dim],
&conv_out_g[k_dim..k_dim * 2],
&conv_out_g[k_dim * 2..conv_dim],
&beta_g,
&dt_g,
&mut ssm_state_c[..],
&mut out_h_c,
qwen.num_v_heads,
qwen.head_v_dim,
);
assert_rel_linf(
&out_h_g,
&out_h_c,
TOL,
&format!("pos {pos}: delta-rule output"),
);
assert_rel_linf(
&ssm_state_g,
&ssm_state_c,
TOL,
&format!("pos {pos}: recurrent state after the delta rule"),
);
let mut ssm_out_in_c = vec![0.0f32; v_dim];
gated_rmsnorm(
&out_h_g,
&gate_g,
&d.ssm_norm_weight,
eps,
qwen.head_v_dim,
&mut ssm_out_in_c,
);
assert_rel_linf(
&ssm_out_in_g,
&ssm_out_in_c,
TOL,
&format!("pos {pos}: gated RMSNorm"),
);
qwen.forward_deltanet(
d,
&mut hidden,
&mut cpu_state,
il,
pos,
&mut normed,
&mut post_normed,
)
.expect("cpu deltanet");
cpu_state.kv_cache.advance();
}
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_sigmoid_gate_matches_cpu() {
let mut executor = qwen35_cuda_fixture_or_skip!();
let n = 2048usize;
let x: Vec<f32> = (0..n).map(|i| (i as f32).mul_add(0.001, -1.0)).collect();
let g: Vec<f32> = (0..n).map(|i| 2.0 - (i as f32) * 0.002).collect();
let mut want = x.clone();
crate::gguf::forward_qwen35::apply_sigmoid_gate(&mut want, &g);
let xd = GpuBuffer::from_host(executor.context(), &x).expect("upload x");
let gd = GpuBuffer::from_host(executor.context(), &g).expect("upload gate");
executor
.gdn_sigmoid_gate_into(&xd, &gd, u32::try_from(n).expect("n fits"))
.expect("launch");
executor.sync_stream().expect("sync");
let mut got = vec![0.0f32; n];
xd.copy_to_host(&mut got).expect("download");
assert_rel_linf(&got, &want, TOL, "sigmoid gate");
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_layer_kind_mismatch_refuses_by_name() {
let executor = qwen35_cuda_fixture_or_skip!();
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH).expect("map the GGUF");
let base = load_cpu_model(&mapped);
let qwen =
Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data()).expect("qwen35");
let attention_il = qwen
.layers
.iter()
.position(|l| matches!(l, Qwen35OwnedLayer::Attention(_)))
.expect("the hybrid file carries full-attention layers");
let deltanet_il = qwen
.layers
.iter()
.position(|l| matches!(l, Qwen35OwnedLayer::DeltaNet(_)))
.expect("the hybrid file carries Gated DeltaNet layers");
let mut gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the CUDA model");
let dev = GpuBuffer::<f32>::from_host(
gpu.executor_mut().context(),
&vec![0.0f32; qwen.base.config.hidden_dim],
)
.expect("upload hidden");
let err = gpu
.forward_deltanet_layer(attention_il, &dev)
.expect_err("an attention layer must not run the DeltaNet path");
assert!(
format!("{err}").contains("qwen35_cuda_attention"),
"the refusal must name the seam: {err}"
);
let err = gpu
.forward_attention_layer(deltanet_il, &dev, 0)
.expect_err("a DeltaNet layer must not run the attention path");
assert!(format!("{err}").contains("qwen35_cuda_deltanet"), "{err}");
}
#[test]
#[serial_test::serial]
#[allow(clippy::too_many_lines)]
fn qwen35_cuda_attention_layers_match_cpu_on_the_real_file() {
let executor = qwen35_cuda_fixture_or_skip!();
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH).expect("map the GGUF");
let base = load_cpu_model(&mapped);
let qwen =
Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data()).expect("qwen35");
let attention_layers = qwen
.layers
.iter()
.filter(|l| matches!(l, Qwen35OwnedLayer::Attention(_)))
.count();
assert!(
attention_layers > 0,
"the fixture must carry full-attention layers, else this test proves nothing"
);
let mut gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the CUDA model");
gpu.pin_reference_gemv();
let hidden_dim = qwen.base.config.hidden_dim;
let head_dim = qwen.head_dim;
let num_kv_heads = qwen.base.config.num_kv_heads;
let kv_row = num_kv_heads * head_dim;
let eps = qwen.base.config.eps;
let n_rot = 2 * qwen.rope_sections.iter().sum::<usize>();
let freq_base = qwen.base.config.rope_theta;
let mut cpu_state = qwen.new_state(PROMPT.len() + 1);
let mut normed = vec![0.0f32; hidden_dim];
let mut post_normed = vec![0.0f32; hidden_dim];
let mut compared = 0usize;
let mut worst_cos = 1.0f32;
let mut worst_linf = 0.0f32;
for (pos, &token) in PROMPT.iter().enumerate() {
let mut hidden = qwen.base.token_embedding()
[(token as usize) * hidden_dim..(token as usize + 1) * hidden_dim]
.to_vec();
for (il, layer) in qwen.layers.iter().enumerate() {
match layer {
Qwen35OwnedLayer::DeltaNet(d) => {
qwen.forward_deltanet(
d,
&mut hidden,
&mut cpu_state,
il,
pos,
&mut normed,
&mut post_normed,
)
.expect("cpu deltanet");
},
Qwen35OwnedLayer::Attention(a) => {
let k_before = cpu_state.kv_cache.get_k(il).to_vec();
let v_before = cpu_state.kv_cache.get_v(il).to_vec();
let hidden_in = hidden.clone();
assert_eq!(
k_before.len(),
pos * kv_row,
"pos {pos} layer {il}: the CPU cache must hold exactly {pos} rows"
);
gpu.upload_attention_kv(il, &k_before, &v_before)
.expect("seed the device KV cache");
let dev = GpuBuffer::from_host(gpu.executor_mut().context(), &hidden)
.expect("upload hidden");
qwen.forward_attention(
a,
&mut hidden,
&mut cpu_state,
il,
pos,
&mut normed,
&mut post_normed,
)
.expect("cpu attention");
gpu.forward_attention_layer(il, &dev, pos)
.expect("gpu attention");
gpu.executor_mut().sync_stream().expect("sync");
let mut got = vec![0.0f32; hidden_dim];
dev.copy_to_host(&mut got).expect("download hidden");
let (cos, linf) = assert_forward_parity(
&got,
&hidden,
LAYER_BUDGET,
&format!("pos {pos} layer {il} hidden"),
);
eprintln!(
"[attn layer] pos {pos} layer {il}: cosine {cos:.6} relative L-inf \
{linf:.3e}"
);
worst_cos = worst_cos.min(cos);
worst_linf = worst_linf.max(linf);
let (k_after, v_after) = gpu.download_layer(il).expect("download KV");
assert_eq!(k_after.len(), (pos + 1) * kv_row, "K rows written");
let mut normed_ref = vec![0.0f32; hidden_dim];
crate::gguf::ops::rms_norm_into(&hidden_in, &a.attn_norm, eps, &mut normed_ref);
let mut k_ref = exact_gemv(&a.attn_k, &normed_ref);
crate::gguf::ops::apply_per_head_rms_norm(
&mut k_ref,
&a.attn_k_norm,
num_kv_heads,
eps,
);
crate::gguf::forward_qwen35::apply_partial_neox_rope(
&mut k_ref,
num_kv_heads,
head_dim,
n_rot,
pos,
freq_base,
);
let v_ref = exact_gemv(&a.attn_v, &normed_ref);
eprintln!(
"[attn layer] pos {pos} layer {il}: K row vs exact {:.3e} V row vs \
exact {:.3e}",
rel_linf(&k_after[pos * kv_row..], &k_ref),
rel_linf(&v_after[pos * kv_row..], &v_ref),
);
assert_rel_linf(
&k_after[pos * kv_row..],
&k_ref,
KV_TOL,
&format!("pos {pos} layer {il} K row"),
);
assert_rel_linf(
&v_after[pos * kv_row..],
&v_ref,
KV_TOL,
&format!("pos {pos} layer {il} V row"),
);
compared += 1;
},
}
}
cpu_state.kv_cache.advance();
}
assert_eq!(
compared,
attention_layers * PROMPT.len(),
"every attention layer must be compared at every position"
);
eprintln!(
"[attn layer] worst over {compared} comparisons: cosine {worst_cos:.6} relative L-inf \
{worst_linf:.3e}"
);
}
#[test]
#[serial_test::serial]
#[allow(clippy::too_many_lines)]
fn qwen35_cuda_attention_ops_match_cpu_given_the_same_inputs() {
let executor = qwen35_cuda_fixture_or_skip!();
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH).expect("map the GGUF");
let base = load_cpu_model(&mapped);
let qwen =
Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data()).expect("qwen35");
let il = qwen
.layers
.iter()
.position(|l| matches!(l, Qwen35OwnedLayer::Attention(_)))
.expect("an attention layer");
let Qwen35OwnedLayer::Attention(a) = &qwen.layers[il] else {
unreachable!("just matched")
};
let mut gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the CUDA model");
gpu.pin_reference_gemv();
let hidden_dim = qwen.base.config.hidden_dim;
let eps = qwen.base.config.eps;
let num_heads = qwen.base.config.num_heads;
let num_kv_heads = qwen.base.config.num_kv_heads;
let head_dim = a.attn_q_norm.len();
let kv_row = num_kv_heads * head_dim;
let n_rot = 2 * qwen.rope_sections.iter().sum::<usize>();
let freq_base = qwen.base.config.rope_theta;
let mut cpu_state = qwen.new_state(PROMPT.len() + 1);
let mut normed = vec![0.0f32; hidden_dim];
let mut post_normed = vec![0.0f32; hidden_dim];
for (pos, &token) in PROMPT.iter().enumerate() {
let k_before = cpu_state.kv_cache.get_k(il).to_vec();
let v_before = cpu_state.kv_cache.get_v(il).to_vec();
let mut hidden = qwen.base.token_embedding()
[(token as usize) * hidden_dim..(token as usize + 1) * hidden_dim]
.to_vec();
gpu.upload_attention_kv(il, &k_before, &v_before)
.expect("seed");
let dev =
GpuBuffer::from_host(gpu.executor_mut().context(), &hidden).expect("upload hidden");
gpu.forward_attention_layer(il, &dev, pos)
.expect("gpu attention");
let normed_g = gpu.dump_stage("normed");
let q_full_g = gpu.dump_stage("q_full");
let q_g = gpu.dump_stage("q");
let q_rot_g = gpu.dump_stage("q_normed");
let gate_g = gpu.dump_stage("attn_gate");
let k_raw_g = gpu.dump_stage("k_raw");
let attn_out_in_g = gpu.dump_stage("attn_out_in");
let (k_all_g, v_all_g) = gpu.download_layer(il).expect("download KV");
let mut q_c = vec![0.0f32; num_heads * head_dim];
let mut gate_c = vec![0.0f32; num_heads * head_dim];
for h in 0..num_heads {
let src = h * head_dim * 2;
let dst = h * head_dim;
q_c[dst..dst + head_dim].copy_from_slice(&q_full_g[src..src + head_dim]);
gate_c[dst..dst + head_dim]
.copy_from_slice(&q_full_g[src + head_dim..src + head_dim * 2]);
}
assert_eq!(q_g, q_c, "pos {pos}: the q half of the split is not exact");
assert_eq!(
gate_g, gate_c,
"pos {pos}: the gate half of the split is not exact"
);
let mut q_rot_c = q_g.clone();
crate::gguf::ops::apply_per_head_rms_norm(&mut q_rot_c, &a.attn_q_norm, num_heads, eps);
crate::gguf::forward_qwen35::apply_partial_neox_rope(
&mut q_rot_c,
num_heads,
head_dim,
n_rot,
pos,
freq_base,
);
assert_rel_linf(&q_rot_g, &q_rot_c, TOL, &format!("pos {pos}: q norm+rope"));
let mut k_rot_c = k_raw_g.clone();
crate::gguf::ops::apply_per_head_rms_norm(&mut k_rot_c, &a.attn_k_norm, num_kv_heads, eps);
crate::gguf::forward_qwen35::apply_partial_neox_rope(
&mut k_rot_c,
num_kv_heads,
head_dim,
n_rot,
pos,
freq_base,
);
assert_rel_linf(
&k_all_g[pos * kv_row..],
&k_rot_c,
TOL,
&format!("pos {pos}: k norm+rope (the appended row)"),
);
let mut attn_c = vec![0.0f32; num_heads * head_dim];
let group_size = num_heads / num_kv_heads;
for h in 0..num_heads {
let kv_h = h / group_size;
let q_h = &q_rot_g[h * head_dim..(h + 1) * head_dim];
let mut scores = vec![0.0f32; pos + 1];
for (p, score) in scores.iter_mut().enumerate() {
let base = p * kv_row + kv_h * head_dim;
let mut dot = 0.0;
for i in 0..head_dim {
dot += q_h[i] * k_all_g[base + i];
}
*score = dot / (head_dim as f32).sqrt();
}
crate::gguf::ops::softmax(&mut scores);
let out_h = &mut attn_c[h * head_dim..(h + 1) * head_dim];
for (p, &w) in scores.iter().enumerate() {
let base = p * kv_row + kv_h * head_dim;
for i in 0..head_dim {
out_h[i] += w * v_all_g[base + i];
}
}
}
crate::gguf::forward_qwen35::apply_sigmoid_gate(&mut attn_c, &gate_g);
assert_rel_linf(
&attn_out_in_g,
&attn_c,
TOL,
&format!("pos {pos}: decode attention + output gate"),
);
let q_full_e = exact_gemv(&a.attn_q, &normed_g);
let k_e = exact_gemv(&a.attn_k, &normed_g);
let out_e = exact_gemv(&a.attn_output, &attn_out_in_g);
let attn_out_g = gpu.dump_stage("attn_out");
eprintln!(
"[GEMV vs exact pos {pos}] attn_q(t{}) {:.3e} attn_k(t{}) {:.3e} attn_output(t{}) \
{:.3e}",
a.attn_q.qtype,
rel_linf(&q_full_g, &q_full_e),
a.attn_k.qtype,
rel_linf(&k_raw_g, &k_e),
a.attn_output.qtype,
rel_linf(&attn_out_g, &out_e),
);
assert_rel_linf(
&q_full_g,
&q_full_e,
TOL,
&format!("pos {pos}: attn_q GEMV"),
);
assert_rel_linf(&k_raw_g, &k_e, TOL, &format!("pos {pos}: attn_k GEMV"));
assert_rel_linf(
&attn_out_g,
&out_e,
TOL,
&format!("pos {pos}: attn_output GEMV"),
);
let mut normed_c = vec![0.0f32; hidden_dim];
crate::gguf::ops::rms_norm_into(&hidden, &a.attn_norm, eps, &mut normed_c);
assert_rel_linf(&normed_g, &normed_c, TOL, &format!("pos {pos}: rms_norm"));
qwen.forward_attention(
a,
&mut hidden,
&mut cpu_state,
il,
pos,
&mut normed,
&mut post_normed,
)
.expect("cpu attention");
cpu_state.kv_cache.advance();
}
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_forward_single_matches_cpu_logits_end_to_end() {
let executor = qwen35_cuda_fixture_or_skip!();
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH).expect("map the GGUF");
let base = load_cpu_model(&mapped);
let qwen =
Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data()).expect("qwen35");
let mut gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the CUDA model");
gpu.pin_reference_gemv();
let mut gpu_state = gpu.new_state().expect("device state");
let mut cpu_state = qwen.new_state(LONG_PROMPT.len() + 1);
let mut worst_cos = 1.0f32;
let mut worst_linf = 0.0f32;
for (pos, &token) in LONG_PROMPT.iter().enumerate() {
let want = qwen
.forward_single_qwen35(token, &mut cpu_state, pos)
.expect("cpu forward");
let t0 = std::time::Instant::now();
let got = gpu
.forward_single(token, &mut gpu_state, pos)
.expect("gpu forward");
let gpu_ms = t0.elapsed().as_secs_f64() * 1e3;
let (cos, linf) =
assert_forward_parity(&got, &want, LOGITS_BUDGET, &format!("pos {pos} logits"));
eprintln!(
"[e2e] pos {pos}: argmax {} cosine {cos:.6} relative L-inf {linf:.3e} \
forward_single {gpu_ms:.3} ms",
crate::gguf::ops::argmax(&want),
);
worst_cos = worst_cos.min(cos);
worst_linf = worst_linf.max(linf);
assert_eq!(
gpu_state.kv_len(),
pos + 1,
"pos {pos}: the device KV cache must have advanced"
);
}
eprintln!(
"[e2e] worst over {} positions: cosine {worst_cos:.6} relative L-inf {worst_linf:.3e}",
LONG_PROMPT.len(),
);
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_state_is_sized_from_the_config() {
let executor = qwen35_cuda_fixture_or_skip!();
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH).expect("map the GGUF");
let base = load_cpu_model(&mapped);
let qwen =
Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data()).expect("qwen35");
let conv_dim = qwen.head_k_dim * qwen.num_k_heads * 2 + qwen.head_v_dim * qwen.num_v_heads;
let gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the CUDA model");
assert_eq!(
gpu.state().conv_len(),
conv_dim * (qwen.conv_kernel - 1),
"conv window is conv_dim * (conv_kernel - 1)"
);
assert_eq!(
gpu.state().ssm_len(),
qwen.num_v_heads * qwen.head_v_dim * qwen.head_k_dim,
"recurrent state is num_v_heads * head_v_dim * head_k_dim (equal dims on this file)"
);
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_forward_hidden_refuses_a_wrong_width() {
let executor = qwen35_cuda_fixture_or_skip!();
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH).expect("map the GGUF");
let base = load_cpu_model(&mapped);
let qwen =
Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data()).expect("qwen35");
let mut gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the CUDA model");
let err = gpu
.forward_hidden_deltanet_only(&[0.0f32; 7])
.expect_err("7 is not the hidden width");
assert!(format!("{err}").contains("hidden state is 7 wide"), "{err}");
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_the_parity_floor_is_the_cpu_references_own_activation_quantization() {
let executor = qwen35_cuda_fixture_or_skip!();
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH).expect("map the GGUF");
let base = load_cpu_model(&mapped);
let qwen =
Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data()).expect("qwen35");
let il = qwen
.layers
.iter()
.position(|l| matches!(l, Qwen35OwnedLayer::Attention(_)))
.expect("the hybrid file carries full-attention layers");
let Qwen35OwnedLayer::Attention(a) = &qwen.layers[il] else {
unreachable!("just matched")
};
assert_eq!(
a.attn_q.qtype,
crate::gguf::types::GGUF_TYPE_Q4_K,
"this test reads the Q4_K dequantizer; the projection is no longer Q4_K"
);
let hidden_dim = qwen.base.config.hidden_dim;
let x: Vec<f32> = (0..hidden_dim)
.map(|i| ((i as f32) * 0.37).sin() * 1.3)
.collect();
let exact = exact_gemv(&a.attn_q, &x);
let mut cpu = vec![0.0f32; a.attn_q.out_dim];
qwen.base
.fused_matmul_into(&x, &a.attn_q, &mut cpu)
.expect("cpu attn_q");
let mut gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the CUDA model");
let got = gpu
.attn_q_gemv_of_host_input(il, &x)
.expect("gpu attn_q GEMV");
let gpu_err = rel_linf(&got, &exact);
let cpu_err = rel_linf(&cpu, &exact);
eprintln!("[parity floor] gpu-vs-exact {gpu_err:.3e} cpu-vs-exact {cpu_err:.3e}");
assert!(
gpu_err <= 1e-5,
"the GPU float GEMV must reproduce the exact dot: {gpu_err:.3e} > 1e-5"
);
assert!(
cpu_err > 1e-4,
"the CPU reference no longer quantizes its activation (cpu-vs-exact {cpu_err:.3e} <= \
1e-4) — the end-to-end parity budget can now come down; re-measure LOGITS_BUDGET and \
consider a tight L-inf bar again"
);
assert!(
cpu_err > gpu_err * 100.0,
"the CPU, not the GPU, must be the side that is far from arithmetic: cpu {cpu_err:.3e} \
vs gpu {gpu_err:.3e}"
);
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_a_fresh_model_pins_the_float_gemv_variants() {
use crate::cuda::gpu_profile::{Q4kVariant, Q6kVariant};
let executor = qwen35_cuda_fixture_or_skip!();
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH).expect("map the GGUF");
let base = load_cpu_model(&mapped);
let qwen =
Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data()).expect("qwen35");
let gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the CUDA model");
assert_eq!(
gpu.gemv_variants(),
(Q4kVariant::Mwv, Q6kVariant::Mwv),
"Qwen35CudaModel::new must pin the float GEMV variants for this architecture"
);
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_dp4a_gemv_is_catastrophic_through_the_recurrence() {
use crate::cuda::gpu_profile::{Q4kVariant, Q6kVariant};
let executor = qwen35_cuda_fixture_or_skip!();
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH).expect("map the GGUF");
let base = load_cpu_model(&mapped);
let qwen =
Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data()).expect("qwen35");
let mut gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the CUDA model");
gpu.executor_mut().gpu_profile.q4k = Q4kVariant::HwDp4a;
gpu.executor_mut().gpu_profile.q6k = Q6kVariant::HwDp4a;
assert_eq!(
gpu.gemv_variants(),
(Q4kVariant::HwDp4a, Q6kVariant::HwDp4a),
"the DP4A variants must actually be armed, else this test proves nothing"
);
let mut gpu_state = gpu.new_state().expect("device state");
let mut cpu_state = qwen.new_state(LONG_PROMPT.len() + 1);
let mut broken = 0usize;
for (pos, &token) in LONG_PROMPT.iter().enumerate() {
let want = qwen
.forward_single_qwen35(token, &mut cpu_state, pos)
.expect("cpu forward");
let got = gpu
.forward_single(token, &mut gpu_state, pos)
.expect("gpu forward");
let cos = cosine(&got, &want);
let (gpu_arg, cpu_arg) = (
crate::gguf::ops::argmax(&got),
crate::gguf::ops::argmax(&want),
);
eprintln!(
"[dp4a] pos {pos}: gpu argmax {gpu_arg} cpu argmax {cpu_arg} cosine {cos:.6} \
relative L-inf {:.3e}",
rel_linf(&got, &want),
);
if gpu_arg != cpu_arg || cos < COSINE_FLOOR {
broken += 1;
}
}
assert!(
broken > 0,
"the DP4A GEMV path passed the parity contract at every position — it is no longer \
catastrophic through the recurrence, so re-measure and revisit the pin in \
Qwen35CudaModel::with_max_seq_len (DP4A-through-recurrence ticket, 0.69.0)"
);
}
const _: Option<&Qwen35OwnedDeltaNetLayer> = None;
const MODEL_PATH_4B: &str = "/home/noah/models/Qwen3.5-4B-Q4_K_M.gguf";
const PROMPT_4B: [u32; 5] = [760, 6511, 314, 9338, 369];
const LAYER_BUDGET_4B: f32 = 5e-2;
macro_rules! qwen35_cuda_file_or_skip {
($path:expr) => {{
if !std::path::Path::new($path).exists() {
eprintln!("SKIP: {} is absent", $path);
return;
}
crate::cuda_executor_or_skip!(0)
}};
}
fn assert_is_gqa(qwen: &Qwen35Model<'_>) {
assert!(
qwen.num_v_heads > qwen.num_k_heads,
"{MODEL_PATH_4B} is not a GQA DeltaNet file (num_v_heads {} vs num_k_heads {}); this \
test cannot falsify the head mapping against it",
qwen.num_v_heads,
qwen.num_k_heads
);
assert_eq!(
qwen.num_v_heads % qwen.num_k_heads,
0,
"num_v_heads {} is not a multiple of num_k_heads {}",
qwen.num_v_heads,
qwen.num_k_heads
);
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_4b_deltanet_layers_match_cpu_on_the_real_file() {
let executor = qwen35_cuda_file_or_skip!(MODEL_PATH_4B);
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH_4B).expect("map the 4B GGUF");
let base = load_cpu_model(&mapped);
let qwen = Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data())
.expect("4B hybrid layers");
assert_is_gqa(&qwen);
let deltanet_layers = qwen
.layers
.iter()
.filter(|l| matches!(l, Qwen35OwnedLayer::DeltaNet(_)))
.count();
assert!(
deltanet_layers > 0,
"the 4B file must carry DeltaNet layers"
);
let mut gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the 4B CUDA model");
gpu.pin_reference_gemv();
let hidden_dim = qwen.base.config.hidden_dim;
let tokens = &PROMPT_4B[..2];
let mut cpu_state = qwen.new_state(tokens.len() + 1);
let mut normed = vec![0.0f32; hidden_dim];
let mut post_normed = vec![0.0f32; hidden_dim];
let mut compared = 0usize;
let mut worst_hidden = 0.0f32;
let mut worst_ssm = 0.0f32;
let mut worst_where = String::new();
for (pos, &token) in tokens.iter().enumerate() {
let mut hidden = qwen.base.token_embedding()
[(token as usize) * hidden_dim..(token as usize + 1) * hidden_dim]
.to_vec();
for (il, layer) in qwen.layers.iter().enumerate() {
match layer {
Qwen35OwnedLayer::DeltaNet(d) => {
gpu.upload_layer(il, &cpu_state.conv_states[il], &cpu_state.ssm_states[il])
.expect("seed the device state");
let dev = GpuBuffer::from_host(gpu.executor_mut().context(), &hidden)
.expect("upload hidden");
qwen.forward_deltanet(
d,
&mut hidden,
&mut cpu_state,
il,
pos,
&mut normed,
&mut post_normed,
)
.expect("cpu deltanet");
gpu.forward_deltanet_layer(il, &dev).expect("gpu deltanet");
gpu.executor_mut().sync_stream().expect("sync");
let mut got = vec![0.0f32; hidden_dim];
dev.copy_to_host(&mut got).expect("download hidden");
let l = rel_linf(&got, &hidden);
if l > worst_hidden {
worst_hidden = l;
worst_where = format!("pos {pos} layer {il}");
}
let (conv, ssm) = gpu.download_layer(il).expect("download state");
assert_rel_linf(
&conv,
&cpu_state.conv_states[il],
TOL,
&format!("4B pos {pos} layer {il} conv window"),
);
let ls = rel_linf(&ssm, &cpu_state.ssm_states[il]);
if ls > worst_ssm {
worst_ssm = ls;
}
compared += 1;
},
Qwen35OwnedLayer::Attention(a) => {
qwen.forward_attention(
a,
&mut hidden,
&mut cpu_state,
il,
pos,
&mut normed,
&mut post_normed,
)
.expect("cpu attention");
},
}
}
cpu_state.kv_cache.advance();
}
assert_eq!(
compared,
deltanet_layers * tokens.len(),
"every 4B DeltaNet layer must be compared at every position"
);
eprintln!(
"[4b-layer] worst hidden {worst_hidden:.3e} at {worst_where}, worst ssm {worst_ssm:.3e}, \
over {compared} comparisons"
);
assert!(
worst_hidden <= LAYER_BUDGET_4B,
"4B layer hidden: worst relative L-inf {worst_hidden:.3e} at {worst_where} exceeds the \
measured budget {LAYER_BUDGET_4B:.0e}"
);
assert!(
worst_ssm <= LAYER_BUDGET_4B,
"4B recurrent state: worst relative L-inf {worst_ssm:.3e} exceeds the measured budget \
{LAYER_BUDGET_4B:.0e}"
);
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_4b_forward_single_matches_cpu_argmax_end_to_end() {
let executor = qwen35_cuda_file_or_skip!(MODEL_PATH_4B);
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH_4B).expect("map the 4B GGUF");
let base = load_cpu_model(&mapped);
let qwen = Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data())
.expect("4B hybrid layers");
assert_is_gqa(&qwen);
const WANT: [u32; 4] = [11751, 13, 198, 32];
let mut gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the 4B CUDA model");
gpu.pin_reference_gemv();
let mut gpu_state = gpu.new_state().expect("device state");
let mut cpu_state = qwen.new_state(PROMPT_4B.len() + WANT.len());
let mut cpu_logits = Vec::new();
let mut gpu_logits = Vec::new();
for (pos, &token) in PROMPT_4B.iter().enumerate() {
cpu_logits = qwen
.forward_single_qwen35(token, &mut cpu_state, pos)
.expect("cpu 4B forward");
gpu_logits = gpu
.forward_single(token, &mut gpu_state, pos)
.expect("gpu 4B forward");
assert!(
gpu_logits.iter().all(|l| l.is_finite()),
"4B pos {pos}: the GPU forward produced a non-finite logit"
);
let (cos, linf) = assert_forward_parity(
&gpu_logits,
&cpu_logits,
LOGITS_BUDGET,
&format!("4B prompt pos {pos} logits"),
);
eprintln!(
"[4b-e2e] prompt pos {pos}: argmax {} cosine {cos:.6} relative L-inf {linf:.3e}",
crate::gguf::ops::argmax(&cpu_logits),
);
}
let mut got = Vec::with_capacity(WANT.len());
for step in 0..WANT.len() {
let pos = PROMPT_4B.len() + step;
let next = crate::gguf::ops::argmax(&gpu_logits);
assert_eq!(
next,
crate::gguf::ops::argmax(&cpu_logits),
"4B step {step}: GPU and CPU disagree on the next token"
);
got.push(next);
let token = next;
cpu_logits = qwen
.forward_single_qwen35(token, &mut cpu_state, pos)
.expect("cpu 4B forward");
gpu_logits = gpu
.forward_single(token, &mut gpu_state, pos)
.expect("gpu 4B forward");
let (cos, linf) = assert_forward_parity(
&gpu_logits,
&cpu_logits,
LOGITS_BUDGET,
&format!("4B decode pos {pos} logits"),
);
eprintln!("[4b-e2e] decode pos {pos}: token {next} cosine {cos:.6} L-inf {linf:.3e}");
}
assert_eq!(
got,
WANT.to_vec(),
"the 4B GPU forward does not follow llama.cpp greedily: got {got:?}, want {WANT:?} — \
num_k_heads {}, num_v_heads {}, head_k_dim {}, head_v_dim {}",
qwen.num_k_heads,
qwen.num_v_heads,
qwen.head_k_dim,
qwen.head_v_dim
);
assert_eq!(
gpu_state.kv_len(),
PROMPT_4B.len() + WANT.len(),
"the device KV cache must have advanced once per position"
);
}
#[test]
#[serial_test::serial]
fn qwen35_cuda_4b_state_is_sized_from_the_grouped_config() {
let executor = qwen35_cuda_file_or_skip!(MODEL_PATH_4B);
let mapped = crate::gguf::MappedGGUFModel::from_path(MODEL_PATH_4B).expect("map the 4B GGUF");
let base = load_cpu_model(&mapped);
let qwen = Qwen35Model::from_model_and_layers(&base, &mapped.model, mapped.data())
.expect("4B hybrid layers");
assert_is_gqa(&qwen);
let conv_dim = qwen.head_k_dim * qwen.num_k_heads * 2 + qwen.head_v_dim * qwen.num_v_heads;
let gpu = Qwen35CudaModel::new(&qwen, executor).expect("build the 4B CUDA model");
assert_eq!(
gpu.state().conv_len(),
conv_dim * (qwen.conv_kernel - 1),
"conv window is (2 * k_dim + v_dim) * (conv_kernel - 1)"
);
assert_eq!(
gpu.state().ssm_len(),
qwen.num_v_heads * qwen.head_v_dim * qwen.head_k_dim,
"the recurrent state is num_v_heads * head_v_dim * head_k_dim"
);
assert_eq!(
gpu.state().ssm_len(),
qwen.new_state(1).ssm_states[0].len(),
"the device state must be the same length as the CPU's own"
);
}
#[test]
fn qwen35_cuda_refuses_only_value_heads_that_do_not_group() {
fn dims(
num_k_heads: u32,
num_v_heads: u32,
head_k_dim: u32,
head_v_dim: u32,
) -> super::Qwen35CudaDims {
super::Qwen35CudaDims {
hidden_dim: 1024,
intermediate_dim: 3072,
conv_dim: num_k_heads * head_k_dim * 2 + num_v_heads * head_v_dim,
k_dim: num_k_heads * head_k_dim,
v_dim: num_v_heads * head_v_dim,
head_k_dim,
num_k_heads,
head_v_dim,
num_v_heads,
conv_kernel: 4,
eps: 1e-6,
num_heads: 16,
num_kv_heads: 2,
attn_head_dim: 128,
n_rot: 128,
theta_scale: 0.5,
vocab_size: 32,
}
}
for (nk, nv, label) in [
(16u32, 16u32, "0.8B / 2B"),
(16, 32, "4B / 9B"),
(16, 48, "27B"),
] {
Qwen35CudaModel::check_head_grouping(dims(nk, nv, 128, 128))
.unwrap_or_else(|e| panic!("{label} ({nk} key heads, {nv} value heads) refused: {e}"));
}
Qwen35CudaModel::check_head_grouping(dims(16, 32, 128, 64))
.expect("head_k_dim != head_v_dim must be accepted");
let err = Qwen35CudaModel::check_head_grouping(dims(2, 5, 8, 8))
.expect_err("5 value heads do not group onto 2 key heads");
let text = format!("{err}");
assert!(
text.contains("qwen35_cuda_deltanet"),
"the refusal must name the operation: {text}"
);
assert!(
text.contains('2') && text.contains('5'),
"the refusal must quote both head counts so the file is identifiable: {text}"
);
Qwen35CudaModel::check_head_grouping(dims(0, 16, 128, 128))
.expect_err("zero key heads must be refused, not divided by");
}