usearch 2.4.0 → 2.5.0

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/rust/lib.rs CHANGED
@@ -33,198 +33,331 @@ pub mod ffi {
33
33
  connectivity: usize,
34
34
  expansion_add: usize,
35
35
  expansion_search: usize,
36
+ multi: bool,
36
37
  }
37
38
 
38
39
  // C++ types and signatures exposed to Rust.
39
40
  unsafe extern "C++" {
40
41
  include!("lib.hpp");
41
42
 
42
- /// Represents the USearch index.
43
- type Index;
44
-
45
- /// Initializes a new instance of the index with the provided options.
46
- ///
47
- /// # Arguments
48
- ///
49
- /// * `options` - A reference to the `IndexOptions` structure containing initialization options.
50
- ///
51
- /// # Returns
52
- ///
53
- /// A `Result` which is `Ok` if the index is successfully initialized, or `Err` if an error occurs.
54
- pub fn new_index(options: &IndexOptions) -> Result<UniquePtr<Index>>;
55
-
56
- /// Reserves memory for a specified number of incoming vectors.
57
- ///
58
- /// # Arguments
59
- ///
60
- /// * `capacity` - The desired total capacity including the current size.
61
- pub fn reserve(self: &Index, capacity: usize) -> Result<()>;
62
-
63
- /// Retrieves the number of dimensions in the vectors indexed.
64
- pub fn dimensions(self: &Index) -> usize;
65
-
66
- /// Retrieves the connectivity parameter that limits connections-per-node in the graph.
67
- pub fn connectivity(self: &Index) -> usize;
68
-
69
- /// Retrieves the current number of vectors in the index.
70
- pub fn size(self: &Index) -> usize;
71
-
72
- /// Retrieves the total capacity of the index, including reserved space.
73
- pub fn capacity(self: &Index) -> usize;
74
-
75
- /// Reports expected file size after serialization.
76
- pub fn serialized_length(self: &Index) -> usize;
77
-
78
- /// Adds a vector with a specified key to the index.
79
- ///
80
- /// # Arguments
81
- ///
82
- /// * `key` - The key associated with the vector.
83
- /// * `vector` - A slice containing the vector data.
84
- fn add<T>(self: &Index, key: u64, vector: &[T]) -> Result<()> {
85
- // Use type-based dispatch
86
- if std::any::TypeId::of::<T>() == std::any::TypeId::of::<i8>() {
87
- add_i8(key, unsafe { std::mem::transmute::<&[T], &[i8]>(vector) })
88
- } else if std::any::TypeId::of::<T>() == std::any::TypeId::of::<f32>() {
89
- add_f32(key, unsafe { std::mem::transmute::<&[T], &[f32]>(vector) })
90
- } else if std::any::TypeId::of::<T>() == std::any::TypeId::of::<f64>() {
91
- add_f64(key, unsafe { std::mem::transmute::<&[T], &[f64]>(vector) })
92
- } else {
93
- Err(Box::new("Unsupported type"))
94
- }
43
+ /// Low-level C++ interface, that is further wrapped into the high-level `Index`
44
+ type NativeIndex;
45
+
46
+ pub fn new_native_index(options: &IndexOptions) -> Result<UniquePtr<NativeIndex>>;
47
+ pub fn reserve(self: &NativeIndex, capacity: usize) -> Result<()>;
48
+ pub fn dimensions(self: &NativeIndex) -> usize;
49
+ pub fn connectivity(self: &NativeIndex) -> usize;
50
+ pub fn size(self: &NativeIndex) -> usize;
51
+ pub fn capacity(self: &NativeIndex) -> usize;
52
+ pub fn serialized_length(self: &NativeIndex) -> usize;
53
+
54
+ pub fn add_i8(self: &NativeIndex, key: u64, vector: &[i8]) -> Result<()>;
55
+ pub fn add_f16(self: &NativeIndex, key: u64, vector: &[u16]) -> Result<()>;
56
+ pub fn add_f32(self: &NativeIndex, key: u64, vector: &[f32]) -> Result<()>;
57
+ pub fn add_f64(self: &NativeIndex, key: u64, vector: &[f64]) -> Result<()>;
58
+
59
+ pub fn search_i8(self: &NativeIndex, query: &[i8], count: usize) -> Result<Matches>;
60
+ pub fn search_f16(self: &NativeIndex, query: &[u16], count: usize) -> Result<Matches>;
61
+ pub fn search_f32(self: &NativeIndex, query: &[f32], count: usize) -> Result<Matches>;
62
+ pub fn search_f64(self: &NativeIndex, query: &[f64], count: usize) -> Result<Matches>;
63
+
64
+ pub fn get_i8(self: &NativeIndex, key: u64, buffer: &mut [i8]) -> Result<usize>;
65
+ pub fn get_f16(self: &NativeIndex, key: u64, buffer: &mut [u16]) -> Result<usize>;
66
+ pub fn get_f32(self: &NativeIndex, key: u64, buffer: &mut [f32]) -> Result<usize>;
67
+ pub fn get_f64(self: &NativeIndex, key: u64, buffer: &mut [f64]) -> Result<usize>;
68
+
69
+ pub fn remove(self: &NativeIndex, key: u64) -> Result<usize>;
70
+ pub fn rename(self: &NativeIndex, from: u64, to: u64) -> Result<usize>;
71
+ pub fn contains(self: &NativeIndex, key: u64) -> bool;
72
+ pub fn count(self: &NativeIndex, key: u64) -> usize;
73
+
74
+ pub fn save(self: &NativeIndex, path: &str) -> Result<()>;
75
+ pub fn load(self: &NativeIndex, path: &str) -> Result<()>;
76
+ pub fn view(self: &NativeIndex, path: &str) -> Result<()>;
77
+ pub fn save_to_buffer(self: &NativeIndex, buffer: &mut [u8]) -> Result<()>;
78
+ pub fn load_from_buffer(self: &NativeIndex, buffer: &[u8]) -> Result<()>;
79
+ pub fn view_from_buffer(self: &NativeIndex, buffer: &[u8]) -> Result<()>;
80
+ }
81
+ }
82
+
83
+ pub struct Index {
84
+ inner: cxx::UniquePtr<ffi::NativeIndex>,
85
+ }
86
+
87
+ impl Default for ffi::IndexOptions {
88
+ fn default() -> Self {
89
+ use crate::ffi::MetricKind;
90
+ use crate::ffi::ScalarKind;
91
+ Self {
92
+ dimensions: 128,
93
+ metric: MetricKind::IP,
94
+ quantization: ScalarKind::F32,
95
+ connectivity: 32,
96
+ expansion_add: 2,
97
+ expansion_search: 3,
98
+ multi: false,
95
99
  }
100
+ }
101
+ }
96
102
 
97
- pub fn add_i8(self: &Index, key: u64, vector: &[i8]) -> Result<()>;
98
- pub fn add_f16(self: &Index, key: u64, vector: &[u16]) -> Result<()>;
99
- pub fn add_f32(self: &Index, key: u64, vector: &[f32]) -> Result<()>;
100
- pub fn add_f64(self: &Index, key: u64, vector: &[f64]) -> Result<()>;
101
-
102
- /// Performs k-Approximate Nearest Neighbors (kANN) Search for closest vectors to the provided query.
103
- ///
104
- /// # Arguments
105
- ///
106
- /// * `query` - A slice containing the query vector data.
107
- /// * `count` - The maximum number of neighbors to search for.
108
- ///
109
- /// # Returns
110
- ///
111
- /// A `Result` containing the matches found.
112
- pub fn search<T>(self: &Index, query: &[T], count: usize) -> Result<Matches> {
113
- // Use type-based dispatch
114
- if std::any::TypeId::of::<T>() == std::any::TypeId::of::<i8>() {
115
- search_i8(unsafe { std::mem::transmute::<&[T], &[i8]>(query) }, count)
116
- } else if std::any::TypeId::of::<T>() == std::any::TypeId::of::<f32>() {
117
- search_f32(unsafe { std::mem::transmute::<&[T], &[f32]>(query) }, count)
118
- } else if std::any::TypeId::of::<T>() == std::any::TypeId::of::<f64>() {
119
- search_f64(unsafe { std::mem::transmute::<&[T], &[f64]>(query) }, count)
120
- } else {
121
- Err(Box::new("Unsupported type"))
122
- }
103
+ pub trait VectorType {
104
+ fn add(index: &Index, key: u64, vector: &[Self]) -> Result<(), cxx::Exception>
105
+ where
106
+ Self: Sized;
107
+ fn get(index: &Index, key: u64, buffer: &mut [Self]) -> Result<usize, cxx::Exception>
108
+ where
109
+ Self: Sized;
110
+ fn search(index: &Index, query: &[Self], count: usize) -> Result<ffi::Matches, cxx::Exception>
111
+ where
112
+ Self: Sized;
113
+ }
114
+
115
+ impl VectorType for i8 {
116
+ fn search(index: &Index, query: &[Self], count: usize) -> Result<ffi::Matches, cxx::Exception> {
117
+ index.inner.search_i8(query, count)
118
+ }
119
+ fn get(index: &Index, key: u64, vector: &mut [Self]) -> Result<usize, cxx::Exception> {
120
+ index.inner.get_i8(key, vector)
121
+ }
122
+ fn add(index: &Index, key: u64, vector: &[Self]) -> Result<(), cxx::Exception> {
123
+ index.inner.add_i8(key, vector)
124
+ }
125
+ }
126
+
127
+ impl VectorType for f32 {
128
+ fn search(index: &Index, query: &[Self], count: usize) -> Result<ffi::Matches, cxx::Exception> {
129
+ index.inner.search_f32(query, count)
130
+ }
131
+ fn get(index: &Index, key: u64, vector: &mut [Self]) -> Result<usize, cxx::Exception> {
132
+ index.inner.get_f32(key, vector)
133
+ }
134
+ fn add(index: &Index, key: u64, vector: &[Self]) -> Result<(), cxx::Exception> {
135
+ index.inner.add_f32(key, vector)
136
+ }
137
+ }
138
+
139
+ impl VectorType for f64 {
140
+ fn search(index: &Index, query: &[Self], count: usize) -> Result<ffi::Matches, cxx::Exception> {
141
+ index.inner.search_f64(query, count)
142
+ }
143
+ fn get(index: &Index, key: u64, vector: &mut [Self]) -> Result<usize, cxx::Exception> {
144
+ index.inner.get_f64(key, vector)
145
+ }
146
+ fn add(index: &Index, key: u64, vector: &[Self]) -> Result<(), cxx::Exception> {
147
+ index.inner.add_f64(key, vector)
148
+ }
149
+ }
150
+
151
+ impl Index {
152
+ pub fn new(options: &ffi::IndexOptions) -> Result<Self, cxx::Exception> {
153
+ match ffi::new_native_index(options) {
154
+ Ok(inner) => Result::Ok(Self { inner }),
155
+ Err(err) => Err(err),
123
156
  }
157
+ }
158
+
159
+ /// Performs k-Approximate Nearest Neighbors (kANN) Search for closest vectors to the provided query.
160
+ ///
161
+ /// # Arguments
162
+ ///
163
+ /// * `query` - A slice containing the query vector data.
164
+ /// * `count` - The maximum number of neighbors to search for.
165
+ ///
166
+ /// # Returns
167
+ ///
168
+ /// A `Result` containing the matches found.
169
+ pub fn search<T: VectorType>(
170
+ self: &Index,
171
+ query: &[T],
172
+ count: usize,
173
+ ) -> Result<ffi::Matches, cxx::Exception> {
174
+ T::search(self, query, count)
175
+ }
176
+
177
+ /// Adds a vector with a specified key to the index.
178
+ ///
179
+ /// # Arguments
180
+ ///
181
+ /// * `key` - The key associated with the vector.
182
+ /// * `vector` - A slice containing the vector data.
183
+ pub fn add<T: VectorType>(self: &Index, key: u64, vector: &[T]) -> Result<(), cxx::Exception> {
184
+ T::add(self, key, vector)
185
+ }
186
+
187
+ /// Extracts one or more vectors matching specified key.
188
+ ///
189
+ /// # Arguments
190
+ ///
191
+ /// * `key` - The key associated with the vector.
192
+ /// * `vector` - A slice containing the vector data.
193
+ pub fn get<T: VectorType>(
194
+ self: &Index,
195
+ key: u64,
196
+ vector: &mut [T],
197
+ ) -> Result<usize, cxx::Exception> {
198
+ T::get(self, key, vector)
199
+ }
124
200
 
125
- pub fn search_i8(self: &Index, query: &[i8], count: usize) -> Result<Matches>;
126
- pub fn search_f16(self: &Index, query: &[u16], count: usize) -> Result<Matches>;
127
- pub fn search_f32(self: &Index, query: &[f32], count: usize) -> Result<Matches>;
128
- pub fn search_f64(self: &Index, query: &[f64], count: usize) -> Result<Matches>;
129
-
130
- /// Removes the vector associated with the given key from the index.
131
- ///
132
- /// # Arguments
133
- ///
134
- /// * `key` - The key of the vector to be removed.
135
- ///
136
- /// # Returns
137
- ///
138
- /// `true` if the vector is successfully removed, `false` otherwise.
139
- pub fn remove(self: &Index, key: u64) -> Result<bool>;
140
-
141
- /// Renames the vector under a certain key.
142
- ///
143
- /// # Arguments
144
- ///
145
- /// * `from` - The key of the vector to be renamed.
146
- /// * `to` - The new name.
147
- ///
148
- /// # Returns
149
- ///
150
- /// `true` if the vector is successfully renamed, `false` otherwise.
151
- pub fn rename(self: &Index, from: u64, to: u64) -> Result<bool>;
152
-
153
- /// Checks if the index contains a vector with a specified key.
154
- ///
155
- /// # Arguments
156
- ///
157
- /// * `key` - The key to be checked.
158
- ///
159
- /// # Returns
160
- ///
161
- /// `true` if the index contains the vector with the given key, `false` otherwise.
162
- pub fn contains(self: &Index, key: u64) -> bool;
163
-
164
- /// Saves the index to a specified file.
165
- ///
166
- /// # Arguments
167
- ///
168
- /// * `path` - The file path where the index will be saved.
169
- pub fn save(self: &Index, path: &str) -> Result<()>;
170
-
171
- /// Loads the index from a specified file.
172
- ///
173
- /// # Arguments
174
- ///
175
- /// * `path` - The file path from where the index will be loaded.
176
- pub fn load(self: &Index, path: &str) -> Result<()>;
177
-
178
- /// Creates a view of the index from a file without loading it into memory.
179
- ///
180
- /// # Arguments
181
- ///
182
- /// * `path` - The file path from where the view will be created.
183
- pub fn view(self: &Index, path: &str) -> Result<()>;
184
-
185
- /// Saves the index to a specified file.
186
- ///
187
- /// # Arguments
188
- ///
189
- /// * `path` - The file path where the index will be saved.
190
- pub fn save_to_buffer(self: &Index, buffer: &mut [u8]) -> Result<()>;
191
-
192
- /// Loads the index from a specified file.
193
- ///
194
- /// # Arguments
195
- ///
196
- /// * `path` - The file path from where the index will be loaded.
197
- pub fn load_from_buffer(self: &Index, buffer: &[u8]) -> Result<()>;
198
-
199
- /// Creates a view of the index from a file without loading it into memory.
200
- ///
201
- /// # Arguments
202
- ///
203
- /// * `path` - The file path from where the view will be created.
204
- pub fn view_from_buffer(self: &Index, buffer: &[u8]) -> Result<()>;
201
+ /// Reserves memory for a specified number of incoming vectors.
202
+ ///
203
+ /// # Arguments
204
+ ///
205
+ /// * `capacity` - The desired total capacity including the current size.
206
+ pub fn reserve(self: &Index, capacity: usize) -> Result<(), cxx::Exception> {
207
+ self.inner.reserve(capacity)
208
+ }
205
209
 
210
+ /// Retrieves the number of dimensions in the vectors indexed.
211
+ pub fn dimensions(self: &Index) -> usize {
212
+ self.inner.dimensions()
213
+ }
214
+
215
+ /// Retrieves the connectivity parameter that limits connections-per-node in the graph.
216
+ pub fn connectivity(self: &Index) -> usize {
217
+ self.inner.connectivity()
218
+ }
219
+
220
+ /// Retrieves the current number of vectors in the index.
221
+ pub fn size(self: &Index) -> usize {
222
+ self.inner.size()
223
+ }
224
+
225
+ /// Retrieves the total capacity of the index, including reserved space.
226
+ pub fn capacity(self: &Index) -> usize {
227
+ self.inner.capacity()
228
+ }
229
+
230
+ /// Reports expected file size after serialization.
231
+ pub fn serialized_length(self: &Index) -> usize {
232
+ self.inner.serialized_length()
233
+ }
234
+
235
+ /// Removes the vector associated with the given key from the index.
236
+ ///
237
+ /// # Arguments
238
+ ///
239
+ /// * `key` - The key of the vector to be removed.
240
+ ///
241
+ /// # Returns
242
+ ///
243
+ /// `true` if the vector is successfully removed, `false` otherwise.
244
+ pub fn remove(self: &Index, key: u64) -> Result<usize, cxx::Exception> {
245
+ self.inner.remove(key)
246
+ }
247
+
248
+ /// Renames the vector under a certain key.
249
+ ///
250
+ /// # Arguments
251
+ ///
252
+ /// * `from` - The key of the vector to be renamed.
253
+ /// * `to` - The new name.
254
+ ///
255
+ /// # Returns
256
+ ///
257
+ /// `true` if the vector is successfully renamed, `false` otherwise.
258
+ pub fn rename(self: &Index, from: u64, to: u64) -> Result<usize, cxx::Exception> {
259
+ self.inner.rename(from, to)
260
+ }
261
+
262
+ /// Checks if the index contains a vector with a specified key.
263
+ ///
264
+ /// # Arguments
265
+ ///
266
+ /// * `key` - The key to be checked.
267
+ ///
268
+ /// # Returns
269
+ ///
270
+ /// `true` if the index contains the vector with the given key, `false` otherwise.
271
+ pub fn contains(self: &Index, key: u64) -> bool {
272
+ self.inner.contains(key)
273
+ }
274
+
275
+ /// Count the count of vector with the same specified key.
276
+ ///
277
+ /// # Arguments
278
+ ///
279
+ /// * `key` - The key to be checked.
280
+ ///
281
+ /// # Returns
282
+ ///
283
+ /// Number of vectors found.
284
+ pub fn count(self: &Index, key: u64) -> usize {
285
+ self.inner.count(key)
286
+ }
287
+
288
+ /// Saves the index to a specified file.
289
+ ///
290
+ /// # Arguments
291
+ ///
292
+ /// * `path` - The file path where the index will be saved.
293
+ pub fn save(self: &Index, path: &str) -> Result<(), cxx::Exception> {
294
+ self.inner.save(path)
295
+ }
296
+
297
+ /// Loads the index from a specified file.
298
+ ///
299
+ /// # Arguments
300
+ ///
301
+ /// * `path` - The file path from where the index will be loaded.
302
+ pub fn load(self: &Index, path: &str) -> Result<(), cxx::Exception> {
303
+ self.inner.load(path)
304
+ }
305
+
306
+ /// Creates a view of the index from a file without loading it into memory.
307
+ ///
308
+ /// # Arguments
309
+ ///
310
+ /// * `path` - The file path from where the view will be created.
311
+ pub fn view(self: &Index, path: &str) -> Result<(), cxx::Exception> {
312
+ self.inner.view(path)
313
+ }
314
+
315
+ /// Saves the index to a specified file.
316
+ ///
317
+ /// # Arguments
318
+ ///
319
+ /// * `path` - The file path where the index will be saved.
320
+ pub fn save_to_buffer(self: &Index, buffer: &mut [u8]) -> Result<(), cxx::Exception> {
321
+ self.inner.save_to_buffer(buffer)
322
+ }
323
+
324
+ /// Loads the index from a specified file.
325
+ ///
326
+ /// # Arguments
327
+ ///
328
+ /// * `path` - The file path from where the index will be loaded.
329
+ pub fn load_from_buffer(self: &Index, buffer: &[u8]) -> Result<(), cxx::Exception> {
330
+ self.inner.load_from_buffer(buffer)
331
+ }
332
+
333
+ /// Creates a view of the index from a file without loading it into memory.
334
+ ///
335
+ /// # Arguments
336
+ ///
337
+ /// * `path` - The file path from where the view will be created.
338
+ pub fn view_from_buffer(self: &Index, buffer: &[u8]) -> Result<(), cxx::Exception> {
339
+ self.inner.view_from_buffer(buffer)
206
340
  }
207
341
  }
208
342
 
343
+ pub fn new_index(options: &ffi::IndexOptions) -> Result<Index, cxx::Exception> {
344
+ Index::new(options)
345
+ }
346
+
209
347
  #[cfg(test)]
210
348
  mod tests {
211
- use crate::ffi::new_index;
212
349
  use crate::ffi::IndexOptions;
213
350
  use crate::ffi::MetricKind;
214
- use crate::ffi::ScalarKind;
351
+
352
+ use crate::new_index;
353
+ use crate::Index;
215
354
 
216
355
  #[test]
217
356
  fn integration() {
218
- let mut options = IndexOptions {
219
- dimensions: 5,
220
- metric: MetricKind::IP,
221
- quantization: ScalarKind::F16,
222
- connectivity: 0,
223
- expansion_add: 0,
224
- expansion_search: 0,
225
- };
357
+ let mut options = IndexOptions::default();
358
+ options.dimensions = 5;
226
359
 
227
- let index = new_index(&options).unwrap();
360
+ let index = Index::new(&options).unwrap();
228
361
 
229
362
  assert!(index.reserve(10).is_ok());
230
363
  assert!(index.capacity() >= 10);
package/wasmer.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name="unum/usearch"
3
- version="2.4.0"
3
+ version="2.5.0"
4
4
  description="Smaller & Faster Single-File Vector Search Engine from Unum"
5
5
  license="Apache-2.0"
6
6
  readme="README.md"