usearch 0.18.2 → 0.18.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/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "usearch"
3
- version = "0.18.2"
3
+ version = "0.18.4"
4
4
  authors = ["Ashot Vardanian <1983160+ashvardanian@users.noreply.github.com>"]
5
5
  description = "Smaller & Faster Single-File Vector Search Engine from Unum"
6
6
  edition = "2021"
package/conanfile.py CHANGED
@@ -4,7 +4,7 @@ from conans import ConanFile
4
4
  class USearchConan(ConanFile):
5
5
 
6
6
  name = 'USearch'
7
- version = '0.18.2'
7
+ version = '0.18.4'
8
8
  license = 'Apache 2.0'
9
9
  url = 'https://github.com/unum-cloud/usearch'
10
10
  description = 'Smaller & Faster Single-File Vector Search Engine from Unum'
@@ -15,34 +15,38 @@
15
15
 
16
16
  // Inferring C++ version
17
17
  #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
18
- #define USEARCH_IS_CPP17
18
+ #define USEARCH_DEFINED_CPP17
19
19
  #endif
20
20
 
21
21
  // Inferring target OS
22
22
  #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
23
- #define USEARCH_IS_WINDOWS
23
+ #define USEARCH_DEFINED_WINDOWS
24
24
  #elif defined(__APPLE__) && defined(__MACH__)
25
- #define USEARCH_IS_APPLE
25
+ #define USEARCH_DEFINED_APPLE
26
26
  #elif defined(__linux__)
27
- #define USEARCH_IS_LINUX
27
+ #define USEARCH_DEFINED_LINUX
28
28
  #endif
29
29
 
30
30
  // Inferring the compiler
31
31
  #if defined(__clang__)
32
- #define USEARCH_IS_CLANG
32
+ #define USEARCH_DEFINED_CLANG
33
33
  #elif defined(__GNUC__)
34
- #define USEARCH_IS_GCC
34
+ #define USEARCH_DEFINED_GCC
35
35
  #endif
36
36
 
37
37
  // Inferring hardware architecture: x86 vs Arm
38
38
  #if defined(__x86_64__)
39
- #define USEARCH_IS_X86
39
+ #define USEARCH_DEFINED_X86
40
40
  #elif defined(__aarch64__)
41
- #define USEARCH_IS_ARM
41
+ #define USEARCH_DEFINED_ARM
42
+ #endif
43
+
44
+ #if !defined(USEARCH_USE_OPENMP)
45
+ #define USEARCH_USE_OPENMP 0
42
46
  #endif
43
47
 
44
48
  // OS-specific includes
45
- #if defined(USEARCH_IS_WINDOWS)
49
+ #if defined(USEARCH_DEFINED_WINDOWS)
46
50
  #define _USE_MATH_DEFINES
47
51
  #include <Windows.h>
48
52
  #include <sys/stat.h> // `fstat` for file size
@@ -69,20 +73,20 @@
69
73
  #include <utility> // `std::pair`
70
74
 
71
75
  // Prefetching
72
- #if defined(USEARCH_IS_GCC)
76
+ #if defined(USEARCH_DEFINED_GCC)
73
77
  // https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
74
78
  // Zero means we are only going to read from that memory.
75
79
  // Three means high temporal locality and suggests to keep
76
80
  // the data in all layers of cache.
77
81
  #define prefetch_m(ptr) __builtin_prefetch((void*)(ptr), 0, 3)
78
- #elif defined(USEARCH_IS_X86)
82
+ #elif defined(USEARCH_DEFINED_X86)
79
83
  #define prefetch_m(ptr) _mm_prefetch((void*)(ptr), _MM_HINT_T0)
80
84
  #else
81
85
  #define prefetch_m(ptr)
82
86
  #endif
83
87
 
84
88
  // Alignment
85
- #if defined(USEARCH_IS_WINDOWS)
89
+ #if defined(USEARCH_DEFINED_WINDOWS)
86
90
  #define usearch_pack_m
87
91
  #define usearch_align_m __declspec(align(64))
88
92
  #else
@@ -225,11 +229,11 @@ template <typename scalar_at = float, typename result_at = scalar_at> struct ip_
225
229
 
226
230
  inline result_t operator()(scalar_t const* a, scalar_t const* b, std::size_t dim) const noexcept {
227
231
  result_type ab{};
228
- #if defined(USEARCH_USE_OPENMP)
232
+ #if USEARCH_USE_OPENMP
229
233
  #pragma omp simd reduction(+ : ab)
230
- #elif defined(USEARCH_IS_CLANG)
234
+ #elif defined(USEARCH_DEFINED_CLANG)
231
235
  #pragma clang loop vectorize(enable)
232
- #elif defined(USEARCH_IS_GCC)
236
+ #elif defined(USEARCH_DEFINED_GCC)
233
237
  #pragma GCC ivdep
234
238
  #endif
235
239
  for (std::size_t i = 0; i != dim; ++i)
@@ -254,18 +258,18 @@ template <typename scalar_at = float, typename result_at = scalar_at> struct cos
254
258
 
255
259
  inline result_t operator()(scalar_t const* a, scalar_t const* b, std::size_t dim) const noexcept {
256
260
  result_t ab{}, a2{}, b2{};
257
- #if defined(USEARCH_USE_OPENMP)
261
+ #if USEARCH_USE_OPENMP
258
262
  #pragma omp simd reduction(+ : ab, a2, b2)
259
- #elif defined(USEARCH_IS_CLANG)
263
+ #elif defined(USEARCH_DEFINED_CLANG)
260
264
  #pragma clang loop vectorize(enable)
261
- #elif defined(USEARCH_IS_GCC)
265
+ #elif defined(USEARCH_DEFINED_GCC)
262
266
  #pragma GCC ivdep
263
267
  #endif
264
268
  for (std::size_t i = 0; i != dim; ++i)
265
269
  ab += result_t(a[i]) * result_t(b[i]), //
266
270
  a2 += square<result_t>(a[i]), //
267
271
  b2 += square<result_t>(b[i]);
268
- return ab ? (1 - ab / (std::sqrt(a2) * std::sqrt(b2))) : 0;
272
+ return (ab != 0) ? (1 - ab / (std::sqrt(a2) * std::sqrt(b2))) : 0;
269
273
  }
270
274
  };
271
275
 
@@ -283,11 +287,11 @@ template <typename scalar_at = float, typename result_at = scalar_at> struct l2s
283
287
 
284
288
  inline result_t operator()(scalar_t const* a, scalar_t const* b, std::size_t dim) const noexcept {
285
289
  result_t ab_deltas_sq{};
286
- #if defined(USEARCH_USE_OPENMP)
290
+ #if USEARCH_USE_OPENMP
287
291
  #pragma omp simd reduction(+ : ab_deltas_sq)
288
- #elif defined(USEARCH_IS_CLANG)
292
+ #elif defined(USEARCH_DEFINED_CLANG)
289
293
  #pragma clang loop vectorize(enable)
290
- #elif defined(USEARCH_IS_GCC)
294
+ #elif defined(USEARCH_DEFINED_GCC)
291
295
  #pragma GCC ivdep
292
296
  #endif
293
297
  for (std::size_t i = 0; i != dim; ++i)
@@ -313,11 +317,11 @@ template <typename scalar_at = std::uint64_t, typename result_at = std::size_t>
313
317
  inline result_t operator()(scalar_t const* a, scalar_t const* b, std::size_t words) const noexcept {
314
318
  constexpr std::size_t bits_per_word_k = sizeof(scalar_t) * CHAR_BIT;
315
319
  result_t matches{};
316
- #if defined(USEARCH_USE_OPENMP)
320
+ #if USEARCH_USE_OPENMP
317
321
  #pragma omp simd reduction(+ : matches)
318
- #elif defined(USEARCH_IS_CLANG)
322
+ #elif defined(USEARCH_DEFINED_CLANG)
319
323
  #pragma clang loop vectorize(enable)
320
- #elif defined(USEARCH_IS_GCC)
324
+ #elif defined(USEARCH_DEFINED_GCC)
321
325
  #pragma GCC ivdep
322
326
  #endif
323
327
  for (std::size_t i = 0; i != words; ++i)
@@ -344,11 +348,11 @@ template <typename scalar_at = std::uint64_t, typename result_at = float> struct
344
348
  constexpr std::size_t bits_per_word_k = sizeof(scalar_t) * CHAR_BIT;
345
349
  result_t and_count{};
346
350
  result_t or_count{};
347
- #if defined(USEARCH_USE_OPENMP)
351
+ #if USEARCH_USE_OPENMP
348
352
  #pragma omp simd reduction(+ : and_count, or_count)
349
- #elif defined(USEARCH_IS_CLANG)
353
+ #elif defined(USEARCH_DEFINED_CLANG)
350
354
  #pragma clang loop vectorize(enable)
351
- #elif defined(USEARCH_IS_GCC)
355
+ #elif defined(USEARCH_DEFINED_GCC)
352
356
  #pragma GCC ivdep
353
357
  #endif
354
358
  for (std::size_t i = 0; i != words; ++i)
@@ -376,11 +380,11 @@ template <typename scalar_at = std::uint64_t, typename result_at = float> struct
376
380
  constexpr std::size_t bits_per_word_k = sizeof(scalar_t) * CHAR_BIT;
377
381
  result_t and_count{};
378
382
  result_t any_count{};
379
- #if defined(USEARCH_USE_OPENMP)
383
+ #if USEARCH_USE_OPENMP
380
384
  #pragma omp simd reduction(+ : and_count, any_count)
381
- #elif defined(USEARCH_IS_CLANG)
385
+ #elif defined(USEARCH_DEFINED_CLANG)
382
386
  #pragma clang loop vectorize(enable)
383
- #elif defined(USEARCH_IS_GCC)
387
+ #elif defined(USEARCH_DEFINED_GCC)
384
388
  #pragma GCC ivdep
385
389
  #endif
386
390
  for (std::size_t i = 0; i != words; ++i)
@@ -437,11 +441,11 @@ template <typename scalar_at = float, typename result_at = float> struct pearson
437
441
  inline result_t operator()(scalar_t const* a, scalar_t const* b, std::size_t dim) const noexcept {
438
442
  result_t a_sum{}, b_sum{}, ab_sum{};
439
443
  result_t a_sq_sum{}, b_sq_sum{};
440
- #if defined(USEARCH_USE_OPENMP)
444
+ #if USEARCH_USE_OPENMP
441
445
  #pragma omp simd reduction(+ : a_sum, b_sum, ab_sum, a_sq_sum, b_sq_sum)
442
- #elif defined(USEARCH_IS_CLANG)
446
+ #elif defined(USEARCH_DEFINED_CLANG)
443
447
  #pragma clang loop vectorize(enable)
444
- #elif defined(USEARCH_IS_GCC)
448
+ #elif defined(USEARCH_DEFINED_GCC)
445
449
  #pragma GCC ivdep
446
450
  #endif
447
451
  for (std::size_t i = 0; i != dim; ++i) {
@@ -603,7 +607,7 @@ template <typename allocator_at = std::allocator<char>> class visits_bitset_gt {
603
607
  inline bool test(std::size_t i) const noexcept { return slots_[i / bits_per_slot()] & (1ul << (i & bits_mask())); }
604
608
  inline void set(std::size_t i) noexcept { slots_[i / bits_per_slot()] |= (1ul << (i & bits_mask())); }
605
609
 
606
- #if defined(USEARCH_IS_WINDOWS)
610
+ #if defined(USEARCH_DEFINED_WINDOWS)
607
611
 
608
612
  inline bool atomic_set(std::size_t i) noexcept {
609
613
  slot_t mask{1ul << (i & bits_mask())};
@@ -890,7 +894,7 @@ class sorted_buffer_gt {
890
894
  static bool less(element_t const& a, element_t const& b) noexcept { return comparator_t{}(a, b); }
891
895
  };
892
896
 
893
- #if defined(USEARCH_IS_WINDOWS)
897
+ #if defined(USEARCH_DEFINED_WINDOWS)
894
898
  #pragma pack(push, 1) // Pack struct elements on 1-byte alignment
895
899
  #endif
896
900
 
@@ -904,7 +908,7 @@ class usearch_pack_m uint40_t {
904
908
  inline uint40_t() noexcept { std::memset(octets, 0, 5); }
905
909
  inline uint40_t(std::uint32_t n) noexcept { std::memcpy(octets + 1, (char*)&n, 4), octets[0] = 0; }
906
910
  inline uint40_t(std::uint64_t n) noexcept { std::memcpy(octets, (char*)&n + 3, 5); }
907
- #if defined(USEARCH_IS_CLANG) && defined(USEARCH_IS_APPLE)
911
+ #if defined(USEARCH_DEFINED_CLANG) && defined(USEARCH_DEFINED_APPLE)
908
912
  inline uint40_t(std::size_t n) noexcept { std::memcpy(octets, (char*)&n + 3, 5); }
909
913
  #endif
910
914
 
@@ -935,7 +939,7 @@ class usearch_pack_m uint40_t {
935
939
  }
936
940
  };
937
941
 
938
- #if defined(USEARCH_IS_WINDOWS)
942
+ #if defined(USEARCH_DEFINED_WINDOWS)
939
943
  #pragma pack(pop) // Reset alignment to default
940
944
  #endif
941
945
 
@@ -1093,7 +1097,7 @@ static_assert(sizeof(file_header_t) == 64, "File header should be exactly 64 byt
1093
1097
  /// @brief C++17 and newer version deprecate the `std::result_of`
1094
1098
  template <typename metric_at, typename... args_at>
1095
1099
  using return_type_gt =
1096
- #if defined(USEARCH_IS_CPP17)
1100
+ #if defined(USEARCH_DEFINED_CPP17)
1097
1101
  typename std::invoke_result<metric_at, args_at...>::type;
1098
1102
  #else
1099
1103
  typename std::result_of<metric_at(args_at...)>::type;
@@ -2034,7 +2038,7 @@ class index_gt {
2034
2038
  */
2035
2039
  serialization_result_t view(char const* file_path) noexcept {
2036
2040
  serialization_result_t result;
2037
- #if defined(USEARCH_IS_WINDOWS)
2041
+ #if defined(USEARCH_DEFINED_WINDOWS)
2038
2042
  return result.failed("Memory-mapping is not yet available for Windows");
2039
2043
  #else
2040
2044
  int open_flags = O_RDONLY;
@@ -7,16 +7,11 @@
7
7
  #include <thread> // `std::thread`
8
8
  #include <vector> // `std::vector`
9
9
 
10
- #include <fp16/fp16.h>
11
- #include <tsl/robin_map.h>
12
-
13
- #if defined(USEARCH_USE_SIMSIMD)
14
- #include <simsimd/simsimd.h>
15
- #endif
16
-
17
10
  #include <usearch/index.hpp>
18
11
  #include <usearch/index_punned_helpers.hpp>
19
12
 
13
+ #include <tsl/robin_map.h>
14
+
20
15
  namespace unum {
21
16
  namespace usearch {
22
17
 
@@ -27,6 +22,13 @@ struct cos_f8_t {
27
22
  inline cos_f8_t(std::size_t dims) noexcept : dimensions(dims) {}
28
23
  inline punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b) const noexcept {
29
24
  std::int32_t ab{}, a2{}, b2{};
25
+ #if USEARCH_USE_OPENMP
26
+ #pragma omp simd reduction(+ : ab, a2, b2)
27
+ #elif defined(USEARCH_DEFINED_CLANG)
28
+ #pragma clang loop vectorize(enable)
29
+ #elif defined(USEARCH_DEFINED_GCC)
30
+ #pragma GCC ivdep
31
+ #endif
30
32
  for (std::size_t i = 0; i != dimensions; i++) {
31
33
  std::int16_t ai{a[i]};
32
34
  std::int16_t bi{b[i]};
@@ -34,7 +36,7 @@ struct cos_f8_t {
34
36
  a2 += square(ai);
35
37
  b2 += square(bi);
36
38
  }
37
- return 1.f - ab / (std::sqrt(a2) * std::sqrt(b2));
39
+ return (ab != 0) ? (1.f - ab / (std::sqrt(a2) * std::sqrt(b2))) : 0;
38
40
  }
39
41
  };
40
42
 
@@ -45,6 +47,13 @@ struct l2sq_f8_t {
45
47
  inline l2sq_f8_t(std::size_t dims) noexcept : dimensions(dims) {}
46
48
  inline punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b) const noexcept {
47
49
  std::int32_t ab_deltas_sq{};
50
+ #if USEARCH_USE_OPENMP
51
+ #pragma omp simd reduction(+ : ab_deltas_sq)
52
+ #elif defined(USEARCH_DEFINED_CLANG)
53
+ #pragma clang loop vectorize(enable)
54
+ #elif defined(USEARCH_DEFINED_GCC)
55
+ #pragma GCC ivdep
56
+ #endif
48
57
  for (std::size_t i = 0; i != dimensions; i++)
49
58
  ab_deltas_sq += square(std::int16_t(a[i]) - std::int16_t(b[i]));
50
59
  return ab_deltas_sq;
@@ -495,70 +504,62 @@ class index_punned_dense_gt {
495
504
 
496
505
  static metric_and_meta_t ip_metric_f32_(std::size_t dimensions) {
497
506
  (void)dimensions;
498
- #if defined(USEARCH_USE_SIMSIMD)
499
- #if defined(USEARCH_IS_X86)
500
- if (dimensions % 4 == 0)
507
+ #if USEARCH_USE_SIMSIMD
508
+ if (hardware_supports(isa_t::avx2_k) && dimensions % 4 == 0)
501
509
  return {
502
510
  pun_metric_<simsimd_f32_t>([=](simsimd_f32_t const* a, simsimd_f32_t const* b) {
503
511
  return 1.f - simsimd_dot_f32x4avx2(a, b, dimensions);
504
512
  }),
505
513
  isa_t::avx2_k,
506
514
  };
507
- #elif defined(USEARCH_IS_ARM)
508
- if (supports_arm_sve())
515
+ if (hardware_supports(isa_t::sve_k))
509
516
  return {
510
517
  pun_metric_<simsimd_f32_t>([=](simsimd_f32_t const* a, simsimd_f32_t const* b) {
511
518
  return 1.f - simsimd_dot_f32sve(a, b, dimensions);
512
519
  }),
513
520
  isa_t::sve_k,
514
521
  };
515
- if (dimensions % 4 == 0)
522
+ if (hardware_supports(isa_t::neon_k) && dimensions % 4 == 0)
516
523
  return {
517
524
  pun_metric_<simsimd_f32_t>([=](simsimd_f32_t const* a, simsimd_f32_t const* b) {
518
525
  return 1.f - simsimd_dot_f32x4neon(a, b, dimensions);
519
526
  }),
520
527
  isa_t::neon_k,
521
528
  };
522
- #endif
523
529
  #endif
524
530
  return pun_metric_<f32_t>(ip_gt<f32_t>{});
525
531
  }
526
532
 
527
533
  static metric_and_meta_t cos_metric_f16_(std::size_t dimensions) {
528
534
  (void)dimensions;
529
- #if defined(USEARCH_USE_SIMSIMD)
530
- #if defined(USEARCH_IS_X86)
531
- if (dimensions % 16 == 0)
535
+ #if USEARCH_USE_SIMSIMD
536
+ if (hardware_supports(isa_t::avx512_k) && dimensions % 16 == 0)
532
537
  return {
533
538
  pun_metric_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) {
534
539
  return 1.f - simsimd_cos_f16x16avx512(a, b, dimensions);
535
540
  }),
536
541
  isa_t::avx512_k,
537
542
  };
538
- #elif defined(USEARCH_IS_ARM)
539
- if (dimensions % 4 == 0)
543
+ if (hardware_supports(isa_t::neon_k) && dimensions % 4 == 0)
540
544
  return {
541
545
  pun_metric_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) {
542
546
  return 1.f - simsimd_cos_f16x4neon(a, b, dimensions);
543
547
  }),
544
548
  isa_t::neon_k,
545
549
  };
546
- #endif
547
550
  #endif
548
551
  return pun_metric_<f16_t>(cos_gt<f16_t, f32_t>{});
549
552
  }
550
553
 
551
554
  static metric_and_meta_t cos_metric_f8_(std::size_t dimensions) {
552
555
  (void)dimensions;
553
- #if defined(USEARCH_USE_SIMSIMD)
554
- #if defined(USEARCH_IS_ARM)
555
- if (dimensions % 16 == 0)
556
+ #if USEARCH_USE_SIMSIMD
557
+ if (hardware_supports(isa_t::neon_k) && dimensions % 16 == 0)
556
558
  return {
557
559
  pun_metric_<int8_t>(
558
560
  [=](int8_t const* a, int8_t const* b) { return 1.f - simsimd_cos_i8x16neon(a, b, dimensions); }),
559
561
  isa_t::neon_k,
560
562
  };
561
- #endif
562
563
  #endif
563
564
  return pun_metric_<f8_bits_t>(cos_f8_t{dimensions});
564
565
  }
@@ -6,21 +6,41 @@
6
6
  #include <thread> // `std::thread`
7
7
  #include <vector> // `std::vector`
8
8
 
9
- #include <fp16/fp16.h>
9
+ #include <usearch/index.hpp> // `expected_gt` and macros
10
10
 
11
- #if defined(USEARCH_USE_OPENMP)
11
+ #if USEARCH_USE_OPENMP
12
12
  #include <omp.h> // `omp_get_num_threads()`
13
13
  #endif
14
14
 
15
- #if defined(USEARCH_IS_LINUX)
15
+ #if defined(USEARCH_DEFINED_LINUX)
16
16
  #include <sys/auxv.h> // `getauxval()`
17
17
  #endif
18
18
 
19
- #if defined(USEARCH_IS_ARM)
19
+ #if defined(USEARCH_DEFINED_ARM)
20
20
  #include <arm_fp16.h> // `__fp16`
21
21
  #endif
22
22
 
23
- #include <usearch/index.hpp> // `expected_gt`
23
+ #if !defined(USEARCH_USE_NATIVE_F16)
24
+ #if defined(__AVX512F__)
25
+ #define USEARCH_USE_NATIVE_F16 1
26
+ #elif defined(USEARCH_DEFINED_ARM)
27
+ #define USEARCH_USE_NATIVE_F16 1
28
+ #else
29
+ #define USEARCH_USE_NATIVE_F16 0
30
+ #include <fp16/fp16.h>
31
+ #endif
32
+ #else
33
+ #define USEARCH_USE_NATIVE_F16 0
34
+ #include <fp16/fp16.h>
35
+ #endif
36
+
37
+ #if !defined(USEARCH_USE_SIMSIMD)
38
+ #define USEARCH_USE_SIMSIMD 0
39
+ #endif
40
+
41
+ #if USEARCH_USE_SIMSIMD
42
+ #include <simsimd/simsimd.h>
43
+ #endif
24
44
 
25
45
  namespace unum {
26
46
  namespace usearch {
@@ -56,13 +76,23 @@ inline char const* isa_name(isa_t isa) noexcept {
56
76
  }
57
77
  }
58
78
 
59
- inline bool supports_arm_sve() noexcept {
60
- #if defined(USEARCH_IS_ARM)
61
- #if defined(USEARCH_IS_LINUX)
79
+ inline bool hardware_supports(isa_t isa) noexcept {
80
+ #if defined(USEARCH_DEFINED_ARM) && defined(USEARCH_DEFINED_LINUX)
62
81
  unsigned long capabilities = getauxval(AT_HWCAP);
63
- if (capabilities & HWCAP_SVE)
64
- return true;
82
+ switch (isa) {
83
+ case isa_t::neon_k: return capabilities & HWCAP_NEON;
84
+ case isa_t::sve_k: return capabilities & HWCAP_SVE;
85
+ default: return false;
86
+ }
65
87
  #endif
88
+
89
+ #if defined(USEARCH_DEFINED_X86) && defined(USEARCH_DEFINED_GCC)
90
+ __builtin_cpu_init();
91
+ switch (isa) {
92
+ case isa_t::avx2_k: return __builtin_cpu_supports("avx2");
93
+ case isa_t::avx512_k: return __builtin_cpu_supports("avx512f");
94
+ default: return false;
95
+ }
66
96
  #endif
67
97
  return false;
68
98
  }
@@ -161,26 +191,16 @@ using punned_metric_t = std::function<punned_distance_t(punned_vector_view_t, pu
161
191
  class f8_bits_t;
162
192
  class f16_bits_t;
163
193
 
164
- // using f16_native_t = __bf16;
165
-
166
- #if defined(__AVX512F__)
167
- #define USEARCH_F16_NATIVE_SUPPORT 1
194
+ #if USEARCH_USE_NATIVE_F16
168
195
  using f16_native_t = _Float16;
169
- #elif defined(USEARCH_IS_ARM)
170
- #define USEARCH_F16_NATIVE_SUPPORT 1
171
- using f16_native_t = __fp16;
172
- #else
173
- using f16_native_t = void;
174
- #endif
175
-
176
- #if defined(USEARCH_F16_NATIVE_SUPPORT)
177
196
  using f16_t = f16_native_t;
178
197
  #else
198
+ using f16_native_t = void;
179
199
  using f16_t = f16_bits_t;
180
200
  #endif
181
201
 
182
202
  inline float f16_to_f32(std::uint16_t u16) noexcept {
183
- #if defined(USEARCH_F16_NATIVE_SUPPORT)
203
+ #if USEARCH_USE_NATIVE_F16
184
204
  f16_native_t f16;
185
205
  std::memcpy(&f16, &u16, sizeof(std::uint16_t));
186
206
  return float(f16);
@@ -190,7 +210,7 @@ inline float f16_to_f32(std::uint16_t u16) noexcept {
190
210
  }
191
211
 
192
212
  inline std::uint16_t f32_to_f16(float f32) noexcept {
193
- #if defined(USEARCH_F16_NATIVE_SUPPORT)
213
+ #if USEARCH_USE_NATIVE_F16
194
214
  f16_native_t f16 = f16_native_t(f32);
195
215
  std::uint16_t u16;
196
216
  std::memcpy(&u16, &f16, sizeof(std::uint16_t));
@@ -316,7 +336,7 @@ class executor_stl_t {
316
336
  }
317
337
  };
318
338
 
319
- #if defined(USEARCH_USE_OPENMP)
339
+ #if USEARCH_USE_OPENMP
320
340
 
321
341
  class executor_openmp_t {
322
342
  public:
@@ -362,7 +382,7 @@ class aligned_allocator_gt {
362
382
  // void* result = nullptr;
363
383
  // int status = posix_memalign(&result, alignment, length_bytes);
364
384
  // return status == 0 ? (pointer)result : nullptr;
365
- #if defined(USEARCH_IS_WINDOWS)
385
+ #if defined(USEARCH_DEFINED_WINDOWS)
366
386
  return (pointer)_aligned_malloc(length_bytes, alignment);
367
387
  #else
368
388
  return (pointer)aligned_alloc(alignment, length_bytes);
@@ -370,7 +390,7 @@ class aligned_allocator_gt {
370
390
  }
371
391
 
372
392
  void deallocate(pointer begin, size_type) const {
373
- #if defined(USEARCH_IS_WINDOWS)
393
+ #if defined(USEARCH_DEFINED_WINDOWS)
374
394
  _aligned_free(begin);
375
395
  #else
376
396
  free(begin);
@@ -380,7 +400,7 @@ class aligned_allocator_gt {
380
400
 
381
401
  using aligned_allocator_t = aligned_allocator_gt<>;
382
402
 
383
- #if !defined(USEARCH_IS_WINDOWS)
403
+ #if !defined(USEARCH_DEFINED_WINDOWS)
384
404
 
385
405
  /**
386
406
  * @brief Memory-mapping allocator designed for "alloc many, free at once" usage patterns.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "0.18.2",
3
+ "version": "0.18.4",
4
4
  "description": "Smaller & Faster Single-File Vector Search Engine from Unum",
5
5
  "author": "Ashot Vardanian",
6
6
  "license": "Apache 2.0",
package/python/lib.cpp CHANGED
@@ -13,6 +13,7 @@
13
13
  * @copyright Copyright (c) 2023
14
14
  */
15
15
  #define PY_SSIZE_T_CLEAN
16
+ #define NOMINMAX // Some of our dependencies call `std::max(x, y)`, which crashes Windows builds
16
17
  #include <thread>
17
18
 
18
19
  #include <pybind11/numpy.h>
@@ -533,17 +534,9 @@ PYBIND11_MODULE(compiled, m) {
533
534
  m.attr("DEFAULT_EXPANSION_ADD") = py::int_(default_expansion_add());
534
535
  m.attr("DEFAULT_EXPANSION_SEARCH") = py::int_(default_expansion_search());
535
536
 
536
- #if defined(USEARCH_USE_OPENMP)
537
- m.attr("USES_OPENMP") = py::int_(1);
538
- #else
539
- m.attr("USES_OPENMP") = py::int_(0);
540
- #endif
541
-
542
- #if defined(USEARCH_USE_SIMSIMD)
543
- m.attr("USES_SIMSIMD") = py::int_(1);
544
- #else
545
- m.attr("USES_SIMSIMD") = py::int_(0);
546
- #endif
537
+ m.attr("USES_OPENMP") = py::int_(USEARCH_USE_OPENMP);
538
+ m.attr("USES_SIMSIMD") = py::int_(USEARCH_USE_SIMSIMD);
539
+ m.attr("USES_NATIVE_F16") = py::int_(USEARCH_USE_NATIVE_F16);
547
540
 
548
541
  py::enum_<metric_kind_t>(m, "MetricKind")
549
542
  .value("Unknown", metric_kind_t::unknown_k)
@@ -19,6 +19,7 @@ from usearch.compiled import (
19
19
 
20
20
  USES_OPENMP,
21
21
  USES_SIMSIMD,
22
+ USES_NATIVE_F16,
22
23
  )
23
24
 
24
25
  MetricKindBitwise = (
@@ -337,6 +338,7 @@ class Index:
337
338
  'Expansion@Search': self.expansion_search,
338
339
  'OpenMP': USES_OPENMP,
339
340
  'SimSIMD': USES_SIMSIMD,
341
+ 'NativeF16': USES_NATIVE_F16,
340
342
  'JIT': self.jit,
341
343
  'DType': self.dtype,
342
344
  'Path': self.path,
package/setup.py CHANGED
@@ -7,13 +7,16 @@ from pybind11.setup_helpers import Pybind11Extension
7
7
 
8
8
  compile_args = ['-std=c++11', '-O3']
9
9
  link_args = []
10
- macros_args = []
10
+ macros_args = [
11
+ ('USEARCH_USE_NATIVE_F16', '0'),
12
+ ('USEARCH_USE_SIMSIMD', '0'),
13
+ ]
11
14
 
12
15
  if sys.platform == 'linux':
13
16
  compile_args.append('-fopenmp')
14
17
  link_args.append('-lgomp')
15
- macros_args.append(('USEARCH_USE_OPENMP', '1'))
16
18
  compile_args.append('-Wno-unknown-pragmas')
19
+ macros_args.append(('USEARCH_USE_OPENMP', '1'))
17
20
 
18
21
  if sys.platform == 'darwin':
19
22
  # MacOS 10.15 or higher is needed for `aligned_alloc` support.