usearch 2.8.7 → 2.8.9
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/CMakeLists.txt +3 -3
- package/CONTRIBUTING.md +26 -3
- package/Cargo.toml +1 -1
- package/Dockerfile +16 -0
- package/README.md +41 -38
- package/VERSION +1 -0
- package/binding.gyp +57 -8
- package/conanfile.py +1 -1
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_plugins.hpp +28 -24
- package/javascript/README.md +64 -12
- package/javascript/lib.cpp +11 -3
- package/javascript/usearch.js +4 -2
- package/javascript/usearch.test.js +15 -0
- package/package.json +1 -2
- package/setup.py +76 -15
- package/simsimd/README.md +14 -13
- package/simsimd/include/simsimd/probability.h +2 -2
- package/simsimd/package.json +1 -1
- package/wasmer.toml +1 -1
- package/. npmignore +0 -14
- package/.gitignore +0 -95
- package/.vscode/launch.json +0 -181
- package/.vscode/settings.json +0 -180
- package/.vscode/tasks.json +0 -71
- package/Package.swift +0 -41
- package/csharp/.editorconfig +0 -361
- package/csharp/Cloud.Unum.USearch.sln +0 -34
- package/csharp/DEV_SETUP.md +0 -51
- package/csharp/Directory.Build.props +0 -29
- package/csharp/README.md +0 -74
- package/csharp/build_and_test.cmd +0 -31
- package/csharp/build_and_test.sh +0 -30
- package/csharp/nuget/nuget-package.props +0 -33
- package/csharp/pack.sh +0 -56
- package/csharp/src/Cloud.Unum.USearch.Tests/Cloud.Unum.USearch.Tests.csproj +0 -39
- package/csharp/src/Cloud.Unum.USearch.Tests/USearchIndexTests.cs +0 -1013
- package/csharp/src/Cloud.Unum.USearch.Tests/Usings.cs +0 -1
- package/docs/_static/custom.css +0 -26
- package/docs/_static/custom.js +0 -7
- package/docs/benchmarks.md +0 -245
- package/docs/benchmarks.rst +0 -5
- package/docs/c/index.rst +0 -11
- package/docs/c/reference.rst +0 -7
- package/docs/conf.dox +0 -2668
- package/docs/conf.py +0 -53
- package/docs/contributing.rst +0 -5
- package/docs/cpp/index.rst +0 -11
- package/docs/cpp/reference.rst +0 -17
- package/docs/csharp/index.rst +0 -6
- package/docs/format.md +0 -86
- package/docs/format.rst +0 -5
- package/docs/golang/index.rst +0 -6
- package/docs/index.rst +0 -33
- package/docs/java/index.rst +0 -11
- package/docs/java/reference.rst +0 -7
- package/docs/javascript/index.rst +0 -11
- package/docs/javascript/reference.rst +0 -12
- package/docs/python/index.rst +0 -11
- package/docs/python/reference.rst +0 -42
- package/docs/rust/index.rst +0 -6
- package/docs/swift/index.rst +0 -6
- package/docs/wolfram/index.rst +0 -6
- package/golang/README.md +0 -95
- package/golang/go.mod +0 -3
- package/golang/lib.go +0 -379
- package/golang/lib_test.go +0 -108
- package/java/README.md +0 -22
- package/java/cloud/unum/usearch/Index.java +0 -337
- package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +0 -197
- package/java/cloud/unum/usearch/cloud_unum_usearch_Index.h +0 -125
- package/java/test/IndexTest.java +0 -30
- package/objc/USearchObjective.mm +0 -299
- package/objc/include/USearchObjective.h +0 -154
- package/python/README.md +0 -292
- package/python/lib.cpp +0 -1216
- package/python/scripts/bench.ipynb +0 -193
- package/python/scripts/bench.py +0 -137
- package/python/scripts/bench_exact.py +0 -44
- package/python/scripts/cluster.ipynb +0 -160
- package/python/scripts/index_faiss.py +0 -100
- package/python/scripts/join.py +0 -155
- package/python/scripts/test_index.py +0 -247
- package/python/scripts/test_jit.py +0 -231
- package/python/scripts/test_sparse.py +0 -32
- package/python/scripts/test_tooling.py +0 -133
- package/python/usearch/__init__.py +0 -0
- package/python/usearch/client.py +0 -124
- package/python/usearch/eval.py +0 -470
- package/python/usearch/index.py +0 -1401
- package/python/usearch/io.py +0 -113
- package/python/usearch/numba.py +0 -121
- package/python/usearch/server.py +0 -137
- package/rust/README.md +0 -66
- package/rust/lib.cpp +0 -136
- package/rust/lib.hpp +0 -69
- package/rust/lib.rs +0 -610
- package/simsimd/CMakeLists.txt +0 -67
- package/simsimd/cpp/bench.cxx +0 -206
- package/simsimd/package-ci.json +0 -10
- package/simsimd/setup.py +0 -86
- package/swift/Index+Sugar.swift +0 -153
- package/swift/README.md +0 -48
- package/swift/Test.swift +0 -37
- package/swift/USearch.swift +0 -8
- package/wasm/CMakeLists.txt +0 -7
- package/wasm/README.md +0 -13
- package/wasm/lib.cpp +0 -5
- package/wolfram/CMakeLists.txt +0 -18
- package/wolfram/README.md +0 -13
- package/wolfram/lib.cpp +0 -160
- package/wolfram/test.wls +0 -27
package/rust/lib.rs
DELETED
|
@@ -1,610 +0,0 @@
|
|
|
1
|
-
#[cxx::bridge]
|
|
2
|
-
pub mod ffi {
|
|
3
|
-
// Shared structs with fields visible to both languages.
|
|
4
|
-
#[derive(Debug)]
|
|
5
|
-
struct Matches {
|
|
6
|
-
keys: Vec<u64>,
|
|
7
|
-
distances: Vec<f32>,
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
#[derive(Debug)]
|
|
11
|
-
enum MetricKind {
|
|
12
|
-
IP,
|
|
13
|
-
L2sq,
|
|
14
|
-
Cos,
|
|
15
|
-
Pearson,
|
|
16
|
-
Haversine,
|
|
17
|
-
Divergence,
|
|
18
|
-
Hamming,
|
|
19
|
-
Tanimoto,
|
|
20
|
-
Sorensen,
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
#[derive(Debug)]
|
|
24
|
-
enum ScalarKind {
|
|
25
|
-
F64,
|
|
26
|
-
F32,
|
|
27
|
-
F16,
|
|
28
|
-
I8,
|
|
29
|
-
B1,
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
#[derive(Debug, PartialEq)]
|
|
33
|
-
struct IndexOptions {
|
|
34
|
-
dimensions: usize,
|
|
35
|
-
metric: MetricKind,
|
|
36
|
-
quantization: ScalarKind,
|
|
37
|
-
connectivity: usize,
|
|
38
|
-
expansion_add: usize,
|
|
39
|
-
expansion_search: usize,
|
|
40
|
-
multi: bool,
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// C++ types and signatures exposed to Rust.
|
|
44
|
-
unsafe extern "C++" {
|
|
45
|
-
include!("lib.hpp");
|
|
46
|
-
|
|
47
|
-
/// Low-level C++ interface that is further wrapped into the high-level `Index`
|
|
48
|
-
type NativeIndex;
|
|
49
|
-
|
|
50
|
-
pub fn expansion_add(self: &NativeIndex) -> usize;
|
|
51
|
-
pub fn expansion_search(self: &NativeIndex) -> usize;
|
|
52
|
-
pub fn change_expansion_add(self: &NativeIndex, n: usize) -> Result<()>;
|
|
53
|
-
pub fn change_expansion_search(self: &NativeIndex, n: usize) -> Result<()>;
|
|
54
|
-
|
|
55
|
-
pub fn new_native_index(options: &IndexOptions) -> Result<UniquePtr<NativeIndex>>;
|
|
56
|
-
pub fn reserve(self: &NativeIndex, capacity: usize) -> Result<()>;
|
|
57
|
-
pub fn dimensions(self: &NativeIndex) -> usize;
|
|
58
|
-
pub fn connectivity(self: &NativeIndex) -> usize;
|
|
59
|
-
pub fn size(self: &NativeIndex) -> usize;
|
|
60
|
-
pub fn capacity(self: &NativeIndex) -> usize;
|
|
61
|
-
pub fn serialized_length(self: &NativeIndex) -> usize;
|
|
62
|
-
|
|
63
|
-
pub fn add_i8(self: &NativeIndex, key: u64, vector: &[i8]) -> Result<()>;
|
|
64
|
-
pub fn add_f16(self: &NativeIndex, key: u64, vector: &[u16]) -> Result<()>;
|
|
65
|
-
pub fn add_f32(self: &NativeIndex, key: u64, vector: &[f32]) -> Result<()>;
|
|
66
|
-
pub fn add_f64(self: &NativeIndex, key: u64, vector: &[f64]) -> Result<()>;
|
|
67
|
-
|
|
68
|
-
pub fn search_i8(self: &NativeIndex, query: &[i8], count: usize) -> Result<Matches>;
|
|
69
|
-
pub fn search_f16(self: &NativeIndex, query: &[u16], count: usize) -> Result<Matches>;
|
|
70
|
-
pub fn search_f32(self: &NativeIndex, query: &[f32], count: usize) -> Result<Matches>;
|
|
71
|
-
pub fn search_f64(self: &NativeIndex, query: &[f64], count: usize) -> Result<Matches>;
|
|
72
|
-
|
|
73
|
-
pub fn get_i8(self: &NativeIndex, key: u64, buffer: &mut [i8]) -> Result<usize>;
|
|
74
|
-
pub fn get_f16(self: &NativeIndex, key: u64, buffer: &mut [u16]) -> Result<usize>;
|
|
75
|
-
pub fn get_f32(self: &NativeIndex, key: u64, buffer: &mut [f32]) -> Result<usize>;
|
|
76
|
-
pub fn get_f64(self: &NativeIndex, key: u64, buffer: &mut [f64]) -> Result<usize>;
|
|
77
|
-
|
|
78
|
-
pub fn remove(self: &NativeIndex, key: u64) -> Result<usize>;
|
|
79
|
-
pub fn rename(self: &NativeIndex, from: u64, to: u64) -> Result<usize>;
|
|
80
|
-
pub fn contains(self: &NativeIndex, key: u64) -> bool;
|
|
81
|
-
pub fn count(self: &NativeIndex, key: u64) -> usize;
|
|
82
|
-
|
|
83
|
-
pub fn save(self: &NativeIndex, path: &str) -> Result<()>;
|
|
84
|
-
pub fn load(self: &NativeIndex, path: &str) -> Result<()>;
|
|
85
|
-
pub fn view(self: &NativeIndex, path: &str) -> Result<()>;
|
|
86
|
-
pub fn reset(self: &NativeIndex) -> Result<()>;
|
|
87
|
-
pub fn memory_usage(self: &NativeIndex) -> usize;
|
|
88
|
-
|
|
89
|
-
pub fn save_to_buffer(self: &NativeIndex, buffer: &mut [u8]) -> Result<()>;
|
|
90
|
-
pub fn load_from_buffer(self: &NativeIndex, buffer: &[u8]) -> Result<()>;
|
|
91
|
-
pub fn view_from_buffer(self: &NativeIndex, buffer: &[u8]) -> Result<()>;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
pub struct Index {
|
|
96
|
-
inner: cxx::UniquePtr<ffi::NativeIndex>,
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
impl Default for ffi::IndexOptions {
|
|
100
|
-
fn default() -> Self {
|
|
101
|
-
use crate::ffi::MetricKind;
|
|
102
|
-
use crate::ffi::ScalarKind;
|
|
103
|
-
Self {
|
|
104
|
-
dimensions: 128,
|
|
105
|
-
metric: MetricKind::IP,
|
|
106
|
-
quantization: ScalarKind::F32,
|
|
107
|
-
connectivity: 32,
|
|
108
|
-
expansion_add: 2,
|
|
109
|
-
expansion_search: 3,
|
|
110
|
-
multi: false,
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
impl Clone for ffi::IndexOptions {
|
|
116
|
-
fn clone(&self) -> Self {
|
|
117
|
-
ffi::IndexOptions {
|
|
118
|
-
dimensions: (self.dimensions),
|
|
119
|
-
metric: (self.metric),
|
|
120
|
-
quantization: (self.quantization),
|
|
121
|
-
connectivity: (self.connectivity),
|
|
122
|
-
expansion_add: (self.expansion_add),
|
|
123
|
-
expansion_search: (self.expansion_search),
|
|
124
|
-
multi: (self.multi),
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
pub trait VectorType {
|
|
130
|
-
fn add(index: &Index, key: u64, vector: &[Self]) -> Result<(), cxx::Exception>
|
|
131
|
-
where
|
|
132
|
-
Self: Sized;
|
|
133
|
-
fn get(index: &Index, key: u64, buffer: &mut [Self]) -> Result<usize, cxx::Exception>
|
|
134
|
-
where
|
|
135
|
-
Self: Sized;
|
|
136
|
-
fn search(index: &Index, query: &[Self], count: usize) -> Result<ffi::Matches, cxx::Exception>
|
|
137
|
-
where
|
|
138
|
-
Self: Sized;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
impl VectorType for i8 {
|
|
142
|
-
fn search(index: &Index, query: &[Self], count: usize) -> Result<ffi::Matches, cxx::Exception> {
|
|
143
|
-
index.inner.search_i8(query, count)
|
|
144
|
-
}
|
|
145
|
-
fn get(index: &Index, key: u64, vector: &mut [Self]) -> Result<usize, cxx::Exception> {
|
|
146
|
-
index.inner.get_i8(key, vector)
|
|
147
|
-
}
|
|
148
|
-
fn add(index: &Index, key: u64, vector: &[Self]) -> Result<(), cxx::Exception> {
|
|
149
|
-
index.inner.add_i8(key, vector)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
impl VectorType for f32 {
|
|
154
|
-
fn search(index: &Index, query: &[Self], count: usize) -> Result<ffi::Matches, cxx::Exception> {
|
|
155
|
-
index.inner.search_f32(query, count)
|
|
156
|
-
}
|
|
157
|
-
fn get(index: &Index, key: u64, vector: &mut [Self]) -> Result<usize, cxx::Exception> {
|
|
158
|
-
index.inner.get_f32(key, vector)
|
|
159
|
-
}
|
|
160
|
-
fn add(index: &Index, key: u64, vector: &[Self]) -> Result<(), cxx::Exception> {
|
|
161
|
-
index.inner.add_f32(key, vector)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
impl VectorType for f64 {
|
|
166
|
-
fn search(index: &Index, query: &[Self], count: usize) -> Result<ffi::Matches, cxx::Exception> {
|
|
167
|
-
index.inner.search_f64(query, count)
|
|
168
|
-
}
|
|
169
|
-
fn get(index: &Index, key: u64, vector: &mut [Self]) -> Result<usize, cxx::Exception> {
|
|
170
|
-
index.inner.get_f64(key, vector)
|
|
171
|
-
}
|
|
172
|
-
fn add(index: &Index, key: u64, vector: &[Self]) -> Result<(), cxx::Exception> {
|
|
173
|
-
index.inner.add_f64(key, vector)
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
impl Index {
|
|
178
|
-
pub fn new(options: &ffi::IndexOptions) -> Result<Self, cxx::Exception> {
|
|
179
|
-
match ffi::new_native_index(options) {
|
|
180
|
-
Ok(inner) => Result::Ok(Self { inner }),
|
|
181
|
-
Err(err) => Err(err),
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/// Retrieves the expansion value used during index creation.
|
|
186
|
-
pub fn expansion_add(self: &Index) -> usize {
|
|
187
|
-
self.inner.expansion_add()
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/// Retrieves the expansion value used during search.
|
|
191
|
-
pub fn expansion_search(self: &Index) -> usize {
|
|
192
|
-
self.inner.expansion_search()
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/// Updates the expansion value used during index creation. Rarely used.
|
|
196
|
-
pub fn change_expansion_add(self: &Index, n: usize) -> Result<(), cxx::Exception> {
|
|
197
|
-
self.inner.change_expansion_add(n)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/// Updates the expansion value used during search operations.
|
|
201
|
-
pub fn change_expansion_search(self: &Index, n: usize) -> Result<(), cxx::Exception> {
|
|
202
|
-
self.inner.change_expansion_search(n)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/// Performs k-Approximate Nearest Neighbors (kANN) Search for closest vectors to the provided query.
|
|
206
|
-
///
|
|
207
|
-
/// # Arguments
|
|
208
|
-
///
|
|
209
|
-
/// * `query` - A slice containing the query vector data.
|
|
210
|
-
/// * `count` - The maximum number of neighbors to search for.
|
|
211
|
-
///
|
|
212
|
-
/// # Returns
|
|
213
|
-
///
|
|
214
|
-
/// A `Result` containing the matches found.
|
|
215
|
-
pub fn search<T: VectorType>(
|
|
216
|
-
self: &Index,
|
|
217
|
-
query: &[T],
|
|
218
|
-
count: usize,
|
|
219
|
-
) -> Result<ffi::Matches, cxx::Exception> {
|
|
220
|
-
T::search(self, query, count)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/// Adds a vector with a specified key to the index.
|
|
224
|
-
///
|
|
225
|
-
/// # Arguments
|
|
226
|
-
///
|
|
227
|
-
/// * `key` - The key associated with the vector.
|
|
228
|
-
/// * `vector` - A slice containing the vector data.
|
|
229
|
-
pub fn add<T: VectorType>(self: &Index, key: u64, vector: &[T]) -> Result<(), cxx::Exception> {
|
|
230
|
-
T::add(self, key, vector)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/// Extracts one or more vectors matching the specified key.
|
|
234
|
-
/// The `vector` slice must be a multiple of the number of dimensions in the index.
|
|
235
|
-
/// After the execution, return the number `X` of vectors found.
|
|
236
|
-
/// The vector slice's first `X * dimensions` elements will be filled.
|
|
237
|
-
///
|
|
238
|
-
/// If you are a novice user, consider `export`.
|
|
239
|
-
///
|
|
240
|
-
/// # Arguments
|
|
241
|
-
///
|
|
242
|
-
/// * `key` - The key associated with the vector.
|
|
243
|
-
/// * `vector` - A slice containing the vector data.
|
|
244
|
-
pub fn get<T: VectorType>(
|
|
245
|
-
self: &Index,
|
|
246
|
-
key: u64,
|
|
247
|
-
vector: &mut [T],
|
|
248
|
-
) -> Result<usize, cxx::Exception> {
|
|
249
|
-
T::get(self, key, vector)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/// Extracts one or more vectors matching specified key into supplied resizable vector.
|
|
253
|
-
/// The `vector` is resized to a multiple of the number of dimensions in the index.
|
|
254
|
-
///
|
|
255
|
-
/// # Arguments
|
|
256
|
-
///
|
|
257
|
-
/// * `key` - The key associated with the vector.
|
|
258
|
-
/// * `vector` - A mutable vector containing the vector data.
|
|
259
|
-
pub fn export<T: VectorType + Default + Clone>(
|
|
260
|
-
self: &Index,
|
|
261
|
-
key: u64,
|
|
262
|
-
vector: &mut Vec<T>,
|
|
263
|
-
) -> Result<usize, cxx::Exception> {
|
|
264
|
-
let dim = self.dimensions();
|
|
265
|
-
let max_matches = self.count(key);
|
|
266
|
-
vector.resize(dim * max_matches, T::default());
|
|
267
|
-
let matches = T::get(self, key, &mut vector[..]);
|
|
268
|
-
if matches.is_err() {
|
|
269
|
-
return matches;
|
|
270
|
-
}
|
|
271
|
-
vector.resize(dim * matches.as_ref().unwrap(), T::default());
|
|
272
|
-
return matches;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/// Reserves memory for a specified number of incoming vectors.
|
|
276
|
-
///
|
|
277
|
-
/// # Arguments
|
|
278
|
-
///
|
|
279
|
-
/// * `capacity` - The desired total capacity, including the current size.
|
|
280
|
-
pub fn reserve(self: &Index, capacity: usize) -> Result<(), cxx::Exception> {
|
|
281
|
-
self.inner.reserve(capacity)
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/// Retrieves the number of dimensions in the vectors indexed.
|
|
285
|
-
pub fn dimensions(self: &Index) -> usize {
|
|
286
|
-
self.inner.dimensions()
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/// Retrieves the connectivity parameter that limits connections-per-node in the graph.
|
|
290
|
-
pub fn connectivity(self: &Index) -> usize {
|
|
291
|
-
self.inner.connectivity()
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/// Retrieves the current number of vectors in the index.
|
|
295
|
-
pub fn size(self: &Index) -> usize {
|
|
296
|
-
self.inner.size()
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/// Retrieves the total capacity of the index, including reserved space.
|
|
300
|
-
pub fn capacity(self: &Index) -> usize {
|
|
301
|
-
self.inner.capacity()
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/// Reports expected file size after serialization.
|
|
305
|
-
pub fn serialized_length(self: &Index) -> usize {
|
|
306
|
-
self.inner.serialized_length()
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/// Removes the vector associated with the given key from the index.
|
|
310
|
-
///
|
|
311
|
-
/// # Arguments
|
|
312
|
-
///
|
|
313
|
-
/// * `key` - The key of the vector to be removed.
|
|
314
|
-
///
|
|
315
|
-
/// # Returns
|
|
316
|
-
///
|
|
317
|
-
/// `true` if the vector is successfully removed, `false` otherwise.
|
|
318
|
-
pub fn remove(self: &Index, key: u64) -> Result<usize, cxx::Exception> {
|
|
319
|
-
self.inner.remove(key)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/// Renames the vector under a specific key.
|
|
323
|
-
///
|
|
324
|
-
/// # Arguments
|
|
325
|
-
///
|
|
326
|
-
/// * `from` - The key of the vector to be renamed.
|
|
327
|
-
/// * `to` - The new name.
|
|
328
|
-
///
|
|
329
|
-
/// # Returns
|
|
330
|
-
///
|
|
331
|
-
/// `true` if the vector is renamed, `false` otherwise.
|
|
332
|
-
pub fn rename(self: &Index, from: u64, to: u64) -> Result<usize, cxx::Exception> {
|
|
333
|
-
self.inner.rename(from, to)
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/// Checks if the index contains a vector with a specified key.
|
|
337
|
-
///
|
|
338
|
-
/// # Arguments
|
|
339
|
-
///
|
|
340
|
-
/// * `key` - The key to be checked.
|
|
341
|
-
///
|
|
342
|
-
/// # Returns
|
|
343
|
-
///
|
|
344
|
-
/// `true` if the index contains the vector with the given key, `false` otherwise.
|
|
345
|
-
pub fn contains(self: &Index, key: u64) -> bool {
|
|
346
|
-
self.inner.contains(key)
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/// Count the count of vectors with the same specified key.
|
|
350
|
-
///
|
|
351
|
-
/// # Arguments
|
|
352
|
-
///
|
|
353
|
-
/// * `key` - The key to be checked.
|
|
354
|
-
///
|
|
355
|
-
/// # Returns
|
|
356
|
-
///
|
|
357
|
-
/// Number of vectors found.
|
|
358
|
-
pub fn count(self: &Index, key: u64) -> usize {
|
|
359
|
-
self.inner.count(key)
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/// Saves the index to a specified file.
|
|
363
|
-
///
|
|
364
|
-
/// # Arguments
|
|
365
|
-
///
|
|
366
|
-
/// * `path` - The file path where the index will be saved.
|
|
367
|
-
pub fn save(self: &Index, path: &str) -> Result<(), cxx::Exception> {
|
|
368
|
-
self.inner.save(path)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/// Loads the index from a specified file.
|
|
372
|
-
///
|
|
373
|
-
/// # Arguments
|
|
374
|
-
///
|
|
375
|
-
/// * `path` - The file path from where the index will be loaded.
|
|
376
|
-
pub fn load(self: &Index, path: &str) -> Result<(), cxx::Exception> {
|
|
377
|
-
self.inner.load(path)
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/// Creates a view of the index from a file without loading it into memory.
|
|
381
|
-
///
|
|
382
|
-
/// # Arguments
|
|
383
|
-
///
|
|
384
|
-
/// * `path` - The file path from where the view will be created.
|
|
385
|
-
pub fn view(self: &Index, path: &str) -> Result<(), cxx::Exception> {
|
|
386
|
-
self.inner.view(path)
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/// Erases all members from the index, closes files, and returns RAM to OS.
|
|
390
|
-
pub fn reset(self: &Index) -> Result<(), cxx::Exception> {
|
|
391
|
-
self.inner.reset()
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/// A relatively accurate lower bound on the amount of memory consumed by the system.
|
|
395
|
-
/// In practice, its error will be below 10%.
|
|
396
|
-
pub fn memory_usage(self: &Index) -> usize {
|
|
397
|
-
self.inner.memory_usage()
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/// Saves the index to a specified file.
|
|
401
|
-
///
|
|
402
|
-
/// # Arguments
|
|
403
|
-
///
|
|
404
|
-
/// * `path` - The file path where the index will be saved.
|
|
405
|
-
pub fn save_to_buffer(self: &Index, buffer: &mut [u8]) -> Result<(), cxx::Exception> {
|
|
406
|
-
self.inner.save_to_buffer(buffer)
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/// Loads the index from a specified file.
|
|
410
|
-
///
|
|
411
|
-
/// # Arguments
|
|
412
|
-
///
|
|
413
|
-
/// * `path` - The file path from where the index will be loaded.
|
|
414
|
-
pub fn load_from_buffer(self: &Index, buffer: &[u8]) -> Result<(), cxx::Exception> {
|
|
415
|
-
self.inner.load_from_buffer(buffer)
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/// Creates a view of the index from a file without loading it into memory.
|
|
419
|
-
///
|
|
420
|
-
/// # Arguments
|
|
421
|
-
///
|
|
422
|
-
/// * `path` - The file path from where the view will be created.
|
|
423
|
-
pub fn view_from_buffer(self: &Index, buffer: &[u8]) -> Result<(), cxx::Exception> {
|
|
424
|
-
self.inner.view_from_buffer(buffer)
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
pub fn new_index(options: &ffi::IndexOptions) -> Result<Index, cxx::Exception> {
|
|
429
|
-
Index::new(options)
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
#[cfg(test)]
|
|
433
|
-
mod tests {
|
|
434
|
-
use crate::ffi::IndexOptions;
|
|
435
|
-
use crate::ffi::MetricKind;
|
|
436
|
-
use crate::ffi::ScalarKind;
|
|
437
|
-
|
|
438
|
-
use crate::new_index;
|
|
439
|
-
use crate::Index;
|
|
440
|
-
|
|
441
|
-
#[test]
|
|
442
|
-
fn test_add_get_vector() {
|
|
443
|
-
let mut options = IndexOptions::default();
|
|
444
|
-
options.dimensions = 5;
|
|
445
|
-
let index = Index::new(&options).unwrap();
|
|
446
|
-
assert!(index.reserve(10).is_ok());
|
|
447
|
-
|
|
448
|
-
let first: [f32; 5] = [0.2, 0.1, 0.2, 0.1, 0.3];
|
|
449
|
-
let second: [f32; 5] = [0.3, 0.2, 0.4, 0.0, 0.1];
|
|
450
|
-
assert!(index.add(1, &first).is_ok());
|
|
451
|
-
assert!(index.add(2, &second).is_ok());
|
|
452
|
-
assert_eq!(index.size(), 2);
|
|
453
|
-
|
|
454
|
-
// Test using Vec<T>
|
|
455
|
-
let mut found_vec: Vec<f32> = Vec::new();
|
|
456
|
-
assert_eq!(index.export(1, &mut found_vec).unwrap(), 1);
|
|
457
|
-
assert_eq!(found_vec.len(), 5);
|
|
458
|
-
assert_eq!(found_vec, first.to_vec());
|
|
459
|
-
|
|
460
|
-
// Test using slice
|
|
461
|
-
let mut found_slice = [0.0 as f32; 5];
|
|
462
|
-
assert_eq!(index.get(1, &mut found_slice).unwrap(), 1);
|
|
463
|
-
assert_eq!(found_slice, first);
|
|
464
|
-
|
|
465
|
-
// Create a slice with incorrect size
|
|
466
|
-
let mut found = [0.0 as f32; 6]; // This isn't a multiple of the index's dimensions.
|
|
467
|
-
let result = index.get(1, &mut found);
|
|
468
|
-
assert!(result.is_err());
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
#[test]
|
|
472
|
-
fn test_add_remove_vector() {
|
|
473
|
-
let mut options = IndexOptions::default();
|
|
474
|
-
options.dimensions = 4;
|
|
475
|
-
options.metric = MetricKind::IP;
|
|
476
|
-
options.quantization = ScalarKind::F64;
|
|
477
|
-
options.connectivity = 10;
|
|
478
|
-
options.expansion_add = 128;
|
|
479
|
-
options.expansion_search = 3;
|
|
480
|
-
let index = Index::new(&options).unwrap();
|
|
481
|
-
assert!(index.reserve(10).is_ok());
|
|
482
|
-
assert_eq!(index.capacity(), 10);
|
|
483
|
-
|
|
484
|
-
let first: [f32; 4] = [0.2, 0.1, 0.2, 0.1];
|
|
485
|
-
let second: [f32; 4] = [0.3, 0.2, 0.4, 0.0];
|
|
486
|
-
|
|
487
|
-
// IDs until 18446744073709551615 should be fine:
|
|
488
|
-
let id1 = 483367403120493160;
|
|
489
|
-
let id2 = 483367403120558696;
|
|
490
|
-
let id3 = 483367403120624232;
|
|
491
|
-
let id4 = 483367403120624233;
|
|
492
|
-
|
|
493
|
-
assert!(index.add(id1, &first).is_ok());
|
|
494
|
-
let mut found_slice = [0.0 as f32; 4];
|
|
495
|
-
assert_eq!(index.get(id1, &mut found_slice).unwrap(), 1);
|
|
496
|
-
assert!(index.remove(id1).is_ok());
|
|
497
|
-
|
|
498
|
-
assert!(index.add(id2, &second).is_ok());
|
|
499
|
-
let mut found_slice = [0.0 as f32; 4];
|
|
500
|
-
assert_eq!(index.get(id2, &mut found_slice).unwrap(), 1);
|
|
501
|
-
assert!(index.remove(id2).is_ok());
|
|
502
|
-
|
|
503
|
-
assert!(index.add(id3, &second).is_ok());
|
|
504
|
-
let mut found_slice = [0.0 as f32; 4];
|
|
505
|
-
assert_eq!(index.get(id3, &mut found_slice).unwrap(), 1);
|
|
506
|
-
assert!(index.remove(id3).is_ok());
|
|
507
|
-
|
|
508
|
-
assert!(index.add(id4, &second).is_ok());
|
|
509
|
-
let mut found_slice = [0.0 as f32; 4];
|
|
510
|
-
assert_eq!(index.get(id4, &mut found_slice).unwrap(), 1);
|
|
511
|
-
assert!(index.remove(id4).is_ok());
|
|
512
|
-
|
|
513
|
-
assert_eq!(index.size(), 0);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
#[test]
|
|
517
|
-
fn integration() {
|
|
518
|
-
let mut options = IndexOptions::default();
|
|
519
|
-
options.dimensions = 5;
|
|
520
|
-
|
|
521
|
-
let index = Index::new(&options).unwrap();
|
|
522
|
-
|
|
523
|
-
assert!(index.expansion_add() > 0);
|
|
524
|
-
assert!(index.expansion_search() > 0);
|
|
525
|
-
|
|
526
|
-
assert!(index.reserve(10).is_ok());
|
|
527
|
-
assert!(index.capacity() >= 10);
|
|
528
|
-
assert!(index.connectivity() != 0);
|
|
529
|
-
assert_eq!(index.dimensions(), 5);
|
|
530
|
-
assert_eq!(index.size(), 0);
|
|
531
|
-
|
|
532
|
-
let first: [f32; 5] = [0.2, 0.1, 0.2, 0.1, 0.3];
|
|
533
|
-
let second: [f32; 5] = [0.3, 0.2, 0.4, 0.0, 0.1];
|
|
534
|
-
|
|
535
|
-
println!(
|
|
536
|
-
"before add, memory_usage: {} \
|
|
537
|
-
cap: {} \
|
|
538
|
-
",
|
|
539
|
-
index.memory_usage(),
|
|
540
|
-
index.capacity(),
|
|
541
|
-
);
|
|
542
|
-
assert!(index.change_expansion_add(10).is_ok());
|
|
543
|
-
assert_eq!(index.expansion_add(), 10);
|
|
544
|
-
assert!(index.add(42, &first).is_ok());
|
|
545
|
-
assert!(index.change_expansion_add(12).is_ok());
|
|
546
|
-
assert_eq!(index.expansion_add(), 12);
|
|
547
|
-
assert!(index.add(43, &second).is_ok());
|
|
548
|
-
assert_eq!(index.size(), 2);
|
|
549
|
-
println!(
|
|
550
|
-
"after add, memory_usage: {} \
|
|
551
|
-
cap: {} \
|
|
552
|
-
",
|
|
553
|
-
index.memory_usage(),
|
|
554
|
-
index.capacity(),
|
|
555
|
-
);
|
|
556
|
-
|
|
557
|
-
assert!(index.change_expansion_search(10).is_ok());
|
|
558
|
-
assert_eq!(index.expansion_search(), 10);
|
|
559
|
-
// Read back the tags
|
|
560
|
-
let results = index.search(&first, 10).unwrap();
|
|
561
|
-
println!("{:?}", results);
|
|
562
|
-
assert_eq!(results.keys.len(), 2);
|
|
563
|
-
|
|
564
|
-
assert!(index.change_expansion_search(12).is_ok());
|
|
565
|
-
assert_eq!(index.expansion_search(), 12);
|
|
566
|
-
let results = index.search(&first, 10).unwrap();
|
|
567
|
-
println!("{:?}", results);
|
|
568
|
-
assert_eq!(results.keys.len(), 2);
|
|
569
|
-
|
|
570
|
-
// Validate serialization
|
|
571
|
-
assert!(index.save("index.rust.usearch").is_ok());
|
|
572
|
-
assert!(index.load("index.rust.usearch").is_ok());
|
|
573
|
-
assert!(index.view("index.rust.usearch").is_ok());
|
|
574
|
-
|
|
575
|
-
// Make sure every function is called at least once
|
|
576
|
-
assert!(new_index(&options).is_ok());
|
|
577
|
-
options.metric = MetricKind::L2sq;
|
|
578
|
-
assert!(new_index(&options).is_ok());
|
|
579
|
-
options.metric = MetricKind::Cos;
|
|
580
|
-
assert!(new_index(&options).is_ok());
|
|
581
|
-
options.metric = MetricKind::Haversine;
|
|
582
|
-
assert!(new_index(&options).is_ok());
|
|
583
|
-
|
|
584
|
-
let mut serialization_buffer = Vec::new();
|
|
585
|
-
serialization_buffer.resize(index.serialized_length(), 0);
|
|
586
|
-
assert!(index.save_to_buffer(&mut serialization_buffer).is_ok());
|
|
587
|
-
|
|
588
|
-
let deserialized_index = new_index(&options).unwrap();
|
|
589
|
-
assert!(deserialized_index
|
|
590
|
-
.load_from_buffer(&serialization_buffer)
|
|
591
|
-
.is_ok());
|
|
592
|
-
assert_eq!(index.size(), deserialized_index.size());
|
|
593
|
-
|
|
594
|
-
// reset
|
|
595
|
-
assert_ne!(index.memory_usage(), 0);
|
|
596
|
-
assert!(index.reset().is_ok());
|
|
597
|
-
assert_eq!(index.size(), 0);
|
|
598
|
-
assert_eq!(index.memory_usage(), 0);
|
|
599
|
-
|
|
600
|
-
// clone
|
|
601
|
-
options.metric = MetricKind::Haversine;
|
|
602
|
-
let mut opts = options.clone();
|
|
603
|
-
assert_eq!(opts.metric, options.metric);
|
|
604
|
-
assert_eq!(opts.quantization, options.quantization);
|
|
605
|
-
assert_eq!(opts, options);
|
|
606
|
-
opts.metric = MetricKind::Cos;
|
|
607
|
-
assert_ne!(opts.metric, options.metric);
|
|
608
|
-
assert!(new_index(&opts).is_ok());
|
|
609
|
-
}
|
|
610
|
-
}
|
package/simsimd/CMakeLists.txt
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# SimSIMD library CMakeLists.txt
|
|
2
|
-
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
|
|
3
|
-
project(simsimd LANGUAGES C CXX)
|
|
4
|
-
|
|
5
|
-
set(CMAKE_C_STANDARD 11)
|
|
6
|
-
set(CMAKE_C_STANDARD_REQUIRED YES)
|
|
7
|
-
set(CMAKE_C_EXTENSIONS NO)
|
|
8
|
-
|
|
9
|
-
set(CMAKE_CXX_STANDARD 17)
|
|
10
|
-
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
|
11
|
-
set(CMAKE_CXX_EXTENSIONS NO)
|
|
12
|
-
|
|
13
|
-
option(SIMSIMD_BUILD_BENCHMARKS "Compile a micro-benchmark for current ISA" OFF)
|
|
14
|
-
|
|
15
|
-
# Fetch external dependencies
|
|
16
|
-
include(FetchContent)
|
|
17
|
-
|
|
18
|
-
# Suppress building tests of Google Benchmark
|
|
19
|
-
set(BENCHMARK_ENABLE_TESTING OFF)
|
|
20
|
-
set(BENCHMARK_ENABLE_INSTALL OFF)
|
|
21
|
-
set(BENCHMARK_ENABLE_DOXYGEN OFF)
|
|
22
|
-
set(BENCHMARK_INSTALL_DOCS OFF)
|
|
23
|
-
set(BENCHMARK_DOWNLOAD_DEPENDENCIES ON)
|
|
24
|
-
set(BENCHMARK_ENABLE_GTEST_TESTS OFF)
|
|
25
|
-
set(BENCHMARK_USE_BUNDLED_GTEST ON)
|
|
26
|
-
|
|
27
|
-
FetchContent_Declare(
|
|
28
|
-
benchmark
|
|
29
|
-
GIT_REPOSITORY https://github.com/google/benchmark.git
|
|
30
|
-
GIT_TAG v1.7.0)
|
|
31
|
-
FetchContent_MakeAvailable(benchmark)
|
|
32
|
-
|
|
33
|
-
# Default to Release build type if not set
|
|
34
|
-
if(NOT CMAKE_BUILD_TYPE)
|
|
35
|
-
set(CMAKE_BUILD_TYPE Release)
|
|
36
|
-
endif()
|
|
37
|
-
|
|
38
|
-
# Global compiler flags for debug and release
|
|
39
|
-
set(CMAKE_CXX_FLAGS_DEBUG "-g -fsanitize=address")
|
|
40
|
-
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
|
41
|
-
set(CMAKE_C_FLAGS_DEBUG "-g -fsanitize=address")
|
|
42
|
-
set(CMAKE_C_FLAGS_RELEASE "-O3")
|
|
43
|
-
|
|
44
|
-
# Compiler-specific flags
|
|
45
|
-
if(CMAKE_CXX_COMPILER_ID MATCHES "^(Apple)?Clang$")
|
|
46
|
-
if(NOT APPLE)
|
|
47
|
-
add_compile_options(-march=native)
|
|
48
|
-
endif()
|
|
49
|
-
add_compile_options(-pedantic -ferror-limit=1)
|
|
50
|
-
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
51
|
-
add_compile_options(-march=native -pedantic -fmax-errors=1 -Wno-tautological-constant-compare)
|
|
52
|
-
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
|
|
53
|
-
add_compile_options(-w -ferror-limit=1)
|
|
54
|
-
endif()
|
|
55
|
-
|
|
56
|
-
# Define the header-only library
|
|
57
|
-
file(GLOB SIMSIMD_SOURCES include/simsimd/*.h)
|
|
58
|
-
add_library(simsimd INTERFACE)
|
|
59
|
-
target_sources(simsimd INTERFACE ${SIMSIMD_SOURCES})
|
|
60
|
-
target_include_directories(simsimd INTERFACE "${PROJECT_SOURCE_DIR}/include")
|
|
61
|
-
|
|
62
|
-
# Build benchmarks if required
|
|
63
|
-
if(SIMSIMD_BUILD_BENCHMARKS)
|
|
64
|
-
find_package(Threads REQUIRED)
|
|
65
|
-
add_executable(simsimd_bench cpp/bench.cxx)
|
|
66
|
-
target_link_libraries(simsimd_bench simsimd Threads::Threads benchmark)
|
|
67
|
-
endif()
|