usearch 0.22.2 → 1.0.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/prerelease.yml +3 -3
- package/.releaserc +9 -0
- package/.vscode/launch.json +15 -11
- package/.vscode/settings.json +12 -2
- package/.vscode/tasks.json +4 -4
- package/CMakeLists.txt +6 -1
- package/Cargo.toml +2 -2
- package/README.md +39 -46
- package/build.rs +2 -2
- package/c/CMakeLists.txt +48 -0
- package/c/README.md +34 -0
- package/c/lib.cpp +83 -72
- package/c/test.c +20 -20
- package/c/usearch.h +132 -32
- package/conanfile.py +1 -1
- package/cpp/CMakeLists.txt +3 -1
- package/cpp/README.md +16 -16
- package/cpp/bench.cpp +50 -126
- package/cpp/test.cpp +164 -77
- package/docs/benchmarks.md +26 -5
- package/docs/c/reference.rst +3 -3
- package/docs/compilation.md +49 -45
- package/docs/cpp/index.rst +2 -2
- package/docs/cpp/reference.rst +13 -3
- package/docs/index.rst +8 -8
- package/docs/java/reference.rst +1 -1
- package/golang/lib.go +275 -141
- package/golang/lib_test.go +105 -0
- package/include/usearch/index.hpp +1733 -1659
- package/include/usearch/index_dense.hpp +1400 -0
- package/include/usearch/index_plugins.hpp +1263 -0
- package/java/README.md +1 -1
- package/java/cloud/unum/usearch/Index.java +10 -10
- package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +34 -37
- package/java/test/IndexTest.java +1 -1
- package/javascript/README.md +7 -7
- package/javascript/lib.cpp +94 -55
- package/javascript/test.js +25 -11
- package/javascript/usearch.d.ts +87 -0
- package/javascript/usearch.js +2 -118
- package/objc/USearchObjective.mm +57 -31
- package/objc/include/USearchObjective.h +32 -22
- package/package.json +5 -3
- package/pyproject.toml +3 -3
- package/python/README.md +23 -14
- package/python/lib.cpp +472 -444
- package/python/scripts/bench.ipynb +3 -2
- package/python/scripts/bench.py +4 -4
- package/python/scripts/index_faiss.py +5 -5
- package/python/scripts/join.py +4 -4
- package/python/scripts/test.py +152 -61
- package/python/scripts/test_jit.py +10 -12
- package/python/scripts/test_sparse.py +32 -0
- package/python/usearch/client.py +26 -28
- package/python/usearch/eval.py +73 -23
- package/python/usearch/index.py +520 -228
- package/python/usearch/io.py +57 -25
- package/python/usearch/numba.py +4 -4
- package/python/usearch/server.py +10 -10
- package/rust/lib.cpp +22 -27
- package/rust/lib.hpp +11 -12
- package/rust/lib.rs +3 -3
- package/setup.py +5 -2
- package/simsimd/.github/workflows/prerelease.yml +73 -0
- package/simsimd/.github/workflows/release.yml +110 -0
- package/simsimd/.github/workflows/update_version.sh +3 -0
- package/simsimd/.releaserc +77 -0
- package/simsimd/.vscode/launch.json +27 -1
- package/simsimd/.vscode/settings.json +22 -1
- package/simsimd/.vscode/tasks.json +6 -0
- package/simsimd/CMakeLists.txt +3 -2
- package/simsimd/README.md +82 -46
- package/simsimd/bench.cxx +63 -24
- package/simsimd/include/simsimd/simsimd.h +88 -66
- package/simsimd/include/simsimd/simsimd_chem.h +126 -0
- package/simsimd/package.json +8 -0
- package/simsimd/pyproject.toml +42 -0
- package/simsimd/python/python.c +81 -0
- package/simsimd/python/run.sh +1 -0
- package/simsimd/python/test.c +109 -0
- package/simsimd/python/test.py +23 -0
- package/simsimd/setup.py +44 -0
- package/swift/Index+Sugar.swift +75 -23
- package/swift/README.md +26 -2
- package/swift/Test.swift +19 -7
- package/swift/USearch.swift +2 -2
- package/tsconfig.json +8 -0
- package/wasm/CMakeLists.txt +1 -0
- package/wasmer.toml +5 -1
- package/wolfram/CMakeLists.txt +1 -0
- package/wolfram/lib.cpp +11 -13
- package/golang/usearch.h +0 -9
- package/golang/usearch_par_test.go +0 -40
- package/golang/usearch_test.go +0 -82
- package/include/usearch/index_punned_dense.hpp +0 -871
- package/include/usearch/index_punned_helpers.hpp +0 -566
package/cpp/test.cpp
CHANGED
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
*/
|
|
4
4
|
#include <algorithm>
|
|
5
5
|
#include <stdexcept>
|
|
6
|
+
#include <unordered_map>
|
|
6
7
|
|
|
7
|
-
#include <usearch/
|
|
8
|
+
#include <usearch/index.hpp>
|
|
9
|
+
#include <usearch/index_dense.hpp>
|
|
10
|
+
#include <usearch/index_plugins.hpp>
|
|
8
11
|
|
|
9
12
|
using namespace unum::usearch;
|
|
10
13
|
using namespace unum;
|
|
@@ -14,93 +17,197 @@ void expect(bool must_be_true) {
|
|
|
14
17
|
throw std::runtime_error("Failed!");
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
template <
|
|
20
|
+
template <bool punned_ak, typename index_at, typename scalar_at, typename... extra_args_at>
|
|
21
|
+
void test_cosine(index_at& index, std::vector<std::vector<scalar_at>> const& vectors, extra_args_at&&... args) {
|
|
18
22
|
|
|
19
23
|
using scalar_t = scalar_at;
|
|
20
|
-
using
|
|
21
|
-
using
|
|
24
|
+
using index_t = index_at;
|
|
25
|
+
using key_t = typename index_t::key_t;
|
|
22
26
|
using distance_t = typename index_t::distance_t;
|
|
23
|
-
using label_t = typename index_t::label_t;
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
// Generate some keys starting from end,
|
|
29
|
+
// for three vectors from the dataset
|
|
30
|
+
key_t const key_max = default_free_value<key_t>() - 1;
|
|
31
|
+
key_t const key_first = key_max - 0;
|
|
32
|
+
key_t const key_second = key_max - 1;
|
|
33
|
+
key_t const key_third = key_max - 2;
|
|
34
|
+
scalar_t const* vector_first = vectors[0].data();
|
|
35
|
+
scalar_t const* vector_second = vectors[1].data();
|
|
36
|
+
scalar_t const* vector_third = vectors[2].data();
|
|
37
|
+
std::size_t dimensions = vectors[0].size();
|
|
28
38
|
|
|
29
39
|
index.reserve(10);
|
|
30
|
-
index.add(
|
|
40
|
+
index.add(key_first, vector_first, args...);
|
|
31
41
|
|
|
32
42
|
// Default approximate search
|
|
33
|
-
|
|
43
|
+
key_t matched_labels[10] = {0};
|
|
34
44
|
distance_t matched_distances[10] = {0};
|
|
35
|
-
std::size_t matched_count = index.search(
|
|
45
|
+
std::size_t matched_count = index.search(vector_first, 5, args...).dump_to(matched_labels, matched_distances);
|
|
36
46
|
|
|
37
47
|
expect(matched_count == 1);
|
|
38
|
-
expect(matched_labels[0] ==
|
|
48
|
+
expect(matched_labels[0] == key_first);
|
|
39
49
|
expect(std::abs(matched_distances[0]) < 0.01);
|
|
40
50
|
|
|
41
51
|
// Add more entries
|
|
42
|
-
|
|
52
|
+
index_search_config_t search_config;
|
|
43
53
|
search_config.exact = true;
|
|
44
|
-
index.add(
|
|
45
|
-
index.add(
|
|
54
|
+
index.add(key_second, vector_second, args...);
|
|
55
|
+
index.add(key_third, vector_third, args...);
|
|
46
56
|
expect(index.size() == 3);
|
|
47
57
|
|
|
48
58
|
// Perform exact search
|
|
49
|
-
matched_count = index.search(
|
|
59
|
+
matched_count = index.search(vector_first, 5, args..., search_config).dump_to(matched_labels, matched_distances);
|
|
50
60
|
|
|
51
61
|
// Validate scans
|
|
52
62
|
std::size_t count = 0;
|
|
53
63
|
for (auto member : index) {
|
|
54
|
-
|
|
55
|
-
expect(id
|
|
64
|
+
key_t id = member.key;
|
|
65
|
+
expect(id <= key_first && id >= key_third);
|
|
56
66
|
count++;
|
|
57
67
|
}
|
|
58
68
|
expect((count == 3));
|
|
59
69
|
expect((index.stats(0).nodes == 3));
|
|
60
70
|
|
|
71
|
+
// Try removals and replacements
|
|
72
|
+
if constexpr (punned_ak) {
|
|
73
|
+
using labeling_result_t = typename index_t::labeling_result_t;
|
|
74
|
+
labeling_result_t result = index.remove(key_third);
|
|
75
|
+
expect(bool(result));
|
|
76
|
+
expect(index.size() == 2);
|
|
77
|
+
index.add(key_third, vector_third, args...);
|
|
78
|
+
expect(index.size() == 3);
|
|
79
|
+
}
|
|
80
|
+
|
|
61
81
|
// Search again over reconstructed index
|
|
62
82
|
index.save("tmp.usearch");
|
|
63
83
|
index.load("tmp.usearch");
|
|
64
|
-
matched_count = index.search(
|
|
84
|
+
matched_count = index.search(vector_first, 5, args...).dump_to(matched_labels, matched_distances);
|
|
65
85
|
expect(matched_count == 3);
|
|
66
|
-
expect(matched_labels[0] ==
|
|
86
|
+
expect(matched_labels[0] == key_first);
|
|
67
87
|
expect(std::abs(matched_distances[0]) < 0.01);
|
|
68
88
|
|
|
89
|
+
if constexpr (punned_ak) {
|
|
90
|
+
std::vector<scalar_t> vec_recovered_from_load(dimensions);
|
|
91
|
+
index.get(key_second, vec_recovered_from_load.data());
|
|
92
|
+
expect(std::equal(vector_second, vector_second + dimensions, vec_recovered_from_load.data()));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Try batch requests
|
|
96
|
+
executor_default_t executor;
|
|
97
|
+
index.reserve({vectors.size(), executor.size()});
|
|
98
|
+
executor.execute_bulk(vectors.size() - 3, [&](std::size_t thread, std::size_t task) {
|
|
99
|
+
index_update_config_t config;
|
|
100
|
+
config.thread = thread;
|
|
101
|
+
index.add(key_max - task - 3, vectors[task + 3].data(), args..., config);
|
|
102
|
+
});
|
|
103
|
+
|
|
69
104
|
// Search again over mapped index
|
|
70
|
-
|
|
105
|
+
// file_head_result_t head = index_dense_metadata("tmp.usearch");
|
|
106
|
+
// expect(head.size == 3);
|
|
71
107
|
index.view("tmp.usearch");
|
|
72
|
-
matched_count = index.search(
|
|
108
|
+
matched_count = index.search(vector_first, 5, args...).dump_to(matched_labels, matched_distances);
|
|
73
109
|
expect(matched_count == 3);
|
|
74
|
-
expect(matched_labels[0] ==
|
|
110
|
+
expect(matched_labels[0] == key_first);
|
|
75
111
|
expect(std::abs(matched_distances[0]) < 0.01);
|
|
76
112
|
|
|
113
|
+
if constexpr (punned_ak) {
|
|
114
|
+
std::vector<scalar_t> vec_recovered_from_view(dimensions);
|
|
115
|
+
index.get(key_second, vec_recovered_from_view.data());
|
|
116
|
+
expect(std::equal(vector_second, vector_second + dimensions, vec_recovered_from_view.data()));
|
|
117
|
+
|
|
118
|
+
auto compaction_result = index.compact();
|
|
119
|
+
expect(bool(compaction_result));
|
|
120
|
+
}
|
|
121
|
+
|
|
77
122
|
expect(index.memory_usage() > 0);
|
|
78
123
|
expect(index.stats().max_edges > 0);
|
|
124
|
+
|
|
125
|
+
// Check metadata
|
|
126
|
+
if constexpr (punned_ak) {
|
|
127
|
+
index_dense_metadata_result_t meta = index_dense_metadata("tmp.usearch");
|
|
128
|
+
expect(bool(meta));
|
|
129
|
+
}
|
|
79
130
|
}
|
|
80
131
|
|
|
81
|
-
template <typename scalar_at, typename
|
|
132
|
+
template <typename scalar_at, typename key_at, typename slot_at> //
|
|
133
|
+
void test_cosine(std::size_t collection_size, std::size_t dimensions) {
|
|
82
134
|
|
|
83
135
|
using scalar_t = scalar_at;
|
|
84
|
-
using
|
|
85
|
-
using
|
|
136
|
+
using key_t = key_at;
|
|
137
|
+
using slot_t = slot_at;
|
|
86
138
|
|
|
87
|
-
|
|
88
|
-
|
|
139
|
+
using index_typed_t = index_gt<float, key_t, slot_t>;
|
|
140
|
+
using member_cref_t = typename index_typed_t::member_cref_t;
|
|
141
|
+
using member_citerator_t = typename index_typed_t::member_citerator_t;
|
|
89
142
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
143
|
+
std::vector<std::vector<scalar_at>> matrix(collection_size);
|
|
144
|
+
for (std::vector<scalar_at>& vector : matrix) {
|
|
145
|
+
vector.resize(dimensions);
|
|
146
|
+
std::generate(vector.begin(), vector.end(), [=] { return float(std::rand()) / float(INT_MAX); });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
struct metric_t {
|
|
150
|
+
std::vector<std::vector<scalar_at>> const* matrix_ptr;
|
|
151
|
+
std::size_t dimensions;
|
|
152
|
+
|
|
153
|
+
scalar_t const* row(std::size_t i) const noexcept { return (*matrix_ptr)[i].data(); }
|
|
154
|
+
|
|
155
|
+
float operator()(member_cref_t const& a, member_cref_t const& b) const {
|
|
156
|
+
return metric_cos_gt<scalar_t>{}(row(get_slot(b)), row(get_slot(a)), dimensions);
|
|
157
|
+
}
|
|
158
|
+
float operator()(scalar_t const* some_vector, member_cref_t const& member) const {
|
|
159
|
+
return metric_cos_gt<scalar_t>{}(some_vector, row(get_slot(member)), dimensions);
|
|
160
|
+
}
|
|
161
|
+
float operator()(member_citerator_t const& a, member_citerator_t const& b) const {
|
|
162
|
+
return metric_cos_gt<scalar_t>{}(row(get_slot(b)), row(get_slot(a)), dimensions);
|
|
163
|
+
}
|
|
164
|
+
float operator()(scalar_t const* some_vector, member_citerator_t const& member) const {
|
|
165
|
+
return metric_cos_gt<scalar_t>{}(some_vector, row(get_slot(member)), dimensions);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// Template:
|
|
170
|
+
for (std::size_t connectivity : {3, 13, 50}) {
|
|
171
|
+
std::printf("- templates with connectivity %zu \n", connectivity);
|
|
172
|
+
metric_t metric{&matrix, dimensions};
|
|
173
|
+
index_config_t config(connectivity);
|
|
174
|
+
index_typed_t index_typed(config);
|
|
175
|
+
test_cosine<false>(index_typed, matrix, metric);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Type-punned:
|
|
179
|
+
for (std::size_t connectivity : {3, 13, 50}) {
|
|
180
|
+
std::printf("- punned with connectivity %zu \n", connectivity);
|
|
181
|
+
using index_t = index_dense_gt<key_t, slot_t>;
|
|
182
|
+
metric_punned_t metric(dimensions, metric_kind_t::cos_k, scalar_kind<scalar_at>());
|
|
183
|
+
index_config_t config(connectivity);
|
|
184
|
+
index_t index = index_t::make(metric, config);
|
|
185
|
+
test_cosine<true>(index, matrix);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
template <typename key_at, typename slot_at> void test_tanimoto(std::size_t dimensions, std::size_t connectivity) {
|
|
190
|
+
|
|
191
|
+
using key_t = key_at;
|
|
192
|
+
using slot_t = slot_at;
|
|
193
|
+
|
|
194
|
+
using index_punned_t = index_dense_gt<key_t, slot_t>;
|
|
195
|
+
std::size_t words = divide_round_up<CHAR_BIT>(dimensions);
|
|
196
|
+
metric_punned_t metric(words, metric_kind_t::tanimoto_k, scalar_kind_t::b1x8_k);
|
|
197
|
+
index_config_t config(connectivity);
|
|
198
|
+
index_punned_t index = index_punned_t::make(metric, config);
|
|
199
|
+
|
|
200
|
+
executor_default_t executor;
|
|
201
|
+
std::size_t batch_size = 1000;
|
|
202
|
+
std::vector<b1x8_t> scalars(batch_size * index.scalar_words());
|
|
203
|
+
std::generate(scalars.begin(), scalars.end(), [] { return static_cast<b1x8_t>(std::rand()); });
|
|
204
|
+
|
|
205
|
+
index.reserve({batch_size + index.size(), executor.size()});
|
|
206
|
+
executor.execute_bulk(batch_size, [&](std::size_t thread, std::size_t task) {
|
|
207
|
+
index_update_config_t config;
|
|
208
|
+
config.thread = thread;
|
|
209
|
+
index.add(task + 25000, scalars.data() + index.scalar_words() * task, config);
|
|
210
|
+
});
|
|
104
211
|
}
|
|
105
212
|
|
|
106
213
|
template <typename index_at> void test_sets(index_at&& index) {
|
|
@@ -121,18 +228,18 @@ template <typename index_at> void test_sets(index_at&& index) {
|
|
|
121
228
|
expect(index.size() == 3);
|
|
122
229
|
}
|
|
123
230
|
|
|
124
|
-
template <typename index_at> void test_sets_moved() {
|
|
231
|
+
template <typename index_at> void test_sets_moved(index_config_t const& config) {
|
|
125
232
|
{
|
|
126
|
-
index_at index;
|
|
233
|
+
index_at index{config};
|
|
127
234
|
test_sets(index);
|
|
128
235
|
}
|
|
129
236
|
{
|
|
130
|
-
index_at index;
|
|
237
|
+
index_at index{config};
|
|
131
238
|
index.reserve(1);
|
|
132
239
|
test_sets(index_at(std::move(index)));
|
|
133
240
|
}
|
|
134
241
|
{
|
|
135
|
-
index_at index;
|
|
242
|
+
index_at index{config};
|
|
136
243
|
index.reserve(1);
|
|
137
244
|
index_at index_moved = std::move(index);
|
|
138
245
|
test_sets(index_moved);
|
|
@@ -141,37 +248,17 @@ template <typename index_at> void test_sets_moved() {
|
|
|
141
248
|
|
|
142
249
|
int main(int, char**) {
|
|
143
250
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
test3d<float>(index_gt<cos_gt<float>, big_point_id_t, std::uint32_t>{});
|
|
156
|
-
test3d<float>(index_gt<l2sq_gt<float>, big_point_id_t, std::uint32_t>{});
|
|
157
|
-
|
|
158
|
-
test3d<double>(index_gt<cos_gt<double>, big_point_id_t, std::uint32_t>{});
|
|
159
|
-
test3d<double>(index_gt<l2sq_gt<double>, big_point_id_t, std::uint32_t>{});
|
|
160
|
-
|
|
161
|
-
test3d<float>(punned_small_t::make(3, metric_kind_t::cos_k));
|
|
162
|
-
test3d<float>(punned_small_t::make(3, metric_kind_t::l2sq_k));
|
|
163
|
-
|
|
164
|
-
test3d<double>(punned_small_t::make(3, metric_kind_t::cos_k));
|
|
165
|
-
test3d<double>(punned_small_t::make(3, metric_kind_t::l2sq_k));
|
|
166
|
-
|
|
167
|
-
test3d_punned<float>(punned_small_t::make(3, metric_kind_t::cos_k));
|
|
168
|
-
test3d_punned<float>(punned_small_t::make(3, metric_kind_t::l2sq_k));
|
|
169
|
-
|
|
170
|
-
test_sets(index_gt<jaccard_gt<std::int32_t, float>, big_point_id_t, std::uint32_t>{});
|
|
171
|
-
test_sets(index_gt<jaccard_gt<std::int64_t, float>, big_point_id_t, std::uint32_t>{});
|
|
172
|
-
|
|
173
|
-
test_sets_moved<index_gt<jaccard_gt<std::int32_t, float>, big_point_id_t, std::uint32_t>>();
|
|
174
|
-
test_sets_moved<index_gt<jaccard_gt<std::int64_t, float>, big_point_id_t, std::uint32_t>>();
|
|
251
|
+
for (std::size_t collection_size : {10, 500})
|
|
252
|
+
for (std::size_t dimensions : {97, 256}) {
|
|
253
|
+
std::printf("Indexing %zu vectors with cos: <float, std::int64_t, std::uint32_t> \n", collection_size);
|
|
254
|
+
test_cosine<float, std::int64_t, std::uint32_t>(collection_size, dimensions);
|
|
255
|
+
std::printf("Indexing %zu vectors with cos: <float, std::int64_t, uint40_t> \n", collection_size);
|
|
256
|
+
test_cosine<float, std::int64_t, uint40_t>(collection_size, dimensions);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
for (std::size_t connectivity : {3, 13, 50})
|
|
260
|
+
for (std::size_t dimensions : {97, 256})
|
|
261
|
+
test_tanimoto<std::int64_t, std::uint32_t>(dimensions, connectivity);
|
|
175
262
|
|
|
176
263
|
return 0;
|
|
177
264
|
}
|
package/docs/benchmarks.md
CHANGED
|
@@ -39,24 +39,24 @@ The main columns are:
|
|
|
39
39
|
| `f32` x256 | 16 | 64 | 32 | 128'644 | 228'422 | 97.2% |
|
|
40
40
|
| `f32` x256 | 16 | 256 | 128 | 39'981 | 69'065 | 99.2% |
|
|
41
41
|
|
|
42
|
-
### Different vectors "
|
|
42
|
+
### Different vectors "quantization"
|
|
43
43
|
|
|
44
44
|
| Vectors | Connectivity | EF @ A | EF @ S | **Add**, QPS | **Search**, QPS | **Recall @1** |
|
|
45
45
|
| :----------- | :----------: | :----: | :----: | :----------: | :-------------: | ------------: |
|
|
46
46
|
| `f32` x256 | 16 | 128 | 64 | 87'995 | 171'856 | 99.1% |
|
|
47
47
|
| `f16` x256 | 16 | 128 | 64 | 87'270 | 153'788 | 98.4% |
|
|
48
48
|
| `f16` x256 ✳️ | 16 | 128 | 64 | 71'454 | 132'673 | 98.4% |
|
|
49
|
-
| `
|
|
49
|
+
| `i8` x256 | 16 | 128 | 64 | 115'923 | 274'653 | 98.9% |
|
|
50
50
|
|
|
51
|
-
As seen on the chart, for `f16`
|
|
52
|
-
Also worth noting, 8-bit quantization results in almost no
|
|
51
|
+
As seen on the chart, for `f16` quantization, performance may differ depending on native hardware support for that numeric type.
|
|
52
|
+
Also worth noting, 8-bit quantization results in almost no quantization loss and may perform better than `f16`.
|
|
53
53
|
|
|
54
54
|
## Utilities
|
|
55
55
|
|
|
56
56
|
Within this repository you will find two commonly used utilities:
|
|
57
57
|
|
|
58
58
|
- `cpp/bench.cpp` the produces the `bench` binary for broad USearch benchmarks.
|
|
59
|
-
- `python/bench.py` for
|
|
59
|
+
- `python/bench.py` and `python/bench.ipynb` for interactive charts against FAISS.
|
|
60
60
|
|
|
61
61
|
To achieve best highest results we suggest compiling locally for the target architecture.
|
|
62
62
|
|
|
@@ -112,6 +112,27 @@ OPTIONS
|
|
|
112
112
|
-h, --help Print this help information on this tool and exit
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
Here is an example of running the C++ benchmark:
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
./build_release/bench \
|
|
119
|
+
--vectors datasets/wiki_1M/base.1M.fbin \
|
|
120
|
+
--queries datasets/wiki_1M/query.public.100K.fbin \
|
|
121
|
+
--neighbors datasets/wiki_1M/groundtruth.public.100K.ibin
|
|
122
|
+
|
|
123
|
+
./build_release/bench \
|
|
124
|
+
--vectors datasets/t2i_1B/base.1B.fbin \
|
|
125
|
+
--queries datasets/t2i_1B/query.public.100K.fbin \
|
|
126
|
+
--neighbors datasets/t2i_1B/groundtruth.public.100K.ibin \
|
|
127
|
+
--output datasets/t2i_1B/index.usearch \
|
|
128
|
+
--cos
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
> Optional parameters include `connectivity`, `expansion_add`, `expansion_search`.
|
|
133
|
+
|
|
134
|
+
For Python, jut open the Jupyter Notebook and start playing around.
|
|
135
|
+
|
|
115
136
|
## Datasets
|
|
116
137
|
|
|
117
138
|
BigANN benchmark is a good starting point, if you are searching for large collections of high-dimensional vectors.
|
package/docs/c/reference.rst
CHANGED
package/docs/compilation.md
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# Compiling USearch
|
|
2
2
|
|
|
3
|
+
This document describes the workflow for advanced USearch users and enthusiasts willing to contribute to the project.
|
|
3
4
|
Before building the first time, please pull submodules.
|
|
4
5
|
|
|
5
6
|
```sh
|
|
6
7
|
git submodule update --init --recursive
|
|
7
8
|
```
|
|
8
9
|
|
|
9
|
-
## C++
|
|
10
|
+
## C++11
|
|
10
11
|
|
|
11
12
|
Linux:
|
|
12
13
|
|
|
@@ -43,29 +44,19 @@ cmake -B ./build_release \
|
|
|
43
44
|
Linting:
|
|
44
45
|
|
|
45
46
|
```sh
|
|
46
|
-
cppcheck --enable=all --suppress=cstyleCast --suppress=unusedFunction \
|
|
47
|
+
cppcheck --enable=all --force --suppress=cstyleCast --suppress=unusedFunction \
|
|
47
48
|
include/usearch/index.hpp \
|
|
48
|
-
include/
|
|
49
|
-
include/
|
|
49
|
+
include/index_dense.hpp \
|
|
50
|
+
include/index_plugins.hpp
|
|
50
51
|
```
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
Testing:
|
|
53
54
|
|
|
54
55
|
```sh
|
|
55
|
-
./
|
|
56
|
-
--vectors datasets/wiki_1M/base.1M.fbin \
|
|
57
|
-
--queries datasets/wiki_1M/query.public.100K.fbin \
|
|
58
|
-
--neighbors datasets/wiki_1M/groundtruth.public.100K.ibin
|
|
59
|
-
|
|
60
|
-
./build_release/bench \
|
|
61
|
-
--vectors datasets/t2i_1B/base.1B.fbin \
|
|
62
|
-
--queries datasets/t2i_1B/query.public.100K.fbin \
|
|
63
|
-
--neighbors datasets/t2i_1B/groundtruth.public.100K.ibin \
|
|
64
|
-
--output datasets/t2i_1B/index.usearch \
|
|
65
|
-
--cos
|
|
56
|
+
cmake -DCMAKE_CXX_COMPILER=gcc-12 -DCMAKE_CXX_COMPILER=g++-12 -DCMAKE_BUILD_TYPE=Debug -B ./build_debug && make -C ./build_debug && ./build_debug/test
|
|
66
57
|
```
|
|
67
58
|
|
|
68
|
-
## Python
|
|
59
|
+
## Python 3
|
|
69
60
|
|
|
70
61
|
Use PyTest to validate the build.
|
|
71
62
|
The `-s` option will disable capturing the logs.
|
|
@@ -89,30 +80,26 @@ pip install cibuildwheel
|
|
|
89
80
|
cibuildwheel --platform linux
|
|
90
81
|
```
|
|
91
82
|
|
|
92
|
-
|
|
83
|
+
## JavaScript
|
|
84
|
+
|
|
85
|
+
Node.JS:
|
|
93
86
|
|
|
94
87
|
```sh
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
--vectors datasets/wiki_1M/base.1M.fbin \
|
|
98
|
-
--queries datasets/wiki_1M/query.public.100K.fbin \
|
|
99
|
-
--neighbors datasets/wiki_1M/groundtruth.public.100K.ibin
|
|
88
|
+
npm install && node javascript/test.js
|
|
89
|
+
npm publish
|
|
100
90
|
```
|
|
101
91
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Checking the effect of different embedding dimensions on construction speed:
|
|
92
|
+
WebAssembly:
|
|
105
93
|
|
|
106
94
|
```sh
|
|
107
|
-
|
|
108
|
-
|
|
95
|
+
emcmake cmake -B ./build -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -s TOTAL_MEMORY=64MB" && emmake make -C ./build
|
|
96
|
+
node ./build/test.js
|
|
109
97
|
```
|
|
110
98
|
|
|
111
|
-
|
|
99
|
+
If you don't yet have `emcmake` installed:
|
|
112
100
|
|
|
113
101
|
```sh
|
|
114
|
-
|
|
115
|
-
npm publish
|
|
102
|
+
git clone https://github.com/emscripten-core/emsdk.git && ./emsdk/emsdk install latest && ./emsdk/emsdk activate latest && source ./emsdk/emsdk_env.sh
|
|
116
103
|
```
|
|
117
104
|
|
|
118
105
|
## Rust
|
|
@@ -156,34 +143,51 @@ java -cp . -Djava.library.path="$(pwd)/java/cloud/unum/usearch/" Index.java
|
|
|
156
143
|
java -cp . -Djava.library.path="$(pwd)/java" cloud.unum.usearch.Index
|
|
157
144
|
```
|
|
158
145
|
|
|
159
|
-
##
|
|
146
|
+
## Objective-C and Swift
|
|
160
147
|
|
|
161
148
|
```sh
|
|
162
|
-
|
|
149
|
+
swift build
|
|
150
|
+
swift test -v
|
|
163
151
|
```
|
|
164
152
|
|
|
165
|
-
##
|
|
153
|
+
## C 99
|
|
154
|
+
|
|
155
|
+
There are a few ways to compile the C 99 USearch SDK.
|
|
156
|
+
Using the Makefile:
|
|
166
157
|
|
|
167
158
|
```sh
|
|
168
|
-
|
|
169
|
-
go test -v
|
|
159
|
+
make -C ./c build
|
|
170
160
|
```
|
|
171
161
|
|
|
172
|
-
|
|
162
|
+
Using CMake:
|
|
173
163
|
|
|
174
164
|
```sh
|
|
175
|
-
|
|
176
|
-
swift test -v
|
|
165
|
+
cmake -B ./build_release -DUSEARCH_BUILD_C=1 && make -C ./build_release -j
|
|
177
166
|
```
|
|
178
167
|
|
|
179
|
-
|
|
168
|
+
Linux:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
g++ -std=c++11 -shared -fPIC c/lib.cpp -I ./include/ -I ./fp16/include/ -I ./robin-map/include/ -o libusearch.so
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
## GoLang
|
|
176
|
+
|
|
177
|
+
GoLang bindings are based on C.
|
|
178
|
+
So one should first compile the C library, link it with GoLang, and only then run tests.
|
|
180
179
|
|
|
181
|
-
|
|
180
|
+
```sh
|
|
181
|
+
cd golang && make -C ../c build && mv ../c/libusearch.so libusearch.so && go test -v
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Wolfram
|
|
182
185
|
|
|
183
186
|
```sh
|
|
184
|
-
|
|
187
|
+
brew install --cask wolfram-engine
|
|
185
188
|
```
|
|
186
189
|
|
|
190
|
+
|
|
187
191
|
## Docker
|
|
188
192
|
|
|
189
193
|
```sh
|
|
@@ -205,10 +209,10 @@ docker buildx create --use &&
|
|
|
205
209
|
--push .
|
|
206
210
|
```
|
|
207
211
|
|
|
208
|
-
|
|
212
|
+
## Sub-Modules
|
|
209
213
|
|
|
210
214
|
Extending metrics in SimSIMD:
|
|
211
215
|
|
|
212
|
-
```
|
|
216
|
+
```sh
|
|
213
217
|
git push --set-upstream https://github.com/ashvardanian/simsimd.git HEAD:main
|
|
214
|
-
```
|
|
218
|
+
```
|
package/docs/cpp/index.rst
CHANGED
package/docs/cpp/reference.rst
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
API Reference
|
|
2
2
|
===============
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
usearch
|
|
6
|
-
|
|
4
|
+
=================================
|
|
5
|
+
usearch/index.hpp
|
|
6
|
+
=================================
|
|
7
7
|
.. doxygenfile:: index.hpp
|
|
8
|
+
|
|
9
|
+
=================================
|
|
10
|
+
usearch/index_plugins.hpp
|
|
11
|
+
=================================
|
|
12
|
+
.. doxygenfile:: index_plugins.hpp
|
|
13
|
+
|
|
14
|
+
=================================
|
|
15
|
+
usearch/index_dense.hpp
|
|
16
|
+
=================================
|
|
17
|
+
.. doxygenfile:: index_dense.hpp
|
package/docs/index.rst
CHANGED
|
@@ -3,27 +3,27 @@ Overview
|
|
|
3
3
|
==========
|
|
4
4
|
.. mdinclude:: ../README.md
|
|
5
5
|
|
|
6
|
-
.. toctree::
|
|
7
|
-
:hidden:
|
|
8
|
-
:caption: �
|
|
9
|
-
|
|
10
|
-
benchmarks
|
|
11
|
-
compilation
|
|
12
|
-
|
|
13
6
|
.. toctree::
|
|
14
7
|
:hidden:
|
|
15
8
|
:caption: �
|
|
16
9
|
|
|
17
10
|
cpp/index
|
|
18
11
|
python/index
|
|
12
|
+
rust/index
|
|
19
13
|
javascript/index
|
|
20
14
|
java/index
|
|
21
|
-
rust/index
|
|
22
15
|
c/index
|
|
23
16
|
swift/index
|
|
24
17
|
golang/index
|
|
25
18
|
wolfram/index
|
|
26
19
|
|
|
20
|
+
.. toctree::
|
|
21
|
+
:hidden:
|
|
22
|
+
:caption: �
|
|
23
|
+
|
|
24
|
+
compilation
|
|
25
|
+
benchmarks
|
|
26
|
+
|
|
27
27
|
.. toctree::
|
|
28
28
|
:hidden:
|
|
29
29
|
:caption: �
|