usearch 2.8.3 → 2.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.vscode/settings.json +1 -0
  2. package/CITATION.cff +1 -1
  3. package/CONTRIBUTING.md +258 -6
  4. package/Cargo.toml +1 -1
  5. package/README.md +57 -53
  6. package/conanfile.py +1 -1
  7. package/cpp/README.md +89 -21
  8. package/csharp/DEV_SETUP.md +1 -1
  9. package/csharp/nuget/nuget-package.props +1 -1
  10. package/docs/{compilation.rst → contributing.rst} +2 -2
  11. package/docs/index.rst +1 -1
  12. package/include/usearch/index.hpp +1 -1
  13. package/include/usearch/index_dense.hpp +11 -7
  14. package/package.json +1 -1
  15. package/python/README.md +1 -1
  16. package/simsimd/CMakeLists.txt +1 -1
  17. package/simsimd/README.md +7 -1
  18. package/simsimd/include/simsimd/probability.h +24 -24
  19. package/simsimd/include/simsimd/types.h +8 -0
  20. package/simsimd/javascript/{test.js → test/simsimd.test.js} +15 -15
  21. package/simsimd/package.json +4 -8
  22. package/simsimd/setup.py +3 -0
  23. package/wasmer.toml +1 -1
  24. package/docs/compilation.md +0 -215
  25. package/simsimd/.clang-format +0 -10
  26. package/simsimd/.github/workflows/prerelease.yml +0 -89
  27. package/simsimd/.github/workflows/release.yml +0 -141
  28. package/simsimd/.github/workflows/update_version.sh +0 -5
  29. package/simsimd/.vscode/launch.json +0 -53
  30. package/simsimd/.vscode/settings.json +0 -114
  31. package/simsimd/.vscode/tasks.json +0 -23
  32. package/simsimd/go.mod +0 -3
  33. package/simsimd/golang/bench_test.go +0 -40
  34. package/simsimd/golang/simsimd.go +0 -47
  35. package/simsimd/golang/unit_test.go +0 -40
  36. package/simsimd/pyproject.toml +0 -42
  37. package/simsimd/python/bench.ipynb +0 -187
  38. package/simsimd/python/bench.py +0 -268
  39. package/simsimd/python/lib.c +0 -492
  40. package/simsimd/python/test.py +0 -195
@@ -147,6 +147,7 @@
147
147
  "pytest",
148
148
  "Quickstart",
149
149
  "rtype",
150
+ "SIMD",
150
151
  "simsimd",
151
152
  "SLOC",
152
153
  "sorensen",
package/CITATION.cff CHANGED
@@ -5,7 +5,7 @@ authors:
5
5
  given-names: "Ash"
6
6
  orcid: "https://orcid.org/0000-0002-4882-1815"
7
7
  title: "USearch by Unum Cloud"
8
- version: 2.8.3
8
+ version: 2.8.4
9
9
  doi: 10.5281/zenodo.7949416
10
10
  date-released: 2023-10-22
11
11
  url: "https://github.com/unum-cloud/usearch"
package/CONTRIBUTING.md CHANGED
@@ -1,12 +1,264 @@
1
1
  # Contribution Guide
2
2
 
3
- Thank you for even opening this page!
4
- It's always nice to have third-party contributors!
3
+ Thank you for coming here!
4
+ It's always nice to have third-party contributors 🤗
5
5
 
6
6
  ---
7
7
 
8
- To keep the quality of the code high, we have a set of guidelines common to all Unum projects.
8
+ To keep the quality of the code high, we have a set of guidelines common to [all Unum projects](https://github.com/unum-cloud).
9
9
 
10
- - Whats the procedure? [answered](https://github.com/unum-cloud/awesome/blob/main/Workflow.md#organizing-software-development)
11
- - How to organize branches? [answered](https://github.com/unum-cloud/awesome/blob/main/Workflow.md#branches)
12
- - How to style commits? [answered](https://github.com/unum-cloud/awesome/blob/main/Workflow.md#commits)
10
+ - [What's the procedure?](https://github.com/unum-cloud/awesome/blob/main/Workflow.md#organizing-software-development)
11
+ - [How to organize branches?](https://github.com/unum-cloud/awesome/blob/main/Workflow.md#branches)
12
+ - [How to style commits?](https://github.com/unum-cloud/awesome/blob/main/Workflow.md#commits)
13
+
14
+ ## Before you start
15
+
16
+ Before building the first time, please pull `git` submodules.
17
+ That's how we bring in SimSIMD and other optional dependencies to test all of the available functionality.
18
+
19
+ ```sh
20
+ git submodule update --init --recursive
21
+ ```
22
+
23
+ ## C++ 11 and C 99
24
+
25
+ Our primary C++ implementation uses CMake for builds.
26
+ If this is your first experience with CMake, use the following commands to get started:
27
+
28
+ ```sh
29
+ sudo apt-get update && sudo apt-get install cmake build-essential libjemalloc-dev g++-12 gcc-12 # Ubuntu
30
+ brew install libomp llvm # MacOS
31
+ ```
32
+
33
+ Using modern syntax, this is how you build and run the test suite:
34
+
35
+ ```sh
36
+ cmake -DUSEARCH_BUILD_TEST_CPP=1 -B ./build_debug
37
+ cmake --build ./build_debug --config Debug
38
+ ./build_debug/test_cpp
39
+ ```
40
+
41
+ The CMakeLists.txt file has a number of options you can pass:
42
+
43
+ - What to build:
44
+ - `USEARCH_BUILD_TEST_CPP` - build the C++ test suite
45
+ - `USEARCH_BUILD_BENCH_CPP` - build the C++ benchmark suite
46
+ - `USEARCH_BUILD_LIB_C` - build the C library
47
+ - `USEARCH_BUILD_TEST_C` - build the C test suite
48
+ - Which dependencies to use:
49
+ - `USEARCH_USE_OPENMP` - use OpenMP for parallelism
50
+ - `USEARCH_USE_SIMSIMD` - use SimSIMD for vectorization
51
+ - `USEARCH_USE_JEMALLOC` - use Jemalloc for memory management
52
+ - `USEARCH_USE_NATIVE_F16` - use native half-precision floating point
53
+
54
+ Putting all of this together on Ubuntu and compiling the "release" version using the GCC 12 compiler:
55
+
56
+ ```sh
57
+ cmake \
58
+ -DCMAKE_BUILD_TYPE=Release \
59
+ -DCMAKE_C_COMPILER=gcc-12 \
60
+ -DCMAKE_CXX_COMPILER=g++-12 \
61
+ -DUSEARCH_USE_NATIVE_F16=1 \
62
+ -DUSEARCH_USE_OPENMP=1 \
63
+ -DUSEARCH_USE_SIMSIMD=1 \
64
+ -DUSEARCH_USE_JEMALLOC=1 \
65
+ -DUSEARCH_BUILD_TEST_CPP=1 \
66
+ -DUSEARCH_BUILD_BENCH_CPP=1 \
67
+ -DUSEARCH_BUILD_LIB_C=1 \
68
+ -DUSEARCH_BUILD_TEST_C=1 \
69
+ -B ./build_release
70
+
71
+ cmake --build ./build_release --config Release
72
+ ./build_release/test_cpp
73
+ ./build_release/test_c
74
+ ```
75
+
76
+ Similarly, to use the most recent Clang compiler version from HomeBrew on MacOS:
77
+
78
+ ```sh
79
+ cmake \
80
+ -DCMAKE_BUILD_TYPE=Release \
81
+ -DCMAKE_C_COMPILER="/opt/homebrew/opt/llvm/bin/clang" \
82
+ -DCMAKE_CXX_COMPILER="/opt/homebrew/opt/llvm/bin/clang++" \
83
+ -DUSEARCH_USE_NATIVE_F16=1 \
84
+ -DUSEARCH_USE_OPENMP=1 \
85
+ -DUSEARCH_USE_SIMSIMD=1 \
86
+ -DUSEARCH_USE_JEMALLOC=1 \
87
+ -DUSEARCH_BUILD_TEST_CPP=1 \
88
+ -DUSEARCH_BUILD_BENCH_CPP=1 \
89
+ -DUSEARCH_BUILD_LIB_C=1 \
90
+ -DUSEARCH_BUILD_TEST_C=1 \
91
+ -B ./build_release
92
+
93
+ cmake --build ./build_release --config Release
94
+ ./build_release/test_cpp
95
+ ./build_release/test_c
96
+ ```
97
+
98
+ Linting:
99
+
100
+ ```sh
101
+ cppcheck --enable=all --force --suppress=cstyleCast --suppress=unusedFunction \
102
+ include/usearch/index.hpp \
103
+ include/index_dense.hpp \
104
+ include/index_plugins.hpp
105
+ ```
106
+
107
+ ## Python 3
108
+
109
+ Python bindings are built using PyBind11 and are available on [PyPi](https://pypi.org/project/usearch/).
110
+ The compilation settings are controlled by the `setup.py` and are independent from CMake used for C/C++ builds.
111
+ Use PyTest to validate the build.
112
+
113
+ - The `-s` option will disable capturing the logs.
114
+ - The `-x` option will exit after first failure to simplify debugging.
115
+
116
+ ```sh
117
+ pip install -e . && pytest python/scripts/ -s -x
118
+ ```
119
+
120
+ Linting:
121
+
122
+ ```sh
123
+ pip install ruff
124
+ ruff --format=github --select=E9,F63,F7,F82 --target-version=py37 python
125
+ ```
126
+
127
+ Before merging your changes you may want to test your changes against the entire matrix of Python versions USearch supports.
128
+ For that you need the `cibuildwheel`, which is tricky to use on MacOS and Windows, as it would target just the local environment.
129
+ Still, if you have Docker running on any desktop OS, you can use it to build and test the Python bindings for all Python versions for Linux:
130
+
131
+ ```sh
132
+ pip install cibuildwheel
133
+ cibuildwheel --platform linux
134
+ ```
135
+
136
+ ## JavaScript
137
+
138
+ USearch provides NAPI bindings for NodeJS available on [NPM](https://www.npmjs.com/package/usearch).
139
+ The compilation settings are controlled by the `binding.gyp` and are independent from CMake used for C/C++ builds.
140
+
141
+ ```sh
142
+ npm install
143
+ node --test ./javascript/usearch.test.js
144
+ npm publish
145
+ ```
146
+
147
+ To compile to WebAssembly make sure you have `emscripten` installed and run the following script:
148
+
149
+ ```sh
150
+ emcmake cmake -B ./build -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -s TOTAL_MEMORY=64MB" && emmake make -C ./build
151
+ node ./build/usearch.test.js
152
+ ```
153
+
154
+ If you don't yet have `emcmake` installed:
155
+
156
+ ```sh
157
+ git clone https://github.com/emscripten-core/emsdk.git && ./emsdk/emsdk install latest && ./emsdk/emsdk activate latest && source ./emsdk/emsdk_env.sh
158
+ ```
159
+
160
+ ## Rust
161
+
162
+ USearch provides Rust bindings available on [Crates.io](https://crates.io/crates/usearch).
163
+ The compilation settings are controlled by the `build.rs` and are independent from CMake used for C/C++ builds.
164
+
165
+ ```sh
166
+ cargo test -p usearch
167
+ cargo publish
168
+ ```
169
+
170
+ ## Objective-C and Swift
171
+
172
+ USearch provides both Objective-C and Swift bindings through the [Swift Package Manager](https://swiftpackageindex.com/unum-cloud/usearch).
173
+ The compilation settings are controlled by the `Package.swift` and are independent from CMake used for C/C++ builds.
174
+
175
+ ```sh
176
+ swift build
177
+ swift test -v
178
+ ```
179
+
180
+ ## GoLang
181
+
182
+ USearch provides GoLang bindings, that depend on the C library that must be installed beforehand.
183
+ So one should first compile the C library, link it with GoLang, and only then run tests.
184
+
185
+ ```sh
186
+ cmake -B ./build_release -DUSEARCH_BUILD_LIB_C=1 -DUSEARCH_BUILD_TEST_C=1 -DUSEARCH_USE_OPENMP=1 -DUSEARCH_USE_SIMSIMD=1
187
+ cmake --build ./build_release --config Release -j
188
+
189
+ mv ./c/libusearch_c.so ./golang/ # or .dylib to install the library on MacOS
190
+ cp ./c/usearch.h ./golang/ # to make the header available to GoLang
191
+
192
+ cd golang && go test -v ; cd ..
193
+ ```
194
+
195
+ ## Java
196
+
197
+ USearch provides Java bindings available from the [GitHub Maven registry](https://github.com/unum-cloud/usearch/packages/1867475).
198
+ The compilation settings are controlled by the `build.gradle` and are independent from CMake used for C/C++ builds.
199
+
200
+ ```sh
201
+ gradle clean build
202
+ java -cp . -Djava.library.path="$(pwd)/build/libs/usearch/shared" java/cloud/unum/usearch/Index.java
203
+ ```
204
+
205
+ Or step by-step:
206
+
207
+ ```sh
208
+ cs java/unum/cloud/usearch
209
+ javac -h . Index.java
210
+
211
+ # Ubuntu:
212
+ g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
213
+ g++ -shared -fPIC -o libusearch_c.so cloud_unum_usearch_Index.o -lc
214
+
215
+ # Windows
216
+ g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
217
+ g++ -shared -o USearchJNI.dll cloud_unum_usearch_Index.o -Wl,--add-stdcall-alias
218
+
219
+ # MacOS
220
+ g++ -std=c++11 -c -fPIC \
221
+ -I../../../../include \
222
+ -I../../../../fp16/include \
223
+ -I../../../../simsimd/include \
224
+ -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
225
+ g++ -dynamiclib -o libusearch.dylib cloud_unum_usearch_Index.o -lc
226
+ # Run linking to that directory
227
+ java -cp . -Djava.library.path="$(pwd)/java/cloud/unum/usearch/" Index.java
228
+ java -cp . -Djava.library.path="$(pwd)/java" cloud.unum.usearch.Index
229
+ ```
230
+
231
+ ## Wolfram
232
+
233
+ ```sh
234
+ brew install --cask wolfram-engine
235
+ ```
236
+
237
+ ## Docker
238
+
239
+ ```sh
240
+ docker build -t unum/usearch . && docker run unum/usearch
241
+ ```
242
+
243
+ For multi-architecture builds and publications:
244
+
245
+ ```sh
246
+ version=$(cat VERSION)
247
+ docker buildx create --use &&
248
+ docker login &&
249
+ docker buildx build \
250
+ --platform "linux/amd64,linux/arm64" \
251
+ --build-arg version=$version \
252
+ --file Dockerfile \
253
+ --tag unum/usearch:$version \
254
+ --tag unum/usearch:latest \
255
+ --push .
256
+ ```
257
+
258
+ ## Working on Sub-Modules
259
+
260
+ Extending metrics in SimSIMD:
261
+
262
+ ```sh
263
+ git push --set-upstream https://github.com/ashvardanian/simsimd.git HEAD:main
264
+ ```
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "usearch"
3
- version = "2.8.3"
3
+ version = "2.8.4"
4
4
  authors = ["Ash Vardanian <1983160+ashvardanian@users.noreply.github.com>"]
5
5
  description = "Smaller & Faster Single-File Vector Search Engine from Unum"
6
6
  edition = "2021"
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <h1 align="center">USearch</h1>
2
2
  <h3 align="center">
3
- Faster & Smaller Single-File<br/>
4
- Search Engine for Vectors & Texts<br/>
3
+ Smaller & <a href="https://www.unum.cloud/blog/2023-11-11-scaling-vector-search">Faster</a> Single-File<br/>
4
+ Similarity Search Engine for <a href="https://github.com/ashvardanian/simsimd">Vectors</a> & 🔜 <a href="https://github.com/ashvardanian/stringzilla">Texts</a>
5
5
  </h3>
6
6
  <br/>
7
7
 
@@ -18,7 +18,7 @@ Search Engine for Vectors & Texts<br/>
18
18
  </p>
19
19
 
20
20
  <p align="center">
21
- Euclidean • Angular • Bitwise • Haversine • User-Defined Metrics
21
+ Spatial • Binary • Probabilistic • User-Defined Metrics
22
22
  <br/>
23
23
  <a href="https://unum-cloud.github.io/usearch/cpp">C++ 11</a> •
24
24
  <a href="https://unum-cloud.github.io/usearch/python">Python 3</a> •
@@ -32,7 +32,7 @@ Euclidean • Angular • Bitwise • Haversine • User-Defined Metrics
32
32
  <a href="https://unum-cloud.github.io/usearch/golang">GoLang</a> •
33
33
  <a href="https://unum-cloud.github.io/usearch/wolfram">Wolfram</a>
34
34
  <br/>
35
- Linux • MacOS • Windows • iOS • Docker • WebAssembly
35
+ Linux • MacOS • Windows • iOS • WebAssembly
36
36
  </p>
37
37
 
38
38
  <div align="center">
@@ -46,40 +46,57 @@ Linux • MacOS • Windows • iOS • Docker • WebAssembly
46
46
 
47
47
  ---
48
48
 
49
- - ✅ Benchmark-topping performance.
49
+ - ✅ 10x faster than [FAISS][faiss] implementation of [HNSW algorithm][hnsw-algorithm].
50
50
  - ✅ Simple and extensible [single C++11 header][usearch-header] implementation.
51
- - ✅ SIMD-optimized and [user-defined metrics](#user-defined-functions) with JIT compilation.
52
- - ✅ Variable dimensionality vectors for unique applications, including search over compressed data.
53
- - ✅ Bitwise Tanimoto and Sorensen coefficients for [Genomics and Chemistry applications](#usearch--rdkit--molecular-search).
51
+ - ✅ Compatible with a dozen programming languages out of the box.
52
+ - ✅ [SIMD][simd]-optimized and [user-defined metrics](#user-defined-functions) with JIT compilation.
54
53
  - ✅ Hardware-agnostic `f16` & `i8` - [half-precision & quarter-precision support](#memory-efficiency-downcasting-and-quantization).
55
54
  - ✅ [View large indexes from disk](#serving-index-from-disk) without loading into RAM.
55
+ - ✅ Heterogeneous lookups, renaming/relabeling, and on-the-fly deletions.
56
+ - ✅ Variable dimensionality vectors for unique applications, including search over compressed data.
57
+ - ✅ Binary Tanimoto and Sorensen coefficients for [Genomics and Chemistry applications](#usearch--rdkit--molecular-search).
56
58
  - ✅ Space-efficient point-clouds with `uint40_t`, accommodating 4B+ size.
57
59
  - ✅ Compatible with OpenMP and custom "executors", for fine-grained control over CPU utilization.
58
- - ✅ Heterogeneous lookups, renaming/relabeling, and on-the-fly deletions.
59
- - ✅ Near-real-time [clustering and sub-clusterings](#clustering) for Tens or Millions of clusters.
60
+ - ✅ Near-real-time [clustering and sub-clustering](#clustering) for Tens or Millions of clusters.
60
61
  - ✅ [Semantic Search](#usearch--ai--multi-modal-semantic-search) and [Joins](#joins).
61
62
 
63
+ [faiss]: https://github.com/facebookresearch/faiss
62
64
  [usearch-header]: https://github.com/unum-cloud/usearch/blob/main/include/usearch/index.hpp
63
65
  [obscure-use-cases]: https://ashvardanian.com/posts/abusing-vector-search
66
+ [hnsw-algorithm]: https://arxiv.org/abs/1603.09320
67
+ [simd]: https://en.wikipedia.org/wiki/Single_instruction,_multiple_data
64
68
 
65
69
  ## Comparison with FAISS
66
70
 
67
71
  FAISS is a widely recognized standard for high-performance vector search engines.
68
72
  USearch and FAISS both employ the same HNSW algorithm, but they differ significantly in their design principles.
69
- USearch is compact and broadly compatible without sacrificing performance, with a primary focus on user-defined metrics and fewer dependencies.
70
-
71
- | | FAISS | USearch |
72
- | :------------------ | :---------------------------- | :--------------------------------- |
73
- | Implementation | 84 K [SLOC][sloc] in `faiss/` | 3 K [SLOC][sloc] in `usearch/` |
74
- | Supported metrics | 9 fixed metrics | Any User-Defined metrics |
75
- | Supported languages | C++, Python | 10 languages |
76
- | Supported ID types | `uint32_t`, `uint64_t` | `uint32_t`, `uint40_t`, `uint64_t` |
77
- | Dependencies | BLAS, OpenMP | None |
78
- | Bindings | SWIG | Native |
79
- | Acceleration | Learned Quantization | Downcasting |
73
+ USearch is compact and broadly compatible without sacrificing performance, primarily focusing on user-defined metrics and fewer dependencies.
74
+
75
+ | | FAISS | USearch | Improvement |
76
+ | :------------------------------------------- | ----------------------------: | -----------------------------: | -----------------------: |
77
+ | Indexing time | | | |
78
+ | 100 Million 96d `f32`, `f16`, `i8` vectors | 2.6 h, 2.6 h, 2.6 h | 0.3 h, 0.2 h, 0.2 h | __9.6x, 10.4x, 10.7x__ ⁰ |
79
+ | 100 Million 1536d `f32`, `f16`, `i8` vectors | 5.0 h, 4.1 h, 3.8 h | 2.1 h, 1.1 h, 0.8 h | __2.3x 3.6x, 4.4x__ ⁰ |
80
+ | | | | |
81
+ | Codebase length | 84 K [SLOC][sloc] in `faiss/` | 3 K [SLOC][sloc] in `usearch/` | maintainable ¹ |
82
+ | Supported metrics | 9 fixed metrics | any user-defined metrics | extendible ² |
83
+ | Supported languages | C++, Python | 10 languages | portable ³ |
84
+ | Supported ID types | 32-bit, 64-bit | 32-bit, 40-bit, 64-bit | efficient ⁴ |
85
+ | Required dependencies | BLAS, OpenMP | - | light-weight ⁵ |
86
+ | Bindings | SWIG | Native | low-latency ⁶ |
80
87
 
81
88
  [sloc]: https://en.wikipedia.org/wiki/Source_lines_of_code
82
89
 
90
+ > ⁰ [Tested][intel-benchmarks] on Intel Sapphire Rapids, with the simplest inner-product distance, equivalent recall, and memory consumption, while also providing far superior search speed.
91
+ > ¹ A shorter codebase makes the project easier to maintain and audit.
92
+ > ² User-defined metrics allow you to customize your search for various applications, from GIS to creating custom metrics for composite embeddings from multiple AI models or hybrid full-text and semantic search.
93
+ > ³ With USearch, you can reuse the same preconstructed index in various programming languages.
94
+ > ⁴ The 40-bit integer allows you to store 4B+ vectors without allocating 8 bytes for every neighbor reference in the proximity graph.
95
+ > ⁵ Lack of obligatory dependencies makes USearch much more portable.
96
+ > ⁶ Native bindings introduce lower call latencies than more straightforward approaches.
97
+
98
+ [intel-benchmarks]: https://www.unum.cloud/blog/2023-11-11-scaling-vector-search
99
+
83
100
  Base functionality is identical to FAISS, and the interface must be familiar if you have ever investigated Approximate Nearest Neighbors search:
84
101
 
85
102
  ```py
@@ -88,44 +105,30 @@ $ pip install usearch
88
105
  import numpy as np
89
106
  from usearch.index import Index
90
107
 
91
- index = Index(
92
- ndim=3, # Define the number of dimensions in input vectors
93
- metric='cos', # Choose 'l2sq', 'haversine' or other metric, default = 'ip'
94
- dtype='f32', # Quantize to 'f16' or 'i8' if needed, default = 'f32'
95
- connectivity=16, # Optional: How frequent should the connections in the graph be
96
- expansion_add=128, # Optional: Control the recall of indexing
97
- expansion_search=64, # Optional: Control the quality of search
98
- )
108
+ index = Index(ndim=3)
99
109
 
100
110
  vector = np.array([0.2, 0.6, 0.4])
101
111
  index.add(42, vector)
112
+
102
113
  matches = index.search(vector, 10)
103
114
 
104
- assert len(index) == 1
105
- assert len(matches) == 1
106
115
  assert matches[0].key == 42
107
116
  assert matches[0].distance <= 0.001
108
117
  assert np.allclose(index[42], vector)
109
118
  ```
110
119
 
111
- Comparing the performance of FAISS against USearch on 1 Million 96-dimensional vectors from the famous Deep1B dataset, once can expect the following numbers on modern AWS `c7g.metal` instances.
112
-
113
- | | FAISS, `f32` | USearch, `f32` | USearch, `f16` | USearch, `i8` |
114
- | :----------- | -----------: | -------------: | -------------: | ----------------: |
115
- | Batch Insert | 16 K/s | 73 K/s | 100 K/s | 104 K/s __+550%__ |
116
- | Batch Search | 82 K/s | 103 K/s | 113 K/s | 134 K/s __+63%__ |
117
- | Bulk Insert | 76 K/s | 105 K/s | 115 K/s | 202 K/s __+165%__ |
118
- | Bulk Search | 118 K/s | 174 K/s | 173 K/s | 304 K/s __+157%__ |
119
- | Recall @ 10 | 99% | 99.2% | 99.1% | 99.2% |
120
+ More settings are always available, and the API is designed to be as flexible as possible.
120
121
 
121
- > HNSW was configured with identical hyper-parameters:
122
- > connectivity `M=16`,
123
- > expansion @ construction `efConstruction=128`,
124
- > and expansion @ search `ef=64`.
125
- > Batch size is 256.
126
- > Jump to the [Performance Tuning][benchmarking] section to read about the effects of those hyper-parameters.
127
-
128
- [benchmarking]: https://github.com/unum-cloud/usearch/blob/main/docs/benchmarks.md
122
+ ```py
123
+ index = Index(
124
+ ndim=3, # Define the number of dimensions in input vectors
125
+ metric='cos', # Choose 'l2sq', 'haversine' or other metric, default = 'ip'
126
+ dtype='f32', # Quantize to 'f16' or 'i8' if needed, default = 'f32'
127
+ connectivity=16, # Optional: How frequent should the connections in the graph be
128
+ expansion_add=128, # Optional: Control the recall of indexing
129
+ expansion_search=64, # Optional: Control the quality of search
130
+ )
131
+ ```
129
132
 
130
133
  ## User-Defined Functions
131
134
 
@@ -220,7 +223,7 @@ multi_index.search(...)
220
223
  ## Clustering
221
224
 
222
225
  Once the index is constructed, it can be used to cluster entries much faster.
223
- In essense, the `Index` itself can be seen as a clustering, and it allows iterative deepening.
226
+ In essence, the `Index` itself can be seen as a clustering, and it allows iterative deepening.
224
227
 
225
228
  ```py
226
229
  clustering = index.cluster(
@@ -241,7 +244,7 @@ g = clustering.network
241
244
  # Get members of a specific cluster
242
245
  first_members = clustering.members_of(centroid_keys[0])
243
246
 
244
- # Deepen into that cluster spliting it into more parts, all same arguments supported
247
+ # Deepen into that cluster splitting it into more parts, all same arguments supported
245
248
  sub_clustering = clustering.subcluster(min_count=..., max_count=...)
246
249
  ```
247
250
 
@@ -342,7 +345,7 @@ Comparing molecule graphs and searching for similar structures is expensive and
342
345
  It can be seen as a special case of the NP-Complete Subgraph Isomorphism problem.
343
346
  Luckily, domain-specific approximate methods exist.
344
347
  The one commonly used in Chemistry, is to generate structures from [SMILES][smiles], and later hash them into binary fingerprints.
345
- The latter are searchable with bitwise similarity metrics, like the Tanimoto coefficient.
348
+ The latter are searchable with binary similarity metrics, like the Tanimoto coefficient.
346
349
  Below is an example using the RDKit package.
347
350
 
348
351
  ```python
@@ -380,9 +383,10 @@ The search is based on the Haversine distance metric, but can easily be extended
380
383
  ## Integrations
381
384
 
382
385
  - [x] GPTCache: [Python](https://github.com/zilliztech/GPTCache/releases/tag/0.1.29).
383
- - [x] LangChain: [Python](https://github.com/langchain-ai/langchain/releases/tag/v0.0.257) and [JavaScipt](https://github.com/hwchase17/langchainjs/releases/tag/0.0.125).
386
+ - [x] LangChain: [Python](https://github.com/langchain-ai/langchain/releases/tag/v0.0.257) and [JavaScript](https://github.com/hwchase17/langchainjs/releases/tag/0.0.125).
384
387
  - [x] ClickHouse: [C++](https://github.com/ClickHouse/ClickHouse/pull/53447).
385
388
  - [x] Microsoft Semantic Kernel: [Python](https://github.com/microsoft/semantic-kernel/releases/tag/python-0.3.9.dev) and C#.
389
+ - [x] LanternDB: [C++](https://github.com/lanterndata/lantern) and [Rust](https://github.com/lanterndata/lantern_extras).
386
390
 
387
391
  ## Citations
388
392
 
@@ -392,7 +396,7 @@ doi = {10.5281/zenodo.7949416},
392
396
  author = {Vardanian, Ash},
393
397
  title = {{USearch by Unum Cloud}},
394
398
  url = {https://github.com/unum-cloud/usearch},
395
- version = {2.8.3},
399
+ version = {2.8.4},
396
400
  year = {2023},
397
401
  month = oct,
398
402
  }
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.3'
10
+ version = '2.8.4'
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"
package/cpp/README.md CHANGED
@@ -25,14 +25,20 @@ metric_punned_t metric(256, metric_kind_t::l2sq_k, scalar_kind_t::f32_k);
25
25
  index_dense_t index = index_dense_t::make(metric);
26
26
  float vec[3] = {0.1, 0.3, 0.2};
27
27
 
28
- index.reserve(10);
29
- index.add(/* key: */ 42, /* vector: */ {&vec[0], 3});
30
- auto results = index.search(/* query: */ {&vec[0], 3}, 5 /* neighbors */);
28
+ index.reserve(10); // Pre-allocate memory for 10 vectors
29
+ index.add(42, &vec[0]); // Pass a key and a vector
30
+ auto results = index.search(&vec[0], 5); // Pass a query and limit number of results
31
31
 
32
32
  for (std::size_t i = 0; i != results.size(); ++i)
33
33
  results[i].element.key, results[i].element.vector, results[i].distance;
34
34
  ```
35
35
 
36
+ Here we:
37
+
38
+ - define a metric of kind [`metric_kind_t::l2sq_k`](https://unum-cloud.github.io/usearch/cpp/reference.html#_CPPv413metric_kind_t),
39
+ - to be applied to [`scalar_kind_t::f32_k`](https://unum-cloud.github.io/usearch/cpp/reference.html#_CPPv413scalar_kind_t) floating-point vectors,
40
+ - instantiate an [`index_dense_t`](https://unum-cloud.github.io/usearch/cpp/reference.html#_CPPv4I00EN4unum7usearch14index_dense_gtE) index.
41
+
36
42
  The `add` is thread-safe for concurrent index construction.
37
43
  It also has an overload for different vector types, casting them under the hood.
38
44
  The same applies to the `search`, `get`, `cluster`, and `distance_between` functions.
@@ -52,20 +58,78 @@ index.load("index.usearch"); // Copying from disk
52
58
  index.view("index.usearch"); // Memory-mapping from disk
53
59
  ```
54
60
 
55
- ## User-Defined Metrics in C++
61
+ ## Multi-Threading
56
62
 
57
- For advanced users, more compile-time abstractions are available.
63
+ Most AI, HPC, or Big Data packages use some form of a thread pool.
64
+ Instead of spawning additional threads within USearch, we focus on the thread safety of `add()` function, simplifying resource management.
58
65
 
59
66
  ```cpp
60
- template <typename distance_at = default_distance_t, // `float`
61
- typename key_at = default_key_t, // `int64_t`, `uuid_t`
62
- typename compressed_slot_at = default_slot_t, // `uint32_t`, `uint40_t`
63
- typename dynamic_allocator_at = std::allocator<byte_t>, //
64
- typename tape_allocator_at = dynamic_allocator_at> //
65
- class index_gt;
67
+ #pragma omp parallel for
68
+ for (std::size_t i = 0; i < n; ++i)
69
+ native.add(key, span_t{vector, dims});
70
+ ```
71
+
72
+ During initialization, we allocate enough temporary memory for all the cores on the machine.
73
+ On the call, the user can supply the identifier of the current thread, making this library easy to integrate with OpenMP and similar tools.
74
+
75
+ Moreover, you can take advantage of one of the provided "executors" to parallelize the search:
76
+
77
+ - `executor_openmp_t`, that would use OpenMP under the hood.
78
+ - `executor_stl_t`, that will spawn `std::thread` instances.
79
+ - `dummy_executor_t`, that will run everything sequentially.
80
+
81
+ ## Clustering
82
+
83
+ Aside from basic Create-Read-Update-Delete (CRUD) operations and search, USearch also supports clustering.
84
+ Once the index is constructed, you can either:
85
+
86
+ - Identify a cluster to which any external vector belongs, once mapped onto the index.
87
+ - Split the entire index into a set of clusters, each with its own centroid.
88
+
89
+ For the first, the interface accepts a vector and a "clustering level", which is essentially the index of the HNSW graph layer, in which to search.
90
+ If you pass zero, the traversal will happen in every level except the bottom one.
91
+ Otherwise, the search will be limited to the specified level.
92
+
93
+ ```cpp
94
+ some_scalar_t vector[3] = {0.1, 0.3, 0.2};
95
+ cluster_result_t result = index.cluster(&vector, index.max_level() / 2);
96
+ match_t cluster = result.cluster;
97
+ member_cref_t member = cluster.member;
98
+ distance_t distance = cluster.distance;
66
99
  ```
67
100
 
68
- The following distances are pre-packaged:
101
+ If you wish to split the whole structure into clusters, you must provide an iterator over a range of vectors, that will be processed in parallel using the previously described function.
102
+ Unlike the previous function, you don't have to manually specify the level, as the algorithm will pick the best one for you, depending on the number of clusters you want to highlight.
103
+ Aside from that auto-tuning, this function will regroup some of the clusters, if they are too small, and return the final number of clusters.
104
+
105
+ ```cpp
106
+ std::size_t queries_count = queries_end - queries_begin;
107
+ index_dense_clustering_config_t config;
108
+ config.min_clusters = 1000;
109
+ config.max_clusters = 2000;
110
+ config.mode = index_dense_clustering_config_t::merge_smallest_k;
111
+
112
+ // Outputs:
113
+ vector_key_t cluster_centroids_keys[queries_count];
114
+ distance_t distances_to_cluster_centroids[queries_count];
115
+ executor_default_t thread_pool;
116
+ dummy_progress_t progress_bar;
117
+
118
+ clustering_result_t result = cluster(
119
+ queries_begin, queries_end,
120
+ config,
121
+ &cluster_centroids_keys, &distances_to_cluster_centroids,
122
+ thread_pool, progress_bar);
123
+ ```
124
+
125
+ This approach requires basic understanding of templates meta-programming to implement the `queries_begin` and `queries_end` smart-iterators.
126
+ On the bright side, it allows iteratively deepening into a specific cluster.
127
+
128
+ As in many other bulk-processing APIs, the `executor` and `progress` are optional.
129
+
130
+ ## User-Defined Metrics
131
+
132
+ In its high-level interface, USearch supports a variety of metrics, including the most popular ones:
69
133
 
70
134
  - `metric_cos_gt<scalar_t>` for "Cosine" or "Angular" distance.
71
135
  - `metric_ip_gt<scalar_t>` for "Inner Product" or "Dot Product" distance.
@@ -78,16 +142,20 @@ The following distances are pre-packaged:
78
142
  - `metric_haversine_gt<scalar_t>` for "Haversine" or "Great Circle" distance between coordinates used in GIS applications.
79
143
  - `metric_divergence_gt<scalar_t>` for the "Jensen Shannon" similarity between probability distributions.
80
144
 
81
- ## Multi-Threading
145
+ In reality, for most common types, one of the [SimSIMD](https://github.com/ashvardanian/SimSIMD) backends will be triggered, providing hardware-acceleration for most common CPUs.
82
146
 
83
- Most AI, HPC, or Big Data packages use some form of a thread pool.
84
- Instead of spawning additional threads within USearch, we focus on the thread safety of `add()` function, simplifying resource management.
147
+ If you need a different metric, you can implement it yourself and wrap it into a `metric_punned_t`, which is our alternative to the `std::function`.
148
+ Unlike the `std::function`, it is a trivial type, which is important for performance.
149
+
150
+ ## Advanced Interface
151
+
152
+ If you are proficient in C++ and ready to get your hands dirty, you can use the low-level interface.
85
153
 
86
154
  ```cpp
87
- #pragma omp parallel for
88
- for (std::size_t i = 0; i < n; ++i)
89
- native.add(key, span_t{vector, dims});
155
+ template <typename distance_at = default_distance_t, // `float`
156
+ typename key_at = default_key_t, // `int64_t`, `uuid_t`
157
+ typename compressed_slot_at = default_slot_t, // `uint32_t`, `uint40_t`
158
+ typename dynamic_allocator_at = std::allocator<byte_t>, //
159
+ typename tape_allocator_at = dynamic_allocator_at> //
160
+ class index_gt;
90
161
  ```
91
-
92
- During initialization, we allocate enough temporary memory for all the cores on the machine.
93
- On the call, the user can supply the identifier of the current thread, making this library easy to integrate with OpenMP and similar tools.