usearch 0.18.8 → 0.19.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.
@@ -24,15 +24,13 @@ def bench_speed(
24
24
  jit: bool = False,
25
25
  train: bool = False,
26
26
  ) -> pd.DataFrame:
27
-
28
27
  # Build various indexes:
29
28
  indexes = []
30
29
  jit_options = [False]
31
30
  if jit:
32
31
  jit_options.append(jit)
33
- dtype_options = ['f32', 'f16', 'f8']
32
+ dtype_options = ["f32", "f16", "f8"]
34
33
  for jit, dtype in itertools.product(jit_options, dtype_options):
35
-
36
34
  index = Index(
37
35
  ndim=eval.ndim,
38
36
  jit=jit,
@@ -40,9 +38,9 @@ def bench_speed(
40
38
  expansion_add=expansion_add,
41
39
  expansion_search=expansion_search,
42
40
  connectivity=connectivity,
43
- path='USearch' + ['', '+JIT'][jit] + ':' + dtype,
41
+ path="USearch" + ["", "+JIT"][jit] + ":" + dtype,
44
42
  )
45
-
43
+
46
44
  # Skip the cases, where JIT-ing is impossible
47
45
  if jit and not index.jit:
48
46
  continue
@@ -51,41 +49,60 @@ def bench_speed(
51
49
  # Add FAISS indexes to the mix:
52
50
  try:
53
51
  from index_faiss import IndexFAISS, IndexQuantizedFAISS
54
- indexes.append(IndexFAISS(
55
- ndim=eval.ndim,
56
- expansion_add=expansion_add,
57
- expansion_search=expansion_search,
58
- connectivity=connectivity,
59
- path='FAISS:f32',
60
- ))
61
- if train:
62
- indexes.append(IndexQuantizedFAISS(
63
- train=eval.tasks[0].vectors,
52
+
53
+ indexes.append(
54
+ IndexFAISS(
55
+ ndim=eval.ndim,
64
56
  expansion_add=expansion_add,
65
57
  expansion_search=expansion_search,
66
58
  connectivity=connectivity,
67
- path='FAISS+IVFPQ:f32',
68
- ))
59
+ path="FAISS:f32",
60
+ )
61
+ )
62
+ if train:
63
+ indexes.append(
64
+ IndexQuantizedFAISS(
65
+ train=eval.tasks[0].vectors,
66
+ expansion_add=expansion_add,
67
+ expansion_search=expansion_search,
68
+ connectivity=connectivity,
69
+ path="FAISS+IVFPQ:f32",
70
+ )
71
+ )
69
72
  except (ImportError, ModuleNotFoundError):
70
73
  pass
71
74
 
72
75
  # Time to evaluate:
73
76
  results = [eval(index) for index in indexes]
74
- return pd.DataFrame({
75
- 'names': [i.path for i in indexes],
76
- 'add_per_second': [x['add_per_second'] for x in results],
77
- 'search_per_second': [x['search_per_second'] for x in results],
78
- 'recall_at_one': [x['recall_at_one'] for x in results],
79
- })
77
+ return pd.DataFrame(
78
+ {
79
+ "names": [i.path for i in indexes],
80
+ "add_per_second": [x["add_per_second"] for x in results],
81
+ "search_per_second": [x["search_per_second"] for x in results],
82
+ "recall_at_one": [x["recall_at_one"] for x in results],
83
+ }
84
+ )
80
85
 
81
86
 
82
87
  def bench_params(
83
88
  count: int = 1_000_000,
84
89
  connectivities: int = range(10, 20),
85
90
  dimensions: List[int] = [
86
- 2, 3, 4, 8, 16, 32, 96, 100,
87
- 256, 384, 512, 768, 1024, 1536],
88
-
91
+ 2,
92
+ 3,
93
+ 4,
94
+ 8,
95
+ 16,
96
+ 32,
97
+ 96,
98
+ 100,
99
+ 256,
100
+ 384,
101
+ 512,
102
+ 768,
103
+ 1024,
104
+ 1536,
105
+ ],
89
106
  expansion_add: int = DEFAULT_EXPANSION_ADD,
90
107
  expansion_search: int = DEFAULT_EXPANSION_SEARCH,
91
108
  ) -> pd.DataFrame:
@@ -97,7 +114,6 @@ def bench_params(
97
114
 
98
115
  results = []
99
116
  for connectivity, ndim in itertools.product(connectivities, dimensions):
100
-
101
117
  task = AddTask(
102
118
  labels=np.arange(count, dtype=Label),
103
119
  vectors=np.random.rand(count, ndim).astype(np.float32),
@@ -109,8 +125,8 @@ def bench_params(
109
125
  expansion_search=expansion_search,
110
126
  )
111
127
  result = asdict(task(index))
112
- result['ndim'] = dimensions
113
- result['connectivity'] = connectivity
128
+ result["ndim"] = dimensions
129
+ result["connectivity"] = connectivity
114
130
  results.append(result)
115
131
 
116
132
  # return self._execute_tasks(
@@ -13,7 +13,6 @@ from usearch.index import (
13
13
 
14
14
 
15
15
  class IndexFAISS:
16
-
17
16
  def __init__(
18
17
  self,
19
18
  index: IndexHNSWFlat = None,
@@ -22,9 +21,9 @@ class IndexFAISS:
22
21
  expansion_add: int = DEFAULT_EXPANSION_ADD,
23
22
  expansion_search: int = DEFAULT_EXPANSION_SEARCH,
24
23
  path: Optional[os.PathLike] = None,
25
- *args, **kwargs,
24
+ *args,
25
+ **kwargs,
26
26
  ):
27
-
28
27
  if index is None:
29
28
  index = IndexHNSWFlat(ndim, connectivity)
30
29
  index.hnsw.efConstruction = expansion_add
@@ -32,11 +31,11 @@ class IndexFAISS:
32
31
 
33
32
  self._faiss = index
34
33
  self._specs = {
35
- 'Class': 'usearch.IndexFAISS',
36
- 'Dimensions': ndim,
37
- 'Connectivity': connectivity,
38
- 'Expansion@Add': expansion_add,
39
- 'Expansion@Search': expansion_search,
34
+ "Class": "usearch.IndexFAISS",
35
+ "Dimensions": ndim,
36
+ "Connectivity": connectivity,
37
+ "Expansion@Add": expansion_add,
38
+ "Expansion@Search": expansion_search,
40
39
  }
41
40
 
42
41
  self.path = path
@@ -58,9 +57,11 @@ class IndexFAISS:
58
57
 
59
58
  @property
60
59
  def specs(self) -> dict:
61
- self._specs.update({
62
- 'Size': len(self),
63
- })
60
+ self._specs.update(
61
+ {
62
+ "Size": len(self),
63
+ }
64
+ )
64
65
  return self._specs
65
66
 
66
67
  def load(self, path: os.PathLike):
@@ -68,33 +69,32 @@ class IndexFAISS:
68
69
 
69
70
 
70
71
  class IndexQuantizedFAISS(IndexFAISS):
71
-
72
72
  def __init__(
73
73
  self,
74
74
  train: np.ndarray,
75
75
  connectivity: int = DEFAULT_CONNECTIVITY,
76
76
  expansion_add: int = DEFAULT_EXPANSION_ADD,
77
77
  expansion_search: int = DEFAULT_EXPANSION_SEARCH,
78
- *args, **kwargs,
78
+ *args,
79
+ **kwargs,
79
80
  ):
80
-
81
81
  ndim = train.shape[1]
82
82
  super().__init__(
83
83
  ndim=ndim,
84
84
  connectivity=connectivity,
85
85
  expansion_add=expansion_add,
86
86
  expansion_search=expansion_search,
87
- *args, **kwargs,
87
+ *args,
88
+ **kwargs,
88
89
  )
89
90
 
90
91
  nlist = 10000 # Number of inverted lists (number of partitions).
91
92
  nsegment = 16 # Number of segments for PQ (number of subquantizers).
92
- nbit = 8 # Number of bits to encode each segment.
93
+ nbit = 8 # Number of bits to encode each segment.
93
94
 
94
95
  self._original_faiss = self._faiss
95
- self._faiss = IndexIVFPQ(
96
- self._faiss, ndim, nlist, nsegment, nbit)
96
+ self._faiss = IndexIVFPQ(self._faiss, ndim, nlist, nsegment, nbit)
97
97
 
98
98
  self._faiss.train(train)
99
99
  self._faiss.nprobe = 10
100
- self._specs['Class'] = 'usearch.IndexQuantizedFAISS'
100
+ self._specs["Class"] = "usearch.IndexQuantizedFAISS"
@@ -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 Index, SparseIndex, MetricKind, ScalarKind, Matches, MetricKindBitwise
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,
@@ -17,8 +26,10 @@ from usearch.index import (
17
26
  dimensions = [3, 97, 256]
18
27
  batch_sizes = [1, 33]
19
28
  index_types = [
20
- ScalarKind.F32, ScalarKind.F64,
21
- ScalarKind.F16, ScalarKind.F8,
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('rows', batch_sizes)
39
- @pytest.mark.parametrize('cols', dimensions)
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, 'tmp.fbin')
44
- reconstructed = load_matrix('tmp.fbin')
53
+ save_matrix(original, "tmp.fbin")
54
+ reconstructed = load_matrix("tmp.fbin")
45
55
  assert np.allclose(original, reconstructed)
46
- os.remove('tmp.fbin')
56
+ os.remove("tmp.fbin")
47
57
 
48
58
 
49
- @pytest.mark.parametrize('rows', batch_sizes)
50
- @pytest.mark.parametrize('cols', dimensions)
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 = np.random.randint(0, rows+1, size=(rows, cols)).astype(np.int32)
54
- save_matrix(original, 'tmp.ibin')
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('tmp.ibin')
66
+ os.remove("tmp.ibin")
58
67
 
59
68
 
60
- @pytest.mark.parametrize('ndim', dimensions)
61
- @pytest.mark.parametrize('metric', continuous_metrics)
62
- @pytest.mark.parametrize('index_type', index_types)
63
- @pytest.mark.parametrize('numpy_type', numpy_types)
64
- @pytest.mark.parametrize('connectivity', connectivity_options)
65
- @pytest.mark.parametrize('jit', jit_options)
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
- ndim: int, metric: MetricKind,
68
- index_type: str, numpy_type: str,
69
- connectivity: int, jit: bool):
70
-
76
+ ndim: int,
77
+ metric: MetricKind,
78
+ index_type: str,
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,30 @@ 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('tmp.usearch')
116
+ index.save("tmp.usearch")
104
117
  index.clear()
105
118
  assert len(index) == 0
106
119
 
107
- index.load('tmp.usearch')
120
+ index.load("tmp.usearch")
108
121
  assert len(index) == 1
122
+ assert len(index[42]) == ndim
109
123
 
124
+ index = Index.restore("tmp.usearch")
125
+ assert len(index) == 1
126
+ assert len(index[42]) == ndim
110
127
 
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):
128
+ # Cleanup
129
+ os.remove("tmp.usearch")
119
130
 
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, metric: MetricKind, batch_size: int, index_type: str, numpy_type: str
139
+ ):
120
140
  index = Index(ndim=ndim, metric=metric, dtype=index_type)
121
141
 
122
142
  labels = np.arange(batch_size)
@@ -124,8 +144,7 @@ def test_index_batch(
124
144
 
125
145
  index.add(labels, vectors)
126
146
  assert len(index) == batch_size
127
- assert np.allclose(index.get_vectors(
128
- labels).astype(numpy_type), vectors, atol=0.1)
147
+ assert np.allclose(index.get_vectors(labels).astype(numpy_type), vectors, atol=0.1)
129
148
 
130
149
  matches: Matches = index.search(vectors, 10)
131
150
  assert matches.labels.shape[0] == matches.distances.shape[0]
@@ -136,10 +155,9 @@ def test_index_batch(
136
155
  assert recall_members(index, exact=True) == 1
137
156
 
138
157
 
139
- @pytest.mark.parametrize('ndim', dimensions)
140
- @pytest.mark.parametrize('batch_size', batch_sizes)
158
+ @pytest.mark.parametrize("ndim", dimensions)
159
+ @pytest.mark.parametrize("batch_size", batch_sizes)
141
160
  def test_index_numba(ndim: int, batch_size: int):
142
-
143
161
  try:
144
162
  from numba import cfunc, types, carray
145
163
  except ImportError:
@@ -147,11 +165,24 @@ def test_index_numba(ndim: int, batch_size: int):
147
165
 
148
166
  # Showcases how to use Numba to JIT-compile similarity measures for USearch.
149
167
  # https://numba.readthedocs.io/en/stable/reference/jit-compilation.html#c-callbacks
150
- signature = types.float32(types.CPointer(
151
- types.float32), types.CPointer(types.float32))
168
+ signature_two_args = types.float32(
169
+ types.CPointer(types.float32),
170
+ types.CPointer(types.float32),
171
+ )
172
+ signature_three_args = types.float32(
173
+ types.CPointer(types.float32),
174
+ types.CPointer(types.float32),
175
+ types.uint64,
176
+ )
177
+ signature_four_args = types.float32(
178
+ types.CPointer(types.float32),
179
+ types.uint64,
180
+ types.CPointer(types.float32),
181
+ types.uint64,
182
+ )
152
183
 
153
- @cfunc(signature)
154
- def python_dot(a, b):
184
+ @cfunc(signature_two_args)
185
+ def python_inner_product_two_args(a, b):
155
186
  a_array = carray(a, ndim)
156
187
  b_array = carray(b, ndim)
157
188
  c = 0.0
@@ -159,7 +190,195 @@ def test_index_numba(ndim: int, batch_size: int):
159
190
  c += a_array[i] * b_array[i]
160
191
  return 1 - c
161
192
 
162
- index = Index(ndim=ndim, metric=python_dot)
193
+ @cfunc(signature_three_args)
194
+ def python_inner_product_three_args(a, b, ndim):
195
+ a_array = carray(a, ndim)
196
+ b_array = carray(b, ndim)
197
+ c = 0.0
198
+ for i in range(ndim):
199
+ c += a_array[i] * b_array[i]
200
+ return 1 - c
201
+
202
+ @cfunc(signature_four_args)
203
+ def python_inner_product_four_args(a, a_ndim, b, b_ndim):
204
+ a_array = carray(a, a_ndim)
205
+ b_array = carray(b, b_ndim)
206
+ c = 0.0
207
+ for i in range(a_ndim):
208
+ c += a_array[i] * b_array[i]
209
+ return 1 - c
210
+
211
+ functions = [
212
+ python_inner_product_two_args,
213
+ python_inner_product_three_args,
214
+ python_inner_product_four_args,
215
+ ]
216
+ signatures = [
217
+ MetricSignature.ArrayArray,
218
+ MetricSignature.ArrayArraySize,
219
+ MetricSignature.ArraySizeArraySize,
220
+ ]
221
+ for function, signature in zip(functions, signatures):
222
+ metric = CompiledMetric(
223
+ pointer=function.address,
224
+ kind=MetricKind.IP,
225
+ signature=signature,
226
+ )
227
+ index = Index(ndim=ndim, metric=metric)
228
+
229
+ labels = np.arange(batch_size)
230
+ vectors = random_vectors(count=batch_size, ndim=ndim)
231
+
232
+ index.add(labels, vectors)
233
+ matches, distances, count = index.search(vectors, 10)
234
+ assert matches.shape[0] == distances.shape[0]
235
+ assert count.shape[0] == batch_size
236
+
237
+ assert recall_members(index, exact=True) == 1
238
+
239
+
240
+ @pytest.mark.parametrize("ndim", dimensions[-1:])
241
+ @pytest.mark.parametrize("batch_size", batch_sizes[-1:])
242
+ def test_index_cppyy(ndim: int, batch_size: int):
243
+ try:
244
+ import cppyy
245
+ import cppyy.ll
246
+ except ImportError:
247
+ return
248
+
249
+ cppyy.cppdef(
250
+ """
251
+ float inner_product_two_args(float *a, float *b) {
252
+ float result = 0;
253
+ #pragma unroll
254
+ for (size_t i = 0; i != ndim; ++i)
255
+ result += a[i] * b[i];
256
+ return 1 - result;
257
+ }
258
+
259
+ float inner_product_three_args(float *a, float *b, size_t n) {
260
+ float result = 0;
261
+ for (size_t i = 0; i != n; ++i)
262
+ result += a[i] * b[i];
263
+ return 1 - result;
264
+ }
265
+
266
+ float inner_product_four_args(float *a, size_t an, float *b, size_t) {
267
+ float result = 0;
268
+ for (size_t i = 0; i != an; ++i)
269
+ result += a[i] * b[i];
270
+ return 1 - result;
271
+ }
272
+ """.replace(
273
+ "ndim", str(ndim)
274
+ )
275
+ )
276
+
277
+ functions = [
278
+ cppyy.gbl.inner_product_two_args,
279
+ cppyy.gbl.inner_product_three_args,
280
+ cppyy.gbl.inner_product_four_args,
281
+ ]
282
+ signatures = [
283
+ MetricSignature.ArrayArray,
284
+ MetricSignature.ArrayArraySize,
285
+ MetricSignature.ArraySizeArraySize,
286
+ ]
287
+ for function, signature in zip(functions, signatures):
288
+ metric = CompiledMetric(
289
+ pointer=cppyy.ll.addressof(function),
290
+ kind=MetricKind.IP,
291
+ signature=signature,
292
+ )
293
+ index = Index(ndim=ndim, metric=metric)
294
+
295
+ labels = np.arange(batch_size)
296
+ vectors = random_vectors(count=batch_size, ndim=ndim)
297
+
298
+ index.add(labels, vectors)
299
+ matches, distances, count = index.search(vectors, 10)
300
+ assert matches.shape[0] == distances.shape[0]
301
+ assert count.shape[0] == batch_size
302
+
303
+ assert recall_members(index, exact=True) == 1
304
+
305
+
306
+ @pytest.mark.parametrize("ndim", [8])
307
+ @pytest.mark.parametrize("batch_size", batch_sizes)
308
+ def test_index_peachpy(ndim: int, batch_size: int):
309
+ try:
310
+ from peachpy import (
311
+ Argument,
312
+ ptr,
313
+ float_,
314
+ const_float_,
315
+ )
316
+ from peachpy.x86_64 import (
317
+ abi,
318
+ Function,
319
+ uarch,
320
+ isa,
321
+ GeneralPurposeRegister64,
322
+ LOAD,
323
+ YMMRegister,
324
+ VSUBPS,
325
+ VADDPS,
326
+ VHADDPS,
327
+ VMOVUPS,
328
+ VFMADD231PS,
329
+ VPERM2F128,
330
+ VXORPS,
331
+ RETURN,
332
+ )
333
+ except ImportError:
334
+ return
335
+
336
+ a = Argument(ptr(const_float_), name="a")
337
+ b = Argument(ptr(const_float_), name="b")
338
+
339
+ with Function(
340
+ "InnerProduct", (a, b), float_, target=uarch.default + isa.avx + isa.avx2
341
+ ) as asm_function:
342
+ # Request two 64-bit general-purpose registers for addresses
343
+ reg_a, reg_b = GeneralPurposeRegister64(), GeneralPurposeRegister64()
344
+ LOAD.ARGUMENT(reg_a, a)
345
+ LOAD.ARGUMENT(reg_b, b)
346
+
347
+ # Load the vectors
348
+ ymm_a = YMMRegister()
349
+ ymm_b = YMMRegister()
350
+ VMOVUPS(ymm_a, [reg_a])
351
+ VMOVUPS(ymm_b, [reg_b])
352
+
353
+ # Prepare the accumulator
354
+ ymm_c = YMMRegister()
355
+ ymm_one = YMMRegister()
356
+ VXORPS(ymm_c, ymm_c, ymm_c)
357
+ VXORPS(ymm_one, ymm_one, ymm_one)
358
+
359
+ # Accumulate A and B product into C
360
+ VFMADD231PS(ymm_c, ymm_a, ymm_b)
361
+
362
+ # Reduce the contents of a YMM register
363
+ ymm_c_permuted = YMMRegister()
364
+ VPERM2F128(ymm_c_permuted, ymm_c, ymm_c, 1)
365
+ VADDPS(ymm_c, ymm_c, ymm_c_permuted)
366
+ VHADDPS(ymm_c, ymm_c, ymm_c)
367
+ VHADDPS(ymm_c, ymm_c, ymm_c)
368
+
369
+ # Negate the values, to go from "similarity" to "distance"
370
+ VSUBPS(ymm_c, ymm_one, ymm_c)
371
+
372
+ # A common convention is to return floats in XMM registers
373
+ RETURN(ymm_c.as_xmm)
374
+
375
+ python_function = asm_function.finalize(abi.detect()).encode().load()
376
+ metric = CompiledMetric(
377
+ pointer=python_function.loader.code_address,
378
+ kind=MetricKind.IP,
379
+ signature=MetricSignature.ArrayArray,
380
+ )
381
+ index = Index(ndim=ndim, metric=metric)
163
382
 
164
383
  labels = np.arange(batch_size)
165
384
  vectors = random_vectors(count=batch_size, ndim=ndim)
@@ -172,12 +391,13 @@ def test_index_numba(ndim: int, batch_size: int):
172
391
  assert recall_members(index, exact=True) == 1
173
392
 
174
393
 
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
-
394
+ @pytest.mark.parametrize("bits", dimensions)
395
+ @pytest.mark.parametrize("metric", hash_metrics)
396
+ @pytest.mark.parametrize("connectivity", connectivity_options)
397
+ @pytest.mark.parametrize("batch_size", batch_sizes)
398
+ def test_bitwise_index(
399
+ bits: int, metric: MetricKind, connectivity: int, batch_size: int
400
+ ):
181
401
  index = Index(ndim=bits, metric=metric, connectivity=connectivity)
182
402
 
183
403
  labels = np.arange(batch_size)
@@ -187,14 +407,13 @@ def test_bitwise_index(bits: int, metric: MetricKind, connectivity: int, batch_s
187
407
  index.add(labels, bit_vectors)
188
408
  assert np.allclose(index.get_vectors(labels), byte_vectors, atol=0.1)
189
409
  assert np.all(index.get_vectors(labels, ScalarKind.B1) == bit_vectors)
190
-
410
+
191
411
  index.search(bit_vectors, 10)
192
412
 
193
413
 
194
- @pytest.mark.parametrize('connectivity', connectivity_options)
195
- @pytest.mark.skipif(os.name == 'nt', reason='Spurious behaviour on windows')
414
+ @pytest.mark.parametrize("connectivity", connectivity_options)
415
+ @pytest.mark.skipif(os.name == "nt", reason="Spurious behaviour on windows")
196
416
  def test_sets_index(connectivity: int):
197
-
198
417
  index = SparseIndex(connectivity=connectivity)
199
418
  index.add(10, np.array([10, 12, 15], dtype=np.uint32))
200
419
  index.add(11, np.array([11, 12, 15, 16], dtype=np.uint32))