usearch 0.18.6 → 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.
- package/Cargo.toml +1 -1
- package/README.md +184 -11
- package/conanfile.py +1 -1
- package/cpp/bench.cpp +2 -2
- package/cpp/test.cpp +11 -9
- package/docs/compilation.md +4 -2
- package/include/usearch/index.hpp +269 -50
- package/include/usearch/index_punned_dense.hpp +123 -108
- package/include/usearch/index_punned_helpers.hpp +5 -13
- package/package.json +1 -1
- package/python/lib.cpp +116 -40
- package/python/scripts/bench.ipynb +4 -4
- package/python/scripts/bench.py +51 -28
- package/python/scripts/index_faiss.py +19 -19
- package/python/scripts/test.py +275 -56
- package/python/usearch/eval.py +100 -49
- package/python/usearch/index.py +118 -88
- package/python/usearch/io.py +23 -23
- package/python/usearch/join.py +2 -4
- package/python/usearch/numba.py +44 -30
- package/python/usearch/server.py +38 -26
- 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 +22 -7
- 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,
|
|
@@ -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,
|
|
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: 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(
|
|
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")
|
|
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
|
-
|
|
112
|
-
|
|
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(
|
|
140
|
-
@pytest.mark.parametrize(
|
|
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
|
-
|
|
151
|
-
types.
|
|
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(
|
|
154
|
-
def
|
|
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
|
-
|
|
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(
|
|
176
|
-
@pytest.mark.parametrize(
|
|
177
|
-
@pytest.mark.parametrize(
|
|
178
|
-
@pytest.mark.parametrize(
|
|
179
|
-
def test_bitwise_index(
|
|
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(
|
|
195
|
-
@pytest.mark.skipif(os.name ==
|
|
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))
|