use crate::gguf::{
quantize_row_q8_k, quantized_matvec, quantized_matvec_preq, sparse_ternary_matvec,
ternary_matvec, BlockQ8K, GgmlType, GgufFile, GgufTokenizer, SparseTernaryMatrix,
TernaryMatrix,
};
use std::fmt::Write;
use std::time::Instant;
pub fn dump_hidden_jsonl_stderr(backend: &str, hidden: &[f32]) {
let l2 = hidden.iter().map(|x| x * x).sum::<f32>().sqrt();
let mut idxs: Vec<(usize, f32)> = hidden.iter().enumerate().map(|(i, &v)| (i, v)).collect();
idxs.sort_by(|a, b| {
b.1.abs()
.partial_cmp(&a.1.abs())
.unwrap_or(std::cmp::Ordering::Equal)
});
let dim = hidden.len();
let mut line = String::with_capacity(dim * 10 + 256);
let _ = write!(
line,
"{{\"backend\":\"{backend}\",\"kind\":\"pre_output_hidden\",\"dim\":{dim},\"l2\":{l2:.6},\"top8\":["
);
for (k, (i, v)) in idxs.iter().take(8).enumerate() {
if k > 0 {
line.push(',');
}
let _ = write!(line, "[{i},{v:.6}]");
}
line.push_str("],\"full\":[");
for (k, v) in hidden.iter().enumerate() {
if k > 0 {
line.push(',');
}
let _ = write!(line, "{v:.6}");
}
line.push_str("]}");
eprintln!("{line}");
}
pub type SurpriseVec<'a> = &'a [f32];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModelArch {
Llama,
Mistral,
Gemma2,
Gemma3n,
Gemma4,
Qwen2,
Qwen3,
Qwen3_5,
DeepSeekV3,
KimiK3,
Hy3,
}
impl ModelArch {
pub fn from_gguf<'a, G: crate::gguf::GgufSource<'a>>(gguf: &'a G) -> Self {
match gguf.meta_str("general.architecture") {
Some("mistral") => Self::Mistral,
Some("gemma2") => Self::Gemma2,
Some("gemma3n") => Self::Gemma3n,
Some("gemma4") => Self::Gemma4,
Some("qwen3moe" | "qwen3") => {
if gguf.meta_u32("qwen3.full_attention_interval").is_some()
|| gguf.meta_u32("qwen3moe.full_attention_interval").is_some()
{
Self::Qwen3_5
} else {
Self::Qwen3
}
}
Some("qwen35" | "qwen35moe") => Self::Qwen3_5,
Some(s) if s.starts_with("qwen") => Self::Qwen2,
Some("deepseek2") => Self::DeepSeekV3,
Some(s) if s.starts_with("kimi") => Self::KimiK3,
Some("hy3" | "hy3moe" | "hunyuan_moe") => Self::Hy3,
Some(s) if s.starts_with("hunyuan") => Self::Hy3,
_ => Self::Llama,
}
}
const fn meta_prefix(&self) -> &'static str {
match self {
Self::Llama => "llama",
Self::Mistral => "mistral",
Self::Gemma2 => "gemma2",
Self::Gemma3n => "gemma3n",
Self::Gemma4 => "gemma4",
Self::Qwen2 => "qwen2",
Self::Qwen3 | Self::Qwen3_5 => "qwen3",
Self::DeepSeekV3 => "deepseek2",
Self::KimiK3 => "kimi-k3",
Self::Hy3 => "hunyuan",
}
}
pub const fn use_neox_rope(&self) -> bool {
matches!(
self,
Self::Qwen2
| Self::Qwen3
| Self::Qwen3_5
| Self::Gemma2
| Self::Gemma3n
| Self::Gemma4
| Self::DeepSeekV3
)
}
fn resolve_prefix<'a, G: crate::gguf::GgufSource<'a>>(&self, gguf: &'a G) -> String {
let raw = gguf
.meta_str("general.architecture")
.unwrap_or(self.meta_prefix());
if gguf.meta_u32(&format!("{raw}.embedding_length")).is_some() {
return raw.to_string();
}
self.meta_prefix().to_string()
}
}
#[derive(Debug, Clone)]
pub struct AttentionExtrasConfig {
pub sliding_window: Option<usize>,
pub attn_logit_softcap: Option<f32>,
pub final_logit_softcap: Option<f32>,
}
#[derive(Debug, Clone)]
pub struct SsmDeltaNetConfig {
pub full_attention_interval: Option<usize>,
pub linear_num_kv_heads: Option<usize>,
pub linear_qk_head_dim: Option<usize>,
pub linear_kv_head_dim: Option<usize>,
pub linear_num_v_heads: Option<usize>,
pub linear_conv_kernel_dim: Option<usize>,
pub ssm_inner_size: Option<usize>,
pub ssm_state_size: Option<usize>,
pub ssm_group_count: Option<usize>,
pub ssm_time_step_rank: Option<usize>,
pub n_layer_nextn: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct MoeConfig {
pub num_experts: Option<usize>,
pub num_experts_active: Option<usize>,
pub expert_ffn_size: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct Gemma3nConfig {
pub sliding_window_pattern: Option<Vec<bool>>,
pub activation_sparsity_scale: Option<Vec<f32>>,
pub shared_kv_layers: Option<usize>,
pub per_layer_input_embedding_dim: Option<usize>,
pub altup_num_inputs: Option<usize>,
pub altup_active_idx: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct Gemma4Config {
pub head_dim_swa: Option<usize>,
pub rope_theta_swa: Option<f32>,
pub rope_dim_swa: Option<usize>,
pub ffn_size_per_layer: Option<Vec<usize>>,
}
#[derive(Debug, Clone)]
pub struct DeepSeekV3Config {
pub q_lora_rank: Option<usize>,
pub kv_lora_rank: Option<usize>,
pub qk_nope_head_dim: Option<usize>,
pub qk_rope_head_dim: Option<usize>,
pub v_head_dim: Option<usize>,
pub n_routed_experts: Option<usize>,
pub n_shared_experts: Option<usize>,
pub num_experts_per_tok: Option<usize>,
pub moe_intermediate_size: Option<usize>,
pub first_k_dense_replace: Option<usize>,
pub routed_scaling_factor: Option<f32>,
pub noaux_tc: Option<bool>,
pub mtp_layer: Option<usize>,
}
#[derive(Debug, Clone, Default)]
pub struct KimiDeltaConfig {
pub full_attn_layers: Option<Vec<usize>>,
pub kda_layers: Option<Vec<usize>>,
pub kda_head_dim: Option<usize>,
pub kda_num_heads: Option<usize>,
pub kda_short_conv_kernel_size: Option<usize>,
pub kda_use_full_rank_gate: Option<bool>,
pub kda_gate_lower_bound: Option<f32>,
pub q_lora_rank: Option<usize>,
pub kv_lora_rank: Option<usize>,
pub qk_nope_head_dim: Option<usize>,
pub qk_rope_head_dim: Option<usize>,
pub v_head_dim: Option<usize>,
pub mla_use_nope: Option<bool>,
pub mla_use_output_gate: Option<bool>,
pub attn_res_block_size: Option<usize>,
pub situ_beta: Option<f32>,
pub situ_linear_beta: Option<f32>,
pub n_routed_experts: Option<usize>,
pub num_experts_per_tok: Option<usize>,
pub n_shared_experts: Option<usize>,
pub num_expert_group: Option<usize>,
pub topk_group: Option<usize>,
pub moe_router_activation: Option<String>,
pub moe_topk_method: Option<String>,
pub moe_intermediate_size: Option<usize>,
pub first_k_dense_replace: Option<usize>,
pub moe_renormalize: Option<bool>,
pub routed_expert_hidden_size: Option<usize>,
pub latent_moe_use_norm: Option<bool>,
pub routed_scaling_factor: Option<f32>,
pub num_nextn_predict_layers: Option<usize>,
pub mxfp4_group_size: Option<usize>,
pub mxfp4_num_bits: Option<usize>,
}
#[cfg(feature = "hf-config")]
impl KimiDeltaConfig {
pub fn from_hf_config(json_bytes: &[u8]) -> Result<Self, serde_json::Error> {
let root: serde_json::Value = serde_json::from_slice(json_bytes)?;
let tc = &root["text_config"];
let la = &tc["linear_attn_config"];
let qw = &tc["quantization_config"]["config_groups"]["group_0"]["weights"];
let opt_usize = |v: &serde_json::Value| v.as_u64().map(|x| x as usize);
let opt_f32 = |v: &serde_json::Value| v.as_f64().map(|x| x as f32);
let opt_bool = |v: &serde_json::Value| v.as_bool();
let opt_str = |v: &serde_json::Value| v.as_str().map(str::to_owned);
let opt_vec_usize = |v: &serde_json::Value| {
v.as_array().map(|arr| {
arr.iter()
.filter_map(|x| x.as_u64().map(|n| n as usize))
.collect()
})
};
Ok(Self {
full_attn_layers: opt_vec_usize(&la["full_attn_layers"]),
kda_layers: opt_vec_usize(&la["kda_layers"]),
kda_head_dim: opt_usize(&la["head_dim"]),
kda_num_heads: opt_usize(&la["num_heads"]),
kda_short_conv_kernel_size: opt_usize(&la["short_conv_kernel_size"]),
kda_use_full_rank_gate: opt_bool(&la["use_full_rank_gate"]),
kda_gate_lower_bound: opt_f32(&la["gate_lower_bound"]),
q_lora_rank: opt_usize(&tc["q_lora_rank"]),
kv_lora_rank: opt_usize(&tc["kv_lora_rank"]),
qk_nope_head_dim: opt_usize(&tc["qk_nope_head_dim"]),
qk_rope_head_dim: opt_usize(&tc["qk_rope_head_dim"]),
v_head_dim: opt_usize(&tc["v_head_dim"]),
mla_use_nope: opt_bool(&tc["mla_use_nope"]),
mla_use_output_gate: opt_bool(&tc["mla_use_output_gate"]),
attn_res_block_size: opt_usize(&tc["attn_res_block_size"]),
situ_beta: opt_f32(&tc["activation_situ_beta"]),
situ_linear_beta: opt_f32(&tc["activation_situ_linear_beta"]),
n_routed_experts: opt_usize(&tc["num_experts"]),
num_experts_per_tok: opt_usize(&tc["num_experts_per_token"]),
n_shared_experts: opt_usize(&tc["num_shared_experts"]),
num_expert_group: opt_usize(&tc["num_expert_group"]),
topk_group: opt_usize(&tc["topk_group"]),
moe_router_activation: opt_str(&tc["moe_router_activation_func"]),
moe_topk_method: opt_str(&tc["topk_method"]),
moe_intermediate_size: opt_usize(&tc["moe_intermediate_size"]),
first_k_dense_replace: opt_usize(&tc["first_k_dense_replace"]),
moe_renormalize: opt_bool(&tc["moe_renormalize"]),
routed_expert_hidden_size: opt_usize(&tc["routed_expert_hidden_size"]),
latent_moe_use_norm: opt_bool(&tc["latent_moe_use_norm"]),
routed_scaling_factor: opt_f32(&tc["routed_scaling_factor"]),
num_nextn_predict_layers: opt_usize(&tc["num_nextn_predict_layers"]),
mxfp4_group_size: opt_usize(&qw["group_size"]),
mxfp4_num_bits: opt_usize(&qw["num_bits"]),
})
}
}
impl KimiDeltaConfig {
#[must_use]
pub fn from_gguf<'a, G: crate::gguf::GgufSource<'a>>(gguf: &'a G, prefix: &str) -> Self {
let opt_usize = |key: &str| gguf.meta_u32(key).map(|v| v as usize);
let opt_bool = |key: &str| gguf.meta_bool(key);
let opt_str = |key: &str| gguf.meta_str(key).map(str::to_owned);
let opt_f32 = |key: &str| gguf.meta_f32(key);
let opt_vec_usize = |key: &str| {
gguf.meta(key)
.and_then(crate::gguf::MetaValue::as_u32_array)
.map(|arr| arr.into_iter().map(|v| v as usize).collect())
};
let opt_usize_any = |keys: &[String]| keys.iter().find_map(|k| opt_usize(k));
let opt_f32_any = |keys: &[String]| keys.iter().find_map(|k| opt_f32(k));
let attn = |key: &str| format!("{prefix}.attention.{key}");
let ssm = |key: &str| format!("{prefix}.ssm.{key}");
let rope = |key: &str| format!("{prefix}.rope.{key}");
let k3 = |key: &str| format!("{prefix}.{key}");
let kda = |key: &str| format!("{prefix}.kda.{key}");
let attn_res = |key: &str| format!("{prefix}.attn_res.{key}");
let qk_rope_head_dim = opt_usize(&rope("dimension_count"));
let qk_nope_head_dim = opt_usize(&attn("key_length_mla"))
.zip(qk_rope_head_dim)
.map(|(kl_mla, rope_dim)| kl_mla.saturating_sub(rope_dim));
Self {
full_attn_layers: opt_vec_usize(&k3("full_attn_layers")),
kda_layers: opt_vec_usize(&k3("kda_layers")),
kda_head_dim: opt_usize_any(&[attn("kda_head_dim"), kda("head_dim")]),
kda_num_heads: opt_usize(&attn("head_count")),
kda_short_conv_kernel_size: opt_usize(&ssm("conv_kernel")),
kda_use_full_rank_gate: opt_bool(&k3("use_full_rank_gate")),
kda_gate_lower_bound: opt_f32_any(&[k3("gate_lower_bound"), kda("gate_lower_bound")]),
q_lora_rank: opt_usize(&attn("q_lora_rank")),
kv_lora_rank: opt_usize(&attn("kv_lora_rank")),
qk_nope_head_dim,
qk_rope_head_dim,
v_head_dim: opt_usize(&attn("value_length_mla")),
mla_use_nope: opt_bool(&k3("mla_use_nope")),
mla_use_output_gate: opt_bool(&k3("mla_use_output_gate")),
attn_res_block_size: opt_usize_any(&[
k3("attn_res_block_size"),
attn_res("block_size"),
]),
situ_beta: opt_f32_any(&[k3("activation_situ_beta"), k3("activation.situ_beta")]),
situ_linear_beta: opt_f32_any(&[
k3("activation_situ_linear_beta"),
k3("activation.situ_linear_beta"),
]),
n_routed_experts: opt_usize(&k3("expert_count")),
num_experts_per_tok: opt_usize(&k3("expert_used_count")),
n_shared_experts: opt_usize(&k3("expert_shared_count")),
num_expert_group: opt_usize(&k3("num_expert_group")),
topk_group: opt_usize(&k3("topk_group")),
moe_router_activation: opt_str(&k3("moe_router_activation_func"))
.or_else(|| opt_str(&k3("expert_gating_func"))),
moe_topk_method: opt_str(&k3("topk_method")),
moe_intermediate_size: opt_usize(&k3("expert_feed_forward_length")),
first_k_dense_replace: opt_usize(&k3("leading_dense_block_count")),
moe_renormalize: opt_bool(&k3("moe_renormalize"))
.or_else(|| opt_bool(&k3("expert_weights_norm"))),
routed_expert_hidden_size: opt_usize_any(&[
k3("routed_expert_hidden_size"),
k3("expert_latent_length"),
]),
latent_moe_use_norm: opt_bool(&k3("latent_moe_use_norm")),
routed_scaling_factor: opt_f32(&k3("expert_weights_scale")),
num_nextn_predict_layers: gguf.meta_u32(&k3("mtp_layer_count")).map(|v| v as usize),
mxfp4_group_size: None,
mxfp4_num_bits: None,
}
}
#[must_use]
pub fn is_mla_layer(&self, il: usize) -> Option<bool> {
self.full_attn_layers
.as_ref()
.map(|full| full.contains(&il))
}
}
#[cfg(test)]
mod kimi_k3_gguf_loader_tests {
use super::{KimiDeltaConfig, ModelArch};
use crate::gguf::GgufFile;
const GGUF_TYPE_U32: u32 = 4;
const GGUF_TYPE_F32: u32 = 6;
const GGUF_TYPE_BOOL: u32 = 7;
const GGUF_TYPE_STRING: u32 = 8;
const GGUF_TYPE_ARRAY: u32 = 9;
fn write_u32(buf: &mut Vec<u8>, v: u32) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn write_u64(buf: &mut Vec<u8>, v: u64) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn write_f32(buf: &mut Vec<u8>, v: f32) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn write_str(buf: &mut Vec<u8>, s: &str) {
write_u64(buf, s.len() as u64);
buf.extend_from_slice(s.as_bytes());
}
fn write_kv_str(buf: &mut Vec<u8>, key: &str, value: &str) {
write_str(buf, key);
write_u32(buf, GGUF_TYPE_STRING);
write_str(buf, value);
}
fn write_kv_u32(buf: &mut Vec<u8>, key: &str, value: u32) {
write_str(buf, key);
write_u32(buf, GGUF_TYPE_U32);
write_u32(buf, value);
}
fn write_kv_f32(buf: &mut Vec<u8>, key: &str, value: f32) {
write_str(buf, key);
write_u32(buf, GGUF_TYPE_F32);
write_f32(buf, value);
}
fn write_kv_bool(buf: &mut Vec<u8>, key: &str, value: bool) {
write_str(buf, key);
write_u32(buf, GGUF_TYPE_BOOL);
buf.push(u8::from(value));
}
fn write_kv_u32_array(buf: &mut Vec<u8>, key: &str, values: &[u32]) {
write_str(buf, key);
write_u32(buf, GGUF_TYPE_ARRAY);
write_u32(buf, GGUF_TYPE_U32);
write_u64(buf, values.len() as u64);
for &v in values {
write_u32(buf, v);
}
}
#[allow(clippy::too_many_lines)]
fn build_synthetic_kimi_k3_gguf() -> Vec<u8> {
let mut kv = Vec::new();
let mut n_kv: u64 = 0;
write_kv_str(&mut kv, "general.architecture", "kimi-k3");
n_kv += 1;
{
write_kv_u32(&mut kv, "general.alignment", 32);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.block_count", 8);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.embedding_length", 64);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.feed_forward_length", 128);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.context_length", 4096);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.vocab_size", 256);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.attention.head_count", 8);
n_kv += 1;
write_kv_f32(&mut kv, "kimi-k3.attention.layer_norm_rms_epsilon", 1e-5);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.attention.q_lora_rank", 16);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.attention.kv_lora_rank", 8);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.attention.key_length_mla", 12);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.attention.value_length_mla", 8);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.rope.dimension_count", 4);
n_kv += 1;
write_kv_bool(&mut kv, "kimi-k3.mla_use_nope", true);
n_kv += 1;
write_kv_bool(&mut kv, "kimi-k3.mla_use_output_gate", true);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.attention.kda_head_dim", 8);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.ssm.conv_kernel", 4);
n_kv += 1;
write_kv_bool(&mut kv, "kimi-k3.use_full_rank_gate", true);
n_kv += 1;
write_kv_f32(&mut kv, "kimi-k3.gate_lower_bound", -5.0);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.attn_res_block_size", 4);
n_kv += 1;
write_kv_f32(&mut kv, "kimi-k3.activation_situ_beta", 4.0);
n_kv += 1;
write_kv_f32(&mut kv, "kimi-k3.activation_situ_linear_beta", 25.0);
n_kv += 1;
write_kv_str(&mut kv, "kimi-k3.activation", "situ");
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.expert_count", 32);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.expert_used_count", 4);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.expert_shared_count", 2);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.num_expert_group", 1);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.topk_group", 1);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.expert_feed_forward_length", 64);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.leading_dense_block_count", 1);
n_kv += 1;
write_kv_u32(&mut kv, "kimi-k3.routed_expert_hidden_size", 32);
n_kv += 1;
write_kv_bool(&mut kv, "kimi-k3.moe_renormalize", true);
n_kv += 1;
write_kv_bool(&mut kv, "kimi-k3.latent_moe_use_norm", true);
n_kv += 1;
write_kv_f32(&mut kv, "kimi-k3.expert_weights_scale", 1.0);
n_kv += 1;
write_kv_str(&mut kv, "kimi-k3.moe_router_activation_func", "sigmoid");
n_kv += 1;
write_kv_str(&mut kv, "kimi-k3.topk_method", "noaux_tc");
n_kv += 1;
write_kv_u32_array(&mut kv, "kimi-k3.full_attn_layers", &[3, 7]);
n_kv += 1;
write_kv_u32_array(&mut kv, "kimi-k3.kda_layers", &[0, 1, 2, 4, 5, 6]);
n_kv += 1;
}
let mut buf = Vec::new();
buf.extend_from_slice(b"GGUF");
write_u32(&mut buf, 3); write_u64(&mut buf, 1); write_u64(&mut buf, n_kv);
buf.extend_from_slice(&kv);
write_str(&mut buf, "token_embd.weight");
write_u32(&mut buf, 1); write_u64(&mut buf, 1); write_u32(&mut buf, 0); write_u64(&mut buf, 0);
while !buf.len().is_multiple_of(32) {
buf.push(0);
}
write_f32(&mut buf, 42.0);
buf
}
#[test]
fn detects_kimi_k3_arch_from_general_architecture() {
let bytes = build_synthetic_kimi_k3_gguf();
let gguf = GgufFile::parse(&bytes).expect("synthetic K3 GGUF must parse");
assert_eq!(gguf.meta_str("general.architecture"), Some("kimi-k3"));
assert_eq!(ModelArch::from_gguf(&gguf), ModelArch::KimiK3);
}
#[test]
fn parses_kimi_k3_metadata_from_synthetic_gguf() {
let bytes = build_synthetic_kimi_k3_gguf();
let gguf = GgufFile::parse(&bytes).expect("parse");
let cfg = KimiDeltaConfig::from_gguf(&gguf, "kimi-k3");
assert_eq!(cfg.q_lora_rank, Some(16));
assert_eq!(cfg.kv_lora_rank, Some(8));
assert_eq!(cfg.qk_rope_head_dim, Some(4));
assert_eq!(cfg.qk_nope_head_dim, Some(8));
assert_eq!(cfg.v_head_dim, Some(8));
assert_eq!(cfg.mla_use_nope, Some(true));
assert_eq!(cfg.mla_use_output_gate, Some(true));
assert_eq!(cfg.kda_head_dim, Some(8));
assert_eq!(cfg.kda_num_heads, Some(8));
assert_eq!(cfg.kda_short_conv_kernel_size, Some(4));
assert_eq!(cfg.kda_use_full_rank_gate, Some(true));
assert_eq!(cfg.kda_gate_lower_bound, Some(-5.0));
assert_eq!(cfg.attn_res_block_size, Some(4));
assert_eq!(cfg.situ_beta, Some(4.0));
assert_eq!(cfg.situ_linear_beta, Some(25.0));
assert_eq!(cfg.n_routed_experts, Some(32));
assert_eq!(cfg.num_experts_per_tok, Some(4));
assert_eq!(cfg.n_shared_experts, Some(2));
assert_eq!(cfg.num_expert_group, Some(1));
assert_eq!(cfg.topk_group, Some(1));
assert_eq!(cfg.moe_intermediate_size, Some(64));
assert_eq!(cfg.first_k_dense_replace, Some(1));
assert_eq!(cfg.routed_expert_hidden_size, Some(32));
assert_eq!(cfg.moe_renormalize, Some(true));
assert_eq!(cfg.latent_moe_use_norm, Some(true));
assert_eq!(cfg.routed_scaling_factor, Some(1.0));
assert_eq!(cfg.moe_router_activation.as_deref(), Some("sigmoid"));
assert_eq!(cfg.moe_topk_method.as_deref(), Some("noaux_tc"));
assert_eq!(cfg.full_attn_layers.as_deref(), Some(&[3usize, 7][..]));
assert_eq!(
cfg.kda_layers.as_deref(),
Some(&[0usize, 1, 2, 4, 5, 6][..])
);
assert_eq!(cfg.num_nextn_predict_layers, None);
assert_eq!(cfg.mxfp4_group_size, None);
assert_eq!(cfg.mxfp4_num_bits, None);
}
#[test]
fn is_mla_layer_matches_full_attn_layers_zero_indexed() {
let bytes = build_synthetic_kimi_k3_gguf();
let gguf = GgufFile::parse(&bytes).expect("parse");
let cfg = KimiDeltaConfig::from_gguf(&gguf, "kimi-k3");
for il in 0..8 {
let expect_mla = il == 3 || il == 7;
assert_eq!(
cfg.is_mla_layer(il),
Some(expect_mla),
"layer {il}: expected MLA = {expect_mla}"
);
}
}
#[test]
fn is_mla_layer_returns_none_when_full_attn_layers_absent() {
let cfg = KimiDeltaConfig::default();
assert_eq!(cfg.is_mla_layer(0), None);
assert_eq!(cfg.is_mla_layer(93), None);
}
#[test]
fn missing_optional_fields_default_to_none() {
let mut buf = Vec::new();
buf.extend_from_slice(b"GGUF");
write_u32(&mut buf, 3);
write_u64(&mut buf, 1); write_u64(&mut buf, 2); write_kv_str(&mut buf, "general.architecture", "kimi-k3");
write_kv_u32(&mut buf, "general.alignment", 32);
write_str(&mut buf, "token_embd.weight");
write_u32(&mut buf, 1);
write_u64(&mut buf, 1);
write_u32(&mut buf, 0);
write_u64(&mut buf, 0);
while !buf.len().is_multiple_of(32) {
buf.push(0);
}
write_f32(&mut buf, 0.0);
let gguf = GgufFile::parse(&buf).expect("minimal K3 parse");
let cfg = KimiDeltaConfig::from_gguf(&gguf, "kimi-k3");
assert!(cfg.q_lora_rank.is_none());
assert!(cfg.n_routed_experts.is_none());
assert!(cfg.full_attn_layers.is_none());
assert!(cfg.attn_res_block_size.is_none());
assert!(cfg.moe_router_activation.is_none());
}
#[test]
fn meta_prefix_returns_hyphenated_kimi_k3() {
assert_eq!(ModelArch::KimiK3.meta_prefix(), "kimi-k3");
}
#[test]
fn load_weight_ref_any_shape_reads_both_dims_from_gguf() {
let mut buf = Vec::new();
buf.extend_from_slice(b"GGUF");
write_u32(&mut buf, 3);
write_u64(&mut buf, 1); write_u64(&mut buf, 1); write_kv_u32(&mut buf, "general.alignment", 32);
write_str(&mut buf, "any_shape_test");
write_u32(&mut buf, 2); write_u64(&mut buf, 3); write_u64(&mut buf, 2); write_u32(&mut buf, 0); write_u64(&mut buf, 0); while !buf.len().is_multiple_of(32) {
buf.push(0);
}
for x in [1.0_f32, 2.0, 3.0, 4.0, 5.0, 6.0] {
write_f32(&mut buf, x);
}
let gguf = GgufFile::parse(&buf).expect("parse");
let wref = super::load_weight_ref_any_shape(&gguf, "any_shape_test")
.expect("shape helper must find the tensor");
assert_eq!(wref.cols, 3, "cols must come from dims[0]");
assert_eq!(wref.rows, 2, "rows must come from dims[1]");
}
#[test]
fn load_weight_ref_any_shape_1d_tensor_gets_rows_1() {
let mut buf = Vec::new();
buf.extend_from_slice(b"GGUF");
write_u32(&mut buf, 3);
write_u64(&mut buf, 1);
write_u64(&mut buf, 1);
write_kv_u32(&mut buf, "general.alignment", 32);
write_str(&mut buf, "norm_1d");
write_u32(&mut buf, 1); write_u64(&mut buf, 4); write_u32(&mut buf, 0); write_u64(&mut buf, 0);
while !buf.len().is_multiple_of(32) {
buf.push(0);
}
for x in [1.0_f32, 2.0, 3.0, 4.0] {
write_f32(&mut buf, x);
}
let gguf = GgufFile::parse(&buf).expect("parse");
let wref = super::load_weight_ref_any_shape(&gguf, "norm_1d")
.expect("1D helper must find the tensor");
assert_eq!(wref.cols, 4);
assert_eq!(wref.rows, 1, "1D tensor falls back to rows=1");
}
#[test]
fn model_weights_loader_returns_err_on_missing_global_tensors() {
let bytes = build_synthetic_kimi_k3_gguf();
let gguf = GgufFile::parse(&bytes).expect("parse fixture");
let config = crate::gguf::GgufFile::parse(&bytes)
.and_then(|g| super::Llama3Config::from_gguf(&g))
.expect("kimi-k3 config must load from synthetic fixture");
assert_eq!(config.arch, ModelArch::KimiK3);
let Err(err) = super::load_kimi_k3_model_weights(&gguf, &config) else {
panic!("loader must Err on the metadata-only fixture");
};
assert!(
err.contains("output_norm.weight") || err.contains("token_embd"),
"expected descriptive missing-tensor error, got: {err}"
);
}
#[test]
fn layer_weights_loader_returns_err_on_missing_attn_norm() {
let bytes = build_synthetic_kimi_k3_gguf();
let gguf = GgufFile::parse(&bytes).expect("parse fixture");
let config = super::Llama3Config::from_gguf(&gguf).expect("config");
let Err(err) = super::load_kimi_k3_layer_weights(&gguf, 0, &config) else {
panic!("layer loader must Err on empty fixture");
};
assert!(
err.contains("blk.0.attn_norm"),
"expected error to name blk.0.attn_norm, got: {err}"
);
}
#[test]
fn layer_weights_loader_dispatches_mla_vs_kda_by_layer_index() {
let bytes = build_synthetic_kimi_k3_gguf();
let gguf = GgufFile::parse(&bytes).expect("parse fixture");
let config = super::Llama3Config::from_gguf(&gguf).expect("config");
let kd = config.kimi_delta.as_ref().expect("kimi_delta populated");
assert_eq!(kd.is_mla_layer(0), Some(false), "layer 0 = KDA");
assert_eq!(kd.is_mla_layer(3), Some(true), "layer 3 = MLA");
assert_eq!(kd.is_mla_layer(7), Some(true), "layer 7 = MLA");
assert_eq!(kd.is_mla_layer(4), Some(false), "layer 4 = KDA");
}
#[test]
fn layer_weights_loader_dense_vs_moe_matches_first_k_dense_replace() {
let bytes = build_synthetic_kimi_k3_gguf();
let gguf = GgufFile::parse(&bytes).expect("parse");
let config = super::Llama3Config::from_gguf(&gguf).expect("config");
let kd = config.kimi_delta.as_ref().expect("kimi_delta populated");
assert_eq!(kd.first_k_dense_replace, Some(1));
let first_k = kd.first_k_dense_replace.unwrap();
assert!(0 < first_k, "layer 0 must be Dense");
assert!((1 >= first_k), "layer 1 must be LatentMoE (not Dense)");
}
}
#[cfg(all(test, feature = "hf-config"))]
mod kimi_k3_hf_config_tests {
use super::KimiDeltaConfig;
const KIMI_K3_FIXTURE: &str = r#"{
"model_type": "kimi_k3",
"text_config": {
"model_type": "kimi_linear",
"hidden_size": 7168,
"num_hidden_layers": 93,
"num_attention_heads": 96,
"num_key_value_heads": 96,
"num_experts": 896,
"num_experts_per_token": 16,
"num_shared_experts": 2,
"num_expert_group": 1,
"topk_group": 1,
"topk_method": "noaux_tc",
"moe_router_activation_func": "sigmoid",
"moe_intermediate_size": 3072,
"moe_renormalize": true,
"first_k_dense_replace": 1,
"num_nextn_predict_layers": 0,
"routed_expert_hidden_size": 3584,
"routed_scaling_factor": 1.0,
"latent_moe_use_norm": true,
"q_lora_rank": 1536,
"kv_lora_rank": 512,
"qk_nope_head_dim": 128,
"qk_rope_head_dim": 64,
"v_head_dim": 128,
"mla_use_nope": true,
"mla_use_output_gate": true,
"attn_res_block_size": 12,
"activation_situ_beta": 4.0,
"activation_situ_linear_beta": 25.0,
"linear_attn_config": {
"full_attn_layers": [4, 8, 12, 93],
"kda_layers": [1, 2, 3, 5, 6, 7],
"num_heads": 96,
"head_dim": 128,
"short_conv_kernel_size": 4,
"use_full_rank_gate": true,
"gate_lower_bound": -5.0
},
"quantization_config": {
"config_groups": {
"group_0": {
"weights": {
"num_bits": 4,
"group_size": 32
}
}
}
}
}
}"#;
#[test]
fn parses_kimi_k3_confirmed_spec() {
let cfg = KimiDeltaConfig::from_hf_config(KIMI_K3_FIXTURE.as_bytes())
.expect("Kimi K3 fixture should parse");
assert_eq!(cfg.n_routed_experts, Some(896));
assert_eq!(cfg.num_experts_per_tok, Some(16));
assert_eq!(cfg.n_shared_experts, Some(2));
assert_eq!(cfg.num_expert_group, Some(1));
assert_eq!(cfg.topk_group, Some(1));
assert_eq!(cfg.moe_intermediate_size, Some(3072));
assert_eq!(cfg.first_k_dense_replace, Some(1));
assert_eq!(cfg.num_nextn_predict_layers, Some(0));
assert_eq!(cfg.routed_expert_hidden_size, Some(3584));
assert_eq!(cfg.moe_renormalize, Some(true));
assert_eq!(cfg.latent_moe_use_norm, Some(true));
assert_eq!(cfg.routed_scaling_factor, Some(1.0));
assert_eq!(cfg.moe_router_activation.as_deref(), Some("sigmoid"));
assert_eq!(cfg.moe_topk_method.as_deref(), Some("noaux_tc"));
assert_eq!(cfg.q_lora_rank, Some(1536));
assert_eq!(cfg.kv_lora_rank, Some(512));
assert_eq!(cfg.qk_nope_head_dim, Some(128));
assert_eq!(cfg.qk_rope_head_dim, Some(64));
assert_eq!(cfg.v_head_dim, Some(128));
assert_eq!(cfg.mla_use_nope, Some(true));
assert_eq!(cfg.mla_use_output_gate, Some(true));
assert_eq!(cfg.attn_res_block_size, Some(12));
assert_eq!(cfg.situ_beta, Some(4.0));
assert_eq!(cfg.situ_linear_beta, Some(25.0));
assert_eq!(cfg.kda_num_heads, Some(96));
assert_eq!(cfg.kda_head_dim, Some(128));
assert_eq!(cfg.kda_short_conv_kernel_size, Some(4));
assert_eq!(cfg.kda_use_full_rank_gate, Some(true));
assert_eq!(cfg.kda_gate_lower_bound, Some(-5.0));
assert_eq!(
cfg.full_attn_layers.as_deref(),
Some(&[4usize, 8, 12, 93][..]),
);
assert_eq!(
cfg.kda_layers.as_deref(),
Some(&[1usize, 2, 3, 5, 6, 7][..]),
);
assert_eq!(cfg.mxfp4_num_bits, Some(4));
assert_eq!(cfg.mxfp4_group_size, Some(32));
}
#[test]
fn missing_fields_default_to_none() {
let minimal = br#"{"text_config": {}}"#;
let cfg = KimiDeltaConfig::from_hf_config(minimal).expect("empty text_config still parses");
assert!(cfg.n_routed_experts.is_none());
assert!(cfg.full_attn_layers.is_none());
assert!(cfg.moe_router_activation.is_none());
assert!(cfg.mxfp4_num_bits.is_none());
}
#[test]
fn missing_text_config_is_all_none() {
let bare = b"{}";
let cfg = KimiDeltaConfig::from_hf_config(bare).expect("bare root object still parses");
assert!(cfg.n_routed_experts.is_none());
assert!(cfg.q_lora_rank.is_none());
}
#[test]
fn malformed_json_returns_error() {
assert!(KimiDeltaConfig::from_hf_config(b"{not valid json").is_err());
}
#[test]
fn confirmed_hybrid_layer_totals() {
let cfg = KimiDeltaConfig::from_hf_config(KIMI_K3_FIXTURE.as_bytes()).unwrap();
let full = cfg.full_attn_layers.expect("full_attn_layers present");
let kda = cfg.kda_layers.expect("kda_layers present");
assert_eq!(full.len(), 4, "fixture uses 4 truncated MLA layers");
assert_eq!(kda.len(), 6, "fixture uses 6 truncated KDA layers");
}
}
#[derive(Debug, Clone)]
pub struct KimiDeltaState {
state: Vec<f32>,
d_k: usize,
d_v: usize,
}
impl KimiDeltaState {
#[must_use]
pub fn new(d_k: usize, d_v: usize) -> Self {
Self {
state: vec![0.0; d_k * d_v],
d_k,
d_v,
}
}
#[inline]
#[must_use]
pub const fn d_k(&self) -> usize {
self.d_k
}
#[inline]
#[must_use]
pub const fn d_v(&self) -> usize {
self.d_v
}
pub fn reset(&mut self) {
self.state.fill(0.0);
}
#[inline]
#[must_use]
pub fn as_slice(&self) -> &[f32] {
&self.state
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [f32] {
&mut self.state
}
}
pub fn kimi_delta_step(state: &mut KimiDeltaState, k: &[f32], v: &[f32], alpha: &[f32], beta: f32) {
let d_k = state.d_k;
let d_v = state.d_v;
assert_eq!(k.len(), d_k, "k length must equal d_k");
assert_eq!(v.len(), d_v, "v length must equal d_v");
assert_eq!(alpha.len(), d_k, "alpha length must equal d_k");
let s = &mut state.state;
for i in 0..d_k {
let a = alpha[i];
let row_start = i * d_v;
for j in 0..d_v {
s[row_start + j] *= a;
}
}
let mut w = vec![0.0_f32; d_v];
for i in 0..d_k {
let ki = k[i];
if ki == 0.0 {
continue;
}
let row_start = i * d_v;
for j in 0..d_v {
w[j] += ki * s[row_start + j];
}
}
for i in 0..d_k {
let ki = k[i];
let coef = beta * ki;
if coef == 0.0 {
continue;
}
let row_start = i * d_v;
for j in 0..d_v {
s[row_start + j] += coef * (v[j] - w[j]);
}
}
}
#[must_use]
pub fn kimi_delta_read(state: &KimiDeltaState, q: &[f32]) -> Vec<f32> {
let d_k = state.d_k;
let d_v = state.d_v;
assert_eq!(q.len(), d_k, "q length must equal d_k");
let s = &state.state;
let mut out = vec![0.0_f32; d_v];
for i in 0..d_k {
let qi = q[i];
if qi == 0.0 {
continue;
}
let row_start = i * d_v;
for j in 0..d_v {
out[j] += qi * s[row_start + j];
}
}
out
}
#[must_use]
pub fn kimi_delta_lower_bounded_decay(z: &[f32], log_scale_a: f32, g_min: f32) -> Vec<f32> {
let a = log_scale_a.exp();
z.iter()
.map(|&zi| {
let g = g_min * sigmoid(a * zi);
g.exp()
})
.collect()
}
#[must_use]
pub fn kimi_delta_output_gate(
o_bar: &[f32],
gate_pre: &[f32],
rms_weight: Option<&[f32]>,
rms_eps: f32,
w_out: &[f32],
d_out: usize,
) -> Vec<f32> {
let d_v = o_bar.len();
assert_eq!(
gate_pre.len(),
d_v,
"gate_pre length must equal o_bar length"
);
assert_eq!(
w_out.len(),
d_out * d_v,
"w_out length must equal d_out * d_v"
);
if let Some(gamma) = rms_weight {
assert_eq!(gamma.len(), d_v, "rms_weight length must equal d_v");
}
let mut gated = vec![0.0_f32; d_v];
if let Some(gamma) = rms_weight {
let mut ss = 0.0_f64;
for &o in o_bar {
ss += f64::from(o) * f64::from(o);
}
let mean = (ss / d_v as f64) as f32;
let scale = (mean + rms_eps).sqrt().recip();
for j in 0..d_v {
gated[j] = sigmoid(gate_pre[j]) * o_bar[j] * scale * gamma[j];
}
} else {
for j in 0..d_v {
gated[j] = sigmoid(gate_pre[j]) * o_bar[j];
}
}
let mut y = vec![0.0_f32; d_out];
for i in 0..d_out {
let row_start = i * d_v;
let mut acc = 0.0_f64;
for j in 0..d_v {
acc += f64::from(w_out[row_start + j]) * f64::from(gated[j]);
}
y[i] = acc as f32;
}
y
}
#[cfg(test)]
mod kimi_delta_tests {
use super::{
kimi_delta_lower_bounded_decay, kimi_delta_output_gate, kimi_delta_read, kimi_delta_step,
KimiDeltaState,
};
fn state_from(d_k: usize, d_v: usize, buf: Vec<f32>) -> KimiDeltaState {
assert_eq!(buf.len(), d_k * d_v);
let mut s = KimiDeltaState::new(d_k, d_v);
s.as_mut_slice().copy_from_slice(&buf);
s
}
#[test]
fn new_state_is_zeroed() {
let s = KimiDeltaState::new(4, 4);
assert_eq!(s.d_k(), 4);
assert_eq!(s.d_v(), 4);
assert!(s.as_slice().iter().all(|&x| x == 0.0));
}
#[test]
fn reset_zeroes_state() {
let mut s = state_from(2, 3, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
s.reset();
assert!(s.as_slice().iter().all(|&x| x == 0.0));
}
#[test]
fn step_beta_1_alpha_1_from_zero_writes_k_v_transpose() {
let mut s = KimiDeltaState::new(4, 4);
let k = [1.0_f32, 0.0, 0.0, 0.0];
let v = [2.0_f32, -1.0, 3.5, 0.25];
let alpha = [1.0_f32; 4];
kimi_delta_step(&mut s, &k, &v, &alpha, 1.0);
let expected = [
2.0, -1.0, 3.5, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ];
assert_eq!(s.as_slice(), &expected);
}
#[test]
fn step_alpha_0_annihilates_history_before_write() {
let mut s = state_from(2, 2, vec![7.0_f32, 8.0, 9.0, 10.0]);
let k = [1.0_f32, 0.0];
let v = [3.0_f32, 4.0];
let alpha = [0.0_f32, 0.0];
kimi_delta_step(&mut s, &k, &v, &alpha, 1.0);
let expected = [
3.0, 4.0, 0.0, 0.0, ];
assert_eq!(s.as_slice(), &expected);
}
#[test]
fn step_beta_0_only_applies_row_decay() {
let mut s = state_from(2, 2, vec![10.0_f32, 20.0, 40.0, 80.0]);
let k = [1.0_f32, 1.0];
let v = [99.0_f32, 99.0];
let alpha = [0.5_f32, 0.25];
kimi_delta_step(&mut s, &k, &v, &alpha, 0.0);
let expected = [
5.0, 10.0, 10.0, 20.0, ];
assert_eq!(s.as_slice(), &expected);
}
#[test]
fn step_two_orthogonal_writes_produce_two_row_state() {
let mut s = KimiDeltaState::new(2, 2);
let alpha = [1.0_f32, 1.0];
kimi_delta_step(&mut s, &[1.0, 0.0], &[1.0, 2.0], &alpha, 1.0);
kimi_delta_step(&mut s, &[0.0, 1.0], &[3.0, 4.0], &alpha, 1.0);
let expected = [
1.0, 2.0, 3.0, 4.0, ];
assert_eq!(s.as_slice(), &expected);
}
#[test]
fn read_from_zero_state_returns_zero() {
let s = KimiDeltaState::new(4, 4);
let q = [1.0_f32, 2.0, 3.0, 4.0];
let out = kimi_delta_read(&s, &q);
assert_eq!(out, vec![0.0; 4]);
}
#[test]
fn read_recovers_v_after_single_write() {
let mut s = KimiDeltaState::new(2, 2);
kimi_delta_step(&mut s, &[1.0, 0.0], &[5.0, -3.0], &[1.0, 1.0], 1.0);
let out = kimi_delta_read(&s, &[1.0, 0.0]);
assert_eq!(out, vec![5.0, -3.0]);
}
#[test]
fn lower_bounded_decay_at_zero_z_is_sigmoid_half() {
let alpha = kimi_delta_lower_bounded_decay(&[0.0, 0.0, 0.0], 0.0, -5.0);
let expected = (-2.5_f32).exp();
for &a in &alpha {
assert!(
(a - expected).abs() < 1e-6,
"α = {a}, expected ≈ {expected}"
);
}
}
#[test]
fn lower_bounded_decay_saturates_to_lower_bound() {
let alpha = kimi_delta_lower_bounded_decay(&[100.0, 100.0], 0.0, -5.0);
let expected = (-5.0_f32).exp();
for &a in &alpha {
assert!(
(a - expected).abs() < 1e-4,
"large-positive-z α = {a}, expected ≈ {expected}"
);
}
}
#[test]
fn lower_bounded_decay_saturates_to_one() {
let alpha = kimi_delta_lower_bounded_decay(&[-100.0, -100.0], 0.0, -5.0);
for &a in &alpha {
assert!(
(a - 1.0).abs() < 1e-6,
"large-negative-z α = {a}, expected ≈ 1.0"
);
}
}
#[test]
fn output_gate_zero_gate_pre_gives_half_rms_norm() {
let o_bar = [3.0_f32, 4.0]; let inv_scale = (12.5_f32).sqrt();
let y = kimi_delta_output_gate(
&o_bar,
&[0.0, 0.0],
Some(&[1.0, 1.0]),
1e-6,
&[1.0, 0.0, 0.0, 1.0], 2,
);
let expected = [0.5 * 3.0 / inv_scale, 0.5 * 4.0 / inv_scale];
for i in 0..2 {
assert!(
(y[i] - expected[i]).abs() < 1e-4,
"y[{i}] = {}, expected ≈ {}",
y[i],
expected[i]
);
}
}
#[test]
fn output_gate_zero_o_bar_gives_zero() {
let y = kimi_delta_output_gate(
&[0.0, 0.0, 0.0, 0.0],
&[1.5, -2.0, 3.0, 0.7],
Some(&[1.0, 1.0, 1.0, 1.0]),
1e-6,
&[1.0_f32; 4 * 4], 4,
);
assert_eq!(y, vec![0.0; 4]);
}
#[test]
fn output_gate_without_rms_norm_matches_mla_shape() {
let o_bar = [2.0_f32, 4.0];
let y = kimi_delta_output_gate(
&o_bar,
&[0.0, 0.0],
None,
1e-6,
&[1.0, 0.0, 0.0, 1.0], 2,
);
assert_eq!(y, vec![1.0, 2.0]); }
}
#[derive(Debug, Clone)]
pub struct KimiDeltaHeadCache {
pub state: KimiDeltaState,
conv_state_q: Vec<f32>,
conv_state_k: Vec<f32>,
conv_state_v: Vec<f32>,
ring_pos_q: usize,
ring_pos_k: usize,
ring_pos_v: usize,
kernel_size: usize,
d_k: usize,
d_v: usize,
}
impl KimiDeltaHeadCache {
#[must_use]
pub fn new(d_k: usize, d_v: usize, kernel_size: usize) -> Self {
assert!(kernel_size >= 2, "kernel_size must be at least 2");
let hist = kernel_size - 1;
Self {
state: KimiDeltaState::new(d_k, d_v),
conv_state_q: vec![0.0; hist * d_k],
conv_state_k: vec![0.0; hist * d_k],
conv_state_v: vec![0.0; hist * d_v],
ring_pos_q: 0,
ring_pos_k: 0,
ring_pos_v: 0,
kernel_size,
d_k,
d_v,
}
}
pub fn reset(&mut self) {
self.state.reset();
self.conv_state_q.fill(0.0);
self.conv_state_k.fill(0.0);
self.conv_state_v.fill(0.0);
self.ring_pos_q = 0;
self.ring_pos_k = 0;
self.ring_pos_v = 0;
}
#[inline]
#[must_use]
pub const fn d_k(&self) -> usize {
self.d_k
}
#[inline]
#[must_use]
pub const fn d_v(&self) -> usize {
self.d_v
}
#[inline]
#[must_use]
pub const fn kernel_size(&self) -> usize {
self.kernel_size
}
pub fn apply_update(&mut self, update: &KimiDeltaHeadUpdate) {
assert_eq!(update.q_pre.len(), self.d_k, "q_pre length must equal d_k");
assert_eq!(update.k_pre.len(), self.d_k, "k_pre length must equal d_k");
assert_eq!(update.v_pre.len(), self.d_v, "v_pre length must equal d_v");
assert_eq!(
update.k_conv.len(),
self.d_k,
"k_conv length must equal d_k"
);
assert_eq!(
update.v_conv.len(),
self.d_v,
"v_conv length must equal d_v"
);
assert_eq!(update.alpha.len(), self.d_k, "alpha length must equal d_k");
let hist = self.kernel_size - 1;
{
let off = self.ring_pos_q * self.d_k;
self.conv_state_q[off..off + self.d_k].copy_from_slice(&update.q_pre);
self.ring_pos_q = (self.ring_pos_q + 1) % hist;
}
{
let off = self.ring_pos_k * self.d_k;
self.conv_state_k[off..off + self.d_k].copy_from_slice(&update.k_pre);
self.ring_pos_k = (self.ring_pos_k + 1) % hist;
}
{
let off = self.ring_pos_v * self.d_v;
self.conv_state_v[off..off + self.d_v].copy_from_slice(&update.v_pre);
self.ring_pos_v = (self.ring_pos_v + 1) % hist;
}
kimi_delta_step(
&mut self.state,
&update.k_conv,
&update.v_conv,
&update.alpha,
update.beta,
);
}
}
#[derive(Clone, Debug)]
pub struct KimiDeltaHeadUpdate {
pub q_pre: Vec<f32>,
pub k_pre: Vec<f32>,
pub v_pre: Vec<f32>,
pub k_conv: Vec<f32>,
pub v_conv: Vec<f32>,
pub alpha: Vec<f32>,
pub beta: f32,
}
pub struct KimiDeltaHeadParams<'a> {
pub w_q: &'a [f32],
pub w_k: &'a [f32],
pub w_v: &'a [f32],
pub conv_kernel_q: &'a [f32],
pub conv_kernel_k: &'a [f32],
pub conv_kernel_v: &'a [f32],
pub conv_bias_q: &'a [f32],
pub conv_bias_k: &'a [f32],
pub conv_bias_v: &'a [f32],
pub w_beta: &'a [f32],
pub w_alpha_down: &'a [f32],
pub w_alpha_up: &'a [f32],
pub b_alpha: &'a [f32],
pub a_h: f32,
pub alpha_rank: usize,
pub g_min: f32,
pub w_gate: &'a [f32],
pub w_out: &'a [f32],
pub d_out: usize,
pub rms_gamma: Option<&'a [f32]>,
pub rms_eps: f32,
}
pub fn kimi_delta_l2_norm_in_place(x: &mut [f32], eps: f32) {
let sum_sq: f64 = x.iter().map(|&v| f64::from(v) * f64::from(v)).sum();
let norm = sum_sq.sqrt() as f32 + eps;
let scale = norm.recip();
for v in x.iter_mut() {
*v *= scale;
}
}
fn kimi_delta_matvec(w: &[f32], x: &[f32], out_dim: usize, in_dim: usize) -> Vec<f32> {
debug_assert_eq!(w.len(), out_dim * in_dim, "w shape mismatch");
debug_assert_eq!(x.len(), in_dim, "x length mismatch");
let mut y = vec![0.0_f32; out_dim];
for i in 0..out_dim {
let row_start = i * in_dim;
let mut acc = 0.0_f64;
for j in 0..in_dim {
acc += f64::from(w[row_start + j]) * f64::from(x[j]);
}
y[i] = acc as f32;
}
y
}
fn kimi_delta_dot(a: &[f32], b: &[f32]) -> f32 {
debug_assert_eq!(a.len(), b.len(), "dot length mismatch");
let mut acc = 0.0_f64;
for i in 0..a.len() {
acc += f64::from(a[i]) * f64::from(b[i]);
}
acc as f32
}
#[must_use]
pub fn kimi_delta_forward_head(
x: &[f32],
params: &KimiDeltaHeadParams<'_>,
cache: &mut KimiDeltaHeadCache,
l2_eps: f32,
) -> Vec<f32> {
let d = x.len();
let d_k = cache.d_k;
let d_v = cache.d_v;
let ks = cache.kernel_size;
let q_pre = kimi_delta_matvec(params.w_q, x, d_k, d);
let k_pre = kimi_delta_matvec(params.w_k, x, d_k, d);
let v_pre = kimi_delta_matvec(params.w_v, x, d_v, d);
let mut q_conv = vec![0.0_f32; d_k];
let mut k_conv = vec![0.0_f32; d_k];
let mut v_conv = vec![0.0_f32; d_v];
causal_conv1d_step(
&q_pre,
&mut cache.conv_state_q,
&mut cache.ring_pos_q,
params.conv_kernel_q,
params.conv_bias_q,
&mut q_conv,
d_k,
ks,
);
causal_conv1d_step(
&k_pre,
&mut cache.conv_state_k,
&mut cache.ring_pos_k,
params.conv_kernel_k,
params.conv_bias_k,
&mut k_conv,
d_k,
ks,
);
causal_conv1d_step(
&v_pre,
&mut cache.conv_state_v,
&mut cache.ring_pos_v,
params.conv_kernel_v,
params.conv_bias_v,
&mut v_conv,
d_v,
ks,
);
for v in &mut q_conv {
*v = silu(*v);
}
for v in &mut k_conv {
*v = silu(*v);
}
for v in &mut v_conv {
*v = silu(*v);
}
kimi_delta_l2_norm_in_place(&mut q_conv, l2_eps);
kimi_delta_l2_norm_in_place(&mut k_conv, l2_eps);
debug_assert_eq!(
params.w_beta.len(),
d,
"w_beta length must equal hidden dim"
);
let beta = sigmoid(kimi_delta_dot(params.w_beta, x));
let z_mid = kimi_delta_matvec(params.w_alpha_down, x, params.alpha_rank, d);
let mut z = kimi_delta_matvec(params.w_alpha_up, &z_mid, d_k, params.alpha_rank);
debug_assert_eq!(params.b_alpha.len(), d_k, "b_alpha length must equal d_k");
for i in 0..d_k {
z[i] += params.b_alpha[i];
}
let alpha = kimi_delta_lower_bounded_decay(&z, params.a_h, params.g_min);
kimi_delta_step(&mut cache.state, &k_conv, &v_conv, &alpha, beta);
let o_bar = kimi_delta_read(&cache.state, &q_conv);
let gate_pre = kimi_delta_matvec(params.w_gate, x, d_v, d);
kimi_delta_output_gate(
&o_bar,
&gate_pre,
params.rms_gamma,
params.rms_eps,
params.w_out,
params.d_out,
)
}
#[cfg(feature = "dspark")]
#[must_use]
pub fn kimi_delta_forward_head_with_capture(
x: &[f32],
params: &KimiDeltaHeadParams<'_>,
cache: &mut KimiDeltaHeadCache,
l2_eps: f32,
) -> (Vec<f32>, KimiDeltaHeadUpdate) {
let d = x.len();
let d_k = cache.d_k;
let d_v = cache.d_v;
let ks = cache.kernel_size;
let q_pre = kimi_delta_matvec(params.w_q, x, d_k, d);
let k_pre = kimi_delta_matvec(params.w_k, x, d_k, d);
let v_pre = kimi_delta_matvec(params.w_v, x, d_v, d);
let q_pre_captured = q_pre.clone();
let k_pre_captured = k_pre.clone();
let v_pre_captured = v_pre.clone();
let mut q_conv = vec![0.0_f32; d_k];
let mut k_conv = vec![0.0_f32; d_k];
let mut v_conv = vec![0.0_f32; d_v];
causal_conv1d_step(
&q_pre,
&mut cache.conv_state_q,
&mut cache.ring_pos_q,
params.conv_kernel_q,
params.conv_bias_q,
&mut q_conv,
d_k,
ks,
);
causal_conv1d_step(
&k_pre,
&mut cache.conv_state_k,
&mut cache.ring_pos_k,
params.conv_kernel_k,
params.conv_bias_k,
&mut k_conv,
d_k,
ks,
);
causal_conv1d_step(
&v_pre,
&mut cache.conv_state_v,
&mut cache.ring_pos_v,
params.conv_kernel_v,
params.conv_bias_v,
&mut v_conv,
d_v,
ks,
);
for v in &mut q_conv {
*v = silu(*v);
}
for v in &mut k_conv {
*v = silu(*v);
}
for v in &mut v_conv {
*v = silu(*v);
}
kimi_delta_l2_norm_in_place(&mut q_conv, l2_eps);
kimi_delta_l2_norm_in_place(&mut k_conv, l2_eps);
debug_assert_eq!(
params.w_beta.len(),
d,
"w_beta length must equal hidden dim"
);
let beta = sigmoid(kimi_delta_dot(params.w_beta, x));
let z_mid = kimi_delta_matvec(params.w_alpha_down, x, params.alpha_rank, d);
let mut z = kimi_delta_matvec(params.w_alpha_up, &z_mid, d_k, params.alpha_rank);
debug_assert_eq!(params.b_alpha.len(), d_k, "b_alpha length must equal d_k");
for i in 0..d_k {
z[i] += params.b_alpha[i];
}
let alpha = kimi_delta_lower_bounded_decay(&z, params.a_h, params.g_min);
let update = KimiDeltaHeadUpdate {
q_pre: q_pre_captured,
k_pre: k_pre_captured,
v_pre: v_pre_captured,
k_conv: k_conv.clone(),
v_conv: v_conv.clone(),
alpha: alpha.clone(),
beta,
};
kimi_delta_step(&mut cache.state, &k_conv, &v_conv, &alpha, beta);
let o_bar = kimi_delta_read(&cache.state, &q_conv);
let gate_pre = kimi_delta_matvec(params.w_gate, x, d_v, d);
let out = kimi_delta_output_gate(
&o_bar,
&gate_pre,
params.rms_gamma,
params.rms_eps,
params.w_out,
params.d_out,
);
(out, update)
}
#[cfg(test)]
mod kimi_delta_forward_tests {
use super::{
kimi_delta_forward_head, kimi_delta_l2_norm_in_place, KimiDeltaHeadCache,
KimiDeltaHeadParams,
};
#[allow(clippy::too_many_arguments)]
fn params_from_bufs<'a>(
w_q: &'a [f32],
w_k: &'a [f32],
w_v: &'a [f32],
conv_kernel_q: &'a [f32],
conv_kernel_k: &'a [f32],
conv_kernel_v: &'a [f32],
conv_bias_q: &'a [f32],
conv_bias_k: &'a [f32],
conv_bias_v: &'a [f32],
w_beta: &'a [f32],
w_alpha_down: &'a [f32],
w_alpha_up: &'a [f32],
b_alpha: &'a [f32],
a_h: f32,
alpha_rank: usize,
g_min: f32,
w_gate: &'a [f32],
w_out: &'a [f32],
d_out: usize,
rms_gamma: Option<&'a [f32]>,
rms_eps: f32,
) -> KimiDeltaHeadParams<'a> {
KimiDeltaHeadParams {
w_q,
w_k,
w_v,
conv_kernel_q,
conv_kernel_k,
conv_kernel_v,
conv_bias_q,
conv_bias_k,
conv_bias_v,
w_beta,
w_alpha_down,
w_alpha_up,
b_alpha,
a_h,
alpha_rank,
g_min,
w_gate,
w_out,
d_out,
rms_gamma,
rms_eps,
}
}
#[test]
fn cache_new_is_zeroed() {
let c = KimiDeltaHeadCache::new(4, 4, 4);
assert_eq!(c.d_k(), 4);
assert_eq!(c.d_v(), 4);
assert_eq!(c.kernel_size(), 4);
assert!(c.state.as_slice().iter().all(|&x| x == 0.0));
assert!(c.conv_state_q.iter().all(|&x| x == 0.0));
assert!(c.conv_state_k.iter().all(|&x| x == 0.0));
assert!(c.conv_state_v.iter().all(|&x| x == 0.0));
assert_eq!(c.ring_pos_q, 0);
assert_eq!(c.ring_pos_k, 0);
assert_eq!(c.ring_pos_v, 0);
}
#[test]
fn cache_reset_zeroes_all_buffers() {
let mut c = KimiDeltaHeadCache::new(2, 2, 3);
c.state.as_mut_slice().fill(9.0);
c.conv_state_q.fill(1.0);
c.conv_state_k.fill(2.0);
c.conv_state_v.fill(3.0);
c.ring_pos_q = 1;
c.ring_pos_k = 1;
c.ring_pos_v = 1;
c.reset();
assert!(c.state.as_slice().iter().all(|&x| x == 0.0));
assert!(c.conv_state_q.iter().all(|&x| x == 0.0));
assert!(c.conv_state_k.iter().all(|&x| x == 0.0));
assert!(c.conv_state_v.iter().all(|&x| x == 0.0));
assert_eq!(c.ring_pos_q, 0);
assert_eq!(c.ring_pos_k, 0);
assert_eq!(c.ring_pos_v, 0);
}
#[test]
fn l2_norm_in_place_gives_unit_length() {
let mut x = [3.0_f32, 4.0];
kimi_delta_l2_norm_in_place(&mut x, 0.0);
assert!((x[0] - 0.6).abs() < 1e-6);
assert!((x[1] - 0.8).abs() < 1e-6);
let magsq: f32 = x.iter().map(|&v| v * v).sum();
assert!((magsq - 1.0).abs() < 1e-6);
}
#[test]
fn l2_norm_in_place_handles_zero_input_with_eps() {
let mut x = [0.0_f32, 0.0, 0.0];
kimi_delta_l2_norm_in_place(&mut x, 1e-6);
for &v in &x {
assert_eq!(v, 0.0);
}
}
#[allow(clippy::too_many_arguments)]
struct PassThroughHead {
d: usize,
w_q: Vec<f32>,
w_k: Vec<f32>,
w_v: Vec<f32>,
conv_kernel: Vec<f32>, conv_bias: Vec<f32>, w_beta: Vec<f32>,
w_alpha_down: Vec<f32>,
w_alpha_up: Vec<f32>,
b_alpha: Vec<f32>,
w_gate: Vec<f32>,
w_out: Vec<f32>,
}
impl PassThroughHead {
fn new(d: usize) -> Self {
let mut identity = vec![0.0_f32; d * d];
for i in 0..d {
identity[i * d + i] = 1.0;
}
let kernel_size = 3;
let mut conv_kernel = vec![0.0_f32; d * kernel_size];
for c in 0..d {
conv_kernel[c * kernel_size + (kernel_size - 1)] = 1.0;
}
Self {
d,
w_q: identity.clone(),
w_k: identity.clone(),
w_v: identity.clone(),
conv_kernel,
conv_bias: vec![0.0; d],
w_beta: vec![0.0; d],
w_alpha_down: vec![0.0; d],
w_alpha_up: vec![0.0; d],
b_alpha: vec![0.0; d],
w_gate: identity.clone(),
w_out: identity,
}
}
fn params(&self) -> KimiDeltaHeadParams<'_> {
params_from_bufs(
&self.w_q,
&self.w_k,
&self.w_v,
&self.conv_kernel,
&self.conv_kernel,
&self.conv_kernel,
&self.conv_bias,
&self.conv_bias,
&self.conv_bias,
&self.w_beta,
&self.w_alpha_down,
&self.w_alpha_up,
&self.b_alpha,
0.0, 1, -5.0, &self.w_gate,
&self.w_out,
self.d,
None, 1e-6,
)
}
}
#[test]
fn forward_head_zero_input_gives_zero_output() {
let head = PassThroughHead::new(2);
let params = head.params();
let mut cache = KimiDeltaHeadCache::new(2, 2, 3);
let y = kimi_delta_forward_head(&[0.0, 0.0], ¶ms, &mut cache, 1e-6);
assert_eq!(y, vec![0.0, 0.0]);
}
#[test]
fn forward_head_advances_all_ring_positions_per_call() {
let head = PassThroughHead::new(3);
let params = head.params();
let mut cache = KimiDeltaHeadCache::new(3, 3, 3);
let _ = kimi_delta_forward_head(&[1.0, 2.0, -1.0], ¶ms, &mut cache, 1e-6);
assert_eq!(cache.ring_pos_q, 1);
assert_eq!(cache.ring_pos_k, 1);
assert_eq!(cache.ring_pos_v, 1);
}
#[test]
fn forward_head_two_tokens_progresses_state_and_conv_rings() {
let head = PassThroughHead::new(2);
let params = head.params();
let mut cache = KimiDeltaHeadCache::new(2, 2, 3);
let _ = kimi_delta_forward_head(&[0.5, -0.25], ¶ms, &mut cache, 1e-6);
let _ = kimi_delta_forward_head(&[1.0, 0.5], ¶ms, &mut cache, 1e-6);
assert_eq!(cache.ring_pos_q, 0);
assert_eq!(cache.ring_pos_k, 0);
assert_eq!(cache.ring_pos_v, 0);
let state_sum: f32 = cache.state.as_slice().iter().map(|&v| v.abs()).sum();
assert!(state_sum > 0.0, "state should be non-zero after 2 writes");
}
#[test]
fn forward_head_reset_returns_to_fresh_start() {
let head = PassThroughHead::new(2);
let params = head.params();
let mut cache_a = KimiDeltaHeadCache::new(2, 2, 3);
let mut cache_b = KimiDeltaHeadCache::new(2, 2, 3);
let x = [0.7_f32, -0.3];
let _ = kimi_delta_forward_head(&[3.0, -2.5], ¶ms, &mut cache_a, 1e-6);
cache_a.reset();
let y_a = kimi_delta_forward_head(&x, ¶ms, &mut cache_a, 1e-6);
let y_b = kimi_delta_forward_head(&x, ¶ms, &mut cache_b, 1e-6);
for i in 0..2 {
assert!(
(y_a[i] - y_b[i]).abs() < 1e-6,
"reset should give parity with fresh cache: y_a[{i}] = {}, y_b[{i}] = {}",
y_a[i],
y_b[i],
);
}
}
#[test]
fn forward_head_first_token_output_bounded_by_gate_and_alpha() {
let head = PassThroughHead::new(2);
let params = head.params();
let mut cache = KimiDeltaHeadCache::new(2, 2, 3);
let x = [1.0_f32, -1.0];
let y = kimi_delta_forward_head(&x, ¶ms, &mut cache, 1e-6);
for (i, &v) in y.iter().enumerate() {
assert!(v.is_finite(), "y[{i}] = {v} must be finite");
assert!(v.abs() <= 2.0, "y[{i}] = {v} exceeded reasonable bound");
}
}
#[test]
fn forward_head_with_zero_gate_projection_halves_output_magnitude() {
let mut head = PassThroughHead::new(2);
let params_baseline = head.params();
let mut cache_a = KimiDeltaHeadCache::new(2, 2, 3);
let x = [0.5_f32, -0.5];
let y_baseline = kimi_delta_forward_head(&x, ¶ms_baseline, &mut cache_a, 1e-6);
head.w_gate.iter_mut().for_each(|v| *v = 0.0);
let params_zero_gate = head.params();
let mut cache_b = KimiDeltaHeadCache::new(2, 2, 3);
let y_zero_gate = kimi_delta_forward_head(&x, ¶ms_zero_gate, &mut cache_b, 1e-6);
for j in 0..2 {
let s_x_j = 1.0 / (1.0 + (-x[j]).exp());
let expected_ratio = 0.5 / s_x_j;
let actual_ratio = y_zero_gate[j] / y_baseline[j];
assert!(
(actual_ratio - expected_ratio).abs() < 1e-3,
"channel {j} ratio {actual_ratio}, expected {expected_ratio} \
(baseline sigmoid {s_x_j})",
);
}
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_part3_capture_output_matches_uncaptured() {
let d_k = 2_usize;
let d_v = 2_usize;
let ks = 3_usize;
let alpha_rank = 2_usize;
let w_q = vec![0.1, -0.2, 0.3, 0.15, -0.05, 0.25];
let w_k = vec![0.2, 0.1, -0.15, 0.3, 0.1, -0.25];
let w_v = vec![-0.1, 0.2, 0.05, 0.1, -0.2, 0.15];
let conv_kernel_q = vec![0.5, 0.3, 0.1, 0.4, 0.2, 0.05];
let conv_kernel_k = vec![0.3, 0.4, 0.1, 0.5, 0.2, 0.1];
let conv_kernel_v = vec![0.2, 0.3, 0.4, 0.1, 0.5, 0.3];
let conv_bias = vec![0.0; d_k];
let w_beta = vec![0.3, -0.2, 0.1];
let w_alpha_down = vec![0.1, 0.2, -0.1, 0.3, 0.15, 0.05];
let w_alpha_up = vec![0.5, 0.1, -0.2, 0.4];
let b_alpha = vec![0.0, 0.0];
let w_gate = vec![0.1, -0.1, 0.2, 0.3, 0.1, -0.15];
let w_out = vec![1.0, 0.0, 0.0, 1.0];
let rms_gamma = vec![1.0, 1.0];
let params = params_from_bufs(
&w_q,
&w_k,
&w_v,
&conv_kernel_q,
&conv_kernel_k,
&conv_kernel_v,
&conv_bias,
&conv_bias,
&conv_bias,
&w_beta,
&w_alpha_down,
&w_alpha_up,
&b_alpha,
0.0,
alpha_rank,
-5.0,
&w_gate,
&w_out,
d_v,
Some(&rms_gamma),
1e-6,
);
let x = vec![0.5, -0.3, 0.7];
let mut cache_a = KimiDeltaHeadCache::new(d_k, d_v, ks);
let mut cache_b = KimiDeltaHeadCache::new(d_k, d_v, ks);
let out_a = kimi_delta_forward_head(&x, ¶ms, &mut cache_a, 1e-6);
let (out_b, _update) =
super::kimi_delta_forward_head_with_capture(&x, ¶ms, &mut cache_b, 1e-6);
assert_eq!(
out_a, out_b,
"kimi_delta_forward_head_with_capture output must match uncaptured version"
);
assert_eq!(cache_a.state.as_slice(), cache_b.state.as_slice());
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_part3_captured_update_replays_state() {
let d_k = 2_usize;
let d_v = 2_usize;
let ks = 3_usize;
let alpha_rank = 2_usize;
let w_q = vec![0.1, -0.2, 0.3, 0.15, -0.05, 0.25];
let w_k = vec![0.2, 0.1, -0.15, 0.3, 0.1, -0.25];
let w_v = vec![-0.1, 0.2, 0.05, 0.1, -0.2, 0.15];
let conv_kernel_q = vec![0.5, 0.3, 0.1, 0.4, 0.2, 0.05];
let conv_kernel_k = vec![0.3, 0.4, 0.1, 0.5, 0.2, 0.1];
let conv_kernel_v = vec![0.2, 0.3, 0.4, 0.1, 0.5, 0.3];
let conv_bias = vec![0.0; d_k];
let w_beta = vec![0.3, -0.2, 0.1];
let w_alpha_down = vec![0.1, 0.2, -0.1, 0.3, 0.15, 0.05];
let w_alpha_up = vec![0.5, 0.1, -0.2, 0.4];
let b_alpha = vec![0.0, 0.0];
let w_gate = vec![0.1, -0.1, 0.2, 0.3, 0.1, -0.15];
let w_out = vec![1.0, 0.0, 0.0, 1.0];
let rms_gamma = vec![1.0, 1.0];
let params = params_from_bufs(
&w_q,
&w_k,
&w_v,
&conv_kernel_q,
&conv_kernel_k,
&conv_kernel_v,
&conv_bias,
&conv_bias,
&conv_bias,
&w_beta,
&w_alpha_down,
&w_alpha_up,
&b_alpha,
0.0,
alpha_rank,
-5.0,
&w_gate,
&w_out,
d_v,
Some(&rms_gamma),
1e-6,
);
let x = vec![0.5, -0.3, 0.7];
let mut cache_a = KimiDeltaHeadCache::new(d_k, d_v, ks);
let (_out, update) =
super::kimi_delta_forward_head_with_capture(&x, ¶ms, &mut cache_a, 1e-6);
let mut cache_b = KimiDeltaHeadCache::new(d_k, d_v, ks);
cache_b.apply_update(&update);
assert_eq!(
cache_a.state.as_slice(),
cache_b.state.as_slice(),
"apply_update state must match forward capture state"
);
assert_eq!(cache_a.ring_pos_q, cache_b.ring_pos_q);
assert_eq!(cache_a.ring_pos_k, cache_b.ring_pos_k);
assert_eq!(cache_a.ring_pos_v, cache_b.ring_pos_v);
}
}
#[derive(Debug, Clone)]
pub struct BlockAttnResState {
pub block_reps: Vec<Vec<f32>>,
pub current_partial: Vec<f32>,
current_block: usize,
pos_in_block: usize,
d: usize,
block_size: usize,
}
impl BlockAttnResState {
#[must_use]
pub fn new(embedding: &[f32], block_size: usize) -> Self {
assert!(!embedding.is_empty(), "embedding must be non-empty");
assert!(block_size > 0, "block_size must be at least 1");
let d = embedding.len();
Self {
block_reps: vec![embedding.to_vec()],
current_partial: vec![0.0; d],
current_block: 1, pos_in_block: 0,
d,
block_size,
}
}
#[inline]
#[must_use]
pub fn num_block_reps(&self) -> usize {
self.block_reps.len()
}
#[inline]
#[must_use]
pub const fn current_block_idx(&self) -> usize {
self.current_block
}
#[inline]
#[must_use]
pub const fn pos_in_block(&self) -> usize {
self.pos_in_block
}
}
#[must_use]
pub fn block_attnres_softmax_attention(
query: &[f32],
keys_values: &[&[f32]],
rms_gamma: Option<&[f32]>,
rms_eps: f32,
) -> Vec<f32> {
assert!(!keys_values.is_empty(), "keys_values must be non-empty");
let d = query.len();
if let Some(gamma) = rms_gamma {
assert_eq!(gamma.len(), d, "rms_gamma length must equal query length");
}
for (i, k) in keys_values.iter().enumerate() {
assert_eq!(
k.len(),
d,
"keys_values[{i}] length {} != query length {d}",
k.len()
);
}
let mut logits = Vec::with_capacity(keys_values.len());
for k in keys_values {
let ss: f64 = k.iter().map(|&v| f64::from(v) * f64::from(v)).sum();
let mean = (ss / d as f64) as f32;
let scale = (mean + rms_eps).sqrt().recip();
let mut logit = 0.0_f64;
if let Some(gamma) = rms_gamma {
for j in 0..d {
logit += f64::from(query[j]) * f64::from(k[j] * scale * gamma[j]);
}
} else {
for j in 0..d {
logit += f64::from(query[j]) * f64::from(k[j] * scale);
}
}
logits.push(logit as f32);
}
let max_logit = logits.iter().copied().fold(f32::NEG_INFINITY, f32::max);
let mut exp_logits = Vec::with_capacity(logits.len());
let mut sum_exp = 0.0_f64;
for &l in &logits {
let e = (l - max_logit).exp();
exp_logits.push(e);
sum_exp += f64::from(e);
}
let inv_sum = (sum_exp as f32).recip();
let mut h = vec![0.0_f32; d];
for (i, v) in keys_values.iter().enumerate() {
let alpha = exp_logits[i] * inv_sum;
for j in 0..d {
h[j] += alpha * v[j];
}
}
h
}
pub fn block_attnres_layer_step(
state: &mut BlockAttnResState,
layer_output: &[f32],
w_l: &[f32],
rms_gamma_k: Option<&[f32]>,
rms_eps: f32,
) -> Vec<f32> {
let d = state.d;
assert_eq!(layer_output.len(), d, "layer_output length must equal d");
assert_eq!(w_l.len(), d, "w_l length must equal d");
let mut kv_slices: Vec<&[f32]> = state.block_reps.iter().map(Vec::as_slice).collect();
if state.pos_in_block > 0 {
kv_slices.push(state.current_partial.as_slice());
}
let h_l = block_attnres_softmax_attention(w_l, &kv_slices, rms_gamma_k, rms_eps);
for j in 0..d {
state.current_partial[j] += layer_output[j];
}
state.pos_in_block += 1;
if state.pos_in_block == state.block_size {
let finalized = std::mem::replace(&mut state.current_partial, vec![0.0_f32; d]);
state.block_reps.push(finalized);
state.current_block += 1;
state.pos_in_block = 0;
}
h_l
}
#[cfg(test)]
mod block_attnres_tests {
use super::{block_attnres_layer_step, block_attnres_softmax_attention, BlockAttnResState};
#[test]
fn state_new_stores_embedding_as_b0() {
let embedding = [1.0_f32, 2.0, 3.0, 4.0];
let s = BlockAttnResState::new(&embedding, 12);
assert_eq!(s.num_block_reps(), 1);
assert_eq!(s.block_reps[0], embedding);
assert!(s.current_partial.iter().all(|&x| x == 0.0));
assert_eq!(s.current_block_idx(), 1);
assert_eq!(s.pos_in_block(), 0);
}
#[test]
fn softmax_attention_single_key_returns_that_key() {
let q = [0.5_f32, -0.5, 1.0];
let v0 = [7.0_f32, -3.0, 2.0];
let vs: Vec<&[f32]> = vec![&v0];
let h = block_attnres_softmax_attention(&q, &vs, None, 1e-6);
for i in 0..3 {
assert!((h[i] - v0[i]).abs() < 1e-4, "h[{i}] = {}", h[i]);
}
}
#[test]
fn softmax_attention_zero_query_averages_keys() {
let q = [0.0_f32, 0.0, 0.0];
let v0 = [1.0_f32, 2.0, 3.0];
let v1 = [4.0_f32, 5.0, 6.0];
let v2 = [7.0_f32, 8.0, 9.0];
let vs: Vec<&[f32]> = vec![&v0, &v1, &v2];
let h = block_attnres_softmax_attention(&q, &vs, None, 1e-6);
let expected = [
(1.0 + 4.0 + 7.0) / 3.0,
(2.0 + 5.0 + 8.0) / 3.0,
(3.0 + 6.0 + 9.0) / 3.0,
];
for i in 0..3 {
assert!(
(h[i] - expected[i]).abs() < 1e-4,
"h[{i}] = {}, expected {}",
h[i],
expected[i]
);
}
}
#[test]
fn softmax_attention_dominant_key_wins() {
let q = [1.0_f32, 0.0, 0.0];
let v0 = [10.0_f32, 0.0, 0.0];
let v1 = [0.0_f32, 0.01, 0.0];
let v2 = [0.0_f32, 0.0, 0.01];
let vs: Vec<&[f32]> = vec![&v0, &v1, &v2];
let h = block_attnres_softmax_attention(&q, &vs, None, 1e-6);
assert!(h[0] > 5.0, "dominant channel got h[0]={}", h[0]);
assert!(h[1].abs() < 0.01, "off-axis channel h[1]={}", h[1]);
assert!(h[2].abs() < 0.01, "off-axis channel h[2]={}", h[2]);
}
#[test]
fn softmax_attention_with_gamma_scales_keys() {
let q = [0.5_f32, 0.3, -0.2];
let v0 = [1.0_f32, -1.0, 2.0];
let gamma = [2.0_f32, 2.0, 2.0];
let vs: Vec<&[f32]> = vec![&v0];
let h = block_attnres_softmax_attention(&q, &vs, Some(&gamma), 1e-6);
for i in 0..3 {
assert!((h[i] - v0[i]).abs() < 1e-4);
}
}
#[test]
fn layer_step_first_layer_in_block_omits_partial_from_v() {
let embedding = [1.0_f32, 2.0, 3.0, 4.0];
let mut s = BlockAttnResState::new(&embedding, 12);
let layer_output = [0.5_f32, -0.5, 0.5, -0.5];
let w_l = [1.0_f32, 0.0, 0.0, 0.0];
let h = block_attnres_layer_step(&mut s, &layer_output, &w_l, None, 1e-6);
let vs: Vec<&[f32]> = vec![&embedding];
let expected = block_attnres_softmax_attention(&w_l, &vs, None, 1e-6);
for i in 0..4 {
assert!(
(h[i] - expected[i]).abs() < 1e-5,
"h[{i}] = {}, expected {}",
h[i],
expected[i]
);
}
}
#[test]
fn layer_step_second_layer_includes_prior_partial_snapshot() {
let embedding = [1.0_f32, 0.0, 0.0, 0.0];
let mut s = BlockAttnResState::new(&embedding, 12);
let layer_output_1 = [0.0_f32, 1.0, 0.0, 0.0];
let w_l = [0.5_f32, 0.5, 0.5, 0.5];
let _ = block_attnres_layer_step(&mut s, &layer_output_1, &w_l, None, 1e-6);
let partial_before_step_2 = s.current_partial.clone();
assert_eq!(partial_before_step_2, layer_output_1);
let layer_output_2 = [0.0_f32, 0.0, 1.0, 0.0];
let h = block_attnres_layer_step(&mut s, &layer_output_2, &w_l, None, 1e-6);
let vs: Vec<&[f32]> = vec![&embedding, &partial_before_step_2];
let expected = block_attnres_softmax_attention(&w_l, &vs, None, 1e-6);
for i in 0..4 {
assert!(
(h[i] - expected[i]).abs() < 1e-5,
"h[{i}] = {}, expected {}",
h[i],
expected[i]
);
}
}
#[test]
fn partial_sum_equals_sum_of_layer_outputs_within_block() {
let embedding = [0.0_f32; 3];
let mut s = BlockAttnResState::new(&embedding, 4);
let w_l = [0.0_f32; 3];
let outs = [
[1.0_f32, 2.0, 3.0],
[0.5_f32, -1.0, 4.0],
[-2.0_f32, 0.25, 1.5],
];
for out in &outs {
let _ = block_attnres_layer_step(&mut s, out, &w_l, None, 1e-6);
}
for j in 0..3 {
let expected: f32 = outs.iter().map(|o| o[j]).sum();
assert!(
(s.current_partial[j] - expected).abs() < 1e-4,
"channel {j}: partial = {}, expected {}",
s.current_partial[j],
expected
);
}
assert_eq!(s.num_block_reps(), 1);
assert_eq!(s.pos_in_block(), 3);
}
#[test]
fn block_finalizes_after_block_size_steps() {
let embedding = [1.0_f32, 2.0];
let mut s = BlockAttnResState::new(&embedding, 2);
let w_l = [0.0_f32, 0.0];
let out1 = [1.0_f32, 1.0];
let out2 = [2.0_f32, 3.0];
let _ = block_attnres_layer_step(&mut s, &out1, &w_l, None, 1e-6);
assert_eq!(s.num_block_reps(), 1);
assert_eq!(s.pos_in_block(), 1);
let _ = block_attnres_layer_step(&mut s, &out2, &w_l, None, 1e-6);
assert_eq!(s.num_block_reps(), 2, "block should have finalized");
assert_eq!(s.pos_in_block(), 0, "pos should wrap back to 0");
assert_eq!(s.current_block_idx(), 2, "current_block should advance");
assert_eq!(s.block_reps[1], vec![3.0_f32, 4.0]);
assert!(s.current_partial.iter().all(|&x| x == 0.0));
}
#[test]
fn full_workflow_two_blocks_end_to_end() {
let embedding = [1.0_f32, 0.0];
let mut s = BlockAttnResState::new(&embedding, 2);
let w_l = [0.0_f32, 0.0]; let outs = [
[1.0_f32, 1.0], [2.0_f32, 2.0], [4.0_f32, 4.0], [8.0_f32, 8.0], ];
for out in &outs {
let _ = block_attnres_layer_step(&mut s, out, &w_l, None, 1e-6);
}
assert_eq!(s.num_block_reps(), 3);
assert_eq!(s.block_reps[0], vec![1.0_f32, 0.0]); assert_eq!(s.block_reps[1], vec![3.0_f32, 3.0]); assert_eq!(s.block_reps[2], vec![12.0_f32, 12.0]); assert_eq!(s.pos_in_block(), 0);
assert!(s.current_partial.iter().all(|&x| x == 0.0));
}
}
pub const KIMI_DELTA_CHUNK_SIZE: usize = 16;
#[must_use]
pub fn kimi_delta_chunk_forward(
xs: &[Vec<f32>],
params: &KimiDeltaHeadParams<'_>,
cache: &mut KimiDeltaHeadCache,
l2_eps: f32,
) -> Vec<Vec<f32>> {
assert_eq!(
xs.len(),
KIMI_DELTA_CHUNK_SIZE,
"chunk must contain exactly {KIMI_DELTA_CHUNK_SIZE} tokens, got {}",
xs.len()
);
let mut outs = Vec::with_capacity(KIMI_DELTA_CHUNK_SIZE);
for x in xs {
outs.push(kimi_delta_forward_head(x, params, cache, l2_eps));
}
outs
}
#[must_use]
pub fn kimi_delta_chunk_cumulative_decay(alphas: &[Vec<f32>]) -> Vec<Vec<f32>> {
assert!(!alphas.is_empty(), "alphas must not be empty");
assert!(
alphas.len() <= 128,
"chunk length {} exceeds safety bound 128",
alphas.len()
);
let d_k = alphas[0].len();
for (i, a) in alphas.iter().enumerate() {
assert_eq!(a.len(), d_k, "alphas[{i}].len() = {} != d_k {d_k}", a.len());
}
let mut gamma = Vec::with_capacity(alphas.len());
let mut running = alphas[0].clone();
gamma.push(running.clone());
for a in &alphas[1..] {
for j in 0..d_k {
running[j] *= a[j];
}
gamma.push(running.clone());
}
gamma
}
#[cfg(test)]
mod kimi_delta_chunk_tests {
use super::{
kimi_delta_chunk_cumulative_decay, kimi_delta_chunk_forward, kimi_delta_forward_head,
KimiDeltaHeadCache, KimiDeltaHeadParams, KIMI_DELTA_CHUNK_SIZE,
};
struct PassThroughHead {
d: usize,
w_q: Vec<f32>,
w_k: Vec<f32>,
w_v: Vec<f32>,
conv_kernel: Vec<f32>,
conv_bias: Vec<f32>,
w_beta: Vec<f32>,
w_alpha_down: Vec<f32>,
w_alpha_up: Vec<f32>,
b_alpha: Vec<f32>,
w_gate: Vec<f32>,
w_out: Vec<f32>,
}
impl PassThroughHead {
fn new(d: usize) -> Self {
let mut identity = vec![0.0_f32; d * d];
for i in 0..d {
identity[i * d + i] = 1.0;
}
let kernel_size = 3;
let mut conv_kernel = vec![0.0_f32; d * kernel_size];
for c in 0..d {
conv_kernel[c * kernel_size + (kernel_size - 1)] = 1.0;
}
Self {
d,
w_q: identity.clone(),
w_k: identity.clone(),
w_v: identity.clone(),
conv_kernel,
conv_bias: vec![0.0; d],
w_beta: vec![0.0; d],
w_alpha_down: vec![0.0; d],
w_alpha_up: vec![0.0; d],
b_alpha: vec![0.0; d],
w_gate: identity.clone(),
w_out: identity,
}
}
fn params(&self) -> KimiDeltaHeadParams<'_> {
KimiDeltaHeadParams {
w_q: &self.w_q,
w_k: &self.w_k,
w_v: &self.w_v,
conv_kernel_q: &self.conv_kernel,
conv_kernel_k: &self.conv_kernel,
conv_kernel_v: &self.conv_kernel,
conv_bias_q: &self.conv_bias,
conv_bias_k: &self.conv_bias,
conv_bias_v: &self.conv_bias,
w_beta: &self.w_beta,
w_alpha_down: &self.w_alpha_down,
w_alpha_up: &self.w_alpha_up,
b_alpha: &self.b_alpha,
a_h: 0.0,
alpha_rank: 1,
g_min: -5.0,
w_gate: &self.w_gate,
w_out: &self.w_out,
d_out: self.d,
rms_gamma: None,
rms_eps: 1e-6,
}
}
}
#[test]
fn chunk_size_is_sixteen_per_paper() {
assert_eq!(KIMI_DELTA_CHUNK_SIZE, 16);
}
#[test]
fn chunk_forward_matches_sequential_forward_bit_exact() {
let head = PassThroughHead::new(2);
let params = head.params();
let mut cache_a = KimiDeltaHeadCache::new(2, 2, 3);
let mut cache_b = KimiDeltaHeadCache::new(2, 2, 3);
let xs: Vec<Vec<f32>> = (0..KIMI_DELTA_CHUNK_SIZE)
.map(|i| vec![(i as f32) * 0.1, -((i as f32) * 0.05)])
.collect();
let chunk_out = kimi_delta_chunk_forward(&xs, ¶ms, &mut cache_a, 1e-6);
let mut seq_out: Vec<Vec<f32>> = Vec::with_capacity(KIMI_DELTA_CHUNK_SIZE);
for x in &xs {
seq_out.push(kimi_delta_forward_head(x, ¶ms, &mut cache_b, 1e-6));
}
assert_eq!(chunk_out.len(), seq_out.len());
for (t, (c, s)) in chunk_out.iter().zip(seq_out.iter()).enumerate() {
assert_eq!(c, s, "token {t}: chunk output must equal sequential output");
}
}
#[test]
fn chunk_forward_advances_cache_state() {
let head = PassThroughHead::new(2);
let params = head.params();
let mut cache = KimiDeltaHeadCache::new(2, 2, 3);
let xs: Vec<Vec<f32>> = (0..KIMI_DELTA_CHUNK_SIZE)
.map(|_| vec![0.5_f32, -0.5])
.collect();
let _ = kimi_delta_chunk_forward(&xs, ¶ms, &mut cache, 1e-6);
let state_sum: f32 = cache.state.as_slice().iter().map(|&v| v.abs()).sum();
assert!(
state_sum > 0.0,
"state must be non-zero after a full chunk of writes"
);
}
#[test]
#[should_panic(expected = "chunk must contain exactly 16 tokens")]
fn chunk_forward_rejects_wrong_chunk_size() {
let head = PassThroughHead::new(2);
let params = head.params();
let mut cache = KimiDeltaHeadCache::new(2, 2, 3);
let xs = vec![vec![0.0_f32, 0.0]; 8];
let _ = kimi_delta_chunk_forward(&xs, ¶ms, &mut cache, 1e-6);
}
#[test]
fn cumulative_decay_single_position_returns_alpha_1() {
let alphas = vec![vec![0.5_f32, 0.25, 0.9]];
let gamma = kimi_delta_chunk_cumulative_decay(&alphas);
assert_eq!(gamma.len(), 1);
assert_eq!(gamma[0], vec![0.5_f32, 0.25, 0.9]);
}
#[test]
fn cumulative_decay_computes_running_product() {
let alphas = vec![vec![0.5_f32, 0.5], vec![0.5_f32, 0.5], vec![0.5_f32, 0.5]];
let gamma = kimi_delta_chunk_cumulative_decay(&alphas);
assert_eq!(gamma.len(), 3);
assert_eq!(gamma[0], vec![0.5_f32, 0.5]);
for j in 0..2 {
assert!((gamma[1][j] - 0.25).abs() < 1e-6);
assert!((gamma[2][j] - 0.125).abs() < 1e-6);
}
}
#[test]
fn cumulative_decay_all_ones_stays_at_one() {
let alphas = vec![vec![1.0_f32; 4]; KIMI_DELTA_CHUNK_SIZE];
let gamma = kimi_delta_chunk_cumulative_decay(&alphas);
assert_eq!(gamma.len(), KIMI_DELTA_CHUNK_SIZE);
for g in &gamma {
for &v in g {
assert!((v - 1.0).abs() < 1e-6);
}
}
}
#[test]
fn cumulative_decay_channel_independence() {
let alphas: Vec<Vec<f32>> = (0..4).map(|_| vec![1.0_f32, 0.5]).collect();
let gamma = kimi_delta_chunk_cumulative_decay(&alphas);
for (t, g) in gamma.iter().enumerate() {
let expected_ch1 = 0.5_f32.powi((t + 1) as i32);
assert!((g[0] - 1.0).abs() < 1e-6);
assert!(
(g[1] - expected_ch1).abs() < 1e-6,
"pos {t} channel 1: got {}, expected {expected_ch1}",
g[1]
);
}
}
#[test]
#[should_panic(expected = "alphas must not be empty")]
fn cumulative_decay_rejects_empty_input() {
let _ = kimi_delta_chunk_cumulative_decay(&[]);
}
#[test]
#[should_panic(expected = "!= d_k")]
fn cumulative_decay_rejects_mismatched_dims() {
let alphas = vec![vec![0.5_f32; 3], vec![0.5_f32; 4]];
let _ = kimi_delta_chunk_cumulative_decay(&alphas);
}
}
#[derive(Debug, Clone)]
pub struct Llama3Config {
pub arch: ModelArch,
pub vocab_size: usize,
pub hidden_dim: usize,
pub intermediate_dim: usize,
pub num_heads: usize,
pub num_kv_heads: usize,
pub num_layers: usize,
pub max_seq_len: usize,
pub head_dim: usize,
pub rope_theta: f32,
pub norm_eps: f32,
pub attention_extras: Option<AttentionExtrasConfig>,
pub ssm: Option<SsmDeltaNetConfig>,
pub moe: Option<MoeConfig>,
pub gemma3n: Option<Gemma3nConfig>,
pub gemma4: Option<Gemma4Config>,
pub deepseek_v3: Option<DeepSeekV3Config>,
pub kimi_delta: Option<KimiDeltaConfig>,
}
impl Llama3Config {
#[inline]
pub fn sliding_window(&self) -> Option<usize> {
self.attention_extras
.as_ref()
.and_then(|a| a.sliding_window)
}
#[inline]
pub fn attn_logit_softcap(&self) -> Option<f32> {
self.attention_extras
.as_ref()
.and_then(|a| a.attn_logit_softcap)
}
#[inline]
pub fn final_logit_softcap(&self) -> Option<f32> {
self.attention_extras
.as_ref()
.and_then(|a| a.final_logit_softcap)
}
#[inline]
pub fn full_attention_interval(&self) -> Option<usize> {
self.ssm.as_ref().and_then(|s| s.full_attention_interval)
}
#[inline]
pub fn linear_num_kv_heads(&self) -> Option<usize> {
self.ssm.as_ref().and_then(|s| s.linear_num_kv_heads)
}
#[inline]
pub fn linear_qk_head_dim(&self) -> Option<usize> {
self.ssm.as_ref().and_then(|s| s.linear_qk_head_dim)
}
#[inline]
pub fn linear_kv_head_dim(&self) -> Option<usize> {
self.ssm.as_ref().and_then(|s| s.linear_kv_head_dim)
}
#[inline]
pub fn linear_num_v_heads(&self) -> Option<usize> {
self.ssm.as_ref().and_then(|s| s.linear_num_v_heads)
}
#[inline]
pub fn linear_conv_kernel_dim(&self) -> Option<usize> {
self.ssm.as_ref().and_then(|s| s.linear_conv_kernel_dim)
}
#[inline]
pub fn ssm_inner_size(&self) -> Option<usize> {
self.ssm.as_ref().and_then(|s| s.ssm_inner_size)
}
#[inline]
pub fn ssm_state_size(&self) -> Option<usize> {
self.ssm.as_ref().and_then(|s| s.ssm_state_size)
}
#[inline]
pub fn ssm_group_count(&self) -> Option<usize> {
self.ssm.as_ref().and_then(|s| s.ssm_group_count)
}
#[inline]
pub fn ssm_time_step_rank(&self) -> Option<usize> {
self.ssm.as_ref().and_then(|s| s.ssm_time_step_rank)
}
#[inline]
pub fn n_layer_nextn(&self) -> Option<usize> {
self.ssm.as_ref().and_then(|s| s.n_layer_nextn)
}
#[inline]
pub fn num_experts(&self) -> Option<usize> {
self.moe.as_ref().and_then(|m| m.num_experts)
}
#[inline]
pub fn num_experts_active(&self) -> Option<usize> {
self.moe.as_ref().and_then(|m| m.num_experts_active)
}
#[inline]
pub fn expert_ffn_size(&self) -> Option<usize> {
self.moe.as_ref().and_then(|m| m.expert_ffn_size)
}
#[inline]
pub fn sliding_window_pattern(&self) -> Option<&[bool]> {
self.gemma3n
.as_ref()
.and_then(|g| g.sliding_window_pattern.as_deref())
}
#[inline]
pub fn activation_sparsity_scale(&self) -> Option<&[f32]> {
self.gemma3n
.as_ref()
.and_then(|g| g.activation_sparsity_scale.as_deref())
}
#[inline]
pub fn shared_kv_layers(&self) -> Option<usize> {
self.gemma3n.as_ref().and_then(|g| g.shared_kv_layers)
}
#[inline]
pub fn per_layer_input_embedding_dim(&self) -> Option<usize> {
self.gemma3n
.as_ref()
.and_then(|g| g.per_layer_input_embedding_dim)
}
#[inline]
pub fn altup_num_inputs(&self) -> Option<usize> {
self.gemma3n.as_ref().and_then(|g| g.altup_num_inputs)
}
#[inline]
pub fn altup_active_idx(&self) -> Option<usize> {
self.gemma3n.as_ref().and_then(|g| g.altup_active_idx)
}
#[inline]
pub fn head_dim_swa(&self) -> Option<usize> {
self.gemma4.as_ref().and_then(|g| g.head_dim_swa)
}
#[inline]
pub fn rope_theta_swa(&self) -> Option<f32> {
self.gemma4.as_ref().and_then(|g| g.rope_theta_swa)
}
#[inline]
pub fn rope_dim_swa(&self) -> Option<usize> {
self.gemma4.as_ref().and_then(|g| g.rope_dim_swa)
}
#[inline]
pub fn ffn_size_per_layer(&self) -> Option<&[usize]> {
self.gemma4
.as_ref()
.and_then(|g| g.ffn_size_per_layer.as_deref())
}
#[inline]
pub fn deepseek_q_lora_rank(&self) -> Option<usize> {
self.deepseek_v3.as_ref().and_then(|d| d.q_lora_rank)
}
#[inline]
pub fn deepseek_kv_lora_rank(&self) -> Option<usize> {
self.deepseek_v3.as_ref().and_then(|d| d.kv_lora_rank)
}
#[inline]
pub fn deepseek_qk_nope_head_dim(&self) -> Option<usize> {
self.deepseek_v3.as_ref().and_then(|d| d.qk_nope_head_dim)
}
#[inline]
pub fn deepseek_qk_rope_head_dim(&self) -> Option<usize> {
self.deepseek_v3.as_ref().and_then(|d| d.qk_rope_head_dim)
}
#[inline]
pub fn deepseek_v_head_dim(&self) -> Option<usize> {
self.deepseek_v3.as_ref().and_then(|d| d.v_head_dim)
}
#[inline]
pub fn deepseek_n_routed_experts(&self) -> Option<usize> {
self.deepseek_v3.as_ref().and_then(|d| d.n_routed_experts)
}
#[inline]
pub fn deepseek_n_shared_experts(&self) -> Option<usize> {
self.deepseek_v3.as_ref().and_then(|d| d.n_shared_experts)
}
#[inline]
pub fn deepseek_num_experts_per_tok(&self) -> Option<usize> {
self.deepseek_v3
.as_ref()
.and_then(|d| d.num_experts_per_tok)
}
#[inline]
pub fn deepseek_moe_intermediate_size(&self) -> Option<usize> {
self.deepseek_v3
.as_ref()
.and_then(|d| d.moe_intermediate_size)
}
#[inline]
pub fn deepseek_first_k_dense_replace(&self) -> Option<usize> {
self.deepseek_v3
.as_ref()
.and_then(|d| d.first_k_dense_replace)
}
#[inline]
pub fn deepseek_routed_scaling_factor(&self) -> Option<f32> {
self.deepseek_v3
.as_ref()
.and_then(|d| d.routed_scaling_factor)
}
#[inline]
pub fn deepseek_noaux_tc(&self) -> Option<bool> {
self.deepseek_v3.as_ref().and_then(|d| d.noaux_tc)
}
#[inline]
pub fn deepseek_mtp_layer(&self) -> Option<usize> {
self.deepseek_v3.as_ref().and_then(|d| d.mtp_layer)
}
pub fn from_gguf<'a, G: crate::gguf::GgufSource<'a>>(gguf: &'a G) -> Option<Self> {
let arch = ModelArch::from_gguf(gguf);
let prefix = arch.resolve_prefix(gguf);
let hidden_dim = gguf.meta_u32(&format!("{prefix}.embedding_length"))? as usize;
let num_heads = gguf.meta_u32(&format!("{prefix}.attention.head_count"))? as usize;
let num_kv_heads = gguf
.meta_u32(&format!("{prefix}.attention.head_count_kv"))
.unwrap_or(num_heads as u32) as usize;
let num_layers = gguf.meta_u32(&format!("{prefix}.block_count"))? as usize;
let intermediate_dim = gguf
.meta_u32(&format!("{prefix}.feed_forward_length"))
.map(|v| v as usize)
.or_else(|| {
gguf.meta(&format!("{prefix}.feed_forward_length"))
.and_then(|v| match v {
crate::gguf::MetaValue::Array(arr) => arr
.first()
.and_then(|item| item.as_u32().map(|v| v as usize)),
_ => None,
})
})?;
let max_seq_len = gguf
.meta_u32(&format!("{prefix}.context_length"))
.unwrap_or(8192) as usize;
let max_seq_len = max_seq_len.min(8192);
let vocab_size = gguf.meta_u32(&format!("{prefix}.vocab_size")).or_else(|| {
gguf.meta("tokenizer.ggml.tokens").and_then(|v| match v {
crate::gguf::MetaValue::Array(arr) => Some(arr.len() as u32),
_ => None,
})
})? as usize;
let rope_theta = gguf
.meta_f32(&format!("{prefix}.rope.freq_base"))
.unwrap_or(if arch == ModelArch::Mistral {
1_000_000.0
} else {
500_000.0
});
let norm_eps = gguf
.meta_f32(&format!("{prefix}.attention.layer_norm_rms_epsilon"))
.unwrap_or(1e-5);
let head_dim = gguf
.meta_u32(&format!("{prefix}.attention.key_length"))
.map_or(hidden_dim / num_heads, |v| v as usize);
let sliding_window = gguf
.meta_u32(&format!("{prefix}.attention.sliding_window"))
.map(|v| v as usize);
let attn_logit_softcap = gguf.meta_f32(&format!("{prefix}.attn_logit_softcapping"));
let final_logit_softcap = gguf.meta_f32(&format!("{prefix}.final_logit_softcapping"));
let full_attention_interval = gguf
.meta_u32(&format!("{prefix}.full_attention_interval"))
.map(|v| v as usize);
let linear_num_kv_heads = gguf
.meta_u32(&format!("{prefix}.linear_num_key_heads"))
.map(|v| v as usize);
let linear_qk_head_dim = gguf
.meta_u32(&format!("{prefix}.linear_qk_head_dim"))
.map(|v| v as usize);
let linear_kv_head_dim = gguf
.meta_u32(&format!("{prefix}.linear_key_value_head_dim"))
.map(|v| v as usize);
let linear_num_v_heads = gguf
.meta_u32(&format!("{prefix}.linear_num_value_heads"))
.map(|v| v as usize);
let linear_conv_kernel_dim = gguf
.meta_u32(&format!("{prefix}.linear_conv_kernel_dim"))
.map(|v| v as usize);
let ssm_inner_size = gguf
.meta_u32(&format!("{prefix}.ssm.inner_size"))
.map(|v| v as usize);
let ssm_state_size = gguf
.meta_u32(&format!("{prefix}.ssm.state_size"))
.map(|v| v as usize);
let ssm_group_count = gguf
.meta_u32(&format!("{prefix}.ssm.group_count"))
.map(|v| v as usize);
let ssm_time_step_rank = gguf
.meta_u32(&format!("{prefix}.ssm.time_step_rank"))
.map(|v| v as usize);
let ssm_conv_kernel = gguf
.meta_u32(&format!("{prefix}.ssm.conv_kernel"))
.map(|v| v as usize);
let linear_num_kv_heads = linear_num_kv_heads.or(ssm_group_count);
let linear_qk_head_dim = linear_qk_head_dim.or(ssm_state_size);
let linear_kv_head_dim = linear_kv_head_dim.or(ssm_state_size);
let linear_num_v_heads =
linear_num_v_heads.or_else(|| match (ssm_inner_size, linear_kv_head_dim) {
(Some(inner), Some(head_dim)) if head_dim > 0 => Some(inner / head_dim),
_ => None,
});
let linear_conv_kernel_dim = linear_conv_kernel_dim.or(ssm_conv_kernel);
let n_layer_nextn = gguf
.meta_u32(&format!("{prefix}.nextn.predict_layers"))
.map(|v| v as usize);
let num_experts = gguf
.meta_u32(&format!("{prefix}.expert_count"))
.map(|v| v as usize);
let num_experts_active = gguf
.meta_u32(&format!("{prefix}.expert_used_count"))
.map(|v| v as usize);
let expert_ffn_size = gguf
.meta_u32(&format!("{prefix}.expert_feed_forward_length"))
.map(|v| v as usize);
let sliding_window_pattern = gguf
.meta(&format!("{prefix}.attention.sliding_window_pattern"))
.and_then(|v| match v {
crate::gguf::MetaValue::Array(arr) => {
let mut out = Vec::with_capacity(arr.len());
for item in arr {
match item {
crate::gguf::MetaValue::Bool(b) => out.push(*b),
_ => return None,
}
}
Some(out)
}
_ => None,
});
let activation_sparsity_scale = gguf
.meta(&format!("{prefix}.activation_sparsity_scale"))
.and_then(|v| match v {
crate::gguf::MetaValue::Array(arr) => {
let mut out = Vec::with_capacity(arr.len());
for item in arr {
match item {
crate::gguf::MetaValue::F32(f) => out.push(*f),
_ => return None,
}
}
Some(out)
}
_ => None,
});
let shared_kv_layers = gguf
.meta_u32(&format!("{prefix}.attention.shared_kv_layers"))
.map(|v| v as usize);
let per_layer_input_embedding_dim = gguf
.meta_u32(&format!("{prefix}.embedding_length_per_layer_input"))
.map(|v| v as usize);
let altup_num_inputs = gguf
.meta_u32(&format!("{prefix}.altup.num_inputs"))
.map(|v| v as usize);
let altup_active_idx = gguf
.meta_u32(&format!("{prefix}.altup.active_idx"))
.map(|v| v as usize);
let head_dim_swa = gguf
.meta_u32(&format!("{prefix}.attention.key_length_swa"))
.map(|v| v as usize);
let rope_theta_swa = gguf.meta_f32(&format!("{prefix}.rope.freq_base_swa"));
let rope_dim_swa = gguf
.meta_u32(&format!("{prefix}.rope.dimension_count_swa"))
.map(|v| v as usize);
let ffn_size_per_layer = gguf
.meta(&format!("{prefix}.feed_forward_length"))
.and_then(|v| match v {
crate::gguf::MetaValue::Array(arr) if arr.len() == num_layers => {
let mut out = Vec::with_capacity(arr.len());
for item in arr {
out.push(item.as_u32()? as usize);
}
Some(out)
}
_ => None,
});
let attention_extras = if sliding_window.is_some()
|| attn_logit_softcap.is_some()
|| final_logit_softcap.is_some()
{
Some(AttentionExtrasConfig {
sliding_window,
attn_logit_softcap,
final_logit_softcap,
})
} else {
None
};
let ssm = if full_attention_interval.is_some()
|| linear_num_kv_heads.is_some()
|| linear_qk_head_dim.is_some()
|| linear_kv_head_dim.is_some()
|| linear_num_v_heads.is_some()
|| linear_conv_kernel_dim.is_some()
|| ssm_inner_size.is_some()
|| ssm_state_size.is_some()
|| ssm_group_count.is_some()
|| ssm_time_step_rank.is_some()
|| n_layer_nextn.is_some()
{
Some(SsmDeltaNetConfig {
full_attention_interval,
linear_num_kv_heads,
linear_qk_head_dim,
linear_kv_head_dim,
linear_num_v_heads,
linear_conv_kernel_dim,
ssm_inner_size,
ssm_state_size,
ssm_group_count,
ssm_time_step_rank,
n_layer_nextn,
})
} else {
None
};
let moe =
if num_experts.is_some() || num_experts_active.is_some() || expert_ffn_size.is_some() {
Some(MoeConfig {
num_experts,
num_experts_active,
expert_ffn_size,
})
} else {
None
};
let gemma3n = if sliding_window_pattern.is_some()
|| activation_sparsity_scale.is_some()
|| shared_kv_layers.is_some()
|| per_layer_input_embedding_dim.is_some()
|| altup_num_inputs.is_some()
|| altup_active_idx.is_some()
{
Some(Gemma3nConfig {
sliding_window_pattern,
activation_sparsity_scale,
shared_kv_layers,
per_layer_input_embedding_dim,
altup_num_inputs,
altup_active_idx,
})
} else {
None
};
let gemma4 = if head_dim_swa.is_some()
|| rope_theta_swa.is_some()
|| rope_dim_swa.is_some()
|| ffn_size_per_layer.is_some()
{
Some(Gemma4Config {
head_dim_swa,
rope_theta_swa,
rope_dim_swa,
ffn_size_per_layer,
})
} else {
None
};
let deepseek_v3 = if arch == ModelArch::DeepSeekV3 {
let q_lora_rank = gguf
.meta_u32(&format!("{prefix}.attention.q_lora_rank"))
.map(|v| v as usize);
let kv_lora_rank = gguf
.meta_u32(&format!("{prefix}.attention.kv_lora_rank"))
.map(|v| v as usize);
let key_length = gguf
.meta_u32(&format!("{prefix}.attention.key_length"))
.map(|v| v as usize);
let qk_rope_head_dim = gguf
.meta_u32(&format!("{prefix}.rope.dimension_count"))
.map(|v| v as usize);
let qk_nope_head_dim = if q_lora_rank.is_none() {
key_length.zip(qk_rope_head_dim).map(|(kl, rope)| kl - rope)
} else {
key_length
};
let v_head_dim = gguf
.meta_u32(&format!("{prefix}.attention.value_length"))
.map(|v| v as usize);
let n_routed_experts = gguf
.meta_u32(&format!("{prefix}.expert_count"))
.map(|v| v as usize);
let n_shared_experts = gguf
.meta_u32(&format!("{prefix}.expert_shared_count"))
.map(|v| v as usize);
let num_experts_per_tok = gguf
.meta_u32(&format!("{prefix}.expert_used_count"))
.map(|v| v as usize);
let moe_intermediate_size = gguf
.meta_u32(&format!("{prefix}.expert_feed_forward_length"))
.map(|v| v as usize);
let first_k_dense_replace = gguf
.meta_u32(&format!("{prefix}.leading_dense_block_count"))
.map(|v| v as usize);
let routed_scaling_factor = gguf.meta_f32(&format!("{prefix}.expert_weights_scale"));
let noaux_tc = gguf
.meta_u32(&format!("{prefix}.expert_gating_func"))
.map(|v| v == 2); let mtp_layer = gguf
.meta_u32(&format!("{prefix}.mtp_layer_count"))
.and_then(|c| {
if c > 0 {
Some(num_layers + c as usize - 1)
} else {
None
}
});
Some(DeepSeekV3Config {
q_lora_rank,
kv_lora_rank,
qk_nope_head_dim,
qk_rope_head_dim,
v_head_dim,
n_routed_experts,
n_shared_experts,
num_experts_per_tok,
moe_intermediate_size,
first_k_dense_replace,
routed_scaling_factor,
noaux_tc,
mtp_layer,
})
} else {
None
};
let kimi_delta = if arch == ModelArch::KimiK3 {
Some(KimiDeltaConfig::from_gguf(gguf, &prefix))
} else {
None
};
Some(Self {
arch,
vocab_size,
hidden_dim,
intermediate_dim,
num_heads,
num_kv_heads,
num_layers,
max_seq_len,
head_dim,
rope_theta,
norm_eps,
attention_extras,
ssm,
moe,
gemma3n,
gemma4,
deepseek_v3,
kimi_delta,
})
}
pub fn is_hybrid(&self) -> bool {
self.full_attention_interval().is_some()
}
pub fn is_deltanet_layer(&self, i: usize) -> bool {
match self.full_attention_interval() {
Some(interval) => !(i + 1).is_multiple_of(interval),
None => false,
}
}
pub const fn use_neox_rope(&self) -> bool {
self.arch.use_neox_rope()
}
#[inline]
pub fn apply_ffn_act(&self, _layer_idx: usize, x: f32) -> f32 {
match self.arch {
ModelArch::Gemma2 | ModelArch::Gemma3n | ModelArch::Gemma4 => gelu_approx(x),
_ => silu(x),
}
}
pub fn apply_ffn_sparsity(&self, layer_idx: usize, gate_buf: &mut [f32]) {
if self.arch != ModelArch::Gemma3n {
return;
}
let scale = match self
.activation_sparsity_scale()
.and_then(|arr| arr.get(layer_idx))
{
Some(s) if s.is_finite() => *s,
_ => return,
};
let n = gate_buf.len();
if n < 2 {
return;
}
let mut sum = 0.0f64;
for &x in gate_buf.iter() {
sum += x as f64;
}
let mean = (sum / n as f64) as f32;
let mut sq_sum = 0.0f64;
for &x in gate_buf.iter() {
let d = (x - mean) as f64;
sq_sum += d * d;
}
let std = (sq_sum / (n - 1) as f64).sqrt() as f32;
let cutoff = mean + scale * std;
for x in gate_buf.iter_mut() {
*x = (*x - cutoff).max(0.0);
}
}
pub fn kv_from_start_layers(&self) -> usize {
match (self.arch, self.shared_kv_layers()) {
(ModelArch::Gemma3n | ModelArch::Gemma4, Some(shared)) if shared < self.num_layers => {
self.num_layers - shared
}
_ => self.num_layers,
}
}
pub fn kv_source_layer(&self, i: usize) -> usize {
if !matches!(self.arch, ModelArch::Gemma3n | ModelArch::Gemma4) {
return i;
}
let root = self.kv_from_start_layers();
if i < root {
return i;
}
let is_swa = self.sliding_window_for_layer(i).is_some();
let offset = if is_swa { 2 } else { 1 };
root.saturating_sub(offset)
}
pub fn build_kv_layer_map(&self) -> Vec<usize> {
(0..self.num_layers)
.map(|i| self.kv_source_layer(i))
.collect()
}
pub fn sliding_window_for_layer(&self, i: usize) -> Option<usize> {
match self.arch {
ModelArch::Gemma2 => {
if i.is_multiple_of(2) {
self.sliding_window()
} else {
None
}
}
ModelArch::Gemma3n | ModelArch::Gemma4 => {
if let Some(pattern) = self.sliding_window_pattern().as_ref() {
if pattern.get(i).copied().unwrap_or(false) {
self.sliding_window()
} else {
None
}
} else {
self.sliding_window()
}
}
_ => self.sliding_window(),
}
}
#[must_use]
pub const fn llama3_8b() -> Self {
Self {
arch: ModelArch::Llama,
vocab_size: 128_256,
hidden_dim: 4096,
intermediate_dim: 14_336,
num_heads: 32,
num_kv_heads: 8,
num_layers: 32,
max_seq_len: 8192,
head_dim: 128,
rope_theta: 500_000.0,
norm_eps: 1e-5,
attention_extras: None,
ssm: None,
moe: None,
gemma3n: None,
gemma4: None,
deepseek_v3: None,
kimi_delta: None,
}
}
pub fn head_dim_for_layer(&self, i: usize) -> usize {
match (self.arch, self.head_dim_swa()) {
(ModelArch::Gemma4, Some(hs)) if self.sliding_window_for_layer(i).is_some() => hs,
_ => self.head_dim,
}
}
pub fn rope_theta_for_layer(&self, i: usize) -> f32 {
match (self.arch, self.rope_theta_swa()) {
(ModelArch::Gemma4, Some(ts)) if self.sliding_window_for_layer(i).is_some() => ts,
_ => self.rope_theta,
}
}
pub fn ffn_size_for_layer(&self, i: usize) -> usize {
self.ffn_size_per_layer()
.as_ref()
.and_then(|arr| arr.get(i).copied())
.unwrap_or(self.intermediate_dim)
}
pub fn q_dim_for_layer(&self, i: usize) -> usize {
self.num_heads * self.head_dim_for_layer(i)
}
pub fn kv_dim_for_layer(&self, i: usize) -> usize {
self.num_kv_heads * self.head_dim_for_layer(i)
}
}
struct KvCache {
keys: Vec<f32>,
values: Vec<f32>,
_num_layers: usize,
max_seq_len: usize,
kv_dim: usize,
seq_len: usize,
kv_layer_map: Vec<usize>,
}
impl KvCache {
fn new(num_layers: usize, max_seq_len: usize, kv_dim: usize) -> Self {
let total = num_layers * max_seq_len * kv_dim;
Self {
keys: vec![0.0f32; total],
values: vec![0.0f32; total],
_num_layers: num_layers,
max_seq_len,
kv_dim,
seq_len: 0,
kv_layer_map: (0..num_layers).collect(),
}
}
fn set_layer_map(&mut self, map: Vec<usize>) {
assert_eq!(map.len(), self._num_layers, "kv_layer_map length mismatch");
self.kv_layer_map = map;
}
#[inline]
const fn offset(&self, layer: usize, pos: usize) -> usize {
(layer * self.max_seq_len + pos) * self.kv_dim
}
fn append(&mut self, layer: usize, k: &[f32], v: &[f32]) {
if self.kv_layer_map[layer] != layer {
return;
}
let off = self.offset(layer, self.seq_len);
let n = k.len().min(self.kv_dim);
self.keys[off..off + n].copy_from_slice(&k[..n]);
self.values[off..off + n].copy_from_slice(&v[..n]);
if n < self.kv_dim {
for i in n..self.kv_dim {
self.keys[off + i] = 0.0;
self.values[off + i] = 0.0;
}
}
}
const fn advance(&mut self) {
self.seq_len += 1;
}
const fn seq_len(&self) -> usize {
self.seq_len
}
#[inline]
fn key_at(&self, layer: usize, pos: usize) -> &[f32] {
let src_layer = self.kv_layer_map[layer];
let off = self.offset(src_layer, pos);
&self.keys[off..off + self.kv_dim]
}
#[inline]
fn value_at(&self, layer: usize, pos: usize) -> &[f32] {
let src_layer = self.kv_layer_map[layer];
let off = self.offset(src_layer, pos);
&self.values[off..off + self.kv_dim]
}
const fn rollback_to(&mut self, pos: usize) {
self.seq_len = pos;
}
const fn clear(&mut self) {
self.seq_len = 0;
}
}
const KV_MAGIC: &[u8; 8] = b"ALICEKV1";
const KV_FORMAT_VERSION: u32 = 1;
#[derive(Debug)]
pub enum KvCacheLoadError {
Io(std::io::Error),
BadMagic { got: [u8; 8] },
UnsupportedVersion { got: u32, expected: u32 },
FingerprintMismatch { got: u64, expected: u64 },
ShapeMismatch(String),
OverflowSeqLen { got: u64, max: usize },
}
impl std::fmt::Display for KvCacheLoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io(e) => write!(f, "kv cache load: {e}"),
Self::BadMagic { got } => {
write!(f, "kv cache load: expected magic {KV_MAGIC:?}, got {got:?}")
}
Self::UnsupportedVersion { got, expected } => write!(
f,
"kv cache load: unsupported version {got} (expected {expected})"
),
Self::FingerprintMismatch { got, expected } => write!(
f,
"kv cache load: model config fingerprint mismatch (got {got:#x}, expected {expected:#x})"
),
Self::ShapeMismatch(msg) => write!(f, "kv cache load: shape mismatch — {msg}"),
Self::OverflowSeqLen { got, max } => write!(
f,
"kv cache load: seq_len {got} exceeds max_seq_len {max}"
),
}
}
}
impl std::error::Error for KvCacheLoadError {}
impl From<std::io::Error> for KvCacheLoadError {
fn from(e: std::io::Error) -> Self {
Self::Io(e)
}
}
fn kv_cache_fingerprint(config: &Llama3Config) -> u64 {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
(config.num_layers as u64).hash(&mut hasher);
(config.num_kv_heads as u64).hash(&mut hasher);
(config.head_dim as u64).hash(&mut hasher);
(config.hidden_dim as u64).hash(&mut hasher);
(config.max_seq_len as u64).hash(&mut hasher);
format!("{:?}", config.arch).hash(&mut hasher);
hasher.finish()
}
impl KvCache {
fn write_to(&self, writer: &mut impl std::io::Write, fingerprint: u64) -> std::io::Result<()> {
writer.write_all(KV_MAGIC)?;
writer.write_all(&KV_FORMAT_VERSION.to_le_bytes())?;
writer.write_all(&fingerprint.to_le_bytes())?;
writer.write_all(&(self._num_layers as u64).to_le_bytes())?;
writer.write_all(&(self.max_seq_len as u64).to_le_bytes())?;
writer.write_all(&(self.kv_dim as u64).to_le_bytes())?;
writer.write_all(&(self.seq_len as u64).to_le_bytes())?;
for &src in &self.kv_layer_map {
writer.write_all(&(src as u32).to_le_bytes())?;
}
let n = self.seq_len * self.kv_dim;
for layer in 0..self._num_layers {
if self.kv_layer_map[layer] != layer {
continue;
}
let off = self.offset(layer, 0);
let keys_bytes = f32_slice_as_bytes(&self.keys[off..off + n]);
let values_bytes = f32_slice_as_bytes(&self.values[off..off + n]);
writer.write_all(keys_bytes)?;
writer.write_all(values_bytes)?;
}
Ok(())
}
fn read_from(
&mut self,
reader: &mut impl std::io::Read,
expected_fingerprint: u64,
) -> Result<(), KvCacheLoadError> {
let mut magic = [0u8; 8];
reader.read_exact(&mut magic)?;
if &magic != KV_MAGIC {
return Err(KvCacheLoadError::BadMagic { got: magic });
}
let mut buf4 = [0u8; 4];
let mut buf8 = [0u8; 8];
reader.read_exact(&mut buf4)?;
let version = u32::from_le_bytes(buf4);
if version != KV_FORMAT_VERSION {
return Err(KvCacheLoadError::UnsupportedVersion {
got: version,
expected: KV_FORMAT_VERSION,
});
}
reader.read_exact(&mut buf8)?;
let file_fingerprint = u64::from_le_bytes(buf8);
if file_fingerprint != expected_fingerprint {
return Err(KvCacheLoadError::FingerprintMismatch {
got: file_fingerprint,
expected: expected_fingerprint,
});
}
reader.read_exact(&mut buf8)?;
let num_layers = u64::from_le_bytes(buf8) as usize;
reader.read_exact(&mut buf8)?;
let max_seq_len = u64::from_le_bytes(buf8) as usize;
reader.read_exact(&mut buf8)?;
let kv_dim = u64::from_le_bytes(buf8) as usize;
reader.read_exact(&mut buf8)?;
let seq_len = u64::from_le_bytes(buf8);
if num_layers != self._num_layers
|| max_seq_len != self.max_seq_len
|| kv_dim != self.kv_dim
{
return Err(KvCacheLoadError::ShapeMismatch(format!(
"file num_layers/max_seq_len/kv_dim = {num_layers}/{max_seq_len}/{kv_dim}, \
model = {}/{}/{}",
self._num_layers, self.max_seq_len, self.kv_dim
)));
}
if (seq_len as usize) > max_seq_len {
return Err(KvCacheLoadError::OverflowSeqLen {
got: seq_len,
max: max_seq_len,
});
}
let seq_len = seq_len as usize;
let mut layer_map = Vec::with_capacity(num_layers);
for _ in 0..num_layers {
reader.read_exact(&mut buf4)?;
layer_map.push(u32::from_le_bytes(buf4) as usize);
}
self.kv_layer_map = layer_map;
let n = seq_len * kv_dim;
for layer in 0..num_layers {
if self.kv_layer_map[layer] != layer {
continue;
}
let off = self.offset(layer, 0);
let keys_bytes = f32_slice_as_bytes_mut(&mut self.keys[off..off + n]);
reader.read_exact(keys_bytes)?;
let values_bytes = f32_slice_as_bytes_mut(&mut self.values[off..off + n]);
reader.read_exact(values_bytes)?;
}
self.seq_len = seq_len;
Ok(())
}
}
fn f32_slice_as_bytes(s: &[f32]) -> &[u8] {
unsafe { std::slice::from_raw_parts(s.as_ptr().cast::<u8>(), std::mem::size_of_val(s)) }
}
fn f32_slice_as_bytes_mut(s: &mut [f32]) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(s.as_mut_ptr().cast::<u8>(), std::mem::size_of_val(s)) }
}
const PAGE_SIZE: usize = 16;
struct KvPage {
keys: Vec<f32>,
values: Vec<f32>,
used: usize,
}
impl KvPage {
fn new(kv_dim: usize) -> Self {
Self {
keys: vec![0.0f32; PAGE_SIZE * kv_dim],
values: vec![0.0f32; PAGE_SIZE * kv_dim],
used: 0,
}
}
const fn is_full(&self) -> bool {
self.used >= PAGE_SIZE
}
}
#[allow(dead_code)]
struct PagedKvCache {
pages: Vec<Vec<KvPage>>, num_layers: usize,
kv_dim: usize,
seq_len: usize,
}
#[allow(dead_code)]
impl PagedKvCache {
fn new(num_layers: usize, kv_dim: usize) -> Self {
let pages = (0..num_layers).map(|_| Vec::new()).collect();
Self {
pages,
num_layers,
kv_dim,
seq_len: 0,
}
}
fn append(&mut self, layer: usize, k: &[f32], v: &[f32]) {
let layer_pages = &mut self.pages[layer];
if layer_pages.is_empty() || layer_pages.last().unwrap().is_full() {
layer_pages.push(KvPage::new(self.kv_dim));
}
let page = layer_pages.last_mut().unwrap();
let off = page.used * self.kv_dim;
page.keys[off..off + self.kv_dim].copy_from_slice(k);
page.values[off..off + self.kv_dim].copy_from_slice(v);
page.used += 1;
}
const fn advance(&mut self) {
self.seq_len += 1;
}
const fn seq_len(&self) -> usize {
self.seq_len
}
#[inline]
fn key_at(&self, layer: usize, pos: usize) -> &[f32] {
let page_idx = pos / PAGE_SIZE;
let slot = pos % PAGE_SIZE;
let off = slot * self.kv_dim;
&self.pages[layer][page_idx].keys[off..off + self.kv_dim]
}
#[inline]
fn value_at(&self, layer: usize, pos: usize) -> &[f32] {
let page_idx = pos / PAGE_SIZE;
let slot = pos % PAGE_SIZE;
let off = slot * self.kv_dim;
&self.pages[layer][page_idx].values[off..off + self.kv_dim]
}
fn rollback_to(&mut self, pos: usize) {
self.seq_len = pos;
for layer_pages in &mut self.pages {
let needed = if pos == 0 {
0
} else {
(pos - 1) / PAGE_SIZE + 1
};
layer_pages.truncate(needed);
if let Some(last) = layer_pages.last_mut() {
let rem = pos % PAGE_SIZE;
last.used = if rem == 0 && pos > 0 { PAGE_SIZE } else { rem };
}
}
}
fn clear(&mut self) {
self.seq_len = 0;
for layer_pages in &mut self.pages {
layer_pages.clear();
}
}
fn total_pages(&self) -> usize {
self.pages.iter().map(std::vec::Vec::len).sum()
}
fn memory_bytes(&self) -> usize {
self.total_pages() * PAGE_SIZE * self.kv_dim * 4 * 2
}
}
pub struct BatchRequest {
pub id: usize,
pub tokens: Vec<u32>,
pub generated: Vec<u32>,
pub max_new_tokens: usize,
pub temperature: f32,
pub done: bool,
kv_cache: PagedKvCache,
}
pub struct BatchScheduler {
requests: Vec<BatchRequest>,
next_id: usize,
}
impl Default for BatchScheduler {
fn default() -> Self {
Self::new()
}
}
impl BatchScheduler {
pub const fn new() -> Self {
Self {
requests: Vec::new(),
next_id: 0,
}
}
pub fn add_request(
&mut self,
tokens: Vec<u32>,
max_new_tokens: usize,
temperature: f32,
num_layers: usize,
kv_dim: usize,
) -> usize {
let id = self.next_id;
self.next_id += 1;
self.requests.push(BatchRequest {
id,
tokens,
generated: Vec::new(),
max_new_tokens,
temperature,
done: false,
kv_cache: PagedKvCache::new(num_layers, kv_dim),
});
id
}
pub fn active_count(&self) -> usize {
self.requests.iter().filter(|r| !r.done).count()
}
pub fn requests_mut(&mut self) -> &mut [BatchRequest] {
&mut self.requests
}
pub fn get_result(&self, id: usize) -> Option<&BatchRequest> {
self.requests.iter().find(|r| r.id == id && r.done)
}
}
fn rms_norm(x: &[f32], weight: &[f32], eps: f32, out: &mut [f32]) {
let n = x.len();
let mut ss = 0.0f64;
for &v in x {
ss += (v as f64) * (v as f64);
}
let mean = (ss / n as f64) as f32;
let scale = 1.0f32 / (mean + eps).sqrt();
for i in 0..n {
out[i] = x[i] * scale * weight[i];
}
}
fn apply_head_rms_norm_identity(buf: &mut [f32], head_dim: usize, eps: f32) {
let num_heads = buf.len() / head_dim;
for h in 0..num_heads {
let start = h * head_dim;
let slice = &mut buf[start..start + head_dim];
let mut ss = 0.0f64;
for &v in slice.iter() {
ss += (v as f64) * (v as f64);
}
let mean = (ss / head_dim as f64) as f32;
let scale = 1.0f32 / (mean + eps).sqrt();
for v in slice.iter_mut() {
*v *= scale;
}
}
}
fn mat_vec_f32(w: &[f32], rows: usize, cols: usize, x: &[f32], out: &mut [f32]) {
for i in 0..rows {
let row = &w[i * cols..(i + 1) * cols];
let mut sum = 0.0f32;
for j in 0..cols {
sum += row[j] * x[j];
}
out[i] = sum;
}
}
fn l2_magnitude(x: &[f32]) -> f32 {
let mut ss = 0.0f64;
for &v in x {
ss += (v as f64) * (v as f64);
}
(ss as f32).sqrt()
}
fn apply_qk_norm(buf: &mut [f32], weight: &[f32], head_dim: usize, eps: f32) {
let num_heads = buf.len() / head_dim;
for h in 0..num_heads {
let start = h * head_dim;
let slice = &mut buf[start..start + head_dim];
let mut ss = 0.0f64;
for &v in slice.iter() {
ss += (v as f64) * (v as f64);
}
let mean = (ss / head_dim as f64) as f32;
let scale = 1.0f32 / (mean + eps).sqrt();
for (i, w) in weight.iter().enumerate() {
slice[i] = slice[i] * scale * w;
}
}
}
fn apply_rope(vec: &mut [f32], position: usize, head_dim: usize, theta: f32) {
for i in (0..head_dim).step_by(2) {
let freq = 1.0 / theta.powf(i as f32 / head_dim as f32);
let angle = position as f32 * freq;
let (sin_val, cos_val) = angle.sin_cos();
let x0 = vec[i];
let x1 = vec[i + 1];
vec[i] = x0 * cos_val - x1 * sin_val;
vec[i + 1] = x0 * sin_val + x1 * cos_val;
}
}
fn apply_rope_scaled(
vec: &mut [f32],
position: usize,
head_dim: usize,
theta: f32,
freq_factors: &[f32],
) {
for i in (0..head_dim).step_by(2) {
let freq_idx = i / 2;
let base_freq = 1.0 / theta.powf(i as f32 / head_dim as f32);
let factor = if freq_idx < freq_factors.len() {
freq_factors[freq_idx]
} else {
1.0
};
let freq = base_freq / factor;
let angle = position as f32 * freq;
let (sin_val, cos_val) = angle.sin_cos();
let x0 = vec[i];
let x1 = vec[i + 1];
vec[i] = x0 * cos_val - x1 * sin_val;
vec[i + 1] = x0 * sin_val + x1 * cos_val;
}
}
fn apply_rope_neox(vec: &mut [f32], position: usize, head_dim: usize, theta: f32) {
let half = head_dim / 2;
for i in 0..half {
let freq = 1.0 / theta.powf(2.0 * i as f32 / head_dim as f32);
let angle = position as f32 * freq;
let (sin_val, cos_val) = angle.sin_cos();
let x0 = vec[i];
let x1 = vec[i + half];
vec[i] = x0 * cos_val - x1 * sin_val;
vec[i + half] = x0 * sin_val + x1 * cos_val;
}
}
fn apply_rope_scaled_neox(
vec: &mut [f32],
position: usize,
head_dim: usize,
theta: f32,
freq_factors: &[f32],
) {
let half = head_dim / 2;
for i in 0..half {
let base_freq = 1.0 / theta.powf(2.0 * i as f32 / head_dim as f32);
let factor = if i < freq_factors.len() {
freq_factors[i]
} else {
1.0
};
let freq = base_freq / factor;
let angle = position as f32 * freq;
let (sin_val, cos_val) = angle.sin_cos();
let x0 = vec[i];
let x1 = vec[i + half];
vec[i] = x0 * cos_val - x1 * sin_val;
vec[i + half] = x0 * sin_val + x1 * cos_val;
}
}
#[inline]
fn apply_rope_auto(
vec: &mut [f32],
position: usize,
head_dim: usize,
theta: f32,
freq_scales: Option<&[f32]>,
neox: bool,
) {
match (neox, freq_scales) {
(false, Some(s)) => apply_rope_scaled(vec, position, head_dim, theta, s),
(false, None) => apply_rope(vec, position, head_dim, theta),
(true, Some(s)) => apply_rope_scaled_neox(vec, position, head_dim, theta, s),
(true, None) => apply_rope_neox(vec, position, head_dim, theta),
}
}
fn gqa_attention(
q_buf: &[f32],
kv_cache: &KvCache,
layer_idx: usize,
pos: usize,
num_heads: usize,
num_kv_heads: usize,
head_dim: usize,
sliding_window: Option<usize>,
attn_logit_softcap: Option<f32>,
attention_scale: Option<f32>,
attn_out: &mut [f32],
) {
let seq_len = pos + 1;
let heads_per_kv = num_heads / num_kv_heads;
let inv_sqrt_d = attention_scale.unwrap_or_else(|| 1.0 / (head_dim as f32).sqrt());
let attn_start = match sliding_window {
Some(w) => seq_len.saturating_sub(w),
None => 0,
};
let use_f64_acc = std::env::var_os("ALICE_ATTN_F64_ACC").is_some();
let use_online_softmax = std::env::var_os("ALICE_ATTN_ONLINE_SOFTMAX").is_some();
if attn_logit_softcap.is_none() && !use_online_softmax && !use_f64_acc {
if let Some(sparse_topk) = std::env::var("ALICE_SPARSE_TOPK")
.ok()
.and_then(|s| s.parse::<usize>().ok())
{
let used_len = seq_len - attn_start;
let expected_q_len = num_heads * head_dim;
let expected_kv_dim = num_kv_heads * head_dim;
if used_len > 0 && q_buf.len() >= expected_q_len && attn_out.len() >= expected_q_len {
let mut k_dense = Vec::with_capacity(used_len * expected_kv_dim);
let mut v_dense = Vec::with_capacity(used_len * expected_kv_dim);
for t in attn_start..seq_len {
let k_row = kv_cache.key_at(layer_idx, t);
let v_row = kv_cache.value_at(layer_idx, t);
let take = expected_kv_dim.min(k_row.len());
if take != expected_kv_dim {
break;
}
k_dense.extend_from_slice(&k_row[..expected_kv_dim]);
v_dense.extend_from_slice(&v_row[..expected_kv_dim]);
}
if k_dense.len() == used_len * expected_kv_dim {
let cfg = crate::sparse_attention::llama3_bridge::BridgeConfig {
hq: num_heads,
hkv: num_kv_heads,
head_dim,
block_size: 64,
page_size: 64,
softmax_scale: inv_sqrt_d,
causal: false,
topk: sparse_topk,
};
let kv_view = crate::sparse_attention::llama3_bridge::DenseKvCacheView {
k: &k_dense,
v: &v_dense,
seq_len: used_len,
hkv: num_kv_heads,
head_dim,
};
let bridge_result =
crate::sparse_attention::llama3_bridge::llama3_sparse_attention(
&q_buf[..expected_q_len],
&kv_view,
1,
&cfg,
);
match bridge_result {
Ok(out) => {
if std::env::var_os("ALICE_SPARSE_DEBUG").is_some() {
eprintln!(
"[sparse] hit L{layer_idx} pos={pos} used_len={used_len} topk={sparse_topk} hq={num_heads} hkv={num_kv_heads} d={head_dim}"
);
}
attn_out[..expected_q_len].copy_from_slice(&out);
return;
}
Err(e) => {
if std::env::var_os("ALICE_SPARSE_DEBUG").is_some() {
eprintln!(
"[sparse] REJECT L{layer_idx} pos={pos} used_len={used_len} err={e}"
);
}
}
}
}
}
}
}
attn_out.fill(0.0);
for h in 0..num_heads {
let kv_h = h / heads_per_kv;
let q_start = h * head_dim;
let q_head = &q_buf[q_start..q_start + head_dim];
let k_offset = kv_h * head_dim;
let v_offset = kv_h * head_dim;
if use_online_softmax {
let mut m = f32::NEG_INFINITY;
let mut s = 0.0f32;
for t in attn_start..seq_len {
let k_cached = kv_cache.key_at(layer_idx, t);
let v_cached = kv_cache.value_at(layer_idx, t);
let mut score = 0.0f32;
for d in 0..head_dim {
score += q_head[d] * k_cached[k_offset + d];
}
score *= inv_sqrt_d;
if let Some(cap) = attn_logit_softcap {
score = cap * (score / cap).tanh();
}
let m_old = m;
let (ms, vs) = if score > m {
m = score;
let ms = (m_old - m).exp(); for d in 0..head_dim {
attn_out[q_start + d] *= ms;
}
(ms, 1.0f32)
} else {
(1.0f32, (score - m).exp())
};
for d in 0..head_dim {
attn_out[q_start + d] += vs * v_cached[v_offset + d];
}
s = s * ms + vs;
}
if s > 0.0 {
let inv_s = 1.0 / s;
for d in 0..head_dim {
attn_out[q_start + d] *= inv_s;
}
}
continue;
}
let window_len = seq_len - attn_start;
let mut scores = Vec::with_capacity(window_len);
for t in attn_start..seq_len {
let k_cached = kv_cache.key_at(layer_idx, t);
let mut score = if use_f64_acc {
let mut acc = 0.0f64;
for d in 0..head_dim {
acc += q_head[d] as f64 * k_cached[k_offset + d] as f64;
}
(acc as f32) * inv_sqrt_d
} else {
let mut acc = 0.0f32;
for d in 0..head_dim {
acc += q_head[d] * k_cached[k_offset + d];
}
acc * inv_sqrt_d
};
if let Some(cap) = attn_logit_softcap {
score = cap * (score / cap).tanh();
}
scores.push(score);
}
let max_score = scores.iter().copied().fold(f32::NEG_INFINITY, f32::max);
if use_f64_acc {
let mut sum_f64 = 0.0f64;
for s in &mut scores {
*s = (*s - max_score).exp();
sum_f64 += *s as f64;
}
if sum_f64 > 0.0 {
let inv_sum = (1.0 / sum_f64) as f32;
for s in &mut scores {
*s *= inv_sum;
}
}
} else {
let mut sum = 0.0f32;
for s in &mut scores {
*s = (*s - max_score).exp();
sum += *s;
}
if sum > 0.0 {
let inv_sum = 1.0 / sum;
for s in &mut scores {
*s *= inv_sum;
}
}
}
for (si, t) in (attn_start..seq_len).enumerate() {
let v_cached = kv_cache.value_at(layer_idx, t);
let w = scores[si];
if use_f64_acc {
for d in 0..head_dim {
let acc =
attn_out[q_start + d] as f64 + w as f64 * v_cached[v_offset + d] as f64;
attn_out[q_start + d] = acc as f32;
}
} else {
for d in 0..head_dim {
attn_out[q_start + d] += w * v_cached[v_offset + d];
}
}
}
}
}
fn gqa_attention_paged(
q_buf: &[f32],
kv_cache: &PagedKvCache,
layer_idx: usize,
pos: usize,
num_heads: usize,
num_kv_heads: usize,
head_dim: usize,
sliding_window: Option<usize>,
attn_logit_softcap: Option<f32>,
attn_out: &mut [f32],
) {
let seq_len = pos + 1;
let heads_per_kv = num_heads / num_kv_heads;
let inv_sqrt_d = 1.0 / (head_dim as f32).sqrt();
let attn_start = match sliding_window {
Some(w) => seq_len.saturating_sub(w),
None => 0,
};
attn_out.fill(0.0);
for h in 0..num_heads {
let kv_h = h / heads_per_kv;
let q_start = h * head_dim;
let q_head = &q_buf[q_start..q_start + head_dim];
let k_offset = kv_h * head_dim;
let window_len = seq_len - attn_start;
let mut scores = Vec::with_capacity(window_len);
for t in attn_start..seq_len {
let k_cached = kv_cache.key_at(layer_idx, t);
let mut score = 0.0f32;
for d in 0..head_dim {
score += q_head[d] * k_cached[k_offset + d];
}
score *= inv_sqrt_d;
if let Some(cap) = attn_logit_softcap {
score = cap * (score / cap).tanh();
}
scores.push(score);
}
let max_score = scores.iter().copied().fold(f32::NEG_INFINITY, f32::max);
let mut sum = 0.0f32;
for s in &mut scores {
*s = (*s - max_score).exp();
sum += *s;
}
if sum > 0.0 {
let inv_sum = 1.0 / sum;
for s in &mut scores {
*s *= inv_sum;
}
}
for (si, t) in (attn_start..seq_len).enumerate() {
let v_cached = kv_cache.value_at(layer_idx, t);
let v_offset = kv_h * head_dim;
let w = scores[si];
for d in 0..head_dim {
attn_out[q_start + d] += w * v_cached[v_offset + d];
}
}
}
}
#[inline]
fn silu(x: f32) -> f32 {
x / (1.0 + (-x).exp())
}
#[inline]
fn softplus(x: f32) -> f32 {
if x > 20.0 {
x
} else if x < -20.0 {
x.exp()
} else {
x.exp().ln_1p()
}
}
#[inline]
fn sigmoid(x: f32) -> f32 {
1.0 / (1.0 + (-x).exp())
}
#[inline]
fn dump_slice(name: &str, s: &[f32], head_n: usize) {
let sum: f64 = s.iter().map(|&v| v as f64).sum();
let head: Vec<String> = s.iter().take(head_n).map(|v| format!("{v:.6}")).collect();
let tail: Vec<String> = s
.iter()
.rev()
.take(head_n)
.map(|v| format!("{v:.6}"))
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect();
eprintln!(
"DN0 {name} len={} head=[{}] tail=[{}] sum={sum:.6}",
s.len(),
head.join(","),
tail.join(","),
);
}
fn causal_conv1d_step(
x: &[f32],
state: &mut [f32],
ring_pos: &mut usize,
weight: &[f32],
bias: &[f32],
out: &mut [f32],
dim: usize,
kernel_size: usize,
) {
debug_assert_eq!(x.len(), dim);
debug_assert_eq!(out.len(), dim);
debug_assert_eq!(bias.len(), dim);
debug_assert_eq!(weight.len(), kernel_size * dim);
let ring = kernel_size - 1;
debug_assert_eq!(state.len(), ring * dim);
debug_assert!(kernel_size >= 2, "kernel_size must be at least 2");
let rp = *ring_pos;
for d in 0..dim {
let mut acc = bias[d];
let w_base = d * kernel_size;
for k in 0..(kernel_size - 1) {
let hist = state[((rp + 1 + k) % ring) * dim + d];
acc += weight[w_base + k] * hist;
}
acc += weight[w_base + (kernel_size - 1)] * x[d];
out[d] = acc;
}
let write_slot = (rp + 1) % ring;
for d in 0..dim {
state[write_slot * dim + d] = x[d];
}
*ring_pos = write_slot;
}
#[allow(clippy::too_many_arguments)]
fn gated_deltanet_step(
q: &[f32],
k: &[f32],
v: &[f32],
alpha: &[f32],
beta: &[f32],
z: &[f32],
state: &mut [f32],
out: &mut [f32],
num_kv_heads: usize,
num_v_heads: usize,
qk_dim: usize,
v_dim: usize,
bonsai_semantics: bool,
) {
debug_assert!(num_kv_heads > 0);
debug_assert!(num_v_heads > 0);
debug_assert_eq!(
num_v_heads % num_kv_heads,
0,
"num_v_heads ({num_v_heads}) must be a multiple of num_kv_heads ({num_kv_heads})",
);
debug_assert_eq!(q.len(), num_kv_heads * qk_dim);
debug_assert_eq!(k.len(), num_kv_heads * qk_dim);
debug_assert_eq!(v.len(), num_v_heads * v_dim);
debug_assert_eq!(alpha.len(), num_v_heads);
debug_assert_eq!(beta.len(), num_v_heads);
debug_assert_eq!(z.len(), num_v_heads * v_dim);
debug_assert_eq!(state.len(), num_v_heads * qk_dim * v_dim);
debug_assert_eq!(out.len(), num_v_heads * v_dim);
#[cfg(feature = "parallel")]
{
if num_v_heads >= 8 {
gated_deltanet_step_parallel(
q,
k,
v,
alpha,
beta,
z,
state,
out,
num_kv_heads,
num_v_heads,
qk_dim,
v_dim,
bonsai_semantics,
);
return;
}
}
for v_head in 0..num_v_heads {
let kv_head = v_head % num_kv_heads;
let q_off = kv_head * qk_dim;
let k_off = kv_head * qk_dim;
let v_off = v_head * v_dim;
let z_off = v_head * v_dim;
let s_off = v_head * qk_dim * v_dim;
gated_deltanet_head_disjoint(
&q[q_off..q_off + qk_dim],
&k[k_off..k_off + qk_dim],
&v[v_off..v_off + v_dim],
alpha[v_head],
beta[v_head],
&z[z_off..z_off + v_dim],
&mut state[s_off..s_off + qk_dim * v_dim],
&mut out[v_off..v_off + v_dim],
qk_dim,
v_dim,
bonsai_semantics,
);
}
}
#[cfg(feature = "parallel")]
#[allow(clippy::too_many_arguments)]
fn gated_deltanet_step_parallel(
q: &[f32],
k: &[f32],
v: &[f32],
alpha: &[f32],
beta: &[f32],
z: &[f32],
state: &mut [f32],
out: &mut [f32],
num_kv_heads: usize,
_num_v_heads: usize,
qk_dim: usize,
v_dim: usize,
bonsai_semantics: bool,
) {
use rayon::iter::{IndexedParallelIterator, ParallelIterator};
use rayon::slice::ParallelSliceMut;
let state_stride = qk_dim * v_dim;
state
.par_chunks_mut(state_stride)
.zip(out.par_chunks_mut(v_dim))
.enumerate()
.for_each(|(v_head, (state_slab, out_slab))| {
let kv_head = v_head % num_kv_heads;
gated_deltanet_head_disjoint(
&q[kv_head * qk_dim..(kv_head + 1) * qk_dim],
&k[kv_head * qk_dim..(kv_head + 1) * qk_dim],
&v[v_head * v_dim..(v_head + 1) * v_dim],
alpha[v_head],
beta[v_head],
&z[v_head * v_dim..(v_head + 1) * v_dim],
state_slab,
out_slab,
qk_dim,
v_dim,
bonsai_semantics,
);
});
}
#[cfg(test)]
#[allow(clippy::too_many_arguments)]
fn gated_deltanet_head(
q: &[f32],
k: &[f32],
v: &[f32],
alpha: &[f32],
beta: &[f32],
z: &[f32],
state: &mut [f32],
out: &mut [f32],
head: usize,
qk_dim: usize,
v_dim: usize,
) {
let q_off = head * qk_dim;
let k_off = head * qk_dim;
let v_off = head * v_dim;
let z_off = head * v_dim;
let s_off = head * qk_dim * v_dim;
gated_deltanet_head_disjoint(
&q[q_off..q_off + qk_dim],
&k[k_off..k_off + qk_dim],
&v[v_off..v_off + v_dim],
alpha[head],
beta[head],
&z[z_off..z_off + v_dim],
&mut state[s_off..s_off + qk_dim * v_dim],
&mut out[v_off..v_off + v_dim],
qk_dim,
v_dim,
false,
);
}
#[allow(clippy::too_many_arguments)]
fn gated_deltanet_head_disjoint(
q: &[f32],
k: &[f32],
v: &[f32],
alpha_h: f32,
beta_h: f32,
z: &[f32],
state: &mut [f32],
out: &mut [f32],
qk_dim: usize,
v_dim: usize,
bonsai_semantics: bool,
) {
let mut q_sum_sq = 0.0f32;
for &val in q {
q_sum_sq += val * val;
}
let q_scale = 1.0 / (qk_dim as f32).sqrt();
let q_norm = q_scale / q_sum_sq.sqrt().max(1e-12);
let mut k_sum_sq = 0.0f32;
for &val in k {
k_sum_sq += val * val;
}
let k_norm = 1.0 / k_sum_sq.sqrt().max(1e-12);
for j in 0..v_dim {
let mut st_k = 0.0f32;
for i in 0..qk_dim {
let k_i = if bonsai_semantics {
k[i] * k_norm
} else {
silu(k[i]) * k_norm
};
st_k += state[i * v_dim + j] * k_i;
}
let error_j = v[j] - alpha_h * st_k;
let mut out_j = 0.0f32;
for i in 0..qk_dim {
let (k_i, q_i) = if bonsai_semantics {
(k[i] * k_norm, q[i] * q_norm)
} else {
(silu(k[i]) * k_norm, silu(q[i]) * q_norm)
};
let idx = i * v_dim + j;
let s_new = alpha_h * state[idx] + beta_h * k_i * error_j;
state[idx] = s_new;
out_j += s_new * q_i;
}
out[j] = if bonsai_semantics {
out_j
} else {
out_j * silu(z[j])
};
}
}
#[inline]
fn gelu_approx(x: f32) -> f32 {
const SQRT_2_OVER_PI: f32 = 0.797_884_6;
0.5 * x * (1.0 + (SQRT_2_OVER_PI * (x + 0.044_715 * x * x * x)).tanh())
}
#[derive(Clone)]
struct WeightRef<'a> {
data: &'a [u8],
qtype: GgmlType,
rows: usize,
cols: usize,
}
impl WeightRef<'_> {
fn matvec(&self, input: &[f32], output: &mut [f32]) {
quantized_matvec(input, self.data, self.qtype, self.rows, self.cols, output);
}
fn matvec_preq(&self, q8_blocks: &[BlockQ8K], output: &mut [f32]) {
quantized_matvec_preq(
self.data, self.qtype, self.rows, self.cols, q8_blocks, output,
);
}
fn dequantize_all(&self, rows: usize, cols: usize) -> Vec<f32> {
let mut out = vec![0.0f32; rows * cols];
let mut row_buf = vec![0.0f32; cols];
let elems_per_block = self.qtype.elements_per_block();
let bytes_per_block = self.qtype.block_bytes();
let blocks_per_row = cols / elems_per_block;
let row_bytes = blocks_per_row * bytes_per_block;
for r in 0..rows {
let row_data = &self.data[r * row_bytes..(r + 1) * row_bytes];
crate::gguf::dequantize_weight_row(row_data, self.qtype, &mut row_buf);
out[r * cols..(r + 1) * cols].copy_from_slice(&row_buf);
}
out
}
}
#[allow(clippy::struct_field_names)]
struct QwenAttentionBiases {
q_bias: Vec<f32>,
k_bias: Vec<f32>,
v_bias: Vec<f32>,
}
struct QwenAttentionNorms {
q_norm: Vec<f32>,
k_norm: Vec<f32>,
}
struct Gemma3nLayerAugmentations<'a> {
post_norm: Vec<f32>,
inp_gate: WeightRef<'a>,
proj: WeightRef<'a>,
laurel_l: Vec<f32>,
laurel_r: Vec<f32>,
laurel_post_norm: Vec<f32>,
altup_router: Vec<f32>,
altup_router_norm: Vec<f32>,
altup_predict_coef: Vec<f32>,
altup_correct_coef: Vec<f32>,
altup_correct_scale: Vec<f32>,
}
#[allow(clippy::struct_field_names)]
struct MoeExpertWeights<'a> {
ffn_gate_inp: Vec<f32>,
ffn_gate_exps: WeightRef<'a>,
ffn_up_exps: WeightRef<'a>,
ffn_down_exps: WeightRef<'a>,
}
struct LayerWeights<'a> {
attn_norm: Vec<f32>,
q_proj: WeightRef<'a>,
k_proj: Option<WeightRef<'a>>,
v_proj: Option<WeightRef<'a>>,
o_proj: WeightRef<'a>,
ffn_norm: Vec<f32>,
gate_proj: Option<WeightRef<'a>>,
up_proj: Option<WeightRef<'a>>,
down_proj: Option<WeightRef<'a>>,
post_attn_norm: Option<Vec<f32>>,
post_ffn_norm: Option<Vec<f32>>,
out_scale: Option<Vec<f32>>,
rope_freqs: Option<Vec<f32>>,
qwen_biases: Option<QwenAttentionBiases>,
qwen_norms: Option<QwenAttentionNorms>,
gemma3n: Option<Gemma3nLayerAugmentations<'a>>,
moe: Option<MoeExpertWeights<'a>>,
gated_output: bool,
}
impl<'a> LayerWeights<'a> {
#[inline]
fn q_bias(&self) -> Option<&[f32]> {
self.qwen_biases.as_ref().map(|b| b.q_bias.as_slice())
}
#[inline]
fn k_bias(&self) -> Option<&[f32]> {
self.qwen_biases.as_ref().map(|b| b.k_bias.as_slice())
}
#[inline]
fn v_bias(&self) -> Option<&[f32]> {
self.qwen_biases.as_ref().map(|b| b.v_bias.as_slice())
}
#[inline]
fn q_norm(&self) -> Option<&[f32]> {
self.qwen_norms.as_ref().map(|n| n.q_norm.as_slice())
}
#[inline]
fn k_norm(&self) -> Option<&[f32]> {
self.qwen_norms.as_ref().map(|n| n.k_norm.as_slice())
}
#[inline]
fn post_norm(&self) -> Option<&[f32]> {
self.gemma3n.as_ref().map(|g| g.post_norm.as_slice())
}
#[inline]
fn inp_gate(&self) -> Option<&WeightRef<'a>> {
self.gemma3n.as_ref().map(|g| &g.inp_gate)
}
#[inline]
fn proj(&self) -> Option<&WeightRef<'a>> {
self.gemma3n.as_ref().map(|g| &g.proj)
}
#[inline]
fn laurel_l(&self) -> Option<&[f32]> {
self.gemma3n.as_ref().map(|g| g.laurel_l.as_slice())
}
#[inline]
fn laurel_r(&self) -> Option<&[f32]> {
self.gemma3n.as_ref().map(|g| g.laurel_r.as_slice())
}
#[inline]
fn laurel_post_norm(&self) -> Option<&[f32]> {
self.gemma3n.as_ref().map(|g| g.laurel_post_norm.as_slice())
}
#[inline]
fn altup_router(&self) -> Option<&[f32]> {
self.gemma3n.as_ref().map(|g| g.altup_router.as_slice())
}
#[inline]
fn altup_router_norm(&self) -> Option<&[f32]> {
self.gemma3n
.as_ref()
.map(|g| g.altup_router_norm.as_slice())
}
#[inline]
fn altup_predict_coef(&self) -> Option<&[f32]> {
self.gemma3n
.as_ref()
.map(|g| g.altup_predict_coef.as_slice())
}
#[inline]
fn altup_correct_coef(&self) -> Option<&[f32]> {
self.gemma3n
.as_ref()
.map(|g| g.altup_correct_coef.as_slice())
}
#[inline]
fn altup_correct_scale(&self) -> Option<&[f32]> {
self.gemma3n
.as_ref()
.map(|g| g.altup_correct_scale.as_slice())
}
#[inline]
fn ffn_gate_inp(&self) -> Option<&[f32]> {
self.moe.as_ref().map(|m| m.ffn_gate_inp.as_slice())
}
#[inline]
fn ffn_gate_exps(&self) -> Option<&WeightRef<'a>> {
self.moe.as_ref().map(|m| &m.ffn_gate_exps)
}
#[inline]
fn ffn_up_exps(&self) -> Option<&WeightRef<'a>> {
self.moe.as_ref().map(|m| &m.ffn_up_exps)
}
#[inline]
fn ffn_down_exps(&self) -> Option<&WeightRef<'a>> {
self.moe.as_ref().map(|m| &m.ffn_down_exps)
}
#[inline]
#[allow(dead_code)]
fn is_moe_layer(&self) -> bool {
self.moe.is_some()
}
}
struct DeepSeekV3LayerWeights<'a> {
attn_norm: Vec<f32>,
q: DeepSeekQProjection<'a>,
kv_a_proj_with_mqa: WeightRef<'a>,
kv_a_norm: Vec<f32>,
kv_b_proj: WeightRef<'a>,
o_proj: WeightRef<'a>,
ffn_norm: Option<Vec<f32>>,
gate_proj: Option<WeightRef<'a>>,
up_proj: Option<WeightRef<'a>>,
down_proj: Option<WeightRef<'a>>,
moe: Option<DeepSeekMoeWeights<'a>>,
}
enum DeepSeekQProjection<'a> {
Dense { q_proj: WeightRef<'a> },
LoRA {
q_a_proj: WeightRef<'a>,
q_a_norm: Vec<f32>,
q_b_proj: WeightRef<'a>,
},
}
struct DeepSeekMoeWeights<'a> {
ffn_norm: Vec<f32>,
ffn_gate_inp: Vec<f32>,
exp_probs_b: Option<Vec<f32>>,
routed: RoutedExpertStorage<'a>,
ffn_gate_shexp: WeightRef<'a>,
ffn_up_shexp: WeightRef<'a>,
ffn_down_shexp: WeightRef<'a>,
}
#[allow(dead_code)]
struct KimiK3MlaAttn<'a> {
q_a: WeightRef<'a>,
q_a_norm: Vec<f32>,
q_b: WeightRef<'a>,
kv_a_mqa: WeightRef<'a>,
kv_a_norm: Vec<f32>,
k_b: WeightRef<'a>,
v_b: WeightRef<'a>,
}
#[allow(dead_code)]
struct KimiK3KdaAttn<'a> {
q: WeightRef<'a>,
k: WeightRef<'a>,
v: WeightRef<'a>,
ssm_conv1d_q: WeightRef<'a>,
ssm_conv1d_k: WeightRef<'a>,
ssm_conv1d_v: WeightRef<'a>,
ssm_f_a: WeightRef<'a>,
ssm_f_b: WeightRef<'a>,
ssm_beta: WeightRef<'a>,
ssm_norm: Vec<f32>,
ssm_dt_bias: Option<Vec<f32>>,
ssm_g: Option<WeightRef<'a>>,
ssm_a: Option<Vec<f32>>,
}
#[allow(dead_code)]
#[allow(clippy::large_enum_variant)]
enum KimiK3Attention<'a> {
Mla(KimiK3MlaAttn<'a>),
Kda(KimiK3KdaAttn<'a>),
}
#[allow(dead_code)]
struct KimiK3LatentMoe<'a> {
ffn_gate_inp: Vec<f32>,
exp_probs_b: Option<Vec<f32>>,
ffn_gate_shexp: WeightRef<'a>,
ffn_up_shexp: WeightRef<'a>,
ffn_down_shexp: WeightRef<'a>,
routed_exp_up: WeightRef<'a>,
routed_exp_down: WeightRef<'a>,
routed_exp_norm: Vec<f32>,
ffn_gate_exps: WeightRef<'a>,
ffn_up_exps: WeightRef<'a>,
ffn_down_exps: WeightRef<'a>,
}
#[allow(dead_code, clippy::large_enum_variant)]
enum KimiK3Ffn<'a> {
Dense {
gate: WeightRef<'a>,
up: WeightRef<'a>,
down: WeightRef<'a>,
},
LatentMoe(KimiK3LatentMoe<'a>),
}
#[allow(dead_code)]
struct KimiK3LayerWeights<'a> {
attn_norm: Vec<f32>,
ffn_norm: Vec<f32>,
attn_output: WeightRef<'a>,
attn_gate: WeightRef<'a>,
attn_res_score: Vec<f32>,
ffn_res_score: Vec<f32>,
attn: KimiK3Attention<'a>,
ffn: KimiK3Ffn<'a>,
}
#[allow(dead_code)]
pub struct KimiK3ModelWeights<'a> {
token_embd: WeightRef<'a>,
output_norm: Vec<f32>,
output: WeightRef<'a>,
output_res_score: Vec<f32>,
layers: Vec<KimiK3LayerWeights<'a>>,
}
#[allow(dead_code)]
fn load_kimi_k3_layer_weights<'a, G: crate::gguf::GgufSource<'a>>(
gguf: &'a G,
il: usize,
config: &Llama3Config,
) -> Result<KimiK3LayerWeights<'a>, String> {
let prefix = format!("blk.{il}");
let kd = config
.kimi_delta
.as_ref()
.ok_or_else(|| format!("layer {il}: kimi_delta sub-config not populated"))?;
let load_ref = |name: &str| -> Result<WeightRef<'a>, String> {
load_weight_ref_any_shape(gguf, name)
.ok_or_else(|| format!("layer {il}: missing tensor '{name}'"))
};
let load_norm = |name: &str| -> Result<Vec<f32>, String> {
gguf.tensor_to_f32(name)
.ok_or_else(|| format!("layer {il}: missing norm tensor '{name}'"))
};
let attn_norm = load_norm(&format!("{prefix}.attn_norm.weight"))?;
let ffn_norm = load_norm(&format!("{prefix}.ffn_norm.weight"))?;
let attn_output = load_ref(&format!("{prefix}.attn_output.weight"))?;
let attn_res_score = load_norm(&format!("{prefix}.attn_res_score.weight"))?;
let ffn_res_score = load_norm(&format!("{prefix}.ffn_res_score.weight"))?;
let is_mla = match kd.is_mla_layer(il) {
Some(v) => v,
None => gguf
.tensor_info(&format!("{prefix}.attn_q_a.weight"))
.is_some(),
};
let attn = if is_mla {
let _attn_gate_mla: Option<WeightRef<'a>> =
load_weight_ref_any_shape(gguf, &format!("{prefix}.attn_gate.weight"));
KimiK3Attention::Mla(KimiK3MlaAttn {
q_a: load_ref(&format!("{prefix}.attn_q_a.weight"))?,
q_a_norm: load_norm(&format!("{prefix}.attn_q_a_norm.weight"))?,
q_b: load_ref(&format!("{prefix}.attn_q_b.weight"))?,
kv_a_mqa: load_ref(&format!("{prefix}.attn_kv_a_mqa.weight"))?,
kv_a_norm: load_norm(&format!("{prefix}.attn_kv_a_norm.weight"))?,
k_b: load_ref(&format!("{prefix}.attn_k_b.weight"))?,
v_b: load_ref(&format!("{prefix}.attn_v_b.weight"))?,
})
} else {
let ssm_dt_bias = gguf.tensor_to_f32(&format!("{prefix}.ssm_dt.bias"));
let ssm_g = load_weight_ref_any_shape(gguf, &format!("{prefix}.ssm_g.weight"));
let ssm_a = gguf
.tensor_to_f32(&format!("{prefix}.ssm_a"))
.or_else(|| gguf.tensor_to_f32(&format!("{prefix}.ssm_a.weight")));
KimiK3Attention::Kda(KimiK3KdaAttn {
q: load_ref(&format!("{prefix}.attn_q.weight"))?,
k: load_ref(&format!("{prefix}.attn_k.weight"))?,
v: load_ref(&format!("{prefix}.attn_v.weight"))?,
ssm_conv1d_q: load_ref(&format!("{prefix}.ssm_conv1d_q.weight"))?,
ssm_conv1d_k: load_ref(&format!("{prefix}.ssm_conv1d_k.weight"))?,
ssm_conv1d_v: load_ref(&format!("{prefix}.ssm_conv1d_v.weight"))?,
ssm_f_a: load_ref(&format!("{prefix}.ssm_f_a.weight"))?,
ssm_f_b: load_ref(&format!("{prefix}.ssm_f_b.weight"))?,
ssm_beta: load_ref(&format!("{prefix}.ssm_beta.weight"))?,
ssm_norm: load_norm(&format!("{prefix}.ssm_norm.weight"))?,
ssm_dt_bias,
ssm_g,
ssm_a,
})
};
let attn_gate = load_weight_ref_any_shape(gguf, &format!("{prefix}.attn_gate.weight"))
.unwrap_or_else(|| {
let tok_bytes = gguf
.tensor_data(&format!("{prefix}.attn_norm.weight"))
.unwrap_or(&[0u8; 4]);
WeightRef {
data: &tok_bytes[..4.min(tok_bytes.len())],
qtype: GgmlType::F32,
rows: 1,
cols: 1,
}
});
let first_k_dense = kd.first_k_dense_replace.unwrap_or(0);
let ffn = if il < first_k_dense {
KimiK3Ffn::Dense {
gate: load_ref(&format!("{prefix}.ffn_gate.weight"))?,
up: load_ref(&format!("{prefix}.ffn_up.weight"))?,
down: load_ref(&format!("{prefix}.ffn_down.weight"))?,
}
} else {
let ffn_gate_inp = load_norm(&format!("{prefix}.ffn_gate_inp.weight"))?;
let exp_probs_b = gguf.tensor_to_f32(&format!("{prefix}.exp_probs_b.bias"));
let load_ref_any = |names: &[String]| -> Result<WeightRef<'a>, String> {
for n in names {
if let Some(w) = load_weight_ref_any_shape(gguf, n) {
return Ok(w);
}
}
Err(format!("layer {il}: no tensor found among {names:?}"))
};
let load_norm_any = |names: &[String]| -> Result<Vec<f32>, String> {
for n in names {
if let Some(v) = gguf.tensor_to_f32(n) {
return Ok(v);
}
}
Err(format!("layer {il}: no norm tensor found among {names:?}"))
};
KimiK3Ffn::LatentMoe(KimiK3LatentMoe {
ffn_gate_inp,
exp_probs_b,
ffn_gate_shexp: load_ref(&format!("{prefix}.ffn_gate_shexp.weight"))?,
ffn_up_shexp: load_ref(&format!("{prefix}.ffn_up_shexp.weight"))?,
ffn_down_shexp: load_ref(&format!("{prefix}.ffn_down_shexp.weight"))?,
routed_exp_up: load_ref_any(&[
format!("{prefix}.routed_exp_up.weight"),
format!("{prefix}.ffn_routed_up.weight"),
])?,
routed_exp_down: load_ref_any(&[
format!("{prefix}.routed_exp_down.weight"),
format!("{prefix}.ffn_routed_down.weight"),
])?,
routed_exp_norm: load_norm_any(&[
format!("{prefix}.routed_exp_norm.weight"),
format!("{prefix}.ffn_routed_norm.weight"),
])?,
ffn_gate_exps: load_ref(&format!("{prefix}.ffn_gate_exps.weight"))?,
ffn_up_exps: load_ref(&format!("{prefix}.ffn_up_exps.weight"))?,
ffn_down_exps: load_ref(&format!("{prefix}.ffn_down_exps.weight"))?,
})
};
Ok(KimiK3LayerWeights {
attn_norm,
ffn_norm,
attn_output,
attn_gate,
attn_res_score,
ffn_res_score,
attn,
ffn,
})
}
#[allow(dead_code)]
pub fn load_kimi_k3_model_weights<'a, G: crate::gguf::GgufSource<'a>>(
gguf: &'a G,
config: &Llama3Config,
) -> Result<KimiK3ModelWeights<'a>, String> {
let load_ref = |name: &str| -> Result<WeightRef<'a>, String> {
load_weight_ref_any_shape(gguf, name)
.ok_or_else(|| format!("global: missing tensor '{name}'"))
};
let load_norm = |name: &str| -> Result<Vec<f32>, String> {
gguf.tensor_to_f32(name)
.ok_or_else(|| format!("global: missing norm tensor '{name}'"))
};
let token_embd = load_ref("token_embd.weight")?;
let output_norm = load_norm("output_norm.weight")?;
let output = load_ref("output.weight")?;
let output_res_score = load_norm("output_res_score.weight")?;
let mut layers = Vec::with_capacity(config.num_layers);
for il in 0..config.num_layers {
layers.push(load_kimi_k3_layer_weights(gguf, il, config)?);
}
Ok(KimiK3ModelWeights {
token_embd,
output_norm,
output,
output_res_score,
layers,
})
}
#[allow(dead_code)]
#[derive(Clone)]
pub struct KimiK3MlaCache {
c_k: Vec<f32>,
k_rope: Vec<f32>,
n_positions: usize,
kv_lora_rank: usize,
qk_rope_head_dim: usize,
}
impl KimiK3MlaCache {
#[must_use]
pub fn new(kv_lora_rank: usize, qk_rope_head_dim: usize, capacity: usize) -> Self {
Self {
c_k: Vec::with_capacity(capacity * kv_lora_rank),
k_rope: Vec::with_capacity(capacity * qk_rope_head_dim),
n_positions: 0,
kv_lora_rank,
qk_rope_head_dim,
}
}
pub fn reset(&mut self) {
self.c_k.clear();
self.k_rope.clear();
self.n_positions = 0;
}
pub fn rollback_to(&mut self, pos: usize) {
assert!(
pos <= self.n_positions,
"KimiK3MlaCache::rollback_to({pos}) exceeds n_positions {}",
self.n_positions
);
self.c_k.truncate(pos * self.kv_lora_rank);
self.k_rope.truncate(pos * self.qk_rope_head_dim);
self.n_positions = pos;
}
#[inline]
#[must_use]
pub const fn n_positions(&self) -> usize {
self.n_positions
}
fn append(&mut self, c_k: &[f32], k_rope: &[f32]) {
assert_eq!(c_k.len(), self.kv_lora_rank);
assert_eq!(k_rope.len(), self.qk_rope_head_dim);
self.c_k.extend_from_slice(c_k);
self.k_rope.extend_from_slice(k_rope);
self.n_positions += 1;
}
fn c_k_at(&self, pos: usize) -> &[f32] {
let base = pos * self.kv_lora_rank;
&self.c_k[base..base + self.kv_lora_rank]
}
fn k_rope_at(&self, pos: usize) -> &[f32] {
let base = pos * self.qk_rope_head_dim;
&self.k_rope[base..base + self.qk_rope_head_dim]
}
}
#[allow(dead_code)]
pub struct KimiK3MlaConfig {
pub d: usize,
pub num_heads: usize,
pub qk_nope_head_dim: usize,
pub qk_rope_head_dim: usize,
pub v_head_dim: usize,
pub q_lora_rank: usize,
pub kv_lora_rank: usize,
pub rms_eps: f32,
}
#[inline]
fn kimi_k3_matvec_ref(w: &WeightRef<'_>, x: &[f32]) -> Vec<f32> {
let mut out = vec![0.0_f32; w.rows];
w.matvec(x, &mut out);
out
}
fn kimi_k3_rms_norm(x: &[f32], gamma: &[f32], eps: f32) -> Vec<f32> {
debug_assert_eq!(x.len(), gamma.len());
let mut out = vec![0.0_f32; x.len()];
rms_norm(x, gamma, eps, &mut out);
out
}
#[allow(dead_code)]
fn kimi_k3_gated_mla_step(
x: &[f32],
attn_norm: &[f32],
attn_gate: &WeightRef<'_>,
attn_output: &WeightRef<'_>,
mla: &KimiK3MlaAttn<'_>,
cache: &mut KimiK3MlaCache,
config: &KimiK3MlaConfig,
) -> Vec<f32> {
let d = config.d;
let h = config.num_heads;
let qk_nope = config.qk_nope_head_dim;
let qk_rope = config.qk_rope_head_dim;
let qk_head = qk_nope + qk_rope;
let v_head = config.v_head_dim;
let kv_lr = config.kv_lora_rank;
assert_eq!(x.len(), d, "x length must equal hidden dim d");
assert_eq!(cache.kv_lora_rank, kv_lr, "cache.kv_lora_rank mismatch");
assert_eq!(
cache.qk_rope_head_dim, qk_rope,
"cache.qk_rope_head_dim mismatch"
);
let x_norm = kimi_k3_rms_norm(x, attn_norm, config.rms_eps);
let q_latent = kimi_k3_matvec_ref(&mla.q_a, &x_norm);
let q_latent_norm = kimi_k3_rms_norm(&q_latent, &mla.q_a_norm, config.rms_eps);
let q_full = kimi_k3_matvec_ref(&mla.q_b, &q_latent_norm);
debug_assert_eq!(q_full.len(), h * qk_head, "q_full length");
let kv_a_out = kimi_k3_matvec_ref(&mla.kv_a_mqa, &x_norm);
debug_assert_eq!(kv_a_out.len(), kv_lr + qk_rope);
let c_k_slice = &kv_a_out[..kv_lr];
let k_rope_slice = &kv_a_out[kv_lr..];
let c_k_norm = kimi_k3_rms_norm(c_k_slice, &mla.kv_a_norm, config.rms_eps);
cache.append(&c_k_norm, k_rope_slice);
let n_pos = cache.n_positions;
let scale = (qk_head as f32).sqrt().recip();
let mut concat_out = vec![0.0_f32; h * v_head];
let mut k_all = vec![0.0_f32; n_pos * h * qk_head];
let mut v_all = vec![0.0_f32; n_pos * h * v_head];
for pos in 0..n_pos {
let c_k_i = cache.c_k_at(pos);
let k_rope_i = cache.k_rope_at(pos);
let k_nope_reconstructed = kimi_k3_matvec_ref(&mla.k_b, c_k_i);
let v_reconstructed = kimi_k3_matvec_ref(&mla.v_b, c_k_i);
debug_assert_eq!(k_nope_reconstructed.len(), h * qk_nope);
debug_assert_eq!(v_reconstructed.len(), h * v_head);
for head in 0..h {
let k_dst_base = pos * h * qk_head + head * qk_head;
let k_nope_src = head * qk_nope;
k_all[k_dst_base..k_dst_base + qk_nope]
.copy_from_slice(&k_nope_reconstructed[k_nope_src..k_nope_src + qk_nope]);
k_all[k_dst_base + qk_nope..k_dst_base + qk_head].copy_from_slice(k_rope_i);
let v_dst_base = pos * h * v_head + head * v_head;
let v_src = head * v_head;
v_all[v_dst_base..v_dst_base + v_head]
.copy_from_slice(&v_reconstructed[v_src..v_src + v_head]);
}
}
let mut logits_buf = vec![0.0_f32; n_pos];
for head in 0..h {
let q_head_start = head * qk_head;
let q_head = &q_full[q_head_start..q_head_start + qk_head];
for pos in 0..n_pos {
let k_start = pos * h * qk_head + head * qk_head;
let k_head = &k_all[k_start..k_start + qk_head];
let mut dot = 0.0_f64;
for j in 0..qk_head {
dot += f64::from(q_head[j]) * f64::from(k_head[j]);
}
logits_buf[pos] = (dot as f32) * scale;
}
let max_logit = logits_buf.iter().copied().fold(f32::NEG_INFINITY, f32::max);
let mut sum_exp = 0.0_f64;
for l in &mut logits_buf {
*l = (*l - max_logit).exp();
sum_exp += f64::from(*l);
}
let inv_sum = (sum_exp as f32).recip();
let out_start = head * v_head;
for j in 0..v_head {
concat_out[out_start + j] = 0.0;
}
for pos in 0..n_pos {
let alpha = logits_buf[pos] * inv_sum;
let v_start = pos * h * v_head + head * v_head;
for j in 0..v_head {
concat_out[out_start + j] += alpha * v_all[v_start + j];
}
}
}
let attn_out = kimi_k3_matvec_ref(attn_output, &concat_out);
debug_assert_eq!(attn_out.len(), d);
let gate_pre = kimi_k3_matvec_ref(attn_gate, &x_norm);
debug_assert_eq!(gate_pre.len(), d);
let mut y = vec![0.0_f32; d];
for i in 0..d {
y[i] = sigmoid(gate_pre[i]) * attn_out[i];
}
y
}
#[allow(dead_code)]
fn kimi_k3_extract_mla_config(config: &Llama3Config) -> Option<KimiK3MlaConfig> {
let kd = config.kimi_delta.as_ref()?;
Some(KimiK3MlaConfig {
d: config.hidden_dim,
num_heads: config.num_heads,
qk_nope_head_dim: kd.qk_nope_head_dim?,
qk_rope_head_dim: kd.qk_rope_head_dim?,
v_head_dim: kd.v_head_dim?,
q_lora_rank: kd.q_lora_rank?,
kv_lora_rank: kd.kv_lora_rank?,
rms_eps: config.norm_eps,
})
}
#[allow(dead_code)]
fn kimi_k3_dense_ffn_forward(
x: &[f32],
ffn_norm: &[f32],
gate: &WeightRef<'_>,
up: &WeightRef<'_>,
down: &WeightRef<'_>,
rms_eps: f32,
) -> Vec<f32> {
let d = x.len();
debug_assert_eq!(ffn_norm.len(), d);
debug_assert_eq!(down.rows, d, "down.rows must equal hidden dim");
let intermediate = gate.rows;
debug_assert_eq!(
up.rows, intermediate,
"gate/up must have same intermediate dim"
);
debug_assert_eq!(
down.cols, intermediate,
"down.cols must equal intermediate dim"
);
let x_norm = kimi_k3_rms_norm(x, ffn_norm, rms_eps);
let mut gate_out = vec![0.0_f32; intermediate];
gate.matvec(&x_norm, &mut gate_out);
let mut up_out = vec![0.0_f32; intermediate];
up.matvec(&x_norm, &mut up_out);
let mut gated = vec![0.0_f32; intermediate];
for i in 0..intermediate {
gated[i] = silu(gate_out[i]) * up_out[i];
}
let mut y = vec![0.0_f32; d];
down.matvec(&gated, &mut y);
y
}
#[allow(dead_code)]
#[derive(Clone)]
pub(crate) struct KimiK3AttnResState {
d: usize,
block_size: usize,
banked: Vec<Vec<f32>>,
}
#[allow(dead_code)]
impl KimiK3AttnResState {
pub(crate) fn new(hidden_dim: usize, block_size: usize) -> Self {
assert!(hidden_dim > 0, "hidden_dim must be > 0");
assert!(block_size > 0, "block_size must be > 0");
Self {
d: hidden_dim,
block_size,
banked: Vec::new(),
}
}
pub(crate) fn is_checkpoint_layer(&self, il: usize) -> bool {
il.is_multiple_of(self.block_size)
}
pub(crate) fn bank(&mut self, prefix_sum: &[f32]) {
assert_eq!(
prefix_sum.len(),
self.d,
"prefix_sum length {} must equal hidden dim {}",
prefix_sum.len(),
self.d
);
self.banked.push(prefix_sum.to_vec());
}
pub(crate) fn reset(&mut self) {
self.banked.clear();
}
#[cfg(test)]
pub(crate) fn banked_count(&self) -> usize {
self.banked.len()
}
}
#[allow(dead_code)]
pub(crate) fn kimi_k3_res_mix(
state: &KimiK3AttnResState,
prefix_sum: &[f32],
score_w: &[f32],
rms_eps: f32,
) -> Vec<f32> {
let d = state.d;
assert_eq!(prefix_sum.len(), d, "prefix_sum length must equal d");
assert_eq!(score_w.len(), d, "score_w length must equal d");
if state.banked.is_empty() {
return prefix_sum.to_vec();
}
let n_ckpt = state.banked.len();
let mut scores = Vec::with_capacity(n_ckpt + 1);
for bank in &state.banked {
let ss: f64 = bank.iter().map(|&v| f64::from(v) * f64::from(v)).sum();
let mean = (ss / d as f64) as f32;
let scale = (mean + rms_eps).sqrt().recip();
let mut s = 0.0_f64;
for j in 0..d {
s += f64::from(bank[j]) * f64::from(scale) * f64::from(score_w[j]);
}
scores.push(s as f32);
}
let ss: f64 = prefix_sum
.iter()
.map(|&v| f64::from(v) * f64::from(v))
.sum();
let mean = (ss / d as f64) as f32;
let scale_cur = (mean + rms_eps).sqrt().recip();
let mut s_cur = 0.0_f64;
for j in 0..d {
s_cur += f64::from(prefix_sum[j]) * f64::from(scale_cur) * f64::from(score_w[j]);
}
scores.push(s_cur as f32);
let max_s = scores.iter().copied().fold(f32::NEG_INFINITY, f32::max);
let mut exp_s = Vec::with_capacity(scores.len());
let mut sum_exp = 0.0_f64;
for &sc in &scores {
let e = (sc - max_s).exp();
exp_s.push(e);
sum_exp += f64::from(e);
}
let inv_sum = (sum_exp as f32).recip();
let mut out = vec![0.0_f32; d];
for (i, bank) in state.banked.iter().enumerate() {
let p = exp_s[i] * inv_sum;
for j in 0..d {
out[j] += p * bank[j];
}
}
let p_cur = exp_s[n_ckpt] * inv_sum;
for j in 0..d {
out[j] += p_cur * prefix_sum[j];
}
out
}
#[cfg(test)]
mod kimi_k3_attnres_tests {
use super::{kimi_k3_res_mix, KimiK3AttnResState};
#[test]
fn attnres_state_new_starts_empty() {
let state = KimiK3AttnResState::new(4, 12);
assert_eq!(state.banked_count(), 0);
}
#[test]
fn attnres_state_checkpoint_layer_predicate() {
let state = KimiK3AttnResState::new(4, 12);
assert!(state.is_checkpoint_layer(0));
assert!(!state.is_checkpoint_layer(1));
assert!(!state.is_checkpoint_layer(11));
assert!(state.is_checkpoint_layer(12));
assert!(state.is_checkpoint_layer(24));
assert!(!state.is_checkpoint_layer(23));
}
#[test]
fn attnres_state_bank_grows_banked_list() {
let mut state = KimiK3AttnResState::new(3, 12);
let inp1 = vec![1.0_f32, 2.0, 3.0];
state.bank(&inp1);
assert_eq!(state.banked_count(), 1);
let inp2 = vec![4.0_f32, 5.0, 6.0];
state.bank(&inp2);
assert_eq!(state.banked_count(), 2);
}
#[test]
fn attnres_state_reset_clears_banked() {
let mut state = KimiK3AttnResState::new(2, 12);
state.bank(&[1.0_f32, 2.0]);
assert_eq!(state.banked_count(), 1);
state.reset();
assert_eq!(state.banked_count(), 0);
}
#[test]
fn res_mix_identity_when_no_banked_ckpts() {
let state = KimiK3AttnResState::new(3, 12);
let prefix = vec![0.5_f32, -0.5, 0.25];
let score_w = vec![1.0_f32; 3];
let out = kimi_k3_res_mix(&state, &prefix, &score_w, 1e-6);
assert_eq!(out, prefix, "no banked → return prefix_sum unchanged");
}
#[test]
fn res_mix_with_one_bank_gives_convex_combination() {
let mut state = KimiK3AttnResState::new(3, 12);
let bank = vec![10.0_f32, 20.0, 30.0];
state.bank(&bank);
let prefix = vec![100.0_f32, 200.0, 300.0];
let score_w = vec![1.0_f32; 3];
let out = kimi_k3_res_mix(&state, &prefix, &score_w, 1e-6);
assert_eq!(out.len(), 3);
for i in 0..3 {
let lo = bank[i].min(prefix[i]);
let hi = bank[i].max(prefix[i]);
assert!(
out[i] >= lo - 1e-4 && out[i] <= hi + 1e-4,
"out[{i}]={} must lie in [{lo}, {hi}]",
out[i]
);
}
}
#[test]
fn res_mix_score_w_bias_shifts_weight_toward_matching_bank() {
let mut state = KimiK3AttnResState::new(3, 12);
let bank_orthogonal = vec![1.0_f32, 0.0, 0.0]; let bank_aligned = vec![0.0_f32, 1.0, 0.0]; state.bank(&bank_orthogonal);
state.bank(&bank_aligned);
let prefix = vec![1.0_f32, 0.0, 0.0];
let score_w = vec![0.0_f32, 1.0, 0.0];
let out = kimi_k3_res_mix(&state, &prefix, &score_w, 1e-6);
assert!(
out[1] > 0.5,
"aligned bank must dominate output y-component, got {}",
out[1]
);
}
#[test]
fn res_mix_deterministic_across_repeated_calls() {
let mut state = KimiK3AttnResState::new(4, 12);
state.bank(&[1.0_f32, 2.0, 3.0, 4.0]);
state.bank(&[5.0_f32, 6.0, 7.0, 8.0]);
let prefix = vec![0.1_f32, 0.2, 0.3, 0.4];
let score_w = vec![1.0_f32, -1.0, 1.0, -1.0];
let out1 = kimi_k3_res_mix(&state, &prefix, &score_w, 1e-6);
let out2 = kimi_k3_res_mix(&state, &prefix, &score_w, 1e-6);
assert_eq!(out1, out2, "res_mix must be deterministic");
}
}
#[allow(dead_code)]
fn kimi_k3_slice_weight_ref_rows<'a>(
w: &WeightRef<'a>,
row_start: usize,
row_end: usize,
) -> Option<WeightRef<'a>> {
if row_start > row_end || row_end > w.rows {
return None;
}
if matches!(w.qtype, GgmlType::Other(_)) {
return None;
}
let elements_per_block = w.qtype.elements_per_block();
let block_bytes = w.qtype.block_bytes();
if elements_per_block == 0 || block_bytes == 0 {
return None;
}
if !w.cols.is_multiple_of(elements_per_block) {
return None;
}
let blocks_per_row = w.cols / elements_per_block;
let row_bytes = blocks_per_row.checked_mul(block_bytes)?;
let byte_start = row_start.checked_mul(row_bytes)?;
let byte_end = row_end.checked_mul(row_bytes)?;
if byte_end > w.data.len() {
return None;
}
Some(WeightRef {
data: &w.data[byte_start..byte_end],
qtype: w.qtype,
rows: row_end - row_start,
cols: w.cols,
})
}
#[allow(dead_code, clippy::too_many_arguments)]
fn kimi_k3_kda_layer_forward(
x: &[f32],
attn_norm: &[f32],
attn_output: &WeightRef<'_>,
kda: &KimiK3KdaAttn<'_>,
caches: &mut [KimiDeltaHeadCache],
num_heads: usize,
head_dim: usize,
alpha_rank: usize,
g_min: f32,
rms_eps: f32,
capture_updates: Option<&mut [KimiDeltaHeadUpdate]>,
) -> Vec<f32> {
let d = x.len();
assert_eq!(attn_norm.len(), d, "attn_norm length must equal hidden dim");
assert_eq!(
caches.len(),
num_heads,
"caches length must equal num_heads"
);
let x_norm = kimi_k3_rms_norm(x, attn_norm, rms_eps);
let v_head_dim = head_dim; let mut concat_out = vec![0.0_f32; num_heads * v_head_dim];
let zero_bias = vec![0.0_f32; head_dim];
let identity_gate = identity_matrix_f32(v_head_dim);
let ssm_f_a_f32 = weight_ref_row_dequant(&kda.ssm_f_a);
let b_alpha_zeros = vec![0.0_f32; head_dim];
let identity_out = identity_matrix_f32(v_head_dim);
if let Some(caps) = capture_updates {
assert_eq!(
caps.len(),
num_heads,
"capture_updates slice length {} must equal num_heads {num_heads}",
caps.len()
);
for ((head_idx, (cache, out_slice)), cap) in caches
.iter_mut()
.zip(concat_out.chunks_mut(v_head_dim))
.enumerate()
.zip(caps.iter_mut())
{
kimi_k3_kda_head_forward(
head_idx,
&x_norm,
head_dim,
v_head_dim,
alpha_rank,
g_min,
rms_eps,
kda,
&ssm_f_a_f32,
&zero_bias,
&b_alpha_zeros,
&identity_gate,
&identity_out,
cache,
out_slice,
Some(cap),
);
}
} else {
let head_iter = caches
.iter_mut()
.zip(concat_out.chunks_mut(v_head_dim))
.enumerate();
#[cfg(feature = "parallel")]
let head_iter = {
use rayon::iter::ParallelBridge;
head_iter.par_bridge()
};
#[cfg(feature = "parallel")]
let process_head =
|(head_idx, (cache, out_slice)): (usize, (&mut KimiDeltaHeadCache, &mut [f32]))| {
kimi_k3_kda_head_forward(
head_idx,
&x_norm,
head_dim,
v_head_dim,
alpha_rank,
g_min,
rms_eps,
kda,
&ssm_f_a_f32,
&zero_bias,
&b_alpha_zeros,
&identity_gate,
&identity_out,
cache,
out_slice,
None,
);
};
#[cfg(feature = "parallel")]
{
use rayon::iter::ParallelIterator;
head_iter.for_each(process_head);
}
#[cfg(not(feature = "parallel"))]
for (head_idx, (cache, out_slice)) in head_iter {
kimi_k3_kda_head_forward(
head_idx,
&x_norm,
head_dim,
v_head_dim,
alpha_rank,
g_min,
rms_eps,
kda,
&ssm_f_a_f32,
&zero_bias,
&b_alpha_zeros,
&identity_gate,
&identity_out,
cache,
out_slice,
None,
);
}
}
debug_assert_eq!(
attn_output.cols,
num_heads * v_head_dim,
"attn_output.cols must equal num_heads × v_head_dim"
);
debug_assert_eq!(
attn_output.rows, d,
"attn_output.rows must equal hidden dim"
);
let mut y = vec![0.0_f32; d];
attn_output.matvec(&concat_out, &mut y);
y
}
#[allow(dead_code, clippy::too_many_arguments)]
fn kimi_k3_kda_head_forward(
head_idx: usize,
x_norm: &[f32],
head_dim: usize,
v_head_dim: usize,
alpha_rank: usize,
g_min: f32,
rms_eps: f32,
kda: &KimiK3KdaAttn<'_>,
ssm_f_a_f32: &[f32],
zero_bias: &[f32],
b_alpha_zeros: &[f32],
identity_gate: &[f32],
identity_out: &[f32],
cache: &mut KimiDeltaHeadCache,
out_slice: &mut [f32],
capture: Option<&mut KimiDeltaHeadUpdate>,
) {
let row_start = head_idx * head_dim;
let row_end = row_start + head_dim;
let describe = |t: &WeightRef<'_>| {
format!(
"qtype={:?} rows={} cols={} data_len={} elements_per_block={} block_bytes={}",
t.qtype,
t.rows,
t.cols,
t.data.len(),
t.qtype.elements_per_block(),
t.qtype.block_bytes()
)
};
let w_q = kimi_k3_slice_weight_ref_rows(&kda.q, row_start, row_end).unwrap_or_else(|| {
panic!(
"kda_layer_forward: q slice failed (head {head_idx}, rows [{row_start}..{row_end}], \
tensor {})",
describe(&kda.q)
)
});
let w_k = kimi_k3_slice_weight_ref_rows(&kda.k, row_start, row_end).unwrap_or_else(|| {
panic!(
"kda_layer_forward: k slice failed (head {head_idx}, rows [{row_start}..{row_end}], \
tensor {})",
describe(&kda.k)
)
});
let w_v = kimi_k3_slice_weight_ref_rows(&kda.v, row_start, row_end).unwrap_or_else(|| {
panic!(
"kda_layer_forward: v slice failed (head {head_idx}, rows [{row_start}..{row_end}], \
tensor {})",
describe(&kda.v)
)
});
let w_conv_q = kimi_k3_slice_weight_ref_rows(&kda.ssm_conv1d_q, row_start, row_end)
.unwrap_or_else(|| {
panic!(
"kda_layer_forward: conv1d_q slice failed (head {head_idx}, rows \
[{row_start}..{row_end}], tensor {})",
describe(&kda.ssm_conv1d_q)
)
});
let w_conv_k = kimi_k3_slice_weight_ref_rows(&kda.ssm_conv1d_k, row_start, row_end)
.unwrap_or_else(|| {
panic!(
"kda_layer_forward: conv1d_k slice failed (head {head_idx}, rows \
[{row_start}..{row_end}], tensor {})",
describe(&kda.ssm_conv1d_k)
)
});
let w_conv_v = kimi_k3_slice_weight_ref_rows(&kda.ssm_conv1d_v, row_start, row_end)
.unwrap_or_else(|| {
panic!(
"kda_layer_forward: conv1d_v slice failed (head {head_idx}, rows \
[{row_start}..{row_end}], tensor {})",
describe(&kda.ssm_conv1d_v)
)
});
let w_alpha_up = kimi_k3_slice_weight_ref_rows(&kda.ssm_f_b, row_start, row_end)
.unwrap_or_else(|| {
panic!(
"kda_layer_forward: ssm_f_b slice failed (head {head_idx}, rows \
[{row_start}..{row_end}], tensor {})",
describe(&kda.ssm_f_b)
)
});
let w_beta_ref = kimi_k3_slice_weight_ref_rows(&kda.ssm_beta, head_idx, head_idx + 1)
.unwrap_or_else(|| panic!("kda_layer_forward: ssm_beta slice failed (head {head_idx})"));
let w_beta = weight_ref_row_dequant(&w_beta_ref);
let ssm_norm_f32: Vec<f32> = if kda.ssm_norm.len() == head_dim {
kda.ssm_norm.clone()
} else if row_end <= kda.ssm_norm.len() {
kda.ssm_norm[row_start..row_end].to_vec()
} else {
panic!(
"kda_layer_forward: ssm_norm shape unexpected (len={}, head_dim={head_dim}, \
head_idx={head_idx}). Expected `[head_dim]` (shared) or `[num_heads * head_dim]`.",
kda.ssm_norm.len(),
);
};
let a_h = kda
.ssm_a
.as_ref()
.and_then(|arr| arr.get(head_idx).copied())
.unwrap_or(0.0);
let ssm_g_slice = kda
.ssm_g
.as_ref()
.and_then(|g| kimi_k3_slice_weight_ref_rows(g, row_start, row_end));
let w_gate_owned = ssm_g_slice.as_ref().map(weight_ref_row_dequant);
let w_gate_ref: &[f32] = w_gate_owned.as_deref().unwrap_or(identity_gate);
let params = KimiDeltaHeadParams {
w_q: &weight_ref_row_dequant(&w_q),
w_k: &weight_ref_row_dequant(&w_k),
w_v: &weight_ref_row_dequant(&w_v),
conv_kernel_q: &weight_ref_row_dequant(&w_conv_q),
conv_kernel_k: &weight_ref_row_dequant(&w_conv_k),
conv_kernel_v: &weight_ref_row_dequant(&w_conv_v),
conv_bias_q: zero_bias,
conv_bias_k: zero_bias,
conv_bias_v: zero_bias,
w_beta: &w_beta,
w_alpha_down: ssm_f_a_f32,
w_alpha_up: &weight_ref_row_dequant(&w_alpha_up),
b_alpha: b_alpha_zeros,
a_h,
alpha_rank,
g_min,
w_gate: w_gate_ref,
w_out: identity_out,
d_out: v_head_dim,
rms_gamma: Some(&ssm_norm_f32),
rms_eps,
};
let head_out = if let Some(cap_slot) = capture {
#[cfg(feature = "dspark")]
{
let (out, update) =
kimi_delta_forward_head_with_capture(x_norm, ¶ms, cache, rms_eps);
*cap_slot = update;
out
}
#[cfg(not(feature = "dspark"))]
{
let _ = cap_slot;
kimi_delta_forward_head(x_norm, ¶ms, cache, rms_eps)
}
} else {
kimi_delta_forward_head(x_norm, ¶ms, cache, rms_eps)
};
out_slice.copy_from_slice(&head_out);
}
#[allow(dead_code)]
fn weight_ref_as_f32(w: &WeightRef<'_>) -> Vec<f32> {
assert!(
matches!(w.qtype, GgmlType::F32),
"weight_ref_as_f32: F32 only"
);
let n = w.rows * w.cols;
assert_eq!(
w.data.len(),
n * 4,
"weight_ref_as_f32: byte length mismatch"
);
let mut out = Vec::with_capacity(n);
for i in 0..n {
let b = &w.data[i * 4..i * 4 + 4];
out.push(f32::from_le_bytes([b[0], b[1], b[2], b[3]]));
}
out
}
#[allow(dead_code)]
fn weight_ref_slice_as_f32(w: &WeightRef<'_>, start: usize, end: usize) -> Vec<f32> {
assert!(
matches!(w.qtype, GgmlType::F32),
"weight_ref_slice_as_f32: F32 only"
);
let total_elements = w.rows * w.cols;
assert!(end <= total_elements, "slice end out of range");
let mut out = Vec::with_capacity(end - start);
for i in start..end {
let b = &w.data[i * 4..i * 4 + 4];
out.push(f32::from_le_bytes([b[0], b[1], b[2], b[3]]));
}
out
}
#[allow(dead_code)]
fn identity_matrix_f32(d: usize) -> Vec<f32> {
let mut out = vec![0.0_f32; d * d];
for i in 0..d {
out[i * d + i] = 1.0;
}
out
}
#[allow(dead_code)]
fn kimi_k3_moe_router(
x: &[f32],
ffn_gate_inp: &[f32],
exp_probs_b: Option<&[f32]>,
top_k: usize,
renormalize: bool,
) -> Vec<(usize, f32)> {
let d = x.len();
assert!(!ffn_gate_inp.is_empty(), "ffn_gate_inp must not be empty");
assert!(
ffn_gate_inp.len().is_multiple_of(d),
"ffn_gate_inp length {} must be multiple of hidden dim {d}",
ffn_gate_inp.len()
);
let num_experts = ffn_gate_inp.len() / d;
assert!(
top_k <= num_experts,
"top_k {top_k} > num_experts {num_experts}"
);
let mut scores = Vec::with_capacity(num_experts);
for e in 0..num_experts {
let row_start = e * d;
let mut acc = 0.0_f64;
for j in 0..d {
acc += f64::from(ffn_gate_inp[row_start + j]) * f64::from(x[j]);
}
scores.push(sigmoid(acc as f32));
}
let selection_scores: Vec<f32> = if let Some(bias) = exp_probs_b {
assert_eq!(
bias.len(),
num_experts,
"exp_probs_b length must equal num_experts"
);
scores
.iter()
.zip(bias.iter())
.map(|(&s, &b)| s + b)
.collect()
} else {
scores.clone()
};
let mut indexed: Vec<(usize, f32)> = selection_scores
.iter()
.enumerate()
.map(|(i, &v)| (i, v))
.collect();
indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
indexed.truncate(top_k);
let mut selected: Vec<(usize, f32)> =
indexed.into_iter().map(|(i, _)| (i, scores[i])).collect();
if renormalize {
let sum: f32 = selected.iter().map(|(_, w)| *w).sum();
if sum > 0.0 {
let inv = sum.recip();
for (_, w) in &mut selected {
*w *= inv;
}
}
}
selected.sort_by_key(|(i, _)| *i);
selected
}
#[allow(dead_code)]
fn kimi_k3_shared_experts_forward(
x: &[f32],
gate: &WeightRef<'_>,
up: &WeightRef<'_>,
down: &WeightRef<'_>,
) -> Vec<f32> {
let d = x.len();
let intermediate = gate.rows;
debug_assert_eq!(up.rows, intermediate);
debug_assert_eq!(down.cols, intermediate);
debug_assert_eq!(down.rows, d);
let mut gate_out = vec![0.0_f32; intermediate];
gate.matvec(x, &mut gate_out);
let mut up_out = vec![0.0_f32; intermediate];
up.matvec(x, &mut up_out);
let mut gated = vec![0.0_f32; intermediate];
for i in 0..intermediate {
gated[i] = silu(gate_out[i]) * up_out[i];
}
let mut y = vec![0.0_f32; d];
down.matvec(&gated, &mut y);
y
}
#[allow(dead_code)]
fn kimi_k3_situ_scalar(g: f32, u: f32, beta: f32, linear_beta: f32) -> f32 {
let g_tanh = (g / beta).tanh() * beta;
let g_sig = sigmoid(g);
let u_tanh = (u / linear_beta).tanh() * linear_beta;
g_tanh * g_sig * u_tanh
}
#[allow(dead_code)]
fn kimi_k3_cube_per_expert_rows(cube: &WeightRef<'_>, num_experts: usize) -> usize {
if num_experts == 0 {
return cube.rows;
}
let block_bytes = cube.qtype.block_bytes();
let elements_per_block = cube.qtype.elements_per_block();
if block_bytes == 0 || elements_per_block == 0 || cube.cols == 0 {
return cube.rows;
}
if !cube.data.len().is_multiple_of(num_experts) {
return cube.rows;
}
let per_expert_bytes = cube.data.len() / num_experts;
if !per_expert_bytes.is_multiple_of(block_bytes) {
return cube.rows;
}
let per_expert_blocks = per_expert_bytes / block_bytes;
let per_expert_elements = per_expert_blocks * elements_per_block;
if !per_expert_elements.is_multiple_of(cube.cols) {
return cube.rows;
}
per_expert_elements / cube.cols
}
#[allow(dead_code)]
fn kimi_k3_expert_plane_weight_ref<'a>(
cube: &WeightRef<'a>,
expert_idx: usize,
num_experts: usize,
) -> Option<WeightRef<'a>> {
if expert_idx >= num_experts || num_experts == 0 {
return None;
}
if matches!(cube.qtype, GgmlType::Other(_)) {
return None;
}
let elements_per_block = cube.qtype.elements_per_block();
let block_bytes = cube.qtype.block_bytes();
if elements_per_block == 0 || block_bytes == 0 {
return None;
}
if !cube.data.len().is_multiple_of(num_experts) {
return None;
}
let plane_bytes = cube.data.len() / num_experts;
if !plane_bytes.is_multiple_of(block_bytes) {
return None;
}
let plane_blocks = plane_bytes / block_bytes;
let plane_elements = plane_blocks.checked_mul(elements_per_block)?;
if cube.cols == 0 || !plane_elements.is_multiple_of(cube.cols) {
return None;
}
let per_expert_rows = plane_elements / cube.cols;
if !plane_elements.is_multiple_of(elements_per_block) {
return None;
}
let start = expert_idx.checked_mul(plane_bytes)?;
let end = start.checked_add(plane_bytes)?;
Some(WeightRef {
data: &cube.data[start..end],
qtype: cube.qtype,
rows: per_expert_rows,
cols: cube.cols,
})
}
#[allow(dead_code, clippy::too_many_arguments)]
fn kimi_k3_latent_moe_forward(
x: &[f32],
ffn_norm: &[f32],
moe: &KimiK3LatentMoe<'_>,
top_k: usize,
renormalize: bool,
rms_eps: f32,
) -> Vec<f32> {
let x_norm = kimi_k3_rms_norm(x, ffn_norm, rms_eps);
let hidden = x.len();
let selected = kimi_k3_moe_router(
&x_norm,
&moe.ffn_gate_inp,
moe.exp_probs_b.as_deref(),
top_k,
renormalize,
);
let shared_out = kimi_k3_shared_experts_forward(
&x_norm,
&moe.ffn_gate_shexp,
&moe.ffn_up_shexp,
&moe.ffn_down_shexp,
);
let n_embd_latent = moe.routed_exp_down.rows;
assert_eq!(
moe.routed_exp_down.cols, hidden,
"routed_exp_down.cols must equal hidden dim"
);
assert_eq!(
moe.routed_exp_up.cols, n_embd_latent,
"routed_exp_up.cols must equal n_embd_latent"
);
assert_eq!(
moe.routed_exp_up.rows, hidden,
"routed_exp_up.rows must equal hidden dim"
);
assert_eq!(
moe.routed_exp_norm.len(),
n_embd_latent,
"routed_exp_norm length must equal n_embd_latent"
);
assert!(
moe.ffn_gate_inp.len().is_multiple_of(hidden),
"ffn_gate_inp length {} must be multiple of hidden {hidden}",
moe.ffn_gate_inp.len()
);
let num_experts = moe.ffn_gate_inp.len() / hidden;
assert_eq!(
moe.ffn_gate_exps.cols, n_embd_latent,
"ffn_gate_exps.cols must equal n_embd_latent"
);
assert_eq!(
moe.ffn_up_exps.cols, n_embd_latent,
"ffn_up_exps.cols must equal n_embd_latent"
);
let n_ff_exp = kimi_k3_cube_per_expert_rows(&moe.ffn_gate_exps, num_experts);
let per_expert_up_rows = kimi_k3_cube_per_expert_rows(&moe.ffn_up_exps, num_experts);
assert_eq!(
per_expert_up_rows, n_ff_exp,
"ffn_up_exps per-expert rows must equal ffn_gate_exps per-expert rows"
);
assert_eq!(
moe.ffn_down_exps.cols, n_ff_exp,
"ffn_down_exps.cols must equal n_ff_exp"
);
let per_expert_down_rows = kimi_k3_cube_per_expert_rows(&moe.ffn_down_exps, num_experts);
assert_eq!(
per_expert_down_rows, n_embd_latent,
"ffn_down_exps per-expert rows must equal n_embd_latent"
);
let madv_on = std::env::var("ALICE_K3_MADV").ok().as_deref() == Some("1");
if madv_on {
for (expert_idx, _weight) in &selected {
for cube in [&moe.ffn_gate_exps, &moe.ffn_up_exps, &moe.ffn_down_exps] {
if let Some(plane) = kimi_k3_expert_plane_weight_ref(cube, *expert_idx, num_experts)
{
let _ = crate::deepseek_streaming::advise_willneed(plane.data);
}
}
}
}
let mut routed_in = vec![0.0_f32; n_embd_latent];
moe.routed_exp_down.matvec(&x_norm, &mut routed_in);
let mut routed_sum = vec![0.0_f32; n_embd_latent];
for (expert_idx, weight) in &selected {
let gate_ref =
kimi_k3_expert_plane_weight_ref(&moe.ffn_gate_exps, *expert_idx, num_experts)
.unwrap_or_else(|| {
panic!(
"K3 LatentMoE routed dispatch: ffn_gate_exps for expert \
{expert_idx} not sliceable (qtype {:?}, num_experts \
{num_experts}, plane bytes {plane_bytes} × num_experts vs \
cube data {}). Quantized cubes require \
`plane_elements % elements_per_block == 0` — check that \
`n_embd_latent * n_ff_exp` is a multiple of the quant \
block size ({} for this qtype).",
moe.ffn_gate_exps.qtype,
moe.ffn_gate_exps.data.len(),
moe.ffn_gate_exps.qtype.elements_per_block(),
plane_bytes = {
let epb = moe.ffn_gate_exps.qtype.elements_per_block().max(1);
let bb = moe.ffn_gate_exps.qtype.block_bytes();
(moe.ffn_gate_exps.rows * moe.ffn_gate_exps.cols / epb) * bb
}
)
});
let up_ref = kimi_k3_expert_plane_weight_ref(&moe.ffn_up_exps, *expert_idx, num_experts)
.expect("ffn_up_exps expert slice must succeed if gate slice did");
let down_ref =
kimi_k3_expert_plane_weight_ref(&moe.ffn_down_exps, *expert_idx, num_experts)
.expect("ffn_down_exps expert slice must succeed if gate slice did");
let mut gate_out = vec![0.0_f32; n_ff_exp];
gate_ref.matvec(&routed_in, &mut gate_out);
let mut up_out = vec![0.0_f32; n_ff_exp];
up_ref.matvec(&routed_in, &mut up_out);
let act: Vec<f32> = gate_out
.iter()
.zip(up_out.iter())
.map(|(&g, &u)| kimi_k3_situ_scalar(g, u, 4.0, 25.0))
.collect();
let mut expert_out = vec![0.0_f32; n_embd_latent];
down_ref.matvec(&act, &mut expert_out);
for i in 0..n_embd_latent {
routed_sum[i] += weight * expert_out[i];
}
}
let routed_normed = kimi_k3_rms_norm(&routed_sum, &moe.routed_exp_norm, rms_eps);
let mut up_projected = vec![0.0_f32; hidden];
moe.routed_exp_up.matvec(&routed_normed, &mut up_projected);
let mut y = up_projected;
for i in 0..hidden {
y[i] += shared_out[i];
}
y
}
#[cfg(test)]
mod kimi_k3_latent_moe_tests {
use super::{
kimi_k3_expert_plane_weight_ref, kimi_k3_latent_moe_forward, kimi_k3_moe_router,
kimi_k3_shared_experts_forward, kimi_k3_situ_scalar, GgmlType, KimiK3LatentMoe, WeightRef,
};
fn f32_bytes(v: &[f32]) -> Vec<u8> {
let mut out = Vec::with_capacity(v.len() * 4);
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
out
}
#[test]
fn moe_router_selects_top_k_by_score() {
let x = vec![1.0_f32, 1.0];
let router = vec![
5.0, 5.0, 0.1, 0.1, 0.1, 0.1, 5.0, 5.0, ];
let selected = kimi_k3_moe_router(&x, &router, None, 2, true);
let indices: Vec<usize> = selected.iter().map(|(i, _)| *i).collect();
assert_eq!(indices, vec![0, 3], "top-2 should pick experts 0 and 3");
let sum: f32 = selected.iter().map(|(_, w)| *w).sum();
assert!(
(sum - 1.0).abs() < 1e-5,
"renormalized weights must sum to 1"
);
}
#[test]
fn moe_router_bias_shifts_selection_without_affecting_weights() {
let x = vec![1.0_f32, 1.0];
let router = vec![
5.0, 5.0, 1.0, 1.0, 0.0, 0.0, 3.0, 3.0, ];
let bias = vec![0.0_f32, 10.0, 0.0, 0.0];
let selected = kimi_k3_moe_router(&x, &router, Some(&bias), 2, false);
let indices: Vec<usize> = selected.iter().map(|(i, _)| *i).collect();
assert_eq!(indices, vec![0, 1], "bias must promote expert 1 over 3");
let raw_1 = 1.0_f32 / (1.0 + (-2.0_f32).exp()); let w_1 = selected.iter().find(|(i, _)| *i == 1).unwrap().1;
assert!(
(w_1 - raw_1).abs() < 1e-5,
"expert 1 weight {w_1} must equal raw sigmoid {raw_1}, bias omitted"
);
}
#[test]
fn moe_router_returns_sorted_indices() {
let x = vec![1.0_f32, 1.0];
let router = vec![
0.1, 0.1, 5.0, 5.0, 3.0, 3.0, 5.0, 5.0, ];
let selected = kimi_k3_moe_router(&x, &router, None, 3, true);
let indices: Vec<usize> = selected.iter().map(|(i, _)| *i).collect();
assert_eq!(indices, vec![1, 2, 3], "indices must be sorted ascending");
}
#[test]
fn shared_experts_forward_zero_input_gives_zero() {
let x = vec![0.0_f32; 4];
let gate_bytes = f32_bytes(&(0..32).map(|i| 0.01 * i as f32).collect::<Vec<_>>());
let up_bytes = f32_bytes(&(0..32).map(|i| 0.02 * i as f32).collect::<Vec<_>>());
let down_bytes = f32_bytes(&(0..32).map(|i| 0.03 * i as f32).collect::<Vec<_>>());
let gate = WeightRef {
data: &gate_bytes,
qtype: GgmlType::F32,
rows: 8,
cols: 4,
};
let up = WeightRef {
data: &up_bytes,
qtype: GgmlType::F32,
rows: 8,
cols: 4,
};
let down = WeightRef {
data: &down_bytes,
qtype: GgmlType::F32,
rows: 4,
cols: 8,
};
let y = kimi_k3_shared_experts_forward(&x, &gate, &up, &down);
for (i, &v) in y.iter().enumerate() {
assert_eq!(v, 0.0, "y[{i}] = {v} but zero input must give zero");
}
}
#[test]
fn situ_scalar_at_zero_gives_zero() {
let y = kimi_k3_situ_scalar(0.0, 0.0, 4.0, 25.0);
assert!(y.abs() < 1e-6, "situ(0, 0) must be 0, got {y}");
}
#[test]
fn situ_scalar_saturates_symmetric_at_large_gate() {
let y = kimi_k3_situ_scalar(40.0, 250.0, 4.0, 25.0);
assert!(
(y - 100.0).abs() < 0.1,
"situ(+40, +250) with β=4, β_linear=25 must saturate near 100, got {y}"
);
let y_neg = kimi_k3_situ_scalar(-40.0, 250.0, 4.0, 25.0);
assert!(
y_neg.abs() < 1e-3,
"situ(-40, +250) must collapse via σ(-40) ≈ 0, got {y_neg}"
);
}
#[test]
fn expert_plane_weight_ref_extracts_correct_slice() {
let e0: Vec<f32> = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let e1: Vec<f32> = vec![10.0, 20.0, 30.0, 40.0, 50.0, 60.0];
let mut cube_vals = e0;
cube_vals.extend(e1);
let cube_bytes = f32_bytes(&cube_vals);
let cube = WeightRef {
data: &cube_bytes,
qtype: GgmlType::F32,
rows: 3, cols: 2, };
let plane0 =
kimi_k3_expert_plane_weight_ref(&cube, 0, 2).expect("expert 0 plane must extract");
assert_eq!(plane0.rows, 3);
assert_eq!(plane0.cols, 2);
assert_eq!(plane0.data.len(), 6 * 4, "24 bytes = 6 F32");
let first_bytes: [u8; 4] = plane0.data[..4].try_into().unwrap();
assert_eq!(f32::from_le_bytes(first_bytes), 1.0);
let plane1 =
kimi_k3_expert_plane_weight_ref(&cube, 1, 2).expect("expert 1 plane must extract");
let first_bytes: [u8; 4] = plane1.data[..4].try_into().unwrap();
assert_eq!(f32::from_le_bytes(first_bytes), 10.0);
}
#[test]
fn expert_plane_weight_ref_rejects_out_of_range_expert() {
let bytes = vec![0u8; 24];
let cube = WeightRef {
data: &bytes,
qtype: GgmlType::F32,
rows: 3,
cols: 2,
};
assert!(kimi_k3_expert_plane_weight_ref(&cube, 2, 2).is_none());
}
#[test]
fn expert_plane_weight_ref_supports_quantized_cube_when_aligned() {
let num_experts = 2;
let per_expert_rows = 4;
let bytes = vec![0u8; 144 * num_experts];
let cube = WeightRef {
data: &bytes,
qtype: GgmlType::Q4_K,
rows: per_expert_rows * num_experts,
cols: 64,
};
let plane0 = kimi_k3_expert_plane_weight_ref(&cube, 0, num_experts)
.expect("Q4_K plane slicing must succeed when per-expert plane % 256 == 0");
assert_eq!(plane0.rows, per_expert_rows);
assert_eq!(plane0.cols, 64);
assert_eq!(plane0.data.len(), 144, "1 Q4_K block per plane");
let plane1 = kimi_k3_expert_plane_weight_ref(&cube, 1, num_experts)
.expect("expert 1 slice must succeed for aligned Q4_K cube");
assert_eq!(plane1.data.len(), 144);
}
#[test]
fn expert_plane_weight_ref_rejects_quantized_cube_when_misaligned() {
let bytes = vec![0u8; 200];
let cube = WeightRef {
data: &bytes,
qtype: GgmlType::Q4_K,
rows: 4,
cols: 64,
};
assert!(
kimi_k3_expert_plane_weight_ref(&cube, 0, 2).is_none(),
"misaligned Q4_K (100 bytes/expert vs 144-byte block) must fail slicing"
);
}
#[test]
fn expert_plane_weight_ref_supports_mxfp4_when_aligned() {
let num_experts = 3;
let bytes = vec![0u8; 68 * num_experts];
let cube = WeightRef {
data: &bytes,
qtype: GgmlType::Mxfp4,
rows: 4,
cols: 32,
};
let plane2 = kimi_k3_expert_plane_weight_ref(&cube, 2, num_experts)
.expect("MXFP4 plane slicing must succeed at aligned boundary");
assert_eq!(plane2.data.len(), 68);
}
#[test]
fn latent_moe_forward_smoke_test_with_2_experts_f32() {
let hidden = 4;
let n_embd_latent = 2;
let n_ff_exp = 3;
let num_experts = 2;
let x = vec![0.5_f32; hidden];
let ffn_norm = vec![1.0_f32; hidden];
let ffn_gate_inp = vec![
0.1, 0.1, 0.1, 0.1, 5.0, 5.0, 5.0, 5.0, ];
let gate_shexp_vals: Vec<f32> = (0..32).map(|i| 0.01 * i as f32).collect();
let up_shexp_vals: Vec<f32> = (0..32).map(|i| 0.02 * i as f32).collect();
let down_shexp_vals: Vec<f32> = (0..32).map(|i| 0.03 * i as f32).collect();
let gate_shexp_bytes = f32_bytes(&gate_shexp_vals);
let up_shexp_bytes = f32_bytes(&up_shexp_vals);
let down_shexp_bytes = f32_bytes(&down_shexp_vals);
let routed_exp_down_vals: Vec<f32> = vec![
0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, ];
let routed_exp_down_bytes = f32_bytes(&routed_exp_down_vals);
let routed_exp_up_vals: Vec<f32> = vec![
0.5, 0.5, 0.4, 0.4, 0.3, 0.3, 0.2, 0.2, ];
let routed_exp_up_bytes = f32_bytes(&routed_exp_up_vals);
let routed_exp_norm = vec![1.0_f32; n_embd_latent];
let per_expert_gate = 6;
let per_expert_up = 6;
let per_expert_down = 6;
let mut gate_exps_vals: Vec<f32> = Vec::new();
gate_exps_vals.extend((0..per_expert_gate).map(|i| 0.1 + 0.1 * i as f32));
gate_exps_vals.extend((0..per_expert_gate).map(|i| 1.0 + 0.1 * i as f32));
let mut up_exps_vals: Vec<f32> = Vec::new();
up_exps_vals.extend((0..per_expert_up).map(|i| 0.15 + 0.05 * i as f32));
up_exps_vals.extend((0..per_expert_up).map(|i| 1.5 + 0.05 * i as f32));
let mut down_exps_vals: Vec<f32> = Vec::new();
down_exps_vals.extend((0..per_expert_down).map(|i| 0.2 + 0.1 * i as f32));
down_exps_vals.extend((0..per_expert_down).map(|i| 2.0 + 0.1 * i as f32));
let gate_exps_bytes = f32_bytes(&gate_exps_vals);
let up_exps_bytes = f32_bytes(&up_exps_vals);
let down_exps_bytes = f32_bytes(&down_exps_vals);
let moe = KimiK3LatentMoe {
ffn_gate_inp,
exp_probs_b: None,
ffn_gate_shexp: WeightRef {
data: &gate_shexp_bytes,
qtype: GgmlType::F32,
rows: 8,
cols: 4,
},
ffn_up_shexp: WeightRef {
data: &up_shexp_bytes,
qtype: GgmlType::F32,
rows: 8,
cols: 4,
},
ffn_down_shexp: WeightRef {
data: &down_shexp_bytes,
qtype: GgmlType::F32,
rows: 4,
cols: 8,
},
routed_exp_up: WeightRef {
data: &routed_exp_up_bytes,
qtype: GgmlType::F32,
rows: hidden,
cols: n_embd_latent,
},
routed_exp_down: WeightRef {
data: &routed_exp_down_bytes,
qtype: GgmlType::F32,
rows: n_embd_latent,
cols: hidden,
},
routed_exp_norm,
ffn_gate_exps: WeightRef {
data: &gate_exps_bytes,
qtype: GgmlType::F32,
rows: n_ff_exp,
cols: n_embd_latent,
},
ffn_up_exps: WeightRef {
data: &up_exps_bytes,
qtype: GgmlType::F32,
rows: n_ff_exp,
cols: n_embd_latent,
},
ffn_down_exps: WeightRef {
data: &down_exps_bytes,
qtype: GgmlType::F32,
rows: n_embd_latent,
cols: n_ff_exp,
},
};
let _ = num_experts;
let y = kimi_k3_latent_moe_forward(&x, &ffn_norm, &moe, 1, true, 1e-6);
assert_eq!(y.len(), hidden, "output must have hidden dim");
assert!(
y.iter().any(|&v| v.abs() > 1e-6),
"output must be non-zero for non-zero input"
);
for (i, &v) in y.iter().enumerate() {
assert!(v.is_finite(), "y[{i}] = {v} must be finite");
}
}
}
#[cfg(test)]
mod kimi_k3_kda_layer_tests {
use super::{
identity_matrix_f32, kimi_k3_slice_weight_ref_rows, weight_ref_as_f32,
weight_ref_slice_as_f32, GgmlType, WeightRef,
};
fn f32_bytes(v: &[f32]) -> Vec<u8> {
let mut out = Vec::with_capacity(v.len() * 4);
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
out
}
#[test]
fn slice_weight_ref_rows_extracts_correct_byte_range() {
let vals: Vec<f32> = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let bytes = f32_bytes(&vals);
let w = WeightRef {
data: &bytes,
qtype: GgmlType::F32,
rows: 4,
cols: 2,
};
let sliced = kimi_k3_slice_weight_ref_rows(&w, 1, 3).expect("slicing must succeed for F32");
assert_eq!(sliced.rows, 2);
assert_eq!(sliced.cols, 2);
let sliced_f32 = weight_ref_as_f32(&sliced);
assert_eq!(sliced_f32, vec![3.0, 4.0, 5.0, 6.0]);
}
#[test]
fn slice_weight_ref_rows_rejects_misaligned_quantized_cols() {
let bytes = vec![0u8; 144]; let w = WeightRef {
data: &bytes,
qtype: GgmlType::Q4_K,
rows: 4,
cols: 64,
};
assert!(kimi_k3_slice_weight_ref_rows(&w, 0, 2).is_none());
}
#[test]
fn slice_weight_ref_rows_supports_q4_k_when_aligned() {
let bytes = vec![0u8; 144 * 4]; let w = WeightRef {
data: &bytes,
qtype: GgmlType::Q4_K,
rows: 4,
cols: 256,
};
let sliced = kimi_k3_slice_weight_ref_rows(&w, 1, 3)
.expect("Q4_K per-row slicing must succeed when cols % 256 == 0");
assert_eq!(sliced.rows, 2);
assert_eq!(sliced.cols, 256);
assert_eq!(sliced.data.len(), 144 * 2, "2 Q4_K blocks");
}
#[test]
fn slice_weight_ref_rows_supports_mxfp4_when_aligned() {
let bytes = vec![0u8; 17 * 4];
let w = WeightRef {
data: &bytes,
qtype: GgmlType::Mxfp4,
rows: 4,
cols: 32,
};
let sliced = kimi_k3_slice_weight_ref_rows(&w, 0, 2)
.expect("MXFP4 per-row slicing must succeed at 32-element aligned cols");
assert_eq!(sliced.rows, 2);
assert_eq!(sliced.cols, 32);
assert_eq!(sliced.data.len(), 17 * 2);
}
#[test]
fn slice_weight_ref_rows_supports_q8_0_when_aligned() {
let bytes = vec![0u8; 34 * 6];
let w = WeightRef {
data: &bytes,
qtype: GgmlType::Q8_0,
rows: 3,
cols: 64,
};
let sliced = kimi_k3_slice_weight_ref_rows(&w, 1, 2)
.expect("Q8_0 per-row slicing must succeed at 32-element aligned cols");
assert_eq!(sliced.rows, 1);
assert_eq!(sliced.data.len(), 34 * 2);
}
#[test]
fn slice_weight_ref_rows_returns_none_on_out_of_range() {
let bytes = vec![0u8; 32];
let w = WeightRef {
data: &bytes,
qtype: GgmlType::F32,
rows: 2,
cols: 4,
};
assert!(kimi_k3_slice_weight_ref_rows(&w, 0, 3).is_none());
assert!(kimi_k3_slice_weight_ref_rows(&w, 2, 1).is_none());
}
#[test]
fn weight_ref_slice_as_f32_extracts_element_range() {
let vals: Vec<f32> = (0..8).map(|i| i as f32).collect();
let bytes = f32_bytes(&vals);
let w = WeightRef {
data: &bytes,
qtype: GgmlType::F32,
rows: 1,
cols: 8,
};
let sliced = weight_ref_slice_as_f32(&w, 2, 6);
assert_eq!(sliced, vec![2.0, 3.0, 4.0, 5.0]);
}
#[test]
fn identity_matrix_f32_places_ones_on_diagonal() {
let m = identity_matrix_f32(3);
assert_eq!(m.len(), 9);
assert_eq!(m[0], 1.0);
assert_eq!(m[4], 1.0);
assert_eq!(m[8], 1.0);
assert_eq!(m[1], 0.0);
assert_eq!(m[3], 0.0);
}
}
#[cfg(test)]
mod kimi_k3_forward_helpers_tests {
use super::{
kimi_k3_dense_ffn_forward, kimi_k3_extract_mla_config, GgmlType, KimiDeltaConfig,
Llama3Config, ModelArch, WeightRef,
};
fn f32_bytes(v: &[f32]) -> Vec<u8> {
let mut out = Vec::with_capacity(v.len() * 4);
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
out
}
fn tiny_k3_config() -> Llama3Config {
Llama3Config {
arch: ModelArch::KimiK3,
vocab_size: 32,
hidden_dim: 4,
intermediate_dim: 8,
num_heads: 2,
num_kv_heads: 2,
num_layers: 2,
max_seq_len: 128,
head_dim: 2,
rope_theta: 10000.0,
norm_eps: 1e-5,
attention_extras: None,
ssm: None,
moe: None,
gemma3n: None,
gemma4: None,
deepseek_v3: None,
kimi_delta: Some(KimiDeltaConfig {
full_attn_layers: Some(vec![0, 1]),
kda_layers: Some(vec![]),
kda_head_dim: Some(2),
kda_num_heads: Some(2),
kda_short_conv_kernel_size: Some(4),
kda_use_full_rank_gate: Some(true),
kda_gate_lower_bound: Some(-5.0),
q_lora_rank: Some(4),
kv_lora_rank: Some(2),
qk_nope_head_dim: Some(2),
qk_rope_head_dim: Some(1),
v_head_dim: Some(2),
mla_use_nope: Some(true),
mla_use_output_gate: Some(true),
attn_res_block_size: Some(2),
situ_beta: Some(4.0),
situ_linear_beta: Some(25.0),
n_routed_experts: Some(4),
num_experts_per_tok: Some(2),
n_shared_experts: Some(1),
num_expert_group: Some(1),
topk_group: Some(1),
moe_router_activation: Some("sigmoid".to_string()),
moe_topk_method: Some("noaux_tc".to_string()),
moe_intermediate_size: Some(4),
first_k_dense_replace: Some(2),
moe_renormalize: Some(true),
routed_expert_hidden_size: Some(2),
latent_moe_use_norm: Some(true),
routed_scaling_factor: Some(1.0),
num_nextn_predict_layers: None,
mxfp4_group_size: None,
mxfp4_num_bits: None,
}),
}
}
#[test]
fn extract_mla_config_populates_from_llama3_config() {
let config = tiny_k3_config();
let mla_cfg = kimi_k3_extract_mla_config(&config).expect("mla config must extract");
assert_eq!(mla_cfg.d, 4);
assert_eq!(mla_cfg.num_heads, 2);
assert_eq!(mla_cfg.qk_nope_head_dim, 2);
assert_eq!(mla_cfg.qk_rope_head_dim, 1);
assert_eq!(mla_cfg.v_head_dim, 2);
assert_eq!(mla_cfg.q_lora_rank, 4);
assert_eq!(mla_cfg.kv_lora_rank, 2);
assert!((mla_cfg.rms_eps - 1e-5).abs() < 1e-10);
}
#[test]
fn extract_mla_config_returns_none_when_kimi_delta_absent() {
let mut config = tiny_k3_config();
config.kimi_delta = None;
assert!(kimi_k3_extract_mla_config(&config).is_none());
}
#[test]
fn extract_mla_config_returns_none_when_required_dim_absent() {
let mut config = tiny_k3_config();
if let Some(kd) = config.kimi_delta.as_mut() {
kd.q_lora_rank = None;
}
assert!(kimi_k3_extract_mla_config(&config).is_none());
}
#[test]
fn dense_ffn_forward_zero_input_gives_zero_output() {
let x = vec![0.0_f32; 4];
let ffn_norm = vec![1.0_f32; 4];
let gate_bytes = f32_bytes(&(0..32).map(|i| 0.01 * i as f32).collect::<Vec<_>>());
let up_bytes = f32_bytes(&(0..32).map(|i| 0.02 * i as f32).collect::<Vec<_>>());
let down_bytes = f32_bytes(&(0..32).map(|i| 0.03 * i as f32).collect::<Vec<_>>());
let gate = WeightRef {
data: &gate_bytes,
qtype: GgmlType::F32,
rows: 8,
cols: 4,
};
let up = WeightRef {
data: &up_bytes,
qtype: GgmlType::F32,
rows: 8,
cols: 4,
};
let down = WeightRef {
data: &down_bytes,
qtype: GgmlType::F32,
rows: 4,
cols: 8,
};
let y = kimi_k3_dense_ffn_forward(&x, &ffn_norm, &gate, &up, &down, 1e-5);
for (i, &v) in y.iter().enumerate() {
assert_eq!(v, 0.0, "y[{i}] = {v} but zero input must give zero output");
}
}
#[test]
fn dense_ffn_forward_produces_finite_bounded_output() {
let x = vec![0.5_f32, -0.5, 0.25, -0.25];
let ffn_norm = vec![1.0_f32; 4];
let gate_bytes = f32_bytes(
&(0..32)
.map(|i| 0.01 * (i as f32 - 15.5))
.collect::<Vec<_>>(),
);
let up_bytes = f32_bytes(
&(0..32)
.map(|i| 0.02 * (i as f32 - 15.5))
.collect::<Vec<_>>(),
);
let down_bytes = f32_bytes(
&(0..32)
.map(|i| 0.03 * (i as f32 - 15.5))
.collect::<Vec<_>>(),
);
let gate = WeightRef {
data: &gate_bytes,
qtype: GgmlType::F32,
rows: 8,
cols: 4,
};
let up = WeightRef {
data: &up_bytes,
qtype: GgmlType::F32,
rows: 8,
cols: 4,
};
let down = WeightRef {
data: &down_bytes,
qtype: GgmlType::F32,
rows: 4,
cols: 8,
};
let y = kimi_k3_dense_ffn_forward(&x, &ffn_norm, &gate, &up, &down, 1e-5);
for (i, &v) in y.iter().enumerate() {
assert!(v.is_finite(), "y[{i}] = {v} not finite");
assert!(v.abs() <= 2.0, "y[{i}] = {v} exceeds reasonable envelope");
}
}
}
#[cfg(test)]
mod kimi_k3_gated_mla_tests {
use super::{
kimi_k3_gated_mla_step, GgmlType, KimiK3MlaAttn, KimiK3MlaCache, KimiK3MlaConfig, WeightRef,
};
fn f32_bytes(v: &[f32]) -> Vec<u8> {
let mut out = Vec::with_capacity(v.len() * 4);
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
out
}
fn tiny_config() -> KimiK3MlaConfig {
KimiK3MlaConfig {
d: 4,
num_heads: 2,
qk_nope_head_dim: 2,
qk_rope_head_dim: 1,
v_head_dim: 2,
q_lora_rank: 2,
kv_lora_rank: 2,
rms_eps: 1e-6,
}
}
#[allow(clippy::too_many_arguments)]
fn build_tiny_mla_attn<'a>(
q_a: &'a [u8],
q_a_norm: Vec<f32>,
q_b: &'a [u8],
kv_a_mqa: &'a [u8],
kv_a_norm: Vec<f32>,
k_b: &'a [u8],
v_b: &'a [u8],
) -> KimiK3MlaAttn<'a> {
KimiK3MlaAttn {
q_a: WeightRef {
data: q_a,
qtype: GgmlType::F32,
rows: 2,
cols: 4,
},
q_a_norm,
q_b: WeightRef {
data: q_b,
qtype: GgmlType::F32,
rows: 6,
cols: 2,
},
kv_a_mqa: WeightRef {
data: kv_a_mqa,
qtype: GgmlType::F32,
rows: 3,
cols: 4,
},
kv_a_norm,
k_b: WeightRef {
data: k_b,
qtype: GgmlType::F32,
rows: 4,
cols: 2,
},
v_b: WeightRef {
data: v_b,
qtype: GgmlType::F32,
rows: 4,
cols: 2,
},
}
}
#[test]
fn mla_cache_new_starts_empty() {
let cache = KimiK3MlaCache::new(2, 1, 4);
assert_eq!(cache.n_positions(), 0);
}
#[test]
fn mla_cache_reset_clears_positions() {
let mut cache = KimiK3MlaCache::new(2, 1, 4);
cache.append(&[1.0, 2.0], &[3.0]);
cache.append(&[4.0, 5.0], &[6.0]);
assert_eq!(cache.n_positions(), 2);
cache.reset();
assert_eq!(cache.n_positions(), 0);
}
#[test]
fn gated_mla_step_zero_output_projection_gives_zero() {
let q_a = f32_bytes(&[0.1_f32; 8]);
let q_b = f32_bytes(&[0.1_f32; 12]);
let kv_a_mqa = f32_bytes(&[0.1_f32; 12]);
let k_b = f32_bytes(&[0.1_f32; 8]);
let v_b = f32_bytes(&[0.1_f32; 8]);
let mla = build_tiny_mla_attn(
&q_a,
vec![1.0; 2],
&q_b,
&kv_a_mqa,
vec![1.0; 2],
&k_b,
&v_b,
);
let attn_norm = vec![1.0_f32; 4];
let attn_gate_bytes = f32_bytes(&[0.5_f32; 16]);
let attn_output_bytes = f32_bytes(&[0.0_f32; 16]);
let attn_gate = WeightRef {
data: &attn_gate_bytes,
qtype: GgmlType::F32,
rows: 4,
cols: 4,
};
let attn_output = WeightRef {
data: &attn_output_bytes,
qtype: GgmlType::F32,
rows: 4,
cols: 4,
};
let mut cache = KimiK3MlaCache::new(2, 1, 4);
let config = tiny_config();
let x = vec![0.5_f32, -0.5, 0.25, -0.25];
let y = kimi_k3_gated_mla_step(
&x,
&attn_norm,
&attn_gate,
&attn_output,
&mla,
&mut cache,
&config,
);
for (i, &v) in y.iter().enumerate() {
assert_eq!(v, 0.0, "y[{i}] = {v} but zero W_o must give zero output");
}
assert_eq!(cache.n_positions(), 1, "cache appends one position");
}
#[test]
fn gated_mla_step_single_token_produces_finite_bounded_output() {
let q_a = f32_bytes(&[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]);
let q_b_vals: Vec<f32> = (0..12).map(|i| 0.05 + 0.01 * i as f32).collect();
let q_b = f32_bytes(&q_b_vals);
let kv_a_vals: Vec<f32> = (0..12).map(|i| -0.02 * i as f32).collect();
let kv_a_mqa = f32_bytes(&kv_a_vals);
let k_b = f32_bytes(&[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]);
let v_b = f32_bytes(&[-0.1, 0.2, -0.3, 0.4, -0.5, 0.6, -0.7, 0.8]);
let mla = build_tiny_mla_attn(
&q_a,
vec![1.0; 2],
&q_b,
&kv_a_mqa,
vec![1.0; 2],
&k_b,
&v_b,
);
let attn_norm = vec![1.0_f32; 4];
let mut ident = vec![0.0_f32; 16];
for i in 0..4 {
ident[i * 4 + i] = 1.0;
}
let ident_bytes = f32_bytes(&ident);
let attn_gate = WeightRef {
data: &ident_bytes,
qtype: GgmlType::F32,
rows: 4,
cols: 4,
};
let attn_output = WeightRef {
data: &ident_bytes,
qtype: GgmlType::F32,
rows: 4,
cols: 4,
};
let mut cache = KimiK3MlaCache::new(2, 1, 4);
let config = tiny_config();
let x = vec![0.5_f32, -0.5, 0.25, -0.25];
let y = kimi_k3_gated_mla_step(
&x,
&attn_norm,
&attn_gate,
&attn_output,
&mla,
&mut cache,
&config,
);
assert_eq!(y.len(), 4);
for (i, &v) in y.iter().enumerate() {
assert!(v.is_finite(), "y[{i}] = {v} not finite");
assert!(v.abs() <= 4.0, "y[{i}] = {v} exceeds reasonable envelope");
}
assert_eq!(cache.n_positions(), 1);
}
#[test]
fn gated_mla_step_two_tokens_grow_cache() {
let q_a = f32_bytes(&[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]);
let q_b_vals: Vec<f32> = (0..12).map(|i| 0.05 + 0.01 * i as f32).collect();
let q_b = f32_bytes(&q_b_vals);
let kv_a_vals: Vec<f32> = (0..12).map(|i| -0.02 * i as f32).collect();
let kv_a_mqa = f32_bytes(&kv_a_vals);
let k_b = f32_bytes(&[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]);
let v_b = f32_bytes(&[-0.1, 0.2, -0.3, 0.4, -0.5, 0.6, -0.7, 0.8]);
let mla = build_tiny_mla_attn(
&q_a,
vec![1.0; 2],
&q_b,
&kv_a_mqa,
vec![1.0; 2],
&k_b,
&v_b,
);
let attn_norm = vec![1.0_f32; 4];
let mut ident = vec![0.0_f32; 16];
for i in 0..4 {
ident[i * 4 + i] = 1.0;
}
let ident_bytes = f32_bytes(&ident);
let attn_gate = WeightRef {
data: &ident_bytes,
qtype: GgmlType::F32,
rows: 4,
cols: 4,
};
let attn_output = WeightRef {
data: &ident_bytes,
qtype: GgmlType::F32,
rows: 4,
cols: 4,
};
let mut cache = KimiK3MlaCache::new(2, 1, 4);
let config = tiny_config();
let x1 = vec![0.5_f32, -0.5, 0.25, -0.25];
let x2 = vec![-0.3_f32, 0.4, -0.1, 0.2];
let y1 = kimi_k3_gated_mla_step(
&x1,
&attn_norm,
&attn_gate,
&attn_output,
&mla,
&mut cache,
&config,
);
let y2 = kimi_k3_gated_mla_step(
&x2,
&attn_norm,
&attn_gate,
&attn_output,
&mla,
&mut cache,
&config,
);
for (i, (&a, &b)) in y1.iter().zip(y2.iter()).enumerate() {
assert!(a.is_finite(), "y1[{i}] = {a}");
assert!(b.is_finite(), "y2[{i}] = {b}");
}
assert_eq!(cache.n_positions(), 2);
}
}
#[allow(dead_code)]
#[derive(Clone)]
pub(crate) enum KimiK3LayerCache {
Mla(KimiK3MlaCache),
Kda(Vec<KimiDeltaHeadCache>),
}
#[allow(dead_code)]
pub struct KimiK3Model<'a> {
weights: KimiK3ModelWeights<'a>,
config: Llama3Config,
layer_caches: Vec<KimiK3LayerCache>,
attn_res_state: KimiK3AttnResState,
block_size: usize,
token_count: usize,
snapshot_ring: std::collections::VecDeque<KimiK3ModelSnapshot>,
#[cfg(feature = "dspark")]
snapshot_ring_compact: std::collections::VecDeque<KimiK3ModelSnapshotCompact>,
max_snapshot_ring: usize,
#[cfg(feature = "dspark")]
compact_snapshots: bool,
#[cfg(feature = "dspark")]
snapshot_ring_delta: std::collections::VecDeque<KimiK3ModelSnapshotDelta>,
#[cfg(feature = "dspark")]
delta_snapshots: bool,
}
#[derive(Clone)]
pub struct KimiK3ModelSnapshot {
layer_caches: Vec<KimiK3LayerCache>,
attn_res_state: KimiK3AttnResState,
token_count: usize,
}
pub const KIMI_K3_DEFAULT_MAX_SNAPSHOT_RING: usize = 8;
#[cfg(feature = "dspark")]
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
fn f32_to_f16_bits(v: f32) -> u16 {
let bits = v.to_bits();
let sign = ((bits >> 16) & 0x8000) as u16;
let mantissa = bits & 0x007F_FFFF; let exp32 = ((bits >> 23) & 0xFF) as i32;
if exp32 == 0xFF {
return sign | 0x7C00 | if mantissa != 0 { 0x0200 } else { 0 };
}
let biased_exp = exp32 - 127 + 15;
if biased_exp >= 31 {
return sign | 0x7C00;
}
if biased_exp <= 0 {
return sign;
}
let mantissa_shifted = mantissa >> 13;
let round_bit = (mantissa >> 12) & 1;
let sticky = mantissa & 0x0FFF;
let rounded = mantissa_shifted
+ u32::from(round_bit != 0 && (sticky != 0 || (mantissa_shifted & 1) != 0));
if rounded > 0x3FF {
let new_exp = biased_exp + 1;
if new_exp >= 31 {
return sign | 0x7C00;
}
return sign | ((new_exp as u16) << 10);
}
sign | ((biased_exp as u16) << 10) | (rounded as u16)
}
#[cfg(feature = "dspark")]
#[allow(clippy::cast_sign_loss)]
fn f16_bits_to_f32(h: u16) -> f32 {
let sign = u32::from(h & 0x8000) << 16;
let exp16 = ((h >> 10) & 0x1F) as i32;
let mant16 = u32::from(h & 0x03FF);
if exp16 == 0 {
return f32::from_bits(sign);
}
if exp16 == 0x1F {
let mant32 = if mant16 != 0 { mant16 << 13 } else { 0 };
return f32::from_bits(sign | 0x7F80_0000 | mant32);
}
let exp32 = (exp16 - 15 + 127) as u32;
let mant32 = mant16 << 13;
f32::from_bits(sign | (exp32 << 23) | mant32)
}
#[cfg(feature = "dspark")]
fn f32_vec_to_f16_bits(v: &[f32]) -> Vec<u16> {
v.iter().map(|&x| f32_to_f16_bits(x)).collect()
}
#[cfg(feature = "dspark")]
fn f16_bits_to_f32_vec(v: &[u16]) -> Vec<f32> {
v.iter().map(|&h| f16_bits_to_f32(h)).collect()
}
#[cfg(feature = "dspark")]
#[derive(Clone)]
pub struct KimiK3ModelSnapshotCompact {
layer_caches: Vec<KimiK3LayerCacheCompact>,
attn_res_state: KimiK3AttnResStateCompact,
token_count: usize,
}
#[cfg(feature = "dspark")]
#[derive(Clone)]
pub(crate) enum KimiK3LayerCacheCompact {
Mla(KimiK3MlaCacheCompact),
Kda(Vec<KimiDeltaHeadCacheCompact>),
}
#[cfg(feature = "dspark")]
#[derive(Clone)]
pub struct KimiK3MlaCacheCompact {
c_k_f16: Vec<u16>,
k_rope_f16: Vec<u16>,
n_positions: usize,
kv_lora_rank: usize,
qk_rope_head_dim: usize,
}
#[cfg(feature = "dspark")]
#[derive(Clone)]
pub struct KimiDeltaHeadCacheCompact {
state_f16: Vec<u16>,
conv_state_q_f16: Vec<u16>,
conv_state_k_f16: Vec<u16>,
conv_state_v_f16: Vec<u16>,
ring_pos_q: usize,
ring_pos_k: usize,
ring_pos_v: usize,
kernel_size: usize,
d_k: usize,
d_v: usize,
}
#[cfg(feature = "dspark")]
#[derive(Clone)]
pub struct KimiK3AttnResStateCompact {
d: usize,
block_size: usize,
banked_f16: Vec<Vec<u16>>,
}
#[cfg(feature = "dspark")]
#[derive(Clone)]
pub struct KimiK3ModelSnapshotDelta {
pub base_snapshot: KimiK3ModelSnapshot,
pub per_step_updates: Vec<Vec<Vec<KimiDeltaHeadUpdate>>>,
}
#[allow(dead_code)]
impl<'a> KimiK3Model<'a> {
pub fn new(weights: KimiK3ModelWeights<'a>, config: Llama3Config) -> Result<Self, String> {
let kd = config
.kimi_delta
.as_ref()
.ok_or_else(|| "kimi_delta sub-config missing (X.4.b.1 loader?)".to_string())?;
let block_size = kd
.attn_res_block_size
.ok_or_else(|| "attn_res_block_size missing from kimi_delta config".to_string())?;
let kda_head_dim = kd
.kda_head_dim
.ok_or_else(|| "kda_head_dim missing from kimi_delta config".to_string())?;
let kda_num_heads = kd.kda_num_heads.unwrap_or(config.num_heads);
let kda_short_conv_kernel_size = kd.kda_short_conv_kernel_size.unwrap_or(4);
let kv_lora_rank = kd
.kv_lora_rank
.ok_or_else(|| "kv_lora_rank missing from kimi_delta config".to_string())?;
let qk_rope_head_dim = kd
.qk_rope_head_dim
.ok_or_else(|| "qk_rope_head_dim missing from kimi_delta config".to_string())?;
const MLA_CACHE_CAPACITY: usize = 4096;
let mut layer_caches: Vec<KimiK3LayerCache> = Vec::with_capacity(config.num_layers);
for il in 0..config.num_layers {
let is_mla = if il < weights.layers.len() {
matches!(weights.layers[il].attn, KimiK3Attention::Mla(_))
} else {
kd.is_mla_layer(il).unwrap_or(false)
};
if is_mla {
layer_caches.push(KimiK3LayerCache::Mla(KimiK3MlaCache::new(
kv_lora_rank,
qk_rope_head_dim,
MLA_CACHE_CAPACITY,
)));
} else {
let heads: Vec<KimiDeltaHeadCache> = (0..kda_num_heads)
.map(|_| {
KimiDeltaHeadCache::new(
kda_head_dim,
kda_head_dim,
kda_short_conv_kernel_size,
)
})
.collect();
layer_caches.push(KimiK3LayerCache::Kda(heads));
}
}
let attn_res_state = KimiK3AttnResState::new(config.hidden_dim, block_size);
Ok(Self {
weights,
config,
layer_caches,
attn_res_state,
block_size,
token_count: 0,
snapshot_ring: std::collections::VecDeque::with_capacity(
KIMI_K3_DEFAULT_MAX_SNAPSHOT_RING,
),
#[cfg(feature = "dspark")]
snapshot_ring_compact: std::collections::VecDeque::with_capacity(
KIMI_K3_DEFAULT_MAX_SNAPSHOT_RING,
),
max_snapshot_ring: KIMI_K3_DEFAULT_MAX_SNAPSHOT_RING,
#[cfg(feature = "dspark")]
compact_snapshots: false,
#[cfg(feature = "dspark")]
snapshot_ring_delta: std::collections::VecDeque::with_capacity(2),
#[cfg(feature = "dspark")]
delta_snapshots: false,
})
}
pub fn reset(&mut self) {
for cache in &mut self.layer_caches {
match cache {
KimiK3LayerCache::Mla(c) => c.reset(),
KimiK3LayerCache::Kda(heads) => {
for head in heads {
head.reset();
}
}
}
}
self.attn_res_state.reset();
self.token_count = 0;
self.snapshot_ring.clear();
#[cfg(feature = "dspark")]
self.snapshot_ring_compact.clear();
#[cfg(feature = "dspark")]
self.snapshot_ring_delta.clear();
}
#[must_use]
pub fn num_layers(&self) -> usize {
self.config.num_layers
}
#[must_use]
pub fn seq_len(&self) -> usize {
self.token_count
}
#[cfg(any(test, feature = "dspark"))]
#[must_use]
pub fn snapshot_ring_len(&self) -> usize {
#[cfg(feature = "dspark")]
if self.delta_snapshots {
return self.snapshot_ring_delta.len();
}
#[cfg(feature = "dspark")]
if self.compact_snapshots {
return self.snapshot_ring_compact.len();
}
self.snapshot_ring.len()
}
pub fn set_max_snapshot_ring(&mut self, new_max: usize) {
assert!(new_max > 0, "max_snapshot_ring must be > 0");
self.max_snapshot_ring = new_max;
while self.snapshot_ring.len() > new_max {
self.snapshot_ring.pop_front();
}
#[cfg(feature = "dspark")]
while self.snapshot_ring_compact.len() > new_max {
self.snapshot_ring_compact.pop_front();
}
#[cfg(feature = "dspark")]
while self.snapshot_ring_delta.len() > new_max {
self.snapshot_ring_delta.pop_front();
}
}
#[must_use]
pub fn snapshot(&self) -> KimiK3ModelSnapshot {
KimiK3ModelSnapshot {
layer_caches: self.layer_caches.clone(),
attn_res_state: self.attn_res_state.clone(),
token_count: self.token_count,
}
}
pub fn restore(&mut self, snap: KimiK3ModelSnapshot) {
self.layer_caches = snap.layer_caches;
self.attn_res_state = snap.attn_res_state;
self.token_count = snap.token_count;
self.snapshot_ring.clear();
#[cfg(feature = "dspark")]
self.snapshot_ring_compact.clear();
#[cfg(feature = "dspark")]
self.snapshot_ring_delta.clear();
}
#[cfg(feature = "dspark")]
pub fn set_snapshot_compact_mode(&mut self, enable: bool) {
if self.compact_snapshots == enable {
return;
}
self.compact_snapshots = enable;
if enable {
self.delta_snapshots = false;
}
self.snapshot_ring.clear();
self.snapshot_ring_compact.clear();
self.snapshot_ring_delta.clear();
}
#[cfg(feature = "dspark")]
#[must_use]
pub fn is_snapshot_compact_mode(&self) -> bool {
self.compact_snapshots
}
#[cfg(feature = "dspark")]
pub fn set_snapshot_delta_mode(&mut self, enable: bool) {
if self.delta_snapshots == enable {
return;
}
self.delta_snapshots = enable;
if enable {
self.compact_snapshots = false;
}
self.snapshot_ring.clear();
self.snapshot_ring_compact.clear();
self.snapshot_ring_delta.clear();
}
#[cfg(feature = "dspark")]
#[must_use]
pub fn is_snapshot_delta_mode(&self) -> bool {
self.delta_snapshots
}
#[cfg(feature = "dspark")]
#[must_use]
pub fn snapshot_compact(&self) -> KimiK3ModelSnapshotCompact {
let layer_caches: Vec<KimiK3LayerCacheCompact> = self
.layer_caches
.iter()
.map(|cache| match cache {
KimiK3LayerCache::Mla(m) => KimiK3LayerCacheCompact::Mla(KimiK3MlaCacheCompact {
c_k_f16: f32_vec_to_f16_bits(&m.c_k),
k_rope_f16: f32_vec_to_f16_bits(&m.k_rope),
n_positions: m.n_positions,
kv_lora_rank: m.kv_lora_rank,
qk_rope_head_dim: m.qk_rope_head_dim,
}),
KimiK3LayerCache::Kda(heads) => KimiK3LayerCacheCompact::Kda(
heads
.iter()
.map(|h| KimiDeltaHeadCacheCompact {
state_f16: f32_vec_to_f16_bits(h.state.as_slice()),
conv_state_q_f16: f32_vec_to_f16_bits(&h.conv_state_q),
conv_state_k_f16: f32_vec_to_f16_bits(&h.conv_state_k),
conv_state_v_f16: f32_vec_to_f16_bits(&h.conv_state_v),
ring_pos_q: h.ring_pos_q,
ring_pos_k: h.ring_pos_k,
ring_pos_v: h.ring_pos_v,
kernel_size: h.kernel_size,
d_k: h.d_k,
d_v: h.d_v,
})
.collect(),
),
})
.collect();
let attn_res_state = KimiK3AttnResStateCompact {
d: self.attn_res_state.d,
block_size: self.attn_res_state.block_size,
banked_f16: self
.attn_res_state
.banked
.iter()
.map(|v| f32_vec_to_f16_bits(v))
.collect(),
};
KimiK3ModelSnapshotCompact {
layer_caches,
attn_res_state,
token_count: self.token_count,
}
}
#[cfg(feature = "dspark")]
pub fn restore_compact(&mut self, snap: KimiK3ModelSnapshotCompact) {
let layer_caches: Vec<KimiK3LayerCache> = snap
.layer_caches
.into_iter()
.map(|cache| match cache {
KimiK3LayerCacheCompact::Mla(m) => KimiK3LayerCache::Mla(KimiK3MlaCache {
c_k: f16_bits_to_f32_vec(&m.c_k_f16),
k_rope: f16_bits_to_f32_vec(&m.k_rope_f16),
n_positions: m.n_positions,
kv_lora_rank: m.kv_lora_rank,
qk_rope_head_dim: m.qk_rope_head_dim,
}),
KimiK3LayerCacheCompact::Kda(heads) => KimiK3LayerCache::Kda(
heads
.into_iter()
.map(|h| {
let mut state = KimiDeltaState::new(h.d_k, h.d_v);
let restored = f16_bits_to_f32_vec(&h.state_f16);
state.as_mut_slice().copy_from_slice(&restored);
KimiDeltaHeadCache {
state,
conv_state_q: f16_bits_to_f32_vec(&h.conv_state_q_f16),
conv_state_k: f16_bits_to_f32_vec(&h.conv_state_k_f16),
conv_state_v: f16_bits_to_f32_vec(&h.conv_state_v_f16),
ring_pos_q: h.ring_pos_q,
ring_pos_k: h.ring_pos_k,
ring_pos_v: h.ring_pos_v,
kernel_size: h.kernel_size,
d_k: h.d_k,
d_v: h.d_v,
}
})
.collect(),
),
})
.collect();
self.layer_caches = layer_caches;
self.attn_res_state = KimiK3AttnResState {
d: snap.attn_res_state.d,
block_size: snap.attn_res_state.block_size,
banked: snap
.attn_res_state
.banked_f16
.into_iter()
.map(|v| f16_bits_to_f32_vec(&v))
.collect(),
};
self.token_count = snap.token_count;
self.snapshot_ring.clear();
self.snapshot_ring_compact.clear();
}
#[cfg(feature = "dspark")]
#[must_use]
pub fn snapshot_ring_bytes_estimate(&self) -> usize {
let mut total = 0_usize;
for snap in &self.snapshot_ring {
for cache in &snap.layer_caches {
match cache {
KimiK3LayerCache::Mla(m) => {
total += m.c_k.len() * 4 + m.k_rope.len() * 4;
}
KimiK3LayerCache::Kda(heads) => {
for h in heads {
total += h.state.as_slice().len() * 4;
total += h.conv_state_q.len() * 4;
total += h.conv_state_k.len() * 4;
total += h.conv_state_v.len() * 4;
}
}
}
}
for bank in &snap.attn_res_state.banked {
total += bank.len() * 4;
}
}
for snap in &self.snapshot_ring_compact {
for cache in &snap.layer_caches {
match cache {
KimiK3LayerCacheCompact::Mla(m) => {
total += m.c_k_f16.len() * 2 + m.k_rope_f16.len() * 2;
}
KimiK3LayerCacheCompact::Kda(heads) => {
for h in heads {
total += h.state_f16.len() * 2;
total += h.conv_state_q_f16.len() * 2;
total += h.conv_state_k_f16.len() * 2;
total += h.conv_state_v_f16.len() * 2;
}
}
}
}
for bank in &snap.attn_res_state.banked_f16 {
total += bank.len() * 2;
}
}
for delta in &self.snapshot_ring_delta {
total += Self::snapshot_delta_bytes_estimate(delta);
}
total
}
#[cfg(feature = "dspark")]
#[must_use]
pub fn snapshot_delta_from(&self) -> KimiK3ModelSnapshotDelta {
KimiK3ModelSnapshotDelta {
base_snapshot: self.snapshot(),
per_step_updates: Vec::new(),
}
}
#[cfg(feature = "dspark")]
pub fn restore_from_delta(&mut self, delta: KimiK3ModelSnapshotDelta) {
self.restore(delta.base_snapshot);
for step_updates in &delta.per_step_updates {
let mut kda_layer_idx = 0_usize;
for layer_cache in &mut self.layer_caches {
if let KimiK3LayerCache::Kda(heads) = layer_cache {
assert!(
kda_layer_idx < step_updates.len(),
"delta step_updates missing KDA layer at idx {kda_layer_idx}"
);
let head_updates = &step_updates[kda_layer_idx];
assert_eq!(
heads.len(),
head_updates.len(),
"delta step_updates[{kda_layer_idx}] has {} heads but cache has {}",
head_updates.len(),
heads.len(),
);
for (head, update) in heads.iter_mut().zip(head_updates.iter()) {
head.apply_update(update);
}
kda_layer_idx += 1;
}
}
self.token_count += 1;
}
self.snapshot_ring.clear();
self.snapshot_ring_compact.clear();
}
#[cfg(feature = "dspark")]
#[must_use]
pub fn snapshot_delta_bytes_estimate(delta: &KimiK3ModelSnapshotDelta) -> usize {
let mut total = 0_usize;
for cache in &delta.base_snapshot.layer_caches {
match cache {
KimiK3LayerCache::Mla(m) => {
total += m.c_k.len() * 4 + m.k_rope.len() * 4;
}
KimiK3LayerCache::Kda(heads) => {
for h in heads {
total += h.state.as_slice().len() * 4;
total += h.conv_state_q.len() * 4;
total += h.conv_state_k.len() * 4;
total += h.conv_state_v.len() * 4;
}
}
}
}
for bank in &delta.base_snapshot.attn_res_state.banked {
total += bank.len() * 4;
}
for step in &delta.per_step_updates {
for layer in step {
for u in layer {
total += u.q_pre.len() * 4;
total += u.k_pre.len() * 4;
total += u.v_pre.len() * 4;
total += u.k_conv.len() * 4;
total += u.v_conv.len() * 4;
total += u.alpha.len() * 4;
total += 4; }
}
}
total
}
pub fn rollback_to(&mut self, pos: usize) {
if pos == self.token_count {
return;
}
assert!(
pos <= self.token_count,
"KimiK3Model::rollback_to({pos}) exceeds current token_count {}",
self.token_count
);
let distance = self.token_count - pos;
#[cfg(feature = "dspark")]
if self.delta_snapshots {
let last = self.snapshot_ring_delta.back().cloned();
let delta = last.unwrap_or_else(|| {
panic!(
"KimiK3Model::rollback_to({pos}) delta ring is empty (mode enabled but no snapshots pushed)"
)
});
let updates_len = delta.per_step_updates.len();
assert!(
distance <= updates_len,
"KimiK3Model::rollback_to({pos}) distance {distance} exceeds delta chunk updates {} \
(base at position {})",
updates_len,
delta.base_snapshot.token_count
);
let mut truncated = delta;
truncated.per_step_updates.truncate(updates_len - distance);
self.restore_from_delta(truncated);
self.snapshot_ring_delta
.push_back(KimiK3ModelSnapshotDelta {
base_snapshot: self.snapshot(),
per_step_updates: Vec::new(),
});
return;
}
#[cfg(feature = "dspark")]
if self.compact_snapshots {
assert!(
distance <= self.snapshot_ring_compact.len(),
"KimiK3Model::rollback_to({pos}) distance {distance} exceeds \
compact snapshot_ring size {} (max_snapshot_ring = {})",
self.snapshot_ring_compact.len(),
self.max_snapshot_ring
);
let target_idx = self.snapshot_ring_compact.len() - distance;
let snap = self
.snapshot_ring_compact
.get(target_idx)
.cloned()
.expect("target compact snapshot must exist (bounds checked)");
self.snapshot_ring_compact.truncate(target_idx);
self.restore_compact(snap);
return;
}
assert!(
distance <= self.snapshot_ring.len(),
"KimiK3Model::rollback_to({pos}) distance {distance} exceeds snapshot_ring size {} \
(max_snapshot_ring = {}). Increase max_snapshot_ring or reduce rollback distance.",
self.snapshot_ring.len(),
self.max_snapshot_ring
);
let target_idx = self.snapshot_ring.len() - distance;
let snap = self
.snapshot_ring
.get(target_idx)
.cloned()
.expect("target snapshot must exist (bounds checked)");
self.snapshot_ring.truncate(target_idx);
self.restore(snap);
}
#[cfg(feature = "dspark")]
pub fn forward_with_snapshot(&mut self, token_id: u32) -> Vec<f32> {
if self.delta_snapshots {
let need_rebase = self.snapshot_ring_delta.is_empty()
|| self
.snapshot_ring_delta
.back()
.is_some_and(|d| d.per_step_updates.len() >= self.max_snapshot_ring);
if need_rebase {
let new_base = self.snapshot();
self.snapshot_ring_delta
.push_back(KimiK3ModelSnapshotDelta {
base_snapshot: new_base,
per_step_updates: Vec::new(),
});
while self.snapshot_ring_delta.len() > 2 {
self.snapshot_ring_delta.pop_front();
}
}
let (logits, updates) = self.forward_capture_updates(token_id);
self.snapshot_ring_delta
.back_mut()
.expect("just pushed base")
.per_step_updates
.push(updates);
self.token_count += 1;
return logits;
}
self.push_snapshot_bounded();
let logits = self.forward(token_id);
self.token_count += 1;
logits
}
#[cfg(feature = "dspark")]
pub fn forward_capture_hidden_with_snapshot(
&mut self,
token_id: u32,
layer_idx: Option<usize>,
) -> (Vec<f32>, Vec<f32>) {
self.push_snapshot_bounded();
let out = self.forward_capture_hidden(token_id, layer_idx);
self.token_count += 1;
out
}
#[cfg(feature = "dspark")]
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn forward_capture_updates(
&mut self,
token_id: u32,
) -> (Vec<f32>, Vec<Vec<KimiDeltaHeadUpdate>>) {
let vocab_size = self.config.vocab_size;
let hidden_dim = self.config.hidden_dim;
assert!(
(token_id as usize) < vocab_size,
"token_id {token_id} out of vocab range 0..{vocab_size}"
);
let row_start = token_id as usize;
let embed_row =
kimi_k3_slice_weight_ref_rows(&self.weights.token_embd, row_start, row_start + 1)
.unwrap_or_else(|| {
panic!(
"K3 embed lookup: token_embd per-row slicing failed for token {token_id}"
)
});
let x_full = weight_ref_row_dequant(&embed_row);
let mut x = vec![0.0_f32; hidden_dim];
let take = hidden_dim.min(x_full.len());
x[..take].copy_from_slice(&x_full[..take]);
let mla_config = kimi_k3_extract_mla_config(&self.config)
.expect("KimiK3Model was constructed but MLA sub-config no longer extractable");
let mut per_layer_updates: Vec<Vec<KimiDeltaHeadUpdate>> = Vec::new();
let trace = std::env::var("ALICE_K3_TRACE").ok().as_deref() == Some("1");
let layer_t0 = std::time::Instant::now();
for il in 0..self.config.num_layers {
let layer = &self.weights.layers[il];
let il_t0 = std::time::Instant::now();
let cur_attn = kimi_k3_res_mix(
&self.attn_res_state,
&x,
&layer.attn_res_score,
self.config.norm_eps,
);
let banked = self.attn_res_state.is_checkpoint_layer(il);
if banked {
self.attn_res_state.bank(&x);
}
let attn_output: Vec<f32> = match (&mut self.layer_caches[il], &layer.attn) {
(KimiK3LayerCache::Mla(cache), KimiK3Attention::Mla(mla_attn)) => {
kimi_k3_gated_mla_step(
&cur_attn,
&layer.attn_norm,
&layer.attn_gate,
&layer.attn_output,
mla_attn,
cache,
&mla_config,
)
}
(KimiK3LayerCache::Kda(head_caches), KimiK3Attention::Kda(kda_attn)) => {
let kd = self
.config
.kimi_delta
.as_ref()
.expect("kimi_delta config must be present at forward time");
let head_dim = kd
.kda_head_dim
.expect("kda_head_dim missing from kimi_delta config");
let num_kda_heads = kd.kda_num_heads.unwrap_or(self.config.num_heads);
let g_min = kd.kda_gate_lower_bound.unwrap_or(-5.0);
let alpha_rank = kda_attn.ssm_f_a.rows;
let mut layer_capture: Vec<KimiDeltaHeadUpdate> = (0..num_kda_heads)
.map(|_| KimiDeltaHeadUpdate {
q_pre: Vec::new(),
k_pre: Vec::new(),
v_pre: Vec::new(),
k_conv: Vec::new(),
v_conv: Vec::new(),
alpha: Vec::new(),
beta: 0.0,
})
.collect();
let out = kimi_k3_kda_layer_forward(
&cur_attn,
&layer.attn_norm,
&layer.attn_output,
kda_attn,
head_caches,
num_kda_heads,
head_dim,
alpha_rank,
g_min,
self.config.norm_eps,
Some(&mut layer_capture),
);
per_layer_updates.push(layer_capture);
out
}
_ => panic!("KimiK3Model layer {il}: cache/attn tag mismatch — invariant broken"),
};
if banked {
x.copy_from_slice(&attn_output);
} else {
for i in 0..hidden_dim {
x[i] += attn_output[i];
}
}
let cur_ffn = kimi_k3_res_mix(
&self.attn_res_state,
&x,
&layer.ffn_res_score,
self.config.norm_eps,
);
let ffn_output: Vec<f32> = match &layer.ffn {
KimiK3Ffn::Dense { gate, up, down } => kimi_k3_dense_ffn_forward(
&cur_ffn,
&layer.ffn_norm,
gate,
up,
down,
self.config.norm_eps,
),
KimiK3Ffn::LatentMoe(moe) => {
let kd = self
.config
.kimi_delta
.as_ref()
.expect("kimi_delta config must be present at forward time");
let top_k = kd.num_experts_per_tok.unwrap_or(16);
let renormalize = kd.moe_renormalize.unwrap_or(true);
kimi_k3_latent_moe_forward(
&cur_ffn,
&layer.ffn_norm,
moe,
top_k,
renormalize,
self.config.norm_eps,
)
}
};
for i in 0..hidden_dim {
x[i] += ffn_output[i];
}
if trace {
let kind = if matches!(layer.attn, KimiK3Attention::Mla(_)) {
"MLA"
} else {
"KDA"
};
let ffn_kind = if matches!(layer.ffn, KimiK3Ffn::Dense { .. }) {
"Dense"
} else {
"MoE"
};
let ms = il_t0.elapsed().as_millis();
let cumulative_s = layer_t0.elapsed().as_secs_f64();
eprintln!(
"[K3 trace] layer {il:>2}/{} {kind}+{ffn_kind} {ms:>6} ms (cum {cumulative_s:>7.2}s)",
self.config.num_layers
);
}
}
let x_after_final_mix = kimi_k3_res_mix(
&self.attn_res_state,
&x,
&self.weights.output_res_score,
self.config.norm_eps,
);
let mut x_norm = vec![0.0_f32; hidden_dim];
rms_norm(
&x_after_final_mix,
&self.weights.output_norm,
self.config.norm_eps,
&mut x_norm,
);
let mut logits = vec![0.0_f32; vocab_size];
self.weights.output.matvec(&x_norm, &mut logits);
(logits, per_layer_updates)
}
fn push_snapshot_bounded(&mut self) {
#[cfg(feature = "dspark")]
if self.compact_snapshots {
if self.snapshot_ring_compact.len() >= self.max_snapshot_ring {
self.snapshot_ring_compact.pop_front();
}
let snap = self.snapshot_compact();
self.snapshot_ring_compact.push_back(snap);
return;
}
if self.snapshot_ring.len() >= self.max_snapshot_ring {
self.snapshot_ring.pop_front();
}
self.snapshot_ring.push_back(self.snapshot());
}
#[must_use]
pub fn mla_cache_positions(&self, il: usize) -> Option<usize> {
match self.layer_caches.get(il)? {
KimiK3LayerCache::Mla(c) => Some(c.n_positions()),
KimiK3LayerCache::Kda(_) => None,
}
}
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn forward(&mut self, token_id: u32) -> Vec<f32> {
let vocab_size = self.config.vocab_size;
let hidden_dim = self.config.hidden_dim;
assert!(
(token_id as usize) < vocab_size,
"token_id {token_id} out of vocab range 0..{vocab_size}"
);
let row_start = token_id as usize;
let embed_row =
kimi_k3_slice_weight_ref_rows(&self.weights.token_embd, row_start, row_start + 1)
.unwrap_or_else(|| {
panic!(
"K3 embed lookup: token_embd per-row slicing failed for token {token_id} \
(qtype {:?}, cols {}, rows {}). Ensure `cols % elements_per_block == 0` \
(K3 hidden = 7168 = 28 × 256 = 224 × 32, block-aligned for all K3 quant \
types). Phase X.4.c.3.3.b.2 upgrade.",
self.weights.token_embd.qtype,
self.weights.token_embd.cols,
self.weights.token_embd.rows,
)
});
let x_full = weight_ref_row_dequant(&embed_row);
let mut x = vec![0.0_f32; hidden_dim];
let take = hidden_dim.min(x_full.len());
x[..take].copy_from_slice(&x_full[..take]);
let mla_config = kimi_k3_extract_mla_config(&self.config).expect(
"KimiK3Model was constructed but MLA sub-config no longer extractable — \
invariant broken (should have failed at ::new)",
);
let trace = std::env::var("ALICE_K3_TRACE").ok().as_deref() == Some("1");
let layer_t0 = std::time::Instant::now();
for il in 0..self.config.num_layers {
let layer = &self.weights.layers[il];
let il_t0 = std::time::Instant::now();
let cur_attn = kimi_k3_res_mix(
&self.attn_res_state,
&x,
&layer.attn_res_score,
self.config.norm_eps,
);
let banked = self.attn_res_state.is_checkpoint_layer(il);
if banked {
self.attn_res_state.bank(&x);
}
let attn_output: Vec<f32> = match (&mut self.layer_caches[il], &layer.attn) {
(KimiK3LayerCache::Mla(cache), KimiK3Attention::Mla(mla_attn)) => {
kimi_k3_gated_mla_step(
&cur_attn,
&layer.attn_norm,
&layer.attn_gate,
&layer.attn_output,
mla_attn,
cache,
&mla_config,
)
}
(KimiK3LayerCache::Kda(head_caches), KimiK3Attention::Kda(kda_attn)) => {
let kd = self
.config
.kimi_delta
.as_ref()
.expect("kimi_delta config must be present at forward time");
let head_dim = kd
.kda_head_dim
.expect("kda_head_dim missing from kimi_delta config");
let num_kda_heads = kd.kda_num_heads.unwrap_or(self.config.num_heads);
let g_min = kd.kda_gate_lower_bound.unwrap_or(-5.0);
let alpha_rank = kda_attn.ssm_f_a.rows;
kimi_k3_kda_layer_forward(
&cur_attn,
&layer.attn_norm,
&layer.attn_output,
kda_attn,
head_caches,
num_kda_heads,
head_dim,
alpha_rank,
g_min,
self.config.norm_eps,
None,
)
}
_ => panic!(
"KimiK3Model layer {il}: cache/attn tag mismatch — invariant \
broken (cache and weights.layers[{il}].attn must both be MLA or \
both KDA, dispatched by `is_mla_layer` at construction and \
tensor-load time)"
),
};
if banked {
x.copy_from_slice(&attn_output);
} else {
for i in 0..hidden_dim {
x[i] += attn_output[i];
}
}
let cur_ffn = kimi_k3_res_mix(
&self.attn_res_state,
&x,
&layer.ffn_res_score,
self.config.norm_eps,
);
let ffn_output: Vec<f32> = match &layer.ffn {
KimiK3Ffn::Dense { gate, up, down } => kimi_k3_dense_ffn_forward(
&cur_ffn,
&layer.ffn_norm,
gate,
up,
down,
self.config.norm_eps,
),
KimiK3Ffn::LatentMoe(moe) => {
let kd = self
.config
.kimi_delta
.as_ref()
.expect("kimi_delta config must be present at forward time");
let top_k = kd.num_experts_per_tok.unwrap_or(16);
let renormalize = kd.moe_renormalize.unwrap_or(true);
kimi_k3_latent_moe_forward(
&cur_ffn,
&layer.ffn_norm,
moe,
top_k,
renormalize,
self.config.norm_eps,
)
}
};
for i in 0..hidden_dim {
x[i] += ffn_output[i];
}
if trace {
let kind = if matches!(layer.attn, KimiK3Attention::Mla(_)) {
"MLA"
} else {
"KDA"
};
let ffn_kind = if matches!(layer.ffn, KimiK3Ffn::Dense { .. }) {
"Dense"
} else {
"MoE"
};
let ms = il_t0.elapsed().as_millis();
let cumulative_s = layer_t0.elapsed().as_secs_f64();
eprintln!(
"[K3 trace] layer {il:>2}/{} {kind}+{ffn_kind} {ms:>6} ms (cum {cumulative_s:>7.2}s)",
self.config.num_layers
);
}
}
let x_after_final_mix = kimi_k3_res_mix(
&self.attn_res_state,
&x,
&self.weights.output_res_score,
self.config.norm_eps,
);
let mut x_norm = vec![0.0_f32; hidden_dim];
rms_norm(
&x_after_final_mix,
&self.weights.output_norm,
self.config.norm_eps,
&mut x_norm,
);
let mut logits = vec![0.0_f32; vocab_size];
self.weights.output.matvec(&x_norm, &mut logits);
logits
}
pub fn forward_with_layer_hook<F>(&mut self, token_id: u32, mut hook: F) -> Vec<f32>
where
F: FnMut(usize, &[f32]),
{
let vocab_size = self.config.vocab_size;
let hidden_dim = self.config.hidden_dim;
assert!(
(token_id as usize) < vocab_size,
"token_id {token_id} out of vocab range 0..{vocab_size}"
);
let row_start = token_id as usize;
let embed_row =
kimi_k3_slice_weight_ref_rows(&self.weights.token_embd, row_start, row_start + 1)
.unwrap_or_else(|| {
panic!(
"K3 embed lookup: token_embd per-row slicing failed for token {token_id} \
(qtype {:?}, cols {}, rows {}). Ensure `cols % elements_per_block == 0` \
(K3 hidden = 7168 = 28 × 256 = 224 × 32, block-aligned for all K3 quant \
types). Phase X.4.c.3.3.b.2 upgrade.",
self.weights.token_embd.qtype,
self.weights.token_embd.cols,
self.weights.token_embd.rows,
)
});
let x_full = weight_ref_row_dequant(&embed_row);
let mut x = vec![0.0_f32; hidden_dim];
let take = hidden_dim.min(x_full.len());
x[..take].copy_from_slice(&x_full[..take]);
let mla_config = kimi_k3_extract_mla_config(&self.config).expect(
"KimiK3Model was constructed but MLA sub-config no longer extractable — \
invariant broken (should have failed at ::new)",
);
let trace = std::env::var("ALICE_K3_TRACE").ok().as_deref() == Some("1");
let layer_t0 = std::time::Instant::now();
for il in 0..self.config.num_layers {
let layer = &self.weights.layers[il];
let il_t0 = std::time::Instant::now();
let cur_attn = kimi_k3_res_mix(
&self.attn_res_state,
&x,
&layer.attn_res_score,
self.config.norm_eps,
);
let banked = self.attn_res_state.is_checkpoint_layer(il);
if banked {
self.attn_res_state.bank(&x);
}
let attn_output: Vec<f32> = match (&mut self.layer_caches[il], &layer.attn) {
(KimiK3LayerCache::Mla(cache), KimiK3Attention::Mla(mla_attn)) => {
kimi_k3_gated_mla_step(
&cur_attn,
&layer.attn_norm,
&layer.attn_gate,
&layer.attn_output,
mla_attn,
cache,
&mla_config,
)
}
(KimiK3LayerCache::Kda(head_caches), KimiK3Attention::Kda(kda_attn)) => {
let kd = self
.config
.kimi_delta
.as_ref()
.expect("kimi_delta config must be present at forward time");
let head_dim = kd
.kda_head_dim
.expect("kda_head_dim missing from kimi_delta config");
let num_kda_heads = kd.kda_num_heads.unwrap_or(self.config.num_heads);
let g_min = kd.kda_gate_lower_bound.unwrap_or(-5.0);
let alpha_rank = kda_attn.ssm_f_a.rows;
kimi_k3_kda_layer_forward(
&cur_attn,
&layer.attn_norm,
&layer.attn_output,
kda_attn,
head_caches,
num_kda_heads,
head_dim,
alpha_rank,
g_min,
self.config.norm_eps,
None,
)
}
_ => panic!(
"KimiK3Model layer {il}: cache/attn tag mismatch — invariant \
broken (cache and weights.layers[{il}].attn must both be MLA or \
both KDA, dispatched by `is_mla_layer` at construction and \
tensor-load time)"
),
};
if banked {
x.copy_from_slice(&attn_output);
} else {
for i in 0..hidden_dim {
x[i] += attn_output[i];
}
}
let cur_ffn = kimi_k3_res_mix(
&self.attn_res_state,
&x,
&layer.ffn_res_score,
self.config.norm_eps,
);
let ffn_output: Vec<f32> = match &layer.ffn {
KimiK3Ffn::Dense { gate, up, down } => kimi_k3_dense_ffn_forward(
&cur_ffn,
&layer.ffn_norm,
gate,
up,
down,
self.config.norm_eps,
),
KimiK3Ffn::LatentMoe(moe) => {
let kd = self
.config
.kimi_delta
.as_ref()
.expect("kimi_delta config must be present at forward time");
let top_k = kd.num_experts_per_tok.unwrap_or(16);
let renormalize = kd.moe_renormalize.unwrap_or(true);
kimi_k3_latent_moe_forward(
&cur_ffn,
&layer.ffn_norm,
moe,
top_k,
renormalize,
self.config.norm_eps,
)
}
};
for i in 0..hidden_dim {
x[i] += ffn_output[i];
}
hook(il, &x);
if trace {
let kind = if matches!(layer.attn, KimiK3Attention::Mla(_)) {
"MLA"
} else {
"KDA"
};
let ffn_kind = if matches!(layer.ffn, KimiK3Ffn::Dense { .. }) {
"Dense"
} else {
"MoE"
};
let ms = il_t0.elapsed().as_millis();
let cumulative_s = layer_t0.elapsed().as_secs_f64();
eprintln!(
"[K3 trace] layer {il:>2}/{} {kind}+{ffn_kind} {ms:>6} ms (cum {cumulative_s:>7.2}s)",
self.config.num_layers
);
}
}
let x_after_final_mix = kimi_k3_res_mix(
&self.attn_res_state,
&x,
&self.weights.output_res_score,
self.config.norm_eps,
);
let mut x_norm = vec![0.0_f32; hidden_dim];
rms_norm(
&x_after_final_mix,
&self.weights.output_norm,
self.config.norm_eps,
&mut x_norm,
);
let mut logits = vec![0.0_f32; vocab_size];
self.weights.output.matvec(&x_norm, &mut logits);
logits
}
#[cfg(feature = "dspark")]
pub fn forward_capture_hidden(
&mut self,
token_id: u32,
layer_idx: Option<usize>,
) -> (Vec<f32>, Vec<f32>) {
let num_layers = self.config.num_layers;
let target = layer_idx
.filter(|&n| n < num_layers)
.unwrap_or(num_layers.saturating_sub(1));
let mut captured: Vec<f32> = Vec::new();
let logits = self.forward_with_layer_hook(token_id, |idx, hidden| {
if idx == target {
captured.clear();
captured.extend_from_slice(hidden);
}
});
(logits, captured)
}
}
#[allow(dead_code)]
fn bytes_per_element(qtype: GgmlType) -> usize {
match qtype {
GgmlType::F32 => 4,
GgmlType::F16 => 2,
_ => panic!(
"bytes_per_element: quantized token_embd type {qtype:?} not yet supported \
in KimiK3Model skeleton — Phase X.4.c.3.3 will add block-aware embed lookup"
),
}
}
#[allow(dead_code)]
fn weight_ref_row_dequant(w: &WeightRef<'_>) -> Vec<f32> {
w.dequantize_all(w.rows, w.cols)
}
#[allow(dead_code)]
fn dequantize_row_to_f32(bytes: &[u8], qtype: GgmlType, out: &mut [f32]) {
match qtype {
GgmlType::F32 => {
let n = out.len().min(bytes.len() / 4);
for i in 0..n {
let b = &bytes[i * 4..i * 4 + 4];
out[i] = f32::from_le_bytes([b[0], b[1], b[2], b[3]]);
}
}
GgmlType::F16 => {
let n = out.len().min(bytes.len() / 2);
for i in 0..n {
let raw = u16::from_le_bytes([bytes[i * 2], bytes[i * 2 + 1]]);
out[i] = crate::gguf::f16_to_f32(raw);
}
}
_ => panic!(
"dequantize_row_to_f32: quantized type {qtype:?} not yet supported \
in KimiK3Model skeleton — Phase X.4.c.3.3 will add block-aware dequant"
),
}
}
#[cfg(test)]
mod kimi_k3_model_tests {
use super::{
GgmlType, KimiDeltaConfig, KimiK3LayerCache, KimiK3Model, KimiK3ModelWeights, Llama3Config,
ModelArch, WeightRef,
};
fn tiny_kimi_k3_config() -> Llama3Config {
Llama3Config {
arch: ModelArch::KimiK3,
vocab_size: 256,
hidden_dim: 64,
intermediate_dim: 128,
num_heads: 8,
num_kv_heads: 8,
num_layers: 8,
max_seq_len: 4096,
head_dim: 8,
rope_theta: 10000.0,
norm_eps: 1e-5,
attention_extras: None,
ssm: None,
moe: None,
gemma3n: None,
gemma4: None,
deepseek_v3: None,
kimi_delta: Some(KimiDeltaConfig {
full_attn_layers: Some(vec![3, 7]),
kda_layers: Some(vec![0, 1, 2, 4, 5, 6]),
kda_head_dim: Some(8),
kda_num_heads: Some(8),
kda_short_conv_kernel_size: Some(4),
kda_use_full_rank_gate: Some(true),
kda_gate_lower_bound: Some(-5.0),
q_lora_rank: Some(16),
kv_lora_rank: Some(8),
qk_nope_head_dim: Some(8),
qk_rope_head_dim: Some(4),
v_head_dim: Some(8),
mla_use_nope: Some(true),
mla_use_output_gate: Some(true),
attn_res_block_size: Some(4),
situ_beta: Some(4.0),
situ_linear_beta: Some(25.0),
n_routed_experts: Some(32),
num_experts_per_tok: Some(4),
n_shared_experts: Some(2),
num_expert_group: Some(1),
topk_group: Some(1),
moe_router_activation: Some("sigmoid".to_string()),
moe_topk_method: Some("noaux_tc".to_string()),
moe_intermediate_size: Some(64),
first_k_dense_replace: Some(1),
moe_renormalize: Some(true),
routed_expert_hidden_size: Some(32),
latent_moe_use_norm: Some(true),
routed_scaling_factor: Some(1.0),
num_nextn_predict_layers: None,
mxfp4_group_size: None,
mxfp4_num_bits: None,
}),
}
}
fn dummy_weights(hidden_dim: usize, vocab_size: usize, buf: &[u8]) -> KimiK3ModelWeights<'_> {
KimiK3ModelWeights {
token_embd: WeightRef {
data: buf,
qtype: GgmlType::F32,
rows: vocab_size,
cols: hidden_dim,
},
output_norm: vec![1.0; hidden_dim],
output: WeightRef {
data: buf,
qtype: GgmlType::F32,
rows: vocab_size,
cols: hidden_dim,
},
output_res_score: vec![1.0; hidden_dim],
layers: Vec::new(),
}
}
#[test]
fn model_new_allocates_per_layer_caches_matching_layer_type() {
let config = tiny_kimi_k3_config();
let hidden = config.hidden_dim;
let vocab = config.vocab_size;
let buf = vec![0u8; vocab * hidden * 4];
let weights = dummy_weights(hidden, vocab, &buf);
let model = KimiK3Model::new(weights, config).expect("model must construct");
assert_eq!(model.num_layers(), 8);
assert_eq!(model.layer_caches.len(), 8);
for (il, cache) in model.layer_caches.iter().enumerate() {
match cache {
KimiK3LayerCache::Mla(_) => {
assert!(il == 3 || il == 7, "layer {il} unexpectedly MLA");
}
KimiK3LayerCache::Kda(heads) => {
assert!(il != 3 && il != 7, "layer {il} unexpectedly KDA");
assert_eq!(heads.len(), 8, "KDA layer {il} must have 8 head caches");
}
}
}
}
#[test]
fn model_new_returns_err_when_kimi_delta_config_absent() {
let mut config = tiny_kimi_k3_config();
config.kimi_delta = None;
let buf = vec![0u8; 4];
let weights = dummy_weights(config.hidden_dim, config.vocab_size, &buf);
let Err(err) = KimiK3Model::new(weights, config) else {
panic!("expected Err on missing kimi_delta");
};
assert!(
err.contains("kimi_delta"),
"expected error to mention kimi_delta, got: {err}"
);
}
#[test]
fn model_reset_clears_all_caches_and_block_attnres() {
let config = tiny_kimi_k3_config();
let hidden = config.hidden_dim;
let vocab = config.vocab_size;
let buf = vec![0u8; vocab * hidden * 4];
let weights = dummy_weights(hidden, vocab, &buf);
let mut model = KimiK3Model::new(weights, config).expect("construct");
if let KimiK3LayerCache::Mla(c) = &mut model.layer_caches[3] {
c.append(&[0.0; 8], &[0.0; 4]);
c.append(&[0.0; 8], &[0.0; 4]);
assert_eq!(c.n_positions(), 2);
}
model.attn_res_state.bank(&vec![1.0_f32; hidden]);
assert_eq!(model.attn_res_state.banked_count(), 1);
model.reset();
assert_eq!(model.mla_cache_positions(3), Some(0));
assert_eq!(model.attn_res_state.banked_count(), 0);
}
#[test]
fn model_forward_panics_on_empty_layers_vec() {
let config = tiny_kimi_k3_config();
let hidden = config.hidden_dim;
let vocab = config.vocab_size;
let buf = vec![0u8; vocab * hidden * 4];
let weights = dummy_weights(hidden, vocab, &buf);
let mut model = KimiK3Model::new(weights, config).expect("construct");
let panic_msg = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| model.forward(0)))
.expect_err("forward must panic when weights.layers is empty");
let s = panic_msg
.downcast_ref::<String>()
.map(String::as_str)
.or_else(|| panic_msg.downcast_ref::<&str>().copied())
.unwrap_or("");
assert!(
s.contains("index out of bounds"),
"panic message must reference index out of bounds (unpopulated \
weights.layers), got: {s}"
);
}
fn build_model_for_snapshot_tests() -> KimiK3Model<'static> {
let config = tiny_kimi_k3_config();
let hidden = config.hidden_dim;
let vocab = config.vocab_size;
let buf: &'static [u8] = Vec::leak(vec![0u8; vocab * hidden * 4]);
let weights = dummy_weights(hidden, vocab, buf);
KimiK3Model::new(weights, config).expect("construct")
}
#[test]
fn phase10_new_initializes_dspark_fields() {
let model = build_model_for_snapshot_tests();
assert_eq!(model.seq_len(), 0);
assert_eq!(model.snapshot_ring_len(), 0);
assert_eq!(
model.max_snapshot_ring,
super::KIMI_K3_DEFAULT_MAX_SNAPSHOT_RING
);
}
#[test]
fn phase10_snapshot_captures_state() {
let mut model = build_model_for_snapshot_tests();
if let KimiK3LayerCache::Mla(c) = &mut model.layer_caches[3] {
c.append(&[1.5; 8], &[2.5; 4]);
}
model
.attn_res_state
.bank(&vec![0.7; model.config.hidden_dim]);
model.token_count = 5;
let snap = model.snapshot();
assert_eq!(snap.token_count, 5);
if let KimiK3LayerCache::Mla(c) = &snap.layer_caches[3] {
assert_eq!(c.n_positions(), 1);
} else {
panic!("layer 3 should be MLA");
}
}
#[test]
fn phase10_restore_replaces_state() {
let mut model = build_model_for_snapshot_tests();
let baseline = model.snapshot();
if let KimiK3LayerCache::Mla(c) = &mut model.layer_caches[3] {
c.append(&[9.9; 8], &[9.9; 4]);
}
model.token_count = 42;
model.restore(baseline);
assert_eq!(model.seq_len(), 0);
assert_eq!(model.snapshot_ring_len(), 0);
if let KimiK3LayerCache::Mla(c) = &model.layer_caches[3] {
assert_eq!(c.n_positions(), 0);
}
}
#[test]
fn phase10_reset_clears_dspark_state() {
let mut model = build_model_for_snapshot_tests();
model.token_count = 7;
model.snapshot_ring.push_back(model.snapshot());
model.snapshot_ring.push_back(model.snapshot());
assert_eq!(model.snapshot_ring_len(), 2);
model.reset();
assert_eq!(model.seq_len(), 0);
assert_eq!(model.snapshot_ring_len(), 0);
}
#[test]
fn phase10_set_max_snapshot_ring_trims_oldest() {
let mut model = build_model_for_snapshot_tests();
for _ in 0..5 {
model.snapshot_ring.push_back(model.snapshot());
}
assert_eq!(model.snapshot_ring_len(), 5);
model.set_max_snapshot_ring(3);
assert_eq!(model.snapshot_ring_len(), 3);
assert_eq!(model.max_snapshot_ring, 3);
}
#[test]
#[should_panic(expected = "must be > 0")]
fn phase10_set_max_snapshot_ring_rejects_zero() {
let mut model = build_model_for_snapshot_tests();
model.set_max_snapshot_ring(0);
}
#[test]
fn phase10_rollback_to_current_position_is_noop() {
let mut model = build_model_for_snapshot_tests();
model.token_count = 3;
model.rollback_to(3);
assert_eq!(model.seq_len(), 3);
}
#[test]
#[should_panic(expected = "exceeds current token_count")]
fn phase10_rollback_to_future_panics() {
let mut model = build_model_for_snapshot_tests();
model.token_count = 3;
model.rollback_to(10);
}
#[test]
#[should_panic(expected = "exceeds snapshot_ring size")]
fn phase10_rollback_to_beyond_ring_capacity_panics() {
let mut model = build_model_for_snapshot_tests();
model.token_count = 5;
model.snapshot_ring.push_back(model.snapshot());
model.snapshot_ring.push_back(model.snapshot());
model.rollback_to(0);
}
#[test]
fn phase10_rollback_within_ring_restores_snapshot() {
let mut model = build_model_for_snapshot_tests();
let snap0 = model.snapshot();
model.snapshot_ring.push_back(snap0);
if let KimiK3LayerCache::Mla(c) = &mut model.layer_caches[3] {
c.append(&[1.0; 8], &[1.0; 4]);
}
model.token_count = 1;
let snap1 = model.snapshot();
model.snapshot_ring.push_back(snap1);
if let KimiK3LayerCache::Mla(c) = &mut model.layer_caches[3] {
c.append(&[2.0; 8], &[2.0; 4]);
}
model.token_count = 2;
model.rollback_to(1);
assert_eq!(model.seq_len(), 1);
if let KimiK3LayerCache::Mla(c) = &model.layer_caches[3] {
assert_eq!(c.n_positions(), 1);
}
assert_eq!(model.snapshot_ring_len(), 0);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12_f16_roundtrip_precision_zero() {
for &v in &[0.0_f32, -0.0_f32] {
let bits = super::f32_to_f16_bits(v);
let back = super::f16_bits_to_f32(bits);
assert_eq!(back.to_bits(), v.to_bits(), "0 roundtrip preserves sign");
}
}
#[cfg(feature = "dspark")]
#[test]
fn phase12_f16_roundtrip_precision_normal() {
for &v in &[1.0_f32, -1.0, 0.5, 3.14159, -2.71828, 100.0, -1000.0] {
let bits = super::f32_to_f16_bits(v);
let back = super::f16_bits_to_f32(bits);
let rel_err = ((back - v) / v).abs();
assert!(
rel_err < 1e-3,
"roundtrip precision {v} → {back}, rel_err = {rel_err}"
);
}
}
#[cfg(feature = "dspark")]
#[test]
fn phase12_f16_special_values() {
let inf_bits = super::f32_to_f16_bits(f32::INFINITY);
let inf_back = super::f16_bits_to_f32(inf_bits);
assert!(inf_back.is_infinite() && inf_back > 0.0);
let ninf_bits = super::f32_to_f16_bits(f32::NEG_INFINITY);
let ninf_back = super::f16_bits_to_f32(ninf_bits);
assert!(ninf_back.is_infinite() && ninf_back < 0.0);
let nan_bits = super::f32_to_f16_bits(f32::NAN);
let nan_back = super::f16_bits_to_f32(nan_bits);
assert!(nan_back.is_nan());
let huge_bits = super::f32_to_f16_bits(1.0e30_f32);
let huge_back = super::f16_bits_to_f32(huge_bits);
assert!(huge_back.is_infinite() && huge_back > 0.0);
let tiny_bits = super::f32_to_f16_bits(1.0e-10_f32);
let tiny_back = super::f16_bits_to_f32(tiny_bits);
assert_eq!(tiny_back, 0.0);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12_snapshot_compact_roundtrip_precision() {
let mut model = build_model_for_snapshot_tests();
if let KimiK3LayerCache::Mla(c) = &mut model.layer_caches[3] {
c.append(&[1.5_f32; 8], &[-2.75_f32; 4]);
c.append(&[3.125_f32; 8], &[0.0625_f32; 4]);
}
model
.attn_res_state
.bank(&vec![0.7_f32; model.config.hidden_dim]);
model.token_count = 5;
let compact_snap = model.snapshot_compact();
model.restore_compact(compact_snap);
assert_eq!(model.seq_len(), 5);
if let KimiK3LayerCache::Mla(c) = &model.layer_caches[3] {
assert_eq!(c.n_positions(), 2);
for (i, &v) in c.c_k.iter().enumerate() {
let expected = if i < 8 { 1.5 } else { 3.125 };
let diff = (v - expected).abs();
assert!(diff < 1e-2, "c_k[{i}] = {v} (expected {expected})");
}
} else {
panic!("layer 3 should be MLA");
}
assert_eq!(model.attn_res_state.banked.len(), 1);
for &v in &model.attn_res_state.banked[0] {
let diff = (v - 0.7_f32).abs();
assert!(diff < 1e-2, "banked value = {v} (expected 0.7)");
}
}
#[cfg(feature = "dspark")]
#[test]
fn phase12_set_compact_mode_clears_both_rings() {
let mut model = build_model_for_snapshot_tests();
model.snapshot_ring.push_back(model.snapshot());
model
.snapshot_ring_compact
.push_back(model.snapshot_compact());
assert_eq!(model.snapshot_ring.len(), 1);
assert_eq!(model.snapshot_ring_compact.len(), 1);
model.set_snapshot_compact_mode(true);
assert!(model.is_snapshot_compact_mode());
assert_eq!(model.snapshot_ring.len(), 0);
assert_eq!(model.snapshot_ring_compact.len(), 0);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12_set_compact_mode_noop_when_unchanged() {
let mut model = build_model_for_snapshot_tests();
model.snapshot_ring.push_back(model.snapshot());
assert!(!model.is_snapshot_compact_mode());
model.set_snapshot_compact_mode(false);
assert_eq!(model.snapshot_ring.len(), 1);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12_snapshot_ring_len_reflects_active_ring() {
let mut model = build_model_for_snapshot_tests();
model.snapshot_ring.push_back(model.snapshot());
model.snapshot_ring.push_back(model.snapshot());
assert_eq!(model.snapshot_ring_len(), 2);
model.set_snapshot_compact_mode(true);
model
.snapshot_ring_compact
.push_back(model.snapshot_compact());
model
.snapshot_ring_compact
.push_back(model.snapshot_compact());
model
.snapshot_ring_compact
.push_back(model.snapshot_compact());
assert_eq!(model.snapshot_ring_len(), 3);
assert_eq!(model.snapshot_ring.len(), 0);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12_bytes_estimate_shows_compression() {
let mut model = build_model_for_snapshot_tests();
for _ in 0..3 {
model.snapshot_ring.push_back(model.snapshot());
}
let full_bytes = model.snapshot_ring_bytes_estimate();
model.snapshot_ring.clear();
for _ in 0..3 {
model
.snapshot_ring_compact
.push_back(model.snapshot_compact());
}
let compact_bytes = model.snapshot_ring_bytes_estimate();
assert!(
compact_bytes * 2 <= full_bytes + full_bytes / 10,
"compact ({compact_bytes}) should be ~half of full ({full_bytes})"
);
assert!(compact_bytes > 0, "compact bytes should be > 0");
}
#[cfg(feature = "dspark")]
#[test]
fn phase12_push_snapshot_bounded_uses_compact_ring() {
let mut model = build_model_for_snapshot_tests();
model.set_snapshot_compact_mode(true);
model.set_max_snapshot_ring(3);
for _ in 0..5 {
model.push_snapshot_bounded();
}
assert_eq!(model.snapshot_ring_compact.len(), 3);
assert_eq!(model.snapshot_ring.len(), 0);
}
#[cfg(feature = "dspark")]
fn synth_head_update(d_k: usize, d_v: usize) -> super::KimiDeltaHeadUpdate {
super::KimiDeltaHeadUpdate {
q_pre: (0..d_k).map(|i| i as f32 * 0.01).collect(),
k_pre: (0..d_k).map(|i| i as f32 * 0.02).collect(),
v_pre: (0..d_v).map(|i| i as f32 * 0.03).collect(),
k_conv: (0..d_k).map(|i| (i as f32).sin() * 0.5).collect(),
v_conv: (0..d_v).map(|i| (i as f32).cos() * 0.7).collect(),
alpha: (0..d_k).map(|i| 0.9 + (i as f32) * 0.001).collect(),
beta: 0.3,
}
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_head_update_construction() {
let u = synth_head_update(8, 8);
assert_eq!(u.q_pre.len(), 8);
assert_eq!(u.k_pre.len(), 8);
assert_eq!(u.v_pre.len(), 8);
assert_eq!(u.k_conv.len(), 8);
assert_eq!(u.v_conv.len(), 8);
assert_eq!(u.alpha.len(), 8);
assert!((u.beta - 0.3).abs() < 1e-9);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_apply_update_state_matches_kimi_delta_step() {
let d_k = 8_usize;
let d_v = 8_usize;
let kernel_size = 4_usize;
let u = synth_head_update(d_k, d_v);
let mut cache_baseline = super::KimiDeltaHeadCache::new(d_k, d_v, kernel_size);
super::kimi_delta_step(
&mut cache_baseline.state,
&u.k_conv,
&u.v_conv,
&u.alpha,
u.beta,
);
let mut cache_replay = super::KimiDeltaHeadCache::new(d_k, d_v, kernel_size);
cache_replay.apply_update(&u);
assert_eq!(
cache_baseline.state.as_slice(),
cache_replay.state.as_slice(),
"apply_update state result must match direct kimi_delta_step"
);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_apply_update_advances_conv_state_rings() {
let d_k = 8_usize;
let d_v = 8_usize;
let kernel_size = 4_usize; let u = synth_head_update(d_k, d_v);
let mut cache = super::KimiDeltaHeadCache::new(d_k, d_v, kernel_size);
assert_eq!(cache.ring_pos_q, 0);
assert_eq!(cache.ring_pos_k, 0);
assert_eq!(cache.ring_pos_v, 0);
cache.apply_update(&u);
assert_eq!(cache.ring_pos_q, 1);
assert_eq!(cache.ring_pos_k, 1);
assert_eq!(cache.ring_pos_v, 1);
assert_eq!(&cache.conv_state_q[0..d_k], u.q_pre.as_slice());
cache.apply_update(&u);
assert_eq!(cache.ring_pos_q, 2);
cache.apply_update(&u);
assert_eq!(cache.ring_pos_q, 0); }
#[cfg(feature = "dspark")]
#[test]
fn phase12b_snapshot_delta_from_uses_current_state_as_base() {
let mut model = build_model_for_snapshot_tests();
if let KimiK3LayerCache::Mla(c) = &mut model.layer_caches[3] {
c.append(&[2.5_f32; 8], &[-1.25_f32; 4]);
}
model.token_count = 7;
let delta = model.snapshot_delta_from();
assert_eq!(delta.base_snapshot.token_count, 7);
assert!(delta.per_step_updates.is_empty());
if let KimiK3LayerCache::Mla(c) = &delta.base_snapshot.layer_caches[3] {
assert_eq!(c.n_positions(), 1);
}
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_restore_from_delta_empty_updates_is_base_restore() {
let mut model = build_model_for_snapshot_tests();
let baseline = model.snapshot_delta_from();
if let KimiK3LayerCache::Mla(c) = &mut model.layer_caches[3] {
c.append(&[9.9_f32; 8], &[9.9_f32; 4]);
}
model.token_count = 99;
model.restore_from_delta(baseline);
assert_eq!(model.seq_len(), 0);
if let KimiK3LayerCache::Mla(c) = &model.layer_caches[3] {
assert_eq!(c.n_positions(), 0);
}
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_restore_from_delta_replays_updates_on_kda_layers() {
let mut model = build_model_for_snapshot_tests();
let d_k = 8_usize;
let d_v = 8_usize;
let num_kda_layers = 6_usize;
let num_heads = 8_usize;
let base_snap = model.snapshot_delta_from();
let mut per_step_updates: Vec<Vec<Vec<super::KimiDeltaHeadUpdate>>> = Vec::new();
for _step in 0..2 {
let mut layers = Vec::new();
for _kda_layer in 0..num_kda_layers {
let mut heads = Vec::new();
for _h in 0..num_heads {
heads.push(synth_head_update(d_k, d_v));
}
layers.push(heads);
}
per_step_updates.push(layers);
}
let delta = super::KimiK3ModelSnapshotDelta {
base_snapshot: base_snap.base_snapshot,
per_step_updates,
};
model.restore_from_delta(delta);
assert_eq!(model.seq_len(), 2, "token_count = base 0 + 2 steps");
for il in [0, 1, 2, 4, 5, 6] {
if let KimiK3LayerCache::Kda(heads) = &model.layer_caches[il] {
for head in heads {
assert_eq!(
head.ring_pos_q, 2,
"layer {il} head ring_pos_q should be 2 after 2 apply_updates"
);
}
} else {
panic!("layer {il} should be KDA");
}
}
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_snapshot_delta_bytes_estimate_grows_with_updates() {
let model = build_model_for_snapshot_tests();
let d_k = 8_usize;
let d_v = 8_usize;
let base_delta = model.snapshot_delta_from();
let base_bytes = super::KimiK3Model::snapshot_delta_bytes_estimate(&base_delta);
let mut with_step = base_delta;
let heads_per_layer: Vec<super::KimiDeltaHeadUpdate> =
(0..8).map(|_| synth_head_update(d_k, d_v)).collect();
let single_step: Vec<Vec<super::KimiDeltaHeadUpdate>> =
(0..6).map(|_| heads_per_layer.clone()).collect();
with_step.per_step_updates.push(single_step);
let with_step_bytes = super::KimiK3Model::snapshot_delta_bytes_estimate(&with_step);
assert!(
with_step_bytes > base_bytes,
"adding a step should increase bytes: base {base_bytes}, with_step {with_step_bytes}"
);
let step_bytes = with_step_bytes - base_bytes;
assert!(
step_bytes > 0 && step_bytes < 20_000,
"per-step bytes reasonable range: {step_bytes}"
);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_part3b_set_delta_mode_clears_all_rings() {
let mut model = build_model_for_snapshot_tests();
model.snapshot_ring.push_back(model.snapshot());
model
.snapshot_ring_compact
.push_back(model.snapshot_compact());
model
.snapshot_ring_delta
.push_back(model.snapshot_delta_from());
assert_eq!(model.snapshot_ring.len(), 1);
assert_eq!(model.snapshot_ring_compact.len(), 1);
assert_eq!(model.snapshot_ring_delta.len(), 1);
model.set_snapshot_delta_mode(true);
assert!(model.is_snapshot_delta_mode());
assert_eq!(model.snapshot_ring.len(), 0);
assert_eq!(model.snapshot_ring_compact.len(), 0);
assert_eq!(model.snapshot_ring_delta.len(), 0);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_part3b_set_delta_mode_disables_compact() {
let mut model = build_model_for_snapshot_tests();
model.set_snapshot_compact_mode(true);
assert!(model.is_snapshot_compact_mode());
model.set_snapshot_delta_mode(true);
assert!(model.is_snapshot_delta_mode());
assert!(
!model.is_snapshot_compact_mode(),
"delta mode enable should disable compact"
);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_part3b_set_compact_mode_disables_delta() {
let mut model = build_model_for_snapshot_tests();
model.set_snapshot_delta_mode(true);
assert!(model.is_snapshot_delta_mode());
model.set_snapshot_compact_mode(true);
assert!(model.is_snapshot_compact_mode());
assert!(
!model.is_snapshot_delta_mode(),
"compact mode enable should disable delta"
);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_part3b_set_delta_mode_noop_when_unchanged() {
let mut model = build_model_for_snapshot_tests();
model
.snapshot_ring_delta
.push_back(model.snapshot_delta_from());
assert!(!model.is_snapshot_delta_mode());
model.set_snapshot_delta_mode(false);
assert_eq!(model.snapshot_ring_delta.len(), 1);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_part3b_snapshot_ring_len_reflects_delta_mode() {
let mut model = build_model_for_snapshot_tests();
model.set_snapshot_delta_mode(true);
model
.snapshot_ring_delta
.push_back(model.snapshot_delta_from());
model
.snapshot_ring_delta
.push_back(model.snapshot_delta_from());
assert_eq!(model.snapshot_ring_len(), 2);
assert_eq!(model.snapshot_ring.len(), 0);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_part3b_reset_clears_delta_ring() {
let mut model = build_model_for_snapshot_tests();
model
.snapshot_ring_delta
.push_back(model.snapshot_delta_from());
assert_eq!(model.snapshot_ring_delta.len(), 1);
model.reset();
assert_eq!(model.snapshot_ring_delta.len(), 0);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_part3b_snapshot_ring_bytes_estimate_includes_delta() {
let mut model = build_model_for_snapshot_tests();
let base_bytes = model.snapshot_ring_bytes_estimate();
model
.snapshot_ring_delta
.push_back(model.snapshot_delta_from());
let with_delta_bytes = model.snapshot_ring_bytes_estimate();
assert!(
with_delta_bytes > base_bytes,
"adding delta to ring should increase bytes: base {base_bytes}, with_delta {with_delta_bytes}"
);
}
#[cfg(feature = "dspark")]
#[test]
fn phase12b_part3b_delta_rollback_via_synthetic_ring() {
let mut model = build_model_for_snapshot_tests();
model.set_snapshot_delta_mode(true);
let d_k = 8_usize;
let d_v = 8_usize;
let num_kda_layers = 6_usize;
let num_heads = 8_usize;
let base = model.snapshot();
model.token_count = 2;
let synth_update = super::KimiDeltaHeadUpdate {
q_pre: vec![0.0; d_k],
k_pre: vec![0.0; d_k],
v_pre: vec![0.0; d_v],
k_conv: vec![0.0; d_k],
v_conv: vec![0.0; d_v],
alpha: vec![1.0; d_k],
beta: 0.0,
};
let mut per_step_updates = Vec::new();
for _step in 0..2 {
let mut layers = Vec::new();
for _kda_layer in 0..num_kda_layers {
let mut heads = Vec::new();
for _h in 0..num_heads {
heads.push(synth_update.clone());
}
layers.push(heads);
}
per_step_updates.push(layers);
}
model
.snapshot_ring_delta
.push_back(super::KimiK3ModelSnapshotDelta {
base_snapshot: base,
per_step_updates,
});
model.rollback_to(1);
assert_eq!(model.seq_len(), 1);
assert_eq!(model.snapshot_ring_delta.len(), 1);
let after_delta = &model.snapshot_ring_delta[0];
assert!(
after_delta.per_step_updates.is_empty(),
"new base has no updates"
);
}
}
#[allow(dead_code)]
struct DeepSeekV3MtpWeights<'a> {
enorm: Vec<f32>,
hnorm: Vec<f32>,
eh_proj: WeightRef<'a>,
block: DeepSeekV3LayerWeights<'a>,
final_norm: Vec<f32>,
}
enum RoutedExpertStorage<'a> {
InMemory {
gate: WeightRef<'a>,
up: WeightRef<'a>,
down: WeightRef<'a>,
},
#[allow(dead_code)]
Streaming {
pool: std::sync::Arc<crate::deepseek_streaming::StreamingExpertPool>,
layer_idx: usize,
},
}
struct DeltaNetLayerWeights<'a> {
attn_norm: Vec<f32>,
ssm_in: Option<WeightRef<'a>>,
attn_qkv: Option<WeightRef<'a>>,
attn_gate: Option<WeightRef<'a>>,
ssm_a: Option<Vec<f32>>,
ssm_dt_bias: Option<Vec<f32>>,
ssm_norm: Option<Vec<f32>>,
conv1d_weight: Vec<f32>,
conv1d_bias: Vec<f32>,
alpha_proj: WeightRef<'a>,
beta_proj: WeightRef<'a>,
ssm_out: WeightRef<'a>,
ffn_norm: Vec<f32>,
gate_proj: WeightRef<'a>,
up_proj: WeightRef<'a>,
down_proj: WeightRef<'a>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayerQuantMode {
Ternary,
Binary,
SparseTernary { n_keep: usize },
}
#[derive(Debug, Clone)]
pub struct LayerQuantConfig {
pub attention_mode: LayerQuantMode,
pub ffn_mode: LayerQuantMode,
}
#[derive(Debug, Clone)]
pub struct MixedPrecisionConfig {
pub layer_configs: Vec<LayerQuantConfig>,
}
impl LayerQuantConfig {
pub const fn full_ternary() -> Self {
Self {
attention_mode: LayerQuantMode::Ternary,
ffn_mode: LayerQuantMode::Ternary,
}
}
pub const fn aggressive_compression(n_keep: usize) -> Self {
Self {
attention_mode: LayerQuantMode::Ternary,
ffn_mode: LayerQuantMode::SparseTernary { n_keep },
}
}
}
impl MixedPrecisionConfig {
pub fn uniform(config: LayerQuantConfig, num_layers: usize) -> Self {
Self {
layer_configs: vec![config; num_layers],
}
}
pub fn target_10gb(num_layers: usize) -> Self {
Self::uniform(
LayerQuantConfig::aggressive_compression(8), num_layers,
)
}
pub fn get(&self, layer_idx: usize) -> &LayerQuantConfig {
if layer_idx < self.layer_configs.len() {
&self.layer_configs[layer_idx]
} else {
self.layer_configs.last().unwrap_or_else(|| {
&self.layer_configs[0]
})
}
}
pub fn estimate_bits_per_param(&self) -> f32 {
if self.layer_configs.is_empty() {
return 1.58;
}
let mut total_bits = 0.0f32;
let n = self.layer_configs.len() as f32;
for cfg in &self.layer_configs {
let attn_bits = mode_bits(cfg.attention_mode);
let ffn_bits = mode_bits(cfg.ffn_mode);
total_bits += 0.30 * attn_bits + 0.70 * ffn_bits;
}
total_bits / n
}
}
fn mode_bits(mode: LayerQuantMode) -> f32 {
match mode {
LayerQuantMode::Ternary => 1.58,
LayerQuantMode::Binary => 1.0,
LayerQuantMode::SparseTernary { n_keep } => {
let density = n_keep as f32 / 16.0; density * 1.58 + 2.0 * (1.0 / 16.0) }
}
}
struct TernaryLayerWeights {
attn_norm: Vec<f32>,
q_proj: TernaryMatrix,
k_proj: TernaryMatrix,
v_proj: TernaryMatrix,
o_proj: TernaryMatrix,
ffn_norm: Vec<f32>,
gate_proj: TernaryMatrix,
up_proj: TernaryMatrix,
down_proj: TernaryMatrix,
}
struct SparseTernaryLayerWeights {
attn_norm: Vec<f32>,
q_proj: SparseTernaryMatrix,
k_proj: SparseTernaryMatrix,
v_proj: SparseTernaryMatrix,
o_proj: SparseTernaryMatrix,
ffn_norm: Vec<f32>,
gate_proj: SparseTernaryMatrix,
up_proj: SparseTernaryMatrix,
down_proj: SparseTernaryMatrix,
}
pub struct Llama3Model<'a> {
pub config: Llama3Config,
embedding: Vec<f32>,
layers: Vec<LayerWeights<'a>>,
output_norm: Vec<f32>,
output_proj: WeightRef<'a>,
kv_cache: KvCache,
pub rope_freqs: Option<Vec<f32>>,
ternary_layers: Option<Vec<TernaryLayerWeights>>,
ternary_output_proj: Option<TernaryMatrix>,
sparse_ternary_layers: Option<Vec<SparseTernaryLayerWeights>>,
sparse_ternary_output: Option<SparseTernaryMatrix>,
per_layer_token_embd_raw: Option<&'a [u8]>,
per_layer_token_embd_qtype: Option<crate::gguf::GgmlType>,
per_layer_model_proj: Option<WeightRef<'a>>,
per_layer_proj_norm: Option<Vec<f32>>,
altup_proj: Option<Vec<f32>>,
altup_unembd_proj: Option<Vec<f32>>,
deltanet_layers: Option<Vec<DeltaNetLayerWeights<'a>>>,
layer_kind_map: Vec<LayerKind>,
deltanet_state: Vec<Vec<f32>>,
deltanet_conv_state: Vec<Vec<f32>>,
deltanet_conv_ring_pos: Vec<usize>,
deepseek_v3_layers: Option<Vec<DeepSeekV3LayerWeights<'a>>>,
deepseek_v3_mtp: Option<DeepSeekV3MtpWeights<'a>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum LayerKind {
Attention(usize),
DeltaNet(usize),
}
impl<'a> Llama3Model<'a> {
pub fn from_gguf(gguf: &'a GgufFile<'a>) -> Option<Self> {
let config = Llama3Config::from_gguf(gguf)?;
let embedding = gguf.tensor_to_f32("token_embd.weight")?;
let output_norm = gguf.tensor_to_f32("output_norm.weight")?;
let output_proj =
load_weight_ref(gguf, "output.weight", config.vocab_size, config.hidden_dim).or_else(
|| {
load_weight_ref(
gguf,
"token_embd.weight",
config.vocab_size,
config.hidden_dim,
)
},
)?;
let is_deepseek_model = matches!(config.arch, ModelArch::DeepSeekV3);
let is_hybrid_model = config.is_hybrid();
let mut layers = Vec::with_capacity(if is_deepseek_model {
0
} else {
config.num_layers
});
let mut deltanet_layers_vec: Vec<DeltaNetLayerWeights<'a>> = Vec::new();
let mut deepseek_v3_layers_vec: Vec<DeepSeekV3LayerWeights<'a>> = Vec::new();
let deepseek_streaming_pool = if is_deepseek_model {
build_deepseek_streaming_pool(gguf, &config)
} else {
None
};
let mut layer_kind_map: Vec<LayerKind> = if is_hybrid_model {
Vec::with_capacity(config.num_layers)
} else {
Vec::new()
};
for i in 0..config.num_layers {
if is_deepseek_model {
let dsv = load_deepseek_v3_layer_weights(
gguf,
i,
&config,
deepseek_streaming_pool.as_ref(),
)?;
deepseek_v3_layers_vec.push(dsv);
} else if is_hybrid_model && config.is_deltanet_layer(i) {
let dn = load_deltanet_layer_weights(gguf, i, &config)?;
layer_kind_map.push(LayerKind::DeltaNet(deltanet_layers_vec.len()));
deltanet_layers_vec.push(dn);
} else {
let layer = load_layer_weights(gguf, i, &config)?;
if is_hybrid_model {
layer_kind_map.push(LayerKind::Attention(layers.len()));
}
layers.push(layer);
}
}
let deltanet_layers = if is_hybrid_model {
Some(deltanet_layers_vec)
} else {
None
};
let deepseek_v3_layers = if is_deepseek_model {
Some(deepseek_v3_layers_vec)
} else {
None
};
let deepseek_v3_mtp = if is_deepseek_model {
load_deepseek_v3_mtp_weights(gguf, &config, deepseek_streaming_pool.as_ref())
} else {
None
};
let deltanet_layer_count = deltanet_layers.as_ref().map_or(0, Vec::len);
let dn_num_kv_heads_state = config.linear_num_kv_heads().unwrap_or(config.num_kv_heads);
let dn_qk_dim_state = config.linear_qk_head_dim().unwrap_or(128);
let dn_v_dim_state = config.linear_kv_head_dim().unwrap_or(128);
let dn_num_v_heads_state = config.linear_num_v_heads().unwrap_or(config.num_heads);
let dn_conv_kernel = config.linear_conv_kernel_dim().unwrap_or(4);
let dn_conv_dim_state =
dn_qk_dim_state * dn_num_kv_heads_state * 2 + dn_v_dim_state * dn_num_v_heads_state;
let dn_state_elems = dn_num_v_heads_state * dn_qk_dim_state * dn_v_dim_state;
let dn_conv_ring_slots = dn_conv_kernel.saturating_sub(1);
let deltanet_state = (0..deltanet_layer_count)
.map(|_| vec![0.0f32; dn_state_elems])
.collect();
let deltanet_conv_state = (0..deltanet_layer_count)
.map(|_| vec![0.0f32; dn_conv_ring_slots * dn_conv_dim_state])
.collect();
let deltanet_conv_ring_pos = vec![0usize; deltanet_layer_count];
let kv_dim = if is_deepseek_model {
config.deepseek_kv_lora_rank().unwrap_or(0)
+ config.deepseek_qk_rope_head_dim().unwrap_or(0)
} else {
config.num_kv_heads * config.head_dim
};
let mut kv_cache = KvCache::new(config.num_layers, config.max_seq_len, kv_dim);
kv_cache.set_layer_map(config.build_kv_layer_map());
let rope_freqs: Option<Vec<f32>> =
gguf.tensor_to_f32("rope_freqs.weight").and_then(|scales| {
let half_dim = config.head_dim / 2;
if scales.len() != half_dim {
return None;
}
let needs_scaling = scales.iter().any(|&s| (s - 1.0).abs() > 0.01);
if needs_scaling {
Some(scales)
} else {
None
}
});
let (
per_layer_token_embd_raw,
per_layer_model_proj,
per_layer_proj_norm,
altup_proj,
altup_unembd_proj,
) = if matches!(config.arch, ModelArch::Gemma3n | ModelArch::Gemma4) {
let per_layer_token_embd_raw = gguf.tensor_data("per_layer_token_embd.weight");
let per_layer_model_proj = config.per_layer_input_embedding_dim().and_then(|dim| {
load_weight_ref(
gguf,
"per_layer_model_proj.weight",
config.num_layers * dim, config.hidden_dim, )
});
let per_layer_proj_norm = gguf.tensor_to_f32("per_layer_proj_norm.weight");
let altup_proj = if config.arch == ModelArch::Gemma3n {
gguf.tensor_to_f32("altup_proj.weight")
} else {
None
};
let altup_unembd_proj = if config.arch == ModelArch::Gemma3n {
gguf.tensor_to_f32("altup_unembd_proj.weight")
} else {
None
};
(
per_layer_token_embd_raw,
per_layer_model_proj,
per_layer_proj_norm,
altup_proj,
altup_unembd_proj,
)
} else {
(None, None, None, None, None)
};
let per_layer_token_embd_qtype = gguf
.tensor_info("per_layer_token_embd.weight")
.map(|info| info.qtype);
Some(Self {
config,
embedding,
layers,
output_norm,
output_proj,
kv_cache,
rope_freqs,
ternary_layers: None,
ternary_output_proj: None,
sparse_ternary_layers: None,
sparse_ternary_output: None,
per_layer_token_embd_raw,
per_layer_token_embd_qtype,
per_layer_model_proj,
per_layer_proj_norm,
altup_proj,
altup_unembd_proj,
deltanet_layers,
layer_kind_map,
deltanet_state,
deltanet_conv_state,
deltanet_conv_ring_pos,
deepseek_v3_layers,
deepseek_v3_mtp,
})
}
#[must_use]
pub fn has_deepseek_mtp(&self) -> bool {
self.deepseek_v3_mtp.is_some()
}
pub fn mtp_draft(&mut self, prev_hidden: &[f32], next_token: u32) -> Vec<f32> {
let mtp = self
.deepseek_v3_mtp
.as_ref()
.expect("mtp_draft called without a loaded MTP head — check has_deepseek_mtp() first");
let c = self.config.clone();
let hidden_dim = c.hidden_dim;
debug_assert_eq!(
prev_hidden.len(),
hidden_dim,
"prev_hidden length must match config.hidden_dim"
);
let num_heads = c.num_heads;
let q_lora_rank = c.deepseek_q_lora_rank().expect("q_lora_rank");
let kv_lora_rank = c.deepseek_kv_lora_rank().expect("kv_lora_rank");
let qk_nope = c.deepseek_qk_nope_head_dim().expect("qk_nope_head_dim");
let qk_rope = c.deepseek_qk_rope_head_dim().expect("qk_rope_head_dim");
let v_head_dim = c.deepseek_v_head_dim().expect("v_head_dim");
let q_head_total = qk_nope + qk_rope;
let kv_up_head_total = qk_nope + v_head_dim;
let emb_start = next_token as usize * hidden_dim;
let embed = &self.embedding[emb_start..emb_start + hidden_dim];
let mut hnorm_out = vec![0.0f32; hidden_dim];
let mut enorm_out = vec![0.0f32; hidden_dim];
rms_norm(prev_hidden, &mtp.hnorm, c.norm_eps, &mut hnorm_out);
rms_norm(embed, &mtp.enorm, c.norm_eps, &mut enorm_out);
let mut concat = Vec::with_capacity(2 * hidden_dim);
concat.extend_from_slice(&hnorm_out);
concat.extend_from_slice(&enorm_out);
let mut hidden = vec![0.0f32; hidden_dim];
mtp.eh_proj.matvec(&concat, &mut hidden);
let block = &mtp.block;
let mut norm_buf = vec![0.0f32; hidden_dim];
rms_norm(&hidden, &block.attn_norm, c.norm_eps, &mut norm_buf);
let mut q_a_buf = vec![0.0f32; q_lora_rank];
let mut q_a_normed = vec![0.0f32; q_lora_rank];
let mut q_full = vec![0.0f32; num_heads * q_head_total];
match &block.q {
DeepSeekQProjection::LoRA {
q_a_proj,
q_a_norm,
q_b_proj,
} => {
q_a_proj.matvec(&norm_buf, &mut q_a_buf);
rms_norm(&q_a_buf, q_a_norm, c.norm_eps, &mut q_a_normed);
q_b_proj.matvec(&q_a_normed, &mut q_full);
}
DeepSeekQProjection::Dense { .. } => {
unreachable!("MTP head is V3-only; V2 / V2-Lite never load a dense-Q MTP block")
}
}
let mut kv_a_full = vec![0.0f32; kv_lora_rank + qk_rope];
let mut kv_a_normed = vec![0.0f32; kv_lora_rank];
let mut kv_up = vec![0.0f32; num_heads * kv_up_head_total];
block.kv_a_proj_with_mqa.matvec(&norm_buf, &mut kv_a_full);
let (kv_a_slice, k_pe_shared) = kv_a_full.split_at_mut(kv_lora_rank);
rms_norm(kv_a_slice, &block.kv_a_norm, c.norm_eps, &mut kv_a_normed);
block.kv_b_proj.matvec(&kv_a_normed, &mut kv_up);
for h in 0..num_heads {
let q_head_off = h * q_head_total;
let q_pe_slice = &mut q_full[q_head_off + qk_nope..q_head_off + q_head_total];
apply_rope_auto(
q_pe_slice,
0,
qk_rope,
c.rope_theta,
self.rope_freqs.as_deref(),
true, );
}
apply_rope_auto(
k_pe_shared,
0,
qk_rope,
c.rope_theta,
self.rope_freqs.as_deref(),
true,
);
let mut attn_out = vec![0.0f32; num_heads * v_head_dim];
for h in 0..num_heads {
let head_off_up = h * kv_up_head_total;
let head_off_out = h * v_head_dim;
let v_h = &kv_up[head_off_up + qk_nope..head_off_up + kv_up_head_total];
attn_out[head_off_out..head_off_out + v_head_dim].copy_from_slice(v_h);
}
let mut o_buf = vec![0.0f32; hidden_dim];
block.o_proj.matvec(&attn_out, &mut o_buf);
for i in 0..hidden_dim {
hidden[i] += o_buf[i];
}
let moe = block
.moe
.as_ref()
.expect("MTP block requires MoE FFN weights (Phase 5a.2 loader guarantee)");
let mut down_buf = vec![0.0f32; hidden_dim];
forward_deepseek_moe_layer(&c, moe, &hidden, &mut norm_buf, &mut down_buf);
for i in 0..hidden_dim {
hidden[i] += down_buf[i];
}
rms_norm(&hidden, &mtp.final_norm, c.norm_eps, &mut norm_buf);
let mut logits = vec![0.0f32; c.vocab_size];
self.output_proj.matvec(&norm_buf, &mut logits);
logits
}
pub fn save_kv_cache(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
let file = std::fs::File::create(path)?;
let mut writer = std::io::BufWriter::new(file);
let fingerprint = kv_cache_fingerprint(&self.config);
self.kv_cache.write_to(&mut writer, fingerprint)?;
use std::io::Write;
writer.flush()
}
pub fn load_kv_cache(
&mut self,
path: impl AsRef<std::path::Path>,
) -> Result<(), KvCacheLoadError> {
let file = std::fs::File::open(path)?;
let mut reader = std::io::BufReader::new(file);
let fingerprint = kv_cache_fingerprint(&self.config);
self.kv_cache.read_from(&mut reader, fingerprint)?;
Ok(())
}
pub fn kv_cache_seq_len(&self) -> usize {
self.kv_cache.seq_len()
}
pub fn per_layer_embedding_for_token(&self, token_id: u32) -> Option<Vec<f32>> {
use crate::gguf::GgmlType;
let raw = self.per_layer_token_embd_raw?;
let qtype = self.per_layer_token_embd_qtype?;
let per_layer_dim = self.config.per_layer_input_embedding_dim()?;
let elements_per_token = self.config.num_layers * per_layer_dim;
let qk = qtype.elements_per_block();
let block_bytes = qtype.block_bytes();
if qk == 0 || block_bytes == 0 || !elements_per_token.is_multiple_of(qk) {
return None;
}
let blocks_per_token = elements_per_token / qk;
let start = (token_id as usize) * blocks_per_token * block_bytes;
let end = start + blocks_per_token * block_bytes;
if end > raw.len() {
return None;
}
let mut out = vec![0.0f32; elements_per_token];
match qtype {
GgmlType::Q5_1 => crate::gguf::dequantize_q5_1(&raw[start..end], &mut out),
GgmlType::Q6_K => crate::gguf::dequantize_q6_k_public(&raw[start..end], &mut out),
_ => return None,
}
Some(out)
}
#[must_use]
pub fn kv_seq_len(&self) -> usize {
self.kv_cache.seq_len()
}
pub fn kv_rollback_to(&mut self, pos: usize) {
self.kv_cache.rollback_to(pos);
}
pub fn reset(&mut self) {
self.kv_cache.clear();
}
pub fn load_ternary(&mut self, threshold_ratio: f32) {
let c = &self.config;
let kv_dim = c.num_kv_heads * c.head_dim;
let mut ternary_layers = Vec::with_capacity(c.num_layers);
for (i, layer) in self.layers.iter().enumerate() {
eprint!(" Ternarizing layer {i}/{} ...\r", c.num_layers);
ternary_layers.push(TernaryLayerWeights {
attn_norm: layer.attn_norm.clone(),
q_proj: ternarize_weight(
&layer.q_proj,
c.hidden_dim,
c.hidden_dim,
threshold_ratio,
),
k_proj: ternarize_weight(
layer
.k_proj
.as_ref()
.expect("k_proj required for ternarize"),
kv_dim,
c.hidden_dim,
threshold_ratio,
),
v_proj: ternarize_weight(
layer
.v_proj
.as_ref()
.expect("v_proj required for ternarize"),
kv_dim,
c.hidden_dim,
threshold_ratio,
),
o_proj: ternarize_weight(
&layer.o_proj,
c.hidden_dim,
c.hidden_dim,
threshold_ratio,
),
ffn_norm: layer.ffn_norm.clone(),
gate_proj: ternarize_weight(
layer
.gate_proj
.as_ref()
.expect("gate_proj required for ternarize"),
c.intermediate_dim,
c.hidden_dim,
threshold_ratio,
),
up_proj: ternarize_weight(
layer
.up_proj
.as_ref()
.expect("up_proj required for ternarize"),
c.intermediate_dim,
c.hidden_dim,
threshold_ratio,
),
down_proj: ternarize_weight(
layer
.down_proj
.as_ref()
.expect("down_proj required for ternarize"),
c.hidden_dim,
c.intermediate_dim,
threshold_ratio,
),
});
}
eprintln!(" Ternarized {}/{} layers", c.num_layers, c.num_layers);
self.ternary_output_proj = Some(ternarize_weight(
&self.output_proj,
c.vocab_size,
c.hidden_dim,
threshold_ratio,
));
self.ternary_layers = Some(ternary_layers);
}
pub fn clear_cache(&mut self) {
self.kv_cache.clear();
}
pub fn forward(&mut self, token_id: u32) -> Vec<f32> {
self.forward_with_layer_hook(token_id, |_layer_idx, _hidden| false)
}
#[cfg(feature = "dspark")]
pub fn forward_capture_hidden(
&mut self,
token_id: u32,
layer_idx: Option<usize>,
) -> (Vec<f32>, Vec<f32>) {
match self.config.arch {
ModelArch::Gemma3n
| ModelArch::Gemma4
| ModelArch::DeepSeekV3
| ModelArch::KimiK3
| ModelArch::Hy3 => {
unimplemented!(
"dspark forward_capture_hidden: arch {:?} uses specialized forward path that does not invoke layer hook",
self.config.arch
);
}
_ => {}
}
let num_layers = self.config.num_layers;
let target = layer_idx
.filter(|&n| n < num_layers)
.unwrap_or(num_layers.saturating_sub(1));
let mut captured: Vec<f32> = Vec::new();
let logits = self.forward_with_layer_hook(token_id, |idx, hidden| {
if idx == target {
captured.clear();
captured.extend_from_slice(hidden);
}
false
});
(logits, captured)
}
pub fn forward_with_surprise<F>(
&mut self,
token_id: u32,
surprise: Option<SurpriseVec<'_>>,
gate: F,
) -> Vec<f32>
where
F: Fn(usize, Option<SurpriseVec<'_>>) -> bool,
{
self.forward_with_layer_hook(token_id, |layer_idx, _hidden| gate(layer_idx, surprise))
}
pub fn forward_with_layer_hook<F>(&mut self, token_id: u32, hook: F) -> Vec<f32>
where
F: FnMut(usize, &mut Vec<f32>) -> bool,
{
if self.config.arch == ModelArch::Gemma3n {
return self.forward_gemma3n(token_id);
}
if self.config.arch == ModelArch::Gemma4 {
return self.forward_gemma4(token_id);
}
if self.config.arch == ModelArch::DeepSeekV3 {
return self.forward_deepseek_v3(token_id);
}
if self.config.arch == ModelArch::KimiK3 {
return self.forward_kimi_k3(token_id);
}
if self.config.arch == ModelArch::Hy3 {
return self.forward_hy3(token_id);
}
let c = &self.config;
let pos = self.kv_cache.seq_len();
let rope_freqs_ref = self.rope_freqs.as_deref();
let emb_start = token_id as usize * c.hidden_dim;
let mut hidden: Vec<f32> = self.embedding[emb_start..emb_start + c.hidden_dim].to_vec();
if std::env::var("ALICE_DUMP_DN0").is_ok() {
static ONCE_EMB: std::sync::Once = std::sync::Once::new();
let mut fire = false;
ONCE_EMB.call_once(|| fire = true);
if fire {
dump_slice("input_embed", &hidden, 3);
eprintln!("DN0 token_id={token_id}");
}
}
if c.arch == ModelArch::Gemma2 {
let scale = (c.hidden_dim as f32).sqrt();
for h in &mut hidden {
*h *= scale;
}
}
let mut norm_buf = vec![0.0f32; c.hidden_dim];
let kv_dim = c.num_kv_heads * c.head_dim;
let q_dim = c.num_heads * c.head_dim;
let mut q_buf = vec![0.0f32; q_dim * 2];
let mut k_buf = vec![0.0f32; kv_dim];
let mut v_buf = vec![0.0f32; kv_dim];
let mut attn_out = vec![0.0f32; q_dim.max(c.hidden_dim)];
let mut o_buf = vec![0.0f32; c.hidden_dim];
let mut gate_buf = vec![0.0f32; c.intermediate_dim];
let mut up_buf = vec![0.0f32; c.intermediate_dim];
let mut down_buf = vec![0.0f32; c.hidden_dim];
let is_hybrid = self.deltanet_layers.is_some();
let dn_qk_dim = c.linear_qk_head_dim().unwrap_or(0);
let dn_v_dim = c.linear_kv_head_dim().unwrap_or(0);
let dn_num_kv_heads = c.linear_num_kv_heads().unwrap_or(0);
let dn_num_v_heads = c.linear_num_v_heads().unwrap_or(0);
let dn_conv_kernel = c.linear_conv_kernel_dim().unwrap_or(0);
let dn_conv_dim = dn_qk_dim * dn_num_kv_heads * 2 + dn_v_dim * dn_num_v_heads;
let dn_in_proj_out = dn_conv_dim + dn_v_dim * dn_num_v_heads; let dn_v_out_dim = dn_v_dim * dn_num_v_heads;
let mut dn_in_proj = if is_hybrid {
vec![0.0f32; dn_in_proj_out]
} else {
Vec::new()
};
let dn_alpha_max = dn_num_kv_heads.max(dn_num_v_heads);
let mut dn_alpha = if is_hybrid {
vec![0.0f32; dn_alpha_max]
} else {
Vec::new()
};
let mut dn_beta = if is_hybrid {
vec![0.0f32; dn_alpha_max]
} else {
Vec::new()
};
let mut dn_conv_out = if is_hybrid {
vec![0.0f32; dn_conv_dim]
} else {
Vec::new()
};
let mut dn_delta_out = if is_hybrid {
vec![0.0f32; dn_v_out_dim]
} else {
Vec::new()
};
let mut hook = hook;
for layer_idx in 0..c.num_layers {
if hook(layer_idx, &mut hidden) {
continue;
}
if is_hybrid {
if let LayerKind::DeltaNet(dn_idx) = self.layer_kind_map[layer_idx] {
let dn_layer = &self.deltanet_layers.as_ref().expect("hybrid model")[dn_idx];
rms_norm(&hidden, &dn_layer.attn_norm, c.norm_eps, &mut norm_buf);
if std::env::var("ALICE_DUMP_ATTN_ALL").is_ok()
&& matches!(
layer_idx,
4 | 5 | 6 | 8 | 9 | 10 | 12 | 13 | 14 | 16 | 17 | 18
)
{
dump_slice(&format!("attn{layer_idx}_norm"), &norm_buf, 3);
}
let dump_dn0 = std::env::var("ALICE_DUMP_DN0").is_ok() && dn_idx == 0 && {
static ONCE: std::sync::Once = std::sync::Once::new();
let mut fire = false;
ONCE.call_once(|| fire = true);
fire
};
if dump_dn0 {
dump_slice("attn_norm", &norm_buf, 3);
}
let qkv_len = dn_conv_dim;
if let Some(ssm_in) = dn_layer.ssm_in.as_ref() {
ssm_in.matvec(&norm_buf, &mut dn_in_proj);
} else {
let attn_qkv = dn_layer
.attn_qkv
.as_ref()
.expect("DeltaNet layer with neither ssm_in nor attn_qkv slipped past the loader");
let attn_gate = dn_layer
.attn_gate
.as_ref()
.expect("Bonsai DeltaNet layer requires attn_gate alongside attn_qkv");
attn_qkv.matvec(&norm_buf, &mut dn_in_proj[..qkv_len]);
attn_gate.matvec(&norm_buf, &mut dn_in_proj[qkv_len..]);
}
if dump_dn0 {
dump_slice("qkv_mixed", &dn_in_proj[..qkv_len], 3);
eprintln!(
"DN0 qkv_mixed[125..128] = [{:.6},{:.6},{:.6}]",
dn_in_proj[125], dn_in_proj[126], dn_in_proj[127]
);
eprintln!(
"DN0 qkv_mixed[2045..2048] (KV-h15 Q tail) = [{:.6},{:.6},{:.6}]",
dn_in_proj[2045], dn_in_proj[2046], dn_in_proj[2047]
);
dump_slice("z_pre", &dn_in_proj[qkv_len..], 3);
let z_slice = &dn_in_proj[qkv_len..];
let mut per_head_z = String::from("DN0 z_pre_per_head_sum=[");
for h in 0..dn_num_v_heads {
let s: f32 = z_slice[h * dn_v_dim..(h + 1) * dn_v_dim].iter().sum();
let _ = write!(per_head_z, "{s:.4},");
}
per_head_z.push(']');
eprintln!("{per_head_z}");
}
dn_layer.alpha_proj.matvec(&norm_buf, &mut dn_alpha);
dn_layer.beta_proj.matvec(&norm_buf, &mut dn_beta);
if dump_dn0 {
dump_slice("alpha_raw", &dn_alpha[..dn_num_v_heads], 3);
dump_slice("beta_raw", &dn_beta[..dn_num_v_heads], 3);
}
let is_bonsai_path = if std::env::var("ALICE_DISABLE_BONSAI_FLAG").is_ok() {
false
} else {
dn_layer.ssm_a.is_some() && dn_layer.ssm_dt_bias.is_some()
};
let disable_gap_b = std::env::var("ALICE_DISABLE_GAP_B").is_ok();
if !disable_gap_b {
if let (Some(ssm_a), Some(ssm_dt_bias)) =
(dn_layer.ssm_a.as_ref(), dn_layer.ssm_dt_bias.as_ref())
{
debug_assert_eq!(ssm_a.len(), dn_num_v_heads);
debug_assert_eq!(ssm_dt_bias.len(), dn_num_v_heads);
for h in 0..dn_num_v_heads {
let alpha_biased = dn_alpha[h] + ssm_dt_bias[h];
let gate = softplus(alpha_biased) * ssm_a[h];
dn_alpha[h] = gate.exp();
}
for h in 0..dn_num_v_heads {
dn_beta[h] = sigmoid(dn_beta[h]);
}
}
}
if dump_dn0 {
dump_slice("alpha_after_gapB", &dn_alpha[..dn_num_v_heads], 3);
dump_slice("beta_after_gapB", &dn_beta[..dn_num_v_heads], 3);
}
let q_start = 0;
let k_start = dn_qk_dim * dn_num_kv_heads;
let v_start = dn_qk_dim * dn_num_kv_heads * 2;
let z_start = qkv_len;
causal_conv1d_step(
&dn_in_proj[..qkv_len],
&mut self.deltanet_conv_state[dn_idx],
&mut self.deltanet_conv_ring_pos[dn_idx],
&dn_layer.conv1d_weight,
&dn_layer.conv1d_bias,
&mut dn_conv_out,
dn_conv_dim,
dn_conv_kernel,
);
if dump_dn0 {
dump_slice("conv_out_raw", &dn_conv_out[..qkv_len], 3);
}
if is_bonsai_path {
for val in &mut dn_conv_out[..qkv_len] {
*val = silu(*val);
}
}
if dump_dn0 {
dump_slice("conv_out_silu", &dn_conv_out[..qkv_len], 3);
dump_slice("q_conv_head0", &dn_conv_out[0..dn_qk_dim], 3);
for kv in [0usize, 12, 15] {
let q_off = kv * dn_qk_dim;
let k_off = dn_qk_dim * dn_num_kv_heads + kv * dn_qk_dim;
let q_first = &dn_conv_out[q_off..q_off + 3];
let q_last = &dn_conv_out[q_off + dn_qk_dim - 3..q_off + dn_qk_dim];
let k_first = &dn_conv_out[k_off..k_off + 3];
let k_last = &dn_conv_out[k_off + dn_qk_dim - 3..k_off + dn_qk_dim];
eprintln!("DN0 q_kv{kv}: first3={q_first:?} last3={q_last:?}");
eprintln!("DN0 k_kv{kv}: first3={k_first:?} last3={k_last:?}");
let dot: f32 = (0..dn_qk_dim)
.map(|i| dn_conv_out[q_off + i] * dn_conv_out[k_off + i])
.sum();
eprintln!("DN0 q_kv{kv} · k_kv{kv} = {dot:.6}");
}
let mut q_sums = String::from("DN0 q_per_kv_head_sum=[");
let mut k_sums = String::from("DN0 k_per_kv_head_sum=[");
let mut v_sums = String::from("DN0 v_per_v_head_sum=[");
for h in 0..dn_num_kv_heads {
let q_off = h * dn_qk_dim;
let qs: f32 = dn_conv_out[q_off..q_off + dn_qk_dim].iter().sum();
let _ = write!(q_sums, "{qs:.4},");
let k_off = dn_qk_dim * dn_num_kv_heads + h * dn_qk_dim;
let ks: f32 = dn_conv_out[k_off..k_off + dn_qk_dim].iter().sum();
let _ = write!(k_sums, "{ks:.4},");
}
q_sums.push(']');
k_sums.push(']');
eprintln!("{q_sums}");
eprintln!("{k_sums}");
let v_base = dn_qk_dim * dn_num_kv_heads * 2;
for h in 0..dn_num_v_heads {
let v_off = v_base + h * dn_v_dim;
let vs: f32 = dn_conv_out[v_off..v_off + dn_v_dim].iter().sum();
let _ = write!(v_sums, "{vs:.4},");
}
v_sums.push(']');
eprintln!("{v_sums}");
}
let q_slice = &dn_conv_out[q_start..q_start + k_start];
let k_slice = &dn_conv_out[k_start..v_start];
let v_slice = &dn_conv_out[v_start..v_start + dn_v_out_dim];
let z_slice = &dn_in_proj[z_start..z_start + dn_v_out_dim];
gated_deltanet_step(
q_slice,
k_slice,
v_slice,
&dn_alpha[..dn_num_v_heads],
&dn_beta[..dn_num_v_heads],
z_slice,
&mut self.deltanet_state[dn_idx],
&mut dn_delta_out,
dn_num_kv_heads,
dn_num_v_heads,
dn_qk_dim,
dn_v_dim,
is_bonsai_path,
);
if dump_dn0 {
dump_slice("attn_output", &dn_delta_out, 3);
}
if dump_dn0 {
let head0 = &dn_delta_out[..dn_v_dim];
dump_slice("attn_head0", head0, 3);
let mut ao_sums = String::from("DN0 attn_output_per_head_sum=[");
for h in 0..dn_num_v_heads {
let s: f32 =
dn_delta_out[h * dn_v_dim..(h + 1) * dn_v_dim].iter().sum();
let _ = write!(ao_sums, "{s:.6},");
}
ao_sums.push(']');
eprintln!("{ao_sums}");
}
if let Some(ssm_norm) = dn_layer.ssm_norm.as_ref() {
if std::env::var("ALICE_DISABLE_GAP_C").is_err() {
apply_qk_norm(&mut dn_delta_out, ssm_norm, dn_v_dim, c.norm_eps);
}
}
if dump_dn0 {
dump_slice("post_ssm_norm_head0", &dn_delta_out[..dn_v_dim], 3);
let mut n51_sums = String::from("DN0 post_ssm_norm_per_head_sum=[");
for h in 0..dn_num_v_heads {
let s: f32 =
dn_delta_out[h * dn_v_dim..(h + 1) * dn_v_dim].iter().sum();
let _ = write!(n51_sums, "{s:.4},");
}
n51_sums.push(']');
eprintln!("{n51_sums}");
for h in [6, 15, 20, 25] {
dump_slice(
&format!("post_ssm_norm_head{h}"),
&dn_delta_out[h * dn_v_dim..(h + 1) * dn_v_dim],
3,
);
}
}
if is_bonsai_path {
for h in 0..dn_num_v_heads {
for j in 0..dn_v_dim {
let idx = h * dn_v_dim + j;
dn_delta_out[idx] *= silu(z_slice[idx]);
}
}
}
if dump_dn0 {
for h in [1, 2, 18, 31] {
dump_slice(
&format!("post_zgate_head{h}"),
&dn_delta_out[h * dn_v_dim..(h + 1) * dn_v_dim],
3,
);
}
dump_slice("post_zgate_head0", &dn_delta_out[..dn_v_dim], 3);
dump_slice("post_zgate_all", &dn_delta_out, 3);
let mut per_head = String::from("DN0 post_zgate_per_head_sum=[");
for h in 0..dn_num_v_heads {
let s: f32 =
dn_delta_out[h * dn_v_dim..(h + 1) * dn_v_dim].iter().sum();
let _ = write!(per_head, "{s:.4},");
}
per_head.push(']');
eprintln!("{per_head}");
}
dn_layer.ssm_out.matvec(&dn_delta_out, &mut o_buf);
if dump_dn0 {
dump_slice("ssm_out_o", &o_buf[..c.hidden_dim], 3);
}
for i in 0..c.hidden_dim {
hidden[i] += o_buf[i];
}
rms_norm(&hidden, &dn_layer.ffn_norm, c.norm_eps, &mut norm_buf);
let q8_ffn = quantize_row_q8_k(&norm_buf);
dn_layer.gate_proj.matvec_preq(&q8_ffn, &mut gate_buf);
dn_layer.up_proj.matvec_preq(&q8_ffn, &mut up_buf);
for i in 0..c.intermediate_dim {
gate_buf[i] = silu(gate_buf[i]) * up_buf[i];
}
dn_layer.down_proj.matvec(&gate_buf, &mut down_buf);
for i in 0..c.hidden_dim {
hidden[i] += down_buf[i];
}
if std::env::var_os("ALICE_LLM_DUMP_LAYERS").is_some()
&& matches!(layer_idx, 0 | 6 | 13 | 20 | 27)
{
dump_hidden_jsonl_stderr(&format!("cpu_layer_{layer_idx}"), &hidden);
}
continue;
}
}
let attention_layer_idx = if is_hybrid {
match self.layer_kind_map[layer_idx] {
LayerKind::Attention(k) => k,
LayerKind::DeltaNet(_) => unreachable!("handled above"),
}
} else {
layer_idx
};
let layer = &self.layers[attention_layer_idx];
rms_norm(&hidden, &layer.attn_norm, c.norm_eps, &mut norm_buf);
let dump_l0 = std::env::var("ALICE_DUMP_LAYER0_OPS").is_ok() && layer_idx == 0 && {
static POS_COUNTER: std::sync::atomic::AtomicUsize =
std::sync::atomic::AtomicUsize::new(0);
let max_pos: usize = std::env::var("ALICE_DUMP_LAYER0_MAX_POS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(2);
let cur = POS_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if cur < max_pos {
eprintln!("DN0 L0_pos_marker pos={cur}");
true
} else {
false
}
};
if dump_l0 {
dump_slice("L0_attn_norm_out", &norm_buf, 3);
}
let q8_attn = quantize_row_q8_k(&norm_buf);
layer.q_proj.matvec_preq(&q8_attn, &mut q_buf);
layer
.k_proj
.as_ref()
.expect("k_proj required for non-shared layer")
.matvec_preq(&q8_attn, &mut k_buf);
layer
.v_proj
.as_ref()
.expect("v_proj required for non-shared layer")
.matvec_preq(&q8_attn, &mut v_buf);
if layer.gated_output {
let head_dim = c.head_dim;
let mut gate_extract = vec![0f32; q_dim];
for h in 0..c.num_heads {
for p in 0..head_dim {
gate_extract[h * head_dim + p] = q_buf[h * head_dim * 2 + head_dim + p];
}
}
for h in 1..c.num_heads {
let src = h * head_dim * 2;
let dst = h * head_dim;
for p in 0..head_dim {
q_buf[dst + p] = q_buf[src + p];
}
}
q_buf[q_dim..2 * q_dim].copy_from_slice(&gate_extract);
}
let dump_attn3 = std::env::var("ALICE_DUMP_ATTN3").is_ok() && layer_idx == 3 && {
static ONCE_A3: std::sync::Once = std::sync::Once::new();
let mut fire = false;
ONCE_A3.call_once(|| fire = true);
fire
};
if dump_attn3 {
dump_slice("attn3_norm", &norm_buf, 3);
dump_slice("attn3_q_buf_full", &q_buf[..q_dim * 2], 3);
dump_slice("attn3_q_head0_consec", &q_buf[..c.head_dim], 3);
dump_slice(
"attn3_offset_256_gate_if_interleaved",
&q_buf[c.head_dim..c.head_dim * 2],
3,
);
dump_slice(
"attn3_offset_512_head1_if_interleaved",
&q_buf[c.head_dim * 2..c.head_dim * 3],
3,
);
dump_slice("attn3_v_head0", &v_buf[..c.head_dim], 3);
dump_slice("attn3_k_head0", &k_buf[..c.head_dim], 3);
let kv_dim = c.num_kv_heads * c.head_dim;
dump_hidden_jsonl_stderr("cpu_attn3_v_full", &v_buf[..kv_dim]);
dump_hidden_jsonl_stderr("cpu_attn3_k_full", &k_buf[..kv_dim]);
}
if std::env::var("ALICE_DUMP_ATTN_ALL").is_ok()
&& matches!(layer_idx, 7 | 11 | 15 | 19 | 23 | 27 | 31)
{
dump_slice(&format!("attn{layer_idx}_norm"), &norm_buf, 3);
}
if let Some(b) = layer.q_bias() {
for (q, bi) in q_buf.iter_mut().zip(b.iter()) {
*q += bi;
}
}
if let Some(b) = layer.k_bias() {
for (k, bi) in k_buf.iter_mut().zip(b.iter()) {
*k += bi;
}
}
if let Some(b) = layer.v_bias() {
for (v, bi) in v_buf.iter_mut().zip(b.iter()) {
*v += bi;
}
}
if let Some(w) = layer.q_norm() {
apply_qk_norm(&mut q_buf[..q_dim], w, c.head_dim, c.norm_eps);
}
if let Some(w) = layer.k_norm() {
apply_qk_norm(&mut k_buf, w, c.head_dim, c.norm_eps);
}
for h in 0..c.num_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut q_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
for h in 0..c.num_kv_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut k_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
self.kv_cache.append(layer_idx, &k_buf, &v_buf);
if dump_l0 {
dump_slice("L0_q_buf", &q_buf[..q_dim], 3);
dump_slice("L0_k_buf", &k_buf, 3);
dump_slice("L0_v_buf", &v_buf, 3);
let head_dim = c.head_dim;
for head_idx in [0, 1, 12, 23_usize].iter().copied() {
if head_idx >= c.num_heads {
continue;
}
let start = head_idx * head_dim;
let end = start + head_dim;
if end > q_dim {
continue;
}
let head_slice = &q_buf[start..end];
let sum: f64 = head_slice.iter().map(|&v| v as f64).sum();
eprintln!(
"DN0 L0_q_head{} first3=[{:.6},{:.6},{:.6}] last3=[{:.6},{:.6},{:.6}] sum={:.6}",
head_idx,
head_slice[0], head_slice[1], head_slice[2],
head_slice[head_dim - 3], head_slice[head_dim - 2], head_slice[head_dim - 1],
sum
);
}
}
gqa_attention(
&q_buf,
&self.kv_cache,
layer_idx,
pos,
c.num_heads,
c.num_kv_heads,
c.head_dim,
c.sliding_window_for_layer(layer_idx),
c.attn_logit_softcap(),
if c.arch == ModelArch::Gemma3n {
Some(1.0)
} else {
None
},
&mut attn_out,
);
if dump_l0 {
dump_slice("L0_attn_out", &attn_out, 3);
}
let dump_gated_layer3 = std::env::var("ALICE_DUMP_GATED3").is_ok()
&& layer_idx == 3
&& layer.gated_output
&& {
static ONCE_G3: std::sync::Once = std::sync::Once::new();
let mut fire = false;
ONCE_G3.call_once(|| fire = true);
fire
};
if dump_gated_layer3 {
dump_slice("gated3_attn_pregate", &attn_out[..q_dim], 3);
dump_slice("gated3_gate_raw", &q_buf[q_dim..2 * q_dim], 3);
let gate_sigmoid: Vec<f32> = q_buf[q_dim..2 * q_dim]
.iter()
.map(|&g| sigmoid(g))
.collect();
dump_slice("gated3_gate_sigmoid", &gate_sigmoid, 3);
}
if layer.gated_output {
for i in 0..q_dim {
attn_out[i] *= sigmoid(q_buf[q_dim + i]);
}
}
if dump_gated_layer3 {
dump_slice("gated3_attn_gated", &attn_out[..q_dim], 3);
dump_hidden_jsonl_stderr("cpu_gated3_attn_gated_full", &attn_out[..q_dim]);
}
layer.o_proj.matvec(&attn_out, &mut o_buf);
if dump_l0 {
dump_slice("L0_o_buf", &o_buf, 3);
}
if dump_gated_layer3 {
dump_slice("gated3_o_buf", &o_buf, 3);
dump_hidden_jsonl_stderr("cpu_gated3_o_buf_full", &o_buf);
dump_slice("gated3_hidden_pre_residual", &hidden, 3);
let post_res: Vec<f32> = hidden
.iter()
.zip(o_buf.iter())
.map(|(&h, &o)| h + o)
.collect();
dump_slice("gated3_hidden_post_residual", &post_res, 3);
let ss: f64 = post_res.iter().map(|&v| (v as f64) * (v as f64)).sum();
let mean_sq = ss / post_res.len() as f64;
let rms = (mean_sq + 1e-6_f64).sqrt();
eprintln!(
"DN0 gated3_hidden_stats: sum_sq={:.4} mean_sq={:.6} rms={:.6} scale=1/rms={:.4}",
ss, mean_sq, rms, 1.0/rms
);
let mid = post_res.len() / 2;
eprintln!(
"DN0 gated3_hidden_middle: pos {} = [{:.4},{:.4},{:.4},{:.4},{:.4}]",
mid,
post_res[mid],
post_res[mid + 1],
post_res[mid + 2],
post_res[mid + 3],
post_res[mid + 4]
);
}
if let Some(w) = &layer.post_attn_norm {
let mut tmp = vec![0.0f32; c.hidden_dim];
rms_norm(&o_buf, w, c.norm_eps, &mut tmp);
o_buf.copy_from_slice(&tmp);
}
for i in 0..c.hidden_dim {
hidden[i] += o_buf[i];
}
if dump_l0 {
dump_slice("L0_ffn_inp", &hidden, 3);
}
if layer_idx == 3 && std::env::var("ALICE_DUMP_GATED3").is_ok() {
static ONCE_H3: std::sync::Once = std::sync::Once::new();
let mut fire = false;
ONCE_H3.call_once(|| fire = true);
if fire {
dump_slice("gated3_hidden_pre_ffnnorm", &hidden, 3);
let ss: f64 = hidden.iter().map(|&v| (v as f64) * (v as f64)).sum();
let mean_sq = ss / hidden.len() as f64;
let scale = 1.0_f64 / (mean_sq + 1e-6_f64).sqrt();
eprintln!("DN0 gated3_pre_ffnnorm_rms: mean_sq={mean_sq:.6} scale={scale:.4}");
let manual_out_0 = hidden[0] as f64 * scale * layer.ffn_norm[0] as f64;
eprintln!(
"DN0 gated3_manual_out0: hidden[0]={:.4} scale={:.4} weight[0]={:.6} manual_out[0]={:.4}",
hidden[0], scale, layer.ffn_norm[0], manual_out_0
);
}
}
rms_norm(&hidden, &layer.ffn_norm, c.norm_eps, &mut norm_buf);
if dump_l0 {
dump_slice("L0_ffn_norm_out", &norm_buf, 3);
}
if layer_idx == 3 && std::env::var("ALICE_DUMP_GATED3").is_ok() {
static ONCE_FN3: std::sync::Once = std::sync::Once::new();
let mut fire = false;
ONCE_FN3.call_once(|| fire = true);
if fire {
dump_slice("gated3_ffn_norm_out", &norm_buf, 3);
dump_slice("gated3_ffn_norm_weight", &layer.ffn_norm, 3);
dump_slice("gated3_attn_norm_weight", &layer.attn_norm, 3);
}
}
if layer.ffn_gate_inp().is_some() {
forward_moe_layer(c, layer, &norm_buf, &mut down_buf);
} else {
let q8_ffn = quantize_row_q8_k(&norm_buf);
layer
.gate_proj
.as_ref()
.expect("gate_proj required for non-MoE layer")
.matvec_preq(&q8_ffn, &mut gate_buf);
layer
.up_proj
.as_ref()
.expect("up_proj required for non-MoE layer")
.matvec_preq(&q8_ffn, &mut up_buf);
c.apply_ffn_sparsity(layer_idx, &mut gate_buf);
for i in 0..c.intermediate_dim {
gate_buf[i] = c.apply_ffn_act(layer_idx, gate_buf[i]) * up_buf[i];
}
layer
.down_proj
.as_ref()
.expect("down_proj required for non-MoE layer")
.matvec(&gate_buf, &mut down_buf);
}
if let Some(w) = &layer.post_ffn_norm {
let mut tmp = vec![0.0f32; c.hidden_dim];
rms_norm(&down_buf, w, c.norm_eps, &mut tmp);
down_buf.copy_from_slice(&tmp);
}
if dump_l0 {
dump_slice("L0_ffn_out", &down_buf, 3);
}
for i in 0..c.hidden_dim {
hidden[i] += down_buf[i];
}
if dump_l0 {
dump_slice("L0_l_out_final", &hidden, 3);
}
if layer_idx == 3 && std::env::var("ALICE_DUMP_GATED3").is_ok() {
static ONCE_L3: std::sync::Once = std::sync::Once::new();
let mut fire = false;
ONCE_L3.call_once(|| fire = true);
if fire {
dump_slice("gated3_ffn_out", &down_buf, 3);
dump_slice("gated3_l_out_3", &hidden, 3);
dump_hidden_jsonl_stderr("cpu_gated3_ffn_out_full", &down_buf);
}
}
if std::env::var_os("ALICE_LLM_DUMP_LAYERS").is_some()
&& matches!(layer_idx, 0 | 6 | 13 | 20 | 27)
{
dump_hidden_jsonl_stderr(&format!("cpu_layer_{layer_idx}"), &hidden);
}
}
self.kv_cache.advance();
rms_norm(&hidden, &self.output_norm, c.norm_eps, &mut norm_buf);
if std::env::var_os("ALICE_LLM_DUMP_HIDDEN").is_some() {
dump_hidden_jsonl_stderr("cpu", &norm_buf);
}
let mut logits = vec![0.0f32; c.vocab_size];
self.output_proj.matvec(&norm_buf, &mut logits);
if let Some(cap) = c.final_logit_softcap() {
for l in &mut logits {
*l = cap * (*l / cap).tanh();
}
}
logits
}
pub fn project_hidden_to_logits(&self, hidden: &[f32]) -> Vec<f32> {
let c = &self.config;
assert_eq!(
hidden.len(),
c.hidden_dim,
"project_hidden_to_logits: hidden.len() must equal config.hidden_dim"
);
let mut norm_buf = vec![0.0f32; c.hidden_dim];
rms_norm(hidden, &self.output_norm, c.norm_eps, &mut norm_buf);
let mut logits = vec![0.0f32; c.vocab_size];
self.output_proj.matvec(&norm_buf, &mut logits);
if let Some(cap) = c.final_logit_softcap() {
for l in &mut logits {
*l = cap * (*l / cap).tanh();
}
}
logits
}
fn forward_gemma3n(&mut self, token_id: u32) -> Vec<f32> {
let c = self.config.clone();
let hidden_dim = c.hidden_dim;
let n_altup = c.altup_num_inputs().expect("Gemma3n: altup_num_inputs");
let n_embd_altup = c
.per_layer_input_embedding_dim()
.expect("Gemma3n: per_layer_input_embedding_dim");
let i_altup_act = c.altup_active_idx().unwrap_or(0);
let n_layer_sparsity = c
.activation_sparsity_scale()
.map_or(0, |arr| arr.iter().take_while(|s| s.is_finite()).count());
let pos = self.kv_cache.seq_len();
let rope_freqs_ref = self.rope_freqs.as_deref();
let emb_start = token_id as usize * hidden_dim;
let mut inpl: Vec<f32> = self.embedding[emb_start..emb_start + hidden_dim].to_vec();
let scale = (hidden_dim as f32).sqrt();
for v in &mut inpl {
*v *= scale;
}
let inp_per_layer =
self.gemma3n_per_layer_inputs(token_id, &inpl, n_embd_altup, c.num_layers);
let mut streams: Vec<Vec<f32>> = vec![Vec::new(); n_altup];
streams[i_altup_act] = inpl.clone();
let target_magnitude = l2_magnitude(&inpl);
let altup_proj = self
.altup_proj
.as_ref()
.expect("Gemma3n: altup_proj missing");
let n_embd = hidden_dim;
for i_altup in 0..(n_altup - 1) {
let slab_start = i_altup * n_embd * n_embd;
let slab = &altup_proj[slab_start..slab_start + n_embd * n_embd];
let mut added = vec![0.0f32; n_embd];
mat_vec_f32(slab, n_embd, n_embd, &inpl, &mut added);
let new_mag = l2_magnitude(&added);
if new_mag > 0.0 {
let factor = target_magnitude / new_mag;
for v in &mut added {
*v *= factor;
}
}
let dest_idx = if i_altup < i_altup_act {
i_altup
} else {
i_altup + 1
};
streams[dest_idx] = added;
}
let mut norm_buf = vec![0.0f32; hidden_dim];
let kv_dim = c.num_kv_heads * c.head_dim;
let mut q_buf = vec![0.0f32; c.num_heads * c.head_dim];
let mut k_buf = vec![0.0f32; kv_dim];
let mut v_buf = vec![0.0f32; kv_dim];
let mut attn_out = vec![0.0f32; hidden_dim];
let mut o_buf = vec![0.0f32; hidden_dim];
let mut gate_buf = vec![0.0f32; c.intermediate_dim];
let mut up_buf = vec![0.0f32; c.intermediate_dim];
let mut down_buf = vec![0.0f32; hidden_dim];
for layer_idx in 0..c.num_layers {
let layer = &self.layers[layer_idx];
let predictions = self.altup_predict(&streams, layer_idx, n_altup);
let active_prediction = predictions[i_altup_act].clone();
let mut cur = active_prediction.clone();
rms_norm(&cur, &layer.attn_norm, c.norm_eps, &mut norm_buf);
cur.copy_from_slice(&norm_buf);
let laurel_out = self.laurel(&cur, layer_idx);
let q8_attn = quantize_row_q8_k(&cur);
layer.q_proj.matvec_preq(&q8_attn, &mut q_buf);
let owns_kv = self.kv_cache.kv_layer_map[layer_idx] == layer_idx;
if owns_kv {
layer
.k_proj
.as_ref()
.expect("k_proj required for non-shared layer")
.matvec_preq(&q8_attn, &mut k_buf);
layer
.v_proj
.as_ref()
.expect("v_proj required for non-shared layer")
.matvec_preq(&q8_attn, &mut v_buf);
}
if let Some(w) = layer.q_norm() {
apply_qk_norm(&mut q_buf, w, c.head_dim, c.norm_eps);
}
if owns_kv {
if let Some(w) = layer.k_norm() {
apply_qk_norm(&mut k_buf, w, c.head_dim, c.norm_eps);
}
apply_head_rms_norm_identity(&mut v_buf, c.head_dim, c.norm_eps);
}
for h in 0..c.num_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut q_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
if owns_kv {
for h in 0..c.num_kv_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut k_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
self.kv_cache.append(layer_idx, &k_buf, &v_buf);
}
gqa_attention(
&q_buf,
&self.kv_cache,
layer_idx,
pos,
c.num_heads,
c.num_kv_heads,
c.head_dim,
c.sliding_window_for_layer(layer_idx),
c.attn_logit_softcap(),
if c.arch == ModelArch::Gemma3n {
Some(1.0)
} else {
None
},
&mut attn_out,
);
let layer = &self.layers[layer_idx];
layer.o_proj.matvec(&attn_out, &mut o_buf);
if let Some(w) = &layer.post_attn_norm {
rms_norm(&o_buf, w, c.norm_eps, &mut norm_buf);
o_buf.copy_from_slice(&norm_buf);
}
for i in 0..hidden_dim {
cur[i] = o_buf[i] + active_prediction[i];
}
let inv_sqrt2 = 1.0f32 / 2.0f32.sqrt();
let mut attn_laurel = vec![0.0f32; hidden_dim];
for i in 0..hidden_dim {
attn_laurel[i] = (cur[i] + laurel_out[i]) * inv_sqrt2;
}
rms_norm(&attn_laurel, &layer.ffn_norm, c.norm_eps, &mut norm_buf);
let q8_ffn = quantize_row_q8_k(&norm_buf);
layer
.gate_proj
.as_ref()
.expect("gate_proj required for non-MoE layer")
.matvec_preq(&q8_ffn, &mut gate_buf);
layer
.up_proj
.as_ref()
.expect("up_proj required for non-MoE layer")
.matvec_preq(&q8_ffn, &mut up_buf);
if layer_idx < n_layer_sparsity {
c.apply_ffn_sparsity(layer_idx, &mut gate_buf);
}
for i in 0..c.intermediate_dim {
gate_buf[i] = gelu_approx(gate_buf[i]) * up_buf[i];
}
layer
.down_proj
.as_ref()
.expect("down_proj required for non-MoE layer")
.matvec(&gate_buf, &mut down_buf);
if let Some(w) = &layer.post_ffn_norm {
rms_norm(&down_buf, w, c.norm_eps, &mut norm_buf);
down_buf.copy_from_slice(&norm_buf);
}
let mut ffn_out = attn_laurel.clone();
for i in 0..hidden_dim {
ffn_out[i] += down_buf[i];
}
let mut corrected = self.altup_correct(&predictions, &ffn_out, layer_idx, n_altup);
let first_prediction = self.gemma3n_first_prediction(
&corrected[i_altup_act],
layer_idx,
&inp_per_layer[layer_idx],
n_embd_altup,
);
for a in 0..n_altup {
if a == i_altup_act {
continue;
}
for i in 0..hidden_dim {
corrected[a][i] += first_prediction[i];
}
}
streams = corrected;
}
self.kv_cache.advance();
let _ = inp_per_layer;
let mut cur = streams[i_altup_act].clone();
let target_magnitude = l2_magnitude(&cur);
let altup_unembd_proj = self
.altup_unembd_proj
.as_ref()
.expect("Gemma3n: altup_unembd_proj missing");
for i_altup in 0..(n_altup - 1) {
let src_stream_idx = if i_altup < i_altup_act {
i_altup
} else {
i_altup + 1
};
let slab_start = i_altup * n_embd * n_embd;
let slab = &altup_unembd_proj[slab_start..slab_start + n_embd * n_embd];
let mut unembd = vec![0.0f32; n_embd];
mat_vec_f32(slab, n_embd, n_embd, &streams[src_stream_idx], &mut unembd);
let new_mag = l2_magnitude(&unembd);
if new_mag > 0.0 {
let factor = target_magnitude / new_mag;
for v in &mut unembd {
*v *= factor;
}
}
for i in 0..hidden_dim {
cur[i] += unembd[i];
}
}
let inv_n_altup = 1.0f32 / n_altup as f32;
for v in &mut cur {
*v *= inv_n_altup;
}
rms_norm(&cur, &self.output_norm, c.norm_eps, &mut norm_buf);
let mut logits = vec![0.0f32; c.vocab_size];
self.output_proj.matvec(&norm_buf, &mut logits);
if let Some(cap) = c.final_logit_softcap() {
for l in &mut logits {
*l = cap * (*l / cap).tanh();
}
}
logits
}
fn gemma3n_per_layer_inputs(
&self,
token_id: u32,
inpl_scaled: &[f32],
n_embd_altup: usize,
n_layer: usize,
) -> Vec<Vec<f32>> {
let c = &self.config;
let raw = self
.per_layer_embedding_for_token(token_id)
.expect("Gemma3n: per_layer_token_embd");
assert_eq!(raw.len(), n_layer * n_embd_altup);
let tok_scale = (n_embd_altup as f32).sqrt();
let mut inp_per_layer_lookup: Vec<Vec<f32>> = (0..n_layer)
.map(|l| {
raw[l * n_embd_altup..(l + 1) * n_embd_altup]
.iter()
.map(|v| v * tok_scale)
.collect()
})
.collect();
let proj = self
.per_layer_model_proj
.as_ref()
.expect("Gemma3n: per_layer_model_proj");
let mut per_layer_proj_flat = vec![0.0f32; n_layer * n_embd_altup];
proj.matvec(inpl_scaled, &mut per_layer_proj_flat);
let proj_scale = 1.0f32 / (c.hidden_dim as f32).sqrt();
for v in &mut per_layer_proj_flat {
*v *= proj_scale;
}
let proj_norm_w = self
.per_layer_proj_norm
.as_ref()
.expect("Gemma3n: per_layer_proj_norm");
let mut per_layer_proj_normed = vec![0.0f32; n_layer * n_embd_altup];
for l in 0..n_layer {
let start = l * n_embd_altup;
rms_norm(
&per_layer_proj_flat[start..start + n_embd_altup],
proj_norm_w,
c.norm_eps,
&mut per_layer_proj_normed[start..start + n_embd_altup],
);
}
let inv_sqrt2 = 1.0f32 / 2.0f32.sqrt();
for l in 0..n_layer {
for i in 0..n_embd_altup {
inp_per_layer_lookup[l][i] = (inp_per_layer_lookup[l][i]
+ per_layer_proj_normed[l * n_embd_altup + i])
* inv_sqrt2;
}
}
inp_per_layer_lookup
}
fn altup_router_modalities(
&self,
active: &[f32],
layer_idx: usize,
n_altup: usize,
) -> Vec<f32> {
let c = &self.config;
let layer = &self.layers[layer_idx];
let router_norm_w = layer
.altup_router_norm()
.expect("Gemma3n: altup_router_norm");
let router_w = layer.altup_router().expect("Gemma3n: altup_router");
let mut router_inputs = vec![0.0f32; c.hidden_dim];
rms_norm(active, router_norm_w, c.norm_eps, &mut router_inputs);
let scale = 1.0f32 / c.hidden_dim as f32;
for v in &mut router_inputs {
*v *= scale;
}
let mut modalities = vec![0.0f32; n_altup];
mat_vec_f32(
router_w,
n_altup,
c.hidden_dim,
&router_inputs,
&mut modalities,
);
for v in &mut modalities {
*v = v.tanh();
}
modalities
}
fn altup_predict(
&self,
streams: &[Vec<f32>],
layer_idx: usize,
n_altup: usize,
) -> Vec<Vec<f32>> {
let c = &self.config;
let layer = &self.layers[layer_idx];
let i_altup_act = c.altup_active_idx().unwrap_or(0);
let hidden_dim = c.hidden_dim;
let modalities = self.altup_router_modalities(&streams[i_altup_act], layer_idx, n_altup);
let predict_coef = layer
.altup_predict_coef()
.expect("Gemma3n: altup_predict_coef");
let mut coef_flat = vec![0.0f32; n_altup * n_altup];
mat_vec_f32(
predict_coef,
n_altup * n_altup,
n_altup,
&modalities,
&mut coef_flat,
);
let mut predictions: Vec<Vec<f32>> = vec![vec![0.0f32; hidden_dim]; n_altup];
for out_i in 0..n_altup {
for in_i in 0..n_altup {
let c_val = coef_flat[out_i * n_altup + in_i];
if c_val == 0.0 {
continue;
}
for j in 0..hidden_dim {
predictions[out_i][j] += c_val * streams[in_i][j];
}
}
for j in 0..hidden_dim {
predictions[out_i][j] += streams[out_i][j];
}
}
predictions
}
fn altup_correct(
&self,
predictions: &[Vec<f32>],
activated: &[f32],
layer_idx: usize,
n_altup: usize,
) -> Vec<Vec<f32>> {
let c = &self.config;
let layer = &self.layers[layer_idx];
let i_altup_act = c.altup_active_idx().unwrap_or(0);
let hidden_dim = c.hidden_dim;
let modalities = self.altup_router_modalities(activated, layer_idx, n_altup);
let correct_coef = layer
.altup_correct_coef()
.expect("Gemma3n: altup_correct_coef");
let mut all_coefs = vec![0.0f32; n_altup];
mat_vec_f32(correct_coef, n_altup, n_altup, &modalities, &mut all_coefs);
for v in &mut all_coefs {
*v += 1.0;
}
let mut innovation = vec![0.0f32; hidden_dim];
for i in 0..hidden_dim {
innovation[i] = activated[i] - predictions[i_altup_act][i];
}
let mut corrected: Vec<Vec<f32>> = (0..n_altup).map(|a| predictions[a].clone()).collect();
for a in 0..n_altup {
let coef = all_coefs[a];
for j in 0..hidden_dim {
corrected[a][j] += coef * innovation[j];
}
}
corrected
}
fn laurel(&self, cur: &[f32], layer_idx: usize) -> Vec<f32> {
let c = &self.config;
let layer = &self.layers[layer_idx];
let hidden_dim = c.hidden_dim;
let laurel_l = layer.laurel_l().expect("Gemma3n: laurel_l");
let laurel_r = layer.laurel_r().expect("Gemma3n: laurel_r");
let laurel_post_norm = layer.laurel_post_norm().expect("Gemma3n: laurel_post_norm");
let laurel_rank = laurel_l.len() / hidden_dim;
let mut mid = vec![0.0f32; laurel_rank];
mat_vec_f32(laurel_l, laurel_rank, hidden_dim, cur, &mut mid);
let mut tmp = vec![0.0f32; hidden_dim];
mat_vec_f32(laurel_r, hidden_dim, laurel_rank, &mid, &mut tmp);
let mut normed = vec![0.0f32; hidden_dim];
rms_norm(&tmp, laurel_post_norm, c.norm_eps, &mut normed);
for i in 0..hidden_dim {
normed[i] += cur[i];
}
normed
}
fn gemma3n_first_prediction(
&self,
active_corrected: &[f32],
layer_idx: usize,
inp_this_layer: &[f32],
n_embd_altup: usize,
) -> Vec<f32> {
let c = &self.config;
let layer = &self.layers[layer_idx];
let hidden_dim = c.hidden_dim;
let correct_scale = layer
.altup_correct_scale()
.expect("Gemma3n: altup_correct_scale");
let mut scaled = vec![0.0f32; hidden_dim];
for i in 0..hidden_dim {
scaled[i] = active_corrected[i] * correct_scale[i];
}
let inp_gate = layer.inp_gate().expect("Gemma3n: inp_gate");
let mut gated = vec![0.0f32; n_embd_altup];
inp_gate.matvec(&scaled, &mut gated);
for v in &mut gated {
*v = gelu_approx(*v);
}
for i in 0..n_embd_altup {
gated[i] *= inp_this_layer[i];
}
let proj = layer.proj().expect("Gemma3n: proj");
let mut projected = vec![0.0f32; hidden_dim];
proj.matvec(&gated, &mut projected);
let post_norm = layer.post_norm().expect("Gemma3n: post_norm");
let mut normed = vec![0.0f32; hidden_dim];
rms_norm(&projected, post_norm, c.norm_eps, &mut normed);
normed
}
#[allow(clippy::needless_pass_by_ref_mut)]
fn forward_kimi_k3(&mut self, _token_id: u32) -> Vec<f32> {
todo!(
"KIMI-K3 forward: open weights released 2026-07-27, spec is \
now captured in KimiDeltaConfig (parseable from HF config.json \
via KimiDeltaConfig::from_hf_config under `hf-config` feature). \
CPU forward path (Phase X.4.c) is still pending community GGUF \
conversion (X.4.b, mradermacher / bartowski watch). See \
docs/KIMI_K3_INTEGRATION.md for the phased integration plan \
(Phase X.4.a-X.4.j) and the reusable ALICE-LLM components \
(`gated_deltanet_step*`, `SsmDeltaNetConfig`, MoE routing, \
MLA scaffolding shared with DeepSeek V3)."
);
}
#[allow(clippy::needless_pass_by_ref_mut)]
fn forward_hy3(&mut self, _token_id: u32) -> Vec<f32> {
todo!(
"HY3 forward: waiting for community GGUF conversion of \
Tencent Hy3 (Hunyuan 3, 295B / 21B active MoE, 192 experts \
top-8, MTP head 3.8B, GQA 8 KV, 256K context, FP8 native) \
before implementation. Expected to inherit ~90% from the \
Bonsai `gated_deltanet_step*` path; net-new pieces are the \
192-expert top-8 sparse routing and MTP head. See the \
Tencent-Hunyuan/Hy3 GitHub repository for the model spec."
);
}
fn forward_deepseek_v3(&mut self, token_id: u32) -> Vec<f32> {
let c = self.config.clone();
let hidden_dim = c.hidden_dim;
let num_heads = c.num_heads;
let q_lora_rank = c.deepseek_q_lora_rank().unwrap_or(0);
let kv_lora_rank = c.deepseek_kv_lora_rank().expect("kv_lora_rank");
let qk_nope = c.deepseek_qk_nope_head_dim().expect("qk_nope_head_dim");
let qk_rope = c.deepseek_qk_rope_head_dim().expect("qk_rope_head_dim");
let v_head_dim = c.deepseek_v_head_dim().expect("v_head_dim");
let first_k_dense = c.deepseek_first_k_dense_replace().unwrap_or(0);
let q_head_total = qk_nope + qk_rope;
let kv_up_head_total = qk_nope + v_head_dim;
let compressed_kv_dim = kv_lora_rank + qk_rope;
let deepseek_layers = self
.deepseek_v3_layers
.as_ref()
.expect("DeepSeek-V3 layers not loaded");
let pos = self.kv_cache.seq_len();
let emb_start = token_id as usize * hidden_dim;
let mut hidden: Vec<f32> = self.embedding[emb_start..emb_start + hidden_dim].to_vec();
let mut norm_buf = vec![0.0f32; hidden_dim];
let mut q_a_buf = vec![0.0f32; q_lora_rank];
let mut q_a_normed = vec![0.0f32; q_lora_rank];
let mut q_full = vec![0.0f32; num_heads * q_head_total];
let mut kv_a_full = vec![0.0f32; compressed_kv_dim];
let mut kv_a_normed = vec![0.0f32; kv_lora_rank];
let mut kv_up = vec![0.0f32; num_heads * kv_up_head_total];
let mut attn_out = vec![0.0f32; num_heads * v_head_dim];
let mut o_buf = vec![0.0f32; hidden_dim];
let mut gate_buf = vec![0.0f32; c.intermediate_dim];
let mut up_buf = vec![0.0f32; c.intermediate_dim];
let mut down_buf = vec![0.0f32; hidden_dim];
let dump_dsv3 = std::env::var("ALICE_DEEPSEEK_DUMP").is_ok();
let dump_tensor = |name: &str, layer_idx: usize, pos: usize, t: &[f32]| {
if !dump_dsv3 || layer_idx != 0 {
return;
}
let n = t.len();
let head: Vec<f32> = t.iter().take(5).copied().collect();
let l2: f32 = t.iter().map(|v| v * v).sum::<f32>().sqrt();
let sum: f32 = t.iter().sum();
eprintln!(
"{{\"engine\":\"alice\",\"pos\":{pos},\"layer\":{layer_idx},\"op\":\"{name}\",\"len\":{n},\"head\":{head:?},\"l2\":{l2:.6},\"sum\":{sum:.6}}}"
);
};
let _ = &dump_tensor;
if dump_dsv3 {
dump_tensor("hidden_in", 0, pos, &hidden);
}
for layer_idx in 0..c.num_layers {
let layer = &deepseek_layers[layer_idx];
rms_norm(&hidden, &layer.attn_norm, c.norm_eps, &mut norm_buf);
dump_tensor("attn_norm", layer_idx, pos, &norm_buf);
match &layer.q {
DeepSeekQProjection::Dense { q_proj } => {
q_proj.matvec(&norm_buf, &mut q_full);
}
DeepSeekQProjection::LoRA {
q_a_proj,
q_a_norm,
q_b_proj,
} => {
q_a_proj.matvec(&norm_buf, &mut q_a_buf);
rms_norm(&q_a_buf, q_a_norm, c.norm_eps, &mut q_a_normed);
q_b_proj.matvec(&q_a_normed, &mut q_full);
}
}
dump_tensor("q_full", layer_idx, pos, &q_full);
layer.kv_a_proj_with_mqa.matvec(&norm_buf, &mut kv_a_full);
dump_tensor("kv_a_full", layer_idx, pos, &kv_a_full);
dump_tensor(
"kv_a_full_first64",
layer_idx,
pos,
&kv_a_full[..64.min(kv_a_full.len())],
);
let kpe_start = kv_lora_rank.min(kv_a_full.len());
dump_tensor("kv_a_full_tail64", layer_idx, pos, &kv_a_full[kpe_start..]);
dump_tensor("kv_a_full_head512", layer_idx, pos, &kv_a_full[..kpe_start]);
let (kv_a_slice, k_pe_shared) = kv_a_full.split_at_mut(kv_lora_rank);
rms_norm(kv_a_slice, &layer.kv_a_norm, c.norm_eps, &mut kv_a_normed);
dump_tensor("kv_a_normed", layer_idx, pos, &kv_a_normed);
dump_tensor("k_pe_pre_rope", layer_idx, pos, k_pe_shared);
let mut cache_entry = Vec::with_capacity(compressed_kv_dim);
cache_entry.extend_from_slice(&kv_a_normed);
cache_entry.extend_from_slice(k_pe_shared);
layer.kv_b_proj.matvec(&kv_a_normed, &mut kv_up);
dump_tensor("kv_up", layer_idx, pos, &kv_up);
for h in 0..num_heads {
let q_head_off = h * q_head_total;
let q_pe_slice = &mut q_full[q_head_off + qk_nope..q_head_off + q_head_total];
apply_rope_auto(
q_pe_slice,
pos,
qk_rope,
c.rope_theta,
self.rope_freqs.as_deref(),
true, );
}
dump_tensor("q_full_post_rope", layer_idx, pos, &q_full);
apply_rope_auto(
k_pe_shared,
pos,
qk_rope,
c.rope_theta,
self.rope_freqs.as_deref(),
true,
);
dump_tensor("k_pe_post_rope", layer_idx, pos, k_pe_shared);
cache_entry[kv_lora_rank..].copy_from_slice(k_pe_shared);
self.kv_cache.append(layer_idx, &cache_entry, &cache_entry);
let seq_len = pos + 1;
let scale = 1.0 / ((qk_nope + qk_rope) as f32).sqrt();
attn_out.fill(0.0);
let mut scratch_kv_up = vec![0.0f32; num_heads * kv_up_head_total];
let mut scores = vec![0.0f32; seq_len];
for h in 0..num_heads {
let q_head_off = h * q_head_total;
let q_nope_head = &q_full[q_head_off..q_head_off + qk_nope];
let q_pe_head = &q_full[q_head_off + qk_nope..q_head_off + q_head_total];
let mut max_score = f32::NEG_INFINITY;
for t in 0..seq_len {
let cache = self.kv_cache.key_at(layer_idx, t);
let cached_kv_a = &cache[..kv_lora_rank];
let cached_k_pe = &cache[kv_lora_rank..];
layer.kv_b_proj.matvec(cached_kv_a, &mut scratch_kv_up);
let head_off = h * kv_up_head_total;
let k_nope_t = &scratch_kv_up[head_off..head_off + qk_nope];
let mut dot = 0.0f32;
for i in 0..qk_nope {
dot += q_nope_head[i] * k_nope_t[i];
}
for i in 0..qk_rope {
dot += q_pe_head[i] * cached_k_pe[i];
}
dot *= scale;
scores[t] = dot;
if dot > max_score {
max_score = dot;
}
}
let mut denom = 0.0f32;
for s in &mut scores {
*s = (*s - max_score).exp();
denom += *s;
}
for s in &mut scores {
*s /= denom;
}
let attn_head_off = h * v_head_dim;
for t in 0..seq_len {
let cache = self.kv_cache.value_at(layer_idx, t);
let cached_kv_a = &cache[..kv_lora_rank];
layer.kv_b_proj.matvec(cached_kv_a, &mut scratch_kv_up);
let head_off = h * kv_up_head_total;
let v_t = &scratch_kv_up[head_off + qk_nope..head_off + kv_up_head_total];
let w = scores[t];
for j in 0..v_head_dim {
attn_out[attn_head_off + j] += w * v_t[j];
}
}
}
dump_tensor("attn_out", layer_idx, pos, &attn_out);
layer.o_proj.matvec(&attn_out, &mut o_buf);
dump_tensor("o_proj_out", layer_idx, pos, &o_buf);
for i in 0..hidden_dim {
hidden[i] += o_buf[i];
}
dump_tensor("hidden_post_attn", layer_idx, pos, &hidden);
if layer_idx < first_k_dense {
let ffn_norm = layer.ffn_norm.as_ref().expect("dense ffn_norm");
let gate_proj = layer.gate_proj.as_ref().expect("dense gate_proj");
let up_proj = layer.up_proj.as_ref().expect("dense up_proj");
let down_proj = layer.down_proj.as_ref().expect("dense down_proj");
rms_norm(&hidden, ffn_norm, c.norm_eps, &mut norm_buf);
gate_proj.matvec(&norm_buf, &mut gate_buf);
up_proj.matvec(&norm_buf, &mut up_buf);
for i in 0..c.intermediate_dim {
gate_buf[i] = silu(gate_buf[i]) * up_buf[i];
}
down_proj.matvec(&gate_buf, &mut down_buf);
for i in 0..hidden_dim {
hidden[i] += down_buf[i];
}
} else {
let moe = layer
.moe
.as_ref()
.expect("DeepSeek-V3 MoE weights required past first_k_dense_replace");
forward_deepseek_moe_layer(&c, moe, &hidden, &mut norm_buf, &mut down_buf);
for i in 0..hidden_dim {
hidden[i] += down_buf[i];
}
}
dump_tensor("hidden_post_ffn", layer_idx, pos, &hidden);
}
self.kv_cache.advance();
rms_norm(&hidden, &self.output_norm, c.norm_eps, &mut norm_buf);
let mut logits = vec![0.0f32; c.vocab_size];
self.output_proj.matvec(&norm_buf, &mut logits);
logits
}
fn forward_gemma4(&mut self, token_id: u32) -> Vec<f32> {
let c = self.config.clone();
let hidden_dim = c.hidden_dim;
let n_embd_altup = c
.per_layer_input_embedding_dim()
.expect("Gemma4: per_layer_input_embedding_dim");
let pos = self.kv_cache.seq_len();
let emb_start = token_id as usize * hidden_dim;
let mut inpl: Vec<f32> = self.embedding[emb_start..emb_start + hidden_dim].to_vec();
let embed_scale = (hidden_dim as f32).sqrt();
for v in &mut inpl {
*v *= embed_scale;
}
let inp_per_layer =
self.gemma3n_per_layer_inputs(token_id, &inpl, n_embd_altup, c.num_layers);
let max_head_dim = c.head_dim_swa().unwrap_or(c.head_dim).max(c.head_dim);
let max_q_dim = c.num_heads * max_head_dim;
let max_kv_dim = c.num_kv_heads * max_head_dim;
let max_ffn_size = c.ffn_size_per_layer().map_or(c.intermediate_dim, |a| {
a.iter().copied().max().unwrap_or(c.intermediate_dim)
});
let mut norm_buf = vec![0.0f32; hidden_dim];
let mut q_buf = vec![0.0f32; max_q_dim];
let mut k_buf = vec![0.0f32; max_kv_dim];
let mut v_buf = vec![0.0f32; max_kv_dim];
let mut attn_out = vec![0.0f32; max_q_dim];
let mut o_buf = vec![0.0f32; hidden_dim];
let mut gate_buf = vec![0.0f32; max_ffn_size];
let mut up_buf = vec![0.0f32; max_ffn_size];
let mut down_buf = vec![0.0f32; hidden_dim];
for layer_idx in 0..c.num_layers {
let layer = &self.layers[layer_idx];
let head_dim = c.head_dim_for_layer(layer_idx);
let q_dim = c.num_heads * head_dim;
let kv_dim = c.num_kv_heads * head_dim;
let ffn_size = c.ffn_size_for_layer(layer_idx);
let freq_base = c.rope_theta_for_layer(layer_idx);
rms_norm(&inpl, &layer.attn_norm, c.norm_eps, &mut norm_buf);
layer.q_proj.matvec(&norm_buf, &mut q_buf[..q_dim]);
let owns_kv = self.kv_cache.kv_layer_map[layer_idx] == layer_idx;
if owns_kv {
let k_ref = layer
.k_proj
.as_ref()
.expect("Gemma4: k_proj required for own-KV layer");
k_ref.matvec(&norm_buf, &mut k_buf[..kv_dim]);
if let Some(v_ref) = layer.v_proj.as_ref() {
v_ref.matvec(&norm_buf, &mut v_buf[..kv_dim]);
} else {
v_buf[..kv_dim].copy_from_slice(&k_buf[..kv_dim]);
}
}
if let Some(w) = layer.q_norm() {
apply_qk_norm(&mut q_buf[..q_dim], w, head_dim, c.norm_eps);
}
if owns_kv {
if let Some(w) = layer.k_norm() {
apply_qk_norm(&mut k_buf[..kv_dim], w, head_dim, c.norm_eps);
}
apply_head_rms_norm_identity(&mut v_buf[..kv_dim], head_dim, c.norm_eps);
}
let rope_freqs_ref = layer.rope_freqs.as_deref();
for h in 0..c.num_heads {
let start = h * head_dim;
apply_rope_auto(
&mut q_buf[start..start + head_dim],
pos,
head_dim,
freq_base,
rope_freqs_ref,
c.use_neox_rope(),
);
}
if owns_kv {
for h in 0..c.num_kv_heads {
let start = h * head_dim;
apply_rope_auto(
&mut k_buf[start..start + head_dim],
pos,
head_dim,
freq_base,
rope_freqs_ref,
c.use_neox_rope(),
);
}
self.kv_cache
.append(layer_idx, &k_buf[..kv_dim], &v_buf[..kv_dim]);
}
for v in &mut attn_out[..q_dim] {
*v = 0.0;
}
gqa_attention(
&q_buf[..q_dim],
&self.kv_cache,
layer_idx,
pos,
c.num_heads,
c.num_kv_heads,
head_dim,
c.sliding_window_for_layer(layer_idx),
c.attn_logit_softcap(),
Some(1.0),
&mut attn_out[..q_dim],
);
let layer = &self.layers[layer_idx];
layer.o_proj.matvec(&attn_out[..q_dim], &mut o_buf);
if let Some(w) = &layer.post_attn_norm {
rms_norm(&o_buf, w, c.norm_eps, &mut norm_buf);
o_buf.copy_from_slice(&norm_buf);
}
let mut attn_out_local = vec![0.0f32; hidden_dim];
for i in 0..hidden_dim {
attn_out_local[i] = o_buf[i] + inpl[i];
}
rms_norm(&attn_out_local, &layer.ffn_norm, c.norm_eps, &mut norm_buf);
layer
.gate_proj
.as_ref()
.expect("gate_proj required for non-MoE Gemma 4 layer")
.matvec(&norm_buf, &mut gate_buf[..ffn_size]);
layer
.up_proj
.as_ref()
.expect("up_proj required for non-MoE Gemma 4 layer")
.matvec(&norm_buf, &mut up_buf[..ffn_size]);
for i in 0..ffn_size {
gate_buf[i] = gelu_approx(gate_buf[i]) * up_buf[i];
}
layer
.down_proj
.as_ref()
.expect("down_proj required for non-MoE Gemma 4 layer")
.matvec(&gate_buf[..ffn_size], &mut down_buf);
if let Some(w) = &layer.post_ffn_norm {
rms_norm(&down_buf, w, c.norm_eps, &mut norm_buf);
down_buf.copy_from_slice(&norm_buf);
}
let mut cur = vec![0.0f32; hidden_dim];
for i in 0..hidden_dim {
cur[i] = down_buf[i] + attn_out_local[i];
}
if layer.inp_gate().is_some() && layer.proj().is_some() && layer.post_norm().is_some() {
let pe_in = cur.clone();
let inp_gate = layer.inp_gate().unwrap();
let mut gated = vec![0.0f32; n_embd_altup];
inp_gate.matvec(&cur, &mut gated);
for v in &mut gated {
*v = gelu_approx(*v);
}
for i in 0..n_embd_altup {
gated[i] *= inp_per_layer[layer_idx][i];
}
let proj = layer.proj().unwrap();
let mut projected = vec![0.0f32; hidden_dim];
proj.matvec(&gated, &mut projected);
let post_norm = layer.post_norm().unwrap();
rms_norm(&projected, post_norm, c.norm_eps, &mut norm_buf);
for i in 0..hidden_dim {
cur[i] = norm_buf[i] + pe_in[i];
}
}
if let Some(scale) = layer.out_scale.as_ref() {
if let Some(&s) = scale.first() {
for v in &mut cur {
*v *= s;
}
}
}
inpl = cur;
}
self.kv_cache.advance();
rms_norm(&inpl, &self.output_norm, c.norm_eps, &mut norm_buf);
let mut logits = vec![0.0f32; c.vocab_size];
self.output_proj.matvec(&norm_buf, &mut logits);
if let Some(cap) = c.final_logit_softcap() {
for l in &mut logits {
*l = cap * (*l / cap).tanh();
}
}
logits
}
pub fn forward_batch(&mut self, token_ids: &[u32]) -> Vec<Vec<f32>> {
let mut all_logits = Vec::with_capacity(token_ids.len());
for &tok in token_ids {
all_logits.push(self.forward(tok));
}
all_logits
}
fn forward_draft(&mut self, token_id: u32, draft_layers: usize) -> Vec<f32> {
let c = &self.config;
let pos = self.kv_cache.seq_len();
let rope_freqs_ref = self.rope_freqs.as_deref();
let num_draft = draft_layers.min(c.num_layers);
let emb_start = token_id as usize * c.hidden_dim;
let mut hidden: Vec<f32> = self.embedding[emb_start..emb_start + c.hidden_dim].to_vec();
if c.arch == ModelArch::Gemma2 {
let scale = (c.hidden_dim as f32).sqrt();
for h in &mut hidden {
*h *= scale;
}
}
let mut norm_buf = vec![0.0f32; c.hidden_dim];
let kv_dim = c.num_kv_heads * c.head_dim;
let q_dim = c.num_heads * c.head_dim;
let mut q_buf = vec![0.0f32; q_dim];
let mut k_buf = vec![0.0f32; kv_dim];
let mut v_buf = vec![0.0f32; kv_dim];
let mut attn_out = vec![0.0f32; c.hidden_dim];
let mut o_buf = vec![0.0f32; c.hidden_dim];
let mut gate_buf = vec![0.0f32; c.intermediate_dim];
let mut up_buf = vec![0.0f32; c.intermediate_dim];
let mut down_buf = vec![0.0f32; c.hidden_dim];
for layer_idx in 0..num_draft {
let layer = &self.layers[layer_idx];
rms_norm(&hidden, &layer.attn_norm, c.norm_eps, &mut norm_buf);
let q8_attn = quantize_row_q8_k(&norm_buf);
layer.q_proj.matvec_preq(&q8_attn, &mut q_buf);
layer
.k_proj
.as_ref()
.expect("k_proj required for non-shared layer")
.matvec_preq(&q8_attn, &mut k_buf);
layer
.v_proj
.as_ref()
.expect("v_proj required for non-shared layer")
.matvec_preq(&q8_attn, &mut v_buf);
if let Some(b) = layer.q_bias() {
for (q, bi) in q_buf.iter_mut().zip(b.iter()) {
*q += bi;
}
}
if let Some(b) = layer.k_bias() {
for (k, bi) in k_buf.iter_mut().zip(b.iter()) {
*k += bi;
}
}
if let Some(b) = layer.v_bias() {
for (v, bi) in v_buf.iter_mut().zip(b.iter()) {
*v += bi;
}
}
if let Some(w) = layer.q_norm() {
apply_qk_norm(&mut q_buf[..q_dim], w, c.head_dim, c.norm_eps);
}
if let Some(w) = layer.k_norm() {
apply_qk_norm(&mut k_buf, w, c.head_dim, c.norm_eps);
}
for h in 0..c.num_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut q_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
for h in 0..c.num_kv_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut k_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
self.kv_cache.append(layer_idx, &k_buf, &v_buf);
gqa_attention(
&q_buf,
&self.kv_cache,
layer_idx,
pos,
c.num_heads,
c.num_kv_heads,
c.head_dim,
c.sliding_window_for_layer(layer_idx),
c.attn_logit_softcap(),
if c.arch == ModelArch::Gemma3n {
Some(1.0)
} else {
None
},
&mut attn_out,
);
layer.o_proj.matvec(&attn_out, &mut o_buf);
for i in 0..c.hidden_dim {
hidden[i] += o_buf[i];
}
rms_norm(&hidden, &layer.ffn_norm, c.norm_eps, &mut norm_buf);
let q8_ffn = quantize_row_q8_k(&norm_buf);
layer
.gate_proj
.as_ref()
.expect("gate_proj required for non-MoE layer")
.matvec_preq(&q8_ffn, &mut gate_buf);
layer
.up_proj
.as_ref()
.expect("up_proj required for non-MoE layer")
.matvec_preq(&q8_ffn, &mut up_buf);
c.apply_ffn_sparsity(layer_idx, &mut gate_buf);
for i in 0..c.intermediate_dim {
gate_buf[i] = c.apply_ffn_act(layer_idx, gate_buf[i]) * up_buf[i];
}
layer
.down_proj
.as_ref()
.expect("down_proj required for non-MoE layer")
.matvec(&gate_buf, &mut down_buf);
for i in 0..c.hidden_dim {
hidden[i] += down_buf[i];
}
}
self.kv_cache.advance();
rms_norm(&hidden, &self.output_norm, c.norm_eps, &mut norm_buf);
let mut logits = vec![0.0f32; c.vocab_size];
self.output_proj.matvec(&norm_buf, &mut logits);
logits
}
pub fn generate(
&mut self,
tokenizer: &GgufTokenizer,
prompt: &str,
max_new_tokens: usize,
temperature: f32,
top_k: usize,
) -> GenerateResult {
let start = Instant::now();
let mut tokens = tokenizer.encode(prompt);
if tokenizer.add_bos_token && (tokens.is_empty() || tokens[0] != tokenizer.bos_id) {
tokens.insert(0, tokenizer.bos_id);
}
self.clear_cache();
let prompt_token_count = tokens.len();
let prefill_start = Instant::now();
let mut logits = vec![0.0f32; self.config.vocab_size];
for &tok in &tokens {
logits = self.forward(tok);
}
let prefill_ms = prefill_start.elapsed().as_millis() as u64;
let decode_start = Instant::now();
let mut generated = Vec::with_capacity(max_new_tokens);
let rep_penalty: f32 = std::env::var("ALICE_LLM_REP_PENALTY")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(1.0);
let rep_window: usize = std::env::var("ALICE_LLM_REP_WINDOW")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(64);
for _ in 0..max_new_tokens {
if (rep_penalty - 1.0).abs() > f32::EPSILON {
let start = generated.len().saturating_sub(rep_window);
for &tok in &generated[start..] {
let idx = tok as usize;
if idx < logits.len() {
if logits[idx] > 0.0 {
logits[idx] /= rep_penalty;
} else {
logits[idx] *= rep_penalty;
}
}
}
}
if temperature > 0.0 && temperature != 1.0 {
let inv_t = 1.0 / temperature;
for l in &mut logits {
*l *= inv_t;
}
}
let next_token = if top_k > 0 && top_k < logits.len() {
let mut indexed: Vec<(usize, f32)> = logits.iter().copied().enumerate().collect();
indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
indexed.truncate(top_k);
let max_val = indexed[0].1;
let mut sum = 0.0f32;
for (_, l) in &mut indexed {
*l = (*l - max_val).exp();
sum += *l;
}
for (_, l) in &mut indexed {
*l /= sum;
}
indexed
.iter()
.max_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
.map_or(0, |(idx, _)| *idx as u32)
} else {
logits
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
.map_or(0, |(idx, _)| idx as u32)
};
if next_token == tokenizer.eos_id {
break;
}
tokens.push(next_token);
generated.push(next_token);
if std::env::var("ALICE_LLM_DEBUG_TOKENS").is_ok() {
let tok_text = tokenizer.decode(&[next_token]);
eprintln!("[TOK] id={next_token} text={tok_text:?}");
}
logits = self.forward(next_token);
}
let decode_ms = decode_start.elapsed().as_millis() as u64;
let total_ms = start.elapsed().as_millis() as u64;
let gen_count = generated.len();
let tok_per_sec = if decode_ms > 0 {
gen_count as f64 / (decode_ms as f64 / 1000.0)
} else {
0.0
};
let output_text = tokenizer.decode(&generated);
GenerateResult {
text: output_text,
tokens_generated: gen_count,
prompt_tokens: prompt_token_count,
prefill_ms,
decode_ms,
total_ms,
tokens_per_sec: tok_per_sec,
spec_stats: None,
}
}
pub fn generate_speculative(
&mut self,
tokenizer: &GgufTokenizer,
prompt: &str,
max_new_tokens: usize,
temperature: f32,
_top_k: usize,
spec_k: usize,
draft_layers: usize,
) -> GenerateResult {
let start = Instant::now();
let mut tokens = tokenizer.encode(prompt);
if tokenizer.add_bos_token && (tokens.is_empty() || tokens[0] != tokenizer.bos_id) {
tokens.insert(0, tokenizer.bos_id);
}
self.clear_cache();
let prompt_token_count = tokens.len();
let prefill_start = Instant::now();
let mut logits = vec![0.0f32; self.config.vocab_size];
for &tok in &tokens {
logits = self.forward(tok);
}
let prefill_ms = prefill_start.elapsed().as_millis() as u64;
let decode_start = Instant::now();
let mut generated = Vec::with_capacity(max_new_tokens);
let mut total_drafted: usize = 0;
let mut total_accepted: usize = 0;
let mut rng = Rng64::new(42);
while generated.len() < max_new_tokens {
let next_token = if temperature > 0.0 {
sample_from_probs(&softmax(&logits), rng.next_f32())
} else {
argmax(&logits)
};
if next_token == tokenizer.eos_id {
break;
}
generated.push(next_token);
tokens.push(next_token);
let remaining = max_new_tokens - generated.len();
if remaining == 0 {
logits = self.forward(next_token);
continue;
}
let k = spec_k.min(remaining);
let saved_pos = self.kv_cache.seq_len();
let mut draft_tokens = Vec::with_capacity(k);
let mut draft_logits_all = Vec::with_capacity(k);
let mut draft_input = next_token;
for _ in 0..k {
let dl = self.forward_draft(draft_input, draft_layers);
draft_input = argmax(&dl);
draft_tokens.push(draft_input);
draft_logits_all.push(dl);
}
total_drafted += draft_tokens.len();
self.kv_cache.rollback_to(saved_pos);
logits = self.forward(next_token);
let mut all_accepted = true;
for i in 0..draft_tokens.len() {
let p = softmax(&logits);
let q = softmax(&draft_logits_all[i]);
let x = draft_tokens[i] as usize;
let p_x = if x < p.len() { p[x] } else { 0.0 };
let q_x = if x < q.len() { q[x] } else { 1e-10 };
let accept_prob = (p_x / q_x.max(1e-10)).min(1.0);
let r = rng.next_f32();
if r < accept_prob {
generated.push(draft_tokens[i]);
tokens.push(draft_tokens[i]);
total_accepted += 1;
logits = self.forward(draft_tokens[i]);
} else {
let mut adjusted = vec![0.0f32; p.len()];
let mut adj_sum = 0.0f32;
for j in 0..p.len() {
adjusted[j] = (p[j] - q[j]).max(0.0);
adj_sum += adjusted[j];
}
let resampled = if adj_sum > 0.0 {
let inv = 1.0 / adj_sum;
for a in &mut adjusted {
*a *= inv;
}
sample_from_probs(&adjusted, rng.next_f32())
} else {
sample_from_probs(&p, rng.next_f32())
};
if resampled == tokenizer.eos_id {
all_accepted = false;
break;
}
generated.push(resampled);
tokens.push(resampled);
logits = self.forward(resampled);
all_accepted = false;
break;
}
}
if all_accepted && generated.len() < max_new_tokens {
let bonus = sample_from_probs(&softmax(&logits), rng.next_f32());
if bonus != tokenizer.eos_id {
generated.push(bonus);
tokens.push(bonus);
logits = self.forward(bonus);
}
}
}
let decode_ms = decode_start.elapsed().as_millis() as u64;
let total_ms = start.elapsed().as_millis() as u64;
let gen_count = generated.len();
let tok_per_sec = if decode_ms > 0 {
gen_count as f64 / (decode_ms as f64 / 1000.0)
} else {
0.0
};
let output_text = tokenizer.decode(&generated);
GenerateResult {
text: output_text,
tokens_generated: gen_count,
prompt_tokens: prompt_token_count,
prefill_ms,
decode_ms,
total_ms,
tokens_per_sec: tok_per_sec,
spec_stats: Some(SpecStats {
draft_tokens: total_drafted,
accepted_tokens: total_accepted,
draft_layers,
spec_k,
}),
}
}
pub fn generate_speculative_dual(
&mut self,
draft_model: &mut Llama3Model,
tokenizer: &GgufTokenizer,
prompt: &str,
max_new_tokens: usize,
temperature: f32,
spec_k: usize,
) -> GenerateResult {
let start = Instant::now();
let mut tokens = tokenizer.encode(prompt);
if tokenizer.add_bos_token && (tokens.is_empty() || tokens[0] != tokenizer.bos_id) {
tokens.insert(0, tokenizer.bos_id);
}
self.clear_cache();
draft_model.clear_cache();
let prompt_token_count = tokens.len();
let prefill_start = Instant::now();
let mut logits = vec![0.0f32; self.config.vocab_size];
for &tok in &tokens {
logits = self.forward(tok);
draft_model.forward(tok);
}
let prefill_ms = prefill_start.elapsed().as_millis() as u64;
let decode_start = Instant::now();
let mut generated = Vec::with_capacity(max_new_tokens);
let mut total_drafted: usize = 0;
let mut total_accepted: usize = 0;
let mut rng = Rng64::new(42);
while generated.len() < max_new_tokens {
let next_token = if temperature > 0.0 {
sample_from_probs(&softmax(&logits), rng.next_f32())
} else {
argmax(&logits)
};
if next_token == tokenizer.eos_id {
break;
}
generated.push(next_token);
tokens.push(next_token);
let remaining = max_new_tokens - generated.len();
if remaining == 0 {
logits = self.forward(next_token);
draft_model.forward(next_token);
continue;
}
let k = spec_k.min(remaining);
let saved_draft_pos = draft_model.kv_cache.seq_len();
let _saved_main_pos = self.kv_cache.seq_len();
let mut draft_tokens = Vec::with_capacity(k);
let mut draft_logits_all = Vec::with_capacity(k);
let mut draft_input = next_token;
let dl = draft_model.forward(draft_input);
draft_input = argmax(&dl);
draft_tokens.push(draft_input);
draft_logits_all.push(dl);
for _ in 1..k {
let dl = draft_model.forward(draft_input);
draft_input = argmax(&dl);
draft_tokens.push(draft_input);
draft_logits_all.push(dl);
}
total_drafted += draft_tokens.len();
logits = self.forward(next_token);
let mut num_accepted = 0;
let mut rejected = false;
for i in 0..draft_tokens.len() {
let p = softmax(&logits);
let q = softmax(&draft_logits_all[i]);
let x = draft_tokens[i] as usize;
let p_x = if x < p.len() { p[x] } else { 0.0 };
let q_x = if x < q.len() { q[x] } else { 1e-10 };
let accept_prob = (p_x / q_x.max(1e-10)).min(1.0);
let r = rng.next_f32();
if r < accept_prob {
generated.push(draft_tokens[i]);
tokens.push(draft_tokens[i]);
total_accepted += 1;
num_accepted += 1;
logits = self.forward(draft_tokens[i]);
} else {
let mut adjusted = vec![0.0f32; p.len()];
let mut adj_sum = 0.0f32;
for j in 0..p.len() {
adjusted[j] = (p[j] - q[j]).max(0.0);
adj_sum += adjusted[j];
}
let resampled = if adj_sum > 0.0 {
let inv = 1.0 / adj_sum;
for a in &mut adjusted {
*a *= inv;
}
sample_from_probs(&adjusted, rng.next_f32())
} else {
sample_from_probs(&p, rng.next_f32())
};
if resampled != tokenizer.eos_id {
generated.push(resampled);
tokens.push(resampled);
logits = self.forward(resampled);
}
rejected = true;
break;
}
}
if !rejected && num_accepted == draft_tokens.len() && generated.len() < max_new_tokens {
let bonus = sample_from_probs(&softmax(&logits), rng.next_f32());
if bonus != tokenizer.eos_id {
generated.push(bonus);
tokens.push(bonus);
logits = self.forward(bonus);
}
}
let draft_keep = saved_draft_pos + 1 + num_accepted;
draft_model.kv_cache.rollback_to(draft_keep);
if let Some(&last) = tokens.last() {
draft_model.forward(last);
}
}
let decode_ms = decode_start.elapsed().as_millis() as u64;
let total_ms = start.elapsed().as_millis() as u64;
let gen_count = generated.len();
let tok_per_sec = if decode_ms > 0 {
gen_count as f64 / (decode_ms as f64 / 1000.0)
} else {
0.0
};
GenerateResult {
text: tokenizer.decode(&generated),
tokens_generated: gen_count,
prompt_tokens: prompt_token_count,
prefill_ms,
decode_ms,
total_ms,
tokens_per_sec: tok_per_sec,
spec_stats: Some(SpecStats {
draft_tokens: total_drafted,
accepted_tokens: total_accepted,
draft_layers: draft_model.config.num_layers,
spec_k,
}),
}
}
#[cfg(feature = "dspark")]
#[allow(clippy::too_many_arguments)]
pub fn generate_speculative_dual_dspark(
&mut self,
draft_model: &mut dyn crate::speculative_dspark::DraftBackend,
tokenizer: &GgufTokenizer,
prompt: &str,
max_new_tokens: usize,
temperature: f32,
spec_k: usize,
bigram_bias: Option<&dyn crate::speculative_dspark::BigramBias>,
bigram_strength: f32,
advanced: Option<&crate::speculative_dspark::DsparkAdvancedConfig<'_>>,
) -> Result<GenerateResult, crate::speculative_dspark::DsparkError> {
if let Some(b) = bigram_bias {
let draft_vocab = draft_model.vocab_size();
if b.vocab_size() != draft_vocab {
return Err(crate::speculative_dspark::DsparkError::VocabSizeMismatch {
expected: draft_vocab,
got: b.vocab_size(),
});
}
}
if let Some(cfg) = advanced {
let head = cfg.confidence_head;
let need_block = spec_k as u32;
if head.block_size() < need_block {
return Err(
crate::speculative_dspark::DsparkError::ConfidenceHeadBlockSizeMismatch {
expected: need_block,
got: head.block_size(),
},
);
}
let draft_hidden = draft_model.hidden_dim();
if head.hidden_dim() != draft_hidden {
return Err(crate::speculative_dspark::DsparkError::HiddenDimMismatch {
expected: draft_hidden,
got: head.hidden_dim(),
});
}
}
let start = Instant::now();
let mut tokens = tokenizer.encode(prompt);
if tokenizer.add_bos_token && (tokens.is_empty() || tokens[0] != tokenizer.bos_id) {
tokens.insert(0, tokenizer.bos_id);
}
self.clear_cache();
draft_model.clear_cache();
let prompt_token_count = tokens.len();
let prefill_start = Instant::now();
let mut logits = vec![0.0f32; self.config.vocab_size];
for &tok in &tokens {
logits = self.forward(tok);
draft_model.forward(tok);
}
let prefill_ms = prefill_start.elapsed().as_millis() as u64;
let decode_start = Instant::now();
let mut generated = Vec::with_capacity(max_new_tokens);
let mut total_drafted: usize = 0;
let mut total_accepted: usize = 0;
let mut rng = Rng64::new(42);
while generated.len() < max_new_tokens {
let next_token = if temperature > 0.0 {
sample_from_probs(&softmax(&logits), rng.next_f32())
} else {
argmax(&logits)
};
if next_token == tokenizer.eos_id {
break;
}
generated.push(next_token);
tokens.push(next_token);
let remaining = max_new_tokens - generated.len();
if remaining == 0 {
logits = self.forward(next_token);
draft_model.forward(next_token);
continue;
}
let k = spec_k.min(remaining);
let saved_draft_pos = draft_model.seq_len();
let _saved_main_pos = self.kv_cache.seq_len();
let mut draft_tokens: Vec<u32> = Vec::with_capacity(k);
let mut draft_logits_all: Vec<Vec<f32>> = Vec::with_capacity(k);
let mut draft_input = next_token;
let mut prev_for_bigram = next_token;
for i in 0..k {
let (mut dl, hidden_opt) = if let Some(cfg) = advanced {
let (l, h) =
draft_model.forward_capture_hidden(draft_input, cfg.hidden_capture_layer);
(l, Some(h))
} else {
(draft_model.forward(draft_input), None)
};
crate::speculative_dspark::apply_bigram_bias_maybe(
&mut dl,
prev_for_bigram,
bigram_bias,
bigram_strength,
)?;
if let (Some(cfg), Some(hidden)) = (advanced, hidden_opt.as_ref()) {
let conf = cfg.confidence_head.predict(i as u32, hidden)?;
if conf < cfg.confidence_threshold {
break;
}
}
draft_input = argmax(&dl);
draft_tokens.push(draft_input);
draft_logits_all.push(dl);
prev_for_bigram = draft_input;
}
total_drafted += draft_tokens.len();
logits = self.forward(next_token);
let mut num_accepted = 0;
let mut rejected = false;
for i in 0..draft_tokens.len() {
let p = softmax(&logits);
let q = softmax(&draft_logits_all[i]);
let x = draft_tokens[i] as usize;
let p_x = if x < p.len() { p[x] } else { 0.0 };
let q_x = if x < q.len() { q[x] } else { 1e-10 };
let accept_prob = (p_x / q_x.max(1e-10)).min(1.0);
let r = rng.next_f32();
if r < accept_prob {
generated.push(draft_tokens[i]);
tokens.push(draft_tokens[i]);
total_accepted += 1;
num_accepted += 1;
logits = self.forward(draft_tokens[i]);
} else {
let mut adjusted = vec![0.0f32; p.len()];
let mut adj_sum = 0.0f32;
for j in 0..p.len() {
adjusted[j] = (p[j] - q[j]).max(0.0);
adj_sum += adjusted[j];
}
let resampled = if adj_sum > 0.0 {
let inv = 1.0 / adj_sum;
for a in &mut adjusted {
*a *= inv;
}
sample_from_probs(&adjusted, rng.next_f32())
} else {
sample_from_probs(&p, rng.next_f32())
};
if resampled != tokenizer.eos_id {
generated.push(resampled);
tokens.push(resampled);
logits = self.forward(resampled);
}
rejected = true;
break;
}
}
if !rejected && num_accepted == draft_tokens.len() && generated.len() < max_new_tokens {
let bonus = sample_from_probs(&softmax(&logits), rng.next_f32());
if bonus != tokenizer.eos_id {
generated.push(bonus);
tokens.push(bonus);
logits = self.forward(bonus);
}
}
let draft_keep = saved_draft_pos + 1 + num_accepted;
draft_model.rollback_to(draft_keep);
if let Some(&last) = tokens.last() {
draft_model.forward(last);
}
}
let decode_ms = decode_start.elapsed().as_millis() as u64;
let total_ms = start.elapsed().as_millis() as u64;
let gen_count = generated.len();
let tok_per_sec = if decode_ms > 0 {
gen_count as f64 / (decode_ms as f64 / 1000.0)
} else {
0.0
};
Ok(GenerateResult {
text: tokenizer.decode(&generated),
tokens_generated: gen_count,
prompt_tokens: prompt_token_count,
prefill_ms,
decode_ms,
total_ms,
tokens_per_sec: tok_per_sec,
spec_stats: Some(SpecStats {
draft_tokens: total_drafted,
accepted_tokens: total_accepted,
draft_layers: draft_model.num_layers() as usize,
spec_k,
}),
})
}
#[cfg(feature = "dspark")]
#[allow(clippy::too_many_arguments)]
pub fn generate_speculative_dual_collect_labels(
&mut self,
draft_model: &mut dyn crate::speculative_dspark::DraftBackend,
tokenizer: &GgufTokenizer,
prompt: &str,
max_new_tokens: usize,
temperature: f32,
spec_k: usize,
hidden_capture_layer: Option<usize>,
) -> Result<
(
GenerateResult,
Vec<crate::speculative_dspark::DsparkLabelSample>,
),
crate::speculative_dspark::DsparkError,
> {
let start = Instant::now();
let mut tokens = tokenizer.encode(prompt);
if tokenizer.add_bos_token && (tokens.is_empty() || tokens[0] != tokenizer.bos_id) {
tokens.insert(0, tokenizer.bos_id);
}
self.clear_cache();
draft_model.clear_cache();
let prompt_token_count = tokens.len();
let prefill_start = Instant::now();
let mut logits = vec![0.0f32; self.config.vocab_size];
for &tok in &tokens {
logits = self.forward(tok);
draft_model.forward(tok);
}
let prefill_ms = prefill_start.elapsed().as_millis() as u64;
let decode_start = Instant::now();
let mut generated = Vec::with_capacity(max_new_tokens);
let mut total_drafted: usize = 0;
let mut total_accepted: usize = 0;
let mut rng = Rng64::new(42);
let mut samples: Vec<crate::speculative_dspark::DsparkLabelSample> = Vec::new();
while generated.len() < max_new_tokens {
let next_token = if temperature > 0.0 {
sample_from_probs(&softmax(&logits), rng.next_f32())
} else {
argmax(&logits)
};
if next_token == tokenizer.eos_id {
break;
}
generated.push(next_token);
tokens.push(next_token);
let remaining = max_new_tokens - generated.len();
if remaining == 0 {
logits = self.forward(next_token);
draft_model.forward(next_token);
continue;
}
let k = spec_k.min(remaining);
let saved_draft_pos = draft_model.seq_len();
let mut draft_tokens: Vec<u32> = Vec::with_capacity(k);
let mut draft_logits_all: Vec<Vec<f32>> = Vec::with_capacity(k);
let mut draft_hiddens: Vec<Vec<f32>> = Vec::with_capacity(k);
let mut draft_input = next_token;
for _ in 0..k {
let (dl, hidden) =
draft_model.forward_capture_hidden(draft_input, hidden_capture_layer);
draft_input = argmax(&dl);
draft_tokens.push(draft_input);
draft_logits_all.push(dl);
draft_hiddens.push(hidden);
}
total_drafted += draft_tokens.len();
logits = self.forward(next_token);
let mut num_accepted = 0;
let mut rejected = false;
for i in 0..draft_tokens.len() {
let p = softmax(&logits);
let q = softmax(&draft_logits_all[i]);
let x = draft_tokens[i] as usize;
let p_x = if x < p.len() { p[x] } else { 0.0 };
let q_x = if x < q.len() { q[x] } else { 1e-10 };
let accept_prob = (p_x / q_x.max(1e-10)).min(1.0);
let r = rng.next_f32();
let accepted = r < accept_prob;
samples.push(crate::speculative_dspark::DsparkLabelSample {
position: i as u32,
hidden: core::mem::take(&mut draft_hiddens[i]),
was_accepted: accepted,
});
if accepted {
generated.push(draft_tokens[i]);
tokens.push(draft_tokens[i]);
total_accepted += 1;
num_accepted += 1;
logits = self.forward(draft_tokens[i]);
} else {
let mut adjusted = vec![0.0f32; p.len()];
let mut adj_sum = 0.0f32;
for j in 0..p.len() {
adjusted[j] = (p[j] - q[j]).max(0.0);
adj_sum += adjusted[j];
}
let resampled = if adj_sum > 0.0 {
let inv = 1.0 / adj_sum;
for a in &mut adjusted {
*a *= inv;
}
sample_from_probs(&adjusted, rng.next_f32())
} else {
sample_from_probs(&p, rng.next_f32())
};
if resampled != tokenizer.eos_id {
generated.push(resampled);
tokens.push(resampled);
logits = self.forward(resampled);
}
rejected = true;
break;
}
}
if !rejected && num_accepted == draft_tokens.len() && generated.len() < max_new_tokens {
let bonus = sample_from_probs(&softmax(&logits), rng.next_f32());
if bonus != tokenizer.eos_id {
generated.push(bonus);
tokens.push(bonus);
logits = self.forward(bonus);
}
}
let draft_keep = saved_draft_pos + 1 + num_accepted;
draft_model.rollback_to(draft_keep);
if let Some(&last) = tokens.last() {
draft_model.forward(last);
}
}
let decode_ms = decode_start.elapsed().as_millis() as u64;
let total_ms = start.elapsed().as_millis() as u64;
let gen_count = generated.len();
let tok_per_sec = if decode_ms > 0 {
gen_count as f64 / (decode_ms as f64 / 1000.0)
} else {
0.0
};
let result = GenerateResult {
text: tokenizer.decode(&generated),
tokens_generated: gen_count,
prompt_tokens: prompt_token_count,
prefill_ms,
decode_ms,
total_ms,
tokens_per_sec: tok_per_sec,
spec_stats: Some(SpecStats {
draft_tokens: total_drafted,
accepted_tokens: total_accepted,
draft_layers: draft_model.num_layers() as usize,
spec_k,
}),
};
Ok((result, samples))
}
pub fn forward_ternary(&mut self, token_id: u32) -> Vec<f32> {
let ternary_layers = self
.ternary_layers
.as_ref()
.expect("call load_ternary() first");
let ternary_output = self
.ternary_output_proj
.as_ref()
.expect("call load_ternary() first");
let c = &self.config;
let pos = self.kv_cache.seq_len();
let rope_freqs_ref = self.rope_freqs.as_deref();
let emb_start = token_id as usize * c.hidden_dim;
let mut hidden: Vec<f32> = self.embedding[emb_start..emb_start + c.hidden_dim].to_vec();
if c.arch == ModelArch::Gemma2 {
let scale = (c.hidden_dim as f32).sqrt();
for h in &mut hidden {
*h *= scale;
}
}
let mut norm_buf = vec![0.0f32; c.hidden_dim];
let kv_dim = c.num_kv_heads * c.head_dim;
let mut q_buf = vec![0.0f32; c.num_heads * c.head_dim];
let mut k_buf = vec![0.0f32; kv_dim];
let mut v_buf = vec![0.0f32; kv_dim];
let mut attn_out = vec![0.0f32; c.hidden_dim];
let mut o_buf = vec![0.0f32; c.hidden_dim];
let mut gate_buf = vec![0.0f32; c.intermediate_dim];
let mut up_buf = vec![0.0f32; c.intermediate_dim];
let mut down_buf = vec![0.0f32; c.hidden_dim];
for layer_idx in 0..c.num_layers {
let tl = &ternary_layers[layer_idx];
rms_norm(&hidden, &tl.attn_norm, c.norm_eps, &mut norm_buf);
ternary_matvec(&tl.q_proj, &norm_buf, &mut q_buf);
ternary_matvec(&tl.k_proj, &norm_buf, &mut k_buf);
ternary_matvec(&tl.v_proj, &norm_buf, &mut v_buf);
for h in 0..c.num_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut q_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
for h in 0..c.num_kv_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut k_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
self.kv_cache.append(layer_idx, &k_buf, &v_buf);
gqa_attention(
&q_buf,
&self.kv_cache,
layer_idx,
pos,
c.num_heads,
c.num_kv_heads,
c.head_dim,
c.sliding_window_for_layer(layer_idx),
c.attn_logit_softcap(),
if c.arch == ModelArch::Gemma3n {
Some(1.0)
} else {
None
},
&mut attn_out,
);
ternary_matvec(&tl.o_proj, &attn_out, &mut o_buf);
for i in 0..c.hidden_dim {
hidden[i] += o_buf[i];
}
rms_norm(&hidden, &tl.ffn_norm, c.norm_eps, &mut norm_buf);
ternary_matvec(&tl.gate_proj, &norm_buf, &mut gate_buf);
ternary_matvec(&tl.up_proj, &norm_buf, &mut up_buf);
c.apply_ffn_sparsity(layer_idx, &mut gate_buf);
for i in 0..c.intermediate_dim {
gate_buf[i] = c.apply_ffn_act(layer_idx, gate_buf[i]) * up_buf[i];
}
ternary_matvec(&tl.down_proj, &gate_buf, &mut down_buf);
for i in 0..c.hidden_dim {
hidden[i] += down_buf[i];
}
}
self.kv_cache.advance();
rms_norm(&hidden, &self.output_norm, c.norm_eps, &mut norm_buf);
let mut logits = vec![0.0f32; c.vocab_size];
ternary_matvec(ternary_output, &norm_buf, &mut logits);
logits
}
pub fn generate_ternary(
&mut self,
tokenizer: &GgufTokenizer,
prompt: &str,
max_new_tokens: usize,
temperature: f32,
top_k: usize,
) -> GenerateResult {
let start = Instant::now();
let mut tokens = tokenizer.encode(prompt);
if tokenizer.add_bos_token && (tokens.is_empty() || tokens[0] != tokenizer.bos_id) {
tokens.insert(0, tokenizer.bos_id);
}
self.clear_cache();
let prompt_token_count = tokens.len();
let prefill_start = Instant::now();
let mut logits = vec![0.0f32; self.config.vocab_size];
for &tok in &tokens {
logits = self.forward_ternary(tok);
}
let prefill_ms = prefill_start.elapsed().as_millis() as u64;
let decode_start = Instant::now();
let mut generated = Vec::with_capacity(max_new_tokens);
for _ in 0..max_new_tokens {
let next_token = sample_token(&logits, temperature, top_k);
if next_token == tokenizer.eos_id {
break;
}
tokens.push(next_token);
generated.push(next_token);
logits = self.forward_ternary(next_token);
}
let decode_ms = decode_start.elapsed().as_millis() as u64;
let total_ms = start.elapsed().as_millis() as u64;
let gen_count = generated.len();
let tok_per_sec = if decode_ms > 0 {
gen_count as f64 / (decode_ms as f64 / 1000.0)
} else {
0.0
};
GenerateResult {
text: tokenizer.decode(&generated),
tokens_generated: gen_count,
prompt_tokens: prompt_token_count,
prefill_ms,
decode_ms,
total_ms,
tokens_per_sec: tok_per_sec,
spec_stats: None,
}
}
pub fn load_sparse_ternary(&mut self, threshold_ratio: f32, n_keep: usize) {
let c = &self.config;
let kv_dim = c.num_kv_heads * c.head_dim;
let mut layers = Vec::with_capacity(c.num_layers);
for (i, layer) in self.layers.iter().enumerate() {
eprint!(" Sparse-ternarizing layer {i}/{} ...\r", c.num_layers);
layers.push(SparseTernaryLayerWeights {
attn_norm: layer.attn_norm.clone(),
q_proj: sparsify_weight(
&layer.q_proj,
c.hidden_dim,
c.hidden_dim,
threshold_ratio,
n_keep,
),
k_proj: sparsify_weight(
layer.k_proj.as_ref().expect("k_proj required for sparsify"),
kv_dim,
c.hidden_dim,
threshold_ratio,
n_keep,
),
v_proj: sparsify_weight(
layer.v_proj.as_ref().expect("v_proj required for sparsify"),
kv_dim,
c.hidden_dim,
threshold_ratio,
n_keep,
),
o_proj: sparsify_weight(
&layer.o_proj,
c.hidden_dim,
c.hidden_dim,
threshold_ratio,
n_keep,
),
ffn_norm: layer.ffn_norm.clone(),
gate_proj: sparsify_weight(
layer
.gate_proj
.as_ref()
.expect("gate_proj required for sparsify"),
c.intermediate_dim,
c.hidden_dim,
threshold_ratio,
n_keep,
),
up_proj: sparsify_weight(
layer
.up_proj
.as_ref()
.expect("up_proj required for sparsify"),
c.intermediate_dim,
c.hidden_dim,
threshold_ratio,
n_keep,
),
down_proj: sparsify_weight(
layer
.down_proj
.as_ref()
.expect("down_proj required for sparsify"),
c.hidden_dim,
c.intermediate_dim,
threshold_ratio,
n_keep,
),
});
}
eprintln!(
" Sparse-ternarized {}/{} layers ({}:16)",
c.num_layers, c.num_layers, n_keep
);
self.sparse_ternary_output = Some(sparsify_weight(
&self.output_proj,
c.vocab_size,
c.hidden_dim,
threshold_ratio,
n_keep,
));
self.sparse_ternary_layers = Some(layers);
}
pub fn forward_sparse_ternary(&mut self, token_id: u32) -> Vec<f32> {
let st_layers = self
.sparse_ternary_layers
.as_ref()
.expect("call load_sparse_ternary() first");
let st_output = self
.sparse_ternary_output
.as_ref()
.expect("call load_sparse_ternary() first");
let c = &self.config;
let pos = self.kv_cache.seq_len();
let rope_freqs_ref = self.rope_freqs.as_deref();
let emb_start = token_id as usize * c.hidden_dim;
let mut hidden: Vec<f32> = self.embedding[emb_start..emb_start + c.hidden_dim].to_vec();
if c.arch == ModelArch::Gemma2 {
let scale = (c.hidden_dim as f32).sqrt();
for h in &mut hidden {
*h *= scale;
}
}
let mut norm_buf = vec![0.0f32; c.hidden_dim];
let kv_dim = c.num_kv_heads * c.head_dim;
let mut q_buf = vec![0.0f32; c.num_heads * c.head_dim];
let mut k_buf = vec![0.0f32; kv_dim];
let mut v_buf = vec![0.0f32; kv_dim];
let mut attn_out = vec![0.0f32; c.hidden_dim];
let mut o_buf = vec![0.0f32; c.hidden_dim];
let mut gate_buf = vec![0.0f32; c.intermediate_dim];
let mut up_buf = vec![0.0f32; c.intermediate_dim];
let mut down_buf = vec![0.0f32; c.hidden_dim];
for layer_idx in 0..c.num_layers {
let sl = &st_layers[layer_idx];
rms_norm(&hidden, &sl.attn_norm, c.norm_eps, &mut norm_buf);
sparse_ternary_matvec(&sl.q_proj, &norm_buf, &mut q_buf);
sparse_ternary_matvec(&sl.k_proj, &norm_buf, &mut k_buf);
sparse_ternary_matvec(&sl.v_proj, &norm_buf, &mut v_buf);
for h in 0..c.num_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut q_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
for h in 0..c.num_kv_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut k_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
self.kv_cache.append(layer_idx, &k_buf, &v_buf);
gqa_attention(
&q_buf,
&self.kv_cache,
layer_idx,
pos,
c.num_heads,
c.num_kv_heads,
c.head_dim,
c.sliding_window_for_layer(layer_idx),
c.attn_logit_softcap(),
if c.arch == ModelArch::Gemma3n {
Some(1.0)
} else {
None
},
&mut attn_out,
);
sparse_ternary_matvec(&sl.o_proj, &attn_out, &mut o_buf);
for i in 0..c.hidden_dim {
hidden[i] += o_buf[i];
}
rms_norm(&hidden, &sl.ffn_norm, c.norm_eps, &mut norm_buf);
sparse_ternary_matvec(&sl.gate_proj, &norm_buf, &mut gate_buf);
sparse_ternary_matvec(&sl.up_proj, &norm_buf, &mut up_buf);
c.apply_ffn_sparsity(layer_idx, &mut gate_buf);
for i in 0..c.intermediate_dim {
gate_buf[i] = c.apply_ffn_act(layer_idx, gate_buf[i]) * up_buf[i];
}
sparse_ternary_matvec(&sl.down_proj, &gate_buf, &mut down_buf);
for i in 0..c.hidden_dim {
hidden[i] += down_buf[i];
}
}
self.kv_cache.advance();
rms_norm(&hidden, &self.output_norm, c.norm_eps, &mut norm_buf);
let mut logits = vec![0.0f32; c.vocab_size];
sparse_ternary_matvec(st_output, &norm_buf, &mut logits);
logits
}
fn forward_sparse_ternary_draft(&mut self, token_id: u32, draft_layers: usize) -> Vec<f32> {
let st_layers = self
.sparse_ternary_layers
.as_ref()
.expect("call load_sparse_ternary() first");
let st_output = self
.sparse_ternary_output
.as_ref()
.expect("call load_sparse_ternary() first");
let c = &self.config;
let pos = self.kv_cache.seq_len();
let rope_freqs_ref = self.rope_freqs.as_deref();
let num_draft = draft_layers.min(c.num_layers);
let emb_start = token_id as usize * c.hidden_dim;
let mut hidden: Vec<f32> = self.embedding[emb_start..emb_start + c.hidden_dim].to_vec();
if c.arch == ModelArch::Gemma2 {
let scale = (c.hidden_dim as f32).sqrt();
for h in &mut hidden {
*h *= scale;
}
}
let mut norm_buf = vec![0.0f32; c.hidden_dim];
let kv_dim = c.num_kv_heads * c.head_dim;
let q_dim = c.num_heads * c.head_dim;
let mut q_buf = vec![0.0f32; q_dim];
let mut k_buf = vec![0.0f32; kv_dim];
let mut v_buf = vec![0.0f32; kv_dim];
let mut attn_out = vec![0.0f32; c.hidden_dim];
let mut o_buf = vec![0.0f32; c.hidden_dim];
let mut gate_buf = vec![0.0f32; c.intermediate_dim];
let mut up_buf = vec![0.0f32; c.intermediate_dim];
let mut down_buf = vec![0.0f32; c.hidden_dim];
for layer_idx in 0..num_draft {
let sl = &st_layers[layer_idx];
rms_norm(&hidden, &sl.attn_norm, c.norm_eps, &mut norm_buf);
sparse_ternary_matvec(&sl.q_proj, &norm_buf, &mut q_buf);
sparse_ternary_matvec(&sl.k_proj, &norm_buf, &mut k_buf);
sparse_ternary_matvec(&sl.v_proj, &norm_buf, &mut v_buf);
for h in 0..c.num_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut q_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
for h in 0..c.num_kv_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut k_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
self.kv_cache.append(layer_idx, &k_buf, &v_buf);
gqa_attention(
&q_buf,
&self.kv_cache,
layer_idx,
pos,
c.num_heads,
c.num_kv_heads,
c.head_dim,
c.sliding_window_for_layer(layer_idx),
c.attn_logit_softcap(),
if c.arch == ModelArch::Gemma3n {
Some(1.0)
} else {
None
},
&mut attn_out,
);
sparse_ternary_matvec(&sl.o_proj, &attn_out, &mut o_buf);
for i in 0..c.hidden_dim {
hidden[i] += o_buf[i];
}
rms_norm(&hidden, &sl.ffn_norm, c.norm_eps, &mut norm_buf);
sparse_ternary_matvec(&sl.gate_proj, &norm_buf, &mut gate_buf);
sparse_ternary_matvec(&sl.up_proj, &norm_buf, &mut up_buf);
c.apply_ffn_sparsity(layer_idx, &mut gate_buf);
for i in 0..c.intermediate_dim {
gate_buf[i] = c.apply_ffn_act(layer_idx, gate_buf[i]) * up_buf[i];
}
sparse_ternary_matvec(&sl.down_proj, &gate_buf, &mut down_buf);
for i in 0..c.hidden_dim {
hidden[i] += down_buf[i];
}
}
self.kv_cache.advance();
rms_norm(&hidden, &self.output_norm, c.norm_eps, &mut norm_buf);
let mut logits = vec![0.0f32; c.vocab_size];
sparse_ternary_matvec(st_output, &norm_buf, &mut logits);
logits
}
pub fn generate_sparse_ternary_speculative(
&mut self,
tokenizer: &GgufTokenizer,
prompt: &str,
max_new_tokens: usize,
temperature: f32,
_top_k: usize,
spec_k: usize,
draft_layers: usize,
) -> GenerateResult {
let start = Instant::now();
let mut tokens = tokenizer.encode(prompt);
if tokenizer.add_bos_token && (tokens.is_empty() || tokens[0] != tokenizer.bos_id) {
tokens.insert(0, tokenizer.bos_id);
}
self.clear_cache();
let prompt_token_count = tokens.len();
let prefill_start = Instant::now();
let mut logits = vec![0.0f32; self.config.vocab_size];
for &tok in &tokens {
logits = self.forward_sparse_ternary(tok);
}
let prefill_ms = prefill_start.elapsed().as_millis() as u64;
let decode_start = Instant::now();
let mut generated = Vec::with_capacity(max_new_tokens);
let mut total_drafted: usize = 0;
let mut total_accepted: usize = 0;
let mut rng = Rng64::new(42);
while generated.len() < max_new_tokens {
let next_token = if temperature > 0.0 {
sample_from_probs(&softmax(&logits), rng.next_f32())
} else {
argmax(&logits)
};
if next_token == tokenizer.eos_id {
break;
}
generated.push(next_token);
tokens.push(next_token);
let remaining = max_new_tokens - generated.len();
if remaining == 0 {
logits = self.forward_sparse_ternary(next_token);
continue;
}
let k = spec_k.min(remaining);
let saved_pos = self.kv_cache.seq_len();
let mut draft_tokens = Vec::with_capacity(k);
let mut draft_logits_all = Vec::with_capacity(k);
let mut draft_input = next_token;
for _ in 0..k {
let dl = self.forward_sparse_ternary_draft(draft_input, draft_layers);
draft_input = argmax(&dl);
draft_tokens.push(draft_input);
draft_logits_all.push(dl);
}
total_drafted += draft_tokens.len();
self.kv_cache.rollback_to(saved_pos);
logits = self.forward_sparse_ternary(next_token);
let mut all_accepted = true;
for i in 0..draft_tokens.len() {
let p = softmax(&logits);
let q = softmax(&draft_logits_all[i]);
let x = draft_tokens[i] as usize;
let p_x = if x < p.len() { p[x] } else { 0.0 };
let q_x = if x < q.len() { q[x] } else { 1e-10 };
let accept_prob = (p_x / q_x.max(1e-10)).min(1.0);
let r = rng.next_f32();
if r < accept_prob {
generated.push(draft_tokens[i]);
tokens.push(draft_tokens[i]);
total_accepted += 1;
logits = self.forward_sparse_ternary(draft_tokens[i]);
} else {
let mut adjusted = vec![0.0f32; p.len()];
let mut adj_sum = 0.0f32;
for j in 0..p.len() {
adjusted[j] = (p[j] - q[j]).max(0.0);
adj_sum += adjusted[j];
}
let resampled = if adj_sum > 0.0 {
let inv = 1.0 / adj_sum;
for a in &mut adjusted {
*a *= inv;
}
sample_from_probs(&adjusted, rng.next_f32())
} else {
sample_from_probs(&p, rng.next_f32())
};
if resampled == tokenizer.eos_id {
all_accepted = false;
break;
}
generated.push(resampled);
tokens.push(resampled);
logits = self.forward_sparse_ternary(resampled);
all_accepted = false;
break;
}
}
if all_accepted && generated.len() < max_new_tokens {
let bonus = sample_from_probs(&softmax(&logits), rng.next_f32());
if bonus != tokenizer.eos_id {
generated.push(bonus);
tokens.push(bonus);
logits = self.forward_sparse_ternary(bonus);
}
}
}
let decode_ms = decode_start.elapsed().as_millis() as u64;
let total_ms = start.elapsed().as_millis() as u64;
let gen_count = generated.len();
let tok_per_sec = if decode_ms > 0 {
gen_count as f64 / (decode_ms as f64 / 1000.0)
} else {
0.0
};
GenerateResult {
text: tokenizer.decode(&generated),
tokens_generated: gen_count,
prompt_tokens: prompt_token_count,
prefill_ms,
decode_ms,
total_ms,
tokens_per_sec: tok_per_sec,
spec_stats: Some(SpecStats {
draft_tokens: total_drafted,
accepted_tokens: total_accepted,
draft_layers,
spec_k,
}),
}
}
fn forward_paged(&self, token_id: u32, paged_cache: &mut PagedKvCache) -> Vec<f32> {
let c = &self.config;
let pos = paged_cache.seq_len();
let rope_freqs_ref = self.rope_freqs.as_deref();
let emb_start = token_id as usize * c.hidden_dim;
let mut hidden: Vec<f32> = self.embedding[emb_start..emb_start + c.hidden_dim].to_vec();
if c.arch == ModelArch::Gemma2 {
let scale = (c.hidden_dim as f32).sqrt();
for h in &mut hidden {
*h *= scale;
}
}
let mut norm_buf = vec![0.0f32; c.hidden_dim];
let kv_dim = c.num_kv_heads * c.head_dim;
let q_dim = c.num_heads * c.head_dim;
let mut q_buf = vec![0.0f32; q_dim];
let mut k_buf = vec![0.0f32; kv_dim];
let mut v_buf = vec![0.0f32; kv_dim];
let mut attn_out = vec![0.0f32; q_dim.max(c.hidden_dim)];
let mut o_buf = vec![0.0f32; c.hidden_dim];
let mut gate_buf = vec![0.0f32; c.intermediate_dim];
let mut up_buf = vec![0.0f32; c.intermediate_dim];
let mut down_buf = vec![0.0f32; c.hidden_dim];
for layer_idx in 0..c.num_layers {
let layer = &self.layers[layer_idx];
rms_norm(&hidden, &layer.attn_norm, c.norm_eps, &mut norm_buf);
let q8_attn = quantize_row_q8_k(&norm_buf);
layer.q_proj.matvec_preq(&q8_attn, &mut q_buf);
layer
.k_proj
.as_ref()
.expect("k_proj required for non-shared layer")
.matvec_preq(&q8_attn, &mut k_buf);
layer
.v_proj
.as_ref()
.expect("v_proj required for non-shared layer")
.matvec_preq(&q8_attn, &mut v_buf);
if let Some(b) = layer.q_bias() {
for (q, bi) in q_buf.iter_mut().zip(b.iter()) {
*q += bi;
}
}
if let Some(b) = layer.k_bias() {
for (k, bi) in k_buf.iter_mut().zip(b.iter()) {
*k += bi;
}
}
if let Some(b) = layer.v_bias() {
for (v, bi) in v_buf.iter_mut().zip(b.iter()) {
*v += bi;
}
}
if let Some(w) = layer.q_norm() {
apply_qk_norm(&mut q_buf[..q_dim], w, c.head_dim, c.norm_eps);
}
if let Some(w) = layer.k_norm() {
apply_qk_norm(&mut k_buf, w, c.head_dim, c.norm_eps);
}
for h in 0..c.num_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut q_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
for h in 0..c.num_kv_heads {
let start = h * c.head_dim;
apply_rope_auto(
&mut k_buf[start..start + c.head_dim],
pos,
c.head_dim,
c.rope_theta,
rope_freqs_ref,
c.use_neox_rope(),
);
}
paged_cache.append(layer_idx, &k_buf, &v_buf);
gqa_attention_paged(
&q_buf,
paged_cache,
layer_idx,
pos,
c.num_heads,
c.num_kv_heads,
c.head_dim,
c.sliding_window_for_layer(layer_idx),
c.attn_logit_softcap(),
&mut attn_out,
);
layer.o_proj.matvec(&attn_out, &mut o_buf);
for i in 0..c.hidden_dim {
hidden[i] += o_buf[i];
}
rms_norm(&hidden, &layer.ffn_norm, c.norm_eps, &mut norm_buf);
let q8_ffn = quantize_row_q8_k(&norm_buf);
layer
.gate_proj
.as_ref()
.expect("gate_proj required for non-MoE layer")
.matvec_preq(&q8_ffn, &mut gate_buf);
layer
.up_proj
.as_ref()
.expect("up_proj required for non-MoE layer")
.matvec_preq(&q8_ffn, &mut up_buf);
c.apply_ffn_sparsity(layer_idx, &mut gate_buf);
for i in 0..c.intermediate_dim {
gate_buf[i] = c.apply_ffn_act(layer_idx, gate_buf[i]) * up_buf[i];
}
layer
.down_proj
.as_ref()
.expect("down_proj required for non-MoE layer")
.matvec(&gate_buf, &mut down_buf);
for i in 0..c.hidden_dim {
hidden[i] += down_buf[i];
}
}
paged_cache.advance();
rms_norm(&hidden, &self.output_norm, c.norm_eps, &mut norm_buf);
let mut logits = vec![0.0f32; c.vocab_size];
self.output_proj.matvec(&norm_buf, &mut logits);
if let Some(cap) = c.final_logit_softcap() {
for l in &mut logits {
*l = cap * (*l / cap).tanh();
}
}
logits
}
pub fn generate_batch(
&self,
tokenizer: &GgufTokenizer,
scheduler: &mut BatchScheduler,
top_k: usize,
) {
let mut pending_logits: Vec<Option<Vec<f32>>> = Vec::new();
for req in scheduler.requests_mut().iter_mut() {
if req.done {
pending_logits.push(None);
continue;
}
let prompt_tokens = req.tokens.clone();
let mut logits = vec![0.0f32; 0];
for &tok in &prompt_tokens {
logits = self.forward_paged(tok, &mut req.kv_cache);
}
pending_logits.push(Some(logits));
}
for (i, req) in scheduler.requests_mut().iter_mut().enumerate() {
if req.done {
continue;
}
if let Some(ref logits) = pending_logits[i] {
let next_token = sample_token(logits, req.temperature, top_k);
if next_token == tokenizer.eos_id {
req.done = true;
} else {
req.generated.push(next_token);
}
}
}
loop {
let any_active = scheduler.requests_mut().iter().any(|r| !r.done);
if !any_active {
break;
}
for req in scheduler.requests_mut().iter_mut().filter(|r| !r.done) {
if req.generated.len() >= req.max_new_tokens {
req.done = true;
continue;
}
let last_tok = *req.generated.last().unwrap();
let logits = self.forward_paged(last_tok, &mut req.kv_cache);
let next_token = sample_token(&logits, req.temperature, top_k);
if next_token == tokenizer.eos_id {
req.done = true;
} else {
req.generated.push(next_token);
}
}
}
}
#[cfg(feature = "grammar")]
pub fn generate_grammar(
&mut self,
tokenizer: &GgufTokenizer,
prompt: &str,
max_new_tokens: usize,
grammar: &crate::grammar::Grammar,
temperature: f32,
top_k: usize,
) -> Result<GenerateResult, GrammarGenError> {
let start = Instant::now();
let mut tokens = tokenizer.encode(prompt);
if tokenizer.add_bos_token && (tokens.is_empty() || tokens[0] != tokenizer.bos_id) {
tokens.insert(0, tokenizer.bos_id);
}
self.clear_cache();
let prompt_token_count = tokens.len();
let prefill_start = Instant::now();
let mut logits = vec![0.0f32; self.config.vocab_size];
for &tok in &tokens {
logits = self.forward(tok);
}
let prefill_ms = prefill_start.elapsed().as_millis() as u64;
let decode_start = Instant::now();
let generated = self.grammar_decode_loop(
tokenizer,
logits,
max_new_tokens,
grammar,
temperature,
top_k,
)?;
let decode_ms = decode_start.elapsed().as_millis() as u64;
let total_ms = start.elapsed().as_millis() as u64;
let gen_count = generated.len();
let tok_per_sec = if decode_ms > 0 {
gen_count as f64 / (decode_ms as f64 / 1000.0)
} else {
0.0
};
let output_text = tokenizer.decode(&generated);
Ok(GenerateResult {
text: output_text,
tokens_generated: gen_count,
prompt_tokens: prompt_token_count,
prefill_ms,
decode_ms,
total_ms,
tokens_per_sec: tok_per_sec,
spec_stats: None,
})
}
#[cfg(feature = "grammar")]
pub fn generate_grammar_prefixed(
&mut self,
tokenizer: &GgufTokenizer,
prompt: &str,
prefix: &GrammarPrefix<'_>,
max_new_tokens: usize,
grammar: &crate::grammar::Grammar,
temperature: f32,
top_k: usize,
) -> Result<GrammarGenResult, GrammarGenError> {
let start = Instant::now();
let mut tokens = tokenizer.encode(prompt);
if tokenizer.add_bos_token && (tokens.is_empty() || tokens[0] != tokenizer.bos_id) {
tokens.insert(0, tokenizer.bos_id);
}
self.clear_cache();
let prompt_token_count = tokens.len();
let prefill_start = Instant::now();
let mut logits = vec![0.0f32; self.config.vocab_size];
for &tok in &tokens {
logits = self.forward(tok);
}
let prefill_ms = prefill_start.elapsed().as_millis() as u64;
let prefix_start = Instant::now();
let marker_ids = tokenizer.encode(prefix.stop_marker);
let mut prefix_tokens: Vec<u32> = Vec::new();
let mut marker_hit = false;
for _ in 0..prefix.max_prefix_tokens {
let next = Self::pick_token(&logits, temperature, top_k);
if next == tokenizer.eos_id {
break;
}
prefix_tokens.push(next);
logits = self.forward(next);
let by_ids = !marker_ids.is_empty()
&& prefix_tokens.len() >= marker_ids.len()
&& prefix_tokens[prefix_tokens.len() - marker_ids.len()..] == marker_ids[..];
let by_text = !prefix.stop_marker.is_empty()
&& tokenizer
.decode(&prefix_tokens[prefix_tokens.len().saturating_sub(8)..])
.ends_with(prefix.stop_marker);
if by_ids || by_text {
marker_hit = true;
break;
}
}
if !marker_hit && !marker_ids.is_empty() {
for &tok in &marker_ids {
logits = self.forward(tok);
}
prefix_tokens.extend_from_slice(&marker_ids);
}
let prefix_ms = prefix_start.elapsed().as_millis() as u64;
let decode_start = Instant::now();
let generated = self.grammar_decode_loop(
tokenizer,
logits,
max_new_tokens,
grammar,
temperature,
top_k,
)?;
let decode_ms = decode_start.elapsed().as_millis() as u64;
let total_ms = start.elapsed().as_millis() as u64;
let gen_count = generated.len();
let tok_per_sec = if decode_ms > 0 {
gen_count as f64 / (decode_ms as f64 / 1000.0)
} else {
0.0
};
Ok(GrammarGenResult {
prefix_text: tokenizer.decode(&prefix_tokens),
prefix_tokens: prefix_tokens.len(),
prefix_marker_hit: marker_hit,
prefix_ms,
text: tokenizer.decode(&generated),
tokens_generated: gen_count,
prompt_tokens: prompt_token_count,
prefill_ms,
decode_ms,
total_ms,
tokens_per_sec: tok_per_sec,
})
}
#[cfg(feature = "grammar")]
fn pick_token(logits: &[f32], temperature: f32, top_k: usize) -> u32 {
let scale = if temperature > 0.0 && temperature != 1.0 {
1.0 / temperature
} else {
1.0
};
if top_k > 0 && top_k < logits.len() {
let mut indexed: Vec<(usize, f32)> = logits
.iter()
.copied()
.enumerate()
.filter(|(_, l)| l.is_finite())
.map(|(i, l)| (i, l * scale))
.collect();
indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
indexed.truncate(top_k);
indexed.first().map_or(0u32, |(idx, _)| *idx as u32)
} else {
logits
.iter()
.enumerate()
.filter(|(_, l)| l.is_finite())
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.map_or(0u32, |(idx, _)| idx as u32)
}
}
#[cfg(feature = "grammar")]
fn grammar_decode_loop(
&mut self,
tokenizer: &GgufTokenizer,
mut logits: Vec<f32>,
max_new_tokens: usize,
grammar: &crate::grammar::Grammar,
temperature: f32,
top_k: usize,
) -> Result<Vec<u32>, GrammarGenError> {
use crate::grammar::{Fsm, TokenTrie};
use crate::sampling::{advance_fsm_on_emit, mask_logits_by_grammar_trie};
let mut fsm = Fsm::start(grammar)?;
let trie = TokenTrie::build(tokenizer, self.config.vocab_size);
let mut allowed_scratch: Vec<u32> = Vec::new();
let mut generated = Vec::with_capacity(max_new_tokens);
for step in 0..max_new_tokens {
mask_logits_by_grammar_trie(&fsm, &trie, tokenizer, &mut logits, &mut allowed_scratch);
if !logits.iter().any(|l| l.is_finite()) {
return Err(GrammarGenError::NoValidToken { step });
}
let next_token = Self::pick_token(&logits, temperature, top_k);
if next_token == tokenizer.eos_id {
break;
}
advance_fsm_on_emit(&mut fsm, tokenizer, next_token)?;
generated.push(next_token);
logits = self.forward(next_token);
}
Ok(generated)
}
}
#[cfg(feature = "dspark")]
impl crate::speculative_dspark::DraftBackend for Llama3Model<'_> {
fn forward(&mut self, token_id: crate::speculative_dspark::TokenId) -> Vec<f32> {
Self::forward(self, token_id)
}
fn forward_capture_hidden(
&mut self,
token_id: crate::speculative_dspark::TokenId,
layer_idx: Option<usize>,
) -> (Vec<f32>, Vec<f32>) {
Self::forward_capture_hidden(self, token_id, layer_idx)
}
fn seq_len(&self) -> usize {
self.kv_seq_len()
}
fn rollback_to(&mut self, pos: usize) {
self.kv_rollback_to(pos);
}
fn clear_cache(&mut self) {
Self::clear_cache(self);
}
fn vocab_size(&self) -> u32 {
self.config.vocab_size as u32
}
fn hidden_dim(&self) -> u32 {
self.config.hidden_dim as u32
}
fn num_layers(&self) -> u32 {
self.config.num_layers as u32
}
}
#[cfg(feature = "dspark")]
impl crate::speculative_dspark::DraftBackend for KimiK3Model<'_> {
fn forward(&mut self, token_id: crate::speculative_dspark::TokenId) -> Vec<f32> {
Self::forward_with_snapshot(self, token_id)
}
fn forward_capture_hidden(
&mut self,
token_id: crate::speculative_dspark::TokenId,
layer_idx: Option<usize>,
) -> (Vec<f32>, Vec<f32>) {
Self::forward_capture_hidden_with_snapshot(self, token_id, layer_idx)
}
fn seq_len(&self) -> usize {
Self::seq_len(self)
}
fn rollback_to(&mut self, pos: usize) {
Self::rollback_to(self, pos);
}
fn clear_cache(&mut self) {
Self::reset(self);
}
fn vocab_size(&self) -> u32 {
self.config.vocab_size as u32
}
fn hidden_dim(&self) -> u32 {
self.config.hidden_dim as u32
}
fn num_layers(&self) -> u32 {
self.config.num_layers as u32
}
}
fn sample_token(logits: &[f32], temperature: f32, top_k: usize) -> u32 {
let mut logits = logits.to_vec();
if temperature > 0.0 && temperature != 1.0 {
let inv_t = 1.0 / temperature;
for l in &mut logits {
*l *= inv_t;
}
}
if top_k > 0 && top_k < logits.len() {
let mut indexed: Vec<(usize, f32)> = logits.iter().copied().enumerate().collect();
indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
indexed.truncate(top_k);
indexed
.iter()
.max_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
.map_or(0, |(idx, _)| *idx as u32)
} else {
argmax(&logits)
}
}
fn argmax(logits: &[f32]) -> u32 {
logits
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
.map_or(0, |(idx, _)| idx as u32)
}
fn softmax(logits: &[f32]) -> Vec<f32> {
let max_val = logits.iter().copied().fold(f32::NEG_INFINITY, f32::max);
let mut probs: Vec<f32> = logits.iter().map(|&l| (l - max_val).exp()).collect();
let sum: f32 = probs.iter().sum();
if sum > 0.0 {
let inv = 1.0 / sum;
for p in &mut probs {
*p *= inv;
}
}
probs
}
fn sample_from_probs(probs: &[f32], rand_val: f32) -> u32 {
let mut cumsum = 0.0f32;
for (i, &p) in probs.iter().enumerate() {
cumsum += p;
if rand_val < cumsum {
return i as u32;
}
}
(probs.len() - 1) as u32
}
struct Rng64 {
state: u64,
}
impl Rng64 {
const fn new(seed: u64) -> Self {
Self {
state: if seed == 0 {
0xDEAD_BEEF_CAFE_BABEu64
} else {
seed
},
}
}
fn next_f32(&mut self) -> f32 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
(x >> 40) as f32 / (1u64 << 24) as f32
}
}
#[derive(Debug)]
pub struct GenerateResult {
pub text: String,
pub tokens_generated: usize,
pub prompt_tokens: usize,
pub prefill_ms: u64,
pub decode_ms: u64,
pub total_ms: u64,
pub tokens_per_sec: f64,
pub spec_stats: Option<SpecStats>,
}
#[derive(Debug, Clone)]
pub struct SpecStats {
pub draft_tokens: usize,
pub accepted_tokens: usize,
pub draft_layers: usize,
pub spec_k: usize,
}
#[cfg(feature = "grammar")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GrammarGenError {
Fsm(crate::grammar::FsmError),
NoValidToken { step: usize },
}
#[cfg(feature = "grammar")]
#[derive(Debug, Clone, Copy)]
pub struct GrammarPrefix<'a> {
pub stop_marker: &'a str,
pub max_prefix_tokens: usize,
}
#[cfg(feature = "grammar")]
#[derive(Debug, Clone)]
pub struct GrammarGenResult {
pub prefix_text: String,
pub prefix_tokens: usize,
pub prefix_marker_hit: bool,
pub prefix_ms: u64,
pub text: String,
pub tokens_generated: usize,
pub prompt_tokens: usize,
pub prefill_ms: u64,
pub decode_ms: u64,
pub total_ms: u64,
pub tokens_per_sec: f64,
}
#[cfg(feature = "grammar")]
impl std::fmt::Display for GrammarGenError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Fsm(e) => write!(f, "grammar FSM error: {e}"),
Self::NoValidToken { step } => {
write!(f, "no valid token accepted by grammar at step {step}")
}
}
}
}
#[cfg(feature = "grammar")]
impl std::error::Error for GrammarGenError {}
#[cfg(feature = "grammar")]
impl From<crate::grammar::FsmError> for GrammarGenError {
fn from(e: crate::grammar::FsmError) -> Self {
Self::Fsm(e)
}
}
fn ternarize_weight(
w: &WeightRef<'_>,
rows: usize,
cols: usize,
threshold_ratio: f32,
) -> TernaryMatrix {
TernaryMatrix::from_quantized(w.data, w.qtype, rows, cols, threshold_ratio)
}
fn sparsify_weight(
w: &WeightRef<'_>,
rows: usize,
cols: usize,
threshold_ratio: f32,
n_keep: usize,
) -> SparseTernaryMatrix {
let weights_f32 = w.dequantize_all(rows, cols);
SparseTernaryMatrix::from_f32_weights(&weights_f32, rows, cols, threshold_ratio, n_keep)
}
fn load_weight_ref<'a>(
gguf: &'a GgufFile<'a>,
name: &str,
rows: usize,
cols: usize,
) -> Option<WeightRef<'a>> {
let info = gguf.tensor_info(name)?;
let data = gguf.tensor_data(name)?;
Some(WeightRef {
data,
qtype: info.qtype,
rows,
cols,
})
}
fn load_weight_ref_any_shape<'a, G: crate::gguf::GgufSource<'a>>(
gguf: &'a G,
name: &str,
) -> Option<WeightRef<'a>> {
let info = gguf.tensor_info(name)?;
let data = gguf.tensor_data(name)?;
let cols = *info.dims.first()? as usize;
let rows: usize = info
.dims
.iter()
.skip(1)
.map(|&d| d as usize)
.product::<usize>()
.max(1);
Some(WeightRef {
data,
qtype: info.qtype,
rows,
cols,
})
}
fn load_weight_ref_any_rows<'a>(
gguf: &'a GgufFile<'a>,
name: &str,
cols: usize,
) -> Option<WeightRef<'a>> {
let info = gguf.tensor_info(name)?;
let data = gguf.tensor_data(name)?;
let rows = *info.dims.get(1)?;
Some(WeightRef {
data,
qtype: info.qtype,
rows: rows as usize,
cols,
})
}
fn deepseek_moe_route(
router_logits: &[f32],
exp_probs_b: Option<&[f32]>,
top_k: usize,
routed_scale: f32,
) -> Vec<(usize, f32)> {
let n_experts = router_logits.len();
let scores: Vec<f32> = router_logits
.iter()
.map(|&x| {
if x >= 0.0 {
1.0 / (1.0 + (-x).exp())
} else {
let e = x.exp();
e / (1.0 + e)
}
})
.collect();
let mut idx_biased: Vec<(usize, f32)> = if let Some(bias) = exp_probs_b {
assert_eq!(bias.len(), n_experts, "exp_probs_b shape mismatch");
scores
.iter()
.zip(bias.iter())
.enumerate()
.map(|(i, (&s, &b))| (i, s + b))
.collect()
} else {
scores.iter().enumerate().map(|(i, &s)| (i, s)).collect()
};
idx_biased.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
idx_biased.truncate(top_k);
let mut selected: Vec<(usize, f32)> =
idx_biased.iter().map(|(i, _)| (*i, scores[*i])).collect();
let sum: f32 = selected.iter().map(|(_, w)| *w).sum();
if sum > 0.0 {
let inv = 1.0 / sum;
for (_, w) in &mut selected {
*w *= inv;
}
}
for (_, w) in &mut selected {
*w *= routed_scale;
}
selected
}
fn forward_deepseek_moe_layer(
c: &Llama3Config,
moe: &DeepSeekMoeWeights<'_>,
hidden: &[f32],
norm_buf: &mut [f32],
output: &mut [f32],
) {
let hidden_dim = c.hidden_dim;
let n_experts = c
.deepseek_n_routed_experts()
.expect("DeepSeek MoE requires n_routed_experts");
let n_shared = c
.deepseek_n_shared_experts()
.expect("DeepSeek MoE requires n_shared_experts");
let top_k = c
.deepseek_num_experts_per_tok()
.expect("DeepSeek MoE requires num_experts_per_tok");
let moe_ffn = c
.deepseek_moe_intermediate_size()
.expect("DeepSeek MoE requires moe_intermediate_size");
let shared_ffn = n_shared * moe_ffn;
let routed_scale = c.deepseek_routed_scaling_factor().unwrap_or(1.0);
rms_norm(hidden, &moe.ffn_norm, c.norm_eps, norm_buf);
let router_w = &moe.ffn_gate_inp;
assert_eq!(
router_w.len(),
n_experts * hidden_dim,
"ffn_gate_inp shape mismatch"
);
let mut router_logits = vec![0.0f32; n_experts];
for (e, logit) in router_logits.iter_mut().enumerate() {
let base = e * hidden_dim;
let mut acc = 0.0f32;
for (h, &x) in norm_buf.iter().enumerate() {
acc += router_w[base + h] * x;
}
*logit = acc;
}
let selected = deepseek_moe_route(
&router_logits,
moe.exp_probs_b.as_deref(),
top_k,
routed_scale,
);
use crate::deepseek_streaming::{ExpertKey, ExpertKind};
let (gate_qtype, up_qtype, down_qtype) = match &moe.routed {
RoutedExpertStorage::InMemory { gate, up, down } => (gate.qtype, up.qtype, down.qtype),
RoutedExpertStorage::Streaming { pool, layer_idx } => {
let mut prefetch_keys = Vec::with_capacity(selected.len() * 3);
for &(e, _) in &selected {
prefetch_keys.push(ExpertKey::new(*layer_idx, ExpertKind::Gate, e));
prefetch_keys.push(ExpertKey::new(*layer_idx, ExpertKind::Up, e));
prefetch_keys.push(ExpertKey::new(*layer_idx, ExpertKind::Down, e));
}
pool.prefetch_parallel(&prefetch_keys);
(
pool.qtype(*layer_idx, ExpertKind::Gate),
pool.qtype(*layer_idx, ExpertKind::Up),
pool.qtype(*layer_idx, ExpertKind::Down),
)
}
};
for v in output.iter_mut() {
*v = 0.0;
}
let mut gate_buf = vec![0.0f32; moe_ffn];
let mut up_buf = vec![0.0f32; moe_ffn];
let mut expert_out = vec![0.0f32; hidden_dim];
for &(e, weight) in &selected {
let (g_arc, u_arc, d_arc);
let (g_data, u_data, d_data): (&[u8], &[u8], &[u8]) = match &moe.routed {
RoutedExpertStorage::InMemory { gate, up, down } => {
let gate_slab = expert_slab_bytes(gate, moe_ffn, hidden_dim);
let up_slab = expert_slab_bytes(up, moe_ffn, hidden_dim);
let down_slab = expert_slab_bytes(down, hidden_dim, moe_ffn);
(
&gate.data[e * gate_slab..(e + 1) * gate_slab],
&up.data[e * up_slab..(e + 1) * up_slab],
&down.data[e * down_slab..(e + 1) * down_slab],
)
}
RoutedExpertStorage::Streaming { pool, layer_idx } => {
g_arc = pool.get_or_load(*layer_idx, ExpertKind::Gate, e);
u_arc = pool.get_or_load(*layer_idx, ExpertKind::Up, e);
d_arc = pool.get_or_load(*layer_idx, ExpertKind::Down, e);
(g_arc.as_slice(), u_arc.as_slice(), d_arc.as_slice())
}
};
crate::gguf::quantized_matvec(
norm_buf,
g_data,
gate_qtype,
moe_ffn,
hidden_dim,
&mut gate_buf,
);
crate::gguf::quantized_matvec(norm_buf, u_data, up_qtype, moe_ffn, hidden_dim, &mut up_buf);
for i in 0..moe_ffn {
gate_buf[i] = silu(gate_buf[i]) * up_buf[i];
}
crate::gguf::quantized_matvec(
&gate_buf,
d_data,
down_qtype,
hidden_dim,
moe_ffn,
&mut expert_out,
);
for i in 0..hidden_dim {
output[i] += weight * expert_out[i];
}
}
let mut shared_gate = vec![0.0f32; shared_ffn];
let mut shared_up = vec![0.0f32; shared_ffn];
let mut shared_out = vec![0.0f32; hidden_dim];
moe.ffn_gate_shexp.matvec(norm_buf, &mut shared_gate);
moe.ffn_up_shexp.matvec(norm_buf, &mut shared_up);
for i in 0..shared_ffn {
shared_gate[i] = silu(shared_gate[i]) * shared_up[i];
}
moe.ffn_down_shexp.matvec(&shared_gate, &mut shared_out);
for i in 0..hidden_dim {
output[i] += shared_out[i];
}
}
fn forward_moe_layer(
c: &Llama3Config,
layer: &LayerWeights<'_>,
norm_buf: &[f32],
output: &mut [f32],
) {
let n_expert = c
.num_experts()
.expect("MoE layer requires num_experts in config");
let n_active = c
.num_experts_active()
.expect("MoE layer requires num_experts_active in config");
let expert_ffn = c.expert_ffn_size().unwrap_or(c.intermediate_dim);
let hidden_dim = c.hidden_dim;
let router_w = layer
.ffn_gate_inp()
.expect("MoE layer requires ffn_gate_inp");
assert_eq!(
router_w.len(),
n_expert * hidden_dim,
"ffn_gate_inp shape mismatch"
);
let mut router_logits = vec![0.0f32; n_expert];
for e in 0..n_expert {
let mut acc = 0.0f32;
let base = e * hidden_dim;
for h in 0..hidden_dim {
acc += router_w[base + h] * norm_buf[h];
}
router_logits[e] = acc;
}
let max_logit = router_logits
.iter()
.copied()
.fold(f32::NEG_INFINITY, f32::max);
let mut probs: Vec<f32> = router_logits
.iter()
.map(|&v| (v - max_logit).exp())
.collect();
let sum_exp: f32 = probs.iter().sum();
for p in &mut probs {
*p /= sum_exp;
}
let mut idx_prob: Vec<(usize, f32)> = probs.iter().enumerate().map(|(i, &v)| (i, v)).collect();
idx_prob.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
idx_prob.truncate(n_active);
let top_k_sum: f32 = idx_prob.iter().map(|(_, p)| *p).sum();
if top_k_sum > 0.0 {
for (_, p) in &mut idx_prob {
*p /= top_k_sum;
}
}
let gate_exps = layer
.ffn_gate_exps()
.expect("MoE layer requires ffn_gate_exps");
let up_exps = layer.ffn_up_exps().expect("MoE layer requires ffn_up_exps");
let down_exps = layer
.ffn_down_exps()
.expect("MoE layer requires ffn_down_exps");
let gate_expert_bytes = expert_slab_bytes(gate_exps, expert_ffn, hidden_dim);
let up_expert_bytes = expert_slab_bytes(up_exps, expert_ffn, hidden_dim);
let down_expert_bytes = expert_slab_bytes(down_exps, hidden_dim, expert_ffn);
for v in output.iter_mut() {
*v = 0.0;
}
let mut gate_buf = vec![0.0f32; expert_ffn];
let mut up_buf = vec![0.0f32; expert_ffn];
let mut expert_out = vec![0.0f32; hidden_dim];
for &(e, weight) in &idx_prob {
let gate_slab = &gate_exps.data[e * gate_expert_bytes..(e + 1) * gate_expert_bytes];
let up_slab = &up_exps.data[e * up_expert_bytes..(e + 1) * up_expert_bytes];
let down_slab = &down_exps.data[e * down_expert_bytes..(e + 1) * down_expert_bytes];
crate::gguf::quantized_matvec(
norm_buf,
gate_slab,
gate_exps.qtype,
expert_ffn,
hidden_dim,
&mut gate_buf,
);
crate::gguf::quantized_matvec(
norm_buf,
up_slab,
up_exps.qtype,
expert_ffn,
hidden_dim,
&mut up_buf,
);
for i in 0..expert_ffn {
gate_buf[i] = silu(gate_buf[i]) * up_buf[i];
}
crate::gguf::quantized_matvec(
&gate_buf,
down_slab,
down_exps.qtype,
hidden_dim,
expert_ffn,
&mut expert_out,
);
for i in 0..hidden_dim {
output[i] += weight * expert_out[i];
}
}
}
fn expert_slab_bytes(w: &WeightRef<'_>, rows_per_expert: usize, cols_per_expert: usize) -> usize {
let elems = rows_per_expert * cols_per_expert;
let epb = w.qtype.elements_per_block();
let bpb = w.qtype.block_bytes();
assert!(epb > 0 && bpb > 0, "unsupported qtype for expert slab");
(elems / epb) * bpb
}
#[derive(Debug, Clone)]
pub struct SpeculativeResult {
pub tokens: Vec<u32>,
pub draft_tokens_produced: usize,
pub draft_tokens_accepted: usize,
pub bonus_tokens: usize,
}
impl SpeculativeResult {
#[must_use]
pub fn acceptance_rate(&self) -> f32 {
if self.draft_tokens_produced == 0 {
0.0
} else {
self.draft_tokens_accepted as f32 / self.draft_tokens_produced as f32
}
}
}
pub fn speculative_decode(
draft: &mut Llama3Model<'_>,
main: &mut Llama3Model<'_>,
prompt: &[u32],
n_draft: usize,
max_new_tokens: usize,
eos_id: Option<u32>,
) -> SpeculativeResult {
assert!(n_draft >= 1, "n_draft must be at least 1");
assert!(!prompt.is_empty(), "prompt must be non-empty");
for &tok in prompt.iter().take(prompt.len() - 1) {
let _ = draft.forward(tok);
let _ = main.forward(tok);
}
let mut last_tok = *prompt.last().unwrap();
let mut result = SpeculativeResult {
tokens: Vec::with_capacity(max_new_tokens),
draft_tokens_produced: 0,
draft_tokens_accepted: 0,
bonus_tokens: 0,
};
while result.tokens.len() < max_new_tokens {
let mut draft_tokens = Vec::with_capacity(n_draft);
let mut curr = last_tok;
for _ in 0..n_draft {
let logits = draft.forward(curr);
let argmax = greedy_argmax(&logits);
draft_tokens.push(argmax);
curr = argmax;
}
result.draft_tokens_produced += n_draft;
let mut accepted = 0usize;
let mut mismatch_replacement: Option<u32> = None;
curr = last_tok;
for &draft_tok in &draft_tokens {
let logits = main.forward(curr);
let main_argmax = greedy_argmax(&logits);
if main_argmax == draft_tok {
result.tokens.push(draft_tok);
accepted += 1;
curr = draft_tok;
if result.tokens.len() >= max_new_tokens {
break;
}
if eos_id == Some(draft_tok) {
break;
}
} else {
result.tokens.push(main_argmax);
result.bonus_tokens += 1;
mismatch_replacement = Some(main_argmax);
break;
}
}
result.draft_tokens_accepted += accepted;
if let Some(replacement) = mismatch_replacement {
let target = main.kv_seq_len();
draft.kv_rollback_to(target);
last_tok = replacement;
} else if accepted == n_draft && result.tokens.len() < max_new_tokens {
let _ = draft.forward(curr);
let logits = main.forward(curr);
let bonus = greedy_argmax(&logits);
result.tokens.push(bonus);
result.bonus_tokens += 1;
last_tok = bonus;
} else {
let target = main.kv_seq_len();
draft.kv_rollback_to(target);
last_tok = *result.tokens.last().unwrap_or(&last_tok);
}
if eos_id.is_some_and(|e| Some(&e) == result.tokens.last()) {
break;
}
}
result.tokens.truncate(max_new_tokens);
result
}
fn greedy_argmax(logits: &[f32]) -> u32 {
let mut best_idx = 0usize;
let mut best_val = f32::NEG_INFINITY;
for (i, &v) in logits.iter().enumerate() {
if v > best_val {
best_val = v;
best_idx = i;
}
}
best_idx as u32
}
#[derive(Debug, Clone)]
pub struct SpeculativeConfig {
pub n_draft: usize,
pub max_new_tokens: usize,
pub eos_id: Option<u32>,
pub temperature: Option<f32>,
pub top_p: Option<f32>,
pub sample_seed: Option<u64>,
}
impl Default for SpeculativeConfig {
fn default() -> Self {
Self {
n_draft: 4,
max_new_tokens: 128,
eos_id: None,
temperature: None,
top_p: None,
sample_seed: None,
}
}
}
#[derive(Debug, Clone)]
pub struct MtpDraftPolicy {
window: std::collections::VecDeque<bool>,
window_size: usize,
disable_threshold: f32,
cooldown_tokens: usize,
cooldown_remaining: usize,
total_accepted: u64,
total_rejected: u64,
total_cooldowns: u64,
}
impl MtpDraftPolicy {
#[must_use]
pub fn new_default() -> Self {
Self::with_params(32, 0.30, 16)
}
#[must_use]
pub fn with_params(window_size: usize, disable_threshold: f32, cooldown_tokens: usize) -> Self {
assert!(window_size > 0, "MTP policy window_size must be > 0");
assert!(
(0.0..=1.0).contains(&disable_threshold),
"MTP policy disable_threshold must be in [0.0, 1.0]"
);
Self {
window: std::collections::VecDeque::with_capacity(window_size),
window_size,
disable_threshold,
cooldown_tokens,
cooldown_remaining: 0,
total_accepted: 0,
total_rejected: 0,
total_cooldowns: 0,
}
}
pub fn should_draft(&mut self) -> bool {
if self.cooldown_remaining > 0 {
self.cooldown_remaining -= 1;
false
} else {
true
}
}
pub fn record(&mut self, accepted: bool) {
if accepted {
self.total_accepted += 1;
} else {
self.total_rejected += 1;
}
if self.window.len() == self.window_size {
self.window.pop_front();
}
self.window.push_back(accepted);
if self.window.len() >= self.window_size {
let accepts: usize = self.window.iter().filter(|&&v| v).count();
let rate = accepts as f32 / self.window.len() as f32;
if rate < self.disable_threshold {
self.cooldown_remaining = self.cooldown_tokens;
self.total_cooldowns += 1;
self.window.clear();
}
}
}
#[must_use]
pub fn stats(&self) -> MtpDraftStats {
let total_decisions = self.total_accepted + self.total_rejected;
let accept_rate = if total_decisions > 0 {
self.total_accepted as f32 / total_decisions as f32
} else {
0.0
};
MtpDraftStats {
in_cooldown: self.cooldown_remaining > 0,
cooldown_remaining: self.cooldown_remaining,
window_len: self.window.len(),
total_accepted: self.total_accepted,
total_rejected: self.total_rejected,
total_cooldowns: self.total_cooldowns,
overall_accept_rate: accept_rate,
}
}
}
impl Default for MtpDraftPolicy {
fn default() -> Self {
Self::new_default()
}
}
#[derive(Debug, Clone, Copy)]
pub struct MtpDraftStats {
pub in_cooldown: bool,
pub cooldown_remaining: usize,
pub window_len: usize,
pub total_accepted: u64,
pub total_rejected: u64,
pub total_cooldowns: u64,
pub overall_accept_rate: f32,
}
#[derive(Debug, Clone, Copy)]
struct SplitMix64(u64);
impl SplitMix64 {
fn new(seed: u64) -> Self {
Self(seed)
}
fn next_u64(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
fn next_unit_f32(&mut self) -> f32 {
#[allow(clippy::cast_precision_loss)]
{
(self.next_u64() >> 40) as f32 / f32::from(1u16 << 8) / f32::from(1u16 << 8) / 256.0
}
}
}
fn apply_temperature_and_top_p(logits: &[f32], temperature: f32, top_p: Option<f32>) -> Vec<f32> {
assert!(
temperature > 0.0 && temperature.is_finite(),
"temperature must be positive and finite"
);
let inv_t = 1.0 / temperature;
let max_logit = logits
.iter()
.copied()
.fold(f32::NEG_INFINITY, |a, b| if b > a { b } else { a });
let mut probs: Vec<f32> = logits
.iter()
.map(|&l| ((l - max_logit) * inv_t).exp())
.collect();
let sum: f32 = probs.iter().sum();
assert!(
sum > 0.0 && sum.is_finite(),
"softmax normaliser is degenerate"
);
for p in &mut probs {
*p /= sum;
}
if let Some(p_thresh) = top_p {
if p_thresh > 0.0 && p_thresh < 1.0 - 1e-6 {
let mut indexed: Vec<(usize, f32)> = probs.iter().copied().enumerate().collect();
indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
let mut cum = 0.0f32;
let mut keep_mask = vec![false; probs.len()];
for &(orig_idx, prob) in &indexed {
keep_mask[orig_idx] = true;
cum += prob;
if cum >= p_thresh {
break;
}
}
for (i, p) in probs.iter_mut().enumerate() {
if !keep_mask[i] {
*p = 0.0;
}
}
let sum: f32 = probs.iter().sum();
if sum > 0.0 {
for p in &mut probs {
*p /= sum;
}
}
}
}
probs
}
fn sample_multinomial(dist: &[f32], rng: &mut SplitMix64) -> u32 {
let u = rng.next_unit_f32();
let mut cum = 0.0f32;
for (i, &p) in dist.iter().enumerate() {
cum += p;
if u < cum {
return u32::try_from(i).expect("index fits in u32");
}
}
for (i, &p) in dist.iter().enumerate().rev() {
if p > 0.0 {
return u32::try_from(i).expect("index fits in u32");
}
}
0
}
fn residual_dist(p_main: &[f32], p_draft: &[f32]) -> Vec<f32> {
assert_eq!(
p_main.len(),
p_draft.len(),
"distributions must match length"
);
let mut r: Vec<f32> = p_main
.iter()
.zip(p_draft.iter())
.map(|(&m, &d)| (m - d).max(0.0))
.collect();
let sum: f32 = r.iter().sum();
if sum > 0.0 {
for x in &mut r {
*x /= sum;
}
} else {
r.clone_from_slice(p_main);
}
r
}
pub fn speculative_decode_v2(
draft: &mut Llama3Model<'_>,
main: &mut Llama3Model<'_>,
prompt: &[u32],
cfg: &SpeculativeConfig,
) -> SpeculativeResult {
assert!(cfg.n_draft >= 1, "n_draft must be at least 1");
assert!(!prompt.is_empty(), "prompt must be non-empty");
if cfg.temperature.is_none() {
return speculative_decode(
draft,
main,
prompt,
cfg.n_draft,
cfg.max_new_tokens,
cfg.eos_id,
);
}
let temperature = cfg.temperature.expect("checked above");
let top_p = cfg.top_p;
let mut rng = SplitMix64::new(cfg.sample_seed.unwrap_or(0));
for &tok in prompt.iter().take(prompt.len() - 1) {
let _ = draft.forward(tok);
let _ = main.forward(tok);
}
let mut last_tok = *prompt.last().expect("non-empty prompt");
let mut result = SpeculativeResult {
tokens: Vec::with_capacity(cfg.max_new_tokens),
draft_tokens_produced: 0,
draft_tokens_accepted: 0,
bonus_tokens: 0,
};
while result.tokens.len() < cfg.max_new_tokens {
let mut draft_tokens = Vec::with_capacity(cfg.n_draft);
let mut draft_probs_at_pick: Vec<f32> = Vec::with_capacity(cfg.n_draft);
let mut draft_dists: Vec<Vec<f32>> = Vec::with_capacity(cfg.n_draft);
let mut curr = last_tok;
for _ in 0..cfg.n_draft {
let logits = draft.forward(curr);
let dist = apply_temperature_and_top_p(&logits, temperature, top_p);
let sample = sample_multinomial(&dist, &mut rng);
let p_at = dist[sample as usize];
draft_probs_at_pick.push(p_at);
draft_dists.push(dist);
draft_tokens.push(sample);
curr = sample;
}
result.draft_tokens_produced += cfg.n_draft;
let mut accepted = 0usize;
let mut rejected_replacement: Option<u32> = None;
curr = last_tok;
for (idx, &draft_tok) in draft_tokens.iter().enumerate() {
let logits = main.forward(curr);
let p_main = apply_temperature_and_top_p(&logits, temperature, top_p);
let p_draft = &draft_dists[idx];
let p_m = p_main[draft_tok as usize];
let p_d = draft_probs_at_pick[idx];
let ratio = if p_d > 0.0 { (p_m / p_d).min(1.0) } else { 0.0 };
let u = rng.next_unit_f32();
if u < ratio {
result.tokens.push(draft_tok);
accepted += 1;
curr = draft_tok;
if result.tokens.len() >= cfg.max_new_tokens {
break;
}
if cfg.eos_id == Some(draft_tok) {
break;
}
} else {
let residual = residual_dist(&p_main, p_draft);
let replacement = sample_multinomial(&residual, &mut rng);
result.tokens.push(replacement);
result.bonus_tokens += 1;
rejected_replacement = Some(replacement);
break;
}
}
result.draft_tokens_accepted += accepted;
if let Some(replacement) = rejected_replacement {
let target = main.kv_seq_len();
draft.kv_rollback_to(target);
last_tok = replacement;
} else if accepted == cfg.n_draft && result.tokens.len() < cfg.max_new_tokens {
let _ = draft.forward(curr);
let logits = main.forward(curr);
let p_main = apply_temperature_and_top_p(&logits, temperature, top_p);
let bonus = sample_multinomial(&p_main, &mut rng);
result.tokens.push(bonus);
result.bonus_tokens += 1;
last_tok = bonus;
} else {
let target = main.kv_seq_len();
draft.kv_rollback_to(target);
last_tok = *result.tokens.last().unwrap_or(&last_tok);
}
if cfg.eos_id.is_some_and(|e| Some(&e) == result.tokens.last()) {
break;
}
}
result.tokens.truncate(cfg.max_new_tokens);
result
}
fn load_ffn_norm<'a>(gguf: &'a GgufFile<'a>, prefix: &str) -> Option<Vec<f32>> {
gguf.tensor_to_f32(&format!("{prefix}.ffn_norm.weight"))
.or_else(|| gguf.tensor_to_f32(&format!("{prefix}.post_attention_norm.weight")))
}
fn load_layer_weights<'a>(
gguf: &'a GgufFile<'a>,
layer: usize,
config: &Llama3Config,
) -> Option<LayerWeights<'a>> {
let prefix = format!("blk.{layer}");
let q_dim = config.q_dim_for_layer(layer);
let kv_dim = config.kv_dim_for_layer(layer);
let ffn_size = config.ffn_size_for_layer(layer);
let attn_norm = gguf.tensor_to_f32(&format!("{prefix}.attn_norm.weight"))?;
let q_proj =
load_weight_ref_any_rows(gguf, &format!("{prefix}.attn_q.weight"), config.hidden_dim)?;
let gated_output = match q_proj.rows {
r if r == q_dim => false,
r if r == 2 * q_dim => true,
_ => return None, };
let is_shared_layer =
matches!(config.arch, ModelArch::Gemma4) && layer >= config.kv_from_start_layers();
let k_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_k.weight"),
kv_dim,
config.hidden_dim,
);
if k_proj.is_none() && !is_shared_layer {
return None;
}
let v_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_v.weight"),
kv_dim,
config.hidden_dim,
);
if v_proj.is_none() && !is_shared_layer {
return None;
}
let o_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_output.weight"),
config.hidden_dim,
q_dim,
)?;
let ffn_norm = load_ffn_norm(gguf, &prefix)?;
let gate_proj = load_weight_ref(
gguf,
&format!("{prefix}.ffn_gate.weight"),
ffn_size,
config.hidden_dim,
);
let up_proj = load_weight_ref(
gguf,
&format!("{prefix}.ffn_up.weight"),
ffn_size,
config.hidden_dim,
);
let down_proj = load_weight_ref(
gguf,
&format!("{prefix}.ffn_down.weight"),
config.hidden_dim,
ffn_size,
);
let q_bias = gguf.tensor_to_f32(&format!("{prefix}.attn_q.bias"));
let k_bias = gguf.tensor_to_f32(&format!("{prefix}.attn_k.bias"));
let v_bias = gguf.tensor_to_f32(&format!("{prefix}.attn_v.bias"));
let q_norm = gguf.tensor_to_f32(&format!("{prefix}.attn_q_norm.weight"));
let k_norm = gguf.tensor_to_f32(&format!("{prefix}.attn_k_norm.weight"));
let has_ffn_norm_tensor = gguf
.tensor_to_f32(&format!("{prefix}.ffn_norm.weight"))
.is_some();
let post_attn_norm = if has_ffn_norm_tensor {
gguf.tensor_to_f32(&format!("{prefix}.post_attention_norm.weight"))
} else {
None
};
let post_ffn_norm = gguf.tensor_to_f32(&format!("{prefix}.post_ffw_norm.weight"));
let post_norm = gguf.tensor_to_f32(&format!("{prefix}.post_norm.weight"));
let laurel_l = gguf.tensor_to_f32(&format!("{prefix}.laurel_l.weight"));
let laurel_r = gguf.tensor_to_f32(&format!("{prefix}.laurel_r.weight"));
let laurel_post_norm = gguf.tensor_to_f32(&format!("{prefix}.laurel_post_norm.weight"));
let altup_router = gguf.tensor_to_f32(&format!("{prefix}.altup_router.weight"));
let altup_router_norm = gguf.tensor_to_f32(&format!("{prefix}.altup_router_norm.weight"));
let altup_predict_coef = gguf.tensor_to_f32(&format!("{prefix}.altup_predict_coef.weight"));
let altup_correct_coef = gguf.tensor_to_f32(&format!("{prefix}.altup_correct_coef.weight"));
let altup_correct_scale = gguf.tensor_to_f32(&format!("{prefix}.altup_correct_scale.weight"));
let out_scale = gguf.tensor_to_f32(&format!("{prefix}.layer_output_scale.weight"));
let rope_freqs = gguf.tensor_to_f32(&format!("{prefix}.rope_freqs.weight"));
let ffn_gate_inp = gguf.tensor_to_f32(&format!("{prefix}.ffn_gate_inp.weight"));
let expert_ffn = config.expert_ffn_size().unwrap_or(ffn_size);
let (ffn_gate_exps, ffn_up_exps, ffn_down_exps) =
if let (Some(_), Some(n_expert)) = (ffn_gate_inp.as_ref(), config.num_experts()) {
let gate = load_weight_ref(
gguf,
&format!("{prefix}.ffn_gate_exps.weight"),
expert_ffn * n_expert, config.hidden_dim, );
let up = load_weight_ref(
gguf,
&format!("{prefix}.ffn_up_exps.weight"),
expert_ffn * n_expert,
config.hidden_dim,
);
let down = load_weight_ref(
gguf,
&format!("{prefix}.ffn_down_exps.weight"),
config.hidden_dim * n_expert, expert_ffn, );
(gate, up, down)
} else {
(None, None, None)
};
let (inp_gate, proj) = if let Some(per_layer_dim) = config.per_layer_input_embedding_dim() {
let inp_gate = load_weight_ref(
gguf,
&format!("{prefix}.inp_gate.weight"),
per_layer_dim, config.hidden_dim, );
let proj = load_weight_ref(
gguf,
&format!("{prefix}.proj.weight"),
config.hidden_dim, per_layer_dim, );
(inp_gate, proj)
} else {
(None, None)
};
let qwen_biases = match (q_bias, k_bias, v_bias) {
(Some(q_bias), Some(k_bias), Some(v_bias)) => Some(QwenAttentionBiases {
q_bias,
k_bias,
v_bias,
}),
_ => None,
};
let qwen_norms = match (q_norm, k_norm) {
(Some(q_norm), Some(k_norm)) => Some(QwenAttentionNorms { q_norm, k_norm }),
_ => None,
};
let gemma3n = match (
post_norm,
inp_gate,
proj,
laurel_l,
laurel_r,
laurel_post_norm,
altup_router,
altup_router_norm,
altup_predict_coef,
altup_correct_coef,
altup_correct_scale,
) {
(
Some(post_norm),
Some(inp_gate),
Some(proj),
Some(laurel_l),
Some(laurel_r),
Some(laurel_post_norm),
Some(altup_router),
Some(altup_router_norm),
Some(altup_predict_coef),
Some(altup_correct_coef),
Some(altup_correct_scale),
) => Some(Gemma3nLayerAugmentations {
post_norm,
inp_gate,
proj,
laurel_l,
laurel_r,
laurel_post_norm,
altup_router,
altup_router_norm,
altup_predict_coef,
altup_correct_coef,
altup_correct_scale,
}),
_ => None,
};
let moe = match (ffn_gate_inp, ffn_gate_exps, ffn_up_exps, ffn_down_exps) {
(Some(ffn_gate_inp), Some(ffn_gate_exps), Some(ffn_up_exps), Some(ffn_down_exps)) => {
Some(MoeExpertWeights {
ffn_gate_inp,
ffn_gate_exps,
ffn_up_exps,
ffn_down_exps,
})
}
_ => None,
};
Some(LayerWeights {
attn_norm,
q_proj,
k_proj,
v_proj,
o_proj,
ffn_norm,
gate_proj,
up_proj,
down_proj,
post_attn_norm,
post_ffn_norm,
out_scale,
rope_freqs,
qwen_biases,
qwen_norms,
gemma3n,
moe,
gated_output,
})
}
#[cfg(not(feature = "gguf"))]
fn build_deepseek_streaming_pool(
_gguf: &GgufFile<'_>,
_config: &Llama3Config,
) -> Option<std::sync::Arc<crate::deepseek_streaming::StreamingExpertPool>> {
None
}
#[cfg(feature = "gguf")]
#[allow(clippy::too_many_lines)]
fn build_deepseek_streaming_pool(
gguf: &GgufFile<'_>,
config: &Llama3Config,
) -> Option<std::sync::Arc<crate::deepseek_streaming::StreamingExpertPool>> {
use crate::deepseek_streaming::{ExpertLayerInfo, StreamingExpertPool};
if std::env::var("ALICE_LLM_MOE_STREAMING").ok().as_deref() != Some("1") {
return None;
}
let Ok(path) = std::env::var("ALICE_LLM_MOE_STREAMING_FILE") else {
eprintln!(
"[alice-llm] ALICE_LLM_MOE_STREAMING=1 but ALICE_LLM_MOE_STREAMING_FILE not set; \
falling back to InMemory routed experts."
);
return None;
};
let file = std::fs::File::open(&path)
.map_err(|e| {
eprintln!("[alice-llm] streaming pool disabled: cannot open '{path}': {e}");
})
.ok()?;
let mmap = unsafe { memmap2::Mmap::map(&file) }
.map_err(|e| {
eprintln!("[alice-llm] streaming pool disabled: mmap failed: {e}");
})
.ok()?;
let advised = crate::deepseek_streaming::advise_random(&mmap);
if advised {
eprintln!("[alice-llm] MADV_RANDOM applied to streaming pool mmap");
}
let first_k_dense = config.deepseek_first_k_dense_replace().unwrap_or(0);
let n_experts = config.deepseek_n_routed_experts()?;
let placeholder = ExpertLayerInfo {
base_offset: 0,
bytes_per_expert: 0,
n_experts: 0,
qtype: crate::gguf::GgmlType::F32,
};
let mut layer_info: Vec<[ExpertLayerInfo; 3]> = Vec::with_capacity(config.num_layers);
for layer_idx in 0..config.num_layers {
if layer_idx < first_k_dense {
layer_info.push([placeholder; 3]);
continue;
}
let prefix = format!("blk.{layer_idx}");
let gate_name = format!("{prefix}.ffn_gate_exps.weight");
let up_name = format!("{prefix}.ffn_up_exps.weight");
let down_name = format!("{prefix}.ffn_down_exps.weight");
let gate_info = gguf.tensors.get(&gate_name)?;
let up_info = gguf.tensors.get(&up_name)?;
let down_info = gguf.tensors.get(&down_name)?;
let gate_bytes_per_expert = gate_info.data_size().checked_div(n_experts)?;
let up_bytes_per_expert = up_info.data_size().checked_div(n_experts)?;
let down_bytes_per_expert = down_info.data_size().checked_div(n_experts)?;
let gate_off = gguf.tensor_absolute_offset(&gate_name)? as usize;
let up_off = gguf.tensor_absolute_offset(&up_name)? as usize;
let down_off = gguf.tensor_absolute_offset(&down_name)? as usize;
layer_info.push([
ExpertLayerInfo {
base_offset: gate_off,
bytes_per_expert: gate_bytes_per_expert,
n_experts,
qtype: gate_info.qtype,
},
ExpertLayerInfo {
base_offset: up_off,
bytes_per_expert: up_bytes_per_expert,
n_experts,
qtype: up_info.qtype,
},
ExpertLayerInfo {
base_offset: down_off,
bytes_per_expert: down_bytes_per_expert,
n_experts,
qtype: down_info.qtype,
},
]);
}
let budget: usize = std::env::var("ALICE_LLM_MOE_CACHE_BYTES")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(4usize * 1024 * 1024 * 1024);
eprintln!(
"[alice-llm] DeepSeek streaming pool: mmap '{path}' ({} bytes), budget {} MiB",
mmap.len(),
budget / (1024 * 1024)
);
let source: std::sync::Arc<dyn crate::deepseek_streaming::ExpertByteSource> =
std::sync::Arc::new(mmap);
Some(std::sync::Arc::new(StreamingExpertPool::new(
source, layer_info, budget,
)))
}
fn load_deepseek_v3_layer_weights<'a>(
gguf: &'a GgufFile<'a>,
layer: usize,
config: &Llama3Config,
streaming_pool: Option<&std::sync::Arc<crate::deepseek_streaming::StreamingExpertPool>>,
) -> Option<DeepSeekV3LayerWeights<'a>> {
let prefix = format!("blk.{layer}");
let attn_norm = gguf.tensor_to_f32(&format!("{prefix}.attn_norm.weight"))?;
let kv_lora_rank = config.deepseek_kv_lora_rank()?;
let qk_nope_head_dim = config.deepseek_qk_nope_head_dim()?;
let qk_rope_head_dim = config.deepseek_qk_rope_head_dim()?;
let v_head_dim = config.deepseek_v_head_dim()?;
let q_head_total = qk_nope_head_dim + qk_rope_head_dim;
let kv_up_head_total = qk_nope_head_dim + v_head_dim;
let q = if let Some(q_lora_rank) = config.deepseek_q_lora_rank() {
let q_a_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_q_a.weight"),
q_lora_rank,
config.hidden_dim,
)?;
let q_a_norm = gguf.tensor_to_f32(&format!("{prefix}.attn_q_a_norm.weight"))?;
let q_b_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_q_b.weight"),
config.num_heads * q_head_total,
q_lora_rank,
)?;
DeepSeekQProjection::LoRA {
q_a_proj,
q_a_norm,
q_b_proj,
}
} else {
let q_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_q.weight"),
config.num_heads * q_head_total,
config.hidden_dim,
)?;
DeepSeekQProjection::Dense { q_proj }
};
let kv_a_proj_with_mqa = load_weight_ref(
gguf,
&format!("{prefix}.attn_kv_a_mqa.weight"),
kv_lora_rank + qk_rope_head_dim,
config.hidden_dim,
)?;
let kv_a_norm = gguf.tensor_to_f32(&format!("{prefix}.attn_kv_a_norm.weight"))?;
let kv_b_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_kv_b.weight"),
config.num_heads * kv_up_head_total,
kv_lora_rank,
)?;
let o_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_output.weight"),
config.hidden_dim,
config.num_heads * v_head_dim,
)?;
let first_k_dense = config.deepseek_first_k_dense_replace().unwrap_or(0);
let (ffn_norm, gate_proj, up_proj, down_proj, moe) = if layer < first_k_dense {
let ffn_size = config.ffn_size_for_layer(layer);
let ffn_norm = load_ffn_norm(gguf, &prefix)?;
let gate_proj = load_weight_ref(
gguf,
&format!("{prefix}.ffn_gate.weight"),
ffn_size,
config.hidden_dim,
)?;
let up_proj = load_weight_ref(
gguf,
&format!("{prefix}.ffn_up.weight"),
ffn_size,
config.hidden_dim,
)?;
let down_proj = load_weight_ref(
gguf,
&format!("{prefix}.ffn_down.weight"),
config.hidden_dim,
ffn_size,
)?;
(
Some(ffn_norm),
Some(gate_proj),
Some(up_proj),
Some(down_proj),
None,
)
} else {
let n_experts = config.deepseek_n_routed_experts()?;
let n_shared = config.deepseek_n_shared_experts()?;
let moe_ffn = config.deepseek_moe_intermediate_size()?;
let shared_ffn = n_shared * moe_ffn;
let ffn_norm = load_ffn_norm(gguf, &prefix)?;
let ffn_gate_inp = gguf.tensor_to_f32(&format!("{prefix}.ffn_gate_inp.weight"))?;
let exp_probs_b = gguf.tensor_to_f32(&format!("{prefix}.exp_probs_b.bias"));
let ffn_gate_shexp = load_weight_ref(
gguf,
&format!("{prefix}.ffn_gate_shexp.weight"),
shared_ffn,
config.hidden_dim,
)?;
let ffn_up_shexp = load_weight_ref(
gguf,
&format!("{prefix}.ffn_up_shexp.weight"),
shared_ffn,
config.hidden_dim,
)?;
let ffn_down_shexp = load_weight_ref(
gguf,
&format!("{prefix}.ffn_down_shexp.weight"),
config.hidden_dim,
shared_ffn,
)?;
let routed = if let Some(pool) = streaming_pool {
RoutedExpertStorage::Streaming {
pool: pool.clone(),
layer_idx: layer,
}
} else {
let ffn_gate_exps = load_weight_ref(
gguf,
&format!("{prefix}.ffn_gate_exps.weight"),
moe_ffn * n_experts,
config.hidden_dim,
)?;
let ffn_up_exps = load_weight_ref(
gguf,
&format!("{prefix}.ffn_up_exps.weight"),
moe_ffn * n_experts,
config.hidden_dim,
)?;
let ffn_down_exps = load_weight_ref(
gguf,
&format!("{prefix}.ffn_down_exps.weight"),
config.hidden_dim * n_experts,
moe_ffn,
)?;
RoutedExpertStorage::InMemory {
gate: ffn_gate_exps,
up: ffn_up_exps,
down: ffn_down_exps,
}
};
let moe = DeepSeekMoeWeights {
ffn_norm,
ffn_gate_inp,
exp_probs_b,
routed,
ffn_gate_shexp,
ffn_up_shexp,
ffn_down_shexp,
};
(None, None, None, None, Some(moe))
};
Some(DeepSeekV3LayerWeights {
attn_norm,
q,
kv_a_proj_with_mqa,
kv_a_norm,
kv_b_proj,
o_proj,
ffn_norm,
gate_proj,
up_proj,
down_proj,
moe,
})
}
fn load_deepseek_v3_mtp_weights<'a>(
gguf: &'a GgufFile<'a>,
config: &Llama3Config,
streaming_pool: Option<&std::sync::Arc<crate::deepseek_streaming::StreamingExpertPool>>,
) -> Option<DeepSeekV3MtpWeights<'a>> {
config.deepseek_mtp_layer()?;
let enorm = gguf.tensor_to_f32("mtp.enorm.weight")?;
let hnorm = gguf.tensor_to_f32("mtp.hnorm.weight")?;
let eh_proj = load_weight_ref(
gguf,
"mtp.eh_proj.weight",
config.hidden_dim,
2 * config.hidden_dim,
)?;
let _ = streaming_pool; let block = {
load_deepseek_v3_layer_weights_with_prefix(gguf, "mtp.blk.0", config, None)?
};
let final_norm = gguf.tensor_to_f32("mtp.norm.weight")?;
Some(DeepSeekV3MtpWeights {
enorm,
hnorm,
eh_proj,
block,
final_norm,
})
}
fn load_deepseek_v3_layer_weights_with_prefix<'a>(
gguf: &'a GgufFile<'a>,
prefix: &str,
config: &Llama3Config,
streaming_pool: Option<&std::sync::Arc<crate::deepseek_streaming::StreamingExpertPool>>,
) -> Option<DeepSeekV3LayerWeights<'a>> {
let attn_norm = gguf.tensor_to_f32(&format!("{prefix}.attn_norm.weight"))?;
let kv_lora_rank = config.deepseek_kv_lora_rank()?;
let qk_nope_head_dim = config.deepseek_qk_nope_head_dim()?;
let qk_rope_head_dim = config.deepseek_qk_rope_head_dim()?;
let v_head_dim = config.deepseek_v_head_dim()?;
let q_head_total = qk_nope_head_dim + qk_rope_head_dim;
let kv_up_head_total = qk_nope_head_dim + v_head_dim;
let q = if let Some(q_lora_rank) = config.deepseek_q_lora_rank() {
let q_a_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_q_a.weight"),
q_lora_rank,
config.hidden_dim,
)?;
let q_a_norm = gguf.tensor_to_f32(&format!("{prefix}.attn_q_a_norm.weight"))?;
let q_b_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_q_b.weight"),
config.num_heads * q_head_total,
q_lora_rank,
)?;
DeepSeekQProjection::LoRA {
q_a_proj,
q_a_norm,
q_b_proj,
}
} else {
let q_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_q.weight"),
config.num_heads * q_head_total,
config.hidden_dim,
)?;
DeepSeekQProjection::Dense { q_proj }
};
let kv_a_proj_with_mqa = load_weight_ref(
gguf,
&format!("{prefix}.attn_kv_a_mqa.weight"),
kv_lora_rank + qk_rope_head_dim,
config.hidden_dim,
)?;
let kv_a_norm = gguf.tensor_to_f32(&format!("{prefix}.attn_kv_a_norm.weight"))?;
let kv_b_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_kv_b.weight"),
config.num_heads * kv_up_head_total,
kv_lora_rank,
)?;
let o_proj = load_weight_ref(
gguf,
&format!("{prefix}.attn_output.weight"),
config.hidden_dim,
config.num_heads * v_head_dim,
)?;
let n_experts = config.deepseek_n_routed_experts()?;
let n_shared = config.deepseek_n_shared_experts()?;
let moe_ffn = config.deepseek_moe_intermediate_size()?;
let shared_ffn = n_shared * moe_ffn;
let ffn_norm_v = load_ffn_norm(gguf, prefix)?;
let ffn_gate_inp = gguf.tensor_to_f32(&format!("{prefix}.ffn_gate_inp.weight"))?;
let exp_probs_b = gguf.tensor_to_f32(&format!("{prefix}.exp_probs_b.bias"));
let ffn_gate_shexp = load_weight_ref(
gguf,
&format!("{prefix}.ffn_gate_shexp.weight"),
shared_ffn,
config.hidden_dim,
)?;
let ffn_up_shexp = load_weight_ref(
gguf,
&format!("{prefix}.ffn_up_shexp.weight"),
shared_ffn,
config.hidden_dim,
)?;
let ffn_down_shexp = load_weight_ref(
gguf,
&format!("{prefix}.ffn_down_shexp.weight"),
config.hidden_dim,
shared_ffn,
)?;
let routed = if streaming_pool.is_some() {
unreachable!("MTP loader unexpectedly received a streaming pool");
} else {
let ffn_gate_exps = load_weight_ref(
gguf,
&format!("{prefix}.ffn_gate_exps.weight"),
moe_ffn * n_experts,
config.hidden_dim,
)?;
let ffn_up_exps = load_weight_ref(
gguf,
&format!("{prefix}.ffn_up_exps.weight"),
moe_ffn * n_experts,
config.hidden_dim,
)?;
let ffn_down_exps = load_weight_ref(
gguf,
&format!("{prefix}.ffn_down_exps.weight"),
config.hidden_dim * n_experts,
moe_ffn,
)?;
RoutedExpertStorage::InMemory {
gate: ffn_gate_exps,
up: ffn_up_exps,
down: ffn_down_exps,
}
};
let moe = DeepSeekMoeWeights {
ffn_norm: ffn_norm_v,
ffn_gate_inp,
exp_probs_b,
routed,
ffn_gate_shexp,
ffn_up_shexp,
ffn_down_shexp,
};
Some(DeepSeekV3LayerWeights {
attn_norm,
q,
kv_a_proj_with_mqa,
kv_a_norm,
kv_b_proj,
o_proj,
ffn_norm: None,
gate_proj: None,
up_proj: None,
down_proj: None,
moe: Some(moe),
})
}
fn load_deltanet_layer_weights<'a>(
gguf: &'a GgufFile<'a>,
layer: usize,
config: &Llama3Config,
) -> Option<DeltaNetLayerWeights<'a>> {
let prefix = format!("blk.{layer}");
let attn_norm = gguf.tensor_to_f32(&format!("{prefix}.attn_norm.weight"))?;
let qk_dim = config.linear_qk_head_dim().unwrap_or(128);
let v_dim = config.linear_kv_head_dim().unwrap_or(128);
let num_kv_heads = config.linear_num_kv_heads().unwrap_or(config.num_kv_heads);
let num_v_heads = config.linear_num_v_heads().unwrap_or(config.num_heads);
let in_proj_out = qk_dim * num_kv_heads * 2 + v_dim * num_v_heads * 2;
let ssm_in = load_weight_ref(
gguf,
&format!("{prefix}.ssm_in.weight"),
in_proj_out,
config.hidden_dim,
);
let attn_qkv = load_weight_ref_any_rows(
gguf,
&format!("{prefix}.attn_qkv.weight"),
config.hidden_dim,
);
if ssm_in.is_none() && attn_qkv.is_none() {
return None;
}
let attn_gate = load_weight_ref_any_rows(
gguf,
&format!("{prefix}.attn_gate.weight"),
config.hidden_dim,
);
let ssm_a = gguf.tensor_to_f32(&format!("{prefix}.ssm_a"));
let ssm_dt_bias = gguf.tensor_to_f32(&format!("{prefix}.ssm_dt.bias"));
let ssm_norm = gguf.tensor_to_f32(&format!("{prefix}.ssm_norm.weight"));
let conv1d_weight = gguf.tensor_to_f32(&format!("{prefix}.ssm_conv1d.weight"))?;
let conv_dim = qk_dim * num_kv_heads * 2 + v_dim * num_v_heads;
let conv1d_bias = gguf
.tensor_to_f32(&format!("{prefix}.ssm_conv1d.bias"))
.unwrap_or_else(|| vec![0.0f32; conv_dim]);
let alpha_proj = load_weight_ref_any_rows(
gguf,
&format!("{prefix}.ssm_alpha.weight"),
config.hidden_dim,
)?;
let beta_proj = load_weight_ref_any_rows(
gguf,
&format!("{prefix}.ssm_beta.weight"),
config.hidden_dim,
)?;
let ssm_out = load_weight_ref(
gguf,
&format!("{prefix}.ssm_out.weight"),
config.hidden_dim,
v_dim * num_v_heads,
)?;
let ffn_norm = load_ffn_norm(gguf, &prefix)?;
let ffn_size = config.ffn_size_for_layer(layer);
let gate_proj = load_weight_ref(
gguf,
&format!("{prefix}.ffn_gate.weight"),
ffn_size,
config.hidden_dim,
)?;
let up_proj = load_weight_ref(
gguf,
&format!("{prefix}.ffn_up.weight"),
ffn_size,
config.hidden_dim,
)?;
let down_proj = load_weight_ref(
gguf,
&format!("{prefix}.ffn_down.weight"),
config.hidden_dim,
ffn_size,
)?;
Some(DeltaNetLayerWeights {
attn_norm,
ssm_in,
attn_qkv,
attn_gate,
ssm_a,
ssm_dt_bias,
ssm_norm,
conv1d_weight,
conv1d_bias,
alpha_proj,
beta_proj,
ssm_out,
ffn_norm,
gate_proj,
up_proj,
down_proj,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_llama3_config_defaults() {
let c = Llama3Config::llama3_8b();
assert_eq!(c.vocab_size, 128_256);
assert_eq!(c.hidden_dim, 4096);
assert_eq!(c.num_heads, 32);
assert_eq!(c.num_kv_heads, 8);
assert_eq!(c.num_layers, 32);
assert_eq!(c.head_dim, 128);
assert_eq!(c.intermediate_dim, 14_336);
}
#[test]
fn test_gqa_heads_per_kv() {
let c = Llama3Config::llama3_8b();
assert_eq!(c.num_heads / c.num_kv_heads, 4);
}
#[test]
fn test_online_softmax_matches_two_pass() {
let head_dim = 8;
let seq_len = 5;
let inv_sqrt_d = 1.0 / (head_dim as f32).sqrt();
let q: Vec<f32> = (0..head_dim).map(|i| (i as f32) * 0.11 - 0.4).collect();
let mut k = vec![0.0f32; seq_len * head_dim];
let mut v = vec![0.0f32; seq_len * head_dim];
for t in 0..seq_len {
for d in 0..head_dim {
k[t * head_dim + d] = ((t + 1) as f32 * 0.07 - d as f32 * 0.03).sin();
v[t * head_dim + d] = ((t + 3) as f32 * 0.13 + d as f32 * 0.05).cos();
}
}
let mut scores = vec![0.0f32; seq_len];
for t in 0..seq_len {
let mut s = 0.0f32;
for d in 0..head_dim {
s += q[d] * k[t * head_dim + d];
}
scores[t] = s * inv_sqrt_d;
}
let m = scores.iter().copied().fold(f32::NEG_INFINITY, f32::max);
let mut sum = 0.0f32;
for s in &mut scores {
*s = (*s - m).exp();
sum += *s;
}
for s in &mut scores {
*s /= sum;
}
let mut two_pass = vec![0.0f32; head_dim];
for t in 0..seq_len {
let w = scores[t];
for d in 0..head_dim {
two_pass[d] += w * v[t * head_dim + d];
}
}
let mut online = vec![0.0f32; head_dim];
let mut m_run = f32::NEG_INFINITY;
let mut s_run = 0.0f32;
for t in 0..seq_len {
let mut score = 0.0f32;
for d in 0..head_dim {
score += q[d] * k[t * head_dim + d];
}
score *= inv_sqrt_d;
let m_old = m_run;
let (ms, vs) = if score > m_run {
m_run = score;
let ms = (m_old - m_run).exp();
for d in 0..head_dim {
online[d] *= ms;
}
(ms, 1.0f32)
} else {
(1.0f32, (score - m_run).exp())
};
for d in 0..head_dim {
online[d] += vs * v[t * head_dim + d];
}
s_run = s_run * ms + vs;
}
let inv_s = 1.0 / s_run;
for d in 0..head_dim {
online[d] *= inv_s;
}
for d in 0..head_dim {
assert!(
(online[d] - two_pass[d]).abs() < 1e-5,
"online[{d}]={} vs two_pass[{d}]={}",
online[d],
two_pass[d]
);
}
}
#[test]
fn test_rms_norm() {
let x = [1.0f32, 2.0, 3.0, 4.0];
let w = [1.0f32; 4];
let mut out = [0.0f32; 4];
rms_norm(&x, &w, 1e-5, &mut out);
let rms = (7.5f32 + 1e-5).sqrt();
for i in 0..4 {
let expected = x[i] / rms;
assert!(
(out[i] - expected).abs() < 1e-5,
"rms_norm[{i}]: got {}, expected {expected}",
out[i]
);
}
}
#[test]
fn test_silu() {
assert!((silu(0.0) - 0.0).abs() < 1e-6);
assert!((silu(1.0) - 0.7311).abs() < 1e-3);
assert!((silu(10.0) - 10.0).abs() < 0.01);
}
#[test]
fn test_rope_identity_at_zero() {
let mut vec = [1.0f32, 0.0, 1.0, 0.0];
apply_rope(&mut vec, 0, 4, 10000.0);
assert!((vec[0] - 1.0).abs() < 1e-6);
assert!(vec[1].abs() < 1e-6);
}
#[test]
fn test_rope_preserves_norm() {
let mut vec = [3.0f32, 4.0, 1.0, 2.0];
let norm_before: f32 = vec.iter().map(|x| x * x).sum::<f32>().sqrt();
apply_rope(&mut vec, 5, 4, 10000.0);
let norm_after: f32 = vec.iter().map(|x| x * x).sum::<f32>().sqrt();
assert!(
(norm_before - norm_after).abs() < 1e-4,
"RoPE changed norm: {norm_before} -> {norm_after}"
);
}
#[test]
fn test_kv_cache() {
let kv_dim = 1024;
let mut cache = KvCache::new(2, 128, kv_dim);
assert_eq!(cache.seq_len(), 0);
cache.append(0, &vec![1.0f32; kv_dim], &vec![2.0f32; kv_dim]);
cache.append(1, &vec![5.0f32; kv_dim], &vec![6.0f32; kv_dim]);
cache.advance();
assert_eq!(cache.seq_len(), 1);
cache.append(0, &vec![3.0f32; kv_dim], &vec![4.0f32; kv_dim]);
cache.append(1, &vec![7.0f32; kv_dim], &vec![8.0f32; kv_dim]);
cache.advance();
assert_eq!(cache.seq_len(), 2);
assert_eq!(cache.key_at(0, 0)[0], 1.0);
assert_eq!(cache.key_at(0, 1)[0], 3.0);
assert_eq!(cache.value_at(1, 0)[0], 6.0);
cache.clear();
assert_eq!(cache.seq_len(), 0);
}
#[test]
fn test_paged_kv_cache_basic() {
let kv_dim = 64;
let mut cache = PagedKvCache::new(2, kv_dim);
assert_eq!(cache.seq_len(), 0);
assert_eq!(cache.total_pages(), 0);
cache.append(0, &vec![1.0f32; kv_dim], &vec![2.0f32; kv_dim]);
cache.append(1, &vec![5.0f32; kv_dim], &vec![6.0f32; kv_dim]);
cache.advance();
assert_eq!(cache.seq_len(), 1);
assert_eq!(cache.total_pages(), 2);
assert_eq!(cache.key_at(0, 0)[0], 1.0);
assert_eq!(cache.value_at(0, 0)[0], 2.0);
assert_eq!(cache.key_at(1, 0)[0], 5.0);
assert_eq!(cache.value_at(1, 0)[0], 6.0);
cache.append(0, &vec![3.0f32; kv_dim], &vec![4.0f32; kv_dim]);
cache.append(1, &vec![7.0f32; kv_dim], &vec![8.0f32; kv_dim]);
cache.advance();
assert_eq!(cache.seq_len(), 2);
assert_eq!(cache.key_at(0, 1)[0], 3.0);
assert_eq!(cache.value_at(1, 1)[0], 8.0);
}
#[test]
fn test_paged_kv_cache_page_boundary() {
let kv_dim = 32;
let mut cache = PagedKvCache::new(1, kv_dim);
for i in 0..PAGE_SIZE {
cache.append(0, &vec![i as f32; kv_dim], &vec![(i as f32) * 10.0; kv_dim]);
cache.advance();
}
assert_eq!(cache.seq_len(), PAGE_SIZE);
assert_eq!(cache.total_pages(), 1);
cache.append(0, &vec![99.0f32; kv_dim], &vec![990.0f32; kv_dim]);
cache.advance();
assert_eq!(cache.seq_len(), PAGE_SIZE + 1);
assert_eq!(cache.total_pages(), 2);
assert_eq!(cache.key_at(0, 0)[0], 0.0);
assert_eq!(cache.key_at(0, 15)[0], 15.0);
assert_eq!(cache.key_at(0, 16)[0], 99.0);
}
#[test]
fn test_paged_kv_cache_rollback() {
let kv_dim = 32;
let mut cache = PagedKvCache::new(1, kv_dim);
for i in 0..20 {
cache.append(0, &vec![i as f32; kv_dim], &vec![0.0; kv_dim]);
cache.advance();
}
assert_eq!(cache.seq_len(), 20);
assert_eq!(cache.total_pages(), 2);
cache.rollback_to(10);
assert_eq!(cache.seq_len(), 10);
assert_eq!(cache.total_pages(), 1);
cache.rollback_to(0);
assert_eq!(cache.seq_len(), 0);
assert_eq!(cache.total_pages(), 0);
}
#[test]
fn test_paged_kv_cache_memory() {
let kv_dim = 128;
let cache = PagedKvCache::new(4, kv_dim);
assert_eq!(cache.memory_bytes(), 0); }
#[test]
fn test_batch_scheduler() {
let mut sched = BatchScheduler::new();
let id0 = sched.add_request(vec![1, 2, 3], 10, 0.0, 4, 128);
let id1 = sched.add_request(vec![4, 5], 5, 0.7, 4, 128);
assert_eq!(id0, 0);
assert_eq!(id1, 1);
assert_eq!(sched.active_count(), 2);
sched.requests_mut()[0].done = true;
assert_eq!(sched.active_count(), 1);
assert!(sched.get_result(id0).is_some());
assert!(sched.get_result(id1).is_none());
}
#[test]
fn test_memory_estimate_8b_q4k() {
let c = Llama3Config::llama3_8b();
let total_params: usize = c.vocab_size * c.hidden_dim + c.num_layers * (
c.hidden_dim * c.hidden_dim + c.num_kv_heads * c.head_dim * c.hidden_dim * 2 + c.hidden_dim * c.hidden_dim + c.intermediate_dim * c.hidden_dim * 3 )
+ c.vocab_size * c.hidden_dim;
let q4k_bytes = (total_params as f64 * 0.6) / 1e9;
assert!(
q4k_bytes > 3.0 && q4k_bytes < 7.0,
"Q4_K estimate: {q4k_bytes:.1} GB"
);
}
#[test]
fn test_layer_quant_config_full_ternary() {
let cfg = LayerQuantConfig::full_ternary();
assert_eq!(cfg.attention_mode, LayerQuantMode::Ternary);
assert_eq!(cfg.ffn_mode, LayerQuantMode::Ternary);
}
#[test]
fn test_layer_quant_config_aggressive() {
let cfg = LayerQuantConfig::aggressive_compression(8);
assert_eq!(cfg.attention_mode, LayerQuantMode::Ternary);
assert_eq!(cfg.ffn_mode, LayerQuantMode::SparseTernary { n_keep: 8 });
}
#[test]
fn test_mixed_precision_uniform() {
let mp = MixedPrecisionConfig::uniform(LayerQuantConfig::full_ternary(), 32);
assert_eq!(mp.layer_configs.len(), 32);
assert_eq!(mp.get(0).attention_mode, LayerQuantMode::Ternary);
assert_eq!(mp.get(31).ffn_mode, LayerQuantMode::Ternary);
}
#[test]
fn test_mixed_precision_10gb_target() {
let mp = MixedPrecisionConfig::target_10gb(80); assert_eq!(mp.layer_configs.len(), 80);
assert_eq!(mp.get(0).attention_mode, LayerQuantMode::Ternary);
assert_eq!(
mp.get(0).ffn_mode,
LayerQuantMode::SparseTernary { n_keep: 8 }
);
}
#[test]
fn test_mixed_precision_bits_estimate() {
let mp_ternary = MixedPrecisionConfig::uniform(LayerQuantConfig::full_ternary(), 32);
let bits_ternary = mp_ternary.estimate_bits_per_param();
assert!(
(bits_ternary - 1.58).abs() < 0.01,
"full ternary: {bits_ternary}"
);
let mp_aggressive = MixedPrecisionConfig::target_10gb(32);
let bits_aggressive = mp_aggressive.estimate_bits_per_param();
assert!(
bits_aggressive < bits_ternary,
"aggressive ({bits_aggressive}) should be < ternary ({bits_ternary})"
);
}
#[test]
fn test_70b_10gb_feasibility() {
let mp = MixedPrecisionConfig::target_10gb(80);
let bpp = mp.estimate_bits_per_param();
let model_size_gb = 70e9 * bpp as f64 / 8.0 / 1e9;
assert!(
bpp < 1.58,
"bits/param={bpp}, should be < 1.58 for 10GB target"
);
eprintln!("70B estimate: {bpp:.3} bits/param → {model_size_gb:.1} GB (target: <10 GB)");
}
#[test]
fn test_mode_bits() {
assert!((mode_bits(LayerQuantMode::Ternary) - 1.58).abs() < 0.01);
assert!((mode_bits(LayerQuantMode::Binary) - 1.0).abs() < 0.01);
let sparse_bits = mode_bits(LayerQuantMode::SparseTernary { n_keep: 8 });
assert!(sparse_bits < 1.58, "sparse={sparse_bits}");
assert!(sparse_bits > 0.5, "sparse={sparse_bits}");
}
fn gemma3n_config_with_sparsity(scales: Vec<f32>) -> Llama3Config {
Llama3Config {
arch: ModelArch::Gemma3n,
gemma3n: Some(Gemma3nConfig {
sliding_window_pattern: None,
activation_sparsity_scale: Some(scales),
shared_kv_layers: None,
per_layer_input_embedding_dim: None,
altup_num_inputs: None,
altup_active_idx: None,
}),
..Llama3Config::llama3_8b()
}
}
#[test]
fn test_apply_ffn_sparsity_noop_non_gemma3n() {
let c = Llama3Config::llama3_8b(); let mut buf = vec![1.0, 2.0, 3.0, 4.0];
c.apply_ffn_sparsity(0, &mut buf);
assert_eq!(buf, vec![1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn test_apply_ffn_sparsity_noop_dense_layer() {
let c = gemma3n_config_with_sparsity(vec![f32::NEG_INFINITY; 4]);
let mut buf = vec![1.0, 2.0, 3.0, 4.0];
c.apply_ffn_sparsity(0, &mut buf);
assert_eq!(buf, vec![1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn test_apply_ffn_sparsity_gaussian_topk_math() {
let c = gemma3n_config_with_sparsity(vec![1.6448536; 4]);
let mut buf = vec![1.0, 2.0, 3.0, 4.0, 5.0];
c.apply_ffn_sparsity(0, &mut buf);
for &v in &buf {
assert!(v.abs() < 1e-5, "expected all zeros, got {buf:?}");
}
}
#[test]
fn test_apply_ffn_sparsity_preserves_extreme_tail() {
let c = gemma3n_config_with_sparsity(vec![1.6448536; 4]);
let mut buf = vec![0.0; 10];
buf[9] = 10.0;
c.apply_ffn_sparsity(0, &mut buf);
for &v in &buf[..9] {
assert!(v.abs() < 1e-5, "prefix must be zero, got {buf:?}");
}
assert!(
(buf[9] - 3.799).abs() < 0.01,
"outlier survives with expected value, got {}",
buf[9]
);
}
#[test]
fn test_apply_ffn_sparsity_short_buffer_noop() {
let c = gemma3n_config_with_sparsity(vec![1.6448; 4]);
let mut buf = vec![42.0];
c.apply_ffn_sparsity(0, &mut buf);
assert_eq!(buf, vec![42.0]);
}
#[test]
fn test_apply_ffn_act_gemma3n_always_gelu() {
let c = gemma3n_config_with_sparsity(vec![1.6448, f32::NEG_INFINITY]);
let sparse_result = c.apply_ffn_act(0, 1.0);
let dense_result = c.apply_ffn_act(1, 1.0);
let expected_gelu = gelu_approx(1.0);
assert!((sparse_result - expected_gelu).abs() < 1e-6);
assert!((dense_result - expected_gelu).abs() < 1e-6);
}
fn gemma3n_e2b_config() -> Llama3Config {
let pattern: Vec<bool> = (0..30).map(|i| i % 5 != 4).collect();
Llama3Config {
arch: ModelArch::Gemma3n,
num_layers: 30,
attention_extras: Some(AttentionExtrasConfig {
sliding_window: Some(512),
attn_logit_softcap: None,
final_logit_softcap: None,
}),
gemma3n: Some(Gemma3nConfig {
sliding_window_pattern: Some(pattern),
activation_sparsity_scale: None,
shared_kv_layers: Some(10),
per_layer_input_embedding_dim: None,
altup_num_inputs: None,
altup_active_idx: None,
}),
..Llama3Config::llama3_8b()
}
}
#[test]
fn test_kv_from_start_layers_non_gemma3n() {
let c = Llama3Config::llama3_8b();
assert_eq!(c.kv_from_start_layers(), c.num_layers);
}
#[test]
fn test_kv_from_start_layers_gemma3n_e2b() {
let c = gemma3n_e2b_config();
assert_eq!(c.kv_from_start_layers(), 20);
}
#[test]
fn test_kv_source_layer_identity_for_roots() {
let c = gemma3n_e2b_config();
for i in 0..20 {
assert_eq!(c.kv_source_layer(i), i, "layer {i} should own its KV");
}
}
#[test]
fn test_kv_source_layer_shared_layers_gemma3n() {
let c = gemma3n_e2b_config();
assert_eq!(c.kv_source_layer(20), 18, "layer 20 (SWA) should map to 18");
assert_eq!(
c.kv_source_layer(24),
19,
"layer 24 (full) should map to 19"
);
assert_eq!(
c.kv_source_layer(29),
19,
"layer 29 (full) should map to 19"
);
for i in 21..24 {
assert_eq!(c.kv_source_layer(i), 18, "layer {i} (SWA) should map to 18");
}
}
#[test]
fn test_kv_source_layer_non_gemma3n_identity() {
let c = Llama3Config::llama3_8b();
for i in 0..c.num_layers {
assert_eq!(c.kv_source_layer(i), i);
}
}
#[test]
fn test_build_kv_layer_map_gemma3n() {
let c = gemma3n_e2b_config();
let map = c.build_kv_layer_map();
assert_eq!(map.len(), 30);
for (i, &m) in map.iter().enumerate().take(20) {
assert_eq!(m, i);
}
assert_eq!(map[20], 18); assert_eq!(map[24], 19); assert_eq!(map[29], 19); }
#[test]
fn test_kv_cache_shared_read_uses_source_layer() {
let mut cache = KvCache::new(30, 8, 4);
let config = gemma3n_e2b_config();
cache.set_layer_map(config.build_kv_layer_map());
let k18 = vec![1.0, 2.0, 3.0, 4.0];
let v18 = vec![10.0, 20.0, 30.0, 40.0];
cache.append(18, &k18, &v18);
let k19 = vec![5.0, 6.0, 7.0, 8.0];
let v19 = vec![50.0, 60.0, 70.0, 80.0];
cache.append(19, &k19, &v19);
let sink_k = vec![99.0; 4];
let sink_v = vec![99.0; 4];
cache.append(20, &sink_k, &sink_v);
cache.advance();
assert_eq!(cache.key_at(20, 0), k18.as_slice());
assert_eq!(cache.value_at(20, 0), v18.as_slice());
assert_eq!(cache.key_at(24, 0), k19.as_slice());
assert_eq!(cache.value_at(24, 0), v19.as_slice());
assert_eq!(cache.key_at(18, 0), k18.as_slice());
}
#[test]
fn test_kv_cache_default_identity_map() {
let mut cache = KvCache::new(3, 4, 2);
let k0 = vec![1.0, 2.0];
let k1 = vec![3.0, 4.0];
let k2 = vec![5.0, 6.0];
cache.append(0, &k0, &k0);
cache.append(1, &k1, &k1);
cache.append(2, &k2, &k2);
cache.advance();
assert_eq!(cache.key_at(0, 0), k0.as_slice());
assert_eq!(cache.key_at(1, 0), k1.as_slice());
assert_eq!(cache.key_at(2, 0), k2.as_slice());
}
#[test]
fn test_q5_1_dequant_zero_block() {
let data = vec![0u8; 24];
let mut out = vec![0.0f32; 32];
crate::gguf::dequantize_q5_1(&data, &mut out);
for &v in &out {
assert_eq!(v, 0.0);
}
}
#[test]
fn test_q5_1_dequant_constant_block() {
let mut data = vec![0u8; 24];
data[2] = 0x00;
data[3] = 0x3C;
let mut out = vec![0.0f32; 32];
crate::gguf::dequantize_q5_1(&data, &mut out);
for &v in &out {
assert!((v - 1.0).abs() < 1e-4, "expected 1.0, got {v}");
}
}
#[test]
fn test_ggml_type_q5_1_block_layout() {
use crate::gguf::GgmlType;
assert_eq!(GgmlType::Q5_1.block_bytes(), 24);
assert_eq!(GgmlType::Q5_1.elements_per_block(), 32);
}
const F16_ONE_LE: [u8; 2] = [0x00, 0x3C];
#[test]
fn test_ggml_type_q4_0_layout() {
use crate::gguf::GgmlType;
assert_eq!(GgmlType::Q4_0.block_bytes(), 18);
assert_eq!(GgmlType::Q4_0.elements_per_block(), 32);
}
#[test]
fn test_ggml_type_q4_1_layout() {
use crate::gguf::GgmlType;
assert_eq!(GgmlType::Q4_1.block_bytes(), 20);
assert_eq!(GgmlType::Q4_1.elements_per_block(), 32);
}
#[test]
fn test_ggml_type_q5_0_layout() {
use crate::gguf::GgmlType;
assert_eq!(GgmlType::Q5_0.block_bytes(), 22);
assert_eq!(GgmlType::Q5_0.elements_per_block(), 32);
}
#[test]
fn test_ggml_type_iq4_xs_layout() {
use crate::gguf::GgmlType;
assert_eq!(GgmlType::IQ4_XS.block_bytes(), 136);
assert_eq!(GgmlType::IQ4_XS.elements_per_block(), 256);
}
#[test]
fn test_q4_0_dequant_zero_block() {
let data = vec![0u8; 18];
let mut out = vec![0.0f32; 32];
crate::gguf::dequantize_q4_0(&data, &mut out);
for &v in &out {
assert_eq!(v, 0.0);
}
}
#[test]
fn test_q4_0_dequant_signed_range() {
let mut data = vec![0u8; 18];
data[0] = F16_ONE_LE[0];
data[1] = F16_ONE_LE[1];
data[2] = 0x80; let mut out = vec![0.0f32; 32];
crate::gguf::dequantize_q4_0(&data, &mut out);
assert!((out[0] - (-8.0)).abs() < 1e-4);
assert!((out[16] - 0.0).abs() < 1e-4);
assert!((out[1] - (-8.0)).abs() < 1e-4);
assert!((out[17] - (-8.0)).abs() < 1e-4);
}
#[test]
fn test_q4_1_dequant_min_offset() {
let mut data = vec![0u8; 20];
data[2] = F16_ONE_LE[0];
data[3] = F16_ONE_LE[1];
let mut out = vec![0.0f32; 32];
crate::gguf::dequantize_q4_1(&data, &mut out);
for &v in &out {
assert!((v - 1.0).abs() < 1e-4, "expected 1.0, got {v}");
}
}
#[test]
fn test_q4_1_dequant_unsigned_range() {
let mut data = vec![0u8; 20];
data[0] = F16_ONE_LE[0];
data[1] = F16_ONE_LE[1];
data[4] = 0xF3;
let mut out = vec![0.0f32; 32];
crate::gguf::dequantize_q4_1(&data, &mut out);
assert!((out[0] - 3.0).abs() < 1e-4);
assert!((out[16] - 15.0).abs() < 1e-4);
}
#[test]
fn test_q5_0_dequant_signed_5bit_range() {
let mut data = vec![0u8; 22];
data[0] = F16_ONE_LE[0];
data[1] = F16_ONE_LE[1];
data[6] = 0x0F;
let mut out = vec![0.0f32; 32];
crate::gguf::dequantize_q5_0(&data, &mut out);
assert!((out[0] - (-1.0)).abs() < 1e-4);
assert!((out[16] - (-16.0)).abs() < 1e-4);
}
#[test]
fn test_q5_0_dequant_high_bit() {
let mut data = vec![0u8; 22];
data[0] = F16_ONE_LE[0];
data[1] = F16_ONE_LE[1];
data[2] = 0x01; data[6] = 0x0F;
let mut out = vec![0.0f32; 32];
crate::gguf::dequantize_q5_0(&data, &mut out);
assert!((out[0] - 15.0).abs() < 1e-4);
}
#[test]
fn test_iq4_xs_dequant_zero_block() {
let data = vec![0u8; 136];
let mut out = vec![0.0f32; 256];
crate::gguf::dequantize_iq4_xs(&data, &mut out);
for &v in &out {
assert_eq!(v, 0.0);
}
}
#[test]
fn test_iq4_xs_dequant_lookup_table() {
let mut data = vec![0u8; 136];
data[0] = F16_ONE_LE[0];
data[1] = F16_ONE_LE[1];
let mut out = vec![0.0f32; 256];
crate::gguf::dequantize_iq4_xs(&data, &mut out);
for &v in out.iter().take(32) {
assert!((v - 4064.0).abs() < 1.0, "expected ~4064, got {v}");
}
}
#[test]
fn test_qwen35_config_defaults_none() {
let c = Llama3Config::llama3_8b();
assert!(c.ssm_inner_size().is_none());
assert!(c.ssm_state_size().is_none());
assert!(c.ssm_group_count().is_none());
assert!(c.ssm_time_step_rank().is_none());
assert!(c.n_layer_nextn().is_none());
}
fn qwen35_hybrid_config() -> Llama3Config {
Llama3Config {
arch: ModelArch::Qwen3_5,
num_layers: 64,
ssm: Some(SsmDeltaNetConfig {
full_attention_interval: Some(4),
linear_num_kv_heads: None,
linear_qk_head_dim: None,
linear_kv_head_dim: None,
linear_num_v_heads: None,
linear_conv_kernel_dim: None,
ssm_inner_size: Some(6144),
ssm_state_size: Some(128),
ssm_group_count: Some(16),
ssm_time_step_rank: Some(48),
n_layer_nextn: None,
}),
..Llama3Config::llama3_8b()
}
}
#[test]
fn test_qwen35_is_hybrid() {
let c = qwen35_hybrid_config();
assert!(
c.is_hybrid(),
"Qwen 3.5/3.6 with full_attention_interval is hybrid"
);
}
#[test]
fn test_qwen35_is_deltanet_layer_pattern() {
let c = qwen35_hybrid_config();
assert!(c.is_deltanet_layer(0), "layer 0 should be DeltaNet");
assert!(c.is_deltanet_layer(1), "layer 1 should be DeltaNet");
assert!(c.is_deltanet_layer(2), "layer 2 should be DeltaNet");
assert!(
!c.is_deltanet_layer(3),
"layer 3 (i+1 % 4 == 0) should be full"
);
assert!(c.is_deltanet_layer(4), "layer 4 should be DeltaNet again");
assert!(!c.is_deltanet_layer(7), "layer 7 should be full");
}
#[test]
fn test_qwen35_pure_attention_not_hybrid() {
let c = Llama3Config {
arch: ModelArch::Qwen3,
..Llama3Config::llama3_8b()
};
assert!(!c.is_hybrid());
}
#[test]
fn test_qwen35_use_neox_rope() {
assert!(ModelArch::Qwen3_5.use_neox_rope());
}
#[test]
fn test_greedy_argmax_basic() {
let logits = [0.1_f32, 0.5, 0.3, 0.7, 0.2];
assert_eq!(greedy_argmax(&logits), 3);
}
#[test]
fn test_greedy_argmax_first_wins_on_tie() {
let logits = [0.7_f32, 0.5, 0.7, 0.3];
assert_eq!(greedy_argmax(&logits), 0);
}
#[test]
fn test_greedy_argmax_single_element() {
let logits = [42.0_f32];
assert_eq!(greedy_argmax(&logits), 0);
}
#[test]
fn test_greedy_argmax_negative_values() {
let logits = [-3.0_f32, -1.5, -2.0, -0.5];
assert_eq!(greedy_argmax(&logits), 3);
}
#[test]
fn test_speculative_result_acceptance_rate_empty() {
let r = SpeculativeResult {
tokens: vec![],
draft_tokens_produced: 0,
draft_tokens_accepted: 0,
bonus_tokens: 0,
};
assert_eq!(r.acceptance_rate(), 0.0);
}
#[test]
fn test_speculative_result_acceptance_rate_full() {
let r = SpeculativeResult {
tokens: vec![1, 2, 3, 4, 5],
draft_tokens_produced: 4,
draft_tokens_accepted: 4,
bonus_tokens: 1,
};
assert!((r.acceptance_rate() - 1.0).abs() < 1e-6);
}
#[test]
fn test_speculative_result_acceptance_rate_partial() {
let r = SpeculativeResult {
tokens: vec![1, 2, 3],
draft_tokens_produced: 8,
draft_tokens_accepted: 2,
bonus_tokens: 1,
};
assert!((r.acceptance_rate() - 0.25).abs() < 1e-6);
}
#[test]
fn test_splitmix64_deterministic() {
let mut a = SplitMix64::new(42);
let mut b = SplitMix64::new(42);
for _ in 0..10 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn test_splitmix64_next_unit_f32_range() {
let mut rng = SplitMix64::new(7);
for _ in 0..1000 {
let x = rng.next_unit_f32();
assert!((0.0..1.0).contains(&x), "sample {x} out of [0, 1)");
}
}
#[test]
fn test_apply_temperature_and_top_p_uniform_at_high_temp() {
let logits = [1.0f32, 2.0, 3.0, 4.0];
let dist = apply_temperature_and_top_p(&logits, 1000.0, None);
let sum: f32 = dist.iter().sum();
assert!((sum - 1.0).abs() < 1e-4);
for &p in &dist {
assert!((p - 0.25).abs() < 0.01, "prob {p} not near uniform");
}
}
#[test]
fn test_apply_temperature_and_top_p_argmax_at_low_temp() {
let logits = [1.0f32, 5.0, 2.0, 3.0];
let dist = apply_temperature_and_top_p(&logits, 0.001, None);
assert!(dist[1] > 0.999, "argmax prob {} not near 1", dist[1]);
for (i, &p) in dist.iter().enumerate() {
if i != 1 {
assert!(p < 1e-3, "non-argmax prob {p} at {i} too high");
}
}
}
#[test]
fn test_apply_temperature_and_top_p_sums_to_one() {
let logits = [0.5f32, -1.0, 2.5, 0.1, -0.3];
let dist = apply_temperature_and_top_p(&logits, 1.0, None);
let sum: f32 = dist.iter().sum();
assert!((sum - 1.0).abs() < 1e-5);
}
#[test]
fn test_apply_temperature_and_top_p_filter_keeps_head() {
let logits = [-0.693f32, -1.204, -1.897, -2.996];
let dist = apply_temperature_and_top_p(&logits, 1.0, Some(0.7));
assert!(dist[0] > 0.0);
assert!(dist[1] > 0.0);
assert!(dist[2].abs() < 1e-6, "tail should be zero, got {}", dist[2]);
assert!(dist[3].abs() < 1e-6, "tail should be zero, got {}", dist[3]);
let sum: f32 = dist.iter().sum();
assert!((sum - 1.0).abs() < 1e-4);
}
#[test]
fn test_sample_multinomial_respects_zero_prob() {
let dist = [0.0f32, 0.0, 1.0, 0.0];
let mut rng = SplitMix64::new(1234);
for _ in 0..100 {
assert_eq!(sample_multinomial(&dist, &mut rng), 2);
}
}
#[test]
fn test_sample_multinomial_matches_expected_frequency() {
let dist = [0.5f32, 0.5, 0.0, 0.0];
let mut rng = SplitMix64::new(1);
let mut counts = [0usize; 4];
for _ in 0..10_000 {
let idx = sample_multinomial(&dist, &mut rng) as usize;
counts[idx] += 1;
}
assert!(counts[0].abs_diff(5000) < 300, "counts[0]={}", counts[0]);
assert!(counts[1].abs_diff(5000) < 300, "counts[1]={}", counts[1]);
assert_eq!(counts[2], 0);
assert_eq!(counts[3], 0);
}
#[test]
fn test_residual_dist_basic() {
let p_main = [0.5f32, 0.3, 0.15, 0.05];
let p_draft = [0.1f32, 0.4, 0.4, 0.1];
let r = residual_dist(&p_main, &p_draft);
assert!((r[0] - 1.0).abs() < 1e-5);
assert!(r[1].abs() < 1e-6);
assert!(r[2].abs() < 1e-6);
assert!(r[3].abs() < 1e-6);
}
#[test]
fn test_residual_dist_degenerate_falls_back_to_main() {
let p = [0.25f32, 0.25, 0.25, 0.25];
let r = residual_dist(&p, &p);
for (i, &v) in r.iter().enumerate() {
assert!((v - 0.25).abs() < 1e-5, "residual[{i}]={v} != 0.25");
}
}
#[test]
fn test_speculative_config_default() {
let cfg = SpeculativeConfig::default();
assert_eq!(cfg.n_draft, 4);
assert_eq!(cfg.max_new_tokens, 128);
assert!(cfg.temperature.is_none());
assert!(cfg.top_p.is_none());
assert!(cfg.sample_seed.is_none());
}
#[test]
fn causal_conv1d_step_matches_direct_convolution() {
let dim = 1;
let kernel = 4;
let weight: Vec<f32> = vec![1.0, 2.0, 3.0, 4.0]; let bias: Vec<f32> = vec![0.0];
let inputs: Vec<f32> = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let mut state = vec![0.0f32; (kernel - 1) * dim];
let mut ring_pos = 0usize;
let mut outputs = Vec::with_capacity(inputs.len());
for &x in &inputs {
let mut out = vec![0.0f32; dim];
causal_conv1d_step(
&[x],
&mut state,
&mut ring_pos,
&weight,
&bias,
&mut out,
dim,
kernel,
);
outputs.push(out[0]);
}
assert_eq!(outputs, vec![4.0, 11.0, 20.0, 30.0, 40.0]);
}
#[test]
fn causal_conv1d_ring_buffer_slides_across_steps() {
let dim = 2;
let kernel = 4;
let weight: Vec<f32> = vec![1.0; kernel * dim];
let bias: Vec<f32> = vec![0.0; dim];
let inputs = [
vec![1.0f32, 10.0],
vec![2.0, 20.0],
vec![3.0, 30.0],
vec![4.0, 40.0],
vec![5.0, 50.0],
];
let mut state = vec![0.0f32; (kernel - 1) * dim];
let mut ring_pos = 0usize;
let expected_ch0 = [1.0f32, 3.0, 6.0, 10.0, 14.0];
let expected_ch1 = [10.0f32, 30.0, 60.0, 100.0, 140.0];
for (i, x) in inputs.iter().enumerate() {
let mut out = vec![0.0f32; dim];
causal_conv1d_step(
x,
&mut state,
&mut ring_pos,
&weight,
&bias,
&mut out,
dim,
kernel,
);
assert!(
(out[0] - expected_ch0[i]).abs() < 1e-6,
"step {i} ch0: got {} expected {}",
out[0],
expected_ch0[i]
);
assert!(
(out[1] - expected_ch1[i]).abs() < 1e-6,
"step {i} ch1: got {} expected {}",
out[1],
expected_ch1[i]
);
}
}
#[test]
fn causal_conv1d_step_weight_layout_dim_outer_kernel_inner() {
let dim = 2;
let kernel = 4;
let weight: Vec<f32> = vec![1.0, 2.0, 3.0, 4.0, 10.0, 20.0, 30.0, 40.0];
let bias: Vec<f32> = vec![0.0, 0.0];
let inputs = [
vec![1.0f32, 100.0],
vec![1.0, 100.0],
vec![1.0, 100.0],
vec![1.0, 100.0], ];
let mut state = vec![0.0f32; (kernel - 1) * dim];
let mut ring_pos = 0usize;
let mut out = vec![0.0f32; dim];
for x in &inputs {
causal_conv1d_step(
x,
&mut state,
&mut ring_pos,
&weight,
&bias,
&mut out,
dim,
kernel,
);
}
assert!(
(out[0] - 10.0).abs() < 1e-5,
"ch0 expected 10.0 got {} (weight indexing likely transposed)",
out[0]
);
assert!(
(out[1] - 10_000.0).abs() < 1e-3,
"ch1 expected 10000.0 got {} (weight indexing likely transposed)",
out[1]
);
}
#[test]
fn gated_deltanet_state_absorbs_current_when_alpha_zero_beta_one() {
let num_heads = 1;
let qk_dim = 2;
let v_dim = 2;
let q = vec![1.0, 0.0]; let k = vec![0.5, 0.5]; let v = vec![1.0, -1.0];
let alpha = vec![0.0];
let beta = vec![1.0];
let z = vec![10.0, -10.0];
let mut state = vec![0.0f32; num_heads * qk_dim * v_dim];
let mut out = vec![0.0f32; num_heads * v_dim];
gated_deltanet_step(
&q, &k, &v, &alpha, &beta, &z, &mut state, &mut out, num_heads, num_heads, qk_dim,
v_dim, false,
);
assert!(state.iter().all(|v| v.is_finite()));
assert!(state.iter().any(|&v| v.abs() > 0.0));
assert!(out.iter().all(|v| v.is_finite()));
assert!(out[1].abs() < 1e-3);
}
#[test]
fn gated_deltanet_step_handles_zero_input_without_nan() {
let num_heads = 1;
let qk_dim = 4;
let v_dim = 4;
let q = vec![0.0f32; qk_dim];
let k = vec![0.0f32; qk_dim];
let v = vec![1.0f32; v_dim];
let alpha = vec![0.5];
let beta = vec![0.5];
let z = vec![0.0f32; v_dim];
let mut state = vec![0.0f32; num_heads * qk_dim * v_dim];
let mut out = vec![0.0f32; num_heads * v_dim];
gated_deltanet_step(
&q, &k, &v, &alpha, &beta, &z, &mut state, &mut out, num_heads, num_heads, qk_dim,
v_dim, false,
);
assert!(state.iter().all(|v| v.is_finite()));
assert!(out.iter().all(|v| v.is_finite()));
}
#[test]
fn gated_deltanet_step_parallel_matches_serial() {
let num_heads = 16;
let qk_dim = 3;
let v_dim = 3;
let mut q = Vec::with_capacity(num_heads * qk_dim);
let mut k = Vec::with_capacity(num_heads * qk_dim);
let mut v = Vec::with_capacity(num_heads * v_dim);
let mut z = Vec::with_capacity(num_heads * v_dim);
for h in 0..num_heads {
for i in 0..qk_dim {
let seed = (h * qk_dim + i) as f32 * 0.3;
q.push(seed.sin());
k.push(seed.cos());
}
for j in 0..v_dim {
let seed = (h * v_dim + j) as f32 * 0.5;
v.push(seed.sin());
z.push(seed.cos());
}
}
let alpha: Vec<f32> = (0..num_heads).map(|h| 0.9 - 0.01 * h as f32).collect();
let beta: Vec<f32> = (0..num_heads).map(|h| 0.1 + 0.01 * h as f32).collect();
let mut state_serial = vec![0.0f32; num_heads * qk_dim * v_dim];
let mut out_serial = vec![0.0f32; num_heads * v_dim];
for head in 0..num_heads {
gated_deltanet_head(
&q,
&k,
&v,
&alpha,
&beta,
&z,
&mut state_serial,
&mut out_serial,
head,
qk_dim,
v_dim,
);
}
let mut state_actual = vec![0.0f32; num_heads * qk_dim * v_dim];
let mut out_actual = vec![0.0f32; num_heads * v_dim];
gated_deltanet_step(
&q,
&k,
&v,
&alpha,
&beta,
&z,
&mut state_actual,
&mut out_actual,
num_heads,
num_heads,
qk_dim,
v_dim,
false,
);
for (i, (a, s)) in out_actual.iter().zip(out_serial.iter()).enumerate() {
assert!((a - s).abs() < 1e-6, "out[{i}]: parallel {a} serial {s}");
}
for (i, (a, s)) in state_actual.iter().zip(state_serial.iter()).enumerate() {
assert!((a - s).abs() < 1e-6, "state[{i}]: parallel {a} serial {s}");
}
}
#[test]
fn gated_deltanet_step_bonsai_per_v_head_consumption() {
let num_kv_heads = 4;
let num_v_heads = 12;
let qk_dim = 3;
let v_dim = 3;
let v_per_kv = num_v_heads / num_kv_heads;
let state_stride = qk_dim * v_dim;
let mut q = Vec::with_capacity(num_kv_heads * qk_dim);
let mut k = Vec::with_capacity(num_kv_heads * qk_dim);
for h in 0..num_kv_heads {
for i in 0..qk_dim {
let seed = (h * qk_dim + i) as f32 * 0.3;
q.push(seed.sin());
k.push(seed.cos());
}
}
let mut v = Vec::with_capacity(num_v_heads * v_dim);
let mut z = Vec::with_capacity(num_v_heads * v_dim);
for h in 0..num_v_heads {
for j in 0..v_dim {
let seed = (h * v_dim + j) as f32 * 0.5;
v.push(seed.sin() + 0.1);
z.push(seed.cos());
}
}
let alpha: Vec<f32> = (0..num_v_heads).map(|h| 0.9 - 0.02 * h as f32).collect();
let beta: Vec<f32> = (0..num_v_heads).map(|h| 0.1 + 0.02 * h as f32).collect();
let mut state = vec![0.0f32; num_v_heads * state_stride];
let mut out = vec![0.0f32; num_v_heads * v_dim];
gated_deltanet_step(
&q,
&k,
&v,
&alpha,
&beta,
&z,
&mut state,
&mut out,
num_kv_heads,
num_v_heads,
qk_dim,
v_dim,
false,
);
assert!(state.iter().all(|val| val.is_finite()));
assert!(out.iter().all(|val| val.is_finite()));
for v_head in 0..num_v_heads {
let slab = &state[v_head * state_stride..(v_head + 1) * state_stride];
assert!(
slab.iter().any(|&val| val.abs() > 0.0),
"V head {v_head} state slab all zero — per-V-head loop did not cover it",
);
}
for kv in 0..num_kv_heads {
let head_a = kv * v_per_kv;
let head_b = kv * v_per_kv + 1;
let slab_a = &state[head_a * state_stride..(head_a + 1) * state_stride];
let slab_b = &state[head_b * state_stride..(head_b + 1) * state_stride];
let max_diff = slab_a
.iter()
.zip(slab_b.iter())
.map(|(a, b)| (a - b).abs())
.fold(0.0f32, f32::max);
assert!(
max_diff > 1e-6,
"KV group {kv}: V head {head_a} state ≈ V head {head_b} state (max_diff={max_diff}), per-V-head alpha/beta collapsed",
);
}
}
#[test]
fn ssm_norm_broadcasts_across_v_heads() {
let num_v_heads = 3;
let v_dim = 4;
let eps = 1e-5f32;
let mut buf = vec![
0.1, -0.2, 0.3, -0.4, 2.0, -3.0, 4.0, -5.0, -1.0, 0.5, 1.5, -0.5, ];
let weight = vec![1.0, -0.5, 2.0, 0.25];
let mut expected = Vec::with_capacity(num_v_heads * v_dim);
for h in 0..num_v_heads {
let slice = &buf[h * v_dim..(h + 1) * v_dim];
let ss: f64 = slice.iter().map(|&v| (v as f64) * (v as f64)).sum();
let mean = (ss / v_dim as f64) as f32;
let scale = 1.0f32 / (mean + eps).sqrt();
for i in 0..v_dim {
expected.push(slice[i] * scale * weight[i]);
}
}
apply_qk_norm(&mut buf, &weight, v_dim, eps);
for (i, (actual, expected)) in buf.iter().zip(expected.iter()).enumerate() {
let head = i / v_dim;
let lane = i % v_dim;
assert!(
(actual - expected).abs() < 1e-6,
"head {head} lane {lane}: got {actual}, expected {expected}",
);
}
}
#[test]
fn softplus_matches_reference() {
assert!(
(softplus(0.0) - 0.6931472_f32).abs() < 1e-6,
"softplus(0) = {}",
softplus(0.0)
);
assert!(
(softplus(1.0) - 1.3132617_f32).abs() < 1e-5,
"softplus(1) = {}",
softplus(1.0)
);
assert!(
(softplus(-1.0) - 0.3132617_f32).abs() < 1e-5,
"softplus(-1) = {}",
softplus(-1.0)
);
assert!((softplus(30.0) - 30.0_f32).abs() < 1e-6);
assert!(softplus(-30.0).abs() < 1e-10);
for &x in &[-100.0_f32, -50.0, -20.0, -1.0, 0.0, 1.0, 20.0, 50.0, 100.0] {
let y = softplus(x);
assert!(y.is_finite(), "softplus({x}) = {y} not finite");
assert!(y >= 0.0, "softplus({x}) = {y} negative");
}
}
#[test]
fn ssm_alpha_transformation_matches_reference() {
let decay = (-softplus(0.0 + 0.0)).exp();
assert!(
(decay - 0.5_f32).abs() < 1e-6,
"anchor 1: decay = {decay}, expected 0.5",
);
let decay2 = (softplus(10.0) * -2.0_f32).exp();
assert!(
decay2 < 0.01 && decay2 > 0.0,
"anchor 2: decay = {decay2}, expected tiny positive",
);
let decay3 = (-softplus(-30.0)).exp();
assert!(
(decay3 - 1.0_f32).abs() < 1e-6,
"anchor 3: decay = {decay3}, expected 1",
);
for &alpha in &[-100.0_f32, -10.0, -1.0, 0.0, 1.0, 10.0, 100.0] {
for &dt_bias in &[-5.0_f32, 0.0, 5.0] {
for &ssm_a_val in &[-5.0_f32, -1.0, -0.1] {
let gate = softplus(alpha + dt_bias) * ssm_a_val;
let decay = gate.exp();
assert!(
decay.is_finite(),
"α={alpha}, dt_b={dt_bias}, a={ssm_a_val}: decay={decay} not finite",
);
assert!(
(0.0..=1.0 + 1e-6).contains(&decay),
"α={alpha}, dt_b={dt_bias}, a={ssm_a_val}: decay={decay} outside [0, 1]",
);
}
}
}
}
#[test]
fn sigmoid_matches_reference() {
assert!((sigmoid(0.0) - 0.5_f32).abs() < 1e-6);
for &x in &[-5.0_f32, -1.0, 0.5, 2.0, 8.0] {
let s = sigmoid(x) + sigmoid(-x);
assert!(
(s - 1.0_f32).abs() < 1e-6,
"sigmoid({x}) + sigmoid({}) = {s}, expected 1",
-x,
);
}
assert!((sigmoid(30.0) - 1.0_f32).abs() < 1e-6);
assert!(sigmoid(-30.0).abs() < 1e-6);
for &x in &[-100.0_f32, -20.0, -1.0, 0.0, 1.0, 20.0, 100.0] {
let y = sigmoid(x);
assert!(
y.is_finite() && (0.0..=1.0).contains(&y),
"sigmoid({x}) = {y}"
);
}
for &x in &[-2.0_f32, -0.5, 0.0, 0.5, 2.0] {
let silu_ref = x * sigmoid(x);
let silu_direct = silu(x);
assert!(
(silu_ref - silu_direct).abs() < 1e-6,
"silu({x}) inconsistent with x * sigmoid(x)",
);
}
}
#[test]
fn ssm_beta_sigmoid_applied_per_v_head() {
let num_v_heads = 5;
let mut dn_beta = [-8.0_f32, -1.0, 0.0, 2.5, 10.0];
for h in 0..num_v_heads {
dn_beta[h] = sigmoid(dn_beta[h]);
}
for (h, &val) in dn_beta.iter().enumerate() {
assert!(
val.is_finite() && val > 0.0 && val < 1.0,
"head {h}: sigmoid output {val} outside (0, 1)",
);
}
for pair in dn_beta.windows(2) {
assert!(pair[0] < pair[1], "sigmoid not monotonic: {pair:?}");
}
assert!((dn_beta[2] - 0.5_f32).abs() < 1e-6);
}
#[test]
fn gated_deltanet_step_bonsai_semantics_toggle() {
let num_heads = 1;
let qk_dim = 4;
let v_dim = 4;
let q = vec![0.7f32, -0.3, 0.5, 0.2];
let k = vec![0.6f32, 0.4, -0.2, 0.8];
let v = vec![0.1f32, -0.5, 0.3, 0.7];
let alpha = vec![0.5f32];
let beta = vec![0.4f32];
let z = vec![1.5f32, -1.0, 0.5, 2.0];
let mut state_legacy = vec![0.0f32; num_heads * qk_dim * v_dim];
let mut out_legacy = vec![0.0f32; num_heads * v_dim];
gated_deltanet_step(
&q,
&k,
&v,
&alpha,
&beta,
&z,
&mut state_legacy,
&mut out_legacy,
num_heads,
num_heads,
qk_dim,
v_dim,
false,
);
let mut state_bonsai = vec![0.0f32; num_heads * qk_dim * v_dim];
let mut out_bonsai = vec![0.0f32; num_heads * v_dim];
gated_deltanet_step(
&q,
&k,
&v,
&alpha,
&beta,
&z,
&mut state_bonsai,
&mut out_bonsai,
num_heads,
num_heads,
qk_dim,
v_dim,
true,
);
for &val in out_legacy.iter().chain(out_bonsai.iter()) {
assert!(val.is_finite(), "non-finite value in output");
}
let max_diff = out_legacy
.iter()
.zip(out_bonsai.iter())
.map(|(a, b)| (a - b).abs())
.fold(0.0f32, f32::max);
assert!(
max_diff > 1e-4,
"legacy vs Bonsai output max_diff={max_diff}, expected > 1e-4",
);
let z_alt = vec![-3.0f32, 5.0, -0.5, 7.5];
let mut state_bonsai_alt = vec![0.0f32; num_heads * qk_dim * v_dim];
let mut out_bonsai_alt = vec![0.0f32; num_heads * v_dim];
gated_deltanet_step(
&q,
&k,
&v,
&alpha,
&beta,
&z_alt,
&mut state_bonsai_alt,
&mut out_bonsai_alt,
num_heads,
num_heads,
qk_dim,
v_dim,
true,
);
for (i, (a, b)) in out_bonsai.iter().zip(out_bonsai_alt.iter()).enumerate() {
assert!(
(a - b).abs() < 1e-6,
"Bonsai output must be z-independent, lane {i}: {a} vs {b}",
);
}
}
fn empty_layer_weights<'a>() -> LayerWeights<'a> {
static ZERO: [u8; 256] = [0u8; 256];
let core_weight = WeightRef {
data: &ZERO,
qtype: crate::gguf::GgmlType::F32,
rows: 0,
cols: 0,
};
LayerWeights {
attn_norm: Vec::new(),
q_proj: core_weight.clone(),
k_proj: None,
v_proj: None,
o_proj: core_weight,
ffn_norm: Vec::new(),
gate_proj: None,
up_proj: None,
down_proj: None,
post_attn_norm: None,
post_ffn_norm: None,
out_scale: None,
rope_freqs: None,
qwen_biases: None,
qwen_norms: None,
gemma3n: None,
moe: None,
gated_output: false,
}
}
#[test]
fn layer_weights_accessors_return_none_when_sub_structs_absent() {
let lw = empty_layer_weights();
assert!(lw.q_bias().is_none());
assert!(lw.k_bias().is_none());
assert!(lw.v_bias().is_none());
assert!(lw.q_norm().is_none());
assert!(lw.k_norm().is_none());
assert!(lw.post_norm().is_none());
assert!(lw.laurel_l().is_none());
assert!(lw.laurel_r().is_none());
assert!(lw.laurel_post_norm().is_none());
assert!(lw.altup_router().is_none());
assert!(lw.altup_router_norm().is_none());
assert!(lw.altup_predict_coef().is_none());
assert!(lw.altup_correct_coef().is_none());
assert!(lw.altup_correct_scale().is_none());
assert!(lw.inp_gate().is_none());
assert!(lw.proj().is_none());
assert!(lw.ffn_gate_inp().is_none());
assert!(lw.ffn_gate_exps().is_none());
assert!(lw.ffn_up_exps().is_none());
assert!(lw.ffn_down_exps().is_none());
assert!(!lw.is_moe_layer());
}
#[test]
fn layer_weights_qwen_biases_visible_through_accessors() {
let mut lw = empty_layer_weights();
lw.qwen_biases = Some(QwenAttentionBiases {
q_bias: vec![1.0, 2.0],
k_bias: vec![3.0, 4.0],
v_bias: vec![5.0, 6.0],
});
assert_eq!(lw.q_bias(), Some(&[1.0f32, 2.0][..]));
assert_eq!(lw.k_bias(), Some(&[3.0f32, 4.0][..]));
assert_eq!(lw.v_bias(), Some(&[5.0f32, 6.0][..]));
assert!(lw.q_norm().is_none());
assert!(lw.k_norm().is_none());
}
#[test]
fn layer_weights_qwen_norms_visible_through_accessors() {
let mut lw = empty_layer_weights();
lw.qwen_norms = Some(QwenAttentionNorms {
q_norm: vec![0.1, 0.2],
k_norm: vec![0.3, 0.4],
});
assert_eq!(lw.q_norm(), Some(&[0.1f32, 0.2][..]));
assert_eq!(lw.k_norm(), Some(&[0.3f32, 0.4][..]));
assert!(lw.q_bias().is_none());
}
#[test]
fn layer_weights_moe_visible_through_accessors_and_flag() {
static ZERO: [u8; 256] = [0u8; 256];
let expert_weight = WeightRef {
data: &ZERO,
qtype: crate::gguf::GgmlType::F32,
rows: 0,
cols: 0,
};
let mut lw = empty_layer_weights();
lw.moe = Some(MoeExpertWeights {
ffn_gate_inp: vec![9.0, 8.0, 7.0],
ffn_gate_exps: expert_weight.clone(),
ffn_up_exps: expert_weight.clone(),
ffn_down_exps: expert_weight,
});
assert_eq!(lw.ffn_gate_inp(), Some(&[9.0f32, 8.0, 7.0][..]));
assert!(lw.ffn_gate_exps().is_some());
assert!(lw.ffn_up_exps().is_some());
assert!(lw.ffn_down_exps().is_some());
assert!(lw.is_moe_layer());
}
#[test]
fn layer_weights_arch_groups_are_independent() {
let mut lw = empty_layer_weights();
lw.qwen_biases = Some(QwenAttentionBiases {
q_bias: vec![0.0],
k_bias: vec![0.0],
v_bias: vec![0.0],
});
assert!(lw.laurel_l().is_none());
assert!(lw.altup_router().is_none());
assert!(lw.ffn_gate_inp().is_none());
assert!(!lw.is_moe_layer());
}
#[test]
fn config_baseline_llama_has_no_arch_extras() {
let c = Llama3Config::llama3_8b();
assert!(c.attention_extras.is_none());
assert!(c.ssm.is_none());
assert!(c.moe.is_none());
assert!(c.gemma3n.is_none());
assert!(c.gemma4.is_none());
assert!(c.sliding_window().is_none());
assert!(c.attn_logit_softcap().is_none());
assert!(c.final_logit_softcap().is_none());
assert!(c.full_attention_interval().is_none());
assert!(c.linear_qk_head_dim().is_none());
assert!(c.ssm_inner_size().is_none());
assert!(c.num_experts().is_none());
assert!(c.num_experts_active().is_none());
assert!(c.expert_ffn_size().is_none());
assert!(c.altup_num_inputs().is_none());
assert!(c.altup_active_idx().is_none());
assert!(c.shared_kv_layers().is_none());
assert!(c.per_layer_input_embedding_dim().is_none());
assert!(c.sliding_window_pattern().is_none());
assert!(c.activation_sparsity_scale().is_none());
assert!(c.head_dim_swa().is_none());
assert!(c.rope_theta_swa().is_none());
assert!(c.rope_dim_swa().is_none());
assert!(c.ffn_size_per_layer().is_none());
assert!(!c.is_hybrid());
}
#[test]
fn config_attention_extras_accessors_read_populated_sub_config() {
let mut c = Llama3Config::llama3_8b();
c.attention_extras = Some(AttentionExtrasConfig {
sliding_window: Some(4096),
attn_logit_softcap: Some(30.0),
final_logit_softcap: Some(30.0),
});
assert_eq!(c.sliding_window(), Some(4096));
assert_eq!(c.attn_logit_softcap(), Some(30.0));
assert_eq!(c.final_logit_softcap(), Some(30.0));
assert!(c.ssm_inner_size().is_none());
assert!(c.num_experts().is_none());
}
#[test]
fn config_ssm_accessors_read_populated_sub_config() {
let mut c = Llama3Config::llama3_8b();
c.ssm = Some(SsmDeltaNetConfig {
full_attention_interval: Some(4),
linear_num_kv_heads: Some(16),
linear_qk_head_dim: Some(128),
linear_kv_head_dim: Some(128),
linear_num_v_heads: Some(32),
linear_conv_kernel_dim: Some(4),
ssm_inner_size: Some(6144),
ssm_state_size: Some(128),
ssm_group_count: Some(16),
ssm_time_step_rank: Some(48),
n_layer_nextn: Some(1),
});
assert_eq!(c.full_attention_interval(), Some(4));
assert_eq!(c.linear_num_kv_heads(), Some(16));
assert_eq!(c.linear_qk_head_dim(), Some(128));
assert_eq!(c.linear_kv_head_dim(), Some(128));
assert_eq!(c.linear_num_v_heads(), Some(32));
assert_eq!(c.linear_conv_kernel_dim(), Some(4));
assert_eq!(c.ssm_inner_size(), Some(6144));
assert_eq!(c.ssm_state_size(), Some(128));
assert_eq!(c.ssm_group_count(), Some(16));
assert_eq!(c.ssm_time_step_rank(), Some(48));
assert_eq!(c.n_layer_nextn(), Some(1));
assert!(c.is_hybrid());
}
#[test]
fn config_moe_accessors_read_populated_sub_config() {
let mut c = Llama3Config::llama3_8b();
c.moe = Some(MoeConfig {
num_experts: Some(8),
num_experts_active: Some(2),
expert_ffn_size: Some(2048),
});
assert_eq!(c.num_experts(), Some(8));
assert_eq!(c.num_experts_active(), Some(2));
assert_eq!(c.expert_ffn_size(), Some(2048));
}
#[test]
fn config_gemma3n_accessors_read_populated_sub_config() {
let mut c = Llama3Config::llama3_8b();
c.gemma3n = Some(Gemma3nConfig {
sliding_window_pattern: Some(vec![true, false, true]),
activation_sparsity_scale: Some(vec![1.5, f32::NEG_INFINITY]),
shared_kv_layers: Some(10),
per_layer_input_embedding_dim: Some(256),
altup_num_inputs: Some(4),
altup_active_idx: Some(0),
});
assert_eq!(c.sliding_window_pattern(), Some(&[true, false, true][..]));
assert_eq!(
c.activation_sparsity_scale(),
Some(&[1.5f32, f32::NEG_INFINITY][..])
);
assert_eq!(c.shared_kv_layers(), Some(10));
assert_eq!(c.per_layer_input_embedding_dim(), Some(256));
assert_eq!(c.altup_num_inputs(), Some(4));
assert_eq!(c.altup_active_idx(), Some(0));
}
#[test]
fn config_gemma4_accessors_read_populated_sub_config() {
let mut c = Llama3Config::llama3_8b();
c.gemma4 = Some(Gemma4Config {
head_dim_swa: Some(64),
rope_theta_swa: Some(10_000.0),
rope_dim_swa: Some(64),
ffn_size_per_layer: Some(vec![6144, 12_288]),
});
assert_eq!(c.head_dim_swa(), Some(64));
assert_eq!(c.rope_theta_swa(), Some(10_000.0));
assert_eq!(c.rope_dim_swa(), Some(64));
assert_eq!(c.ffn_size_per_layer(), Some(&[6144usize, 12_288][..]));
}
fn make_populated_kv_cache(
num_layers: usize,
max_seq_len: usize,
kv_dim: usize,
active_seq_len: usize,
seed: u32,
) -> KvCache {
let mut cache = KvCache::new(num_layers, max_seq_len, kv_dim);
for pos in 0..active_seq_len {
for layer in 0..num_layers {
let k: Vec<f32> = (0..kv_dim)
.map(|i| {
f32::from(
seed.wrapping_add(pos as u32 * 7 + layer as u32 * 3 + i as u32) as u16
as i16,
) * 0.001
})
.collect();
let v: Vec<f32> = (0..kv_dim)
.map(|i| {
f32::from(
seed.wrapping_add(pos as u32 * 11 + layer as u32 * 5 + i as u32) as u16
as i16,
) * 0.001
})
.collect();
cache.append(layer, &k, &v);
}
cache.advance();
}
cache
}
#[test]
fn kv_cache_save_load_roundtrip_bit_exact() {
let num_layers = 4;
let max_seq_len = 16;
let kv_dim = 32;
let active_seq_len = 7;
let cache = make_populated_kv_cache(num_layers, max_seq_len, kv_dim, active_seq_len, 42);
let fingerprint = 0xDEAD_BEEF_CAFE_F00D_u64;
let mut bytes = Vec::new();
cache.write_to(&mut bytes, fingerprint).expect("write ok");
let mut restored = KvCache::new(num_layers, max_seq_len, kv_dim);
restored
.read_from(&mut bytes.as_slice(), fingerprint)
.expect("read ok");
assert_eq!(cache.seq_len, restored.seq_len);
assert_eq!(cache.kv_layer_map, restored.kv_layer_map);
for layer in 0..num_layers {
let n = active_seq_len * kv_dim;
let off = cache.offset(layer, 0);
assert_eq!(&cache.keys[off..off + n], &restored.keys[off..off + n]);
assert_eq!(&cache.values[off..off + n], &restored.values[off..off + n]);
}
}
#[test]
fn kv_cache_load_rejects_bad_magic() {
let mut restored = KvCache::new(4, 16, 32);
let mut buf: Vec<u8> = b"NOTMAGIC".to_vec();
buf.extend_from_slice(&[0u8; 64]);
let err = restored
.read_from(&mut buf.as_slice(), 0)
.expect_err("bad magic must fail");
assert!(
matches!(err, KvCacheLoadError::BadMagic { .. }),
"expected BadMagic, got {err:?}"
);
}
#[test]
fn kv_cache_load_rejects_fingerprint_mismatch() {
let cache = make_populated_kv_cache(2, 8, 16, 3, 7);
let mut bytes = Vec::new();
cache.write_to(&mut bytes, 0x1111).expect("write ok");
let mut restored = KvCache::new(2, 8, 16);
let err = restored
.read_from(&mut bytes.as_slice(), 0x2222)
.expect_err("fingerprint mismatch must fail");
assert!(
matches!(err, KvCacheLoadError::FingerprintMismatch { .. }),
"expected FingerprintMismatch, got {err:?}"
);
}
#[test]
fn kv_cache_load_rejects_shape_mismatch() {
let cache = make_populated_kv_cache(4, 16, 32, 3, 7);
let mut bytes = Vec::new();
cache.write_to(&mut bytes, 0x1234).expect("write ok");
let mut restored = KvCache::new(8, 16, 32);
let err = restored
.read_from(&mut bytes.as_slice(), 0x1234)
.expect_err("shape mismatch must fail");
assert!(
matches!(err, KvCacheLoadError::ShapeMismatch(_)),
"expected ShapeMismatch, got {err:?}"
);
}
#[test]
fn kv_cache_fingerprint_is_deterministic_and_config_sensitive() {
let base = Llama3Config::llama3_8b();
let mut variant = Llama3Config::llama3_8b();
variant.num_layers += 1;
assert_eq!(kv_cache_fingerprint(&base), kv_cache_fingerprint(&base));
assert_ne!(kv_cache_fingerprint(&base), kv_cache_fingerprint(&variant));
}
#[test]
fn deepseek_v3_config_accessors_return_none_when_absent() {
let c = Llama3Config::llama3_8b();
assert!(c.deepseek_v3.is_none());
assert!(c.deepseek_q_lora_rank().is_none());
assert!(c.deepseek_kv_lora_rank().is_none());
assert!(c.deepseek_qk_nope_head_dim().is_none());
assert!(c.deepseek_qk_rope_head_dim().is_none());
assert!(c.deepseek_v_head_dim().is_none());
assert!(c.deepseek_n_routed_experts().is_none());
assert!(c.deepseek_n_shared_experts().is_none());
assert!(c.deepseek_num_experts_per_tok().is_none());
assert!(c.deepseek_moe_intermediate_size().is_none());
assert!(c.deepseek_first_k_dense_replace().is_none());
assert!(c.deepseek_routed_scaling_factor().is_none());
assert!(c.deepseek_noaux_tc().is_none());
assert!(c.deepseek_mtp_layer().is_none());
}
#[test]
fn deepseek_v3_config_reads_typical_v3_values() {
let mut c = Llama3Config::llama3_8b();
c.arch = ModelArch::DeepSeekV3;
c.deepseek_v3 = Some(DeepSeekV3Config {
q_lora_rank: Some(1536),
kv_lora_rank: Some(512),
qk_nope_head_dim: Some(128),
qk_rope_head_dim: Some(64),
v_head_dim: Some(128),
n_routed_experts: Some(256),
n_shared_experts: Some(1),
num_experts_per_tok: Some(8),
moe_intermediate_size: Some(2048),
first_k_dense_replace: Some(3),
routed_scaling_factor: Some(2.5),
noaux_tc: Some(true),
mtp_layer: Some(60),
});
assert_eq!(c.deepseek_q_lora_rank(), Some(1536));
assert_eq!(c.deepseek_kv_lora_rank(), Some(512));
assert_eq!(c.deepseek_qk_nope_head_dim(), Some(128));
assert_eq!(c.deepseek_qk_rope_head_dim(), Some(64));
assert_eq!(c.deepseek_v_head_dim(), Some(128));
assert_eq!(c.deepseek_n_routed_experts(), Some(256));
assert_eq!(c.deepseek_n_shared_experts(), Some(1));
assert_eq!(c.deepseek_num_experts_per_tok(), Some(8));
assert_eq!(c.deepseek_moe_intermediate_size(), Some(2048));
assert_eq!(c.deepseek_first_k_dense_replace(), Some(3));
assert_eq!(c.deepseek_routed_scaling_factor(), Some(2.5));
assert_eq!(c.deepseek_noaux_tc(), Some(true));
assert_eq!(c.deepseek_mtp_layer(), Some(60));
}
#[test]
fn deepseek_v3_arch_uses_neox_rope() {
assert!(ModelArch::DeepSeekV3.use_neox_rope());
}
#[test]
fn deepseek_v3_arch_meta_prefix_is_deepseek2() {
assert_eq!(ModelArch::DeepSeekV3.meta_prefix(), "deepseek2");
}
fn tiny_deepseek_v3_config() -> Llama3Config {
Llama3Config {
arch: ModelArch::DeepSeekV3,
vocab_size: 32,
hidden_dim: 16,
intermediate_dim: 32,
num_heads: 2,
num_kv_heads: 2,
num_layers: 4,
max_seq_len: 32,
head_dim: 6,
rope_theta: 10_000.0,
norm_eps: 1e-5,
attention_extras: None,
ssm: None,
moe: None,
gemma3n: None,
gemma4: None,
deepseek_v3: Some(DeepSeekV3Config {
q_lora_rank: Some(8),
kv_lora_rank: Some(4),
qk_nope_head_dim: Some(4),
qk_rope_head_dim: Some(2),
v_head_dim: Some(4),
n_routed_experts: Some(8),
n_shared_experts: Some(1),
num_experts_per_tok: Some(2),
moe_intermediate_size: Some(32),
first_k_dense_replace: Some(4),
routed_scaling_factor: Some(2.5),
noaux_tc: Some(true),
mtp_layer: None,
}),
kimi_delta: None,
}
}
#[test]
fn deepseek_v3_compressed_kv_dim_matches_paper() {
let c = tiny_deepseek_v3_config();
let kv_dim = c.deepseek_kv_lora_rank().unwrap() + c.deepseek_qk_rope_head_dim().unwrap();
assert_eq!(kv_dim, 6);
assert_eq!(512usize + 64usize, 576);
}
#[test]
fn deepseek_v3_shape_totals_align_with_head_layout() {
let c = tiny_deepseek_v3_config();
let num_heads = c.num_heads;
let q_head_total =
c.deepseek_qk_nope_head_dim().unwrap() + c.deepseek_qk_rope_head_dim().unwrap();
let kv_up_head_total =
c.deepseek_qk_nope_head_dim().unwrap() + c.deepseek_v_head_dim().unwrap();
assert_eq!(num_heads * q_head_total, 2 * 6);
assert_eq!(num_heads * kv_up_head_total, 2 * 8);
}
#[test]
fn deepseek_moe_forward_streaming_matches_in_memory() {
use crate::deepseek_streaming::{ExpertLayerInfo, StreamingExpertPool};
use crate::gguf::GgmlType;
use std::sync::Arc;
const HIDDEN: usize = 4;
const MOE_FFN: usize = 8;
const N_EXPERTS: usize = 4;
const TOP_K: usize = 2;
const N_SHARED: usize = 1;
const ROUTED_SCALE: f32 = 1.5;
const SHARED_FFN: usize = N_SHARED * MOE_FFN;
const SLAB_BYTES: usize = MOE_FFN * HIDDEN * 4;
const DOWN_SLAB_BYTES: usize = HIDDEN * MOE_FFN * 4;
assert_eq!(SLAB_BYTES, DOWN_SLAB_BYTES);
let make_slab = |seed: u8| -> Vec<u8> {
(0..SLAB_BYTES)
.flat_map(|i| {
let v = ((seed as f32 + i as f32 * 0.017).sin() * 0.5).to_le_bytes();
v.into_iter()
})
.take(SLAB_BYTES)
.collect()
};
let mut source_bytes = Vec::with_capacity(3 * N_EXPERTS * SLAB_BYTES);
for e in 0..N_EXPERTS {
source_bytes.extend(make_slab(e as u8));
}
for e in 0..N_EXPERTS {
source_bytes.extend(make_slab((e + 10) as u8));
}
for e in 0..N_EXPERTS {
source_bytes.extend(make_slab((e + 20) as u8));
}
let gate_bytes = &source_bytes[0..N_EXPERTS * SLAB_BYTES];
let up_bytes = &source_bytes[N_EXPERTS * SLAB_BYTES..2 * N_EXPERTS * SLAB_BYTES];
let down_bytes = &source_bytes[2 * N_EXPERTS * SLAB_BYTES..3 * N_EXPERTS * SLAB_BYTES];
let shared_gate: Vec<u8> = (0..SHARED_FFN * HIDDEN * 4)
.flat_map(|i| ((i as f32 * 0.03 - 0.1).cos() * 0.2).to_le_bytes())
.collect();
let shared_up: Vec<u8> = (0..SHARED_FFN * HIDDEN * 4)
.flat_map(|i| ((i as f32 * 0.05 + 0.05).sin() * 0.15).to_le_bytes())
.collect();
let shared_down: Vec<u8> = (0..HIDDEN * SHARED_FFN * 4)
.flat_map(|i| ((i as f32 * 0.07 - 0.2).cos() * 0.18).to_le_bytes())
.collect();
let ffn_norm: Vec<f32> = (0..HIDDEN).map(|i| 1.0 + i as f32 * 0.05).collect();
let ffn_gate_inp: Vec<f32> = (0..N_EXPERTS * HIDDEN)
.map(|i| (i as f32 * 0.1).sin() * 0.3)
.collect();
let exp_probs_b: Vec<f32> = vec![0.0, 0.1, -0.05, 0.02];
let config = Llama3Config {
arch: ModelArch::DeepSeekV3,
vocab_size: 128,
hidden_dim: HIDDEN,
intermediate_dim: MOE_FFN,
num_heads: 2,
num_kv_heads: 2,
num_layers: 1,
max_seq_len: 32,
head_dim: 4,
rope_theta: 10_000.0,
norm_eps: 1e-6,
attention_extras: None,
ssm: None,
moe: None,
gemma3n: None,
gemma4: None,
deepseek_v3: Some(DeepSeekV3Config {
q_lora_rank: Some(8),
kv_lora_rank: Some(4),
qk_nope_head_dim: Some(4),
qk_rope_head_dim: Some(2),
v_head_dim: Some(4),
n_routed_experts: Some(N_EXPERTS),
n_shared_experts: Some(N_SHARED),
num_experts_per_tok: Some(TOP_K),
moe_intermediate_size: Some(MOE_FFN),
first_k_dense_replace: Some(0),
routed_scaling_factor: Some(ROUTED_SCALE),
noaux_tc: Some(true),
mtp_layer: None,
}),
kimi_delta: None,
};
let in_memory = DeepSeekMoeWeights {
ffn_norm: ffn_norm.clone(),
ffn_gate_inp: ffn_gate_inp.clone(),
exp_probs_b: Some(exp_probs_b.clone()),
routed: RoutedExpertStorage::InMemory {
gate: WeightRef {
data: gate_bytes,
qtype: GgmlType::F32,
rows: N_EXPERTS * MOE_FFN,
cols: HIDDEN,
},
up: WeightRef {
data: up_bytes,
qtype: GgmlType::F32,
rows: N_EXPERTS * MOE_FFN,
cols: HIDDEN,
},
down: WeightRef {
data: down_bytes,
qtype: GgmlType::F32,
rows: N_EXPERTS * HIDDEN,
cols: MOE_FFN,
},
},
ffn_gate_shexp: WeightRef {
data: &shared_gate,
qtype: GgmlType::F32,
rows: SHARED_FFN,
cols: HIDDEN,
},
ffn_up_shexp: WeightRef {
data: &shared_up,
qtype: GgmlType::F32,
rows: SHARED_FFN,
cols: HIDDEN,
},
ffn_down_shexp: WeightRef {
data: &shared_down,
qtype: GgmlType::F32,
rows: HIDDEN,
cols: SHARED_FFN,
},
};
let source_owned: Arc<dyn crate::deepseek_streaming::ExpertByteSource> =
Arc::new(source_bytes.clone());
let layer_info = vec![[
ExpertLayerInfo {
base_offset: 0,
bytes_per_expert: SLAB_BYTES,
n_experts: N_EXPERTS,
qtype: GgmlType::F32,
},
ExpertLayerInfo {
base_offset: N_EXPERTS * SLAB_BYTES,
bytes_per_expert: SLAB_BYTES,
n_experts: N_EXPERTS,
qtype: GgmlType::F32,
},
ExpertLayerInfo {
base_offset: 2 * N_EXPERTS * SLAB_BYTES,
bytes_per_expert: SLAB_BYTES,
n_experts: N_EXPERTS,
qtype: GgmlType::F32,
},
]];
let pool = Arc::new(StreamingExpertPool::new(
source_owned,
layer_info,
1024 * 1024, ));
let streaming = DeepSeekMoeWeights {
ffn_norm,
ffn_gate_inp,
exp_probs_b: Some(exp_probs_b),
routed: RoutedExpertStorage::Streaming {
pool: pool.clone(),
layer_idx: 0,
},
ffn_gate_shexp: WeightRef {
data: &shared_gate,
qtype: GgmlType::F32,
rows: SHARED_FFN,
cols: HIDDEN,
},
ffn_up_shexp: WeightRef {
data: &shared_up,
qtype: GgmlType::F32,
rows: SHARED_FFN,
cols: HIDDEN,
},
ffn_down_shexp: WeightRef {
data: &shared_down,
qtype: GgmlType::F32,
rows: HIDDEN,
cols: SHARED_FFN,
},
};
let hidden_state: Vec<f32> = (0..HIDDEN).map(|i| 0.5 - i as f32 * 0.1).collect();
let mut norm_buf_a = vec![0.0f32; HIDDEN];
let mut norm_buf_b = vec![0.0f32; HIDDEN];
let mut out_in_memory = vec![0.0f32; HIDDEN];
let mut out_streaming = vec![0.0f32; HIDDEN];
forward_deepseek_moe_layer(
&config,
&in_memory,
&hidden_state,
&mut norm_buf_a,
&mut out_in_memory,
);
forward_deepseek_moe_layer(
&config,
&streaming,
&hidden_state,
&mut norm_buf_b,
&mut out_streaming,
);
assert_eq!(
out_in_memory
.iter()
.map(|v| v.to_bits())
.collect::<Vec<_>>(),
out_streaming
.iter()
.map(|v| v.to_bits())
.collect::<Vec<_>>(),
"InMemory vs Streaming forward diverged: {out_in_memory:?} vs {out_streaming:?}"
);
let stats = pool.cache_stats();
assert_eq!(
stats.misses, 6,
"expected 6 pool misses (2 experts × 3 kinds)"
);
assert_eq!(
stats.hits, 6,
"matvec loop re-fetches prefetched keys as hits"
);
}
#[test]
fn deepseek_moe_route_sigmoid_topk_no_bias() {
let logits = vec![-1.0, 0.0, 1.0, 2.0, 3.0];
let selected = deepseek_moe_route(&logits, None, 2, 1.0);
assert_eq!(selected.len(), 2);
assert_eq!(selected[0].0, 4);
assert_eq!(selected[1].0, 3);
let sum: f32 = selected.iter().map(|(_, w)| *w).sum();
assert!((sum - 1.0).abs() < 1e-5, "weights sum = {sum}");
}
#[test]
fn deepseek_moe_route_noaux_tc_bias_affects_selection_not_weights() {
let logits = vec![3.0, -5.0, -5.0, -1.0, -5.0];
let bias = vec![0.0, 0.0, 0.0, 10.0, 0.0];
let selected = deepseek_moe_route(&logits, Some(&bias), 2, 1.0);
let picked: Vec<usize> = selected.iter().map(|(i, _)| *i).collect();
assert!(picked.contains(&3), "expert 3 must be top-k via bias");
assert!(
picked.contains(&0),
"expert 0 must be top-k via un-biased score"
);
let w3 = selected.iter().find(|(i, _)| *i == 3).unwrap().1;
let w0 = selected.iter().find(|(i, _)| *i == 0).unwrap().1;
assert!(
w0 > w3,
"un-biased weight of expert 0 must exceed expert 3 (w0={w0} w3={w3})"
);
}
#[test]
fn deepseek_moe_route_routed_scaling_factor() {
let logits = vec![0.0, 1.0, 2.0, 3.0];
let selected = deepseek_moe_route(&logits, None, 2, 2.5);
let sum: f32 = selected.iter().map(|(_, w)| *w).sum();
assert!(
(sum - 2.5).abs() < 1e-5,
"weights sum = {sum}, expected 2.5"
);
}
#[test]
fn deepseek_moe_route_top_k_larger_than_experts() {
let logits = vec![1.0, 2.0];
let selected = deepseek_moe_route(&logits, None, 8, 1.0);
assert_eq!(selected.len(), 2, "must select all experts when top-k > n");
let sum: f32 = selected.iter().map(|(_, w)| *w).sum();
assert!((sum - 1.0).abs() < 1e-5);
}
#[test]
fn mtp_policy_starts_enabled() {
let mut p = MtpDraftPolicy::new_default();
for _ in 0..5 {
assert!(p.should_draft(), "fresh policy must draft immediately");
}
}
#[test]
fn mtp_policy_enters_cooldown_after_all_rejections() {
let window = 4;
let cooldown = 3;
let mut p = MtpDraftPolicy::with_params(window, 0.30, cooldown);
for _ in 0..window {
assert!(p.should_draft());
p.record(false); }
assert!(
p.stats().in_cooldown,
"must be in cooldown after all-reject window"
);
for _ in 0..cooldown {
assert!(!p.should_draft(), "cooldown must skip drafts");
}
assert!(p.should_draft(), "post-cooldown must resume drafting");
}
#[test]
fn mtp_policy_stays_enabled_above_threshold() {
let window = 4;
let mut p = MtpDraftPolicy::with_params(window, 0.30, 8);
for &accept in &[true, true, true, false] {
assert!(p.should_draft());
p.record(accept);
}
assert!(
!p.stats().in_cooldown,
"75% accept rate must not trigger cooldown"
);
}
#[test]
fn mtp_policy_clears_window_on_cooldown() {
let mut p = MtpDraftPolicy::with_params(4, 0.30, 2);
for _ in 0..4 {
assert!(p.should_draft());
p.record(false);
}
assert!(p.stats().in_cooldown);
for _ in 0..2 {
let _ = p.should_draft();
}
assert!(p.should_draft());
p.record(true);
assert!(
!p.stats().in_cooldown,
"single post-cooldown accept must not re-trigger"
);
}
#[test]
fn mtp_policy_stats_track_lifetime_totals() {
let mut p = MtpDraftPolicy::with_params(4, 0.30, 1);
for _ in 0..4 {
assert!(p.should_draft());
p.record(false);
}
assert_eq!(p.stats().total_rejected, 4);
let _ = p.should_draft();
for _ in 0..3 {
assert!(p.should_draft());
p.record(true);
}
let stats = p.stats();
assert_eq!(stats.total_accepted, 3);
assert_eq!(stats.total_rejected, 4);
assert!((stats.overall_accept_rate - 3.0 / 7.0).abs() < 1e-5);
}
#[test]
fn mtp_loader_returns_none_when_config_has_no_mtp_layer() {
let mut c = tiny_deepseek_v3_config();
if let Some(d) = c.deepseek_v3.as_mut() {
d.mtp_layer = None;
}
assert!(c.deepseek_mtp_layer().is_none());
}
#[test]
fn has_deepseek_mtp_false_when_arch_is_not_deepseek() {
let llama_config = Llama3Config {
arch: ModelArch::Llama,
vocab_size: 128,
hidden_dim: 16,
intermediate_dim: 32,
num_heads: 2,
num_kv_heads: 2,
num_layers: 2,
max_seq_len: 32,
head_dim: 8,
rope_theta: 500_000.0,
norm_eps: 1e-6,
attention_extras: None,
ssm: None,
moe: None,
gemma3n: None,
gemma4: None,
deepseek_v3: None,
kimi_delta: None,
};
assert!(llama_config.deepseek_mtp_layer().is_none());
}
#[test]
fn deepseek_v3_first_k_dense_boundary_gates_moe_branch() {
let c = tiny_deepseek_v3_config();
let first_k = c.deepseek_first_k_dense_replace().unwrap();
assert!(first_k >= c.num_layers, "tiny fixture must stay dense-only");
}
#[test]
fn deepseek_v3_config_fixture_declares_all_mla_fields() {
let c = tiny_deepseek_v3_config();
let d = c.deepseek_v3.as_ref().unwrap();
assert!(d.q_lora_rank.is_some());
assert!(d.kv_lora_rank.is_some());
assert!(d.qk_nope_head_dim.is_some());
assert!(d.qk_rope_head_dim.is_some());
assert!(d.v_head_dim.is_some());
}
#[test]
fn load_ffn_norm_accepts_both_names() {
fn tiny_gguf_with(tensor_name: &str) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(b"GGUF");
buf.extend_from_slice(&3u32.to_le_bytes()); buf.extend_from_slice(&1u64.to_le_bytes()); buf.extend_from_slice(&1u64.to_le_bytes());
let key = "general.alignment";
buf.extend_from_slice(&(key.len() as u64).to_le_bytes());
buf.extend_from_slice(key.as_bytes());
buf.extend_from_slice(&4u32.to_le_bytes()); buf.extend_from_slice(&32u32.to_le_bytes());
buf.extend_from_slice(&(tensor_name.len() as u64).to_le_bytes());
buf.extend_from_slice(tensor_name.as_bytes());
buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&4u64.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&0u64.to_le_bytes());
while !buf.len().is_multiple_of(32) {
buf.push(0);
}
for x in [1.0f32, 2.0, 3.0, 4.0] {
buf.extend_from_slice(&x.to_le_bytes());
}
buf
}
let bytes = tiny_gguf_with("blk.0.ffn_norm.weight");
let gguf = crate::gguf::GgufFile::parse(&bytes).expect("parse standard");
let v = load_ffn_norm(&gguf, "blk.0").expect("load standard ffn_norm");
assert_eq!(v, vec![1.0, 2.0, 3.0, 4.0]);
let bytes = tiny_gguf_with("blk.0.post_attention_norm.weight");
let gguf = crate::gguf::GgufFile::parse(&bytes).expect("parse bonsai");
let v = load_ffn_norm(&gguf, "blk.0").expect("load bonsai post_attention_norm");
assert_eq!(v, vec![1.0, 2.0, 3.0, 4.0]);
let bytes = tiny_gguf_with("blk.0.something_else.weight");
let gguf = crate::gguf::GgufFile::parse(&bytes).expect("parse other");
assert!(load_ffn_norm(&gguf, "blk.0").is_none());
}
#[test]
fn load_weight_ref_any_rows_derives_shape_from_gguf() {
let mut buf = Vec::new();
buf.extend_from_slice(b"GGUF");
buf.extend_from_slice(&3u32.to_le_bytes()); buf.extend_from_slice(&1u64.to_le_bytes()); buf.extend_from_slice(&1u64.to_le_bytes());
let key = "general.alignment";
buf.extend_from_slice(&(key.len() as u64).to_le_bytes());
buf.extend_from_slice(key.as_bytes());
buf.extend_from_slice(&4u32.to_le_bytes()); buf.extend_from_slice(&32u32.to_le_bytes());
let name = "w";
buf.extend_from_slice(&(name.len() as u64).to_le_bytes());
buf.extend_from_slice(name.as_bytes());
buf.extend_from_slice(&2u32.to_le_bytes()); buf.extend_from_slice(&2u64.to_le_bytes()); buf.extend_from_slice(&3u64.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&0u64.to_le_bytes());
while !buf.len().is_multiple_of(32) {
buf.push(0);
}
for x in 0..6 {
buf.extend_from_slice(&(x as f32).to_le_bytes());
}
let gguf = crate::gguf::GgufFile::parse(&buf).expect("parse ok");
let w = load_weight_ref_any_rows(&gguf, "w", 2).expect("load ok");
assert_eq!(w.rows, 3, "rows must be derived from dims[1]");
assert_eq!(w.cols, 2, "cols is passed by caller");
assert!(load_weight_ref_any_rows(&gguf, "not_present", 2).is_none());
}
#[test]
fn gated_attention_sigmoid_math_matches_reference() {
let mut attn_out = [1.0f32, 2.0, -1.0, 0.5, -0.5, 3.0];
let gate = [0.0f32, 1.0, -1.0, 10.0, -10.0, 0.5];
let q_dim = attn_out.len();
let expected: Vec<f32> = attn_out
.iter()
.zip(gate.iter())
.map(|(&a, &g)| a * sigmoid(g))
.collect();
for i in 0..q_dim {
attn_out[i] *= sigmoid(gate[i]);
}
for (i, (&got, &want)) in attn_out.iter().zip(expected.iter()).enumerate() {
assert!(
(got - want).abs() < 1e-6,
"gated attention at {i}: got {got}, expected {want}"
);
}
assert!((attn_out[0] - 0.5).abs() < 1e-6);
assert!(attn_out[4].abs() < 1e-3);
assert!((attn_out[3] - 0.5).abs() < 1e-3);
}
#[cfg(feature = "grammar")]
#[test]
fn grammar_gen_error_display_no_valid_token() {
let e = GrammarGenError::NoValidToken { step: 3 };
let s = format!("{e}");
assert!(s.contains("step 3"));
}
#[cfg(feature = "grammar")]
#[test]
fn grammar_gen_error_display_wraps_fsm_error() {
let e = GrammarGenError::Fsm(crate::grammar::FsmError::NoTransition { ch: 'x' });
let s = format!("{e}");
assert!(s.contains("FSM error"));
assert!(s.contains("'x'"));
}
#[cfg(feature = "grammar")]
#[test]
fn grammar_gen_error_from_fsm_error() {
let fsm_err = crate::grammar::FsmError::EmptyRoot;
let converted: GrammarGenError = fsm_err.clone().into();
assert_eq!(converted, GrammarGenError::Fsm(fsm_err));
}
}