Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,18 @@ impl Qwen3AttentionLayer {
1,
mla_rope,
mla_rope,
mla.yarn_inv_freq,
super::super::helpers::yarn_rope_mscale(ctx.config),
// Sliding layers (compressor==None) = reference "main" rope:
// plain θ=10000, mscale=1 (no yarn). CSA/HCA keep θ=160000 yarn.
if mla.compressor.is_none() {
mla.main_inv_freq
} else {
mla.yarn_inv_freq
},
if mla.compressor.is_none() {
1.0f32
} else {
super::super::helpers::yarn_rope_mscale(ctx.config)
},
stream,
)
})?;
Expand Down Expand Up @@ -677,8 +687,19 @@ impl Qwen3AttentionLayer {
0, // no KV heads — de-rotate the query/output heads only
mla_rope,
mla_rope,
mla.yarn_inv_freq,
super::super::helpers::yarn_rope_mscale(ctx.config),
// MUST match the Q/K rope inv_freq for this layer type (rope-in
// == de-rotate-out), else the output is scrambled. Sliding =
// main θ=10000; CSA/HCA = θ=160000 yarn.
if mla.compressor.is_none() {
mla.main_inv_freq
} else {
mla.yarn_inv_freq
},
if mla.compressor.is_none() {
1.0f32
} else {
super::super::helpers::yarn_rope_mscale(ctx.config)
},
stream,
)?;
ops::mla_q_rope_writeback_batched(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: AGPL-3.0-only

//! DeepSeek-V4-Flash prefill path using standard GQA FlashAttention.
Expand Down Expand Up @@ -332,8 +332,18 @@
nkv,
rope,
rope,
mla.yarn_inv_freq,
super::super::helpers::yarn_rope_mscale(ctx.config),
// Sliding layers (compressor==None) = reference "main" rope: plain
// θ=10000, mscale=1 (no yarn). CSA/HCA keep the θ=160000 yarn table.
if mla.compressor.is_none() {
mla.main_inv_freq
} else {
mla.yarn_inv_freq
},
if mla.compressor.is_none() {
1.0f32
} else {
super::super::helpers::yarn_rope_mscale(ctx.config)
},
stream,
)?;
ops::mla_q_rope_writeback_batched(
Expand Down Expand Up @@ -685,8 +695,18 @@
0,
rope,
rope,
mla.yarn_inv_freq,
super::super::helpers::yarn_rope_mscale(ctx.config),
// De-rotation must match this layer's Q/K rope (rope-in ==
// de-rotate-out): sliding = main θ=10000; CSA/HCA = θ=160000.
if mla.compressor.is_none() {
mla.main_inv_freq
} else {
mla.yarn_inv_freq
},
if mla.compressor.is_none() {
1.0f32
} else {
super::super::helpers::yarn_rope_mscale(ctx.config)
},
stream,
)?;
ops::mla_q_rope_writeback_batched(
Expand Down
6 changes: 6 additions & 0 deletions crates/spark-model/src/layers/qwen3_attention/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ pub struct MlaWeights {
/// Precomputed YaRN inv_freq table [rotary_dim/2] FP32 on GPU.
/// NULL = use standard theta computation in the RoPE kernel.
pub yarn_inv_freq: spark_runtime::gpu::DevicePtr,
/// Plain θ=10000 inv_freq [rotary_dim/2] FP32 on GPU, NO YaRN. Used for the
/// raw-arm Q/K rope on `sliding_attention` layers (compressor==None): the
/// reference gives sliding layers the "main" rope (θ=rope_theta=10000, no
/// yarn) while CSA/HCA layers use "compress" (θ=compress_rope_theta=160000
/// + yarn). Atlas previously applied the single yarn table to every layer.
pub main_inv_freq: spark_runtime::gpu::DevicePtr,
pub q_lora_rank: usize,
pub kv_lora_rank: usize,
pub o_lora_rank: usize,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ pub(super) fn assemble_layer(
w_uk_block_diag: require(ctx.w_uk_block_diag, "w_uk_block_diag")?,
w_uv_block_diag: require(ctx.w_uv_block_diag, "w_uv_block_diag")?,
yarn_inv_freq,
main_inv_freq: yarn_inv_freq, // non-V4: no sliding/compress rope split
q_lora_rank: q_lora,
kv_lora_rank: kv_lora,
o_lora_rank: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ pub fn assemble_layer(
w_uk_block_diag,
w_uv_block_diag,
yarn_inv_freq,
main_inv_freq: super::compute::main_inv_freq(config, gpu)?,
q_lora_rank: config.q_lora_rank,
kv_lora_rank: config.kv_lora_rank,
o_lora_rank: config.o_lora_rank,
Expand Down
20 changes: 20 additions & 0 deletions crates/spark-model/src/weight_loader/deepseek_v4/compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,23 @@ pub fn ensure_yarn_inv_freq(
*shared = ptr;
Ok(ptr)
}

/// Plain θ=10000 inv_freq (NO YaRN) for the raw-arm rope on `sliding_attention`
/// layers. The reference's "main" rope: θ=10000 (config `rope_theta`, which
/// Atlas's dispatch overrides to compress_rope_theta=160000 for the shared yarn
/// table — so the 10000 base is hardcoded here to match the reference). Cheap
/// (~32 floats); computed per compressor-less layer, no shared cache needed.
pub fn main_inv_freq(config: &ModelConfig, gpu: &dyn GpuBackend) -> Result<DevicePtr> {
const MAIN_ROPE_THETA: f32 = 10000.0; // reference rope_theta for sliding layers
let rope = config.qk_rope_head_dim;
let n_pairs = rope / 2;
let dim_f = rope as f32;
let mut inv_freq = vec![0.0f32; n_pairs];
for (j, f) in inv_freq.iter_mut().enumerate() {
*f = 1.0 / MAIN_ROPE_THETA.powf((2 * j) as f32 / dim_f);
}
let bytes: Vec<u8> = inv_freq.iter().flat_map(|v| v.to_le_bytes()).collect();
let ptr = alloc_or_managed(gpu, bytes.len())?;
gpu.copy_h2d(&bytes, ptr)?;
Ok(ptr)
}
Loading