usearch 2.16.8 → 2.16.9

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.
@@ -108,7 +108,7 @@ SIMSIMD_PUBLIC void simsimd_js_f16_sapphire(simsimd_f16_t const* a, simsimd_f16_
108
108
  d += ai * SIMSIMD_LOG((ai + epsilon) / (mi + epsilon)); \
109
109
  d += bi * SIMSIMD_LOG((bi + epsilon) / (mi + epsilon)); \
110
110
  } \
111
- *result = (simsimd_distance_t)d / 2; \
111
+ *result = SIMSIMD_SQRT(((simsimd_distance_t)d / 2)); \
112
112
  }
113
113
 
114
114
  SIMSIMD_MAKE_KL(serial, f64, f64, SIMSIMD_DEREFERENCE, SIMSIMD_F32_DIVISION_EPSILON) // simsimd_kl_f64_serial
@@ -219,12 +219,13 @@ simsimd_js_f32_neon_cycle:
219
219
  float32x4_t log_ratio_b_vec = _simsimd_log2_f32_neon(ratio_b_vec);
220
220
  float32x4_t prod_a_vec = vmulq_f32(a_vec, log_ratio_a_vec);
221
221
  float32x4_t prod_b_vec = vmulq_f32(b_vec, log_ratio_b_vec);
222
+
222
223
  sum_vec = vaddq_f32(sum_vec, vaddq_f32(prod_a_vec, prod_b_vec));
223
224
  if (n != 0) goto simsimd_js_f32_neon_cycle;
224
225
 
225
226
  simsimd_f32_t log2_normalizer = 0.693147181f;
226
- simsimd_f32_t sum = vaddvq_f32(sum_vec) * log2_normalizer;
227
- *result = sum / 2;
227
+ simsimd_f32_t sum = vaddvq_f32(sum_vec) * log2_normalizer / 2;
228
+ *result = _simsimd_sqrt_f32_neon(sum);
228
229
  }
229
230
 
230
231
  #pragma clang attribute pop
@@ -296,8 +297,8 @@ simsimd_js_f16_neon_cycle:
296
297
  if (n) goto simsimd_js_f16_neon_cycle;
297
298
 
298
299
  simsimd_f32_t log2_normalizer = 0.693147181f;
299
- simsimd_f32_t sum = vaddvq_f32(sum_vec) * log2_normalizer;
300
- *result = sum / 2;
300
+ simsimd_f32_t sum = vaddvq_f32(sum_vec) * log2_normalizer / 2;
301
+ *result = _simsimd_sqrt_f32_neon(sum);
301
302
  }
302
303
 
303
304
  #pragma clang attribute pop
@@ -403,8 +404,8 @@ simsimd_js_f16_haswell_cycle:
403
404
 
404
405
  simsimd_f32_t log2_normalizer = 0.693147181f;
405
406
  simsimd_f32_t sum = _simsimd_reduce_f32x8_haswell(sum_vec);
406
- sum *= log2_normalizer;
407
- *result = sum / 2;
407
+ sum *= log2_normalizer / 2;
408
+ *result = _simsimd_sqrt_f32_haswell(sum);
408
409
  }
409
410
 
410
411
  #pragma clang attribute pop
@@ -496,7 +497,9 @@ simsimd_js_f32_skylake_cycle:
496
497
  if (n) goto simsimd_js_f32_skylake_cycle;
497
498
 
498
499
  simsimd_f32_t log2_normalizer = 0.693147181f;
499
- *result = _mm512_reduce_add_ps(_mm512_add_ps(sum_a_vec, sum_b_vec)) * log2_normalizer / 2;
500
+ simsimd_f32_t sum = _mm512_reduce_add_ps(_mm512_add_ps(sum_a_vec, sum_b_vec));
501
+ sum *= log2_normalizer / 2;
502
+ *result = _simsimd_sqrt_f32_haswell(sum);
500
503
  }
501
504
 
502
505
  #pragma clang attribute pop
@@ -586,7 +589,9 @@ simsimd_js_f16_sapphire_cycle:
586
589
  if (n) goto simsimd_js_f16_sapphire_cycle;
587
590
 
588
591
  simsimd_f32_t log2_normalizer = 0.693147181f;
589
- *result = _mm512_reduce_add_ph(_mm512_add_ph(sum_a_vec, sum_b_vec)) * log2_normalizer / 2;
592
+ simsimd_f32_t sum = _mm512_reduce_add_ph(_mm512_add_ph(sum_a_vec, sum_b_vec));
593
+ sum *= log2_normalizer / 2;
594
+ *result = _simsimd_sqrt_f32_haswell(sum);
590
595
  }
591
596
 
592
597
  #pragma clang attribute pop
@@ -89,8 +89,8 @@
89
89
  #define SIMSIMD_H
90
90
 
91
91
  #define SIMSIMD_VERSION_MAJOR 6
92
- #define SIMSIMD_VERSION_MINOR 0
93
- #define SIMSIMD_VERSION_PATCH 5
92
+ #define SIMSIMD_VERSION_MINOR 2
93
+ #define SIMSIMD_VERSION_PATCH 3
94
94
 
95
95
  /**
96
96
  * @brief Removes compile-time dispatching, and replaces it with runtime dispatching.
@@ -434,7 +434,7 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_x86(void) {
434
434
  SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_arm(void) {
435
435
  #if defined(_SIMSIMD_DEFINED_APPLE)
436
436
  // On Apple Silicon, `mrs` is not allowed in user-space, so we need to use the `sysctl` API.
437
- uint32_t supports_neon = 0, supports_fp16 = 0, supports_bf16 = 0, supports_i8mm = 0;
437
+ unsigned supports_neon = 0, supports_fp16 = 0, supports_bf16 = 0, supports_i8mm = 0;
438
438
  size_t size = sizeof(supports_neon);
439
439
  if (sysctlbyname("hw.optional.neon", &supports_neon, &size, NULL, 0) != 0) supports_neon = 0;
440
440
  if (sysctlbyname("hw.optional.arm.FEAT_FP16", &supports_fp16, &size, NULL, 0) != 0) supports_fp16 = 0;
@@ -975,12 +975,14 @@ SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f64c(simsimd_capability_t v, s
975
975
  if (v & simsimd_cap_skylake_k) switch (k) {
976
976
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_skylake, *c = simsimd_cap_skylake_k; return;
977
977
  case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f64c_skylake, *c = simsimd_cap_skylake_k; return;
978
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f64c_skylake, *c = simsimd_cap_skylake_k; return;
978
979
  default: break;
979
980
  }
980
981
  #endif
981
982
  if (v & simsimd_cap_serial_k) switch (k) {
982
983
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_serial, *c = simsimd_cap_serial_k; return;
983
984
  case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f64c_serial, *c = simsimd_cap_serial_k; return;
985
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f64c_serial, *c = simsimd_cap_serial_k; return;
984
986
  default: break;
985
987
  }
986
988
  }
@@ -999,6 +1001,7 @@ SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f32c(simsimd_capability_t v, s
999
1001
  if (v & simsimd_cap_neon_k) switch (k) {
1000
1002
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_neon, *c = simsimd_cap_neon_k; return;
1001
1003
  case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_neon, *c = simsimd_cap_neon_k; return;
1004
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f32c_neon, *c = simsimd_cap_neon_k; return;
1002
1005
  default: break;
1003
1006
  }
1004
1007
  #endif
@@ -1006,6 +1009,7 @@ SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f32c(simsimd_capability_t v, s
1006
1009
  if (v & simsimd_cap_skylake_k) switch (k) {
1007
1010
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_skylake, *c = simsimd_cap_skylake_k; return;
1008
1011
  case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_skylake, *c = simsimd_cap_skylake_k; return;
1012
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f32c_skylake, *c = simsimd_cap_skylake_k; return;
1009
1013
  default: break;
1010
1014
  }
1011
1015
  #endif
@@ -1019,6 +1023,7 @@ SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f32c(simsimd_capability_t v, s
1019
1023
  if (v & simsimd_cap_serial_k) switch (k) {
1020
1024
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_serial, *c = simsimd_cap_serial_k; return;
1021
1025
  case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_serial, *c = simsimd_cap_serial_k; return;
1026
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f32c_serial, *c = simsimd_cap_serial_k; return;
1022
1027
  default: break;
1023
1028
  }
1024
1029
  }
@@ -1037,6 +1042,7 @@ SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f16c(simsimd_capability_t v, s
1037
1042
  if (v & simsimd_cap_neon_f16_k) switch (k) {
1038
1043
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_neon, *c = simsimd_cap_neon_f16_k; return;
1039
1044
  case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_neon, *c = simsimd_cap_neon_f16_k; return;
1045
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f16c_neon, *c = simsimd_cap_neon_bf16_k; return;
1040
1046
  default: break;
1041
1047
  }
1042
1048
  #endif
@@ -1044,6 +1050,7 @@ SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f16c(simsimd_capability_t v, s
1044
1050
  if (v & simsimd_cap_sapphire_k) switch (k) {
1045
1051
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
1046
1052
  case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
1053
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
1047
1054
  default: break;
1048
1055
  }
1049
1056
  #endif
@@ -1057,6 +1064,7 @@ SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f16c(simsimd_capability_t v, s
1057
1064
  if (v & simsimd_cap_serial_k) switch (k) {
1058
1065
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_serial, *c = simsimd_cap_serial_k; return;
1059
1066
  case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_serial, *c = simsimd_cap_serial_k; return;
1067
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f16c_serial, *c = simsimd_cap_serial_k; return;
1060
1068
  default: break;
1061
1069
  }
1062
1070
  }
@@ -1068,6 +1076,7 @@ SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_bf16c(simsimd_capability_t v,
1068
1076
  if (v & simsimd_cap_neon_bf16_k) switch (k) {
1069
1077
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_neon, *c = simsimd_cap_neon_bf16_k; return;
1070
1078
  case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_neon, *c = simsimd_cap_neon_bf16_k; return;
1079
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_bf16c_neon, *c = simsimd_cap_neon_bf16_k; return;
1071
1080
  default: break;
1072
1081
  }
1073
1082
  #endif
@@ -1075,12 +1084,14 @@ SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_bf16c(simsimd_capability_t v,
1075
1084
  if (v & simsimd_cap_genoa_k) switch (k) {
1076
1085
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_genoa, *c = simsimd_cap_genoa_k; return;
1077
1086
  case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_genoa, *c = simsimd_cap_genoa_k; return;
1087
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_bf16c_genoa, *c = simsimd_cap_genoa_k; return;
1078
1088
  default: break;
1079
1089
  }
1080
1090
  #endif
1081
1091
  if (v & simsimd_cap_serial_k) switch (k) {
1082
1092
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_serial, *c = simsimd_cap_serial_k; return;
1083
1093
  case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_serial, *c = simsimd_cap_serial_k; return;
1094
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_bf16c_serial, *c = simsimd_cap_serial_k; return;
1084
1095
  default: break;
1085
1096
  }
1086
1097
  }
@@ -1314,21 +1325,21 @@ SIMSIMD_DYNAMIC void simsimd_dot_f32(simsimd_f32_t const *a, simsimd_f32_t const
1314
1325
  simsimd_distance_t *d);
1315
1326
  SIMSIMD_DYNAMIC void simsimd_dot_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1316
1327
  simsimd_distance_t *d);
1317
- SIMSIMD_DYNAMIC void simsimd_dot_f16c(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1328
+ SIMSIMD_DYNAMIC void simsimd_dot_f16c(simsimd_f16c_t const *a, simsimd_f16c_t const *b, simsimd_size_t n,
1318
1329
  simsimd_distance_t *d);
1319
- SIMSIMD_DYNAMIC void simsimd_dot_bf16c(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1330
+ SIMSIMD_DYNAMIC void simsimd_dot_bf16c(simsimd_bf16c_t const *a, simsimd_bf16c_t const *b, simsimd_size_t n,
1320
1331
  simsimd_distance_t *d);
1321
- SIMSIMD_DYNAMIC void simsimd_dot_f32c(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1332
+ SIMSIMD_DYNAMIC void simsimd_dot_f32c(simsimd_f32c_t const *a, simsimd_f32c_t const *b, simsimd_size_t n,
1322
1333
  simsimd_distance_t *d);
1323
- SIMSIMD_DYNAMIC void simsimd_dot_f64c(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1334
+ SIMSIMD_DYNAMIC void simsimd_dot_f64c(simsimd_f64c_t const *a, simsimd_f64c_t const *b, simsimd_size_t n,
1324
1335
  simsimd_distance_t *d);
1325
- SIMSIMD_DYNAMIC void simsimd_vdot_f16c(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1336
+ SIMSIMD_DYNAMIC void simsimd_vdot_f16c(simsimd_f16c_t const *a, simsimd_f16c_t const *b, simsimd_size_t n,
1326
1337
  simsimd_distance_t *d);
1327
- SIMSIMD_DYNAMIC void simsimd_vdot_bf16c(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1338
+ SIMSIMD_DYNAMIC void simsimd_vdot_bf16c(simsimd_bf16c_t const *a, simsimd_bf16c_t const *b, simsimd_size_t n,
1328
1339
  simsimd_distance_t *d);
1329
- SIMSIMD_DYNAMIC void simsimd_vdot_f32c(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1340
+ SIMSIMD_DYNAMIC void simsimd_vdot_f32c(simsimd_f32c_t const *a, simsimd_f32c_t const *b, simsimd_size_t n,
1330
1341
  simsimd_distance_t *d);
1331
- SIMSIMD_DYNAMIC void simsimd_vdot_f64c(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1342
+ SIMSIMD_DYNAMIC void simsimd_vdot_f64c(simsimd_f64c_t const *a, simsimd_f64c_t const *b, simsimd_size_t n,
1332
1343
  simsimd_distance_t *d);
1333
1344
 
1334
1345
  /* Spatial distances
@@ -1562,7 +1573,7 @@ SIMSIMD_PUBLIC void simsimd_dot_f64(simsimd_f64_t const *a, simsimd_f64_t const
1562
1573
  simsimd_dot_f64_serial(a, b, n, d);
1563
1574
  #endif
1564
1575
  }
1565
- SIMSIMD_PUBLIC void simsimd_dot_f16c(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1576
+ SIMSIMD_PUBLIC void simsimd_dot_f16c(simsimd_f16c_t const *a, simsimd_f16c_t const *b, simsimd_size_t n,
1566
1577
  simsimd_distance_t *d) {
1567
1578
  #if SIMSIMD_TARGET_SVE_F16
1568
1579
  simsimd_dot_f16c_sve(a, b, n, d);
@@ -1576,7 +1587,7 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c(simsimd_f16_t const *a, simsimd_f16_t const
1576
1587
  simsimd_dot_f16c_serial(a, b, n, d);
1577
1588
  #endif
1578
1589
  }
1579
- SIMSIMD_PUBLIC void simsimd_dot_bf16c(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1590
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c(simsimd_bf16c_t const *a, simsimd_bf16c_t const *b, simsimd_size_t n,
1580
1591
  simsimd_distance_t *d) {
1581
1592
  #if SIMSIMD_TARGET_GENOA
1582
1593
  simsimd_dot_bf16c_genoa(a, b, n, d);
@@ -1586,7 +1597,7 @@ SIMSIMD_PUBLIC void simsimd_dot_bf16c(simsimd_bf16_t const *a, simsimd_bf16_t co
1586
1597
  simsimd_dot_bf16c_serial(a, b, n, d);
1587
1598
  #endif
1588
1599
  }
1589
- SIMSIMD_PUBLIC void simsimd_dot_f32c(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1600
+ SIMSIMD_PUBLIC void simsimd_dot_f32c(simsimd_f32c_t const *a, simsimd_f32c_t const *b, simsimd_size_t n,
1590
1601
  simsimd_distance_t *d) {
1591
1602
  #if SIMSIMD_TARGET_SVE
1592
1603
  simsimd_dot_f32c_sve(a, b, n, d);
@@ -1600,7 +1611,7 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c(simsimd_f32_t const *a, simsimd_f32_t const
1600
1611
  simsimd_dot_f32c_serial(a, b, n, d);
1601
1612
  #endif
1602
1613
  }
1603
- SIMSIMD_PUBLIC void simsimd_dot_f64c(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1614
+ SIMSIMD_PUBLIC void simsimd_dot_f64c(simsimd_f64c_t const *a, simsimd_f64c_t const *b, simsimd_size_t n,
1604
1615
  simsimd_distance_t *d) {
1605
1616
  #if SIMSIMD_TARGET_SVE
1606
1617
  simsimd_dot_f64c_sve(a, b, n, d);
@@ -1610,7 +1621,7 @@ SIMSIMD_PUBLIC void simsimd_dot_f64c(simsimd_f64_t const *a, simsimd_f64_t const
1610
1621
  simsimd_dot_f64c_serial(a, b, n, d);
1611
1622
  #endif
1612
1623
  }
1613
- SIMSIMD_PUBLIC void simsimd_vdot_f16c(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1624
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c(simsimd_f16c_t const *a, simsimd_f16c_t const *b, simsimd_size_t n,
1614
1625
  simsimd_distance_t *d) {
1615
1626
  #if SIMSIMD_TARGET_SVE
1616
1627
  simsimd_vdot_f16c_sve(a, b, n, d);
@@ -1624,7 +1635,7 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c(simsimd_f16_t const *a, simsimd_f16_t cons
1624
1635
  simsimd_vdot_f16c_serial(a, b, n, d);
1625
1636
  #endif
1626
1637
  }
1627
- SIMSIMD_PUBLIC void simsimd_vdot_bf16c(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1638
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c(simsimd_bf16c_t const *a, simsimd_bf16c_t const *b, simsimd_size_t n,
1628
1639
  simsimd_distance_t *d) {
1629
1640
  #if SIMSIMD_TARGET_GENOA
1630
1641
  simsimd_vdot_bf16c_genoa(a, b, n, d);
@@ -1634,7 +1645,7 @@ SIMSIMD_PUBLIC void simsimd_vdot_bf16c(simsimd_bf16_t const *a, simsimd_bf16_t c
1634
1645
  simsimd_vdot_bf16c_serial(a, b, n, d);
1635
1646
  #endif
1636
1647
  }
1637
- SIMSIMD_PUBLIC void simsimd_vdot_f32c(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1648
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c(simsimd_f32c_t const *a, simsimd_f32c_t const *b, simsimd_size_t n,
1638
1649
  simsimd_distance_t *d) {
1639
1650
  #if SIMSIMD_TARGET_SVE
1640
1651
  simsimd_vdot_f32c_sve(a, b, n, d);
@@ -1648,7 +1659,7 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c(simsimd_f32_t const *a, simsimd_f32_t cons
1648
1659
  simsimd_vdot_f32c_serial(a, b, n, d);
1649
1660
  #endif
1650
1661
  }
1651
- SIMSIMD_PUBLIC void simsimd_vdot_f64c(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1662
+ SIMSIMD_PUBLIC void simsimd_vdot_f64c(simsimd_f64c_t const *a, simsimd_f64c_t const *b, simsimd_size_t n,
1652
1663
  simsimd_distance_t *d) {
1653
1664
  #if SIMSIMD_TARGET_SVE
1654
1665
  simsimd_vdot_f64c_sve(a, b, n, d);
@@ -2102,12 +2113,11 @@ SIMSIMD_PUBLIC void simsimd_spdot_weights_u16(simsimd_u16_t const *a, simsimd_u1
2102
2113
  */
2103
2114
  SIMSIMD_PUBLIC void simsimd_bilinear_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_f64_t const *c,
2104
2115
  simsimd_size_t n, simsimd_distance_t *d) {
2105
-
2116
+ #if SIMSIMD_TARGET_SKYLAKE
2117
+ simsimd_bilinear_f64_skylake(a, b, c, n, d);
2118
+ #else
2106
2119
  simsimd_bilinear_f64_serial(a, b, c, n, d);
2107
- }
2108
- SIMSIMD_PUBLIC void simsimd_mahalanobis_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_f64_t const *c,
2109
- simsimd_size_t n, simsimd_distance_t *d) {
2110
- simsimd_mahalanobis_f64_serial(a, b, c, n, d);
2120
+ #endif
2111
2121
  }
2112
2122
  SIMSIMD_PUBLIC void simsimd_bilinear_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_f32_t const *c,
2113
2123
  simsimd_size_t n, simsimd_distance_t *d) {
@@ -2119,16 +2129,6 @@ SIMSIMD_PUBLIC void simsimd_bilinear_f32(simsimd_f32_t const *a, simsimd_f32_t c
2119
2129
  simsimd_bilinear_f32_serial(a, b, c, n, d);
2120
2130
  #endif
2121
2131
  }
2122
- SIMSIMD_PUBLIC void simsimd_mahalanobis_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_f32_t const *c,
2123
- simsimd_size_t n, simsimd_distance_t *d) {
2124
- #if SIMSIMD_TARGET_SKYLAKE
2125
- simsimd_mahalanobis_f32_skylake(a, b, c, n, d);
2126
- #elif SIMSIMD_TARGET_NEON
2127
- simsimd_mahalanobis_f32_neon(a, b, c, n, d);
2128
- #else
2129
- simsimd_mahalanobis_f32_serial(a, b, c, n, d);
2130
- #endif
2131
- }
2132
2132
  SIMSIMD_PUBLIC void simsimd_bilinear_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_f16_t const *c,
2133
2133
  simsimd_size_t n, simsimd_distance_t *d) {
2134
2134
  #if SIMSIMD_TARGET_SAPPHIRE
@@ -2141,18 +2141,6 @@ SIMSIMD_PUBLIC void simsimd_bilinear_f16(simsimd_f16_t const *a, simsimd_f16_t c
2141
2141
  simsimd_bilinear_f16_serial(a, b, c, n, d);
2142
2142
  #endif
2143
2143
  }
2144
- SIMSIMD_PUBLIC void simsimd_mahalanobis_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_f16_t const *c,
2145
- simsimd_size_t n, simsimd_distance_t *d) {
2146
- #if SIMSIMD_TARGET_SAPPHIRE
2147
- simsimd_mahalanobis_f16_sapphire(a, b, c, n, d);
2148
- #elif SIMSIMD_TARGET_HASWELL
2149
- simsimd_mahalanobis_f16_haswell(a, b, c, n, d);
2150
- #elif SIMSIMD_TARGET_NEON
2151
- simsimd_mahalanobis_f16_neon(a, b, c, n, d);
2152
- #else
2153
- simsimd_mahalanobis_f16_serial(a, b, c, n, d);
2154
- #endif
2155
- }
2156
2144
  SIMSIMD_PUBLIC void simsimd_bilinear_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_bf16_t const *c,
2157
2145
  simsimd_size_t n, simsimd_distance_t *d) {
2158
2146
  #if SIMSIMD_TARGET_GENOA
@@ -2165,6 +2153,74 @@ SIMSIMD_PUBLIC void simsimd_bilinear_bf16(simsimd_bf16_t const *a, simsimd_bf16_
2165
2153
  simsimd_bilinear_bf16_serial(a, b, c, n, d);
2166
2154
  #endif
2167
2155
  }
2156
+ SIMSIMD_PUBLIC void simsimd_bilinear_f64c(simsimd_f64c_t const *a, simsimd_f64c_t const *b, simsimd_f64c_t const *c,
2157
+ simsimd_size_t n, simsimd_distance_t *d) {
2158
+ #if SIMSIMD_TARGET_SKYLAKE
2159
+ simsimd_bilinear_f64c_skylake(a, b, c, n, d);
2160
+ #else
2161
+ simsimd_bilinear_f64c_serial(a, b, c, n, d);
2162
+ #endif
2163
+ }
2164
+ SIMSIMD_PUBLIC void simsimd_bilinear_f32c(simsimd_f32c_t const *a, simsimd_f32c_t const *b, simsimd_f32c_t const *c,
2165
+ simsimd_size_t n, simsimd_distance_t *d) {
2166
+ #if SIMSIMD_TARGET_SKYLAKE
2167
+ simsimd_bilinear_f32c_skylake(a, b, c, n, d);
2168
+ #elif SIMSIMD_TARGET_NEON
2169
+ simsimd_bilinear_f32c_neon(a, b, c, n, d);
2170
+ #else
2171
+ simsimd_bilinear_f32c_serial(a, b, c, n, d);
2172
+ #endif
2173
+ }
2174
+ SIMSIMD_PUBLIC void simsimd_bilinear_f16c(simsimd_f16c_t const *a, simsimd_f16c_t const *b, simsimd_f16c_t const *c,
2175
+ simsimd_size_t n, simsimd_distance_t *d) {
2176
+ #if SIMSIMD_TARGET_SAPPHIRE
2177
+ simsimd_bilinear_f16c_sapphire(a, b, c, n, d);
2178
+ #elif SIMSIMD_TARGET_NEON
2179
+ simsimd_bilinear_f16c_neon(a, b, c, n, d);
2180
+ #else
2181
+ simsimd_bilinear_f16c_serial(a, b, c, n, d);
2182
+ #endif
2183
+ }
2184
+ SIMSIMD_PUBLIC void simsimd_bilinear_bf16c(simsimd_bf16c_t const *a, simsimd_bf16c_t const *b, simsimd_bf16c_t const *c,
2185
+ simsimd_size_t n, simsimd_distance_t *d) {
2186
+ #if SIMSIMD_TARGET_GENOA
2187
+ simsimd_bilinear_bf16c_genoa(a, b, c, n, d);
2188
+ #elif SIMSIMD_TARGET_NEON
2189
+ simsimd_bilinear_bf16c_neon(a, b, c, n, d);
2190
+ #else
2191
+ simsimd_bilinear_bf16c_serial(a, b, c, n, d);
2192
+ #endif
2193
+ }
2194
+ SIMSIMD_PUBLIC void simsimd_mahalanobis_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_f64_t const *c,
2195
+ simsimd_size_t n, simsimd_distance_t *d) {
2196
+ #if SIMSIMD_TARGET_SKYLAKE
2197
+ simsimd_mahalanobis_f64_skylake(a, b, c, n, d);
2198
+ #else
2199
+ simsimd_mahalanobis_f64_serial(a, b, c, n, d);
2200
+ #endif
2201
+ }
2202
+ SIMSIMD_PUBLIC void simsimd_mahalanobis_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_f32_t const *c,
2203
+ simsimd_size_t n, simsimd_distance_t *d) {
2204
+ #if SIMSIMD_TARGET_SKYLAKE
2205
+ simsimd_mahalanobis_f32_skylake(a, b, c, n, d);
2206
+ #elif SIMSIMD_TARGET_NEON
2207
+ simsimd_mahalanobis_f32_neon(a, b, c, n, d);
2208
+ #else
2209
+ simsimd_mahalanobis_f32_serial(a, b, c, n, d);
2210
+ #endif
2211
+ }
2212
+ SIMSIMD_PUBLIC void simsimd_mahalanobis_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_f16_t const *c,
2213
+ simsimd_size_t n, simsimd_distance_t *d) {
2214
+ #if SIMSIMD_TARGET_SAPPHIRE
2215
+ simsimd_mahalanobis_f16_sapphire(a, b, c, n, d);
2216
+ #elif SIMSIMD_TARGET_HASWELL
2217
+ simsimd_mahalanobis_f16_haswell(a, b, c, n, d);
2218
+ #elif SIMSIMD_TARGET_NEON
2219
+ simsimd_mahalanobis_f16_neon(a, b, c, n, d);
2220
+ #else
2221
+ simsimd_mahalanobis_f16_serial(a, b, c, n, d);
2222
+ #endif
2223
+ }
2168
2224
  SIMSIMD_PUBLIC void simsimd_mahalanobis_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_bf16_t const *c,
2169
2225
  simsimd_size_t n, simsimd_distance_t *d) {
2170
2226
  #if SIMSIMD_TARGET_GENOA
@@ -139,6 +139,16 @@ SIMSIMD_PUBLIC void simsimd_cos_f64_haswell(simsimd_f64_t const* a, simsimd_f64_
139
139
  /* SIMD-powered backends for AVX512 CPUs of Skylake generation and newer, using 32-bit arithmetic over 512-bit words.
140
140
  * Skylake was launched in 2015, and discontinued in 2019. Skylake had support for F, CD, VL, DQ, and BW extensions,
141
141
  * as well as masked operations. This is enough to supersede auto-vectorization on `f32` and `f64` types.
142
+ *
143
+ * Sadly, we can't effectively interleave different kinds of arithmetic instructions to utilize more ports:
144
+ *
145
+ * > Like Intel server architectures since Skylake-X, SPR cores feature two 512-bit FMA units, and organize them in a similar fashion.
146
+ * > One 512-bit FMA unit is created by fusing two 256-bit ones on port 0 and port 1. The other is added to port 5, as a server-specific
147
+ * > core extension. The FMA units on port 0 and 1 are configured into 2×256-bit or 1×512-bit mode depending on whether 512-bit FMA
148
+ * > instructions are present in the scheduler. That means a mix of 256-bit and 512-bit FMA instructions will not achieve higher IPC
149
+ * > than executing 512-bit instructions alone.
150
+ *
151
+ * Source: https://chipsandcheese.com/p/a-peek-at-sapphire-rapids
142
152
  */
143
153
  SIMSIMD_PUBLIC void simsimd_l2_f32_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* d);
144
154
  SIMSIMD_PUBLIC void simsimd_l2sq_f32_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* d);
@@ -595,10 +605,10 @@ SIMSIMD_PUBLIC void simsimd_l2sq_i8_neon(simsimd_i8_t const *a, simsimd_i8_t con
595
605
  uint8x16_t d_vec = vreinterpretq_u8_s8(vabdq_s8(a_vec, b_vec));
596
606
  d2_vec = vdotq_u32(d2_vec, d_vec, d_vec);
597
607
  }
598
- uint32_t d2 = vaddvq_u32(d2_vec);
608
+ simsimd_u32_t d2 = vaddvq_u32(d2_vec);
599
609
  for (; i < n; ++i) {
600
- int32_t n = (int32_t)a[i] - b[i];
601
- d2 += (uint32_t)(n * n);
610
+ simsimd_i32_t n = (simsimd_i32_t)a[i] - b[i];
611
+ d2 += (simsimd_u32_t)(n * n);
602
612
  }
603
613
  *result = d2;
604
614
  }
@@ -693,9 +703,9 @@ SIMSIMD_PUBLIC void simsimd_cos_i8_neon(simsimd_i8_t const *a, simsimd_i8_t cons
693
703
  // products_high_vec = vmmlaq_s32(products_high_vec, v_vec, y_w_vecs.val[1]);
694
704
  // }
695
705
  // int32x4_t products_vec = vaddq_s32(products_high_vec, products_low_vec);
696
- // int32_t a2 = products_vec[0];
697
- // int32_t ab = products_vec[1];
698
- // int32_t b2 = products_vec[3];
706
+ // simsimd_i32_t a2 = products_vec[0];
707
+ // simsimd_i32_t ab = products_vec[1];
708
+ // simsimd_i32_t b2 = products_vec[3];
699
709
  //
700
710
  // That solution is elegant, but it requires the additional `+i8mm` extension and is currently slower,
701
711
  // at least on AWS Graviton 3.
@@ -709,13 +719,13 @@ SIMSIMD_PUBLIC void simsimd_cos_i8_neon(simsimd_i8_t const *a, simsimd_i8_t cons
709
719
  a2_vec = vdotq_s32(a2_vec, a_vec, a_vec);
710
720
  b2_vec = vdotq_s32(b2_vec, b_vec, b_vec);
711
721
  }
712
- int32_t ab = vaddvq_s32(ab_vec);
713
- int32_t a2 = vaddvq_s32(a2_vec);
714
- int32_t b2 = vaddvq_s32(b2_vec);
722
+ simsimd_i32_t ab = vaddvq_s32(ab_vec);
723
+ simsimd_i32_t a2 = vaddvq_s32(a2_vec);
724
+ simsimd_i32_t b2 = vaddvq_s32(b2_vec);
715
725
 
716
726
  // Take care of the tail:
717
727
  for (; i < n; ++i) {
718
- int32_t ai = a[i], bi = b[i];
728
+ simsimd_i32_t ai = a[i], bi = b[i];
719
729
  ab += ai * bi, a2 += ai * ai, b2 += bi * bi;
720
730
  }
721
731
 
@@ -737,10 +747,10 @@ SIMSIMD_PUBLIC void simsimd_l2sq_u8_neon(simsimd_u8_t const *a, simsimd_u8_t con
737
747
  uint8x16_t d_vec = vabdq_u8(a_vec, b_vec);
738
748
  d2_vec = vdotq_u32(d2_vec, d_vec, d_vec);
739
749
  }
740
- uint32_t d2 = vaddvq_u32(d2_vec);
750
+ simsimd_u32_t d2 = vaddvq_u32(d2_vec);
741
751
  for (; i < n; ++i) {
742
- int32_t n = (int32_t)a[i] - b[i];
743
- d2 += (uint32_t)(n * n);
752
+ simsimd_i32_t n = (simsimd_i32_t)a[i] - b[i];
753
+ d2 += (simsimd_u32_t)(n * n);
744
754
  }
745
755
  *result = d2;
746
756
  }
@@ -759,13 +769,13 @@ SIMSIMD_PUBLIC void simsimd_cos_u8_neon(simsimd_u8_t const *a, simsimd_u8_t cons
759
769
  a2_vec = vdotq_u32(a2_vec, a_vec, a_vec);
760
770
  b2_vec = vdotq_u32(b2_vec, b_vec, b_vec);
761
771
  }
762
- uint32_t ab = vaddvq_u32(ab_vec);
763
- uint32_t a2 = vaddvq_u32(a2_vec);
764
- uint32_t b2 = vaddvq_u32(b2_vec);
772
+ simsimd_u32_t ab = vaddvq_u32(ab_vec);
773
+ simsimd_u32_t a2 = vaddvq_u32(a2_vec);
774
+ simsimd_u32_t b2 = vaddvq_u32(b2_vec);
765
775
 
766
776
  // Take care of the tail:
767
777
  for (; i < n; ++i) {
768
- uint32_t ai = a[i], bi = b[i];
778
+ simsimd_u32_t ai = a[i], bi = b[i];
769
779
  ab += ai * bi, a2 += ai * ai, b2 += bi * bi;
770
780
  }
771
781
 
@@ -1050,7 +1060,7 @@ SIMSIMD_INTERNAL simsimd_distance_t _simsimd_cos_normalize_f32_haswell(simsimd_f
1050
1060
  // Load the squares into an __m128 register for single-precision floating-point operations
1051
1061
  __m128 squares = _mm_set_ps(a2, b2, a2, b2); // We replicate to make use of full register
1052
1062
 
1053
- // Compute the reciprocal square root of the squares using _mm_rsqrt_ps (single-precision)
1063
+ // Compute the reciprocal square root of the squares using `_mm_rsqrt_ps` (single-precision)
1054
1064
  __m128 rsqrts = _mm_rsqrt_ps(squares);
1055
1065
 
1056
1066
  // Perform one iteration of Newton-Raphson refinement to improve the precision of rsqrt:
@@ -458,6 +458,30 @@ typedef union {
458
458
  float f;
459
459
  } simsimd_f32i32_t;
460
460
 
461
+ /** @brief Convenience type addressing the real and imaginary parts of a half-precision complex number. */
462
+ typedef struct {
463
+ simsimd_f16_t real;
464
+ simsimd_f16_t imag;
465
+ } simsimd_f16c_t;
466
+
467
+ /** @brief Convenience type addressing the real and imaginary parts of a half-precision brain-float complex number. */
468
+ typedef struct {
469
+ simsimd_bf16_t real;
470
+ simsimd_bf16_t imag;
471
+ } simsimd_bf16c_t;
472
+
473
+ /** @brief Convenience type addressing the real and imaginary parts of a single-precision complex number. */
474
+ typedef struct {
475
+ simsimd_f32_t real;
476
+ simsimd_f32_t imag;
477
+ } simsimd_f32c_t;
478
+
479
+ /** @brief Convenience type addressing the real and imaginary parts of a double-precision complex number. */
480
+ typedef struct {
481
+ simsimd_f64_t real;
482
+ simsimd_f64_t imag;
483
+ } simsimd_f64c_t;
484
+
461
485
  /**
462
486
  * @brief Computes `1/sqrt(x)` using the trick from Quake 3,
463
487
  * replacing the magic numbers with the ones suggested by Jan Kadlec.
@@ -171,7 +171,7 @@ export const jaccard = (a: Uint8Array, b: Uint8Array): number => {
171
171
  };
172
172
 
173
173
  /**
174
- * @brief Computes the kullbackleibler similarity coefficient between two vectors.
174
+ * @brief Computes the Kullback-Leibler divergence between two probability distributions.
175
175
  * @param {Float64Array|Float32Array} a - The first vector.
176
176
  * @param {Float64Array|Float32Array} b - The second vector.
177
177
  * @returns {number} The Jaccard similarity coefficient between vectors a and b.
@@ -182,38 +182,49 @@ export const kullbackleibler = (a: Float64Array | Float32Array, b: Float64Array
182
182
  }
183
183
 
184
184
  let divergence = 0.0;
185
-
186
185
  for (let i = 0; i < a.length; i++) {
187
- if (a[i] > 0) {
188
- if (b[i] === 0) {
189
- throw new Error(
190
- "Division by zero encountered in KL divergence calculation"
191
- );
192
- }
193
- divergence += a[i] * Math.log(a[i] / b[i]);
186
+ if (a[i] < 0 || b[i] < 0) {
187
+ throw new Error("Negative values are not allowed in probability distributions");
188
+ }
189
+ if (b[i] === 0) {
190
+ throw new Error(
191
+ "Division by zero encountered in KL divergence calculation"
192
+ );
194
193
  }
194
+ divergence += a[i] * Math.log(a[i] / b[i]);
195
195
  }
196
196
 
197
197
  return divergence;
198
198
  };
199
199
 
200
200
  /**
201
- * @brief Computes the jensenshannon similarity coefficient between two vectors.
202
- * @param {Float64Array|Float32Array} a - The first vector.
203
- * @param {Float64Array|Float32Array} b - The second vector.
204
- * @returns {number} The Jaccard similarity coefficient between vectors a and b.
201
+ * @brief Computes the Jensen-Shannon distance between two probability distributions.
202
+ * @param {Float64Array|Float32Array} a - The first probability distribution.
203
+ * @param {Float64Array|Float32Array} b - The second probability distribution.
204
+ * @returns {number} The Jensen-Shannon distance between distributions a and b.
205
205
  */
206
206
  export const jensenshannon = (a: Float64Array | Float32Array, b: Float64Array | Float32Array): number => {
207
207
  if (a.length !== b.length) {
208
208
  throw new Error("Arrays must be of the same length");
209
209
  }
210
210
 
211
- const m = a.map((value, index) => (value + b[index]) / 2);
211
+ let divergence = 0;
212
+ for (let i = 0; i < a.length; i++) {
213
+ if (a[i] < 0 || b[i] < 0) {
214
+ throw new Error("Negative values are not allowed in probability distributions");
215
+ }
216
+ const m = (a[i] + b[i]) / 2;
217
+ if (m > 0) {
218
+ if (a[i] > 0) divergence += a[i] * Math.log(a[i] / m);
219
+ if (b[i] > 0) divergence += b[i] * Math.log(b[i] / m);
220
+ }
221
+ }
212
222
 
213
- const divergence = 0.5 * kullbackleibler(a, m) + 0.5 * kullbackleibler(b, m);
223
+ divergence /= 2;
214
224
  return Math.sqrt(divergence);
215
225
  };
216
226
 
227
+
217
228
  export default {
218
229
  sqeuclidean,
219
230
  euclidean,
@@ -33,7 +33,7 @@ export const sqeuclidean = (
33
33
  * @brief Computes the Euclidean distance between two vectors.
34
34
  * @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
35
35
  * @param {Float64Array|Float32Array|Int8Array|Uint8Array} b - The second vector.
36
- * @returns {number} The squared Euclidean distance between vectors a and b.
36
+ * @returns {number} The Euclidean distance between vectors a and b.
37
37
  */
38
38
  export const euclidean = (
39
39
  a: Float64Array | Float32Array | Int8Array | Uint8Array,