usearch 2.7.3 → 2.7.7

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 (57) 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/cpp/bench.cpp +1 -1
  13. package/csharp/nuget/nuget-package.props +1 -1
  14. package/docs/compilation.md +0 -2
  15. package/golang/README.md +0 -1
  16. package/include/usearch/index.hpp +1 -1
  17. package/include/usearch/index_plugins.hpp +128 -247
  18. package/package.json +1 -1
  19. package/python/lib.cpp +3 -4
  20. package/simsimd/.github/workflows/prerelease.yml +33 -58
  21. package/simsimd/.github/workflows/release.yml +22 -9
  22. package/simsimd/.github/workflows/update_version.sh +3 -1
  23. package/simsimd/.releaserc +9 -1
  24. package/simsimd/.vscode/launch.json +3 -0
  25. package/simsimd/.vscode/settings.json +15 -2
  26. package/simsimd/.vscode/tasks.json +2 -2
  27. package/simsimd/CMakeLists.txt +36 -55
  28. package/simsimd/README.md +163 -109
  29. package/simsimd/binding.gyp +18 -0
  30. package/simsimd/cpp/bench.cxx +187 -0
  31. package/simsimd/go.mod +3 -0
  32. package/simsimd/golang/bench_test.go +40 -0
  33. package/simsimd/golang/simsimd.go +47 -0
  34. package/simsimd/golang/unit_test.go +40 -0
  35. package/simsimd/include/simsimd/binary.h +186 -0
  36. package/simsimd/include/simsimd/probability.h +645 -0
  37. package/simsimd/include/simsimd/simsimd.h +335 -575
  38. package/simsimd/include/simsimd/spatial.h +924 -0
  39. package/simsimd/include/simsimd/types.h +196 -0
  40. package/simsimd/javascript/bench.js +117 -0
  41. package/simsimd/javascript/lib.c +102 -0
  42. package/simsimd/javascript/simsimd.js +45 -0
  43. package/simsimd/javascript/test.js +29 -0
  44. package/simsimd/package.json +31 -2
  45. package/simsimd/pyproject.toml +5 -6
  46. package/simsimd/python/bench.ipynb +187 -0
  47. package/simsimd/python/bench.py +268 -0
  48. package/simsimd/python/lib.c +453 -72
  49. package/simsimd/python/test.py +167 -35
  50. package/simsimd/setup.py +51 -8
  51. package/wasmer.toml +1 -1
  52. package/simsimd/bench.cxx +0 -137
  53. package/simsimd/include/simsimd/simsimd_bio.h +0 -54
  54. package/simsimd/include/simsimd/simsimd_chem.h +0 -167
  55. package/simsimd/include/simsimd/simsimd_text.h +0 -92
  56. package/simsimd/python/run.sh +0 -1
  57. 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,6 @@ 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 {
198
- 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 "";
207
- }
208
- }
209
-
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
189
  inline std::size_t bits_per_scalar(scalar_kind_t scalar_kind) noexcept {
287
190
  switch (scalar_kind) {
288
191
  case scalar_kind_t::f64_k: return 64;
@@ -653,9 +556,7 @@ class aligned_allocator_gt {
653
556
  using size_type = std::size_t;
654
557
  using pointer = element_at*;
655
558
  using const_pointer = element_at const*;
656
- template <typename other_element_at> struct rebind {
657
- using other = aligned_allocator_gt<other_element_at>;
658
- };
559
+ template <typename other_element_at> struct rebind { using other = aligned_allocator_gt<other_element_at>; };
659
560
 
660
561
  constexpr std::size_t alignment() const { return alignment_ak; }
661
562
 
@@ -716,7 +617,7 @@ class page_allocator_t {
716
617
  * @b Thread-safe, @b except constructors and destructors.
717
618
  *
718
619
  * 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.
620
+ * However, it can drastically improve memory usage especially for huge indexes of small vectors.
720
621
  */
721
622
  template <std::size_t alignment_ak = 1> class memory_mapping_allocator_gt {
722
623
 
@@ -1350,6 +1251,7 @@ enum class metric_punned_signature_t {
1350
1251
  /**
1351
1252
  * @brief Type-punned metric class, which unlike STL's `std::function` avoids any memory allocations.
1352
1253
  * It also provides additional APIs to check, if SIMD hardware-acceleration is available.
1254
+ * Wraps the `simsimd_metric_punned_t`.
1353
1255
  */
1354
1256
  class metric_punned_t {
1355
1257
  public:
@@ -1358,15 +1260,19 @@ class metric_punned_t {
1358
1260
 
1359
1261
  private:
1360
1262
  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;
1263
+ using punned_ptr_t = result_t (*)(std::size_t, std::size_t, std::size_t, std::size_t);
1264
+
1265
+ punned_ptr_t raw_ptr_ = nullptr;
1363
1266
  punned_arg_t raw_arg3_ = 0;
1364
1267
  punned_arg_t raw_arg4_ = 0;
1365
1268
 
1366
1269
  std::size_t dimensions_ = 0;
1367
1270
  metric_kind_t metric_kind_ = metric_kind_t::unknown_k;
1368
1271
  scalar_kind_t scalar_kind_ = scalar_kind_t::unknown_k;
1369
- isa_kind_t isa_kind_ = isa_kind_t::auto_k;
1272
+
1273
+ #if USEARCH_USE_SIMSIMD
1274
+ simsimd_capability_t isa_kind_ = simsimd_cap_serial_k;
1275
+ #endif
1370
1276
 
1371
1277
  public:
1372
1278
  /**
@@ -1386,8 +1292,14 @@ class metric_punned_t {
1386
1292
  metric_kind_t metric_kind = metric_kind_t::l2sq_k, //
1387
1293
  scalar_kind_t scalar_kind = scalar_kind_t::f32_k) noexcept
1388
1294
  : 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_();
1295
+ scalar_kind_(scalar_kind) {
1296
+
1297
+ #if USEARCH_USE_SIMSIMD
1298
+ if (configure_with_simsimd())
1299
+ return;
1300
+ #endif
1301
+
1302
+ configure_with_auto_vectorized();
1391
1303
  }
1392
1304
 
1393
1305
  inline metric_punned_t( //
@@ -1395,7 +1307,7 @@ class metric_punned_t {
1395
1307
  std::uintptr_t metric_uintptr, metric_punned_signature_t signature, //
1396
1308
  metric_kind_t metric_kind, //
1397
1309
  scalar_kind_t scalar_kind) noexcept
1398
- : raw_ptr_(reinterpret_cast<raw_ptr_t>(metric_uintptr)), dimensions_(dimensions), metric_kind_(metric_kind),
1310
+ : raw_ptr_(reinterpret_cast<punned_ptr_t>(metric_uintptr)), dimensions_(dimensions), metric_kind_(metric_kind),
1399
1311
  scalar_kind_(scalar_kind) {
1400
1312
 
1401
1313
  // We don't need to explicitly parse signature, as all of them are compatible.
@@ -1405,7 +1317,23 @@ class metric_punned_t {
1405
1317
  inline std::size_t dimensions() const noexcept { return dimensions_; }
1406
1318
  inline metric_kind_t metric_kind() const noexcept { return metric_kind_; }
1407
1319
  inline scalar_kind_t scalar_kind() const noexcept { return scalar_kind_; }
1408
- inline isa_kind_t isa_kind() const noexcept { return isa_kind_; }
1320
+
1321
+ inline char const* isa_name() const noexcept {
1322
+ #if USEARCH_USE_SIMSIMD
1323
+ switch (isa_kind_) {
1324
+ case simsimd_cap_serial_k: return "serial";
1325
+ case simsimd_cap_arm_neon_k: return "neon";
1326
+ case simsimd_cap_arm_sve_k: return "sve";
1327
+ case simsimd_cap_x86_avx2_k: return "avx2";
1328
+ case simsimd_cap_x86_avx512_k: return "avx512";
1329
+ case simsimd_cap_x86_avx2fp16_k: return "avx2+f16";
1330
+ case simsimd_cap_x86_avx512fp16_k: return "avx512+f16";
1331
+ case simsimd_cap_x86_avx512vpopcntdq_k: return "avx512+popcnt";
1332
+ default: return "unknown";
1333
+ }
1334
+ #endif
1335
+ return "serial";
1336
+ }
1409
1337
 
1410
1338
  inline std::size_t bytes_per_vector() const noexcept {
1411
1339
  return divide_round_up<CHAR_BIT>(dimensions_ * bits_per_scalar(scalar_kind_));
@@ -1416,24 +1344,105 @@ class metric_punned_t {
1416
1344
  }
1417
1345
 
1418
1346
  private:
1419
- void reset_() noexcept {
1347
+ bool configure_with_simsimd() noexcept {
1348
+ #if USEARCH_USE_SIMSIMD
1349
+ simsimd_metric_kind_t kind = simsimd_metric_unknown_k;
1350
+ simsimd_datatype_t datatype = simsimd_datatype_unknown_k;
1351
+ simsimd_capability_t allowed = simsimd_cap_any_k;
1352
+ switch (metric_kind_) {
1353
+ case metric_kind_t::ip_k: kind = simsimd_metric_ip_k; break;
1354
+ case metric_kind_t::cos_k: kind = simsimd_metric_cos_k; break;
1355
+ case metric_kind_t::l2sq_k: kind = simsimd_metric_l2sq_k; break;
1356
+ case metric_kind_t::hamming_k: kind = simsimd_metric_hamming_k; break;
1357
+ case metric_kind_t::jaccard_k: kind = simsimd_metric_jaccard_k; break;
1358
+ }
1359
+ switch (scalar_kind_) {
1360
+ case scalar_kind_t::f32_k: datatype = simsimd_datatype_f32_k; break;
1361
+ case scalar_kind_t::f64_k: datatype = simsimd_datatype_f64_k; break;
1362
+ case scalar_kind_t::f16_k: datatype = simsimd_datatype_f16_k; break;
1363
+ case scalar_kind_t::i8_k: datatype = simsimd_datatype_i8_k; break;
1364
+ case scalar_kind_t::b1x8_k: datatype = simsimd_datatype_b8_k; break;
1365
+ }
1366
+ simsimd_metric_punned_t simd_metric = NULL;
1367
+ simsimd_capability_t simd_kind = simsimd_cap_any_k;
1368
+ simsimd_find_metric_punned(kind, datatype, simsimd_capabilities(), allowed, &simd_metric, &simd_kind);
1369
+ if (simd_metric == nullptr)
1370
+ return false;
1371
+
1372
+ raw_ptr_ = (punned_ptr_t)simd_metric;
1373
+ isa_kind_ = simd_kind;
1374
+ return true;
1375
+ #else
1376
+ return false;
1377
+ #endif
1378
+ }
1379
+
1380
+ void configure_with_auto_vectorized() noexcept {
1420
1381
  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_();
1382
+ case metric_kind_t::ip_k: {
1383
+ switch (scalar_kind_) {
1384
+ case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<f32_t>>; break;
1385
+ case scalar_kind_t::f16_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<f16_t, f32_t>>; break;
1386
+ case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<i8_t, f32_t>>; break;
1387
+ case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<f64_t>>; break;
1388
+ default: raw_ptr_ = nullptr; break;
1389
+ }
1390
+ break;
1391
+ }
1392
+ case metric_kind_t::cos_k: {
1393
+ switch (scalar_kind_) {
1394
+ case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<f32_t>>; break;
1395
+ case scalar_kind_t::f16_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<f16_t, f32_t>>; break;
1396
+ case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<i8_t, f32_t>>; break;
1397
+ case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<f64_t>>; break;
1398
+ default: raw_ptr_ = nullptr; break;
1399
+ }
1400
+ break;
1401
+ }
1402
+ case metric_kind_t::l2sq_k: {
1403
+ switch (scalar_kind_) {
1404
+ case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<f32_t>>; break;
1405
+ case scalar_kind_t::f16_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<f16_t, f32_t>>; break;
1406
+ case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<i8_t, f32_t>>; break;
1407
+ case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<f64_t>>; break;
1408
+ default: raw_ptr_ = nullptr; break;
1409
+ }
1410
+ break;
1411
+ }
1412
+ case metric_kind_t::pearson_k: {
1413
+ switch (scalar_kind_) {
1414
+ case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<i8_t, f32_t>>; break;
1415
+ case scalar_kind_t::f16_k:
1416
+ raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<f16_t, f32_t>>;
1417
+ break;
1418
+ case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<f32_t>>; break;
1419
+ case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<f64_t>>; break;
1420
+ default: raw_ptr_ = nullptr; break;
1421
+ }
1422
+ break;
1423
+ }
1424
+ case metric_kind_t::haversine_k: {
1425
+ switch (scalar_kind_) {
1426
+ case scalar_kind_t::f16_k:
1427
+ raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_haversine_gt<f16_t, f32_t>>;
1428
+ break;
1429
+ case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_haversine_gt<f32_t>>; break;
1430
+ case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_haversine_gt<f64_t>>; break;
1431
+ default: raw_ptr_ = nullptr; break;
1432
+ }
1433
+ break;
1434
+ }
1426
1435
  case metric_kind_t::jaccard_k: // Equivalent to Tanimoto
1427
1436
  case metric_kind_t::tanimoto_k:
1428
- raw_ptr_ = &equidimensional_<metric_tanimoto_gt<b1x8_t>>,
1437
+ raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_tanimoto_gt<b1x8_t>>,
1429
1438
  raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
1430
1439
  break;
1431
1440
  case metric_kind_t::hamming_k:
1432
- raw_ptr_ = &equidimensional_<metric_hamming_gt<b1x8_t>>,
1441
+ raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_hamming_gt<b1x8_t>>,
1433
1442
  raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
1434
1443
  break;
1435
1444
  case metric_kind_t::sorensen_k:
1436
- raw_ptr_ = &equidimensional_<metric_sorensen_gt<b1x8_t>>,
1445
+ raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_sorensen_gt<b1x8_t>>,
1437
1446
  raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
1438
1447
  break;
1439
1448
  default: return;
@@ -1448,134 +1457,6 @@ class metric_punned_t {
1448
1457
  (void)b_dimensions;
1449
1458
  return typed_at{}((scalar_t const*)a, (scalar_t const*)b, a_dimensions);
1450
1459
  }
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
1460
  };
1580
1461
 
1581
1462
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "2.7.3",
3
+ "version": "2.7.7",
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",
package/python/lib.cpp CHANGED
@@ -918,7 +918,7 @@ PYBIND11_MODULE(compiled, m) {
918
918
  m.def(
919
919
  "hardware_acceleration",
920
920
  [](scalar_kind_t scalar_kind, std::size_t dimensions, metric_kind_t metric_kind) -> py::str {
921
- return isa_name(metric_t(dimensions, metric_kind, scalar_kind).isa_kind());
921
+ return metric_t(dimensions, metric_kind, scalar_kind).isa_name();
922
922
  },
923
923
  py::kw_only(), //
924
924
  py::arg("dtype") = scalar_kind_t::f32_k, //
@@ -1087,9 +1087,8 @@ PYBIND11_MODULE(compiled, m) {
1087
1087
  py::arg("metric_pointer") = 0 //
1088
1088
  );
1089
1089
 
1090
- i.def_property_readonly("hardware_acceleration", [](dense_index_py_t const& index) -> py::str {
1091
- return isa_name(index.metric().isa_kind());
1092
- });
1090
+ i.def_property_readonly("hardware_acceleration",
1091
+ [](dense_index_py_t const& index) -> py::str { return index.metric().isa_name(); });
1093
1092
 
1094
1093
  i.def("contains_one", &dense_index_py_t::contains);
1095
1094
  i.def("count_one", &dense_index_py_t::count);
@@ -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 }}