usearch 2.7.7 → 2.8.1

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 (54) hide show
  1. package/.dockerignore +93 -0
  2. package/.github/workflows/Dockerfile.libs +6 -2
  3. package/.github/workflows/prerelease.yml +242 -245
  4. package/.github/workflows/release.yml +399 -357
  5. package/.gitignore +1 -0
  6. package/.vscode/settings.json +3 -1
  7. package/CITATION.cff +1 -1
  8. package/CMakeLists.txt +35 -5
  9. package/Cargo.toml +1 -1
  10. package/README.md +1 -1
  11. package/build.gradle +6 -0
  12. package/c/README.md +3 -3
  13. package/c/lib.cpp +75 -11
  14. package/c/usearch.h +22 -1
  15. package/conanfile.py +1 -1
  16. package/cpp/README.md +2 -1
  17. package/cpp/bench.cpp +3 -0
  18. package/cpp/test.cpp +12 -12
  19. package/csharp/nuget/nuget-package.props +1 -1
  20. package/docs/compilation.md +5 -13
  21. package/golang/lib.go +4 -1
  22. package/include/usearch/index.hpp +31 -30
  23. package/include/usearch/index_dense.hpp +49 -48
  24. package/include/usearch/index_plugins.hpp +70 -8
  25. package/java/cloud/unum/usearch/Index.java +118 -26
  26. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +8 -8
  27. package/javascript/usearch.js +1 -0
  28. package/objc/USearchObjective.mm +4 -1
  29. package/objc/include/USearchObjective.h +1 -0
  30. package/package.json +1 -1
  31. package/pyproject.toml +10 -8
  32. package/python/lib.cpp +3 -2
  33. package/python/scripts/bench.ipynb +24 -2
  34. package/python/usearch/index.py +8 -3
  35. package/rust/lib.cpp +14 -12
  36. package/rust/lib.hpp +13 -13
  37. package/rust/lib.rs +47 -0
  38. package/setup.py +15 -3
  39. package/simsimd/.github/workflows/prerelease.yml +7 -8
  40. package/simsimd/.github/workflows/release.yml +18 -20
  41. package/simsimd/README.md +13 -8
  42. package/simsimd/binding.gyp +1 -1
  43. package/simsimd/cpp/bench.cxx +17 -3
  44. package/simsimd/include/simsimd/binary.h +1 -3
  45. package/simsimd/include/simsimd/probability.h +234 -361
  46. package/simsimd/include/simsimd/simsimd.h +95 -65
  47. package/simsimd/include/simsimd/spatial.h +0 -3
  48. package/simsimd/include/simsimd/types.h +12 -7
  49. package/simsimd/javascript/test.js +23 -5
  50. package/simsimd/package.json +2 -2
  51. package/wasmer.toml +1 -1
  52. package/wolfram/lib.cpp +1 -1
  53. package/csharp/eng/build_lib.sh +0 -65
  54. package/csharp/eng/clean.sh +0 -12
package/rust/lib.cpp CHANGED
@@ -8,6 +8,7 @@ using index_t = index_dense_t;
8
8
  using add_result_t = typename index_t::add_result_t;
9
9
  using search_result_t = typename index_t::search_result_t;
10
10
  using labeling_result_t = typename index_t::labeling_result_t;
11
+ using vector_key_t = typename index_dense_t::vector_key_t;
11
12
 
12
13
  template <typename scalar_at> Matches search_(index_dense_t& index, scalar_at const* vec, size_t count) {
13
14
  Matches matches;
@@ -26,20 +27,20 @@ template <typename scalar_at> Matches search_(index_dense_t& index, scalar_at co
26
27
  NativeIndex::NativeIndex(std::unique_ptr<index_t> index) : index_(std::move(index)) {}
27
28
 
28
29
  // clang-format off
29
- void NativeIndex::add_i8(key_t key, rust::Slice<int8_t const> vec) const { index_->add(key, vec.data()).error.raise(); }
30
- void NativeIndex::add_f16(key_t key, rust::Slice<uint16_t const> vec) const { index_->add(key, (f16_t const*)vec.data()).error.raise(); }
31
- void NativeIndex::add_f32(key_t key, rust::Slice<float const> vec) const { index_->add(key, vec.data()).error.raise(); }
32
- void NativeIndex::add_f64(key_t key, rust::Slice<double const> vec) const { index_->add(key, vec.data()).error.raise(); }
30
+ void NativeIndex::add_i8(vector_key_t key, rust::Slice<int8_t const> vec) const { index_->add(key, vec.data()).error.raise(); }
31
+ void NativeIndex::add_f16(vector_key_t key, rust::Slice<uint16_t const> vec) const { index_->add(key, (f16_t const*)vec.data()).error.raise(); }
32
+ void NativeIndex::add_f32(vector_key_t key, rust::Slice<float const> vec) const { index_->add(key, vec.data()).error.raise(); }
33
+ void NativeIndex::add_f64(vector_key_t key, rust::Slice<double const> vec) const { index_->add(key, vec.data()).error.raise(); }
33
34
 
34
35
  Matches NativeIndex::search_i8(rust::Slice<int8_t const> vec, size_t count) const { return search_(*index_, vec.data(), count); }
35
36
  Matches NativeIndex::search_f16(rust::Slice<uint16_t const> vec, size_t count) const { return search_(*index_, (f16_t const*)vec.data(), count); }
36
37
  Matches NativeIndex::search_f32(rust::Slice<float const> vec, size_t count) const { return search_(*index_, vec.data(), count); }
37
38
  Matches NativeIndex::search_f64(rust::Slice<double const> vec, size_t count) const { return search_(*index_, vec.data(), count); }
38
39
 
39
- size_t NativeIndex::get_i8(key_t key, rust::Slice<int8_t> vec) const { if (vec.size() % dimensions()) throw std::invalid_argument("Vector length must be a multiple of index dimensionality"); return index_->get(key, vec.data(), vec.size() / dimensions()); }
40
- size_t NativeIndex::get_f16(key_t key, rust::Slice<uint16_t> vec) const { if (vec.size() % dimensions()) throw std::invalid_argument("Vector length must be a multiple of index dimensionality"); return index_->get(key, (f16_t*)vec.data(), vec.size() / dimensions()); }
41
- size_t NativeIndex::get_f32(key_t key, rust::Slice<float> vec) const { if (vec.size() % dimensions()) throw std::invalid_argument("Vector length must be a multiple of index dimensionality"); return index_->get(key, vec.data(), vec.size() / dimensions()); }
42
- size_t NativeIndex::get_f64(key_t key, rust::Slice<double> vec) const { if (vec.size() % dimensions()) throw std::invalid_argument("Vector length must be a multiple of index dimensionality"); return index_->get(key, vec.data(), vec.size() / dimensions()); }
40
+ size_t NativeIndex::get_i8(vector_key_t key, rust::Slice<int8_t> vec) const { if (vec.size() % dimensions()) throw std::invalid_argument("Vector length must be a multiple of index dimensionality"); return index_->get(key, vec.data(), vec.size() / dimensions()); }
41
+ size_t NativeIndex::get_f16(vector_key_t key, rust::Slice<uint16_t> vec) const { if (vec.size() % dimensions()) throw std::invalid_argument("Vector length must be a multiple of index dimensionality"); return index_->get(key, (f16_t*)vec.data(), vec.size() / dimensions()); }
42
+ size_t NativeIndex::get_f32(vector_key_t key, rust::Slice<float> vec) const { if (vec.size() % dimensions()) throw std::invalid_argument("Vector length must be a multiple of index dimensionality"); return index_->get(key, vec.data(), vec.size() / dimensions()); }
43
+ size_t NativeIndex::get_f64(vector_key_t key, rust::Slice<double> vec) const { if (vec.size() % dimensions()) throw std::invalid_argument("Vector length must be a multiple of index dimensionality"); return index_->get(key, vec.data(), vec.size() / dimensions()); }
43
44
  // clang-format on
44
45
 
45
46
  size_t NativeIndex::expansion_add() const { return index_->expansion_add(); }
@@ -47,20 +48,20 @@ size_t NativeIndex::expansion_search() const { return index_->expansion_search()
47
48
  void NativeIndex::change_expansion_add(size_t n) const { index_->change_expansion_add(n); }
48
49
  void NativeIndex::change_expansion_search(size_t n) const { index_->change_expansion_search(n); }
49
50
 
50
- size_t NativeIndex::remove(key_t key) const {
51
+ size_t NativeIndex::remove(vector_key_t key) const {
51
52
  labeling_result_t result = index_->remove(key);
52
53
  result.error.raise();
53
54
  return result.completed;
54
55
  }
55
56
 
56
- size_t NativeIndex::rename(key_t from, key_t to) const {
57
+ size_t NativeIndex::rename(vector_key_t from, vector_key_t to) const {
57
58
  labeling_result_t result = index_->rename(from, to);
58
59
  result.error.raise();
59
60
  return result.completed;
60
61
  }
61
62
 
62
- size_t NativeIndex::count(key_t key) const { return index_->count(key); }
63
- bool NativeIndex::contains(key_t key) const { return index_->contains(key); }
63
+ size_t NativeIndex::count(vector_key_t key) const { return index_->count(key); }
64
+ bool NativeIndex::contains(vector_key_t key) const { return index_->contains(key); }
64
65
 
65
66
  void NativeIndex::reserve(size_t capacity) const { index_->reserve(capacity); }
66
67
 
@@ -106,6 +107,7 @@ metric_kind_t rust_to_cpp_metric(MetricKind value) {
106
107
  case MetricKind::Cos: return metric_kind_t::cos_k;
107
108
  case MetricKind::Pearson: return metric_kind_t::pearson_k;
108
109
  case MetricKind::Haversine: return metric_kind_t::haversine_k;
110
+ case MetricKind::Divergence: return metric_kind_t::divergence_k;
109
111
  case MetricKind::Hamming: return metric_kind_t::hamming_k;
110
112
  case MetricKind::Tanimoto: return metric_kind_t::tanimoto_k;
111
113
  case MetricKind::Sorensen: return metric_kind_t::sorensen_k;
package/rust/lib.hpp CHANGED
@@ -15,36 +15,36 @@ class NativeIndex {
15
15
  using index_dense_t = unum::usearch::index_dense_t;
16
16
  using add_result_t = typename index_dense_t::add_result_t;
17
17
  using search_result_t = typename index_dense_t::search_result_t;
18
- using key_t = typename index_dense_t::key_t;
18
+ using vector_key_t = typename index_dense_t::vector_key_t;
19
19
 
20
20
  NativeIndex(std::unique_ptr<index_dense_t> index);
21
21
 
22
22
  void reserve(size_t) const;
23
23
 
24
- void add_i8(key_t key, rust::Slice<int8_t const> vector) const;
25
- void add_f16(key_t key, rust::Slice<uint16_t const> vector) const;
26
- void add_f32(key_t key, rust::Slice<float const> vector) const;
27
- void add_f64(key_t key, rust::Slice<double const> vector) const;
24
+ void add_i8(vector_key_t key, rust::Slice<int8_t const> vector) const;
25
+ void add_f16(vector_key_t key, rust::Slice<uint16_t const> vector) const;
26
+ void add_f32(vector_key_t key, rust::Slice<float const> vector) const;
27
+ void add_f64(vector_key_t key, rust::Slice<double const> vector) const;
28
28
 
29
29
  Matches search_i8(rust::Slice<int8_t const> vector, size_t count) const;
30
30
  Matches search_f16(rust::Slice<uint16_t const> vector, size_t count) const;
31
31
  Matches search_f32(rust::Slice<float const> vector, size_t count) const;
32
32
  Matches search_f64(rust::Slice<double const> vector, size_t count) const;
33
33
 
34
- size_t get_i8(key_t key, rust::Slice<int8_t> vector) const;
35
- size_t get_f16(key_t key, rust::Slice<uint16_t> vector) const;
36
- size_t get_f32(key_t key, rust::Slice<float> vector) const;
37
- size_t get_f64(key_t key, rust::Slice<double> vector) const;
34
+ size_t get_i8(vector_key_t key, rust::Slice<int8_t> vector) const;
35
+ size_t get_f16(vector_key_t key, rust::Slice<uint16_t> vector) const;
36
+ size_t get_f32(vector_key_t key, rust::Slice<float> vector) const;
37
+ size_t get_f64(vector_key_t key, rust::Slice<double> vector) const;
38
38
 
39
39
  size_t expansion_add() const;
40
40
  size_t expansion_search() const;
41
41
  void change_expansion_add(size_t n) const;
42
42
  void change_expansion_search(size_t n) const;
43
43
 
44
- size_t count(key_t key) const;
45
- size_t remove(key_t key) const;
46
- size_t rename(key_t from, key_t to) const;
47
- bool contains(key_t key) const;
44
+ size_t count(vector_key_t key) const;
45
+ size_t remove(vector_key_t key) const;
46
+ size_t rename(vector_key_t from, vector_key_t to) const;
47
+ bool contains(vector_key_t key) const;
48
48
 
49
49
  size_t dimensions() const;
50
50
  size_t connectivity() const;
package/rust/lib.rs CHANGED
@@ -14,6 +14,7 @@ pub mod ffi {
14
14
  Cos,
15
15
  Pearson,
16
16
  Haversine,
17
+ Divergence,
17
18
  Hamming,
18
19
  Tanimoto,
19
20
  Sorensen,
@@ -432,6 +433,7 @@ pub fn new_index(options: &ffi::IndexOptions) -> Result<Index, cxx::Exception> {
432
433
  mod tests {
433
434
  use crate::ffi::IndexOptions;
434
435
  use crate::ffi::MetricKind;
436
+ use crate::ffi::ScalarKind;
435
437
 
436
438
  use crate::new_index;
437
439
  use crate::Index;
@@ -466,6 +468,51 @@ mod tests {
466
468
  assert!(result.is_err());
467
469
  }
468
470
 
471
+ #[test]
472
+ fn test_add_remove_vector() {
473
+ let mut options = IndexOptions::default();
474
+ options.dimensions = 4;
475
+ options.metric = MetricKind::IP;
476
+ options.quantization = ScalarKind::F64;
477
+ options.connectivity = 10;
478
+ options.expansion_add = 128;
479
+ options.expansion_search = 3;
480
+ let index = Index::new(&options).unwrap();
481
+ assert!(index.reserve(10).is_ok());
482
+ assert_eq!(index.capacity(), 10);
483
+
484
+ let first: [f32; 4] = [0.2, 0.1, 0.2, 0.1];
485
+ let second: [f32; 4] = [0.3, 0.2, 0.4, 0.0];
486
+
487
+ // IDs until 18446744073709551615 should be fine:
488
+ let id1 = 483367403120493160;
489
+ let id2 = 483367403120558696;
490
+ let id3 = 483367403120624232;
491
+ let id4 = 483367403120624233;
492
+
493
+ assert!(index.add(id1, &first).is_ok());
494
+ let mut found_slice = [0.0 as f32; 4];
495
+ assert_eq!(index.get(id1, &mut found_slice).unwrap(), 1);
496
+ assert!(index.remove(id1).is_ok());
497
+
498
+ assert!(index.add(id2, &second).is_ok());
499
+ let mut found_slice = [0.0 as f32; 4];
500
+ assert_eq!(index.get(id2, &mut found_slice).unwrap(), 1);
501
+ assert!(index.remove(id2).is_ok());
502
+
503
+ assert!(index.add(id3, &second).is_ok());
504
+ let mut found_slice = [0.0 as f32; 4];
505
+ assert_eq!(index.get(id3, &mut found_slice).unwrap(), 1);
506
+ assert!(index.remove(id3).is_ok());
507
+
508
+ assert!(index.add(id4, &second).is_ok());
509
+ let mut found_slice = [0.0 as f32; 4];
510
+ assert_eq!(index.get(id4, &mut found_slice).unwrap(), 1);
511
+ assert!(index.remove(id4).is_ok());
512
+
513
+ assert_eq!(index.size(), 0);
514
+ }
515
+
469
516
  #[test]
470
517
  fn integration() {
471
518
  let mut options = IndexOptions::default();
package/setup.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  import sys
3
+ import subprocess
3
4
  from setuptools import setup
4
5
 
5
6
  from pybind11.setup_helpers import Pybind11Extension
@@ -9,6 +10,13 @@ compile_args = []
9
10
  link_args = []
10
11
  macros_args = []
11
12
 
13
+ def is_gcc():
14
+ try:
15
+ compiler_version = subprocess.check_output(["c++", "--version"], universal_newlines=True)
16
+ return "gcc" in compiler_version.lower()
17
+ except Exception:
18
+ return False
19
+
12
20
  if sys.platform == "linux":
13
21
  compile_args.append("-std=c++17")
14
22
  compile_args.append("-O3") # Maximize performance
@@ -21,9 +29,13 @@ if sys.platform == "linux":
21
29
 
22
30
  macros_args.append(("USEARCH_USE_NATIVE_F16", "0"))
23
31
  macros_args.append(("USEARCH_USE_SIMSIMD", "1"))
24
- macros_args.append(("USEARCH_USE_OPENMP", "1"))
25
- compile_args.append("-fopenmp")
26
- link_args.append("-lgomp")
32
+
33
+ if is_gcc():
34
+ macros_args.append(("USEARCH_USE_OPENMP", "1"))
35
+ compile_args.append("-fopenmp")
36
+ link_args.append("-lgomp")
37
+ else:
38
+ macros_args.append(("USEARCH_USE_OPENMP", "0"))
27
39
 
28
40
  if sys.platform == "darwin":
29
41
  # MacOS 10.15 or higher is needed for `aligned_alloc` support.
@@ -16,7 +16,6 @@ permissions:
16
16
  contents: read
17
17
 
18
18
  jobs:
19
-
20
19
  test_python:
21
20
  name: Test Python ${{ matrix.python-version }} on ${{ matrix.architecture }} ${{ matrix.os }}
22
21
  runs-on: ${{ matrix.os }}
@@ -34,7 +33,7 @@ jobs:
34
33
  uses: actions/setup-python@v4
35
34
  with:
36
35
  python-version: ${{ matrix.python-version }}
37
-
36
+
38
37
  - name: Install dependencies
39
38
  run: |
40
39
  python -m pip install --no-cache-dir --upgrade pip
@@ -58,24 +57,24 @@ jobs:
58
57
  - name: Test with PyTest
59
58
  run: |
60
59
  python -c "import simsimd; print(simsimd.get_capabilities())"
61
- pytest python/test.py
62
-
60
+ pytest python/test.py -s -x -v
61
+
63
62
  test_javascript:
64
63
  name: Test JavaScript
65
64
  runs-on: ubuntu-latest
66
65
  strategy:
67
66
  matrix:
67
+ os: [ubuntu-22.04, macOS-11, windows-2022]
68
68
  node-version: [18.x]
69
69
  steps:
70
-
71
70
  - uses: actions/checkout@v4
72
71
  - name: Set up Node.js
73
72
  uses: actions/setup-node@v3
74
73
  with:
75
- node-version: '18.x'
76
-
74
+ node-version: "18.x"
75
+
77
76
  - name: Build locally
78
- run: npm i
77
+ run: npm install
79
78
 
80
79
  - name: Test
81
80
  run: npm test
@@ -16,17 +16,15 @@ permissions:
16
16
  id-token: write
17
17
 
18
18
  jobs:
19
-
20
19
  versioning:
21
20
  name: Semantic Release
22
21
  runs-on: ubuntu-latest
23
22
  steps:
24
- - uses: actions/checkout@v3
25
- with:
26
- persist-credentials: false
27
- - uses: actions/setup-node@v3
28
- - run: npm install --save-dev @semantic-release/exec @semantic-release/git conventional-changelog-eslint semantic-release && npx semantic-release
29
-
23
+ - uses: actions/checkout@v3
24
+ with:
25
+ persist-credentials: false
26
+ - uses: actions/setup-node@v3
27
+ - run: npm install --save-dev @semantic-release/exec @semantic-release/git conventional-changelog-eslint semantic-release && npx semantic-release
30
28
 
31
29
  rebase:
32
30
  name: Rebase Dev. Branch
@@ -40,9 +38,10 @@ jobs:
40
38
 
41
39
  - name: Perform rebase
42
40
  run: |
41
+ git fetch origin main
43
42
  git checkout main-dev
44
- git rebase main
45
-
43
+ git rebase origin/main
44
+
46
45
  - name: Push changes
47
46
  uses: CasperWA/push-protected@v2
48
47
  with:
@@ -58,19 +57,19 @@ jobs:
58
57
  strategy:
59
58
  fail-fast: false
60
59
  matrix:
61
- os: [ubuntu-20.04, macOS-11, windows-2022]
60
+ os: [ubuntu-22.04, macOS-11, windows-2022]
62
61
  steps:
63
62
  - uses: actions/checkout@v3
64
63
  with:
65
- ref: 'main'
64
+ ref: "main"
66
65
  - uses: actions/setup-python@v3
67
-
66
+
68
67
  - name: Setup Docker
69
- if: matrix.os == 'ubuntu-20.04'
68
+ if: matrix.os == 'ubuntu-22.04'
70
69
  uses: docker-practice/actions-setup-docker@master
71
-
70
+
72
71
  - name: Setup QEMU
73
- if: matrix.os == 'ubuntu-20.04'
72
+ if: matrix.os == 'ubuntu-22.04'
74
73
  uses: docker/setup-qemu-action@v2.1.0
75
74
 
76
75
  - name: Install CIBuildWheel
@@ -82,12 +81,11 @@ jobs:
82
81
  - uses: actions/upload-artifact@v3
83
82
  with:
84
83
  path: ./wheelhouse/*.whl
85
-
86
-
84
+
87
85
  publish_python:
88
86
  name: Publish Python
89
87
  needs: build_wheels
90
- runs-on: ubuntu-20.04
88
+ runs-on: ubuntu-22.04
91
89
  environment:
92
90
  name: pypi
93
91
  url: https://pypi.org/p/simsimd
@@ -110,11 +108,11 @@ jobs:
110
108
  publish_javascript:
111
109
  name: Publish JavaScript
112
110
  needs: versioning
113
- runs-on: ubuntu-latest
111
+ runs-on: ubuntu-22.04
114
112
  steps:
115
113
  - uses: actions/checkout@v3
116
114
  with:
117
- ref: 'main'
115
+ ref: "main"
118
116
  - run: git submodule update --init --recursive
119
117
  - uses: actions/setup-node@v3
120
118
  with:
package/simsimd/README.md CHANGED
@@ -7,6 +7,7 @@ SimSIMD leverages SIMD intrinsics, capabilities that only select compilers effec
7
7
  - ✅ __3-200x faster__ than NumPy and SciPy distance functions.
8
8
  - ✅ Euclidean (L2), Inner Product, and Cosine (Angular) spatial distances.
9
9
  - ✅ Hamming (~ Manhattan) and Jaccard (~ Tanimoto) binary distances.
10
+ - ✅ Kullback-Leibler and Jensen–Shannon divergences for probability distributions.
10
11
  - ✅ Single-precision `f32`, half-precision `f16`, `i8`, and binary vectors.
11
12
  - ✅ Compatible with GCC and Clang on MacOS and Linux, and MinGW on Windows.
12
13
  - ✅ Compatible with NumPy, PyTorch, TensorFlow, and other tensors.
@@ -28,18 +29,19 @@ Given 1000 embeddings from OpenAI Ada API with 1536 dimensions, running on the A
28
29
  | `numpy.inner` | `inner` | __2 x__ | __9 x__ | __18 x__ |
29
30
  | `scipy.spatial.distance.cosine` | `cosine` | __32 x__ | __79 x__ | __133 x__ |
30
31
  | `scipy.spatial.distance.sqeuclidean` | `sqeuclidean` | __5 x__ | __26 x__ | __17 x__ |
31
- | `scipy.spatial.distance.jensenshannon` | `jensenshannon` | __41 x__ | __76 x__ | |
32
+ | `scipy.spatial.distance.jensenshannon` | `jensenshannon` | __31 x__ | __53 x__ | |
32
33
  | `scipy.special.kl_div` | `kullbackleibler` | __21 x__ | __18 x__ | |
33
34
 
34
35
  ### Intel Sapphire Rapids
35
36
 
36
- On the Intel Sapphire Rapids platform, SimSIMD was benchmarked against autovectorized-code using GCC 12. GCC handles single-precision `float` and `int8_t` well. However, it fails on `_Float16` arrays, which has been part of the C language since 2011.
37
+ On the Intel Sapphire Rapids platform, SimSIMD was benchmarked against auto-vectorized code using GCC 12. GCC handles single-precision `float` and `int8_t` well. However, it fails on `_Float16` arrays, which has been part of the C language since 2011.
37
38
 
38
- | | GCC 12 `f32` | GCC 12 `f16` | SimSIMD `f16` | `f16` improvement |
39
- | :------------ | -----------: | -----------: | ------------: | ----------------: |
40
- | `cosine` | 3.28 M/s | 336.29 k/s | 6.88 M/s | __20 x__ |
41
- | `sqeuclidean` | 4.62 M/s | 147.25 k/s | 5.32 M/s | __36 x__ |
42
- | `inner` | 3.81 M/s | 192.02 k/s | 5.99 M/s | __31 x__ |
39
+ | | GCC 12 `f32` | GCC 12 `f16` | SimSIMD `f16` | `f16` improvement |
40
+ | :-------------- | -----------: | -----------: | ------------: | ----------------: |
41
+ | `cosine` | 3.28 M/s | _336.29 k/s_ | _6.88 M/s_ | __20 x__ |
42
+ | `sqeuclidean` | 4.62 M/s | _147.25 k/s_ | _5.32 M/s_ | __36 x__ |
43
+ | `inner` | 3.81 M/s | _192.02 k/s_ | _5.99 M/s_ | __31 x__ |
44
+ | `jensenshannon` | 1.18 M/s | _18.13 k/s_ | _2.14 M/s_ | __118 x__ |
43
45
 
44
46
  __Technical Insights__:
45
47
 
@@ -168,7 +170,10 @@ Should you wish to integrate SimSIMD within USearch, simply compile USearch with
168
170
  __To rerun experiments__ utilize the following command:
169
171
 
170
172
  ```sh
171
- cmake -DCMAKE_BUILD_TYPE=Release -DSIMSIMD_BUILD_BENCHMARKS=1 -B ./build_release && make -C ./build_release && ./build_release/simsimd_bench
173
+ cmake -DCMAKE_BUILD_TYPE=Release -DSIMSIMD_BUILD_BENCHMARKS=1 -B ./build_release
174
+ cmake --build build_release --config Release
175
+ ./build_release/simsimd_bench
176
+ ./build_release/simsimd_bench --benchmark_filter=js
172
177
  ```
173
178
 
174
179
  __To test and benchmark with Python bindings__:
@@ -4,6 +4,7 @@
4
4
  "target_name": "simsimd",
5
5
  "sources": ["javascript/lib.c"],
6
6
  "include_dirs": ["include"],
7
+ "defines": ["SIMSIMD_NATIVE_F16=0"],
7
8
  "cflags": [
8
9
  "-std=c11",
9
10
  "-ffast-math",
@@ -11,7 +12,6 @@
11
12
  "-Wno-maybe-uninitialized",
12
13
  "-Wno-cast-function-type",
13
14
  "-Wno-switch",
14
- "-DSIMSIMD_NATIVE_F16=0",
15
15
  ],
16
16
  }
17
17
  ]
@@ -3,8 +3,8 @@
3
3
 
4
4
  #include <benchmark/benchmark.h>
5
5
 
6
- #define SIMSIMD_RSQRT simsimd_approximate_inverse_square_root
7
- #define SIMSIMD_LOG simsimd_approximate_log
6
+ #define SIMSIMD_RSQRT sqrtf
7
+ #define SIMSIMD_LOG logf
8
8
  #include <simsimd/simsimd.h>
9
9
 
10
10
  namespace bm = benchmark;
@@ -44,7 +44,7 @@ template <typename scalar_at, std::size_t dimensions_ak> struct vectors_pair_gt
44
44
  a2_sum = std::sqrt(a2_sum);
45
45
  b2_sum = std::sqrt(b2_sum);
46
46
  for (std::size_t i = 0; i != dimensions_ak; ++i)
47
- a[i] /= a2_sum, b[i] /= b2_sum;
47
+ a[i] = static_cast<scalar_at>(a[i] / a2_sum), b[i] = static_cast<scalar_at>(b[i] / b2_sum);
48
48
  }
49
49
  }
50
50
  };
@@ -129,10 +129,14 @@ int main(int argc, char** argv) {
129
129
  register_<simsimd_f16_t>("neon_f16_ip", simsimd_neon_f16_ip, simsimd_accurate_f16_ip);
130
130
  register_<simsimd_f16_t>("neon_f16_cos", simsimd_neon_f16_cos, simsimd_accurate_f16_cos);
131
131
  register_<simsimd_f16_t>("neon_f16_l2sq", simsimd_neon_f16_l2sq, simsimd_accurate_f16_l2sq);
132
+ register_<simsimd_f16_t>("neon_f16_kl", simsimd_neon_f16_kl, simsimd_accurate_f16_kl);
133
+ register_<simsimd_f16_t>("neon_f16_js", simsimd_neon_f16_js, simsimd_accurate_f16_js);
132
134
 
133
135
  register_<simsimd_f32_t>("neon_f32_ip", simsimd_neon_f32_ip, simsimd_accurate_f32_ip);
134
136
  register_<simsimd_f32_t>("neon_f32_cos", simsimd_neon_f32_cos, simsimd_accurate_f32_cos);
135
137
  register_<simsimd_f32_t>("neon_f32_l2sq", simsimd_neon_f32_l2sq, simsimd_accurate_f32_l2sq);
138
+ register_<simsimd_f32_t>("neon_f32_kl", simsimd_neon_f32_kl, simsimd_accurate_f32_kl);
139
+ register_<simsimd_f32_t>("neon_f32_js", simsimd_neon_f32_js, simsimd_accurate_f32_js);
136
140
 
137
141
  register_<simsimd_i8_t>("neon_i8_cos", simsimd_neon_i8_cos, simsimd_accurate_i8_cos);
138
142
  register_<simsimd_i8_t>("neon_i8_l2sq", simsimd_neon_i8_l2sq, simsimd_accurate_i8_l2sq);
@@ -152,6 +156,8 @@ int main(int argc, char** argv) {
152
156
  register_<simsimd_f16_t>("avx2_f16_ip", simsimd_avx2_f16_ip, simsimd_accurate_f16_ip);
153
157
  register_<simsimd_f16_t>("avx2_f16_cos", simsimd_avx2_f16_cos, simsimd_accurate_f16_cos);
154
158
  register_<simsimd_f16_t>("avx2_f16_l2sq", simsimd_avx2_f16_l2sq, simsimd_accurate_f16_l2sq);
159
+ register_<simsimd_f16_t>("avx2_f16_kl", simsimd_avx2_f16_kl, simsimd_accurate_f16_kl);
160
+ register_<simsimd_f16_t>("avx2_f16_js", simsimd_avx2_f16_js, simsimd_accurate_f16_js);
155
161
 
156
162
  register_<simsimd_i8_t>("avx2_i8_cos", simsimd_avx2_i8_cos, simsimd_accurate_i8_cos);
157
163
  register_<simsimd_i8_t>("avx2_i8_l2sq", simsimd_avx2_i8_l2sq, simsimd_accurate_i8_l2sq);
@@ -161,6 +167,8 @@ int main(int argc, char** argv) {
161
167
  register_<simsimd_f16_t>("avx512_f16_ip", simsimd_avx512_f16_ip, simsimd_accurate_f16_ip);
162
168
  register_<simsimd_f16_t>("avx512_f16_cos", simsimd_avx512_f16_cos, simsimd_accurate_f16_cos);
163
169
  register_<simsimd_f16_t>("avx512_f16_l2sq", simsimd_avx512_f16_l2sq, simsimd_accurate_f16_l2sq);
170
+ register_<simsimd_f16_t>("avx512_f16_kl", simsimd_avx512_f16_kl, simsimd_accurate_f16_kl);
171
+ register_<simsimd_f16_t>("avx512_f16_js", simsimd_avx512_f16_js, simsimd_accurate_f16_js);
164
172
 
165
173
  register_<simsimd_i8_t>("avx512_i8_cos", simsimd_avx512_i8_cos, simsimd_accurate_i8_cos);
166
174
  register_<simsimd_i8_t>("avx512_i8_l2sq", simsimd_avx512_i8_l2sq, simsimd_accurate_i8_l2sq);
@@ -168,15 +176,21 @@ int main(int argc, char** argv) {
168
176
  register_<simsimd_f32_t>("avx512_f32_ip", simsimd_avx512_f32_ip, simsimd_accurate_f32_ip);
169
177
  register_<simsimd_f32_t>("avx512_f32_cos", simsimd_avx512_f32_cos, simsimd_accurate_f32_cos);
170
178
  register_<simsimd_f32_t>("avx512_f32_l2sq", simsimd_avx512_f32_l2sq, simsimd_accurate_f32_l2sq);
179
+ register_<simsimd_f32_t>("avx512_f32_kl", simsimd_avx512_f32_kl, simsimd_accurate_f32_kl);
180
+ register_<simsimd_f32_t>("avx512_f32_js", simsimd_avx512_f32_js, simsimd_accurate_f32_js);
171
181
  #endif
172
182
 
173
183
  register_<simsimd_f16_t>("serial_f16_ip", simsimd_serial_f16_ip, simsimd_accurate_f16_ip);
174
184
  register_<simsimd_f16_t>("serial_f16_cos", simsimd_serial_f16_cos, simsimd_accurate_f16_cos);
175
185
  register_<simsimd_f16_t>("serial_f16_l2sq", simsimd_serial_f16_l2sq, simsimd_accurate_f16_l2sq);
186
+ register_<simsimd_f16_t>("serial_f16_kl", simsimd_serial_f16_kl, simsimd_accurate_f16_kl);
187
+ register_<simsimd_f16_t>("serial_f16_js", simsimd_serial_f16_js, simsimd_accurate_f16_js);
176
188
 
177
189
  register_<simsimd_f32_t>("serial_f32_ip", simsimd_serial_f32_ip, simsimd_accurate_f32_ip);
178
190
  register_<simsimd_f32_t>("serial_f32_cos", simsimd_serial_f32_cos, simsimd_accurate_f32_cos);
179
191
  register_<simsimd_f32_t>("serial_f32_l2sq", simsimd_serial_f32_l2sq, simsimd_accurate_f32_l2sq);
192
+ register_<simsimd_f32_t>("serial_f32_kl", simsimd_serial_f32_kl, simsimd_accurate_f32_kl);
193
+ register_<simsimd_f32_t>("serial_f32_js", simsimd_serial_f32_js, simsimd_accurate_f32_js);
180
194
 
181
195
  register_<simsimd_i8_t>("serial_i8_cos", simsimd_serial_i8_cos, simsimd_accurate_i8_cos);
182
196
  register_<simsimd_i8_t>("serial_i8_l2sq", simsimd_serial_i8_l2sq, simsimd_accurate_i8_l2sq);
@@ -136,12 +136,11 @@ simsimd_sve_b8_jaccard(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_siz
136
136
  __attribute__((target("avx512vpopcntdq,avx512vl,avx512bw,avx512f"))) //
137
137
  inline static simsimd_f32_t
138
138
  simsimd_avx512_b8_hamming(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words) {
139
- __m512i differences_vec = _mm512_setzero_si512(), union_vec = _mm512_setzero_si512();
139
+ __m512i differences_vec = _mm512_setzero_si512();
140
140
  for (simsimd_size_t i = 0; i < n_words; i += 64) {
141
141
 
142
142
  // Compute mask for tail elements
143
143
  __mmask64 mask = (i + 64 <= n_words) ? 0xFFFFFFFFFFFFFFFF : (((1ull << (n_words - i)) - 1ull));
144
-
145
144
  __m512i a_vec = _mm512_maskz_loadu_epi8(mask, a + i);
146
145
  __m512i b_vec = _mm512_maskz_loadu_epi8(mask, b + i);
147
146
  __m512i xor_vec = _mm512_xor_si512(a_vec, b_vec);
@@ -161,7 +160,6 @@ simsimd_avx512_b8_jaccard(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_
161
160
 
162
161
  // Compute mask for tail elements
163
162
  __mmask64 mask = (i + 64 <= n_words) ? 0xFFFFFFFFFFFFFFFF : (((1ull << (n_words - i)) - 1ull));
164
-
165
163
  __m512i a_vec = _mm512_maskz_loadu_epi8(mask, a + i);
166
164
  __m512i b_vec = _mm512_maskz_loadu_epi8(mask, b + i);
167
165
  __m512i and_vec = _mm512_and_si512(a_vec, b_vec);