usearch 2.16.8 → 2.16.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.
- package/README.md +1 -1
- package/include/usearch/index.hpp +1 -1
- package/package.json +1 -1
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/simsimd/CONTRIBUTING.md +69 -12
- package/simsimd/Cargo.lock +1 -1
- package/simsimd/Cargo.toml +1 -1
- package/simsimd/README.md +253 -40
- package/simsimd/c/lib.c +114 -109
- package/simsimd/include/simsimd/binary.h +33 -17
- package/simsimd/include/simsimd/curved.h +867 -129
- package/simsimd/include/simsimd/dot.h +562 -569
- package/simsimd/include/simsimd/probability.h +14 -9
- package/simsimd/include/simsimd/simsimd.h +102 -46
- package/simsimd/include/simsimd/spatial.h +28 -18
- package/simsimd/include/simsimd/types.h +24 -0
- package/simsimd/javascript/fallback.ts +26 -15
- package/simsimd/javascript/simsimd.ts +1 -1
- package/simsimd/package-lock.json +10 -9
- package/simsimd/package.json +2 -2
- package/simsimd/rust/lib.rs +218 -5
- package/simsimd/scripts/bench.cxx +156 -97
- package/simsimd/scripts/test.c +14 -8
- package/simsimd/scripts/test.mjs +158 -145
- package/simsimd/scripts/test.py +71 -52
package/simsimd/scripts/test.mjs
CHANGED
|
@@ -24,7 +24,6 @@ import test from "node:test";
|
|
|
24
24
|
import assert from "node:assert";
|
|
25
25
|
|
|
26
26
|
import * as simsimd from "../javascript/dist/esm/simsimd.js";
|
|
27
|
-
|
|
28
27
|
import * as fallback from "../javascript/dist/esm/fallback.js";
|
|
29
28
|
|
|
30
29
|
function assertAlmostEqual(actual, expected, tolerance = 1e-6) {
|
|
@@ -40,168 +39,182 @@ test("Distance from itself", () => {
|
|
|
40
39
|
const f32s = new Float32Array([1.0, 2.0, 3.0]);
|
|
41
40
|
assertAlmostEqual(simsimd.sqeuclidean(f32s, f32s), 0.0, 0.01);
|
|
42
41
|
assertAlmostEqual(simsimd.cosine(f32s, f32s), 0.0, 0.01);
|
|
42
|
+
assertAlmostEqual(fallback.sqeuclidean(f32s, f32s), 0.0, 0.01);
|
|
43
|
+
assertAlmostEqual(fallback.cosine(f32s, f32s), 0.0, 0.01);
|
|
43
44
|
|
|
44
45
|
const f64s = new Float64Array([1.0, 2.0, 3.0]);
|
|
45
46
|
assertAlmostEqual(simsimd.sqeuclidean(f64s, f64s), 0.0, 0.01);
|
|
46
47
|
assertAlmostEqual(simsimd.cosine(f64s, f64s), 0.0, 0.01);
|
|
48
|
+
assertAlmostEqual(fallback.sqeuclidean(f64s, f64s), 0.0, 0.01);
|
|
49
|
+
assertAlmostEqual(fallback.cosine(f64s, f64s), 0.0, 0.01);
|
|
47
50
|
|
|
48
|
-
const f32sNormalized = new Float32Array([
|
|
49
|
-
1.0 / Math.sqrt(14),
|
|
50
|
-
2.0 / Math.sqrt(14),
|
|
51
|
-
3.0 / Math.sqrt(14),
|
|
52
|
-
]);
|
|
51
|
+
const f32sNormalized = new Float32Array([1 / Math.sqrt(14), 2 / Math.sqrt(14), 3 / Math.sqrt(14)]);
|
|
53
52
|
assertAlmostEqual(simsimd.inner(f32sNormalized, f32sNormalized), 1.0, 0.01);
|
|
53
|
+
assertAlmostEqual(fallback.inner(f32sNormalized, f32sNormalized), 1.0, 0.01);
|
|
54
54
|
|
|
55
|
-
const
|
|
56
|
-
assertAlmostEqual(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
);
|
|
61
|
-
assertAlmostEqual(
|
|
62
|
-
simsimd.jensenshannon(f32sDistribution, f32sDistribution),
|
|
63
|
-
0.0,
|
|
64
|
-
0.01
|
|
65
|
-
);
|
|
55
|
+
const f32sHistogram = new Float32Array([1.0 / 6, 2.0 / 6, 3.0 / 6]);
|
|
56
|
+
assertAlmostEqual(simsimd.kullbackleibler(f32sHistogram, f32sHistogram), 0.0, 0.01);
|
|
57
|
+
assertAlmostEqual(simsimd.jensenshannon(f32sHistogram, f32sHistogram), 0.0, 0.01);
|
|
58
|
+
assertAlmostEqual(fallback.kullbackleibler(f32sHistogram, f32sHistogram), 0.0, 0.01);
|
|
59
|
+
assertAlmostEqual(fallback.jensenshannon(f32sHistogram, f32sHistogram), 0.0, 0.01);
|
|
66
60
|
|
|
67
61
|
const u8s = new Uint8Array([1, 2, 3]);
|
|
68
62
|
assertAlmostEqual(simsimd.hamming(u8s, u8s), 0.0, 0.01);
|
|
69
63
|
assertAlmostEqual(simsimd.jaccard(u8s, u8s), 0.0, 0.01);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("Distance from itself JS fallback", () => {
|
|
73
|
-
const f32s = new Float32Array([1.0, 2.0, 3.0]);
|
|
74
|
-
|
|
75
|
-
assertAlmostEqual(fallback.sqeuclidean(f32s, f32s), 0.0, 0.01);
|
|
76
|
-
assertAlmostEqual(fallback.cosine(f32s, f32s), 0.0, 0.01);
|
|
77
|
-
|
|
78
|
-
const arrNormalized = new Float32Array([
|
|
79
|
-
1.0 / Math.sqrt(14),
|
|
80
|
-
2.0 / Math.sqrt(14),
|
|
81
|
-
3.0 / Math.sqrt(14),
|
|
82
|
-
]);
|
|
83
|
-
assertAlmostEqual(fallback.inner(arrNormalized, arrNormalized), 1.0, 0.01);
|
|
84
|
-
|
|
85
|
-
const f32sDistribution = new Float32Array([1.0 / 6, 2.0 / 6, 3.0 / 6]);
|
|
86
|
-
assertAlmostEqual(
|
|
87
|
-
fallback.kullbackleibler(f32sDistribution, f32sDistribution),
|
|
88
|
-
0.0,
|
|
89
|
-
0.01
|
|
90
|
-
);
|
|
91
|
-
assertAlmostEqual(
|
|
92
|
-
fallback.jensenshannon(f32sDistribution, f32sDistribution),
|
|
93
|
-
0.0,
|
|
94
|
-
0.01
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
const u8s = new Uint8Array([1, 2, 3]);
|
|
98
64
|
assertAlmostEqual(fallback.hamming(u8s, u8s), 0.0, 0.01);
|
|
99
65
|
assertAlmostEqual(fallback.jaccard(u8s, u8s), 0.0, 0.01);
|
|
100
66
|
});
|
|
101
67
|
|
|
102
|
-
const f32Array1 = new Float32Array([1.0, 2.0, 3.0]);
|
|
103
|
-
const f32Array2 = new Float32Array([4.0, 5.0, 6.0]);
|
|
104
|
-
|
|
105
|
-
const uint8Array1 = new Uint8Array([1, 2, 3]);
|
|
106
|
-
const uint8Array2 = new Uint8Array([4, 5, 6]);
|
|
107
68
|
|
|
108
69
|
test("Squared Euclidean Distance", () => {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
assertAlmostEqual(
|
|
70
|
+
const f64sOne = new Float64Array([1.0, 2.0, 3.0]);
|
|
71
|
+
const f64sTwo = new Float64Array([4.0, 5.0, 6.0]);
|
|
72
|
+
const f64sResult = simsimd.sqeuclidean(f64sOne, f64sTwo);
|
|
73
|
+
const f64sResultJS = fallback.sqeuclidean(f64sOne, f64sTwo);
|
|
74
|
+
assertAlmostEqual(f64sResultJS, 27.0, 0.01);
|
|
75
|
+
assertAlmostEqual(f64sResult, 27.0, 0.01);
|
|
76
|
+
|
|
77
|
+
const f32sOne = new Float32Array([1.0, 2.0, 3.0]);
|
|
78
|
+
const f32sTwo = new Float32Array([4.0, 5.0, 6.0]);
|
|
79
|
+
const f32sResult = simsimd.sqeuclidean(f32sOne, f32sTwo);
|
|
80
|
+
const f32sResultJS = fallback.sqeuclidean(f32sOne, f32sTwo);
|
|
81
|
+
assertAlmostEqual(f32sResultJS, 27.0, 0.01);
|
|
82
|
+
assertAlmostEqual(f32sResult, 27.0, 0.01);
|
|
83
|
+
|
|
84
|
+
const u8sOne = new Uint8Array([1, 2, 3]);
|
|
85
|
+
const u8sTwo = new Uint8Array([4, 5, 6]);
|
|
86
|
+
const u8sResult = simsimd.sqeuclidean(u8sOne, u8sTwo);
|
|
87
|
+
const u8sResultJS = fallback.sqeuclidean(u8sOne, u8sTwo);
|
|
88
|
+
assertAlmostEqual(u8sResultJS, 27.0, 0.01);
|
|
89
|
+
assertAlmostEqual(u8sResult, 27.0, 0.01);
|
|
90
|
+
|
|
91
|
+
const i8sOne = new Int8Array([1, 2, 3]);
|
|
92
|
+
const i8sTwo = new Int8Array([4, 5, 6]);
|
|
93
|
+
const i8sResult = simsimd.sqeuclidean(i8sOne, i8sTwo);
|
|
94
|
+
const i8sResultJS = fallback.sqeuclidean(i8sOne, i8sTwo);
|
|
95
|
+
assertAlmostEqual(i8sResultJS, 27.0, 0.01);
|
|
96
|
+
assertAlmostEqual(i8sResult, 27.0, 0.01);
|
|
114
97
|
});
|
|
115
98
|
|
|
116
99
|
test("Euclidean Distance", () => {
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
assertAlmostEqual(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
assertAlmostEqual(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
assertAlmostEqual(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
assertAlmostEqual(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
100
|
+
const f64sOne = new Float64Array([1.0, 2.0, 3.0]);
|
|
101
|
+
const f64sTwo = new Float64Array([4.0, 5.0, 6.0]);
|
|
102
|
+
const f64sResult = simsimd.euclidean(f64sOne, f64sTwo);
|
|
103
|
+
const f64sResultJS = fallback.euclidean(f64sOne, f64sTwo);
|
|
104
|
+
assertAlmostEqual(f64sResultJS, 5.2, 0.01);
|
|
105
|
+
assertAlmostEqual(f64sResult, 5.2, 0.01);
|
|
106
|
+
|
|
107
|
+
const f32sOne = new Float32Array([1.0, 2.0, 3.0]);
|
|
108
|
+
const f32sTwo = new Float32Array([4.0, 5.0, 6.0]);
|
|
109
|
+
const f32sResult = simsimd.euclidean(f32sOne, f32sTwo);
|
|
110
|
+
const f32sResultJS = fallback.euclidean(f32sOne, f32sTwo);
|
|
111
|
+
assertAlmostEqual(f32sResultJS, 5.2, 0.01);
|
|
112
|
+
assertAlmostEqual(f32sResult, 5.2, 0.01);
|
|
113
|
+
|
|
114
|
+
const u8sOne = new Uint8Array([1, 2, 3]);
|
|
115
|
+
const u8sTwo = new Uint8Array([4, 5, 6]);
|
|
116
|
+
const u8sResult = simsimd.euclidean(u8sOne, u8sTwo);
|
|
117
|
+
const u8sResultJS = fallback.euclidean(u8sOne, u8sTwo);
|
|
118
|
+
assertAlmostEqual(u8sResultJS, 5.2, 0.01);
|
|
119
|
+
assertAlmostEqual(u8sResult, 5.2, 0.01);
|
|
120
|
+
|
|
121
|
+
const i8sOne = new Int8Array([1, 2, 3]);
|
|
122
|
+
const i8sTwo = new Int8Array([4, 5, 6]);
|
|
123
|
+
const i8sResult = simsimd.euclidean(i8sOne, i8sTwo);
|
|
124
|
+
const i8sResultJS = fallback.euclidean(i8sOne, i8sTwo);
|
|
125
|
+
assertAlmostEqual(i8sResultJS, 5.2, 0.01);
|
|
126
|
+
assertAlmostEqual(i8sResult, 5.2, 0.01);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
test("Inner Product", () => {
|
|
131
|
+
const f64sOne = new Float64Array([1.0, 2.0, 3.0]);
|
|
132
|
+
const f64sTwo = new Float64Array([4.0, 5.0, 6.0]);
|
|
133
|
+
const f64sResult = simsimd.inner(f64sOne, f64sTwo);
|
|
134
|
+
const f64sResultJS = fallback.inner(f64sOne, f64sTwo);
|
|
135
|
+
assertAlmostEqual(f64sResultJS, 32.0, 0.01);
|
|
136
|
+
assertAlmostEqual(f64sResult, 32.0, 0.01);
|
|
137
|
+
|
|
138
|
+
const f32sOne = new Float32Array([1.0, 2.0, 3.0]);
|
|
139
|
+
const f32sTwo = new Float32Array([4.0, 5.0, 6.0]);
|
|
140
|
+
const f32sResult = simsimd.inner(f32sOne, f32sTwo);
|
|
141
|
+
const f32sResultJS = fallback.inner(f32sOne, f32sTwo);
|
|
142
|
+
assertAlmostEqual(f32sResultJS, 32.0, 0.01);
|
|
143
|
+
assertAlmostEqual(f32sResult, 32.0, 0.01);
|
|
144
|
+
|
|
145
|
+
const u8sOne = new Uint8Array([1, 2, 3]);
|
|
146
|
+
const u8sTwo = new Uint8Array([4, 5, 6]);
|
|
147
|
+
const u8sResult = simsimd.inner(u8sOne, u8sTwo);
|
|
148
|
+
const u8sResultJS = fallback.inner(u8sOne, u8sTwo);
|
|
149
|
+
assertAlmostEqual(u8sResultJS, 32.0, 0.01);
|
|
150
|
+
assertAlmostEqual(u8sResult, 32.0, 0.01);
|
|
151
|
+
|
|
152
|
+
const i8sOne = new Int8Array([1, 2, 3]);
|
|
153
|
+
const i8sTwo = new Int8Array([4, 5, 6]);
|
|
154
|
+
const i8sResult = simsimd.inner(i8sOne, i8sTwo);
|
|
155
|
+
const i8sResultJS = fallback.inner(i8sOne, i8sTwo);
|
|
156
|
+
assertAlmostEqual(i8sResultJS, 32.0, 0.01);
|
|
157
|
+
assertAlmostEqual(i8sResult, 32.0, 0.01);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("Cosine Distance", () => {
|
|
161
|
+
const f64sOne = new Float64Array([1.0, 2.0, 3.0]);
|
|
162
|
+
const f64sTwo = new Float64Array([4.0, 5.0, 6.0]);
|
|
163
|
+
const f64sResult = simsimd.cosine(f64sOne, f64sTwo);
|
|
164
|
+
const f64sResultJS = fallback.cosine(f64sOne, f64sTwo);
|
|
165
|
+
assertAlmostEqual(f64sResultJS, 0.03, 0.01);
|
|
166
|
+
assertAlmostEqual(f64sResult, 0.03, 0.01);
|
|
167
|
+
|
|
168
|
+
const f32sOne = new Float32Array([1.0, 2.0, 3.0]);
|
|
169
|
+
const f32sTwo = new Float32Array([4.0, 5.0, 6.0]);
|
|
170
|
+
const f32sResult = simsimd.cosine(f32sOne, f32sTwo);
|
|
171
|
+
const f32sResultJS = fallback.cosine(f32sOne, f32sTwo);
|
|
172
|
+
assertAlmostEqual(f32sResultJS, 0.03, 0.01);
|
|
173
|
+
assertAlmostEqual(f32sResult, 0.03, 0.01);
|
|
174
|
+
|
|
175
|
+
const u8sOne = new Uint8Array([1, 2, 3]);
|
|
176
|
+
const u8sTwo = new Uint8Array([4, 5, 6]);
|
|
177
|
+
const u8sResult = simsimd.cosine(u8sOne, u8sTwo);
|
|
178
|
+
const u8sResultJS = fallback.cosine(u8sOne, u8sTwo);
|
|
179
|
+
assertAlmostEqual(u8sResultJS, 0.03, 0.01);
|
|
180
|
+
assertAlmostEqual(u8sResult, 0.03, 0.01);
|
|
181
|
+
|
|
182
|
+
const i8sOne = new Int8Array([1, 2, 3]);
|
|
183
|
+
const i8sTwo = new Int8Array([4, 5, 6]);
|
|
184
|
+
const i8sResult = simsimd.cosine(i8sOne, i8sTwo);
|
|
185
|
+
const i8sResultJS = fallback.cosine(i8sOne, i8sTwo);
|
|
186
|
+
assertAlmostEqual(i8sResultJS, 0.03, 0.01);
|
|
187
|
+
assertAlmostEqual(i8sResult, 0.03, 0.01);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("Kullback-Leibler", () => {
|
|
191
|
+
const f64sOne = new Float64Array([1.0 / 6, 2.0 / 6, 3.0 / 6]);
|
|
192
|
+
const f64sTwo = new Float64Array([4.0 / 17, 5.0 / 17, 6.0 / 17]);
|
|
193
|
+
const f64sResult = simsimd.kullbackleibler(f64sOne, f64sTwo);
|
|
194
|
+
const f64sResultJS = fallback.kullbackleibler(f64sOne, f64sTwo);
|
|
195
|
+
assertAlmostEqual(f64sResultJS, 0.16, 0.01);
|
|
196
|
+
assertAlmostEqual(f64sResult, 0.16, 0.01);
|
|
197
|
+
|
|
198
|
+
const f32sOne = new Float32Array([1.0 / 6, 2.0 / 6, 3.0 / 6]);
|
|
199
|
+
const f32sTwo = new Float32Array([4.0 / 17, 5.0 / 17, 6.0 / 17]);
|
|
200
|
+
const f32sResult = simsimd.kullbackleibler(f32sOne, f32sTwo);
|
|
201
|
+
const f32sResultJS = fallback.kullbackleibler(f32sOne, f32sTwo);
|
|
202
|
+
assertAlmostEqual(f32sResultJS, 0.16, 0.01);
|
|
203
|
+
assertAlmostEqual(f32sResult, 0.16, 0.01);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("Jensen-Shannon", () => {
|
|
207
|
+
const f64sOne = new Float64Array([1.0 / 6, 2.0 / 6, 3.0 / 6]);
|
|
208
|
+
const f64sTwo = new Float64Array([4.0 / 17, 5.0 / 17, 6.0 / 17]);
|
|
209
|
+
const f64sResult = simsimd.jensenshannon(f64sOne, f64sTwo);
|
|
210
|
+
const f64sResultJS = fallback.jensenshannon(f64sOne, f64sTwo);
|
|
211
|
+
assertAlmostEqual(f64sResultJS, 0.095, 0.01);
|
|
212
|
+
assertAlmostEqual(f64sResult, 0.095, 0.01);
|
|
213
|
+
|
|
214
|
+
const f32sOne = new Float32Array([1.0 / 6, 2.0 / 6, 3.0 / 6]);
|
|
215
|
+
const f32sTwo = new Float32Array([4.0 / 17, 5.0 / 17, 6.0 / 17]);
|
|
216
|
+
const f32sResult = simsimd.jensenshannon(f32sOne, f32sTwo);
|
|
217
|
+
const f32sResultJS = fallback.jensenshannon(f32sOne, f32sTwo);
|
|
218
|
+
assertAlmostEqual(f32sResultJS, 0.095, 0.01);
|
|
219
|
+
assertAlmostEqual(f32sResult, 0.095, 0.01);
|
|
207
220
|
});
|
package/simsimd/scripts/test.py
CHANGED
|
@@ -38,7 +38,6 @@ Or run the script directly:
|
|
|
38
38
|
python test.py
|
|
39
39
|
|
|
40
40
|
"""
|
|
41
|
-
|
|
42
41
|
import os
|
|
43
42
|
import math
|
|
44
43
|
import time
|
|
@@ -124,7 +123,7 @@ try:
|
|
|
124
123
|
baseline_euclidean = lambda x, y: np.array(spd.euclidean(x, y)) #! SciPy returns a scalar
|
|
125
124
|
baseline_sqeuclidean = spd.sqeuclidean
|
|
126
125
|
baseline_cosine = spd.cosine
|
|
127
|
-
baseline_jensenshannon = lambda x, y: spd.jensenshannon(x, y)
|
|
126
|
+
baseline_jensenshannon = lambda x, y: spd.jensenshannon(x, y)
|
|
128
127
|
baseline_hamming = lambda x, y: spd.hamming(x, y) * len(x)
|
|
129
128
|
baseline_jaccard = spd.jaccard
|
|
130
129
|
|
|
@@ -453,6 +452,8 @@ def name_to_kernels(name: str):
|
|
|
453
452
|
return baseline_fma, simd.fma
|
|
454
453
|
elif name == "wsum":
|
|
455
454
|
return baseline_wsum, simd.wsum
|
|
455
|
+
elif name == "jensenshannon":
|
|
456
|
+
return baseline_jensenshannon, simd.jensenshannon
|
|
456
457
|
else:
|
|
457
458
|
raise ValueError(f"Unknown kernel name: {name}")
|
|
458
459
|
|
|
@@ -587,7 +588,7 @@ def test_invalid_argument_handling(function, expected_error, args, kwargs):
|
|
|
587
588
|
|
|
588
589
|
|
|
589
590
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
590
|
-
@pytest.mark.repeat(
|
|
591
|
+
@pytest.mark.repeat(5)
|
|
591
592
|
@pytest.mark.parametrize("ndim", [11, 97, 1536])
|
|
592
593
|
@pytest.mark.parametrize("dtype", ["float64", "float32", "float16"])
|
|
593
594
|
@pytest.mark.parametrize("metric", ["inner", "euclidean", "sqeuclidean", "cosine"])
|
|
@@ -616,7 +617,7 @@ def test_dense(ndim, dtype, metric, capability, stats_fixture):
|
|
|
616
617
|
|
|
617
618
|
|
|
618
619
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
619
|
-
@pytest.mark.repeat(
|
|
620
|
+
@pytest.mark.repeat(5)
|
|
620
621
|
@pytest.mark.parametrize("ndim", [11, 97])
|
|
621
622
|
@pytest.mark.parametrize(
|
|
622
623
|
"dtypes", # representation datatype and compute precision
|
|
@@ -671,8 +672,42 @@ def test_curved(ndim, dtypes, metric, capability, stats_fixture):
|
|
|
671
672
|
collect_errors(metric, ndim, dtype, accurate, accurate_dt, expected, expected_dt, result, result_dt, stats_fixture)
|
|
672
673
|
|
|
673
674
|
|
|
675
|
+
@pytest.mark.skipif(is_running_under_qemu(), reason="Complex math in QEMU fails")
|
|
676
|
+
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
677
|
+
@pytest.mark.repeat(5)
|
|
678
|
+
@pytest.mark.parametrize("ndim", [11, 97])
|
|
679
|
+
@pytest.mark.parametrize("dtype", ["complex128", "complex64"])
|
|
680
|
+
@pytest.mark.parametrize("capability", possible_capabilities)
|
|
681
|
+
def test_curved_complex(ndim, dtype, capability, stats_fixture):
|
|
682
|
+
"""Compares various SIMD kernels (like Bilinear Forms and Mahalanobis distances) for curved spaces
|
|
683
|
+
with their NumPy or baseline counterparts, testing accuracy for complex IEEE standard floating-point types."""
|
|
684
|
+
|
|
685
|
+
# Let's generate some uniform complex numbers
|
|
686
|
+
np.random.seed()
|
|
687
|
+
a = (np.random.randn(ndim) + 1.0j * np.random.randn(ndim)).astype(dtype)
|
|
688
|
+
b = (np.random.randn(ndim) + 1.0j * np.random.randn(ndim)).astype(dtype)
|
|
689
|
+
c = (np.random.randn(ndim, ndim) + 1.0j * np.random.randn(ndim, ndim)).astype(dtype)
|
|
690
|
+
|
|
691
|
+
keep_one_capability(capability)
|
|
692
|
+
baseline_kernel, simd_kernel = name_to_kernels("bilinear")
|
|
693
|
+
accurate_dt, accurate = profile(
|
|
694
|
+
baseline_kernel,
|
|
695
|
+
a.astype(np.complex128),
|
|
696
|
+
b.astype(np.complex128),
|
|
697
|
+
c.astype(np.complex128),
|
|
698
|
+
)
|
|
699
|
+
expected_dt, expected = profile(baseline_kernel, a, b, c)
|
|
700
|
+
result_dt, result = profile(simd_kernel, a, b, c)
|
|
701
|
+
result = np.array(result)
|
|
702
|
+
|
|
703
|
+
np.testing.assert_allclose(result, expected, atol=SIMSIMD_ATOL, rtol=SIMSIMD_RTOL)
|
|
704
|
+
collect_errors(
|
|
705
|
+
"bilinear", ndim, dtype, accurate, accurate_dt, expected, expected_dt, result, result_dt, stats_fixture
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
|
|
674
709
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
675
|
-
@pytest.mark.repeat(
|
|
710
|
+
@pytest.mark.repeat(5)
|
|
676
711
|
@pytest.mark.parametrize("ndim", [11, 97, 1536])
|
|
677
712
|
@pytest.mark.parametrize("metric", ["inner", "euclidean", "sqeuclidean", "cosine"])
|
|
678
713
|
@pytest.mark.parametrize("capability", possible_capabilities)
|
|
@@ -712,7 +747,7 @@ def test_dense_bf16(ndim, metric, capability, stats_fixture):
|
|
|
712
747
|
|
|
713
748
|
|
|
714
749
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
715
|
-
@pytest.mark.repeat(
|
|
750
|
+
@pytest.mark.repeat(5)
|
|
716
751
|
@pytest.mark.parametrize("ndim", [11, 16, 33])
|
|
717
752
|
@pytest.mark.parametrize("metric", ["bilinear", "mahalanobis"])
|
|
718
753
|
@pytest.mark.parametrize("capability", possible_capabilities)
|
|
@@ -771,7 +806,7 @@ def test_curved_bf16(ndim, metric, capability, stats_fixture):
|
|
|
771
806
|
|
|
772
807
|
|
|
773
808
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
774
|
-
@pytest.mark.repeat(
|
|
809
|
+
@pytest.mark.repeat(5)
|
|
775
810
|
@pytest.mark.parametrize("ndim", [11, 97, 1536])
|
|
776
811
|
@pytest.mark.parametrize("dtype", ["int8", "uint8"])
|
|
777
812
|
@pytest.mark.parametrize("metric", ["inner", "euclidean", "sqeuclidean", "cosine"])
|
|
@@ -817,7 +852,7 @@ def test_dense_i8(ndim, dtype, metric, capability, stats_fixture):
|
|
|
817
852
|
|
|
818
853
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
819
854
|
@pytest.mark.skipif(not scipy_available, reason="SciPy is not installed")
|
|
820
|
-
@pytest.mark.repeat(
|
|
855
|
+
@pytest.mark.repeat(5)
|
|
821
856
|
@pytest.mark.parametrize("ndim", [11, 97, 1536])
|
|
822
857
|
@pytest.mark.parametrize("metric", ["jaccard", "hamming"])
|
|
823
858
|
@pytest.mark.parametrize("capability", possible_capabilities)
|
|
@@ -838,15 +873,27 @@ def test_dense_bits(ndim, metric, capability, stats_fixture):
|
|
|
838
873
|
np.testing.assert_allclose(result, expected, atol=SIMSIMD_ATOL, rtol=SIMSIMD_RTOL)
|
|
839
874
|
collect_errors(metric, ndim, "bin8", accurate, accurate_dt, expected, expected_dt, result, result_dt, stats_fixture)
|
|
840
875
|
|
|
876
|
+
# Aside from overriding the `dtype` parameter, we can also view as booleans
|
|
877
|
+
result_dt, result = profile(simd_kernel, np.packbits(a).view(np.bool_), np.packbits(b).view(np.bool_))
|
|
878
|
+
result = np.array(result)
|
|
879
|
+
|
|
880
|
+
np.testing.assert_allclose(result, expected, atol=SIMSIMD_ATOL, rtol=SIMSIMD_RTOL)
|
|
881
|
+
collect_errors(metric, ndim, "bin8", accurate, accurate_dt, expected, expected_dt, result, result_dt, stats_fixture)
|
|
882
|
+
|
|
841
883
|
|
|
842
|
-
@pytest.mark.
|
|
843
|
-
@pytest.mark.
|
|
884
|
+
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
885
|
+
@pytest.mark.skipif(not scipy_available, reason="SciPy is not installed")
|
|
886
|
+
@pytest.mark.repeat(5)
|
|
844
887
|
@pytest.mark.parametrize("ndim", [11, 97, 1536])
|
|
845
888
|
@pytest.mark.parametrize("dtype", ["float32", "float16"])
|
|
846
889
|
@pytest.mark.parametrize("capability", possible_capabilities)
|
|
847
|
-
def test_jensen_shannon(ndim, dtype, capability):
|
|
890
|
+
def test_jensen_shannon(ndim, dtype, capability, stats_fixture):
|
|
848
891
|
"""Compares the simd.jensenshannon() function with scipy.spatial.distance.jensenshannon(), measuring the accuracy error for f16, and f32 types."""
|
|
892
|
+
|
|
849
893
|
np.random.seed()
|
|
894
|
+
if dtype == "float16" and is_running_under_qemu():
|
|
895
|
+
pytest.skip("Testing low-precision math isn't reliable in QEMU")
|
|
896
|
+
|
|
850
897
|
a = np.abs(np.random.randn(ndim)).astype(dtype)
|
|
851
898
|
b = np.abs(np.random.randn(ndim)).astype(dtype)
|
|
852
899
|
a /= np.sum(a)
|
|
@@ -890,7 +937,7 @@ def test_cosine_zero_vector(ndim, dtype, capability):
|
|
|
890
937
|
|
|
891
938
|
@pytest.mark.skip(reason="Lacks overflow protection: https://github.com/ashvardanian/SimSIMD/issues/206") # TODO
|
|
892
939
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
893
|
-
@pytest.mark.repeat(
|
|
940
|
+
@pytest.mark.repeat(5)
|
|
894
941
|
@pytest.mark.parametrize("ndim", [11, 97, 1536])
|
|
895
942
|
@pytest.mark.parametrize("dtype", ["float64", "float32", "float16"])
|
|
896
943
|
@pytest.mark.parametrize("metric", ["inner", "euclidean", "sqeuclidean", "cosine"])
|
|
@@ -924,7 +971,7 @@ def test_overflow(ndim, dtype, metric, capability):
|
|
|
924
971
|
|
|
925
972
|
@pytest.mark.skip(reason="Lacks overflow protection: https://github.com/ashvardanian/SimSIMD/issues/206") # TODO
|
|
926
973
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
927
|
-
@pytest.mark.repeat(
|
|
974
|
+
@pytest.mark.repeat(5)
|
|
928
975
|
@pytest.mark.parametrize("ndim", [131072, 262144])
|
|
929
976
|
@pytest.mark.parametrize("metric", ["inner", "euclidean", "sqeuclidean", "cosine"])
|
|
930
977
|
@pytest.mark.parametrize("capability", possible_capabilities)
|
|
@@ -954,16 +1001,15 @@ def test_overflow_i8(ndim, metric, capability):
|
|
|
954
1001
|
|
|
955
1002
|
@pytest.mark.skipif(is_running_under_qemu(), reason="Complex math in QEMU fails")
|
|
956
1003
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
957
|
-
@pytest.mark.repeat(
|
|
958
|
-
@pytest.mark.parametrize("ndim", [
|
|
959
|
-
@pytest.mark.parametrize("dtype", ["
|
|
1004
|
+
@pytest.mark.repeat(5)
|
|
1005
|
+
@pytest.mark.parametrize("ndim", [11, 97, 1536])
|
|
1006
|
+
@pytest.mark.parametrize("dtype", ["complex128", "complex64"])
|
|
960
1007
|
@pytest.mark.parametrize("capability", possible_capabilities)
|
|
961
1008
|
def test_dot_complex(ndim, dtype, capability, stats_fixture):
|
|
962
1009
|
"""Compares the simd.dot() and simd.vdot() against NumPy for complex numbers."""
|
|
963
1010
|
np.random.seed()
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
b = np.random.randn(ndim).astype(dtype=dtype).view(dtype_view)
|
|
1011
|
+
a = (np.random.randn(ndim) + 1.0j * np.random.randn(ndim)).astype(dtype)
|
|
1012
|
+
b = (np.random.randn(ndim) + 1.0j * np.random.randn(ndim)).astype(dtype)
|
|
967
1013
|
|
|
968
1014
|
keep_one_capability(capability)
|
|
969
1015
|
accurate_dt, accurate = profile(np.dot, a.astype(np.complex128), b.astype(np.complex128))
|
|
@@ -972,9 +1018,7 @@ def test_dot_complex(ndim, dtype, capability, stats_fixture):
|
|
|
972
1018
|
result = np.array(result)
|
|
973
1019
|
|
|
974
1020
|
np.testing.assert_allclose(result, expected, atol=SIMSIMD_ATOL, rtol=SIMSIMD_RTOL)
|
|
975
|
-
collect_errors(
|
|
976
|
-
"dot", ndim, dtype + "c", accurate, accurate_dt, expected, expected_dt, result, result_dt, stats_fixture
|
|
977
|
-
)
|
|
1021
|
+
collect_errors("dot", ndim, dtype, accurate, accurate_dt, expected, expected_dt, result, result_dt, stats_fixture)
|
|
978
1022
|
|
|
979
1023
|
accurate_dt, accurate = profile(np.vdot, a.astype(np.complex128), b.astype(np.complex128))
|
|
980
1024
|
expected_dt, expected = profile(np.vdot, a, b)
|
|
@@ -982,36 +1026,11 @@ def test_dot_complex(ndim, dtype, capability, stats_fixture):
|
|
|
982
1026
|
result = np.array(result)
|
|
983
1027
|
|
|
984
1028
|
np.testing.assert_allclose(result, expected, atol=SIMSIMD_ATOL, rtol=SIMSIMD_RTOL)
|
|
985
|
-
collect_errors(
|
|
986
|
-
"vdot", ndim, dtype + "c", accurate, accurate_dt, expected, expected_dt, result, result_dt, stats_fixture
|
|
987
|
-
)
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
@pytest.mark.skipif(is_running_under_qemu(), reason="Complex math in QEMU fails")
|
|
991
|
-
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
992
|
-
@pytest.mark.repeat(50)
|
|
993
|
-
@pytest.mark.parametrize("ndim", [22, 66, 1536])
|
|
994
|
-
@pytest.mark.parametrize("capability", possible_capabilities)
|
|
995
|
-
def test_dot_complex_explicit(ndim, capability):
|
|
996
|
-
"""Compares the simd.dot() and simd.vdot() against NumPy for complex numbers."""
|
|
997
|
-
np.random.seed()
|
|
998
|
-
a = np.random.randn(ndim).astype(dtype=np.float32)
|
|
999
|
-
b = np.random.randn(ndim).astype(dtype=np.float32)
|
|
1000
|
-
|
|
1001
|
-
keep_one_capability(capability)
|
|
1002
|
-
expected = np.dot(a.view(np.complex64), b.view(np.complex64))
|
|
1003
|
-
result = simd.dot(a, b, "complex64")
|
|
1004
|
-
|
|
1005
|
-
np.testing.assert_allclose(result, expected, atol=SIMSIMD_ATOL, rtol=SIMSIMD_RTOL)
|
|
1006
|
-
|
|
1007
|
-
expected = np.vdot(a.view(np.complex64), b.view(np.complex64))
|
|
1008
|
-
result = simd.vdot(a, b, "complex64")
|
|
1009
|
-
|
|
1010
|
-
np.testing.assert_allclose(result, expected, atol=SIMSIMD_ATOL, rtol=SIMSIMD_RTOL)
|
|
1029
|
+
collect_errors("vdot", ndim, dtype, accurate, accurate_dt, expected, expected_dt, result, result_dt, stats_fixture)
|
|
1011
1030
|
|
|
1012
1031
|
|
|
1013
1032
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
1014
|
-
@pytest.mark.repeat(
|
|
1033
|
+
@pytest.mark.repeat(5)
|
|
1015
1034
|
@pytest.mark.parametrize("dtype", ["uint16", "uint32"])
|
|
1016
1035
|
@pytest.mark.parametrize("first_length_bound", [10, 100, 1000])
|
|
1017
1036
|
@pytest.mark.parametrize("second_length_bound", [10, 100, 1000])
|
|
@@ -1041,7 +1060,7 @@ def test_intersect(dtype, first_length_bound, second_length_bound, capability):
|
|
|
1041
1060
|
|
|
1042
1061
|
|
|
1043
1062
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
1044
|
-
@pytest.mark.repeat(
|
|
1063
|
+
@pytest.mark.repeat(5)
|
|
1045
1064
|
@pytest.mark.parametrize("ndim", [11, 97, 1536])
|
|
1046
1065
|
@pytest.mark.parametrize("dtype", ["float64", "float32", "float16", "int8", "uint8"])
|
|
1047
1066
|
@pytest.mark.parametrize("kernel", ["fma"])
|
|
@@ -1102,7 +1121,7 @@ def test_fma(ndim, dtype, kernel, capability, stats_fixture):
|
|
|
1102
1121
|
|
|
1103
1122
|
|
|
1104
1123
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
1105
|
-
@pytest.mark.repeat(
|
|
1124
|
+
@pytest.mark.repeat(5)
|
|
1106
1125
|
@pytest.mark.parametrize("ndim", [11, 97, 1536])
|
|
1107
1126
|
@pytest.mark.parametrize("dtype", ["float64", "float32", "float16", "int8", "uint8"])
|
|
1108
1127
|
@pytest.mark.parametrize("kernel", ["wsum"])
|
|
@@ -1372,7 +1391,7 @@ def test_cdist_complex(ndim, input_dtype, out_dtype, metric, capability):
|
|
|
1372
1391
|
|
|
1373
1392
|
@pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
|
|
1374
1393
|
@pytest.mark.skipif(not scipy_available, reason="SciPy is not installed")
|
|
1375
|
-
@pytest.mark.repeat(
|
|
1394
|
+
@pytest.mark.repeat(5)
|
|
1376
1395
|
@pytest.mark.parametrize("ndim", [11, 97, 1536])
|
|
1377
1396
|
@pytest.mark.parametrize("out_dtype", [None, "float32", "float16", "int8"])
|
|
1378
1397
|
@pytest.mark.parametrize("capability", possible_capabilities)
|