usearch 2.7.3 → 2.7.6

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 (55) hide show
  1. package/.github/workflows/Dockerfile.libs +4 -6
  2. package/.github/workflows/prerelease.yml +4 -1
  3. package/.github/workflows/release.yml +8 -28
  4. package/.github/workflows/update_version.sh +1 -0
  5. package/CITATION.cff +2 -2
  6. package/CMakeLists.txt +1 -1
  7. package/Cargo.toml +1 -1
  8. package/README.md +4 -4
  9. package/binding.gyp +1 -1
  10. package/build.gradle +20 -5
  11. package/conanfile.py +1 -1
  12. package/csharp/nuget/nuget-package.props +1 -1
  13. package/docs/compilation.md +0 -2
  14. package/golang/README.md +0 -1
  15. package/include/usearch/index.hpp +1 -1
  16. package/include/usearch/index_plugins.hpp +119 -243
  17. package/package.json +1 -1
  18. package/simsimd/.github/workflows/prerelease.yml +33 -58
  19. package/simsimd/.github/workflows/release.yml +22 -9
  20. package/simsimd/.github/workflows/update_version.sh +3 -1
  21. package/simsimd/.releaserc +9 -1
  22. package/simsimd/.vscode/launch.json +3 -0
  23. package/simsimd/.vscode/settings.json +15 -2
  24. package/simsimd/.vscode/tasks.json +2 -2
  25. package/simsimd/CMakeLists.txt +36 -55
  26. package/simsimd/README.md +163 -109
  27. package/simsimd/binding.gyp +18 -0
  28. package/simsimd/cpp/bench.cxx +187 -0
  29. package/simsimd/go.mod +3 -0
  30. package/simsimd/golang/bench_test.go +40 -0
  31. package/simsimd/golang/simsimd.go +47 -0
  32. package/simsimd/golang/unit_test.go +40 -0
  33. package/simsimd/include/simsimd/binary.h +186 -0
  34. package/simsimd/include/simsimd/probability.h +645 -0
  35. package/simsimd/include/simsimd/simsimd.h +335 -575
  36. package/simsimd/include/simsimd/spatial.h +924 -0
  37. package/simsimd/include/simsimd/types.h +196 -0
  38. package/simsimd/javascript/bench.js +117 -0
  39. package/simsimd/javascript/lib.c +102 -0
  40. package/simsimd/javascript/simsimd.js +45 -0
  41. package/simsimd/javascript/test.js +29 -0
  42. package/simsimd/package.json +31 -2
  43. package/simsimd/pyproject.toml +5 -6
  44. package/simsimd/python/bench.ipynb +187 -0
  45. package/simsimd/python/bench.py +268 -0
  46. package/simsimd/python/lib.c +453 -72
  47. package/simsimd/python/test.py +167 -35
  48. package/simsimd/setup.py +51 -8
  49. package/wasmer.toml +1 -1
  50. package/simsimd/bench.cxx +0 -137
  51. package/simsimd/include/simsimd/simsimd_bio.h +0 -54
  52. package/simsimd/include/simsimd/simsimd_chem.h +0 -167
  53. package/simsimd/include/simsimd/simsimd_text.h +0 -92
  54. package/simsimd/python/run.sh +0 -1
  55. package/simsimd/python/test.c +0 -109
@@ -41,6 +41,8 @@
41
41
  #endif
42
42
 
43
43
  #if USEARCH_USE_SIMSIMD
44
+ // Propagate the `f16` settings
45
+ #define SIMSIMD_NATIVE_F16 USEARCH_USE_NATIVE_F16
44
46
  #if defined(USEARCH_DEFINED_LINUX)
45
47
  #include <simsimd/simsimd.h>
46
48
  #elif defined(USEARCH_DEFINED_APPLE)
@@ -128,16 +130,6 @@ enum class scalar_kind_t : std::uint8_t {
128
130
  i8_k,
129
131
  };
130
132
 
131
- enum class isa_kind_t {
132
- auto_k,
133
- neon_k,
134
- sve_k,
135
- avx2_k,
136
- avx2f16_k,
137
- avx512_k,
138
- avx512f16_k,
139
- };
140
-
141
133
  enum class prefetching_kind_t {
142
134
  none_k,
143
135
  cpu_k,
@@ -194,95 +186,20 @@ inline bool str_equals(char const* begin, std::size_t len, char const* other_beg
194
186
  return len == other_len && std::strncmp(begin, other_begin, len) == 0;
195
187
  }
196
188
 
197
- inline char const* isa_name(isa_kind_t isa_kind) noexcept {
189
+ inline char const* isa_name(simsimd_capability_t isa_kind) noexcept {
198
190
  switch (isa_kind) {
199
- case isa_kind_t::auto_k: return "auto";
200
- case isa_kind_t::neon_k: return "neon";
201
- case isa_kind_t::sve_k: return "sve";
202
- case isa_kind_t::avx2_k: return "avx2";
203
- case isa_kind_t::avx512_k: return "avx512";
204
- case isa_kind_t::avx2f16_k: return "avx2+f16";
205
- case isa_kind_t::avx512f16_k: return "avx512+f16";
206
- default: return "";
191
+ case simsimd_cap_serial_k: return "serial";
192
+ case simsimd_cap_arm_neon_k: return "neon";
193
+ case simsimd_cap_arm_sve_k: return "sve";
194
+ case simsimd_cap_x86_avx2_k: return "avx2";
195
+ case simsimd_cap_x86_avx512_k: return "avx512";
196
+ case simsimd_cap_x86_avx2fp16_k: return "avx2+f16";
197
+ case simsimd_cap_x86_avx512fp16_k: return "avx512+f16";
198
+ case simsimd_cap_x86_avx512vpopcntdq_k: return "avx512+popcnt";
199
+ default: return "unknown";
207
200
  }
208
201
  }
209
202
 
210
- inline bool hardware_supports(isa_kind_t isa_kind) noexcept {
211
-
212
- // On Linux Arm machines the `getauxval` can be queried to check
213
- // if SVE extensions are available. Arm Neon has no separate capability check.
214
- #if defined(USEARCH_DEFINED_ARM) && defined(USEARCH_DEFINED_LINUX)
215
- unsigned long capabilities = getauxval(AT_HWCAP);
216
- switch (isa_kind) {
217
- case isa_kind_t::neon_k: return true; // Must be supported on 64-bit Arm
218
- case isa_kind_t::sve_k: return capabilities & HWCAP_SVE;
219
- default: return false;
220
- }
221
- #endif
222
-
223
- // When compiling with GCC, one may use the "built-ins", including ones
224
- // designed for CPU capability detection, but they are not fully compatible with Clang.
225
- // So instead I've decided to add a pinch of inline assembly.
226
- #if defined(USEARCH_DEFINED_X86)
227
- auto check_cpu_feature = [](std::uint64_t feature_mask, int function_id, int register_id) -> bool {
228
- std::uint32_t eax, ebx, ecx, edx;
229
- // Execute CPUID instruction and store results in eax, ebx, ecx, edx
230
- // Setting %ecx to 0 as well
231
- __asm__ volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(function_id), "c"(0));
232
- std::uint32_t register_value;
233
- switch (register_id) {
234
- case 0: register_value = eax; break;
235
- case 1: register_value = ebx; break;
236
- case 2: register_value = ecx; break;
237
- case 3: register_value = edx; break;
238
- default: return false;
239
- }
240
-
241
- return (register_value & feature_mask) != 0;
242
- };
243
-
244
- // Check for AVX2 (Function ID 7, EBX register)
245
- // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L148
246
- bool supports_avx2 = check_cpu_feature(1 << 5, 7, 1);
247
-
248
- // Check for F16C (Function ID 1, ECX register)
249
- // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L107
250
- bool supports_f16c = check_cpu_feature(1 << 29, 1, 2);
251
-
252
- // Check for AVX512F (Function ID 7, EBX register)
253
- // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L155
254
- bool supports_avx512f = check_cpu_feature(1 << 16, 7, 1);
255
-
256
- // Check for AVX512FP16 (Function ID 7, EDX register)
257
- // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L198C9-L198C23
258
- bool supports_avx512fp16 = check_cpu_feature(1 << 23, 7, 3);
259
-
260
- switch (isa_kind) {
261
- case isa_kind_t::avx2f16_k: return supports_avx2 && supports_f16c;
262
- case isa_kind_t::avx512f16_k: return supports_avx512fp16 && supports_avx512f;
263
- case isa_kind_t::avx2_k: return supports_avx2;
264
- case isa_kind_t::avx512_k: return supports_avx512f;
265
- default: return false;
266
- }
267
- #endif
268
-
269
- // On Apple we can expect Arm devices to support Neon extesions,
270
- // and the x86 machines to support AVX2 extensions.
271
- #if defined(USEARCH_DEFINED_APPLE)
272
- switch (isa_kind) {
273
- #if defined(USEARCH_DEFINED_ARM)
274
- case isa_kind_t::neon_k: return true;
275
- #else
276
- case isa_kind_t::avx2_k: return true;
277
- #endif
278
- default: return false;
279
- }
280
- #endif
281
-
282
- (void)isa_kind;
283
- return false;
284
- }
285
-
286
203
  inline std::size_t bits_per_scalar(scalar_kind_t scalar_kind) noexcept {
287
204
  switch (scalar_kind) {
288
205
  case scalar_kind_t::f64_k: return 64;
@@ -653,9 +570,7 @@ class aligned_allocator_gt {
653
570
  using size_type = std::size_t;
654
571
  using pointer = element_at*;
655
572
  using const_pointer = element_at const*;
656
- template <typename other_element_at> struct rebind {
657
- using other = aligned_allocator_gt<other_element_at>;
658
- };
573
+ template <typename other_element_at> struct rebind { using other = aligned_allocator_gt<other_element_at>; };
659
574
 
660
575
  constexpr std::size_t alignment() const { return alignment_ak; }
661
576
 
@@ -716,7 +631,7 @@ class page_allocator_t {
716
631
  * @b Thread-safe, @b except constructors and destructors.
717
632
  *
718
633
  * Using this memory allocator won't affect your overall speed much, as that is not the bottleneck.
719
- * However, it can drastically improve memory usage especcially for huge indexes of small vectors.
634
+ * However, it can drastically improve memory usage especially for huge indexes of small vectors.
720
635
  */
721
636
  template <std::size_t alignment_ak = 1> class memory_mapping_allocator_gt {
722
637
 
@@ -1350,6 +1265,7 @@ enum class metric_punned_signature_t {
1350
1265
  /**
1351
1266
  * @brief Type-punned metric class, which unlike STL's `std::function` avoids any memory allocations.
1352
1267
  * It also provides additional APIs to check, if SIMD hardware-acceleration is available.
1268
+ * Wraps the `simsimd_metric_punned_t`.
1353
1269
  */
1354
1270
  class metric_punned_t {
1355
1271
  public:
@@ -1358,15 +1274,16 @@ class metric_punned_t {
1358
1274
 
1359
1275
  private:
1360
1276
  using punned_arg_t = std::size_t;
1361
- using raw_ptr_t = result_t (*)(punned_arg_t, punned_arg_t, punned_arg_t, punned_arg_t);
1362
- raw_ptr_t raw_ptr_ = nullptr;
1277
+ using punned_ptr_t = result_t (*)(std::size_t, std::size_t, std::size_t, std::size_t);
1278
+
1279
+ punned_ptr_t raw_ptr_ = nullptr;
1363
1280
  punned_arg_t raw_arg3_ = 0;
1364
1281
  punned_arg_t raw_arg4_ = 0;
1365
1282
 
1366
1283
  std::size_t dimensions_ = 0;
1367
1284
  metric_kind_t metric_kind_ = metric_kind_t::unknown_k;
1368
1285
  scalar_kind_t scalar_kind_ = scalar_kind_t::unknown_k;
1369
- isa_kind_t isa_kind_ = isa_kind_t::auto_k;
1286
+ simsimd_capability_t isa_kind_ = simsimd_cap_serial_k;
1370
1287
 
1371
1288
  public:
1372
1289
  /**
@@ -1386,8 +1303,14 @@ class metric_punned_t {
1386
1303
  metric_kind_t metric_kind = metric_kind_t::l2sq_k, //
1387
1304
  scalar_kind_t scalar_kind = scalar_kind_t::f32_k) noexcept
1388
1305
  : raw_arg3_(dimensions), raw_arg4_(dimensions), dimensions_(dimensions), metric_kind_(metric_kind),
1389
- scalar_kind_(scalar_kind), isa_kind_(isa_kind_t::auto_k) {
1390
- reset_();
1306
+ scalar_kind_(scalar_kind), isa_kind_(simsimd_cap_serial_k) {
1307
+
1308
+ #if USEARCH_USE_SIMSIMD
1309
+ if (configure_with_simsimd())
1310
+ return;
1311
+ #endif
1312
+
1313
+ configure_with_auto_vectorized();
1391
1314
  }
1392
1315
 
1393
1316
  inline metric_punned_t( //
@@ -1395,7 +1318,7 @@ class metric_punned_t {
1395
1318
  std::uintptr_t metric_uintptr, metric_punned_signature_t signature, //
1396
1319
  metric_kind_t metric_kind, //
1397
1320
  scalar_kind_t scalar_kind) noexcept
1398
- : raw_ptr_(reinterpret_cast<raw_ptr_t>(metric_uintptr)), dimensions_(dimensions), metric_kind_(metric_kind),
1321
+ : raw_ptr_(reinterpret_cast<punned_ptr_t>(metric_uintptr)), dimensions_(dimensions), metric_kind_(metric_kind),
1399
1322
  scalar_kind_(scalar_kind) {
1400
1323
 
1401
1324
  // We don't need to explicitly parse signature, as all of them are compatible.
@@ -1405,7 +1328,7 @@ class metric_punned_t {
1405
1328
  inline std::size_t dimensions() const noexcept { return dimensions_; }
1406
1329
  inline metric_kind_t metric_kind() const noexcept { return metric_kind_; }
1407
1330
  inline scalar_kind_t scalar_kind() const noexcept { return scalar_kind_; }
1408
- inline isa_kind_t isa_kind() const noexcept { return isa_kind_; }
1331
+ inline simsimd_capability_t isa_kind() const noexcept { return isa_kind_; }
1409
1332
 
1410
1333
  inline std::size_t bytes_per_vector() const noexcept {
1411
1334
  return divide_round_up<CHAR_BIT>(dimensions_ * bits_per_scalar(scalar_kind_));
@@ -1416,24 +1339,105 @@ class metric_punned_t {
1416
1339
  }
1417
1340
 
1418
1341
  private:
1419
- void reset_() noexcept {
1342
+ bool configure_with_simsimd() noexcept {
1343
+ #if USEARCH_USE_SIMSIMD
1344
+ simsimd_metric_kind_t kind = simsimd_metric_unknown_k;
1345
+ simsimd_datatype_t datatype = simsimd_datatype_unknown_k;
1346
+ simsimd_capability_t allowed = simsimd_cap_any_k;
1420
1347
  switch (metric_kind_) {
1421
- case metric_kind_t::ip_k: return reset_ip_metric_();
1422
- case metric_kind_t::cos_k: return reset_cos_metric_();
1423
- case metric_kind_t::l2sq_k: return reset_l2sq_metric_();
1424
- case metric_kind_t::pearson_k: return reset_pearson_metric_();
1425
- case metric_kind_t::haversine_k: return reset_haversine_metric_();
1348
+ case metric_kind_t::ip_k: kind = simsimd_metric_ip_k; break;
1349
+ case metric_kind_t::cos_k: kind = simsimd_metric_cos_k; break;
1350
+ case metric_kind_t::l2sq_k: kind = simsimd_metric_l2sq_k; break;
1351
+ case metric_kind_t::hamming_k: kind = simsimd_metric_hamming_k; break;
1352
+ case metric_kind_t::jaccard_k: kind = simsimd_metric_jaccard_k; break;
1353
+ }
1354
+ switch (scalar_kind_) {
1355
+ case scalar_kind_t::f32_k: datatype = simsimd_datatype_f32_k; break;
1356
+ case scalar_kind_t::f64_k: datatype = simsimd_datatype_f64_k; break;
1357
+ case scalar_kind_t::f16_k: datatype = simsimd_datatype_f16_k; break;
1358
+ case scalar_kind_t::i8_k: datatype = simsimd_datatype_i8_k; break;
1359
+ case scalar_kind_t::b1x8_k: datatype = simsimd_datatype_b8_k; break;
1360
+ }
1361
+ simsimd_metric_punned_t simd_metric = NULL;
1362
+ simsimd_capability_t simd_kind = simsimd_cap_any_k;
1363
+ simsimd_find_metric_punned(kind, datatype, simsimd_capabilities(), allowed, &simd_metric, &simd_kind);
1364
+ if (simd_metric == nullptr)
1365
+ return false;
1366
+
1367
+ raw_ptr_ = (punned_ptr_t)simd_metric;
1368
+ isa_kind_ = simd_kind;
1369
+ return true;
1370
+ #else
1371
+ return false;
1372
+ #endif
1373
+ }
1374
+
1375
+ void configure_with_auto_vectorized() noexcept {
1376
+ switch (metric_kind_) {
1377
+ case metric_kind_t::ip_k: {
1378
+ switch (scalar_kind_) {
1379
+ case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<f32_t>>; break;
1380
+ case scalar_kind_t::f16_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<f16_t, f32_t>>; break;
1381
+ case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<i8_t, f32_t>>; break;
1382
+ case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<f64_t>>; break;
1383
+ default: raw_ptr_ = nullptr; break;
1384
+ }
1385
+ break;
1386
+ }
1387
+ case metric_kind_t::cos_k: {
1388
+ switch (scalar_kind_) {
1389
+ case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<f32_t>>; break;
1390
+ case scalar_kind_t::f16_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<f16_t, f32_t>>; break;
1391
+ case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<i8_t, f32_t>>; break;
1392
+ case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<f64_t>>; break;
1393
+ default: raw_ptr_ = nullptr; break;
1394
+ }
1395
+ break;
1396
+ }
1397
+ case metric_kind_t::l2sq_k: {
1398
+ switch (scalar_kind_) {
1399
+ case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<f32_t>>; break;
1400
+ case scalar_kind_t::f16_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<f16_t, f32_t>>; break;
1401
+ case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<i8_t, f32_t>>; break;
1402
+ case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<f64_t>>; break;
1403
+ default: raw_ptr_ = nullptr; break;
1404
+ }
1405
+ break;
1406
+ }
1407
+ case metric_kind_t::pearson_k: {
1408
+ switch (scalar_kind_) {
1409
+ case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<i8_t, f32_t>>; break;
1410
+ case scalar_kind_t::f16_k:
1411
+ raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<f16_t, f32_t>>;
1412
+ break;
1413
+ case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<f32_t>>; break;
1414
+ case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<f64_t>>; break;
1415
+ default: raw_ptr_ = nullptr; break;
1416
+ }
1417
+ break;
1418
+ }
1419
+ case metric_kind_t::haversine_k: {
1420
+ switch (scalar_kind_) {
1421
+ case scalar_kind_t::f16_k:
1422
+ raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_haversine_gt<f16_t, f32_t>>;
1423
+ break;
1424
+ case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_haversine_gt<f32_t>>; break;
1425
+ case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_haversine_gt<f64_t>>; break;
1426
+ default: raw_ptr_ = nullptr; break;
1427
+ }
1428
+ break;
1429
+ }
1426
1430
  case metric_kind_t::jaccard_k: // Equivalent to Tanimoto
1427
1431
  case metric_kind_t::tanimoto_k:
1428
- raw_ptr_ = &equidimensional_<metric_tanimoto_gt<b1x8_t>>,
1432
+ raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_tanimoto_gt<b1x8_t>>,
1429
1433
  raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
1430
1434
  break;
1431
1435
  case metric_kind_t::hamming_k:
1432
- raw_ptr_ = &equidimensional_<metric_hamming_gt<b1x8_t>>,
1436
+ raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_hamming_gt<b1x8_t>>,
1433
1437
  raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
1434
1438
  break;
1435
1439
  case metric_kind_t::sorensen_k:
1436
- raw_ptr_ = &equidimensional_<metric_sorensen_gt<b1x8_t>>,
1440
+ raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_sorensen_gt<b1x8_t>>,
1437
1441
  raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
1438
1442
  break;
1439
1443
  default: return;
@@ -1448,134 +1452,6 @@ class metric_punned_t {
1448
1452
  (void)b_dimensions;
1449
1453
  return typed_at{}((scalar_t const*)a, (scalar_t const*)b, a_dimensions);
1450
1454
  }
1451
-
1452
- template <typename scalar_at>
1453
- bool reset_if_(isa_kind_t isa, result_t (*function_ptr)(scalar_at, scalar_at, size_t),
1454
- bool second_condition = true) noexcept {
1455
- raw_ptr_t punned_ptr = reinterpret_cast<raw_ptr_t>(function_ptr);
1456
- if (hardware_supports(isa) && second_condition) {
1457
- raw_ptr_ = punned_ptr;
1458
- isa_kind_ = isa;
1459
- return true;
1460
- } else
1461
- return false;
1462
- }
1463
-
1464
- // clang-format off
1465
- void reset_ip_metric_f32_() noexcept {
1466
- #if USEARCH_USE_SIMSIMD
1467
- #if SIMSIMD_TARGET_ARM_NEON
1468
- if (reset_if_(isa_kind_t::neon_k, &simsimd_dot_f32x4_neon, dimensions_ % 4 == 0)) return;
1469
- #elif SIMSIMD_TARGET_ARM_SVE
1470
- if (reset_if_(isa_kind_t::sve_k, &simsimd_dot_f32_sve)) return;
1471
- #elif SIMSIMD_TARGET_X86_AVX2
1472
- if (reset_if_(isa_kind_t::avx2_k, &simsimd_dot_f32x4_avx2, dimensions_ % 4 == 0)) return;
1473
- #endif
1474
- #endif
1475
- raw_ptr_ = &equidimensional_<metric_ip_gt<f32_t>>;
1476
- }
1477
-
1478
- void reset_cos_metric_f16_() noexcept {
1479
- #if USEARCH_USE_SIMSIMD
1480
- #if SIMSIMD_TARGET_X86_AVX512
1481
- if (reset_if_(isa_kind_t::avx512f16_k, &simsimd_cos_f16x16_avx512, dimensions_ % 16 == 0)) return;
1482
- #elif SIMSIMD_TARGET_X86_AVX2
1483
- if (reset_if_(isa_kind_t::avx2f16_k, &simsimd_cos_f16x8_avx2, dimensions_ % 8 == 0)) return;
1484
- #elif SIMSIMD_TARGET_ARM_NEON
1485
- if (reset_if_(isa_kind_t::neon_k, &simsimd_cos_f16x4_neon, dimensions_ % 4 == 0)) return;
1486
- #elif SIMSIMD_TARGET_ARM_SVE
1487
- if (reset_if_(isa_kind_t::sve_k, &simsimd_cos_f16_sve)) return;
1488
- #endif
1489
- #endif
1490
- raw_ptr_ = &equidimensional_<metric_cos_gt<f16_t, f32_t>>;
1491
- }
1492
-
1493
- void reset_ip_metric_f16_() noexcept {
1494
- #if USEARCH_USE_SIMSIMD
1495
- #if SIMSIMD_TARGET_X86_AVX512
1496
- if (reset_if_(isa_kind_t::avx512f16_k, &simsimd_dot_f16x16_avx512, dimensions_ % 16 == 0)) return;
1497
- #elif SIMSIMD_TARGET_X86_AVX2
1498
- if (reset_if_(isa_kind_t::avx2f16_k, &simsimd_dot_f16x8_avx2, dimensions_ % 8 == 0)) return;
1499
- #elif SIMSIMD_TARGET_ARM_NEON
1500
- if (reset_if_(isa_kind_t::neon_k, &simsimd_dot_f16x4_neon, dimensions_ % 4 == 0)) return;
1501
- #endif
1502
- #endif
1503
- raw_ptr_ = &equidimensional_<metric_cos_gt<f16_t, f32_t>>;
1504
- }
1505
-
1506
- void reset_l2sq_metric_f16_() noexcept {
1507
- #if USEARCH_USE_SIMSIMD
1508
- #if SIMSIMD_TARGET_X86_AVX512
1509
- if (reset_if_(isa_kind_t::avx512f16_k, &simsimd_l2sq_f16x16_avx512, dimensions_ % 16 == 0)) return;
1510
- #elif SIMSIMD_TARGET_X86_AVX2
1511
- if (reset_if_(isa_kind_t::avx2f16_k, &simsimd_l2sq_f16x8_avx2, dimensions_ % 8 == 0)) return;
1512
- #elif SIMSIMD_TARGET_ARM_SVE
1513
- if (reset_if_(isa_kind_t::sve_k, &simsimd_l2sq_f16_sve)) return;
1514
- #endif
1515
- #endif
1516
- raw_ptr_ = &equidimensional_<metric_l2sq_gt<f16_t, f32_t>>;
1517
- }
1518
-
1519
- void reset_cos_metric_i8_() noexcept {
1520
- #if USEARCH_USE_SIMSIMD
1521
- #if SIMSIMD_TARGET_ARM_NEON
1522
- if (reset_if_(isa_kind_t::neon_k, &simsimd_cos_i8x16_neon, dimensions_ % 16 == 0)) return;
1523
- #elif SIMSIMD_TARGET_X86_AVX2
1524
- if (reset_if_(isa_kind_t::avx2_k, &simsimd_cos_i8x32_avx2, dimensions_ % 32 == 0)) return;
1525
- #endif
1526
- #endif
1527
- raw_ptr_ = &equidimensional_<cos_i8_t>;
1528
- }
1529
- // clang-format on
1530
-
1531
- void reset_ip_metric_() noexcept {
1532
- switch (scalar_kind_) { // The two most common numeric types for the most common metric have optimized versions
1533
- case scalar_kind_t::f32_k: return reset_ip_metric_f32_();
1534
- case scalar_kind_t::f16_k: return reset_ip_metric_f16_();
1535
- case scalar_kind_t::i8_k: return reset_cos_metric_i8_();
1536
- case scalar_kind_t::f64_k: raw_ptr_ = &equidimensional_<metric_ip_gt<f64_t>>; break;
1537
- default: return;
1538
- }
1539
- }
1540
-
1541
- void reset_l2sq_metric_() noexcept {
1542
- switch (scalar_kind_) {
1543
- case scalar_kind_t::i8_k: raw_ptr_ = &equidimensional_<l2sq_i8_t>; break;
1544
- case scalar_kind_t::f16_k: return reset_l2sq_metric_f16_();
1545
- case scalar_kind_t::f32_k: raw_ptr_ = &equidimensional_<metric_l2sq_gt<f32_t>>; break;
1546
- case scalar_kind_t::f64_k: raw_ptr_ = &equidimensional_<metric_l2sq_gt<f64_t>>; break;
1547
- default: return;
1548
- }
1549
- }
1550
-
1551
- void reset_cos_metric_() noexcept {
1552
- switch (scalar_kind_) {
1553
- case scalar_kind_t::i8_k: return reset_cos_metric_i8_();
1554
- case scalar_kind_t::f16_k: return reset_cos_metric_f16_();
1555
- case scalar_kind_t::f32_k: raw_ptr_ = &equidimensional_<metric_cos_gt<f32_t>>; break;
1556
- case scalar_kind_t::f64_k: raw_ptr_ = &equidimensional_<metric_cos_gt<f64_t>>; break;
1557
- default: return;
1558
- }
1559
- }
1560
-
1561
- void reset_haversine_metric_() noexcept {
1562
- switch (scalar_kind_) {
1563
- case scalar_kind_t::f16_k: raw_ptr_ = &equidimensional_<metric_haversine_gt<f16_t, f32_t>>; break;
1564
- case scalar_kind_t::f32_k: raw_ptr_ = &equidimensional_<metric_haversine_gt<f32_t>>; break;
1565
- case scalar_kind_t::f64_k: raw_ptr_ = &equidimensional_<metric_haversine_gt<f64_t>>; break;
1566
- default: return;
1567
- }
1568
- }
1569
-
1570
- void reset_pearson_metric_() noexcept {
1571
- switch (scalar_kind_) {
1572
- case scalar_kind_t::i8_k: raw_ptr_ = &equidimensional_<metric_pearson_gt<i8_t, f32_t>>; break;
1573
- case scalar_kind_t::f16_k: raw_ptr_ = &equidimensional_<metric_pearson_gt<f16_t, f32_t>>; break;
1574
- case scalar_kind_t::f32_k: raw_ptr_ = &equidimensional_<metric_pearson_gt<f32_t>>; break;
1575
- case scalar_kind_t::f64_k: raw_ptr_ = &equidimensional_<metric_pearson_gt<f64_t>>; break;
1576
- default: return;
1577
- }
1578
- }
1579
1455
  };
1580
1456
 
1581
1457
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "2.7.3",
3
+ "version": "2.7.6",
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",
@@ -17,14 +17,15 @@ permissions:
17
17
 
18
18
  jobs:
19
19
 
20
- test_python_311:
21
- name: Test Python
20
+ test_python:
21
+ name: Test Python ${{ matrix.python-version }} on ${{ matrix.architecture }} ${{ matrix.os }}
22
22
  runs-on: ${{ matrix.os }}
23
23
  strategy:
24
24
  fail-fast: false
25
25
  matrix:
26
- os: [ubuntu-22.04, macOS-11] # windows-2022
27
- python-version: ["3.11"]
26
+ os: [ubuntu-22.04, macOS-11, windows-2022]
27
+ python-version: ["3.12", "3.8"]
28
+ architecture: [x64, arm64]
28
29
 
29
30
  steps:
30
31
  - uses: actions/checkout@v3
@@ -33,74 +34,48 @@ jobs:
33
34
  uses: actions/setup-python@v4
34
35
  with:
35
36
  python-version: ${{ matrix.python-version }}
36
-
37
- - name: Set up Clang
38
- run: brew install gcc@12
39
- if: ${{ matrix.os == 'macOS-11' }}
40
-
41
- - name: Set up MinGW
42
- uses: egor-tensin/setup-mingw@v2
43
- with:
44
- platform: x64
45
- if: ${{ matrix.os == 'windows-2022' }}
46
37
 
47
38
  - name: Install dependencies
48
39
  run: |
49
40
  python -m pip install --no-cache-dir --upgrade pip
50
- pip install --no-cache-dir pytest
51
-
41
+ pip install --no-cache-dir pytest numpy scipy py-cpuinfo pytest-repeat
42
+ python -c "from cpuinfo import get_cpu_info; print(get_cpu_info())"
43
+
52
44
  - name: Build locally on Ubuntu
53
45
  run: |
54
- export CC=/usr/bin/gcc-12
55
- export CXX=/usr/bin/g++-12
56
- export LD_LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:$LD_LIBRARY_PATH
46
+ sudo apt-get update
47
+ sudo apt-get install gcc-12 -y
48
+ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12
49
+ sudo update-alternatives --config gcc
50
+ gcc --version
57
51
  python -m pip install .
58
52
  if: ${{ matrix.os == 'ubuntu-22.04' }}
59
53
 
60
- - name: Build locally on MacOS
61
- run: |
62
- export CC=/usr/local/bin/gcc-12
63
- export CXX=/usr/local/bin/g++-12
64
- python -m pip install .
65
- if: ${{ matrix.os == 'macOS-11' }}
54
+ - name: Build locally on other OS
55
+ run: python -m pip install .
56
+ if: ${{ matrix.os != 'ubuntu-22.04' }}
66
57
 
67
- - name: Build locally on Windows
68
- run: |
69
- python -m pip install .
70
- if: ${{ matrix.os == 'windows-2022' }}
71
-
72
58
  - name: Test with PyTest
73
- run: pytest python/test.py
59
+ run: |
60
+ python -c "import simsimd; print(simsimd.get_capabilities())"
61
+ pytest python/test.py
74
62
 
75
-
76
- test_python_37:
77
- name: Test Python 3.7
78
- runs-on: ${{ matrix.os }}
63
+ test_javascript:
64
+ name: Test JavaScript
65
+ runs-on: ubuntu-latest
79
66
  strategy:
80
67
  matrix:
81
- os: [ubuntu-22.04]
82
- python-version: ["3.7"]
83
-
68
+ node-version: [18.x]
84
69
  steps:
85
- - uses: actions/checkout@v3
86
- - run: git submodule update --init --recursive
87
-
88
- - name: Set up Python ${{ matrix.python-version }}
89
- uses: actions/setup-python@v4
70
+
71
+ - uses: actions/checkout@v4
72
+ - name: Set up Node.js
73
+ uses: actions/setup-node@v3
90
74
  with:
91
- python-version: ${{ matrix.python-version }}
92
-
93
- - name: Install dependencies
94
- run: |
95
- python -m pip install --no-cache-dir --upgrade pip
96
- pip install --no-cache-dir pytest
97
-
75
+ node-version: '18.x'
76
+
98
77
  - name: Build locally
99
- run: |
100
- export CC=/usr/bin/gcc-12
101
- export CXX=/usr/bin/g++-12
102
- export LD_LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:$LD_LIBRARY_PATH
103
- python -m pip install .
104
-
105
- - name: Test with PyTest
106
- run: pytest python/test.py
78
+ run: npm i
79
+
80
+ - name: Test
81
+ run: npm test
@@ -56,8 +56,9 @@ jobs:
56
56
  runs-on: ${{ matrix.os }}
57
57
  needs: versioning
58
58
  strategy:
59
+ fail-fast: false
59
60
  matrix:
60
- os: [ubuntu-20.04, macOS-11] # windows-2022
61
+ os: [ubuntu-20.04, macOS-11, windows-2022]
61
62
  steps:
62
63
  - uses: actions/checkout@v3
63
64
  with:
@@ -75,15 +76,8 @@ jobs:
75
76
  - name: Install CIBuildWheel
76
77
  run: python -m pip install --no-cache-dir cibuildwheel
77
78
 
78
- - name: Build wheels on Ubuntu
79
+ - name: Build wheels
79
80
  run: python -m cibuildwheel
80
- if: matrix.os == 'ubuntu-20.04'
81
-
82
- - name: Build wheels on MacOS
83
- run: python -m cibuildwheel
84
- env:
85
- CIBW_ENVIRONMENT: CC=/usr/local/bin/gcc-12 CXX=/usr/local/bin/g++-12
86
- if: matrix.os == 'macOS-11'
87
81
 
88
82
  - uses: actions/upload-artifact@v3
89
83
  with:
@@ -112,3 +106,22 @@ jobs:
112
106
  packages-dir: ./dist/artifact
113
107
  verbose: true
114
108
  print-hash: true
109
+
110
+ publish_javascript:
111
+ name: Publish JavaScript
112
+ needs: versioning
113
+ runs-on: ubuntu-latest
114
+ steps:
115
+ - uses: actions/checkout@v3
116
+ with:
117
+ ref: 'main'
118
+ - run: git submodule update --init --recursive
119
+ - uses: actions/setup-node@v3
120
+ with:
121
+ node-version: 18
122
+ - run: npm install
123
+ - run: npm ci
124
+ - run: npm test
125
+ - uses: JS-DevTools/npm-publish@v2
126
+ with:
127
+ token: ${{ secrets.NPM_TOKEN }}
@@ -1,3 +1,5 @@
1
1
  #!/bin/sh
2
2
 
3
- echo $1 > VERSION
3
+ echo $1 > VERSION && \
4
+ sed -i "s/\"version\": \".*\"/\"version\": \"$1\"/" package.json && \
5
+ sed -i "s/\"__version__\", \".*\"/\"__version__\", \"$1\"/" python/lib.c