usearch 2.8.3 → 2.8.4

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 (40) hide show
  1. package/.vscode/settings.json +1 -0
  2. package/CITATION.cff +1 -1
  3. package/CONTRIBUTING.md +258 -6
  4. package/Cargo.toml +1 -1
  5. package/README.md +57 -53
  6. package/conanfile.py +1 -1
  7. package/cpp/README.md +89 -21
  8. package/csharp/DEV_SETUP.md +1 -1
  9. package/csharp/nuget/nuget-package.props +1 -1
  10. package/docs/{compilation.rst → contributing.rst} +2 -2
  11. package/docs/index.rst +1 -1
  12. package/include/usearch/index.hpp +1 -1
  13. package/include/usearch/index_dense.hpp +11 -7
  14. package/package.json +1 -1
  15. package/python/README.md +1 -1
  16. package/simsimd/CMakeLists.txt +1 -1
  17. package/simsimd/README.md +7 -1
  18. package/simsimd/include/simsimd/probability.h +24 -24
  19. package/simsimd/include/simsimd/types.h +8 -0
  20. package/simsimd/javascript/{test.js → test/simsimd.test.js} +15 -15
  21. package/simsimd/package.json +4 -8
  22. package/simsimd/setup.py +3 -0
  23. package/wasmer.toml +1 -1
  24. package/docs/compilation.md +0 -215
  25. package/simsimd/.clang-format +0 -10
  26. package/simsimd/.github/workflows/prerelease.yml +0 -89
  27. package/simsimd/.github/workflows/release.yml +0 -141
  28. package/simsimd/.github/workflows/update_version.sh +0 -5
  29. package/simsimd/.vscode/launch.json +0 -53
  30. package/simsimd/.vscode/settings.json +0 -114
  31. package/simsimd/.vscode/tasks.json +0 -23
  32. package/simsimd/go.mod +0 -3
  33. package/simsimd/golang/bench_test.go +0 -40
  34. package/simsimd/golang/simsimd.go +0 -47
  35. package/simsimd/golang/unit_test.go +0 -40
  36. package/simsimd/pyproject.toml +0 -42
  37. package/simsimd/python/bench.ipynb +0 -187
  38. package/simsimd/python/bench.py +0 -268
  39. package/simsimd/python/lib.c +0 -492
  40. package/simsimd/python/test.py +0 -195
@@ -1,187 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "code",
5
- "execution_count": 1,
6
- "metadata": {},
7
- "outputs": [],
8
- "source": [
9
- "import numpy as np\n",
10
- "import scipy as sp\n",
11
- "import simsimd as simd"
12
- ]
13
- },
14
- {
15
- "cell_type": "code",
16
- "execution_count": 2,
17
- "metadata": {},
18
- "outputs": [],
19
- "source": [
20
- "simsimd_f = simd.cosine\n",
21
- "conventional_f = sp.spatial.distance.cosine"
22
- ]
23
- },
24
- {
25
- "cell_type": "code",
26
- "execution_count": 3,
27
- "metadata": {},
28
- "outputs": [],
29
- "source": [
30
- "count = 1000 # Common number of documents in a batch\n",
31
- "ndim = 1536 # OpenAI Ada v2 embeddings API returns 1536-dim vectors\n",
32
- "A = np.random.randn(count, ndim).astype(np.float32)\n",
33
- "B = np.random.randn(count, ndim).astype(np.float32)"
34
- ]
35
- },
36
- {
37
- "cell_type": "code",
38
- "execution_count": 4,
39
- "metadata": {},
40
- "outputs": [
41
- {
42
- "name": "stdout",
43
- "output_type": "stream",
44
- "text": [
45
- "16.3 ms ± 3.84 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)\n"
46
- ]
47
- }
48
- ],
49
- "source": [
50
- "%%timeit -n 1 -r 10\n",
51
- "result_np = [conventional_f(A[i], B[i]) for i in range(count)]"
52
- ]
53
- },
54
- {
55
- "cell_type": "code",
56
- "execution_count": 5,
57
- "metadata": {},
58
- "outputs": [
59
- {
60
- "name": "stdout",
61
- "output_type": "stream",
62
- "text": [
63
- "437 µs ± 15.6 µs per loop (mean ± std. dev. of 10 runs, 1 loop each)\n"
64
- ]
65
- }
66
- ],
67
- "source": [
68
- "%%timeit -n 1 -r 10\n",
69
- "result_simd = simsimd_f(A, B)"
70
- ]
71
- },
72
- {
73
- "cell_type": "code",
74
- "execution_count": 6,
75
- "metadata": {},
76
- "outputs": [],
77
- "source": [
78
- "A = np.random.randn(count, ndim).astype(np.float16)\n",
79
- "B = np.random.randn(count, ndim).astype(np.float16)"
80
- ]
81
- },
82
- {
83
- "cell_type": "code",
84
- "execution_count": 7,
85
- "metadata": {},
86
- "outputs": [
87
- {
88
- "name": "stdout",
89
- "output_type": "stream",
90
- "text": [
91
- "36.7 ms ± 843 µs per loop (mean ± std. dev. of 10 runs, 1 loop each)\n"
92
- ]
93
- }
94
- ],
95
- "source": [
96
- "%%timeit -n 1 -r 10\n",
97
- "result_np = [conventional_f(A[i], B[i]) for i in range(count)]"
98
- ]
99
- },
100
- {
101
- "cell_type": "code",
102
- "execution_count": 8,
103
- "metadata": {},
104
- "outputs": [
105
- {
106
- "name": "stdout",
107
- "output_type": "stream",
108
- "text": [
109
- "437 µs ± 7.61 µs per loop (mean ± std. dev. of 10 runs, 1 loop each)\n"
110
- ]
111
- }
112
- ],
113
- "source": [
114
- "%%timeit -n 1 -r 10\n",
115
- "result_simd = simsimd_f(A, B)"
116
- ]
117
- },
118
- {
119
- "cell_type": "code",
120
- "execution_count": 9,
121
- "metadata": {},
122
- "outputs": [],
123
- "source": [
124
- "A = np.random.randint(-100, 100, (count, ndim), np.int8)\n",
125
- "B = np.random.randint(-100, 100, (count, ndim), np.int8)"
126
- ]
127
- },
128
- {
129
- "cell_type": "code",
130
- "execution_count": 10,
131
- "metadata": {},
132
- "outputs": [
133
- {
134
- "name": "stdout",
135
- "output_type": "stream",
136
- "text": [
137
- "12.4 ms ± 493 µs per loop (mean ± std. dev. of 10 runs, 1 loop each)\n"
138
- ]
139
- }
140
- ],
141
- "source": [
142
- "%%timeit -n 1 -r 10\n",
143
- "result_np = [conventional_f(A[i], B[i]) for i in range(count)]"
144
- ]
145
- },
146
- {
147
- "cell_type": "code",
148
- "execution_count": 11,
149
- "metadata": {},
150
- "outputs": [
151
- {
152
- "name": "stdout",
153
- "output_type": "stream",
154
- "text": [
155
- "63.6 µs ± 3.27 µs per loop (mean ± std. dev. of 10 runs, 1 loop each)\n"
156
- ]
157
- }
158
- ],
159
- "source": [
160
- "%%timeit -n 1 -r 10\n",
161
- "result_simd = simsimd_f(A, B)"
162
- ]
163
- }
164
- ],
165
- "metadata": {
166
- "kernelspec": {
167
- "display_name": "base",
168
- "language": "python",
169
- "name": "python3"
170
- },
171
- "language_info": {
172
- "codemirror_mode": {
173
- "name": "ipython",
174
- "version": 3
175
- },
176
- "file_extension": ".py",
177
- "mimetype": "text/x-python",
178
- "name": "python",
179
- "nbconvert_exporter": "python",
180
- "pygments_lexer": "ipython3",
181
- "version": "3.10.11"
182
- },
183
- "orig_nbformat": 4
184
- },
185
- "nbformat": 4,
186
- "nbformat_minor": 2
187
- }
@@ -1,268 +0,0 @@
1
- import timeit
2
- import argparse
3
-
4
- import numpy as np
5
- import scipy.spatial.distance as spd
6
- import scipy.special as scs
7
- import simsimd as simd
8
-
9
- # Argument parsing
10
- parser = argparse.ArgumentParser(description="Benchmark SimSIMD vs. SciPy")
11
- parser.add_argument(
12
- "--n", type=int, default=1000, help="Number of vectors (default: 1000)"
13
- )
14
- parser.add_argument(
15
- "--ndim", type=int, default=1536, help="Number of dimensions (default: 1536)"
16
- )
17
- args = parser.parse_args()
18
-
19
- count = args.n
20
- ndim = args.ndim
21
-
22
-
23
- # Benchmark function
24
- def benchmark(func, A, B, count=1):
25
- start_time = timeit.default_timer()
26
- func(A, B)
27
- end_time = timeit.default_timer()
28
- return (end_time - start_time) / count
29
-
30
-
31
- def wrap_rowwise(conventional_func):
32
- def wrapped(A, B):
33
- for i in range(A.shape[0]):
34
- conventional_func(A[i], B[i])
35
-
36
- return wrapped
37
-
38
-
39
- print()
40
- print("# Benchmarking SimSIMD vs. SciPy")
41
- print()
42
-
43
- print("- Vector dimensions:", ndim)
44
- print("- Vectors count:", count)
45
-
46
- try:
47
- caps = [cap for cap, enabled in simd.get_capabilities().items() if enabled]
48
- print("- Hardware capabilities:", ", ".join(caps))
49
-
50
- deps: dict = np.show_config(mode="dicts").get("Build Dependencies")
51
- print("- NumPy BLAS dependency:", deps["blas"]["name"])
52
- print("- NumPy LAPACK dependency:", deps["lapack"]["name"])
53
-
54
- except:
55
- pass
56
-
57
- count = 1000
58
- ndim = 1536
59
-
60
- generators = {
61
- np.float32: lambda: np.random.randn(count, ndim).astype(np.float32),
62
- np.float16: lambda: np.random.randn(count, ndim).astype(np.float16),
63
- np.int8: lambda: np.random.randint(-100, 100, (count, ndim), np.int8),
64
- np.uint8: lambda: np.packbits(
65
- np.random.randint(0, 2, (count, ndim), np.uint8), axis=0
66
- ),
67
- }
68
-
69
- dtype_names = {
70
- np.float32: "f32",
71
- np.float16: "f16",
72
- np.int8: "i8",
73
- np.uint8: "b8",
74
- }
75
-
76
-
77
- print()
78
- print("## Between 2 Vectors, Batch Size: 1")
79
- print()
80
-
81
- # Table headers
82
- print(
83
- "| Datatype | Method | Ops/s | SimSIMD Ops/s | SimSIMD Improvement |"
84
- )
85
- print(
86
- "| :------- | :-------------------- | -------------------: | -------------------: | ------------------: |"
87
- )
88
-
89
- # Benchmark functions
90
- funcs = [
91
- ("scipy.cosine", spd.cosine, simd.cosine, [np.float32, np.float16, np.int8]),
92
- (
93
- "scipy.sqeuclidean",
94
- spd.sqeuclidean,
95
- simd.sqeuclidean,
96
- [np.float32, np.float16, np.int8],
97
- ),
98
- ("numpy.inner", np.inner, simd.inner, [np.float32, np.float16, np.int8]),
99
- (
100
- "scipy.jensenshannon",
101
- spd.jensenshannon,
102
- simd.jensenshannon,
103
- [np.float32, np.float16],
104
- ),
105
- (
106
- "scipy.kl_div",
107
- scs.kl_div,
108
- simd.kullbackleibler,
109
- [np.float32, np.float16],
110
- ),
111
- ("scipy.hamming", spd.hamming, simd.hamming, [np.uint8]),
112
- ("scipy.jaccard", spd.jaccard, simd.jaccard, [np.uint8]),
113
- ]
114
-
115
-
116
- for name, conventional_func, simd_func, dtypes in funcs:
117
- for dtype in dtypes:
118
- A = generators[dtype]()
119
- B = generators[dtype]()
120
-
121
- conventional_time = benchmark(wrap_rowwise(conventional_func), A, B, count)
122
- simd_time = benchmark(wrap_rowwise(simd_func), A, B, count)
123
-
124
- conventional_ops = 1 / conventional_time
125
- simd_ops = 1 / simd_time
126
- improvement = conventional_time / simd_time
127
-
128
- func_name = "`{}`".format(name)
129
- dtype_name = "`{}`".format(dtype_names[dtype])
130
- print(
131
- f"| {dtype_name:8} | {func_name:21} | {conventional_ops:20,.0f} | {simd_ops:20,.0f} | {improvement:17.2f} x |"
132
- )
133
-
134
- print()
135
-
136
-
137
- print("## Between 2 Vectors, Batch Size: {:,}".format(count))
138
- print()
139
-
140
- # Table headers
141
- print(
142
- "| Datatype | Method | Ops/s | SimSIMD Ops/s | SimSIMD Improvement |"
143
- )
144
- print(
145
- "| :------- | :-------------------- | -------------------: | -------------------: | ------------------: |"
146
- )
147
-
148
- # Benchmark functions
149
- funcs = [
150
- ("scipy.cosine", spd.cosine, simd.cosine, [np.float32, np.float16, np.int8]),
151
- (
152
- "scipy.sqeuclidean",
153
- spd.sqeuclidean,
154
- simd.sqeuclidean,
155
- [np.float32, np.float16, np.int8],
156
- ),
157
- ("numpy.inner", np.inner, simd.inner, [np.float32, np.float16, np.int8]),
158
- (
159
- "scipy.jensenshannon",
160
- spd.jensenshannon,
161
- simd.jensenshannon,
162
- [np.float32, np.float16],
163
- ),
164
- (
165
- "scipy.kl_div",
166
- scs.kl_div,
167
- simd.kullbackleibler,
168
- [np.float32, np.float16],
169
- ),
170
- ("scipy.hamming", spd.hamming, simd.hamming, [np.uint8]),
171
- ("scipy.jaccard", spd.jaccard, simd.jaccard, [np.uint8]),
172
- ]
173
-
174
-
175
- for name, conventional_func, simd_func, dtypes in funcs:
176
- for dtype in dtypes:
177
- A = generators[dtype]()
178
- B = generators[dtype]()
179
-
180
- conventional_time = benchmark(wrap_rowwise(conventional_func), A, B, count)
181
- simd_time = benchmark(simd_func, A, B, count)
182
-
183
- conventional_ops = 1 / conventional_time
184
- simd_ops = 1 / simd_time
185
- improvement = conventional_time / simd_time
186
-
187
- func_name = "`{}`".format(name)
188
- dtype_name = "`{}`".format(dtype_names[dtype])
189
- print(
190
- f"| {dtype_name:8} | {func_name:21} | {conventional_ops:20,.0f} | {simd_ops:20,.0f} | {improvement:17.2f} x |"
191
- )
192
-
193
- print()
194
-
195
-
196
- # Benchmark functions for cdist
197
- print()
198
- print("## Between All Pairs of Vectors (`cdist`), Batch Size: {:,}".format(count))
199
- print()
200
-
201
-
202
- cdist_funcs = [
203
- (
204
- "scipy.cosine",
205
- lambda A, B: spd.cdist(A, B, "cosine"),
206
- lambda A, B: simd.cdist(A, B, "cosine"),
207
- [np.float32, np.float16, np.int8],
208
- ),
209
- (
210
- "scipy.sqeuclidean",
211
- lambda A, B: spd.cdist(A, B, "sqeuclidean"),
212
- lambda A, B: simd.cdist(A, B, "sqeuclidean"),
213
- [np.float32, np.float16, np.int8],
214
- ),
215
- (
216
- "numpy.inner",
217
- lambda A, B: 1 - np.dot(A, B.T),
218
- lambda A, B: simd.cdist(A, B, "inner"),
219
- [np.float32, np.float16, np.int8],
220
- ),
221
- (
222
- "scipy.jensenshannon",
223
- lambda A, B: spd.cdist(A, B, "jensenshannon"),
224
- lambda A, B: simd.cdist(A, B, "jensenshannon"),
225
- [np.float32, np.float16],
226
- ),
227
- (
228
- "scipy.hamming",
229
- lambda A, B: spd.cdist(A, B, "hamming"),
230
- lambda A, B: simd.cdist(A, B, "hamming"),
231
- [np.uint8],
232
- ),
233
- (
234
- "scipy.jaccard",
235
- lambda A, B: spd.cdist(A, B, "jaccard"),
236
- lambda A, B: simd.cdist(A, B, "jaccard"),
237
- [np.uint8],
238
- ),
239
- ]
240
-
241
-
242
- # Table headers
243
- print(
244
- "| Datatype | Method | Ops/s | SimSIMD Ops/s | SimSIMD Improvement |"
245
- )
246
- print(
247
- "| :------- | :-------------------- | -------------------: | -------------------: | ------------------: |"
248
- )
249
-
250
- for name, conventional_func, simd_func, dtypes in cdist_funcs:
251
- for dtype in dtypes:
252
- A = generators[dtype]()
253
- B = generators[dtype]()
254
-
255
- conventional_time = benchmark(conventional_func, A, B)
256
- simd_time = benchmark(simd_func, A, B)
257
-
258
- conventional_ops = count**2 / conventional_time
259
- simd_ops = count**2 / simd_time
260
- improvement = conventional_time / simd_time
261
-
262
- func_name = "`{}`".format(name)
263
- dtype_name = "`{}`".format(dtype_names[dtype])
264
- print(
265
- f"| {dtype_name:8} | {func_name:21} | {conventional_ops:20,.0f} | {simd_ops:20,.0f} | {improvement:17.2f} x |"
266
- )
267
-
268
- print()