usearch 0.14.0 → 0.15.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/.github/workflows/release.yml +56 -4
- package/.github/workflows/update_version.sh +6 -1
- package/.vscode/launch.json +21 -22
- package/.vscode/settings.json +7 -1
- package/CITATION.cff +4 -4
- package/CMakeLists.txt +1 -1
- package/Cargo.toml +1 -1
- package/Package.swift +0 -1
- package/README.md +100 -27
- package/binding.gyp +0 -1
- package/build.gradle +1 -1
- package/build.rs +3 -3
- package/c/lib.cpp +32 -9
- package/c/usearch.h +67 -36
- package/conanfile.py +1 -1
- package/{src → cpp}/bench.cpp +52 -36
- package/{src → cpp}/test.cpp +17 -7
- package/docs/benchmarks.md +33 -10
- package/docs/c/index.rst +11 -0
- package/docs/c/reference.rst +7 -0
- package/docs/compilation.md +24 -9
- package/docs/conf.dox +6 -6
- package/docs/conf.py +3 -1
- package/docs/cpp/index.rst +11 -0
- package/docs/cpp/reference.rst +7 -0
- package/docs/index.rst +10 -0
- package/docs/java/index.rst +11 -0
- package/docs/java/reference.rst +7 -0
- package/docs/javascript/index.rst +11 -0
- package/docs/javascript/reference.rst +9 -0
- package/docs/python/index.rst +11 -0
- package/docs/python/reference.rst +35 -0
- package/golang/lib.go +1 -1
- package/include/usearch/{usearch.hpp → index.hpp} +152 -148
- package/include/usearch/index_punned_dense.hpp +633 -0
- package/include/usearch/index_punned_helpers.hpp +528 -0
- package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +8 -7
- package/javascript/lib.cpp +26 -50
- package/javascript/usearch.js +117 -2
- package/objc/USearchObjective.mm +145 -49
- package/objc/include/USearchObjective.h +106 -30
- package/package.json +2 -2
- package/python/lib.cpp +134 -331
- package/python/scripts/bench.ipynb +170 -0
- package/python/scripts/bench.py +87 -175
- package/python/scripts/index_faiss.py +100 -0
- package/python/scripts/test.py +41 -37
- package/python/usearch/eval.py +353 -0
- package/python/usearch/index.py +117 -113
- package/python/usearch/io.py +7 -2
- package/python/usearch/join.py +100 -0
- package/python/usearch/numba.py +101 -0
- package/rust/lib.cpp +27 -14
- package/rust/lib.hpp +4 -10
- package/rust/lib.rs +50 -17
- package/setup.py +12 -2
- package/simsimd/include/simsimd/simsimd.h +32 -3
- package/swift/Index+Sugar.swift +55 -29
- package/swift/Test.swift +1 -1
- package/wolfram/lib.cpp +4 -4
- package/python/scripts/bench_params.py +0 -201
- package/python/usearch/synthetic.py +0 -31
- package/src/punned.hpp +0 -906
- /package/{src → cpp}/CMakeLists.txt +0 -0
package/python/lib.cpp
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
#include <pybind11/numpy.h>
|
|
19
19
|
#include <pybind11/pybind11.h>
|
|
20
20
|
|
|
21
|
-
#include
|
|
21
|
+
#include <usearch/index_punned_dense.hpp>
|
|
22
22
|
|
|
23
23
|
using namespace unum::usearch;
|
|
24
24
|
using namespace unum;
|
|
@@ -28,9 +28,11 @@ using py_shape_t = py::array::ShapeContainer;
|
|
|
28
28
|
|
|
29
29
|
using label_t = Py_ssize_t;
|
|
30
30
|
using distance_t = punned_distance_t;
|
|
31
|
+
using metric_t = punned_metric_t;
|
|
31
32
|
using id_t = std::uint32_t;
|
|
33
|
+
using big_id_t = std::uint64_t;
|
|
32
34
|
|
|
33
|
-
using punned_index_t =
|
|
35
|
+
using punned_index_t = index_punned_dense_gt<label_t, id_t>;
|
|
34
36
|
using punned_add_result_t = typename punned_index_t::add_result_t;
|
|
35
37
|
using punned_search_result_t = typename punned_index_t::search_result_t;
|
|
36
38
|
|
|
@@ -47,89 +49,41 @@ struct punned_index_py_t : public punned_index_t {
|
|
|
47
49
|
|
|
48
50
|
using set_member_t = std::uint32_t;
|
|
49
51
|
using set_view_t = span_gt<set_member_t const>;
|
|
50
|
-
using
|
|
52
|
+
using sparse_index_t = index_gt<jaccard_gt<set_member_t>, label_t, id_t, set_member_t>;
|
|
51
53
|
|
|
52
|
-
struct
|
|
53
|
-
using native_t =
|
|
54
|
+
struct sparse_index_py_t : public sparse_index_t {
|
|
55
|
+
using native_t = sparse_index_t;
|
|
54
56
|
using native_t::add;
|
|
55
57
|
using native_t::capacity;
|
|
56
58
|
using native_t::reserve;
|
|
57
59
|
using native_t::search;
|
|
58
60
|
using native_t::size;
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
sparse_index_py_t(native_t&& base) : native_t(std::move(base)) {}
|
|
61
63
|
};
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
using
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
using bits_index_t = index_gt<bits_metric_t, label_t, id_t, bits_native_word_t>;
|
|
69
|
-
using bits_add_result_t = typename bits_index_t::add_result_t;
|
|
70
|
-
using bits_search_result_t = typename bits_index_t::search_result_t;
|
|
71
|
-
|
|
72
|
-
static constexpr std::size_t bits_per_hash_numpy_word_k = sizeof(bits_numpy_word_t) * CHAR_BIT;
|
|
73
|
-
static constexpr std::size_t bits_per_hash_native_word_k = sizeof(bits_native_word_t) * CHAR_BIT;
|
|
74
|
-
|
|
75
|
-
struct bits_index_py_t : public bits_index_t {
|
|
76
|
-
using native_t = bits_index_t;
|
|
77
|
-
using native_t::add;
|
|
78
|
-
using native_t::capacity;
|
|
79
|
-
using native_t::limits;
|
|
80
|
-
using native_t::reserve;
|
|
81
|
-
using native_t::search;
|
|
82
|
-
using native_t::size;
|
|
83
|
-
|
|
84
|
-
std::size_t bits_;
|
|
85
|
-
std::size_t numpy_words_;
|
|
86
|
-
std::size_t native_words_;
|
|
87
|
-
std::vector<bits_native_word_t> word_aligned_buffer_;
|
|
88
|
-
|
|
89
|
-
bits_index_py_t(native_t&& base) : native_t(std::move(base)) {}
|
|
90
|
-
|
|
91
|
-
bits_index_py_t(index_config_t config, std::size_t bits, bits_metric_t metric)
|
|
92
|
-
: bits_index_t(config, metric), bits_(bits), //
|
|
93
|
-
numpy_words_(divide_round_up<bits_per_hash_numpy_word_k>(bits)), //
|
|
94
|
-
native_words_(divide_round_up<bits_per_hash_native_word_k>(bits)), //
|
|
95
|
-
word_aligned_buffer_(native_words_* index_limits_t{}.threads()) {}
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
bits_metric_t bits_metric(common_metric_kind_t kind) {
|
|
99
|
-
switch (kind) {
|
|
100
|
-
case common_metric_kind_t::bitwise_hamming_k:
|
|
101
|
-
return bits_metric_t(bitwise_hamming_gt<bits_native_word_t, bits_distance_t>{});
|
|
102
|
-
case common_metric_kind_t::bitwise_tanimoto_k:
|
|
103
|
-
return bits_metric_t(bitwise_tanimoto_gt<bits_native_word_t, bits_distance_t>{});
|
|
104
|
-
case common_metric_kind_t::bitwise_sorensen_k:
|
|
105
|
-
return bits_metric_t(bitwise_sorensen_gt<bits_native_word_t, bits_distance_t>{});
|
|
106
|
-
default: throw std::invalid_argument("Not a bitwise metric!"); return {};
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
template <typename scalar_at> punned_stateful_metric_t udf(std::size_t metric_uintptr) {
|
|
111
|
-
return [metric_uintptr](byte_t const* a_bytes, byte_t const* b_bytes, std::size_t n, std::size_t m) -> distance_t {
|
|
112
|
-
using metric_t = punned_distance_t (*)(scalar_at const*, scalar_at const*, std::size_t, std::size_t);
|
|
113
|
-
metric_t metric_ptr = reinterpret_cast<metric_t>(metric_uintptr);
|
|
114
|
-
scalar_at const* a = reinterpret_cast<scalar_at const*>(a_bytes);
|
|
115
|
-
scalar_at const* b = reinterpret_cast<scalar_at const*>(b_bytes);
|
|
116
|
-
return metric_ptr(a, b, n / sizeof(scalar_at), m / sizeof(scalar_at));
|
|
65
|
+
template <typename scalar_at> metric_t udf(std::size_t metric_uintptr) {
|
|
66
|
+
return [metric_uintptr](punned_vector_view_t a, punned_vector_view_t b) -> distance_t {
|
|
67
|
+
using metric_raw_t = punned_distance_t (*)(scalar_at const*, scalar_at const*);
|
|
68
|
+
metric_raw_t metric_ptr = reinterpret_cast<metric_raw_t>(metric_uintptr);
|
|
69
|
+
return metric_ptr((scalar_at const*)a.data(), (scalar_at const*)b.data());
|
|
117
70
|
};
|
|
118
71
|
}
|
|
119
72
|
|
|
120
|
-
|
|
73
|
+
metric_t udf(std::size_t metric_uintptr, scalar_kind_t accuracy) {
|
|
121
74
|
switch (accuracy) {
|
|
122
|
-
case
|
|
123
|
-
case
|
|
124
|
-
case
|
|
125
|
-
case
|
|
75
|
+
case scalar_kind_t::f8_k: return udf<f8_bits_t>(metric_uintptr);
|
|
76
|
+
case scalar_kind_t::f16_k: return udf<f16_t>(metric_uintptr);
|
|
77
|
+
case scalar_kind_t::f32_k: return udf<f32_t>(metric_uintptr);
|
|
78
|
+
case scalar_kind_t::f64_k: return udf<f64_t>(metric_uintptr);
|
|
79
|
+
default: return {};
|
|
126
80
|
}
|
|
127
81
|
}
|
|
128
82
|
|
|
129
83
|
static punned_index_py_t make_index( //
|
|
130
84
|
std::size_t dimensions, //
|
|
131
|
-
|
|
132
|
-
|
|
85
|
+
scalar_kind_t scalar_kind, //
|
|
86
|
+
metric_kind_t metric_kind, //
|
|
133
87
|
std::size_t connectivity, //
|
|
134
88
|
std::size_t expansion_add, //
|
|
135
89
|
std::size_t expansion_search, //
|
|
@@ -137,45 +91,27 @@ static punned_index_py_t make_index( //
|
|
|
137
91
|
bool tune) {
|
|
138
92
|
|
|
139
93
|
index_config_t config;
|
|
140
|
-
config.expansion_add = expansion_add;
|
|
141
|
-
config.expansion_search = expansion_search;
|
|
142
94
|
config.connectivity = connectivity;
|
|
143
95
|
|
|
144
96
|
if (tune)
|
|
145
97
|
config = punned_index_t::optimize(config);
|
|
146
98
|
|
|
147
|
-
accuracy_t accuracy = accuracy_from_name(scalar_type.c_str(), scalar_type.size());
|
|
148
99
|
if (metric_uintptr)
|
|
149
|
-
return punned_index_t::
|
|
100
|
+
return punned_index_t::make( //
|
|
101
|
+
dimensions, udf(metric_uintptr, scalar_kind), config, scalar_kind, expansion_add, expansion_search);
|
|
150
102
|
else
|
|
151
|
-
return punned_index_t(
|
|
103
|
+
return punned_index_t::make(dimensions, metric_kind, config, scalar_kind, expansion_add, expansion_search);
|
|
152
104
|
}
|
|
153
105
|
|
|
154
|
-
static std::unique_ptr<
|
|
155
|
-
std::size_t connectivity,
|
|
156
|
-
std::size_t expansion_add,
|
|
157
|
-
std::size_t expansion_search
|
|
106
|
+
static std::unique_ptr<sparse_index_py_t> make_sparse_index( //
|
|
107
|
+
std::size_t connectivity, //
|
|
108
|
+
std::size_t expansion_add, //
|
|
109
|
+
std::size_t expansion_search //
|
|
158
110
|
) {
|
|
159
111
|
index_config_t config;
|
|
160
|
-
config.expansion_add = expansion_add;
|
|
161
|
-
config.expansion_search = expansion_search;
|
|
162
112
|
config.connectivity = connectivity;
|
|
163
113
|
|
|
164
|
-
return std::unique_ptr<
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
static std::unique_ptr<bits_index_py_t> make_hash_index( //
|
|
168
|
-
std::size_t bits, //
|
|
169
|
-
common_metric_kind_t metric, //
|
|
170
|
-
std::size_t connectivity, //
|
|
171
|
-
std::size_t expansion_add, //
|
|
172
|
-
std::size_t expansion_search //
|
|
173
|
-
) {
|
|
174
|
-
index_config_t config;
|
|
175
|
-
config.expansion_add = expansion_add;
|
|
176
|
-
config.expansion_search = expansion_search;
|
|
177
|
-
config.connectivity = connectivity;
|
|
178
|
-
return std::unique_ptr<bits_index_py_t>(new bits_index_py_t(config, bits, bits_metric(metric)));
|
|
114
|
+
return std::unique_ptr<sparse_index_py_t>(new sparse_index_py_t(sparse_index_t(config)));
|
|
179
115
|
}
|
|
180
116
|
|
|
181
117
|
static void add_one_to_index(punned_index_py_t& index, label_t label, py::buffer vector, bool copy, std::size_t) {
|
|
@@ -186,7 +122,7 @@ static void add_one_to_index(punned_index_py_t& index, label_t label, py::buffer
|
|
|
186
122
|
|
|
187
123
|
Py_ssize_t vector_dimensions = vector_info.shape[0];
|
|
188
124
|
char const* vector_data = reinterpret_cast<char const*>(vector_info.ptr);
|
|
189
|
-
if (vector_dimensions != static_cast<Py_ssize_t>(index.
|
|
125
|
+
if (vector_dimensions != static_cast<Py_ssize_t>(index.scalar_words()))
|
|
190
126
|
throw std::invalid_argument("The number of vector dimensions doesn't match!");
|
|
191
127
|
|
|
192
128
|
if (index.size() + 1 >= index.capacity())
|
|
@@ -196,16 +132,18 @@ static void add_one_to_index(punned_index_py_t& index, label_t label, py::buffer
|
|
|
196
132
|
config.store_vector = copy;
|
|
197
133
|
|
|
198
134
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
199
|
-
if (vector_info.format == "
|
|
135
|
+
if (vector_info.format == "B")
|
|
136
|
+
index.add(label, reinterpret_cast<b1x8_t const*>(vector_data), config).error.raise();
|
|
137
|
+
else if (vector_info.format == "c" || vector_info.format == "b")
|
|
200
138
|
index.add(label, reinterpret_cast<f8_bits_t const*>(vector_data), config).error.raise();
|
|
201
139
|
else if (vector_info.format == "e")
|
|
202
|
-
index.add(label, reinterpret_cast<
|
|
140
|
+
index.add(label, reinterpret_cast<f16_t const*>(vector_data), config).error.raise();
|
|
203
141
|
else if (vector_info.format == "f" || vector_info.format == "f4" || vector_info.format == "<f4")
|
|
204
142
|
index.add(label, reinterpret_cast<float const*>(vector_data), config).error.raise();
|
|
205
143
|
else if (vector_info.format == "d" || vector_info.format == "f8" || vector_info.format == "<f8")
|
|
206
144
|
index.add(label, reinterpret_cast<double const*>(vector_data), config).error.raise();
|
|
207
145
|
else
|
|
208
|
-
throw std::invalid_argument("Incompatible scalars in the vector
|
|
146
|
+
throw std::invalid_argument("Incompatible scalars in the vector: " + vector_info.format);
|
|
209
147
|
}
|
|
210
148
|
|
|
211
149
|
static void add_many_to_index( //
|
|
@@ -230,7 +168,7 @@ static void add_many_to_index( //
|
|
|
230
168
|
Py_ssize_t labels_count = labels_info.shape[0];
|
|
231
169
|
Py_ssize_t vectors_count = vectors_info.shape[0];
|
|
232
170
|
Py_ssize_t vectors_dimensions = vectors_info.shape[1];
|
|
233
|
-
if (vectors_dimensions != static_cast<Py_ssize_t>(index.
|
|
171
|
+
if (vectors_dimensions != static_cast<Py_ssize_t>(index.scalar_words()))
|
|
234
172
|
throw std::invalid_argument("The number of vector dimensions doesn't match!");
|
|
235
173
|
|
|
236
174
|
if (labels_count != vectors_count)
|
|
@@ -243,8 +181,17 @@ static void add_many_to_index( //
|
|
|
243
181
|
char const* labels_data = reinterpret_cast<char const*>(labels_info.ptr);
|
|
244
182
|
|
|
245
183
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
246
|
-
if (vectors_info.format == "
|
|
247
|
-
|
|
184
|
+
if (vectors_info.format == "B")
|
|
185
|
+
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
186
|
+
add_config_t config;
|
|
187
|
+
config.store_vector = copy;
|
|
188
|
+
config.thread = thread_idx;
|
|
189
|
+
label_t label = *reinterpret_cast<label_t const*>(labels_data + task_idx * labels_info.strides[0]);
|
|
190
|
+
b1x8_t const* vector = reinterpret_cast<b1x8_t const*>(vectors_data + task_idx * vectors_info.strides[0]);
|
|
191
|
+
index.add(label, vector, config).error.raise();
|
|
192
|
+
});
|
|
193
|
+
else if (vectors_info.format == "c" || vectors_info.format == "b")
|
|
194
|
+
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
248
195
|
add_config_t config;
|
|
249
196
|
config.store_vector = copy;
|
|
250
197
|
config.thread = thread_idx;
|
|
@@ -254,17 +201,16 @@ static void add_many_to_index( //
|
|
|
254
201
|
index.add(label, vector, config).error.raise();
|
|
255
202
|
});
|
|
256
203
|
else if (vectors_info.format == "e")
|
|
257
|
-
|
|
204
|
+
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
258
205
|
add_config_t config;
|
|
259
206
|
config.store_vector = copy;
|
|
260
207
|
config.thread = thread_idx;
|
|
261
208
|
label_t label = *reinterpret_cast<label_t const*>(labels_data + task_idx * labels_info.strides[0]);
|
|
262
|
-
|
|
263
|
-
reinterpret_cast<f16_bits_t const*>(vectors_data + task_idx * vectors_info.strides[0]);
|
|
209
|
+
f16_t const* vector = reinterpret_cast<f16_t const*>(vectors_data + task_idx * vectors_info.strides[0]);
|
|
264
210
|
index.add(label, vector, config).error.raise();
|
|
265
211
|
});
|
|
266
212
|
else if (vectors_info.format == "f" || vectors_info.format == "f4" || vectors_info.format == "<f4")
|
|
267
|
-
|
|
213
|
+
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
268
214
|
add_config_t config;
|
|
269
215
|
config.store_vector = copy;
|
|
270
216
|
config.thread = thread_idx;
|
|
@@ -273,7 +219,7 @@ static void add_many_to_index( //
|
|
|
273
219
|
index.add(label, vector, config).error.raise();
|
|
274
220
|
});
|
|
275
221
|
else if (vectors_info.format == "d" || vectors_info.format == "f8" || vectors_info.format == "<f8")
|
|
276
|
-
|
|
222
|
+
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
277
223
|
add_config_t config;
|
|
278
224
|
config.store_vector = copy;
|
|
279
225
|
config.thread = thread_idx;
|
|
@@ -282,58 +228,7 @@ static void add_many_to_index( //
|
|
|
282
228
|
index.add(label, vector, config).error.raise();
|
|
283
229
|
});
|
|
284
230
|
else
|
|
285
|
-
throw std::invalid_argument("Incompatible scalars in the vectors matrix
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
static void add_many_to_bits_index( //
|
|
289
|
-
bits_index_py_t& index, py::buffer labels, py::buffer vectors, //
|
|
290
|
-
bool copy, std::size_t threads) {
|
|
291
|
-
|
|
292
|
-
if (index.limits().threads_add < threads)
|
|
293
|
-
throw std::invalid_argument("Can't use that many threads!");
|
|
294
|
-
|
|
295
|
-
py::buffer_info labels_info = labels.request();
|
|
296
|
-
py::buffer_info vectors_info = vectors.request();
|
|
297
|
-
|
|
298
|
-
if (labels_info.format != py::format_descriptor<label_t>::format())
|
|
299
|
-
throw std::invalid_argument("Incompatible label type!");
|
|
300
|
-
|
|
301
|
-
if (labels_info.ndim != 1)
|
|
302
|
-
throw std::invalid_argument("Labels must be placed in a single-dimensional array!");
|
|
303
|
-
|
|
304
|
-
if (vectors_info.ndim != 2)
|
|
305
|
-
throw std::invalid_argument("Expects a matrix of vectors to add!");
|
|
306
|
-
|
|
307
|
-
Py_ssize_t labels_count = labels_info.shape[0];
|
|
308
|
-
Py_ssize_t vectors_count = vectors_info.shape[0];
|
|
309
|
-
Py_ssize_t vectors_dimensions = vectors_info.shape[1];
|
|
310
|
-
if (vectors_dimensions != static_cast<Py_ssize_t>(index.numpy_words_))
|
|
311
|
-
throw std::invalid_argument("The number of vector dimensions doesn't match!");
|
|
312
|
-
|
|
313
|
-
if (labels_count != vectors_count)
|
|
314
|
-
throw std::invalid_argument("Number of labels and vectors must match!");
|
|
315
|
-
|
|
316
|
-
if (index.size() + vectors_count >= index.capacity())
|
|
317
|
-
index.reserve(ceil2(index.size() + vectors_count));
|
|
318
|
-
|
|
319
|
-
char const* vectors_data = reinterpret_cast<char const*>(vectors_info.ptr);
|
|
320
|
-
char const* labels_data = reinterpret_cast<char const*>(labels_info.ptr);
|
|
321
|
-
|
|
322
|
-
// https://docs.python.org/3/library/struct.html#format-characters
|
|
323
|
-
if (vectors_info.format != "B")
|
|
324
|
-
throw std::invalid_argument("Incompatible scalars in the vectors matrix!");
|
|
325
|
-
|
|
326
|
-
multithreaded(threads, vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
327
|
-
add_config_t config;
|
|
328
|
-
config.store_vector = copy;
|
|
329
|
-
config.thread = thread_idx;
|
|
330
|
-
label_t label = *reinterpret_cast<label_t const*>(labels_data + task_idx * labels_info.strides[0]);
|
|
331
|
-
bits_numpy_word_t const* vector =
|
|
332
|
-
reinterpret_cast<bits_numpy_word_t const*>(vectors_data + task_idx * vectors_info.strides[0]);
|
|
333
|
-
bits_native_word_t* vector_words = index.word_aligned_buffer_.data() + index.native_words_ * thread_idx;
|
|
334
|
-
std::memcpy(vector_words, vector, vectors_dimensions);
|
|
335
|
-
index.add(label, {vector_words, index.native_words_}, config).error.raise();
|
|
336
|
-
});
|
|
231
|
+
throw std::invalid_argument("Incompatible scalars in the vectors matrix: " + vectors_info.format);
|
|
337
232
|
}
|
|
338
233
|
|
|
339
234
|
static py::tuple search_one_in_index(punned_index_py_t& index, py::buffer vector, std::size_t wanted, bool exact) {
|
|
@@ -341,7 +236,7 @@ static py::tuple search_one_in_index(punned_index_py_t& index, py::buffer vector
|
|
|
341
236
|
py::buffer_info vector_info = vector.request();
|
|
342
237
|
Py_ssize_t vector_dimensions = vector_info.shape[0];
|
|
343
238
|
char const* vector_data = reinterpret_cast<char const*>(vector_info.ptr);
|
|
344
|
-
if (vector_dimensions != static_cast<Py_ssize_t>(index.
|
|
239
|
+
if (vector_dimensions != static_cast<Py_ssize_t>(index.scalar_words()))
|
|
345
240
|
throw std::invalid_argument("The number of vector dimensions doesn't match!");
|
|
346
241
|
|
|
347
242
|
py::array_t<label_t> labels_py(static_cast<Py_ssize_t>(wanted));
|
|
@@ -354,12 +249,16 @@ static py::tuple search_one_in_index(punned_index_py_t& index, py::buffer vector
|
|
|
354
249
|
config.exact = exact;
|
|
355
250
|
|
|
356
251
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
357
|
-
if (vector_info.format == "
|
|
252
|
+
if (vector_info.format == "B") {
|
|
253
|
+
punned_search_result_t result = index.search(reinterpret_cast<b1x8_t const*>(vector_data), wanted, config);
|
|
254
|
+
result.error.raise();
|
|
255
|
+
count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
|
|
256
|
+
} else if (vector_info.format == "c" || vector_info.format == "b") {
|
|
358
257
|
punned_search_result_t result = index.search(reinterpret_cast<f8_bits_t const*>(vector_data), wanted, config);
|
|
359
258
|
result.error.raise();
|
|
360
259
|
count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
|
|
361
260
|
} else if (vector_info.format == "e") {
|
|
362
|
-
punned_search_result_t result = index.search(reinterpret_cast<
|
|
261
|
+
punned_search_result_t result = index.search(reinterpret_cast<f16_t const*>(vector_data), wanted, config);
|
|
363
262
|
result.error.raise();
|
|
364
263
|
count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
|
|
365
264
|
} else if (vector_info.format == "f" || vector_info.format == "f4" || vector_info.format == "<f4") {
|
|
@@ -371,7 +270,7 @@ static py::tuple search_one_in_index(punned_index_py_t& index, py::buffer vector
|
|
|
371
270
|
result.error.raise();
|
|
372
271
|
count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
|
|
373
272
|
} else
|
|
374
|
-
throw std::invalid_argument("Incompatible scalars in the query vector
|
|
273
|
+
throw std::invalid_argument("Incompatible scalars in the query vector: " + vector_info.format);
|
|
375
274
|
|
|
376
275
|
labels_py.resize(py_shape_t{static_cast<Py_ssize_t>(count)});
|
|
377
276
|
distances_py.resize(py_shape_t{static_cast<Py_ssize_t>(count)});
|
|
@@ -410,7 +309,7 @@ static py::tuple search_many_in_index( //
|
|
|
410
309
|
Py_ssize_t vectors_count = vectors_info.shape[0];
|
|
411
310
|
Py_ssize_t vectors_dimensions = vectors_info.shape[1];
|
|
412
311
|
char const* vectors_data = reinterpret_cast<char const*>(vectors_info.ptr);
|
|
413
|
-
if (vectors_dimensions != static_cast<Py_ssize_t>(index.
|
|
312
|
+
if (vectors_dimensions != static_cast<Py_ssize_t>(index.scalar_words()))
|
|
414
313
|
throw std::invalid_argument("The number of vector dimensions doesn't match!");
|
|
415
314
|
|
|
416
315
|
py::array_t<label_t> labels_py({vectors_count, static_cast<Py_ssize_t>(wanted)});
|
|
@@ -421,8 +320,19 @@ static py::tuple search_many_in_index( //
|
|
|
421
320
|
auto counts_py1d = counts_py.template mutable_unchecked<1>();
|
|
422
321
|
|
|
423
322
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
424
|
-
if (vectors_info.format == "
|
|
425
|
-
|
|
323
|
+
if (vectors_info.format == "B")
|
|
324
|
+
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
325
|
+
search_config_t config;
|
|
326
|
+
config.thread = thread_idx;
|
|
327
|
+
config.exact = exact;
|
|
328
|
+
b1x8_t const* vector = (b1x8_t const*)(vectors_data + task_idx * vectors_info.strides[0]);
|
|
329
|
+
punned_search_result_t result = index.search(vector, wanted, config);
|
|
330
|
+
result.error.raise();
|
|
331
|
+
counts_py1d(task_idx) =
|
|
332
|
+
static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
|
|
333
|
+
});
|
|
334
|
+
else if (vectors_info.format == "c" || vectors_info.format == "b")
|
|
335
|
+
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
426
336
|
search_config_t config;
|
|
427
337
|
config.thread = thread_idx;
|
|
428
338
|
config.exact = exact;
|
|
@@ -433,18 +343,18 @@ static py::tuple search_many_in_index( //
|
|
|
433
343
|
static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
|
|
434
344
|
});
|
|
435
345
|
else if (vectors_info.format == "e")
|
|
436
|
-
|
|
346
|
+
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
437
347
|
search_config_t config;
|
|
438
348
|
config.thread = thread_idx;
|
|
439
349
|
config.exact = exact;
|
|
440
|
-
|
|
350
|
+
f16_t const* vector = (f16_t const*)(vectors_data + task_idx * vectors_info.strides[0]);
|
|
441
351
|
punned_search_result_t result = index.search(vector, wanted, config);
|
|
442
352
|
result.error.raise();
|
|
443
353
|
counts_py1d(task_idx) =
|
|
444
354
|
static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
|
|
445
355
|
});
|
|
446
356
|
else if (vectors_info.format == "f" || vectors_info.format == "f4" || vectors_info.format == "<f4")
|
|
447
|
-
|
|
357
|
+
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
448
358
|
search_config_t config;
|
|
449
359
|
config.thread = thread_idx;
|
|
450
360
|
config.exact = exact;
|
|
@@ -455,7 +365,7 @@ static py::tuple search_many_in_index( //
|
|
|
455
365
|
static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
|
|
456
366
|
});
|
|
457
367
|
else if (vectors_info.format == "d" || vectors_info.format == "f8" || vectors_info.format == "<f8")
|
|
458
|
-
|
|
368
|
+
executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
459
369
|
search_config_t config;
|
|
460
370
|
config.thread = thread_idx;
|
|
461
371
|
config.exact = exact;
|
|
@@ -466,71 +376,7 @@ static py::tuple search_many_in_index( //
|
|
|
466
376
|
static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
|
|
467
377
|
});
|
|
468
378
|
else
|
|
469
|
-
throw std::invalid_argument("Incompatible scalars in the query matrix
|
|
470
|
-
|
|
471
|
-
py::tuple results(3);
|
|
472
|
-
results[0] = labels_py;
|
|
473
|
-
results[1] = distances_py;
|
|
474
|
-
results[2] = counts_py;
|
|
475
|
-
return results;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* @param vectors Matrix of vectors to search for.
|
|
480
|
-
* @param wanted Number of matches per request.
|
|
481
|
-
*
|
|
482
|
-
* @return Tuple with:
|
|
483
|
-
* 1. matrix of neighbors,
|
|
484
|
-
* 2. matrix of distances,
|
|
485
|
-
* 3. array with match counts.
|
|
486
|
-
*/
|
|
487
|
-
static py::tuple search_many_in_bits_index( //
|
|
488
|
-
bits_index_py_t& index, py::buffer vectors, std::size_t wanted, bool exact, std::size_t threads) {
|
|
489
|
-
|
|
490
|
-
if (wanted == 0)
|
|
491
|
-
return py::tuple(3);
|
|
492
|
-
|
|
493
|
-
if (index.limits().threads_add < threads)
|
|
494
|
-
throw std::invalid_argument("Can't use that many threads!");
|
|
495
|
-
|
|
496
|
-
py::buffer_info vectors_info = vectors.request();
|
|
497
|
-
// if (vectors_info.ndim == 1)
|
|
498
|
-
// return search_one_in_index(index, vectors, wanted, exact);
|
|
499
|
-
if (vectors_info.ndim != 2)
|
|
500
|
-
throw std::invalid_argument("Expects a matrix of vectors to add!");
|
|
501
|
-
|
|
502
|
-
Py_ssize_t vectors_count = vectors_info.shape[0];
|
|
503
|
-
Py_ssize_t vectors_dimensions = vectors_info.shape[1];
|
|
504
|
-
char const* vectors_data = reinterpret_cast<char const*>(vectors_info.ptr);
|
|
505
|
-
if (vectors_dimensions != static_cast<Py_ssize_t>(index.numpy_words_))
|
|
506
|
-
throw std::invalid_argument("The number of vector dimensions doesn't match!");
|
|
507
|
-
|
|
508
|
-
py::array_t<label_t> labels_py({vectors_count, static_cast<Py_ssize_t>(wanted)});
|
|
509
|
-
py::array_t<distance_t> distances_py({vectors_count, static_cast<Py_ssize_t>(wanted)});
|
|
510
|
-
py::array_t<Py_ssize_t> counts_py(vectors_count);
|
|
511
|
-
auto labels_py2d = labels_py.template mutable_unchecked<2>();
|
|
512
|
-
auto distances_py2d = distances_py.template mutable_unchecked<2>();
|
|
513
|
-
auto counts_py1d = counts_py.template mutable_unchecked<1>();
|
|
514
|
-
|
|
515
|
-
// https://docs.python.org/3/library/struct.html#format-characters
|
|
516
|
-
if (vectors_info.format != "B")
|
|
517
|
-
throw std::invalid_argument("Incompatible scalars in the query matrix!");
|
|
518
|
-
|
|
519
|
-
multithreaded(threads, vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
520
|
-
search_config_t config;
|
|
521
|
-
config.thread = thread_idx;
|
|
522
|
-
config.exact = exact;
|
|
523
|
-
|
|
524
|
-
bits_numpy_word_t const* vector =
|
|
525
|
-
reinterpret_cast<bits_numpy_word_t const*>(vectors_data + task_idx * vectors_info.strides[0]);
|
|
526
|
-
bits_native_word_t* vector_words = index.word_aligned_buffer_.data() + index.native_words_ * thread_idx;
|
|
527
|
-
std::memcpy(vector_words, vector, vectors_dimensions);
|
|
528
|
-
|
|
529
|
-
bits_search_result_t result = index.search({vector_words, index.native_words_}, wanted, config);
|
|
530
|
-
result.error.raise();
|
|
531
|
-
counts_py1d(task_idx) =
|
|
532
|
-
static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
|
|
533
|
-
});
|
|
379
|
+
throw std::invalid_argument("Incompatible scalars in the query matrix: " + vectors_info.format);
|
|
534
380
|
|
|
535
381
|
py::tuple results(3);
|
|
536
382
|
results[0] = labels_py;
|
|
@@ -543,29 +389,27 @@ static py::tuple search_many_in_bits_index( //
|
|
|
543
389
|
template <typename index_at> void save_index(index_at const& index, std::string const& path) { index.save(path.c_str()).error.raise(); }
|
|
544
390
|
template <typename index_at> void load_index(index_at& index, std::string const& path) { index.load(path.c_str()).error.raise(); }
|
|
545
391
|
template <typename index_at> void view_index(index_at& index, std::string const& path) { index.view(path.c_str()).error.raise(); }
|
|
546
|
-
template <typename index_at> void clear_index(index_at& index) { index.clear(); }
|
|
547
|
-
template <typename index_at> std::size_t get_expansion_add(index_at const &index) { return index.config().expansion_add; }
|
|
548
|
-
template <typename index_at> std::size_t get_expansion_search(index_at const &index) { return index.config().expansion_search; }
|
|
549
392
|
// clang-format on
|
|
550
393
|
|
|
551
|
-
template <typename
|
|
552
|
-
py::array_t<
|
|
553
|
-
py::array_t<
|
|
394
|
+
template <typename internal_at, typename external_at = internal_at, typename index_at = void>
|
|
395
|
+
py::array_t<external_at> get_typed_member(index_at const& index, label_t label) {
|
|
396
|
+
py::array_t<external_at> result_py(static_cast<Py_ssize_t>(index.scalar_words()));
|
|
554
397
|
auto result_py1d = result_py.template mutable_unchecked<1>();
|
|
555
|
-
index.reconstruct(label, &result_py1d(0));
|
|
398
|
+
index.reconstruct(label, (internal_at*)&result_py1d(0));
|
|
556
399
|
return result_py;
|
|
557
400
|
}
|
|
558
401
|
|
|
559
|
-
template <typename index_at>
|
|
560
|
-
|
|
561
|
-
if (scalar_kind == common_scalar_kind_t::f32_k)
|
|
402
|
+
template <typename index_at> py::array get_member(index_at const& index, label_t label, scalar_kind_t scalar_kind) {
|
|
403
|
+
if (scalar_kind == scalar_kind_t::f32_k)
|
|
562
404
|
return get_typed_member<f32_t>(index, label);
|
|
563
|
-
else if (scalar_kind ==
|
|
405
|
+
else if (scalar_kind == scalar_kind_t::f64_k)
|
|
564
406
|
return get_typed_member<f64_t>(index, label);
|
|
565
|
-
else if (scalar_kind ==
|
|
566
|
-
return get_typed_member<
|
|
567
|
-
else if (scalar_kind ==
|
|
568
|
-
return get_typed_member<f8_bits_t>(index, label);
|
|
407
|
+
else if (scalar_kind == scalar_kind_t::f16_k)
|
|
408
|
+
return get_typed_member<f16_t>(index, label);
|
|
409
|
+
else if (scalar_kind == scalar_kind_t::f8_k)
|
|
410
|
+
return get_typed_member<f8_bits_t, std::int8_t>(index, label);
|
|
411
|
+
else if (scalar_kind == scalar_kind_t::b1x8_k)
|
|
412
|
+
return get_typed_member<b1x8_t, std::uint8_t>(index, label);
|
|
569
413
|
else
|
|
570
414
|
throw std::invalid_argument("Incompatible scalars in the query matrix!");
|
|
571
415
|
}
|
|
@@ -603,10 +447,11 @@ void validate_set(py::array_t<set_member_t> const& set) {
|
|
|
603
447
|
if (has_duplicates(begin, end))
|
|
604
448
|
throw std::invalid_argument("Set must be deduplicated!");
|
|
605
449
|
}
|
|
450
|
+
#if 0
|
|
606
451
|
|
|
607
|
-
inline
|
|
452
|
+
inline std::size_t hash_ror64(std::size_t v, int r) noexcept { return (v >> r) | (v << (64 - r)); }
|
|
608
453
|
|
|
609
|
-
inline
|
|
454
|
+
inline std::size_t hash(std::size_t v) noexcept {
|
|
610
455
|
v ^= hash_ror64(v, 25) ^ hash_ror64(v, 50);
|
|
611
456
|
v *= 0xA24BAED4963EE407UL;
|
|
612
457
|
v ^= hash_ror64(v, 24) ^ hash_ror64(v, 49);
|
|
@@ -617,18 +462,17 @@ inline bits_native_word_t hash(bits_native_word_t v) noexcept {
|
|
|
617
462
|
template <typename scalar_at>
|
|
618
463
|
void hash_typed_row( //
|
|
619
464
|
byte_t const* scalars, std::size_t scalar_stride, std::size_t scalars_count, //
|
|
620
|
-
|
|
465
|
+
b1x8_t* hashes, std::size_t bits) noexcept {
|
|
621
466
|
|
|
622
467
|
std::size_t words = divide_round_up<8>(bits);
|
|
623
468
|
for (std::size_t i = 0; i != scalars_count; ++i) {
|
|
624
469
|
scalar_at scalar;
|
|
625
470
|
std::memcpy(&scalar, scalars + i * scalar_stride, sizeof(scalar_at));
|
|
626
|
-
|
|
471
|
+
std::size_t scalar_hash = hash(scalar);
|
|
627
472
|
hashes[scalar_hash & (words - 1)] |= std::uint8_t(1) << (scalar_hash % 8);
|
|
628
473
|
}
|
|
629
474
|
}
|
|
630
475
|
|
|
631
|
-
#if 0
|
|
632
476
|
template <typename scalar_at>
|
|
633
477
|
py::array_t<bits_numpy_word_t> hash_typed_buffer(py::buffer_info const& info, std::size_t bits) {
|
|
634
478
|
byte_t const* data = reinterpret_cast<byte_t const*>(info.ptr);
|
|
@@ -700,32 +544,32 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
700
544
|
m.attr("USES_SIMSIMD") = py::int_(0);
|
|
701
545
|
#endif
|
|
702
546
|
|
|
703
|
-
py::enum_<
|
|
704
|
-
.value("Unknown",
|
|
705
|
-
.value("IP",
|
|
706
|
-
.value("Cos",
|
|
707
|
-
.value("L2sq",
|
|
708
|
-
.value("Haversine",
|
|
709
|
-
.value("Pearson",
|
|
710
|
-
.value("Jaccard",
|
|
711
|
-
.value("
|
|
712
|
-
.value("
|
|
713
|
-
.value("
|
|
714
|
-
|
|
715
|
-
py::enum_<
|
|
716
|
-
.value("F64",
|
|
717
|
-
.value("F32",
|
|
718
|
-
.value("F16",
|
|
719
|
-
.value("F8",
|
|
720
|
-
.value("B1",
|
|
547
|
+
py::enum_<metric_kind_t>(m, "MetricKind")
|
|
548
|
+
.value("Unknown", metric_kind_t::unknown_k)
|
|
549
|
+
.value("IP", metric_kind_t::ip_k)
|
|
550
|
+
.value("Cos", metric_kind_t::cos_k)
|
|
551
|
+
.value("L2sq", metric_kind_t::l2sq_k)
|
|
552
|
+
.value("Haversine", metric_kind_t::haversine_k)
|
|
553
|
+
.value("Pearson", metric_kind_t::pearson_k)
|
|
554
|
+
.value("Jaccard", metric_kind_t::jaccard_k)
|
|
555
|
+
.value("Hamming", metric_kind_t::hamming_k)
|
|
556
|
+
.value("Tanimoto", metric_kind_t::tanimoto_k)
|
|
557
|
+
.value("Sorensen", metric_kind_t::sorensen_k);
|
|
558
|
+
|
|
559
|
+
py::enum_<scalar_kind_t>(m, "ScalarKind")
|
|
560
|
+
.value("F64", scalar_kind_t::f64_k)
|
|
561
|
+
.value("F32", scalar_kind_t::f32_k)
|
|
562
|
+
.value("F16", scalar_kind_t::f16_k)
|
|
563
|
+
.value("F8", scalar_kind_t::f8_k)
|
|
564
|
+
.value("B1", scalar_kind_t::b1x8_k);
|
|
721
565
|
|
|
722
566
|
auto i = py::class_<punned_index_py_t>(m, "Index");
|
|
723
567
|
|
|
724
568
|
i.def(py::init(&make_index), //
|
|
725
569
|
py::kw_only(), //
|
|
726
570
|
py::arg("ndim") = 0, //
|
|
727
|
-
py::arg("dtype") =
|
|
728
|
-
py::arg("metric") =
|
|
571
|
+
py::arg("dtype") = scalar_kind_t::f32_k, //
|
|
572
|
+
py::arg("metric") = metric_kind_t::ip_k, //
|
|
729
573
|
py::arg("connectivity") = default_connectivity(), //
|
|
730
574
|
py::arg("expansion_add") = default_expansion_add(), //
|
|
731
575
|
py::arg("expansion_search") = default_expansion_search(), //
|
|
@@ -765,69 +609,28 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
765
609
|
i.def_property_readonly("connectivity", &punned_index_py_t::connectivity);
|
|
766
610
|
i.def_property_readonly("capacity", &punned_index_py_t::capacity);
|
|
767
611
|
i.def_property_readonly( //
|
|
768
|
-
"dtype", [](punned_index_py_t const& index) -> std::string { return
|
|
612
|
+
"dtype", [](punned_index_py_t const& index) -> std::string { return scalar_kind_name(index.scalar_kind()); });
|
|
769
613
|
i.def_property_readonly( //
|
|
770
614
|
"memory_usage", [](punned_index_py_t const& index) -> std::size_t { return index.memory_usage(); });
|
|
771
615
|
|
|
772
|
-
i.def_property("expansion_add", &
|
|
773
|
-
i.def_property("expansion_search", &
|
|
616
|
+
i.def_property("expansion_add", &punned_index_py_t::expansion_add, &punned_index_py_t::change_expansion_add);
|
|
617
|
+
i.def_property("expansion_search", &punned_index_py_t::expansion_search,
|
|
774
618
|
&punned_index_py_t::change_expansion_search);
|
|
775
619
|
|
|
776
620
|
i.def_property_readonly("labels", &get_labels<punned_index_py_t>);
|
|
777
621
|
i.def("__contains__", &punned_index_py_t::contains);
|
|
778
622
|
i.def( //
|
|
779
|
-
"__getitem__", &get_member<punned_index_py_t>, py::arg("label"),
|
|
780
|
-
py::arg("dtype") = common_scalar_kind_t::f32_k);
|
|
623
|
+
"__getitem__", &get_member<punned_index_py_t>, py::arg("label"), py::arg("dtype") = scalar_kind_t::f32_k);
|
|
781
624
|
|
|
782
625
|
i.def("save", &save_index<punned_index_py_t>, py::arg("path"));
|
|
783
626
|
i.def("load", &load_index<punned_index_py_t>, py::arg("path"));
|
|
784
627
|
i.def("view", &view_index<punned_index_py_t>, py::arg("path"));
|
|
785
|
-
i.def("clear", &
|
|
786
|
-
|
|
787
|
-
auto bi = py::class_<bits_index_py_t>(m, "BitsIndex");
|
|
788
|
-
|
|
789
|
-
bi.def( //
|
|
790
|
-
py::init(&make_hash_index), //
|
|
791
|
-
py::kw_only(), //
|
|
792
|
-
py::arg("bits"), //
|
|
793
|
-
py::arg("metric") = common_metric_kind_t::bitwise_hamming_k, //
|
|
794
|
-
py::arg("connectivity") = default_connectivity(), //
|
|
795
|
-
py::arg("expansion_add") = default_expansion_add(), //
|
|
796
|
-
py::arg("expansion_search") = default_expansion_search() //
|
|
797
|
-
);
|
|
798
|
-
|
|
799
|
-
bi.def( //
|
|
800
|
-
"add",
|
|
801
|
-
&add_many_to_bits_index, //
|
|
802
|
-
py::arg("label"), //
|
|
803
|
-
py::arg("array"), //
|
|
804
|
-
py::kw_only(), //
|
|
805
|
-
py::arg("copy") = true, //
|
|
806
|
-
py::arg("threads") = 0 //
|
|
807
|
-
);
|
|
808
|
-
|
|
809
|
-
bi.def( //
|
|
810
|
-
"search", &search_many_in_bits_index, //
|
|
811
|
-
py::arg("query"), //
|
|
812
|
-
py::arg("count") = 10, //
|
|
813
|
-
py::arg("exact") = false, //
|
|
814
|
-
py::arg("threads") = 0 //
|
|
815
|
-
);
|
|
816
|
-
|
|
817
|
-
bi.def("__len__", &bits_index_py_t::size);
|
|
818
|
-
bi.def_property_readonly("size", &bits_index_py_t::size);
|
|
819
|
-
bi.def_property_readonly("connectivity", &bits_index_py_t::connectivity);
|
|
820
|
-
bi.def_property_readonly("capacity", &bits_index_py_t::capacity);
|
|
821
|
-
|
|
822
|
-
bi.def("save", &save_index<bits_index_py_t>, py::arg("path"));
|
|
823
|
-
bi.def("load", &load_index<bits_index_py_t>, py::arg("path"));
|
|
824
|
-
bi.def("view", &view_index<bits_index_py_t>, py::arg("path"));
|
|
825
|
-
bi.def("clear", &clear_index<bits_index_py_t>);
|
|
628
|
+
i.def("clear", &punned_index_py_t::clear);
|
|
826
629
|
|
|
827
|
-
auto si = py::class_<
|
|
630
|
+
auto si = py::class_<sparse_index_py_t>(m, "SparseIndex");
|
|
828
631
|
|
|
829
632
|
si.def( //
|
|
830
|
-
py::init(&
|
|
633
|
+
py::init(&make_sparse_index), //
|
|
831
634
|
py::kw_only(), //
|
|
832
635
|
py::arg("connectivity") = default_connectivity(), //
|
|
833
636
|
py::arg("expansion_add") = default_expansion_add(), //
|
|
@@ -836,7 +639,7 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
836
639
|
|
|
837
640
|
si.def( //
|
|
838
641
|
"add",
|
|
839
|
-
[](
|
|
642
|
+
[](sparse_index_py_t& index, label_t label, py::array_t<set_member_t> set, bool copy) {
|
|
840
643
|
validate_set(set);
|
|
841
644
|
if (index.size() + 1 >= index.capacity())
|
|
842
645
|
index.reserve(ceil2(index.size() + 1));
|
|
@@ -855,7 +658,7 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
855
658
|
|
|
856
659
|
si.def( //
|
|
857
660
|
"search",
|
|
858
|
-
[](
|
|
661
|
+
[](sparse_index_py_t& index, py::array_t<set_member_t> set, std::size_t count) -> py::array_t<label_t> {
|
|
859
662
|
validate_set(set);
|
|
860
663
|
auto proxy = set.unchecked<1>();
|
|
861
664
|
auto view = set_view_t{&proxy(0), static_cast<std::size_t>(proxy.shape(0))};
|
|
@@ -871,13 +674,13 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
871
674
|
py::arg("count") = 10 //
|
|
872
675
|
);
|
|
873
676
|
|
|
874
|
-
si.def("__len__", &
|
|
875
|
-
si.def_property_readonly("size", &
|
|
876
|
-
si.def_property_readonly("connectivity", &
|
|
877
|
-
si.def_property_readonly("capacity", &
|
|
677
|
+
si.def("__len__", &sparse_index_py_t::size);
|
|
678
|
+
si.def_property_readonly("size", &sparse_index_py_t::size);
|
|
679
|
+
si.def_property_readonly("connectivity", &sparse_index_py_t::connectivity);
|
|
680
|
+
si.def_property_readonly("capacity", &sparse_index_py_t::capacity);
|
|
878
681
|
|
|
879
|
-
si.def("save", &
|
|
880
|
-
si.def("load", &
|
|
881
|
-
si.def("view", &
|
|
882
|
-
si.def("clear", &
|
|
682
|
+
si.def("save", &sparse_index_py_t::save, py::arg("path"));
|
|
683
|
+
si.def("load", &sparse_index_py_t::load, py::arg("path"));
|
|
684
|
+
si.def("view", &sparse_index_py_t::view, py::arg("path"));
|
|
685
|
+
si.def("clear", &sparse_index_py_t::clear);
|
|
883
686
|
}
|