usearch 0.18.0 → 0.18.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 +2 -2
- package/conanfile.py +1 -1
- package/package.json +1 -1
- package/python/lib.cpp +14 -14
- package/python/usearch/eval.py +3 -3
- package/python/usearch/index.py +3 -3
- package/python/usearch/server.py +6 -7
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -276,7 +276,7 @@ The difference would be in the shape of the tensors.
|
|
|
276
276
|
|
|
277
277
|
```py
|
|
278
278
|
n = 100
|
|
279
|
-
labels = np.
|
|
279
|
+
labels = np.arange(n)
|
|
280
280
|
vectors = np.random.uniform(0, 0.3, (n, index.ndim)).astype(np.float32)
|
|
281
281
|
|
|
282
282
|
index.add(labels, vectors, threads=..., copy=...)
|
|
@@ -584,7 +584,7 @@ fingerprints = np.vstack([encoder.GetFingerprint(x) for x in molecules])
|
|
|
584
584
|
fingerprints = np.packbits(fingerprints, axis=1)
|
|
585
585
|
|
|
586
586
|
index = Index(ndim=2048, metric=MetricKind.BitwiseTanimoto)
|
|
587
|
-
labels = np.
|
|
587
|
+
labels = np.arange(len(molecules))
|
|
588
588
|
|
|
589
589
|
index.add(labels, fingerprints)
|
|
590
590
|
matches = index.search(fingerprints, 10)
|
package/conanfile.py
CHANGED
|
@@ -4,7 +4,7 @@ from conans import ConanFile
|
|
|
4
4
|
class USearchConan(ConanFile):
|
|
5
5
|
|
|
6
6
|
name = 'USearch'
|
|
7
|
-
version = '0.18.
|
|
7
|
+
version = '0.18.1'
|
|
8
8
|
license = 'Apache 2.0'
|
|
9
9
|
url = 'https://github.com/unum-cloud/usearch'
|
|
10
10
|
description = 'Smaller & Faster Single-File Vector Search Engine from Unum'
|
package/package.json
CHANGED
package/python/lib.cpp
CHANGED
|
@@ -26,7 +26,7 @@ using namespace unum;
|
|
|
26
26
|
namespace py = pybind11;
|
|
27
27
|
using py_shape_t = py::array::ShapeContainer;
|
|
28
28
|
|
|
29
|
-
using label_t =
|
|
29
|
+
using label_t = std::uint32_t;
|
|
30
30
|
using distance_t = punned_distance_t;
|
|
31
31
|
using metric_t = punned_metric_t;
|
|
32
32
|
using id_t = std::uint32_t;
|
|
@@ -132,11 +132,11 @@ static void add_one_to_index(punned_index_py_t& index, label_t label, py::buffer
|
|
|
132
132
|
config.store_vector = copy;
|
|
133
133
|
|
|
134
134
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
135
|
-
if (vector_info.format == "B")
|
|
135
|
+
if (vector_info.format == "B" || vector_info.format == "u1" || vector_info.format == "|u1")
|
|
136
136
|
index.add(label, reinterpret_cast<b1x8_t const*>(vector_data), config).error.raise();
|
|
137
|
-
else if (vector_info.format == "
|
|
137
|
+
else if (vector_info.format == "b" || vector_info.format == "i1" || vector_info.format == "|i1")
|
|
138
138
|
index.add(label, reinterpret_cast<f8_bits_t const*>(vector_data), config).error.raise();
|
|
139
|
-
else if (vector_info.format == "e")
|
|
139
|
+
else if (vector_info.format == "e" || vector_info.format == "f2" || vector_info.format == "<f2")
|
|
140
140
|
index.add(label, reinterpret_cast<f16_t const*>(vector_data), config).error.raise();
|
|
141
141
|
else if (vector_info.format == "f" || vector_info.format == "f4" || vector_info.format == "<f4")
|
|
142
142
|
index.add(label, reinterpret_cast<float const*>(vector_data), config).error.raise();
|
|
@@ -156,7 +156,7 @@ static void add_many_to_index( //
|
|
|
156
156
|
py::buffer_info labels_info = labels.request();
|
|
157
157
|
py::buffer_info vectors_info = vectors.request();
|
|
158
158
|
|
|
159
|
-
if (labels_info.
|
|
159
|
+
if (labels_info.itemsize != sizeof(label_t))
|
|
160
160
|
throw std::invalid_argument("Incompatible label type!");
|
|
161
161
|
|
|
162
162
|
if (labels_info.ndim != 1)
|
|
@@ -181,7 +181,7 @@ static void add_many_to_index( //
|
|
|
181
181
|
char const* labels_data = reinterpret_cast<char const*>(labels_info.ptr);
|
|
182
182
|
|
|
183
183
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
184
|
-
if (vectors_info.format == "B")
|
|
184
|
+
if (vectors_info.format == "B" || vectors_info.format == "u1" || vectors_info.format == "|u1")
|
|
185
185
|
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
186
186
|
add_config_t config;
|
|
187
187
|
config.store_vector = copy;
|
|
@@ -190,7 +190,7 @@ static void add_many_to_index( //
|
|
|
190
190
|
b1x8_t const* vector = reinterpret_cast<b1x8_t const*>(vectors_data + task_idx * vectors_info.strides[0]);
|
|
191
191
|
index.add(label, vector, config).error.raise();
|
|
192
192
|
});
|
|
193
|
-
else if (vectors_info.format == "
|
|
193
|
+
else if (vectors_info.format == "b" || vectors_info.format == "i1" || vectors_info.format == "|i1")
|
|
194
194
|
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
195
195
|
add_config_t config;
|
|
196
196
|
config.store_vector = copy;
|
|
@@ -200,7 +200,7 @@ static void add_many_to_index( //
|
|
|
200
200
|
reinterpret_cast<f8_bits_t const*>(vectors_data + task_idx * vectors_info.strides[0]);
|
|
201
201
|
index.add(label, vector, config).error.raise();
|
|
202
202
|
});
|
|
203
|
-
else if (vectors_info.format == "e")
|
|
203
|
+
else if (vectors_info.format == "e" || vectors_info.format == "f2" || vectors_info.format == "<f2")
|
|
204
204
|
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
205
205
|
add_config_t config;
|
|
206
206
|
config.store_vector = copy;
|
|
@@ -249,15 +249,15 @@ static py::tuple search_one_in_index(punned_index_py_t& index, py::buffer vector
|
|
|
249
249
|
config.exact = exact;
|
|
250
250
|
|
|
251
251
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
252
|
-
if (vector_info.format == "B") {
|
|
252
|
+
if (vector_info.format == "B" || vector_info.format == "u1" || vector_info.format == "|u1") {
|
|
253
253
|
punned_search_result_t result = index.search(reinterpret_cast<b1x8_t const*>(vector_data), wanted, config);
|
|
254
254
|
result.error.raise();
|
|
255
255
|
count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
|
|
256
|
-
} else if (vector_info.format == "
|
|
256
|
+
} else if (vector_info.format == "b" || vector_info.format == "i1" || vector_info.format == "|i1") {
|
|
257
257
|
punned_search_result_t result = index.search(reinterpret_cast<f8_bits_t const*>(vector_data), wanted, config);
|
|
258
258
|
result.error.raise();
|
|
259
259
|
count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
|
|
260
|
-
} else if (vector_info.format == "e") {
|
|
260
|
+
} else if (vector_info.format == "e" || vector_info.format == "f2" || vector_info.format == "<f2") {
|
|
261
261
|
punned_search_result_t result = index.search(reinterpret_cast<f16_t const*>(vector_data), wanted, config);
|
|
262
262
|
result.error.raise();
|
|
263
263
|
count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
|
|
@@ -320,7 +320,7 @@ static py::tuple search_many_in_index( //
|
|
|
320
320
|
auto counts_py1d = counts_py.template mutable_unchecked<1>();
|
|
321
321
|
|
|
322
322
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
323
|
-
if (vectors_info.format == "B")
|
|
323
|
+
if (vectors_info.format == "B" || vectors_info.format == "u1" || vectors_info.format == "|u1")
|
|
324
324
|
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
325
325
|
search_config_t config;
|
|
326
326
|
config.thread = thread_idx;
|
|
@@ -331,7 +331,7 @@ static py::tuple search_many_in_index( //
|
|
|
331
331
|
counts_py1d(task_idx) =
|
|
332
332
|
static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
|
|
333
333
|
});
|
|
334
|
-
else if (vectors_info.format == "
|
|
334
|
+
else if (vectors_info.format == "b" || vectors_info.format == "i1" || vectors_info.format == "|i1")
|
|
335
335
|
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
336
336
|
search_config_t config;
|
|
337
337
|
config.thread = thread_idx;
|
|
@@ -342,7 +342,7 @@ static py::tuple search_many_in_index( //
|
|
|
342
342
|
counts_py1d(task_idx) =
|
|
343
343
|
static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
|
|
344
344
|
});
|
|
345
|
-
else if (vectors_info.format == "e")
|
|
345
|
+
else if (vectors_info.format == "e" || vectors_info.format == "f2" || vectors_info.format == "<f2")
|
|
346
346
|
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
347
347
|
search_config_t config;
|
|
348
348
|
config.thread = thread_idx;
|
package/python/usearch/eval.py
CHANGED
|
@@ -7,7 +7,7 @@ from collections import defaultdict
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
|
|
9
9
|
from usearch.io import load_matrix
|
|
10
|
-
from usearch.index import Index, Matches, MetricKind, MetricKindBitwise
|
|
10
|
+
from usearch.index import Index, Matches, MetricKind, MetricKindBitwise, Label
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def random_vectors(
|
|
@@ -127,7 +127,7 @@ class Dataset:
|
|
|
127
127
|
count = min(
|
|
128
128
|
d.vectors.shape[0], count) if count is not None else d.vectors.shape[0]
|
|
129
129
|
d.vectors = d.vectors[:count, :]
|
|
130
|
-
d.labels = np.arange(count, dtype=
|
|
130
|
+
d.labels = np.arange(count, dtype=Label)
|
|
131
131
|
|
|
132
132
|
if queries is not None:
|
|
133
133
|
d.queries = load_matrix(queries)
|
|
@@ -150,7 +150,7 @@ class Dataset:
|
|
|
150
150
|
|
|
151
151
|
d.vectors = random_vectors(count=count, ndim=ndim)
|
|
152
152
|
d.queries = d.vectors
|
|
153
|
-
d.labels = np.arange(count, dtype=
|
|
153
|
+
d.labels = np.arange(count, dtype=Label)
|
|
154
154
|
d.neighbors = np.reshape(d.labels, (count, 1))
|
|
155
155
|
|
|
156
156
|
return d
|
package/python/usearch/index.py
CHANGED
|
@@ -29,7 +29,7 @@ MetricKindBitwise = (
|
|
|
29
29
|
|
|
30
30
|
SparseIndex = _CompiledSetsIndex
|
|
31
31
|
|
|
32
|
-
Label = np.
|
|
32
|
+
Label = np.uint32
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
def _normalize_dtype(dtype) -> ScalarKind:
|
|
@@ -295,9 +295,9 @@ class Index:
|
|
|
295
295
|
else:
|
|
296
296
|
labels = start_id
|
|
297
297
|
if isinstance(labels, np.ndarray):
|
|
298
|
-
labels = labels.astype(
|
|
298
|
+
labels = labels.astype(Label)
|
|
299
299
|
elif isinstance(labels, Iterable):
|
|
300
|
-
labels = np.array(labels, dtype=
|
|
300
|
+
labels = np.array(labels, dtype=Label)
|
|
301
301
|
|
|
302
302
|
self._compiled.add(labels, vectors, copy=copy, threads=threads)
|
|
303
303
|
|
package/python/usearch/server.py
CHANGED
|
@@ -7,7 +7,7 @@ import numpy as np
|
|
|
7
7
|
from typing import List
|
|
8
8
|
|
|
9
9
|
from ucall.rich_posix import Server
|
|
10
|
-
from usearch.index import Index,
|
|
10
|
+
from usearch.index import Index, Matches, Label
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _ascii_to_vector(string: str) -> np.ndarray:
|
|
@@ -58,26 +58,25 @@ def serve(
|
|
|
58
58
|
@server
|
|
59
59
|
def add_one(label: int, vector: np.ndarray):
|
|
60
60
|
print('adding', label, vector)
|
|
61
|
-
labels = np.array([label], dtype=
|
|
61
|
+
labels = np.array([label], dtype=Label)
|
|
62
62
|
vectors = vector.flatten().reshape(vector.shape[0], 1)
|
|
63
63
|
index.add(labels, vectors)
|
|
64
64
|
|
|
65
65
|
@server
|
|
66
66
|
def add_many(labels: np.ndarray, vectors: np.ndarray):
|
|
67
|
-
labels = labels.astype(np.longlong)
|
|
68
67
|
index.add(labels, vectors, threads=threads)
|
|
69
68
|
|
|
70
69
|
@server
|
|
71
70
|
def search_one(vector: np.ndarray, count: int) -> List[dict]:
|
|
72
71
|
print('search', vector, count)
|
|
73
72
|
vectors = vector.reshape(vector.shape[0], 1)
|
|
74
|
-
results = index.search(vectors, count)
|
|
75
|
-
return
|
|
73
|
+
results: Matches = index.search(vectors, count)
|
|
74
|
+
return results.to_list()
|
|
76
75
|
|
|
77
76
|
@server
|
|
78
77
|
def search_many(vectors: np.ndarray, count: int) -> List[List[dict]]:
|
|
79
|
-
results = index.search(vectors, count)
|
|
80
|
-
return
|
|
78
|
+
results: Matches = index.search(vectors, count)
|
|
79
|
+
return results.to_list()
|
|
81
80
|
|
|
82
81
|
@server
|
|
83
82
|
def add_ascii(label: int, string: str):
|