use std::borrow::Cow;
use bytemuck::{Pod, Zeroable};
use rand::Rng;
use super::two_means;
use crate::distance::Distance;
use crate::node::Leaf;
use crate::parallel::ImmutableSubsetLeafs;
use crate::spaces::simple::dot_product;
use crate::unaligned_vector::UnalignedVector;
#[derive(Debug, Clone)]
pub enum Cosine {}
#[repr(C)]
#[derive(Pod, Zeroable, Debug, Clone, Copy)]
pub struct NodeHeaderCosine {
norm: f32,
}
impl Distance for Cosine {
type Header = NodeHeaderCosine;
type VectorCodec = f32;
fn name() -> &'static str {
"cosine"
}
fn new_header(vector: &UnalignedVector<Self::VectorCodec>) -> Self::Header {
NodeHeaderCosine { norm: Self::norm_no_header(vector) }
}
fn built_distance(p: &Leaf<Self>, q: &Leaf<Self>) -> f32 {
let pn = p.header.norm;
let qn = q.header.norm;
let pq = dot_product(&p.vector, &q.vector);
let pnqn = pn * qn;
if pnqn > f32::EPSILON {
let cos = pq / pnqn;
let cos = cos.clamp(-1.0, 1.0);
(1.0 - cos) / 2.0
} else {
0.0
}
}
fn normalized_distance(d: f32, _dimensions: usize) -> f32 {
d
}
fn norm_no_header(v: &UnalignedVector<Self::VectorCodec>) -> f32 {
dot_product(v, v).sqrt()
}
fn init(node: &mut Leaf<Self>) {
node.header.norm = dot_product(&node.vector, &node.vector).sqrt();
}
fn create_split<'a, R: Rng>(
children: &'a ImmutableSubsetLeafs<Self>,
rng: &mut R,
) -> heed::Result<Cow<'a, UnalignedVector<Self::VectorCodec>>> {
let [node_p, node_q] = two_means(rng, children, true)?;
let vector: Vec<f32> =
node_p.vector.iter().zip(node_q.vector.iter()).map(|(p, q)| p - q).collect();
let unaligned_vector = UnalignedVector::from_vec(vector);
let mut normal = Leaf { header: NodeHeaderCosine { norm: 0.0 }, vector: unaligned_vector };
Self::normalize(&mut normal);
Ok(normal.vector)
}
fn margin_no_header(
p: &UnalignedVector<Self::VectorCodec>,
q: &UnalignedVector<Self::VectorCodec>,
) -> f32 {
dot_product(p, q)
}
}