use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub const DEFAULT_HDC_DIM: usize = 10000;
pub trait HypervectorOps {
fn bind(&self, other: &Self) -> Self;
fn bundle(vectors: &[&Self]) -> Self
where
Self: Sized;
fn permute(&self, shift: usize) -> Self;
fn similarity(&self, other: &Self) -> f32;
fn normalize(&mut self);
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Hypervector {
pub components: Vec<f32>,
}
impl Hypervector {
pub fn zeros(dim: usize) -> Self {
Self {
components: vec![0.0; dim],
}
}
pub fn random(dim: usize) -> Self {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut components = Vec::with_capacity(dim);
let mut hasher = DefaultHasher::new();
for i in 0..dim {
i.hash(&mut hasher);
let h = hasher.finish();
let value = (h as f32 / u64::MAX as f32) * 2.0 - 1.0;
components.push(value);
hasher = DefaultHasher::new();
h.hash(&mut hasher);
}
Self { components }
}
pub fn from_scalar(value: f32, dim: usize) -> Self {
let seed = (value * 1000000.0) as u64;
let mut components = Vec::with_capacity(dim);
for i in 0..dim {
let mixed = seed.wrapping_mul(6364136223846793005).wrapping_add(i as u64);
let normalized = (mixed as f32 / u64::MAX as f32) * 2.0 - 1.0;
components.push(normalized);
}
Self { components }
}
pub fn from_bytes(bytes: &[u8], dim: usize) -> Self {
let mut components = vec![0.0; dim];
for (i, &b) in bytes.iter().enumerate() {
let idx = i % dim;
components[idx] += (b as f32 / 255.0) * 2.0 - 1.0;
}
let mut hv = Self { components };
hv.normalize();
hv
}
pub fn dim(&self) -> usize {
self.components.len()
}
pub fn norm(&self) -> f32 {
self.components.iter().map(|x| x * x).sum::<f32>().sqrt()
}
pub fn scale(&mut self, factor: f32) {
for c in &mut self.components {
*c *= factor;
}
}
pub fn add(&mut self, other: &Self) {
for (a, b) in self.components.iter_mut().zip(other.components.iter()) {
*a += b;
}
}
}
impl HypervectorOps for Hypervector {
fn bind(&self, other: &Self) -> Self {
let components: Vec<f32> = self
.components
.iter()
.zip(other.components.iter())
.map(|(a, b)| a * b)
.collect();
Self { components }
}
fn bundle(vectors: &[&Self]) -> Self {
if vectors.is_empty() {
return Self::zeros(DEFAULT_HDC_DIM);
}
let dim = vectors[0].dim();
let mut result = Self::zeros(dim);
for v in vectors {
result.add(v);
}
result.normalize();
result
}
fn permute(&self, shift: usize) -> Self {
let n = self.components.len();
let shift = shift % n;
let mut components = vec![0.0; n];
for i in 0..n {
components[(i + shift) % n] = self.components[i];
}
Self { components }
}
fn similarity(&self, other: &Self) -> f32 {
let dot: f32 = self
.components
.iter()
.zip(other.components.iter())
.map(|(a, b)| a * b)
.sum();
let norm_a = self.norm();
let norm_b = other.norm();
if norm_a < 1e-10 || norm_b < 1e-10 {
return 0.0;
}
dot / (norm_a * norm_b)
}
fn normalize(&mut self) {
let norm = self.norm();
if norm > 1e-10 {
for c in &mut self.components {
*c /= norm;
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WitnessEncoding {
pub hypervector: Hypervector,
pub witness_id: String,
pub energy: f32,
pub allow: bool,
pub timestamp_ms: u64,
}
impl WitnessEncoding {
pub fn new(
witness_id: impl Into<String>,
energy: f32,
allow: bool,
policy_hash: &[u8],
dim: usize,
) -> Self {
let witness_id = witness_id.into();
let energy_hv = Hypervector::from_scalar(energy, dim);
let decision_hv = Hypervector::from_scalar(if allow { 1.0 } else { -1.0 }, dim);
let policy_hv = Hypervector::from_bytes(policy_hash, dim);
let bound = energy_hv.bind(&decision_hv).bind(&policy_hv);
Self {
hypervector: bound,
witness_id,
energy,
allow,
timestamp_ms: current_time_ms(),
}
}
pub fn similarity(&self, other: &Self) -> f32 {
self.hypervector.similarity(&other.hypervector)
}
}
pub struct HdcMemory {
encodings: HashMap<String, WitnessEncoding>,
dim: usize,
capacity: usize,
}
impl HdcMemory {
pub fn new(dim: usize, capacity: usize) -> Self {
Self {
encodings: HashMap::with_capacity(capacity),
dim,
capacity,
}
}
pub fn store(&mut self, encoding: WitnessEncoding) {
if self.encodings.len() >= self.capacity {
if let Some(oldest_id) = self
.encodings
.iter()
.min_by_key(|(_, e)| e.timestamp_ms)
.map(|(id, _)| id.clone())
{
self.encodings.remove(&oldest_id);
}
}
self.encodings.insert(encoding.witness_id.clone(), encoding);
}
pub fn retrieve(&self, query: &Hypervector, threshold: f32) -> Vec<(String, f32)> {
let mut results: Vec<_> = self
.encodings
.iter()
.map(|(id, enc)| {
let sim = enc.hypervector.similarity(query);
(id.clone(), sim)
})
.filter(|(_, sim)| *sim >= threshold)
.collect();
results.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
results
}
pub fn get(&self, id: &str) -> Option<&WitnessEncoding> {
self.encodings.get(id)
}
pub fn len(&self) -> usize {
self.encodings.len()
}
pub fn is_empty(&self) -> bool {
self.encodings.is_empty()
}
pub fn clear(&mut self) {
self.encodings.clear();
}
}
impl std::fmt::Debug for HdcMemory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HdcMemory")
.field("dim", &self.dim)
.field("stored", &self.encodings.len())
.field("capacity", &self.capacity)
.finish()
}
}
fn current_time_ms() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hypervector_operations() {
let a = Hypervector::random(1000);
let b = Hypervector::random(1000);
let self_sim = a.similarity(&a);
assert!((self_sim - 1.0).abs() < 0.01);
let cross_sim = a.similarity(&b);
assert!(cross_sim.abs() < 0.2);
}
#[test]
fn test_hypervector_bind() {
let a = Hypervector::from_scalar(1.0, 1000);
let b = Hypervector::from_scalar(2.0, 1000);
let bound = a.bind(&b);
assert_eq!(bound.dim(), 1000);
}
#[test]
fn test_witness_encoding() {
let enc = WitnessEncoding::new(
"test_witness",
0.5,
true,
&[1, 2, 3, 4],
1000,
);
assert_eq!(enc.witness_id, "test_witness");
assert!(enc.allow);
}
#[test]
fn test_hdc_memory() {
let mut memory = HdcMemory::new(1000, 100);
let enc = WitnessEncoding::new("w1", 0.5, true, &[1, 2, 3], 1000);
memory.store(enc);
assert_eq!(memory.len(), 1);
assert!(memory.get("w1").is_some());
}
}