usearch 2.9.1 → 2.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CITATION.cff +1 -1
  2. package/CONTRIBUTING.md +10 -2
  3. package/Cargo.toml +1 -1
  4. package/README.md +12 -15
  5. package/VERSION +1 -1
  6. package/c/test.c +58 -50
  7. package/conanfile.py +1 -1
  8. package/include/usearch/index.hpp +1 -1
  9. package/include/usearch/index_plugins.hpp +20 -10
  10. package/javascript/types/usearch.d.ts.map +1 -1
  11. package/javascript/usearch.js +4 -1
  12. package/package.json +1 -1
  13. package/simsimd/CONTRIBUTING.md +88 -0
  14. package/simsimd/Cargo.lock +4 -3
  15. package/simsimd/Cargo.toml +10 -2
  16. package/simsimd/README.md +102 -60
  17. package/simsimd/include/simsimd/binary.h +44 -0
  18. package/simsimd/include/simsimd/complex.h +174 -0
  19. package/simsimd/include/simsimd/simsimd.h +18 -9
  20. package/simsimd/include/simsimd/spatial.h +128 -36
  21. package/simsimd/include/simsimd/types.h +3 -2
  22. package/simsimd/package-lock.json +5 -5
  23. package/simsimd/package.json +1 -1
  24. package/simsimd/rust/benches/native.rs +12 -9
  25. package/simsimd/rust/lib.c +32 -55
  26. package/simsimd/rust/lib.rs +374 -56
  27. package/stringzilla/CONTRIBUTING.md +17 -0
  28. package/stringzilla/Cargo.lock +1 -1
  29. package/stringzilla/Cargo.toml +1 -1
  30. package/stringzilla/README.md +175 -34
  31. package/stringzilla/VERSION +1 -1
  32. package/stringzilla/build.rs +77 -4
  33. package/stringzilla/c/lib.c +33 -0
  34. package/stringzilla/cli/README.md +30 -13
  35. package/stringzilla/cli/__init__.py +0 -0
  36. package/stringzilla/cli/split.py +66 -33
  37. package/stringzilla/cli/wc.py +101 -21
  38. package/stringzilla/include/stringzilla/experimental.h +122 -1
  39. package/stringzilla/include/stringzilla/stringzilla.h +262 -132
  40. package/stringzilla/include/stringzilla/stringzilla.hpp +13 -5
  41. package/stringzilla/package.json +1 -1
  42. package/stringzilla/rust/lib.rs +673 -162
  43. package/wasmer.toml +1 -1
@@ -194,11 +194,25 @@ simsimd_neon_f16_l2sq(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_si
194
194
  float32x4_t diff_vec = vsubq_f32(a_vec, b_vec);
195
195
  sum_vec = vfmaq_f32(sum_vec, diff_vec, diff_vec);
196
196
  }
197
- simsimd_f32_t sum = vaddvq_f32(sum_vec);
198
- for (; i < n; ++i) {
199
- simsimd_f32_t diff = SIMSIMD_UNCOMPRESS_F16(a[i]) - SIMSIMD_UNCOMPRESS_F16(b[i]);
200
- sum += diff * diff;
197
+
198
+ // In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
199
+ // function will run. It is extremely slow, so even for the tail, let's combine serial
200
+ // loads and stores with vectorized math.
201
+ if (i < n) {
202
+ union {
203
+ float16x4_t f16_vec;
204
+ simsimd_f16_t f16[4];
205
+ } a_padded_tail, b_padded_tail;
206
+ simsimd_size_t j = 0;
207
+ for (; i < n; ++i, ++j)
208
+ a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
209
+ for (; j < 4; ++j)
210
+ a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
211
+ float32x4_t diff_vec = vsubq_f32(vcvt_f32_f16(a_padded_tail.f16_vec), vcvt_f32_f16(b_padded_tail.f16_vec));
212
+ sum_vec = vfmaq_f32(sum_vec, diff_vec, diff_vec);
201
213
  }
214
+
215
+ simsimd_f32_t sum = vaddvq_f32(sum_vec);
202
216
  return sum;
203
217
  }
204
218
 
@@ -212,9 +226,23 @@ simsimd_neon_f16_ip(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size
212
226
  float32x4_t b_vec = vcvt_f32_f16(vld1_f16((float16_t const*)b + i));
213
227
  ab_vec = vfmaq_f32(ab_vec, a_vec, b_vec);
214
228
  }
229
+
230
+ // In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
231
+ // function will run. It is extremely slow, so even for the tail, let's combine serial
232
+ // loads and stores with vectorized math.
233
+ if (i < n) {
234
+ union {
235
+ float16x4_t f16_vec;
236
+ simsimd_f16_t f16[4];
237
+ } a_padded_tail, b_padded_tail;
238
+ simsimd_size_t j = 0;
239
+ for (; i < n; ++i, ++j)
240
+ a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
241
+ for (; j < 4; ++j)
242
+ a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
243
+ ab_vec = vfmaq_f32(ab_vec, vcvt_f32_f16(a_padded_tail.f16_vec), vcvt_f32_f16(b_padded_tail.f16_vec));
244
+ }
215
245
  simsimd_f32_t ab = vaddvq_f32(ab_vec);
216
- for (; i < n; ++i)
217
- ab += SIMSIMD_UNCOMPRESS_F16(a[i]) * SIMSIMD_UNCOMPRESS_F16(b[i]);
218
246
  return 1 - ab;
219
247
  }
220
248
 
@@ -230,13 +258,29 @@ simsimd_neon_f16_cos(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_siz
230
258
  a2_vec = vfmaq_f32(a2_vec, a_vec, a_vec);
231
259
  b2_vec = vfmaq_f32(b2_vec, b_vec, b_vec);
232
260
  }
233
- simsimd_f32_t ab = vaddvq_f32(ab_vec), a2 = vaddvq_f32(a2_vec), b2 = vaddvq_f32(b2_vec);
234
- for (; i < n; ++i) {
235
- simsimd_f32_t ai = SIMSIMD_UNCOMPRESS_F16(a[i]), bi = SIMSIMD_UNCOMPRESS_F16(b[i]);
236
- ab += ai * bi, a2 += ai * ai, b2 += bi * bi;
261
+
262
+ // In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
263
+ // function will run. It is extremely slow, so even for the tail, let's combine serial
264
+ // loads and stores with vectorized math.
265
+ if (i < n) {
266
+ union {
267
+ float16x4_t f16_vec;
268
+ simsimd_f16_t f16[4];
269
+ } a_padded_tail, b_padded_tail;
270
+ simsimd_size_t j = 0;
271
+ for (; i < n; ++i, ++j)
272
+ a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
273
+ for (; j < 4; ++j)
274
+ a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
275
+ float32x4_t a_vec = vcvt_f32_f16(a_padded_tail.f16_vec);
276
+ float32x4_t b_vec = vcvt_f32_f16(b_padded_tail.f16_vec);
277
+ ab_vec = vfmaq_f32(ab_vec, a_vec, b_vec);
278
+ a2_vec = vfmaq_f32(a2_vec, a_vec, a_vec);
279
+ b2_vec = vfmaq_f32(b2_vec, b_vec, b_vec);
237
280
  }
238
281
 
239
282
  // Avoid `simsimd_approximate_inverse_square_root` on Arm NEON
283
+ simsimd_f32_t ab = vaddvq_f32(ab_vec), a2 = vaddvq_f32(a2_vec), b2 = vaddvq_f32(b2_vec);
240
284
  simsimd_f32_t a2_b2_arr[2] = {a2, b2};
241
285
  float32x2_t a2_b2 = vld1_f32(a2_b2_arr);
242
286
  a2_b2 = vrsqrte_f32(a2_b2);
@@ -582,18 +626,31 @@ simsimd_avx2_f16_l2sq(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_si
582
626
  d2_vec = _mm256_fmadd_ps(d_vec, d_vec, d2_vec);
583
627
  }
584
628
 
629
+ // In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
630
+ // function will run. It is extremely slow, so even for the tail, let's combine serial
631
+ // loads and stores with vectorized math.
632
+ if (i < n) {
633
+ union {
634
+ __m128i f16_vec;
635
+ simsimd_f16_t f16[8];
636
+ } a_padded_tail, b_padded_tail;
637
+ simsimd_size_t j = 0;
638
+ for (; i < n; ++i, ++j)
639
+ a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
640
+ for (; j < 8; ++j)
641
+ a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
642
+ __m256 a_vec = _mm256_cvtph_ps(a_padded_tail.f16_vec);
643
+ __m256 b_vec = _mm256_cvtph_ps(b_padded_tail.f16_vec);
644
+ __m256 d_vec = _mm256_sub_ps(a_vec, b_vec);
645
+ d2_vec = _mm256_fmadd_ps(d_vec, d_vec, d2_vec);
646
+ }
647
+
585
648
  d2_vec = _mm256_add_ps(_mm256_permute2f128_ps(d2_vec, d2_vec, 1), d2_vec);
586
649
  d2_vec = _mm256_hadd_ps(d2_vec, d2_vec);
587
650
  d2_vec = _mm256_hadd_ps(d2_vec, d2_vec);
588
651
 
589
652
  simsimd_f32_t result;
590
653
  _mm_store_ss(&result, _mm256_castps256_ps128(d2_vec));
591
-
592
- // Accumulate the tail:
593
- for (; i < n; ++i) {
594
- simsimd_f32_t n = SIMSIMD_UNCOMPRESS_F16(a[i]) - SIMSIMD_UNCOMPRESS_F16(b[i]);
595
- result += n * n;
596
- }
597
654
  return result;
598
655
  }
599
656
 
@@ -608,18 +665,33 @@ simsimd_avx2_f16_ip(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size
608
665
  ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
609
666
  }
610
667
 
668
+ // In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
669
+ // function will run. It is extremely slow, so even for the tail, let's combine serial
670
+ // loads and stores with vectorized math.
671
+ if (i < n) {
672
+ union {
673
+ __m128i f16_vec;
674
+ simsimd_f16_t f16[8];
675
+ } a_padded_tail, b_padded_tail;
676
+ simsimd_size_t j = 0;
677
+ for (; i < n; ++i, ++j)
678
+ a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
679
+ for (; j < 8; ++j)
680
+ a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
681
+ __m256 a_vec = _mm256_cvtph_ps(a_padded_tail.f16_vec);
682
+ __m256 b_vec = _mm256_cvtph_ps(b_padded_tail.f16_vec);
683
+ ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
684
+ }
685
+
611
686
  ab_vec = _mm256_add_ps(_mm256_permute2f128_ps(ab_vec, ab_vec, 1), ab_vec);
612
687
  ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
613
688
  ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
614
689
 
615
690
  simsimd_f32_t result;
616
691
  _mm_store_ss(&result, _mm256_castps256_ps128(ab_vec));
617
-
618
- // Accumulate the tail:
619
- for (; i < n; ++i)
620
- result += SIMSIMD_UNCOMPRESS_F16(a[i]) * SIMSIMD_UNCOMPRESS_F16(b[i]);
621
692
  return 1 - result;
622
693
  }
694
+
623
695
  __attribute__((target("avx2,f16c,fma"))) //
624
696
  inline static simsimd_f32_t
625
697
  simsimd_avx2_f16_cos(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n) {
@@ -634,6 +706,27 @@ simsimd_avx2_f16_cos(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_siz
634
706
  b2_vec = _mm256_fmadd_ps(b_vec, b_vec, b2_vec);
635
707
  }
636
708
 
709
+ // In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
710
+ // function will run. It is extremely slow, so even for the tail, let's combine serial
711
+ // loads and stores with vectorized math.
712
+ if (i < n) {
713
+ union {
714
+ __m128i f16_vec;
715
+ simsimd_f16_t f16[8];
716
+ } a_padded_tail, b_padded_tail;
717
+ simsimd_size_t j = 0;
718
+ for (; i < n; ++i, ++j)
719
+ a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
720
+ for (; j < 8; ++j)
721
+ a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
722
+ __m256 a_vec = _mm256_cvtph_ps(a_padded_tail.f16_vec);
723
+ __m256 b_vec = _mm256_cvtph_ps(b_padded_tail.f16_vec);
724
+ ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
725
+ a2_vec = _mm256_fmadd_ps(a_vec, a_vec, a2_vec);
726
+ b2_vec = _mm256_fmadd_ps(b_vec, b_vec, b2_vec);
727
+ }
728
+
729
+ // Horizontal reductions:
637
730
  ab_vec = _mm256_add_ps(_mm256_permute2f128_ps(ab_vec, ab_vec, 1), ab_vec);
638
731
  ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
639
732
  ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
@@ -651,12 +744,6 @@ simsimd_avx2_f16_cos(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_siz
651
744
  _mm_store_ss(&a2, _mm256_castps256_ps128(a2_vec));
652
745
  _mm_store_ss(&b2, _mm256_castps256_ps128(b2_vec));
653
746
 
654
- // Accumulate the tail:
655
- for (; i < n; ++i) {
656
- simsimd_f32_t ai = SIMSIMD_UNCOMPRESS_F16(a[i]), bi = SIMSIMD_UNCOMPRESS_F16(b[i]);
657
- ab += ai * bi, a2 += ai * ai, b2 += bi * bi;
658
- }
659
-
660
747
  // Replace simsimd_approximate_inverse_square_root with `rsqrtss`
661
748
  __m128 a2_sqrt_recip = _mm_rsqrt_ss(_mm_set_ss((float)a2));
662
749
  __m128 b2_sqrt_recip = _mm_rsqrt_ss(_mm_set_ss((float)b2));
@@ -1037,19 +1124,24 @@ simsimd_avx512_i8_cos(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size
1037
1124
  __m512i a_vec, b_vec;
1038
1125
 
1039
1126
  simsimd_avx512_i8_cos_cycle:
1040
- if (n < 64) {
1041
- __mmask64 mask = _bzhi_u64(0xFFFFFFFFFFFFFFFF, n);
1042
- a_vec = _mm512_maskz_loadu_epi8(mask, a);
1043
- b_vec = _mm512_maskz_loadu_epi8(mask, b);
1127
+ if (n < 32) {
1128
+ __mmask32 mask = _bzhi_u32(0xFFFFFFFF, n);
1129
+ a_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, a));
1130
+ b_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, b));
1044
1131
  n = 0;
1045
1132
  } else {
1046
- a_vec = _mm512_loadu_epi8(a);
1047
- b_vec = _mm512_loadu_epi8(b);
1048
- a += 64, b += 64, n -= 64;
1133
+ a_vec = _mm512_cvtepi8_epi16(_mm256_loadu_epi8(a));
1134
+ b_vec = _mm512_cvtepi8_epi16(_mm256_loadu_epi8(b));
1135
+ a += 32, b += 32, n -= 32;
1049
1136
  }
1050
- ab_i32s_vec = _mm512_dpbusd_epi32(ab_i32s_vec, a_vec, b_vec);
1051
- a2_i32s_vec = _mm512_dpbusd_epi32(a2_i32s_vec, a_vec, a_vec);
1052
- b2_i32s_vec = _mm512_dpbusd_epi32(b2_i32s_vec, b_vec, b_vec);
1137
+ // Unfortunately we can't use the `_mm512_dpbusd_epi32` intrinsics here either,
1138
+ // as it's assymetric with respect to the sign of the input arguments:
1139
+ // Signed(ZeroExtend16(a.byte[4*j]) * SignExtend16(b.byte[4*j]))
1140
+ // So we have to use the `_mm512_dpwssd_epi32` intrinsics instead, upcasting
1141
+ // to 16-bit beforehand.
1142
+ ab_i32s_vec = _mm512_dpwssd_epi32(ab_i32s_vec, a_vec, b_vec);
1143
+ a2_i32s_vec = _mm512_dpwssd_epi32(a2_i32s_vec, a_vec, a_vec);
1144
+ b2_i32s_vec = _mm512_dpwssd_epi32(b2_i32s_vec, b_vec, b_vec);
1053
1145
  if (n)
1054
1146
  goto simsimd_avx512_i8_cos_cycle;
1055
1147
 
@@ -84,11 +84,11 @@
84
84
  #endif
85
85
 
86
86
  #ifndef SIMSIMD_F32_DIVISION_EPSILON
87
- #define SIMSIMD_F32_DIVISION_EPSILON 1e-7
87
+ #define SIMSIMD_F32_DIVISION_EPSILON (1e-7)
88
88
  #endif
89
89
 
90
90
  #ifndef SIMSIMD_F16_DIVISION_EPSILON
91
- #define SIMSIMD_F16_DIVISION_EPSILON 1e-3
91
+ #define SIMSIMD_F16_DIVISION_EPSILON (1e-3)
92
92
  #endif
93
93
 
94
94
  #ifdef __cplusplus
@@ -100,6 +100,7 @@ typedef float simsimd_f32_t;
100
100
  typedef double simsimd_f64_t;
101
101
  typedef signed char simsimd_i8_t;
102
102
  typedef unsigned char simsimd_b8_t;
103
+ typedef unsigned long long simsimd_u64_t;
103
104
  typedef unsigned long long simsimd_size_t;
104
105
 
105
106
  #if !defined(SIMSIMD_NATIVE_F16) || SIMSIMD_NATIVE_F16
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "simsimd",
3
- "version": "3.6.4",
3
+ "version": "3.8.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "simsimd",
9
- "version": "3.6.4",
9
+ "version": "3.8.1",
10
10
  "hasInstallScript": true,
11
11
  "license": "Apache 2.0",
12
12
  "dependencies": {
@@ -627,9 +627,9 @@
627
627
  "dev": true
628
628
  },
629
629
  "node_modules/ip": {
630
- "version": "2.0.0",
631
- "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
632
- "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
630
+ "version": "2.0.1",
631
+ "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
632
+ "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==",
633
633
  "dev": true
634
634
  },
635
635
  "node_modules/is-fullwidth-code-point": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simsimd",
3
- "version": "3.8.0",
3
+ "version": "3.9.0",
4
4
  "description": "Fastest SIMD-Accelerated Vector Similarity Functions for x86 and Arm",
5
5
  "homepage": "https://github.com/ashvardanian/simsimd",
6
6
  "author": "Ash Vardanian",
@@ -10,18 +10,21 @@ pub(crate) fn cosine_similarity_cpu(a: &[f32], b: &[f32]) -> Option<f32> {
10
10
  return None;
11
11
  }
12
12
 
13
- let dot_product: f32 = a.iter().zip(b.iter()).map(|(a, b)| a * b).sum();
14
- let norm_a: f32 = a.iter().map(|a| a.powf(2.0)).sum();
15
- let norm_b: f32 = b.iter().map(|b| b.powf(2.0)).sum();
13
+ let (dot_product, norm_a, norm_b) = a
14
+ .iter()
15
+ .zip(b)
16
+ .map(|(a, b)| (a * b, a * a, b * b))
17
+ .fold((0.0, 0.0, 0.0), |acc, x| {
18
+ (acc.0 + x.0, acc.1 + x.1, acc.2 + x.2)
19
+ });
16
20
 
17
21
  Some(1.0 - (dot_product / (norm_a.sqrt() * norm_b.sqrt())))
18
22
  }
19
23
 
20
- pub(crate) fn squared_euclidean_cpu(a: &[f32], b: &[f32]) -> f32 {
21
- assert_eq!(a.len(), b.len());
24
+ pub(crate) fn squared_euclidean_cpu(a: &[f32], b: &[f32]) -> Option<f32> {
25
+ if a.len() != b.len() {
26
+ return None;
27
+ }
22
28
 
23
- a.iter()
24
- .zip(b.iter())
25
- .map(|(x, y)| ((*x) - (*y)) * ((*x) - (*y)))
26
- .fold(0.0, ::std::ops::Add::add)
29
+ Some(a.iter().zip(b).map(|(x, y)| (x - y).powi(2)).sum())
27
30
  }
@@ -7,62 +7,39 @@ simsimd_capability_t cached_capabilities(void) {
7
7
  return static_capabilities;
8
8
  }
9
9
 
10
- simsimd_f32_t cosine_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t d) {
11
- static simsimd_metric_punned_t metric = 0;
12
- if (metric == 0) {
13
- simsimd_capability_t used_capability;
14
- simsimd_find_metric_punned(simsimd_metric_cosine_k, simsimd_datatype_i8_k, cached_capabilities(),
15
- simsimd_cap_any_k, &metric, &used_capability);
10
+ #define SIMSIMD_METRIC_DECLARATION(name, type) \
11
+ simsimd_f32_t name##_##type(simsimd_##type##_t const* a, simsimd_##type##_t const* b, simsimd_size_t d) { \
12
+ static simsimd_metric_punned_t metric = 0; \
13
+ if (metric == 0) { \
14
+ simsimd_capability_t used_capability; \
15
+ simsimd_find_metric_punned(simsimd_metric_##name##_k, simsimd_datatype_##type##_k, cached_capabilities(), \
16
+ simsimd_cap_any_k, &metric, &used_capability); \
17
+ } \
18
+ return metric(a, b, d, d); \
16
19
  }
17
- return metric(a, b, d, d);
18
- }
19
20
 
20
- simsimd_f32_t cosine_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t d) {
21
- static simsimd_metric_punned_t metric = 0;
22
- if (metric == 0) {
23
- simsimd_capability_t used_capability;
24
- simsimd_find_metric_punned(simsimd_metric_cosine_k, simsimd_datatype_f32_k, cached_capabilities(),
25
- simsimd_cap_any_k, &metric, &used_capability);
26
- }
27
- return metric(a, b, d, d);
28
- }
21
+ // Spatial distances
22
+ SIMSIMD_METRIC_DECLARATION(cosine, i8)
23
+ SIMSIMD_METRIC_DECLARATION(cosine, f16)
24
+ SIMSIMD_METRIC_DECLARATION(cosine, f32)
25
+ SIMSIMD_METRIC_DECLARATION(cosine, f64)
26
+ SIMSIMD_METRIC_DECLARATION(inner, i8)
27
+ SIMSIMD_METRIC_DECLARATION(inner, f16)
28
+ SIMSIMD_METRIC_DECLARATION(inner, f32)
29
+ SIMSIMD_METRIC_DECLARATION(inner, f64)
30
+ SIMSIMD_METRIC_DECLARATION(sqeuclidean, i8)
31
+ SIMSIMD_METRIC_DECLARATION(sqeuclidean, f16)
32
+ SIMSIMD_METRIC_DECLARATION(sqeuclidean, f32)
33
+ SIMSIMD_METRIC_DECLARATION(sqeuclidean, f64)
29
34
 
30
- simsimd_f32_t inner_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t d) {
31
- static simsimd_metric_punned_t metric = 0;
32
- if (metric == 0) {
33
- simsimd_capability_t used_capability;
34
- simsimd_find_metric_punned(simsimd_metric_inner_k, simsimd_datatype_i8_k, cached_capabilities(),
35
- simsimd_cap_any_k, &metric, &used_capability);
36
- }
37
- return metric(a, b, d, d);
38
- }
35
+ // Binary distances
36
+ SIMSIMD_METRIC_DECLARATION(hamming, b8)
37
+ SIMSIMD_METRIC_DECLARATION(jaccard, b8)
39
38
 
40
- simsimd_f32_t inner_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t d) {
41
- static simsimd_metric_punned_t metric = 0;
42
- if (metric == 0) {
43
- simsimd_capability_t used_capability;
44
- simsimd_find_metric_punned(simsimd_metric_inner_k, simsimd_datatype_f32_k, cached_capabilities(),
45
- simsimd_cap_any_k, &metric, &used_capability);
46
- }
47
- return metric(a, b, d, d);
48
- }
49
-
50
- simsimd_f32_t sqeuclidean_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t d) {
51
- static simsimd_metric_punned_t metric = 0;
52
- if (metric == 0) {
53
- simsimd_capability_t used_capability;
54
- simsimd_find_metric_punned(simsimd_metric_sqeuclidean_k, simsimd_datatype_i8_k, cached_capabilities(),
55
- simsimd_cap_any_k, &metric, &used_capability);
56
- }
57
- return metric(a, b, d, d);
58
- }
59
-
60
- simsimd_f32_t sqeuclidean_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t d) {
61
- static simsimd_metric_punned_t metric = 0;
62
- if (metric == 0) {
63
- simsimd_capability_t used_capability;
64
- simsimd_find_metric_punned(simsimd_metric_sqeuclidean_k, simsimd_datatype_f32_k, cached_capabilities(),
65
- simsimd_cap_any_k, &metric, &used_capability);
66
- }
67
- return metric(a, b, d, d);
68
- }
39
+ // Probability distributions
40
+ SIMSIMD_METRIC_DECLARATION(kl, f16)
41
+ SIMSIMD_METRIC_DECLARATION(kl, f32)
42
+ SIMSIMD_METRIC_DECLARATION(kl, f64)
43
+ SIMSIMD_METRIC_DECLARATION(js, f16)
44
+ SIMSIMD_METRIC_DECLARATION(js, f32)
45
+ SIMSIMD_METRIC_DECLARATION(js, f64)