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.
Files changed (96) hide show
  1. package/.github/workflows/prerelease.yml +3 -3
  2. package/.releaserc +9 -0
  3. package/.vscode/launch.json +15 -11
  4. package/.vscode/settings.json +12 -2
  5. package/.vscode/tasks.json +4 -4
  6. package/CMakeLists.txt +6 -1
  7. package/Cargo.toml +2 -2
  8. package/README.md +39 -46
  9. package/build.rs +2 -2
  10. package/c/CMakeLists.txt +48 -0
  11. package/c/README.md +34 -0
  12. package/c/lib.cpp +83 -72
  13. package/c/test.c +20 -20
  14. package/c/usearch.h +132 -32
  15. package/conanfile.py +1 -1
  16. package/cpp/CMakeLists.txt +3 -1
  17. package/cpp/README.md +16 -16
  18. package/cpp/bench.cpp +50 -126
  19. package/cpp/test.cpp +164 -77
  20. package/docs/benchmarks.md +26 -5
  21. package/docs/c/reference.rst +3 -3
  22. package/docs/compilation.md +49 -45
  23. package/docs/cpp/index.rst +2 -2
  24. package/docs/cpp/reference.rst +13 -3
  25. package/docs/index.rst +8 -8
  26. package/docs/java/reference.rst +1 -1
  27. package/golang/lib.go +275 -141
  28. package/golang/lib_test.go +105 -0
  29. package/include/usearch/index.hpp +1733 -1659
  30. package/include/usearch/index_dense.hpp +1400 -0
  31. package/include/usearch/index_plugins.hpp +1263 -0
  32. package/java/README.md +1 -1
  33. package/java/cloud/unum/usearch/Index.java +10 -10
  34. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +34 -37
  35. package/java/test/IndexTest.java +1 -1
  36. package/javascript/README.md +7 -7
  37. package/javascript/lib.cpp +94 -55
  38. package/javascript/test.js +25 -11
  39. package/javascript/usearch.d.ts +87 -0
  40. package/javascript/usearch.js +2 -118
  41. package/objc/USearchObjective.mm +57 -31
  42. package/objc/include/USearchObjective.h +32 -22
  43. package/package.json +5 -3
  44. package/pyproject.toml +3 -3
  45. package/python/README.md +23 -14
  46. package/python/lib.cpp +472 -444
  47. package/python/scripts/bench.ipynb +3 -2
  48. package/python/scripts/bench.py +4 -4
  49. package/python/scripts/index_faiss.py +5 -5
  50. package/python/scripts/join.py +4 -4
  51. package/python/scripts/test.py +152 -61
  52. package/python/scripts/test_jit.py +10 -12
  53. package/python/scripts/test_sparse.py +32 -0
  54. package/python/usearch/client.py +26 -28
  55. package/python/usearch/eval.py +73 -23
  56. package/python/usearch/index.py +520 -228
  57. package/python/usearch/io.py +57 -25
  58. package/python/usearch/numba.py +4 -4
  59. package/python/usearch/server.py +10 -10
  60. package/rust/lib.cpp +22 -27
  61. package/rust/lib.hpp +11 -12
  62. package/rust/lib.rs +3 -3
  63. package/setup.py +5 -2
  64. package/simsimd/.github/workflows/prerelease.yml +73 -0
  65. package/simsimd/.github/workflows/release.yml +110 -0
  66. package/simsimd/.github/workflows/update_version.sh +3 -0
  67. package/simsimd/.releaserc +77 -0
  68. package/simsimd/.vscode/launch.json +27 -1
  69. package/simsimd/.vscode/settings.json +22 -1
  70. package/simsimd/.vscode/tasks.json +6 -0
  71. package/simsimd/CMakeLists.txt +3 -2
  72. package/simsimd/README.md +82 -46
  73. package/simsimd/bench.cxx +63 -24
  74. package/simsimd/include/simsimd/simsimd.h +88 -66
  75. package/simsimd/include/simsimd/simsimd_chem.h +126 -0
  76. package/simsimd/package.json +8 -0
  77. package/simsimd/pyproject.toml +42 -0
  78. package/simsimd/python/python.c +81 -0
  79. package/simsimd/python/run.sh +1 -0
  80. package/simsimd/python/test.c +109 -0
  81. package/simsimd/python/test.py +23 -0
  82. package/simsimd/setup.py +44 -0
  83. package/swift/Index+Sugar.swift +75 -23
  84. package/swift/README.md +26 -2
  85. package/swift/Test.swift +19 -7
  86. package/swift/USearch.swift +2 -2
  87. package/tsconfig.json +8 -0
  88. package/wasm/CMakeLists.txt +1 -0
  89. package/wasmer.toml +5 -1
  90. package/wolfram/CMakeLists.txt +1 -0
  91. package/wolfram/lib.cpp +11 -13
  92. package/golang/usearch.h +0 -9
  93. package/golang/usearch_par_test.go +0 -40
  94. package/golang/usearch_test.go +0 -82
  95. package/include/usearch/index_punned_dense.hpp +0 -871
  96. 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/index_punned_dense.hpp>
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 <typename scalar_at, typename index_at> void test3d(index_at&& index) {
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 view_t = span_gt<scalar_t const>;
21
- using index_t = typename std::remove_reference<index_at>::type;
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
- scalar_t vec[3] = {10, 20, 15};
26
- scalar_t vec_a[3] = {15, 16, 17};
27
- scalar_t vec_b[3] = {16, 17, 18};
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(42, view_t{&vec[0], 3ul});
40
+ index.add(key_first, vector_first, args...);
31
41
 
32
42
  // Default approximate search
33
- label_t matched_labels[10] = {0};
43
+ key_t matched_labels[10] = {0};
34
44
  distance_t matched_distances[10] = {0};
35
- std::size_t matched_count = index.search(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
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] == 42);
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
- search_config_t search_config;
52
+ index_search_config_t search_config;
43
53
  search_config.exact = true;
44
- index.add(43, view_t{&vec_a[0], 3ul});
45
- index.add(44, view_t{&vec_b[0], 3ul});
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(view_t{&vec[0], 3ul}, 5, search_config).dump_to(matched_labels, matched_distances);
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
- label_t id = member.label;
55
- expect(id >= 42 && id <= 44);
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(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
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] == 42);
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
- index_metadata("tmp.usearch");
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(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
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] == 42);
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 index_at> void test3d_punned(index_at&& index) {
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 view_t = span_gt<scalar_t const>;
85
- using span_t = span_gt<scalar_at>;
136
+ using key_t = key_at;
137
+ using slot_t = slot_at;
86
138
 
87
- scalar_t vec42[3] = {10, 20, 15};
88
- scalar_t vec43[3] = {19, 22, 11};
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
- index.reserve(10);
91
- index.add(42, view_t{&vec42[0], 3ul});
92
-
93
- // Reconstruct
94
- scalar_t vec42_reconstructed[3] = {0, 0, 0};
95
- index.get(42, span_t{&vec42_reconstructed[0], 3ul});
96
- expect(vec42_reconstructed[0] == vec42[0]);
97
- expect(vec42_reconstructed[1] == vec42[1]);
98
- expect(vec42_reconstructed[2] == vec42[2]);
99
-
100
- index.add(43, view_t{&vec43[0], 3ul});
101
- expect(index.size() == 2);
102
- index.remove(43);
103
- expect(index.size() == 1);
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
- static_assert(!std::is_same<index_gt<ip_gt<>>::value_type, std::true_type>());
145
- static_assert(!std::is_same<index_gt<cos_gt<>>::value_type, std::true_type>());
146
- static_assert(!std::is_same<index_gt<l2sq_gt<>>::value_type, std::true_type>());
147
- // static_assert(!std::is_same<index_gt<hamming_gt<>>::value_type, std::true_type>());
148
- // static_assert(!std::is_same<index_gt<tanimoto_gt<>>::value_type, std::true_type>());
149
- // static_assert(!std::is_same<index_gt<sorensen_gt<>>::value_type, std::true_type>());
150
- // static_assert(!std::is_same<index_gt<jaccard_gt<>>::value_type, std::true_type>());
151
- // static_assert(!std::is_same<index_gt<pearson_correlation_gt<>>::value_type, std::true_type>());
152
- // static_assert(!std::is_same<index_gt<haversine_gt<>>::value_type, std::true_type>());
153
-
154
- using big_point_id_t = std::int64_t;
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
  }
@@ -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 "accuracy"
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
- | `f8` x256 | 16 | 128 | 64 | 115'923 | 274'653 | 98.9% |
49
+ | `i8` x256 | 16 | 128 | 64 | 115'923 | 274'653 | 98.9% |
50
50
 
51
- As seen on the chart, for `f16` accuracy, performance may differ depending on native hardware support for that numeric type.
52
- Also worth noting, 8-bit quantization results in almost no accuracy loss and may perform better than `f16`.
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 simple benchmarks against FAISS.
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.
@@ -1,7 +1,7 @@
1
1
  API Reference
2
2
  ===============
3
3
 
4
- ===============
5
- usearch
6
- ===============
4
+ ==================
5
+ usearch/usearch.h
6
+ ==================
7
7
  .. doxygenfile:: usearch.h
@@ -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/index_punned_dense.hpp \
49
- include/index_punned_helpers.hpp
49
+ include/index_dense.hpp \
50
+ include/index_plugins.hpp
50
51
  ```
51
52
 
52
- Benchmarking:
53
+ Testing:
53
54
 
54
55
  ```sh
55
- ./build_release/bench \
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
- Benchmarking:
83
+ ## JavaScript
84
+
85
+ Node.JS:
93
86
 
94
87
  ```sh
95
- pip install faiss-cpu
96
- python python/scripts/bench.py speed \
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
- > Optional parameters include `connectivity`, `expansion_add`, `expansion_search`.
103
-
104
- Checking the effect of different embedding dimensions on construction speed:
92
+ WebAssembly:
105
93
 
106
94
  ```sh
107
- python python/scripts/bench.py dimensions ...
108
- python python/scripts/bench.py connectivity ...
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
- ## JavaScript
99
+ If you don't yet have `emcmake` installed:
112
100
 
113
101
  ```sh
114
- npm install && node javascript/test.js
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
- ## Wolfram
146
+ ## Objective-C and Swift
160
147
 
161
148
  ```sh
162
- brew install --cask wolfram-engine
149
+ swift build
150
+ swift test -v
163
151
  ```
164
152
 
165
- ## GoLang
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
- cd golang
169
- go test -v
159
+ make -C ./c build
170
160
  ```
171
161
 
172
- ## Objective-C and Swift
162
+ Using CMake:
173
163
 
174
164
  ```sh
175
- swift build
176
- swift test -v
165
+ cmake -B ./build_release -DUSEARCH_BUILD_C=1 && make -C ./build_release -j
177
166
  ```
178
167
 
179
- ## C
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
- ### Linux
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
- g++ -shared -fPIC lib.cpp -I ../include/ -I ../fp16/include/ -o libusearch.so
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
+ ```
@@ -1,6 +1,6 @@
1
- =======
1
+ =====================
2
2
  C++ SDK
3
- =======
3
+ =====================
4
4
 
5
5
 
6
6
  .. mdinclude:: ../../cpp/README.md
@@ -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: �
@@ -2,6 +2,6 @@ API Reference
2
2
  ===============
3
3
 
4
4
  ===============
5
- usearch
5
+ Index.java
6
6
  ===============
7
7
  .. doxygenfile:: Index.java