const DEFAULT_SEED: u64 = 0xDEAD_BEEF_CAFE_F00D;
const DEFAULT_M: usize = 16;
const DEFAULT_EF_CONSTRUCTION: usize = 200;
const DEFAULT_EF_SEARCH: usize = 64;
const DEFAULT_FILTER_WIDEN: usize = 4;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HnswConfig {
pub m: usize,
pub ef_construction: usize,
pub ef_search: usize,
pub filter_widen: usize,
pub seed: u64,
}
impl HnswConfig {
#[must_use]
pub fn with_m(mut self, m: usize) -> Self {
self.m = m;
self
}
#[must_use]
pub fn with_ef_construction(mut self, ef_construction: usize) -> Self {
self.ef_construction = ef_construction;
self
}
#[must_use]
pub fn with_ef_search(mut self, ef_search: usize) -> Self {
self.ef_search = ef_search;
self
}
#[must_use]
pub fn with_filter_widen(mut self, filter_widen: usize) -> Self {
self.filter_widen = filter_widen;
self
}
#[must_use]
pub fn with_seed(mut self, seed: u64) -> Self {
self.seed = seed;
self
}
}
impl Default for HnswConfig {
fn default() -> Self {
Self {
m: DEFAULT_M,
ef_construction: DEFAULT_EF_CONSTRUCTION,
ef_search: DEFAULT_EF_SEARCH,
filter_widen: DEFAULT_FILTER_WIDEN,
seed: DEFAULT_SEED,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_values_are_the_documented_operating_point() {
let cfg = HnswConfig::default();
assert_eq!(cfg.m, 16);
assert_eq!(cfg.ef_construction, 200);
assert_eq!(cfg.ef_search, 64);
assert_eq!(cfg.filter_widen, 4);
assert_eq!(cfg.seed, 0xDEAD_BEEF_CAFE_F00D);
}
#[test]
fn with_helpers_compose() {
let cfg = HnswConfig::default()
.with_m(8)
.with_ef_construction(100)
.with_ef_search(32)
.with_filter_widen(2)
.with_seed(42);
assert_eq!(cfg.m, 8);
assert_eq!(cfg.ef_construction, 100);
assert_eq!(cfg.ef_search, 32);
assert_eq!(cfg.filter_widen, 2);
assert_eq!(cfg.seed, 42);
}
}