#![allow(non_camel_case_types)]
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
pub type Distance = f64;
pub type ComplexProduct = (f64, f64);
type u64size = u64;
#[inline(always)]
fn f32_abs_compat(x: f32) -> f32 {
f32::from_bits(x.to_bits() & 0x7FFF_FFFF)
}
#[link(name = "simsimd")]
extern "C" {
fn simsimd_dot_i8(a: *const i8, b: *const i8, c: u64size, d: *mut Distance);
fn simsimd_dot_f16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_dot_bf16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_dot_f32(a: *const f32, b: *const f32, c: u64size, d: *mut Distance);
fn simsimd_dot_f64(a: *const f64, b: *const f64, c: u64size, d: *mut Distance);
fn simsimd_dot_f16c(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_dot_bf16c(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_dot_f32c(a: *const f32, b: *const f32, c: u64size, d: *mut Distance);
fn simsimd_dot_f64c(a: *const f64, b: *const f64, c: u64size, d: *mut Distance);
fn simsimd_vdot_f16c(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_vdot_bf16c(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_vdot_f32c(a: *const f32, b: *const f32, c: u64size, d: *mut Distance);
fn simsimd_vdot_f64c(a: *const f64, b: *const f64, c: u64size, d: *mut Distance);
fn simsimd_cos_i8(a: *const i8, b: *const i8, c: u64size, d: *mut Distance);
fn simsimd_cos_f16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_cos_bf16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_cos_f32(a: *const f32, b: *const f32, c: u64size, d: *mut Distance);
fn simsimd_cos_f64(a: *const f64, b: *const f64, c: u64size, d: *mut Distance);
fn simsimd_l2sq_i8(a: *const i8, b: *const i8, c: u64size, d: *mut Distance);
fn simsimd_l2sq_f16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_l2sq_bf16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_l2sq_f32(a: *const f32, b: *const f32, c: u64size, d: *mut Distance);
fn simsimd_l2sq_f64(a: *const f64, b: *const f64, c: u64size, d: *mut Distance);
fn simsimd_l2_i8(a: *const i8, b: *const i8, c: u64size, d: *mut Distance);
fn simsimd_l2_f16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_l2_bf16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_l2_f32(a: *const f32, b: *const f32, c: u64size, d: *mut Distance);
fn simsimd_l2_f64(a: *const f64, b: *const f64, c: u64size, d: *mut Distance);
fn simsimd_hamming_b8(a: *const u8, b: *const u8, c: u64size, d: *mut Distance);
fn simsimd_jaccard_b8(a: *const u8, b: *const u8, c: u64size, d: *mut Distance);
fn simsimd_js_f16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_js_bf16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_js_f32(a: *const f32, b: *const f32, c: u64size, d: *mut Distance);
fn simsimd_js_f64(a: *const f64, b: *const f64, c: u64size, d: *mut Distance);
fn simsimd_kl_f16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_kl_bf16(a: *const u16, b: *const u16, c: u64size, d: *mut Distance);
fn simsimd_kl_f32(a: *const f32, b: *const f32, c: u64size, d: *mut Distance);
fn simsimd_kl_f64(a: *const f64, b: *const f64, c: u64size, d: *mut Distance);
fn simsimd_intersect_u16(
a: *const u16,
b: *const u16,
a_length: u64size,
b_length: u64size,
d: *mut Distance,
);
fn simsimd_intersect_u32(
a: *const u32,
b: *const u32,
a_length: u64size,
b_length: u64size,
d: *mut Distance,
);
fn simsimd_uses_neon() -> i32;
fn simsimd_uses_neon_f16() -> i32;
fn simsimd_uses_neon_bf16() -> i32;
fn simsimd_uses_neon_i8() -> i32;
fn simsimd_uses_sve() -> i32;
fn simsimd_uses_sve_f16() -> i32;
fn simsimd_uses_sve_bf16() -> i32;
fn simsimd_uses_sve_i8() -> i32;
fn simsimd_uses_haswell() -> i32;
fn simsimd_uses_skylake() -> i32;
fn simsimd_uses_ice() -> i32;
fn simsimd_uses_genoa() -> i32;
fn simsimd_uses_sapphire() -> i32;
fn simsimd_uses_turin() -> i32;
fn simsimd_uses_sierra() -> i32;
fn simsimd_flush_denormals() -> i32;
fn simsimd_uses_dynamic_dispatch() -> i32;
fn simsimd_f32_to_f16(f32_value: f32, result_ptr: *mut u16);
fn simsimd_f16_to_f32(f16_ptr: *const u16) -> f32;
fn simsimd_f32_to_bf16(f32_value: f32, result_ptr: *mut u16);
fn simsimd_bf16_to_f32(bf16_ptr: *const u16) -> f32;
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct f16(pub u16);
impl f16 {
pub const ZERO: Self = f16(0);
pub const ONE: Self = f16(0x3C00);
pub const NEG_ONE: Self = f16(0xBC00);
#[inline(always)]
pub fn from_f32(value: f32) -> Self {
let mut result: u16 = 0;
unsafe { simsimd_f32_to_f16(value, &mut result) };
f16(result)
}
#[inline(always)]
pub fn to_f32(self) -> f32 {
unsafe { simsimd_f16_to_f32(&self.0) }
}
#[inline(always)]
pub fn is_nan(self) -> bool {
self.to_f32().is_nan()
}
#[inline(always)]
pub fn is_infinite(self) -> bool {
self.to_f32().is_infinite()
}
#[inline(always)]
pub fn is_finite(self) -> bool {
self.to_f32().is_finite()
}
#[inline(always)]
pub fn abs(self) -> Self {
Self::from_f32(f32_abs_compat(self.to_f32()))
}
#[cfg(feature = "std")]
#[inline(always)]
pub fn floor(self) -> Self {
Self::from_f32(self.to_f32().floor())
}
#[cfg(feature = "std")]
#[inline(always)]
pub fn ceil(self) -> Self {
Self::from_f32(self.to_f32().ceil())
}
#[cfg(feature = "std")]
#[inline(always)]
pub fn round(self) -> Self {
Self::from_f32(self.to_f32().round())
}
}
#[cfg(feature = "std")]
impl core::fmt::Display for f16 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.to_f32())
}
}
impl core::ops::Add for f16 {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self::Output {
Self::from_f32(self.to_f32() + rhs.to_f32())
}
}
impl core::ops::Sub for f16 {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
Self::from_f32(self.to_f32() - rhs.to_f32())
}
}
impl core::ops::Mul for f16 {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: Self) -> Self::Output {
Self::from_f32(self.to_f32() * rhs.to_f32())
}
}
impl core::ops::Div for f16 {
type Output = Self;
#[inline(always)]
fn div(self, rhs: Self) -> Self::Output {
Self::from_f32(self.to_f32() / rhs.to_f32())
}
}
impl core::ops::Neg for f16 {
type Output = Self;
#[inline(always)]
fn neg(self) -> Self::Output {
Self::from_f32(-self.to_f32())
}
}
impl core::cmp::PartialOrd for f16 {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.to_f32().partial_cmp(&other.to_f32())
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct bf16(pub u16);
impl bf16 {
pub const ZERO: Self = bf16(0);
pub const ONE: Self = bf16(0x3F80);
pub const NEG_ONE: Self = bf16(0xBF80);
#[inline(always)]
pub fn from_f32(value: f32) -> Self {
let mut result: u16 = 0;
unsafe { simsimd_f32_to_bf16(value, &mut result) };
bf16(result)
}
#[inline(always)]
pub fn to_f32(self) -> f32 {
unsafe { simsimd_bf16_to_f32(&self.0) }
}
#[inline(always)]
pub fn is_nan(self) -> bool {
self.to_f32().is_nan()
}
#[inline(always)]
pub fn is_infinite(self) -> bool {
self.to_f32().is_infinite()
}
#[inline(always)]
pub fn is_finite(self) -> bool {
self.to_f32().is_finite()
}
#[inline(always)]
pub fn abs(self) -> Self {
Self::from_f32(f32_abs_compat(self.to_f32()))
}
#[cfg(feature = "std")]
#[inline(always)]
pub fn floor(self) -> Self {
Self::from_f32(self.to_f32().floor())
}
#[cfg(feature = "std")]
#[inline(always)]
pub fn ceil(self) -> Self {
Self::from_f32(self.to_f32().ceil())
}
#[cfg(feature = "std")]
#[inline(always)]
pub fn round(self) -> Self {
Self::from_f32(self.to_f32().round())
}
}
#[cfg(feature = "std")]
impl core::fmt::Display for bf16 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.to_f32())
}
}
impl core::ops::Add for bf16 {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self::Output {
Self::from_f32(self.to_f32() + rhs.to_f32())
}
}
impl core::ops::Sub for bf16 {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
Self::from_f32(self.to_f32() - rhs.to_f32())
}
}
impl core::ops::Mul for bf16 {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: Self) -> Self::Output {
Self::from_f32(self.to_f32() * rhs.to_f32())
}
}
impl core::ops::Div for bf16 {
type Output = Self;
#[inline(always)]
fn div(self, rhs: Self) -> Self::Output {
Self::from_f32(self.to_f32() / rhs.to_f32())
}
}
impl core::ops::Neg for bf16 {
type Output = Self;
#[inline(always)]
fn neg(self) -> Self::Output {
Self::from_f32(-self.to_f32())
}
}
impl core::cmp::PartialOrd for bf16 {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.to_f32().partial_cmp(&other.to_f32())
}
}
pub mod capabilities {
pub fn uses_neon() -> bool {
unsafe { crate::simsimd_uses_neon() != 0 }
}
pub fn uses_neon_f16() -> bool {
unsafe { crate::simsimd_uses_neon_f16() != 0 }
}
pub fn uses_neon_bf16() -> bool {
unsafe { crate::simsimd_uses_neon_bf16() != 0 }
}
pub fn uses_neon_i8() -> bool {
unsafe { crate::simsimd_uses_neon_i8() != 0 }
}
pub fn uses_sve() -> bool {
unsafe { crate::simsimd_uses_sve() != 0 }
}
pub fn uses_sve_f16() -> bool {
unsafe { crate::simsimd_uses_sve_f16() != 0 }
}
pub fn uses_sve_bf16() -> bool {
unsafe { crate::simsimd_uses_sve_bf16() != 0 }
}
pub fn uses_sve_i8() -> bool {
unsafe { crate::simsimd_uses_sve_i8() != 0 }
}
pub fn uses_haswell() -> bool {
unsafe { crate::simsimd_uses_haswell() != 0 }
}
pub fn uses_skylake() -> bool {
unsafe { crate::simsimd_uses_skylake() != 0 }
}
pub fn uses_ice() -> bool {
unsafe { crate::simsimd_uses_ice() != 0 }
}
pub fn uses_genoa() -> bool {
unsafe { crate::simsimd_uses_genoa() != 0 }
}
pub fn uses_sapphire() -> bool {
unsafe { crate::simsimd_uses_sapphire() != 0 }
}
pub fn uses_turin() -> bool {
unsafe { crate::simsimd_uses_turin() != 0 }
}
pub fn uses_sierra() -> bool {
unsafe { crate::simsimd_uses_sierra() != 0 }
}
pub fn flush_denormals() -> bool {
unsafe { crate::simsimd_flush_denormals() != 0 }
}
pub fn uses_dynamic_dispatch() -> bool {
unsafe { crate::simsimd_uses_dynamic_dispatch() != 0 }
}
}
pub trait SpatialSimilarity
where
Self: Sized,
{
fn cos(a: &[Self], b: &[Self]) -> Option<Distance>;
fn dot(a: &[Self], b: &[Self]) -> Option<Distance>;
fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance>;
fn l2(a: &[Self], b: &[Self]) -> Option<Distance>;
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Distance> {
SpatialSimilarity::l2sq(a, b)
}
fn euclidean(a: &[Self], b: &[Self]) -> Option<Distance> {
SpatialSimilarity::l2(a, b)
}
fn inner(a: &[Self], b: &[Self]) -> Option<Distance> {
SpatialSimilarity::dot(a, b)
}
fn cosine(a: &[Self], b: &[Self]) -> Option<Distance> {
SpatialSimilarity::cos(a, b)
}
}
pub trait BinarySimilarity
where
Self: Sized,
{
fn hamming(a: &[Self], b: &[Self]) -> Option<Distance>;
fn jaccard(a: &[Self], b: &[Self]) -> Option<Distance>;
}
pub trait ProbabilitySimilarity
where
Self: Sized,
{
fn jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance>;
fn kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance>;
}
pub trait ComplexProducts
where
Self: Sized,
{
fn dot(a: &[Self], b: &[Self]) -> Option<ComplexProduct>;
fn vdot(a: &[Self], b: &[Self]) -> Option<ComplexProduct>;
}
pub trait Sparse
where
Self: Sized,
{
fn intersect(a: &[Self], b: &[Self]) -> Option<Distance>;
}
impl BinarySimilarity for u8 {
fn hamming(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_hamming_b8(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn jaccard(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_jaccard_b8(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
}
impl SpatialSimilarity for i8 {
fn cos(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_cos_i8(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn dot(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_dot_i8(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_l2sq_i8(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn l2(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_l2_i8(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
}
impl Sparse for u16 {
fn intersect(a: &[Self], b: &[Self]) -> Option<Distance> {
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe {
simsimd_intersect_u16(
a.as_ptr(),
b.as_ptr(),
a.len() as u64size,
b.len() as u64size,
distance_ptr,
)
};
Some(distance_value)
}
}
impl Sparse for u32 {
fn intersect(a: &[Self], b: &[Self]) -> Option<Distance> {
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe {
simsimd_intersect_u32(
a.as_ptr(),
b.as_ptr(),
a.len() as u64size,
b.len() as u64size,
distance_ptr,
)
};
Some(distance_value)
}
}
impl SpatialSimilarity for f16 {
fn cos(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_cos_f16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn dot(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_dot_f16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_l2sq_f16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn l2(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_l2_f16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
}
impl SpatialSimilarity for bf16 {
fn cos(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_cos_bf16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn dot(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_dot_bf16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_l2sq_bf16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn l2(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_l2_bf16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
}
impl SpatialSimilarity for f32 {
fn cos(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_cos_f32(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn dot(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_dot_f32(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_l2sq_f32(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn l2(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_l2_f32(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
}
impl SpatialSimilarity for f64 {
fn cos(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_cos_f64(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn dot(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_dot_f64(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_l2sq_f64(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn l2(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_l2_f64(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
}
impl ProbabilitySimilarity for f16 {
fn jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_js_f16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_kl_f16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
}
impl ProbabilitySimilarity for bf16 {
fn jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_js_bf16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_kl_bf16(a_ptr, b_ptr, a.len() as u64size, distance_ptr) };
Some(distance_value)
}
}
impl ProbabilitySimilarity for f32 {
fn jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_js_f32(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_kl_f32(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
}
impl ProbabilitySimilarity for f64 {
fn jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_js_f64(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
fn kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance> {
if a.len() != b.len() {
return None;
}
let mut distance_value: Distance = 0.0;
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
unsafe { simsimd_kl_f64(a.as_ptr(), b.as_ptr(), a.len() as u64size, distance_ptr) };
Some(distance_value)
}
}
impl ComplexProducts for f16 {
fn dot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
if a.len() != b.len() || a.len() % 2 != 0 {
return None;
}
let mut product: [Distance; 2] = [0.0, 0.0];
let product_ptr: *mut Distance = &mut product[0] as *mut _;
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
unsafe { simsimd_dot_f16c(a_ptr, b_ptr, a.len() as u64size / 2, product_ptr) };
Some((product[0], product[1]))
}
fn vdot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
if a.len() != b.len() || a.len() % 2 != 0 {
return None;
}
let mut product: [Distance; 2] = [0.0, 0.0];
let product_ptr: *mut Distance = &mut product[0] as *mut _;
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
unsafe { simsimd_vdot_f16c(a_ptr, b_ptr, a.len() as u64size / 2, product_ptr) };
Some((product[0], product[1]))
}
}
impl ComplexProducts for bf16 {
fn dot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
if a.len() != b.len() || a.len() % 2 != 0 {
return None;
}
let mut product: [Distance; 2] = [0.0, 0.0];
let product_ptr: *mut Distance = &mut product[0] as *mut _;
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
unsafe { simsimd_dot_bf16c(a_ptr, b_ptr, a.len() as u64size / 2, product_ptr) };
Some((product[0], product[1]))
}
fn vdot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
if a.len() != b.len() || a.len() % 2 != 0 {
return None;
}
let mut product: [Distance; 2] = [0.0, 0.0];
let product_ptr: *mut Distance = &mut product[0] as *mut _;
let a_ptr = a.as_ptr() as *const u16;
let b_ptr = b.as_ptr() as *const u16;
unsafe { simsimd_vdot_bf16c(a_ptr, b_ptr, a.len() as u64size / 2, product_ptr) };
Some((product[0], product[1]))
}
}
impl ComplexProducts for f32 {
fn dot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
if a.len() != b.len() || a.len() % 2 != 0 {
return None;
}
let mut product: [Distance; 2] = [0.0, 0.0];
let product_ptr: *mut Distance = &mut product[0] as *mut _;
unsafe { simsimd_dot_f32c(a.as_ptr(), b.as_ptr(), a.len() as u64size / 2, product_ptr) };
Some((product[0], product[1]))
}
fn vdot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
if a.len() != b.len() || a.len() % 2 != 0 {
return None;
}
let mut product: [Distance; 2] = [0.0, 0.0];
let product_ptr: *mut Distance = &mut product[0] as *mut _;
unsafe { simsimd_vdot_f32c(a.as_ptr(), b.as_ptr(), a.len() as u64size / 2, product_ptr) };
Some((product[0], product[1]))
}
}
impl ComplexProducts for f64 {
fn dot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
if a.len() != b.len() || a.len() % 2 != 0 {
return None;
}
let mut product: [Distance; 2] = [0.0, 0.0];
let product_ptr: *mut Distance = &mut product[0] as *mut _;
unsafe { simsimd_dot_f64c(a.as_ptr(), b.as_ptr(), a.len() as u64size / 2, product_ptr) };
Some((product[0], product[1]))
}
fn vdot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
if a.len() != b.len() || a.len() % 2 != 0 {
return None;
}
let mut product: [Distance; 2] = [0.0, 0.0];
let product_ptr: *mut Distance = &mut product[0] as *mut _;
unsafe { simsimd_vdot_f64c(a.as_ptr(), b.as_ptr(), a.len() as u64size / 2, product_ptr) };
Some((product[0], product[1]))
}
}
#[cfg(test)]
mod tests {
use super::*;
use half::bf16 as HalfBF16;
use half::f16 as HalfF16;
#[test]
fn hardware_features_detection() {
let uses_arm = capabilities::uses_neon() || capabilities::uses_sve();
let uses_x86 = capabilities::uses_haswell()
|| capabilities::uses_skylake()
|| capabilities::uses_ice()
|| capabilities::uses_genoa()
|| capabilities::uses_sapphire()
|| capabilities::uses_turin();
if uses_arm {
assert!(!uses_x86);
}
if uses_x86 {
assert!(!uses_arm);
}
println!("- uses_neon: {}", capabilities::uses_neon());
println!("- uses_neon_f16: {}", capabilities::uses_neon_f16());
println!("- uses_neon_bf16: {}", capabilities::uses_neon_bf16());
println!("- uses_neon_i8: {}", capabilities::uses_neon_i8());
println!("- uses_sve: {}", capabilities::uses_sve());
println!("- uses_sve_f16: {}", capabilities::uses_sve_f16());
println!("- uses_sve_bf16: {}", capabilities::uses_sve_bf16());
println!("- uses_sve_i8: {}", capabilities::uses_sve_i8());
println!("- uses_haswell: {}", capabilities::uses_haswell());
println!("- uses_skylake: {}", capabilities::uses_skylake());
println!("- uses_ice: {}", capabilities::uses_ice());
println!("- uses_genoa: {}", capabilities::uses_genoa());
println!("- uses_sapphire: {}", capabilities::uses_sapphire());
println!("- uses_turin: {}", capabilities::uses_turin());
println!("- uses_sierra: {}", capabilities::uses_sierra());
}
fn assert_almost_equal(left: Distance, right: Distance, tolerance: Distance) {
let lower = right - tolerance;
let upper = right + tolerance;
assert!(left >= lower && left <= upper);
}
#[test]
fn cos_i8() {
let a = &[3, 97, 127];
let b = &[3, 97, 127];
if let Some(result) = SpatialSimilarity::cosine(a, b) {
assert_almost_equal(0.00012027938, result, 0.01);
}
}
#[test]
fn cos_f32() {
let a = &[1.0, 2.0, 3.0];
let b = &[4.0, 5.0, 6.0];
if let Some(result) = SpatialSimilarity::cosine(a, b) {
assert_almost_equal(0.025, result, 0.01);
}
}
#[test]
fn dot_i8() {
let a = &[1, 2, 3];
let b = &[4, 5, 6];
if let Some(result) = SpatialSimilarity::dot(a, b) {
assert_almost_equal(32.0, result, 0.01);
}
}
#[test]
fn dot_f32() {
let a = &[1.0, 2.0, 3.0];
let b = &[4.0, 5.0, 6.0];
if let Some(result) = SpatialSimilarity::dot(a, b) {
assert_almost_equal(32.0, result, 0.01);
}
}
#[test]
fn dot_f32_complex() {
let a: &[f32; 4] = &[1.0, 2.0, 3.0, 4.0]; let b: &[f32; 4] = &[5.0, 6.0, 7.0, 8.0];
if let Some((real, imag)) = ComplexProducts::dot(a, b) {
assert_almost_equal(-18.0, real, 0.01);
assert_almost_equal(68.0, imag, 0.01);
}
}
#[test]
fn vdot_f32_complex() {
let a: &[f32; 4] = &[1.0, 2.0, 3.0, 4.0]; let b: &[f32; 4] = &[5.0, 6.0, 7.0, 8.0];
if let Some((real, imag)) = ComplexProducts::vdot(a, b) {
assert_almost_equal(70.0, real, 0.01);
assert_almost_equal(-8.0, imag, 0.01);
}
}
#[test]
fn l2sq_i8() {
let a = &[1, 2, 3];
let b = &[4, 5, 6];
if let Some(result) = SpatialSimilarity::sqeuclidean(a, b) {
assert_almost_equal(27.0, result, 0.01);
}
}
#[test]
fn l2sq_f32() {
let a = &[1.0, 2.0, 3.0];
let b = &[4.0, 5.0, 6.0];
if let Some(result) = SpatialSimilarity::sqeuclidean(a, b) {
assert_almost_equal(27.0, result, 0.01);
}
}
#[test]
fn l2_f32() {
let a: &[f32; 3] = &[1.0, 2.0, 3.0];
let b: &[f32; 3] = &[4.0, 5.0, 6.0];
if let Some(result) = SpatialSimilarity::euclidean(a, b) {
assert_almost_equal(5.2, result, 0.01);
}
}
#[test]
fn l2_f64() {
let a: &[f64; 3] = &[1.0, 2.0, 3.0];
let b: &[f64; 3] = &[4.0, 5.0, 6.0];
if let Some(result) = SpatialSimilarity::euclidean(a, b) {
assert_almost_equal(5.2, result, 0.01);
}
}
#[test]
fn l2_f16() {
let a_half: Vec<HalfF16> = vec![1.0, 2.0, 3.0]
.iter()
.map(|&x| HalfF16::from_f32(x))
.collect();
let b_half: Vec<HalfF16> = vec![4.0, 5.0, 6.0]
.iter()
.map(|&x| HalfF16::from_f32(x))
.collect();
let a_simsimd: &[f16] =
unsafe { std::slice::from_raw_parts(a_half.as_ptr() as *const f16, a_half.len()) };
let b_simsimd: &[f16] =
unsafe { std::slice::from_raw_parts(b_half.as_ptr() as *const f16, b_half.len()) };
if let Some(result) = SpatialSimilarity::euclidean(&a_simsimd, &b_simsimd) {
assert_almost_equal(5.2, result, 0.01);
}
}
#[test]
fn l2_i8() {
let a = &[1, 2, 3];
let b = &[4, 5, 6];
if let Some(result) = SpatialSimilarity::euclidean(a, b) {
assert_almost_equal(5.2, result, 0.01);
}
}
#[test]
fn hamming_u8() {
let a = &[0b01010101, 0b11110000, 0b10101010];
let b = &[0b01010101, 0b11110000, 0b10101010];
if let Some(result) = BinarySimilarity::hamming(a, b) {
assert_almost_equal(0.0, result, 0.01);
}
}
#[test]
fn jaccard_u8() {
let a = &[0b11110000, 0b00001111, 0b10101010];
let b = &[0b11110000, 0b00001111, 0b01010101];
if let Some(result) = BinarySimilarity::jaccard(a, b) {
assert_almost_equal(0.5, result, 0.01);
}
}
#[test]
fn js_f32() {
let a: &[f32; 3] = &[0.1, 0.9, 0.0];
let b: &[f32; 3] = &[0.2, 0.8, 0.0];
if let Some(result) = ProbabilitySimilarity::jensenshannon(a, b) {
assert_almost_equal(0.099, result, 0.01);
}
}
#[test]
fn kl_f32() {
let a: &[f32; 3] = &[0.1, 0.9, 0.0];
let b: &[f32; 3] = &[0.2, 0.8, 0.0];
if let Some(result) = ProbabilitySimilarity::kullbackleibler(a, b) {
assert_almost_equal(0.036, result, 0.01);
}
}
#[test]
fn cos_f16_same() {
let a_u16: &[u16] = &[15360, 16384, 17408]; let b_u16: &[u16] = &[15360, 16384, 17408];
let a_f16: &[f16] =
unsafe { std::slice::from_raw_parts(a_u16.as_ptr() as *const f16, a_u16.len()) };
let b_f16: &[f16] =
unsafe { std::slice::from_raw_parts(b_u16.as_ptr() as *const f16, b_u16.len()) };
if let Some(result) = SpatialSimilarity::cosine(a_f16, b_f16) {
assert_almost_equal(0.0, result, 0.01);
}
}
#[test]
fn cos_bf16_same() {
let a_u16: &[u16] = &[15360, 16384, 17408]; let b_u16: &[u16] = &[15360, 16384, 17408];
let a_bf16: &[bf16] =
unsafe { std::slice::from_raw_parts(a_u16.as_ptr() as *const bf16, a_u16.len()) };
let b_bf16: &[bf16] =
unsafe { std::slice::from_raw_parts(b_u16.as_ptr() as *const bf16, b_u16.len()) };
if let Some(result) = SpatialSimilarity::cosine(a_bf16, b_bf16) {
assert_almost_equal(0.0, result, 0.01);
}
}
#[test]
fn cos_f16_interop() {
let a_half: Vec<HalfF16> = vec![1.0, 2.0, 3.0]
.iter()
.map(|&x| HalfF16::from_f32(x))
.collect();
let b_half: Vec<HalfF16> = vec![4.0, 5.0, 6.0]
.iter()
.map(|&x| HalfF16::from_f32(x))
.collect();
let a_simsimd: &[f16] =
unsafe { std::slice::from_raw_parts(a_half.as_ptr() as *const f16, a_half.len()) };
let b_simsimd: &[f16] =
unsafe { std::slice::from_raw_parts(b_half.as_ptr() as *const f16, b_half.len()) };
if let Some(result) = SpatialSimilarity::cosine(a_simsimd, b_simsimd) {
assert_almost_equal(0.025, result, 0.01);
}
}
#[test]
fn cos_bf16_interop() {
let a_half: Vec<HalfBF16> = vec![1.0, 2.0, 3.0]
.iter()
.map(|&x| HalfBF16::from_f32(x))
.collect();
let b_half: Vec<HalfBF16> = vec![4.0, 5.0, 6.0]
.iter()
.map(|&x| HalfBF16::from_f32(x))
.collect();
let a_simsimd: &[bf16] =
unsafe { std::slice::from_raw_parts(a_half.as_ptr() as *const bf16, a_half.len()) };
let b_simsimd: &[bf16] =
unsafe { std::slice::from_raw_parts(b_half.as_ptr() as *const bf16, b_half.len()) };
if let Some(result) = SpatialSimilarity::cosine(a_simsimd, b_simsimd) {
assert_almost_equal(0.025, result, 0.01);
}
}
#[test]
fn intersect_u16() {
{
let a_u16: &[u16] = &[153, 16384, 17408];
let b_u16: &[u16] = &[7408, 15360, 16384];
if let Some(result) = Sparse::intersect(a_u16, b_u16) {
assert_almost_equal(1.0, result, 0.0001);
}
}
{
let a_u16: &[u16] = &[8, 153, 11638];
let b_u16: &[u16] = &[7408, 15360, 16384];
if let Some(result) = Sparse::intersect(a_u16, b_u16) {
assert_almost_equal(0.0, result, 0.0001);
}
}
}
#[test]
fn intersect_u32() {
{
let a_u32: &[u32] = &[11, 153];
let b_u32: &[u32] = &[11, 153, 7408, 16384];
if let Some(result) = Sparse::intersect(a_u32, b_u32) {
assert_almost_equal(2.0, result, 0.0001);
}
}
{
let a_u32: &[u32] = &[153, 7408, 11638];
let b_u32: &[u32] = &[153, 7408, 11638];
if let Some(result) = Sparse::intersect(a_u32, b_u32) {
assert_almost_equal(3.0, result, 0.0001);
}
}
}
fn reference_intersect<T: Ord>(a: &[T], b: &[T]) -> usize {
let mut a_iter = a.iter();
let mut b_iter = b.iter();
let mut a_current = a_iter.next();
let mut b_current = b_iter.next();
let mut count = 0;
while let (Some(a_val), Some(b_val)) = (a_current, b_current) {
match a_val.cmp(b_val) {
core::cmp::Ordering::Less => a_current = a_iter.next(),
core::cmp::Ordering::Greater => b_current = b_iter.next(),
core::cmp::Ordering::Equal => {
count += 1;
a_current = a_iter.next();
b_current = b_iter.next();
}
}
}
count
}
fn generate_intersection_test_arrays<T>() -> Vec<Vec<T>>
where
T: core::convert::TryFrom<u32> + Copy,
<T as core::convert::TryFrom<u32>>::Error: core::fmt::Debug,
{
vec![
vec![],
vec![T::try_from(42).unwrap()],
vec![
T::try_from(1).unwrap(),
T::try_from(5).unwrap(),
T::try_from(10).unwrap(),
],
vec![
T::try_from(2).unwrap(),
T::try_from(4).unwrap(),
T::try_from(6).unwrap(),
T::try_from(8).unwrap(),
T::try_from(10).unwrap(),
T::try_from(12).unwrap(),
T::try_from(14).unwrap(),
],
(0..14).map(|x| T::try_from(x * 10).unwrap()).collect(),
(5..20).map(|x| T::try_from(x * 10).unwrap()).collect(),
(0..40).map(|x| T::try_from(x * 2).unwrap()).collect(),
(10..50).map(|x| T::try_from(x * 2).unwrap()).collect(), (0..45).map(|x| T::try_from(x * 3).unwrap()).collect(), (0..100).map(|x| T::try_from(x * 2).unwrap()).collect(),
(50..150).map(|x| T::try_from(x * 2).unwrap()).collect(), (0..100).map(|x| T::try_from(x * 5).unwrap()).collect(), (0..150)
.filter(|x| x % 7 == 0)
.map(|x| T::try_from(x).unwrap())
.collect(),
(0..500).map(|x| T::try_from(x * 3).unwrap()).collect(),
(100..600).map(|x| T::try_from(x * 3).unwrap()).collect(), (0..600).map(|x| T::try_from(x * 7).unwrap()).collect(), (0..50).map(|x| T::try_from(x * 2).unwrap()).collect(),
(1000..1050).map(|x| T::try_from(x * 2).unwrap()).collect(), (0..16).map(|x| T::try_from(x).unwrap()).collect(), (0..32).map(|x| T::try_from(x).unwrap()).collect(), (0..64).map(|x| T::try_from(x).unwrap()).collect(), ]
}
#[test]
fn intersect_u32_comprehensive() {
let test_arrays: Vec<Vec<u32>> = generate_intersection_test_arrays();
for (i, array_a) in test_arrays.iter().enumerate() {
for (j, array_b) in test_arrays.iter().enumerate() {
let expected = reference_intersect(array_a, array_b);
let result =
Sparse::intersect(array_a.as_slice(), array_b.as_slice()).unwrap() as usize;
assert_eq!(
expected,
result,
"Intersection mismatch for arrays[{}] (len={}) and arrays[{}] (len={})",
i,
array_a.len(),
j,
array_b.len()
);
}
}
}
#[test]
fn intersect_u16_comprehensive() {
let test_arrays: Vec<Vec<u16>> = generate_intersection_test_arrays();
for (i, array_a) in test_arrays.iter().enumerate() {
for (j, array_b) in test_arrays.iter().enumerate() {
let expected = reference_intersect(array_a, array_b);
let result =
Sparse::intersect(array_a.as_slice(), array_b.as_slice()).unwrap() as usize;
assert_eq!(
expected,
result,
"Intersection mismatch for arrays[{}] (len={}) and arrays[{}] (len={})",
i,
array_a.len(),
j,
array_b.len()
);
}
}
}
#[test]
fn intersect_edge_cases() {
let empty: &[u32] = &[];
let non_empty: &[u32] = &[1, 2, 3];
assert_eq!(Sparse::intersect(empty, empty), Some(0.0));
assert_eq!(Sparse::intersect(empty, non_empty), Some(0.0));
assert_eq!(Sparse::intersect(non_empty, empty), Some(0.0));
assert_eq!(Sparse::intersect(&[42u32], &[42u32]), Some(1.0));
assert_eq!(Sparse::intersect(&[42u32], &[43u32]), Some(0.0));
let a: &[u32] = &[1, 2, 3, 4, 5];
let b: &[u32] = &[10, 20, 30, 40, 50];
assert_eq!(Sparse::intersect(a, b), Some(0.0));
let c: &[u32] = &[10, 20, 30, 40, 50];
assert_eq!(Sparse::intersect(c, c), Some(5.0));
let boundary_16: Vec<u32> = (0..16).collect();
let boundary_32: Vec<u32> = (0..32).collect();
let boundary_64: Vec<u32> = (0..64).collect();
assert_eq!(Sparse::intersect(&boundary_16, &boundary_16), Some(16.0));
assert_eq!(Sparse::intersect(&boundary_32, &boundary_32), Some(32.0));
assert_eq!(Sparse::intersect(&boundary_64, &boundary_64), Some(64.0));
let first_half: Vec<u32> = (0..32).collect();
let second_half: Vec<u32> = (16..48).collect();
assert_eq!(Sparse::intersect(&first_half, &second_half), Some(16.0));
}
#[test]
fn f16_arithmetic() {
let a = f16::from_f32(3.5);
let b = f16::from_f32(2.0);
assert!((a + b).to_f32() - 5.5 < 0.01);
assert!((a - b).to_f32() - 1.5 < 0.01);
assert!((a * b).to_f32() - 7.0 < 0.01);
assert!((a / b).to_f32() - 1.75 < 0.01);
assert!((-a).to_f32() + 3.5 < 0.01);
assert!(f16::ZERO.to_f32() == 0.0);
assert!((f16::ONE.to_f32() - 1.0).abs() < 0.01);
assert!((f16::NEG_ONE.to_f32() + 1.0).abs() < 0.01);
assert!(a > b);
assert!(!(a < b));
assert!(a == a);
assert!((-a).abs().to_f32() - 3.5 < 0.01);
assert!(a.is_finite());
assert!(!a.is_nan());
assert!(!a.is_infinite());
}
#[test]
fn bf16_arithmetic() {
let a = bf16::from_f32(3.5);
let b = bf16::from_f32(2.0);
assert!((a + b).to_f32() - 5.5 < 0.1);
assert!((a - b).to_f32() - 1.5 < 0.1);
assert!((a * b).to_f32() - 7.0 < 0.1);
assert!((a / b).to_f32() - 1.75 < 0.1);
assert!((-a).to_f32() + 3.5 < 0.1);
assert!(bf16::ZERO.to_f32() == 0.0);
assert!((bf16::ONE.to_f32() - 1.0).abs() < 0.01);
assert!((bf16::NEG_ONE.to_f32() + 1.0).abs() < 0.01);
assert!(a > b);
assert!(!(a < b));
assert!(a == a);
assert!((-a).abs().to_f32() - 3.5 < 0.1);
assert!(a.is_finite());
assert!(!a.is_nan());
assert!(!a.is_infinite());
}
#[test]
fn bf16_dot() {
let brain_a: Vec<bf16> = vec![1.0, 2.0, 3.0, 1.0, 2.0]
.iter()
.map(|&x| bf16::from_f32(x))
.collect();
let brain_b: Vec<bf16> = vec![4.0, 5.0, 6.0, 4.0, 5.0]
.iter()
.map(|&x| bf16::from_f32(x))
.collect();
if let Some(result) = <bf16 as SpatialSimilarity>::dot(&brain_a, &brain_b) {
assert_eq!(46.0, result);
}
}
}