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.
@@ -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 = (a: Float64Array | Float32Array, b: Float64Array | Float32Array): number => {
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 = (a: Float64Array | Float32Array, b: Float64Array | Float32Array): number => {
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": "4.2.1",
3
+ "version": "5.9.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "simsimd",
9
- "version": "4.2.1",
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.12.2",
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.12.2",
111
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.2.tgz",
112
- "integrity": "sha512-zQ0NYO87hyN6Xrclcqp7f8ZbXNbRfoGWNcMvHTPQp9UUrwI0mI7XBz+cu7/W6/VClYo2g63B0cjull/srU7LgQ==",
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": "~5.26.4"
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": "5.26.5",
1529
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
1530
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simsimd",
3
- "version": "5.9.6",
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.12.2",
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
- "bits", #! Not supported by SciPy
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
- ["bits"],
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, "bits"),
256
- lambda A, B: simd.cdist(A, B, "bits", metric="hamming"),
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
- ["bits"],
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, "bits"),
265
- lambda A, B: simd.cdist(A, B, "bits", metric="jaccard"),
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 == "bits":
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:,.2f} GB/s"
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:,.2f} GB/s"
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
 
@@ -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
- simsimd_b8_t b8s[1536 / 8]; // 8 bits per word
95
- simsimd_distance_t distance;
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
- simsimd_cos_f16(f16s, f16s, 1536, &distance);
100
- simsimd_cos_bf16(bf16s, bf16s, 1536, &distance);
101
- simsimd_cos_f32(f32s, f32s, 1536, &distance);
102
- simsimd_cos_f64(f64s, f64s, 1536, &distance);
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
- simsimd_l2sq_f16(f16s, f16s, 1536, &distance);
107
- simsimd_l2sq_bf16(bf16s, bf16s, 1536, &distance);
108
- simsimd_l2sq_f32(f32s, f32s, 1536, &distance);
109
- simsimd_l2sq_f64(f64s, f64s, 1536, &distance);
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
- simsimd_dot_f16(f16s, f16s, 1536, &distance);
113
- simsimd_dot_bf16(bf16s, bf16s, 1536, &distance);
114
- simsimd_dot_f32(f32s, f32s, 1536, &distance);
115
- simsimd_dot_f64(f64s, f64s, 1536, &distance);
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
- // simsimd_dot_bf16c(bf16s, bf16s, 1536, &distance);
121
- simsimd_dot_f32c(f32s, f32s, 1536, &distance);
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) {
@@ -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", () => {
@@ -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("f64") != 0
495
- assert simd.pointer_to_cosine("f64") != 0
496
- assert simd.pointer_to_inner("f64") != 0
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("f32") != 0
499
- assert simd.pointer_to_cosine("f32") != 0
500
- assert simd.pointer_to_inner("f32") != 0
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("f16") != 0
503
- assert simd.pointer_to_cosine("f16") != 0
504
- assert simd.pointer_to_inner("f16") != 0
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("i8") != 0
507
- assert simd.pointer_to_cosine("i8") != 0
508
- assert simd.pointer_to_inner("i8") != 0
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("u8") != 0
511
- assert simd.pointer_to_cosine("u8") != 0
512
- assert simd.pointer_to_inner("u8") != 0
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), "b8")
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, "bits", accurate, accurate_dt, expected, expected_dt, result, result_dt, stats_fixture)
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="b8")
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="b8", out_dtype=out_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: simsimd_metric_punned_t { get }
6
- static var dotProduct: simsimd_metric_punned_t { get }
7
- static var squaredEuclidean: simsimd_metric_punned_t { get }
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: simsimd_metric_punned_t, a: A, b: B) -> Double? where A: Sequence, B: Sequence, A.Element == B.Element {
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) -> simsimd_metric_punned_t {
122
- var output: simsimd_metric_punned_t?
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
- simsimd_find_metric_punned(kind, dataType, .available, .any, &output, &used)
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
  }