usearch 0.18.8 → 0.19.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.
- package/Cargo.toml +1 -1
- package/README.md +187 -40
- package/c/lib.cpp +71 -51
- package/c/test.c +182 -0
- package/c/usearch.h +11 -5
- package/conanfile.py +1 -1
- package/cpp/bench.cpp +6 -6
- package/cpp/test.cpp +11 -9
- package/docs/compilation.md +1 -0
- package/include/usearch/index.hpp +290 -54
- package/include/usearch/index_punned_dense.hpp +125 -108
- package/include/usearch/index_punned_helpers.hpp +2 -12
- package/objc/USearchObjective.mm +4 -0
- package/objc/include/USearchObjective.h +5 -0
- package/package.json +2 -2
- package/python/lib.cpp +234 -199
- package/python/scripts/bench.ipynb +13 -2113
- package/python/scripts/bench.py +45 -29
- package/python/scripts/index_faiss.py +19 -19
- package/python/scripts/test.py +320 -61
- package/python/usearch/eval.py +100 -49
- package/python/usearch/index.py +245 -129
- package/python/usearch/io.py +25 -25
- package/python/usearch/join.py +2 -4
- package/python/usearch/numba.py +22 -18
- package/python/usearch/server.py +38 -26
- package/setup.py +56 -59
- package/simsimd/.vscode/settings.json +2 -1
- package/simsimd/CMakeLists.txt +1 -1
- package/simsimd/README.md +14 -2
- package/simsimd/bench.cxx +44 -27
- package/simsimd/include/simsimd/simsimd.h +2 -1
- package/swift/Test.swift +13 -9
- package/simsimd/include/simsimd/simsimd.hpp +0 -443
package/python/scripts/test.py
CHANGED
|
@@ -5,7 +5,16 @@ import numpy as np
|
|
|
5
5
|
from usearch.io import load_matrix, save_matrix
|
|
6
6
|
from usearch.eval import recall_members, random_vectors
|
|
7
7
|
|
|
8
|
-
from usearch.index import
|
|
8
|
+
from usearch.index import (
|
|
9
|
+
Index,
|
|
10
|
+
SparseIndex,
|
|
11
|
+
MetricKind,
|
|
12
|
+
ScalarKind,
|
|
13
|
+
MetricSignature,
|
|
14
|
+
CompiledMetric,
|
|
15
|
+
Matches,
|
|
16
|
+
MetricKindBitwise,
|
|
17
|
+
)
|
|
9
18
|
from usearch.index import _normalize_dtype
|
|
10
19
|
from usearch.index import (
|
|
11
20
|
DEFAULT_CONNECTIVITY,
|
|
@@ -15,10 +24,12 @@ from usearch.index import (
|
|
|
15
24
|
|
|
16
25
|
|
|
17
26
|
dimensions = [3, 97, 256]
|
|
18
|
-
batch_sizes = [1,
|
|
27
|
+
batch_sizes = [1, 77]
|
|
19
28
|
index_types = [
|
|
20
|
-
ScalarKind.F32,
|
|
21
|
-
ScalarKind.
|
|
29
|
+
ScalarKind.F32,
|
|
30
|
+
ScalarKind.F64,
|
|
31
|
+
ScalarKind.F16,
|
|
32
|
+
ScalarKind.F8,
|
|
22
33
|
]
|
|
23
34
|
numpy_types = [np.float32, np.float64, np.float16]
|
|
24
35
|
|
|
@@ -35,39 +46,40 @@ hash_metrics = [
|
|
|
35
46
|
]
|
|
36
47
|
|
|
37
48
|
|
|
38
|
-
@pytest.mark.parametrize(
|
|
39
|
-
@pytest.mark.parametrize(
|
|
49
|
+
@pytest.mark.parametrize("rows", batch_sizes)
|
|
50
|
+
@pytest.mark.parametrize("cols", dimensions)
|
|
40
51
|
def test_serializing_fbin_matrix(rows: int, cols: int):
|
|
41
|
-
|
|
42
52
|
original = np.random.rand(rows, cols).astype(np.float32)
|
|
43
|
-
save_matrix(original,
|
|
44
|
-
reconstructed = load_matrix(
|
|
53
|
+
save_matrix(original, "tmp.fbin")
|
|
54
|
+
reconstructed = load_matrix("tmp.fbin")
|
|
45
55
|
assert np.allclose(original, reconstructed)
|
|
46
|
-
os.remove(
|
|
56
|
+
os.remove("tmp.fbin")
|
|
47
57
|
|
|
48
58
|
|
|
49
|
-
@pytest.mark.parametrize(
|
|
50
|
-
@pytest.mark.parametrize(
|
|
59
|
+
@pytest.mark.parametrize("rows", batch_sizes)
|
|
60
|
+
@pytest.mark.parametrize("cols", dimensions)
|
|
51
61
|
def test_serializing_ibin_matrix(rows: int, cols: int):
|
|
52
|
-
|
|
53
|
-
original
|
|
54
|
-
|
|
55
|
-
reconstructed = load_matrix('tmp.ibin')
|
|
62
|
+
original = np.random.randint(0, rows + 1, size=(rows, cols)).astype(np.int32)
|
|
63
|
+
save_matrix(original, "tmp.ibin")
|
|
64
|
+
reconstructed = load_matrix("tmp.ibin")
|
|
56
65
|
assert np.allclose(original, reconstructed)
|
|
57
|
-
os.remove(
|
|
66
|
+
os.remove("tmp.ibin")
|
|
58
67
|
|
|
59
68
|
|
|
60
|
-
@pytest.mark.parametrize(
|
|
61
|
-
@pytest.mark.parametrize(
|
|
62
|
-
@pytest.mark.parametrize(
|
|
63
|
-
@pytest.mark.parametrize(
|
|
64
|
-
@pytest.mark.parametrize(
|
|
65
|
-
@pytest.mark.parametrize(
|
|
69
|
+
@pytest.mark.parametrize("ndim", dimensions)
|
|
70
|
+
@pytest.mark.parametrize("metric", continuous_metrics)
|
|
71
|
+
@pytest.mark.parametrize("index_type", index_types)
|
|
72
|
+
@pytest.mark.parametrize("numpy_type", numpy_types)
|
|
73
|
+
@pytest.mark.parametrize("connectivity", connectivity_options)
|
|
74
|
+
@pytest.mark.parametrize("jit", jit_options)
|
|
66
75
|
def test_index(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
ndim: int,
|
|
77
|
+
metric: MetricKind,
|
|
78
|
+
index_type: ScalarKind,
|
|
79
|
+
numpy_type: str,
|
|
80
|
+
connectivity: int,
|
|
81
|
+
jit: bool,
|
|
82
|
+
):
|
|
71
83
|
index = Index(
|
|
72
84
|
metric=metric,
|
|
73
85
|
ndim=ndim,
|
|
@@ -89,6 +101,7 @@ def test_index(
|
|
|
89
101
|
assert 43 not in index
|
|
90
102
|
assert index[42] is not None
|
|
91
103
|
assert index[43] is None
|
|
104
|
+
assert len(index[42]) == ndim
|
|
92
105
|
if numpy_type != np.byte:
|
|
93
106
|
assert np.allclose(index[42], vector, atol=0.1)
|
|
94
107
|
|
|
@@ -100,23 +113,34 @@ def test_index(
|
|
|
100
113
|
assert matches[0] == 42
|
|
101
114
|
assert distances[0] == pytest.approx(0, abs=1e-3)
|
|
102
115
|
|
|
103
|
-
index.save(
|
|
116
|
+
index.save("tmp.usearch")
|
|
104
117
|
index.clear()
|
|
105
118
|
assert len(index) == 0
|
|
106
119
|
|
|
107
|
-
index.load(
|
|
120
|
+
index.load("tmp.usearch")
|
|
121
|
+
assert len(index) == 1
|
|
122
|
+
assert len(index[42]) == ndim
|
|
123
|
+
|
|
124
|
+
index = Index.restore("tmp.usearch")
|
|
108
125
|
assert len(index) == 1
|
|
126
|
+
assert len(index[42]) == ndim
|
|
109
127
|
|
|
128
|
+
# Cleanup
|
|
129
|
+
os.remove("tmp.usearch")
|
|
110
130
|
|
|
111
|
-
@pytest.mark.parametrize('ndim', dimensions)
|
|
112
|
-
@pytest.mark.parametrize('metric', continuous_metrics)
|
|
113
|
-
@pytest.mark.parametrize('batch_size', batch_sizes)
|
|
114
|
-
@pytest.mark.parametrize('index_type', index_types)
|
|
115
|
-
@pytest.mark.parametrize('numpy_type', numpy_types)
|
|
116
|
-
def test_index_batch(
|
|
117
|
-
ndim: int, metric: MetricKind,
|
|
118
|
-
batch_size: int, index_type: str, numpy_type: str):
|
|
119
131
|
|
|
132
|
+
@pytest.mark.parametrize("ndim", dimensions)
|
|
133
|
+
@pytest.mark.parametrize("metric", continuous_metrics)
|
|
134
|
+
@pytest.mark.parametrize("batch_size", batch_sizes)
|
|
135
|
+
@pytest.mark.parametrize("index_type", index_types)
|
|
136
|
+
@pytest.mark.parametrize("numpy_type", numpy_types)
|
|
137
|
+
def test_index_batch(
|
|
138
|
+
ndim: int,
|
|
139
|
+
metric: MetricKind,
|
|
140
|
+
batch_size: int,
|
|
141
|
+
index_type: ScalarKind,
|
|
142
|
+
numpy_type: str,
|
|
143
|
+
):
|
|
120
144
|
index = Index(ndim=ndim, metric=metric, dtype=index_type)
|
|
121
145
|
|
|
122
146
|
labels = np.arange(batch_size)
|
|
@@ -124,22 +148,62 @@ def test_index_batch(
|
|
|
124
148
|
|
|
125
149
|
index.add(labels, vectors)
|
|
126
150
|
assert len(index) == batch_size
|
|
127
|
-
assert np.allclose(index.get_vectors(
|
|
128
|
-
labels).astype(numpy_type), vectors, atol=0.1)
|
|
151
|
+
assert np.allclose(index.get_vectors(labels).astype(numpy_type), vectors, atol=0.1)
|
|
129
152
|
|
|
130
153
|
matches: Matches = index.search(vectors, 10)
|
|
131
154
|
assert matches.labels.shape[0] == matches.distances.shape[0]
|
|
132
155
|
assert matches.counts.shape[0] == batch_size
|
|
133
156
|
assert np.all(np.sort(index.labels) == np.sort(labels))
|
|
134
157
|
|
|
135
|
-
|
|
136
|
-
|
|
158
|
+
index.save("tmp.usearch")
|
|
159
|
+
index.clear()
|
|
160
|
+
assert len(index) == 0
|
|
137
161
|
|
|
162
|
+
index.load("tmp.usearch")
|
|
163
|
+
assert len(index) == batch_size
|
|
164
|
+
assert len(index[0]) == ndim
|
|
138
165
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
166
|
+
index = Index.restore("tmp.usearch")
|
|
167
|
+
assert len(index) == batch_size
|
|
168
|
+
assert len(index[0]) == ndim
|
|
169
|
+
|
|
170
|
+
# Cleanup
|
|
171
|
+
os.remove("tmp.usearch")
|
|
142
172
|
|
|
173
|
+
|
|
174
|
+
@pytest.mark.parametrize("metric", [MetricKind.L2sq])
|
|
175
|
+
@pytest.mark.parametrize("batch_size", batch_sizes)
|
|
176
|
+
@pytest.mark.parametrize("index_type", index_types)
|
|
177
|
+
@pytest.mark.parametrize("numpy_type", numpy_types)
|
|
178
|
+
def test_exact_recall(
|
|
179
|
+
metric: MetricKind, batch_size: int, index_type: ScalarKind, numpy_type: str
|
|
180
|
+
):
|
|
181
|
+
ndim: int = batch_size
|
|
182
|
+
index = Index(ndim=ndim, metric=metric, dtype=index_type)
|
|
183
|
+
|
|
184
|
+
vectors = np.zeros((batch_size, ndim), dtype=numpy_type)
|
|
185
|
+
for i in range(batch_size):
|
|
186
|
+
vectors[i, i] = 1
|
|
187
|
+
labels = np.arange(batch_size)
|
|
188
|
+
index.add(labels, vectors)
|
|
189
|
+
assert len(index) == batch_size
|
|
190
|
+
|
|
191
|
+
# Search one at a time
|
|
192
|
+
for i in range(batch_size):
|
|
193
|
+
matches: Matches = index.search(vectors[i], 10, exact=True)
|
|
194
|
+
found_labels = matches.labels
|
|
195
|
+
assert found_labels[0] == i
|
|
196
|
+
|
|
197
|
+
# Search the whole batch
|
|
198
|
+
matches: Matches = index.search(vectors, 10, exact=True)
|
|
199
|
+
found_labels = matches.labels
|
|
200
|
+
for i in range(batch_size):
|
|
201
|
+
assert found_labels[i, 0] == i
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@pytest.mark.parametrize("ndim", dimensions)
|
|
205
|
+
@pytest.mark.parametrize("batch_size", batch_sizes)
|
|
206
|
+
def test_index_numba(ndim: int, batch_size: int):
|
|
143
207
|
try:
|
|
144
208
|
from numba import cfunc, types, carray
|
|
145
209
|
except ImportError:
|
|
@@ -147,11 +211,24 @@ def test_index_numba(ndim: int, batch_size: int):
|
|
|
147
211
|
|
|
148
212
|
# Showcases how to use Numba to JIT-compile similarity measures for USearch.
|
|
149
213
|
# https://numba.readthedocs.io/en/stable/reference/jit-compilation.html#c-callbacks
|
|
150
|
-
|
|
151
|
-
types.
|
|
214
|
+
signature_two_args = types.float32(
|
|
215
|
+
types.CPointer(types.float32),
|
|
216
|
+
types.CPointer(types.float32),
|
|
217
|
+
)
|
|
218
|
+
signature_three_args = types.float32(
|
|
219
|
+
types.CPointer(types.float32),
|
|
220
|
+
types.CPointer(types.float32),
|
|
221
|
+
types.uint64,
|
|
222
|
+
)
|
|
223
|
+
signature_four_args = types.float32(
|
|
224
|
+
types.CPointer(types.float32),
|
|
225
|
+
types.uint64,
|
|
226
|
+
types.CPointer(types.float32),
|
|
227
|
+
types.uint64,
|
|
228
|
+
)
|
|
152
229
|
|
|
153
|
-
@cfunc(
|
|
154
|
-
def
|
|
230
|
+
@cfunc(signature_two_args)
|
|
231
|
+
def python_inner_product_two_args(a, b):
|
|
155
232
|
a_array = carray(a, ndim)
|
|
156
233
|
b_array = carray(b, ndim)
|
|
157
234
|
c = 0.0
|
|
@@ -159,7 +236,191 @@ def test_index_numba(ndim: int, batch_size: int):
|
|
|
159
236
|
c += a_array[i] * b_array[i]
|
|
160
237
|
return 1 - c
|
|
161
238
|
|
|
162
|
-
|
|
239
|
+
@cfunc(signature_three_args)
|
|
240
|
+
def python_inner_product_three_args(a, b, ndim):
|
|
241
|
+
a_array = carray(a, ndim)
|
|
242
|
+
b_array = carray(b, ndim)
|
|
243
|
+
c = 0.0
|
|
244
|
+
for i in range(ndim):
|
|
245
|
+
c += a_array[i] * b_array[i]
|
|
246
|
+
return 1 - c
|
|
247
|
+
|
|
248
|
+
@cfunc(signature_four_args)
|
|
249
|
+
def python_inner_product_four_args(a, a_ndim, b, b_ndim):
|
|
250
|
+
a_array = carray(a, a_ndim)
|
|
251
|
+
b_array = carray(b, b_ndim)
|
|
252
|
+
c = 0.0
|
|
253
|
+
for i in range(a_ndim):
|
|
254
|
+
c += a_array[i] * b_array[i]
|
|
255
|
+
return 1 - c
|
|
256
|
+
|
|
257
|
+
functions = [
|
|
258
|
+
python_inner_product_two_args,
|
|
259
|
+
python_inner_product_three_args,
|
|
260
|
+
python_inner_product_four_args,
|
|
261
|
+
]
|
|
262
|
+
signatures = [
|
|
263
|
+
MetricSignature.ArrayArray,
|
|
264
|
+
MetricSignature.ArrayArraySize,
|
|
265
|
+
MetricSignature.ArraySizeArraySize,
|
|
266
|
+
]
|
|
267
|
+
for function, signature in zip(functions, signatures):
|
|
268
|
+
metric = CompiledMetric(
|
|
269
|
+
pointer=function.address,
|
|
270
|
+
kind=MetricKind.IP,
|
|
271
|
+
signature=signature,
|
|
272
|
+
)
|
|
273
|
+
index = Index(ndim=ndim, metric=metric)
|
|
274
|
+
|
|
275
|
+
labels = np.arange(batch_size)
|
|
276
|
+
vectors = random_vectors(count=batch_size, ndim=ndim)
|
|
277
|
+
|
|
278
|
+
index.add(labels, vectors)
|
|
279
|
+
matches, distances, count = index.search(vectors, 10)
|
|
280
|
+
assert matches.shape[0] == distances.shape[0]
|
|
281
|
+
assert count.shape[0] == batch_size
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@pytest.mark.parametrize("ndim", dimensions[-1:])
|
|
285
|
+
@pytest.mark.parametrize("batch_size", batch_sizes[-1:])
|
|
286
|
+
def test_index_cppyy(ndim: int, batch_size: int):
|
|
287
|
+
try:
|
|
288
|
+
import cppyy
|
|
289
|
+
import cppyy.ll
|
|
290
|
+
except ImportError:
|
|
291
|
+
return
|
|
292
|
+
|
|
293
|
+
cppyy.cppdef(
|
|
294
|
+
"""
|
|
295
|
+
float inner_product_two_args(float *a, float *b) {
|
|
296
|
+
float result = 0;
|
|
297
|
+
#pragma unroll
|
|
298
|
+
for (size_t i = 0; i != ndim; ++i)
|
|
299
|
+
result += a[i] * b[i];
|
|
300
|
+
return 1 - result;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
float inner_product_three_args(float *a, float *b, size_t n) {
|
|
304
|
+
float result = 0;
|
|
305
|
+
for (size_t i = 0; i != n; ++i)
|
|
306
|
+
result += a[i] * b[i];
|
|
307
|
+
return 1 - result;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
float inner_product_four_args(float *a, size_t an, float *b, size_t) {
|
|
311
|
+
float result = 0;
|
|
312
|
+
for (size_t i = 0; i != an; ++i)
|
|
313
|
+
result += a[i] * b[i];
|
|
314
|
+
return 1 - result;
|
|
315
|
+
}
|
|
316
|
+
""".replace(
|
|
317
|
+
"ndim", str(ndim)
|
|
318
|
+
)
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
functions = [
|
|
322
|
+
cppyy.gbl.inner_product_two_args,
|
|
323
|
+
cppyy.gbl.inner_product_three_args,
|
|
324
|
+
cppyy.gbl.inner_product_four_args,
|
|
325
|
+
]
|
|
326
|
+
signatures = [
|
|
327
|
+
MetricSignature.ArrayArray,
|
|
328
|
+
MetricSignature.ArrayArraySize,
|
|
329
|
+
MetricSignature.ArraySizeArraySize,
|
|
330
|
+
]
|
|
331
|
+
for function, signature in zip(functions, signatures):
|
|
332
|
+
metric = CompiledMetric(
|
|
333
|
+
pointer=cppyy.ll.addressof(function),
|
|
334
|
+
kind=MetricKind.IP,
|
|
335
|
+
signature=signature,
|
|
336
|
+
)
|
|
337
|
+
index = Index(ndim=ndim, metric=metric)
|
|
338
|
+
|
|
339
|
+
labels = np.arange(batch_size)
|
|
340
|
+
vectors = random_vectors(count=batch_size, ndim=ndim)
|
|
341
|
+
|
|
342
|
+
index.add(labels, vectors)
|
|
343
|
+
matches, distances, count = index.search(vectors, 10)
|
|
344
|
+
assert matches.shape[0] == distances.shape[0]
|
|
345
|
+
assert count.shape[0] == batch_size
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
@pytest.mark.parametrize("ndim", [8])
|
|
349
|
+
@pytest.mark.parametrize("batch_size", batch_sizes)
|
|
350
|
+
def test_index_peachpy(ndim: int, batch_size: int):
|
|
351
|
+
try:
|
|
352
|
+
from peachpy import (
|
|
353
|
+
Argument,
|
|
354
|
+
ptr,
|
|
355
|
+
float_,
|
|
356
|
+
const_float_,
|
|
357
|
+
)
|
|
358
|
+
from peachpy.x86_64 import (
|
|
359
|
+
abi,
|
|
360
|
+
Function,
|
|
361
|
+
uarch,
|
|
362
|
+
isa,
|
|
363
|
+
GeneralPurposeRegister64,
|
|
364
|
+
LOAD,
|
|
365
|
+
YMMRegister,
|
|
366
|
+
VSUBPS,
|
|
367
|
+
VADDPS,
|
|
368
|
+
VHADDPS,
|
|
369
|
+
VMOVUPS,
|
|
370
|
+
VFMADD231PS,
|
|
371
|
+
VPERM2F128,
|
|
372
|
+
VXORPS,
|
|
373
|
+
RETURN,
|
|
374
|
+
)
|
|
375
|
+
except ImportError:
|
|
376
|
+
return
|
|
377
|
+
|
|
378
|
+
a = Argument(ptr(const_float_), name="a")
|
|
379
|
+
b = Argument(ptr(const_float_), name="b")
|
|
380
|
+
|
|
381
|
+
with Function(
|
|
382
|
+
"InnerProduct", (a, b), float_, target=uarch.default + isa.avx + isa.avx2
|
|
383
|
+
) as asm_function:
|
|
384
|
+
# Request two 64-bit general-purpose registers for addresses
|
|
385
|
+
reg_a, reg_b = GeneralPurposeRegister64(), GeneralPurposeRegister64()
|
|
386
|
+
LOAD.ARGUMENT(reg_a, a)
|
|
387
|
+
LOAD.ARGUMENT(reg_b, b)
|
|
388
|
+
|
|
389
|
+
# Load the vectors
|
|
390
|
+
ymm_a = YMMRegister()
|
|
391
|
+
ymm_b = YMMRegister()
|
|
392
|
+
VMOVUPS(ymm_a, [reg_a])
|
|
393
|
+
VMOVUPS(ymm_b, [reg_b])
|
|
394
|
+
|
|
395
|
+
# Prepare the accumulator
|
|
396
|
+
ymm_c = YMMRegister()
|
|
397
|
+
ymm_one = YMMRegister()
|
|
398
|
+
VXORPS(ymm_c, ymm_c, ymm_c)
|
|
399
|
+
VXORPS(ymm_one, ymm_one, ymm_one)
|
|
400
|
+
|
|
401
|
+
# Accumulate A and B product into C
|
|
402
|
+
VFMADD231PS(ymm_c, ymm_a, ymm_b)
|
|
403
|
+
|
|
404
|
+
# Reduce the contents of a YMM register
|
|
405
|
+
ymm_c_permuted = YMMRegister()
|
|
406
|
+
VPERM2F128(ymm_c_permuted, ymm_c, ymm_c, 1)
|
|
407
|
+
VADDPS(ymm_c, ymm_c, ymm_c_permuted)
|
|
408
|
+
VHADDPS(ymm_c, ymm_c, ymm_c)
|
|
409
|
+
VHADDPS(ymm_c, ymm_c, ymm_c)
|
|
410
|
+
|
|
411
|
+
# Negate the values, to go from "similarity" to "distance"
|
|
412
|
+
VSUBPS(ymm_c, ymm_one, ymm_c)
|
|
413
|
+
|
|
414
|
+
# A common convention is to return floats in XMM registers
|
|
415
|
+
RETURN(ymm_c.as_xmm)
|
|
416
|
+
|
|
417
|
+
python_function = asm_function.finalize(abi.detect()).encode().load()
|
|
418
|
+
metric = CompiledMetric(
|
|
419
|
+
pointer=python_function.loader.code_address,
|
|
420
|
+
kind=MetricKind.IP,
|
|
421
|
+
signature=MetricSignature.ArrayArray,
|
|
422
|
+
)
|
|
423
|
+
index = Index(ndim=ndim, metric=metric)
|
|
163
424
|
|
|
164
425
|
labels = np.arange(batch_size)
|
|
165
426
|
vectors = random_vectors(count=batch_size, ndim=ndim)
|
|
@@ -169,15 +430,14 @@ def test_index_numba(ndim: int, batch_size: int):
|
|
|
169
430
|
assert matches.shape[0] == distances.shape[0]
|
|
170
431
|
assert count.shape[0] == batch_size
|
|
171
432
|
|
|
172
|
-
assert recall_members(index, exact=True) == 1
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
@pytest.mark.parametrize('bits', dimensions)
|
|
176
|
-
@pytest.mark.parametrize('metric', hash_metrics)
|
|
177
|
-
@pytest.mark.parametrize('connectivity', connectivity_options)
|
|
178
|
-
@pytest.mark.parametrize('batch_size', batch_sizes)
|
|
179
|
-
def test_bitwise_index(bits: int, metric: MetricKind, connectivity: int, batch_size: int):
|
|
180
433
|
|
|
434
|
+
@pytest.mark.parametrize("bits", dimensions)
|
|
435
|
+
@pytest.mark.parametrize("metric", hash_metrics)
|
|
436
|
+
@pytest.mark.parametrize("connectivity", connectivity_options)
|
|
437
|
+
@pytest.mark.parametrize("batch_size", batch_sizes)
|
|
438
|
+
def test_bitwise_index(
|
|
439
|
+
bits: int, metric: MetricKind, connectivity: int, batch_size: int
|
|
440
|
+
):
|
|
181
441
|
index = Index(ndim=bits, metric=metric, connectivity=connectivity)
|
|
182
442
|
|
|
183
443
|
labels = np.arange(batch_size)
|
|
@@ -187,14 +447,13 @@ def test_bitwise_index(bits: int, metric: MetricKind, connectivity: int, batch_s
|
|
|
187
447
|
index.add(labels, bit_vectors)
|
|
188
448
|
assert np.allclose(index.get_vectors(labels), byte_vectors, atol=0.1)
|
|
189
449
|
assert np.all(index.get_vectors(labels, ScalarKind.B1) == bit_vectors)
|
|
190
|
-
|
|
450
|
+
|
|
191
451
|
index.search(bit_vectors, 10)
|
|
192
452
|
|
|
193
453
|
|
|
194
|
-
@pytest.mark.parametrize(
|
|
195
|
-
@pytest.mark.skipif(os.name ==
|
|
454
|
+
@pytest.mark.parametrize("connectivity", connectivity_options)
|
|
455
|
+
@pytest.mark.skipif(os.name == "nt", reason="Spurious behaviour on windows")
|
|
196
456
|
def test_sets_index(connectivity: int):
|
|
197
|
-
|
|
198
457
|
index = SparseIndex(connectivity=connectivity)
|
|
199
458
|
index.add(10, np.array([10, 12, 15], dtype=np.uint32))
|
|
200
459
|
index.add(11, np.array([11, 12, 15, 16], dtype=np.uint32))
|