Skip to content

Commit 9c2d1c2

Browse files
authored
Merge pull request #163 from AdaWorldAPI/claude/review-rustynum-pr-80-2zNy5
Remove duplicate core::simd wrapper — delegate to rustynum_accel The core/simd.rs module was a thin forwarding wrapper that only called rustynum_accel::fingerprint_hamming. Moved hamming_distance, batch_hamming, HammingEngine, and simd_level directly into rustynum_accel.rs and updated all imports (server.rs, python/mod.rs, fingerprint.rs, core/mod.rs). rustynum-core provides all stable SIMD (std::arch, no nightly features): AVX-512 VPOPCNTDQ → AVX2 Harley-Seal → scalar POPCNT https://claude.ai/code/session_0152b2NJYnjCJjvMAmgsTx3p
2 parents 52135b8 + ced28ba commit 9c2d1c2

6 files changed

Lines changed: 115 additions & 179 deletions

File tree

src/bin/server.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ use arrow_ipc::writer::StreamWriter;
4848
use arrow_schema::{DataType, Field, Schema, SchemaRef};
4949

5050
use ladybug::core::Fingerprint;
51-
use ladybug::core::simd::{self, hamming_distance};
51+
use ladybug::core::rustynum_accel::{self as simd, hamming_distance};
5252
use ladybug::nars::TruthValue;
5353
use ladybug::storage::service::{CognitiveService, CpuFeatures, ServiceConfig};
5454
use ladybug::storage::{Addr, BindSpace, CogRedis, FINGERPRINT_WORDS, RedisResult};

src/core/fingerprint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ impl Fingerprint {
136136
/// Hamming distance to another fingerprint
137137
#[inline]
138138
pub fn hamming(&self, other: &Fingerprint) -> u32 {
139-
super::simd::hamming_distance(self, other)
139+
super::rustynum_accel::hamming_distance(self, other)
140140
}
141141

142142
/// Similarity (0.0 - 1.0)

src/core/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
mod buffer;
44
mod fingerprint;
55
mod scent;
6-
pub mod simd;
76
pub mod vsa;
87

98
pub mod rustynum_accel;
109

1110
pub use buffer::BufferPool;
1211
pub use fingerprint::Fingerprint;
1312
pub use scent::*;
14-
pub use simd::{HammingEngine, batch_hamming, hamming_distance};
13+
pub use rustynum_accel::{HammingEngine, batch_hamming, hamming_distance, simd_level};
1514
pub use vsa::VsaOps;
1615

1716
/// Dense embedding vector

src/core/rustynum_accel.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,116 @@ pub fn slice_dot_i8(a: &[u64], b: &[u64]) -> i64 {
179179
rustynum_core::simd::dot_i8(view_u64_as_bytes(a), view_u64_as_bytes(b))
180180
}
181181

182+
// ────────────────────────────────────────────────────────────────
183+
// Fingerprint-level convenience functions (formerly core::simd)
184+
// ────────────────────────────────────────────────────────────────
185+
186+
/// Compute Hamming distance between two fingerprints.
187+
///
188+
/// Delegates to rustynum's runtime-dispatched SIMD (AVX-512 → AVX2 → scalar).
189+
#[inline]
190+
pub fn hamming_distance(a: &Fingerprint, b: &Fingerprint) -> u32 {
191+
fingerprint_hamming(a, b)
192+
}
193+
194+
/// Batch Hamming distance computation (parallel when `parallel` feature is on)
195+
#[cfg(feature = "parallel")]
196+
pub fn batch_hamming(query: &Fingerprint, corpus: &[Fingerprint]) -> Vec<u32> {
197+
use rayon::prelude::*;
198+
corpus
199+
.par_iter()
200+
.map(|fp| hamming_distance(query, fp))
201+
.collect()
202+
}
203+
204+
/// Non-parallel batch Hamming
205+
#[cfg(not(feature = "parallel"))]
206+
pub fn batch_hamming(query: &Fingerprint, corpus: &[Fingerprint]) -> Vec<u32> {
207+
corpus
208+
.iter()
209+
.map(|fp| hamming_distance(query, fp))
210+
.collect()
211+
}
212+
213+
/// Hamming search engine with pre-allocated buffers
214+
pub struct HammingEngine {
215+
corpus: Vec<Fingerprint>,
216+
#[cfg(feature = "parallel")]
217+
thread_pool: rayon::ThreadPool,
218+
}
219+
220+
impl HammingEngine {
221+
/// Create new engine
222+
pub fn new() -> Self {
223+
Self {
224+
corpus: Vec::new(),
225+
#[cfg(feature = "parallel")]
226+
thread_pool: rayon::ThreadPoolBuilder::new()
227+
.num_threads(num_cpus::get())
228+
.build()
229+
.unwrap(),
230+
}
231+
}
232+
233+
/// Index corpus
234+
pub fn index(&mut self, corpus: Vec<Fingerprint>) {
235+
self.corpus = corpus;
236+
}
237+
238+
/// Search for k nearest neighbors
239+
pub fn search(&self, query: &Fingerprint, k: usize) -> Vec<(usize, u32, f32)> {
240+
let distances = batch_hamming(query, &self.corpus);
241+
242+
let mut indexed: Vec<(usize, u32)> = distances.into_iter().enumerate().collect();
243+
244+
let k = k.min(indexed.len());
245+
indexed.select_nth_unstable_by_key(k.saturating_sub(1), |&(_, d)| d);
246+
indexed.truncate(k);
247+
indexed.sort_by_key(|&(_, d)| d);
248+
249+
indexed
250+
.into_iter()
251+
.map(|(idx, dist)| {
252+
let similarity = 1.0 - (dist as f32 / crate::FINGERPRINT_BITS as f32);
253+
(idx, dist, similarity)
254+
})
255+
.collect()
256+
}
257+
258+
/// Search with threshold
259+
pub fn search_threshold(
260+
&self,
261+
query: &Fingerprint,
262+
threshold: f32,
263+
limit: usize,
264+
) -> Vec<(usize, u32, f32)> {
265+
let max_distance = ((1.0 - threshold) * crate::FINGERPRINT_BITS as f32) as u32;
266+
let mut results = self.search(query, limit);
267+
results.retain(|&(_, dist, _)| dist <= max_distance);
268+
results
269+
}
270+
271+
/// Corpus size
272+
pub fn len(&self) -> usize {
273+
self.corpus.len()
274+
}
275+
276+
pub fn is_empty(&self) -> bool {
277+
self.corpus.is_empty()
278+
}
279+
}
280+
281+
impl Default for HammingEngine {
282+
fn default() -> Self {
283+
Self::new()
284+
}
285+
}
286+
287+
/// Detect SIMD capability at runtime
288+
pub fn simd_level() -> &'static str {
289+
"rustynum-runtime-dispatch"
290+
}
291+
182292
// ────────────────────────────────────────────────────────────────
183293
// Tests
184294
// ────────────────────────────────────────────────────────────────

src/core/simd.rs

Lines changed: 0 additions & 173 deletions
This file was deleted.

src/python/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use pyo3::prelude::*;
3636
use pyo3::types::{PyBytes, PyList};
3737

3838
use crate::core::Fingerprint;
39-
use crate::core::simd::{batch_hamming as rust_batch_hamming, hamming_distance};
39+
use crate::core::rustynum_accel::{batch_hamming as rust_batch_hamming, hamming_distance};
4040
use crate::nars::TruthValue;
4141
use crate::storage::Database;
4242
use crate::{FINGERPRINT_BITS, FINGERPRINT_BYTES};
@@ -382,7 +382,7 @@ fn open(path: &str) -> PyResult<PyDatabase> {
382382
/// Get SIMD level
383383
#[pyfunction]
384384
fn simd_level() -> &'static str {
385-
crate::core::simd::simd_level()
385+
crate::core::rustynum_accel::simd_level()
386386
}
387387

388388
// =============================================================================

0 commit comments

Comments
 (0)