Skip to main content

csm_core_lib/
hyperdim_ops.rs

1//! Hyperdimensional computing operations and trait abstractions.
2//!
3//! Contains the `Hypervector` trait definition, its implementation for `HVec10240`,
4//! and scalar helper functions used by the bundle algorithm.
5
6use serde::{Deserialize, Serialize};
7use std::fmt::Debug;
8use std::hash::Hash;
9
10use crate::error::Result;
11use crate::hyperdim::HVec10240;
12
13/// Common interface for hypervectors
14pub trait Hypervector:
15    Debug + Clone + Copy + PartialEq + Eq + Hash + Send + Sync + Serialize + for<'de> Deserialize<'de>
16{
17    const DIMENSION: usize;
18    const FORMAT_NAME: &'static str;
19    fn zero() -> Self;
20    fn random() -> Self;
21    fn new_seeded(seed: u64) -> Self;
22    fn bundle(vectors: &[&Self]) -> Result<Self>;
23    fn bind(&self, other: &Self) -> Self;
24    fn cosine_similarity(&self, other: &Self) -> f32;
25    fn hamming_distance(&self, other: &Self) -> u32;
26    fn permute(&self, shift: usize) -> Self;
27    fn to_bytes(&self) -> Vec<u8>;
28    fn from_bytes(bytes: &[u8]) -> Result<Self>;
29}
30
31impl Hypervector for HVec10240 {
32    const DIMENSION: usize = 10240;
33    const FORMAT_NAME: &'static str = "f32";
34
35    fn zero() -> Self {
36        Self::zero()
37    }
38
39    fn random() -> Self {
40        Self::random()
41    }
42
43    fn new_seeded(seed: u64) -> Self {
44        Self::new_seeded(seed)
45    }
46
47    fn bundle(vectors: &[&Self]) -> Result<Self> {
48        // HVec10240::bundle expects &[Self], but trait gives &[&Self]
49        let owned_vecs: Vec<Self> = vectors.iter().map(|&v| *v).collect();
50        Self::bundle(&owned_vecs)
51    }
52
53    fn bind(&self, other: &Self) -> Self {
54        self.bind(other)
55    }
56
57    fn cosine_similarity(&self, other: &Self) -> f32 {
58        self.cosine_similarity(other)
59    }
60
61    fn hamming_distance(&self, other: &Self) -> u32 {
62        self.hamming_distance(other)
63    }
64
65    fn permute(&self, shift: usize) -> Self {
66        self.permute(shift)
67    }
68
69    fn to_bytes(&self) -> Vec<u8> {
70        self.to_bytes()
71    }
72
73    fn from_bytes(bytes: &[u8]) -> Result<Self> {
74        Self::from_bytes(bytes)
75    }
76}
77
78/// Max bit-planes for bit-sliced bundle accumulation.
79///
80/// Supports N up to `2^64 - 1`. Shared by HVec (`bundle_word_scalar`) and
81/// BHVec (`bundle_word_u64`) so capacity stays in lockstep.
82pub const BUNDLE_MAX_PLANES: usize = 64;
83
84/// Scalar bit-sliced addition for a single `u128` word (HVec).
85///
86/// Centralized helper for sequential and parallel fallback paths.
87/// Semantics: bit set when `count >= threshold` (callers pass `N/2 + 1`).
88/// Must stay in lockstep with [`bundle_word_u64`].
89#[allow(dead_code)]
90pub fn bundle_word_scalar(
91    vectors: &[HVec10240],
92    word_idx: usize,
93    threshold: usize,
94    num_planes: usize,
95) -> u128 {
96    debug_assert!(
97        num_planes <= BUNDLE_MAX_PLANES,
98        "num_planes={num_planes} exceeds BUNDLE_MAX_PLANES={BUNDLE_MAX_PLANES}"
99    );
100    let mut planes = [0u128; BUNDLE_MAX_PLANES];
101    for v in vectors {
102        let mut carry = v.data[word_idx];
103        for plane in planes.iter_mut().take(num_planes) {
104            let next_carry = *plane & carry;
105            *plane ^= carry;
106            carry = next_carry;
107            if carry == 0 {
108                break;
109            }
110        }
111    }
112    let (mut current_eq, mut current_gt) = (!0u128, 0u128);
113    for p in (0..num_planes).rev() {
114        if ((threshold >> p) & 1) == 1 {
115            current_eq &= planes[p];
116        } else {
117            current_gt |= current_eq & planes[p];
118            current_eq &= !planes[p];
119        }
120    }
121    current_gt | current_eq
122}
123
124/// Bit-sliced majority for a single `u64` word (BHVec).
125///
126/// Same algorithm as [`bundle_word_scalar`], differing only in word width.
127/// Callers pass `threshold = N/2 + 1` and `num_planes = bit_width(N)`.
128pub fn bundle_word_u64(
129    words: impl IntoIterator<Item = u64>,
130    threshold: usize,
131    num_planes: usize,
132) -> u64 {
133    debug_assert!(
134        num_planes <= BUNDLE_MAX_PLANES,
135        "num_planes={num_planes} exceeds BUNDLE_MAX_PLANES={BUNDLE_MAX_PLANES}"
136    );
137    let mut planes = [0u64; BUNDLE_MAX_PLANES];
138    for word in words {
139        let mut carry = word;
140        for plane in planes.iter_mut().take(num_planes) {
141            let next_carry = *plane & carry;
142            *plane ^= carry;
143            carry = next_carry;
144            if carry == 0 {
145                break;
146            }
147        }
148    }
149    let (mut current_eq, mut current_gt) = (!0u64, 0u64);
150    for p in (0..num_planes).rev() {
151        if ((threshold >> p) & 1) == 1 {
152            current_eq &= planes[p];
153        } else {
154            current_gt |= current_eq & planes[p];
155            current_eq &= !planes[p];
156        }
157    }
158    current_gt | current_eq
159}