use crate::attn::{AttnConfig, KvCache};
use crate::config::TransformerConfig;
use crate::layers::{decoder_layer_forward, encoder_layer_forward, DecoderLayer, EncoderLayer};
use crate::math;
use crate::norm::zc_rms_norm_vec;
use crate::rope::RopeCache;
use alloc::vec;
use alloc::vec::Vec;
pub struct NeedleModel {
pub cfg: TransformerConfig,
pub embedding: Vec<f32>,
pub encoder_layers: Vec<EncoderLayer>,
pub decoder_layers: Vec<DecoderLayer>,
pub encoder_final_norm: Vec<f32>,
pub decoder_final_norm: Vec<f32>,
rope: RopeCache,
attn_cfg: AttnConfig,
embed_scale: f32,
}
impl NeedleModel {
pub fn new(
cfg: TransformerConfig,
embedding: Vec<f32>,
encoder_layers: Vec<EncoderLayer>,
decoder_layers: Vec<DecoderLayer>,
encoder_final_norm: Vec<f32>,
decoder_final_norm: Vec<f32>,
) -> Self {
let rope = RopeCache::new(
cfg.max_enc_len.max(cfg.max_dec_len),
cfg.head_dim(),
cfg.rope_theta,
);
let attn_cfg = AttnConfig {
num_heads: cfg.num_heads,
num_kv_heads: cfg.num_kv_heads,
head_dim: cfg.head_dim(),
d_model: cfg.d_model,
};
let embed_scale = math::sqrt(cfg.d_model as f32);
Self {
cfg,
embedding,
encoder_layers,
decoder_layers,
encoder_final_norm,
decoder_final_norm,
rope,
attn_cfg,
embed_scale,
}
}
fn embed_scaled(&self, token_id: u32) -> Vec<f32> {
let d = self.cfg.d_model;
let idx = token_id as usize;
debug_assert!(idx < self.cfg.vocab_size);
let row = &self.embedding[idx * d..(idx + 1) * d];
row.iter().map(|v| v * self.embed_scale).collect()
}
fn lm_head(&self, hidden: &[f32], logits: &mut [f32]) {
debug_assert_eq!(hidden.len(), self.cfg.d_model);
debug_assert_eq!(logits.len(), self.cfg.vocab_size);
#[cfg(all(target_arch = "x86_64", feature = "simd"))]
if crate::quant::has_avx2() {
return unsafe { self.lm_head_avx2(hidden, logits) };
}
self.lm_head_scalar(hidden, logits);
}
#[allow(clippy::needless_range_loop)]
fn lm_head_scalar(&self, hidden: &[f32], logits: &mut [f32]) {
let d = self.cfg.d_model;
let v = self.cfg.vocab_size;
for tok in 0..v {
let emb = &self.embedding[tok * d..(tok + 1) * d];
let mut acc = 0.0f32;
for i in 0..d {
acc += hidden[i] * emb[i];
}
logits[tok] = acc;
}
}
#[cfg(all(target_arch = "x86_64", feature = "simd"))]
#[target_feature(enable = "avx2")]
#[allow(clippy::needless_range_loop)]
unsafe fn lm_head_avx2(&self, hidden: &[f32], logits: &mut [f32]) {
use core::arch::x86_64::*;
let d = self.cfg.d_model;
let v = self.cfg.vocab_size;
let chunks = d / 8;
for tok in 0..v {
let emb_ptr = self.embedding.as_ptr().add(tok * d);
let mut acc = _mm256_setzero_ps();
for c in 0..chunks {
let h = _mm256_loadu_ps(hidden.as_ptr().add(c * 8));
let e = _mm256_loadu_ps(emb_ptr.add(c * 8));
acc = _mm256_fmadd_ps(h, e, acc);
}
let hi128 = _mm256_extractf128_ps(acc, 1);
let lo128 = _mm256_castps256_ps128(acc);
let sum128 = _mm_add_ps(hi128, lo128);
let shuf = _mm_shuffle_ps(sum128, sum128, 0x4E);
let sum64 = _mm_add_ps(sum128, shuf);
let shuf2 = _mm_shuffle_ps(sum64, sum64, 0xB1);
let sum32 = _mm_add_ss(sum64, shuf2);
let mut result = _mm_cvtss_f32(sum32);
for i in chunks * 8..d {
result += hidden[i] * *emb_ptr.add(i);
}
logits[tok] = result;
}
}
pub fn encode(&self, input_ids: &[u32], enc_kv_caches: &mut [KvCache]) -> Vec<f32> {
let seq_len = input_ids.len();
let d = self.cfg.d_model;
let mut x = vec![0.0f32; seq_len * d];
for (t, &tok) in input_ids.iter().enumerate() {
let row = &self.embedding[tok as usize * d..(tok as usize + 1) * d];
for (dst, &src) in x[t * d..(t + 1) * d].iter_mut().zip(row.iter()) {
*dst = src * self.embed_scale;
}
}
let mut tmp = Vec::with_capacity(seq_len * d);
let mut normed = vec![0.0f32; seq_len * d];
for layer in self.encoder_layers.iter() {
encoder_layer_forward(
&mut x,
layer,
&self.attn_cfg,
&self.rope,
seq_len,
None,
&mut tmp,
&mut normed,
);
}
for t in 0..seq_len {
zc_rms_norm_vec(&mut x[t * d..(t + 1) * d], &self.encoder_final_norm);
}
self.fill_cross_kv(&x, seq_len, enc_kv_caches);
x
}
fn fill_cross_kv(&self, enc_hidden: &[f32], seq_len: usize, kv_caches: &mut [KvCache]) {
let kv_h = self.cfg.num_kv_heads;
let hd = self.cfg.head_dim();
let kv_dim = kv_h * hd;
for (li, layer) in self.decoder_layers.iter().enumerate() {
let kv = &mut kv_caches[li];
kv.reset();
let mut k_all = vec![0.0f32; seq_len * kv_dim];
let mut v_all = vec![0.0f32; seq_len * kv_dim];
layer.cross_attn.wk.matmul(enc_hidden, seq_len, &mut k_all);
layer.cross_attn.wv.matmul(enc_hidden, seq_len, &mut v_all);
for t in 0..seq_len {
for ki in 0..kv_h {
crate::norm::zc_rms_norm_vec(
&mut k_all[(t * kv_h + ki) * hd..(t * kv_h + ki + 1) * hd],
&layer.cross_attn.k_norm,
);
}
}
let kv_stride = kv_h * hd;
for t in 0..seq_len {
kv.push_kv(
&k_all[t * kv_stride..(t + 1) * kv_stride],
&v_all[t * kv_stride..(t + 1) * kv_stride],
);
}
}
}
pub fn decode_step(
&self,
token_id: u32,
enc_kv_caches: &[KvCache],
dec_kv_caches: &mut [KvCache],
logits: &mut [f32],
) {
let d = self.cfg.d_model;
let mut x: Vec<f32> = self.embed_scaled(token_id);
let mut tmp = Vec::with_capacity(d);
let mut normed = vec![0.0f32; d];
for (li, layer) in self.decoder_layers.iter().enumerate() {
decoder_layer_forward(
&mut x,
layer,
&self.attn_cfg,
&self.rope,
&enc_kv_caches[li],
&mut dec_kv_caches[li],
&mut tmp,
&mut normed,
);
}
zc_rms_norm_vec(&mut x, &self.decoder_final_norm);
self.lm_head(&x, logits);
}
pub fn make_enc_kv_caches(&self, enc_len: usize) -> Vec<KvCache> {
(0..self.cfg.num_dec_layers)
.map(|_| KvCache::new(enc_len, self.cfg.num_kv_heads, self.cfg.head_dim()))
.collect()
}
pub fn make_dec_kv_caches(&self) -> Vec<KvCache> {
(0..self.cfg.num_dec_layers)
.map(|_| {
KvCache::new(
self.cfg.max_dec_len,
self.cfg.num_kv_heads,
self.cfg.head_dim(),
)
})
.collect()
}
}