usearch 2.13.3 → 2.13.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.
package/README.md CHANGED
@@ -541,7 +541,7 @@ doi = {10.5281/zenodo.7949416},
541
541
  author = {Vardanian, Ash},
542
542
  title = {{USearch by Unum Cloud}},
543
543
  url = {https://github.com/unum-cloud/usearch},
544
- version = {2.13.3},
544
+ version = {2.13.4},
545
545
  year = {2023},
546
546
  month = oct,
547
547
  }
@@ -9,7 +9,7 @@
9
9
 
10
10
  #define USEARCH_VERSION_MAJOR 2
11
11
  #define USEARCH_VERSION_MINOR 13
12
- #define USEARCH_VERSION_PATCH 3
12
+ #define USEARCH_VERSION_PATCH 4
13
13
 
14
14
  // Inferring C++ version
15
15
  // https://stackoverflow.com/a/61552074
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "2.13.3",
3
+ "version": "2.13.4",
4
4
  "description": "Smaller & Faster Single-File Vector Search Engine from Unum",
5
5
  "author": "Ash Vardanian (https://ashvardanian.com/)",
6
6
  "license": "Apache 2.0",
Binary file
Binary file
@@ -73,7 +73,7 @@ SIMSIMD_PUBLIC void simsimd_jaccard_b8_serial(simsimd_b8_t const* a, simsimd_b8_
73
73
  simsimd_i32_t intersection = 0, union_ = 0;
74
74
  for (simsimd_size_t i = 0; i != n_words; ++i)
75
75
  intersection += simsimd_popcount_b8(a[i] & b[i]), union_ += simsimd_popcount_b8(a[i] | b[i]);
76
- *result = (union_ != 0) ? 1 - (simsimd_f32_t)intersection / (simsimd_f32_t)union_ : 0;
76
+ *result = (union_ != 0) ? 1 - (simsimd_f32_t)intersection / (simsimd_f32_t)union_ : 1;
77
77
  }
78
78
 
79
79
  #if SIMSIMD_TARGET_ARM
@@ -110,7 +110,7 @@ SIMSIMD_PUBLIC void simsimd_jaccard_b8_neon(simsimd_b8_t const* a, simsimd_b8_t
110
110
  // Handle the tail
111
111
  for (; i != n_words; ++i)
112
112
  intersection += simsimd_popcount_b8(a[i] & b[i]), union_ += simsimd_popcount_b8(a[i] | b[i]);
113
- *result = (union_ != 0) ? 1 - (simsimd_f32_t)intersection / (simsimd_f32_t)union_ : 0;
113
+ *result = (union_ != 0) ? 1 - (simsimd_f32_t)intersection / (simsimd_f32_t)union_ : 1;
114
114
  }
115
115
 
116
116
  #pragma clang attribute pop
@@ -148,7 +148,7 @@ SIMSIMD_PUBLIC void simsimd_jaccard_b8_sve(simsimd_b8_t const* a, simsimd_b8_t c
148
148
  union_ += svaddv_u8(svptrue_b8(), svcnt_u8_x(svptrue_b8(), svorr_u8_m(svptrue_b8(), a_vec, b_vec)));
149
149
  i += svcntb();
150
150
  } while (i < n_words);
151
- *result = (union_ != 0) ? 1 - (simsimd_f32_t)intersection / (simsimd_f32_t)union_ : 0;
151
+ *result = (union_ != 0) ? 1 - (simsimd_f32_t)intersection / (simsimd_f32_t)union_ : 1;
152
152
  }
153
153
 
154
154
  #pragma clang attribute pop
@@ -213,7 +213,7 @@ simsimd_jaccard_b8_ice_cycle:
213
213
 
214
214
  simsimd_size_t intersection = _mm512_reduce_add_epi64(intersection_vec),
215
215
  union_ = _mm512_reduce_add_epi64(union_vec);
216
- *result = (union_ != 0) ? 1 - (simsimd_f32_t)intersection / (simsimd_f32_t)union_ : 0;
216
+ *result = (union_ != 0) ? 1 - (simsimd_f32_t)intersection / (simsimd_f32_t)union_ : 1;
217
217
  }
218
218
 
219
219
  #pragma clang attribute pop
@@ -245,7 +245,7 @@ SIMSIMD_PUBLIC void simsimd_jaccard_b8_haswell(simsimd_b8_t const* a, simsimd_b8
245
245
  union_ += _mm_popcnt_u64(*(simsimd_u64_t const*)a | *(simsimd_u64_t const*)b);
246
246
  for (; n_words; --n_words, ++a, ++b)
247
247
  intersection += _mm_popcnt_u32(*a & *b), union_ += _mm_popcnt_u32(*a | *b);
248
- *result = (union_ != 0) ? 1 - (simsimd_f32_t)intersection / (simsimd_f32_t)union_ : 0;
248
+ *result = (union_ != 0) ? 1 - (simsimd_f32_t)intersection / (simsimd_f32_t)union_ : 1;
249
249
  }
250
250
 
251
251
  #pragma clang attribute pop
@@ -858,6 +858,22 @@ inline simsimd_f64_t _mm256_reduce_add_ps_dbl(__m256 vec) {
858
858
  return _mm_cvtsd_f64(sum128);
859
859
  }
860
860
 
861
+ SIMSIMD_PUBLIC void simsimd_dot_f32_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
862
+ simsimd_distance_t* results) {
863
+
864
+ __m256 ab_vec = _mm256_setzero_ps();
865
+ simsimd_size_t i = 0;
866
+ for (; i + 8 <= n; i += 8) {
867
+ __m256 a_vec = _mm256_loadu_ps(a + i);
868
+ __m256 b_vec = _mm256_loadu_ps(b + i);
869
+ ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
870
+ }
871
+ simsimd_f64_t ab = _mm256_reduce_add_ps_dbl(ab_vec);
872
+ for (; i < n; ++i)
873
+ ab += a[i] * b[i];
874
+ *results = ab;
875
+ }
876
+
861
877
  SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
862
878
  simsimd_distance_t* results) {
863
879
 
@@ -527,6 +527,16 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
527
527
  default: break;
528
528
  }
529
529
  #endif
530
+ #if SIMSIMD_TARGET_HASWELL
531
+ if (viable & simsimd_cap_haswell_k)
532
+ switch (kind) {
533
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_haswell, *c = simsimd_cap_haswell_k; return;
534
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_haswell, *c = simsimd_cap_haswell_k; return;
535
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_haswell, *c = simsimd_cap_haswell_k; return;
536
+ default: break;
537
+ }
538
+ #endif
539
+
530
540
  if (viable & simsimd_cap_serial_k)
531
541
  switch (kind) {
532
542
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_serial, *c = simsimd_cap_serial_k; return;
@@ -1120,6 +1130,8 @@ SIMSIMD_PUBLIC void simsimd_dot_f32(simsimd_f32_t const* a, simsimd_f32_t const*
1120
1130
  simsimd_dot_f32_neon(a, b, n, d);
1121
1131
  #elif SIMSIMD_TARGET_SKYLAKE
1122
1132
  simsimd_dot_f32_skylake(a, b, n, d);
1133
+ #elif SIMSIMD_TARGET_HASWELL
1134
+ simsimd_dot_f32_haswell(a, b, n, d);
1123
1135
  #else
1124
1136
  simsimd_dot_f32_serial(a, b, n, d);
1125
1137
  #endif
@@ -1292,6 +1304,8 @@ SIMSIMD_PUBLIC void simsimd_cos_f32(simsimd_f32_t const* a, simsimd_f32_t const*
1292
1304
  simsimd_cos_f32_neon(a, b, n, d);
1293
1305
  #elif SIMSIMD_TARGET_SKYLAKE
1294
1306
  simsimd_cos_f32_skylake(a, b, n, d);
1307
+ #elif SIMSIMD_TARGET_HASWELL
1308
+ simsimd_cos_f32_haswell(a, b, n, d);
1295
1309
  #else
1296
1310
  simsimd_cos_f32_serial(a, b, n, d);
1297
1311
  #endif
@@ -1338,6 +1352,8 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f32(simsimd_f32_t const* a, simsimd_f32_t const
1338
1352
  simsimd_l2sq_f32_neon(a, b, n, d);
1339
1353
  #elif SIMSIMD_TARGET_SKYLAKE
1340
1354
  simsimd_l2sq_f32_skylake(a, b, n, d);
1355
+ #elif SIMSIMD_TARGET_HASWELL
1356
+ simsimd_l2sq_f32_haswell(a, b, n, d);
1341
1357
  #else
1342
1358
  simsimd_l2sq_f32_serial(a, b, n, d);
1343
1359
  #endif
@@ -27,6 +27,8 @@
27
27
 
28
28
  #include "types.h"
29
29
 
30
+ #include "dot.h" // `_mm256_reduce_add_ps_dbl`
31
+
30
32
  #ifdef __cplusplus
31
33
  extern "C" {
32
34
  #endif
@@ -93,6 +95,8 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f16_haswell(simsimd_f16_t const* a, simsimd_f16
93
95
  SIMSIMD_PUBLIC void simsimd_cos_f16_haswell(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t*);
94
96
  SIMSIMD_PUBLIC void simsimd_l2sq_bf16_haswell(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t*);
95
97
  SIMSIMD_PUBLIC void simsimd_cos_bf16_haswell(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t*);
98
+ SIMSIMD_PUBLIC void simsimd_l2sq_f32_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t*);
99
+ SIMSIMD_PUBLIC void simsimd_cos_f32_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t*);
96
100
 
97
101
  /* SIMD-powered backends for AVX512 CPUs of Skylake generation and newer, using 32-bit arithmetic over 512-bit words.
98
102
  * Skylake was launched in 2015, and discontinued in 2019. Skylake had support for F, CD, VL, DQ, and BW extensions,
@@ -839,8 +843,7 @@ SIMSIMD_PUBLIC void simsimd_cos_f16_haswell(simsimd_f16_t const* a, simsimd_f16_
839
843
  __m128 b2_sqrt_recip = _mm_rsqrt_ss(_mm_set_ss((float)b2));
840
844
  __m128 result_vec = _mm_mul_ss(a2_sqrt_recip, b2_sqrt_recip); // Multiply the reciprocal square roots
841
845
  result_vec = _mm_mul_ss(result_vec, _mm_set_ss((float)ab)); // Multiply by ab
842
- result_vec = _mm_sub_ss(_mm_set_ss(1.0f), result_vec); // Subtract from 1
843
- *result = ab != 0 ? _mm_cvtss_f32(result_vec) : 1; // Extract the final result
846
+ *result = ab != 0 ? 1 - _mm_cvtss_f32(result_vec) : 1; // Extract the final result
844
847
  }
845
848
 
846
849
  SIMSIMD_PUBLIC void simsimd_l2sq_bf16_haswell(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
@@ -946,8 +949,7 @@ SIMSIMD_PUBLIC void simsimd_cos_bf16_haswell(simsimd_bf16_t const* a, simsimd_bf
946
949
  __m128 b2_sqrt_recip = _mm_rsqrt_ss(_mm_set_ss((float)b2));
947
950
  __m128 result_vec = _mm_mul_ss(a2_sqrt_recip, b2_sqrt_recip); // Multiply the reciprocal square roots
948
951
  result_vec = _mm_mul_ss(result_vec, _mm_set_ss((float)ab)); // Multiply by ab
949
- result_vec = _mm_sub_ss(_mm_set_ss(1.0f), result_vec); // Subtract from 1
950
- *result = ab != 0 ? _mm_cvtss_f32(result_vec) : 1; // Extract the final result
952
+ *result = ab != 0 ? 1 - _mm_cvtss_f32(result_vec) : 1; // Extract the final result
951
953
  }
952
954
 
953
955
  SIMSIMD_PUBLIC void simsimd_l2sq_i8_haswell(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
@@ -1058,7 +1060,60 @@ SIMSIMD_PUBLIC void simsimd_cos_i8_haswell(simsimd_i8_t const* a, simsimd_i8_t c
1058
1060
  // Compute cosine similarity: ab / sqrt(a2 * b2)
1059
1061
  __m128 denom = _mm_mul_ss(a2_sqrt_recip, b2_sqrt_recip); // Reciprocal of sqrt(a2 * b2)
1060
1062
  __m128 result_vec = _mm_mul_ss(_mm_set_ss((float)ab), denom); // ab * reciprocal of sqrt(a2 * b2)
1061
- *result = ab != 0 ? 1 - _mm_cvtss_f32(result_vec) : 0; // Extract the final result
1063
+ *result = ab != 0 ? 1 - _mm_cvtss_f32(result_vec) : 1; // Extract the final result
1064
+ }
1065
+
1066
+ SIMSIMD_PUBLIC void simsimd_l2sq_f32_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1067
+ simsimd_distance_t* result) {
1068
+
1069
+ __m256 d2_vec = _mm256_setzero_ps();
1070
+ simsimd_size_t i = 0;
1071
+ for (; i + 8 <= n; i += 8) {
1072
+ __m256 a_vec = _mm256_loadu_ps(a + i);
1073
+ __m256 b_vec = _mm256_loadu_ps(b + i);
1074
+ __m256 d_vec = _mm256_sub_ps(a_vec, b_vec);
1075
+ d2_vec = _mm256_fmadd_ps(d_vec, d_vec, d2_vec);
1076
+ }
1077
+
1078
+ simsimd_f64_t d2 = _mm256_reduce_add_ps_dbl(d2_vec);
1079
+ for (; i < n; ++i) {
1080
+ float d = a[i] - b[i];
1081
+ d2 += d * d;
1082
+ }
1083
+
1084
+ *result = d2;
1085
+ }
1086
+
1087
+ SIMSIMD_PUBLIC void simsimd_cos_f32_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1088
+ simsimd_distance_t* result) {
1089
+
1090
+ __m256 ab_vec = _mm256_setzero_ps();
1091
+ __m256 a2_vec = _mm256_setzero_ps();
1092
+ __m256 b2_vec = _mm256_setzero_ps();
1093
+ simsimd_size_t i = 0;
1094
+ for (; i + 8 <= n; i += 8) {
1095
+ __m256 a_vec = _mm256_loadu_ps(a + i);
1096
+ __m256 b_vec = _mm256_loadu_ps(b + i);
1097
+ ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
1098
+ a2_vec = _mm256_fmadd_ps(a_vec, a_vec, a2_vec);
1099
+ b2_vec = _mm256_fmadd_ps(b_vec, b_vec, b2_vec);
1100
+ }
1101
+ simsimd_f64_t ab = _mm256_reduce_add_ps_dbl(ab_vec);
1102
+ simsimd_f64_t a2 = _mm256_reduce_add_ps_dbl(a2_vec);
1103
+ simsimd_f64_t b2 = _mm256_reduce_add_ps_dbl(b2_vec);
1104
+ for (; i < n; ++i) {
1105
+ float ai = a[i], bi = b[i];
1106
+ ab += ai * bi, a2 += ai * ai, b2 += bi * bi;
1107
+ }
1108
+
1109
+ // Compute the reciprocal of the square roots
1110
+ __m128 a2_sqrt_recip = _mm_rsqrt_ss(_mm_set_ss((float)a2));
1111
+ __m128 b2_sqrt_recip = _mm_rsqrt_ss(_mm_set_ss((float)b2));
1112
+
1113
+ // Compute cosine similarity: ab / sqrt(a2 * b2)
1114
+ __m128 denom = _mm_mul_ss(a2_sqrt_recip, b2_sqrt_recip); // Reciprocal of sqrt(a2 * b2)
1115
+ __m128 result_vec = _mm_mul_ss(_mm_set_ss((float)ab), denom); // ab * reciprocal of sqrt(a2 * b2)
1116
+ *result = ab != 0 ? 1 - _mm_cvtss_f32(result_vec) : 1; // Extract the final result
1062
1117
  }
1063
1118
 
1064
1119
  #pragma clang attribute pop
@@ -1282,7 +1337,7 @@ simsimd_cos_bf16_genoa_cycle:
1282
1337
  __m128 rsqrts = _mm_rsqrt14_ps(_mm_set_ps(0.f, 0.f, a2 + 1.e-9f, b2 + 1.e-9f));
1283
1338
  simsimd_f32_t rsqrt_a2 = _mm_cvtss_f32(rsqrts);
1284
1339
  simsimd_f32_t rsqrt_b2 = _mm_cvtss_f32(_mm_shuffle_ps(rsqrts, rsqrts, _MM_SHUFFLE(0, 0, 0, 1)));
1285
- *result = ab != 0 ? 1 - ab * rsqrt_a2 * rsqrt_b2 : 0;
1340
+ *result = ab != 0 ? 1 - ab * rsqrt_a2 * rsqrt_b2 : 1;
1286
1341
  }
1287
1342
 
1288
1343
  #pragma clang attribute pop
@@ -1350,7 +1405,7 @@ simsimd_cos_f16_sapphire_cycle:
1350
1405
  __m128 rsqrts = _mm_rsqrt14_ps(_mm_set_ps(0.f, 0.f, a2 + 1.e-9f, b2 + 1.e-9f));
1351
1406
  simsimd_f32_t rsqrt_a2 = _mm_cvtss_f32(rsqrts);
1352
1407
  simsimd_f32_t rsqrt_b2 = _mm_cvtss_f32(_mm_shuffle_ps(rsqrts, rsqrts, _MM_SHUFFLE(0, 0, 0, 1)));
1353
- *result = ab != 0 ? 1 - ab * rsqrt_a2 * rsqrt_b2 : 0;
1408
+ *result = ab != 0 ? 1 - ab * rsqrt_a2 * rsqrt_b2 : 1;
1354
1409
  }
1355
1410
 
1356
1411
  #pragma clang attribute pop
@@ -1440,7 +1495,7 @@ simsimd_cos_i8_ice_cycle:
1440
1495
  __m128 rsqrts = _mm_maskz_rsqrt14_ps(0xFF, _mm_set_ps(0.f, 0.f, a2 + 1.e-9f, b2 + 1.e-9f));
1441
1496
  simsimd_f32_t rsqrt_a2 = _mm_cvtss_f32(rsqrts);
1442
1497
  simsimd_f32_t rsqrt_b2 = _mm_cvtss_f32(_mm_shuffle_ps(rsqrts, rsqrts, _MM_SHUFFLE(0, 0, 0, 1)));
1443
- *result = ab != 0 ? 1 - ab * rsqrt_a2 * rsqrt_b2 : 0;
1498
+ *result = ab != 0 ? 1 - ab * rsqrt_a2 * rsqrt_b2 : 1;
1444
1499
  }
1445
1500
 
1446
1501
  #pragma clang attribute pop