use std::collections::BTreeMap;
use crate::gguf::TensorInfo;
use crate::imatrix::tier::{QuantTier, SizeBudget, TierBand, TierMenu};
pub const MTP_DROP_BYTES: u64 = 8_370_000_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockKind {
PureAttention,
DeltaNetHybrid,
NormOnly,
}
#[derive(Debug, Clone)]
pub struct LayerClassification {
pub kinds: BTreeMap<u32, BlockKind>,
pub max_block: u32,
}
impl LayerClassification {
#[must_use]
pub const fn late_layer_threshold(&self) -> u32 {
self.max_block.saturating_sub(self.max_block / 3)
}
#[must_use]
pub fn kind_of(&self, block_idx: u32) -> BlockKind {
self.kinds
.get(&block_idx)
.copied()
.unwrap_or(BlockKind::NormOnly)
}
}
#[must_use]
pub fn classify_layers<'a, I>(tensors: I) -> LayerClassification
where
I: IntoIterator<Item = &'a TensorInfo>,
{
let mut kinds: BTreeMap<u32, BlockKind> = BTreeMap::new();
let mut max_block: u32 = 0;
for info in tensors {
let Some((block_idx, part)) = parse_block_tensor(&info.name) else {
continue;
};
if block_idx > max_block {
max_block = block_idx;
}
let new_kind = if part.starts_with("ssm_") || part == "attn_qkv" {
BlockKind::DeltaNetHybrid
} else if matches!(part, "attn_q" | "attn_k" | "attn_v") {
BlockKind::PureAttention
} else {
continue;
};
kinds
.entry(block_idx)
.and_modify(|existing| {
if new_kind == BlockKind::DeltaNetHybrid {
*existing = BlockKind::DeltaNetHybrid;
}
})
.or_insert(new_kind);
}
LayerClassification { kinds, max_block }
}
fn parse_block_tensor(name: &str) -> Option<(u32, &str)> {
let rest = name.strip_prefix("blk.")?;
let dot = rest.find('.')?;
let (idx_str, tail) = rest.split_at(dot);
let block_idx = idx_str.parse::<u32>().ok()?;
let part = tail.strip_prefix('.')?.strip_suffix(".weight")?;
Some((block_idx, part))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Assignment {
Keep(QuantTier),
Drop,
}
#[must_use]
pub fn assign_tier(
name: &str,
classification: &LayerClassification,
menu: &TierMenu,
budget: SizeBudget,
) -> Assignment {
if name.starts_with("nextn.")
|| name.contains(".nextn.")
|| name.starts_with("mtp.")
|| name.contains(".mtp.")
{
if budget.total_bytes() < MTP_DROP_BYTES {
return Assignment::Drop;
}
return Assignment::Keep(TierMenu::bump(menu.high));
}
if is_norm_or_conv1d(name) {
return Assignment::Keep(QuantTier::F32);
}
if name == "token_embd.weight" {
return Assignment::Keep(QuantTier::Q2_K);
}
if name == "output.weight" {
return Assignment::Keep(menu.mid);
}
let Some((block_idx, part)) = parse_block_tensor(name) else {
return Assignment::Keep(menu.mid_low);
};
if matches!(part, "ssm_alpha" | "ssm_beta") {
return Assignment::Keep(QuantTier::Q8_0);
}
let is_late = block_idx >= classification.late_layer_threshold();
let kind = classification.kind_of(block_idx);
let base_band = slot_to_band(part, kind);
let base_tier = menu.resolve(base_band);
let tier = if is_late && should_bump_late(part) {
TierMenu::bump(base_tier)
} else {
base_tier
};
Assignment::Keep(tier)
}
fn is_norm_or_conv1d(name: &str) -> bool {
name.ends_with("_norm.weight")
|| name.ends_with(".norm.weight")
|| name.ends_with(".conv1d.weight")
|| name.ends_with("_conv1d.weight")
|| name == "output_norm.weight"
}
fn slot_to_band(part: &str, kind: BlockKind) -> TierBand {
match (part, kind) {
("attn_v", _) => TierBand::High,
("attn_q" | "attn_k", BlockKind::PureAttention) => TierBand::Mid,
("attn_output", BlockKind::PureAttention) => TierBand::Mid,
("attn_qkv", _) => TierBand::Low,
("ffn_down", _) => TierBand::MidLow,
("ffn_gate" | "ffn_up", _) => TierBand::Low,
("ssm_out", _) => TierBand::MidLow,
("attn_q" | "attn_k" | "attn_output", _) => TierBand::MidLow,
_ => TierBand::Mid,
}
}
fn should_bump_late(part: &str) -> bool {
!matches!(part, "ssm_alpha" | "ssm_beta" | "ffn_gate" | "ffn_up")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::gguf::GgmlType;
fn ti(name: &str) -> TensorInfo {
TensorInfo {
name: name.to_string(),
n_dims: 2,
dims: vec![1024, 1024],
qtype: GgmlType::F32,
offset: 0,
}
}
fn qwen35_layout() -> Vec<TensorInfo> {
let mut ts = Vec::new();
for i in 0..3 {
ts.push(ti(&format!("blk.{i}.attn_q.weight")));
ts.push(ti(&format!("blk.{i}.attn_k.weight")));
ts.push(ti(&format!("blk.{i}.attn_v.weight")));
ts.push(ti(&format!("blk.{i}.attn_output.weight")));
ts.push(ti(&format!("blk.{i}.ffn_gate.weight")));
}
for i in 3..6 {
ts.push(ti(&format!("blk.{i}.attn_qkv.weight")));
ts.push(ti(&format!("blk.{i}.ssm_alpha.weight")));
ts.push(ti(&format!("blk.{i}.ssm_beta.weight")));
ts.push(ti(&format!("blk.{i}.ffn_gate.weight")));
}
ts
}
fn menu_9g() -> TierMenu {
TierMenu::for_budget(SizeBudget::from_bytes(9_000_000_000).expect("nonzero"))
}
fn budget_9g() -> SizeBudget {
SizeBudget::from_bytes(9_000_000_000).expect("nonzero")
}
fn budget_5g() -> SizeBudget {
SizeBudget::from_bytes(5_000_000_000).expect("nonzero")
}
#[test]
fn parse_block_tensor_ok() {
assert_eq!(
parse_block_tensor("blk.7.attn_v.weight"),
Some((7, "attn_v"))
);
assert_eq!(
parse_block_tensor("blk.64.nextn.eh_proj.weight"),
Some((64, "nextn.eh_proj"))
);
}
#[test]
fn parse_block_tensor_rejects_globals() {
assert!(parse_block_tensor("output.weight").is_none());
assert!(parse_block_tensor("blk.NaN.attn_v.weight").is_none());
}
#[test]
fn classify_detects_pure_and_deltanet_blocks() {
let ts = qwen35_layout();
let c = classify_layers(&ts);
assert_eq!(c.kind_of(0), BlockKind::PureAttention);
assert_eq!(c.kind_of(2), BlockKind::PureAttention);
assert_eq!(c.kind_of(3), BlockKind::DeltaNetHybrid);
assert_eq!(c.kind_of(5), BlockKind::DeltaNetHybrid);
assert_eq!(c.max_block, 5);
}
#[test]
fn h2_norms_always_f32() {
let c = classify_layers(&qwen35_layout());
let m = menu_9g();
for name in [
"output_norm.weight",
"blk.0.attn_norm.weight",
"blk.0.attn_k_norm.weight",
"blk.0.attn_q_norm.weight",
"blk.0.post_attention_norm.weight",
"blk.0.ssm_norm.weight",
"blk.0.ssm_conv1d.weight",
] {
assert_eq!(
assign_tier(name, &c, &m, budget_9g()),
Assignment::Keep(QuantTier::F32),
"tensor {name} should be F32"
);
}
}
#[test]
fn h3_deltanet_gates_always_q8_0() {
let c = classify_layers(&qwen35_layout());
let m = menu_9g();
assert_eq!(
assign_tier("blk.3.ssm_alpha.weight", &c, &m, budget_9g()),
Assignment::Keep(QuantTier::Q8_0)
);
assert_eq!(
assign_tier("blk.4.ssm_beta.weight", &c, &m, budget_9g()),
Assignment::Keep(QuantTier::Q8_0)
);
}
#[test]
fn h1_token_embd_always_q2k_output_at_mid() {
let c = classify_layers(&qwen35_layout());
let m = menu_9g();
assert_eq!(
assign_tier("token_embd.weight", &c, &m, budget_9g()),
Assignment::Keep(QuantTier::Q2_K)
);
assert_eq!(
assign_tier("output.weight", &c, &m, budget_9g()),
Assignment::Keep(m.mid)
);
}
#[test]
fn h4_attn_v_higher_than_attn_qk_in_pure_attention_block() {
let c = classify_layers(&qwen35_layout());
let m = menu_9g();
let v = assign_tier("blk.0.attn_v.weight", &c, &m, budget_9g());
let q = assign_tier("blk.0.attn_q.weight", &c, &m, budget_9g());
let Assignment::Keep(v_t) = v else {
panic!("attn_v dropped")
};
let Assignment::Keep(q_t) = q else {
panic!("attn_q dropped")
};
assert!(
v_t.bpw() >= q_t.bpw(),
"attn_v ({v_t:?}) should be >= attn_q ({q_t:?})",
);
}
#[test]
fn h5_ffn_gate_up_at_low_band() {
let c = classify_layers(&qwen35_layout());
let m = menu_9g();
assert_eq!(
assign_tier("blk.0.ffn_gate.weight", &c, &m, budget_9g()),
Assignment::Keep(m.low)
);
assert_eq!(
assign_tier("blk.0.ffn_up.weight", &c, &m, budget_9g()),
Assignment::Keep(m.low)
);
}
#[test]
fn h6_late_layer_bumps_attn_v_but_not_ffn_gate() {
let mut ts = Vec::new();
for i in 0..30 {
ts.push(ti(&format!("blk.{i}.attn_q.weight")));
ts.push(ti(&format!("blk.{i}.attn_k.weight")));
ts.push(ti(&format!("blk.{i}.attn_v.weight")));
ts.push(ti(&format!("blk.{i}.ffn_gate.weight")));
}
let c = classify_layers(&ts);
let m = menu_9g();
let late = assign_tier("blk.29.attn_v.weight", &c, &m, budget_9g());
let early = assign_tier("blk.0.attn_v.weight", &c, &m, budget_9g());
let (Assignment::Keep(late_t), Assignment::Keep(early_t)) = (late, early) else {
panic!("attn_v dropped");
};
assert!(
late_t.bpw() > early_t.bpw(),
"late attn_v ({late_t:?}) should exceed early ({early_t:?})",
);
let late_gate = assign_tier("blk.29.ffn_gate.weight", &c, &m, budget_9g());
let early_gate = assign_tier("blk.0.ffn_gate.weight", &c, &m, budget_9g());
assert_eq!(late_gate, early_gate, "ffn_gate must not receive late bump");
}
#[test]
fn h7_mtp_dropped_below_threshold_kept_above() {
let c = classify_layers(&qwen35_layout());
let m_low = TierMenu::for_budget(budget_5g());
assert_eq!(
assign_tier("blk.64.nextn.eh_proj.weight", &c, &m_low, budget_5g()),
Assignment::Drop,
"MTP must be dropped when budget < 8.37 GB"
);
let m_hi = menu_9g();
let kept = assign_tier("blk.64.nextn.eh_proj.weight", &c, &m_hi, budget_9g());
assert!(
matches!(kept, Assignment::Keep(_)),
"MTP must be kept when budget >= 8.37 GB"
);
}
#[test]
fn h8_pure_attention_qkv_preserved_over_deltanet_qkv() {
let c = classify_layers(&qwen35_layout());
let m = menu_9g();
let pure_q = assign_tier("blk.0.attn_q.weight", &c, &m, budget_9g());
let delta_qkv = assign_tier("blk.3.attn_qkv.weight", &c, &m, budget_9g());
let (Assignment::Keep(pure_t), Assignment::Keep(delta_t)) = (pure_q, delta_qkv) else {
panic!("dropped");
};
assert!(
pure_t.bpw() > delta_t.bpw(),
"pure attention Q ({pure_t:?}) should exceed DeltaNet fused QKV ({delta_t:?})",
);
}
#[test]
fn late_layer_threshold_matches_qwen38_layout() {
let mut ts = Vec::new();
for i in 0..65 {
ts.push(ti(&format!("blk.{i}.attn_qkv.weight")));
ts.push(ti(&format!("blk.{i}.ssm_alpha.weight")));
}
let c = classify_layers(&ts);
assert_eq!(c.max_block, 64);
assert_eq!(c.late_layer_threshold(), 43);
}
}