Skip to content

Commit a31f508

Browse files
GoWindGovindarajanNagarajan-TomTom
authored andcommitted
Improve: Expose L2 distance to JS (#232)
Closes #204 > add support for `Uint8Array` inputs > use name `l2` to stay consistent with naming > add tests Co-authored-by: GoWind <[email protected]> Co-authored-by: GovindarajanNagarajan-TomTom <[email protected]>
1 parent c0807de commit a31f508

File tree

4 files changed

+74
-5
lines changed

4 files changed

+74
-5
lines changed

javascript/fallback.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ export function dot(a: Float64Array | Float32Array, b: Float64Array | Float32Arr
2828

2929
/**
3030
* @brief Computes the squared Euclidean distance between two vectors.
31-
* @param {Float64Array|Float32Array|Int8Array} a - The first vector.
32-
* @param {Float64Array|Float32Array|Int8Array} b - The second vector.
31+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
32+
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} b - The second vector.
3333
* @returns {number} The squared Euclidean distance between vectors a and b.
3434
*/
3535
export function sqeuclidean(
36-
a: Float64Array | Float32Array | Int8Array,
37-
b: Float64Array | Float32Array | Int8Array
36+
a: Float64Array | Float32Array | Int8Array | Uint8Array,
37+
b: Float64Array | Float32Array | Int8Array | Uint8Array
3838
): number {
3939
if (a.length !== b.length) {
4040
throw new Error("Vectors must have the same length");
@@ -47,6 +47,28 @@ export function sqeuclidean(
4747
return result;
4848
}
4949

50+
/**
51+
* @brief Computes the L2 Euclidean distance between two vectors.
52+
* @param {Float64Array|Float32Array|Int8Array | Uint8Array} a - The first vector.
53+
* @param {Float64Array|Float32Array|Int8Array | Uint8Array} b - The second vector.
54+
* @returns {number} The L2 euclidean distance between vectors a and b.
55+
*/
56+
export function euclidean(
57+
a: Float64Array | Float32Array | Int8Array | Uint8Array,
58+
b: Float64Array | Float32Array | Int8Array | Uint8Array
59+
): number {
60+
if (a.length !== b.length) {
61+
throw new Error("Vectors must have the same length");
62+
}
63+
64+
let result = 0;
65+
for (let i = 0; i < a.length; i++) {
66+
result += (a[i] - b[i]) * (a[i] - b[i]);
67+
}
68+
return Math.sqrt(result);
69+
}
70+
71+
5072
/**
5173
* @brief Computes the cosine distance between two vectors.
5274
* @param {Float64Array|Float32Array|Int8Array} a - The first vector.
@@ -194,6 +216,7 @@ export const jensenshannon = (a: Float64Array | Float32Array, b: Float64Array |
194216

195217
export default {
196218
sqeuclidean,
219+
euclidean,
197220
cosine,
198221
inner,
199222
hamming,

javascript/lib.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ napi_value api_cos(napi_env env, napi_callback_info info) {
8282
napi_value api_l2sq(napi_env env, napi_callback_info info) {
8383
return dense(env, info, simsimd_metric_sqeuclidean_k, simsimd_datatype_unknown_k);
8484
}
85+
napi_value api_l2(napi_env env, napi_callback_info info) {
86+
return dense(env, info, simsimd_metric_l2_k, simsimd_datatype_unknown_k);
87+
}
8588
napi_value api_kl(napi_env env, napi_callback_info info) {
8689
return dense(env, info, simsimd_metric_kl_k, simsimd_datatype_unknown_k);
8790
}
@@ -101,13 +104,14 @@ napi_value Init(napi_env env, napi_value exports) {
101104
napi_property_descriptor dot_descriptor = {"dot", 0, api_ip, 0, 0, 0, napi_default, 0};
102105
napi_property_descriptor inner_descriptor = {"inner", 0, api_ip, 0, 0, 0, napi_default, 0};
103106
napi_property_descriptor sqeuclidean_descriptor = {"sqeuclidean", 0, api_l2sq, 0, 0, 0, napi_default, 0};
107+
napi_property_descriptor euclidean_descriptor = {"euclidean", 0, api_l2, 0, 0, 0, napi_default, 0};
104108
napi_property_descriptor cosine_descriptor = {"cosine", 0, api_cos, 0, 0, 0, napi_default, 0};
105109
napi_property_descriptor hamming_descriptor = {"hamming", 0, api_hamming, 0, 0, 0, napi_default, 0};
106110
napi_property_descriptor jaccard_descriptor = {"jaccard", 0, api_jaccard, 0, 0, 0, napi_default, 0};
107111
napi_property_descriptor kl_descriptor = {"kullbackleibler", 0, api_kl, 0, 0, 0, napi_default, 0};
108112
napi_property_descriptor js_descriptor = {"jensenshannon", 0, api_js, 0, 0, 0, napi_default, 0};
109113
napi_property_descriptor properties[] = {
110-
dot_descriptor, inner_descriptor, sqeuclidean_descriptor, cosine_descriptor,
114+
dot_descriptor, inner_descriptor, sqeuclidean_descriptor, euclidean_descriptor, cosine_descriptor,
111115
hamming_descriptor, jaccard_descriptor, kl_descriptor, js_descriptor,
112116
};
113117

javascript/simsimd.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ export const sqeuclidean = (
2929
return compiled.sqeuclidean(a, b);
3030
};
3131

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+
3245
/**
3346
* @brief Computes the cosine distance between two vectors.
3447
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
@@ -133,6 +146,7 @@ export default {
133146
dot,
134147
inner,
135148
sqeuclidean,
149+
euclidean,
136150
cosine,
137151
hamming,
138152
jaccard,

scripts/test.mjs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,20 @@ test("Distance from itself JS fallback", () => {
102102
const f32Array1 = new Float32Array([1.0, 2.0, 3.0]);
103103
const f32Array2 = new Float32Array([4.0, 5.0, 6.0]);
104104

105+
const uint8Array1 = new Uint8Array([1, 2, 3]);
106+
const uint8Array2 = new Uint8Array([4, 5, 6]);
107+
105108
test("Squared Euclidean Distance", () => {
106109
const result = simsimd.sqeuclidean(f32Array1, f32Array2);
107110
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);
108119
});
109120

110121
test("Inner Distance", () => {
@@ -120,6 +131,9 @@ test("Cosine Similarity", () => {
120131
test("Squared Euclidean Distance JS", () => {
121132
const result = fallback.sqeuclidean(f32Array1, f32Array2);
122133
assertAlmostEqual(result, 27.0, 0.01);
134+
135+
const uint8Result = fallback.sqeuclidean(uint8Array1, uint8Array2);
136+
assertAlmostEqual(uint8Result, 27.0, 0.01);
123137
});
124138

125139
test("Inner Distance JS", () => {
@@ -136,6 +150,20 @@ test("Squared Euclidean Distance C vs JS", () => {
136150
const result = simsimd.sqeuclidean(f32Array1, f32Array2);
137151
const resultjs = fallback.sqeuclidean(f32Array1, f32Array2);
138152
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);
139167
});
140168

141169
test("Inner Distance C vs JS", () => {

0 commit comments

Comments
 (0)