usearch 2.8.8 → 2.8.9

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/CITATION.cff CHANGED
@@ -5,7 +5,7 @@ authors:
5
5
  given-names: "Ash"
6
6
  orcid: "https://orcid.org/0000-0002-4882-1815"
7
7
  title: "USearch by Unum Cloud"
8
- version: 2.8.8
8
+ version: 2.8.9
9
9
  doi: 10.5281/zenodo.7949416
10
10
  date-released: 2023-10-22
11
11
  url: "https://github.com/unum-cloud/usearch"
package/CONTRIBUTING.md CHANGED
@@ -144,6 +144,29 @@ node --test ./javascript/usearch.test.js
144
144
  npm publish
145
145
  ```
146
146
 
147
+ To compile for AWS Lambda you'd need to recompile the binding.
148
+ You can test the setup locally, overriding some of the compilation variables in Docker image:
149
+
150
+ ```Dockerfile
151
+ FROM public.ecr.aws/lambda/nodejs:18-x86_64
152
+ RUN npm init -y
153
+ RUN yum install tar git python3 cmake gcc-c++ -y && yum groupinstall "Development Tools" -y
154
+
155
+ # Assuming AWS Linux 2 uses old compilers:
156
+ ENV USEARCH_USE_FP16LIB 1
157
+ ENV DUSEARCH_USE_SIMSIMD 1
158
+ ENV SIMSIMD_TARGET_X86_AVX2 1
159
+ ENV SIMSIMD_TARGET_X86_AVX512 0
160
+ ENV SIMSIMD_TARGET_ARM_NEON 1
161
+ ENV SIMSIMD_TARGET_ARM_SVE 0
162
+
163
+ # For specific PR:
164
+ # RUN npm install --build-from-source unum-cloud/usearch#pull/302/head
165
+ # For specific version:
166
+ # RUN npm install --build-from-source usearch@2.8.8
167
+ RUN npm install --build-from-source usearch
168
+ ```
169
+
147
170
  To compile to WebAssembly make sure you have `emscripten` installed and run the following script:
148
171
 
149
172
  ```sh
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "usearch"
3
- version = "2.8.8"
3
+ version = "2.8.9"
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/README.md CHANGED
@@ -400,7 +400,7 @@ doi = {10.5281/zenodo.7949416},
400
400
  author = {Vardanian, Ash},
401
401
  title = {{USearch by Unum Cloud}},
402
402
  url = {https://github.com/unum-cloud/usearch},
403
- version = {2.8.8},
403
+ version = {2.8.9},
404
404
  year = {2023},
405
405
  month = oct,
406
406
  }
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.8.8
1
+ 2.8.9
package/conanfile.py CHANGED
@@ -7,7 +7,7 @@ required_conan_version = ">=1.53.0"
7
7
  class USearchConan(ConanFile):
8
8
 
9
9
  name = "usearch"
10
- version = '2.8.8'
10
+ version = '2.8.9'
11
11
  license = "Apache-2.0"
12
12
  description = "Smaller & Faster Single-File Vector Search Engine from Unum"
13
13
  homepage = "https://github.com/unum-cloud/usearch"
@@ -11,7 +11,7 @@
11
11
 
12
12
  #define USEARCH_VERSION_MAJOR 2
13
13
  #define USEARCH_VERSION_MINOR 8
14
- #define USEARCH_VERSION_PATCH 8
14
+ #define USEARCH_VERSION_PATCH 9
15
15
 
16
16
  // Inferring C++ version
17
17
  // https://stackoverflow.com/a/61552074
@@ -1419,7 +1419,7 @@ class metric_punned_t {
1419
1419
  if (simd_metric == nullptr)
1420
1420
  return false;
1421
1421
 
1422
- raw_ptr_ = (punned_ptr_t)simd_metric;
1422
+ std::memcpy(&raw_ptr_, &simd_metric, sizeof(simd_metric));
1423
1423
  isa_kind_ = simd_kind;
1424
1424
  return true;
1425
1425
  }
@@ -1,15 +1,16 @@
1
1
  # USearch for JavaScript
2
2
 
3
+ USearch is a high-performance library for building and querying vector search indexes, optimized for Node.js and WASM environments.
4
+
3
5
  ## Installation
4
6
 
5
- USearch is available both for Node.js backend runtime and WASM frontend runtime.
6
- For the first option, use the conventional `npm install`:
7
+ For Node.js environments, install USearch using `npm`:
7
8
 
8
9
  ```sh
9
10
  npm install usearch
10
11
  ```
11
12
 
12
- For latter:
13
+ For front-end applications using WASM, use the Wasmer package manager:
13
14
 
14
15
  ```sh
15
16
  wasmer install unum/usearch
@@ -17,20 +18,71 @@ wasmer install unum/usearch
17
18
 
18
19
  ## Quickstart
19
20
 
21
+ Create an index, add vectors, and perform searches with ease:
22
+
20
23
  ```js
21
- var index = new usearch.Index({ metric: 'cos', connectivity: 16n, dimensions: 3n })
22
- index.add(42n, new Float32Array([0.2, 0.6, 0.4]))
23
- var results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10n)
24
+ const usearch = require('usearch');
25
+ const index = new usearch.Index({ metric: 'cos', connectivity: 16, dimensions: 3 });
26
+ index.add(42n, new Float32Array([0.2, 0.6, 0.4]));
27
+ const results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10);
28
+
29
+ assert(index.size() === 1);
30
+ assert.deepEqual(results.keys, new BigUint64Array([42n]));
31
+ assert.deepEqual(results.distances, new Float32Array([0]));
24
32
 
25
- assert.equal(index.size(), 1n)
26
- assert.deepEqual(results.keys, new BigUint64Array([42n]))
27
- assert.deepEqual(results.distances, new Float32Array([0]))
33
+ index.remove(42n);
28
34
  ```
29
35
 
30
36
  ## Serialization
31
37
 
38
+ Persist and restore your index with serialization methods:
39
+
40
+ ```js
41
+ index.save('index.usearch'); // Save the index to a file
42
+ index.load('index.usearch'); // Load the index from a file
43
+ index.view('index.usearch'); // View the index from a file without loading into memory
44
+ ```
45
+
46
+ ## Advanced Index Configuration
47
+
48
+ Customize your index with additional configuration options:
49
+
50
+ ```js
51
+ const index = new usearch.Index({
52
+ dimensions: 128,
53
+ metric: 'ip',
54
+ quantization: 'f32',
55
+ connectivity: 10,
56
+ expansion_add: 5,
57
+ expansion_search: 3,
58
+ multi: true
59
+ });
60
+ ```
61
+
62
+ ## Batch Operations
63
+
64
+ Process multiple vectors at once for efficiency.
65
+ For performance reasons we prefer a flattened `TypedArray` over an Array of Arrays:
66
+
67
+ ```js
68
+ const keys = new BigUint64Array([15n, 16n]);
69
+ const vectors = new Float32Array([10, 20, 10, 25]);
70
+ index.add(keys, vectors);
71
+ ```
72
+
73
+ Retrieve batch search results:
74
+
75
+ ```js
76
+ const batchResults = index.search(vectors, 2);
77
+ const firstMatch = batchResults.get(0);
78
+ ```
79
+
80
+ ## Index Introspection
81
+
82
+ Inspect and interact with the index:
83
+
32
84
  ```js
33
- index.save('index.usearch')
34
- index.load('index.usearch')
35
- index.view('index.usearch')
85
+ const dimensions = index.dimensions(); // Get the number of dimensions
86
+ const containsKey = index.contains(42n); // Check if a key is in the index
87
+ const count = index.count(42n); // Get the count of vectors for a key
36
88
  ```
@@ -181,7 +181,7 @@ Napi::Value CompiledIndex::Search(Napi::CallbackInfo const& ctx) {
181
181
  std::size_t tasks = queries.ElementLength() / native_->dimensions();
182
182
  std::size_t wanted = napi_argument_to_size(ctx[1]);
183
183
 
184
- auto run_parallel = [&](auto vectors) {
184
+ auto run_parallel = [&](auto vectors) -> Napi::Value {
185
185
  Napi::Array result_js = Napi::Array::New(env, 3);
186
186
  Napi::BigUint64Array matches_js = Napi::BigUint64Array::New(env, tasks * wanted);
187
187
  Napi::Float32Array distances_js = Napi::Float32Array::New(env, tasks * wanted);
@@ -192,21 +192,29 @@ Napi::Value CompiledIndex::Search(Napi::CallbackInfo const& ctx) {
192
192
  auto counts_data = counts_js.Data();
193
193
 
194
194
  try {
195
+ bool failed = false;
195
196
  executor_stl_t executor;
196
197
  executor.fixed(tasks, [&](std::size_t /*thread_idx*/, std::size_t task_idx) {
197
198
  auto result = native_->search(vectors + task_idx * native_->dimensions(), wanted);
198
199
  if (!result) {
199
- // Handle the error appropriately
200
- // For example, log the error or set some flag in the result_js object
200
+ failed = true;
201
+ Napi::TypeError::New(env, result.error.release()).ThrowAsJavaScriptException();
201
202
  } else {
202
203
  counts_data[task_idx] = result.dump_to(matches_data + task_idx * native_->dimensions(),
203
204
  distances_data + task_idx * native_->dimensions());
204
205
  }
205
206
  });
207
+
208
+ if (failed)
209
+ return env.Null();
210
+
206
211
  } catch (std::bad_alloc const&) {
207
212
  Napi::TypeError::New(env, "Out of memory").ThrowAsJavaScriptException();
213
+ return env.Null();
214
+
208
215
  } catch (...) {
209
216
  Napi::TypeError::New(env, "Search failed").ThrowAsJavaScriptException();
217
+ return env.Null();
210
218
  }
211
219
 
212
220
  result_js.Set(0u, matches_js);
@@ -247,10 +247,12 @@ class Index {
247
247
  // Call the compiled method and create Matches or BatchMatches object with the result
248
248
  const result = this._compiledIndex.search(normalizedVectors, k);
249
249
  const countInQueries = normalizedVectors.length / Number(this._compiledIndex.dimensions());
250
+ const batchMatches = new BatchMatches(result[0], result[1], result[2], k);
251
+
250
252
  if (countInQueries === 1) {
251
- return new Matches(result[0], result[1]);
253
+ return batchMatches.get(0);
252
254
  } else {
253
- return new BatchMatches(result[0], result[1], result[2], k);
255
+ return batchMatches;
254
256
  }
255
257
  }
256
258
 
@@ -36,6 +36,21 @@ test('Batch operations', () => {
36
36
  assert.deepEqual(results.distances, new Float32Array([45, 130]), 'distances should be 45 and 130');
37
37
  });
38
38
 
39
+ test("Expected results", () => {
40
+ var index = new usearch.Index({
41
+ metric: "cos",
42
+ connectivity: 16,
43
+ dimensions: 3,
44
+ });
45
+ index.add(42n, new Float32Array([0.2, 0.6, 0.4]));
46
+ var results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10);
47
+
48
+ assert.equal(index.size(), 1);
49
+ assert.deepEqual(results.keys, new BigUint64Array([42n]));
50
+ assert.deepEqual(results.distances, new Float32Array([0]));
51
+ });
52
+
53
+
39
54
 
40
55
  test('Operations with invalid values', () => {
41
56
  const indexBatch = new usearch.Index(2, 'l2sq');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "2.8.8",
3
+ "version": "2.8.9",
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/simsimd/README.md CHANGED
@@ -30,24 +30,23 @@ SimSIMD leverages SIMD intrinsics, capabilities that only select compilers effec
30
30
 
31
31
  Given 1000 embeddings from OpenAI Ada API with 1536 dimensions, running on the Apple M2 Pro Arm CPU with NEON support, here's how SimSIMD performs against conventional methods:
32
32
 
33
- | Conventional | SimSIMD | `f32` improvement | `f16` improvement | `i8` improvement |
34
- | :------------------------------------- | :---------------- | ----------------: | ----------------: | ---------------: |
35
- | `numpy.inner` | `inner` | __2 x__ | __9 x__ | __18 x__ |
36
- | `scipy.spatial.distance.cosine` | `cosine` | __32 x__ | __79 x__ | __133 x__ |
37
- | `scipy.spatial.distance.sqeuclidean` | `sqeuclidean` | __5 x__ | __26 x__ | __17 x__ |
38
- | `scipy.spatial.distance.jensenshannon` | `jensenshannon` | __31 x__ | __53 x__ | |
39
- | `scipy.special.kl_div` | `kullbackleibler` | __21 x__ | __18 x__ | |
33
+ | Kind | `f32` improvement | `f16` improvement | `i8` improvement | Conventional method | SimSIMD |
34
+ | :------------- | ----------------: | ----------------: | ---------------: | :------------------------------------- | :-------------- |
35
+ | Cosine | __32 x__ | __79 x__ | __133 x__ | `scipy.spatial.distance.cosine` | `cosine` |
36
+ | Euclidean ² | __5 x__ | __26 x__ | __17 x__ | `scipy.spatial.distance.sqeuclidean` | `sqeuclidean` |
37
+ | Inner Product | __2 x__ | __9 x__ | __18 x__ | `numpy.inner` | `inner` |
38
+ | Jensen Shannon | __31 x__ | __53 x__ | | `scipy.spatial.distance.jensenshannon` | `jensenshannon` |
40
39
 
41
40
  ### Intel Sapphire Rapids
42
41
 
43
42
  On the Intel Sapphire Rapids platform, SimSIMD was benchmarked against auto-vectorized code using GCC 12. GCC handles single-precision `float`, but might not be the best choice for `int8` and `_Float16` arrays, which has been part of the C language since 2011.
44
43
 
45
- | | GCC 12 `f32` | GCC 12 `f16` | SimSIMD `f16` | `f16` improvement |
46
- | :-------------- | -----------: | -----------: | ------------: | ----------------: |
47
- | `cosine` | 3.28 M/s | _336.29 k/s_ | _6.88 M/s_ | __20 x__ |
48
- | `sqeuclidean` | 4.62 M/s | _147.25 k/s_ | _5.32 M/s_ | __36 x__ |
49
- | `inner` | 3.81 M/s | _192.02 k/s_ | _5.99 M/s_ | __31 x__ |
50
- | `jensenshannon` | 1.18 M/s | _18.13 k/s_ | _2.14 M/s_ | __118 x__ |
44
+ | Kind | GCC 12 `f32` | GCC 12 `f16` | SimSIMD `f16` | `f16` improvement |
45
+ | :------------- | -----------: | -----------: | ------------: | ----------------: |
46
+ | Cosine | 3.28 M/s | _336.29 k/s_ | _6.88 M/s_ | __20 x__ |
47
+ | Euclidean ² | 4.62 M/s | _147.25 k/s_ | _5.32 M/s_ | __36 x__ |
48
+ | Inner Product | 3.81 M/s | _192.02 k/s_ | _5.99 M/s_ | __31 x__ |
49
+ | Jensen Shannon | 1.18 M/s | _18.13 k/s_ | _2.14 M/s_ | __118 x__ |
51
50
 
52
51
  __Technical Insights__:
53
52
 
@@ -173,6 +172,8 @@ Stay updated with the latest advancements by always using the most recent compil
173
172
 
174
173
  Should you wish to integrate SimSIMD within USearch, simply compile USearch with the flag `USEARCH_USE_SIMSIMD=1`. Notably, this is the default setting on the majority of platforms.
175
174
 
175
+ ## Benchmarking and Contributing
176
+
176
177
  __To rerun experiments__ utilize the following command:
177
178
 
178
179
  ```sh
@@ -425,8 +425,8 @@ simsimd_avx512_f32_js_cycle:
425
425
  __mmask16 nonzero_mask_b = _mm512_cmp_ps_mask(b_vec, epsilon_vec, _CMP_GE_OQ);
426
426
  __mmask16 nonzero_mask = nonzero_mask_a & nonzero_mask_b;
427
427
  __m512 m_recip_approx = _mm512_rcp14_ps(m_vec);
428
- __m512 ratio_a_vec = _mm512_div_ps(a_vec, m_vec);
429
- __m512 ratio_b_vec = _mm512_div_ps(b_vec, m_vec);
428
+ __m512 ratio_a_vec = _mm512_mul_ps(a_vec, m_recip_approx);
429
+ __m512 ratio_b_vec = _mm512_mul_ps(b_vec, m_recip_approx);
430
430
  __m512 log_ratio_a_vec = simsimd_avx512_f32_log2(ratio_a_vec);
431
431
  __m512 log_ratio_b_vec = simsimd_avx512_f32_log2(ratio_b_vec);
432
432
  sum_a_vec = _mm512_maskz_fmadd_ps(nonzero_mask, a_vec, log_ratio_a_vec, sum_a_vec);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simsimd",
3
- "version": "3.5.3",
3
+ "version": "3.5.4",
4
4
  "description": "Vector Similarity Functions 3x-200x Faster than SciPy and NumPy",
5
5
  "author": "Ash Vardanian",
6
6
  "license": "Apache 2.0",
package/wasmer.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name="unum/usearch"
3
- version="2.8.8"
3
+ version="2.8.9"
4
4
  description="Smaller & Faster Single-File Vector Search Engine from Unum"
5
5
  license="Apache-2.0"
6
6
  readme="README.md"
@@ -1,67 +0,0 @@
1
- # SimSIMD library CMakeLists.txt
2
- cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
3
- project(simsimd LANGUAGES C CXX)
4
-
5
- set(CMAKE_C_STANDARD 11)
6
- set(CMAKE_C_STANDARD_REQUIRED YES)
7
- set(CMAKE_C_EXTENSIONS NO)
8
-
9
- set(CMAKE_CXX_STANDARD 17)
10
- set(CMAKE_CXX_STANDARD_REQUIRED YES)
11
- set(CMAKE_CXX_EXTENSIONS NO)
12
-
13
- option(SIMSIMD_BUILD_BENCHMARKS "Compile a micro-benchmark for current ISA" OFF)
14
-
15
- # Fetch external dependencies
16
- include(FetchContent)
17
-
18
- # Suppress building tests of Google Benchmark
19
- set(BENCHMARK_ENABLE_TESTING OFF)
20
- set(BENCHMARK_ENABLE_INSTALL OFF)
21
- set(BENCHMARK_ENABLE_DOXYGEN OFF)
22
- set(BENCHMARK_INSTALL_DOCS OFF)
23
- set(BENCHMARK_DOWNLOAD_DEPENDENCIES ON)
24
- set(BENCHMARK_ENABLE_GTEST_TESTS OFF)
25
- set(BENCHMARK_USE_BUNDLED_GTEST ON)
26
-
27
- FetchContent_Declare(
28
- benchmark
29
- GIT_REPOSITORY https://github.com/google/benchmark.git
30
- GIT_TAG v1.7.0)
31
- FetchContent_MakeAvailable(benchmark)
32
-
33
- # Default to Release build type if not set
34
- if(NOT CMAKE_BUILD_TYPE)
35
- set(CMAKE_BUILD_TYPE Release)
36
- endif()
37
-
38
- # Global compiler flags for debug and release
39
- set(CMAKE_CXX_FLAGS_DEBUG "-g -fsanitize=address")
40
- set(CMAKE_CXX_FLAGS_RELEASE "-O3")
41
- set(CMAKE_C_FLAGS_DEBUG "-g -fsanitize=address")
42
- set(CMAKE_C_FLAGS_RELEASE "-O3")
43
-
44
- # Compiler-specific flags
45
- if(CMAKE_CXX_COMPILER_ID MATCHES "^(Apple)?Clang$")
46
- if(NOT APPLE)
47
- add_compile_options(-march=native)
48
- endif()
49
- add_compile_options(-pedantic -ferror-limit=1)
50
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
51
- add_compile_options(-march=native -pedantic -fmax-errors=1 -Wno-tautological-constant-compare)
52
- elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
53
- add_compile_options(-w -ferror-limit=1)
54
- endif()
55
-
56
- # Define the header-only library
57
- file(GLOB SIMSIMD_SOURCES include/simsimd/*.h)
58
- add_library(simsimd INTERFACE)
59
- target_sources(simsimd INTERFACE ${SIMSIMD_SOURCES})
60
- target_include_directories(simsimd INTERFACE "${PROJECT_SOURCE_DIR}/include")
61
-
62
- # Build benchmarks if required
63
- if(SIMSIMD_BUILD_BENCHMARKS)
64
- find_package(Threads REQUIRED)
65
- add_executable(simsimd_bench cpp/bench.cxx)
66
- target_link_libraries(simsimd_bench simsimd Threads::Threads benchmark)
67
- endif()
@@ -1,206 +0,0 @@
1
- #include <cmath> // `std::sqrt`
2
- #include <thread> // `std::thread`
3
-
4
- #include <benchmark/benchmark.h>
5
-
6
- #define SIMSIMD_RSQRT(x) (1 / sqrtf(x))
7
- #define SIMSIMD_LOG(x) (logf(x))
8
- #include <simsimd/simsimd.h>
9
-
10
- namespace bm = benchmark;
11
-
12
- template <typename return_at, typename... args_at>
13
- constexpr std::size_t number_of_arguments(return_at (*f)(args_at...)) {
14
- return sizeof...(args_at);
15
- }
16
-
17
- template <typename scalar_at, std::size_t dimensions_ak> struct vectors_pair_gt {
18
- scalar_at a[dimensions_ak]{};
19
- scalar_at b[dimensions_ak]{};
20
-
21
- std::size_t dimensions() const noexcept { return dimensions_ak; }
22
- std::size_t size_bytes() const noexcept { return dimensions_ak * sizeof(scalar_at); }
23
-
24
- void set(scalar_at v) noexcept {
25
- for (std::size_t i = 0; i != dimensions_ak; ++i)
26
- a[i] = b[i] = v;
27
- }
28
-
29
- void randomize() noexcept {
30
-
31
- double a2_sum = 0, b2_sum = 0;
32
- for (std::size_t i = 0; i != dimensions_ak; ++i) {
33
- if constexpr (std::is_integral_v<scalar_at>)
34
- a[i] = static_cast<scalar_at>(rand()), b[i] = static_cast<scalar_at>(rand());
35
- else {
36
- double ai = double(rand()) / double(RAND_MAX), bi = double(rand()) / double(RAND_MAX);
37
- a2_sum += ai * ai, b2_sum += bi * bi;
38
- a[i] = static_cast<scalar_at>(ai), b[i] = static_cast<scalar_at>(bi);
39
- }
40
- }
41
-
42
- // Normalize the vectors:
43
- if constexpr (!std::is_integral_v<scalar_at>) {
44
- a2_sum = std::sqrt(a2_sum);
45
- b2_sum = std::sqrt(b2_sum);
46
- for (std::size_t i = 0; i != dimensions_ak; ++i)
47
- a[i] = static_cast<scalar_at>(a[i] / a2_sum), b[i] = static_cast<scalar_at>(b[i] / b2_sum);
48
- }
49
- }
50
- };
51
-
52
- template <typename pair_at, typename metric_at = void>
53
- static void measure(bm::State& state, metric_at metric, metric_at baseline) {
54
-
55
- pair_at pair;
56
- pair.randomize();
57
- // pair.set(1);
58
-
59
- double c_baseline = baseline(pair.a, pair.b, pair.dimensions());
60
- double c = 0;
61
- std::size_t iterations = 0;
62
- for (auto _ : state)
63
- bm::DoNotOptimize((c = metric(pair.a, pair.b, pair.dimensions()))), iterations++;
64
-
65
- state.counters["bytes"] = bm::Counter(iterations * pair.size_bytes() * 2, bm::Counter::kIsRate);
66
- state.counters["pairs"] = bm::Counter(iterations, bm::Counter::kIsRate);
67
-
68
- double delta = std::abs(c - c_baseline) > 0.0001 ? std::abs(c - c_baseline) : 0;
69
- double error = delta != 0 && c_baseline != 0 ? delta / c_baseline : 0;
70
- state.counters["abs_delta"] = delta;
71
- state.counters["relative_error"] = error;
72
- }
73
-
74
- template <typename scalar_at, typename metric_at = void>
75
- void register_(std::string name, metric_at* distance_func, metric_at* baseline_func) {
76
-
77
- using pair_dims_t = vectors_pair_gt<scalar_at, 1536>;
78
- using pair_bytes_t = vectors_pair_gt<scalar_at, 1536 / sizeof(scalar_at)>;
79
-
80
- std::size_t seconds = 10;
81
- std::size_t threads = 1; // std::thread::hardware_concurrency(); // 1;
82
- std::string name_dims = name + "_" + std::to_string(pair_dims_t{}.dimensions()) + "d";
83
- std::string name_bytes = name + "_" + std::to_string(pair_bytes_t{}.size_bytes()) + "b";
84
-
85
- bm::RegisterBenchmark(name_dims.c_str(), measure<pair_dims_t, metric_at*>, distance_func, baseline_func)
86
- ->MinTime(seconds)
87
- ->Threads(threads);
88
- bm::RegisterBenchmark(name_bytes.c_str(), measure<pair_bytes_t, metric_at*>, distance_func, baseline_func)
89
- ->MinTime(seconds)
90
- ->Threads(threads);
91
- }
92
-
93
- int main(int argc, char** argv) {
94
-
95
- bool compiled_with_sve = false;
96
- bool compiled_with_neon = false;
97
- bool compiled_with_avx2 = false;
98
- bool compiled_with_avx512vpopcntdq = false;
99
- bool compiled_with_avx512vnni = false;
100
-
101
- #if defined(__ARM_FEATURE_SVE)
102
- compiled_with_sve = true;
103
- #endif
104
- #if defined(__ARM_NEON)
105
- compiled_with_neon = true;
106
- #endif
107
- #if defined(__AVX2__)
108
- compiled_with_avx2 = true;
109
- #endif
110
- #if defined(__AVX512VPOPCNTDQ__)
111
- compiled_with_avx512vpopcntdq = true;
112
- #endif
113
- #if defined(__AVX512VNNI__)
114
- compiled_with_avx512vnni = true;
115
- #endif
116
-
117
- // Log supported functionality
118
- char const* flags[2] = {"false", "true"};
119
- std::printf("Benchmarking Similarity Measures\n");
120
- std::printf("\n");
121
- std::printf("- Arm NEON support enabled: %s\n", flags[compiled_with_neon]);
122
- std::printf("- Arm SVE support enabled: %s\n", flags[compiled_with_sve]);
123
- std::printf("- x86 AVX2 support enabled: %s\n", flags[compiled_with_avx2]);
124
- std::printf("- x86 AVX512VPOPCNTDQ support enabled: %s\n", flags[compiled_with_avx512vpopcntdq]);
125
- std::printf("- x86 AVX512VNNI support enabled: %s\n", flags[compiled_with_avx512vnni]);
126
- std::printf("\n");
127
-
128
- // Run the benchmarks
129
- bm::Initialize(&argc, argv);
130
- if (bm::ReportUnrecognizedArguments(argc, argv))
131
- return 1;
132
-
133
- #if SIMSIMD_TARGET_ARM_NEON
134
- register_<simsimd_f16_t>("neon_f16_ip", simsimd_neon_f16_ip, simsimd_accurate_f16_ip);
135
- register_<simsimd_f16_t>("neon_f16_cos", simsimd_neon_f16_cos, simsimd_accurate_f16_cos);
136
- register_<simsimd_f16_t>("neon_f16_l2sq", simsimd_neon_f16_l2sq, simsimd_accurate_f16_l2sq);
137
- register_<simsimd_f16_t>("neon_f16_kl", simsimd_neon_f16_kl, simsimd_accurate_f16_kl);
138
- register_<simsimd_f16_t>("neon_f16_js", simsimd_neon_f16_js, simsimd_accurate_f16_js);
139
-
140
- register_<simsimd_f32_t>("neon_f32_ip", simsimd_neon_f32_ip, simsimd_accurate_f32_ip);
141
- register_<simsimd_f32_t>("neon_f32_cos", simsimd_neon_f32_cos, simsimd_accurate_f32_cos);
142
- register_<simsimd_f32_t>("neon_f32_l2sq", simsimd_neon_f32_l2sq, simsimd_accurate_f32_l2sq);
143
- register_<simsimd_f32_t>("neon_f32_kl", simsimd_neon_f32_kl, simsimd_accurate_f32_kl);
144
- register_<simsimd_f32_t>("neon_f32_js", simsimd_neon_f32_js, simsimd_accurate_f32_js);
145
-
146
- register_<simsimd_i8_t>("neon_i8_cos", simsimd_neon_i8_cos, simsimd_accurate_i8_cos);
147
- register_<simsimd_i8_t>("neon_i8_l2sq", simsimd_neon_i8_l2sq, simsimd_accurate_i8_l2sq);
148
- #endif
149
-
150
- #if SIMSIMD_TARGET_ARM_SVE
151
- register_<simsimd_f16_t>("sve_f16_ip", simsimd_sve_f16_ip, simsimd_accurate_f16_ip);
152
- register_<simsimd_f16_t>("sve_f16_cos", simsimd_sve_f16_cos, simsimd_accurate_f16_cos);
153
- register_<simsimd_f16_t>("sve_f16_l2sq", simsimd_sve_f16_l2sq, simsimd_accurate_f16_l2sq);
154
-
155
- register_<simsimd_f32_t>("sve_f32_ip", simsimd_sve_f32_ip, simsimd_accurate_f32_ip);
156
- register_<simsimd_f32_t>("sve_f32_cos", simsimd_sve_f32_cos, simsimd_accurate_f32_cos);
157
- register_<simsimd_f32_t>("sve_f32_l2sq", simsimd_sve_f32_l2sq, simsimd_accurate_f32_l2sq);
158
- #endif
159
-
160
- #if SIMSIMD_TARGET_X86_AVX2
161
- register_<simsimd_f16_t>("avx2_f16_ip", simsimd_avx2_f16_ip, simsimd_accurate_f16_ip);
162
- register_<simsimd_f16_t>("avx2_f16_cos", simsimd_avx2_f16_cos, simsimd_accurate_f16_cos);
163
- register_<simsimd_f16_t>("avx2_f16_l2sq", simsimd_avx2_f16_l2sq, simsimd_accurate_f16_l2sq);
164
- register_<simsimd_f16_t>("avx2_f16_kl", simsimd_avx2_f16_kl, simsimd_accurate_f16_kl);
165
- register_<simsimd_f16_t>("avx2_f16_js", simsimd_avx2_f16_js, simsimd_accurate_f16_js);
166
-
167
- register_<simsimd_i8_t>("avx2_i8_cos", simsimd_avx2_i8_cos, simsimd_accurate_i8_cos);
168
- register_<simsimd_i8_t>("avx2_i8_l2sq", simsimd_avx2_i8_l2sq, simsimd_accurate_i8_l2sq);
169
- #endif
170
-
171
- #if SIMSIMD_TARGET_X86_AVX512
172
- register_<simsimd_f16_t>("avx512_f16_ip", simsimd_avx512_f16_ip, simsimd_accurate_f16_ip);
173
- register_<simsimd_f16_t>("avx512_f16_cos", simsimd_avx512_f16_cos, simsimd_accurate_f16_cos);
174
- register_<simsimd_f16_t>("avx512_f16_l2sq", simsimd_avx512_f16_l2sq, simsimd_accurate_f16_l2sq);
175
- register_<simsimd_f16_t>("avx512_f16_kl", simsimd_avx512_f16_kl, simsimd_accurate_f16_kl);
176
- register_<simsimd_f16_t>("avx512_f16_js", simsimd_avx512_f16_js, simsimd_accurate_f16_js);
177
-
178
- register_<simsimd_i8_t>("avx512_i8_cos", simsimd_avx512_i8_cos, simsimd_accurate_i8_cos);
179
- register_<simsimd_i8_t>("avx512_i8_l2sq", simsimd_avx512_i8_l2sq, simsimd_accurate_i8_l2sq);
180
-
181
- register_<simsimd_f32_t>("avx512_f32_ip", simsimd_avx512_f32_ip, simsimd_accurate_f32_ip);
182
- register_<simsimd_f32_t>("avx512_f32_cos", simsimd_avx512_f32_cos, simsimd_accurate_f32_cos);
183
- register_<simsimd_f32_t>("avx512_f32_l2sq", simsimd_avx512_f32_l2sq, simsimd_accurate_f32_l2sq);
184
- register_<simsimd_f32_t>("avx512_f32_kl", simsimd_avx512_f32_kl, simsimd_accurate_f32_kl);
185
- register_<simsimd_f32_t>("avx512_f32_js", simsimd_avx512_f32_js, simsimd_accurate_f32_js);
186
- #endif
187
-
188
- register_<simsimd_f16_t>("serial_f16_ip", simsimd_serial_f16_ip, simsimd_accurate_f16_ip);
189
- register_<simsimd_f16_t>("serial_f16_cos", simsimd_serial_f16_cos, simsimd_accurate_f16_cos);
190
- register_<simsimd_f16_t>("serial_f16_l2sq", simsimd_serial_f16_l2sq, simsimd_accurate_f16_l2sq);
191
- register_<simsimd_f16_t>("serial_f16_kl", simsimd_serial_f16_kl, simsimd_accurate_f16_kl);
192
- register_<simsimd_f16_t>("serial_f16_js", simsimd_serial_f16_js, simsimd_accurate_f16_js);
193
-
194
- register_<simsimd_f32_t>("serial_f32_ip", simsimd_serial_f32_ip, simsimd_accurate_f32_ip);
195
- register_<simsimd_f32_t>("serial_f32_cos", simsimd_serial_f32_cos, simsimd_accurate_f32_cos);
196
- register_<simsimd_f32_t>("serial_f32_l2sq", simsimd_serial_f32_l2sq, simsimd_accurate_f32_l2sq);
197
- register_<simsimd_f32_t>("serial_f32_kl", simsimd_serial_f32_kl, simsimd_accurate_f32_kl);
198
- register_<simsimd_f32_t>("serial_f32_js", simsimd_serial_f32_js, simsimd_accurate_f32_js);
199
-
200
- register_<simsimd_i8_t>("serial_i8_cos", simsimd_serial_i8_cos, simsimd_accurate_i8_cos);
201
- register_<simsimd_i8_t>("serial_i8_l2sq", simsimd_serial_i8_l2sq, simsimd_accurate_i8_l2sq);
202
-
203
- bm::RunSpecifiedBenchmarks();
204
- bm::Shutdown();
205
- return 0;
206
- }
@@ -1,10 +0,0 @@
1
- {
2
- "name": "simsimd-ci",
3
- "version": "1.0.0",
4
- "devDependencies": {
5
- "@semantic-release/exec": "^6.0.3",
6
- "@semantic-release/git": "^10.0.1",
7
- "conventional-changelog-eslint": "^3.0.9",
8
- "semantic-release": "^21.1.2"
9
- }
10
- }
package/simsimd/setup.py DELETED
@@ -1,86 +0,0 @@
1
- import os
2
- import sys
3
- import platform
4
- from os.path import dirname
5
- from setuptools import setup, Extension
6
-
7
- import glob
8
- import numpy
9
-
10
- __version__ = open("VERSION", "r").read().strip()
11
- __lib_name__ = "simsimd"
12
-
13
- compile_args = []
14
- link_args = []
15
- macros_args = []
16
-
17
- if sys.platform == "linux":
18
- compile_args.append("-std=c11")
19
- compile_args.append("-O3")
20
- compile_args.append("-ffast-math")
21
- compile_args.append("-fdiagnostics-color=always")
22
-
23
- # Disable warnings
24
- compile_args.append("-w")
25
-
26
- # Enable OpenMP for Linux
27
- compile_args.append("-fopenmp")
28
- link_args.append("-lgomp")
29
-
30
- # Add vectorized `logf` implementation from the `glibc`
31
- link_args.append("-lm")
32
-
33
- if sys.platform == "darwin":
34
- compile_args.append("-std=c11")
35
- compile_args.append("-O3")
36
- compile_args.append("-ffast-math")
37
-
38
- # Disable warnings
39
- compile_args.append("-w")
40
-
41
- if sys.platform == "win32":
42
- compile_args.append("/std:c11")
43
- compile_args.append("/O2")
44
- compile_args.append("/fp:fast")
45
-
46
- # Dealing with MinGW linking errors
47
- # https://cibuildwheel.readthedocs.io/en/stable/faq/#windows-importerror-dll-load-failed-the-specific-module-could-not-be-found
48
- compile_args.append("/d2FH4-")
49
-
50
- ext_modules = [
51
- Extension(
52
- "simsimd",
53
- sources=["python/lib.c"],
54
- include_dirs=["include", numpy.get_include()],
55
- extra_compile_args=compile_args,
56
- extra_link_args=link_args,
57
- define_macros=macros_args,
58
- ),
59
- ]
60
-
61
- this_directory = os.path.abspath(dirname(__file__))
62
- with open(os.path.join(this_directory, "README.md"), "r", encoding="utf8") as f:
63
- long_description = f.read()
64
-
65
- setup(
66
- name=__lib_name__,
67
- version=__version__,
68
- author="Ash Vardanian",
69
- author_email="1983160+ashvardanian@users.noreply.github.com",
70
- url="https://github.com/ashvardanian/simsimd",
71
- description="Vector Similarity Functions 3x-200x Faster than SciPy and NumPy",
72
- long_description=long_description,
73
- long_description_content_type="text/markdown",
74
- license="Apache-2.0",
75
- classifiers=[
76
- "Development Status :: 5 - Production/Stable",
77
- "Natural Language :: English",
78
- "Intended Audience :: Developers",
79
- "Intended Audience :: Information Technology",
80
- "License :: OSI Approved :: Apache Software License",
81
- "Programming Language :: C",
82
- "Programming Language :: Python :: Implementation :: CPython",
83
- ],
84
- ext_modules=ext_modules,
85
- zip_safe=False,
86
- )