mod children;
mod criteria;
mod uni;
pub use children::Children;
pub use criteria::{MaxDepth, MinCardinality, PartitionCriteria, PartitionCriterion};
#[allow(clippy::module_name_repetitions)]
pub use uni::UniBall;
use core::{
fmt::{Debug, Display},
hash::Hash,
ops::Range,
};
use std::{
fs::File,
io::{BufReader, BufWriter},
path::Path,
};
use distances::Number;
use serde::{Deserialize, Serialize};
use crate::{Dataset, Instance};
pub trait Cluster<U: Number>:
Serialize + for<'a> Deserialize<'a> + PartialEq + Eq + PartialOrd + Ord + Debug + Hash + Display + Send + Sync + Clone
{
fn new_root<I: Instance, D: Dataset<I, U>>(data: &D, seed: Option<u64>) -> Self;
#[must_use]
fn partition<I, D, P>(self, data: &mut D, criteria: &P, seed: Option<u64>) -> Self
where
I: Instance,
D: Dataset<I, U>,
P: PartitionCriterion<U>;
fn offset(&self) -> usize;
fn cardinality(&self) -> usize;
fn depth(&self) -> usize;
fn arg_center(&self) -> usize;
fn radius(&self) -> U;
fn arg_radial(&self) -> usize;
fn lfd(&self) -> f64;
fn children(&self) -> Option<[&Self; 2]>;
fn polar_distance(&self) -> Option<U>;
fn arg_poles(&self) -> Option<[usize; 2]>;
fn name(&self) -> String {
format!("{}-{}", self.offset(), self.cardinality())
}
fn descend_to(&self, offset: usize, cardinality: usize) -> Option<&Self> {
if self.offset() == offset && self.cardinality() == cardinality {
Some(self)
} else {
self.children()
.and_then(|ch| ch.iter().find_map(|c| c.descend_to(offset, cardinality)))
}
}
fn is_ancestor_of(&self, other: &Self) -> bool {
other.depth() > self.depth()
&& self.indices().contains(&other.offset())
&& other.cardinality() < self.cardinality()
}
fn is_descendant_of(&self, other: &Self) -> bool {
other.is_ancestor_of(self)
}
fn is_leaf(&self) -> bool {
self.children().is_none()
}
fn is_singleton(&self) -> bool {
self.cardinality() == 1 || self.radius() == U::zero()
}
fn indices(&self) -> Range<usize> {
self.offset()..(self.offset() + self.cardinality())
}
fn subtree(&self) -> Vec<&Self> {
let subtree = vec![self];
match self.children() {
Some(children) => subtree
.into_iter()
.chain(children.iter().flat_map(|c| c.subtree()))
.collect(),
None => subtree,
}
}
fn max_leaf_depth(&self) -> usize {
self.subtree()
.iter()
.map(|c| c.depth())
.max()
.unwrap_or_else(|| self.depth())
}
fn distance_to_instance<I: Instance, D: Dataset<I, U>>(&self, data: &D, instance: &I) -> U {
data.query_to_one(instance, self.arg_center())
}
fn distance_to_other<I: Instance, D: Dataset<I, U>>(&self, data: &D, other: &Self) -> U {
data.one_to_one(self.arg_center(), other.arg_center())
}
fn overlapping_children<I: Instance, D: Dataset<I, U>>(&self, data: &D, query: &I, radius: U) -> Vec<&Self> {
if self.is_leaf() {
Vec::new()
} else {
let [left, right] = self
.children()
.unwrap_or_else(|| unreachable!("We checked that the cluster is not a leaf."));
let [arg_l, arg_r] = self
.arg_poles()
.unwrap_or_else(|| unreachable!("We checked that the cluster is not a leaf."));
let polar_distance = self
.polar_distance()
.unwrap_or_else(|| unreachable!("We checked that the cluster is not a leaf."));
let ql = data.query_to_one(query, arg_l);
let qr = data.query_to_one(query, arg_r);
let swap = ql < qr;
let (ql, qr) = if swap { (qr, ql) } else { (ql, qr) };
if (ql + qr) * (ql - qr) <= U::from(2) * polar_distance * radius {
vec![left, right]
} else if swap {
vec![left]
} else {
vec![right]
}
}
}
fn save(&self, path: &Path) -> Result<(), String> {
let mut writer = BufWriter::new(File::create(path).map_err(|e| e.to_string())?);
bincode::serialize_into(&mut writer, self).map_err(|e| e.to_string())?;
Ok(())
}
fn load(path: &Path) -> Result<Self, String> {
let reader = BufReader::new(File::open(path).map_err(|e| e.to_string())?);
bincode::deserialize_from(reader).map_err(|e| e.to_string())
}
}