usearch 2.8.2 → 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 (44) hide show
  1. package/.github/workflows/release.yml +1 -1
  2. package/.vscode/settings.json +1 -0
  3. package/CITATION.cff +1 -1
  4. package/CONTRIBUTING.md +258 -6
  5. package/Cargo.toml +1 -1
  6. package/README.md +57 -53
  7. package/conanfile.py +1 -1
  8. package/cpp/README.md +89 -21
  9. package/csharp/DEV_SETUP.md +1 -1
  10. package/csharp/nuget/nuget-package.props +1 -1
  11. package/docs/{compilation.rst → contributing.rst} +2 -2
  12. package/docs/index.rst +1 -1
  13. package/include/usearch/index.hpp +7 -6
  14. package/include/usearch/index_dense.hpp +11 -7
  15. package/javascript/usearch.js +4 -4
  16. package/javascript/usearch.test.js +22 -0
  17. package/package-ci.json +10 -0
  18. package/package.json +1 -5
  19. package/python/README.md +1 -1
  20. package/simsimd/CMakeLists.txt +1 -1
  21. package/simsimd/README.md +7 -1
  22. package/simsimd/include/simsimd/probability.h +24 -24
  23. package/simsimd/include/simsimd/types.h +8 -0
  24. package/simsimd/javascript/{test.js → test/simsimd.test.js} +15 -15
  25. package/simsimd/package.json +4 -8
  26. package/simsimd/setup.py +3 -0
  27. package/wasmer.toml +1 -1
  28. package/docs/compilation.md +0 -215
  29. package/simsimd/.clang-format +0 -10
  30. package/simsimd/.github/workflows/prerelease.yml +0 -89
  31. package/simsimd/.github/workflows/release.yml +0 -141
  32. package/simsimd/.github/workflows/update_version.sh +0 -5
  33. package/simsimd/.vscode/launch.json +0 -53
  34. package/simsimd/.vscode/settings.json +0 -114
  35. package/simsimd/.vscode/tasks.json +0 -23
  36. package/simsimd/go.mod +0 -3
  37. package/simsimd/golang/bench_test.go +0 -40
  38. package/simsimd/golang/simsimd.go +0 -47
  39. package/simsimd/golang/unit_test.go +0 -40
  40. package/simsimd/pyproject.toml +0 -42
  41. package/simsimd/python/bench.ipynb +0 -187
  42. package/simsimd/python/bench.py +0 -268
  43. package/simsimd/python/lib.c +0 -492
  44. package/simsimd/python/test.py +0 -195
@@ -1,23 +0,0 @@
1
- {
2
- "version": "2.0.0",
3
- "tasks": [
4
- {
5
- "label": "Build Benchmark: Debug",
6
- "command": "cmake -DCMAKE_BUILD_TYPE=Debug -DSIMSIMD_BUILD_BENCHMARKS=1 -B ./build_debug && make -j 2 -C ./build_debug",
7
- "args": [],
8
- "type": "shell"
9
- },
10
- {
11
- "label": "Build Test: Debug",
12
- "command": "cmake -DCMAKE_BUILD_TYPE=Debug -B ./build && make test py_simsimd -C ./build",
13
- "args": [],
14
- "type": "shell"
15
- },
16
- {
17
- "label": "Build Benchmark: Release",
18
- "command": "cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DSIMSIMD_BUILD_BENCHMARKS=1 -B ./build_release && make -j 2 -C ./build_release",
19
- "args": [],
20
- "type": "shell"
21
- }
22
- ]
23
- }
package/simsimd/go.mod DELETED
@@ -1,3 +0,0 @@
1
- module github.com/ashvardanian/simsimd
2
-
3
- go 1.19
@@ -1,40 +0,0 @@
1
- package simsimd
2
-
3
- import (
4
- "math"
5
- "math/rand"
6
- "testing"
7
- )
8
-
9
- func cosineSimilarity(a, b []float32) float32 {
10
- var dotProduct float32
11
- var normA, normB float32
12
- for i := range a {
13
- dotProduct += a[i] * b[i]
14
- normA += a[i] * a[i]
15
- normB += b[i] * b[i]
16
- }
17
- return dotProduct / (float32(math.Sqrt(float64(normA))) * float32(math.Sqrt(float64(normB))))
18
- }
19
-
20
- func generateRandomVector(dim int) []float32 {
21
- vec := make([]float32, dim)
22
- for i := range vec {
23
- vec[i] = rand.Float32()
24
- }
25
- return vec
26
- }
27
-
28
- func BenchmarkCosineSimilarityNative(b *testing.B) {
29
- first, second := generateRandomVector(1536), generateRandomVector(1536)
30
- for i := 0; i < b.N; i++ {
31
- cosineSimilarity(first, second)
32
- }
33
- }
34
-
35
- func BenchmarkCosineSimilaritySIMD(b *testing.B) {
36
- first, second := generateRandomVector(1536), generateRandomVector(1536)
37
- for i := 0; i < b.N; i++ {
38
- CosineF32(first, second)
39
- }
40
- }
@@ -1,47 +0,0 @@
1
- package simsimd
2
-
3
- /*
4
- #cgo CFLAGS: -O3
5
- #cgo LDFLAGS: -O3 -L.
6
- #include "../simsimd/simsimd.h"
7
- #include <stdlib.h>
8
-
9
- inline static simsimd_f32_t cosine_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t d) { return simsimd_metric_punned(simsimd_metric_cosine_k, simsimd_datatype_i8_k, simsimd_cap_any_k)(a, b, d, d); }
10
- inline static simsimd_f32_t cosine_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t d) { return simsimd_neon_f32_cos(a, b, d); }
11
- inline static simsimd_f32_t inner_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t d) { return simsimd_metric_punned(simsimd_metric_inner_k, simsimd_datatype_i8_k, simsimd_cap_any_k)(a, b, d, d); }
12
- inline static simsimd_f32_t inner_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t d) { return simsimd_metric_punned(simsimd_metric_inner_k, simsimd_datatype_f32_k, simsimd_cap_any_k)(a, b, d, d); }
13
- inline static simsimd_f32_t sqeuclidean_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t d) { return simsimd_metric_punned(simsimd_metric_sqeuclidean_k, simsimd_datatype_i8_k, simsimd_cap_any_k)(a, b, d, d); }
14
- inline static simsimd_f32_t sqeuclidean_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t d) { return simsimd_metric_punned(simsimd_metric_sqeuclidean_k, simsimd_datatype_f32_k, simsimd_cap_any_k)(a, b, d, d); }
15
- */
16
- import "C"
17
-
18
-
19
- // CosineI8 computes the cosine similarity between two i8 vectors using the most suitable SIMD instruction set available.
20
- func CosineI8(a, b []int8) float32 {
21
- return float32(C.cosine_i8((*C.simsimd_i8_t)(&a[0]), (*C.simsimd_i8_t)(&b[0]), C.simsimd_size_t(len(a))))
22
- }
23
-
24
- // CosineF32 computes the cosine similarity between two i8 vectors using the most suitable SIMD instruction set available.
25
- func CosineF32(a, b []float32) float32 {
26
- return float32(C.cosine_f32((*C.simsimd_f32_t)(&a[0]), (*C.simsimd_f32_t)(&b[0]), C.simsimd_size_t(len(a))))
27
- }
28
-
29
- // InnerI8 computes the inner-product similarity between two i8 vectors using the most suitable SIMD instruction set available.
30
- func InnerI8(a, b []int8) float32 {
31
- return float32(C.inner_i8((*C.simsimd_i8_t)(&a[0]), (*C.simsimd_i8_t)(&b[0]), C.simsimd_size_t(len(a))))
32
- }
33
-
34
- // InnerF32 computes the inner-product similarity between two i8 vectors using the most suitable SIMD instruction set available.
35
- func InnerF32(a, b []float32) float32 {
36
- return float32(C.inner_f32((*C.simsimd_f32_t)(&a[0]), (*C.simsimd_f32_t)(&b[0]), C.simsimd_size_t(len(a))))
37
- }
38
-
39
- // SqEuclideanI8 computes the squared euclidean similarity between two i8 vectors using the most suitable SIMD instruction set available.
40
- func SqEuclideanI8(a, b []int8) float32 {
41
- return float32(C.sqeuclidean_i8((*C.simsimd_i8_t)(&a[0]), (*C.simsimd_i8_t)(&b[0]), C.simsimd_size_t(len(a))))
42
- }
43
-
44
- // SqEuclideanF32 computes the squared euclidean similarity between two i8 vectors using the most suitable SIMD instruction set available.
45
- func SqEuclideanF32(a, b []float32) float32 {
46
- return float32(C.sqeuclidean_f32((*C.simsimd_f32_t)(&a[0]), (*C.simsimd_f32_t)(&b[0]), C.simsimd_size_t(len(a))))
47
- }
@@ -1,40 +0,0 @@
1
- package simsimd
2
-
3
- import (
4
- "math"
5
- "testing"
6
- )
7
-
8
- func TestCosineI8(t *testing.T) {
9
- a := []int8{1, 0}
10
- b := []int8{0, 1}
11
-
12
- result := CosineI8(a, b)
13
- expected := float32(1.0) // Cosine similarity of orthogonal vectors is 0
14
- if math.Abs(float64(result-expected)) > 1e-3 {
15
- t.Errorf("Expected %v, got %v", expected, result)
16
- }
17
- }
18
-
19
- func TestCosineF32(t *testing.T) {
20
- a := []float32{1, 0}
21
- b := []float32{0, 1}
22
-
23
- result := CosineF32(a, b)
24
- expected := float32(1.0) // Cosine similarity of orthogonal vectors is 0
25
- if math.Abs(float64(result-expected)) > 1e-3 {
26
- t.Errorf("Expected %v, got %v", expected, result)
27
- }
28
- }
29
-
30
- func TestVectorLengthMismatch(t *testing.T) {
31
- defer func() {
32
- if r := recover(); r == nil {
33
- t.Errorf("The code did not panic")
34
- }
35
- }()
36
-
37
- a := []int8{1, 0}
38
- b := []int8{0}
39
- _ = CosineI8(a, b) // This should panic
40
- }
@@ -1,42 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=42", "wheel", "numpy"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [tool.pytest.ini_options]
6
- minversion = "6.0"
7
- addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
8
- xfail_strict = true
9
- filterwarnings = ["error"]
10
-
11
- [tool.cibuildwheel]
12
- test-requires = ["pytest", "pytest-repeat", "scipy"]
13
- test-command = "pytest {project}/python/test.py -s -x"
14
- build-verbosity = 0
15
- skip = ["*musllinux*", "*i686*", "pp*", "cp36-*", "cp37-*"]
16
-
17
- [tool.cibuildwheel.linux]
18
- before-build = [
19
- "rm -rf {project}/build",
20
- ]
21
- manylinux-x86_64-image = "manylinux_2_28"
22
- manylinux-aarch64-image = "manylinux_2_28"
23
-
24
- archs = ["x86_64", "aarch64"]
25
- before-all = [
26
- "yum install -y glibc-devel wget python3-devel"
27
- ]
28
- repair-wheel-command = "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
29
-
30
- [tool.cibuildwheel.macos]
31
- before-build = [
32
- "rm -rf {project}/build",
33
- ]
34
- archs = ["x86_64", "arm64"]
35
- repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
36
-
37
- [tool.cibuildwheel.windows]
38
- before-build = [
39
- "rd /s /q {project}\\build || echo Done",
40
- ]
41
- archs = ["x86", "AMD64"]
42
- skip = ["*win32*", "pp*"]
@@ -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()