use crate::types::{RootVector, RootSpace};
use alloc::vec::Vec;
pub fn project_to_root(input: &[f32], root_space: &RootSpace) -> RootVector {
root_space.project(input)
}
pub fn embed_from_root(
root_vector: &RootVector,
root_space: &RootSpace,
target_dim: usize
) -> Vec<f32> {
let mut output = Vec::with_capacity(target_dim);
for _ in 0..target_dim {
output.push(0.0);
}
for i in 0..32 {
let coefficient = root_vector.data[i];
let basis_vec = &root_space.basis[i];
for j in 0..target_dim.min(32) {
output[j] += coefficient * basis_vec.data[j];
}
}
output
}
pub struct StreamingProjector {
basis: Vec<RootVector>,
learning_rate: f32,
sample_count: u64,
}
impl StreamingProjector {
pub fn new(initial_basis: Vec<RootVector>, learning_rate: f32) -> Self {
Self {
basis: initial_basis,
learning_rate,
sample_count: 0,
}
}
pub fn project_and_update(&mut self, input: &[f32]) -> RootVector {
let mut result = RootVector::zero();
let mut input_vec = RootVector::zero();
let copy_len = input.len().min(32);
input_vec.data[..copy_len].copy_from_slice(&input[..copy_len]);
for i in 0..32 {
result.data[i] = self.basis[i].dot(&input_vec);
}
let lr = self.learning_rate / libm::sqrtf((self.sample_count + 1) as f32);
for i in 0..32 {
let y_i = result.data[i];
for j in 0..input.len().min(32) {
let x_j = input[j];
let w_ij = self.basis[i].data[j];
self.basis[i].data[j] += lr * y_i * (x_j - y_i * w_ij);
}
}
if self.sample_count % 1000 == 999 {
self.reorthogonalize();
}
self.sample_count += 1;
result
}
fn reorthogonalize(&mut self) {
for i in 0..32 {
self.basis[i].normalize();
self.basis[i].scale(libm::sqrtf(2.0));
for j in (i+1)..32 {
let dot = self.basis[i].dot(&self.basis[j]);
for k in 0..32 {
self.basis[j].data[k] -= dot * self.basis[i].data[k] / 2.0;
}
}
}
}
}
pub fn compute_attention_rank(attention_weights: &[f32], dim: usize) -> usize {
let max_weight = attention_weights.iter()
.map(|w| libm::fabsf(*w))
.fold(0.0f32, f32::max);
let sum_weights: f32 = attention_weights.iter()
.map(|w| libm::fabsf(*w))
.sum();
if max_weight > 0.9 * sum_weights {
1 } else {
dim }
}
pub fn suggest_root_count(input_dim: usize) -> usize {
let sqrt_dim = libm::sqrtf(input_dim as f32);
let suggested = libm::ceilf(sqrt_dim / 2.0) as usize;
suggested.max(8).min(64)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::RootSpace;
use alloc::vec;
#[test]
fn test_projection_embedding_roundtrip() {
let root_space = RootSpace::new();
let input = vec![1.0; 32];
let root_vec = project_to_root(&input, &root_space);
let reconstructed = embed_from_root(&root_vec, &root_space, 32);
let error: f32 = input.iter()
.zip(reconstructed.iter())
.map(|(a, b)| (a - b).powi(2))
.sum();
assert!(error < 100.0);
}
#[test]
fn test_streaming_projector() {
let basis = (0..32)
.map(|_| RootVector::zero())
.collect();
let mut projector = StreamingProjector::new(basis, 0.01);
for _ in 0..10 {
let input = vec![1.0; 32];
let _ = projector.project_and_update(&input);
}
assert_eq!(projector.sample_count, 10);
}
#[test]
fn test_adaptive_root_count() {
assert_eq!(suggest_root_count(64), 8);
assert_eq!(suggest_root_count(256), 8);
assert_eq!(suggest_root_count(768), 14);
assert_eq!(suggest_root_count(2048), 23);
}
}