use ndarray::{Array1, Array2};
pub trait Backend {
fn init_embeddings(&self, vocab_size: usize, dim: usize) -> Array2<f32>;
fn dot(&self, a: &Array1<f32>, b: &Array1<f32>) -> f32;
fn add_scaled(&self, a: &mut Array1<f32>, b: &Array1<f32>, scale: f32);
fn name(&self) -> &'static str;
}
#[derive(Default)]
pub struct CpuBackend;
impl CpuBackend {
pub fn new() -> Self {
Self
}
}
impl Backend for CpuBackend {
fn init_embeddings(&self, vocab_size: usize, dim: usize) -> Array2<f32> {
use ndarray::Array;
use rand::Rng;
let mut rng = rand::thread_rng();
let scale = 1.0 / (dim as f32).sqrt();
Array::from_shape_fn((vocab_size, dim), |_| rng.gen_range(-0.5..0.5) * scale)
}
fn dot(&self, a: &Array1<f32>, b: &Array1<f32>) -> f32 {
a.iter().zip(b.iter()).map(|(&x, &y)| x * y).sum()
}
fn add_scaled(&self, a: &mut Array1<f32>, b: &Array1<f32>, scale: f32) {
for (ai, bi) in a.iter_mut().zip(b.iter()) {
*ai += bi * scale;
}
}
fn name(&self) -> &'static str {
"cpu"
}
}
pub fn default_backend() -> Box<dyn Backend> {
Box::new(CpuBackend::new())
}