pub mod micro_hnsw;
pub mod semantic_memory;
pub mod rag;
pub mod anomaly;
pub mod federated_search;
pub mod hyperbolic;
pub use micro_hnsw::{MicroHNSW, HNSWConfig, SearchResult};
pub use semantic_memory::{SemanticMemory, Memory, MemoryType};
pub use rag::{MicroRAG, RAGConfig, RAGResult};
pub use anomaly::{AnomalyDetector, AnomalyConfig, AnomalyResult};
pub use federated_search::{FederatedIndex, ShardConfig};
pub use hyperbolic::{
HyperbolicConfig, poincare_distance_i8, lorentz_distance_spatial_i8,
to_poincare_i8, to_lorentz_i8, hyperbolic_midpoint,
};
use heapless::Vec as HVec;
pub const MAX_DIMENSIONS: usize = 128;
pub const MAX_VECTORS: usize = 1000;
pub const MAX_NEIGHBORS: usize = 16;
#[derive(Debug, Clone)]
pub struct MicroVector<const DIM: usize> {
pub data: HVec<i8, DIM>,
pub id: u32,
}
impl<const DIM: usize> MicroVector<DIM> {
pub fn from_i8(data: &[i8], id: u32) -> Option<Self> {
if data.len() > DIM {
return None;
}
let mut vec = HVec::new();
for &v in data {
vec.push(v).ok()?;
}
Some(Self { data: vec, id })
}
pub fn from_f32(data: &[f32], id: u32) -> Option<Self> {
if data.len() > DIM {
return None;
}
let mut vec = HVec::new();
for &v in data {
let quantized = (v * 127.0).clamp(-128.0, 127.0) as i8;
vec.push(quantized).ok()?;
}
Some(Self { data: vec, id })
}
pub fn dim(&self) -> usize {
self.data.len()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DistanceMetric {
Euclidean,
Cosine,
Manhattan,
Hamming,
DotProduct,
Poincare,
Lorentz,
}
impl DistanceMetric {
pub fn distance(&self, a: &[i8], b: &[i8]) -> i32 {
match self {
Self::Euclidean => euclidean_distance_i8(a, b),
Self::Cosine => cosine_distance_i8(a, b),
Self::Manhattan => manhattan_distance_i8(a, b),
Self::Hamming => hamming_distance_i8(a, b),
Self::DotProduct => -dot_product_i8(a, b), Self::Poincare => poincare_distance_i8(a, b),
Self::Lorentz => lorentz_distance_spatial_i8(a, b),
}
}
pub const fn is_hyperbolic(&self) -> bool {
matches!(self, Self::Poincare | Self::Lorentz)
}
}
pub fn euclidean_distance_i8(a: &[i8], b: &[i8]) -> i32 {
let mut sum: i32 = 0;
for (x, y) in a.iter().zip(b.iter()) {
let diff = (*x as i32) - (*y as i32);
sum += diff * diff;
}
sum
}
pub fn cosine_distance_i8(a: &[i8], b: &[i8]) -> i32 {
let mut dot: i32 = 0;
let mut norm_a: i32 = 0;
let mut norm_b: i32 = 0;
for (x, y) in a.iter().zip(b.iter()) {
let xi = *x as i32;
let yi = *y as i32;
dot += xi * yi;
norm_a += xi * xi;
norm_b += yi * yi;
}
if norm_a == 0 || norm_b == 0 {
return i32::MAX;
}
let norm_product = ((norm_a as i64) * (norm_b as i64)).min(i64::MAX as i64);
let norm_sqrt = isqrt(norm_product as u64) as i32;
if norm_sqrt == 0 {
return i32::MAX;
}
1000 - ((dot * 1000) / norm_sqrt)
}
pub fn manhattan_distance_i8(a: &[i8], b: &[i8]) -> i32 {
let mut sum: i32 = 0;
for (x, y) in a.iter().zip(b.iter()) {
sum += ((*x as i32) - (*y as i32)).abs();
}
sum
}
pub fn hamming_distance_i8(a: &[i8], b: &[i8]) -> i32 {
let mut count = 0i32;
for (x, y) in a.iter().zip(b.iter()) {
count += (*x ^ *y).count_ones() as i32;
}
count
}
pub fn dot_product_i8(a: &[i8], b: &[i8]) -> i32 {
let mut sum: i32 = 0;
for (x, y) in a.iter().zip(b.iter()) {
sum += (*x as i32) * (*y as i32);
}
sum
}
fn isqrt(n: u64) -> u64 {
if n == 0 {
return 0;
}
let mut x = n;
let mut y = (x + 1) / 2;
while y < x {
x = y;
y = (x + n / x) / 2;
}
x
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_euclidean_distance() {
let a = [10i8, 20, 30, 40];
let b = [11i8, 21, 31, 41];
let dist = euclidean_distance_i8(&a, &b);
assert_eq!(dist, 4); }
#[test]
fn test_micro_vector() {
let data = [1i8, 2, 3, 4, 5, 6, 7, 8];
let vec: MicroVector<16> = MicroVector::from_i8(&data, 42).unwrap();
assert_eq!(vec.dim(), 8);
assert_eq!(vec.id, 42);
}
#[test]
fn test_cosine_distance() {
let a = [100i8, 0, 0, 0];
let b = [50i8, 0, 0, 0];
let dist = cosine_distance_i8(&a, &b);
assert!(dist < 100); }
}