usearch 0.22.3 → 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 +10 -2
- package/.vscode/launch.json +10 -10
- package/.vscode/settings.json +9 -1
- package/.vscode/tasks.json +4 -4
- package/CMakeLists.txt +6 -1
- package/Cargo.toml +2 -2
- package/README.md +35 -41
- 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 -80
- package/docs/benchmarks.md +26 -5
- package/docs/c/reference.rst +3 -3
- package/docs/compilation.md +31 -49
- package/docs/cpp/index.rst +1 -1
- package/docs/cpp/reference.rst +4 -4
- package/golang/lib.go +275 -141
- package/golang/lib_test.go +105 -0
- package/include/usearch/index.hpp +1681 -1656
- 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 +6 -6
- 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/python/README.md +23 -14
- package/python/lib.cpp +468 -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 +1 -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 -1001
- package/include/usearch/index_punned_helpers.hpp +0 -676
package/cpp/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# USearch C++
|
|
1
|
+
# USearch for C++
|
|
2
2
|
|
|
3
3
|
## Installation
|
|
4
4
|
|
|
@@ -18,15 +18,15 @@ This covers 90% of use-cases.
|
|
|
18
18
|
```c++
|
|
19
19
|
using namespace unum::usearch;
|
|
20
20
|
|
|
21
|
-
index_gt<
|
|
21
|
+
index_gt<metric_cos_gt<float>> index;
|
|
22
22
|
float vec[3] = {0.1, 0.3, 0.2};
|
|
23
23
|
|
|
24
24
|
index.reserve(10);
|
|
25
|
-
index.add(/*
|
|
25
|
+
index.add(/* key: */ 42, /* vector: */ {&vec[0], 3});
|
|
26
26
|
auto results = index.search(/* query: */ {&vec[0], 3}, 5 /* neighbors */);
|
|
27
27
|
|
|
28
28
|
for (std::size_t i = 0; i != results.size(); ++i)
|
|
29
|
-
results[i].element.
|
|
29
|
+
results[i].element.key, results[i].element.vector, results[i].distance;
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
The `add` is thread-safe for concurrent index construction.
|
|
@@ -44,8 +44,8 @@ index.view("index.usearch"); // Memory-mapping from disk
|
|
|
44
44
|
For advanced users, more compile-time abstractions are available.
|
|
45
45
|
|
|
46
46
|
```cpp
|
|
47
|
-
template <typename metric_at =
|
|
48
|
-
typename
|
|
47
|
+
template <typename metric_at = metric_ip_gt<float>, //
|
|
48
|
+
typename key_at = std::size_t, // `uint32_t`, `uuid_t`...
|
|
49
49
|
typename id_at = std::uint32_t, // `uint40_t`, `uint64_t`...
|
|
50
50
|
typename scalar_at = float, // `double`, `half`, `char`...
|
|
51
51
|
typename allocator_at = std::allocator<char>> //
|
|
@@ -63,15 +63,15 @@ struct custom_metric_t {
|
|
|
63
63
|
|
|
64
64
|
The following distances are pre-packaged:
|
|
65
65
|
|
|
66
|
-
- `
|
|
67
|
-
- `
|
|
68
|
-
- `
|
|
69
|
-
- `
|
|
70
|
-
- `
|
|
71
|
-
- `
|
|
72
|
-
- `
|
|
73
|
-
- `
|
|
74
|
-
- `
|
|
66
|
+
- `metric_cos_gt<scalar_t>` for "Cosine" or "Angular" distance.
|
|
67
|
+
- `metric_ip_gt<scalar_t>` for "Inner Product" or "Dot Product" distance.
|
|
68
|
+
- `metric_l2sq_gt<scalar_t>` for the squared "L2" or "Euclidean" distance.
|
|
69
|
+
- `metric_jaccard_gt<scalar_t>` for "Jaccard" distance between two ordered sets of unique elements.
|
|
70
|
+
- `metric_hamming_gt<scalar_t>` for "Hamming" distance, as the number of shared bits in hashes.
|
|
71
|
+
- `metric_tanimoto_gt<scalar_t>` for "Tanimoto" coefficient for bit-strings.
|
|
72
|
+
- `metric_sorensen_gt<scalar_t>` for "Dice-Sorensen" coefficient for bit-strings.
|
|
73
|
+
- `metric_pearson_gt<scalar_t>` for "Pearson" correlation between probability distributions.
|
|
74
|
+
- `metric_haversine_gt<scalar_t>` for "Haversine" or "Great Circle" distance between coordinates used in GIS applications.
|
|
75
75
|
|
|
76
76
|
## Multi-Threading
|
|
77
77
|
|
|
@@ -81,7 +81,7 @@ Instead of spawning additional threads within USearch, we focus on the thread sa
|
|
|
81
81
|
```cpp
|
|
82
82
|
#pragma omp parallel for
|
|
83
83
|
for (std::size_t i = 0; i < n; ++i)
|
|
84
|
-
native.add(
|
|
84
|
+
native.add(key, span_t{vector, dims}, index_update_config_t { .thread = omp_get_thread_num() });
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
During initialization, we allocate enough temporary memory for all the cores on the machine.
|
package/cpp/bench.cpp
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @brief A benchmark for the construction speed of the USearch index
|
|
3
|
-
* and the resulting
|
|
3
|
+
* and the resulting quantization (recall) of the Approximate Nearest Neighbors
|
|
4
4
|
* Search queries.
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -44,14 +44,13 @@
|
|
|
44
44
|
|
|
45
45
|
#include <simsimd/simsimd.h>
|
|
46
46
|
|
|
47
|
-
#include <usearch/
|
|
47
|
+
#include <usearch/index_dense.hpp>
|
|
48
48
|
|
|
49
49
|
using namespace unum::usearch;
|
|
50
50
|
using namespace unum;
|
|
51
51
|
|
|
52
|
-
using
|
|
53
|
-
using
|
|
54
|
-
using vector_view_t = span_gt<float const>;
|
|
52
|
+
using compressed_slot_t = std::uint32_t;
|
|
53
|
+
using float_span_t = span_gt<float const>;
|
|
55
54
|
|
|
56
55
|
template <typename element_at>
|
|
57
56
|
std::size_t offset_of(element_at const* begin, element_at const* end, element_at v) noexcept {
|
|
@@ -151,10 +150,10 @@ struct vectors_view_gt {
|
|
|
151
150
|
template <typename scalar_at, typename vector_id_at> //
|
|
152
151
|
struct persisted_dataset_gt {
|
|
153
152
|
using scalar_t = scalar_at;
|
|
154
|
-
using
|
|
153
|
+
using compressed_slot_t = vector_id_at;
|
|
155
154
|
persisted_matrix_gt<scalar_t> vectors_;
|
|
156
155
|
persisted_matrix_gt<scalar_t> queries_;
|
|
157
|
-
persisted_matrix_gt<
|
|
156
|
+
persisted_matrix_gt<compressed_slot_t> neighborhoods_;
|
|
158
157
|
std::size_t vectors_to_skip_{};
|
|
159
158
|
std::size_t vectors_to_take_{};
|
|
160
159
|
|
|
@@ -174,7 +173,7 @@ struct persisted_dataset_gt {
|
|
|
174
173
|
std::size_t neighborhood_size() const noexcept { return neighborhoods_.cols; }
|
|
175
174
|
scalar_t const* vector(std::size_t i) const noexcept { return vectors_.row(i + vectors_to_skip_); }
|
|
176
175
|
scalar_t const* query(std::size_t i) const noexcept { return queries_.row(i); }
|
|
177
|
-
|
|
176
|
+
compressed_slot_t const* neighborhood(std::size_t i) const noexcept { return neighborhoods_.row(i); }
|
|
178
177
|
|
|
179
178
|
std::size_t vectors_count() const noexcept {
|
|
180
179
|
return vectors_to_take_ ? vectors_to_take_ : (vectors_.rows - vectors_to_skip_);
|
|
@@ -187,11 +186,11 @@ struct persisted_dataset_gt {
|
|
|
187
186
|
template <typename scalar_at, typename vector_id_at> //
|
|
188
187
|
struct in_memory_dataset_gt {
|
|
189
188
|
using scalar_t = scalar_at;
|
|
190
|
-
using
|
|
189
|
+
using compressed_slot_t = vector_id_at;
|
|
191
190
|
|
|
192
191
|
std::vector<scalar_t> vectors_{};
|
|
193
192
|
std::vector<scalar_t> queries_{};
|
|
194
|
-
std::vector<
|
|
193
|
+
std::vector<compressed_slot_t> neighborhoods_{};
|
|
195
194
|
std::size_t dimensions_{};
|
|
196
195
|
std::size_t vectors_count_{};
|
|
197
196
|
std::size_t neighborhood_size_{};
|
|
@@ -210,13 +209,13 @@ struct in_memory_dataset_gt {
|
|
|
210
209
|
std::size_t neighborhood_size() const noexcept { return 1; }
|
|
211
210
|
scalar_t const* vector(std::size_t i) const noexcept { return vectors_.data() + i * dimensions_; }
|
|
212
211
|
scalar_t const* query(std::size_t i) const noexcept { return queries_.data() + i * dimensions_; }
|
|
213
|
-
|
|
212
|
+
compressed_slot_t const* neighborhood(std::size_t i) const noexcept {
|
|
214
213
|
return neighborhoods_.data() + i * neighborhood_size_;
|
|
215
214
|
}
|
|
216
215
|
|
|
217
216
|
scalar_t* vector(std::size_t i) noexcept { return vectors_.data() + i * dimensions_; }
|
|
218
217
|
scalar_t* query(std::size_t i) noexcept { return queries_.data() + i * dimensions_; }
|
|
219
|
-
|
|
218
|
+
compressed_slot_t* neighborhood(std::size_t i) noexcept { return neighborhoods_.data() + i * neighborhood_size_; }
|
|
220
219
|
|
|
221
220
|
vectors_view_gt<scalar_t> vectors_view() const noexcept { return {vector(0), vectors_count(), dimensions()}; }
|
|
222
221
|
};
|
|
@@ -279,7 +278,7 @@ struct running_stats_printer_t {
|
|
|
279
278
|
};
|
|
280
279
|
|
|
281
280
|
template <typename index_at, typename vector_id_at, typename real_at>
|
|
282
|
-
void index_many(index_at&
|
|
281
|
+
void index_many(index_at& index, std::size_t n, vector_id_at const* ids, real_at const* vectors, std::size_t dims) {
|
|
283
282
|
|
|
284
283
|
running_stats_printer_t printer{n, "Indexing"};
|
|
285
284
|
|
|
@@ -287,13 +286,12 @@ void index_many(index_at& native, std::size_t n, vector_id_at const* ids, real_a
|
|
|
287
286
|
#pragma omp parallel for schedule(static, 32)
|
|
288
287
|
#endif
|
|
289
288
|
for (std::size_t i = 0; i < n; ++i) {
|
|
290
|
-
|
|
289
|
+
index_update_config_t config;
|
|
291
290
|
#if USEARCH_USE_OPENMP
|
|
292
291
|
config.thread = omp_get_thread_num();
|
|
293
292
|
#endif
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
native.add(ids[i], vector, config);
|
|
293
|
+
float_span_t vector{vectors + dims * i, dims};
|
|
294
|
+
index.add(ids[i], vector, config);
|
|
297
295
|
printer.progress++;
|
|
298
296
|
if (config.thread == 0)
|
|
299
297
|
printer.refresh();
|
|
@@ -302,7 +300,7 @@ void index_many(index_at& native, std::size_t n, vector_id_at const* ids, real_a
|
|
|
302
300
|
|
|
303
301
|
template <typename index_at, typename vector_id_at, typename real_at>
|
|
304
302
|
void search_many( //
|
|
305
|
-
index_at&
|
|
303
|
+
index_at& index, std::size_t n, real_at const* vectors, std::size_t dims, std::size_t wanted, vector_id_at* ids,
|
|
306
304
|
real_at* distances) {
|
|
307
305
|
|
|
308
306
|
std::string name = "Search " + std::to_string(wanted);
|
|
@@ -312,12 +310,12 @@ void search_many( //
|
|
|
312
310
|
#pragma omp parallel for schedule(static, 32)
|
|
313
311
|
#endif
|
|
314
312
|
for (std::size_t i = 0; i < n; ++i) {
|
|
315
|
-
|
|
313
|
+
index_search_config_t config;
|
|
316
314
|
#if USEARCH_USE_OPENMP
|
|
317
315
|
config.thread = omp_get_thread_num();
|
|
318
316
|
#endif
|
|
319
|
-
|
|
320
|
-
|
|
317
|
+
float_span_t vector{vectors + dims * i, dims};
|
|
318
|
+
index.search(vector, wanted, config).dump_to(ids + wanted * i, distances + wanted * i);
|
|
321
319
|
printer.progress++;
|
|
322
320
|
if (config.thread == 0)
|
|
323
321
|
printer.refresh();
|
|
@@ -326,22 +324,20 @@ void search_many( //
|
|
|
326
324
|
|
|
327
325
|
template <typename dataset_at, typename index_at> //
|
|
328
326
|
static void single_shot(dataset_at& dataset, index_at& index, bool construct = true) {
|
|
329
|
-
using label_t = typename index_at::label_t;
|
|
330
327
|
using distance_t = typename index_at::distance_t;
|
|
331
|
-
|
|
332
|
-
constexpr label_t missing_label = std::numeric_limits<label_t>::max();
|
|
328
|
+
constexpr default_key_t missing_key = std::numeric_limits<default_key_t>::max();
|
|
333
329
|
|
|
334
330
|
std::printf("\n");
|
|
335
331
|
std::printf("------------\n");
|
|
336
332
|
if (construct) {
|
|
337
333
|
// Perform insertions, evaluate speed
|
|
338
|
-
std::vector<
|
|
334
|
+
std::vector<default_key_t> ids(dataset.vectors_count());
|
|
339
335
|
std::iota(ids.begin(), ids.end(), 0);
|
|
340
336
|
index_many(index, dataset.vectors_count(), ids.data(), dataset.vector(0), dataset.dimensions());
|
|
341
337
|
}
|
|
342
338
|
|
|
343
339
|
// Perform search, evaluate speed
|
|
344
|
-
std::vector<
|
|
340
|
+
std::vector<default_key_t> found_neighbors(dataset.queries_count() * dataset.neighborhood_size());
|
|
345
341
|
std::vector<distance_t> found_distances(dataset.queries_count() * dataset.neighborhood_size());
|
|
346
342
|
search_many(index, dataset.queries_count(), dataset.query(0), dataset.dimensions(), dataset.neighborhood_size(),
|
|
347
343
|
found_neighbors.data(), found_distances.data());
|
|
@@ -352,39 +348,39 @@ static void single_shot(dataset_at& dataset, index_at& index, bool construct = t
|
|
|
352
348
|
auto expected = dataset.neighborhood(i);
|
|
353
349
|
auto received = found_neighbors.data() + i * dataset.neighborhood_size();
|
|
354
350
|
recall_at_1 += expected[0] == received[0];
|
|
355
|
-
recall_full += contains(received, received + dataset.neighborhood_size(),
|
|
351
|
+
recall_full += contains(received, received + dataset.neighborhood_size(), default_key_t{expected[0]});
|
|
356
352
|
}
|
|
357
353
|
|
|
358
354
|
std::printf("Recall@1 %.2f %%\n", recall_at_1 * 100.f / dataset.queries_count());
|
|
359
355
|
std::printf("Recall %.2f %%\n", recall_full * 100.f / dataset.queries_count());
|
|
360
356
|
|
|
361
357
|
// Perform joins
|
|
362
|
-
std::vector<
|
|
363
|
-
std::vector<
|
|
358
|
+
std::vector<default_key_t> man_to_woman(dataset.vectors_count());
|
|
359
|
+
std::vector<default_key_t> woman_to_man(dataset.vectors_count());
|
|
364
360
|
std::size_t join_attempts = 0;
|
|
365
361
|
{
|
|
366
362
|
index_at& men = index;
|
|
367
363
|
index_at women = index.copy().index;
|
|
368
|
-
std::fill(man_to_woman.begin(), man_to_woman.end(),
|
|
369
|
-
std::fill(woman_to_man.begin(), woman_to_man.end(),
|
|
364
|
+
std::fill(man_to_woman.begin(), man_to_woman.end(), missing_key);
|
|
365
|
+
std::fill(woman_to_man.begin(), woman_to_man.end(), missing_key);
|
|
370
366
|
{
|
|
371
367
|
executor_default_t executor(index.limits().threads());
|
|
372
368
|
running_stats_printer_t printer{1, "Join"};
|
|
373
|
-
join_result_t result =
|
|
374
|
-
men, women,
|
|
375
|
-
man_to_woman.data(), woman_to_man.data(),
|
|
369
|
+
join_result_t result = join( //
|
|
370
|
+
men, women, index_join_config_t{executor.size()}, //
|
|
371
|
+
man_to_woman.data(), woman_to_man.data(), //
|
|
376
372
|
executor, [&](std::size_t progress, std::size_t total) {
|
|
377
373
|
if (progress % 1000 == 0)
|
|
378
374
|
printer.print(progress, total);
|
|
379
375
|
});
|
|
380
|
-
join_attempts = result.
|
|
376
|
+
join_attempts = result.visited_members;
|
|
381
377
|
}
|
|
382
378
|
}
|
|
383
379
|
// Evaluate join quality
|
|
384
380
|
std::size_t recall_join = 0, unmatched_count = 0;
|
|
385
381
|
for (std::size_t i = 0; i != index.size(); ++i) {
|
|
386
|
-
recall_join += man_to_woman[i] == static_cast<
|
|
387
|
-
unmatched_count += man_to_woman[i] ==
|
|
382
|
+
recall_join += man_to_woman[i] == static_cast<default_key_t>(i);
|
|
383
|
+
unmatched_count += man_to_woman[i] == missing_key;
|
|
388
384
|
}
|
|
389
385
|
std::printf("Recall Joins %.2f %%\n", recall_join * 100.f / index.size());
|
|
390
386
|
std::printf("Unmatched %.2f %% (%zu items)\n", unmatched_count * 100.f / index.size(), unmatched_count);
|
|
@@ -453,10 +449,9 @@ struct args_t {
|
|
|
453
449
|
bool help = false;
|
|
454
450
|
|
|
455
451
|
bool big = false;
|
|
456
|
-
bool native = false;
|
|
457
452
|
|
|
458
453
|
bool quantize_f16 = false;
|
|
459
|
-
bool
|
|
454
|
+
bool quantize_i8 = false;
|
|
460
455
|
bool quantize_b1 = false;
|
|
461
456
|
|
|
462
457
|
bool metric_ip = false;
|
|
@@ -483,11 +478,11 @@ struct args_t {
|
|
|
483
478
|
return metric_kind_t::ip_k;
|
|
484
479
|
}
|
|
485
480
|
|
|
486
|
-
scalar_kind_t
|
|
481
|
+
scalar_kind_t quantization() const noexcept {
|
|
487
482
|
if (quantize_f16)
|
|
488
483
|
return scalar_kind_t::f16_k;
|
|
489
|
-
if (
|
|
490
|
-
return scalar_kind_t::
|
|
484
|
+
if (quantize_i8)
|
|
485
|
+
return scalar_kind_t::i8_k;
|
|
491
486
|
if (quantize_b1)
|
|
492
487
|
return scalar_kind_t::b1x8_k;
|
|
493
488
|
return scalar_kind_t::f32_k;
|
|
@@ -497,15 +492,16 @@ struct args_t {
|
|
|
497
492
|
template <typename index_at, typename dataset_at> //
|
|
498
493
|
void run_punned(dataset_at& dataset, args_t const& args, index_config_t config, index_limits_t limits) {
|
|
499
494
|
|
|
500
|
-
scalar_kind_t
|
|
501
|
-
std::printf("--
|
|
495
|
+
scalar_kind_t quantization = args.quantization();
|
|
496
|
+
std::printf("-- Quantization: %s\n", scalar_kind_name(quantization));
|
|
502
497
|
|
|
503
498
|
metric_kind_t kind = args.metric();
|
|
504
499
|
std::printf("-- Metric: %s\n", metric_kind_name(kind));
|
|
505
500
|
|
|
506
|
-
|
|
501
|
+
metric_punned_t metric(dataset.dimensions(), kind, quantization);
|
|
502
|
+
index_at index = index_at::make(metric, config);
|
|
507
503
|
index.reserve(limits);
|
|
508
|
-
std::printf("-- Hardware acceleration: %s\n", isa_name(index.metric().
|
|
504
|
+
std::printf("-- Hardware acceleration: %s\n", isa_name(index.metric().isa_kind()));
|
|
509
505
|
std::printf("Will benchmark in-memory\n");
|
|
510
506
|
|
|
511
507
|
single_shot(dataset, index, true);
|
|
@@ -535,74 +531,6 @@ void run_typed(dataset_at& dataset, args_t const& args, index_config_t config, i
|
|
|
535
531
|
single_shot(dataset, index_view, false);
|
|
536
532
|
}
|
|
537
533
|
|
|
538
|
-
template <typename neighbor_id_at, typename dataset_at> //
|
|
539
|
-
void run_big_or_small(dataset_at& dataset, args_t const& args, index_config_t config, index_limits_t limits) {
|
|
540
|
-
if (args.native) {
|
|
541
|
-
if (args.metric_cos) {
|
|
542
|
-
std::printf("-- Metric: Angular\n");
|
|
543
|
-
run_typed<index_gt<cos_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
|
|
544
|
-
} else if (args.metric_l2) {
|
|
545
|
-
std::printf("-- Metric: Euclidean\n");
|
|
546
|
-
run_typed<index_gt<l2sq_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
|
|
547
|
-
} else if (args.metric_haversine) {
|
|
548
|
-
std::printf("-- Metric: Haversine\n");
|
|
549
|
-
run_typed<index_gt<haversine_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
|
|
550
|
-
} else {
|
|
551
|
-
std::printf("-- Metric: Inner Product\n");
|
|
552
|
-
run_typed<index_gt<ip_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
|
|
553
|
-
}
|
|
554
|
-
} else
|
|
555
|
-
run_punned<index_punned_dense_gt<label_t, neighbor_id_at>>(dataset, args, config, limits);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
void report_alternative_setups() {
|
|
559
|
-
using set_member_t = std::uint32_t;
|
|
560
|
-
using sets_index_t = index_gt<jaccard_gt<set_member_t>, label_t, std::uint32_t>;
|
|
561
|
-
set_member_t set_a[] = {10, 12, 15};
|
|
562
|
-
set_member_t set_b[] = {11, 12, 15, 16};
|
|
563
|
-
sets_index_t index;
|
|
564
|
-
index.reserve(100);
|
|
565
|
-
index.add(10, span_gt<set_member_t const>{&set_a[0], 3});
|
|
566
|
-
index.add(11, span_gt<set_member_t const>{&set_b[0], 4});
|
|
567
|
-
exit(0);
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
void report_expected_losses(persisted_dataset_gt<float, vector_id_t> const& dataset) {
|
|
571
|
-
|
|
572
|
-
auto vec1 = dataset.vector(0);
|
|
573
|
-
auto vec2 = dataset.query(0);
|
|
574
|
-
std::vector<f16_t> vec1f16(dataset.dimensions());
|
|
575
|
-
std::vector<f16_t> vec2f16(dataset.dimensions());
|
|
576
|
-
std::transform(vec1, vec1 + dataset.dimensions(), vec1f16.data(), [](float v) { return v; });
|
|
577
|
-
std::transform(vec2, vec2 + dataset.dimensions(), vec2f16.data(), [](float v) { return v; });
|
|
578
|
-
std::vector<f8_bits_t> vec1f8(dataset.dimensions());
|
|
579
|
-
std::vector<f8_bits_t> vec2f8(dataset.dimensions());
|
|
580
|
-
std::transform(vec1, vec1 + dataset.dimensions(), vec1f8.data(), [](float v) { return v; });
|
|
581
|
-
std::transform(vec2, vec2 + dataset.dimensions(), vec2f8.data(), [](float v) { return v; });
|
|
582
|
-
|
|
583
|
-
#if 0
|
|
584
|
-
auto ip_default = ip_gt<float>{}(vec1, vec2, dataset.dimensions());
|
|
585
|
-
auto ip_f16 = ip_gt<f16_t, float>{}(vec1f16.data(), vec2f16.data(), dataset.dimensions());
|
|
586
|
-
auto ip_f8 = ip_gt<f8_bits_t, float>{}(vec1f8.data(), vec2f8.data(), dataset.dimensions());
|
|
587
|
-
|
|
588
|
-
#if defined(__AVX512F__)
|
|
589
|
-
auto ip_f16avx512 = 1.f - simsimd_dot_f16x16avx512((simsimd_f16_t const*)vec1f16.data(),
|
|
590
|
-
(simsimd_f16_t const*)vec2f16.data(), dataset.dimensions());
|
|
591
|
-
#endif
|
|
592
|
-
#if defined(__ARM_FEATURE_SVE)
|
|
593
|
-
auto ip_sve = 1.f - simsimd_dot_f32sve(vec1, vec2, dataset.dimensions());
|
|
594
|
-
auto ip_f16sve = 1.f - simsimd_dot_f16sve((simsimd_f16_t const*)vec1f16.data(),
|
|
595
|
-
(simsimd_f16_t const*)vec2f16.data(), dataset.dimensions());
|
|
596
|
-
#endif
|
|
597
|
-
#if defined(__ARM_NEON)
|
|
598
|
-
auto ip_neon = 1.f - simsimd_dot_f32x4neon(vec1, vec2, dataset.dimensions());
|
|
599
|
-
auto ip_f16neon = 1.f - simsimd_dot_f16x8neon((simsimd_f16_t const*)vec1f16.data(),
|
|
600
|
-
(simsimd_f16_t const*)vec2f16.data(), dataset.dimensions());
|
|
601
|
-
#endif
|
|
602
|
-
exit(0);
|
|
603
|
-
#endif
|
|
604
|
-
}
|
|
605
|
-
|
|
606
534
|
int main(int argc, char** argv) {
|
|
607
535
|
|
|
608
536
|
// Print backtrace if something goes wrong.
|
|
@@ -624,9 +552,8 @@ int main(int argc, char** argv) {
|
|
|
624
552
|
(option("--rows-skip") & value("integer", args.vectors_to_skip)).doc("Number of vectors to skip"),
|
|
625
553
|
(option("--rows-take") & value("integer", args.vectors_to_take)).doc("Number of vectors to take"),
|
|
626
554
|
( //
|
|
627
|
-
option("--native").set(args.native).doc("Use raw templates instead of type-punned classes") |
|
|
628
555
|
option("-f16", "--f16quant").set(args.quantize_f16).doc("Enable `f16_t` quantization") |
|
|
629
|
-
option("-
|
|
556
|
+
option("-i8", "--i8quant").set(args.quantize_i8).doc("Enable `i8_t` quantization") |
|
|
630
557
|
option("-b1", "--b1quant").set(args.quantize_b1).doc("Enable `b1x8_t` quantization")),
|
|
631
558
|
( //
|
|
632
559
|
option("--ip").set(args.metric_ip).doc("Choose Inner Product metric") |
|
|
@@ -648,7 +575,7 @@ int main(int argc, char** argv) {
|
|
|
648
575
|
}
|
|
649
576
|
|
|
650
577
|
#if USEARCH_USE_OPENMP
|
|
651
|
-
// Instead of relying on `multithreaded` from "
|
|
578
|
+
// Instead of relying on `multithreaded` from "index_dense.hpp" we will use OpenMP
|
|
652
579
|
// to better estimate statistics between tasks batches, without having to recreate
|
|
653
580
|
// the threads.
|
|
654
581
|
omp_set_dynamic(true);
|
|
@@ -661,7 +588,7 @@ int main(int argc, char** argv) {
|
|
|
661
588
|
std::printf("-- Query vectors path: %s\n", args.path_queries.c_str());
|
|
662
589
|
std::printf("-- Ground truth neighbors path: %s\n", args.path_neighbors.c_str());
|
|
663
590
|
|
|
664
|
-
persisted_dataset_gt<float,
|
|
591
|
+
persisted_dataset_gt<float, compressed_slot_t> dataset{
|
|
665
592
|
args.path_vectors.c_str(), //
|
|
666
593
|
args.path_queries.c_str(), //
|
|
667
594
|
args.path_neighbors.c_str(), //
|
|
@@ -676,27 +603,24 @@ int main(int argc, char** argv) {
|
|
|
676
603
|
// report_alternative_setups();
|
|
677
604
|
// report_expected_losses(dataset);
|
|
678
605
|
|
|
679
|
-
|
|
680
|
-
config.connectivity = args.connectivity;
|
|
681
|
-
// config.expansion_add = args.expansion_add;
|
|
682
|
-
// config.expansion_search = args.expansion_search;
|
|
606
|
+
index_dense_config_t config(args.connectivity, args.expansion_add, args.expansion_search);
|
|
683
607
|
index_limits_t limits;
|
|
684
608
|
limits.threads_add = limits.threads_search = args.threads;
|
|
685
609
|
limits.members = dataset.vectors_count();
|
|
686
610
|
|
|
687
611
|
std::printf("- Index: \n");
|
|
688
612
|
std::printf("-- Connectivity: %zu\n", config.connectivity);
|
|
689
|
-
|
|
690
|
-
|
|
613
|
+
std::printf("-- Expansion @ Add: %zu\n", config.expansion_add);
|
|
614
|
+
std::printf("-- Expansion @ Search: %zu\n", config.expansion_search);
|
|
691
615
|
|
|
692
616
|
if (args.big)
|
|
693
617
|
#ifdef USEARCH_64BIT_ENV
|
|
694
|
-
|
|
618
|
+
run_punned<index_dense_gt<default_key_t, uint40_t>>(dataset, args, config, limits);
|
|
695
619
|
#else
|
|
696
620
|
std::printf("Error: Don't use 40 bit identifiers in 32bit environment\n");
|
|
697
621
|
#endif
|
|
698
622
|
else
|
|
699
|
-
|
|
623
|
+
run_punned<index_dense_gt<default_key_t, std::uint32_t>>(dataset, args, config, limits);
|
|
700
624
|
|
|
701
625
|
return 0;
|
|
702
626
|
}
|