usearch 2.8.8 → 2.8.10
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 +1 -1
- package/CONTRIBUTING.md +23 -0
- package/Cargo.toml +1 -1
- package/README.md +54 -30
- package/VERSION +1 -1
- package/conanfile.py +1 -1
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_plugins.hpp +2 -1
- package/javascript/README.md +64 -12
- package/javascript/lib.cpp +11 -3
- package/javascript/usearch.js +4 -2
- package/javascript/usearch.test.js +15 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/setup.py +7 -14
- package/simsimd/README.md +23 -21
- package/simsimd/include/simsimd/binary.h +5 -2
- package/simsimd/include/simsimd/probability.h +7 -4
- package/simsimd/include/simsimd/simsimd.h +5 -1
- package/simsimd/include/simsimd/spatial.h +4 -1
- package/simsimd/include/simsimd/types.h +34 -40
- package/simsimd/package.json +1 -1
- package/wasmer.toml +1 -1
- package/simsimd/CMakeLists.txt +0 -67
- package/simsimd/cpp/bench.cxx +0 -206
- package/simsimd/package-ci.json +0 -10
- package/simsimd/setup.py +0 -86
package/CITATION.cff
CHANGED
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
package/README.md
CHANGED
|
@@ -50,6 +50,7 @@ Linux • MacOS • Windows • iOS • WebAssembly
|
|
|
50
50
|
- ✅ [20x faster than][faster-than-faiss] [FAISS][faiss] implementation of [HNSW algorithm][hnsw-algorithm].
|
|
51
51
|
- ✅ Simple and extensible [single C++11 header][usearch-header] implementation.
|
|
52
52
|
- ✅ Compatible with a dozen programming languages out of the box.
|
|
53
|
+
- ✅ [Trusted](#integrations) by some of the most loved Datalakes and Databases, like [ClickHouse][clickhouse-docs].
|
|
53
54
|
- ✅ [SIMD][simd]-optimized and [user-defined metrics](#user-defined-functions) with JIT compilation.
|
|
54
55
|
- ✅ Hardware-agnostic `f16` & `i8` - [half-precision & quarter-precision support](#memory-efficiency-downcasting-and-quantization).
|
|
55
56
|
- ✅ [View large indexes from disk](#serving-index-from-disk) without loading into RAM.
|
|
@@ -67,6 +68,17 @@ Linux • MacOS • Windows • iOS • WebAssembly
|
|
|
67
68
|
[hnsw-algorithm]: https://arxiv.org/abs/1603.09320
|
|
68
69
|
[simd]: https://en.wikipedia.org/wiki/Single_instruction,_multiple_data
|
|
69
70
|
[faster-than-faiss]: https://www.unum.cloud/blog/2023-11-07-scaling-vector-search-with-intel
|
|
71
|
+
[clickhouse-docs]: https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/annindexes#usearch
|
|
72
|
+
|
|
73
|
+
__Technical Insights__ and related articles:
|
|
74
|
+
|
|
75
|
+
- [Uses Horner's method for polynomial approximations, beating GCC 12 by 119x](https://ashvardanian.com/posts/gcc-12-vs-avx512fp16/).
|
|
76
|
+
- [Uses Arm SVE and x86 AVX-512's masked loads to eliminate tail `for`-loops](https://ashvardanian.com/posts/simsimd-faster-scipy/#tails-of-the-past-the-significance-of-masked-loads).
|
|
77
|
+
- [Uses AVX-512 FP16 for half-precision operations, that few compilers vectorize](https://ashvardanian.com/posts/simsimd-faster-scipy/#the-challenge-of-f16).
|
|
78
|
+
- [Substitutes LibC's `sqrt` calls with bithacks using Jan Kadlec's constant](https://ashvardanian.com/posts/simsimd-faster-scipy/#bonus-section-bypassing-sqrt-and-libc-dependencies).
|
|
79
|
+
- [For every language implements a custom separate binding](https://ashvardanian.com/posts/porting-cpp-library-to-ten-languages/).
|
|
80
|
+
- [For Python avoids slow PyBind11, and even `PyArg_ParseTuple` for speed](https://ashvardanian.com/posts/pybind11-cpython-tutorial/).
|
|
81
|
+
- [For JavaScript uses typed arrays and NAPI for zero-copy calls](https://ashvardanian.com/posts/javascript-ai-vector-search/).
|
|
70
82
|
|
|
71
83
|
## Comparison with FAISS
|
|
72
84
|
|
|
@@ -132,34 +144,6 @@ index = Index(
|
|
|
132
144
|
)
|
|
133
145
|
```
|
|
134
146
|
|
|
135
|
-
## User-Defined Functions
|
|
136
|
-
|
|
137
|
-
While most vector search packages concentrate on just a few metrics - "Inner Product distance" and "Euclidean distance," USearch extends this list to include any user-defined metrics.
|
|
138
|
-
This flexibility allows you to customize your search for various applications, from computing geospatial coordinates with the rare [Haversine][haversine] distance to creating custom metrics for composite embeddings from multiple AI models.
|
|
139
|
-
|
|
140
|
-

|
|
141
|
-
|
|
142
|
-
Unlike older approaches indexing high-dimensional spaces, like KD-Trees and Locality Sensitive Hashing, HNSW doesn't require vectors to be identical in length.
|
|
143
|
-
They only have to be comparable.
|
|
144
|
-
So you can apply it in [obscure][obscure] applications, like searching for similar sets or fuzzy text matching, using [GZip][gzip-similarity] as a distance function.
|
|
145
|
-
|
|
146
|
-
> Read more about [JIT and UDF in USearch Python SDK](https://unum-cloud.github.io/usearch/python#user-defined-metrics-and-jit-in-python).
|
|
147
|
-
|
|
148
|
-
[haversine]: https://ashvardanian.com/posts/abusing-vector-search#geo-spatial-indexing
|
|
149
|
-
[obscure]: https://ashvardanian.com/posts/abusing-vector-search
|
|
150
|
-
[gzip-similarity]: https://twitter.com/LukeGessler/status/1679211291292889100?s=20
|
|
151
|
-
|
|
152
|
-
## Memory Efficiency, Downcasting, and Quantization
|
|
153
|
-
|
|
154
|
-
Training a quantization model and dimension-reduction is a common approach to accelerate vector search.
|
|
155
|
-
Those, however, are only sometimes reliable, can significantly affect the statistical properties of your data, and require regular adjustments if your distribution shifts.
|
|
156
|
-
|
|
157
|
-

|
|
158
|
-
|
|
159
|
-
Instead, we have focused on high-precision arithmetic over low-precision downcasted vectors.
|
|
160
|
-
The same index, and `add` and `search` operations will automatically down-cast or up-cast between `f32_t`, `f16_t`, `f64_t`, and `i8_t` representations, even if the hardware doesn't natively support it.
|
|
161
|
-
Continuing the topic of memory efficiency, we provide a `uint40_t` to allow collection with over 4B+ vectors without allocating 8 bytes for every neighbor reference in the proximity graph.
|
|
162
|
-
|
|
163
147
|
## Serialization & Serving `Index` from Disk
|
|
164
148
|
|
|
165
149
|
USearch supports multiple forms of serialization:
|
|
@@ -275,16 +259,56 @@ pairs: dict = men.join(women, max_proposals=0, exact=False)
|
|
|
275
259
|
|
|
276
260
|
> Read more in the post: [Combinatorial Stable Marriages for Semantic Search 💍](https://ashvardanian.com/posts/searching-stable-marriages)
|
|
277
261
|
|
|
262
|
+
## User-Defined Functions
|
|
263
|
+
|
|
264
|
+
While most vector search packages concentrate on just a few metrics - "Inner Product distance" and "Euclidean distance," USearch extends this list to include any user-defined metrics.
|
|
265
|
+
This flexibility allows you to customize your search for various applications, from computing geospatial coordinates with the rare [Haversine][haversine] distance to creating custom metrics for composite embeddings from multiple AI models.
|
|
266
|
+
|
|
267
|
+

|
|
268
|
+
|
|
269
|
+
Unlike older approaches indexing high-dimensional spaces, like KD-Trees and Locality Sensitive Hashing, HNSW doesn't require vectors to be identical in length.
|
|
270
|
+
They only have to be comparable.
|
|
271
|
+
So you can apply it in [obscure][obscure] applications, like searching for similar sets or fuzzy text matching, using [GZip][gzip-similarity] as a distance function.
|
|
272
|
+
|
|
273
|
+
> Read more about [JIT and UDF in USearch Python SDK](https://unum-cloud.github.io/usearch/python#user-defined-metrics-and-jit-in-python).
|
|
274
|
+
|
|
275
|
+
[haversine]: https://ashvardanian.com/posts/abusing-vector-search#geo-spatial-indexing
|
|
276
|
+
[obscure]: https://ashvardanian.com/posts/abusing-vector-search
|
|
277
|
+
[gzip-similarity]: https://twitter.com/LukeGessler/status/1679211291292889100?s=20
|
|
278
|
+
|
|
279
|
+
## Memory Efficiency, Downcasting, and Quantization
|
|
280
|
+
|
|
281
|
+
Training a quantization model and dimension-reduction is a common approach to accelerate vector search.
|
|
282
|
+
Those, however, are only sometimes reliable, can significantly affect the statistical properties of your data, and require regular adjustments if your distribution shifts.
|
|
283
|
+
Instead, we have focused on high-precision arithmetic over low-precision downcasted vectors.
|
|
284
|
+
The same index, and `add` and `search` operations will automatically down-cast or up-cast between `f64_t`, `f32_t`, `f16_t`, `i8_t`, and single-bit representations.
|
|
285
|
+
You can use the following command to check, if hardware acceleration is enabled:
|
|
286
|
+
|
|
287
|
+
```sh
|
|
288
|
+
$ python -c 'from usearch.index import Index; print(Index(ndim=768, metric="cos", dtype="f16").hardware_acceleration)'
|
|
289
|
+
> avx512+f16
|
|
290
|
+
$ python -c 'from usearch.index import Index; print(Index(ndim=166, metric="tanimoto").hardware_acceleration)'
|
|
291
|
+
> avx512+popcnt
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Using smaller numeric types will save you RAM needed to store the vectors, but you can also compress the neighbors lists forming our proximity graphs.
|
|
295
|
+
By default, 32-bit `uint32_t` is used to enumerate those, which is not enough if you need to address over 4 Billion entries.
|
|
296
|
+
For such cases we provide a custom `uint40_t` type, that will still be 37.5% more space-efficient than the commonly used 8-byte integers, and will scale up to 1 Trillion entries.
|
|
297
|
+
|
|
298
|
+

|
|
299
|
+
|
|
278
300
|
## Functionality
|
|
279
301
|
|
|
280
302
|
By now, the core functionality is supported across all bindings.
|
|
281
303
|
Broader functionality is ported per request.
|
|
304
|
+
In some cases, like Batch operations, feature parity is meaningless, as the host language has full multi-threading capabilities and the USearch index structure is concurrent by design, so the users can implement batching/scheduling/load-balancing in the most optimal way for their applications.
|
|
282
305
|
|
|
283
306
|
| | C++ 11 | Python 3 | C 99 | Java | JavaScript | Rust | GoLang | Swift |
|
|
284
307
|
| :---------------------- | :----: | :------: | :---: | :---: | :--------: | :---: | :----: | :---: |
|
|
285
|
-
| Add, search
|
|
308
|
+
| Add, search, remove | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
286
309
|
| Save, load, view | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
287
310
|
| User-defined metrics | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
311
|
+
| Batch operations | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
|
288
312
|
| Joins | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
289
313
|
| Variable-length vectors | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
290
314
|
| 4B+ capacities | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
@@ -400,7 +424,7 @@ doi = {10.5281/zenodo.7949416},
|
|
|
400
424
|
author = {Vardanian, Ash},
|
|
401
425
|
title = {{USearch by Unum Cloud}},
|
|
402
426
|
url = {https://github.com/unum-cloud/usearch},
|
|
403
|
-
version = {2.8.
|
|
427
|
+
version = {2.8.10},
|
|
404
428
|
year = {2023},
|
|
405
429
|
month = oct,
|
|
406
430
|
}
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.8.
|
|
1
|
+
2.8.10
|
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.
|
|
10
|
+
version = '2.8.10'
|
|
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"
|
|
@@ -1402,6 +1402,7 @@ class metric_punned_t {
|
|
|
1402
1402
|
case metric_kind_t::cos_k: kind = simsimd_metric_cos_k; break;
|
|
1403
1403
|
case metric_kind_t::l2sq_k: kind = simsimd_metric_l2sq_k; break;
|
|
1404
1404
|
case metric_kind_t::hamming_k: kind = simsimd_metric_hamming_k; break;
|
|
1405
|
+
case metric_kind_t::tanimoto_k: kind = simsimd_metric_jaccard_k; break;
|
|
1405
1406
|
case metric_kind_t::jaccard_k: kind = simsimd_metric_jaccard_k; break;
|
|
1406
1407
|
default: break;
|
|
1407
1408
|
}
|
|
@@ -1419,7 +1420,7 @@ class metric_punned_t {
|
|
|
1419
1420
|
if (simd_metric == nullptr)
|
|
1420
1421
|
return false;
|
|
1421
1422
|
|
|
1422
|
-
raw_ptr_
|
|
1423
|
+
std::memcpy(&raw_ptr_, &simd_metric, sizeof(simd_metric));
|
|
1423
1424
|
isa_kind_ = simd_kind;
|
|
1424
1425
|
return true;
|
|
1425
1426
|
}
|
package/javascript/README.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
22
|
-
index.
|
|
23
|
-
|
|
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
|
-
|
|
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.
|
|
34
|
-
index.
|
|
35
|
-
index.
|
|
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
|
```
|
package/javascript/lib.cpp
CHANGED
|
@@ -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
|
-
|
|
200
|
-
|
|
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);
|
package/javascript/usearch.js
CHANGED
|
@@ -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
|
|
253
|
+
return batchMatches.get(0);
|
|
252
254
|
} else {
|
|
253
|
-
return
|
|
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
package/pyproject.toml
CHANGED
|
@@ -9,7 +9,7 @@ xfail_strict = true
|
|
|
9
9
|
filterwarnings = ["error"]
|
|
10
10
|
|
|
11
11
|
# Avoid running tests, as everything is happening in a super slow container
|
|
12
|
-
#
|
|
12
|
+
# We have already run all the relavent Python tests in `prerelease.yml`
|
|
13
13
|
# test-requires = ["pytest", "numpy"]
|
|
14
14
|
# test-command = "pytest {project}/python/scripts"
|
|
15
15
|
[tool.cibuildwheel]
|
package/setup.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
|
-
import
|
|
3
|
+
import platform
|
|
4
4
|
from setuptools import setup
|
|
5
5
|
|
|
6
6
|
from pybind11.setup_helpers import Pybind11Extension
|
|
@@ -14,28 +14,19 @@ def get_bool_env(name: str, preference: bool) -> bool:
|
|
|
14
14
|
return os.environ.get(name, "1" if preference else "0") == "1"
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def get_bool_env_w_name(name: str, preference: bool) ->
|
|
18
|
-
return name, get_bool_env(name, preference)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def get_is_gcc():
|
|
22
|
-
try:
|
|
23
|
-
compiler_version = subprocess.check_output(
|
|
24
|
-
["c++", "--version"], universal_newlines=True
|
|
25
|
-
)
|
|
26
|
-
return "gcc" in compiler_version.lower()
|
|
27
|
-
except Exception:
|
|
28
|
-
return False
|
|
17
|
+
def get_bool_env_w_name(name: str, preference: bool) -> tuple:
|
|
18
|
+
return name, "1" if get_bool_env(name, preference) else "0"
|
|
29
19
|
|
|
30
20
|
|
|
31
21
|
# Check the environment variables
|
|
22
|
+
is_gcc = "GCC" in platform.python_compiler().upper()
|
|
32
23
|
is_linux: bool = sys.platform == "linux"
|
|
33
24
|
is_macos: bool = sys.platform == "darwin"
|
|
34
25
|
is_windows: bool = sys.platform == "win32"
|
|
35
26
|
|
|
36
27
|
prefer_simsimd: bool = is_linux or is_macos
|
|
37
28
|
prefer_fp16lib: bool = True
|
|
38
|
-
prefer_openmp: bool = is_linux and
|
|
29
|
+
prefer_openmp: bool = is_linux and is_gcc
|
|
39
30
|
|
|
40
31
|
use_simsimd: bool = get_bool_env("USEARCH_USE_SIMSIMD", prefer_simsimd)
|
|
41
32
|
use_fp16lib: bool = get_bool_env("USEARCH_USE_FP16LIB", prefer_fp16lib)
|
|
@@ -123,6 +114,8 @@ if is_windows:
|
|
|
123
114
|
]
|
|
124
115
|
)
|
|
125
116
|
|
|
117
|
+
print("macros_args", macros_args)
|
|
118
|
+
|
|
126
119
|
ext_modules = [
|
|
127
120
|
Pybind11Extension(
|
|
128
121
|
"usearch.compiled",
|
package/simsimd/README.md
CHANGED
|
@@ -24,38 +24,38 @@ SimSIMD leverages SIMD intrinsics, capabilities that only select compilers effec
|
|
|
24
24
|
[scipy]: https://docs.scipy.org/doc/scipy/reference/spatial.distance.html#module-scipy.spatial.distance
|
|
25
25
|
[numpy]: https://numpy.org/doc/stable/reference/generated/numpy.inner.html
|
|
26
26
|
|
|
27
|
+
__Technical Insights__ and related articles:
|
|
28
|
+
|
|
29
|
+
- [Uses Horner's method for polynomial approximations, beating GCC 12 by 119x](https://ashvardanian.com/posts/gcc-12-vs-avx512fp16/).
|
|
30
|
+
- [Uses Arm SVE and x86 AVX-512's masked loads to eliminate tail `for`-loops](https://ashvardanian.com/posts/simsimd-faster-scipy/#tails-of-the-past-the-significance-of-masked-loads).
|
|
31
|
+
- [Uses AVX-512 FP16 for half-precision operations, that few compilers vectorize](https://ashvardanian.com/posts/simsimd-faster-scipy/#the-challenge-of-f16).
|
|
32
|
+
- [Substitutes LibC's `sqrt` calls with bithacks using Jan Kadlec's constant](https://ashvardanian.com/posts/simsimd-faster-scipy/#bonus-section-bypassing-sqrt-and-libc-dependencies).
|
|
33
|
+
- [For Python avoids slow PyBind11, SWIG, and even `PyArg_ParseTuple` for speed](https://ashvardanian.com/posts/pybind11-cpython-tutorial/).
|
|
34
|
+
- [For JavaScript uses typed arrays and NAPI for zero-copy calls](https://ashvardanian.com/posts/javascript-ai-vector-search/).
|
|
35
|
+
|
|
27
36
|
## Benchmarks
|
|
28
37
|
|
|
29
38
|
### Apple M2 Pro
|
|
30
39
|
|
|
31
40
|
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
41
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
| `scipy.spatial.distance.jensenshannon` | `jensenshannon`
|
|
39
|
-
| `scipy.special.kl_div` | `kullbackleibler` | __21 x__ | __18 x__ | |
|
|
42
|
+
| Kind | `f32` improvement | `f16` improvement | `i8` improvement | Conventional method | SimSIMD |
|
|
43
|
+
| :------------- | ----------------: | ----------------: | ---------------: | :------------------------------------- | :-------------- |
|
|
44
|
+
| Cosine | __32 x__ | __79 x__ | __133 x__ | `scipy.spatial.distance.cosine` | `cosine` |
|
|
45
|
+
| Euclidean ² | __5 x__ | __26 x__ | __17 x__ | `scipy.spatial.distance.sqeuclidean` | `sqeuclidean` |
|
|
46
|
+
| Inner Product | __2 x__ | __9 x__ | __18 x__ | `numpy.inner` | `inner` |
|
|
47
|
+
| Jensen Shannon | __31 x__ | __53 x__ | | `scipy.spatial.distance.jensenshannon` | `jensenshannon` |
|
|
40
48
|
|
|
41
49
|
### Intel Sapphire Rapids
|
|
42
50
|
|
|
43
51
|
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
52
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
52
|
-
__Technical Insights__:
|
|
53
|
-
|
|
54
|
-
- [Uses Arm SVE and x86 AVX-512's masked loads to eliminate tail `for`-loops](https://ashvardanian.com/posts/simsimd-faster-scipy/#tails-of-the-past-the-significance-of-masked-loads).
|
|
55
|
-
- [Uses AVX-512 FP16 for half-precision operations, that few compilers vectorize](https://ashvardanian.com/posts/simsimd-faster-scipy/#the-challenge-of-f16).
|
|
56
|
-
- [Substitutes LibC's `sqrt` calls with bithacks using Jan Kadlec's constant](https://ashvardanian.com/posts/simsimd-faster-scipy/#bonus-section-bypassing-sqrt-and-libc-dependencies).
|
|
57
|
-
- Avoids slow PyBind11 and SWIG, directly using the CPython C API.
|
|
58
|
-
- Avoids slow `PyArg_ParseTuple` and manually unpacks argument tuples.
|
|
53
|
+
| Kind | GCC 12 `f32` | GCC 12 `f16` | SimSIMD `f16` | `f16` improvement |
|
|
54
|
+
| :------------- | -----------: | -----------: | ------------: | ----------------: |
|
|
55
|
+
| Cosine | 3.28 M/s | _336.29 k/s_ | _6.88 M/s_ | __20 x__ |
|
|
56
|
+
| Euclidean ² | 4.62 M/s | _147.25 k/s_ | _5.32 M/s_ | __36 x__ |
|
|
57
|
+
| Inner Product | 3.81 M/s | _192.02 k/s_ | _5.99 M/s_ | __31 x__ |
|
|
58
|
+
| Jensen Shannon | 1.18 M/s | _18.13 k/s_ | _2.14 M/s_ | __118 x__ |
|
|
59
59
|
|
|
60
60
|
__Broader Benchmarking Results__:
|
|
61
61
|
|
|
@@ -173,6 +173,8 @@ Stay updated with the latest advancements by always using the most recent compil
|
|
|
173
173
|
|
|
174
174
|
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
175
|
|
|
176
|
+
## Benchmarking and Contributing
|
|
177
|
+
|
|
176
178
|
__To rerun experiments__ utilize the following command:
|
|
177
179
|
|
|
178
180
|
```sh
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
* x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
|
|
18
18
|
* Arm intrinsics: https://developer.arm.com/architectures/instruction-sets/intrinsics/
|
|
19
19
|
*/
|
|
20
|
+
#ifndef SIMSIMD_BINARY_H
|
|
21
|
+
#define SIMSIMD_BINARY_H
|
|
20
22
|
|
|
21
|
-
#pragma once
|
|
22
23
|
#include "types.h"
|
|
23
24
|
|
|
24
25
|
#ifdef __cplusplus
|
|
@@ -195,4 +196,6 @@ simsimd_avx512_b8_jaccard_cycle:
|
|
|
195
196
|
|
|
196
197
|
#ifdef __cplusplus
|
|
197
198
|
}
|
|
198
|
-
#endif
|
|
199
|
+
#endif
|
|
200
|
+
|
|
201
|
+
#endif
|
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
* x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
|
|
19
19
|
* Arm intrinsics: https://developer.arm.com/architectures/instruction-sets/intrinsics/
|
|
20
20
|
*/
|
|
21
|
+
#ifndef SIMSIMD_PROBABILITY_H
|
|
22
|
+
#define SIMSIMD_PROBABILITY_H
|
|
21
23
|
|
|
22
|
-
#pragma once
|
|
23
24
|
#include "types.h"
|
|
24
25
|
|
|
25
26
|
#define SIMSIMD_MAKE_KL(name, input_type, accumulator_type, converter, epsilon) \
|
|
@@ -425,8 +426,8 @@ simsimd_avx512_f32_js_cycle:
|
|
|
425
426
|
__mmask16 nonzero_mask_b = _mm512_cmp_ps_mask(b_vec, epsilon_vec, _CMP_GE_OQ);
|
|
426
427
|
__mmask16 nonzero_mask = nonzero_mask_a & nonzero_mask_b;
|
|
427
428
|
__m512 m_recip_approx = _mm512_rcp14_ps(m_vec);
|
|
428
|
-
__m512 ratio_a_vec =
|
|
429
|
-
__m512 ratio_b_vec =
|
|
429
|
+
__m512 ratio_a_vec = _mm512_mul_ps(a_vec, m_recip_approx);
|
|
430
|
+
__m512 ratio_b_vec = _mm512_mul_ps(b_vec, m_recip_approx);
|
|
430
431
|
__m512 log_ratio_a_vec = simsimd_avx512_f32_log2(ratio_a_vec);
|
|
431
432
|
__m512 log_ratio_b_vec = simsimd_avx512_f32_log2(ratio_b_vec);
|
|
432
433
|
sum_a_vec = _mm512_maskz_fmadd_ps(nonzero_mask, a_vec, log_ratio_a_vec, sum_a_vec);
|
|
@@ -539,4 +540,6 @@ simsimd_avx512_f16_js_cycle:
|
|
|
539
540
|
|
|
540
541
|
#ifdef __cplusplus
|
|
541
542
|
}
|
|
542
|
-
#endif
|
|
543
|
+
#endif
|
|
544
|
+
|
|
545
|
+
#endif
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
* Detecting target CPU features at compile time: https://stackoverflow.com/a/28939692/2766161
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
#
|
|
14
|
+
#ifndef SIMSIMD_SIMSIMD_H
|
|
15
|
+
#define SIMSIMD_SIMSIMD_H
|
|
16
|
+
|
|
15
17
|
#include "binary.h" // Hamming, Jaccard
|
|
16
18
|
#include "probability.h" // Kullback-Leibler, Jensen–Shannon
|
|
17
19
|
#include "spatial.h" // L2, Inner Product, Cosine
|
|
@@ -423,3 +425,5 @@ inline static simsimd_metric_punned_t simsimd_metric_punned( //
|
|
|
423
425
|
#ifdef __cplusplus
|
|
424
426
|
}
|
|
425
427
|
#endif
|
|
428
|
+
|
|
429
|
+
#endif
|
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
* x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
|
|
21
21
|
* Arm intrinsics: https://developer.arm.com/architectures/instruction-sets/intrinsics/
|
|
22
22
|
*/
|
|
23
|
+
#ifndef SIMSIMD_SPATIAL_H
|
|
24
|
+
#define SIMSIMD_SPATIAL_H
|
|
23
25
|
|
|
24
|
-
#pragma once
|
|
25
26
|
#include "types.h"
|
|
26
27
|
|
|
27
28
|
#define SIMSIMD_MAKE_L2SQ(name, input_type, accumulator_type, converter) \
|
|
@@ -1000,4 +1001,6 @@ simsimd_avx512_i8_ip(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_
|
|
|
1000
1001
|
|
|
1001
1002
|
#ifdef __cplusplus
|
|
1002
1003
|
}
|
|
1004
|
+
#endif
|
|
1005
|
+
|
|
1003
1006
|
#endif
|
|
@@ -1,67 +1,59 @@
|
|
|
1
|
-
#
|
|
1
|
+
#ifndef SIMSIMD_TYPES_H
|
|
2
|
+
#define SIMSIMD_TYPES_H
|
|
2
3
|
|
|
3
4
|
// Compiling for Arm: SIMSIMD_TARGET_ARM
|
|
5
|
+
#if !defined(SIMSIMD_TARGET_ARM)
|
|
4
6
|
#if defined(__aarch64__) || defined(_M_ARM64)
|
|
5
7
|
#define SIMSIMD_TARGET_ARM 1
|
|
8
|
+
#else
|
|
9
|
+
#define SIMSIMD_TARGET_ARM 0
|
|
10
|
+
#endif // defined(__aarch64__) || defined(_M_ARM64)
|
|
11
|
+
#endif // !defined(SIMSIMD_TARGET_ARM)
|
|
12
|
+
|
|
13
|
+
// Compiling for x86: SIMSIMD_TARGET_X86
|
|
14
|
+
#if !defined(SIMSIMD_TARGET_X86)
|
|
15
|
+
#if defined(__x86_64__) || defined(_M_X64)
|
|
16
|
+
#define SIMSIMD_TARGET_X86 1
|
|
17
|
+
#else
|
|
18
|
+
#define SIMSIMD_TARGET_X86 0
|
|
19
|
+
#endif // defined(__x86_64__) || defined(_M_X64)
|
|
20
|
+
#endif // !defined(SIMSIMD_TARGET_X86)
|
|
6
21
|
|
|
7
22
|
// Compiling for Arm: SIMSIMD_TARGET_ARM_NEON
|
|
8
|
-
#if !defined(SIMSIMD_TARGET_ARM_NEON)
|
|
23
|
+
#if !defined(SIMSIMD_TARGET_ARM_NEON) || !SIMSIMD_TARGET_ARM
|
|
9
24
|
#if defined(__ARM_NEON)
|
|
10
|
-
#define SIMSIMD_TARGET_ARM_NEON
|
|
25
|
+
#define SIMSIMD_TARGET_ARM_NEON SIMSIMD_TARGET_ARM
|
|
11
26
|
#else
|
|
12
27
|
#define SIMSIMD_TARGET_ARM_NEON 0
|
|
13
|
-
#endif
|
|
14
|
-
#endif
|
|
28
|
+
#endif // defined(__ARM_NEON)
|
|
29
|
+
#endif // !defined(SIMSIMD_TARGET_ARM_NEON)
|
|
15
30
|
|
|
16
31
|
// Compiling for Arm: SIMSIMD_TARGET_ARM_SVE
|
|
17
|
-
#if !defined(SIMSIMD_TARGET_ARM_SVE)
|
|
32
|
+
#if !defined(SIMSIMD_TARGET_ARM_SVE) || !SIMSIMD_TARGET_ARM
|
|
18
33
|
#if defined(__ARM_FEATURE_SVE)
|
|
19
|
-
#define SIMSIMD_TARGET_ARM_SVE
|
|
34
|
+
#define SIMSIMD_TARGET_ARM_SVE SIMSIMD_TARGET_ARM
|
|
20
35
|
#else
|
|
21
36
|
#define SIMSIMD_TARGET_ARM_SVE 0
|
|
22
|
-
#endif
|
|
23
|
-
#endif
|
|
24
|
-
|
|
25
|
-
#undef SIMSIMD_TARGET_X86
|
|
26
|
-
#undef SIMSIMD_TARGET_X86_AVX2
|
|
27
|
-
#undef SIMSIMD_TARGET_X86_AVX512
|
|
28
|
-
#define SIMSIMD_TARGET_X86 0
|
|
29
|
-
#define SIMSIMD_TARGET_X86_AVX2 0
|
|
30
|
-
#define SIMSIMD_TARGET_X86_AVX512 0
|
|
31
|
-
|
|
32
|
-
// Compiling for x86: SIMSIMD_TARGET_X86
|
|
33
|
-
#elif defined(__x86_64__) || defined(_M_X64)
|
|
34
|
-
#define SIMSIMD_TARGET_X86 1
|
|
37
|
+
#endif // defined(__ARM_FEATURE_SVE)
|
|
38
|
+
#endif // !defined(SIMSIMD_TARGET_ARM_SVE)
|
|
35
39
|
|
|
36
40
|
// Compiling for x86: SIMSIMD_TARGET_X86_AVX2
|
|
37
|
-
#if !defined(SIMSIMD_TARGET_X86_AVX2)
|
|
41
|
+
#if !defined(SIMSIMD_TARGET_X86_AVX2) || !SIMSIMD_TARGET_X86
|
|
38
42
|
#if defined(__AVX2__)
|
|
39
43
|
#define SIMSIMD_TARGET_X86_AVX2 1
|
|
40
44
|
#else
|
|
41
45
|
#define SIMSIMD_TARGET_X86_AVX2 0
|
|
42
|
-
#endif
|
|
43
|
-
#endif
|
|
46
|
+
#endif // defined(__AVX2__)
|
|
47
|
+
#endif // !defined(SIMSIMD_TARGET_X86_AVX2)
|
|
44
48
|
|
|
45
49
|
// Compiling for x86: SIMSIMD_TARGET_X86_AVX512
|
|
46
|
-
#if !defined(SIMSIMD_TARGET_X86_AVX512)
|
|
47
|
-
#if defined(__AVX512F__)
|
|
50
|
+
#if !defined(SIMSIMD_TARGET_X86_AVX512) || !SIMSIMD_TARGET_X86
|
|
51
|
+
#if defined(__AVX512F__) && defined(__AVX512FP16__) && defined(__AVX512VNNI__) && defined(__AVX512VPOPCNTDQ__)
|
|
48
52
|
#define SIMSIMD_TARGET_X86_AVX512 1
|
|
49
53
|
#else
|
|
50
54
|
#define SIMSIMD_TARGET_X86_AVX512 0
|
|
51
|
-
#endif
|
|
52
|
-
#endif
|
|
53
|
-
|
|
54
|
-
#undef SIMSIMD_TARGET_ARM
|
|
55
|
-
#undef SIMSIMD_TARGET_ARM_NEON
|
|
56
|
-
#undef SIMSIMD_TARGET_ARM_SVE
|
|
57
|
-
#define SIMSIMD_TARGET_ARM 0
|
|
58
|
-
#define SIMSIMD_TARGET_ARM_NEON 0
|
|
59
|
-
#define SIMSIMD_TARGET_ARM_SVE 0
|
|
60
|
-
|
|
61
|
-
// Compiling for unknown hardware architecture
|
|
62
|
-
#else
|
|
63
|
-
#error Unknown hardware architecture!
|
|
64
|
-
#endif
|
|
55
|
+
#endif // defined(__AVX512F__) && defined(__AVX512FP16__) && defined(__AVX512VNNI__) && defined(__AVX512VPOPCNTDQ__)
|
|
56
|
+
#endif // !defined(SIMSIMD_TARGET_X86_AVX512)
|
|
65
57
|
|
|
66
58
|
#ifdef _MSC_VER
|
|
67
59
|
#include <intrin.h>
|
|
@@ -206,4 +198,6 @@ inline static float simsimd_uncompress_f16(unsigned short x) {
|
|
|
206
198
|
|
|
207
199
|
#ifdef __cplusplus
|
|
208
200
|
} // extern "C"
|
|
209
|
-
#endif
|
|
201
|
+
#endif
|
|
202
|
+
|
|
203
|
+
#endif
|
package/simsimd/package.json
CHANGED
package/wasmer.toml
CHANGED
package/simsimd/CMakeLists.txt
DELETED
|
@@ -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()
|
package/simsimd/cpp/bench.cxx
DELETED
|
@@ -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
|
-
}
|
package/simsimd/package-ci.json
DELETED
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
|
-
)
|