usearch 2.8.7 → 2.8.9

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 (113) hide show
  1. package/CITATION.cff +1 -1
  2. package/CMakeLists.txt +3 -3
  3. package/CONTRIBUTING.md +26 -3
  4. package/Cargo.toml +1 -1
  5. package/Dockerfile +16 -0
  6. package/README.md +41 -38
  7. package/VERSION +1 -0
  8. package/binding.gyp +57 -8
  9. package/conanfile.py +1 -1
  10. package/include/usearch/index.hpp +1 -1
  11. package/include/usearch/index_plugins.hpp +28 -24
  12. package/javascript/README.md +64 -12
  13. package/javascript/lib.cpp +11 -3
  14. package/javascript/usearch.js +4 -2
  15. package/javascript/usearch.test.js +15 -0
  16. package/package.json +1 -2
  17. package/setup.py +76 -15
  18. package/simsimd/README.md +14 -13
  19. package/simsimd/include/simsimd/probability.h +2 -2
  20. package/simsimd/package.json +1 -1
  21. package/wasmer.toml +1 -1
  22. package/. npmignore +0 -14
  23. package/.gitignore +0 -95
  24. package/.vscode/launch.json +0 -181
  25. package/.vscode/settings.json +0 -180
  26. package/.vscode/tasks.json +0 -71
  27. package/Package.swift +0 -41
  28. package/csharp/.editorconfig +0 -361
  29. package/csharp/Cloud.Unum.USearch.sln +0 -34
  30. package/csharp/DEV_SETUP.md +0 -51
  31. package/csharp/Directory.Build.props +0 -29
  32. package/csharp/README.md +0 -74
  33. package/csharp/build_and_test.cmd +0 -31
  34. package/csharp/build_and_test.sh +0 -30
  35. package/csharp/nuget/nuget-package.props +0 -33
  36. package/csharp/pack.sh +0 -56
  37. package/csharp/src/Cloud.Unum.USearch.Tests/Cloud.Unum.USearch.Tests.csproj +0 -39
  38. package/csharp/src/Cloud.Unum.USearch.Tests/USearchIndexTests.cs +0 -1013
  39. package/csharp/src/Cloud.Unum.USearch.Tests/Usings.cs +0 -1
  40. package/docs/_static/custom.css +0 -26
  41. package/docs/_static/custom.js +0 -7
  42. package/docs/benchmarks.md +0 -245
  43. package/docs/benchmarks.rst +0 -5
  44. package/docs/c/index.rst +0 -11
  45. package/docs/c/reference.rst +0 -7
  46. package/docs/conf.dox +0 -2668
  47. package/docs/conf.py +0 -53
  48. package/docs/contributing.rst +0 -5
  49. package/docs/cpp/index.rst +0 -11
  50. package/docs/cpp/reference.rst +0 -17
  51. package/docs/csharp/index.rst +0 -6
  52. package/docs/format.md +0 -86
  53. package/docs/format.rst +0 -5
  54. package/docs/golang/index.rst +0 -6
  55. package/docs/index.rst +0 -33
  56. package/docs/java/index.rst +0 -11
  57. package/docs/java/reference.rst +0 -7
  58. package/docs/javascript/index.rst +0 -11
  59. package/docs/javascript/reference.rst +0 -12
  60. package/docs/python/index.rst +0 -11
  61. package/docs/python/reference.rst +0 -42
  62. package/docs/rust/index.rst +0 -6
  63. package/docs/swift/index.rst +0 -6
  64. package/docs/wolfram/index.rst +0 -6
  65. package/golang/README.md +0 -95
  66. package/golang/go.mod +0 -3
  67. package/golang/lib.go +0 -379
  68. package/golang/lib_test.go +0 -108
  69. package/java/README.md +0 -22
  70. package/java/cloud/unum/usearch/Index.java +0 -337
  71. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +0 -197
  72. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.h +0 -125
  73. package/java/test/IndexTest.java +0 -30
  74. package/objc/USearchObjective.mm +0 -299
  75. package/objc/include/USearchObjective.h +0 -154
  76. package/python/README.md +0 -292
  77. package/python/lib.cpp +0 -1216
  78. package/python/scripts/bench.ipynb +0 -193
  79. package/python/scripts/bench.py +0 -137
  80. package/python/scripts/bench_exact.py +0 -44
  81. package/python/scripts/cluster.ipynb +0 -160
  82. package/python/scripts/index_faiss.py +0 -100
  83. package/python/scripts/join.py +0 -155
  84. package/python/scripts/test_index.py +0 -247
  85. package/python/scripts/test_jit.py +0 -231
  86. package/python/scripts/test_sparse.py +0 -32
  87. package/python/scripts/test_tooling.py +0 -133
  88. package/python/usearch/__init__.py +0 -0
  89. package/python/usearch/client.py +0 -124
  90. package/python/usearch/eval.py +0 -470
  91. package/python/usearch/index.py +0 -1401
  92. package/python/usearch/io.py +0 -113
  93. package/python/usearch/numba.py +0 -121
  94. package/python/usearch/server.py +0 -137
  95. package/rust/README.md +0 -66
  96. package/rust/lib.cpp +0 -136
  97. package/rust/lib.hpp +0 -69
  98. package/rust/lib.rs +0 -610
  99. package/simsimd/CMakeLists.txt +0 -67
  100. package/simsimd/cpp/bench.cxx +0 -206
  101. package/simsimd/package-ci.json +0 -10
  102. package/simsimd/setup.py +0 -86
  103. package/swift/Index+Sugar.swift +0 -153
  104. package/swift/README.md +0 -48
  105. package/swift/Test.swift +0 -37
  106. package/swift/USearch.swift +0 -8
  107. package/wasm/CMakeLists.txt +0 -7
  108. package/wasm/README.md +0 -13
  109. package/wasm/lib.cpp +0 -5
  110. package/wolfram/CMakeLists.txt +0 -18
  111. package/wolfram/README.md +0 -13
  112. package/wolfram/lib.cpp +0 -160
  113. package/wolfram/test.wls +0 -27
package/python/README.md DELETED
@@ -1,292 +0,0 @@
1
- # USearch for Python
2
-
3
- ## Installation
4
-
5
- ```sh
6
- pip install usearch
7
- ```
8
-
9
- ## Quickstart
10
-
11
- ```python
12
- import numpy as np
13
- from usearch.index import Index, Matches
14
-
15
- index = Index(
16
- ndim=3, # Define the number of dimensions in input vectors
17
- metric='cos', # Choose 'l2sq', 'haversine' or other metric, default = 'ip'
18
- dtype='f32', # Quantize to 'f16' or 'i8' if needed, default = 'f32'
19
- connectivity=16, # How frequent should the connections in the graph be, optional
20
- expansion_add=128, # Control the recall of indexing, optional
21
- expansion_search=64, # Control the quality of search, optional
22
- )
23
-
24
- vector = np.array([0.2, 0.6, 0.4])
25
- index.add(42, vector)
26
- matches: Matches = index.search(vector, 10)
27
-
28
- assert len(index) == 1
29
- assert len(matches) == 1
30
- assert matches[0].key == 42
31
- assert matches[0].distance <= 0.001
32
- assert np.allclose(index[42], vector)
33
- ```
34
-
35
- Python bindings are implemented with [`pybind/pybind11`](https://github.com/pybind/pybind11).
36
- Assuming the presence of Global Interpreter Lock in Python, we spawn threads in the C++ layer on large insertions.
37
-
38
- ## Serialization
39
-
40
- ```py
41
- index.save('index.usearch')
42
- index.load('index.usearch') # Copy the whole index into memory
43
- index.view('index.usearch') # View from disk without loading in memory
44
- ```
45
-
46
- If you don't know anything about the index except its path, there are two more endpoints to know:
47
-
48
- ```py
49
- Index.metadata('index.usearch') -> IndexMetadata
50
- Index.restore('index.usearch', view=False) -> Index
51
- ```
52
-
53
- ## Batch Operations
54
-
55
- Adding or querying a batch of entries is identical to adding a single vector.
56
- The difference would be in the shape of the tensors.
57
-
58
- ```py
59
- n = 100
60
- keys = np.arange(n)
61
- vectors = np.random.uniform(0, 0.3, (n, index.ndim)).astype(np.float32)
62
-
63
- index.add(keys, vectors, threads=..., copy=...)
64
- matches: BatchMatches = index.search(vectors, 10, threads=...)
65
-
66
- first_query_matches: Matches = matches[0]
67
- assert matches[0].key == 0
68
- assert matches[0].distance <= 0.001
69
-
70
- assert len(matches) == vectors.shape[0]
71
- assert len(matches[0]) <= 10
72
- ```
73
-
74
- You can also override the default `threads` and `copy` arguments in bulk workloads.
75
- The first controls the number of threads spawned for the task.
76
- The second controls whether the vector itself will be persisted inside the index.
77
- If you can preserve the lifetime of the vector somewhere else, you can avoid the copy.
78
-
79
- ## User-Defined Metrics and JIT in Python
80
-
81
- ### [Numba][numba]
82
-
83
- Assuming the language boundary exists between Python user code and C++ implementation, there are more efficient solutions than passing a Python callable to the engine.
84
- Luckily, with the help of [Numba][numba], we can JIT compile a function with a matching signature and pass it down to the engine.
85
-
86
- ```py
87
- from numba import cfunc, types, carray
88
-
89
- ndim = 256
90
- signature = types.float32(
91
- types.CPointer(types.float32),
92
- types.CPointer(types.float32))
93
-
94
- @cfunc(signature)
95
- def inner_product(a, b):
96
- a_array = carray(a, ndim)
97
- b_array = carray(b, ndim)
98
- c = 0.0
99
- for i in range(ndim):
100
- c += a_array[i] * b_array[i]
101
- return 1 - c
102
-
103
- index = Index(ndim=ndim, metric=CompiledMetric(
104
- pointer=inner_product.address,
105
- kind=MetricKind.IP,
106
- signature=MetricSignature.ArrayArray,
107
- ))
108
- ```
109
-
110
- Alternatively, you can avoid pre-defining the number of dimensions, and pass it separately:
111
-
112
- ```py
113
- signature = types.float32(
114
- types.CPointer(types.float32),
115
- types.CPointer(types.float32),
116
- types.uint64)
117
-
118
- @cfunc(signature)
119
- def inner_product(a, b, ndim):
120
- a_array = carray(a, ndim)
121
- b_array = carray(b, ndim)
122
- c = 0.0
123
- for i in range(ndim):
124
- c += a_array[i] * b_array[i]
125
- return 1 - c
126
-
127
- index = Index(ndim=ndim, metric=CompiledMetric(
128
- pointer=inner_product.address,
129
- kind=MetricKind.IP,
130
- signature=MetricSignature.ArrayArraySize,
131
- ))
132
- ```
133
-
134
- ```sh
135
- pip install numba
136
- ```
137
-
138
- ### [Cppyy][cppyy]
139
-
140
- Similarly, you can use Cppyy with Cling to JIT-compile native C or C++ code and pass it to USearch, which may be a good idea, if you want to explicitly request loop-unrolling or other low-level optimizations!
141
-
142
- ```py
143
- import cppyy
144
- import cppyy.ll
145
-
146
- cppyy.cppdef("""
147
- float inner_product(float *a, float *b) {
148
- float result = 0;
149
- #pragma unroll
150
- for (size_t i = 0; i != ndim; ++i)
151
- result += a[i] * b[i];
152
- return 1 - result;
153
- }
154
- """.replace("ndim", str(ndim)))
155
-
156
- function = cppyy.gbl.inner_product
157
- index = Index(ndim=ndim, metric=CompiledMetric(
158
- pointer=cppyy.ll.addressof(function),
159
- kind=MetricKind.IP,
160
- signature=MetricSignature.ArrayArraySize,
161
- ))
162
- ```
163
-
164
- ```sh
165
- conda install -c conda-forge cppyy
166
- ```
167
-
168
- ### [PeachPy][peachpy]
169
-
170
- We have covered JIT-ing Python with Numba and C++ with Cppyy and Cling.
171
- How about writing Assembly directly?
172
- That is also possible.
173
- Below is an example of constructing the "Inner Product" distance for 8-dimensional `f32` vectors for x86 using [PeachPy][peachpy].
174
-
175
- ```py
176
- from peachpy import (
177
- Argument,
178
- ptr,
179
- float_,
180
- const_float_,
181
- )
182
- from peachpy.x86_64 import (
183
- abi,
184
- Function,
185
- uarch,
186
- isa,
187
- GeneralPurposeRegister64,
188
- LOAD,
189
- YMMRegister,
190
- VSUBPS,
191
- VADDPS,
192
- VHADDPS,
193
- VMOVUPS,
194
- VFMADD231PS,
195
- VPERM2F128,
196
- VXORPS,
197
- RETURN,
198
- )
199
-
200
- a = Argument(ptr(const_float_), name="a")
201
- b = Argument(ptr(const_float_), name="b")
202
-
203
- with Function(
204
- "inner_product", (a, b), float_, target=uarch.default + isa.avx2
205
- ) as asm_function:
206
-
207
- # Request two 64-bit general-purpose registers for addresses
208
- reg_a, reg_b = GeneralPurposeRegister64(), GeneralPurposeRegister64()
209
- LOAD.ARGUMENT(reg_a, a)
210
- LOAD.ARGUMENT(reg_b, b)
211
-
212
- # Load the vectors
213
- ymm_a = YMMRegister()
214
- ymm_b = YMMRegister()
215
- VMOVUPS(ymm_a, [reg_a])
216
- VMOVUPS(ymm_b, [reg_b])
217
-
218
- # Prepare the accumulator
219
- ymm_c = YMMRegister()
220
- ymm_one = YMMRegister()
221
- VXORPS(ymm_c, ymm_c, ymm_c)
222
- VXORPS(ymm_one, ymm_one, ymm_one)
223
-
224
- # Accumulate A and B product into C
225
- VFMADD231PS(ymm_c, ymm_a, ymm_b)
226
-
227
- # Reduce the contents of a YMM register
228
- ymm_c_permuted = YMMRegister()
229
- VPERM2F128(ymm_c_permuted, ymm_c, ymm_c, 1)
230
- VADDPS(ymm_c, ymm_c, ymm_c_permuted)
231
- VHADDPS(ymm_c, ymm_c, ymm_c)
232
- VHADDPS(ymm_c, ymm_c, ymm_c)
233
-
234
- # Negate the values, to go from "similarity" to "distance"
235
- VSUBPS(ymm_c, ymm_one, ymm_c)
236
-
237
- # A common convention is to return floats in XMM registers
238
- RETURN(ymm_c.as_xmm)
239
-
240
- python_function = asm_function.finalize(abi.detect()).encode().load()
241
- metric = CompiledMetric(
242
- pointer=python_function.loader.code_address,
243
- kind=MetricKind.IP,
244
- signature=MetricSignature.ArrayArray,
245
- )
246
- index = Index(ndim=ndim, metric=metric)
247
- ```
248
-
249
- [numba]: https://numba.readthedocs.io/en/stable/reference/jit-compilation.html#c-callbacks
250
- [cppyy]: https://cppyy.readthedocs.io/en/latest/
251
- [peachpy]: https://github.com/Maratyszcza/PeachPy
252
-
253
- ## Tooling
254
-
255
- To work with `bbin`, `fbin`, `ibin`, `hbin` matrix files USearch provides `load_matrix` and `save_matrix`.
256
- Such files are standard in k-ANN tasks and represent a binary object with all the scalars, prepended by two 32-bit integers - the number of rows and columns in the matrix.
257
-
258
- ```py
259
- from usearch.index import Index
260
- from usearch.io import load_matrix, save_matrix
261
-
262
- vectors = load_matrix('deep1B.fbin')
263
- index = Index(ndim=vectors.shape[1])
264
- index.add(keys, vectors)
265
- ```
266
-
267
- One may often want to evaluate the quality of the constructed index before running in production.
268
- The trivial way is to measure `recall@1` on the entries already present in the index.
269
-
270
- ```py
271
- from usearch.eval import self_recall
272
-
273
- stats: SearchStats = self_recall(index, exact=True)
274
- assert stats.visited_members == 0, "Exact search won't attend index nodes"
275
- assert stats.computed_distances == len(index), "And will compute the distance to every node"
276
-
277
- stats: SearchStats = self_recall(index, exact=False)
278
- assert stats.visited_members > 0
279
- assert stats.computed_distances <= len(index)
280
- ```
281
-
282
- In case you have some ground-truth data for more than one entry, you compare search results against expected values:
283
-
284
- ```py
285
- from usearch.eval import relevance, dcg, ndcg, random_vectors
286
-
287
- vectors = random_vectors(index=index)
288
- matches_approximate = index.search(vectors)
289
- matches_exact = index.search(vectors, exact=True)
290
- relevance_scores = relevance(matches_exact, matches_approximate)
291
- print(dcg(relevance_scores), ndcg(relevance_scores))
292
- ```