usearch 2.9.0 → 2.9.2
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 +36 -2
- package/Cargo.toml +1 -1
- package/README.md +12 -15
- package/VERSION +1 -1
- package/c/lib.cpp +19 -17
- package/c/test.c +80 -38
- package/c/usearch.h +15 -15
- package/conanfile.py +1 -1
- package/include/usearch/index.hpp +5 -5
- package/include/usearch/index_dense.hpp +8 -4
- package/include/usearch/index_plugins.hpp +52 -32
- package/javascript/types/usearch.d.ts.map +1 -1
- package/javascript/usearch.js +4 -1
- package/package.json +3 -2
- package/simsimd/CONTRIBUTING.md +88 -0
- package/simsimd/Cargo.lock +4 -3
- package/simsimd/Cargo.toml +10 -2
- package/simsimd/README.md +102 -60
- package/simsimd/include/simsimd/binary.h +44 -0
- package/simsimd/include/simsimd/complex.h +174 -0
- package/simsimd/include/simsimd/simsimd.h +18 -9
- package/simsimd/include/simsimd/spatial.h +128 -36
- package/simsimd/include/simsimd/types.h +3 -2
- package/simsimd/package-lock.json +5 -5
- package/simsimd/package.json +1 -1
- package/simsimd/rust/benches/native.rs +12 -9
- package/simsimd/rust/lib.c +32 -55
- package/simsimd/rust/lib.rs +374 -56
- package/stringzilla/CONTRIBUTING.md +17 -0
- package/stringzilla/Cargo.lock +1 -1
- package/stringzilla/Cargo.toml +1 -1
- package/stringzilla/README.md +175 -34
- package/stringzilla/VERSION +1 -1
- package/stringzilla/build.rs +77 -4
- package/stringzilla/c/lib.c +33 -0
- package/stringzilla/cli/README.md +30 -13
- package/stringzilla/cli/__init__.py +0 -0
- package/stringzilla/cli/split.py +66 -33
- package/stringzilla/cli/wc.py +101 -21
- package/stringzilla/include/stringzilla/experimental.h +122 -1
- package/stringzilla/include/stringzilla/stringzilla.h +262 -132
- package/stringzilla/include/stringzilla/stringzilla.hpp +13 -5
- package/stringzilla/package.json +1 -1
- package/stringzilla/rust/lib.rs +673 -162
- package/wasmer.toml +1 -1
package/CITATION.cff
CHANGED
package/CONTRIBUTING.md
CHANGED
|
@@ -104,6 +104,13 @@ cppcheck --enable=all --force --suppress=cstyleCast --suppress=unusedFunction \
|
|
|
104
104
|
include/index_plugins.hpp
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
+
I'd recommend putting the following breakpoints when debugging the code in GDB:
|
|
108
|
+
|
|
109
|
+
- `__asan::ReportGenericError` - to detect illegal memory accesses.
|
|
110
|
+
- `__ubsan::ScopedReport::~ScopedReport` - to catch undefined behavior.
|
|
111
|
+
- `__GI_exit` - to stop at exit points - the end of running any executable.
|
|
112
|
+
- `__builtin_unreachable` - to catch all the places where the code is expected to be unreachable.
|
|
113
|
+
|
|
107
114
|
## Python 3
|
|
108
115
|
|
|
109
116
|
Python bindings are built using PyBind11 and are available on [PyPi](https://pypi.org/project/usearch/).
|
|
@@ -215,10 +222,11 @@ USearch provides both Objective-C and Swift bindings through the [Swift Package
|
|
|
215
222
|
The compilation settings are controlled by the `Package.swift` and are independent from CMake used for C/C++ builds.
|
|
216
223
|
|
|
217
224
|
```sh
|
|
218
|
-
swift build
|
|
219
|
-
swift test -v
|
|
225
|
+
swift build && swift test -v
|
|
220
226
|
```
|
|
221
227
|
|
|
228
|
+
Those depend on Apple's `Foundation` library and can only run on Apple devices.
|
|
229
|
+
|
|
222
230
|
## GoLang
|
|
223
231
|
|
|
224
232
|
USearch provides GoLang bindings, that depend on the C library that must be installed beforehand.
|
|
@@ -291,6 +299,32 @@ cp cloud/unum/usearch/libusearch.* .
|
|
|
291
299
|
java -cp . -Djava.library.path="$(pwd)" cloud.unum.usearch.Index
|
|
292
300
|
```
|
|
293
301
|
|
|
302
|
+
## C#
|
|
303
|
+
|
|
304
|
+
Setup the .NET environment:
|
|
305
|
+
|
|
306
|
+
```sh
|
|
307
|
+
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
USearch provides CSharp bindings, that depend on the C library that must be installed beforehand.
|
|
311
|
+
So one should first compile the C library, link it with CSharp, and only then run tests.
|
|
312
|
+
|
|
313
|
+
```sh
|
|
314
|
+
cmake -B ./build_artifacts -DUSEARCH_BUILD_LIB_C=1 -DUSEARCH_BUILD_TEST_C=1 -DUSEARCH_USE_OPENMP=1 -DUSEARCH_USE_SIMSIMD=1
|
|
315
|
+
cmake --build ./build_artifacts --config Release -j
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Then, on Windows, copy the library to the CSharp project and run the tests:
|
|
319
|
+
|
|
320
|
+
```sh
|
|
321
|
+
mkdir -p ".\csharp\lib\runtimes\win-x64\native"
|
|
322
|
+
cp ".\build_artifacts\libusearch_c.dll" ".\csharp\lib\runtimes\win-x64\native"
|
|
323
|
+
cd csharp
|
|
324
|
+
dotnet test -c Debug --logger "console;verbosity=detailed"
|
|
325
|
+
dotnet test -c Release
|
|
326
|
+
```
|
|
327
|
+
|
|
294
328
|
## Wolfram
|
|
295
329
|
|
|
296
330
|
```sh
|
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -30,19 +30,18 @@ Spatial • Binary • Probabilistic • User-Defined Metrics
|
|
|
30
30
|
<a href="https://unum-cloud.github.io/usearch/swift">Swift</a> •
|
|
31
31
|
<a href="https://unum-cloud.github.io/usearch/csharp">C#</a> •
|
|
32
32
|
<a href="https://unum-cloud.github.io/usearch/golang">GoLang</a> •
|
|
33
|
-
<a href="https://unum-cloud.github.io/usearch/wolfram">Wolfram</a>
|
|
34
|
-
<a href="https://unum-cloud.github.io/usearch/sqlite">SQLite3</a>
|
|
33
|
+
<a href="https://unum-cloud.github.io/usearch/wolfram">Wolfram</a>
|
|
35
34
|
<br/>
|
|
36
|
-
Linux • MacOS • Windows • iOS • WebAssembly
|
|
35
|
+
Linux • MacOS • Windows • iOS • WebAssembly •
|
|
36
|
+
<a href="https://unum-cloud.github.io/usearch/sqlite">SQLite3</a>
|
|
37
37
|
</p>
|
|
38
38
|
|
|
39
39
|
<div align="center">
|
|
40
|
-
<a href="https://pepy.tech/project/usearch"> <img alt="PyPI" src="https://static.pepy.tech/personalized-badge/usearch?period=total&units=abbreviation&left_color=black&right_color=blue&left_text=
|
|
41
|
-
<a href="https://www.npmjs.com/package/usearch"> <img alt="NPM" src="https://img.shields.io/npm/dy/usearch?label=
|
|
42
|
-
<a href="https://crates.io/crates/usearch"> <img alt="Crate" src="https://img.shields.io/crates/d/usearch?label=
|
|
43
|
-
<a href="https://www.nuget.org/packages/Cloud.Unum.USearch"> <img alt="NuGet" src="https://img.shields.io/nuget/dt/Cloud.Unum.USearch?label=
|
|
44
|
-
<a href="https://central.sonatype.com/artifact/cloud.unum/usearch/overview"> <img alt="Maven" src="https://img.shields.io/nexus/r/cloud.unum/usearch?server=https%3A%2F%2Fs01.oss.sonatype.org%2F&label=
|
|
45
|
-
<a href="https://hub.docker.com/r/unum/usearch"> <img alt="Docker" src="https://img.shields.io/docker/pulls/unum/usearch?label=Docker%20installs"> </a>
|
|
40
|
+
<a href="https://pepy.tech/project/usearch"> <img alt="PyPI" src="https://static.pepy.tech/personalized-badge/usearch?period=total&units=abbreviation&left_color=black&right_color=blue&left_text=Python%20PyPi%20installs"> </a>
|
|
41
|
+
<a href="https://www.npmjs.com/package/usearch"> <img alt="NPM" src="https://img.shields.io/npm/dy/usearch?label=JavaScript%20NPM%20installs"> </a>
|
|
42
|
+
<a href="https://crates.io/crates/usearch"> <img alt="Crate" src="https://img.shields.io/crates/d/usearch?label=Rust%20Crate%20installs"> </a>
|
|
43
|
+
<a href="https://www.nuget.org/packages/Cloud.Unum.USearch"> <img alt="NuGet" src="https://img.shields.io/nuget/dt/Cloud.Unum.USearch?label=CSharp%20NuGet%20installs"> </a>
|
|
44
|
+
<a href="https://central.sonatype.com/artifact/cloud.unum/usearch/overview"> <img alt="Maven" src="https://img.shields.io/nexus/r/cloud.unum/usearch?server=https%3A%2F%2Fs01.oss.sonatype.org%2F&label=Java%20Maven%20version"> </a>
|
|
46
45
|
<img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/unum-cloud/usearch?label=Repo%20size">
|
|
47
46
|
</div>
|
|
48
47
|
|
|
@@ -50,18 +49,16 @@ Linux • MacOS • Windows • iOS • WebAssembly
|
|
|
50
49
|
|
|
51
50
|
- ✅ __[10x faster][faster-than-faiss]__ [HNSW][hnsw-algorithm] implementation than [FAISS][faiss].
|
|
52
51
|
- ✅ Simple and extensible [single C++11 header][usearch-header] __library__.
|
|
53
|
-
- ✅ Compatible with a dozen programming languages out of the box.
|
|
54
52
|
- ✅ [Trusted](#integrations) by giants like Google and databases like [ClickHouse][clickhouse-docs].
|
|
55
53
|
- ✅ [SIMD][simd]-optimized and [user-defined metrics](#user-defined-functions) with JIT compilation.
|
|
56
54
|
- ✅ Hardware-agnostic `f16` & `i8` - [half-precision & quarter-precision support](#memory-efficiency-downcasting-and-quantization).
|
|
57
55
|
- ✅ [View large indexes from disk](#serving-index-from-disk) without loading into RAM.
|
|
58
56
|
- ✅ Heterogeneous lookups, renaming/relabeling, and on-the-fly deletions.
|
|
59
|
-
- ✅ Variable dimensionality vectors for unique applications, including search over compressed data.
|
|
60
57
|
- ✅ Binary Tanimoto and Sorensen coefficients for [Genomics and Chemistry applications](#usearch--rdkit--molecular-search).
|
|
61
58
|
- ✅ Space-efficient point-clouds with `uint40_t`, accommodating 4B+ size.
|
|
62
|
-
- ✅ Compatible with OpenMP and custom "executors" for fine-grained
|
|
63
|
-
- ✅ Near-real-time [clustering and sub-clustering](#clustering) for Tens or Millions of clusters.
|
|
59
|
+
- ✅ Compatible with OpenMP and custom "executors" for fine-grained parallelism.
|
|
64
60
|
- ✅ [Semantic Search](#usearch--ai--multi-modal-semantic-search) and [Joins](#joins-one-to-one-one-to-many-and-many-to-many-mappings).
|
|
61
|
+
- 🔄 Near-real-time [clustering and sub-clustering](#clustering) for Tens or Millions of clusters.
|
|
65
62
|
|
|
66
63
|
[faiss]: https://github.com/facebookresearch/faiss
|
|
67
64
|
[usearch-header]: https://github.com/unum-cloud/usearch/blob/main/include/usearch/index.hpp
|
|
@@ -133,7 +130,7 @@ matches = index.search(vector, 10)
|
|
|
133
130
|
|
|
134
131
|
assert matches[0].key == 42
|
|
135
132
|
assert matches[0].distance <= 0.001
|
|
136
|
-
assert np.allclose(index[42], vector)
|
|
133
|
+
assert np.allclose(index[42], vector, atol=0.1) # Ensure high tolerance in mixed-precision comparisons
|
|
137
134
|
```
|
|
138
135
|
|
|
139
136
|
More settings are always available, and the API is designed to be as flexible as possible.
|
|
@@ -434,7 +431,7 @@ doi = {10.5281/zenodo.7949416},
|
|
|
434
431
|
author = {Vardanian, Ash},
|
|
435
432
|
title = {{USearch by Unum Cloud}},
|
|
436
433
|
url = {https://github.com/unum-cloud/usearch},
|
|
437
|
-
version = {2.9.
|
|
434
|
+
version = {2.9.2},
|
|
438
435
|
year = {2023},
|
|
439
436
|
month = oct,
|
|
440
437
|
}
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.9.
|
|
1
|
+
2.9.2
|
package/c/lib.cpp
CHANGED
|
@@ -106,7 +106,7 @@ extern "C" {
|
|
|
106
106
|
|
|
107
107
|
USEARCH_EXPORT usearch_index_t usearch_init(usearch_init_options_t* options, usearch_error_t* error) {
|
|
108
108
|
|
|
109
|
-
assert(options && error);
|
|
109
|
+
assert(options && error && "Missing arguments");
|
|
110
110
|
|
|
111
111
|
index_dense_config_t config(options->connectivity, options->expansion_add, options->expansion_search);
|
|
112
112
|
config.multi = options->multi;
|
|
@@ -132,13 +132,13 @@ USEARCH_EXPORT void usearch_free(usearch_index_t index, usearch_error_t*) {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
USEARCH_EXPORT size_t usearch_serialized_length(usearch_index_t index, usearch_error_t*) {
|
|
135
|
-
assert(index);
|
|
135
|
+
assert(index && "Missing arguments");
|
|
136
136
|
return reinterpret_cast<index_dense_t*>(index)->serialized_length();
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
USEARCH_EXPORT void usearch_save(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
140
140
|
|
|
141
|
-
assert(index && path && error);
|
|
141
|
+
assert(index && path && error && "Missing arguments");
|
|
142
142
|
serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->save(path);
|
|
143
143
|
if (!result)
|
|
144
144
|
*error = result.error.release();
|
|
@@ -146,7 +146,7 @@ USEARCH_EXPORT void usearch_save(usearch_index_t index, char const* path, usearc
|
|
|
146
146
|
|
|
147
147
|
USEARCH_EXPORT void usearch_load(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
148
148
|
|
|
149
|
-
assert(index && path && error);
|
|
149
|
+
assert(index && path && error && "Missing arguments");
|
|
150
150
|
serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->load(path);
|
|
151
151
|
if (!result)
|
|
152
152
|
*error = result.error.release();
|
|
@@ -154,7 +154,7 @@ USEARCH_EXPORT void usearch_load(usearch_index_t index, char const* path, usearc
|
|
|
154
154
|
|
|
155
155
|
USEARCH_EXPORT void usearch_view(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
156
156
|
|
|
157
|
-
assert(index && path && error);
|
|
157
|
+
assert(index && path && error && "Missing arguments");
|
|
158
158
|
serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->view(path);
|
|
159
159
|
if (!result)
|
|
160
160
|
*error = result.error.release();
|
|
@@ -162,7 +162,7 @@ USEARCH_EXPORT void usearch_view(usearch_index_t index, char const* path, usearc
|
|
|
162
162
|
|
|
163
163
|
USEARCH_EXPORT void usearch_metadata(char const* path, usearch_init_options_t* options, usearch_error_t* error) {
|
|
164
164
|
|
|
165
|
-
assert(path && options && error);
|
|
165
|
+
assert(path && options && error && "Missing arguments");
|
|
166
166
|
index_dense_metadata_result_t result = index_dense_metadata_from_path(path);
|
|
167
167
|
if (!result)
|
|
168
168
|
*error = result.error.release();
|
|
@@ -180,7 +180,7 @@ USEARCH_EXPORT void usearch_metadata(char const* path, usearch_init_options_t* o
|
|
|
180
180
|
|
|
181
181
|
USEARCH_EXPORT void usearch_save_buffer(usearch_index_t index, void* buffer, size_t length, usearch_error_t* error) {
|
|
182
182
|
|
|
183
|
-
assert(index && buffer && length && error);
|
|
183
|
+
assert(index && buffer && length && error && "Missing arguments");
|
|
184
184
|
memory_mapped_file_t memory_map((byte_t*)buffer, length);
|
|
185
185
|
serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->save(std::move(memory_map));
|
|
186
186
|
if (!result)
|
|
@@ -190,7 +190,7 @@ USEARCH_EXPORT void usearch_save_buffer(usearch_index_t index, void* buffer, siz
|
|
|
190
190
|
USEARCH_EXPORT void usearch_load_buffer(usearch_index_t index, void const* buffer, size_t length,
|
|
191
191
|
usearch_error_t* error) {
|
|
192
192
|
|
|
193
|
-
assert(index && buffer && length && error);
|
|
193
|
+
assert(index && buffer && length && error && "Missing arguments");
|
|
194
194
|
memory_mapped_file_t memory_map((byte_t*)buffer, length);
|
|
195
195
|
serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->load(std::move(memory_map));
|
|
196
196
|
if (!result)
|
|
@@ -200,7 +200,7 @@ USEARCH_EXPORT void usearch_load_buffer(usearch_index_t index, void const* buffe
|
|
|
200
200
|
USEARCH_EXPORT void usearch_view_buffer(usearch_index_t index, void const* buffer, size_t length,
|
|
201
201
|
usearch_error_t* error) {
|
|
202
202
|
|
|
203
|
-
assert(index && buffer && length && error);
|
|
203
|
+
assert(index && buffer && length && error && "Missing arguments");
|
|
204
204
|
memory_mapped_file_t memory_map((byte_t*)buffer, length);
|
|
205
205
|
serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->view(std::move(memory_map));
|
|
206
206
|
if (!result)
|
|
@@ -210,7 +210,7 @@ USEARCH_EXPORT void usearch_view_buffer(usearch_index_t index, void const* buffe
|
|
|
210
210
|
USEARCH_EXPORT void usearch_metadata_buffer(void const* buffer, size_t length, usearch_init_options_t* options,
|
|
211
211
|
usearch_error_t* error) {
|
|
212
212
|
|
|
213
|
-
assert(buffer && length && options && error);
|
|
213
|
+
assert(buffer && length && options && error && "Missing arguments");
|
|
214
214
|
index_dense_metadata_result_t result =
|
|
215
215
|
index_dense_metadata_from_buffer(memory_mapped_file_t((byte_t*)(buffer), length));
|
|
216
216
|
if (!result)
|
|
@@ -244,7 +244,7 @@ USEARCH_EXPORT size_t usearch_connectivity(usearch_index_t index, usearch_error_
|
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
USEARCH_EXPORT void usearch_reserve(usearch_index_t index, size_t capacity, usearch_error_t* error) {
|
|
247
|
-
assert(index && error);
|
|
247
|
+
assert(index && error && "Missing arguments");
|
|
248
248
|
if (!reinterpret_cast<index_dense_t*>(index)->reserve(capacity))
|
|
249
249
|
*error = "Out of memory!";
|
|
250
250
|
}
|
|
@@ -253,19 +253,19 @@ USEARCH_EXPORT void usearch_add(
|
|
|
253
253
|
usearch_index_t index, usearch_key_t key, void const* vector, usearch_scalar_kind_t kind, //
|
|
254
254
|
usearch_error_t* error) {
|
|
255
255
|
|
|
256
|
-
assert(index && vector && error);
|
|
256
|
+
assert(index && vector && error && "Missing arguments");
|
|
257
257
|
add_result_t result = add_(reinterpret_cast<index_dense_t*>(index), key, vector, scalar_kind_to_cpp(kind));
|
|
258
258
|
if (!result)
|
|
259
259
|
*error = result.error.release();
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
USEARCH_EXPORT bool usearch_contains(usearch_index_t index, usearch_key_t key, usearch_error_t*) {
|
|
263
|
-
assert(index);
|
|
263
|
+
assert(index && "Missing arguments");
|
|
264
264
|
return reinterpret_cast<index_dense_t*>(index)->contains(key);
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
USEARCH_EXPORT size_t usearch_count(usearch_index_t index, usearch_key_t key, usearch_error_t*) {
|
|
268
|
-
assert(index);
|
|
268
|
+
assert(index && "Missing arguments");
|
|
269
269
|
return reinterpret_cast<index_dense_t*>(index)->count(key);
|
|
270
270
|
}
|
|
271
271
|
|
|
@@ -273,7 +273,7 @@ USEARCH_EXPORT size_t usearch_search(
|
|
|
273
273
|
usearch_index_t index, void const* vector, usearch_scalar_kind_t kind, size_t results_limit, //
|
|
274
274
|
usearch_key_t* found_keys, usearch_distance_t* found_distances, usearch_error_t* error) {
|
|
275
275
|
|
|
276
|
-
assert(index && vector && error);
|
|
276
|
+
assert(index && vector && error && "Missing arguments");
|
|
277
277
|
search_result_t result =
|
|
278
278
|
search_(reinterpret_cast<index_dense_t*>(index), vector, scalar_kind_to_cpp(kind), results_limit);
|
|
279
279
|
if (!result) {
|
|
@@ -294,7 +294,7 @@ USEARCH_EXPORT size_t usearch_get( //
|
|
|
294
294
|
|
|
295
295
|
USEARCH_EXPORT size_t usearch_remove(usearch_index_t index, usearch_key_t key, usearch_error_t* error) {
|
|
296
296
|
|
|
297
|
-
assert(index && error);
|
|
297
|
+
assert(index && error && "Missing arguments");
|
|
298
298
|
labeling_result_t result = reinterpret_cast<index_dense_t*>(index)->remove(key);
|
|
299
299
|
if (!result)
|
|
300
300
|
*error = result.error.release();
|
|
@@ -304,7 +304,7 @@ USEARCH_EXPORT size_t usearch_remove(usearch_index_t index, usearch_key_t key, u
|
|
|
304
304
|
USEARCH_EXPORT size_t usearch_rename( //
|
|
305
305
|
usearch_index_t index, usearch_key_t from, usearch_key_t to, usearch_error_t* error) {
|
|
306
306
|
|
|
307
|
-
assert(index && error);
|
|
307
|
+
assert(index && error && "Missing arguments");
|
|
308
308
|
labeling_result_t result = reinterpret_cast<index_dense_t*>(index)->rename(from, to);
|
|
309
309
|
if (!result)
|
|
310
310
|
*error = result.error.release();
|
|
@@ -330,6 +330,8 @@ USEARCH_EXPORT void usearch_exact_search( //
|
|
|
330
330
|
usearch_distance_t* distances, size_t distances_stride, //
|
|
331
331
|
usearch_error_t* error) {
|
|
332
332
|
|
|
333
|
+
assert(dataset && queries && keys && distances && error && "Missing arguments");
|
|
334
|
+
|
|
333
335
|
metric_punned_t metric(dimensions, metric_kind_to_cpp(metric_kind), scalar_kind_to_cpp(scalar_kind));
|
|
334
336
|
executor_default_t executor(threads);
|
|
335
337
|
static exact_search_t search;
|
package/c/test.c
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#include <errno.h>
|
|
2
|
-
#include <stdio.h>
|
|
2
|
+
#include <stdio.h> // `remove`
|
|
3
3
|
#include <stdlib.h>
|
|
4
4
|
#include <sys/stat.h>
|
|
5
5
|
|
|
@@ -35,6 +35,7 @@ usearch_init_options_t create_options(size_t const dimensions) {
|
|
|
35
35
|
opts.metric_kind = usearch_metric_ip_k;
|
|
36
36
|
opts.metric = NULL;
|
|
37
37
|
opts.quantization = usearch_scalar_f32_k;
|
|
38
|
+
opts.multi = false;
|
|
38
39
|
return opts;
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -45,7 +46,7 @@ usearch_init_options_t create_options(size_t const dimensions) {
|
|
|
45
46
|
* dimensions, and connectivity after each operation.
|
|
46
47
|
*/
|
|
47
48
|
void test_init(size_t const collection_size, size_t const dimensions) {
|
|
48
|
-
printf("Test: Index Initialization
|
|
49
|
+
printf("Test: Index Initialization... %zu vectors, %zu dimensions \n", collection_size, dimensions);
|
|
49
50
|
|
|
50
51
|
// Init index
|
|
51
52
|
usearch_error_t error = NULL;
|
|
@@ -84,7 +85,7 @@ void test_init(size_t const collection_size, size_t const dimensions) {
|
|
|
84
85
|
* size, capacity, and presence of each vector in the index.
|
|
85
86
|
*/
|
|
86
87
|
void test_add_vector(size_t const collection_size, size_t const dimensions) {
|
|
87
|
-
printf("Test: Add Vector
|
|
88
|
+
printf("Test: Add Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
|
|
88
89
|
|
|
89
90
|
usearch_error_t error = NULL;
|
|
90
91
|
usearch_init_options_t opts = create_options(dimensions);
|
|
@@ -120,7 +121,7 @@ void test_add_vector(size_t const collection_size, size_t const dimensions) {
|
|
|
120
121
|
* found in the index, validating the count of found vectors.
|
|
121
122
|
*/
|
|
122
123
|
void test_find_vector(size_t const collection_size, size_t const dimensions) {
|
|
123
|
-
printf("Test: Find Vector
|
|
124
|
+
printf("Test: Find Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
|
|
124
125
|
|
|
125
126
|
usearch_error_t error = NULL;
|
|
126
127
|
usearch_init_options_t opts = create_options(dimensions);
|
|
@@ -161,7 +162,7 @@ void test_find_vector(size_t const collection_size, size_t const dimensions) {
|
|
|
161
162
|
* retrieves vectors associated with the key from the index and checks the count of retrieved vectors.
|
|
162
163
|
*/
|
|
163
164
|
void test_get_vector(size_t const collection_size, size_t const dimensions) {
|
|
164
|
-
printf("Test: Get Vector
|
|
165
|
+
printf("Test: Get Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
|
|
165
166
|
|
|
166
167
|
usearch_error_t error = NULL;
|
|
167
168
|
usearch_init_options_t opts = create_options(dimensions);
|
|
@@ -198,7 +199,7 @@ void test_get_vector(size_t const collection_size, size_t const dimensions) {
|
|
|
198
199
|
* the assert in this test expects an error, indicating that the remove functionality is not currently supported.
|
|
199
200
|
*/
|
|
200
201
|
void test_remove_vector(size_t const collection_size, size_t const dimensions) {
|
|
201
|
-
printf("Test: Remove Vector
|
|
202
|
+
printf("Test: Remove Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
|
|
202
203
|
|
|
203
204
|
usearch_error_t error = NULL;
|
|
204
205
|
usearch_init_options_t opts = create_options(dimensions);
|
|
@@ -232,48 +233,89 @@ void test_remove_vector(size_t const collection_size, size_t const dimensions) {
|
|
|
232
233
|
* from the saved index.
|
|
233
234
|
*/
|
|
234
235
|
void test_save_load(size_t const collection_size, size_t const dimensions) {
|
|
235
|
-
printf("Test: Save/Load
|
|
236
|
+
printf("Test: Save/Load... %zu vectors, %zu dimensions \n", collection_size, dimensions);
|
|
237
|
+
float* data = create_vectors(collection_size, dimensions);
|
|
236
238
|
|
|
237
239
|
usearch_error_t error = NULL;
|
|
238
240
|
usearch_init_options_t opts = create_options(dimensions);
|
|
239
|
-
|
|
240
|
-
|
|
241
|
+
usearch_init_options_t weird_ops = opts;
|
|
242
|
+
weird_ops.connectivity = 11;
|
|
243
|
+
weird_ops.expansion_add = 15;
|
|
244
|
+
weird_ops.expansion_search = 19;
|
|
245
|
+
weird_ops.metric_kind = usearch_metric_pearson_k;
|
|
246
|
+
weird_ops.quantization = usearch_scalar_f64_k;
|
|
247
|
+
|
|
248
|
+
{
|
|
249
|
+
|
|
250
|
+
usearch_index_t index = usearch_init(&weird_ops, &error);
|
|
251
|
+
usearch_reserve(index, collection_size, &error);
|
|
252
|
+
|
|
253
|
+
// Add vectors
|
|
254
|
+
for (size_t i = 0; i < collection_size; ++i) {
|
|
255
|
+
usearch_key_t key = i;
|
|
256
|
+
usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
|
|
257
|
+
ASSERT(!error, error);
|
|
258
|
+
}
|
|
241
259
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
|
|
260
|
+
// Save and free the index
|
|
261
|
+
usearch_save(index, "tmp.usearch", &error);
|
|
262
|
+
ASSERT(!error, error);
|
|
263
|
+
usearch_free(index, &error);
|
|
247
264
|
ASSERT(!error, error);
|
|
248
265
|
}
|
|
249
266
|
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
267
|
+
// Reset the options
|
|
268
|
+
opts.connectivity = 0;
|
|
269
|
+
opts.dimensions = 0;
|
|
270
|
+
opts.expansion_add = 0;
|
|
271
|
+
opts.expansion_search = 0;
|
|
272
|
+
opts.metric = NULL;
|
|
273
|
+
opts.quantization = usearch_scalar_unknown_k;
|
|
274
|
+
opts.metric_kind = usearch_metric_unknown_k;
|
|
255
275
|
|
|
256
276
|
// Reinit
|
|
257
|
-
|
|
258
|
-
ASSERT(!error, error);
|
|
259
|
-
ASSERT(usearch_size(index, &error) == 0, error);
|
|
277
|
+
{
|
|
260
278
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
ASSERT(usearch_size(index, &error) == collection_size, error);
|
|
265
|
-
ASSERT(usearch_capacity(index, &error) == collection_size, error);
|
|
266
|
-
ASSERT(usearch_dimensions(index, &error) == dimensions, error);
|
|
267
|
-
ASSERT(usearch_connectivity(index, &error) == opts.connectivity, error);
|
|
279
|
+
usearch_index_t index = usearch_init(&opts, &error);
|
|
280
|
+
ASSERT(!error, error);
|
|
281
|
+
ASSERT(usearch_size(index, &error) == 0, error);
|
|
268
282
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
ASSERT(
|
|
283
|
+
// Load
|
|
284
|
+
usearch_load(index, "tmp.usearch", &error);
|
|
285
|
+
ASSERT(!error, error);
|
|
286
|
+
ASSERT(usearch_size(index, &error) == collection_size, error);
|
|
287
|
+
ASSERT(usearch_capacity(index, &error) == collection_size, error);
|
|
288
|
+
ASSERT(usearch_dimensions(index, &error) == dimensions, error);
|
|
289
|
+
ASSERT(usearch_connectivity(index, &error) == weird_ops.connectivity, error);
|
|
290
|
+
|
|
291
|
+
// Check vectors in the index
|
|
292
|
+
for (size_t i = 0; i < collection_size; ++i) {
|
|
293
|
+
usearch_key_t key = i;
|
|
294
|
+
ASSERT(usearch_contains(index, key, &error), error);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Create result buffers
|
|
298
|
+
usearch_key_t* keys = (usearch_key_t*)malloc(collection_size * sizeof(usearch_key_t));
|
|
299
|
+
float* distances = (float*)malloc(collection_size * sizeof(float));
|
|
300
|
+
ASSERT(keys && distances, "Failed to allocate memory");
|
|
301
|
+
|
|
302
|
+
// Find the vectors
|
|
303
|
+
for (size_t i = 0; i < collection_size; i++) {
|
|
304
|
+
size_t found_count = usearch_search(index, data + i * dimensions, usearch_scalar_f32_k, collection_size,
|
|
305
|
+
keys, distances, &error);
|
|
306
|
+
ASSERT(!error, error);
|
|
307
|
+
ASSERT(found_count >= 1 && found_count <= collection_size, "Vector is missing");
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
free(keys);
|
|
311
|
+
free(distances);
|
|
312
|
+
usearch_free(index, &error);
|
|
273
313
|
}
|
|
274
314
|
|
|
275
315
|
free(data);
|
|
276
|
-
|
|
316
|
+
|
|
317
|
+
// Remove the file from disk
|
|
318
|
+
remove("tmp.usearch");
|
|
277
319
|
printf("Test: Save/Load - PASSED\n");
|
|
278
320
|
}
|
|
279
321
|
|
|
@@ -284,7 +326,7 @@ void test_save_load(size_t const collection_size, size_t const dimensions) {
|
|
|
284
326
|
* not verify the properties or contents of the viewed index.
|
|
285
327
|
*/
|
|
286
328
|
void test_view(size_t const collection_size, size_t const dimensions) {
|
|
287
|
-
printf("Test: View
|
|
329
|
+
printf("Test: View... %zu vectors, %zu dimensions \n", collection_size, dimensions);
|
|
288
330
|
|
|
289
331
|
usearch_error_t error = NULL;
|
|
290
332
|
usearch_init_options_t opts = create_options(dimensions);
|
|
@@ -300,7 +342,7 @@ void test_view(size_t const collection_size, size_t const dimensions) {
|
|
|
300
342
|
}
|
|
301
343
|
|
|
302
344
|
// Save and free the index
|
|
303
|
-
usearch_save(index, "
|
|
345
|
+
usearch_save(index, "tmp.usearch", &error);
|
|
304
346
|
ASSERT(!error, error);
|
|
305
347
|
usearch_free(index, &error);
|
|
306
348
|
ASSERT(!error, error);
|
|
@@ -310,7 +352,7 @@ void test_view(size_t const collection_size, size_t const dimensions) {
|
|
|
310
352
|
ASSERT(!error, error);
|
|
311
353
|
|
|
312
354
|
// View
|
|
313
|
-
usearch_view(index, "
|
|
355
|
+
usearch_view(index, "tmp.usearch", &error);
|
|
314
356
|
ASSERT(!error, error);
|
|
315
357
|
|
|
316
358
|
free(data);
|
|
@@ -321,7 +363,7 @@ void test_view(size_t const collection_size, size_t const dimensions) {
|
|
|
321
363
|
int main(int argc, char const* argv[]) {
|
|
322
364
|
|
|
323
365
|
size_t collection_sizes[] = {11, 512};
|
|
324
|
-
size_t dimensions[] = {83,
|
|
366
|
+
size_t dimensions[] = {83, 2}; // Not all distance functions make sense for 1 dimensional data
|
|
325
367
|
for (size_t index = 0; index < sizeof(collection_sizes) / sizeof(collection_sizes[0]); ++index) {
|
|
326
368
|
for (size_t jdx = 0; jdx < sizeof(dimensions) / sizeof(dimensions[0]); ++jdx) {
|
|
327
369
|
test_init(collection_sizes[index], dimensions[jdx]);
|
package/c/usearch.h
CHANGED
|
@@ -39,25 +39,25 @@ USEARCH_EXPORT typedef usearch_distance_t (*usearch_metric_t)(void const*, void
|
|
|
39
39
|
*/
|
|
40
40
|
USEARCH_EXPORT typedef enum usearch_metric_kind_t {
|
|
41
41
|
usearch_metric_unknown_k = 0,
|
|
42
|
-
usearch_metric_cos_k,
|
|
43
|
-
usearch_metric_ip_k,
|
|
44
|
-
usearch_metric_l2sq_k,
|
|
45
|
-
usearch_metric_haversine_k,
|
|
46
|
-
usearch_metric_divergence_k,
|
|
47
|
-
usearch_metric_pearson_k,
|
|
48
|
-
usearch_metric_jaccard_k,
|
|
49
|
-
usearch_metric_hamming_k,
|
|
50
|
-
usearch_metric_tanimoto_k,
|
|
51
|
-
usearch_metric_sorensen_k,
|
|
42
|
+
usearch_metric_cos_k = 1,
|
|
43
|
+
usearch_metric_ip_k = 2,
|
|
44
|
+
usearch_metric_l2sq_k = 3,
|
|
45
|
+
usearch_metric_haversine_k = 4,
|
|
46
|
+
usearch_metric_divergence_k = 5,
|
|
47
|
+
usearch_metric_pearson_k = 6,
|
|
48
|
+
usearch_metric_jaccard_k = 7,
|
|
49
|
+
usearch_metric_hamming_k = 8,
|
|
50
|
+
usearch_metric_tanimoto_k = 9,
|
|
51
|
+
usearch_metric_sorensen_k = 10,
|
|
52
52
|
} usearch_metric_kind_t;
|
|
53
53
|
|
|
54
54
|
USEARCH_EXPORT typedef enum usearch_scalar_kind_t {
|
|
55
55
|
usearch_scalar_unknown_k = 0,
|
|
56
|
-
usearch_scalar_f32_k,
|
|
57
|
-
usearch_scalar_f64_k,
|
|
58
|
-
usearch_scalar_f16_k,
|
|
59
|
-
usearch_scalar_i8_k,
|
|
60
|
-
usearch_scalar_b1_k,
|
|
56
|
+
usearch_scalar_f32_k = 1,
|
|
57
|
+
usearch_scalar_f64_k = 2,
|
|
58
|
+
usearch_scalar_f16_k = 3,
|
|
59
|
+
usearch_scalar_i8_k = 4,
|
|
60
|
+
usearch_scalar_b1_k = 5,
|
|
61
61
|
} usearch_scalar_kind_t;
|
|
62
62
|
|
|
63
63
|
USEARCH_EXPORT typedef struct usearch_init_options_t {
|
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.9.
|
|
10
|
+
version = '2.9.2'
|
|
11
11
|
license = "Apache-2.0"
|
|
12
12
|
description = "Smaller & Faster Single-File Vector Search Engine from Unum"
|
|
13
13
|
homepage = "https://github.com/unum-cloud/usearch"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
#define USEARCH_VERSION_MAJOR 2
|
|
13
13
|
#define USEARCH_VERSION_MINOR 9
|
|
14
|
-
#define USEARCH_VERSION_PATCH
|
|
14
|
+
#define USEARCH_VERSION_PATCH 2
|
|
15
15
|
|
|
16
16
|
// Inferring C++ version
|
|
17
17
|
// https://stackoverflow.com/a/61552074
|
|
@@ -2543,8 +2543,8 @@ class index_gt {
|
|
|
2543
2543
|
if (!next.reserve(expansion))
|
|
2544
2544
|
return result.failed("Out of memory!");
|
|
2545
2545
|
|
|
2546
|
-
result.cluster.member =
|
|
2547
|
-
|
|
2546
|
+
result.cluster.member = at(search_for_one_(query, metric, prefetch, entry_slot_, max_level_,
|
|
2547
|
+
static_cast<level_t>(level - 1), context));
|
|
2548
2548
|
result.cluster.distance = context.measure(query, result.cluster.member, metric);
|
|
2549
2549
|
|
|
2550
2550
|
// Normalize stats
|
|
@@ -3422,7 +3422,7 @@ class index_gt {
|
|
|
3422
3422
|
distance_t radius = context.measure(query, citerator_at(start_slot), metric);
|
|
3423
3423
|
next.insert_reserved({-radius, static_cast<compressed_slot_t>(start_slot)});
|
|
3424
3424
|
top.insert_reserved({radius, static_cast<compressed_slot_t>(start_slot)});
|
|
3425
|
-
visits.set(start_slot);
|
|
3425
|
+
visits.set(static_cast<compressed_slot_t>(start_slot));
|
|
3426
3426
|
|
|
3427
3427
|
while (!next.empty()) {
|
|
3428
3428
|
|
|
@@ -3496,7 +3496,7 @@ class index_gt {
|
|
|
3496
3496
|
distance_t radius = context.measure(query, citerator_at(start_slot), metric);
|
|
3497
3497
|
next.insert_reserved({-radius, static_cast<compressed_slot_t>(start_slot)});
|
|
3498
3498
|
top.insert_reserved({radius, static_cast<compressed_slot_t>(start_slot)});
|
|
3499
|
-
visits.set(start_slot);
|
|
3499
|
+
visits.set(static_cast<compressed_slot_t>(start_slot));
|
|
3500
3500
|
|
|
3501
3501
|
while (!next.empty()) {
|
|
3502
3502
|
|
|
@@ -199,7 +199,7 @@ inline index_dense_metadata_result_t index_dense_metadata_from_path(char const*
|
|
|
199
199
|
|
|
200
200
|
// Check if it starts with 32-bit
|
|
201
201
|
if (offset_if_u32 + sizeof(index_dense_head_buffer_t) < file_size) {
|
|
202
|
-
if (std::fseek(file.get(), offset_if_u32, SEEK_SET) != 0)
|
|
202
|
+
if (std::fseek(file.get(), static_cast<long>(offset_if_u32), SEEK_SET) != 0)
|
|
203
203
|
return result.failed(std::strerror(errno));
|
|
204
204
|
read = std::fread(result.head_buffer, sizeof(index_dense_head_buffer_t), 1, file.get());
|
|
205
205
|
if (!read)
|
|
@@ -213,7 +213,7 @@ inline index_dense_metadata_result_t index_dense_metadata_from_path(char const*
|
|
|
213
213
|
|
|
214
214
|
// Check if it starts with 64-bit
|
|
215
215
|
if (offset_if_u64 + sizeof(index_dense_head_buffer_t) < file_size) {
|
|
216
|
-
if (std::fseek(file.get(), offset_if_u64, SEEK_SET) != 0)
|
|
216
|
+
if (std::fseek(file.get(), static_cast<long>(offset_if_u64), SEEK_SET) != 0)
|
|
217
217
|
return result.failed(std::strerror(errno));
|
|
218
218
|
read = std::fread(result.head_buffer, sizeof(index_dense_head_buffer_t), 1, file.get());
|
|
219
219
|
if (!read)
|
|
@@ -929,8 +929,10 @@ class index_dense_gt {
|
|
|
929
929
|
if (head.kind_compressed_slot != unum::usearch::scalar_kind<compressed_slot_t>())
|
|
930
930
|
return result.failed("Slot type doesn't match, consider rebuilding");
|
|
931
931
|
|
|
932
|
-
metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
|
|
933
932
|
config_.multi = head.multi;
|
|
933
|
+
metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
|
|
934
|
+
cast_buffer_.resize(available_threads_.size() * metric_.bytes_per_vector());
|
|
935
|
+
casts_ = make_casts_(head.kind_scalar);
|
|
934
936
|
}
|
|
935
937
|
|
|
936
938
|
// Pull the actual proximity graph
|
|
@@ -1013,8 +1015,10 @@ class index_dense_gt {
|
|
|
1013
1015
|
if (head.kind_compressed_slot != unum::usearch::scalar_kind<compressed_slot_t>())
|
|
1014
1016
|
return result.failed("Slot type doesn't match, consider rebuilding");
|
|
1015
1017
|
|
|
1016
|
-
metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
|
|
1017
1018
|
config_.multi = head.multi;
|
|
1019
|
+
metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
|
|
1020
|
+
cast_buffer_.resize(available_threads_.size() * metric_.bytes_per_vector());
|
|
1021
|
+
casts_ = make_casts_(head.kind_scalar);
|
|
1018
1022
|
offset += sizeof(buffer);
|
|
1019
1023
|
}
|
|
1020
1024
|
|