1use diskann_wide::{arch::Target2, Architecture, ARCH};
7
8use super::simd;
10use crate::{
11 AsUnaligned, Half, MathematicalValue, PureDistanceFunction, SimilarityScore, UnalignedSlice,
12};
13
14macro_rules! architecture_hook {
15 ($functor:ty, $impl:path) => {
16 impl<A, T, L, R> diskann_wide::arch::Target2<A, T, L, R> for $functor
17 where
18 A: Architecture,
19 L: AsUnaligned,
20 R: AsUnaligned,
21 $impl: simd::SIMDSchema<L::Element, R::Element, A>,
22 Self: PostOp<<$impl as simd::SIMDSchema<L::Element, R::Element, A>>::Return, T>,
23 {
24 #[inline(always)]
25 fn run(self, arch: A, left: L, right: R) -> T {
26 Self::post_op(simd::simd_op(
27 &$impl,
28 arch,
29 left.as_unaligned(),
30 right.as_unaligned(),
31 ))
32 }
33 }
34
35 impl<A, T, L, R> diskann_wide::arch::FTarget2<A, T, L, R> for $functor
36 where
37 A: Architecture,
38 L: AsUnaligned,
39 R: AsUnaligned,
40 Self: diskann_wide::arch::Target2<A, T, L, R>,
41 {
42 #[inline(always)]
43 fn run(arch: A, left: L, right: R) -> T {
44 arch.run2(Self::default(), left, right)
45 }
46 }
47 };
48}
49
50#[derive(Debug)]
73pub struct Specialize<const N: usize, F>(std::marker::PhantomData<F>);
74
75impl<const N: usize, F> Specialize<N, F> {
76 pub const fn new() -> Self {
78 Self(std::marker::PhantomData)
79 }
80}
81
82impl<const N: usize, F> Default for Specialize<N, F> {
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88impl<const N: usize, F> Clone for Specialize<N, F> {
89 fn clone(&self) -> Self {
90 *self
91 }
92}
93
94impl<const N: usize, F> Copy for Specialize<N, F> {}
95
96impl<A, T, L, R, const N: usize, F>
97 diskann_wide::arch::FTarget2<A, T, UnalignedSlice<'_, L>, UnalignedSlice<'_, R>>
98 for Specialize<N, F>
99where
100 A: Architecture,
101 F: for<'a, 'b> diskann_wide::arch::Target2<A, T, UnalignedSlice<'a, L>, UnalignedSlice<'b, R>>
102 + Default,
103{
104 #[inline(always)]
105 fn run(arch: A, x: UnalignedSlice<'_, L>, y: UnalignedSlice<'_, R>) -> T {
106 if (x.len() != N) | (y.len() != N) {
107 fail_length_check(x, y, N);
108 }
109
110 arch.run2(F::default(), x, y)
113 }
114}
115
116#[inline(never)]
119#[allow(clippy::panic)]
120fn fail_length_check<L, R>(x: UnalignedSlice<'_, L>, y: UnalignedSlice<'_, R>, len: usize) -> ! {
121 let message = if x.len() != len {
122 ("first", x.len())
123 } else {
124 ("second", y.len())
125 };
126 panic!(
127 "expected {} argument to have length {}, instead it has length {}",
128 message.0, len, message.1
129 );
130}
131
132pub(super) trait PostOp<From, To> {
138 fn post_op(x: From) -> To;
139}
140
141macro_rules! use_simd_implementation {
143 ($functor:ty, $T:ty, $U:ty) => {
144 impl PureDistanceFunction<&[$T], &[$U], SimilarityScore<f32>> for $functor {
150 #[inline]
151 fn evaluate(x: &[$T], y: &[$U]) -> SimilarityScore<f32> {
152 <$functor>::default().run(ARCH, x, y)
153 }
154 }
155
156 impl<const N: usize> PureDistanceFunction<&[$T; N], &[$U; N], SimilarityScore<f32>>
158 for $functor
159 {
160 #[inline]
161 fn evaluate(x: &[$T; N], y: &[$U; N]) -> SimilarityScore<f32> {
162 <$functor>::default().run(ARCH, x, y)
163 }
164 }
165
166 impl PureDistanceFunction<&[$T], &[$U], MathematicalValue<f32>> for $functor {
172 #[inline]
173 fn evaluate(x: &[$T], y: &[$U]) -> MathematicalValue<f32> {
174 <$functor>::default().run(ARCH, x, y)
175 }
176 }
177 impl<const N: usize> PureDistanceFunction<&[$T; N], &[$U; N], MathematicalValue<f32>>
179 for $functor
180 {
181 #[inline]
182 fn evaluate(x: &[$T; N], y: &[$U; N]) -> MathematicalValue<f32> {
183 <$functor>::default().run(ARCH, x, y)
184 }
185 }
186
187 impl PureDistanceFunction<&[$T], &[$U], f32> for $functor {
193 #[inline(always)]
194 fn evaluate(x: &[$T], y: &[$U]) -> f32 {
195 <$functor>::default().run(ARCH, x, y)
196 }
197 }
198
199 impl<const N: usize> PureDistanceFunction<&[$T; N], &[$U; N], f32> for $functor {
201 #[inline]
202 fn evaluate(x: &[$T; N], y: &[$U; N]) -> f32 {
203 <$functor>::default().run(ARCH, x, y)
204 }
205 }
206 };
207}
208
209#[derive(Debug, Clone, Copy, Default)]
215pub struct SquaredL2 {}
216
217impl PostOp<f32, SimilarityScore<f32>> for SquaredL2 {
218 #[inline(always)]
219 fn post_op(x: f32) -> SimilarityScore<f32> {
220 SimilarityScore::new(x)
221 }
222}
223
224impl PostOp<f32, f32> for SquaredL2 {
225 #[inline(always)]
226 fn post_op(x: f32) -> f32 {
227 x
228 }
229}
230
231impl PostOp<f32, MathematicalValue<f32>> for SquaredL2 {
232 #[inline(always)]
233 fn post_op(x: f32) -> MathematicalValue<f32> {
234 MathematicalValue::new(x)
235 }
236}
237
238architecture_hook!(SquaredL2, simd::L2);
239use_simd_implementation!(SquaredL2, f32, f32);
240use_simd_implementation!(SquaredL2, f32, Half);
241use_simd_implementation!(SquaredL2, Half, Half);
242use_simd_implementation!(SquaredL2, i8, i8);
243use_simd_implementation!(SquaredL2, u8, u8);
244
245#[derive(Debug, Clone, Copy, Default)]
254pub struct FullL2 {}
255
256impl PostOp<f32, SimilarityScore<f32>> for FullL2 {
257 #[inline(always)]
258 fn post_op(x: f32) -> SimilarityScore<f32> {
259 SimilarityScore::new(x.sqrt())
260 }
261}
262
263impl PostOp<f32, f32> for FullL2 {
264 #[inline(always)]
265 fn post_op(x: f32) -> f32 {
266 x.sqrt()
267 }
268}
269
270impl PostOp<f32, MathematicalValue<f32>> for FullL2 {
271 #[inline(always)]
272 fn post_op(x: f32) -> MathematicalValue<f32> {
273 MathematicalValue::new(x.sqrt())
274 }
275}
276
277architecture_hook!(FullL2, simd::L2);
278use_simd_implementation!(FullL2, f32, f32);
279use_simd_implementation!(FullL2, f32, Half);
280use_simd_implementation!(FullL2, Half, Half);
281use_simd_implementation!(FullL2, i8, i8);
282use_simd_implementation!(FullL2, u8, u8);
283
284#[derive(Debug, Clone, Copy, Default)]
290pub struct InnerProduct {}
291
292impl PostOp<f32, SimilarityScore<f32>> for InnerProduct {
293 #[inline(always)]
297 fn post_op(x: f32) -> SimilarityScore<f32> {
298 SimilarityScore::new(-x)
299 }
300}
301
302impl PostOp<f32, MathematicalValue<f32>> for InnerProduct {
303 #[inline(always)]
304 fn post_op(x: f32) -> MathematicalValue<f32> {
305 MathematicalValue::new(x)
306 }
307}
308
309impl PostOp<f32, f32> for InnerProduct {
310 #[inline(always)]
311 fn post_op(x: f32) -> f32 {
312 <Self as PostOp<f32, SimilarityScore<f32>>>::post_op(x).into_inner()
313 }
314}
315
316architecture_hook!(InnerProduct, simd::IP);
317use_simd_implementation!(InnerProduct, f32, f32);
318use_simd_implementation!(InnerProduct, f32, Half);
319use_simd_implementation!(InnerProduct, Half, Half);
320use_simd_implementation!(InnerProduct, i8, i8);
321use_simd_implementation!(InnerProduct, u8, u8);
322
323fn cosine_transformation(x: f32) -> f32 {
331 1.0 - x
332}
333
334#[derive(Debug, Clone, Copy, Default)]
336pub struct Cosine {}
337
338impl PostOp<f32, SimilarityScore<f32>> for Cosine {
339 fn post_op(x: f32) -> SimilarityScore<f32> {
340 debug_assert!(x >= -1.0);
341 debug_assert!(x <= 1.0);
342 SimilarityScore::new(cosine_transformation(x))
343 }
344}
345
346impl PostOp<f32, MathematicalValue<f32>> for Cosine {
347 fn post_op(x: f32) -> MathematicalValue<f32> {
348 debug_assert!(x >= -1.0);
349 debug_assert!(x <= 1.0);
350 MathematicalValue::new(x)
351 }
352}
353
354impl PostOp<f32, f32> for Cosine {
355 fn post_op(x: f32) -> f32 {
356 <Self as PostOp<f32, SimilarityScore<f32>>>::post_op(x).into_inner()
357 }
358}
359
360architecture_hook!(Cosine, simd::CosineStateless);
361use_simd_implementation!(Cosine, f32, f32);
362use_simd_implementation!(Cosine, f32, Half);
363use_simd_implementation!(Cosine, Half, Half);
364use_simd_implementation!(Cosine, i8, i8);
365use_simd_implementation!(Cosine, u8, u8);
366
367#[derive(Debug, Clone, Copy, Default)]
373pub struct CosineNormalized {}
374
375impl PostOp<f32, SimilarityScore<f32>> for CosineNormalized {
376 #[inline(always)]
377 fn post_op(x: f32) -> SimilarityScore<f32> {
378 SimilarityScore::new(cosine_transformation(x))
384 }
385}
386
387impl PostOp<f32, MathematicalValue<f32>> for CosineNormalized {
388 #[inline(always)]
389 fn post_op(x: f32) -> MathematicalValue<f32> {
390 MathematicalValue::new(x)
391 }
392}
393
394impl PostOp<f32, f32> for CosineNormalized {
395 #[inline(always)]
396 fn post_op(x: f32) -> f32 {
397 <Self as PostOp<f32, SimilarityScore<f32>>>::post_op(x).into_inner()
398 }
399}
400
401architecture_hook!(CosineNormalized, simd::IP);
402use_simd_implementation!(CosineNormalized, f32, f32);
403use_simd_implementation!(CosineNormalized, f32, Half);
404use_simd_implementation!(CosineNormalized, Half, Half);
405
406#[derive(Debug, Clone, Copy, Default)]
412pub struct L1NormFunctor {}
413
414impl PostOp<f32, f32> for L1NormFunctor {
415 #[inline(always)]
416 fn post_op(x: f32) -> f32 {
417 x
418 }
419}
420
421architecture_hook!(L1NormFunctor, simd::L1Norm);
422
423impl PureDistanceFunction<&[f32], &[f32], f32> for L1NormFunctor {
424 #[inline]
425 fn evaluate(x: &[f32], y: &[f32]) -> f32 {
426 L1NormFunctor::default().run(ARCH, x, y)
427 }
428}
429
430#[cfg(test)]
435mod tests {
436
437 use std::hash::{Hash, Hasher};
438
439 use approx::assert_relative_eq;
440 use rand::{Rng, SeedableRng};
441
442 use super::*;
443 use crate::{
444 distance::{
445 reference::{self, ReferenceProvider},
446 Metric,
447 },
448 test_util::{self, Normalize},
449 };
450
451 pub fn as_function_pointer<T, Left, Right, Return>(x: &[Left], y: &[Right]) -> Return
452 where
453 T: for<'a, 'b> PureDistanceFunction<&'a [Left], &'b [Right], Return>,
454 {
455 T::evaluate(x, y)
456 }
457
458 fn simd_provider(metric: Metric) -> fn(&[f32], &[f32]) -> f32 {
459 match metric {
460 Metric::L2 => as_function_pointer::<SquaredL2, _, _, _>,
461 Metric::InnerProduct => as_function_pointer::<InnerProduct, _, _, _>,
462 Metric::Cosine => as_function_pointer::<Cosine, _, _, _>,
463 Metric::CosineNormalized => as_function_pointer::<CosineNormalized, _, _, _>,
464 }
465 }
466
467 fn random_normal_arguments(dim: usize, lo: f32, hi: f32, seed: u64) -> (Vec<f32>, Vec<f32>) {
468 let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
469 let x: Vec<f32> = (0..dim).map(|_| rng.random_range(lo..hi)).collect();
470 let y: Vec<f32> = (0..dim).map(|_| rng.random_range(lo..hi)).collect();
471 (x, y)
472 }
473
474 struct LeftRightPair {
475 pub x: Vec<f32>,
476 pub y: Vec<f32>,
477 }
478
479 fn generate_corner_cases(dim: usize) -> Vec<LeftRightPair> {
480 let mut output = Vec::<LeftRightPair>::new();
481 let fixed_values = [0.0, -5.0, 5.0, 10.0];
482
483 for va in fixed_values.iter() {
484 for vb in fixed_values.iter() {
485 let x: Vec<f32> = vec![*va; dim];
486 let y: Vec<f32> = vec![*vb; dim];
487 output.push(LeftRightPair { x, y });
488 }
489 }
490 output
491 }
492
493 fn collect_random_arguments(
494 dim: usize,
495 num_trials: usize,
496 lo: f32,
497 hi: f32,
498 mut seed: u64,
499 ) -> Vec<LeftRightPair> {
500 (0..num_trials)
501 .map(|_| {
502 let (x, y) = random_normal_arguments(dim, lo, hi, seed);
503
504 let mut hasher = std::hash::DefaultHasher::new();
506 seed.hash(&mut hasher);
507 seed = hasher.finish();
508
509 LeftRightPair { x, y }
510 })
511 .collect()
512 }
513
514 fn test_pure_functions_impl<T>(metric: Metric, _func: T, normalize: bool)
515 where
516 T: for<'a, 'b> PureDistanceFunction<&'a [f32], &'b [f32], f32> + Clone,
517 {
518 let epsilon: f32 = 1e-4;
519 let max_relative: f32 = 1e-4;
520
521 let max_dim = 256;
522 let num_trials = 10;
523
524 let f_reference = <f32 as ReferenceProvider<f32>>::reference_implementation(metric);
525 let f_simd = simd_provider(metric);
526
527 let run_tests = |argument_pairs: Vec<LeftRightPair>| {
529 for LeftRightPair { mut x, mut y } in argument_pairs {
530 if normalize {
531 x.normalize();
532 y.normalize();
533 }
534
535 let reference: f32 = f_reference(&x, &y).into_inner();
536 let simd = f_simd(&x, &y);
537
538 assert_relative_eq!(
539 reference,
540 simd,
541 epsilon = epsilon,
542 max_relative = max_relative
543 );
544
545 let simd_direct = T::evaluate(&x, &y);
547 assert_eq!(simd_direct, simd);
548 }
549 };
550
551 for dim in 0..max_dim {
553 run_tests(generate_corner_cases(dim));
554 }
555
556 for dim in 0..max_dim {
558 run_tests(collect_random_arguments(
559 dim, num_trials, -10.0, 10.0, 0x5643,
560 ));
561 }
562 }
563
564 #[test]
565 fn test_pure_functions() {
566 println!("L2");
567 test_pure_functions_impl(Metric::L2, SquaredL2 {}, false);
568 println!("InnerProduct");
569 test_pure_functions_impl(Metric::InnerProduct, InnerProduct {}, false);
570 println!("Cosine");
571 test_pure_functions_impl(Metric::Cosine, Cosine {}, false);
572 println!("CosineNormalized");
573 test_pure_functions_impl(Metric::CosineNormalized, CosineNormalized {}, true);
574 }
575
576 #[test]
579 fn test_specialize() {
580 use diskann_wide::arch::FTarget2;
581
582 const DIM: usize = 123;
583 let (x, y) = random_normal_arguments(DIM, -100.0, 100.0, 0x023457AA);
584
585 let reference: f32 = SquaredL2::evaluate(x.as_slice(), y.as_slice());
586 let evaluated: f32 = Specialize::<DIM, SquaredL2>::run(
587 diskann_wide::ARCH,
588 x.as_slice().as_unaligned(),
589 y.as_slice().as_unaligned(),
590 );
591
592 assert_eq!(reference, evaluated);
594 }
595
596 #[test]
597 #[should_panic]
598 fn test_function_pointer_const_panics_left() {
599 use diskann_wide::arch::FTarget2;
600
601 const DIM: usize = 34;
602 let x = vec![0.0f32; DIM + 1];
603 let y = vec![0.0f32; DIM];
604 let _: f32 = Specialize::<DIM, SquaredL2>::run(
606 diskann_wide::ARCH,
607 x.as_slice().as_unaligned(),
608 y.as_slice().as_unaligned(),
609 );
610 }
611
612 #[test]
613 #[should_panic]
614 fn test_function_pointer_const_panics_right() {
615 use diskann_wide::arch::FTarget2;
616
617 const DIM: usize = 34;
618 let x = vec![0.0f32; DIM];
619 let y = vec![0.0f32; DIM + 1];
620 let _: f32 = Specialize::<DIM, SquaredL2>::run(
622 diskann_wide::ARCH,
623 x.as_slice().as_unaligned(),
624 y.as_slice().as_unaligned(),
625 );
626 }
627
628 trait GetInner {
633 fn get_inner(self) -> f32;
634 }
635
636 impl GetInner for f32 {
637 fn get_inner(self) -> f32 {
638 self
639 }
640 }
641
642 impl GetInner for SimilarityScore<f32> {
643 fn get_inner(self) -> f32 {
644 self.into_inner()
645 }
646 }
647
648 impl GetInner for MathematicalValue<f32> {
649 fn get_inner(self) -> f32 {
650 self.into_inner()
651 }
652 }
653
654 #[derive(Clone, Copy)]
656 struct EpsilonAndRelative {
657 epsilon: f32,
658 max_relative: f32,
659 }
660
661 #[allow(clippy::too_many_arguments)]
662 fn run_test<L, R, To, Distribution, Callback>(
663 under_test: fn(&[L], &[R]) -> To,
664 reference: fn(&[L], &[R]) -> To,
665 bounds: EpsilonAndRelative,
666 dim: usize,
667 num_trials: usize,
668 distribution: Distribution,
669 rng: &mut impl Rng,
670 mut cb: Callback,
671 ) where
672 L: test_util::CornerCases,
673 R: test_util::CornerCases,
674 Distribution:
675 test_util::GenerateRandomArguments<L> + test_util::GenerateRandomArguments<R> + Clone,
676 To: GetInner + Copy,
677 Callback: FnMut(To, To),
678 {
679 let mut checker =
680 test_util::Checker::<L, R, To>::new(under_test, reference, |got, expected| {
681 cb(got, expected);
683 assert_relative_eq!(
684 got.get_inner(),
685 expected.get_inner(),
686 epsilon = bounds.epsilon,
687 max_relative = bounds.max_relative
688 );
689 });
690
691 test_util::test_distance_function(
692 &mut checker,
693 distribution.clone(),
694 distribution.clone(),
695 dim,
696 num_trials,
697 rng,
698 );
699 }
700
701 #[cfg(not(debug_assertions))]
703 const MAX_DIM: usize = 256;
704
705 #[cfg(debug_assertions)]
706 const MAX_DIM: usize = 160;
707
708 #[cfg(not(debug_assertions))]
710 const INTEGER_TRIALS: usize = 10000;
711
712 #[cfg(debug_assertions)]
713 const INTEGER_TRIALS: usize = 100;
714
715 fn run_integer_test<T, R>(
722 under_test: fn(&[T], &[T]) -> R,
723 reference: fn(&[T], &[T]) -> R,
724 rng: &mut impl Rng,
725 ) where
726 T: test_util::CornerCases,
727 R: GetInner + Copy,
728 rand::distr::StandardUniform: test_util::GenerateRandomArguments<T> + Clone,
729 {
730 let distribution = rand::distr::StandardUniform {};
731 let num_corner_cases = <T as test_util::CornerCases>::corner_cases().len();
732
733 for dim in 0..MAX_DIM {
734 let mut callcount = 0;
735 let callback = |_, _| {
736 callcount += 1;
737 };
738
739 run_test(
740 under_test,
741 reference,
742 EpsilonAndRelative {
743 epsilon: 0.0,
744 max_relative: 0.0,
745 },
746 dim,
747 INTEGER_TRIALS,
748 distribution,
749 rng,
750 callback,
751 );
752
753 assert_eq!(
755 callcount,
756 INTEGER_TRIALS + num_corner_cases * num_corner_cases
757 );
758 }
759 }
760
761 #[test]
766 fn test_l2_i8_mathematical() {
767 let mut rng = rand::rngs::StdRng::seed_from_u64(0x2bb701074c2b81c9);
768 run_integer_test(
769 as_function_pointer::<FullL2, i8, i8, MathematicalValue<f32>>,
770 reference::reference_l2_i8_mathematical,
771 &mut rng,
772 );
773 }
774
775 #[test]
776 fn test_l2_u8_mathematical() {
777 let mut rng = rand::rngs::StdRng::seed_from_u64(0x9284ced6d080808c);
778 run_integer_test(
779 as_function_pointer::<FullL2, u8, u8, MathematicalValue<f32>>,
780 reference::reference_l2_u8_mathematical,
781 &mut rng,
782 );
783 }
784
785 #[test]
786 fn test_l2_i8_similarity() {
787 let mut rng = rand::rngs::StdRng::seed_from_u64(0xb196fecc4def04fa);
788 run_integer_test(
789 as_function_pointer::<FullL2, i8, i8, SimilarityScore<f32>>,
790 reference::reference_l2_i8_similarity,
791 &mut rng,
792 );
793 }
794
795 #[test]
796 fn test_l2_u8_similarity() {
797 let mut rng = rand::rngs::StdRng::seed_from_u64(0x07f6463e4a654aea);
798 run_integer_test(
799 as_function_pointer::<FullL2, u8, u8, SimilarityScore<f32>>,
800 reference::reference_l2_u8_similarity,
801 &mut rng,
802 );
803 }
804
805 #[test]
810 fn test_innerproduct_i8_mathematical() {
811 let mut rng = rand::rngs::StdRng::seed_from_u64(0x2c1b1bddda5774be);
812 run_integer_test(
813 as_function_pointer::<InnerProduct, i8, i8, MathematicalValue<f32>>,
814 reference::reference_innerproduct_i8_mathematical,
815 &mut rng,
816 );
817 }
818
819 #[test]
820 fn test_innerproduct_u8_mathematical() {
821 let mut rng = rand::rngs::StdRng::seed_from_u64(0x757e363832d7f215);
822 run_integer_test(
823 as_function_pointer::<InnerProduct, u8, u8, MathematicalValue<f32>>,
824 reference::reference_innerproduct_u8_mathematical,
825 &mut rng,
826 );
827 }
828
829 #[test]
830 fn test_innerproduct_i8_similarity() {
831 let mut rng = rand::rngs::StdRng::seed_from_u64(0x4788ce0b991eb15a);
832 run_integer_test(
833 as_function_pointer::<InnerProduct, i8, i8, SimilarityScore<f32>>,
834 reference::reference_innerproduct_i8_similarity,
835 &mut rng,
836 );
837 }
838
839 #[test]
840 fn test_innerproduct_u8_similarity() {
841 let mut rng = rand::rngs::StdRng::seed_from_u64(0x4994adb68f814d96);
842 run_integer_test(
843 as_function_pointer::<InnerProduct, u8, u8, SimilarityScore<f32>>,
844 reference::reference_innerproduct_u8_similarity,
845 &mut rng,
846 );
847 }
848
849 #[test]
854 fn test_cosine_i8_mathematical() {
855 let mut rng = rand::rngs::StdRng::seed_from_u64(0xedef81c780491ada);
856 run_integer_test(
857 as_function_pointer::<Cosine, i8, i8, MathematicalValue<f32>>,
858 reference::reference_cosine_i8_mathematical,
859 &mut rng,
860 );
861 }
862
863 #[test]
864 fn test_cosine_u8_mathematical() {
865 let mut rng = rand::rngs::StdRng::seed_from_u64(0x107cee2adcc58b73);
866 run_integer_test(
867 as_function_pointer::<Cosine, u8, u8, MathematicalValue<f32>>,
868 reference::reference_cosine_u8_mathematical,
869 &mut rng,
870 );
871 }
872
873 #[test]
874 fn test_cosine_i8_similarity() {
875 let mut rng = rand::rngs::StdRng::seed_from_u64(0x02d95c1cc0843647);
876 run_integer_test(
877 as_function_pointer::<Cosine, i8, i8, SimilarityScore<f32>>,
878 reference::reference_cosine_i8_similarity,
879 &mut rng,
880 );
881 }
882
883 #[test]
884 fn test_cosine_u8_similarity() {
885 let mut rng = rand::rngs::StdRng::seed_from_u64(0xf5ea1974bf8d8b3b);
886 run_integer_test(
887 as_function_pointer::<Cosine, u8, u8, SimilarityScore<f32>>,
888 reference::reference_cosine_u8_similarity,
889 &mut rng,
890 );
891 }
892
893 fn run_float_test<L, R, To, Dist>(
900 under_test: fn(&[L], &[R]) -> To,
901 reference: fn(&[L], &[R]) -> To,
902 rng: &mut impl Rng,
903 distribution: Dist,
904 bounds: EpsilonAndRelative,
905 ) where
906 L: test_util::CornerCases,
907 R: test_util::CornerCases,
908 To: GetInner + Copy,
909 Dist: test_util::GenerateRandomArguments<L> + test_util::GenerateRandomArguments<R> + Clone,
910 {
911 let left_corner_cases = <L as test_util::CornerCases>::corner_cases().len();
912 let right_corner_cases = <R as test_util::CornerCases>::corner_cases().len();
913 for dim in 0..MAX_DIM {
914 let mut callcount = 0;
915 let callback = |_, _| {
916 callcount += 1;
917 };
918
919 run_test(
920 under_test,
921 reference,
922 bounds,
923 dim,
924 INTEGER_TRIALS,
925 distribution.clone(),
926 rng,
927 callback,
928 );
929
930 assert_eq!(
932 callcount,
933 INTEGER_TRIALS + left_corner_cases * right_corner_cases
934 );
935 }
936 }
937
938 fn expected_l2_errors() -> EpsilonAndRelative {
943 EpsilonAndRelative {
944 epsilon: 0.0,
945 max_relative: 1.2e-6,
946 }
947 }
948
949 #[test]
950 fn test_l2_f32_mathematical() {
951 let mut rng = rand::rngs::StdRng::seed_from_u64(0x6d22d320bdf35aec);
952 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
953 run_float_test(
954 as_function_pointer::<FullL2, f32, f32, MathematicalValue<f32>>,
955 reference::reference_l2_f32_mathematical,
956 &mut rng,
957 distribution,
958 expected_l2_errors(),
959 );
960 }
961
962 #[test]
963 fn test_l2_f16_mathematical() {
964 let mut rng = rand::rngs::StdRng::seed_from_u64(0x755819460c190db4);
965 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
966 run_float_test(
967 as_function_pointer::<FullL2, Half, Half, MathematicalValue<f32>>,
968 reference::reference_l2_f16_mathematical,
969 &mut rng,
970 distribution,
971 expected_l2_errors(),
972 );
973 }
974
975 #[test]
976 fn test_l2_f32xf16_mathematical() {
977 let mut rng = rand::rngs::StdRng::seed_from_u64(0x755819460c190db4);
978 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
979
980 run_float_test(
981 as_function_pointer::<FullL2, f32, Half, MathematicalValue<f32>>,
982 reference::reference_l2_f32xf16_mathematical,
983 &mut rng,
984 distribution,
985 expected_l2_errors(),
986 );
987 }
988
989 #[test]
990 fn test_l2_f32_similarity() {
991 let mut rng = rand::rngs::StdRng::seed_from_u64(0xbfc5f4b42b5bc0c1);
992 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
993 run_float_test(
994 as_function_pointer::<FullL2, f32, f32, SimilarityScore<f32>>,
995 reference::reference_l2_f32_similarity,
996 &mut rng,
997 distribution,
998 expected_l2_errors(),
999 );
1000 }
1001
1002 #[test]
1003 fn test_l2_f16_similarity() {
1004 let mut rng = rand::rngs::StdRng::seed_from_u64(0x9d3809d84f54e4b6);
1005 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1006 run_float_test(
1007 as_function_pointer::<FullL2, Half, Half, SimilarityScore<f32>>,
1008 reference::reference_l2_f16_similarity,
1009 &mut rng,
1010 distribution,
1011 expected_l2_errors(),
1012 );
1013 }
1014
1015 #[test]
1016 fn test_l2_f32xf16_similarity() {
1017 let mut rng = rand::rngs::StdRng::seed_from_u64(0x755819460c190db4);
1018 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1019
1020 run_float_test(
1021 as_function_pointer::<FullL2, f32, Half, SimilarityScore<f32>>,
1022 reference::reference_l2_f32xf16_similarity,
1023 &mut rng,
1024 distribution,
1025 expected_l2_errors(),
1026 );
1027 }
1028
1029 fn expected_innerproduct_errors() -> EpsilonAndRelative {
1034 EpsilonAndRelative {
1035 epsilon: 2.5e-5,
1036 max_relative: 1.6e-5,
1037 }
1038 }
1039
1040 #[test]
1041 fn test_innerproduct_f32_mathematical() {
1042 let mut rng = rand::rngs::StdRng::seed_from_u64(0x1ef6ac3b65869792);
1043 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1044 run_float_test(
1045 as_function_pointer::<InnerProduct, f32, f32, MathematicalValue<f32>>,
1046 reference::reference_innerproduct_f32_mathematical,
1047 &mut rng,
1048 distribution,
1049 expected_innerproduct_errors(),
1050 );
1051 }
1052
1053 #[test]
1054 fn test_innerproduct_f16_mathematical() {
1055 let mut rng = rand::rngs::StdRng::seed_from_u64(0x24c51e4b825b0329);
1056 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1057 run_float_test(
1058 as_function_pointer::<InnerProduct, Half, Half, MathematicalValue<f32>>,
1059 reference::reference_innerproduct_f16_mathematical,
1060 &mut rng,
1061 distribution,
1062 expected_innerproduct_errors(),
1063 );
1064 }
1065
1066 #[test]
1067 fn test_innerproduct_f32xf16_mathematical() {
1068 let mut rng = rand::rngs::StdRng::seed_from_u64(0x24c51e4b825b0329);
1069 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1070 run_float_test(
1071 as_function_pointer::<InnerProduct, f32, Half, MathematicalValue<f32>>,
1072 reference::reference_innerproduct_f32xf16_mathematical,
1073 &mut rng,
1074 distribution,
1075 expected_innerproduct_errors(),
1076 );
1077 }
1078
1079 #[test]
1080 fn test_innerproduct_f32_similarity() {
1081 let mut rng = rand::rngs::StdRng::seed_from_u64(0x40326b22a57db0d7);
1082 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1083 run_float_test(
1084 as_function_pointer::<InnerProduct, f32, f32, SimilarityScore<f32>>,
1085 reference::reference_innerproduct_f32_similarity,
1086 &mut rng,
1087 distribution,
1088 expected_innerproduct_errors(),
1089 );
1090 }
1091
1092 #[test]
1093 fn test_innerproduct_f16_similarity() {
1094 let mut rng = rand::rngs::StdRng::seed_from_u64(0xfb8cff47bcbc9528);
1095 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1096 run_float_test(
1097 as_function_pointer::<InnerProduct, Half, Half, SimilarityScore<f32>>,
1098 reference::reference_innerproduct_f16_similarity,
1099 &mut rng,
1100 distribution,
1101 expected_innerproduct_errors(),
1102 );
1103 }
1104
1105 #[test]
1106 fn test_innerproduct_f32xf16_similarity() {
1107 let mut rng = rand::rngs::StdRng::seed_from_u64(0x24c51e4b825b0329);
1108 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1109 run_float_test(
1110 as_function_pointer::<InnerProduct, f32, Half, SimilarityScore<f32>>,
1111 reference::reference_innerproduct_f32xf16_similarity,
1112 &mut rng,
1113 distribution,
1114 expected_innerproduct_errors(),
1115 );
1116 }
1117
1118 fn expected_cosine_errors() -> EpsilonAndRelative {
1123 EpsilonAndRelative {
1124 epsilon: 3e-7,
1125 max_relative: 5e-6,
1126 }
1127 }
1128
1129 #[test]
1130 fn test_cosine_f32_mathematical() {
1131 let mut rng = rand::rngs::StdRng::seed_from_u64(0xca6eaac942999500);
1132 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1133 run_float_test(
1134 as_function_pointer::<Cosine, f32, f32, MathematicalValue<f32>>,
1135 reference::reference_cosine_f32_mathematical,
1136 &mut rng,
1137 distribution,
1138 expected_cosine_errors(),
1139 );
1140 }
1141
1142 #[test]
1143 fn test_cosine_f16_mathematical() {
1144 let mut rng = rand::rngs::StdRng::seed_from_u64(0xa736c789aa16ce86);
1145 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1146 run_float_test(
1147 as_function_pointer::<Cosine, Half, Half, MathematicalValue<f32>>,
1148 reference::reference_cosine_f16_mathematical,
1149 &mut rng,
1150 distribution,
1151 expected_cosine_errors(),
1152 );
1153 }
1154
1155 #[test]
1156 fn test_cosine_f32xf16_mathematical() {
1157 let mut rng = rand::rngs::StdRng::seed_from_u64(0xac550231088a0d5c);
1158 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1159 run_float_test(
1160 as_function_pointer::<Cosine, f32, Half, MathematicalValue<f32>>,
1161 reference::reference_cosine_f32xf16_mathematical,
1162 &mut rng,
1163 distribution,
1164 expected_cosine_errors(),
1165 );
1166 }
1167
1168 #[test]
1169 fn test_cosine_f32_similarity() {
1170 let mut rng = rand::rngs::StdRng::seed_from_u64(0x4a09ad987a6204f3);
1171 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1172 run_float_test(
1173 as_function_pointer::<Cosine, f32, f32, SimilarityScore<f32>>,
1174 reference::reference_cosine_f32_similarity,
1175 &mut rng,
1176 distribution,
1177 expected_cosine_errors(),
1178 );
1179 }
1180
1181 #[test]
1182 fn test_cosine_f16_similarity() {
1183 let mut rng = rand::rngs::StdRng::seed_from_u64(0x77a48d1914f850f2);
1184 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1185 run_float_test(
1186 as_function_pointer::<Cosine, Half, Half, SimilarityScore<f32>>,
1187 reference::reference_cosine_f16_similarity,
1188 &mut rng,
1189 distribution,
1190 expected_cosine_errors(),
1191 );
1192 }
1193
1194 #[test]
1195 fn test_cosine_f32xf16_similarity() {
1196 let mut rng = rand::rngs::StdRng::seed_from_u64(0xbd7471b815655ca1);
1197 let distribution = rand_distr::Normal::new(0.0, 1.0).unwrap();
1198 run_float_test(
1199 as_function_pointer::<Cosine, f32, Half, SimilarityScore<f32>>,
1200 reference::reference_cosine_f32xf16_similarity,
1201 &mut rng,
1202 distribution,
1203 expected_cosine_errors(),
1204 );
1205 }
1206
1207 fn expected_cosine_normalized_errors() -> EpsilonAndRelative {
1212 EpsilonAndRelative {
1213 epsilon: 3e-7,
1214 max_relative: 5e-6,
1215 }
1216 }
1217
1218 #[test]
1219 fn test_cosine_normalized_f32_mathematical() {
1220 let mut rng = rand::rngs::StdRng::seed_from_u64(0x1fda98112747f8dd);
1221 let distribution = rand_distr::Normal::new(-1.0, 1.0).unwrap();
1222 run_float_test(
1223 as_function_pointer::<CosineNormalized, f32, f32, MathematicalValue<f32>>,
1224 reference::reference_cosine_normalized_f32_mathematical,
1225 &mut rng,
1226 test_util::Normalized(distribution),
1227 expected_cosine_normalized_errors(),
1228 );
1229 }
1230
1231 #[test]
1232 fn test_cosine_normalized_f16_mathematical() {
1233 let mut rng = rand::rngs::StdRng::seed_from_u64(0x5e8c5d5e19cdd840);
1234 let distribution = rand_distr::Normal::new(-1.0, 1.0).unwrap();
1235 run_float_test(
1236 as_function_pointer::<CosineNormalized, Half, Half, MathematicalValue<f32>>,
1237 reference::reference_cosine_normalized_f16_mathematical,
1238 &mut rng,
1239 test_util::Normalized(distribution),
1240 expected_cosine_normalized_errors(),
1241 );
1242 }
1243
1244 #[test]
1245 fn test_cosine_normalized_f32xf16_mathematical() {
1246 let mut rng = rand::rngs::StdRng::seed_from_u64(0x3fd01e1c11c9bc45);
1247 let distribution = rand_distr::Normal::new(-1.0, 1.0).unwrap();
1248 run_float_test(
1249 as_function_pointer::<CosineNormalized, f32, Half, MathematicalValue<f32>>,
1250 reference::reference_cosine_normalized_f32xf16_mathematical,
1251 &mut rng,
1252 test_util::Normalized(distribution),
1253 expected_cosine_normalized_errors(),
1254 );
1255 }
1256
1257 #[test]
1258 fn test_cosine_normalized_f32_similarity() {
1259 let mut rng = rand::rngs::StdRng::seed_from_u64(0x9446d057870e5605);
1260 let distribution = rand_distr::Normal::new(-1.0, 1.0).unwrap();
1261 run_float_test(
1262 as_function_pointer::<CosineNormalized, f32, f32, SimilarityScore<f32>>,
1263 reference::reference_cosine_normalized_f32_similarity,
1264 &mut rng,
1265 test_util::Normalized(distribution),
1266 expected_cosine_normalized_errors(),
1267 );
1268 }
1269
1270 #[test]
1271 fn test_cosine_normalized_f16_similarity() {
1272 let mut rng = rand::rngs::StdRng::seed_from_u64(0x885c371801f18174);
1273 let distribution = rand_distr::Normal::new(-1.0, 1.0).unwrap();
1274 run_float_test(
1275 as_function_pointer::<CosineNormalized, Half, Half, SimilarityScore<f32>>,
1276 reference::reference_cosine_normalized_f16_similarity,
1277 &mut rng,
1278 test_util::Normalized(distribution),
1279 expected_cosine_normalized_errors(),
1280 );
1281 }
1282
1283 #[test]
1284 fn test_cosine_normalized_f32xf16_similarity() {
1285 let mut rng = rand::rngs::StdRng::seed_from_u64(0x1c356c92d0522c0f);
1286 let distribution = rand_distr::Normal::new(-1.0, 1.0).unwrap();
1287 run_float_test(
1288 as_function_pointer::<CosineNormalized, f32, Half, SimilarityScore<f32>>,
1289 reference::reference_cosine_normalized_f32xf16_similarity,
1290 &mut rng,
1291 test_util::Normalized(distribution),
1292 expected_cosine_normalized_errors(),
1293 );
1294 }
1295}