#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TensorDtype {
F16,
F32,
BF16,
Quantized,
}
#[derive(Debug, Clone, Copy)]
pub struct TensorCatalogEntry {
pub name_template: &'static str,
pub scope: LayerScope,
pub dtype: TensorDtype,
pub citation: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayerScope {
Global,
AllLayers,
FullAttentionLayersOnly,
LinearAttentionLayersOnly,
MtpLayers,
MoeExpertsPerLayer,
MoeSharedExpertPerLayer,
MoeRouterPerLayer,
}
#[derive(Debug, Clone, Copy)]
pub struct TensorCatalog {
pub entries: &'static [TensorCatalogEntry],
}
#[derive(Debug, Clone, Copy)]
pub struct CatalogExpansion {
pub num_hidden_layers: u32,
pub num_full_attention_layers: u32,
pub num_linear_attention_layers: u32,
pub num_experts: u32,
pub has_shared_expert: bool,
pub mtp_num_hidden_layers: u32,
}
impl TensorCatalog {
pub fn expected_tensor_count(&self, exp: CatalogExpansion) -> u64 {
let mut total: u64 = 0;
for e in self.entries {
let count = match e.scope {
LayerScope::Global => 1,
LayerScope::AllLayers => exp.num_hidden_layers as u64,
LayerScope::FullAttentionLayersOnly => exp.num_full_attention_layers as u64,
LayerScope::LinearAttentionLayersOnly => exp.num_linear_attention_layers as u64,
LayerScope::MtpLayers => exp.mtp_num_hidden_layers as u64,
LayerScope::MoeExpertsPerLayer => {
(exp.num_experts as u64) * (exp.num_hidden_layers as u64)
}
LayerScope::MoeSharedExpertPerLayer => {
if exp.has_shared_expert {
exp.num_hidden_layers as u64
} else {
0
}
}
LayerScope::MoeRouterPerLayer => exp.num_hidden_layers as u64,
};
total = total.saturating_add(count);
}
total
}
pub fn expand_names(&self, exp: CatalogExpansion) -> Vec<String> {
let mut names = Vec::new();
for e in self.entries {
match e.scope {
LayerScope::Global => names.push(e.name_template.to_string()),
LayerScope::AllLayers => {
for l in 0..exp.num_hidden_layers {
names.push(e.name_template.replace("{L}", &l.to_string()));
}
}
LayerScope::FullAttentionLayersOnly | LayerScope::LinearAttentionLayersOnly => {
for l in 0..exp.num_hidden_layers {
names.push(e.name_template.replace("{L}", &l.to_string()));
}
}
LayerScope::MtpLayers => {
for l in 0..exp.mtp_num_hidden_layers {
let block = exp.num_hidden_layers + l;
names.push(e.name_template.replace("{L}", &block.to_string()));
}
}
LayerScope::MoeExpertsPerLayer => {
for l in 0..exp.num_hidden_layers {
for x in 0..exp.num_experts {
names.push(
e.name_template
.replace("{L}", &l.to_string())
.replace("{X}", &x.to_string()),
);
}
}
}
LayerScope::MoeSharedExpertPerLayer => {
if exp.has_shared_expert {
for l in 0..exp.num_hidden_layers {
names.push(e.name_template.replace("{L}", &l.to_string()));
}
}
}
LayerScope::MoeRouterPerLayer => {
for l in 0..exp.num_hidden_layers {
names.push(e.name_template.replace("{L}", &l.to_string()));
}
}
}
}
names
}
}
#[cfg(test)]
mod tests {
use super::*;
const EMPTY: TensorCatalog = TensorCatalog { entries: &[] };
#[test]
fn empty_catalog_expects_zero() {
let exp = CatalogExpansion {
num_hidden_layers: 40,
num_full_attention_layers: 10,
num_linear_attention_layers: 30,
num_experts: 256,
has_shared_expert: true,
mtp_num_hidden_layers: 1,
};
assert_eq!(EMPTY.expected_tensor_count(exp), 0);
}
#[test]
fn scope_allocations_are_linear_in_their_scope() {
const CAT: TensorCatalog = TensorCatalog {
entries: &[
TensorCatalogEntry {
name_template: "token_embd.weight",
scope: LayerScope::Global,
dtype: TensorDtype::F16,
citation: "test",
},
TensorCatalogEntry {
name_template: "blk.{L}.attn_q.weight",
scope: LayerScope::AllLayers,
dtype: TensorDtype::Quantized,
citation: "test",
},
TensorCatalogEntry {
name_template: "blk.{L}.nextn.embed.weight",
scope: LayerScope::MtpLayers,
dtype: TensorDtype::F16,
citation: "test",
},
],
};
let exp = CatalogExpansion {
num_hidden_layers: 4,
num_full_attention_layers: 1,
num_linear_attention_layers: 3,
num_experts: 0,
has_shared_expert: false,
mtp_num_hidden_layers: 1,
};
assert_eq!(CAT.expected_tensor_count(exp), 6);
let names = CAT.expand_names(exp);
assert_eq!(names.len(), 6);
assert!(names.contains(&"token_embd.weight".to_string()));
assert!(names.contains(&"blk.0.attn_q.weight".to_string()));
assert!(names.contains(&"blk.3.attn_q.weight".to_string()));
assert!(names.contains(&"blk.4.nextn.embed.weight".to_string()));
}
#[test]
fn moe_expert_scope_multiplies_experts_by_layers() {
const CAT: TensorCatalog = TensorCatalog {
entries: &[TensorCatalogEntry {
name_template: "blk.{L}.ffn_gate.{X}.weight",
scope: LayerScope::MoeExpertsPerLayer,
dtype: TensorDtype::Quantized,
citation: "test",
}],
};
let exp = CatalogExpansion {
num_hidden_layers: 40,
num_full_attention_layers: 10,
num_linear_attention_layers: 30,
num_experts: 256,
has_shared_expert: false,
mtp_num_hidden_layers: 0,
};
assert_eq!(CAT.expected_tensor_count(exp), 40 * 256);
}
}