use std::time::{Duration, Instant};
use yo_common::Rng;
use yo_vector::{Bits, Partitions, Tuning, Vectors};
struct Store {
dim: usize,
data: Vec<f32>,
}
impl Vectors for Store {
fn get(&self, id: u64, into: &mut [f32]) -> bool {
let at = id as usize * self.dim;
let Some(v) = self.data.get(at..at + self.dim) else {
return false;
};
into.copy_from_slice(v);
true
}
}
fn main() {
let n: usize = std::env::args()
.nth(1)
.and_then(|a| a.parse().ok())
.unwrap_or(200_000);
let dim = 128;
let store = corpus(dim, n, 200, 0x9e37);
println!("{n} vectors, {dim} dimensions, one core");
println!(
"{:>10}{:>12}{:>12}{:>12}{:>12}{:>10}",
"at", "partitions", "a second", "insert", "maintain", "touched"
);
let mut ix = Partitions::new(dim, Bits::One, 0x51f7, Tuning::default());
let mut buf = vec![0f32; dim];
let mut mark = 12_500usize;
let (mut inserting, mut maintaining) = (Duration::ZERO, Duration::ZERO);
let (mut was_i, mut was_m, mut was_at) = (Duration::ZERO, Duration::ZERO, 0usize);
let (mut touched, mut was_t) = (0usize, 0usize);
for id in 0..n as u64 {
store.get(id, &mut buf);
let t = Instant::now();
ix.insert(id, &buf);
inserting += t.elapsed();
let t = Instant::now();
if ix.needs_maintenance() {
touched += ix.maintain(&store, 4);
}
maintaining += t.elapsed();
let at = id as usize + 1;
if at == mark || at == n {
let took = (inserting - was_i) + (maintaining - was_m);
println!(
"{at:>10}{:>12}{:>12.0}{:>11.1}%{:>11.1}%{:>10.1}",
ix.partitions(),
(at - was_at) as f64 / took.as_secs_f64(),
(inserting - was_i).as_secs_f64() * 100.0 / took.as_secs_f64(),
(maintaining - was_m).as_secs_f64() * 100.0 / took.as_secs_f64(),
(touched - was_t) as f64 / (at - was_at) as f64,
);
(was_i, was_m, was_at, was_t) = (inserting, maintaining, at, touched);
mark *= 2;
}
}
let total = inserting + maintaining;
println!();
println!(
"{n} vectors in {total:?}, {:.0} a second overall, {:?} inserting and {:?} maintaining",
n as f64 / total.as_secs_f64(),
inserting,
maintaining,
);
}
fn corpus(dim: usize, n: usize, clusters: usize, seed: u64) -> Store {
let mut rng = Rng::new(seed);
let centres: Vec<Vec<f32>> = (0..clusters).map(|_| draw(dim, &mut rng)).collect();
let mut data = Vec::with_capacity(n * dim);
for i in 0..n {
let centre = ¢res[i % clusters];
let mut v = draw(dim, &mut rng);
for (x, c) in v.iter_mut().zip(centre) {
*x = *x * 0.35 + c;
}
unit(&mut v);
data.extend_from_slice(&v);
}
Store { dim, data }
}
fn draw(dim: usize, rng: &mut Rng) -> Vec<f32> {
let mut v = Vec::with_capacity(dim);
while v.len() < dim {
let u1 = ((rng.next_u64() >> 11) as f64 / (1u64 << 53) as f64).max(1e-12);
let u2 = (rng.next_u64() >> 11) as f64 / (1u64 << 53) as f64;
let r = (-2.0 * u1.ln()).sqrt();
v.push((r * (std::f64::consts::TAU * u2).cos()) as f32);
if v.len() < dim {
v.push((r * (std::f64::consts::TAU * u2).sin()) as f32);
}
}
v
}
fn unit(v: &mut [f32]) {
let n = v.iter().map(|x| x * x).sum::<f32>().sqrt();
if n > 0.0 {
for x in v {
*x /= n;
}
}
}