usearch 2.16.3 → 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/include/usearch/index.hpp +1 -1
- package/include/usearch/index_plugins.hpp +5 -4
- 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
|
@@ -88,9 +88,9 @@
|
|
|
88
88
|
#ifndef SIMSIMD_H
|
|
89
89
|
#define SIMSIMD_H
|
|
90
90
|
|
|
91
|
-
#define SIMSIMD_VERSION_MAJOR
|
|
92
|
-
#define SIMSIMD_VERSION_MINOR
|
|
93
|
-
#define SIMSIMD_VERSION_PATCH
|
|
91
|
+
#define SIMSIMD_VERSION_MAJOR 6
|
|
92
|
+
#define SIMSIMD_VERSION_MINOR 0
|
|
93
|
+
#define SIMSIMD_VERSION_PATCH 5
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
96
|
* @brief Removes compile-time dispatching, and replaces it with runtime dispatching.
|
|
@@ -304,28 +304,29 @@ typedef void (*simsimd_kernel_wsum_punned_t)(void const *a, void const *b, //
|
|
|
304
304
|
|
|
305
305
|
/**
|
|
306
306
|
* @brief Type-punned function pointer for a SimSIMD public interface.
|
|
307
|
-
*
|
|
308
|
-
*
|
|
307
|
+
*
|
|
308
|
+
* Can be a `simsimd_metric_dense_punned_t`, `simsimd_metric_sparse_punned_t`, `simsimd_metric_curved_punned_t`,
|
|
309
|
+
* `simsimd_kernel_fma_punned_t`, or `simsimd_kernel_wsum_punned_t`.
|
|
309
310
|
*/
|
|
310
|
-
typedef
|
|
311
|
+
typedef void (*simsimd_kernel_punned_t)(void *);
|
|
311
312
|
|
|
312
313
|
#if SIMSIMD_DYNAMIC_DISPATCH
|
|
313
314
|
SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
|
|
314
|
-
SIMSIMD_DYNAMIC void
|
|
315
|
+
SIMSIMD_DYNAMIC void simsimd_find_kernel_punned( //
|
|
315
316
|
simsimd_metric_kind_t kind, //
|
|
316
317
|
simsimd_datatype_t datatype, //
|
|
317
318
|
simsimd_capability_t supported, //
|
|
318
319
|
simsimd_capability_t allowed, //
|
|
319
|
-
|
|
320
|
+
simsimd_kernel_punned_t *kernel_output, //
|
|
320
321
|
simsimd_capability_t *capability_output);
|
|
321
322
|
#else
|
|
322
323
|
SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void);
|
|
323
|
-
SIMSIMD_PUBLIC void
|
|
324
|
+
SIMSIMD_PUBLIC void simsimd_find_kernel_punned( //
|
|
324
325
|
simsimd_metric_kind_t kind, //
|
|
325
326
|
simsimd_datatype_t datatype, //
|
|
326
327
|
simsimd_capability_t supported, //
|
|
327
328
|
simsimd_capability_t allowed, //
|
|
328
|
-
|
|
329
|
+
simsimd_kernel_punned_t *kernel_output, //
|
|
329
330
|
simsimd_capability_t *capability_output);
|
|
330
331
|
#endif
|
|
331
332
|
|
|
@@ -537,9 +538,9 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_implementation(void) {
|
|
|
537
538
|
#pragma clang diagnostic ignored "-Wvolatile"
|
|
538
539
|
#endif
|
|
539
540
|
|
|
540
|
-
SIMSIMD_INTERNAL void
|
|
541
|
-
|
|
542
|
-
typedef
|
|
541
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f64(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
542
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
543
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
543
544
|
#if SIMSIMD_TARGET_SVE
|
|
544
545
|
if (v & simsimd_cap_sve_k) switch (k) {
|
|
545
546
|
case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_sve, *c = simsimd_cap_sve_k; return;
|
|
@@ -593,9 +594,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f64(simsimd_capability_t v, si
|
|
|
593
594
|
}
|
|
594
595
|
}
|
|
595
596
|
|
|
596
|
-
SIMSIMD_INTERNAL void
|
|
597
|
-
|
|
598
|
-
typedef
|
|
597
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f32(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
598
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
599
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
599
600
|
#if SIMSIMD_TARGET_SVE
|
|
600
601
|
if (v & simsimd_cap_sve_k) switch (k) {
|
|
601
602
|
case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_sve, *c = simsimd_cap_sve_k; return;
|
|
@@ -661,9 +662,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f32(simsimd_capability_t v, si
|
|
|
661
662
|
}
|
|
662
663
|
}
|
|
663
664
|
|
|
664
|
-
SIMSIMD_INTERNAL void
|
|
665
|
-
|
|
666
|
-
typedef
|
|
665
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f16(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
666
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
667
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
667
668
|
#if SIMSIMD_TARGET_SVE_F16
|
|
668
669
|
if (v & simsimd_cap_sve_k) switch (k) {
|
|
669
670
|
case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_sve, *c = simsimd_cap_sve_f16_k; return;
|
|
@@ -737,9 +738,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f16(simsimd_capability_t v, si
|
|
|
737
738
|
}
|
|
738
739
|
}
|
|
739
740
|
|
|
740
|
-
SIMSIMD_INTERNAL void
|
|
741
|
-
|
|
742
|
-
typedef
|
|
741
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_bf16(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
742
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
743
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
743
744
|
#if SIMSIMD_TARGET_SVE_BF16
|
|
744
745
|
if (v & simsimd_cap_sve_bf16_k) switch (k) {
|
|
745
746
|
case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_sve, *c = simsimd_cap_sve_bf16_k; return;
|
|
@@ -809,9 +810,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_bf16(simsimd_capability_t v, s
|
|
|
809
810
|
}
|
|
810
811
|
}
|
|
811
812
|
|
|
812
|
-
SIMSIMD_INTERNAL void
|
|
813
|
-
|
|
814
|
-
typedef
|
|
813
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_i8(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
814
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
815
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
815
816
|
#if SIMSIMD_TARGET_NEON_I8
|
|
816
817
|
if (v & simsimd_cap_neon_i8_k) switch (k) {
|
|
817
818
|
case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_neon, *c = simsimd_cap_neon_i8_k; return;
|
|
@@ -865,9 +866,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_i8(simsimd_capability_t v, sim
|
|
|
865
866
|
default: break;
|
|
866
867
|
}
|
|
867
868
|
}
|
|
868
|
-
SIMSIMD_INTERNAL void
|
|
869
|
-
|
|
870
|
-
typedef
|
|
869
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_u8(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
870
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
871
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
871
872
|
#if SIMSIMD_TARGET_NEON_I8
|
|
872
873
|
if (v & simsimd_cap_neon_i8_k) switch (k) {
|
|
873
874
|
case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_u8_neon, *c = simsimd_cap_neon_i8_k; return;
|
|
@@ -922,9 +923,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_u8(simsimd_capability_t v, sim
|
|
|
922
923
|
}
|
|
923
924
|
}
|
|
924
925
|
|
|
925
|
-
SIMSIMD_INTERNAL void
|
|
926
|
-
|
|
927
|
-
typedef
|
|
926
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_b8(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
927
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
928
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
928
929
|
#if SIMSIMD_TARGET_SVE
|
|
929
930
|
if (v & simsimd_cap_sve_k) switch (k) {
|
|
930
931
|
case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_sve, *c = simsimd_cap_sve_k; return;
|
|
@@ -960,9 +961,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_b8(simsimd_capability_t v, sim
|
|
|
960
961
|
}
|
|
961
962
|
}
|
|
962
963
|
|
|
963
|
-
SIMSIMD_INTERNAL void
|
|
964
|
-
|
|
965
|
-
typedef
|
|
964
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f64c(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
965
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
966
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
966
967
|
#if SIMSIMD_TARGET_SVE
|
|
967
968
|
if (v & simsimd_cap_sve_k) switch (k) {
|
|
968
969
|
case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_sve, *c = simsimd_cap_sve_k; return;
|
|
@@ -984,9 +985,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f64c(simsimd_capability_t v, s
|
|
|
984
985
|
}
|
|
985
986
|
}
|
|
986
987
|
|
|
987
|
-
SIMSIMD_INTERNAL void
|
|
988
|
-
|
|
989
|
-
typedef
|
|
988
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f32c(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
989
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
990
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
990
991
|
#if SIMSIMD_TARGET_SVE
|
|
991
992
|
if (v & simsimd_cap_sve_k) switch (k) {
|
|
992
993
|
case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_sve, *c = simsimd_cap_sve_k; return;
|
|
@@ -1022,9 +1023,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f32c(simsimd_capability_t v, s
|
|
|
1022
1023
|
}
|
|
1023
1024
|
}
|
|
1024
1025
|
|
|
1025
|
-
SIMSIMD_INTERNAL void
|
|
1026
|
-
|
|
1027
|
-
typedef
|
|
1026
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_f16c(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
1027
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
1028
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
1028
1029
|
#if SIMSIMD_TARGET_SVE_F16
|
|
1029
1030
|
if (v & simsimd_cap_sve_k) switch (k) {
|
|
1030
1031
|
case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_sve, *c = simsimd_cap_sve_f16_k; return;
|
|
@@ -1060,9 +1061,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f16c(simsimd_capability_t v, s
|
|
|
1060
1061
|
}
|
|
1061
1062
|
}
|
|
1062
1063
|
|
|
1063
|
-
SIMSIMD_INTERNAL void
|
|
1064
|
-
|
|
1065
|
-
typedef
|
|
1064
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_bf16c(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
1065
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
1066
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
1066
1067
|
#if SIMSIMD_TARGET_NEON_BF16
|
|
1067
1068
|
if (v & simsimd_cap_neon_bf16_k) switch (k) {
|
|
1068
1069
|
case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_neon, *c = simsimd_cap_neon_bf16_k; return;
|
|
@@ -1084,9 +1085,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_bf16c(simsimd_capability_t v,
|
|
|
1084
1085
|
}
|
|
1085
1086
|
}
|
|
1086
1087
|
|
|
1087
|
-
SIMSIMD_INTERNAL void
|
|
1088
|
-
|
|
1089
|
-
typedef
|
|
1088
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_u16(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
1089
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
1090
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
1090
1091
|
#if SIMSIMD_TARGET_SVE2
|
|
1091
1092
|
if (v & simsimd_cap_sve2_k) switch (k) {
|
|
1092
1093
|
case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u16_sve2, *c = simsimd_cap_sve2_k; return;
|
|
@@ -1125,9 +1126,9 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_u16(simsimd_capability_t v, si
|
|
|
1125
1126
|
}
|
|
1126
1127
|
}
|
|
1127
1128
|
|
|
1128
|
-
SIMSIMD_INTERNAL void
|
|
1129
|
-
|
|
1130
|
-
typedef
|
|
1129
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_u32(simsimd_capability_t v, simsimd_metric_kind_t k,
|
|
1130
|
+
simsimd_kernel_punned_t *m, simsimd_capability_t *c) {
|
|
1131
|
+
typedef simsimd_kernel_punned_t m_t;
|
|
1131
1132
|
#if SIMSIMD_TARGET_SVE2
|
|
1132
1133
|
if (v & simsimd_cap_sve2_k) switch (k) {
|
|
1133
1134
|
case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u32_sve2, *c = simsimd_cap_sve2_k; return;
|
|
@@ -1166,15 +1167,15 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_u32(simsimd_capability_t v, si
|
|
|
1166
1167
|
* @param datatype The data type for which the metric needs to be evaluated.
|
|
1167
1168
|
* @param supported The hardware capabilities supported by the CPU.
|
|
1168
1169
|
* @param allowed The hardware capabilities allowed for use.
|
|
1169
|
-
* @param
|
|
1170
|
+
* @param kernel_output Output variable for the selected similarity function.
|
|
1170
1171
|
* @param capability_output Output variable for the utilized hardware capabilities.
|
|
1171
1172
|
*/
|
|
1172
|
-
SIMSIMD_INTERNAL void
|
|
1173
|
+
SIMSIMD_INTERNAL void _simsimd_find_kernel_punned_implementation( //
|
|
1173
1174
|
simsimd_metric_kind_t kind, //
|
|
1174
1175
|
simsimd_datatype_t datatype, //
|
|
1175
1176
|
simsimd_capability_t supported, //
|
|
1176
1177
|
simsimd_capability_t allowed, //
|
|
1177
|
-
|
|
1178
|
+
simsimd_kernel_punned_t *kernel_output, //
|
|
1178
1179
|
simsimd_capability_t *capability_output) {
|
|
1179
1180
|
|
|
1180
1181
|
// Modern compilers abso-freaking-lutely love optimizing-out my logic!
|
|
@@ -1186,25 +1187,25 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_implementation( //
|
|
|
1186
1187
|
__asm__ __volatile__("" ::: "memory");
|
|
1187
1188
|
#endif
|
|
1188
1189
|
|
|
1189
|
-
|
|
1190
|
+
simsimd_kernel_punned_t *m = kernel_output;
|
|
1190
1191
|
simsimd_capability_t *c = capability_output;
|
|
1191
1192
|
simsimd_capability_t viable = (simsimd_capability_t)(supported & allowed);
|
|
1192
1193
|
|
|
1193
1194
|
switch (datatype) {
|
|
1194
1195
|
|
|
1195
|
-
case simsimd_datatype_f64_k:
|
|
1196
|
-
case simsimd_datatype_f32_k:
|
|
1197
|
-
case simsimd_datatype_f16_k:
|
|
1198
|
-
case simsimd_datatype_bf16_k:
|
|
1199
|
-
case simsimd_datatype_i8_k:
|
|
1200
|
-
case simsimd_datatype_u8_k:
|
|
1201
|
-
case simsimd_datatype_b8_k:
|
|
1202
|
-
case simsimd_datatype_f32c_k:
|
|
1203
|
-
case simsimd_datatype_f64c_k:
|
|
1204
|
-
case simsimd_datatype_f16c_k:
|
|
1205
|
-
case simsimd_datatype_bf16c_k:
|
|
1206
|
-
case simsimd_datatype_u16_k:
|
|
1207
|
-
case simsimd_datatype_u32_k:
|
|
1196
|
+
case simsimd_datatype_f64_k: _simsimd_find_kernel_punned_f64(viable, kind, m, c); return;
|
|
1197
|
+
case simsimd_datatype_f32_k: _simsimd_find_kernel_punned_f32(viable, kind, m, c); return;
|
|
1198
|
+
case simsimd_datatype_f16_k: _simsimd_find_kernel_punned_f16(viable, kind, m, c); return;
|
|
1199
|
+
case simsimd_datatype_bf16_k: _simsimd_find_kernel_punned_bf16(viable, kind, m, c); return;
|
|
1200
|
+
case simsimd_datatype_i8_k: _simsimd_find_kernel_punned_i8(viable, kind, m, c); return;
|
|
1201
|
+
case simsimd_datatype_u8_k: _simsimd_find_kernel_punned_u8(viable, kind, m, c); return;
|
|
1202
|
+
case simsimd_datatype_b8_k: _simsimd_find_kernel_punned_b8(viable, kind, m, c); return;
|
|
1203
|
+
case simsimd_datatype_f32c_k: _simsimd_find_kernel_punned_f32c(viable, kind, m, c); return;
|
|
1204
|
+
case simsimd_datatype_f64c_k: _simsimd_find_kernel_punned_f64c(viable, kind, m, c); return;
|
|
1205
|
+
case simsimd_datatype_f16c_k: _simsimd_find_kernel_punned_f16c(viable, kind, m, c); return;
|
|
1206
|
+
case simsimd_datatype_bf16c_k: _simsimd_find_kernel_punned_bf16c(viable, kind, m, c); return;
|
|
1207
|
+
case simsimd_datatype_u16_k: _simsimd_find_kernel_punned_u16(viable, kind, m, c); return;
|
|
1208
|
+
case simsimd_datatype_u32_k: _simsimd_find_kernel_punned_u32(viable, kind, m, c); return;
|
|
1208
1209
|
|
|
1209
1210
|
// These data-types are not supported yet
|
|
1210
1211
|
case simsimd_datatype_i4x2_k: break;
|
|
@@ -1217,7 +1218,7 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_implementation( //
|
|
|
1217
1218
|
}
|
|
1218
1219
|
|
|
1219
1220
|
// Replace with zeros if no suitable implementation was found
|
|
1220
|
-
*m = (
|
|
1221
|
+
*m = (simsimd_kernel_punned_t)0;
|
|
1221
1222
|
*c = (simsimd_capability_t)0;
|
|
1222
1223
|
|
|
1223
1224
|
// Modern compilers abso-freaking-lutely love optimizing-out my logic!
|
|
@@ -1242,15 +1243,15 @@ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_implementation( //
|
|
|
1242
1243
|
* @param allowed The hardware capabilities allowed for use.
|
|
1243
1244
|
* @return A function pointer to the selected metric implementation.
|
|
1244
1245
|
*/
|
|
1245
|
-
SIMSIMD_PUBLIC
|
|
1246
|
+
SIMSIMD_PUBLIC simsimd_kernel_punned_t simsimd_metric_punned( //
|
|
1246
1247
|
simsimd_metric_kind_t kind, //
|
|
1247
1248
|
simsimd_datatype_t datatype, //
|
|
1248
1249
|
simsimd_capability_t allowed) {
|
|
1249
1250
|
|
|
1250
|
-
|
|
1251
|
+
simsimd_kernel_punned_t result = 0;
|
|
1251
1252
|
simsimd_capability_t c = simsimd_cap_serial_k;
|
|
1252
1253
|
simsimd_capability_t supported = simsimd_capabilities();
|
|
1253
|
-
|
|
1254
|
+
simsimd_find_kernel_punned(kind, datatype, supported, allowed, &result, &c);
|
|
1254
1255
|
return result;
|
|
1255
1256
|
}
|
|
1256
1257
|
|
|
@@ -1301,6 +1302,10 @@ SIMSIMD_DYNAMIC int simsimd_uses_sierra(void);
|
|
|
1301
1302
|
* @note The dot product is zero if and only if the two vectors are orthogonal.
|
|
1302
1303
|
* @note Defined only for floating-point and integer data types.
|
|
1303
1304
|
*/
|
|
1305
|
+
SIMSIMD_DYNAMIC void simsimd_dot_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
|
|
1306
|
+
simsimd_distance_t *d);
|
|
1307
|
+
SIMSIMD_DYNAMIC void simsimd_dot_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
|
|
1308
|
+
simsimd_distance_t *d);
|
|
1304
1309
|
SIMSIMD_DYNAMIC void simsimd_dot_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
|
|
1305
1310
|
simsimd_distance_t *d);
|
|
1306
1311
|
SIMSIMD_DYNAMIC void simsimd_dot_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
|
|
@@ -1458,14 +1463,14 @@ SIMSIMD_PUBLIC int simsimd_uses_turin(void) { return _SIMSIMD_TARGET_X86 && SIMS
|
|
|
1458
1463
|
SIMSIMD_PUBLIC int simsimd_uses_sierra(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SIERRA; }
|
|
1459
1464
|
SIMSIMD_PUBLIC int simsimd_uses_dynamic_dispatch(void) { return 0; }
|
|
1460
1465
|
SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void) { return _simsimd_capabilities_implementation(); }
|
|
1461
|
-
SIMSIMD_PUBLIC void
|
|
1466
|
+
SIMSIMD_PUBLIC void simsimd_find_kernel_punned( //
|
|
1462
1467
|
simsimd_metric_kind_t kind, //
|
|
1463
1468
|
simsimd_datatype_t datatype, //
|
|
1464
1469
|
simsimd_capability_t supported, //
|
|
1465
1470
|
simsimd_capability_t allowed, //
|
|
1466
|
-
|
|
1471
|
+
simsimd_kernel_punned_t* kernel_output, //
|
|
1467
1472
|
simsimd_capability_t* capability_output) {
|
|
1468
|
-
|
|
1473
|
+
_simsimd_find_kernel_punned_implementation(kind, datatype, supported, allowed, kernel_output, capability_output);
|
|
1469
1474
|
}
|
|
1470
1475
|
// clang-format on
|
|
1471
1476
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
35
35
|
#define SIMSIMD_DYNAMIC __attribute__((visibility("default")))
|
|
36
36
|
#define SIMSIMD_PUBLIC __attribute__((unused)) inline static
|
|
37
|
-
#define SIMSIMD_INTERNAL
|
|
37
|
+
#define SIMSIMD_INTERNAL inline static
|
|
38
38
|
#else
|
|
39
39
|
#define SIMSIMD_DYNAMIC
|
|
40
40
|
#define SIMSIMD_PUBLIC inline static
|
|
@@ -436,16 +436,20 @@ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_bf16_t) == 2, simsimd_bf16_t_must_be_2_byte
|
|
|
436
436
|
#endif
|
|
437
437
|
|
|
438
438
|
#if !defined(SIMSIMD_F32_TO_I8)
|
|
439
|
-
#define SIMSIMD_F32_TO_I8(x, y)
|
|
439
|
+
#define SIMSIMD_F32_TO_I8(x, y) \
|
|
440
|
+
*(y) = (simsimd_i8_t)((x) > 127 ? 127 : ((x) < -128 ? -128 : (int)((x) + ((x) < 0 ? -0.5f : 0.5f))))
|
|
440
441
|
#endif
|
|
441
442
|
#if !defined(SIMSIMD_F32_TO_U8)
|
|
442
|
-
#define SIMSIMD_F32_TO_U8(x, y)
|
|
443
|
+
#define SIMSIMD_F32_TO_U8(x, y) \
|
|
444
|
+
*(y) = (simsimd_u8_t)((x) > 255 ? 255 : ((x) < 0 ? 0 : (int)((x) + ((x) < 0 ? -0.5f : 0.5f))))
|
|
443
445
|
#endif
|
|
444
446
|
#if !defined(SIMSIMD_F64_TO_I8)
|
|
445
|
-
#define SIMSIMD_F64_TO_I8(x, y)
|
|
447
|
+
#define SIMSIMD_F64_TO_I8(x, y) \
|
|
448
|
+
*(y) = (simsimd_i8_t)((x) > 127 ? 127 : ((x) < -128 ? -128 : (int)((x) + ((x) < 0 ? -0.5 : 0.5))))
|
|
446
449
|
#endif
|
|
447
450
|
#if !defined(SIMSIMD_F64_TO_U8)
|
|
448
|
-
#define SIMSIMD_F64_TO_U8(x, y)
|
|
451
|
+
#define SIMSIMD_F64_TO_U8(x, y) \
|
|
452
|
+
*(y) = (simsimd_u8_t)((x) > 255 ? 255 : ((x) < 0 ? 0 : (int)((x) + ((x) < 0 ? -0.5 : 0.5))))
|
|
449
453
|
#endif
|
|
450
454
|
|
|
451
455
|
/** @brief Convenience type for half-precision floating-point type conversions. */
|
|
@@ -467,20 +471,33 @@ typedef union {
|
|
|
467
471
|
* https://web.archive.org/web/20210208132927/http://assemblyrequired.crashworks.org/timing-square-root/
|
|
468
472
|
* https://stackoverflow.com/a/41460625/2766161
|
|
469
473
|
*/
|
|
470
|
-
|
|
474
|
+
SIMSIMD_INTERNAL simsimd_f32_t simsimd_approximate_inverse_square_root(simsimd_f32_t number) {
|
|
471
475
|
simsimd_f32i32_t conv;
|
|
472
476
|
conv.f = number;
|
|
473
477
|
conv.i = 0x5F1FFFF9 - (conv.i >> 1);
|
|
478
|
+
// Refine using a Newton-Raphson step for better accuracy
|
|
474
479
|
conv.f *= 0.703952253f * (2.38924456f - number * conv.f * conv.f);
|
|
475
480
|
return conv.f;
|
|
476
481
|
}
|
|
477
482
|
|
|
483
|
+
/**
|
|
484
|
+
* @brief Approximates `sqrt(x)` using the fast inverse square root trick
|
|
485
|
+
* with adjustments for direct square root approximation.
|
|
486
|
+
*
|
|
487
|
+
* Similar to `rsqrt` approximation but multiplies by `number` to get `sqrt`.
|
|
488
|
+
* This technique is useful where `sqrt` approximation is needed in performance-critical code,
|
|
489
|
+
* though modern hardware provides optimized alternatives.
|
|
490
|
+
*/
|
|
491
|
+
SIMSIMD_INTERNAL simsimd_f32_t simsimd_approximate_square_root(simsimd_f32_t number) {
|
|
492
|
+
return number * simsimd_approximate_inverse_square_root(number);
|
|
493
|
+
}
|
|
494
|
+
|
|
478
495
|
/**
|
|
479
496
|
* @brief Computes `log(x)` using the Mercator series.
|
|
480
497
|
* The series converges to the natural logarithm for args between -1 and 1.
|
|
481
498
|
* Published in 1668 in "Logarithmotechnia".
|
|
482
499
|
*/
|
|
483
|
-
|
|
500
|
+
SIMSIMD_INTERNAL simsimd_f32_t simsimd_approximate_log(simsimd_f32_t number) {
|
|
484
501
|
simsimd_f32_t x = number - 1;
|
|
485
502
|
simsimd_f32_t x2 = x * x;
|
|
486
503
|
simsimd_f32_t x3 = x * x * x;
|
|
@@ -497,7 +514,7 @@ SIMSIMD_PUBLIC simsimd_f32_t simsimd_approximate_log(simsimd_f32_t number) {
|
|
|
497
514
|
* https://gist.github.com/milhidaka/95863906fe828198f47991c813dbe233
|
|
498
515
|
* https://github.com/OpenCyphal/libcanard/blob/636795f4bc395f56af8d2c61d3757b5e762bb9e5/canard.c#L811-L834
|
|
499
516
|
*/
|
|
500
|
-
|
|
517
|
+
SIMSIMD_INTERNAL simsimd_f32_t simsimd_f16_to_f32(simsimd_f16_t const *x_ptr) {
|
|
501
518
|
unsigned short x = *(unsigned short const *)x_ptr;
|
|
502
519
|
unsigned int exponent = (x & 0x7C00) >> 10;
|
|
503
520
|
unsigned int mantissa = (x & 0x03FF) << 13;
|
|
@@ -519,7 +536,7 @@ SIMSIMD_PUBLIC simsimd_f32_t simsimd_f16_to_f32(simsimd_f16_t const *x_ptr) {
|
|
|
519
536
|
* https://gist.github.com/milhidaka/95863906fe828198f47991c813dbe233
|
|
520
537
|
* https://github.com/OpenCyphal/libcanard/blob/636795f4bc395f56af8d2c61d3757b5e762bb9e5/canard.c#L811-L834
|
|
521
538
|
*/
|
|
522
|
-
|
|
539
|
+
SIMSIMD_INTERNAL void simsimd_f32_to_f16(simsimd_f32_t x, simsimd_f16_t *result_ptr) {
|
|
523
540
|
simsimd_f32i32_t conv;
|
|
524
541
|
conv.f = x;
|
|
525
542
|
unsigned int b = conv.i + 0x00001000;
|
|
@@ -538,7 +555,7 @@ SIMSIMD_PUBLIC void simsimd_f32_to_f16(simsimd_f32_t x, simsimd_f16_t *result_pt
|
|
|
538
555
|
* https://stackoverflow.com/questions/55253233/convert-fp32-to-bfloat16-in-c/55254307#55254307
|
|
539
556
|
* https://cloud.google.com/blog/products/ai-machine-learning/bfloat16-the-secret-to-high-performance-on-cloud-tpus
|
|
540
557
|
*/
|
|
541
|
-
|
|
558
|
+
SIMSIMD_INTERNAL simsimd_f32_t simsimd_bf16_to_f32(simsimd_bf16_t const *x_ptr) {
|
|
542
559
|
unsigned short x = *(unsigned short const *)x_ptr;
|
|
543
560
|
simsimd_f32i32_t conv;
|
|
544
561
|
conv.i = x << 16; // Zero extends the mantissa
|
|
@@ -551,7 +568,7 @@ SIMSIMD_PUBLIC simsimd_f32_t simsimd_bf16_to_f32(simsimd_bf16_t const *x_ptr) {
|
|
|
551
568
|
* https://stackoverflow.com/questions/55253233/convert-fp32-to-bfloat16-in-c/55254307#55254307
|
|
552
569
|
* https://cloud.google.com/blog/products/ai-machine-learning/bfloat16-the-secret-to-high-performance-on-cloud-tpus
|
|
553
570
|
*/
|
|
554
|
-
|
|
571
|
+
SIMSIMD_INTERNAL void simsimd_f32_to_bf16(simsimd_f32_t x, simsimd_bf16_t *result_ptr) {
|
|
555
572
|
simsimd_f32i32_t conv;
|
|
556
573
|
conv.f = x;
|
|
557
574
|
conv.i += 0x8000; // Rounding is optional
|
|
@@ -561,12 +578,12 @@ SIMSIMD_PUBLIC void simsimd_f32_to_bf16(simsimd_f32_t x, simsimd_bf16_t *result_
|
|
|
561
578
|
*(unsigned short *)result_ptr = (unsigned short)conv.i;
|
|
562
579
|
}
|
|
563
580
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
581
|
+
SIMSIMD_INTERNAL simsimd_u32_t simsimd_u32_rol(simsimd_u32_t x, int n) { return (x << n) | (x >> (32 - n)); }
|
|
582
|
+
SIMSIMD_INTERNAL simsimd_u16_t simsimd_u16_rol(simsimd_u16_t x, int n) { return (x << n) | (x >> (16 - n)); }
|
|
583
|
+
SIMSIMD_INTERNAL simsimd_u8_t simsimd_u8_rol(simsimd_u8_t x, int n) { return (x << n) | (x >> (8 - n)); }
|
|
584
|
+
SIMSIMD_INTERNAL simsimd_u32_t simsimd_u32_ror(simsimd_u32_t x, int n) { return (x >> n) | (x << (32 - n)); }
|
|
585
|
+
SIMSIMD_INTERNAL simsimd_u16_t simsimd_u16_ror(simsimd_u16_t x, int n) { return (x >> n) | (x << (16 - n)); }
|
|
586
|
+
SIMSIMD_INTERNAL simsimd_u8_t simsimd_u8_ror(simsimd_u8_t x, int n) { return (x >> n) | (x << (8 - n)); }
|
|
570
587
|
|
|
571
588
|
#ifdef __cplusplus
|
|
572
589
|
} // extern "C"
|
|
@@ -28,13 +28,13 @@ export function dot(a: Float64Array | Float32Array, b: Float64Array | Float32Arr
|
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* @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.
|
|
33
33
|
* @returns {number} The squared Euclidean distance between vectors a and b.
|
|
34
34
|
*/
|
|
35
35
|
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
|
|
38
38
|
): number {
|
|
39
39
|
if (a.length !== b.length) {
|
|
40
40
|
throw new Error("Vectors must have the same length");
|
|
@@ -47,6 +47,28 @@ export function sqeuclidean(
|
|
|
47
47
|
return result;
|
|
48
48
|
}
|
|
49
49
|
|
|
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
|
+
|
|
50
72
|
/**
|
|
51
73
|
* @brief Computes the cosine distance between two vectors.
|
|
52
74
|
* @param {Float64Array|Float32Array|Int8Array} a - The first vector.
|
|
@@ -194,6 +216,7 @@ export const jensenshannon = (a: Float64Array | Float32Array, b: Float64Array |
|
|
|
194
216
|
|
|
195
217
|
export default {
|
|
196
218
|
sqeuclidean,
|
|
219
|
+
euclidean,
|
|
197
220
|
cosine,
|
|
198
221
|
inner,
|
|
199
222
|
hamming,
|
package/simsimd/javascript/lib.c
CHANGED
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
/// @brief Global variable that caches the CPU capabilities, and is computed just onc, when the module is loaded.
|
|
15
15
|
simsimd_capability_t static_capabilities = simsimd_cap_serial_k;
|
|
16
16
|
|
|
17
|
-
napi_value
|
|
17
|
+
napi_value dense(napi_env env, napi_callback_info info, simsimd_metric_kind_t metric_kind,
|
|
18
|
+
simsimd_datatype_t datatype) {
|
|
18
19
|
size_t argc = 2;
|
|
19
20
|
napi_value args[2];
|
|
20
21
|
napi_status status;
|
|
@@ -44,18 +45,18 @@ napi_value runAPI(napi_env env, napi_callback_info info, simsimd_metric_kind_t m
|
|
|
44
45
|
return NULL;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
48
|
+
if (datatype == simsimd_datatype_unknown_k) switch (type_a) {
|
|
49
|
+
case napi_float64_array: datatype = simsimd_datatype_f64_k; break;
|
|
50
|
+
case napi_float32_array: datatype = simsimd_datatype_f32_k; break;
|
|
51
|
+
case napi_int8_array: datatype = simsimd_datatype_i8_k; break;
|
|
52
|
+
case napi_uint8_array: datatype = simsimd_datatype_u8_k; break;
|
|
53
|
+
default: break;
|
|
54
|
+
}
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
simsimd_metric_dense_punned_t metric = NULL;
|
|
57
57
|
simsimd_capability_t capability = simsimd_cap_serial_k;
|
|
58
|
-
|
|
58
|
+
simsimd_find_kernel_punned(metric_kind, datatype, static_capabilities, simsimd_cap_any_k,
|
|
59
|
+
(simsimd_kernel_punned_t *)&metric, &capability);
|
|
59
60
|
if (metric == NULL) {
|
|
60
61
|
napi_throw_error(env, NULL, "Unsupported datatype for given metric");
|
|
61
62
|
return NULL;
|
|
@@ -72,32 +73,51 @@ napi_value runAPI(napi_env env, napi_callback_info info, simsimd_metric_kind_t m
|
|
|
72
73
|
return js_result;
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
napi_value
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
napi_value
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
napi_value
|
|
76
|
+
napi_value api_ip(napi_env env, napi_callback_info info) {
|
|
77
|
+
return dense(env, info, simsimd_metric_dot_k, simsimd_datatype_unknown_k);
|
|
78
|
+
}
|
|
79
|
+
napi_value api_cos(napi_env env, napi_callback_info info) {
|
|
80
|
+
return dense(env, info, simsimd_metric_cosine_k, simsimd_datatype_unknown_k);
|
|
81
|
+
}
|
|
82
|
+
napi_value api_l2sq(napi_env env, napi_callback_info info) {
|
|
83
|
+
return dense(env, info, simsimd_metric_sqeuclidean_k, simsimd_datatype_unknown_k);
|
|
84
|
+
}
|
|
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
|
+
}
|
|
88
|
+
napi_value api_kl(napi_env env, napi_callback_info info) {
|
|
89
|
+
return dense(env, info, simsimd_metric_kl_k, simsimd_datatype_unknown_k);
|
|
90
|
+
}
|
|
91
|
+
napi_value api_js(napi_env env, napi_callback_info info) {
|
|
92
|
+
return dense(env, info, simsimd_metric_js_k, simsimd_datatype_unknown_k);
|
|
93
|
+
}
|
|
94
|
+
napi_value api_hamming(napi_env env, napi_callback_info info) {
|
|
95
|
+
return dense(env, info, simsimd_metric_hamming_k, simsimd_datatype_b8_k);
|
|
96
|
+
}
|
|
97
|
+
napi_value api_jaccard(napi_env env, napi_callback_info info) {
|
|
98
|
+
return dense(env, info, simsimd_metric_jaccard_k, simsimd_datatype_b8_k);
|
|
99
|
+
}
|
|
82
100
|
|
|
83
101
|
napi_value Init(napi_env env, napi_value exports) {
|
|
84
102
|
|
|
85
103
|
// Define an array of property descriptors
|
|
86
|
-
napi_property_descriptor
|
|
87
|
-
napi_property_descriptor
|
|
88
|
-
napi_property_descriptor
|
|
89
|
-
napi_property_descriptor
|
|
90
|
-
napi_property_descriptor
|
|
91
|
-
napi_property_descriptor
|
|
92
|
-
napi_property_descriptor
|
|
93
|
-
napi_property_descriptor
|
|
104
|
+
napi_property_descriptor dot_descriptor = {"dot", 0, api_ip, 0, 0, 0, napi_default, 0};
|
|
105
|
+
napi_property_descriptor inner_descriptor = {"inner", 0, api_ip, 0, 0, 0, napi_default, 0};
|
|
106
|
+
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};
|
|
108
|
+
napi_property_descriptor cosine_descriptor = {"cosine", 0, api_cos, 0, 0, 0, napi_default, 0};
|
|
109
|
+
napi_property_descriptor hamming_descriptor = {"hamming", 0, api_hamming, 0, 0, 0, napi_default, 0};
|
|
110
|
+
napi_property_descriptor jaccard_descriptor = {"jaccard", 0, api_jaccard, 0, 0, 0, napi_default, 0};
|
|
111
|
+
napi_property_descriptor kl_descriptor = {"kullbackleibler", 0, api_kl, 0, 0, 0, napi_default, 0};
|
|
112
|
+
napi_property_descriptor js_descriptor = {"jensenshannon", 0, api_js, 0, 0, 0, napi_default, 0};
|
|
94
113
|
napi_property_descriptor properties[] = {
|
|
95
|
-
|
|
114
|
+
dot_descriptor, inner_descriptor, sqeuclidean_descriptor, euclidean_descriptor, cosine_descriptor,
|
|
115
|
+
hamming_descriptor, jaccard_descriptor, kl_descriptor, js_descriptor,
|
|
96
116
|
};
|
|
97
117
|
|
|
98
118
|
// Define the properties on the `exports` object
|
|
99
|
-
size_t
|
|
100
|
-
napi_define_properties(env, exports,
|
|
119
|
+
size_t property_count = sizeof(properties) / sizeof(properties[0]);
|
|
120
|
+
napi_define_properties(env, exports, property_count, properties);
|
|
101
121
|
|
|
102
122
|
static_capabilities = simsimd_capabilities();
|
|
103
123
|
return exports;
|