usearch 0.2.0 → 0.2.3
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/release.yml +11 -0
- package/.vscode/launch.json +5 -4
- package/.vscode/settings.json +2 -1
- package/CMakeLists.txt +11 -6
- package/CMakeListsInstructions.md +39 -17
- package/Cargo.toml +1 -1
- package/Package.swift +40 -0
- package/README.md +81 -35
- package/VERSION +1 -1
- package/build.gradle +5 -1
- package/cmake/jemalloc.cmake +0 -2
- package/conanfile.py +19 -0
- package/include/usearch/usearch.hpp +335 -227
- package/javascript/lib.cpp +8 -2
- package/objc/USearchObjective.mm +150 -0
- package/objc/include/USearchObjective.h +69 -0
- package/package.json +1 -1
- package/pyproject.toml +18 -4
- package/python/bench.py +67 -25
- package/python/lib.cpp +34 -11
- package/python/test.py +4 -5
- package/python/usearch/__init__.py +0 -0
- package/rust/lib.cpp +13 -2
- package/setup.py +17 -4
- package/simsimd/include/simsimd/simsimd.h +15 -0
- package/src/advanced.hpp +230 -119
- package/src/bench.cpp +237 -82
- package/swift/Index+Sugar.swift +70 -0
- package/swift/Test.swift +20 -0
- package/swift/USearch.swift +8 -0
- package/java/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/java/gradle/wrapper/gradle-wrapper.properties +0 -6
- package/java/gradlew +0 -244
|
@@ -130,6 +130,17 @@ jobs:
|
|
|
130
130
|
env:
|
|
131
131
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
132
132
|
|
|
133
|
+
publish_swift:
|
|
134
|
+
name: Publish ObjC & Swift
|
|
135
|
+
runs-on: macos-latest
|
|
136
|
+
steps:
|
|
137
|
+
- uses: actions/checkout@v3
|
|
138
|
+
- run: git submodule update --init --recursive
|
|
139
|
+
- name: Build
|
|
140
|
+
run: swift build
|
|
141
|
+
- name: Run tests
|
|
142
|
+
run: swift test
|
|
143
|
+
|
|
133
144
|
deploy_docs:
|
|
134
145
|
name: Deploy Docs
|
|
135
146
|
environment:
|
package/.vscode/launch.json
CHANGED
|
@@ -36,18 +36,19 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
|
-
"name": "Debug C++ Bench on Text-to-Image
|
|
39
|
+
"name": "Debug C++ Bench on Text-to-Image 1B @Cos",
|
|
40
40
|
"type": "cppdbg",
|
|
41
41
|
"request": "launch",
|
|
42
42
|
"program": "${workspaceFolder}/build_debug/bench",
|
|
43
43
|
"cwd": "${workspaceFolder}",
|
|
44
44
|
"args": [
|
|
45
45
|
"--vectors",
|
|
46
|
-
"datasets/
|
|
46
|
+
"datasets/t2i_1B/base.1B.fbin",
|
|
47
47
|
"--queries",
|
|
48
|
-
"datasets/
|
|
48
|
+
"datasets/t2i_1B/query.public.100K.fbin",
|
|
49
49
|
"--neighbors",
|
|
50
|
-
"datasets/
|
|
50
|
+
"datasets/t2i_1B/groundtruth.public.100K.ibin",
|
|
51
|
+
"--cos"
|
|
51
52
|
],
|
|
52
53
|
"environment": [
|
|
53
54
|
{
|
package/.vscode/settings.json
CHANGED
package/CMakeLists.txt
CHANGED
|
@@ -17,22 +17,18 @@ if(NOT CMAKE_BUILD_TYPE)
|
|
|
17
17
|
set(CMAKE_BUILD_TYPE Release)
|
|
18
18
|
endif()
|
|
19
19
|
|
|
20
|
-
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0")
|
|
21
|
-
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
|
22
|
-
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O3 -pg -g")
|
|
23
|
-
|
|
24
20
|
# List of all possible compiler IDs:
|
|
25
21
|
# https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html
|
|
26
22
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
27
23
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address")
|
|
28
|
-
set(
|
|
24
|
+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -Wno-conversion -Wno-unknown-pragmas")
|
|
29
25
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
|
30
26
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmax-errors=1")
|
|
31
27
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
|
|
32
28
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
|
|
33
29
|
|
|
34
30
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
35
|
-
set(
|
|
31
|
+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wfatal-errors")
|
|
36
32
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
|
|
37
33
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
|
|
38
34
|
|
|
@@ -46,6 +42,15 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "NVIDIA" OR CMAKE_CXX_COMPILER_ID STREQUAL
|
|
|
46
42
|
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
|
|
47
43
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w")
|
|
48
44
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ferror-limit=1")
|
|
45
|
+
|
|
46
|
+
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
|
47
|
+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /DEBUG")
|
|
48
|
+
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2")
|
|
49
|
+
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /O2 /DEBUG")
|
|
50
|
+
else()
|
|
51
|
+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0")
|
|
52
|
+
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
|
53
|
+
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O3 -pg -g")
|
|
49
54
|
endif()
|
|
50
55
|
|
|
51
56
|
if(APPLE)
|
|
@@ -12,6 +12,7 @@ git submodule update --init --recursive
|
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
14
|
cmake -B ./build_release \
|
|
15
|
+
-DCMAKE_CXX_COMPILER="g++-12" \
|
|
15
16
|
-DCMAKE_BUILD_TYPE=Release \
|
|
16
17
|
-DUSEARCH_USE_SIMD=1 \
|
|
17
18
|
-DUSEARCH_USE_OPENMP=1 \
|
|
@@ -24,6 +25,14 @@ cmake -B ./build_release \
|
|
|
24
25
|
--queries datasets/wiki_1M/query.public.100K.fbin \
|
|
25
26
|
--neighbors datasets/wiki_1M/groundtruth.public.100K.ibin
|
|
26
27
|
|
|
28
|
+
# Large benchmark on a 1TB dataset:
|
|
29
|
+
./build_release/bench \
|
|
30
|
+
--vectors datasets/t2i_1B/base.1B.fbin \
|
|
31
|
+
--queries datasets/t2i_1B/query.public.100K.fbin \
|
|
32
|
+
--neighbors datasets/t2i_1B/groundtruth.public.100K.ibin \
|
|
33
|
+
--output datasets/t2i_1B/index.usearch \
|
|
34
|
+
--cos
|
|
35
|
+
|
|
27
36
|
# To benchmark in Python:
|
|
28
37
|
python python/bench.py \
|
|
29
38
|
--vectors datasets/wiki_1M/base.1M.fbin \
|
|
@@ -110,44 +119,57 @@ brew install --cask wolfram-engine
|
|
|
110
119
|
BigANN benchmark is a good starting point, if you are searching for large collections of high-dimensional vectors.
|
|
111
120
|
Those often come with precomputed ground-truth neighbors, which is handy for recall evaluation.
|
|
112
121
|
|
|
113
|
-
| Dataset
|
|
114
|
-
|
|
|
115
|
-
| [
|
|
116
|
-
| [
|
|
117
|
-
|
|
|
118
|
-
| [
|
|
122
|
+
| Dataset | Scalar Type | Dimensions | Metric | Size |
|
|
123
|
+
| :-------------------------------------- | :---------: | :--------: | :----: | :-------: |
|
|
124
|
+
| [Unum UForm Wiki][unum-wiki] | float32 | 256 | IP | 1 GB |
|
|
125
|
+
| [Yandex Text-to-Image Sample][unum-t2i] | float32 | 200 | Cos | 1 GB |
|
|
126
|
+
| | | | | |
|
|
127
|
+
| [Microsoft SPACEV][spacev] | int8 | 100 | L2 | 93 GB |
|
|
128
|
+
| [Microsoft Turing-ANNS][turing] | float32 | 100 | L2 | 373 GB |
|
|
129
|
+
| [Yandex Deep1B][deep] | float32 | 96 | L2 | 358 GB |
|
|
130
|
+
| [Yandex Text-to-Image][t2i] | float32 | 200 | Cos | 750 GB |
|
|
131
|
+
| | | | | |
|
|
132
|
+
| [ViT-L/12 LAION][laion] | float32 | 2048 | Cos | 2 - 10 TB |
|
|
119
133
|
|
|
120
134
|
Luckily, smaller samples of those datasets are available.
|
|
121
135
|
|
|
136
|
+
[unum-wiki]: https://huggingface.co/datasets/unum-cloud/ann-wiki-1m
|
|
137
|
+
[unum-t2i]: https://huggingface.co/datasets/unum-cloud/ann-t2i-1m
|
|
122
138
|
[spacev]: https://github.com/microsoft/SPTAG/tree/main/datasets/SPACEV1B
|
|
123
139
|
[turing]: https://learning2hash.github.io/publications/microsoftturinganns1B/
|
|
124
140
|
[t2i]: https://research.yandex.com/blog/benchmarks-for-billion-scale-similarity-search
|
|
125
141
|
[deep]: https://research.yandex.com/blog/benchmarks-for-billion-scale-similarity-search
|
|
142
|
+
[laion]: https://laion.ai/blog/laion-5b/#download-the-data
|
|
126
143
|
|
|
127
|
-
###
|
|
128
|
-
|
|
129
|
-
There is a 10M subset available.
|
|
144
|
+
### Unum UForm Wiki
|
|
130
145
|
|
|
131
146
|
```sh
|
|
132
|
-
mkdir -p datasets/
|
|
133
|
-
wget -nc https://
|
|
134
|
-
wget -nc https://
|
|
135
|
-
wget -nc https://
|
|
136
|
-
wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/DEEP/groundtruth.public.10K.ibin -P datasets/deep_1B/
|
|
147
|
+
mkdir -p datasets/wiki_1M/ && \
|
|
148
|
+
wget -nc https://huggingface.co/datasets/unum-cloud/ann-wiki-1m/resolve/main/base.1M.fbin -P datasets/wiki_1M/ &&
|
|
149
|
+
wget -nc https://huggingface.co/datasets/unum-cloud/ann-wiki-1m/resolve/main/query.public.100K.fbin -P datasets/wiki_1M/ &&
|
|
150
|
+
wget -nc https://huggingface.co/datasets/unum-cloud/ann-wiki-1m/resolve/main/groundtruth.public.100K.ibin -P datasets/wiki_1M/
|
|
137
151
|
```
|
|
138
152
|
|
|
139
153
|
### Yandex Text-to-Image
|
|
140
154
|
|
|
141
|
-
There is a 1M subset available.
|
|
142
|
-
|
|
143
155
|
```sh
|
|
144
|
-
mkdir -p datasets/
|
|
156
|
+
mkdir -p datasets/t2i_1B/ && \
|
|
145
157
|
wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/T2I/base.1B.fbin -P datasets/t2i_1B/ &&
|
|
146
158
|
wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/T2I/base.1M.fbin -P datasets/t2i_1B/ &&
|
|
147
159
|
wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/T2I/query.public.100K.fbin -P datasets/t2i_1B/ &&
|
|
148
160
|
wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/T2I/groundtruth.public.100K.ibin -P datasets/t2i_1B/
|
|
149
161
|
```
|
|
150
162
|
|
|
163
|
+
### Yandex Deep1B
|
|
164
|
+
|
|
165
|
+
```sh
|
|
166
|
+
mkdir -p datasets/deep_1B/ && \
|
|
167
|
+
wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/DEEP/base.1B.fbin -P datasets/deep_1B/ &&
|
|
168
|
+
wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/DEEP/base.10M.fbin -P datasets/deep_1B/ &&
|
|
169
|
+
wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/DEEP/query.public.10K.fbin -P datasets/deep_1B/ &&
|
|
170
|
+
wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/DEEP/groundtruth.public.10K.ibin -P datasets/deep_1B/
|
|
171
|
+
```
|
|
172
|
+
|
|
151
173
|
### Profiling
|
|
152
174
|
|
|
153
175
|
With `perf`:
|
package/Cargo.toml
CHANGED
package/Package.swift
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// swift-tools-version:5.3
|
|
2
|
+
|
|
3
|
+
import PackageDescription
|
|
4
|
+
|
|
5
|
+
let package = Package(
|
|
6
|
+
name: "USearch",
|
|
7
|
+
products: [
|
|
8
|
+
.library(
|
|
9
|
+
name: "USearch",
|
|
10
|
+
targets: ["USearchObjective", "USearch"]
|
|
11
|
+
)
|
|
12
|
+
],
|
|
13
|
+
dependencies: [],
|
|
14
|
+
targets: [
|
|
15
|
+
.target(
|
|
16
|
+
name: "USearchObjective",
|
|
17
|
+
path: "objc",
|
|
18
|
+
sources: ["USearchObjective.mm"],
|
|
19
|
+
cxxSettings: [
|
|
20
|
+
.headerSearchPath("../include/"),
|
|
21
|
+
.headerSearchPath("../src/"),
|
|
22
|
+
.headerSearchPath("../fp16/include/"),
|
|
23
|
+
.headerSearchPath("../simismd/include/")
|
|
24
|
+
]
|
|
25
|
+
),
|
|
26
|
+
.target(
|
|
27
|
+
name: "USearch",
|
|
28
|
+
dependencies: ["USearchObjective"],
|
|
29
|
+
path: "swift",
|
|
30
|
+
sources: ["USearch.swift", "Index+Sugar.swift"]
|
|
31
|
+
),
|
|
32
|
+
.testTarget(
|
|
33
|
+
name: "USearchTests",
|
|
34
|
+
dependencies: ["USearch"],
|
|
35
|
+
path: "swift",
|
|
36
|
+
sources: ["Test.swift"]
|
|
37
|
+
)
|
|
38
|
+
],
|
|
39
|
+
cxxLanguageStandard: CXXLanguageStandard.cxx11
|
|
40
|
+
)
|
package/README.md
CHANGED
|
@@ -17,30 +17,33 @@ Vector Search Engine<br/>
|
|
|
17
17
|
<a href="https://github.com/unum-cloud/usearch"><img height="25" src="https://github.com/unum-cloud/.github/raw/main/assets/github.svg" alt="GitHub"></a>
|
|
18
18
|
</p>
|
|
19
19
|
|
|
20
|
+
<p align="center">
|
|
21
|
+
Euclidean • Angular • Jaccard • Hamming • Haversine • User-Defined Metrics
|
|
22
|
+
<br/>
|
|
23
|
+
<a href="#c++">C++11</a> •
|
|
24
|
+
<a href="#python">Python</a> •
|
|
25
|
+
<a href="#javascript">JavaScript</a> •
|
|
26
|
+
<a href="#java">Java</a> •
|
|
27
|
+
<a href="#rust">Rust</a> •
|
|
28
|
+
<a href="#objective-c">Objective-C</a> •
|
|
29
|
+
<a href="#swift">Swift</a> •
|
|
30
|
+
<a href="#golang">GoLang</a> •
|
|
31
|
+
<a href="#wolfram">Wolfram</a>
|
|
32
|
+
<br/>
|
|
33
|
+
Linux • MacOS • Windows
|
|
34
|
+
</p>
|
|
35
|
+
|
|
20
36
|
- [x] [Single C++11 header][usearch-header] implementation, easily extendible.
|
|
21
37
|
- [x] [4B+](#go-beyond-4b-entries) sized space efficient point-clouds with `uint40_t`.
|
|
22
|
-
- [x] [Half-precision](#quantize-on-the-fly) support with [`
|
|
38
|
+
- [x] [Half-precision](#quantize-on-the-fly) support with [`fp16`](https://github.com/maratyszcza/fp16).
|
|
23
39
|
- [x] [View from disk](#view-larger-indexes-from-disk), without loading into RAM.
|
|
24
|
-
- [x] [
|
|
25
|
-
- [x] Euclidean, Dot-product, Cosine,
|
|
26
|
-
- [x] Jaccard, Hamming, Haversine.
|
|
27
|
-
- [x] Hardware-accelerated [`ashvardanian/simsimd`](https://github.com/ashvardanian/simsimd).
|
|
40
|
+
- [x] [User-defined](#define-custom-metrics) and hardware-accelerated metrics with [`simsimd`](https://github.com/ashvardanian/simsimd).
|
|
28
41
|
- [x] Variable dimensionality vectors.
|
|
29
42
|
- [x] Don't copy vectors if not needed.
|
|
30
43
|
- [x] [Bring your threads](#bring-your-threads), like OpenMP.
|
|
31
44
|
- [x] Multiple vectors per label.
|
|
32
|
-
- [x] [Python](#python) bindings: `pip install usearch`.
|
|
33
|
-
- [x] [JavaScript](#java) bindings: `npm install usearch`.
|
|
34
|
-
- [x] [Rust](#rust) bindings: `cargo add usearch`.
|
|
35
|
-
- [x] [Java](#java) bindings: `cloud.unum:usearch` on GitHub.
|
|
36
|
-
- [ ] GoLang bindings.
|
|
37
|
-
- [x] Wolfram language bindings.
|
|
38
|
-
- [x] For Linux: GCC, Clang.
|
|
39
|
-
- [x] For MacOS: Apple Clang.
|
|
40
|
-
- [ ] For Windows.
|
|
41
|
-
- [ ] Multi-index lookups in Python.
|
|
42
45
|
- [ ] Thread-safe `reserve`.
|
|
43
|
-
- [
|
|
46
|
+
- [x] Compatible with Linux, MacOS, and Windows.
|
|
44
47
|
- [x] AI + Vector Search = Semantic Search.
|
|
45
48
|
|
|
46
49
|
[usearch-header]: https://github.com/unum-cloud/usearch/blob/main/include/usearch/usearch.hpp
|
|
@@ -108,7 +111,7 @@ So, if you are working on large datasets, but don't need the in-RAM throughput,
|
|
|
108
111
|
### Quantize on the Fly
|
|
109
112
|
|
|
110
113
|
Most modern CPU have at least partial support for `half`-precision `f16_t` arithmetic.
|
|
111
|
-
USearch supports automatic down-casting and up-casting between `f32_t`, `f16_t`, `f64_t`, and `
|
|
114
|
+
USearch supports automatic down-casting and up-casting between `f32_t`, `f16_t`, `f64_t`, and `f8_t` representations.
|
|
112
115
|
|
|
113
116
|
> Making vectors smaller will help pack more of them in-memory, while also increasing performance on CPUs implementing native support for target type.
|
|
114
117
|
|
|
@@ -160,7 +163,7 @@ That can be one of the following templates:
|
|
|
160
163
|
|
|
161
164
|
- `cos_gt<float>` for "Cosine" or "Angular" distance.
|
|
162
165
|
- `ip_gt<float>` for "Inner Product" or "Dot Product" distance.
|
|
163
|
-
- `
|
|
166
|
+
- `l2sq_gt<float>` for the squared "L2" or "Euclidean" distance.
|
|
164
167
|
- `jaccard_gt<int>` for "Jaccard" distance between two ordered sets of unique elements.
|
|
165
168
|
- `bit_hamming_gt<uint>` for "Hamming" distance, as the number of shared bits in hashes.
|
|
166
169
|
- `pearson_correlation_gt<float>` for "Pearson" correlation between probability distributions.
|
|
@@ -209,19 +212,21 @@ struct custom_metric_t {
|
|
|
209
212
|
|
|
210
213
|
### Python
|
|
211
214
|
|
|
215
|
+
```sh
|
|
216
|
+
pip install usearch
|
|
217
|
+
```
|
|
218
|
+
|
|
212
219
|
Python bindings are implemented with [`pybind/pybind11`](https://github.com/pybind/pybind11).
|
|
213
220
|
Assuming the presence of Global Interpreter Lock in Python, on large insertions we spawn threads in the C++ layer.
|
|
214
221
|
|
|
215
222
|
```python
|
|
216
|
-
$ pip install usearch
|
|
217
|
-
|
|
218
223
|
import numpy as np
|
|
219
|
-
import
|
|
224
|
+
from usearch.index import Index
|
|
220
225
|
|
|
221
|
-
index =
|
|
226
|
+
index = Index(
|
|
222
227
|
ndim=256, # Define the number of dimensions in input vectors
|
|
223
228
|
metric='cos', # Choose the "metric" or "distance", default = 'ip', optional
|
|
224
|
-
dtype='f32', # Quantize to 'f16' or '
|
|
229
|
+
dtype='f32', # Quantize to 'f16' or 'f8' if needed, default = 'f32', optional
|
|
225
230
|
connectivity=16, # How frequent should the connections in the graph be, optional
|
|
226
231
|
expansion_add=128, # Control the recall of indexing, optional
|
|
227
232
|
expansion_search=64, # Control the quality of search, optional
|
|
@@ -232,6 +237,8 @@ index.add(42, vector)
|
|
|
232
237
|
matches, distances, count = index.search(vector, 10)
|
|
233
238
|
```
|
|
234
239
|
|
|
240
|
+
#### Batch Operations
|
|
241
|
+
|
|
235
242
|
Same can be done with batches, rather than single entries.
|
|
236
243
|
Under the hood, worker threads will be spawned, to parallelize the procedure.
|
|
237
244
|
|
|
@@ -240,17 +247,31 @@ n = 100
|
|
|
240
247
|
labels = np.array(range(n), dtype=np.longlong)
|
|
241
248
|
vectors = np.random.uniform(0, 0.3, (n, index.ndim)).astype(np.float32)
|
|
242
249
|
|
|
243
|
-
|
|
244
|
-
# Handy when build 1B+ indexes of memory-mapped files
|
|
245
|
-
index.add(labels, vectors, copy=True)
|
|
250
|
+
index.add(labels, vectors)
|
|
246
251
|
matches, distances, counts = index.search(vectors, 10)
|
|
247
252
|
```
|
|
248
253
|
|
|
254
|
+
#### Performance
|
|
255
|
+
|
|
256
|
+
To maximize performance, you can:
|
|
257
|
+
|
|
258
|
+
- adjust the number of threads passed to `index.add(threads=)`.
|
|
259
|
+
- downcast to half-precision with `Index(accuracy=)`.
|
|
260
|
+
- JIT-compile your metrics for hardware and pass to `Index(matric_pointer=)`.
|
|
261
|
+
|
|
262
|
+
For a dataset with 1 Million 256-dimensional embeddings at single precision, the performance would be:
|
|
263
|
+
|
|
264
|
+
- FAISS: 77'563 vectors/s.
|
|
265
|
+
- USearch precompiled for old hardware: 53'198 vectors/s (-32%).
|
|
266
|
+
- USearch with Numba JIT for target hardware: 88'658 vectors/s (+14%).
|
|
267
|
+
|
|
249
268
|
### JavaScript
|
|
250
269
|
|
|
251
|
-
```
|
|
252
|
-
|
|
270
|
+
```sh
|
|
271
|
+
npm install usearch
|
|
272
|
+
```
|
|
253
273
|
|
|
274
|
+
```js
|
|
254
275
|
var index = new usearch.Index({ metric: 'cos', connectivity: 16, dimensions: 2 })
|
|
255
276
|
assert.equal(index.connectivity(), 16)
|
|
256
277
|
assert.equal(index.dimensions(), 2)
|
|
@@ -266,14 +287,16 @@ assert.deepEqual(results.distances, new Float32Array([45, 130]))
|
|
|
266
287
|
|
|
267
288
|
### Rust
|
|
268
289
|
|
|
290
|
+
```sh
|
|
291
|
+
cargo add usearch
|
|
292
|
+
```
|
|
293
|
+
|
|
269
294
|
Being a systems-programming language, Rust has better control over memory management and concurrency, but lacks function overloading.
|
|
270
295
|
Aside from the `add` and `search`, it also provides `add_in_thread` and `search_in_thread` which let users identify the calling thread to use underlying temporary memory more efficiently.
|
|
271
296
|
|
|
272
297
|
```rust
|
|
273
|
-
// cargo add usearch
|
|
274
|
-
|
|
275
298
|
let quant: &str = "f16";
|
|
276
|
-
let index = new_ip(5,
|
|
299
|
+
let index = new_ip(5, &quant, 0, 0, 0).unwrap();
|
|
277
300
|
|
|
278
301
|
assert!(index.reserve(10).is_ok());
|
|
279
302
|
assert!(index.capacity() >= 10);
|
|
@@ -298,13 +321,23 @@ assert!(index.load("index.usearch").is_ok());
|
|
|
298
321
|
assert!(index.view("index.usearch").is_ok());
|
|
299
322
|
|
|
300
323
|
// There are more "metrics" available
|
|
301
|
-
assert!(new_l2(5,
|
|
302
|
-
assert!(new_cos(5,
|
|
324
|
+
assert!(new_l2(5, &quant, 0, 0, 0).is_ok());
|
|
325
|
+
assert!(new_cos(5, &quant, 0, 0, 0).is_ok());
|
|
303
326
|
assert!(new_haversine(&quant, 0, 0, 0).is_ok());
|
|
304
327
|
```
|
|
305
328
|
|
|
306
329
|
### Java
|
|
307
330
|
|
|
331
|
+
```xml
|
|
332
|
+
<dependency>
|
|
333
|
+
<groupId>cloud.unum.usearch</groupId>
|
|
334
|
+
<artifactId>usearch</artifactId>
|
|
335
|
+
<version>0.2.3</version>
|
|
336
|
+
</dependency>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Add that snippet to your `pom.xml` and hit `mvn install`.
|
|
340
|
+
|
|
308
341
|
```java
|
|
309
342
|
Index index = new Index.Config().metric("cos").dimensions(2).build();
|
|
310
343
|
float vec[] = {10, 20};
|
|
@@ -312,6 +345,19 @@ index.add(42, vec);
|
|
|
312
345
|
int[] labels = index.search(vec, 5);
|
|
313
346
|
```
|
|
314
347
|
|
|
348
|
+
### Swift
|
|
349
|
+
|
|
350
|
+
```swift
|
|
351
|
+
let index = Index.l2(dimensions: 4, connectivity: 8)
|
|
352
|
+
let vectorA: [Float32] = [0.3, 0.5, 1.2, 1.4]
|
|
353
|
+
let vectorB: [Float32] = [0.4, 0.2, 1.2, 1.1]
|
|
354
|
+
index.add(label: 42, vector: vectorA[...])
|
|
355
|
+
index.add(label: 43, vector: vectorB[...])
|
|
356
|
+
|
|
357
|
+
let results = index.search(vector: vectorA[...], count: 10)
|
|
358
|
+
assert(results.0[0] == 42)
|
|
359
|
+
```
|
|
360
|
+
|
|
315
361
|
### GoLang
|
|
316
362
|
|
|
317
363
|
### Wolfram
|
|
@@ -400,14 +446,14 @@ One can take an encoder model, like the multi-modal UForm, and a web-programming
|
|
|
400
446
|
```python
|
|
401
447
|
import ucall.rich_posix as ucall
|
|
402
448
|
import uform
|
|
403
|
-
import
|
|
449
|
+
from usearch.index import Index
|
|
404
450
|
|
|
405
451
|
import numpy as np
|
|
406
452
|
from PIL import Image
|
|
407
453
|
|
|
408
454
|
server = ucall.Server()
|
|
409
455
|
model = uform.get_model('unum-cloud/uform-vl-multilingual')
|
|
410
|
-
index =
|
|
456
|
+
index = Index(ndim=256)
|
|
411
457
|
|
|
412
458
|
@server
|
|
413
459
|
def add(label: int, photo: Image.Image):
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.2.
|
|
1
|
+
0.2.3
|
package/build.gradle
CHANGED
|
@@ -56,11 +56,15 @@ model {
|
|
|
56
56
|
if (targetPlatform.operatingSystem.linux) {
|
|
57
57
|
cppCompiler.args '-I', "${Jvm.current().javaHome}/include/linux"
|
|
58
58
|
cppCompiler.args '-D_FILE_OFFSET_BITS=64'
|
|
59
|
+
cppCompiler.args '-std=c++11'
|
|
59
60
|
} else if (targetPlatform.operatingSystem.macOsX) {
|
|
60
61
|
cppCompiler.args '-I', "${Jvm.current().javaHome}/include/darwin"
|
|
61
62
|
cppCompiler.args '-D_FILE_OFFSET_BITS=64'
|
|
63
|
+
cppCompiler.args '-std=c++11'
|
|
64
|
+
} else if (targetPlatform.operatingSystem.windows){
|
|
65
|
+
cppCompiler.args "-I${Jvm.current().javaHome}/include/win32"
|
|
66
|
+
cppCompiler.args '/std:c++11'
|
|
62
67
|
}
|
|
63
|
-
cppCompiler.args '-std=c++11'
|
|
64
68
|
}
|
|
65
69
|
}
|
|
66
70
|
}
|
package/cmake/jemalloc.cmake
CHANGED
package/conanfile.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from conans import ConanFile
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class USearchConan(ConanFile):
|
|
5
|
+
|
|
6
|
+
name = 'USearch'
|
|
7
|
+
version = '0.2.3'
|
|
8
|
+
license = 'Apache 2.0'
|
|
9
|
+
url = 'https://github.com/unum-cloud/usearch'
|
|
10
|
+
description = 'Smaller & Faster Single-File Vector Search Engine from Unum'
|
|
11
|
+
|
|
12
|
+
# No settings/options are necessary, this is header only
|
|
13
|
+
# Potentially add unit-tests in the future:
|
|
14
|
+
# https://docs.conan.io/1/howtos/header_only.html#with-unit-tests
|
|
15
|
+
exports_sources = 'include/*'
|
|
16
|
+
no_copy_source = True
|
|
17
|
+
|
|
18
|
+
def package(self):
|
|
19
|
+
self.copy('*.h')
|