abd-clam 0.11.1

Clustered Learning of Approximate Manifolds
Documentation
use std::collections::HashSet;

use rayon::prelude::*;

use abd_clam::dataset::Dataset;
use abd_clam::number::Number;
use abd_clam::search::cakes::CAKES;
use abd_clam::utils::helpers;

pub mod anomaly_readers;
pub mod distances;
pub mod search_readers;

#[allow(clippy::type_complexity)]
pub fn make_data(n: usize, d: usize, q: usize) -> (Vec<Vec<f32>>, Vec<Vec<f32>>, String) {
    let min_val = 0.;
    let max_val = 1.;
    let data = helpers::gen_data_f32(n * 1_000, d, min_val, max_val, 42);
    let queries = helpers::gen_data_f32(q, d, min_val, max_val, 0);
    let name = format!("{n}k-{d}");

    (data, queries, name)
}

pub fn check_search<T: Number, U: Number, D: Dataset<T, U>>(queries: &[&Vec<T>], cakes: &CAKES<T, U, D>, r: U) {
    let iqp = queries
        .par_iter()
        .enumerate()
        .map(|(i, &query)| {
            let naive = cakes
                .linear_search(query, r, None)
                .into_iter()
                .map(|(i, _)| i)
                .collect::<Vec<_>>();
            let rnn = cakes
                .rnn_search(query, r)
                .into_iter()
                .map(|(i, _)| i)
                .collect::<Vec<_>>();
            (i, query, check_exactness(&naive, &rnn))
        })
        .find_first(|(_, _, p)| p.is_some());
    if let Some((i, _, problem)) = iqp {
        let problem = problem.unwrap();
        println!("Problem at index {i}: {problem}");
    }
}

fn check_exactness(naive: &[usize], cakes: &[usize]) -> Option<String> {
    if naive.len() != cakes.len() {
        return Some(format!(
            "Different number of results: {} vs {}",
            naive.len(),
            cakes.len()
        ));
    }

    let naive = HashSet::<usize>::from_iter(naive.iter().copied());
    if naive.len() != cakes.len() {
        return Some(format!(
            "Got duplicate indices in naive: {} vs {}",
            naive.len(),
            cakes.len()
        ));
    }

    let cakes = HashSet::<usize>::from_iter(cakes.iter().copied());
    if naive.len() != cakes.len() {
        return Some(format!(
            "Got duplicate indices in cakes: {} vs {}",
            naive.len(),
            cakes.len()
        ));
    }

    let common = naive.intersection(&cakes).count();
    if common != naive.len() {
        let recall = common.as_f64() / naive.len().as_f64();
        Some(format!(
            "Got a mismatch in results between naive and cakes: recall = {recall:.12}"
        ))
    } else {
        None
    }
}