usearch 2.16.2 → 2.16.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.
- package/README.md +1 -1
- package/c/lib.cpp +1 -1
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_plugins.hpp +14 -9
- package/javascript/dist/cjs/usearch.d.ts +2 -2
- package/javascript/dist/esm/usearch.d.ts +2 -2
- package/javascript/usearch.ts +2 -2
- 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 +31 -2
- package/simsimd/Cargo.lock +1 -1
- package/simsimd/Cargo.toml +1 -1
- package/simsimd/README.md +93 -39
- package/simsimd/c/lib.c +32 -25
- package/simsimd/include/simsimd/elementwise.h +1068 -49
- package/simsimd/include/simsimd/simsimd.h +78 -73
- package/simsimd/include/simsimd/types.h +34 -17
- package/simsimd/javascript/fallback.ts +27 -4
- package/simsimd/javascript/lib.c +49 -29
- package/simsimd/javascript/simsimd.ts +34 -14
- package/simsimd/package-lock.json +13 -11
- package/simsimd/package.json +3 -3
- package/simsimd/scripts/bench.cxx +2 -5
- package/simsimd/scripts/bench_vectors.py +44 -10
- package/simsimd/scripts/test.c +39 -35
- package/simsimd/scripts/test.mjs +28 -0
- package/simsimd/scripts/test.py +19 -19
- package/simsimd/swift/SimSIMD.swift +12 -7
|
@@ -18,47 +18,66 @@ try {
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* @brief Computes the squared Euclidean distance between two vectors.
|
|
21
|
-
* @param {Float64Array|Float32Array|Int8Array} a - The first vector.
|
|
22
|
-
* @param {Float64Array|Float32Array|Int8Array} b - The second vector.
|
|
21
|
+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
|
|
22
|
+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} b - The second vector.
|
|
23
23
|
* @returns {number} The squared Euclidean distance between vectors a and b.
|
|
24
24
|
*/
|
|
25
25
|
export const sqeuclidean = (
|
|
26
|
-
a: Float64Array | Float32Array | Int8Array,
|
|
27
|
-
b: Float64Array | Float32Array | Int8Array
|
|
26
|
+
a: Float64Array | Float32Array | Int8Array | Uint8Array,
|
|
27
|
+
b: Float64Array | Float32Array | Int8Array | Uint8Array
|
|
28
28
|
): number => {
|
|
29
29
|
return compiled.sqeuclidean(a, b);
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* @brief Computes the Euclidean distance between two vectors.
|
|
34
|
+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
|
|
35
|
+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} b - The second vector.
|
|
36
|
+
* @returns {number} The squared Euclidean distance between vectors a and b.
|
|
37
|
+
*/
|
|
38
|
+
export const euclidean = (
|
|
39
|
+
a: Float64Array | Float32Array | Int8Array | Uint8Array,
|
|
40
|
+
b: Float64Array | Float32Array | Int8Array | Uint8Array
|
|
41
|
+
): number => {
|
|
42
|
+
return compiled.euclidean(a, b);
|
|
43
|
+
};
|
|
44
|
+
|
|
32
45
|
/**
|
|
33
46
|
* @brief Computes the cosine distance between two vectors.
|
|
34
|
-
* @param {Float64Array|Float32Array|Int8Array} a - The first vector.
|
|
35
|
-
* @param {Float64Array|Float32Array|Int8Array} b - The second vector.
|
|
47
|
+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
|
|
48
|
+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} b - The second vector.
|
|
36
49
|
* @returns {number} The cosine distance between vectors a and b.
|
|
37
50
|
*/
|
|
38
51
|
export const cosine = (
|
|
39
|
-
a: Float64Array | Float32Array | Int8Array,
|
|
40
|
-
b: Float64Array | Float32Array | Int8Array
|
|
52
|
+
a: Float64Array | Float32Array | Int8Array | Uint8Array,
|
|
53
|
+
b: Float64Array | Float32Array | Int8Array | Uint8Array
|
|
41
54
|
): number => {
|
|
42
55
|
return compiled.cosine(a, b);
|
|
43
56
|
};
|
|
44
57
|
|
|
45
58
|
/**
|
|
46
59
|
* @brief Computes the inner product of two vectors (same as dot product).
|
|
47
|
-
* @param {Float64Array|Float32Array} a - The first vector.
|
|
48
|
-
* @param {Float64Array|Float32Array} b - The second vector.
|
|
60
|
+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
|
|
61
|
+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} b - The second vector.
|
|
49
62
|
* @returns {number} The inner product of vectors a and b.
|
|
50
63
|
*/
|
|
51
|
-
export const inner = (
|
|
64
|
+
export const inner = (
|
|
65
|
+
a: Float64Array | Float32Array | Int8Array | Uint8Array,
|
|
66
|
+
b: Float64Array | Float32Array | Int8Array | Uint8Array
|
|
67
|
+
): number => {
|
|
52
68
|
return compiled.inner(a, b);
|
|
53
69
|
};
|
|
54
70
|
|
|
55
71
|
/**
|
|
56
72
|
* @brief Computes the dot product of two vectors (same as inner product).
|
|
57
|
-
* @param {Float64Array|Float32Array} a - The first vector.
|
|
58
|
-
* @param {Float64Array|Float32Array} b - The second vector.
|
|
73
|
+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
|
|
74
|
+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} b - The second vector.
|
|
59
75
|
* @returns {number} The dot product of vectors a and b.
|
|
60
76
|
*/
|
|
61
|
-
export const dot = (
|
|
77
|
+
export const dot = (
|
|
78
|
+
a: Float64Array | Float32Array | Int8Array | Uint8Array,
|
|
79
|
+
b: Float64Array | Float32Array | Int8Array | Uint8Array
|
|
80
|
+
): number => {
|
|
62
81
|
return compiled.dot(a, b);
|
|
63
82
|
};
|
|
64
83
|
|
|
@@ -127,6 +146,7 @@ export default {
|
|
|
127
146
|
dot,
|
|
128
147
|
inner,
|
|
129
148
|
sqeuclidean,
|
|
149
|
+
euclidean,
|
|
130
150
|
cosine,
|
|
131
151
|
hamming,
|
|
132
152
|
jaccard,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simsimd",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.9.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "simsimd",
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "5.9.2",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "Apache 2.0",
|
|
12
12
|
"dependencies": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@types/bindings": "^1.5.5",
|
|
19
|
-
"@types/node": "^20.
|
|
19
|
+
"@types/node": "^20.17.1",
|
|
20
20
|
"node-gyp": "^10.0.1",
|
|
21
21
|
"prebuildify": "^6.0.0",
|
|
22
22
|
"typescript": "^5.3.3"
|
|
@@ -107,12 +107,13 @@
|
|
|
107
107
|
}
|
|
108
108
|
},
|
|
109
109
|
"node_modules/@types/node": {
|
|
110
|
-
"version": "20.
|
|
111
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.
|
|
112
|
-
"integrity": "sha512-
|
|
110
|
+
"version": "20.17.1",
|
|
111
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.1.tgz",
|
|
112
|
+
"integrity": "sha512-j2VlPv1NnwPJbaCNv69FO/1z4lId0QmGvpT41YxitRtWlg96g/j8qcv2RKsLKe2F6OJgyXhupN1Xo17b2m139Q==",
|
|
113
113
|
"devOptional": true,
|
|
114
|
+
"license": "MIT",
|
|
114
115
|
"dependencies": {
|
|
115
|
-
"undici-types": "~
|
|
116
|
+
"undici-types": "~6.19.2"
|
|
116
117
|
}
|
|
117
118
|
},
|
|
118
119
|
"node_modules/abbrev": {
|
|
@@ -1525,10 +1526,11 @@
|
|
|
1525
1526
|
}
|
|
1526
1527
|
},
|
|
1527
1528
|
"node_modules/undici-types": {
|
|
1528
|
-
"version": "
|
|
1529
|
-
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-
|
|
1530
|
-
"integrity": "sha512-
|
|
1531
|
-
"devOptional": true
|
|
1529
|
+
"version": "6.19.8",
|
|
1530
|
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
|
1531
|
+
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
|
1532
|
+
"devOptional": true,
|
|
1533
|
+
"license": "MIT"
|
|
1532
1534
|
},
|
|
1533
1535
|
"node_modules/unique-filename": {
|
|
1534
1536
|
"version": "3.0.0",
|
package/simsimd/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simsimd",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.5",
|
|
4
4
|
"description": "Portable mixed-precision BLAS-like vector math library for x86 and ARM",
|
|
5
5
|
"homepage": "https://github.com/ashvardanian/simsimd",
|
|
6
6
|
"author": "Ash Vardanian",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/bindings": "^1.5.5",
|
|
51
|
-
"@types/node": "^20.
|
|
51
|
+
"@types/node": "^20.17.1",
|
|
52
52
|
"node-gyp": "^10.0.1",
|
|
53
53
|
"prebuildify": "^6.0.0",
|
|
54
54
|
"typescript": "^5.3.3"
|
|
@@ -58,4 +58,4 @@
|
|
|
58
58
|
"mathjs": "^11.11.2",
|
|
59
59
|
"usearch": "^2.8.15"
|
|
60
60
|
}
|
|
61
|
-
}
|
|
61
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#include <cmath> // `std::sqrt`
|
|
2
2
|
#include <cstdlib> // `std::aligned_alloc`
|
|
3
3
|
#include <cstring> // `std::memcpy`
|
|
4
|
+
#include <numeric> // `std::accumulate`
|
|
4
5
|
#include <random> // `std::uniform_int_distribution`
|
|
5
6
|
#include <thread> // `std::thread`
|
|
6
7
|
#include <tuple> // `std::tuple` for callable introspection
|
|
7
|
-
#include <type_traits> //
|
|
8
|
+
#include <type_traits> // `std::numeric_limits`
|
|
8
9
|
#include <unordered_set> // `std::unordered_set`
|
|
9
10
|
#include <vector> // `std::vector`
|
|
10
11
|
|
|
@@ -818,10 +819,6 @@ int main(int argc, char **argv) {
|
|
|
818
819
|
dense_<f16_k>("l2sq_f16_sve", simsimd_l2sq_f16_sve, simsimd_l2sq_f16_accurate);
|
|
819
820
|
dense_<f16_k>("l2_f16_sve", simsimd_l2_f16_sve, simsimd_l2_f16_accurate);
|
|
820
821
|
|
|
821
|
-
dense_<bf16_k>("cos_bf16_sve", simsimd_cos_bf16_sve, simsimd_cos_bf16_accurate);
|
|
822
|
-
dense_<bf16_k>("l2sq_bf16_sve", simsimd_l2sq_bf16_sve, simsimd_l2sq_bf16_accurate);
|
|
823
|
-
dense_<bf16_k>("l2_bf16_sve", simsimd_l2_bf16_sve, simsimd_l2_bf16_accurate);
|
|
824
|
-
|
|
825
822
|
dense_<f32_k>("dot_f32_sve", simsimd_dot_f32_sve, simsimd_dot_f32_accurate);
|
|
826
823
|
dense_<f32_k>("cos_f32_sve", simsimd_cos_f32_sve, simsimd_cos_f32_accurate);
|
|
827
824
|
dense_<f32_k>("l2sq_f32_sve", simsimd_l2sq_f32_sve, simsimd_l2sq_f32_accurate);
|
|
@@ -44,7 +44,7 @@ metric_families = [
|
|
|
44
44
|
"sparse", # Intersection of two sparse integer sets, with float/int weights
|
|
45
45
|
]
|
|
46
46
|
dtype_names = [
|
|
47
|
-
"
|
|
47
|
+
"bin8", #! Not supported by SciPy
|
|
48
48
|
"int8", #! Presented as supported, but overflows most of the time
|
|
49
49
|
"uint16",
|
|
50
50
|
"uint32",
|
|
@@ -72,6 +72,21 @@ class Kernel:
|
|
|
72
72
|
tensor_type: callable = np.array
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
def serial_cosine(a: List[float], b: List[float]) -> float:
|
|
76
|
+
dot_product = sum(ai * bi for ai, bi in zip(a, b))
|
|
77
|
+
norm_a = sum(ai * ai for ai in a) ** 0.5
|
|
78
|
+
norm_b = sum(bi * bi for bi in b) ** 0.5
|
|
79
|
+
if norm_a == 0 and norm_b == 0:
|
|
80
|
+
return 1
|
|
81
|
+
if dot_product == 0:
|
|
82
|
+
return 0
|
|
83
|
+
return dot_product / (norm_a * norm_b)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def serial_sqeuclidean(a: List[float], b: List[float]) -> float:
|
|
87
|
+
return sum((ai - bi) ** 2 for ai, bi in zip(a, b))
|
|
88
|
+
|
|
89
|
+
|
|
75
90
|
def yield_kernels(
|
|
76
91
|
metric_families: List[str],
|
|
77
92
|
dtype_names: List[str],
|
|
@@ -197,6 +212,25 @@ def yield_kernels(
|
|
|
197
212
|
simd.vdot,
|
|
198
213
|
lambda A, B: simd.cdist(A, B, metric="vdot"),
|
|
199
214
|
)
|
|
215
|
+
if "spatial" in metric_families:
|
|
216
|
+
yield from for_dtypes(
|
|
217
|
+
"serial.cosine",
|
|
218
|
+
["float64", "float32", "float16", "int8"],
|
|
219
|
+
serial_cosine,
|
|
220
|
+
wrap_rows_batch_calls(serial_cosine),
|
|
221
|
+
lambda A, B: spd.cdist(A, B, "cosine"),
|
|
222
|
+
simd.cosine,
|
|
223
|
+
lambda A, B: simd.cdist(A, B, metric="cosine"),
|
|
224
|
+
)
|
|
225
|
+
yield from for_dtypes(
|
|
226
|
+
"serial.sqeuclidean",
|
|
227
|
+
["float64", "float32", "float16", "int8"],
|
|
228
|
+
serial_sqeuclidean,
|
|
229
|
+
wrap_rows_batch_calls(serial_sqeuclidean),
|
|
230
|
+
lambda A, B: spd.cdist(A, B, "sqeuclidean"),
|
|
231
|
+
simd.sqeuclidean,
|
|
232
|
+
lambda A, B: simd.cdist(A, B, metric="sqeuclidean"),
|
|
233
|
+
)
|
|
200
234
|
if "spatial" in metric_families and include_scipy:
|
|
201
235
|
yield from for_dtypes(
|
|
202
236
|
"scipy.cosine",
|
|
@@ -248,21 +282,21 @@ def yield_kernels(
|
|
|
248
282
|
if "binary" in metric_families and include_scipy:
|
|
249
283
|
yield from for_dtypes(
|
|
250
284
|
"scipy.hamming",
|
|
251
|
-
["
|
|
285
|
+
["bin8"],
|
|
252
286
|
spd.hamming,
|
|
253
287
|
wrap_rows_batch_calls(spd.hamming),
|
|
254
288
|
lambda A, B: spd.cdist(A, B, "hamming"),
|
|
255
|
-
lambda A, B: simd.hamming(A, B, "
|
|
256
|
-
lambda A, B: simd.cdist(A, B, "
|
|
289
|
+
lambda A, B: simd.hamming(A, B, "bin8"),
|
|
290
|
+
lambda A, B: simd.cdist(A, B, "bin8", metric="hamming"),
|
|
257
291
|
)
|
|
258
292
|
yield from for_dtypes(
|
|
259
293
|
"scipy.jaccard",
|
|
260
|
-
["
|
|
294
|
+
["bin8"],
|
|
261
295
|
spd.jaccard,
|
|
262
296
|
wrap_rows_batch_calls(spd.jaccard),
|
|
263
297
|
lambda A, B: spd.cdist(A, B, "jaccard"),
|
|
264
|
-
lambda A, B: simd.jaccard(A, B, "
|
|
265
|
-
lambda A, B: simd.cdist(A, B, "
|
|
298
|
+
lambda A, B: simd.jaccard(A, B, "bin8"),
|
|
299
|
+
lambda A, B: simd.cdist(A, B, "bin8", metric="jaccard"),
|
|
266
300
|
)
|
|
267
301
|
if "spatial" in metric_families and include_scikit:
|
|
268
302
|
yield from for_dtypes(
|
|
@@ -351,7 +385,7 @@ def random_matrix(count: int, ndim: int, dtype: str) -> np.ndarray:
|
|
|
351
385
|
return np.random.randint(0, high=256, size=(count, ndim), dtype=np.int16)
|
|
352
386
|
if dtype == "int8":
|
|
353
387
|
return np.random.randint(-100, high=100, size=(count, ndim), dtype=np.int8)
|
|
354
|
-
if dtype == "
|
|
388
|
+
if dtype == "bin8":
|
|
355
389
|
return np.packbits(np.random.randint(0, high=2, size=(count, ndim), dtype=np.uint8), axis=0)
|
|
356
390
|
|
|
357
391
|
|
|
@@ -528,11 +562,11 @@ def result_to_row(result: Result) -> List[str]:
|
|
|
528
562
|
if isinstance(result.baseline_seconds, float):
|
|
529
563
|
ops_per_second = result.distance_calculations / result.baseline_seconds
|
|
530
564
|
gbs_per_second = result.bytes_per_vector * ops_per_second / 1e9
|
|
531
|
-
baseline_cell = f"{ops_per_second:,.0f} ops/s, {gbs_per_second:,.
|
|
565
|
+
baseline_cell = f"{ops_per_second:,.0f} ops/s, {gbs_per_second:,.3f} GB/s"
|
|
532
566
|
if isinstance(result.simsimd_seconds, float):
|
|
533
567
|
ops_per_second = result.distance_calculations / result.simsimd_seconds
|
|
534
568
|
gbs_per_second = result.bytes_per_vector * ops_per_second / 1e9
|
|
535
|
-
simsimd_cell = f"{ops_per_second:,.0f} ops/s, {gbs_per_second:,.
|
|
569
|
+
simsimd_cell = f"{ops_per_second:,.0f} ops/s, {gbs_per_second:,.3f} GB/s"
|
|
536
570
|
if isinstance(result.baseline_seconds, float) and isinstance(result.simsimd_seconds, float):
|
|
537
571
|
improvement_cell = f"{result.baseline_seconds / result.simsimd_seconds:,.2f} x"
|
|
538
572
|
|
package/simsimd/scripts/test.c
CHANGED
|
@@ -91,59 +91,63 @@ void test_distance_from_itself(void) {
|
|
|
91
91
|
simsimd_f16_t f16s[1536];
|
|
92
92
|
simsimd_bf16_t bf16s[1536];
|
|
93
93
|
simsimd_i8_t i8s[1536];
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
simsimd_u8_t u8s[1536];
|
|
95
|
+
simsimd_b8_t b8s[1536 / 8]; // 8 bits per word
|
|
96
|
+
simsimd_distance_t distance[2]; // For complex dot-products we need two values
|
|
96
97
|
|
|
97
98
|
// Cosine distance between two vectors
|
|
98
|
-
simsimd_cos_i8(i8s, i8s, 1536, &distance);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
simsimd_cos_i8(i8s, i8s, 1536, &distance[0]);
|
|
100
|
+
simsimd_cos_u8(u8s, u8s, 1536, &distance[0]);
|
|
101
|
+
simsimd_cos_f16(f16s, f16s, 1536, &distance[0]);
|
|
102
|
+
simsimd_cos_bf16(bf16s, bf16s, 1536, &distance[0]);
|
|
103
|
+
simsimd_cos_f32(f32s, f32s, 1536, &distance[0]);
|
|
104
|
+
simsimd_cos_f64(f64s, f64s, 1536, &distance[0]);
|
|
103
105
|
|
|
104
106
|
// Euclidean distance between two vectors
|
|
105
|
-
simsimd_l2sq_i8(i8s, i8s, 1536, &distance);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
simsimd_l2sq_i8(i8s, i8s, 1536, &distance[0]);
|
|
108
|
+
simsimd_l2sq_u8(u8s, u8s, 1536, &distance[0]);
|
|
109
|
+
simsimd_l2sq_f16(f16s, f16s, 1536, &distance[0]);
|
|
110
|
+
simsimd_l2sq_bf16(bf16s, bf16s, 1536, &distance[0]);
|
|
111
|
+
simsimd_l2sq_f32(f32s, f32s, 1536, &distance[0]);
|
|
112
|
+
simsimd_l2sq_f64(f64s, f64s, 1536, &distance[0]);
|
|
110
113
|
|
|
111
114
|
// Inner product between two vectors
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
simsimd_dot_i8(i8s, i8s, 1536, &distance[0]);
|
|
116
|
+
simsimd_dot_u8(u8s, u8s, 1536, &distance[0]);
|
|
117
|
+
simsimd_dot_f16(f16s, f16s, 1536, &distance[0]);
|
|
118
|
+
simsimd_dot_bf16(bf16s, bf16s, 1536, &distance[0]);
|
|
119
|
+
simsimd_dot_f32(f32s, f32s, 1536, &distance[0]);
|
|
120
|
+
simsimd_dot_f64(f64s, f64s, 1536, &distance[0]);
|
|
116
121
|
|
|
117
122
|
// Complex inner product between two vectors
|
|
118
|
-
simsimd_dot_f16c(f16s, f16s, 1536, &distance);
|
|
119
|
-
simsimd_dot_bf16c(bf16s, bf16s, 1536, &distance);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
simsimd_dot_f64c(f64s, f64s, 1536, &distance);
|
|
123
|
+
simsimd_dot_f16c(f16s, f16s, 1536, &distance[0]);
|
|
124
|
+
simsimd_dot_bf16c(bf16s, bf16s, 1536, &distance[0]);
|
|
125
|
+
simsimd_dot_f32c(f32s, f32s, 1536, &distance[0]);
|
|
126
|
+
simsimd_dot_f64c(f64s, f64s, 1536, &distance[0]);
|
|
123
127
|
|
|
124
128
|
// Complex conjugate inner product between two vectors
|
|
125
|
-
simsimd_vdot_f16c(f16s, f16s, 1536, &distance);
|
|
126
|
-
simsimd_vdot_bf16c(bf16s, bf16s, 1536, &distance);
|
|
127
|
-
simsimd_vdot_f32c(f32s, f32s, 1536, &distance);
|
|
128
|
-
simsimd_vdot_f64c(f64s, f64s, 1536, &distance);
|
|
129
|
+
simsimd_vdot_f16c(f16s, f16s, 1536, &distance[0]);
|
|
130
|
+
simsimd_vdot_bf16c(bf16s, bf16s, 1536, &distance[0]);
|
|
131
|
+
simsimd_vdot_f32c(f32s, f32s, 1536, &distance[0]);
|
|
132
|
+
simsimd_vdot_f64c(f64s, f64s, 1536, &distance[0]);
|
|
129
133
|
|
|
130
134
|
// Hamming distance between two vectors
|
|
131
|
-
simsimd_hamming_b8(b8s, b8s, 1536 / 8, &distance);
|
|
135
|
+
simsimd_hamming_b8(b8s, b8s, 1536 / 8, &distance[0]);
|
|
132
136
|
|
|
133
137
|
// Jaccard distance between two vectors
|
|
134
|
-
simsimd_jaccard_b8(b8s, b8s, 1536 / 8, &distance);
|
|
138
|
+
simsimd_jaccard_b8(b8s, b8s, 1536 / 8, &distance[0]);
|
|
135
139
|
|
|
136
140
|
// Jensen-Shannon divergence between two vectors
|
|
137
|
-
simsimd_js_f16(f16s, f16s, 1536, &distance);
|
|
138
|
-
simsimd_js_bf16(bf16s, bf16s, 1536, &distance);
|
|
139
|
-
simsimd_js_f32(f32s, f32s, 1536, &distance);
|
|
140
|
-
simsimd_js_f64(f64s, f64s, 1536, &distance);
|
|
141
|
+
simsimd_js_f16(f16s, f16s, 1536, &distance[0]);
|
|
142
|
+
simsimd_js_bf16(bf16s, bf16s, 1536, &distance[0]);
|
|
143
|
+
simsimd_js_f32(f32s, f32s, 1536, &distance[0]);
|
|
144
|
+
simsimd_js_f64(f64s, f64s, 1536, &distance[0]);
|
|
141
145
|
|
|
142
146
|
// Kullback-Leibler divergence between two vectors
|
|
143
|
-
simsimd_kl_f16(f16s, f16s, 1536, &distance);
|
|
144
|
-
simsimd_kl_bf16(bf16s, bf16s, 1536, &distance);
|
|
145
|
-
simsimd_kl_f32(f32s, f32s, 1536, &distance);
|
|
146
|
-
simsimd_kl_f64(f64s, f64s, 1536, &distance);
|
|
147
|
+
simsimd_kl_f16(f16s, f16s, 1536, &distance[0]);
|
|
148
|
+
simsimd_kl_bf16(bf16s, bf16s, 1536, &distance[0]);
|
|
149
|
+
simsimd_kl_f32(f32s, f32s, 1536, &distance[0]);
|
|
150
|
+
simsimd_kl_f64(f64s, f64s, 1536, &distance[0]);
|
|
147
151
|
}
|
|
148
152
|
|
|
149
153
|
int main(int argc, char **argv) {
|
package/simsimd/scripts/test.mjs
CHANGED
|
@@ -102,9 +102,20 @@ test("Distance from itself JS fallback", () => {
|
|
|
102
102
|
const f32Array1 = new Float32Array([1.0, 2.0, 3.0]);
|
|
103
103
|
const f32Array2 = new Float32Array([4.0, 5.0, 6.0]);
|
|
104
104
|
|
|
105
|
+
const uint8Array1 = new Uint8Array([1, 2, 3]);
|
|
106
|
+
const uint8Array2 = new Uint8Array([4, 5, 6]);
|
|
107
|
+
|
|
105
108
|
test("Squared Euclidean Distance", () => {
|
|
106
109
|
const result = simsimd.sqeuclidean(f32Array1, f32Array2);
|
|
107
110
|
assertAlmostEqual(result, 27.0, 0.01);
|
|
111
|
+
|
|
112
|
+
const uint8Result = simsimd.sqeuclidean(uint8Array1, uint8Array2);
|
|
113
|
+
assertAlmostEqual(uint8Result, 27.0, 0.01);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("Euclidean Distance", () => {
|
|
117
|
+
const result = simsimd.euclidean(f32Array1, f32Array2);
|
|
118
|
+
assertAlmostEqual(result, 5.2, 0.01);
|
|
108
119
|
});
|
|
109
120
|
|
|
110
121
|
test("Inner Distance", () => {
|
|
@@ -120,6 +131,9 @@ test("Cosine Similarity", () => {
|
|
|
120
131
|
test("Squared Euclidean Distance JS", () => {
|
|
121
132
|
const result = fallback.sqeuclidean(f32Array1, f32Array2);
|
|
122
133
|
assertAlmostEqual(result, 27.0, 0.01);
|
|
134
|
+
|
|
135
|
+
const uint8Result = fallback.sqeuclidean(uint8Array1, uint8Array2);
|
|
136
|
+
assertAlmostEqual(uint8Result, 27.0, 0.01);
|
|
123
137
|
});
|
|
124
138
|
|
|
125
139
|
test("Inner Distance JS", () => {
|
|
@@ -136,6 +150,20 @@ test("Squared Euclidean Distance C vs JS", () => {
|
|
|
136
150
|
const result = simsimd.sqeuclidean(f32Array1, f32Array2);
|
|
137
151
|
const resultjs = fallback.sqeuclidean(f32Array1, f32Array2);
|
|
138
152
|
assertAlmostEqual(resultjs, result, 0.01);
|
|
153
|
+
|
|
154
|
+
const uint8result = simsimd.sqeuclidean(uint8Array1, uint8Array2);
|
|
155
|
+
const uint8resultjs = fallback.sqeuclidean(uint8Array1, uint8Array2);
|
|
156
|
+
assertAlmostEqual(uint8resultjs, uint8result, 0.01);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("Euclidean Distance C vs JS", () => {
|
|
160
|
+
const result = simsimd.euclidean(f32Array1, f32Array2);
|
|
161
|
+
const resultjs = fallback.euclidean(f32Array1, f32Array2);
|
|
162
|
+
assertAlmostEqual(resultjs, result, 0.01);
|
|
163
|
+
|
|
164
|
+
const uint8result = simsimd.euclidean(uint8Array1, uint8Array2);
|
|
165
|
+
const uint8resultjs = fallback.euclidean(uint8Array1, uint8Array2);
|
|
166
|
+
assertAlmostEqual(uint8resultjs, uint8result, 0.01);
|
|
139
167
|
});
|
|
140
168
|
|
|
141
169
|
test("Inner Distance C vs JS", () => {
|
package/simsimd/scripts/test.py
CHANGED
|
@@ -491,25 +491,25 @@ def hex_array(arr):
|
|
|
491
491
|
|
|
492
492
|
def test_pointers_availability():
|
|
493
493
|
"""Tests the availability of pre-compiled functions for compatibility with USearch."""
|
|
494
|
-
assert simd.pointer_to_sqeuclidean("
|
|
495
|
-
assert simd.pointer_to_cosine("
|
|
496
|
-
assert simd.pointer_to_inner("
|
|
494
|
+
assert simd.pointer_to_sqeuclidean("float64") != 0
|
|
495
|
+
assert simd.pointer_to_cosine("float64") != 0
|
|
496
|
+
assert simd.pointer_to_inner("float64") != 0
|
|
497
497
|
|
|
498
|
-
assert simd.pointer_to_sqeuclidean("
|
|
499
|
-
assert simd.pointer_to_cosine("
|
|
500
|
-
assert simd.pointer_to_inner("
|
|
498
|
+
assert simd.pointer_to_sqeuclidean("float32") != 0
|
|
499
|
+
assert simd.pointer_to_cosine("float32") != 0
|
|
500
|
+
assert simd.pointer_to_inner("float32") != 0
|
|
501
501
|
|
|
502
|
-
assert simd.pointer_to_sqeuclidean("
|
|
503
|
-
assert simd.pointer_to_cosine("
|
|
504
|
-
assert simd.pointer_to_inner("
|
|
502
|
+
assert simd.pointer_to_sqeuclidean("float16") != 0
|
|
503
|
+
assert simd.pointer_to_cosine("float16") != 0
|
|
504
|
+
assert simd.pointer_to_inner("float16") != 0
|
|
505
505
|
|
|
506
|
-
assert simd.pointer_to_sqeuclidean("
|
|
507
|
-
assert simd.pointer_to_cosine("
|
|
508
|
-
assert simd.pointer_to_inner("
|
|
506
|
+
assert simd.pointer_to_sqeuclidean("int8") != 0
|
|
507
|
+
assert simd.pointer_to_cosine("int8") != 0
|
|
508
|
+
assert simd.pointer_to_inner("int8") != 0
|
|
509
509
|
|
|
510
|
-
assert simd.pointer_to_sqeuclidean("
|
|
511
|
-
assert simd.pointer_to_cosine("
|
|
512
|
-
assert simd.pointer_to_inner("
|
|
510
|
+
assert simd.pointer_to_sqeuclidean("uint8") != 0
|
|
511
|
+
assert simd.pointer_to_cosine("uint8") != 0
|
|
512
|
+
assert simd.pointer_to_inner("uint8") != 0
|
|
513
513
|
|
|
514
514
|
|
|
515
515
|
def test_capabilities_list():
|
|
@@ -832,11 +832,11 @@ def test_dense_bits(ndim, metric, capability, stats_fixture):
|
|
|
832
832
|
baseline_kernel, simd_kernel = name_to_kernels(metric)
|
|
833
833
|
accurate_dt, accurate = profile(baseline_kernel, a.astype(np.uint64), b.astype(np.uint64))
|
|
834
834
|
expected_dt, expected = profile(baseline_kernel, a, b)
|
|
835
|
-
result_dt, result = profile(simd_kernel, np.packbits(a), np.packbits(b), "
|
|
835
|
+
result_dt, result = profile(simd_kernel, np.packbits(a), np.packbits(b), "bin8")
|
|
836
836
|
result = np.array(result)
|
|
837
837
|
|
|
838
838
|
np.testing.assert_allclose(result, expected, atol=SIMSIMD_ATOL, rtol=SIMSIMD_RTOL)
|
|
839
|
-
collect_errors(metric, ndim, "
|
|
839
|
+
collect_errors(metric, ndim, "bin8", accurate, accurate_dt, expected, expected_dt, result, result_dt, stats_fixture)
|
|
840
840
|
|
|
841
841
|
|
|
842
842
|
@pytest.mark.skip(reason="Problems inferring the tolerance bounds for numerical errors")
|
|
@@ -1391,10 +1391,10 @@ def test_cdist_hamming(ndim, out_dtype, capability):
|
|
|
1391
1391
|
if out_dtype is None:
|
|
1392
1392
|
# SciPy divides the Hamming distance by the number of dimensions, so we need to multiply it back.
|
|
1393
1393
|
expected = spd.cdist(A, B, "hamming") * ndim
|
|
1394
|
-
result = simd.cdist(A_bits, B_bits, metric="hamming", dtype="
|
|
1394
|
+
result = simd.cdist(A_bits, B_bits, metric="hamming", dtype="bin8")
|
|
1395
1395
|
else:
|
|
1396
1396
|
expected = (spd.cdist(A, B, "hamming") * ndim).astype(out_dtype)
|
|
1397
|
-
result = simd.cdist(A_bits, B_bits, metric="hamming", dtype="
|
|
1397
|
+
result = simd.cdist(A_bits, B_bits, metric="hamming", dtype="bin8", out_dtype=out_dtype)
|
|
1398
1398
|
|
|
1399
1399
|
np.testing.assert_allclose(result, expected, atol=SIMSIMD_ATOL, rtol=SIMSIMD_RTOL)
|
|
1400
1400
|
|
|
@@ -2,9 +2,9 @@ import CSimSIMD
|
|
|
2
2
|
|
|
3
3
|
public protocol SimSIMD {
|
|
4
4
|
static var dataType: simsimd_datatype_t { get }
|
|
5
|
-
static var cosine:
|
|
6
|
-
static var dotProduct:
|
|
7
|
-
static var squaredEuclidean:
|
|
5
|
+
static var cosine: simsimd_metric_dense_punned_t { get }
|
|
6
|
+
static var dotProduct: simsimd_metric_dense_punned_t { get }
|
|
7
|
+
static var squaredEuclidean: simsimd_metric_dense_punned_t { get }
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
extension Int8: SimSIMD {
|
|
@@ -71,7 +71,7 @@ extension RandomAccessCollection where Element: SimSIMD {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
@inlinable @inline(__always)
|
|
74
|
-
func perform<A, B>(_ metric:
|
|
74
|
+
func perform<A, B>(_ metric: simsimd_metric_dense_punned_t, a: A, b: B) -> Double? where A: Sequence, B: Sequence, A.Element == B.Element {
|
|
75
75
|
var distance: simsimd_distance_t = 0
|
|
76
76
|
let result = a.withContiguousStorageIfAvailable { a in
|
|
77
77
|
b.withContiguousStorageIfAvailable { b in
|
|
@@ -118,10 +118,15 @@ extension simsimd_capability_t: OptionSet, CustomStringConvertible {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
@inline(__always)
|
|
121
|
-
private func find(kind: simsimd_metric_kind_t, dataType: simsimd_datatype_t) ->
|
|
122
|
-
var output:
|
|
121
|
+
private func find(kind: simsimd_metric_kind_t, dataType: simsimd_datatype_t) -> simsimd_metric_dense_punned_t {
|
|
122
|
+
var output: simsimd_metric_dense_punned_t?
|
|
123
123
|
var used = simsimd_capability_t.any
|
|
124
|
-
|
|
124
|
+
// Use `withUnsafeMutablePointer` to safely cast `output` to the required pointer type.
|
|
125
|
+
withUnsafeMutablePointer(to: &output) { outputPtr in
|
|
126
|
+
// Cast the pointer to `UnsafeMutablePointer<simsimd_kernel_punned_t?>`
|
|
127
|
+
let castedPtr = outputPtr.withMemoryRebound(to: Optional<simsimd_kernel_punned_t>.self, capacity: 1) { $0 }
|
|
128
|
+
simsimd_find_kernel_punned(kind, dataType, .available, .any, castedPtr, &used)
|
|
129
|
+
}
|
|
125
130
|
guard let output else { fatalError("Could not find function \(kind) for \(dataType)") }
|
|
126
131
|
return output
|
|
127
132
|
}
|