usearch 2.7.7 → 2.8.1

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 (54) hide show
  1. package/.dockerignore +93 -0
  2. package/.github/workflows/Dockerfile.libs +6 -2
  3. package/.github/workflows/prerelease.yml +242 -245
  4. package/.github/workflows/release.yml +399 -357
  5. package/.gitignore +1 -0
  6. package/.vscode/settings.json +3 -1
  7. package/CITATION.cff +1 -1
  8. package/CMakeLists.txt +35 -5
  9. package/Cargo.toml +1 -1
  10. package/README.md +1 -1
  11. package/build.gradle +6 -0
  12. package/c/README.md +3 -3
  13. package/c/lib.cpp +75 -11
  14. package/c/usearch.h +22 -1
  15. package/conanfile.py +1 -1
  16. package/cpp/README.md +2 -1
  17. package/cpp/bench.cpp +3 -0
  18. package/cpp/test.cpp +12 -12
  19. package/csharp/nuget/nuget-package.props +1 -1
  20. package/docs/compilation.md +5 -13
  21. package/golang/lib.go +4 -1
  22. package/include/usearch/index.hpp +31 -30
  23. package/include/usearch/index_dense.hpp +49 -48
  24. package/include/usearch/index_plugins.hpp +70 -8
  25. package/java/cloud/unum/usearch/Index.java +118 -26
  26. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +8 -8
  27. package/javascript/usearch.js +1 -0
  28. package/objc/USearchObjective.mm +4 -1
  29. package/objc/include/USearchObjective.h +1 -0
  30. package/package.json +1 -1
  31. package/pyproject.toml +10 -8
  32. package/python/lib.cpp +3 -2
  33. package/python/scripts/bench.ipynb +24 -2
  34. package/python/usearch/index.py +8 -3
  35. package/rust/lib.cpp +14 -12
  36. package/rust/lib.hpp +13 -13
  37. package/rust/lib.rs +47 -0
  38. package/setup.py +15 -3
  39. package/simsimd/.github/workflows/prerelease.yml +7 -8
  40. package/simsimd/.github/workflows/release.yml +18 -20
  41. package/simsimd/README.md +13 -8
  42. package/simsimd/binding.gyp +1 -1
  43. package/simsimd/cpp/bench.cxx +17 -3
  44. package/simsimd/include/simsimd/binary.h +1 -3
  45. package/simsimd/include/simsimd/probability.h +234 -361
  46. package/simsimd/include/simsimd/simsimd.h +95 -65
  47. package/simsimd/include/simsimd/spatial.h +0 -3
  48. package/simsimd/include/simsimd/types.h +12 -7
  49. package/simsimd/javascript/test.js +23 -5
  50. package/simsimd/package.json +2 -2
  51. package/wasmer.toml +1 -1
  52. package/wolfram/lib.cpp +1 -1
  53. package/csharp/eng/build_lib.sh +0 -65
  54. package/csharp/eng/clean.sh +0 -12
@@ -44,10 +44,22 @@
44
44
  // Propagate the `f16` settings
45
45
  #define SIMSIMD_NATIVE_F16 USEARCH_USE_NATIVE_F16
46
46
  #if defined(USEARCH_DEFINED_LINUX)
47
+ #define SIMSIMD_TARGET_X86_AVX512 1
48
+ #define SIMSIMD_TARGET_ARM_SVE 1
49
+ #define SIMSIMD_TARGET_X86_AVX2 1
50
+ #define SIMSIMD_TARGET_ARM_NEON 1
47
51
  #include <simsimd/simsimd.h>
48
52
  #elif defined(USEARCH_DEFINED_APPLE)
49
53
  #define SIMSIMD_TARGET_X86_AVX512 0
50
54
  #define SIMSIMD_TARGET_ARM_SVE 0
55
+ #define SIMSIMD_TARGET_X86_AVX2 1
56
+ #define SIMSIMD_TARGET_ARM_NEON 1
57
+ #include <simsimd/simsimd.h>
58
+ #else
59
+ #define SIMSIMD_TARGET_X86_AVX512 0
60
+ #define SIMSIMD_TARGET_ARM_SVE 0
61
+ #define SIMSIMD_TARGET_X86_AVX2 0
62
+ #define SIMSIMD_TARGET_ARM_NEON 0
51
63
  #include <simsimd/simsimd.h>
52
64
  #endif
53
65
  #endif
@@ -100,6 +112,7 @@ enum class metric_kind_t : std::uint8_t {
100
112
  // Custom:
101
113
  pearson_k = 'p',
102
114
  haversine_k = 'h',
115
+ divergence_k = 'd',
103
116
 
104
117
  // Sets:
105
118
  jaccard_k = 'j',
@@ -227,6 +240,7 @@ inline char const* metric_kind_name(metric_kind_t metric) noexcept {
227
240
  case metric_kind_t::l2sq_k: return "l2sq";
228
241
  case metric_kind_t::pearson_k: return "pearson";
229
242
  case metric_kind_t::haversine_k: return "haversine";
243
+ case metric_kind_t::divergence_k: return "divergence";
230
244
  case metric_kind_t::jaccard_k: return "jaccard";
231
245
  case metric_kind_t::hamming_k: return "hamming";
232
246
  case metric_kind_t::tanimoto_k: return "tanimoto";
@@ -263,6 +277,8 @@ inline expected_gt<metric_kind_t> metric_from_name(char const* name, std::size_t
263
277
  parsed.result = metric_kind_t::cos_k;
264
278
  } else if (str_equals(name, len, "haversine")) {
265
279
  parsed.result = metric_kind_t::haversine_k;
280
+ } else if (str_equals(name, len, "divergence")) {
281
+ parsed.result = metric_kind_t::divergence_k;
266
282
  } else if (str_equals(name, len, "pearson")) {
267
283
  parsed.result = metric_kind_t::pearson_k;
268
284
  } else if (str_equals(name, len, "hamming")) {
@@ -272,8 +288,8 @@ inline expected_gt<metric_kind_t> metric_from_name(char const* name, std::size_t
272
288
  } else if (str_equals(name, len, "sorensen")) {
273
289
  parsed.result = metric_kind_t::sorensen_k;
274
290
  } else
275
- parsed.failed(
276
- "Unknown distance, choose: l2sq, ip, cos, haversine, jaccard, pearson, hamming, tanimoto, sorensen");
291
+ parsed.failed("Unknown distance, choose: l2sq, ip, cos, haversine, divergence, jaccard, pearson, hamming, "
292
+ "tanimoto, sorensen");
277
293
  return parsed;
278
294
  }
279
295
 
@@ -556,7 +572,9 @@ class aligned_allocator_gt {
556
572
  using size_type = std::size_t;
557
573
  using pointer = element_at*;
558
574
  using const_pointer = element_at const*;
559
- template <typename other_element_at> struct rebind { using other = aligned_allocator_gt<other_element_at>; };
575
+ template <typename other_element_at> struct rebind {
576
+ using other = aligned_allocator_gt<other_element_at>;
577
+ };
560
578
 
561
579
  constexpr std::size_t alignment() const { return alignment_ak; }
562
580
 
@@ -1166,6 +1184,32 @@ template <typename scalar_at = float, typename result_at = float> struct metric_
1166
1184
  }
1167
1185
  };
1168
1186
 
1187
+ /**
1188
+ * @brief Measures Jensen-Shannon Divergence between two probability distributions.
1189
+ */
1190
+ template <typename scalar_at = float, typename result_at = float> struct metric_divergence_gt {
1191
+ using scalar_t = scalar_at;
1192
+ using result_t = result_at;
1193
+
1194
+ inline result_t operator()(scalar_t const* p, scalar_t const* q, std::size_t dim) const noexcept {
1195
+ result_t kld_pm{}, kld_qm{};
1196
+ scalar_t epsilon = std::numeric_limits<scalar_t>::epsilon();
1197
+ #if USEARCH_USE_OPENMP
1198
+ #pragma omp simd reduction(+ : kld_pm, kld_qm)
1199
+ #elif defined(USEARCH_DEFINED_CLANG)
1200
+ #pragma clang loop vectorize(enable)
1201
+ #elif defined(USEARCH_DEFINED_GCC)
1202
+ #pragma GCC ivdep
1203
+ #endif
1204
+ for (std::size_t i = 0; i != dim; ++i) {
1205
+ result_t mi = result_t(p[i] + q[i]) / 2 + epsilon;
1206
+ kld_pm += p[i] * std::log((p[i] + epsilon) / mi);
1207
+ kld_qm += q[i] * std::log((q[i] + epsilon) / mi);
1208
+ }
1209
+ return (kld_pm + kld_qm) / 2;
1210
+ }
1211
+ };
1212
+
1169
1213
  struct cos_i8_t {
1170
1214
  using scalar_t = i8_t;
1171
1215
  using result_t = f32_t;
@@ -1344,8 +1388,8 @@ class metric_punned_t {
1344
1388
  }
1345
1389
 
1346
1390
  private:
1347
- bool configure_with_simsimd() noexcept {
1348
1391
  #if USEARCH_USE_SIMSIMD
1392
+ bool configure_with_simsimd(simsimd_capability_t simd_caps) noexcept {
1349
1393
  simsimd_metric_kind_t kind = simsimd_metric_unknown_k;
1350
1394
  simsimd_datatype_t datatype = simsimd_datatype_unknown_k;
1351
1395
  simsimd_capability_t allowed = simsimd_cap_any_k;
@@ -1355,6 +1399,7 @@ class metric_punned_t {
1355
1399
  case metric_kind_t::l2sq_k: kind = simsimd_metric_l2sq_k; break;
1356
1400
  case metric_kind_t::hamming_k: kind = simsimd_metric_hamming_k; break;
1357
1401
  case metric_kind_t::jaccard_k: kind = simsimd_metric_jaccard_k; break;
1402
+ default: break;
1358
1403
  }
1359
1404
  switch (scalar_kind_) {
1360
1405
  case scalar_kind_t::f32_k: datatype = simsimd_datatype_f32_k; break;
@@ -1362,20 +1407,25 @@ class metric_punned_t {
1362
1407
  case scalar_kind_t::f16_k: datatype = simsimd_datatype_f16_k; break;
1363
1408
  case scalar_kind_t::i8_k: datatype = simsimd_datatype_i8_k; break;
1364
1409
  case scalar_kind_t::b1x8_k: datatype = simsimd_datatype_b8_k; break;
1410
+ default: break;
1365
1411
  }
1366
1412
  simsimd_metric_punned_t simd_metric = NULL;
1367
1413
  simsimd_capability_t simd_kind = simsimd_cap_any_k;
1368
- simsimd_find_metric_punned(kind, datatype, simsimd_capabilities(), allowed, &simd_metric, &simd_kind);
1414
+ simsimd_find_metric_punned(kind, datatype, simd_caps, allowed, &simd_metric, &simd_kind);
1369
1415
  if (simd_metric == nullptr)
1370
1416
  return false;
1371
1417
 
1372
1418
  raw_ptr_ = (punned_ptr_t)simd_metric;
1373
1419
  isa_kind_ = simd_kind;
1374
1420
  return true;
1421
+ }
1422
+ bool configure_with_simsimd() noexcept {
1423
+ static simsimd_capability_t static_capabilities = simsimd_capabilities();
1424
+ return configure_with_simsimd(static_capabilities);
1425
+ }
1375
1426
  #else
1376
- return false;
1427
+ bool configure_with_simsimd() noexcept { return false; }
1377
1428
  #endif
1378
- }
1379
1429
 
1380
1430
  void configure_with_auto_vectorized() noexcept {
1381
1431
  switch (metric_kind_) {
@@ -1432,6 +1482,17 @@ class metric_punned_t {
1432
1482
  }
1433
1483
  break;
1434
1484
  }
1485
+ case metric_kind_t::divergence_k: {
1486
+ switch (scalar_kind_) {
1487
+ case scalar_kind_t::f16_k:
1488
+ raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_divergence_gt<f16_t, f32_t>>;
1489
+ break;
1490
+ case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_divergence_gt<f32_t>>; break;
1491
+ case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_divergence_gt<f64_t>>; break;
1492
+ default: raw_ptr_ = nullptr; break;
1493
+ }
1494
+ break;
1495
+ }
1435
1496
  case metric_kind_t::jaccard_k: // Equivalent to Tanimoto
1436
1497
  case metric_kind_t::tanimoto_k:
1437
1498
  raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_tanimoto_gt<b1x8_t>>,
@@ -2118,7 +2179,8 @@ class flat_hash_multi_set_gt {
2118
2179
  slot_ref_t slot = slot_ref(slot_index);
2119
2180
  if ((~slot.header.populated & slot.mask) | (slot.header.deleted & slot.mask)) {
2120
2181
  // Found an empty or deleted slot
2121
- populated_slots_ += populate_slot(slot, element);
2182
+ populate_slot(slot, element);
2183
+ ++populated_slots_;
2122
2184
  return true;
2123
2185
  }
2124
2186
  // Move to the next slot
@@ -1,83 +1,170 @@
1
1
  package cloud.unum.usearch;
2
2
 
3
3
  import java.io.File;
4
+ import java.io.InputStream;
4
5
  import java.nio.file.Files;
5
6
  import java.nio.file.StandardCopyOption;
6
- import java.io.InputStream;
7
7
 
8
8
  /**
9
- * @brief Java bindings for Unum USearch.
10
- *
11
- * @see Tutorials:
12
- * https://nachtimwald.com/2017/06/06/wrapping-a-c-library-in-java/
13
- * https://www3.ntu.edu.sg/home/ehchua/programming/java/javanativeinterface.html
9
+ * Java bindings for Unum USearch.
10
+ * <p>
11
+ * Provides interface to interact with USearch library and perform various operations related to indexing and searching.
12
+ *
13
+ * @see <a href="https://nachtimwald.com/2017/06/06/wrapping-a-c-library-in-java/">Wrapping a C library in Java</a>
14
+ * @see <a href="https://www3.ntu.edu.sg/home/ehchua/programming/java/javanativeinterface.html">Java Native Interface tutorial</a>
14
15
  */
15
16
  public class Index {
16
17
 
17
18
  private long c_ptr = 0;
18
19
 
19
- public Index(String metric, String quantization, //
20
- long dimensions, long capacity, long connectivity, //
21
- long expansion_add, long expansion_search) {
22
- c_ptr = c_create(
20
+ /**
21
+ * Creates a new instance of Index with specified parameters.
22
+ *
23
+ * @param metric the metric type for distance calculation between vectors
24
+ * @param quantization the scalar type for quantization of vector data
25
+ * @param dimensions the number of dimensions in the vectors
26
+ * @param capacity the initial capacity of the index
27
+ * @param connectivity the connectivity parameter that limits connections-per-node in graph
28
+ * @param expansion_add the expansion factor used for index construction when adding vectors
29
+ * @param expansion_search the expansion factor used for index construction during search operations
30
+ */
31
+ public Index(
32
+ String metric,
33
+ String quantization, //
34
+ long dimensions,
35
+ long capacity,
36
+ long connectivity, //
37
+ long expansion_add,
38
+ long expansion_search
39
+ ) {
40
+ c_ptr =
41
+ c_create(
23
42
  metric,
24
43
  quantization,
25
44
  dimensions,
26
45
  capacity,
27
46
  connectivity,
28
47
  expansion_add,
29
- expansion_search);
48
+ expansion_search
49
+ );
30
50
  }
31
51
 
52
+ /**
53
+ * Retrieves the current size of the index.
54
+ *
55
+ * @return the number of vectors currently indexed.
56
+ */
32
57
  public long size() {
33
58
  return c_size(c_ptr);
34
59
  }
35
60
 
61
+ /**
62
+ * Retrieves the connectivity parameter of the index.
63
+ *
64
+ * @return the connectivity parameter that limits connections-per-node in graph.
65
+ */
36
66
  public long connectivity() {
37
67
  return c_connectivity(c_ptr);
38
68
  }
39
69
 
70
+ /**
71
+ * Retrieves the number of dimensions of the vectors in the index.
72
+ *
73
+ * @return the number of dimensions in the vectors.
74
+ */
40
75
  public long dimensions() {
41
76
  return c_dimensions(c_ptr);
42
77
  }
43
78
 
79
+ /**
80
+ * Retrieves the current capacity of the index.
81
+ *
82
+ * @return the total capacity including current size.
83
+ */
44
84
  public long capacity() {
45
85
  return c_capacity(c_ptr);
46
86
  }
47
87
 
88
+ /**
89
+ * Reserves memory for a specified number of incoming vectors.
90
+ *
91
+ * @param capacity the desired total capacity including current size.
92
+ */
48
93
  public void reserve(long capacity) {
49
94
  c_reserve(c_ptr, capacity);
50
95
  }
51
96
 
97
+ /**
98
+ * Adds a vector with a specified key to the index.
99
+ *
100
+ * @param key the key associated with the vector
101
+ * @param vector the vector data
102
+ */
52
103
  public void add(int key, float vector[]) {
53
104
  c_add(c_ptr, key, vector);
54
105
  }
55
106
 
107
+ /**
108
+ * Searches for closest vectors to the specified query vector.
109
+ *
110
+ * @param vector the query vector data
111
+ * @param count the number of nearest neighbors to search
112
+ * @return an array of keys of the nearest neighbors
113
+ */
56
114
  public int[] search(float vector[], long count) {
57
115
  return c_search(c_ptr, vector, count);
58
116
  }
59
117
 
118
+ /**
119
+ * Saves the index to a file.
120
+ *
121
+ * @param path the file path where the index will be saved.
122
+ */
60
123
  public void save(String path) {
61
124
  c_save(c_ptr, path);
62
125
  }
63
126
 
127
+ /**
128
+ * Loads the index from a file.
129
+ *
130
+ * @param path the file path from where the index will be loaded.
131
+ */
64
132
  public void load(String path) {
65
133
  c_load(c_ptr, path);
66
134
  }
67
135
 
136
+ /**
137
+ * Creates a view of the index from a file without copying it into memory.
138
+ *
139
+ * @param path the file path from where the view will be created.
140
+ */
68
141
  public void view(String path) {
69
142
  c_view(c_ptr, path);
70
143
  }
71
144
 
145
+ /**
146
+ * Removes the vector associated with the given key from the index.
147
+ *
148
+ * @param key the key of the vector to be removed.
149
+ * @return {@code true} if the vector was successfully removed, {@code false} otherwise.
150
+ */
72
151
  public boolean remove(int key) {
73
152
  return c_remove(c_ptr, key);
74
153
  }
75
-
154
+
155
+ /**
156
+ * Renames the vector to map to a different key.
157
+ *
158
+ * @param from the key of the vector to be renamed.
159
+ * @param to the new key for the vector.
160
+ * @return {@code true} if the vector was successfully renamed, {@code false} otherwise.
161
+ */
76
162
  public boolean rename(int from, int to) {
77
163
  return c_rename(c_ptr, from, to);
78
164
  }
79
165
 
80
166
  public static class Config {
167
+
81
168
  private String _metric = "ip";
82
169
  private String _quantization = "f32";
83
170
  private long _dimensions = 0;
@@ -86,18 +173,18 @@ public class Index {
86
173
  private long _expansion_add = 0;
87
174
  private long _expansion_search = 0;
88
175
 
89
- public Config() {
90
- }
176
+ public Config() {}
91
177
 
92
178
  public Index build() {
93
179
  return new Index(
94
- _metric,
95
- _quantization,
96
- _dimensions,
97
- _capacity,
98
- _connectivity,
99
- _expansion_add,
100
- _expansion_search);
180
+ _metric,
181
+ _quantization,
182
+ _dimensions,
183
+ _capacity,
184
+ _connectivity,
185
+ _expansion_add,
186
+ _expansion_search
187
+ );
101
188
  }
102
189
 
103
190
  public Config metric(String _metric) {
@@ -146,10 +233,15 @@ public class Index {
146
233
  System.out.println("Java tests passed!");
147
234
  }
148
235
 
149
- private static native long c_create(//
150
- String metric, String quantization, //
151
- long dimensions, long capacity, long connectivity, //
152
- long expansion_add, long expansion_search);
236
+ private static native long c_create( //
237
+ String metric,
238
+ String quantization, //
239
+ long dimensions,
240
+ long capacity,
241
+ long connectivity, //
242
+ long expansion_add,
243
+ long expansion_search
244
+ );
153
245
 
154
246
  private static native void c_destroy(long ptr);
155
247
 
@@ -176,4 +268,4 @@ public class Index {
176
268
  private static native boolean c_remove(long ptr, int key);
177
269
 
178
270
  private static native boolean c_rename(long ptr, int from, int to);
179
- }
271
+ }
@@ -124,10 +124,10 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1add( //
124
124
  jsize vector_dims = (*env).GetArrayLength(vector);
125
125
  float_span_t vector_span = float_span_t{vector_data, static_cast<std::size_t>(vector_dims)};
126
126
 
127
- using key_t = typename index_dense_t::key_t;
127
+ using vector_key_t = typename index_dense_t::vector_key_t;
128
128
  using add_result_t = typename index_dense_t::add_result_t;
129
129
 
130
- add_result_t result = reinterpret_cast<index_dense_t*>(c_ptr)->add(static_cast<key_t>(key), vector_span);
130
+ add_result_t result = reinterpret_cast<index_dense_t*>(c_ptr)->add(static_cast<vector_key_t>(key), vector_span);
131
131
  if (!result) {
132
132
  jclass jc = (*env).FindClass("java/lang/Error");
133
133
  if (jc)
@@ -152,13 +152,13 @@ JNIEXPORT jintArray JNICALL Java_cloud_unum_usearch_Index_c_1search( //
152
152
  jsize vector_dims = (*env).GetArrayLength(vector);
153
153
  float_span_t vector_span = float_span_t{vector_data, static_cast<std::size_t>(vector_dims)};
154
154
 
155
- using key_t = typename index_dense_t::key_t;
155
+ using vector_key_t = typename index_dense_t::vector_key_t;
156
156
  using search_result_t = typename index_dense_t::search_result_t;
157
157
 
158
158
  search_result_t result =
159
159
  reinterpret_cast<index_dense_t*>(c_ptr)->search(vector_span, static_cast<std::size_t>(wanted));
160
160
  if (result) {
161
- std::size_t found = result.dump_to(reinterpret_cast<key_t*>(matches_data), NULL);
161
+ std::size_t found = result.dump_to(reinterpret_cast<vector_key_t*>(matches_data), NULL);
162
162
  (*env).SetIntArrayRegion(matches, 0, found, matches_data);
163
163
  } else {
164
164
  jclass jc = (*env).FindClass("java/lang/Error");
@@ -172,9 +172,9 @@ JNIEXPORT jintArray JNICALL Java_cloud_unum_usearch_Index_c_1search( //
172
172
  }
173
173
 
174
174
  JNIEXPORT bool JNICALL Java_cloud_unum_usearch_Index_c_1remove(JNIEnv* env, jclass, jlong c_ptr, jlong key) {
175
- using key_t = typename index_dense_t::key_t;
175
+ using vector_key_t = typename index_dense_t::vector_key_t;
176
176
  using labeling_result_t = typename index_dense_t::labeling_result_t;
177
- labeling_result_t result = reinterpret_cast<index_dense_t*>(c_ptr)->remove(static_cast<key_t>(key));
177
+ labeling_result_t result = reinterpret_cast<index_dense_t*>(c_ptr)->remove(static_cast<vector_key_t>(key));
178
178
  if (!result) {
179
179
  jclass jc = (*env).FindClass("java/lang/Error");
180
180
  if (jc)
@@ -184,10 +184,10 @@ JNIEXPORT bool JNICALL Java_cloud_unum_usearch_Index_c_1remove(JNIEnv* env, jcla
184
184
  }
185
185
 
186
186
  JNIEXPORT bool JNICALL Java_cloud_unum_usearch_Index_c_1rename(JNIEnv* env, jclass, jlong c_ptr, jlong from, jlong to) {
187
- using key_t = typename index_dense_t::key_t;
187
+ using vector_key_t = typename index_dense_t::vector_key_t;
188
188
  using labeling_result_t = typename index_dense_t::labeling_result_t;
189
189
  labeling_result_t result =
190
- reinterpret_cast<index_dense_t*>(c_ptr)->rename(static_cast<key_t>(from), static_cast<key_t>(to));
190
+ reinterpret_cast<index_dense_t*>(c_ptr)->rename(static_cast<vector_key_t>(from), static_cast<vector_key_t>(to));
191
191
  if (!result) {
192
192
  jclass jc = (*env).FindClass("java/lang/Error");
193
193
  if (jc)
@@ -11,6 +11,7 @@ const MetricKind = {
11
11
  IP: 'ip',
12
12
  L2sq: 'l2sq',
13
13
  Haversine: 'haversine',
14
+ Divergence: 'divergence',
14
15
  Pearson: 'pearson',
15
16
  Jaccard: 'jaccard',
16
17
  Hamming: 'hamming',
@@ -14,7 +14,7 @@ using labeling_result_t = typename index_dense_t::labeling_result_t;
14
14
  using search_result_t = typename index_dense_t::search_result_t;
15
15
  using shared_index_dense_t = std::shared_ptr<index_dense_t>;
16
16
 
17
- static_assert(std::is_same<USearchKey, index_dense_t::key_t>::value, "Type mismatch between Objective-C and C++");
17
+ static_assert(std::is_same<USearchKey, index_dense_t::vector_key_t>::value, "Type mismatch between Objective-C and C++");
18
18
 
19
19
  metric_kind_t to_native_metric(USearchMetric m) {
20
20
  switch (m) {
@@ -33,6 +33,9 @@ metric_kind_t to_native_metric(USearchMetric m) {
33
33
  case USearchMetricHaversine:
34
34
  return metric_kind_t::haversine_k;
35
35
 
36
+ case USearchMetricDivergence:
37
+ return metric_kind_t::divergence_k;
38
+
36
39
  case USearchMetricJaccard:
37
40
  return metric_kind_t::jaccard_k;
38
41
 
@@ -19,6 +19,7 @@ typedef NS_ENUM(NSUInteger, USearchMetric) {
19
19
  USearchMetricL2sq,
20
20
  USearchMetricPearson,
21
21
  USearchMetricHaversine,
22
+ USearchMetricDivergence,
22
23
  USearchMetricJaccard,
23
24
  USearchMetricHamming,
24
25
  USearchMetricTanimoto,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "2.7.7",
3
+ "version": "2.8.1",
4
4
  "description": "Smaller & Faster Single-File Vector Search Engine from Unum",
5
5
  "author": "Ash Vardanian (https://ashvardanian.com/)",
6
6
  "license": "Apache 2.0",
package/pyproject.toml CHANGED
@@ -8,9 +8,13 @@ addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
8
8
  xfail_strict = true
9
9
  filterwarnings = ["error"]
10
10
 
11
+ # Avoid running tests, as everything is happening in a super slow container
12
+ # We have already run all the relavent Python tests in `prerelease.yml`
13
+ # test-requires = ["pytest", "numpy"]
14
+ # test-command = "pytest {project}/python/scripts"
11
15
  [tool.cibuildwheel]
12
- test-requires = ["pytest", "numpy"]
13
- test-command = "pytest {project}/python/scripts"
16
+ test-requires = []
17
+ test-command = ""
14
18
  build-verbosity = 0
15
19
  skip = ["*musllinux*", "*i686*", "pp*", "cp36-*", "cp312-*"]
16
20
 
@@ -18,22 +22,20 @@ skip = ["*musllinux*", "*i686*", "pp*", "cp36-*", "cp312-*"]
18
22
  before-build = [
19
23
  "rm -rf {project}/CMakeCache.txt {project}/build {project}/build_debug {project}/CMakeFiles.txt {project}/_deps {project}/.pytest_cache",
20
24
  "mkdir -p build/usearch",
21
- "git submodule update --init --recursive"
25
+ "git submodule update --init --recursive",
22
26
  ]
23
27
  manylinux-x86_64-image = "manylinux_2_28"
24
28
  manylinux-aarch64-image = "manylinux_2_28"
25
29
 
26
30
  archs = ["x86_64", "aarch64"]
27
- before-all = [
28
- "yum install -y glibc-devel wget python3-devel"
29
- ]
31
+ before-all = ["yum install -y glibc-devel wget python3-devel"]
30
32
  repair-wheel-command = "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
31
33
 
32
34
  [tool.cibuildwheel.macos]
33
35
  before-build = [
34
36
  "rm -rf {project}/CMakeCache.txt {project}/build {project}/build_debug {project}/CMakeFiles.txt {project}/_deps {project}/.pytest_cache",
35
37
  "mkdir -p build/usearch",
36
- "git submodule update --init --recursive"
38
+ "git submodule update --init --recursive",
37
39
  ]
38
40
  archs = ["x86_64", "universal2", "arm64"]
39
41
  repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
@@ -43,7 +45,7 @@ repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest
43
45
  before-build = [
44
46
  "rd /s /q {project}\\CMakeCache.txt {project}\\build {project}\\build_debug {project}\\CMakeFiles.txt {project}\\_deps {project}\\.pytest_cache || echo Done",
45
47
  "md build\\usearch",
46
- "git submodule update --init --recursive"
48
+ "git submodule update --init --recursive",
47
49
  ]
48
50
  archs = ["x86", "AMD64"]
49
51
  skip = ["*win32*", "pp*", "cp36-*", "cp312-*"]
package/python/lib.cpp CHANGED
@@ -38,7 +38,7 @@ using py_shape_t = py::array::ShapeContainer;
38
38
  using metric_t = metric_punned_t;
39
39
  using distance_t = distance_punned_t;
40
40
 
41
- using dense_key_t = typename index_dense_t::key_t;
41
+ using dense_key_t = typename index_dense_t::vector_key_t;
42
42
  using dense_add_result_t = typename index_dense_t::add_result_t;
43
43
  using dense_search_result_t = typename index_dense_t::search_result_t;
44
44
  using dense_labeling_result_t = typename index_dense_t::labeling_result_t;
@@ -48,7 +48,7 @@ using dense_clustering_result_t = typename index_dense_t::clustering_result_t;
48
48
  using progress_func_t = std::function<bool(std::size_t /*processed*/, std::size_t /*total*/)>;
49
49
 
50
50
  struct progress_t {
51
- inline progress_t(nullptr_t = nullptr) : func_(&dummy_progress) {}
51
+ inline progress_t(std::nullptr_t = nullptr) : func_(&dummy_progress) {}
52
52
  inline progress_t(progress_func_t const& func) : func_(func ? func : &dummy_progress) {}
53
53
  inline bool operator()(std::size_t processed, std::size_t total) const noexcept { return func_(processed, total); }
54
54
 
@@ -864,6 +864,7 @@ PYBIND11_MODULE(compiled, m) {
864
864
  .value("L2sq", metric_kind_t::l2sq_k)
865
865
 
866
866
  .value("Haversine", metric_kind_t::haversine_k)
867
+ .value("Divergence", metric_kind_t::divergence_k)
867
868
  .value("Pearson", metric_kind_t::pearson_k)
868
869
  .value("Jaccard", metric_kind_t::jaccard_k)
869
870
  .value("Hamming", metric_kind_t::hamming_k)
@@ -11,7 +11,7 @@
11
11
  },
12
12
  {
13
13
  "cell_type": "code",
14
- "execution_count": null,
14
+ "execution_count": 1,
15
15
  "metadata": {},
16
16
  "outputs": [],
17
17
  "source": [
@@ -19,6 +19,28 @@
19
19
  "import plotly.graph_objects as go"
20
20
  ]
21
21
  },
22
+ {
23
+ "cell_type": "code",
24
+ "execution_count": 6,
25
+ "metadata": {},
26
+ "outputs": [
27
+ {
28
+ "data": {
29
+ "text/plain": [
30
+ "('auto', 'auto', 'auto')"
31
+ ]
32
+ },
33
+ "execution_count": 6,
34
+ "metadata": {},
35
+ "output_type": "execute_result"
36
+ }
37
+ ],
38
+ "source": [
39
+ "from usearch.index import Index\n",
40
+ "\n",
41
+ "Index(ndim=256, metric=\"cos\", dtype=\"f32\").hardware_acceleration, Index(ndim=256, metric=\"cos\", dtype=\"f16\").hardware_acceleration, Index(ndim=256, metric=\"cos\", dtype=\"i8\").hardware_acceleration"
42
+ ]
43
+ },
22
44
  {
23
45
  "cell_type": "code",
24
46
  "execution_count": null,
@@ -162,7 +184,7 @@
162
184
  "name": "python",
163
185
  "nbconvert_exporter": "python",
164
186
  "pygments_lexer": "ipython3",
165
- "version": "3.10.11"
187
+ "version": "3.11.5"
166
188
  },
167
189
  "orig_nbformat": 4
168
190
  },
@@ -151,6 +151,7 @@ def _normalize_metric(metric) -> MetricKind:
151
151
  "l2sq": MetricKind.L2sq,
152
152
  "l2_sq": MetricKind.L2sq,
153
153
  "haversine": MetricKind.Haversine,
154
+ "divergence": MetricKind.Divergence,
154
155
  "pearson": MetricKind.Pearson,
155
156
  "hamming": MetricKind.Hamming,
156
157
  "tanimoto": MetricKind.Tanimoto,
@@ -573,7 +574,7 @@ class Index:
573
574
  )
574
575
 
575
576
  self.path = path
576
- if path is not None:
577
+ if path is not None and os.path.exists(path):
577
578
  if view:
578
579
  self.view(path)
579
580
  else:
@@ -873,7 +874,7 @@ class Index:
873
874
 
874
875
  @property
875
876
  def metric_kind(self) -> Union[MetricKind, CompiledMetric]:
876
- return self._metric_jit if self._metric_jit.kind else self._metric_kind
877
+ return self._metric_jit.kind if self._metric_jit else self._metric_kind
877
878
 
878
879
  @property
879
880
  def metric(self) -> Union[MetricKind, CompiledMetric]:
@@ -965,7 +966,11 @@ class Index:
965
966
  if isinstance(path_or_buffer, bytes):
966
967
  self._compiled.load_index_from_buffer(path_or_buffer, progress)
967
968
  else:
968
- self._compiled.load_index_from_path(os.fspath(path_or_buffer), progress)
969
+ path_or_buffer = os.fspath(path_or_buffer)
970
+ if os.path.exists(path_or_buffer):
971
+ self._compiled.load_index_from_path(path_or_buffer, progress)
972
+ else:
973
+ raise RuntimeError("Missing file!")
969
974
 
970
975
  def view(
971
976
  self,