pub struct MicroAttention {
head_dim: usize,
num_heads: usize,
scale_shift: u8,
}
impl MicroAttention {
pub fn new(embed_dim: usize, num_heads: usize) -> Self {
let head_dim = embed_dim / num_heads;
let scale_shift = match head_dim {
d if d >= 64 => 3,
d if d >= 32 => 3,
d if d >= 16 => 2,
_ => 1,
};
Self {
head_dim,
num_heads,
scale_shift,
}
}
#[inline]
pub fn compute_scores(
&self,
query: &[i8], keys: &[&[i8]], scores: &mut [i32], ) {
for (i, key) in keys.iter().enumerate() {
let mut dot: i32 = 0;
for j in 0..self.head_dim {
dot += query[j] as i32 * key[j] as i32;
}
scores[i] = dot >> self.scale_shift;
}
}
#[inline]
pub fn apply_causal_mask(&self, scores: &mut [i32], current_pos: usize) {
for i in (current_pos + 1)..scores.len() {
scores[i] = i32::MIN / 2; }
}
#[inline]
pub fn softmax_fixed(&self, scores: &mut [i32]) {
if scores.is_empty() {
return;
}
let max_score = scores.iter().cloned().max().unwrap_or(0);
let mut sum: i64 = 0;
for score in scores.iter_mut() {
let x = *score - max_score;
let x_clamped = x.max(-512).min(0);
*score = (256 + x_clamped / 2).max(1) as i32;
sum += *score as i64;
}
if sum > 0 {
for score in scores.iter_mut() {
*score = ((*score as i64 * 256) / sum) as i32;
}
}
}
#[inline]
pub fn weighted_sum(
&self,
weights: &[i32], values: &[&[i8]], output: &mut [i32], ) {
for o in output.iter_mut() {
*o = 0;
}
for (&weight, value) in weights.iter().zip(values.iter()) {
for j in 0..self.head_dim {
output[j] += weight * value[j] as i32;
}
}
for o in output.iter_mut() {
*o >>= 8;
}
}
}
pub struct LinearAttention {
feature_dim: usize,
}
impl LinearAttention {
pub fn new(feature_dim: usize) -> Self {
Self { feature_dim }
}
#[inline]
pub fn feature_map(&self, x: i8) -> i16 {
(x.max(0) as i16) + 1
}
pub fn forward(
&self,
query: &[i8], keys: &[&[i8]], values: &[&[i8]], output: &mut [i32], ) {
let dim = query.len();
let mut kv_cache = [[0i32; 64]; 64];
for (key, value) in keys.iter().zip(values.iter()) {
for i in 0..dim.min(64) {
let phi_k = self.feature_map(key[i]);
for j in 0..dim.min(64) {
kv_cache[i][j] += phi_k as i32 * value[j] as i32;
}
}
}
for i in 0..dim.min(64) {
let phi_q = self.feature_map(query[i]);
let mut sum: i32 = 0;
for j in 0..dim.min(64) {
sum += phi_q as i32 * kv_cache[j][i];
}
output[i] = sum >> 8;
}
let mut k_sum = [0i32; 64];
for key in keys.iter() {
for i in 0..dim.min(64) {
k_sum[i] += self.feature_map(key[i]) as i32;
}
}
let mut denom: i32 = 0;
for i in 0..dim.min(64) {
denom += self.feature_map(query[i]) as i32 * k_sum[i];
}
if denom > 0 {
for o in output.iter_mut() {
*o = (*o << 8) / denom;
}
}
}
}
pub struct SlidingWindowAttention {
window_size: usize,
head_dim: usize,
}
impl SlidingWindowAttention {
pub fn new(window_size: usize, head_dim: usize) -> Self {
Self { window_size, head_dim }
}
pub fn forward(
&self,
query: &[i8],
keys: &[[i8; 64]], values: &[[i8; 64]], cache_len: usize,
output: &mut [i32],
) {
let window_start = cache_len.saturating_sub(self.window_size);
let mut scores = [0i32; 32];
for i in window_start..cache_len {
let mut dot: i32 = 0;
for j in 0..self.head_dim {
dot += query[j] as i32 * keys[i % self.window_size][j] as i32;
}
scores[i - window_start] = dot >> 3;
}
let window_len = cache_len - window_start;
let scores_slice = &mut scores[..window_len];
let max = scores_slice.iter().cloned().max().unwrap_or(0);
let mut sum: i32 = 0;
for s in scores_slice.iter_mut() {
*s = (256 + (*s - max) / 2).max(1);
sum += *s;
}
for o in output[..self.head_dim].iter_mut() {
*o = 0;
}
for i in 0..window_len {
let weight = (scores[i] * 256) / sum.max(1);
let value = &values[(window_start + i) % self.window_size];
for j in 0..self.head_dim {
output[j] += weight * value[j] as i32;
}
}
for o in output[..self.head_dim].iter_mut() {
*o >>= 8;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_micro_attention() {
let attn = MicroAttention::new(64, 4);
let query = [10i8; 16];
let key1 = [10i8; 16];
let key2 = [5i8; 16];
let keys: [&[i8]; 2] = [&key1, &key2];
let mut scores = [0i32; 2];
attn.compute_scores(&query, &keys, &mut scores);
assert!(scores[0] > scores[1]);
}
#[test]
fn test_softmax_fixed() {
let attn = MicroAttention::new(64, 4);
let mut scores = [100i32, 50, 0, -50];
attn.softmax_fixed(&mut scores);
let sum: i32 = scores.iter().sum();
assert!((sum - 256).abs() < 10);
assert!(scores[0] > scores[1]);
assert!(scores[1] > scores[2]);
assert!(scores[2] > scores[3]);
}
#[test]
fn test_linear_attention() {
let attn = LinearAttention::new(16);
let query = [10i8; 16];
let key = [10i8; 16];
let value = [5i8; 16];
let keys: [&[i8]; 1] = [&key];
let values: [&[i8]; 1] = [&value];
let mut output = [0i32; 16];
attn.forward(&query, &keys, &values, &mut output);
assert!(output.iter().any(|&x| x != 0));
}
}