#[derive(Debug, Clone)]
pub struct BitNetConfig {
pub num_hidden_layers: usize,
pub hidden_size: usize,
pub num_attention_heads: usize,
pub num_key_value_heads: usize,
pub head_dim: usize,
pub intermediate_size: usize,
pub vocab_size: usize,
pub max_position_embeddings: usize,
pub rope_theta: f64,
pub rms_norm_eps: f32,
pub tie_word_embeddings: bool,
}
impl BitNetConfig {
pub fn bitnet_2b4t() -> Self {
Self {
num_hidden_layers: 30,
hidden_size: 2560,
num_attention_heads: 20,
num_key_value_heads: 5,
head_dim: 128,
intermediate_size: 6912,
vocab_size: 128_256,
max_position_embeddings: 4096,
rope_theta: 500_000.0,
rms_norm_eps: 1e-6,
tie_word_embeddings: true,
}
}
pub fn kv_dim(&self) -> usize {
self.num_key_value_heads * self.head_dim
}
pub fn q_dim(&self) -> usize {
self.num_attention_heads * self.head_dim
}
pub fn gqa_ratio(&self) -> usize {
self.num_attention_heads / self.num_key_value_heads
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config_values() {
let cfg = BitNetConfig::bitnet_2b4t();
assert_eq!(cfg.num_hidden_layers, 30);
assert_eq!(cfg.hidden_size, 2560);
assert_eq!(cfg.num_attention_heads, 20);
assert_eq!(cfg.num_key_value_heads, 5);
assert_eq!(cfg.head_dim, 128);
assert_eq!(cfg.intermediate_size, 6912);
assert_eq!(cfg.vocab_size, 128_256);
assert_eq!(cfg.max_position_embeddings, 4096);
assert!((cfg.rope_theta - 500_000.0).abs() < 1e-6);
assert!((cfg.rms_norm_eps - 1e-6).abs() < 1e-12);
assert!(cfg.tie_word_embeddings);
}
#[test]
fn test_kv_dim() {
let cfg = BitNetConfig::bitnet_2b4t();
assert_eq!(cfg.kv_dim(), 640);
}
#[test]
fn test_q_dim() {
let cfg = BitNetConfig::bitnet_2b4t();
assert_eq!(cfg.q_dim(), 2560);
}
#[test]
fn test_gqa_ratio() {
let cfg = BitNetConfig::bitnet_2b4t();
assert_eq!(cfg.gqa_ratio(), 4);
}
#[test]
fn test_hidden_size_equals_q_dim() {
let cfg = BitNetConfig::bitnet_2b4t();
assert_eq!(cfg.hidden_size, cfg.q_dim());
}
}