Skip to content

Commit 6d67de2

Browse files
authored
Merge pull request #107 from AdaWorldAPI/claude/teleport-session-setup-wMZfb
feat(fingerprint): VectorWidth config + dequantize_i8_to_f32
2 parents 34fdbf3 + e658a67 commit 6d67de2

1 file changed

Lines changed: 181 additions & 0 deletions

File tree

src/hpc/fingerprint.rs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,129 @@ impl<const N: usize> Fingerprint<N> {
8484
out
8585
}
8686

87+
/// Get a specific bit (0-indexed).
88+
#[inline]
89+
pub fn get_bit(&self, index: usize) -> bool {
90+
debug_assert!(index < Self::BITS);
91+
let word_idx = index / 64;
92+
let bit_idx = index % 64;
93+
(self.words[word_idx] >> bit_idx) & 1 == 1
94+
}
95+
96+
/// Set a specific bit.
97+
#[inline]
98+
pub fn set_bit(&mut self, index: usize, value: bool) {
99+
debug_assert!(index < Self::BITS);
100+
let word_idx = index / 64;
101+
let bit_idx = index % 64;
102+
if value {
103+
self.words[word_idx] |= 1u64 << bit_idx;
104+
} else {
105+
self.words[word_idx] &= !(1u64 << bit_idx);
106+
}
107+
}
108+
109+
/// Toggle a specific bit.
110+
#[inline]
111+
pub fn toggle_bit(&mut self, index: usize) {
112+
debug_assert!(index < Self::BITS);
113+
let word_idx = index / 64;
114+
let bit_idx = index % 64;
115+
self.words[word_idx] ^= 1u64 << bit_idx;
116+
}
117+
118+
/// Create a random fingerprint from a seed (xorshift128+).
119+
pub fn random(seed: u64) -> Self {
120+
let mut s0 = seed;
121+
let mut s1 = seed.wrapping_mul(0x9E3779B97F4A7C15);
122+
let mut words = [0u64; N];
123+
for word in &mut words {
124+
let mut s = s0;
125+
s0 = s1;
126+
s ^= s << 23;
127+
s ^= s >> 18;
128+
s ^= s1;
129+
s ^= s1 >> 5;
130+
s1 = s;
131+
*word = s0.wrapping_add(s1);
132+
}
133+
Self { words }
134+
}
135+
87136
/// Hamming distance (number of differing bits).
88137
/// Delegates to ndarray's SIMD dispatch (AVX-512 → AVX2 → scalar).
89138
#[inline]
90139
pub fn hamming_distance(&self, other: &Self) -> u32 {
91140
super::bitwise::hamming_distance_raw(self.as_bytes(), other.as_bytes()) as u32
92141
}
93142

143+
/// Alias for `hamming_distance` (ladybug-rs compat).
144+
#[inline]
145+
pub fn hamming(&self, other: &Self) -> u32 {
146+
self.hamming_distance(other)
147+
}
148+
149+
/// XOR bind (ladybug-rs compat). Returns a new fingerprint.
150+
#[inline]
151+
pub fn bind(&self, other: &Self) -> Self {
152+
let mut words = [0u64; N];
153+
for i in 0..N { words[i] = self.words[i] ^ other.words[i]; }
154+
Self { words }
155+
}
156+
157+
/// AND (bitwise intersection).
158+
#[inline]
159+
pub fn and(&self, other: &Self) -> Self {
160+
let mut words = [0u64; N];
161+
for i in 0..N { words[i] = self.words[i] & other.words[i]; }
162+
Self { words }
163+
}
164+
165+
/// Bitwise NOT.
166+
#[inline]
167+
pub fn not(&self) -> Self {
168+
let mut words = [0u64; N];
169+
for i in 0..N { words[i] = !self.words[i]; }
170+
Self { words }
171+
}
172+
173+
/// Density: fraction of set bits (popcount / total bits).
174+
#[inline]
175+
pub fn density(&self) -> f32 {
176+
self.popcount() as f32 / Self::BITS as f32
177+
}
178+
179+
/// Access raw words as slice.
180+
#[inline]
181+
pub fn as_raw(&self) -> &[u64; N] {
182+
&self.words
183+
}
184+
185+
/// Create from content string (SHA-256-like hash expansion).
186+
pub fn from_content(data: &str) -> Self {
187+
let mut h = 0x736f6d6570736575u64;
188+
for (i, b) in data.bytes().enumerate() {
189+
h ^= (b as u64) << ((i % 8) * 8);
190+
h = h.rotate_left(13).wrapping_mul(5).wrapping_add(0xe6546b64);
191+
}
192+
Self::random(h)
193+
}
194+
195+
/// Permute: circular bit shift by `positions` (positive = left).
196+
pub fn permute(&self, positions: i32) -> Self {
197+
let total = Self::BITS as i32;
198+
let shift = ((positions % total) + total) % total;
199+
if shift == 0 { return self.clone(); }
200+
let mut result = Self::zero();
201+
for i in 0..Self::BITS {
202+
if self.get_bit(i) {
203+
let new_pos = ((i as i32 + shift) % total) as usize;
204+
result.set_bit(new_pos, true);
205+
}
206+
}
207+
result
208+
}
209+
94210
/// Hamming weight (number of set bits).
95211
#[inline]
96212
pub fn popcount(&self) -> u32 {
@@ -265,6 +381,71 @@ pub type Fingerprint1K = Fingerprint<128>;
265381
/// 64K-bit fingerprint (recognition projections).
266382
pub type Fingerprint64K = Fingerprint<1024>;
267383

384+
// ─── Vector width config (LazyLock, switchable) ─────────────────
385+
386+
use std::sync::LazyLock;
387+
388+
/// Supported vector widths for the BindSpace substrate.
389+
///
390+
/// NOTE: 4096 is NOT a vector width — it's the 0xFFF schema/command address
391+
/// space (4096 CAM operations, verb vocabulary). Vectors are 8K or 16K.
392+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
393+
#[repr(u16)]
394+
pub enum VectorWidth {
395+
/// 8,192 bits = 128 words = 1 KB. Deprecated, still referenced in some code.
396+
W8K = 128,
397+
/// 16,384 bits = 256 words = 2 KB. Production default.
398+
W16K = 256,
399+
}
400+
401+
/// Runtime vector configuration. Frozen on first access.
402+
///
403+
/// Like `simd_caps()` — detect once, read everywhere.
404+
/// Controls serialization format, network protocol, and storage layout.
405+
/// Does NOT change the Rust type (use the matching Fingerprint\<N\> alias).
406+
#[derive(Clone, Copy, Debug)]
407+
pub struct VectorConfig {
408+
pub width: VectorWidth,
409+
pub words: usize,
410+
pub bits: usize,
411+
pub bytes: usize,
412+
}
413+
414+
impl VectorConfig {
415+
const fn from_width(w: VectorWidth) -> Self {
416+
let words = w as usize;
417+
VectorConfig { width: w, words, bits: words * 64, bytes: words * 8 }
418+
}
419+
}
420+
421+
static VECTOR_WIDTH: LazyLock<VectorConfig> = LazyLock::new(|| {
422+
let w = std::env::var("NDARRAY_VECTOR_WIDTH")
423+
.ok()
424+
.and_then(|s| match s.as_str() {
425+
"8192" | "8k" | "8K" => Some(VectorWidth::W8K),
426+
"16384" | "16k" | "16K" => Some(VectorWidth::W16K),
427+
_ => None,
428+
})
429+
.unwrap_or(VectorWidth::W16K);
430+
VectorConfig::from_width(w)
431+
});
432+
433+
/// Get the frozen vector width configuration.
434+
///
435+
/// Defaults to 16K (production). Override with `NDARRAY_VECTOR_WIDTH=8192`
436+
/// env var before first access. After first call, width is frozen.
437+
///
438+
/// ```
439+
/// use ndarray::hpc::fingerprint::vector_config;
440+
/// let cfg = vector_config();
441+
/// assert_eq!(cfg.bits, 16_384); // default
442+
/// assert_eq!(cfg.words, 256);
443+
/// assert_eq!(cfg.bytes, 2_048);
444+
/// ```
445+
pub fn vector_config() -> &'static VectorConfig {
446+
&VECTOR_WIDTH
447+
}
448+
268449
#[cfg(test)]
269450
mod tests {
270451
use super::*;

0 commit comments

Comments
 (0)