use crate::codebook::Codebook;
use crate::hyper::Hyper;
#[derive(Clone)]
pub struct HoloMemory {
dim: usize,
key_seed: u64,
values: Codebook,
memory: Hyper,
len: usize,
}
impl HoloMemory {
pub fn new(dim: usize, n_values: usize, seed: u64) -> Self {
let key_seed = seed ^ 0x4B45_5953_4545_4421;
let value_seed = seed ^ 0x5641_4C55_4553_2121;
HoloMemory {
dim,
key_seed,
values: Codebook::new(dim, n_values, value_seed),
memory: Hyper::zeros(dim),
len: 0,
}
}
#[inline]
pub fn dim(&self) -> usize {
self.dim
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn memory(&self) -> &Hyper {
&self.memory
}
#[inline]
pub fn values(&self) -> &Codebook {
&self.values
}
pub fn key_atom(&self, key: usize) -> Hyper {
Hyper::atom(self.dim, self.key_seed, key as u64)
}
pub fn insert(&mut self, key: usize, value: usize) {
let bound = self.key_atom(key).bind(&self.values.atom(value));
self.memory.bundle_into(&bound);
self.len += 1;
}
pub fn remove(&mut self, key: usize, value: usize) {
let bound = self.key_atom(key).bind(&self.values.atom(value));
self.memory.unbundle(&bound);
self.len = self.len.saturating_sub(1);
}
pub fn probe(&self, key: usize) -> Hyper {
self.memory.bind(&self.key_atom(key))
}
pub fn get(&self, key: usize) -> (usize, f32) {
self.values.cleanup(&self.probe(key))
}
pub fn batch_get(&self, keys: &[usize]) -> Vec<(usize, f32)> {
let probes: Vec<Hyper> = keys.iter().map(|&k| self.probe(k)).collect();
self.values.cleanup_batch(&probes)
}
pub fn estimated_capacity(&self) -> f64 {
let m = self.values.len().max(2) as f64;
self.dim as f64 / (2.0 * m.ln())
}
pub fn load_factor(&self) -> f64 {
let cap = self.estimated_capacity();
if cap == 0.0 {
f64::INFINITY
} else {
self.len as f64 / cap
}
}
}
impl core::fmt::Debug for HoloMemory {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"HoloMemory {{ dim: {}, entries: {}, values: {}, load: {:.2} }}",
self.dim,
self.len,
self.values.len(),
self.load_factor()
)
}
}