usearch 2.1.3 → 2.2.0

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 = "2.1.3"
3
+ version = "2.2.0"
4
4
  authors = ["Ash 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 = '2.1.3'
7
+ version = '2.2.0'
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'
@@ -42,13 +42,9 @@
42
42
 
43
43
  #if USEARCH_USE_SIMSIMD
44
44
  #if __linux__
45
- #define SIMSIMD_TARGET_ARM_NEON 1
46
- #define SIMSIMD_TARGET_X86_AVX2 1
47
- #define SIMSIMD_TARGET_ARM_SVE 1
48
- #define SIMSIMD_TARGET_X86_AVX512 1
49
- #endif
50
45
  #include <simsimd/simsimd.h>
51
46
  #endif
47
+ #endif
52
48
 
53
49
  namespace unum {
54
50
  namespace usearch {
@@ -133,7 +129,9 @@ enum class isa_kind_t {
133
129
  neon_k,
134
130
  sve_k,
135
131
  avx2_k,
132
+ avx2f16_k,
136
133
  avx512_k,
134
+ avx512f16_k,
137
135
  };
138
136
 
139
137
  enum class prefetching_kind_t {
@@ -199,6 +197,8 @@ inline char const* isa_name(isa_kind_t isa_kind) noexcept {
199
197
  case isa_kind_t::sve_k: return "sve";
200
198
  case isa_kind_t::avx2_k: return "avx2";
201
199
  case isa_kind_t::avx512_k: return "avx512";
200
+ case isa_kind_t::avx2f16_k: return "avx2+f16";
201
+ case isa_kind_t::avx512f16_k: return "avx512+f16";
202
202
  default: return "";
203
203
  }
204
204
  }
@@ -221,6 +221,13 @@ inline bool hardware_supports(isa_kind_t isa_kind) noexcept {
221
221
  #if defined(USEARCH_DEFINED_X86) && defined(USEARCH_DEFINED_GCC)
222
222
  __builtin_cpu_init();
223
223
  switch (isa_kind) {
224
+ // Half-precision is only supported in newer versions of GCC
225
+ #if __GNUC__ > 11 || (__GNUC__ == 11 && __GNUC_MINOR__ >= 0)
226
+ case isa_kind_t::avx2f16_k: return __builtin_cpu_supports("avx2") && __builtin_cpu_supports("f16c");
227
+ #endif
228
+ #if __GNUC__ > 12 || (__GNUC__ == 12 && __GNUC_MINOR__ >= 0)
229
+ case isa_kind_t::avx512f16_k: return __builtin_cpu_supports("avx512f") && __builtin_cpu_supports("avx512fp16");
230
+ #endif
224
231
  case isa_kind_t::avx2_k: return __builtin_cpu_supports("avx2");
225
232
  case isa_kind_t::avx512_k: return __builtin_cpu_supports("avx512f");
226
233
  default: return false;
@@ -1404,15 +1411,36 @@ class metric_punned_t {
1404
1411
  static metric_punned_t cos_metric_f16_(std::size_t bytes_per_vector) {
1405
1412
  #if USEARCH_USE_SIMSIMD
1406
1413
  #if SIMSIMD_TARGET_X86_AVX512
1407
- if (hardware_supports(isa_kind_t::avx512_k) && bytes_per_vector % 32 == 0) return {pun_stl_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) { return simsimd_cos_f16x16_avx512(a, b, bytes_per_vector / 2); }), bytes_per_vector, metric_kind_t::cos_k, scalar_kind_t::f16_k, isa_kind_t::avx512_k};
1414
+ if (hardware_supports(isa_kind_t::avx512f16_k) && bytes_per_vector % 32 == 0) return {pun_stl_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) { return simsimd_cos_f16x16_avx512(a, b, bytes_per_vector / 2); }), bytes_per_vector, metric_kind_t::cos_k, scalar_kind_t::f16_k, isa_kind_t::avx512f16_k};
1415
+ #endif
1416
+ #if SIMSIMD_TARGET_X86_AVX2
1417
+ if (hardware_supports(isa_kind_t::avx2f16_k) && bytes_per_vector % 16 == 0) return {pun_stl_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) { return simsimd_cos_f16x8_avx2(a, b, bytes_per_vector / 2); }), bytes_per_vector, metric_kind_t::cos_k, scalar_kind_t::f16_k, isa_kind_t::avx2f16_k};
1408
1418
  #endif
1409
1419
  #if SIMSIMD_TARGET_ARM_NEON
1410
1420
  if (hardware_supports(isa_kind_t::neon_k) && bytes_per_vector % 8 == 0) return {pun_stl_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) { return simsimd_cos_f16x4_neon(a, b, bytes_per_vector / 2); }), bytes_per_vector, metric_kind_t::cos_k, scalar_kind_t::f16_k, isa_kind_t::neon_k};
1411
1421
  #endif
1422
+ #if SIMSIMD_TARGET_ARM_SVE
1423
+ if (hardware_supports(isa_kind_t::sve_k)) return {pun_stl_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) { return simsimd_cos_f16_sve(a, b, bytes_per_vector / 4); }), bytes_per_vector, metric_kind_t::cos_k, scalar_kind_t::f16_k, isa_kind_t::sve_k};
1424
+ #endif
1412
1425
  #endif
1413
1426
  return {to_stl_<metric_cos_gt<f16_t, f32_t>>(bytes_per_vector), bytes_per_vector, metric_kind_t::cos_k, scalar_kind_t::f16_k, isa_kind_t::auto_k};
1414
1427
  }
1415
1428
 
1429
+ static metric_punned_t l2sq_metric_f16_(std::size_t bytes_per_vector) {
1430
+ #if USEARCH_USE_SIMSIMD
1431
+ #if SIMSIMD_TARGET_X86_AVX512
1432
+ if (hardware_supports(isa_kind_t::avx512f16_k) && bytes_per_vector % 32 == 0) return {pun_stl_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) { return simsimd_l2sq_f16x16_avx512(a, b, bytes_per_vector / 2); }), bytes_per_vector, metric_kind_t::cos_k, scalar_kind_t::f16_k, isa_kind_t::avx512f16_k};
1433
+ #endif
1434
+ #if SIMSIMD_TARGET_X86_AVX2
1435
+ if (hardware_supports(isa_kind_t::avx2f16_k) && bytes_per_vector % 16 == 0) return {pun_stl_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) { return simsimd_l2sq_f16x8_avx2(a, b, bytes_per_vector / 2); }), bytes_per_vector, metric_kind_t::cos_k, scalar_kind_t::f16_k, isa_kind_t::avx2f16_k};
1436
+ #endif
1437
+ #if SIMSIMD_TARGET_ARM_SVE
1438
+ if (hardware_supports(isa_kind_t::sve_k)) return {pun_stl_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) { return simsimd_l2sq_f16_sve(a, b, bytes_per_vector / 4); }), bytes_per_vector, metric_kind_t::l2sq_k, scalar_kind_t::f16_k, isa_kind_t::sve_k};
1439
+ #endif
1440
+ #endif
1441
+ return {to_stl_<metric_l2sq_gt<f16_t, f32_t>>(bytes_per_vector), bytes_per_vector, metric_kind_t::l2sq_k, scalar_kind_t::f16_k, isa_kind_t::auto_k};
1442
+ }
1443
+
1416
1444
  static metric_punned_t cos_metric_i8_(std::size_t bytes_per_vector) {
1417
1445
  #if USEARCH_USE_SIMSIMD
1418
1446
  #if SIMSIMD_TARGET_ARM_NEON
@@ -1435,7 +1463,7 @@ class metric_punned_t {
1435
1463
  static metric_punned_t l2sq_metric_(std::size_t bytes_per_vector, scalar_kind_t scalar_kind) {
1436
1464
  switch (scalar_kind) {
1437
1465
  case scalar_kind_t::i8_k: return {to_stl_<l2sq_i8_t>(bytes_per_vector), bytes_per_vector, metric_kind_t::l2sq_k, scalar_kind_t::i8_k, isa_kind_t::auto_k};
1438
- case scalar_kind_t::f16_k: return {to_stl_<metric_l2sq_gt<f16_t, f32_t>>(bytes_per_vector), bytes_per_vector, metric_kind_t::l2sq_k, scalar_kind_t::f16_k, isa_kind_t::auto_k};
1466
+ case scalar_kind_t::f16_k: return l2sq_metric_f16_(bytes_per_vector);
1439
1467
  case scalar_kind_t::f32_k: return {to_stl_<metric_l2sq_gt<f32_t>>(bytes_per_vector), bytes_per_vector, metric_kind_t::l2sq_k, scalar_kind_t::f32_k, isa_kind_t::auto_k};
1440
1468
  case scalar_kind_t::f64_k: return {to_stl_<metric_l2sq_gt<f64_t>>(bytes_per_vector), bytes_per_vector, metric_kind_t::l2sq_k, scalar_kind_t::f64_k, isa_kind_t::auto_k};
1441
1469
  default: return {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "2.1.3",
3
+ "version": "2.2.0",
4
4
  "description": "Smaller & Faster Single-File Vector Search Engine from Unum",
5
5
  "author": "Ash Vardanian",
6
6
  "license": "Apache 2.0",
package/python/lib.cpp CHANGED
@@ -957,6 +957,17 @@ PYBIND11_MODULE(compiled, m) {
957
957
  py::arg("metric_pointer") = 0 //
958
958
  );
959
959
 
960
+ m.def(
961
+ "hardware_acceleration",
962
+ [](scalar_kind_t scalar_kind, std::size_t dimensions, metric_kind_t metric_kind) -> bool {
963
+ return metric_t(dimensions, metric_kind, scalar_kind).isa_kind() != isa_kind_t::auto_k;
964
+ },
965
+ py::kw_only(), //
966
+ py::arg("dtype") = scalar_kind_t::f32_k, //
967
+ py::arg("ndim") = 0, //
968
+ py::arg("metric_kind") = metric_kind_t::cos_k //
969
+ );
970
+
960
971
  auto i = py::class_<dense_index_py_t, std::shared_ptr<dense_index_py_t>>(m, "Index");
961
972
 
962
973
  i.def(py::init(&make_index), //
@@ -29,6 +29,7 @@ from usearch.compiled import (
29
29
  index_dense_metadata_from_path as _index_dense_metadata_from_path,
30
30
  index_dense_metadata_from_buffer as _index_dense_metadata_from_buffer,
31
31
  exact_search as _exact_search,
32
+ hardware_acceleration as _hardware_acceleration,
32
33
  )
33
34
 
34
35
  # Precompiled symbols that will be exposed
@@ -72,9 +73,17 @@ NoneType = type(None)
72
73
  PathOrBuffer = Union[str, os.PathLike, bytes]
73
74
 
74
75
 
75
- def _normalize_dtype(dtype, metric: MetricKind = MetricKind.Cos) -> ScalarKind:
76
+ def _normalize_dtype(
77
+ dtype,
78
+ ndim: int = 0,
79
+ metric: MetricKind = MetricKind.Cos,
80
+ ) -> ScalarKind:
76
81
  if dtype is None or dtype == "":
77
- return ScalarKind.B1 if metric in MetricKindBitwise else ScalarKind.F32
82
+ if metric in MetricKindBitwise:
83
+ return ScalarKind.B1
84
+ if _hardware_acceleration(dtype=ScalarKind.F16, ndim=ndim, metric_kind=metric):
85
+ return ScalarKind.F16
86
+ return ScalarKind.F32
78
87
 
79
88
  if isinstance(dtype, ScalarKind):
80
89
  return dtype
@@ -491,8 +500,12 @@ class Index:
491
500
  :type dtype: Optional[DTypeLike], defaults to None
492
501
  For continuous metrics can be: f16, f32, f64, or i8.
493
502
  For bitwise metrics it's implementation-defined, and can't change.
503
+ If nothing is provided, the optimal data type is selected based on the metric
504
+ kind and hardware support.
494
505
  Example: you can use the `f16` index with `f32` vectors in Euclidean space,
495
- which will be automatically downcasted.
506
+ which will be automatically downcasted. Moreover, if `dtype=None` is passed,
507
+ and hardware supports `f16` SIMD-instructions, this choice will be done for you.
508
+ You can later double-check the used representation with `index.dtype`.
496
509
 
497
510
  :param connectivity: Connections per node in HNSW
498
511
  :type connectivity: Optional[int], defaults to None
@@ -548,7 +561,7 @@ class Index:
548
561
  )
549
562
 
550
563
  # Validate, that the right scalar type is defined
551
- dtype = _normalize_dtype(dtype, self._metric_kind)
564
+ dtype = _normalize_dtype(dtype, ndim, self._metric_kind)
552
565
  self._compiled = _CompiledIndex(
553
566
  ndim=ndim,
554
567
  dtype=dtype,
@@ -23,7 +23,7 @@ jobs:
23
23
  strategy:
24
24
  fail-fast: false
25
25
  matrix:
26
- os: [ubuntu-22.04] # macOS-11, windows-2022
26
+ os: [ubuntu-22.04, macOS-11] # windows-2022
27
27
  python-version: ["3.11"]
28
28
 
29
29
  steps:
@@ -35,7 +35,7 @@ jobs:
35
35
  python-version: ${{ matrix.python-version }}
36
36
 
37
37
  - name: Set up Clang
38
- run: brew install llvm
38
+ run: brew install gcc@12
39
39
  if: ${{ matrix.os == 'macOS-11' }}
40
40
 
41
41
  - name: Set up MinGW
@@ -59,10 +59,8 @@ jobs:
59
59
 
60
60
  - name: Build locally on MacOS
61
61
  run: |
62
- export LDFLAGS="-L/usr/local/opt/llvm/lib"
63
- export CPPFLAGS="-I/usr/local/opt/llvm/include"
64
- export CC=/usr/local/opt/llvm/bin/clang
65
- export CXX=/usr/local/opt/llvm/bin/clang++
62
+ export CC=/usr/local/bin/gcc-12
63
+ export CXX=/usr/local/bin/g++-12
66
64
  python -m pip install .
67
65
  if: ${{ matrix.os == 'macOS-11' }}
68
66
 
@@ -57,8 +57,7 @@ jobs:
57
57
  needs: versioning
58
58
  strategy:
59
59
  matrix:
60
- os: [ubuntu-20.04] # macOS-11, windows-2022
61
-
60
+ os: [ubuntu-20.04, macOS-11] # windows-2022
62
61
  steps:
63
62
  - uses: actions/checkout@v3
64
63
  with:
@@ -76,8 +75,15 @@ jobs:
76
75
  - name: Install CIBuildWheel
77
76
  run: python -m pip install --no-cache-dir cibuildwheel
78
77
 
79
- - name: Build wheels
78
+ - name: Build wheels on Ubuntu
79
+ run: python -m cibuildwheel
80
+ if: matrix.os == 'ubuntu-20.04'
81
+
82
+ - name: Build wheels on MacOS
80
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'
81
87
 
82
88
  - uses: actions/upload-artifact@v3
83
89
  with:
@@ -160,7 +160,7 @@ simsimd_l2sq_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
160
160
  pg_vec = svwhilelt_b32(i, d);
161
161
  } while (svptest_any(svptrue_b32(), pg_vec));
162
162
  simsimd_f32_t d2 = svaddv_f32(svptrue_b32(), d2_vec);
163
- return sqrt(d2);
163
+ return d2;
164
164
  }
165
165
 
166
166
  __attribute__((target("+sve+fp16"))) inline static simsimd_f32_t //
@@ -308,7 +308,7 @@ simsimd_l2sq_i8x16_neon(int8_t const* a, int8_t const* b, size_t d) {
308
308
 
309
309
  int32x2_t d2_part = vadd_s32(vget_high_s32(d2_vec), vget_low_s32(d2_vec));
310
310
  int32_t d2 = vget_lane_s32(vpadd_s32(d2_part, d2_part), 0);
311
- return sqrt(d2);
311
+ return d2;
312
312
  }
313
313
 
314
314
  __attribute__((target("+simd"))) inline static simsimd_f32_t //
@@ -349,7 +349,7 @@ simsimd_hamming_b1x128_sve(uint8_t const* a, uint8_t const* b, size_t d) {
349
349
  #if SIMSIMD_TARGET_X86
350
350
  #if SIMSIMD_TARGET_X86_AVX2
351
351
 
352
- __attribute__((target("avx2"))) //
352
+ __attribute__((target("avx2"))) //
353
353
  __attribute__((target("fma"))) inline static simsimd_f32_t //
354
354
  simsimd_dot_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
355
355
  __m128 ab_vec = _mm_set1_ps(0);
@@ -362,7 +362,7 @@ simsimd_dot_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d)
362
362
  return 1 - ab_union.f;
363
363
  }
364
364
 
365
- __attribute__((target("avx2"))) //
365
+ __attribute__((target("avx2"))) //
366
366
  __attribute__((target("fma"))) inline static simsimd_f32_t //
367
367
  simsimd_cos_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
368
368
 
@@ -388,10 +388,100 @@ simsimd_cos_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d)
388
388
  return 1 - ab_union.f / (sqrt(a2_union.f) * sqrt(b2_union.f));
389
389
  }
390
390
 
391
+ __attribute__((target("avx2"))) //
392
+ __attribute__((target("fma"))) inline static simsimd_f32_t //
393
+ simsimd_l2sq_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
394
+ __m128 sum_vec = _mm_set1_ps(0);
395
+ for (size_t i = 0; i != d; i += 4) {
396
+ __m128 diff_vec = _mm_sub_ps(_mm_loadu_ps(a + i), _mm_loadu_ps(b + i));
397
+ sum_vec = _mm_fmadd_ps(diff_vec, diff_vec, sum_vec);
398
+ }
399
+ sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
400
+ sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
401
+ float result[1];
402
+ _mm_store_ss(result, sum_vec);
403
+ return result[0];
404
+ }
405
+
406
+ __attribute__((target("avx2"))) //
407
+ __attribute__((target("f16c"))) //
408
+ __attribute__((target("fma"))) inline static simsimd_f32_t //
409
+ simsimd_dot_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
410
+ __m128 ab_vec = _mm_set1_ps(0);
411
+ for (size_t i = 0; i != d; i += 8) {
412
+ __m128 a_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
413
+ __m128 b_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
414
+ ab_vec = _mm_fmadd_ps(a_vec, b_vec, ab_vec);
415
+ }
416
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
417
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
418
+ float result[1];
419
+ _mm_store_ss(result, ab_vec);
420
+ return 1 - result[0];
421
+ }
422
+
423
+ __attribute__((target("avx2"))) //
424
+ __attribute__((target("f16c"))) //
425
+ __attribute__((target("fma"))) inline static simsimd_f32_t //
426
+ simsimd_cos_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
427
+ __m128 ab_vec = _mm_set1_ps(0);
428
+ __m128 a2_vec = _mm_set1_ps(0);
429
+ __m128 b2_vec = _mm_set1_ps(0);
430
+ for (size_t i = 0; i != d; i += 8) {
431
+ __m128 a_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
432
+ __m128 b_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
433
+ ab_vec = _mm_fmadd_ps(a_vec, b_vec, ab_vec);
434
+ a2_vec = _mm_fmadd_ps(a_vec, a_vec, a2_vec);
435
+ b2_vec = _mm_fmadd_ps(b_vec, b_vec, b2_vec);
436
+ }
437
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
438
+ ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
439
+ a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
440
+ a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
441
+ b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
442
+ b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
443
+ float ab_result[1], a2_result[1], b2_result[1];
444
+ _mm_store_ss(ab_result, ab_vec);
445
+ _mm_store_ss(a2_result, a2_vec);
446
+ _mm_store_ss(b2_result, b2_vec);
447
+ return 1 - ab_result[0] / (sqrt(a2_result[0]) * sqrt(b2_result[0]));
448
+ }
449
+
450
+ __attribute__((target("avx2"))) //
451
+ __attribute__((target("f16c"))) //
452
+ __attribute__((target("fma"))) inline static simsimd_f32_t //
453
+ simsimd_l2sq_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
454
+ __m128 sum_vec = _mm_set1_ps(0);
455
+ for (size_t i = 0; i != d; i += 8) {
456
+ __m128 a_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
457
+ __m128 b_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
458
+ __m128 diff_vec = _mm_sub_ps(a_vec, b_vec);
459
+ sum_vec = _mm_fmadd_ps(diff_vec, diff_vec, sum_vec);
460
+ }
461
+ sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
462
+ sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
463
+ float result[1];
464
+ _mm_store_ss(result, sum_vec);
465
+ return result[0];
466
+ }
467
+
391
468
  #endif // SIMSIMD_TARGET_X86_AVX2
392
469
 
393
470
  #if SIMSIMD_TARGET_X86_AVX512
394
471
 
472
+ __attribute__((target("avx512fp16"))) //
473
+ __attribute__((target("avx512f"))) inline static simsimd_f32_t //
474
+ simsimd_dot_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
475
+ __m512 ab_vec = _mm512_set1_ps(0);
476
+ for (size_t i = 0; i != d; i += 16) {
477
+ __m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
478
+ __m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
479
+ ab_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_vec);
480
+ }
481
+ simsimd_f32_t ab = _mm512_reduce_add_ps(ab_vec);
482
+ return 1 - ab;
483
+ }
484
+
395
485
  __attribute__((target("avx512fp16"))) //
396
486
  __attribute__((target("avx512f"))) inline static simsimd_f32_t //
397
487
  simsimd_cos_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
@@ -411,6 +501,20 @@ simsimd_cos_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t
411
501
  return 1 - ab / (sqrt(a2) * sqrt(b2));
412
502
  }
413
503
 
504
+ __attribute__((target("avx512fp16"))) //
505
+ __attribute__((target("avx512f"))) inline static simsimd_f32_t //
506
+ simsimd_l2sq_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
507
+ __m512 sum_vec = _mm512_set1_ps(0);
508
+ for (size_t i = 0; i != d; i += 16) {
509
+ __m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
510
+ __m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
511
+ __m512 diff_vec = _mm512_sub_ps(a_vec, b_vec);
512
+ sum_vec = _mm512_fmadd_ps(diff_vec, diff_vec, sum_vec);
513
+ }
514
+ simsimd_f32_t sum = _mm512_reduce_add_ps(sum_vec);
515
+ return sum;
516
+ }
517
+
414
518
  __attribute__((target("avx512vpopcntdq"))) //
415
519
  __attribute__((target("avx512vl"))) //
416
520
  __attribute__((target("avx512f"))) inline static simsimd_f32_t //
@@ -30,8 +30,9 @@ repair-wheel-command = "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
30
30
  [tool.cibuildwheel.macos]
31
31
  before-build = [
32
32
  "rm -rf {project}/build",
33
+ "brew install gcc@12",
33
34
  ]
34
- archs = ["x86_64", "universal2", "arm64"]
35
+ archs = ["x86_64", "arm64"]
35
36
  repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
36
37
 
37
38
  [tool.cibuildwheel.windows]
@@ -58,6 +58,8 @@ PyMODINIT_FUNC PyInit_simsimd(void) {
58
58
  return NULL;
59
59
 
60
60
  PyModule_AddObject(m, "tanimoto_b1x8_naive", distance(&simsimd_tanimoto_b1x8_naive));
61
+
62
+ // Specialized:
61
63
  PyModule_AddObject(m, "tanimoto_maccs_naive", distance(&simsimd_tanimoto_maccs_naive));
62
64
  PyModule_AddObject(m, "tanimoto_conditional_naive", distance(&simsimd_tanimoto_conditional_naive));
63
65
 
@@ -66,6 +68,8 @@ PyMODINIT_FUNC PyInit_simsimd(void) {
66
68
  PyModule_AddObject(m, "cos_f16x4_neon", distance(&simsimd_cos_f16x4_neon));
67
69
  PyModule_AddObject(m, "cos_i8x16_neon", distance(&simsimd_cos_i8x16_neon));
68
70
  PyModule_AddObject(m, "cos_f32x4_neon", distance(&simsimd_cos_f32x4_neon));
71
+
72
+ // Specialized:
69
73
  PyModule_AddObject(m, "tanimoto_maccs_neon", distance(&simsimd_tanimoto_maccs_neon));
70
74
 
71
75
  #if SIMSIMD_TARGET_ARM_SVE
@@ -76,16 +80,28 @@ PyMODINIT_FUNC PyInit_simsimd(void) {
76
80
  PyModule_AddObject(m, "l2sq_f16_sve", distance(&simsimd_l2sq_f16_sve));
77
81
  PyModule_AddObject(m, "hamming_b1x8_sve", distance(&simsimd_hamming_b1x8_sve));
78
82
  PyModule_AddObject(m, "hamming_b1x128_sve", distance(&simsimd_hamming_b1x128_sve));
83
+
84
+ // Specialized:
79
85
  PyModule_AddObject(m, "tanimoto_maccs_sve", distance(&simsimd_tanimoto_maccs_sve));
80
86
  #endif
81
87
  #endif
82
88
 
83
89
  #if SIMSIMD_TARGET_X86
90
+ PyModule_AddObject(m, "dot_f32x4_avx2", distance(&simsimd_dot_f32x4_avx2));
84
91
  PyModule_AddObject(m, "cos_f32x4_avx2", distance(&simsimd_cos_f32x4_avx2));
92
+ PyModule_AddObject(m, "l2sq_f32x4_avx2", distance(&simsimd_l2sq_f32x4_avx2));
93
+ PyModule_AddObject(m, "dot_f16x8_avx2", distance(&simsimd_dot_f16x8_avx2));
94
+ PyModule_AddObject(m, "cos_f16x8_avx2", distance(&simsimd_cos_f16x8_avx2));
95
+ PyModule_AddObject(m, "l2sq_f16x8_avx2", distance(&simsimd_l2sq_f16x8_avx2));
85
96
 
86
97
  #if SIMSIMD_TARGET_X86_AVX512
98
+ PyModule_AddObject(m, "dot_f16x16_avx512", distance(&simsimd_dot_f16x16_avx512));
87
99
  PyModule_AddObject(m, "cos_f16x16_avx512", distance(&simsimd_cos_f16x16_avx512));
100
+ PyModule_AddObject(m, "l2sq_f16x16_avx512", distance(&simsimd_l2sq_f16x16_avx512));
101
+
88
102
  PyModule_AddObject(m, "hamming_b1x128_avx512", distance(&simsimd_hamming_b1x128_avx512));
103
+
104
+ // Specialized:
89
105
  PyModule_AddObject(m, "tanimoto_maccs_avx512", distance(&simsimd_tanimoto_maccs_avx512));
90
106
  #endif
91
107
  #endif
package/wasmer.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name="unum/usearch"
3
- version="2.1.3"
3
+ version="2.2.0"
4
4
  description="Smaller & Faster Single-File Vector Search Engine from Unum"
5
5
  license="Apache-2.0"
6
6
  readme="README.md"