usearch 2.17.1 → 2.19.4

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 (47) hide show
  1. package/README.md +6 -2
  2. package/c/lib.cpp +5 -0
  3. package/c/module.modulemap +4 -0
  4. package/c/usearch.h +12 -4
  5. package/cpp/README.md +1 -1
  6. package/cpp/bench.cpp +1 -1
  7. package/cpp/test.cpp +110 -16
  8. package/include/usearch/index.hpp +48 -35
  9. package/include/usearch/index_dense.hpp +34 -19
  10. package/include/usearch/index_plugins.hpp +29 -7
  11. package/javascript/README.md +8 -6
  12. package/javascript/dist/cjs/usearch.d.ts +9 -4
  13. package/javascript/dist/cjs/usearch.js +23 -8
  14. package/javascript/dist/esm/usearch.d.ts +9 -4
  15. package/javascript/dist/esm/usearch.js +21 -7
  16. package/javascript/lib.cpp +137 -59
  17. package/javascript/usearch.test.js +359 -192
  18. package/javascript/usearch.ts +35 -11
  19. package/package.json +1 -1
  20. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  21. package/prebuilds/linux-arm64/usearch.node +0 -0
  22. package/prebuilds/linux-x64/usearch.node +0 -0
  23. package/simsimd/CONTRIBUTING.md +23 -8
  24. package/simsimd/Cargo.lock +1 -1
  25. package/simsimd/Cargo.toml +1 -1
  26. package/simsimd/README.md +49 -9
  27. package/simsimd/build.rs +7 -3
  28. package/simsimd/c/lib.c +6 -1
  29. package/simsimd/include/simsimd/binary.h +3 -3
  30. package/simsimd/include/simsimd/dot.h +2 -2
  31. package/simsimd/include/simsimd/elementwise.h +26 -17
  32. package/simsimd/include/simsimd/simsimd.h +125 -49
  33. package/simsimd/include/simsimd/sparse.h +30 -29
  34. package/simsimd/include/simsimd/spatial.h +4 -3
  35. package/simsimd/include/simsimd/types.h +19 -8
  36. package/simsimd/javascript/lib.c +0 -1
  37. package/simsimd/package-lock.json +411 -362
  38. package/simsimd/package.json +9 -9
  39. package/simsimd/rust/lib.rs +63 -29
  40. package/simsimd/scripts/bench.cxx +13 -0
  41. package/simsimd/scripts/bench_cosine.rs +8 -0
  42. package/simsimd/scripts/bench_native.rs +4 -0
  43. package/simsimd/scripts/bench_sqeuclidean.rs +8 -0
  44. package/simsimd/scripts/test.c +22 -0
  45. package/simsimd/scripts/test.py +80 -4
  46. package/simsimd/swift/SimSIMD.swift +17 -0
  47. package/simsimd/swift/Test.swift +36 -0
@@ -3,7 +3,6 @@
3
3
  * @brief SIMD-accelerated Similarity Measures and Distance Functions.
4
4
  * @author Ash Vardanian
5
5
  * @date March 14, 2023
6
- * @copyright Copyright (c) 2023
7
6
  *
8
7
  * References:
9
8
  * x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide
@@ -89,8 +88,8 @@
89
88
  #define SIMSIMD_H
90
89
 
91
90
  #define SIMSIMD_VERSION_MAJOR 6
92
- #define SIMSIMD_VERSION_MINOR 2
93
- #define SIMSIMD_VERSION_PATCH 3
91
+ #define SIMSIMD_VERSION_MINOR 4
92
+ #define SIMSIMD_VERSION_PATCH 10
94
93
 
95
94
  /**
96
95
  * @brief Removes compile-time dispatching, and replaces it with runtime dispatching.
@@ -98,7 +97,7 @@
98
97
  * that runs the program, rather than the most advanced backend supported by the CPU
99
98
  * used to compile the library or the downstream application.
100
99
  */
101
- #ifndef SIMSIMD_DYNAMIC_DISPATCH
100
+ #if !defined(SIMSIMD_DYNAMIC_DISPATCH)
102
101
  #define SIMSIMD_DYNAMIC_DISPATCH (0) // true or false
103
102
  #endif
104
103
 
@@ -113,7 +112,8 @@
113
112
 
114
113
  // On Apple Silicon, `mrs` is not allowed in user-space, so we need to use the `sysctl` API.
115
114
  #if defined(_SIMSIMD_DEFINED_APPLE)
116
- #include <sys/sysctl.h>
115
+ #include <fenv.h> // `fesetenv` - part of C 99 standard
116
+ #include <sys/sysctl.h> // `sysctlbyname`
117
117
  #endif
118
118
 
119
119
  #ifdef __cplusplus
@@ -255,9 +255,10 @@ typedef void (*simsimd_metric_dense_punned_t)(void const *a, void const *b, sims
255
255
  * @param[in] b_length Number of scalar words in the second input array.
256
256
  * @param[out] d Output value as a double-precision float, generally without decimals.
257
257
  */
258
- typedef void (*simsimd_metric_sparse_punned_t)(void const *a, void const *b, //
259
- simsimd_size_t a_length, simsimd_size_t b_length, //
260
- simsimd_distance_t *d);
258
+ typedef void (*simsimd_metric_sparse_punned_t)( //
259
+ void const *a, void const *b, //
260
+ simsimd_size_t a_length, simsimd_size_t b_length, //
261
+ simsimd_distance_t *d);
261
262
 
262
263
  /**
263
264
  * @brief Type-punned function pointer for curved vector spaces and similarity measures.
@@ -268,8 +269,9 @@ typedef void (*simsimd_metric_sparse_punned_t)(void const *a, void const *b,
268
269
  * @param[in] n Number of scalar words in the input arrays.
269
270
  * @param[out] d Output value as a double-precision float.
270
271
  */
271
- typedef void (*simsimd_metric_curved_punned_t)(void const *a, void const *b, void const *c, //
272
- simsimd_size_t n, simsimd_distance_t *d);
272
+ typedef void (*simsimd_metric_curved_punned_t)( //
273
+ void const *a, void const *b, void const *c, //
274
+ simsimd_size_t n, simsimd_distance_t *d);
273
275
 
274
276
  /**
275
277
  * @brief Type-punned function pointer for FMA operations on dense vector representations.
@@ -283,9 +285,9 @@ typedef void (*simsimd_metric_curved_punned_t)(void const *a, void const *b, voi
283
285
  * @param[in] beta Scaling factor for the third array.
284
286
  * @param[out] y Output value in the same precision as the input arrays.
285
287
  */
286
- typedef void (*simsimd_kernel_fma_punned_t)(void const *a, void const *b, void const *c, //
287
- simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta,
288
- void *y);
288
+ typedef void (*simsimd_kernel_fma_punned_t)( //
289
+ void const *a, void const *b, void const *c, //
290
+ simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta, void *y);
289
291
 
290
292
  /**
291
293
  * @brief Type-punned function pointer for Weighted Sum operations on dense vector representations.
@@ -298,9 +300,9 @@ typedef void (*simsimd_kernel_fma_punned_t)(void const *a, void const *b, void c
298
300
  * @param[in] beta Scaling factor for the second array.
299
301
  * @param[out] y Output value in the same precision as the input arrays.
300
302
  */
301
- typedef void (*simsimd_kernel_wsum_punned_t)(void const *a, void const *b, //
302
- simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta,
303
- void *y);
303
+ typedef void (*simsimd_kernel_wsum_punned_t)( //
304
+ void const *a, void const *b, //
305
+ simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta, void *y);
304
306
 
305
307
  /**
306
308
  * @brief Type-punned function pointer for a SimSIMD public interface.
@@ -319,6 +321,7 @@ SIMSIMD_DYNAMIC void simsimd_find_kernel_punned( //
319
321
  simsimd_capability_t allowed, //
320
322
  simsimd_kernel_punned_t *kernel_output, //
321
323
  simsimd_capability_t *capability_output);
324
+ SIMSIMD_DYNAMIC int simsimd_flush_denormals(void);
322
325
  #else
323
326
  SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void);
324
327
  SIMSIMD_PUBLIC void simsimd_find_kernel_punned( //
@@ -328,10 +331,32 @@ SIMSIMD_PUBLIC void simsimd_find_kernel_punned( //
328
331
  simsimd_capability_t allowed, //
329
332
  simsimd_kernel_punned_t *kernel_output, //
330
333
  simsimd_capability_t *capability_output);
334
+ SIMSIMD_PUBLIC int simsimd_flush_denormals(void);
331
335
  #endif
332
336
 
333
337
  #if _SIMSIMD_TARGET_X86
334
338
 
339
+ /**
340
+ * @brief Function to flush denormalized numbers to zero on x86 CPUs.
341
+ * @note This should be called on each thread before any SIMD operations to avoid performance penalties.
342
+ * @return 1 if the operation was successful, 0 otherwise.
343
+ */
344
+ SIMSIMD_PUBLIC int _simsimd_flush_denormals_x86(void) {
345
+ #if defined(_MSC_VER)
346
+ unsigned int mxcsr = _mm_getcsr();
347
+ mxcsr |= 1 << 15; // bit 15 = Flush-To-Zero (FTZ)
348
+ mxcsr |= 1 << 6; // bit 6 = Denormals-Are-Zero (DAZ)
349
+ _mm_setcsr(mxcsr);
350
+ #else // GCC, Clang, ICC
351
+ unsigned int mxcsr;
352
+ __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
353
+ mxcsr |= 1 << 15; // bit 15 = Flush-To-Zero (FTZ)
354
+ mxcsr |= 1 << 6; // bit 6 = Denormals-Are-Zero (DAZ)
355
+ __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
356
+ #endif
357
+ return 1;
358
+ }
359
+
335
360
  /**
336
361
  * @brief Function to determine the SIMD capabilities of the current 64-bit x86 machine at @b runtime.
337
362
  * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
@@ -350,17 +375,19 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_x86(void) {
350
375
  __cpuidex(info1.array, 1, 0);
351
376
  __cpuidex(info7.array, 7, 0);
352
377
  __cpuidex(info7sub1.array, 7, 1);
353
- #else
354
- __asm__ __volatile__("cpuid"
355
- : "=a"(info1.named.eax), "=b"(info1.named.ebx), "=c"(info1.named.ecx), "=d"(info1.named.edx)
356
- : "a"(1), "c"(0));
357
- __asm__ __volatile__("cpuid"
358
- : "=a"(info7.named.eax), "=b"(info7.named.ebx), "=c"(info7.named.ecx), "=d"(info7.named.edx)
359
- : "a"(7), "c"(0));
360
- __asm__ __volatile__("cpuid"
361
- : "=a"(info7sub1.named.eax), "=b"(info7sub1.named.ebx), "=c"(info7sub1.named.ecx),
362
- "=d"(info7sub1.named.edx)
363
- : "a"(7), "c"(1));
378
+ #else // GCC, Clang, ICC
379
+ __asm__ __volatile__( //
380
+ "cpuid"
381
+ : "=a"(info1.named.eax), "=b"(info1.named.ebx), "=c"(info1.named.ecx), "=d"(info1.named.edx)
382
+ : "a"(1), "c"(0));
383
+ __asm__ __volatile__( //
384
+ "cpuid"
385
+ : "=a"(info7.named.eax), "=b"(info7.named.ebx), "=c"(info7.named.ecx), "=d"(info7.named.edx)
386
+ : "a"(7), "c"(0));
387
+ __asm__ __volatile__( //
388
+ "cpuid"
389
+ : "=a"(info7sub1.named.eax), "=b"(info7sub1.named.ebx), "=c"(info7sub1.named.ecx), "=d"(info7sub1.named.edx)
390
+ : "a"(7), "c"(1));
364
391
  #endif
365
392
 
366
393
  // Check for AVX2 (Function ID 7, EBX register)
@@ -400,7 +427,7 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_x86(void) {
400
427
  supports_avx512vbmi2 && supports_avx512vpopcntdq;
401
428
  unsigned supports_genoa = supports_avx512bf16;
402
429
  unsigned supports_sapphire = supports_avx512fp16;
403
- // We don't want to accidently enable AVX512VP2INTERSECT on Intel Tiger Lake CPUs
430
+ // We don't want to accidentally enable AVX512VP2INTERSECT on Intel Tiger Lake CPUs
404
431
  unsigned supports_turin = supports_avx512vp2intersect && supports_avx512bf16;
405
432
  unsigned supports_sierra = 0;
406
433
 
@@ -427,6 +454,33 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_x86(void) {
427
454
  #pragma GCC target("arch=armv8.5-a+sve")
428
455
  #pragma clang attribute push(__attribute__((target("arch=armv8.5-a+sve"))), apply_to = function)
429
456
 
457
+ /**
458
+ * @brief Function to flush denormalized numbers to zero on Arm CPUs.
459
+ * @note This should be called on each thread before any SIMD operations to avoid performance penalties.
460
+ * @note On Apple Silicon, `mrs` is not allowed in user-space, so we need to use the `sysctl` API.
461
+ * @return 1 if the operation was successful, 0 otherwise.
462
+ */
463
+ SIMSIMD_PUBLIC int _simsimd_flush_denormals_arm(void) {
464
+ #if defined(_SIMSIMD_DEFINED_APPLE)
465
+ // https://stackoverflow.com/a/19904907/2766161
466
+ // https://stackoverflow.com/a/78252076/2766161
467
+ int is_success = fesetenv(FE_DFL_DISABLE_DENORMS_ENV) == 0;
468
+ return is_success;
469
+ #elif defined(_SIMSIMD_DEFINED_LINUX)
470
+ // For Linux, we can toggle bits in the Floating-point Control Register (FPCR)
471
+ // https://developer.arm.com/documentation/ddi0601/2024-12/AArch64-Registers/FPCR--Floating-point-Control-Register
472
+ uint64_t fpcr;
473
+ __asm__ volatile("mrs %0, fpcr" : "=r"(fpcr));
474
+ fpcr |= (1 << 19); // bit 19 = FZ16 (Flush half-precision to zero)
475
+ fpcr |= (1 << 24); // bit 24 = FZ (Flush subnormals to zero)
476
+ fpcr |= (1 << 25); // bit 25 = DN (Force Default NaN instead of preserving payload)
477
+ __asm__ volatile("msr fpcr, %0" : : "r"(fpcr));
478
+ return 1;
479
+ #else
480
+ return 0;
481
+ #endif
482
+ }
483
+
430
484
  /**
431
485
  * @brief Function to determine the SIMD capabilities of the current 64-bit Arm machine at @b runtime.
432
486
  * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
@@ -474,7 +528,11 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_arm(void) {
474
528
  // AdvSIMD, bits [23:20] of ID_AA64PFR0_EL1 can be used to check for `fp16` support
475
529
  // - 0b0000: integers, single, double precision arithmetic
476
530
  // - 0b0001: includes support for half-precision floating-point arithmetic
477
- unsigned supports_fp16 = ((id_aa64pfr0_el1 >> 20) & 0xF) == 1;
531
+ // - 0b1111: NEON is not supported?!
532
+ // That's a really weird way to encode lack of NEON support, but it's important to
533
+ // check in case we are running on R-profile CPUs.
534
+ unsigned supports_fp16 = ((id_aa64pfr0_el1 >> 20) & 0xF) == 0x1;
535
+ unsigned supports_neon = ((id_aa64pfr0_el1 >> 20) & 0xF) != 0xF;
478
536
 
479
537
  // Now let's unpack the status flags from ID_AA64ZFR0_EL1
480
538
  // https://developer.arm.com/documentation/ddi0601/2024-03/AArch64-Registers/ID-AA64ZFR0-EL1--SVE-Feature-ID-Register-0?lang=en
@@ -490,7 +548,6 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_arm(void) {
490
548
  // This value must match the existing indicator obtained from ID_AA64PFR0_EL1:
491
549
  unsigned supports_sve2 = ((id_aa64zfr0_el1) & 0xF) >= 1;
492
550
  unsigned supports_sve2p1 = ((id_aa64zfr0_el1) & 0xF) >= 2;
493
- unsigned supports_neon = 1; // NEON is always supported
494
551
 
495
552
  return (simsimd_capability_t)( //
496
553
  (simsimd_cap_neon_k * (supports_neon)) | //
@@ -514,6 +571,23 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_arm(void) {
514
571
 
515
572
  #endif
516
573
 
574
+ /**
575
+ * @brief Function to flush @b denormalized numbers to zero to avoid performance penalties.
576
+ * @return 1 if the operation was successful, 0 otherwise.
577
+ *
578
+ * When facing denormalized values Fused-Multiply-Add (FMA) operations can be up to 30x slower,
579
+ * as measured on Intel Sapphire Rapids: https://github.com/ashvardanian/ParallelReductionsBenchmark
580
+ */
581
+ SIMSIMD_PUBLIC int _simsimd_flush_denormals(void) {
582
+ #if _SIMSIMD_TARGET_X86
583
+ return _simsimd_flush_denormals_x86();
584
+ #endif // _SIMSIMD_TARGET_X86
585
+ #if _SIMSIMD_TARGET_ARM
586
+ return _simsimd_flush_denormals_arm();
587
+ #endif // _SIMSIMD_TARGET_ARM
588
+ return 0;
589
+ }
590
+
517
591
  /**
518
592
  * @brief Function to determine the SIMD capabilities of the current 64-bit x86 machine at @b runtime.
519
593
  * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
@@ -533,7 +607,7 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_implementation(void) {
533
607
  #pragma clang diagnostic push
534
608
  #pragma clang diagnostic ignored "-Wcast-function-type"
535
609
 
536
- #ifdef __cplusplus //! option ‘-Wvolatile’ is valid for C++/ObjC++ but not for C
610
+ #ifdef __cplusplus //! option "-Wvolatile" is valid for C++/ObjC++ but not for C
537
611
  #pragma GCC diagnostic ignored "-Wvolatile"
538
612
  #pragma clang diagnostic ignored "-Wvolatile"
539
613
  #endif
@@ -1281,6 +1355,7 @@ SIMSIMD_PUBLIC simsimd_kernel_punned_t simsimd_metric_punned( //
1281
1355
  * @return 1 if the CPU supports the SIMD instruction set, 0 otherwise.
1282
1356
  */
1283
1357
  SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
1358
+ SIMSIMD_DYNAMIC int simsimd_flush_denormals(void);
1284
1359
  SIMSIMD_DYNAMIC int simsimd_uses_dynamic_dispatch(void);
1285
1360
  SIMSIMD_DYNAMIC int simsimd_uses_neon(void);
1286
1361
  SIMSIMD_DYNAMIC int simsimd_uses_neon_f16(void);
@@ -1473,6 +1548,7 @@ SIMSIMD_PUBLIC int simsimd_uses_sapphire(void) { return _SIMSIMD_TARGET_X86 && S
1473
1548
  SIMSIMD_PUBLIC int simsimd_uses_turin(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_TURIN; }
1474
1549
  SIMSIMD_PUBLIC int simsimd_uses_sierra(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SIERRA; }
1475
1550
  SIMSIMD_PUBLIC int simsimd_uses_dynamic_dispatch(void) { return 0; }
1551
+ SIMSIMD_PUBLIC int simsimd_flush_denormals(void) { return _simsimd_flush_denormals(); }
1476
1552
  SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void) { return _simsimd_capabilities_implementation(); }
1477
1553
  SIMSIMD_PUBLIC void simsimd_find_kernel_punned( //
1478
1554
  simsimd_metric_kind_t kind, //
@@ -1501,7 +1577,7 @@ SIMSIMD_PUBLIC void simsimd_find_kernel_punned( //
1501
1577
  */
1502
1578
  SIMSIMD_PUBLIC void simsimd_dot_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1503
1579
  simsimd_distance_t *d) {
1504
- #if SIMSIMD_TARGET_NEON_F16
1580
+ #if SIMSIMD_TARGET_NEON_I8
1505
1581
  simsimd_dot_i8_neon(a, b, n, d);
1506
1582
  #elif SIMSIMD_TARGET_ICE
1507
1583
  simsimd_dot_i8_ice(a, b, n, d);
@@ -1625,7 +1701,7 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c(simsimd_f16c_t const *a, simsimd_f16c_t co
1625
1701
  simsimd_distance_t *d) {
1626
1702
  #if SIMSIMD_TARGET_SVE
1627
1703
  simsimd_vdot_f16c_sve(a, b, n, d);
1628
- #elif SIMSIMD_TARGET_NEON
1704
+ #elif SIMSIMD_TARGET_NEON_F16
1629
1705
  simsimd_dot_f16c_neon(a, b, n, d);
1630
1706
  #elif SIMSIMD_TARGET_SAPPHIRE
1631
1707
  simsimd_dot_f16c_sapphire(a, b, n, d);
@@ -1685,7 +1761,7 @@ SIMSIMD_PUBLIC void simsimd_vdot_f64c(simsimd_f64c_t const *a, simsimd_f64c_t co
1685
1761
  */
1686
1762
  SIMSIMD_PUBLIC void simsimd_cos_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1687
1763
  simsimd_distance_t *d) {
1688
- #if SIMSIMD_TARGET_NEON
1764
+ #if SIMSIMD_TARGET_NEON_I8
1689
1765
  simsimd_cos_i8_neon(a, b, n, d);
1690
1766
  #elif SIMSIMD_TARGET_ICE
1691
1767
  simsimd_cos_i8_ice(a, b, n, d);
@@ -1697,7 +1773,7 @@ SIMSIMD_PUBLIC void simsimd_cos_i8(simsimd_i8_t const *a, simsimd_i8_t const *b,
1697
1773
  }
1698
1774
  SIMSIMD_PUBLIC void simsimd_cos_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1699
1775
  simsimd_distance_t *d) {
1700
- #if SIMSIMD_TARGET_NEON
1776
+ #if SIMSIMD_TARGET_NEON_I8
1701
1777
  simsimd_cos_u8_neon(a, b, n, d);
1702
1778
  #elif SIMSIMD_TARGET_ICE
1703
1779
  simsimd_cos_u8_ice(a, b, n, d);
@@ -1763,7 +1839,7 @@ SIMSIMD_PUBLIC void simsimd_cos_f64(simsimd_f64_t const *a, simsimd_f64_t const
1763
1839
  }
1764
1840
  SIMSIMD_PUBLIC void simsimd_l2sq_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1765
1841
  simsimd_distance_t *d) {
1766
- #if SIMSIMD_TARGET_NEON
1842
+ #if SIMSIMD_TARGET_NEON_I8
1767
1843
  simsimd_l2sq_i8_neon(a, b, n, d);
1768
1844
  #elif SIMSIMD_TARGET_ICE
1769
1845
  simsimd_l2sq_i8_ice(a, b, n, d);
@@ -1775,7 +1851,7 @@ SIMSIMD_PUBLIC void simsimd_l2sq_i8(simsimd_i8_t const *a, simsimd_i8_t const *b
1775
1851
  }
1776
1852
  SIMSIMD_PUBLIC void simsimd_l2sq_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1777
1853
  simsimd_distance_t *d) {
1778
- #if SIMSIMD_TARGET_NEON
1854
+ #if SIMSIMD_TARGET_NEON_I8
1779
1855
  simsimd_l2sq_u8_neon(a, b, n, d);
1780
1856
  #elif SIMSIMD_TARGET_ICE
1781
1857
  simsimd_l2sq_u8_ice(a, b, n, d);
@@ -1841,7 +1917,7 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f64(simsimd_f64_t const *a, simsimd_f64_t const
1841
1917
  }
1842
1918
  SIMSIMD_PUBLIC void simsimd_l2_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1843
1919
  simsimd_distance_t *d) {
1844
- #if SIMSIMD_TARGET_NEON
1920
+ #if SIMSIMD_TARGET_NEON_I8
1845
1921
  simsimd_l2_i8_neon(a, b, n, d);
1846
1922
  #elif SIMSIMD_TARGET_ICE
1847
1923
  simsimd_l2_i8_ice(a, b, n, d);
@@ -1853,7 +1929,7 @@ SIMSIMD_PUBLIC void simsimd_l2_i8(simsimd_i8_t const *a, simsimd_i8_t const *b,
1853
1929
  }
1854
1930
  SIMSIMD_PUBLIC void simsimd_l2_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1855
1931
  simsimd_distance_t *d) {
1856
- #if SIMSIMD_TARGET_NEON
1932
+ #if SIMSIMD_TARGET_NEON_I8
1857
1933
  simsimd_l2_u8_neon(a, b, n, d);
1858
1934
  #elif SIMSIMD_TARGET_ICE
1859
1935
  simsimd_l2_u8_ice(a, b, n, d);
@@ -1976,7 +2052,7 @@ SIMSIMD_PUBLIC void simsimd_jaccard_b8(simsimd_b8_t const *a, simsimd_b8_t const
1976
2052
  */
1977
2053
  SIMSIMD_PUBLIC void simsimd_kl_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1978
2054
  simsimd_distance_t *d) {
1979
- #if SIMSIMD_TARGET_NEON
2055
+ #if SIMSIMD_TARGET_NEON_F16
1980
2056
  simsimd_kl_f16_neon(a, b, n, d);
1981
2057
  #elif SIMSIMD_TARGET_HASWELL
1982
2058
  simsimd_kl_f16_haswell(a, b, n, d);
@@ -2004,7 +2080,7 @@ SIMSIMD_PUBLIC void simsimd_kl_f64(simsimd_f64_t const *a, simsimd_f64_t const *
2004
2080
  }
2005
2081
  SIMSIMD_PUBLIC void simsimd_js_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
2006
2082
  simsimd_distance_t *d) {
2007
- #if SIMSIMD_TARGET_NEON
2083
+ #if SIMSIMD_TARGET_NEON_F16
2008
2084
  simsimd_js_f16_neon(a, b, n, d);
2009
2085
  #elif SIMSIMD_TARGET_HASWELL
2010
2086
  simsimd_js_f16_haswell(a, b, n, d);
@@ -2135,7 +2211,7 @@ SIMSIMD_PUBLIC void simsimd_bilinear_f16(simsimd_f16_t const *a, simsimd_f16_t c
2135
2211
  simsimd_bilinear_f16_sapphire(a, b, c, n, d);
2136
2212
  #elif SIMSIMD_TARGET_HASWELL
2137
2213
  simsimd_bilinear_f16_haswell(a, b, c, n, d);
2138
- #elif SIMSIMD_TARGET_NEON
2214
+ #elif SIMSIMD_TARGET_NEON_F16
2139
2215
  simsimd_bilinear_f16_neon(a, b, c, n, d);
2140
2216
  #else
2141
2217
  simsimd_bilinear_f16_serial(a, b, c, n, d);
@@ -2147,7 +2223,7 @@ SIMSIMD_PUBLIC void simsimd_bilinear_bf16(simsimd_bf16_t const *a, simsimd_bf16_
2147
2223
  simsimd_bilinear_bf16_genoa(a, b, c, n, d);
2148
2224
  #elif SIMSIMD_TARGET_HASWELL
2149
2225
  simsimd_bilinear_bf16_haswell(a, b, c, n, d);
2150
- #elif SIMSIMD_TARGET_NEON
2226
+ #elif SIMSIMD_TARGET_NEON_BF16
2151
2227
  simsimd_bilinear_bf16_neon(a, b, c, n, d);
2152
2228
  #else
2153
2229
  simsimd_bilinear_bf16_serial(a, b, c, n, d);
@@ -2175,7 +2251,7 @@ SIMSIMD_PUBLIC void simsimd_bilinear_f16c(simsimd_f16c_t const *a, simsimd_f16c_
2175
2251
  simsimd_size_t n, simsimd_distance_t *d) {
2176
2252
  #if SIMSIMD_TARGET_SAPPHIRE
2177
2253
  simsimd_bilinear_f16c_sapphire(a, b, c, n, d);
2178
- #elif SIMSIMD_TARGET_NEON
2254
+ #elif SIMSIMD_TARGET_NEON_F16
2179
2255
  simsimd_bilinear_f16c_neon(a, b, c, n, d);
2180
2256
  #else
2181
2257
  simsimd_bilinear_f16c_serial(a, b, c, n, d);
@@ -2185,7 +2261,7 @@ SIMSIMD_PUBLIC void simsimd_bilinear_bf16c(simsimd_bf16c_t const *a, simsimd_bf1
2185
2261
  simsimd_size_t n, simsimd_distance_t *d) {
2186
2262
  #if SIMSIMD_TARGET_GENOA
2187
2263
  simsimd_bilinear_bf16c_genoa(a, b, c, n, d);
2188
- #elif SIMSIMD_TARGET_NEON
2264
+ #elif SIMSIMD_TARGET_NEON_BF16
2189
2265
  simsimd_bilinear_bf16c_neon(a, b, c, n, d);
2190
2266
  #else
2191
2267
  simsimd_bilinear_bf16c_serial(a, b, c, n, d);
@@ -2215,7 +2291,7 @@ SIMSIMD_PUBLIC void simsimd_mahalanobis_f16(simsimd_f16_t const *a, simsimd_f16_
2215
2291
  simsimd_mahalanobis_f16_sapphire(a, b, c, n, d);
2216
2292
  #elif SIMSIMD_TARGET_HASWELL
2217
2293
  simsimd_mahalanobis_f16_haswell(a, b, c, n, d);
2218
- #elif SIMSIMD_TARGET_NEON
2294
+ #elif SIMSIMD_TARGET_NEON_F16
2219
2295
  simsimd_mahalanobis_f16_neon(a, b, c, n, d);
2220
2296
  #else
2221
2297
  simsimd_mahalanobis_f16_serial(a, b, c, n, d);
@@ -2227,7 +2303,7 @@ SIMSIMD_PUBLIC void simsimd_mahalanobis_bf16(simsimd_bf16_t const *a, simsimd_bf
2227
2303
  simsimd_mahalanobis_bf16_genoa(a, b, c, n, d);
2228
2304
  #elif SIMSIMD_TARGET_HASWELL
2229
2305
  simsimd_mahalanobis_bf16_haswell(a, b, c, n, d);
2230
- #elif SIMSIMD_TARGET_NEON
2306
+ #elif SIMSIMD_TARGET_NEON_BF16
2231
2307
  simsimd_mahalanobis_bf16_neon(a, b, c, n, d);
2232
2308
  #else
2233
2309
  simsimd_mahalanobis_bf16_serial(a, b, c, n, d);
@@ -2274,7 +2350,7 @@ SIMSIMD_PUBLIC void simsimd_wsum_bf16(simsimd_bf16_t const *a, simsimd_bf16_t co
2274
2350
  simsimd_wsum_bf16_skylake(a, b, n, alpha, beta, r);
2275
2351
  #elif SIMSIMD_TARGET_HASWELL
2276
2352
  simsimd_wsum_bf16_haswell(a, b, n, alpha, beta, r);
2277
- #elif SIMSIMD_TARGET_NEON
2353
+ #elif SIMSIMD_TARGET_NEON_BF16
2278
2354
  simsimd_wsum_bf16_neon(a, b, n, alpha, beta, r);
2279
2355
  #else
2280
2356
  simsimd_wsum_bf16_serial(a, b, n, alpha, beta, r);
@@ -2353,7 +2429,7 @@ SIMSIMD_PUBLIC void simsimd_fma_bf16(simsimd_bf16_t const *a, simsimd_bf16_t con
2353
2429
  simsimd_fma_bf16_skylake(a, b, c, n, alpha, beta, r);
2354
2430
  #elif SIMSIMD_TARGET_HASWELL
2355
2431
  simsimd_fma_bf16_haswell(a, b, c, n, alpha, beta, r);
2356
- #elif SIMSIMD_TARGET_NEON
2432
+ #elif SIMSIMD_TARGET_NEON_BF16
2357
2433
  simsimd_fma_bf16_neon(a, b, c, n, alpha, beta, r);
2358
2434
  #else
2359
2435
  simsimd_fma_bf16_serial(a, b, c, n, alpha, beta, r);
@@ -187,8 +187,8 @@ SIMSIMD_MAKE_INTERSECT_LINEAR(accurate, u32, size) // simsimd_intersect_u32_accu
187
187
  simsimd_##input_type##_t ai = a[i]; \
188
188
  simsimd_##input_type##_t bj = b[j]; \
189
189
  int matches = ai == bj; \
190
- simsimd_##counter_type##_t awi = load_and_convert(a_weights + i); \
191
- simsimd_##counter_type##_t bwi = load_and_convert(b_weights + i); \
190
+ simsimd_##accumulator_type##_t awi = load_and_convert(a_weights + i); \
191
+ simsimd_##accumulator_type##_t bwi = load_and_convert(b_weights + i); \
192
192
  weights_product += matches * awi * bwi; \
193
193
  intersection_size += matches; \
194
194
  i += ai < bj; \
@@ -435,6 +435,13 @@ SIMSIMD_PUBLIC void simsimd_intersect_u16_ice( //
435
435
  }
436
436
  b_min = b_vec.u16[0];
437
437
 
438
+ __m512i a_last_broadcasted = _mm512_set1_epi16(*(short const *)&a_max);
439
+ __m512i b_last_broadcasted = _mm512_set1_epi16(*(short const *)&b_max);
440
+ __mmask32 a_step_mask = _mm512_cmple_epu16_mask(a_vec.zmm, b_last_broadcasted);
441
+ __mmask32 b_step_mask = _mm512_cmple_epu16_mask(b_vec.zmm, a_last_broadcasted);
442
+ a += 32 - _lzcnt_u32((simsimd_u32_t)a_step_mask);
443
+ b += 32 - _lzcnt_u32((simsimd_u32_t)b_step_mask);
444
+
438
445
  // Now we are likely to have some overlap, so we can intersect the registers
439
446
  __mmask32 a_matches = _simsimd_intersect_u16x32_ice(a_vec.zmm, b_vec.zmm);
440
447
 
@@ -442,13 +449,6 @@ SIMSIMD_PUBLIC void simsimd_intersect_u16_ice( //
442
449
  // but we don't need it here:
443
450
  // _mm512_mask_compressstoreu_epi16(c, a_matches, a_vec);
444
451
  c += _mm_popcnt_u32(a_matches); // MSVC has no `_popcnt32`
445
-
446
- __m512i a_last_broadcasted = _mm512_set1_epi16(*(short const *)&a_max);
447
- __m512i b_last_broadcasted = _mm512_set1_epi16(*(short const *)&b_max);
448
- __mmask32 a_step_mask = _mm512_cmple_epu16_mask(a_vec.zmm, b_last_broadcasted);
449
- __mmask32 b_step_mask = _mm512_cmple_epu16_mask(b_vec.zmm, a_last_broadcasted);
450
- a += 32 - _lzcnt_u32((simsimd_u32_t)a_step_mask);
451
- b += 32 - _lzcnt_u32((simsimd_u32_t)b_step_mask);
452
452
  }
453
453
 
454
454
  simsimd_intersect_u16_serial(a, b, a_end - a, b_end - b, results);
@@ -500,6 +500,13 @@ SIMSIMD_PUBLIC void simsimd_intersect_u32_ice( //
500
500
  }
501
501
  b_min = b_vec.u32[0];
502
502
 
503
+ __m512i a_last_broadcasted = _mm512_set1_epi32(*(int const *)&a_max);
504
+ __m512i b_last_broadcasted = _mm512_set1_epi32(*(int const *)&b_max);
505
+ __mmask16 a_step_mask = _mm512_cmple_epu32_mask(a_vec.zmm, b_last_broadcasted);
506
+ __mmask16 b_step_mask = _mm512_cmple_epu32_mask(b_vec.zmm, a_last_broadcasted);
507
+ a += 32 - _lzcnt_u32((simsimd_u32_t)a_step_mask);
508
+ b += 32 - _lzcnt_u32((simsimd_u32_t)b_step_mask);
509
+
503
510
  // Now we are likely to have some overlap, so we can intersect the registers
504
511
  __mmask16 a_matches = _simsimd_intersect_u32x16_ice(a_vec.zmm, b_vec.zmm);
505
512
 
@@ -507,13 +514,6 @@ SIMSIMD_PUBLIC void simsimd_intersect_u32_ice( //
507
514
  // but we don't need it here:
508
515
  // _mm512_mask_compressstoreu_epi32(c, a_matches, a_vec);
509
516
  c += _mm_popcnt_u32(a_matches); // MSVC has no `_popcnt32`
510
-
511
- __m512i a_last_broadcasted = _mm512_set1_epi32(*(int const *)&a_max);
512
- __m512i b_last_broadcasted = _mm512_set1_epi32(*(int const *)&b_max);
513
- __mmask16 a_step_mask = _mm512_cmple_epu32_mask(a_vec.zmm, b_last_broadcasted);
514
- __mmask16 b_step_mask = _mm512_cmple_epu32_mask(b_vec.zmm, a_last_broadcasted);
515
- a += 32 - _lzcnt_u32((simsimd_u32_t)a_step_mask);
516
- b += 32 - _lzcnt_u32((simsimd_u32_t)b_step_mask);
517
517
  }
518
518
 
519
519
  simsimd_intersect_u32_serial(a, b, a_end - a, b_end - b, results);
@@ -527,10 +527,10 @@ SIMSIMD_PUBLIC void simsimd_intersect_u32_ice( //
527
527
  #if SIMSIMD_TARGET_TURIN
528
528
  #pragma GCC push_options
529
529
  #pragma GCC target("avx2", "avx512f", "avx512vl", "bmi2", "lzcnt", "popcnt", "avx512bw", "avx512vbmi2", "avx512bf16", \
530
- "avx512vnni", "avx512vp2intersect")
531
- #pragma clang attribute push( \
532
- __attribute__((target( \
533
- "avx2,avx512f,avx512vl,bmi2,lzcnt,popcnt,avx512bw,avx512vbmi2,avx512bf16,avx512vnni,avx512vp2intersect"))), \
530
+ "avx512vnni", "avx512vp2intersect", "avx512dq")
531
+ #pragma clang attribute push( \
532
+ __attribute__((target( \
533
+ "avx2,avx512f,avx512vl,bmi2,lzcnt,popcnt,avx512bw,avx512vbmi2,avx512bf16,avx512vnni,avx512vp2intersect,avx512dq"))), \
534
534
  apply_to = function)
535
535
 
536
536
  SIMSIMD_PUBLIC void simsimd_intersect_u16_turin( //
@@ -678,7 +678,7 @@ SIMSIMD_PUBLIC void simsimd_spdot_weights_u16_turin( //
678
678
 
679
679
  // The baseline implementation for very small arrays (2 registers or less) can be quite simple:
680
680
  if (a_length < 64 && b_length < 64) {
681
- simsimd_intersect_u16_serial(a, b, a_length, b_length, results);
681
+ simsimd_spdot_weights_u16_serial(a, b, a_weights, b_weights, a_length, b_length, results);
682
682
  return;
683
683
  }
684
684
 
@@ -751,9 +751,9 @@ SIMSIMD_PUBLIC void simsimd_spdot_weights_u16_turin( //
751
751
  a += a_step, a_weights += a_step;
752
752
  b += b_step, b_weights += b_step;
753
753
  }
754
-
755
- simsimd_intersect_u16_serial(a, b, a_end - a, b_end - b, results);
756
- *results += intersection_size;
754
+ simsimd_spdot_weights_u16_serial(a, b, a_weights, b_weights, a_end - a, b_end - b, results);
755
+ results[0] += intersection_size;
756
+ results[1] += _mm512_reduce_add_ps(_mm512_insertf32x8(_mm512_setzero_ps(), product_vec.ymmps, 0));
757
757
  }
758
758
 
759
759
  SIMSIMD_PUBLIC void simsimd_spdot_counts_u16_turin( //
@@ -764,7 +764,7 @@ SIMSIMD_PUBLIC void simsimd_spdot_counts_u16_turin( //
764
764
 
765
765
  // The baseline implementation for very small arrays (2 registers or less) can be quite simple:
766
766
  if (a_length < 64 && b_length < 64) {
767
- simsimd_intersect_u16_serial(a, b, a_length, b_length, results);
767
+ simsimd_spdot_counts_u16_serial(a, b, a_weights, b_weights, a_length, b_length, results);
768
768
  return;
769
769
  }
770
770
 
@@ -837,8 +837,9 @@ SIMSIMD_PUBLIC void simsimd_spdot_counts_u16_turin( //
837
837
  b += b_step, b_weights += b_step;
838
838
  }
839
839
 
840
- simsimd_intersect_u16_serial(a, b, a_end - a, b_end - b, results);
841
- *results += intersection_size;
840
+ simsimd_spdot_counts_u16_serial(a, b, a_weights, b_weights, a_end - a, b_end - b, results);
841
+ results[0] += intersection_size;
842
+ results[1] += _mm512_reduce_add_epi32(_mm512_inserti64x4(_mm512_setzero_si512(), product_vec.ymm, 0));
842
843
  }
843
844
 
844
845
  #pragma clang attribute pop
@@ -849,8 +850,8 @@ SIMSIMD_PUBLIC void simsimd_spdot_counts_u16_turin( //
849
850
  #if _SIMSIMD_TARGET_ARM
850
851
  #if SIMSIMD_TARGET_NEON
851
852
  #pragma GCC push_options
852
- #pragma GCC target("arch=armv8.2-a")
853
- #pragma clang attribute push(__attribute__((target("arch=armv8.2-a"))), apply_to = function)
853
+ #pragma GCC target("arch=armv8-a")
854
+ #pragma clang attribute push(__attribute__((target("arch=armv8-a"))), apply_to = function)
854
855
 
855
856
  /**
856
857
  * @brief Uses `vshrn` to produce a bitmask, similar to `movemask` in SSE.
@@ -263,8 +263,8 @@ SIMSIMD_MAKE_L2(accurate, bf16, f64, SIMSIMD_BF16_TO_F32) // simsimd_l2_bf16_a
263
263
  #if _SIMSIMD_TARGET_ARM
264
264
  #if SIMSIMD_TARGET_NEON
265
265
  #pragma GCC push_options
266
- #pragma GCC target("arch=armv8.2-a+simd")
267
- #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd"))), apply_to = function)
266
+ #pragma GCC target("arch=armv8-a+simd")
267
+ #pragma clang attribute push(__attribute__((target("arch=armv8-a+simd"))), apply_to = function)
268
268
 
269
269
  SIMSIMD_INTERNAL simsimd_f32_t _simsimd_sqrt_f32_neon(simsimd_f32_t x) {
270
270
  return vget_lane_f32(vsqrt_f32(vdup_n_f32(x)), 0);
@@ -970,7 +970,7 @@ SIMSIMD_PUBLIC void simsimd_l2sq_bf16_sve(simsimd_bf16_t const *a_enum, simsimd_
970
970
  svfloat32_t a_minus_b_low_vec = svsub_f32_x(pg_low_vec, a_low_vec, b_low_vec);
971
971
  svfloat32_t a_minus_b_high_vec = svsub_f32_x(pg_high_vec, a_high_vec, b_high_vec);
972
972
  d2_low_vec = svmla_f32_x(pg_vec, d2_low_vec, a_minus_b_low_vec, a_minus_b_low_vec);
973
- d2_high_vec = svmla_f32_x(pg_vec, d2_high_vec, a_minus_b_low_vec, a_minus_b_low_vec);
973
+ d2_high_vec = svmla_f32_x(pg_vec, d2_high_vec, a_minus_b_high_vec, a_minus_b_high_vec);
974
974
  i += svcnth();
975
975
  } while (i < n);
976
976
  simsimd_f32_t d2 = svaddv_f32(svptrue_b32(), d2_low_vec) + svaddv_f32(svptrue_b32(), d2_high_vec);
@@ -2278,6 +2278,7 @@ simsimd_cos_i4x2_ice_cycle:
2278
2278
  int ab = _mm512_reduce_add_epi32(_mm512_add_epi32(ab_i32_low_vec, ab_i32_high_vec));
2279
2279
  unsigned short a2_u16[32], b2_u16[32];
2280
2280
  _mm512_storeu_si512(a2_u16, _mm512_add_epi16(a2_u16_low_vec, a2_u16_high_vec));
2281
+ _mm512_storeu_si512(b2_u16, _mm512_add_epi16(b2_u16_low_vec, b2_u16_high_vec));
2281
2282
  unsigned int a2 = 0, b2 = 0;
2282
2283
  for (int i = 0; i < 32; ++i) a2 += a2_u16[i], b2 += b2_u16[i];
2283
2284
  *result = _simsimd_cos_normalize_f32_haswell(ab, a2, b2);