usearch 2.8.9 → 2.8.12
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/.github/workflows/prerelease.yml +1 -1
- package/.github/workflows/release.yml +1 -0
- package/CITATION.cff +1 -1
- package/CONTRIBUTING.md +19 -8
- package/Cargo.toml +1 -1
- package/README.md +54 -30
- package/VERSION +1 -1
- package/build.gradle +26 -5
- package/conanfile.py +1 -1
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_dense.hpp +4 -7
- package/include/usearch/index_plugins.hpp +9 -15
- package/javascript/types/usearch.d.ts +237 -0
- package/javascript/types/usearch.d.ts.map +1 -0
- package/javascript/usearch.js +1 -1
- package/package.json +18 -3
- package/pyproject.toml +1 -1
- package/setup.py +19 -13
- package/simsimd/README.md +9 -8
- package/simsimd/include/simsimd/binary.h +5 -2
- package/simsimd/include/simsimd/probability.h +5 -2
- 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/tsconfig.json +21 -0
- package/wasmer.toml +1 -1
|
@@ -127,7 +127,7 @@ jobs:
|
|
|
127
127
|
wget https://apt.llvm.org/llvm.sh
|
|
128
128
|
chmod +x llvm.sh
|
|
129
129
|
sudo ./llvm.sh 16
|
|
130
|
-
cmake -B build_artifacts -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSEARCH_BUILD_TEST_CPP=1 -DUSEARCH_BUILD_TEST_C=1 -DUSEARCH_BUILD_LIB_C=1 -
|
|
130
|
+
cmake -B build_artifacts -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSEARCH_BUILD_TEST_CPP=1 -DUSEARCH_BUILD_TEST_C=1 -DUSEARCH_BUILD_LIB_C=1 -DUSEARCH_USE_OPENMP=0 -DUSEARCH_USE_SIMSIMD=1 -DUSEARCH_USE_JEMALLOC=1
|
|
131
131
|
cmake --build build_artifacts --config RelWithDebInfo
|
|
132
132
|
- name: Test C++
|
|
133
133
|
run: ./build_artifacts/test_cpp
|
package/CITATION.cff
CHANGED
package/CONTRIBUTING.md
CHANGED
|
@@ -232,21 +232,30 @@ Afterwards, in a new terminal:
|
|
|
232
232
|
|
|
233
233
|
```sh
|
|
234
234
|
gradle clean build
|
|
235
|
-
|
|
235
|
+
gradle test
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Alternatively, to run the `Index.main`:
|
|
239
|
+
|
|
240
|
+
```sh
|
|
241
|
+
java -cp "$(pwd)/build/classes/java/main" -Djava.library.path="$(pwd)/build/libs/usearch/shared" java/cloud/unum/usearch/Index.java
|
|
236
242
|
```
|
|
237
243
|
|
|
238
244
|
Or step by-step:
|
|
239
245
|
|
|
240
246
|
```sh
|
|
241
|
-
|
|
242
|
-
javac -h . Index.java
|
|
247
|
+
cd java/cloud/unum/usearch
|
|
248
|
+
javac -h . Index.java NativeUtils.java
|
|
249
|
+
|
|
250
|
+
# Ensure JAVA_HOME system environment variable has been set
|
|
251
|
+
# e.g. export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
|
|
243
252
|
|
|
244
253
|
# Ubuntu:
|
|
245
|
-
g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
|
|
246
|
-
g++ -shared -fPIC -o
|
|
254
|
+
g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -I../../../../include cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
|
|
255
|
+
g++ -shared -fPIC -o libusearch.so cloud_unum_usearch_Index.o -lc
|
|
247
256
|
|
|
248
257
|
# Windows
|
|
249
|
-
g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
|
|
258
|
+
g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 cloud_unum_usearch_Index.cpp -I..\..\..\..\include -o cloud_unum_usearch_Index.o
|
|
250
259
|
g++ -shared -o USearchJNI.dll cloud_unum_usearch_Index.o -Wl,--add-stdcall-alias
|
|
251
260
|
|
|
252
261
|
# MacOS
|
|
@@ -256,9 +265,11 @@ g++ -std=c++11 -c -fPIC \
|
|
|
256
265
|
-I../../../../simsimd/include \
|
|
257
266
|
-I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
|
|
258
267
|
g++ -dynamiclib -o libusearch.dylib cloud_unum_usearch_Index.o -lc
|
|
268
|
+
|
|
259
269
|
# Run linking to that directory
|
|
260
|
-
|
|
261
|
-
|
|
270
|
+
cd ../../../..
|
|
271
|
+
cp cloud/unum/usearch/libusearch.* .
|
|
272
|
+
java -cp . -Djava.library.path="$(pwd)" cloud.unum.usearch.Index
|
|
262
273
|
```
|
|
263
274
|
|
|
264
275
|
## Wolfram
|
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.12},
|
|
404
428
|
year = {2023},
|
|
405
429
|
month = oct,
|
|
406
430
|
}
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.8.
|
|
1
|
+
2.8.12
|
package/build.gradle
CHANGED
|
@@ -23,6 +23,11 @@ sourceSets {
|
|
|
23
23
|
srcDir "java/"
|
|
24
24
|
exclude "test/"
|
|
25
25
|
}
|
|
26
|
+
// Ensure native libraries are included in the main source set
|
|
27
|
+
resources {
|
|
28
|
+
srcDirs "$buildDir/libs"
|
|
29
|
+
include "usearch/**"
|
|
30
|
+
}
|
|
26
31
|
}
|
|
27
32
|
test {
|
|
28
33
|
java {
|
|
@@ -98,15 +103,25 @@ test {
|
|
|
98
103
|
|
|
99
104
|
// Rearrange test classpath, add compiled JAR instead of main classes
|
|
100
105
|
classpath = project.sourceSets.test.output + configurations.testRuntimeClasspath + files(jar.archiveFile)
|
|
106
|
+
|
|
107
|
+
// Enable detailed test logging
|
|
108
|
+
testLogging {
|
|
109
|
+
// Ensure that we get output from all tests, not just failing ones
|
|
110
|
+
showStandardStreams = true
|
|
111
|
+
exceptionFormat = 'full' // Can be 'short', 'full', or 'summary'
|
|
112
|
+
|
|
113
|
+
// More detailed test result logging
|
|
114
|
+
events "passed", "skipped", "failed"
|
|
115
|
+
}
|
|
101
116
|
}
|
|
102
117
|
|
|
103
118
|
jar {
|
|
104
119
|
dependsOn("usearchSharedLibrary")
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
from("$buildDir/libs/usearch/shared/libusearch.so")
|
|
120
|
+
from("$buildDir/libs/usearch/shared") {
|
|
121
|
+
include "libusearch.so", "libusearch.dylib", "libusearch.dll"
|
|
122
|
+
into "usearch"
|
|
109
123
|
}
|
|
124
|
+
from sourceSets.main.output
|
|
110
125
|
}
|
|
111
126
|
|
|
112
127
|
signing {
|
|
@@ -180,4 +195,10 @@ publishing {
|
|
|
180
195
|
}
|
|
181
196
|
}
|
|
182
197
|
}
|
|
183
|
-
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Ensure that the native library is built before the Java code
|
|
201
|
+
compileJava.dependsOn tasks.withType(SharedLibraryBinarySpec)
|
|
202
|
+
tasks.named("processResources").configure {
|
|
203
|
+
dependsOn tasks.named("linkUsearchSharedLibrary")
|
|
204
|
+
}
|
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.12'
|
|
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"
|
|
@@ -108,13 +108,10 @@ struct index_dense_config_t : public index_config_t {
|
|
|
108
108
|
|
|
109
109
|
index_dense_config_t(index_config_t base) noexcept : index_config_t(base) {}
|
|
110
110
|
|
|
111
|
-
index_dense_config_t(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
: index_config_t(connectivity), //
|
|
116
|
-
expansion_add(expansion_add ? expansion_add : default_expansion_add()),
|
|
117
|
-
expansion_search(expansion_search ? expansion_search : default_expansion_search()) {}
|
|
111
|
+
index_dense_config_t(std::size_t c = default_connectivity(), std::size_t ea = default_expansion_add(),
|
|
112
|
+
std::size_t es = default_expansion_search()) noexcept
|
|
113
|
+
: index_config_t(c), expansion_add(ea ? ea : default_expansion_add()),
|
|
114
|
+
expansion_search(es ? es : default_expansion_search()) {}
|
|
118
115
|
};
|
|
119
116
|
|
|
120
117
|
struct index_dense_clustering_config_t {
|
|
@@ -1343,11 +1343,13 @@ class metric_punned_t {
|
|
|
1343
1343
|
scalar_kind_(scalar_kind) {
|
|
1344
1344
|
|
|
1345
1345
|
#if USEARCH_USE_SIMSIMD
|
|
1346
|
-
if (configure_with_simsimd())
|
|
1347
|
-
|
|
1346
|
+
if (!configure_with_simsimd())
|
|
1347
|
+
configure_with_auto_vectorized();
|
|
1348
1348
|
#endif
|
|
1349
|
-
|
|
1350
1349
|
configure_with_auto_vectorized();
|
|
1350
|
+
|
|
1351
|
+
if (scalar_kind == scalar_kind_t::b1x8_k)
|
|
1352
|
+
raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_);
|
|
1351
1353
|
}
|
|
1352
1354
|
|
|
1353
1355
|
inline metric_punned_t( //
|
|
@@ -1402,6 +1404,7 @@ class metric_punned_t {
|
|
|
1402
1404
|
case metric_kind_t::cos_k: kind = simsimd_metric_cos_k; break;
|
|
1403
1405
|
case metric_kind_t::l2sq_k: kind = simsimd_metric_l2sq_k; break;
|
|
1404
1406
|
case metric_kind_t::hamming_k: kind = simsimd_metric_hamming_k; break;
|
|
1407
|
+
case metric_kind_t::tanimoto_k: kind = simsimd_metric_jaccard_k; break;
|
|
1405
1408
|
case metric_kind_t::jaccard_k: kind = simsimd_metric_jaccard_k; break;
|
|
1406
1409
|
default: break;
|
|
1407
1410
|
}
|
|
@@ -1498,18 +1501,9 @@ class metric_punned_t {
|
|
|
1498
1501
|
break;
|
|
1499
1502
|
}
|
|
1500
1503
|
case metric_kind_t::jaccard_k: // Equivalent to Tanimoto
|
|
1501
|
-
case metric_kind_t::tanimoto_k:
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
break;
|
|
1505
|
-
case metric_kind_t::hamming_k:
|
|
1506
|
-
raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_hamming_gt<b1x8_t>>,
|
|
1507
|
-
raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
|
|
1508
|
-
break;
|
|
1509
|
-
case metric_kind_t::sorensen_k:
|
|
1510
|
-
raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_sorensen_gt<b1x8_t>>,
|
|
1511
|
-
raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
|
|
1512
|
-
break;
|
|
1504
|
+
case metric_kind_t::tanimoto_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_tanimoto_gt<b1x8_t>>; break;
|
|
1505
|
+
case metric_kind_t::hamming_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_hamming_gt<b1x8_t>>; break;
|
|
1506
|
+
case metric_kind_t::sorensen_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_sorensen_gt<b1x8_t>>; break;
|
|
1513
1507
|
default: return;
|
|
1514
1508
|
}
|
|
1515
1509
|
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
export class Index {
|
|
2
|
+
/**
|
|
3
|
+
* Constructs a new index.
|
|
4
|
+
*
|
|
5
|
+
* @param {(number | {dimensions: number, metric: MetricKind = MetricKind.Cos, quantization: ScalarKind = ScalarKind.F32, connectivity: number = 0, expansion_add: number = 0, expansion_search: number = 0, multi: boolean = false})} dimensionsOrConfigs
|
|
6
|
+
* @param {MetricKind} [metric=MetricKind.Cos] - Optional, default is 'cos'.
|
|
7
|
+
* @param {ScalarKind} [quantization=ScalarKind.F32] - Optional, default is 'f32'.
|
|
8
|
+
* @param {number} [connectivity=0] - Optional, default is 0.
|
|
9
|
+
* @param {number} [expansion_add=0] - Optional, default is 0.
|
|
10
|
+
* @param {number} [expansion_search=0] - Optional, default is 0.
|
|
11
|
+
* @param {boolean} [multi=false] - Optional, default is false.
|
|
12
|
+
* @throws Will throw an error if any of the parameters are of incorrect type or invalid value.
|
|
13
|
+
*/
|
|
14
|
+
constructor(dimensionsOrConfigs: number | {
|
|
15
|
+
dimensions: number;
|
|
16
|
+
metric: MetricKind;
|
|
17
|
+
quantization: ScalarKind;
|
|
18
|
+
connectivity: number;
|
|
19
|
+
expansion_add: number;
|
|
20
|
+
expansion_search: number;
|
|
21
|
+
multi: boolean;
|
|
22
|
+
}, metric?: MetricKind, quantization?: ScalarKind, connectivity?: number, expansion_add?: number, expansion_search?: number, multi?: boolean);
|
|
23
|
+
_compiledIndex: any;
|
|
24
|
+
/**
|
|
25
|
+
* Add vectors to the index.
|
|
26
|
+
*
|
|
27
|
+
* This method accepts vectors and their corresponding keys for indexing.
|
|
28
|
+
* Each key should correspond to a vector. If a single key is provided,
|
|
29
|
+
* it is broadcasted to match the number of provided vectors.
|
|
30
|
+
*
|
|
31
|
+
* Vectors should be provided as a flat typed array representing a matrix
|
|
32
|
+
* where each row is a vector to be indexed. The matrix should have a size
|
|
33
|
+
* of n * d, where n is the number of vectors, and d is the dimensionality
|
|
34
|
+
* of the vectors.
|
|
35
|
+
*
|
|
36
|
+
* Keys should be provided as a BigInt or an array-like object of BigInts
|
|
37
|
+
* representing the unique identifier for each vector.
|
|
38
|
+
*
|
|
39
|
+
* @param {bigint|bigint[]|BigUint64Array} keys - Input identifiers for every vector.
|
|
40
|
+
* If a single key is provided, it is associated with all provided vectors.
|
|
41
|
+
* @param {Float32Array|Float64Array|Int8Array} vectors - Input matrix representing vectors,
|
|
42
|
+
* matrix of size n * d, where n is the number of vectors, and d is their dimensionality.
|
|
43
|
+
* @throws Will throw an error if the length of keys doesn't match the number of vectors
|
|
44
|
+
* or if it's not a single key.
|
|
45
|
+
*/
|
|
46
|
+
add(keys: bigint | bigint[] | BigUint64Array, vectors: Float32Array | Float64Array | Int8Array): void;
|
|
47
|
+
/**
|
|
48
|
+
* Perform a k-nearest neighbor search on the index.
|
|
49
|
+
*
|
|
50
|
+
* This method accepts a matrix of query vectors and returns the closest vectors
|
|
51
|
+
* from the index for each query. The method returns an object containing the keys,
|
|
52
|
+
* distances, and counts of the matches found.
|
|
53
|
+
*
|
|
54
|
+
* Vectors should be provided as a flat typed array representing a matrix where
|
|
55
|
+
* each row is a vector. The matrix should be of size n * d, where n is the
|
|
56
|
+
* number of query vectors, and d is their dimensionality.
|
|
57
|
+
*
|
|
58
|
+
* The parameter `k` specifies the number of nearest neighbors to return for each
|
|
59
|
+
* query vector. If there are not enough results for a query, the result array is
|
|
60
|
+
* padded with -1s.
|
|
61
|
+
*
|
|
62
|
+
* @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} vectors - Input matrix representing query vectors, can be a TypedArray or an array of arrays.
|
|
63
|
+
* @param {number} k - The number of nearest neighbors to search for each query vector.
|
|
64
|
+
* @return {Matches|BatchMatches} - Search results for one or more queries, containing keys, distances, and counts of the matches found.
|
|
65
|
+
* @throws Will throw an error if `k` is not a positive integer or if the size of the vectors is not a multiple of dimensions.
|
|
66
|
+
* @throws Will throw an error if `vectors` is not a valid input type (TypedArray or an array of arrays) or if its flattened size is not a multiple of dimensions.
|
|
67
|
+
*/
|
|
68
|
+
search(vectors: Float32Array | Float64Array | Int8Array | Array<Array<number>>, k: number): Matches | BatchMatches;
|
|
69
|
+
/**
|
|
70
|
+
* Verifies the presence of one or more keys in the index.
|
|
71
|
+
*
|
|
72
|
+
* This method accepts one or multiple keys as input and returns a boolean or
|
|
73
|
+
* an array of booleans indicating whether each key is present in the index.
|
|
74
|
+
*
|
|
75
|
+
* @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be checked for presence in the index.
|
|
76
|
+
* @return {boolean|boolean[]} - Returns true if a single key is contained in the index, false otherwise. Returns an array of booleans corresponding to the presence of each key in the index when multiple keys are provided.
|
|
77
|
+
* @throws Will throw an error if keys are not integers.
|
|
78
|
+
*/
|
|
79
|
+
contains(keys: bigint | bigint[] | BigUint64Array): boolean | boolean[];
|
|
80
|
+
/**
|
|
81
|
+
* Counts the number of times keys shows up in the index.
|
|
82
|
+
*
|
|
83
|
+
* @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be enumerated.
|
|
84
|
+
* @return {number|number[]} - Returns the number of vectors found when a single key is provided. Returns an array of big integers corresponding to the number of vectors found for each key when multiple keys are provided.
|
|
85
|
+
* @throws Will throw an error if keys are not integers.
|
|
86
|
+
*/
|
|
87
|
+
count(keys: bigint | bigint[] | BigUint64Array): number | number[];
|
|
88
|
+
/**
|
|
89
|
+
* Removes one or multiple vectors from the index.
|
|
90
|
+
*
|
|
91
|
+
* This method accepts one or multiple keys as input and removes the corresponding vectors from the index.
|
|
92
|
+
* It returns the number of vectors actually removed for each key provided.
|
|
93
|
+
*
|
|
94
|
+
* @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be removed.
|
|
95
|
+
* @return {number|number[]} - Returns the number of vectors deleted when a single key is provided. Returns an array of big integers corresponding to the number of vectors deleted for each key when multiple keys are provided.
|
|
96
|
+
* @throws Will throw an error if keys are not integers.
|
|
97
|
+
*/
|
|
98
|
+
remove(keys: bigint | bigint[] | BigUint64Array): number | number[];
|
|
99
|
+
/**
|
|
100
|
+
* Returns the dimensionality of vectors.
|
|
101
|
+
* @return {number} The dimensionality of vectors.
|
|
102
|
+
*/
|
|
103
|
+
dimensions(): number;
|
|
104
|
+
/**
|
|
105
|
+
* Returns connectivity.
|
|
106
|
+
* @return {number} The connectivity of index.
|
|
107
|
+
*/
|
|
108
|
+
connectivity(): number;
|
|
109
|
+
/**
|
|
110
|
+
* Returns the number of vectors currently indexed.
|
|
111
|
+
* @return {number} The number of vectors currently indexed.
|
|
112
|
+
*/
|
|
113
|
+
size(): number;
|
|
114
|
+
/**
|
|
115
|
+
* Returns index capacity.
|
|
116
|
+
* @return {number} The capacity of index.
|
|
117
|
+
*/
|
|
118
|
+
capacity(): number;
|
|
119
|
+
/**
|
|
120
|
+
* Write index to a file.
|
|
121
|
+
* @param {string} path File path to write.
|
|
122
|
+
* @throws Will throw an error if `path` is not a string.
|
|
123
|
+
*/
|
|
124
|
+
save(path: string): void;
|
|
125
|
+
/**
|
|
126
|
+
* Load index from a file.
|
|
127
|
+
* @param {string} path File path to read.
|
|
128
|
+
* @throws Will throw an error if `path` is not a string.
|
|
129
|
+
*/
|
|
130
|
+
load(path: string): void;
|
|
131
|
+
/**
|
|
132
|
+
* View index from a file, without loading into RAM.
|
|
133
|
+
* @param {string} path File path to read.
|
|
134
|
+
* @throws Will throw an error if `path` is not a string.
|
|
135
|
+
*/
|
|
136
|
+
view(path: string): void;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* *
|
|
140
|
+
*/
|
|
141
|
+
export type MetricKind = string;
|
|
142
|
+
export namespace MetricKind {
|
|
143
|
+
let Unknown: string;
|
|
144
|
+
let Cos: string;
|
|
145
|
+
let IP: string;
|
|
146
|
+
let L2sq: string;
|
|
147
|
+
let Haversine: string;
|
|
148
|
+
let Divergence: string;
|
|
149
|
+
let Pearson: string;
|
|
150
|
+
let Jaccard: string;
|
|
151
|
+
let Hamming: string;
|
|
152
|
+
let Tanimoto: string;
|
|
153
|
+
let Sorensen: string;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* *
|
|
157
|
+
*/
|
|
158
|
+
export type ScalarKind = string;
|
|
159
|
+
export namespace ScalarKind {
|
|
160
|
+
let Unknown_1: string;
|
|
161
|
+
export { Unknown_1 as Unknown };
|
|
162
|
+
export let F32: string;
|
|
163
|
+
export let F64: string;
|
|
164
|
+
export let F16: string;
|
|
165
|
+
export let I8: string;
|
|
166
|
+
export let B1: string;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Represents a set of search results.
|
|
170
|
+
*/
|
|
171
|
+
export class Matches {
|
|
172
|
+
/**
|
|
173
|
+
* Constructs a Matches object.
|
|
174
|
+
*
|
|
175
|
+
* @param {BigUint64Array} keys - The keys of the nearest neighbors found.
|
|
176
|
+
* @param {Float32Array} distances - The distances of the nearest neighbors found.
|
|
177
|
+
*/
|
|
178
|
+
constructor(keys: BigUint64Array, distances: Float32Array);
|
|
179
|
+
keys: BigUint64Array;
|
|
180
|
+
distances: Float32Array;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Represents a set of batched search results.
|
|
184
|
+
*/
|
|
185
|
+
export class BatchMatches {
|
|
186
|
+
/**
|
|
187
|
+
* Constructs a BatchMatches object.
|
|
188
|
+
*
|
|
189
|
+
* @param {BigUint64Array} keys - The keys of the nearest neighbors found in the batch.
|
|
190
|
+
* @param {Float32Array} distances - The distances of the nearest neighbors found in the batch.
|
|
191
|
+
* @param {BigUint64Array} counts - The number of neighbors found for each query in the batch.
|
|
192
|
+
* @param {bigint} k - The limit for search results per query in the batch.
|
|
193
|
+
*/
|
|
194
|
+
constructor(keys: BigUint64Array, distances: Float32Array, counts: BigUint64Array, k: bigint);
|
|
195
|
+
keys: BigUint64Array;
|
|
196
|
+
distances: Float32Array;
|
|
197
|
+
counts: BigUint64Array;
|
|
198
|
+
k: bigint;
|
|
199
|
+
/**
|
|
200
|
+
* Retrieves a Matches object at the specified index in the batch.
|
|
201
|
+
*
|
|
202
|
+
* @param {number} i - The index at which to retrieve the Matches object.
|
|
203
|
+
* @returns {Matches} - A Matches object representing the search results at the specified index in the batch.
|
|
204
|
+
*/
|
|
205
|
+
get(i: number): Matches;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Performs an exact search on the given dataset to find the best matching vectors for each query.
|
|
209
|
+
*
|
|
210
|
+
* @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} dataset - The dataset containing vectors to be searched. It can be a TypedArray or an array of arrays.
|
|
211
|
+
* @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} queries - The queries containing vectors to search for in the dataset. It can be a TypedArray or an array of arrays.
|
|
212
|
+
* @param {number} dimensions - The dimensionality of the vectors in both the dataset and the queries. It defines the number of elements in each vector.
|
|
213
|
+
* @param {number} count - The number of nearest neighbors to return for each query. If the dataset contains fewer vectors than the specified count, the result will contain only the available vectors.
|
|
214
|
+
* @param {MetricKind} metric - The distance metric to be used for the search.
|
|
215
|
+
* @return {Matches|BatchMatches} - Returns a `Matches` or `BatchMatches` object containing the results of the search.
|
|
216
|
+
* @throws Will throw an error if `dimensions` and `count` are not positive integers.
|
|
217
|
+
* @throws Will throw an error if `metric` is not a valid MetricKind.
|
|
218
|
+
* @throws Will throw an error if `dataset` and `queries` are not valid input types (TypedArray or an array of arrays).
|
|
219
|
+
* @throws Will throw an error if the sizes of the flattened `dataset` and `queries` are not multiples of `dimensions`.
|
|
220
|
+
* @throws Will throw an error if `count` is greater than the number of vectors in the `dataset`.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* const dataset = [[1.0, 2.0], [3.0, 4.0]]; // Two vectors: [1.0, 2.0] and [3.0, 4.0]
|
|
224
|
+
* const queries = [[1.5, 2.5]]; // One vector: [1.5, 2.5]
|
|
225
|
+
* const dimensions = 2; // The number of elements in each vector.
|
|
226
|
+
* const count = 1; // The number of nearest neighbors to return for each query.
|
|
227
|
+
* const metric = MetricKind.IP; // Using the Inner Product distance metric.
|
|
228
|
+
*
|
|
229
|
+
* const result = exactSearch(dataset, queries, dimensions, count, metric);
|
|
230
|
+
* // result might be:
|
|
231
|
+
* // {
|
|
232
|
+
* // keys: BigUint64Array [ 1n ],
|
|
233
|
+
* // distances: Float32Array [ some_value ],
|
|
234
|
+
* // }
|
|
235
|
+
*/
|
|
236
|
+
export function exactSearch(dataset: Float32Array | Float64Array | Int8Array | Array<Array<number>>, queries: Float32Array | Float64Array | Int8Array | Array<Array<number>>, dimensions: number, count: number, metric: MetricKind): Matches | BatchMatches;
|
|
237
|
+
//# sourceMappingURL=usearch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usearch.d.ts","sourceRoot":"","sources":["../usearch.js"],"names":[],"mappings":"AAsIA;IAEI;;;;;;;;;;;OAWG;IACH;oBATkC,MAAM;gBAAU,UAAU;sBAAiC,UAAU;sBAAiC,MAAM;uBAAqB,MAAM;0BAAwB,MAAM;eAAa,OAAO;gBAChN,UAAU,iBACV,UAAU,iBACV,MAAM,kBACN,MAAM,qBACN,MAAM,UACN,OAAO,EAgCjB;IADG,oBAAwI;IAG5I;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,UAPW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,WAE9B,YAAY,GAAC,YAAY,GAAC,SAAS,QAoB7C;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBANW,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,KACxD,MAAM,GACL,OAAO,GAAC,YAAY,CAqB/B;IAED;;;;;;;;;OASG;IACH,eAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,OAAO,GAAC,OAAO,EAAE,CAU5B;IAED;;;;;;OAMG;IACH,YAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,MAAM,GAAC,MAAM,EAAE,CAU1B;IAED;;;;;;;;;OASG;IACH,aAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,MAAM,GAAC,MAAM,EAAE,CAU1B;IAED;;;OAGG;IACH,cAFY,MAAM,CAEsC;IAExD;;;OAGG;IACH,gBAFY,MAAM,CAE0C;IAE5D;;;OAGG;IACH,QAFY,MAAM,CAE0B;IAE5C;;;OAGG;IACH,YAFY,MAAM,CAEkC;IAEpD;;;;OAIG;IACH,WAHW,MAAM,QAMhB;IAED;;;;OAIG;IACH,WAHW,MAAM,QAMhB;IAED;;;;OAIG;IACH,WAHW,MAAM,QAMhB;CACJ;;;;yBAzWS,MAAM;;;;;;;;;;;;;;;;;yBAmBN,MAAM;;;;;;;;;;AAYhB;;GAEG;AACH;IACI;;;;;OAKG;IACH,kBAHW,cAAc,aACd,YAAY,EAKtB;IAFG,qBAAgB;IAChB,wBAA0B;CAEjC;AAED;;GAEG;AACH;IACI;;;;;;;OAOG;IACH,kBALW,cAAc,aACd,YAAY,UACZ,cAAc,KACd,MAAM,EAOhB;IAJG,qBAAgB;IAChB,wBAA0B;IAC1B,uBAAoB;IACpB,UAAU;IAGd;;;;;OAKG;IACH,OAHW,MAAM,GACJ,OAAO,CAQnB;CACJ;AA4RD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qCA1BW,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,WACxD,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,cACxD,MAAM,SACN,MAAM,UACN,UAAU,GACT,OAAO,GAAC,YAAY,CA0D/B"}
|
package/javascript/usearch.js
CHANGED
|
@@ -137,7 +137,7 @@ class Index {
|
|
|
137
137
|
/**
|
|
138
138
|
* Constructs a new index.
|
|
139
139
|
*
|
|
140
|
-
* @param {number} dimensionsOrConfigs
|
|
140
|
+
* @param {(number | {dimensions: number, metric: MetricKind = MetricKind.Cos, quantization: ScalarKind = ScalarKind.F32, connectivity: number = 0, expansion_add: number = 0, expansion_search: number = 0, multi: boolean = false})} dimensionsOrConfigs
|
|
141
141
|
* @param {MetricKind} [metric=MetricKind.Cos] - Optional, default is 'cos'.
|
|
142
142
|
* @param {ScalarKind} [quantization=ScalarKind.F32] - Optional, default is 'f32'.
|
|
143
143
|
* @param {number} [connectivity=0] - Optional, default is 0.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "usearch",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.12",
|
|
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",
|
|
@@ -25,9 +25,24 @@
|
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
27
|
"test": "node --test ./javascript/usearch.test.js",
|
|
28
|
-
"install": "node-gyp rebuild"
|
|
28
|
+
"install": "node-gyp rebuild",
|
|
29
|
+
"typegen": "npx tsc"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"typescript": "^5.1.6"
|
|
32
|
-
}
|
|
33
|
+
},
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./javascript/types/usearch.d.ts",
|
|
37
|
+
"require": "./javascript/usearch.js"
|
|
38
|
+
},
|
|
39
|
+
"./package.json": "./package.json"
|
|
40
|
+
},
|
|
41
|
+
"typesVersions": {
|
|
42
|
+
"*": {
|
|
43
|
+
"*": [
|
|
44
|
+
"./javascript/types/usearch.d.ts"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
33
48
|
}
|
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
|
@@ -14,18 +14,8 @@ 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
|
|
@@ -33,9 +23,23 @@ is_linux: bool = sys.platform == "linux"
|
|
|
33
23
|
is_macos: bool = sys.platform == "darwin"
|
|
34
24
|
is_windows: bool = sys.platform == "win32"
|
|
35
25
|
|
|
26
|
+
|
|
27
|
+
is_gcc = False
|
|
28
|
+
if is_linux:
|
|
29
|
+
cxx = os.environ.get("CXX")
|
|
30
|
+
if cxx:
|
|
31
|
+
try:
|
|
32
|
+
command = "where" if os.name == "nt" else "which"
|
|
33
|
+
full_path = subprocess.check_output([command, cxx], text=True).strip()
|
|
34
|
+
compiler_name = os.path.basename(full_path)
|
|
35
|
+
is_gcc = ("g++" in compiler_name) and ("clang++" not in compiler_name)
|
|
36
|
+
except subprocess.CalledProcessError:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
36
40
|
prefer_simsimd: bool = is_linux or is_macos
|
|
37
41
|
prefer_fp16lib: bool = True
|
|
38
|
-
prefer_openmp: bool = is_linux and
|
|
42
|
+
prefer_openmp: bool = is_linux and is_gcc
|
|
39
43
|
|
|
40
44
|
use_simsimd: bool = get_bool_env("USEARCH_USE_SIMSIMD", prefer_simsimd)
|
|
41
45
|
use_fp16lib: bool = get_bool_env("USEARCH_USE_FP16LIB", prefer_fp16lib)
|
|
@@ -123,6 +127,8 @@ if is_windows:
|
|
|
123
127
|
]
|
|
124
128
|
)
|
|
125
129
|
|
|
130
|
+
print("macros_args", macros_args)
|
|
131
|
+
|
|
126
132
|
ext_modules = [
|
|
127
133
|
Pybind11Extension(
|
|
128
134
|
"usearch.compiled",
|
package/simsimd/README.md
CHANGED
|
@@ -24,6 +24,15 @@ 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
|
|
@@ -48,14 +57,6 @@ On the Intel Sapphire Rapids platform, SimSIMD was benchmarked against auto-vect
|
|
|
48
57
|
| Inner Product | 3.81 M/s | _192.02 k/s_ | _5.99 M/s_ | __31 x__ |
|
|
49
58
|
| Jensen Shannon | 1.18 M/s | _18.13 k/s_ | _2.14 M/s_ | __118 x__ |
|
|
50
59
|
|
|
51
|
-
__Technical Insights__:
|
|
52
|
-
|
|
53
|
-
- [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).
|
|
54
|
-
- [Uses AVX-512 FP16 for half-precision operations, that few compilers vectorize](https://ashvardanian.com/posts/simsimd-faster-scipy/#the-challenge-of-f16).
|
|
55
|
-
- [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).
|
|
56
|
-
- Avoids slow PyBind11 and SWIG, directly using the CPython C API.
|
|
57
|
-
- Avoids slow `PyArg_ParseTuple` and manually unpacks argument tuples.
|
|
58
|
-
|
|
59
60
|
__Broader Benchmarking Results__:
|
|
60
61
|
|
|
61
62
|
- [Apple M2 Pro](https://ashvardanian.com/posts/simsimd-faster-scipy/#appendix-1-performance-on-apple-m2-pro).
|
|
@@ -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) \
|
|
@@ -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/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Change this to match your project
|
|
3
|
+
"include": ["javascript/usearch.js"],
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
// Tells TypeScript to read JS files, as
|
|
6
|
+
// normally they are ignored as source files
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
// Generate d.ts files
|
|
9
|
+
"declaration": true,
|
|
10
|
+
// This compiler run should
|
|
11
|
+
// only output d.ts files
|
|
12
|
+
"emitDeclarationOnly": true,
|
|
13
|
+
// Types should go into this directory.
|
|
14
|
+
// Removing this would place the .d.ts files
|
|
15
|
+
// next to the .js files
|
|
16
|
+
"outDir": "javascript/types",
|
|
17
|
+
// go to js file when using IDE functions like
|
|
18
|
+
// "Go to Definition" in VSCode
|
|
19
|
+
"declarationMap": true
|
|
20
|
+
}
|
|
21
|
+
}
|