usearch 1.1.0 → 1.2.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 (71) hide show
  1. package/.github/workflows/prerelease.yml +3 -3
  2. package/.github/workflows/release.yml +3 -3
  3. package/.github/workflows/update_version.sh +1 -1
  4. package/.gitmodules +1 -4
  5. package/Cargo.toml +1 -1
  6. package/Package.swift +2 -1
  7. package/README.md +129 -46
  8. package/binding.gyp +16 -12
  9. package/build.gradle +1 -1
  10. package/build.rs +0 -1
  11. package/c/CMakeLists.txt +11 -7
  12. package/c/lib.cpp +36 -24
  13. package/c/usearch.h +42 -12
  14. package/cluster.ipynb +167 -0
  15. package/conanfile.py +1 -1
  16. package/cpp/CMakeLists.txt +8 -3
  17. package/cpp/bench.cpp +2 -2
  18. package/cpp/test.cpp +42 -25
  19. package/docs/benchmarks.md +8 -0
  20. package/docs/compilation.md +7 -7
  21. package/docs/conf.py +27 -24
  22. package/docs/javascript/reference.rst +3 -0
  23. package/docs/wolfram/index.rst +6 -0
  24. package/golang/README.md +31 -7
  25. package/golang/lib.go +1 -1
  26. package/include/usearch/index.hpp +348 -69
  27. package/include/usearch/index_dense.hpp +587 -179
  28. package/include/usearch/index_plugins.hpp +194 -16
  29. package/java/cloud/unum/usearch/Index.java +13 -0
  30. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +40 -9
  31. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.h +16 -0
  32. package/javascript/docs.js +111 -0
  33. package/javascript/lib.cpp +29 -9
  34. package/javascript/usearch.d.ts +9 -1
  35. package/objc/USearchObjective.mm +11 -11
  36. package/package.json +2 -2
  37. package/pyproject.toml +1 -1
  38. package/python/lib.cpp +492 -159
  39. package/python/scripts/index_faiss.py +3 -3
  40. package/python/scripts/join.py +6 -6
  41. package/python/scripts/test_index.py +219 -0
  42. package/python/scripts/test_sparse.py +21 -21
  43. package/python/scripts/test_tooling.py +111 -0
  44. package/python/usearch/eval.py +9 -5
  45. package/python/usearch/index.py +491 -216
  46. package/rust/lib.cpp +6 -24
  47. package/rust/lib.hpp +1 -3
  48. package/rust/lib.rs +89 -0
  49. package/setup.py +5 -6
  50. package/wasm/CMakeLists.txt +0 -1
  51. package/wasmer.toml +1 -1
  52. package/python/scripts/test.py +0 -364
  53. package/robin-map/.codecov.yml +0 -5
  54. package/robin-map/.github/workflows/ci.yml +0 -136
  55. package/robin-map/CMakeLists.txt +0 -86
  56. package/robin-map/LICENSE +0 -21
  57. package/robin-map/README.md +0 -483
  58. package/robin-map/cmake/tsl-robin-mapConfig.cmake.in +0 -9
  59. package/robin-map/doxygen.conf +0 -2483
  60. package/robin-map/include/tsl/robin_growth_policy.h +0 -406
  61. package/robin-map/include/tsl/robin_hash.h +0 -1639
  62. package/robin-map/include/tsl/robin_map.h +0 -807
  63. package/robin-map/include/tsl/robin_set.h +0 -660
  64. package/robin-map/tests/CMakeLists.txt +0 -26
  65. package/robin-map/tests/custom_allocator_tests.cpp +0 -138
  66. package/robin-map/tests/main.cpp +0 -26
  67. package/robin-map/tests/policy_tests.cpp +0 -97
  68. package/robin-map/tests/robin_map_tests.cpp +0 -1451
  69. package/robin-map/tests/robin_set_tests.cpp +0 -164
  70. package/robin-map/tests/utils.h +0 -443
  71. package/robin-map/tsl-robin-map.natvis +0 -78
@@ -1,406 +0,0 @@
1
- /**
2
- * MIT License
3
- *
4
- * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
5
- *
6
- * Permission is hereby granted, free of charge, to any person obtaining a copy
7
- * of this software and associated documentation files (the "Software"), to deal
8
- * in the Software without restriction, including without limitation the rights
9
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- * copies of the Software, and to permit persons to whom the Software is
11
- * furnished to do so, subject to the following conditions:
12
- *
13
- * The above copyright notice and this permission notice shall be included in
14
- * all copies or substantial portions of the Software.
15
- *
16
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
- * SOFTWARE.
23
- */
24
- #ifndef TSL_ROBIN_GROWTH_POLICY_H
25
- #define TSL_ROBIN_GROWTH_POLICY_H
26
-
27
- #include <algorithm>
28
- #include <array>
29
- #include <climits>
30
- #include <cmath>
31
- #include <cstddef>
32
- #include <cstdint>
33
- #include <iterator>
34
- #include <limits>
35
- #include <ratio>
36
- #include <stdexcept>
37
-
38
- #ifdef TSL_DEBUG
39
- #define tsl_rh_assert(expr) assert(expr)
40
- #else
41
- #define tsl_rh_assert(expr) (static_cast<void>(0))
42
- #endif
43
-
44
- /**
45
- * If exceptions are enabled, throw the exception passed in parameter, otherwise
46
- * call std::terminate.
47
- */
48
- #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
49
- (defined(_MSC_VER) && defined(_CPPUNWIND))) && \
50
- !defined(TSL_NO_EXCEPTIONS)
51
- #define TSL_RH_THROW_OR_TERMINATE(ex, msg) throw ex(msg)
52
- #else
53
- #define TSL_RH_NO_EXCEPTIONS
54
- #ifdef TSL_DEBUG
55
- #include <iostream>
56
- #define TSL_RH_THROW_OR_TERMINATE(ex, msg) \
57
- do { \
58
- std::cerr << msg << std::endl; \
59
- std::terminate(); \
60
- } while (0)
61
- #else
62
- #define TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate()
63
- #endif
64
- #endif
65
-
66
- #if defined(__GNUC__) || defined(__clang__)
67
- #define TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true))
68
- #else
69
- #define TSL_RH_LIKELY(exp) (exp)
70
- #endif
71
-
72
- #define TSL_RH_UNUSED(x) static_cast<void>(x)
73
-
74
- namespace tsl {
75
- namespace rh {
76
-
77
- /**
78
- * Grow the hash table by a factor of GrowthFactor keeping the bucket count to a
79
- * power of two. It allows the table to use a mask operation instead of a modulo
80
- * operation to map a hash to a bucket.
81
- *
82
- * GrowthFactor must be a power of two >= 2.
83
- */
84
- template <std::size_t GrowthFactor>
85
- class power_of_two_growth_policy {
86
- public:
87
- /**
88
- * Called on the hash table creation and on rehash. The number of buckets for
89
- * the table is passed in parameter. This number is a minimum, the policy may
90
- * update this value with a higher value if needed (but not lower).
91
- *
92
- * If 0 is given, min_bucket_count_in_out must still be 0 after the policy
93
- * creation and bucket_for_hash must always return 0 in this case.
94
- */
95
- explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
96
- if (min_bucket_count_in_out > max_bucket_count()) {
97
- TSL_RH_THROW_OR_TERMINATE(std::length_error,
98
- "The hash table exceeds its maximum size.");
99
- }
100
-
101
- if (min_bucket_count_in_out > 0) {
102
- min_bucket_count_in_out =
103
- round_up_to_power_of_two(min_bucket_count_in_out);
104
- m_mask = min_bucket_count_in_out - 1;
105
- } else {
106
- m_mask = 0;
107
- }
108
- }
109
-
110
- /**
111
- * Return the bucket [0, bucket_count()) to which the hash belongs.
112
- * If bucket_count() is 0, it must always return 0.
113
- */
114
- std::size_t bucket_for_hash(std::size_t hash) const noexcept {
115
- return hash & m_mask;
116
- }
117
-
118
- /**
119
- * Return the number of buckets that should be used on next growth.
120
- */
121
- std::size_t next_bucket_count() const {
122
- if ((m_mask + 1) > max_bucket_count() / GrowthFactor) {
123
- TSL_RH_THROW_OR_TERMINATE(std::length_error,
124
- "The hash table exceeds its maximum size.");
125
- }
126
-
127
- return (m_mask + 1) * GrowthFactor;
128
- }
129
-
130
- /**
131
- * Return the maximum number of buckets supported by the policy.
132
- */
133
- std::size_t max_bucket_count() const {
134
- // Largest power of two.
135
- return (std::numeric_limits<std::size_t>::max() / 2) + 1;
136
- }
137
-
138
- /**
139
- * Reset the growth policy as if it was created with a bucket count of 0.
140
- * After a clear, the policy must always return 0 when bucket_for_hash is
141
- * called.
142
- */
143
- void clear() noexcept { m_mask = 0; }
144
-
145
- private:
146
- static std::size_t round_up_to_power_of_two(std::size_t value) {
147
- if (is_power_of_two(value)) {
148
- return value;
149
- }
150
-
151
- if (value == 0) {
152
- return 1;
153
- }
154
-
155
- --value;
156
- for (std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
157
- value |= value >> i;
158
- }
159
-
160
- return value + 1;
161
- }
162
-
163
- static constexpr bool is_power_of_two(std::size_t value) {
164
- return value != 0 && (value & (value - 1)) == 0;
165
- }
166
-
167
- protected:
168
- static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2,
169
- "GrowthFactor must be a power of two >= 2.");
170
-
171
- std::size_t m_mask;
172
- };
173
-
174
- /**
175
- * Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo
176
- * to map a hash to a bucket. Slower but it can be useful if you want a slower
177
- * growth.
178
- */
179
- template <class GrowthFactor = std::ratio<3, 2>>
180
- class mod_growth_policy {
181
- public:
182
- explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
183
- if (min_bucket_count_in_out > max_bucket_count()) {
184
- TSL_RH_THROW_OR_TERMINATE(std::length_error,
185
- "The hash table exceeds its maximum size.");
186
- }
187
-
188
- if (min_bucket_count_in_out > 0) {
189
- m_mod = min_bucket_count_in_out;
190
- } else {
191
- m_mod = 1;
192
- }
193
- }
194
-
195
- std::size_t bucket_for_hash(std::size_t hash) const noexcept {
196
- return hash % m_mod;
197
- }
198
-
199
- std::size_t next_bucket_count() const {
200
- if (m_mod == max_bucket_count()) {
201
- TSL_RH_THROW_OR_TERMINATE(std::length_error,
202
- "The hash table exceeds its maximum size.");
203
- }
204
-
205
- const double next_bucket_count =
206
- std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
207
- if (!std::isnormal(next_bucket_count)) {
208
- TSL_RH_THROW_OR_TERMINATE(std::length_error,
209
- "The hash table exceeds its maximum size.");
210
- }
211
-
212
- if (next_bucket_count > double(max_bucket_count())) {
213
- return max_bucket_count();
214
- } else {
215
- return std::size_t(next_bucket_count);
216
- }
217
- }
218
-
219
- std::size_t max_bucket_count() const { return MAX_BUCKET_COUNT; }
220
-
221
- void clear() noexcept { m_mod = 1; }
222
-
223
- private:
224
- static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR =
225
- 1.0 * GrowthFactor::num / GrowthFactor::den;
226
- static const std::size_t MAX_BUCKET_COUNT =
227
- std::size_t(double(std::numeric_limits<std::size_t>::max() /
228
- REHASH_SIZE_MULTIPLICATION_FACTOR));
229
-
230
- static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1,
231
- "Growth factor should be >= 1.1.");
232
-
233
- std::size_t m_mod;
234
- };
235
-
236
- namespace detail {
237
-
238
- #if SIZE_MAX >= ULLONG_MAX
239
- #define TSL_RH_NB_PRIMES 51
240
- #elif SIZE_MAX >= ULONG_MAX
241
- #define TSL_RH_NB_PRIMES 40
242
- #else
243
- #define TSL_RH_NB_PRIMES 23
244
- #endif
245
-
246
- static constexpr const std::array<std::size_t, TSL_RH_NB_PRIMES> PRIMES = {{
247
- 1u,
248
- 5u,
249
- 17u,
250
- 29u,
251
- 37u,
252
- 53u,
253
- 67u,
254
- 79u,
255
- 97u,
256
- 131u,
257
- 193u,
258
- 257u,
259
- 389u,
260
- 521u,
261
- 769u,
262
- 1031u,
263
- 1543u,
264
- 2053u,
265
- 3079u,
266
- 6151u,
267
- 12289u,
268
- 24593u,
269
- 49157u,
270
- #if SIZE_MAX >= ULONG_MAX
271
- 98317ul,
272
- 196613ul,
273
- 393241ul,
274
- 786433ul,
275
- 1572869ul,
276
- 3145739ul,
277
- 6291469ul,
278
- 12582917ul,
279
- 25165843ul,
280
- 50331653ul,
281
- 100663319ul,
282
- 201326611ul,
283
- 402653189ul,
284
- 805306457ul,
285
- 1610612741ul,
286
- 3221225473ul,
287
- 4294967291ul,
288
- #endif
289
- #if SIZE_MAX >= ULLONG_MAX
290
- 6442450939ull,
291
- 12884901893ull,
292
- 25769803751ull,
293
- 51539607551ull,
294
- 103079215111ull,
295
- 206158430209ull,
296
- 412316860441ull,
297
- 824633720831ull,
298
- 1649267441651ull,
299
- 3298534883309ull,
300
- 6597069766657ull,
301
- #endif
302
- }};
303
-
304
- template <unsigned int IPrime>
305
- static constexpr std::size_t mod(std::size_t hash) {
306
- return hash % PRIMES[IPrime];
307
- }
308
-
309
- // MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for
310
- // faster modulo as the compiler can optimize the modulo code better with a
311
- // constant known at the compilation.
312
- static constexpr const std::array<std::size_t (*)(std::size_t),
313
- TSL_RH_NB_PRIMES>
314
- MOD_PRIME = {{
315
- &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>,
316
- &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, &mod<11>,
317
- &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>,
318
- &mod<18>, &mod<19>, &mod<20>, &mod<21>, &mod<22>,
319
- #if SIZE_MAX >= ULONG_MAX
320
- &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>,
321
- &mod<29>, &mod<30>, &mod<31>, &mod<32>, &mod<33>, &mod<34>,
322
- &mod<35>, &mod<36>, &mod<37>, &mod<38>, &mod<39>,
323
- #endif
324
- #if SIZE_MAX >= ULLONG_MAX
325
- &mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>,
326
- &mod<46>, &mod<47>, &mod<48>, &mod<49>, &mod<50>,
327
- #endif
328
- }};
329
-
330
- } // namespace detail
331
-
332
- /**
333
- * Grow the hash table by using prime numbers as bucket count. Slower than
334
- * tsl::rh::power_of_two_growth_policy in general but will probably distribute
335
- * the values around better in the buckets with a poor hash function.
336
- *
337
- * To allow the compiler to optimize the modulo operation, a lookup table is
338
- * used with constant primes numbers.
339
- *
340
- * With a switch the code would look like:
341
- * \code
342
- * switch(iprime) { // iprime is the current prime of the hash table
343
- * case 0: hash % 5ul;
344
- * break;
345
- * case 1: hash % 17ul;
346
- * break;
347
- * case 2: hash % 29ul;
348
- * break;
349
- * ...
350
- * }
351
- * \endcode
352
- *
353
- * Due to the constant variable in the modulo the compiler is able to optimize
354
- * the operation by a series of multiplications, substractions and shifts.
355
- *
356
- * The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34)
357
- * * 5' in a 64 bits environment.
358
- */
359
- class prime_growth_policy {
360
- public:
361
- explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
362
- auto it_prime = std::lower_bound(
363
- detail::PRIMES.begin(), detail::PRIMES.end(), min_bucket_count_in_out);
364
- if (it_prime == detail::PRIMES.end()) {
365
- TSL_RH_THROW_OR_TERMINATE(std::length_error,
366
- "The hash table exceeds its maximum size.");
367
- }
368
-
369
- m_iprime = static_cast<unsigned int>(
370
- std::distance(detail::PRIMES.begin(), it_prime));
371
- if (min_bucket_count_in_out > 0) {
372
- min_bucket_count_in_out = *it_prime;
373
- } else {
374
- min_bucket_count_in_out = 0;
375
- }
376
- }
377
-
378
- std::size_t bucket_for_hash(std::size_t hash) const noexcept {
379
- return detail::MOD_PRIME[m_iprime](hash);
380
- }
381
-
382
- std::size_t next_bucket_count() const {
383
- if (m_iprime + 1 >= detail::PRIMES.size()) {
384
- TSL_RH_THROW_OR_TERMINATE(std::length_error,
385
- "The hash table exceeds its maximum size.");
386
- }
387
-
388
- return detail::PRIMES[m_iprime + 1];
389
- }
390
-
391
- std::size_t max_bucket_count() const { return detail::PRIMES.back(); }
392
-
393
- void clear() noexcept { m_iprime = 0; }
394
-
395
- private:
396
- unsigned int m_iprime;
397
-
398
- static_assert(std::numeric_limits<decltype(m_iprime)>::max() >=
399
- detail::PRIMES.size(),
400
- "The type of m_iprime is not big enough.");
401
- };
402
-
403
- } // namespace rh
404
- } // namespace tsl
405
-
406
- #endif