usearch 2.3.2 → 2.4.1

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.
@@ -476,6 +476,7 @@ class Index:
476
476
  multi: bool = False,
477
477
  path: Optional[os.PathLike] = None,
478
478
  view: bool = False,
479
+ enable_key_lookups: bool = True,
479
480
  ) -> None:
480
481
  """Construct the index and compiles the functions, if requested (expensive).
481
482
 
@@ -565,6 +566,7 @@ class Index:
565
566
  expansion_add=expansion_add,
566
567
  expansion_search=expansion_search,
567
568
  multi=multi,
569
+ enable_key_lookups=enable_key_lookups,
568
570
  metric_kind=self._metric_kind,
569
571
  metric_pointer=self._metric_pointer,
570
572
  metric_signature=self._metric_signature,
@@ -986,6 +988,8 @@ class Index:
986
988
 
987
989
  def reset(self):
988
990
  """Erases all members from index, closing files, and returning RAM to OS."""
991
+ if not hasattr(self, "_compiled"):
992
+ return
989
993
  self._compiled.reset()
990
994
 
991
995
  def __del__(self):
@@ -1175,23 +1179,28 @@ class Index:
1175
1179
 
1176
1180
  @property
1177
1181
  def specs(self) -> Dict[str, Union[str, int, bool]]:
1182
+ if not hasattr(self, "_compiled"):
1183
+ return "usearch.Index(failed)"
1178
1184
  return {
1179
- "Class": "usearch.Index",
1180
- "Connectivity": self.connectivity,
1181
- "Size": self.size,
1182
- "Dimensions": self.ndim,
1183
- "Expansion@Add": self.expansion_add,
1184
- "Expansion@Search": self.expansion_search,
1185
- "OpenMP": USES_OPENMP,
1186
- "SimSIMD": USES_SIMSIMD,
1187
- "NativeF16": USES_NATIVE_F16,
1188
- "JIT": self.jit,
1189
- "DType": self.dtype,
1190
- "Path": self.path,
1185
+ "type": "usearch.Index",
1186
+ "ndim": self.ndim,
1187
+ "connectivity": self.connectivity,
1188
+ "expansion_add": self.expansion_add,
1189
+ "expansion_search": self.expansion_search,
1190
+ "size": self.size,
1191
+ "jit": self.jit,
1192
+ "hardware_acceleration": self.hardware_acceleration,
1193
+ "dtype": self.dtype,
1194
+ "path": self.path,
1195
+ "compiled_with_openmp": USES_OPENMP,
1196
+ "compiled_with_simsimd": USES_SIMSIMD,
1197
+ "compiled_with_native_f16": USES_NATIVE_F16,
1191
1198
  }
1192
1199
 
1193
1200
  def __repr__(self) -> str:
1194
- f = "usearch.Index({} x {}, {}, connectivity: {}, expansion: {} & {}, {} vectors in {} levels)"
1201
+ if not hasattr(self, "_compiled"):
1202
+ return "usearch.Index(failed)"
1203
+ f = "usearch.Index({} x {}, {}, connectivity: {}, expansion: {} & {}, {:,} vectors in {} levels, {} hardware acceleration)"
1195
1204
  return f.format(
1196
1205
  self.dtype,
1197
1206
  self.ndim,
@@ -1201,9 +1210,12 @@ class Index:
1201
1210
  self.expansion_search,
1202
1211
  len(self),
1203
1212
  self.nlevels,
1213
+ self.hardware_acceleration,
1204
1214
  )
1205
1215
 
1206
1216
  def _repr_pretty_(self, printer, cycle) -> str:
1217
+ if not hasattr(self, "_compiled"):
1218
+ return "usearch.Index(failed)"
1207
1219
  level_stats = [
1208
1220
  f"--- {i}. {self.level_stats(i).nodes:,} nodes" for i in range(self.nlevels)
1209
1221
  ]
package/rust/lib.rs CHANGED
@@ -81,7 +81,18 @@ pub mod ffi {
81
81
  ///
82
82
  /// * `key` - The key associated with the vector.
83
83
  /// * `vector` - A slice containing the vector data.
84
- pub fn add(self: &Index, key: u64, vector: &[f32]) -> Result<()>;
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
+ }
95
+ }
85
96
 
86
97
  pub fn add_i8(self: &Index, key: u64, vector: &[i8]) -> Result<()>;
87
98
  pub fn add_f16(self: &Index, key: u64, vector: &[u16]) -> Result<()>;
@@ -98,7 +109,18 @@ pub mod ffi {
98
109
  /// # Returns
99
110
  ///
100
111
  /// A `Result` containing the matches found.
101
- pub fn search(self: &Index, query: &[f32], count: usize) -> Result<Matches>;
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
+ }
123
+ }
102
124
 
103
125
  pub fn search_i8(self: &Index, query: &[i8], count: usize) -> Result<Matches>;
104
126
  pub fn search_f16(self: &Index, query: &[u16], count: usize) -> Result<Matches>;
package/wasmer.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name="unum/usearch"
3
- version="2.3.2"
3
+ version="2.4.1"
4
4
  description="Smaller & Faster Single-File Vector Search Engine from Unum"
5
5
  license="Apache-2.0"
6
6
  readme="README.md"
@@ -1,306 +0,0 @@
1
- using Cloud.Unum.USearch;
2
-
3
- namespace Cloud.Unum.USearch.Tests;
4
-
5
- public class InitTests
6
- {
7
- // Epsilon for real values comparisons
8
- private const float Epsilon = 0.000001f;
9
-
10
- [Fact]
11
- public void InitializedAndDisposedSucceeds()
12
- {
13
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3))
14
- {
15
- Assert.NotNull(index);
16
- Assert.Equal((uint)3, index.Dimensions());
17
- }
18
- }
19
-
20
- [Fact]
21
- public void AddDoubleVectorSucceeds()
22
- {
23
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float64, 3))
24
- {
25
- index.Add(1, new double[] { 1.0, 2.0, 3.0 });
26
- Assert.True(index.Contains(1));
27
- Assert.False(index.Contains(2));
28
- Assert.Equal((uint)1, index.Size());
29
- }
30
- }
31
-
32
- [Fact]
33
- public void AddDoubleVectorsUnderSameKeySucceeds()
34
- {
35
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float64, 3, multi: true))
36
- {
37
- index.Add(1, new double[] { 1.0, 2.0, 3.0 });
38
- index.Add(2, new double[] { 1.0, 2.0, 3.0 });
39
- index.Add(3, new double[] { 1.0, 2.0, 3.0 });
40
- index.Add(1, new double[] { 1.0, 2.0, 3.0 });
41
- index.Add(2, new double[] { 1.0, 2.0, 3.0 });
42
- Assert.True(index.Contains(1));
43
- Assert.True(index.Contains(2));
44
- Assert.True(index.Contains(3));
45
- Assert.Equal((uint)5, index.Size());
46
- }
47
- }
48
-
49
- [Fact]
50
- public void ItCanGetMultipleFloatVectorsForSingleKey()
51
- {
52
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3, multi: true))
53
- {
54
- var inputVectors = new float[][] {
55
- new float[] { 1.0f, 6.0f, 11.0f },
56
- new float[] { 2.0f, 7.0f, 12.0f },
57
- new float[] { 3.0f, 8.0f, 13.0f },
58
- new float[] { 5.0f, 10.0f, 15.0f }
59
- };
60
- index.Add(
61
- new ulong[] { 2, 2, 2, 2 },
62
- inputVectors
63
- );
64
- Assert.True(index.Contains(2));
65
- Assert.Equal((uint)4, index.Size());
66
- Assert.Equal(4, index.Count(2));
67
-
68
- int foundVectorsCount = index.Get(2, 3, out float[][] retrievedVectors);
69
- Assert.Equal(3, foundVectorsCount);
70
- }
71
- }
72
-
73
- [Fact]
74
- public void ItCanGetMultipleDoubleVectorsForSingleKey()
75
- {
76
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float64, 3, multi: true))
77
- {
78
- var inputVectors = new double[][] {
79
- new double[] { 1.0, 6.0, 11.0 },
80
- new double[] { 2.0, 7.0, 12.0 },
81
- new double[] { 3.0, 8.0, 13.0 },
82
- new double[] { 5.0, 10.0, 15.0 }
83
- };
84
- index.Add(
85
- new ulong[] { 2, 2, 2, 2 },
86
- inputVectors
87
- );
88
- Assert.True(index.Contains(2));
89
- Assert.Equal((uint)4, index.Size());
90
- Assert.Equal(4, index.Count(2));
91
-
92
- int foundVectorsCount = index.Get(2, 3, out double[][] retrievedVectors);
93
- Assert.Equal(3, foundVectorsCount);
94
- }
95
- }
96
-
97
- [Fact]
98
- public void AddFloatVectorSucceeds()
99
- {
100
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3))
101
- {
102
- index.Add(1, new float[] { 1.0f, 2.0f, 3.0f });
103
- Assert.Equal((uint)1, index.Size());
104
- Assert.True(index.Contains(1));
105
- Assert.False(index.Contains(2));
106
- }
107
- }
108
-
109
- [Fact]
110
- public void AddFloatVectorsUnderSameKeySucceeds()
111
- {
112
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3, multi: true))
113
- {
114
- index.Add(1, new float[] { 1.0f, 2.0f, 3.0f });
115
- index.Add(2, new float[] { 1.0f, 2.0f, 3.0f });
116
- index.Add(3, new float[] { 1.0f, 2.0f, 3.0f });
117
- index.Add(1, new float[] { 1.0f, 2.0f, 3.0f });
118
- index.Add(2, new float[] { 1.0f, 2.0f, 3.0f });
119
- Assert.True(index.Contains(1));
120
- Assert.True(index.Contains(2));
121
- Assert.True(index.Contains(3));
122
- Assert.Equal((uint)5, index.Size());
123
- }
124
- }
125
-
126
- [Fact]
127
- public void AddGetFloatVectorSucceeds()
128
- {
129
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3))
130
- {
131
- var inputVector = new float[] { 1.0f, 2.0f, 3.0f };
132
- index.Add(1, inputVector);
133
- Assert.Equal((uint)1, index.Size());
134
- Assert.True(index.Contains(1));
135
- Assert.False(index.Contains(2));
136
- Assert.True(index.Get(1, out float[] retrievedVector) == 1);
137
- Assert.NotNull(retrievedVector);
138
- bool areEqual = inputVector
139
- .Zip(retrievedVector, (a, b) => (a, b))
140
- .All(pair => Math.Abs(pair.a - pair.b) <= Epsilon);
141
-
142
- Assert.True(areEqual);
143
- }
144
- }
145
-
146
- [Fact]
147
- public void AddGetDoubleVectorSucceeds()
148
- {
149
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3))
150
- {
151
- var inputVector = new double[] { 1.0, 2.0, 3.33 };
152
- index.Add(1, inputVector);
153
- Assert.Equal((uint)1, index.Size());
154
- Assert.True(index.Contains(1));
155
- Assert.False(index.Contains(2));
156
- Assert.True(index.Get(1, out double[] retrievedVector) == 1);
157
- Assert.NotNull(retrievedVector);
158
- bool areEqual = inputVector
159
- .Zip(retrievedVector, (a, b) => (a, b))
160
- .All(pair => Math.Abs(pair.a - pair.b) <= Epsilon);
161
-
162
- Assert.True(areEqual);
163
- }
164
- }
165
-
166
- [Fact]
167
- public void ItThrowsException_AddAddWithSameKeyAndGetVector()
168
- {
169
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3))
170
- {
171
- ulong key = 1;
172
- float[] vector1 = { 1.0f, 2.0f, 33.3f };
173
- index.Add(key, vector1);
174
- float[] vector2 = { 3.0f, 4.0f, 34.2f };
175
- Assert.Throws<USearchException>(() => index.Add(key, vector2));
176
- }
177
- }
178
-
179
- [Fact]
180
- public void TestAddRemoveAddAndGet()
181
- {
182
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 2))
183
- {
184
- ulong key = 1;
185
- float[] vector = { 1.0f, 2.0f };
186
-
187
- index.Add(key, vector);
188
- Assert.True(index.Contains(key));
189
- index.Remove(key);
190
- Assert.False(index.Contains(key));
191
- index.Add(key, vector);
192
- Assert.True(index.Contains(key));
193
- Assert.True(index.Get(key, out float[] retrievedVector) == 1);
194
- Assert.Equal(vector, retrievedVector);
195
- }
196
- }
197
-
198
- [Fact]
199
- public void TestAddRemoveAddWithSameKeyVectorAndGet()
200
- {
201
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 2))
202
- {
203
- ulong key = 1;
204
- float[] vector = { 1.0f, 2.0f };
205
-
206
- index.Add(key, vector);
207
- Assert.True(index.Contains(key));
208
- index.Remove(key);
209
- Assert.False(index.Contains(key));
210
- index.Add(key, vector);
211
- Assert.True(index.Contains(key));
212
- Assert.True(index.Get(key, out float[] retrievedVector) == 1);
213
- Assert.Equal(vector, retrievedVector);
214
- }
215
- }
216
-
217
- [Fact]
218
- public void TestGetKeyThatNotExist()
219
- {
220
- using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 2))
221
- {
222
- Assert.True(index.Get(1, out float[] retrievedVector) == 0);
223
- Assert.False(index.Contains(1));
224
- }
225
- }
226
-
227
- [Fact]
228
- public void Search_FindsExpectedResults()
229
- {
230
- using (var index = new USearchIndex(MetricKind.L2sq, ScalarKind.Float32, 2))
231
- {
232
- index.Add(
233
- Enumerable.Range(1, 5).Select(i => (ulong)i).ToArray(),
234
- new float[][]
235
- {
236
- new float[] { -0.5f, -0.6f },
237
- new float[] { -0.1f, -0.1f },
238
- new float[] { 0.2f, 0.2f },
239
- new float[] { 0.7f, 0.8f },
240
- new float[] { 0.9f, 1.0f }
241
- }
242
- );
243
- var queryVector = new float[] { 0.0f, 0.0f };
244
-
245
- var matches = index.Search(queryVector, 2, out ulong[] keys, out float[] distances);
246
-
247
- Assert.Equal(2, matches);
248
- Assert.Contains(2UL, keys);
249
- Assert.Contains(3UL, keys);
250
- Assert.Equal(0.02, distances[0], Epsilon);
251
- Assert.Equal(0.08, distances[1], Epsilon);
252
- }
253
- }
254
-
255
- [Fact]
256
- public void Search_AfterRemovingKey_FindsExpectedResults()
257
- {
258
- using (var index = new USearchIndex(MetricKind.L2sq, ScalarKind.Float32, 2))
259
- {
260
- index.Add(1, new float[] { 0.1f, 0.2f });
261
- index.Add(2, new float[] { 0.2f, 0.3f });
262
- index.Add(3, new float[] { 0.3f, 0.4f });
263
- index.Remove(2);
264
- var queryVector = new float[] { 0.2f, 0.3f };
265
-
266
- var matches = index.Search(queryVector, 2, out ulong[] keys, out float[] distances);
267
-
268
- Assert.Equal(2, matches);
269
- Assert.Contains(1UL, keys);
270
- Assert.Contains(3UL, keys);
271
- Assert.DoesNotContain(2UL, keys);
272
- }
273
- }
274
-
275
- [Fact]
276
- public void Search_WithVectorsLessThanResultsLimit()
277
- {
278
- using (var index = new USearchIndex(MetricKind.L2sq, ScalarKind.Float32, 2))
279
- {
280
- index.Add(1, new float[] { 0.9f, 0.9f });
281
- index.Add(2, new float[] { 0.8f, 0.8f });
282
- var queryVector = new float[] { 0.1f, 0.2f };
283
-
284
- var matches = index.Search(queryVector, 10, out ulong[] keys, out float[] distances);
285
-
286
- Assert.Equal(2, matches);
287
- Assert.Contains(1UL, keys);
288
- Assert.Contains(2UL, keys);
289
- }
290
- }
291
-
292
- [Fact]
293
- public void ItCanRenameKey()
294
- {
295
- using (var index = new USearchIndex(MetricKind.L2sq, ScalarKind.Float32, 2))
296
- {
297
- index.Add(1, new float[] { 0.9f, 0.9f });
298
- index.Add(2, new float[] { 0.8f, 0.8f });
299
- index.Rename(1, 3);
300
- Assert.False(index.Contains(1));
301
- Assert.True(index.Contains(2));
302
- Assert.True(index.Contains(3));
303
- }
304
- }
305
-
306
- }