Skip to content

Commit

Permalink
Improve: Uint8Array support in JS
Browse files Browse the repository at this point in the history
NodeJS type system doesn't define `float16`,
so we don't currently have a way of detecting
such arrays.

In Python bindings we allow passing an additional
string parameter together with `uint16` arrays to
override the logical type for the missing `bfloat16`.
In JS, for now, it's wiser to wait for the NAPI to
define the missing `napi_typedarray_type` value.

nodejs/node#55574 (comment)
nodejs/node#52416 (comment)
  • Loading branch information
ashvardanian committed Nov 3, 2024
1 parent 32f5496 commit 080e9d2
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 41 deletions.
69 changes: 42 additions & 27 deletions javascript/lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
/// @brief Global variable that caches the CPU capabilities, and is computed just onc, when the module is loaded.
simsimd_capability_t static_capabilities = simsimd_cap_serial_k;

napi_value runAPI(napi_env env, napi_callback_info info, simsimd_metric_kind_t metric_kind) {
napi_value dense(napi_env env, napi_callback_info info, simsimd_metric_kind_t metric_kind,
simsimd_datatype_t datatype) {
size_t argc = 2;
napi_value args[2];
napi_status status;
Expand Down Expand Up @@ -44,14 +45,13 @@ napi_value runAPI(napi_env env, napi_callback_info info, simsimd_metric_kind_t m
return NULL;
}

simsimd_datatype_t datatype = simsimd_datatype_unknown_k;
switch (type_a) {
case napi_float64_array: datatype = simsimd_datatype_f64_k; break;
case napi_float32_array: datatype = simsimd_datatype_f32_k; break;
case napi_int8_array: datatype = simsimd_datatype_i8_k; break;
case napi_uint8_array: datatype = simsimd_datatype_b8_k; break;
default: break;
}
if (datatype == simsimd_datatype_unknown_k) switch (type_a) {
case napi_float64_array: datatype = simsimd_datatype_f64_k; break;
case napi_float32_array: datatype = simsimd_datatype_f32_k; break;
case napi_int8_array: datatype = simsimd_datatype_i8_k; break;
case napi_uint8_array: datatype = simsimd_datatype_u8_k; break;
default: break;
}

simsimd_metric_punned_t metric = NULL;
simsimd_capability_t capability = simsimd_cap_serial_k;
Expand All @@ -72,32 +72,47 @@ napi_value runAPI(napi_env env, napi_callback_info info, simsimd_metric_kind_t m
return js_result;
}

napi_value ipAPI(napi_env env, napi_callback_info info) { return runAPI(env, info, simsimd_metric_dot_k); }
napi_value cosAPI(napi_env env, napi_callback_info info) { return runAPI(env, info, simsimd_metric_cosine_k); }
napi_value l2sqAPI(napi_env env, napi_callback_info info) { return runAPI(env, info, simsimd_metric_sqeuclidean_k); }
napi_value klAPI(napi_env env, napi_callback_info info) { return runAPI(env, info, simsimd_metric_kl_k); }
napi_value jsAPI(napi_env env, napi_callback_info info) { return runAPI(env, info, simsimd_metric_js_k); }
napi_value hammingAPI(napi_env env, napi_callback_info info) { return runAPI(env, info, simsimd_metric_hamming_k); }
napi_value jaccardAPI(napi_env env, napi_callback_info info) { return runAPI(env, info, simsimd_metric_jaccard_k); }
napi_value api_ip(napi_env env, napi_callback_info info) {
return dense(env, info, simsimd_metric_dot_k, simsimd_datatype_unknown_k);
}
napi_value api_cos(napi_env env, napi_callback_info info) {
return dense(env, info, simsimd_metric_cosine_k, simsimd_datatype_unknown_k);
}
napi_value api_l2sq(napi_env env, napi_callback_info info) {
return dense(env, info, simsimd_metric_sqeuclidean_k, simsimd_datatype_unknown_k);
}
napi_value api_kl(napi_env env, napi_callback_info info) {
return dense(env, info, simsimd_metric_kl_k, simsimd_datatype_unknown_k);
}
napi_value api_js(napi_env env, napi_callback_info info) {
return dense(env, info, simsimd_metric_js_k, simsimd_datatype_unknown_k);
}
napi_value api_hamming(napi_env env, napi_callback_info info) {
return dense(env, info, simsimd_metric_hamming_k, simsimd_datatype_b8_k);
}
napi_value api_jaccard(napi_env env, napi_callback_info info) {
return dense(env, info, simsimd_metric_jaccard_k, simsimd_datatype_b8_k);
}

napi_value Init(napi_env env, napi_value exports) {

// Define an array of property descriptors
napi_property_descriptor dotDesc = {"dot", 0, ipAPI, 0, 0, 0, napi_default, 0};
napi_property_descriptor innerDesc = {"inner", 0, ipAPI, 0, 0, 0, napi_default, 0};
napi_property_descriptor sqeuclideanDesc = {"sqeuclidean", 0, l2sqAPI, 0, 0, 0, napi_default, 0};
napi_property_descriptor cosineDesc = {"cosine", 0, cosAPI, 0, 0, 0, napi_default, 0};
napi_property_descriptor hammingDesc = {"hamming", 0, hammingAPI, 0, 0, 0, napi_default, 0};
napi_property_descriptor jaccardDesc = {"jaccard", 0, jaccardAPI, 0, 0, 0, napi_default, 0};
napi_property_descriptor klDesc = {"kullbackleibler", 0, klAPI, 0, 0, 0, napi_default, 0};
napi_property_descriptor jsDesc = {"jensenshannon", 0, jsAPI, 0, 0, 0, napi_default, 0};
napi_property_descriptor dot_descriptor = {"dot", 0, api_ip, 0, 0, 0, napi_default, 0};
napi_property_descriptor inner_descriptor = {"inner", 0, api_ip, 0, 0, 0, napi_default, 0};
napi_property_descriptor sqeuclidean_descriptor = {"sqeuclidean", 0, api_l2sq, 0, 0, 0, napi_default, 0};
napi_property_descriptor cosine_descriptor = {"cosine", 0, api_cos, 0, 0, 0, napi_default, 0};
napi_property_descriptor hamming_descriptor = {"hamming", 0, api_hamming, 0, 0, 0, napi_default, 0};
napi_property_descriptor jaccard_descriptor = {"jaccard", 0, api_jaccard, 0, 0, 0, napi_default, 0};
napi_property_descriptor kl_descriptor = {"kullbackleibler", 0, api_kl, 0, 0, 0, napi_default, 0};
napi_property_descriptor js_descriptor = {"jensenshannon", 0, api_js, 0, 0, 0, napi_default, 0};
napi_property_descriptor properties[] = {
dotDesc, innerDesc, sqeuclideanDesc, cosineDesc, hammingDesc, jaccardDesc, klDesc, jsDesc,
dot_descriptor, inner_descriptor, sqeuclidean_descriptor, cosine_descriptor,
hamming_descriptor, jaccard_descriptor, kl_descriptor, js_descriptor,
};

// Define the properties on the `exports` object
size_t propertyCount = sizeof(properties) / sizeof(properties[0]);
napi_define_properties(env, exports, propertyCount, properties);
size_t property_count = sizeof(properties) / sizeof(properties[0]);
napi_define_properties(env, exports, property_count, properties);

static_capabilities = simsimd_capabilities();
return exports;
Expand Down
34 changes: 20 additions & 14 deletions javascript/simsimd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,47 +18,53 @@ try {

/**
* @brief Computes the squared Euclidean distance between two vectors.
* @param {Float64Array|Float32Array|Int8Array} a - The first vector.
* @param {Float64Array|Float32Array|Int8Array} b - The second vector.
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} b - The second vector.
* @returns {number} The squared Euclidean distance between vectors a and b.
*/
export const sqeuclidean = (
a: Float64Array | Float32Array | Int8Array,
b: Float64Array | Float32Array | Int8Array
a: Float64Array | Float32Array | Int8Array | Uint8Array,
b: Float64Array | Float32Array | Int8Array | Uint8Array
): number => {
return compiled.sqeuclidean(a, b);
};

/**
* @brief Computes the cosine distance between two vectors.
* @param {Float64Array|Float32Array|Int8Array} a - The first vector.
* @param {Float64Array|Float32Array|Int8Array} b - The second vector.
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} b - The second vector.
* @returns {number} The cosine distance between vectors a and b.
*/
export const cosine = (
a: Float64Array | Float32Array | Int8Array,
b: Float64Array | Float32Array | Int8Array
a: Float64Array | Float32Array | Int8Array | Uint8Array,
b: Float64Array | Float32Array | Int8Array | Uint8Array
): number => {
return compiled.cosine(a, b);
};

/**
* @brief Computes the inner product of two vectors (same as dot product).
* @param {Float64Array|Float32Array} a - The first vector.
* @param {Float64Array|Float32Array} b - The second vector.
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} b - The second vector.
* @returns {number} The inner product of vectors a and b.
*/
export const inner = (a: Float64Array | Float32Array, b: Float64Array | Float32Array): number => {
export const inner = (
a: Float64Array | Float32Array | Int8Array | Uint8Array,
b: Float64Array | Float32Array | Int8Array | Uint8Array
): number => {
return compiled.inner(a, b);
};

/**
* @brief Computes the dot product of two vectors (same as inner product).
* @param {Float64Array|Float32Array} a - The first vector.
* @param {Float64Array|Float32Array} b - The second vector.
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} a - The first vector.
* @param {Float64Array|Float32Array|Int8Array|Uint8Array} b - The second vector.
* @returns {number} The dot product of vectors a and b.
*/
export const dot = (a: Float64Array | Float32Array, b: Float64Array | Float32Array): number => {
export const dot = (
a: Float64Array | Float32Array | Int8Array | Uint8Array,
b: Float64Array | Float32Array | Int8Array | Uint8Array
): number => {
return compiled.dot(a, b);
};

Expand Down

0 comments on commit 080e9d2

Please sign in to comment.