usearch 2.26.0 → 2.26.2
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 +2 -2
- package/cpp/test.cpp +66 -3
- package/include/usearch/index.hpp +9 -1
- package/include/usearch/index_dense.hpp +16 -5
- package/numkong/CITATION.cff +13 -9
- package/numkong/CONTRIBUTING.md +29 -24
- package/numkong/Cargo.toml +1 -1
- package/numkong/README.md +21 -7
- package/numkong/binding.gyp +47 -31
- package/numkong/build.rs +15 -5
- package/numkong/c/dispatch.h +0 -1
- package/numkong/c/parallel.c +139 -0
- package/numkong/c/parallel.h +47 -0
- package/numkong/cmake/toolchain-wasi.cmake +39 -5
- package/numkong/include/README.md +24 -17
- package/numkong/include/numkong/attention/sapphireamx.h +6 -6
- package/numkong/include/numkong/attention/sme.h +2 -2
- package/numkong/include/numkong/capabilities.h +2 -2
- package/numkong/include/numkong/cast/README.md +9 -7
- package/numkong/include/numkong/cast/loongsonasx.h +48 -40
- package/numkong/include/numkong/cast/serial.h +89 -11
- package/numkong/include/numkong/cast.h +3 -3
- package/numkong/include/numkong/curved/README.md +10 -10
- package/numkong/include/numkong/curved/neon.h +1 -0
- package/numkong/include/numkong/curved/neonbfdot.h +1 -0
- package/numkong/include/numkong/curved/smef64.h +1 -0
- package/numkong/include/numkong/dot/README.md +18 -18
- package/numkong/include/numkong/dot/alder.h +8 -8
- package/numkong/include/numkong/dot/haswell.h +8 -8
- package/numkong/include/numkong/dot/icelake.h +12 -12
- package/numkong/include/numkong/dot/loongsonasx.h +6 -6
- package/numkong/include/numkong/dot/neon.h +2 -1
- package/numkong/include/numkong/dot/neonbfdot.h +1 -0
- package/numkong/include/numkong/dot/neonfhm.h +1 -0
- package/numkong/include/numkong/dot/neonsdot.h +1 -0
- package/numkong/include/numkong/dot/powervsx.h +3 -3
- package/numkong/include/numkong/dot/serial.h +19 -6
- package/numkong/include/numkong/dot/skylake.h +4 -4
- package/numkong/include/numkong/dot/svehalf.h +1 -1
- package/numkong/include/numkong/dot/v128relaxed.h +24 -23
- package/numkong/include/numkong/dots/README.md +26 -26
- package/numkong/include/numkong/dots/genoa.h +3 -1
- package/numkong/include/numkong/dots/icelake.h +1 -0
- package/numkong/include/numkong/dots/powervsx.h +1 -1
- package/numkong/include/numkong/dots/serial.h +32 -33
- package/numkong/include/numkong/dots/sierra.h +4 -3
- package/numkong/include/numkong/each/README.md +5 -3
- package/numkong/include/numkong/each/sapphire.h +2 -0
- package/numkong/include/numkong/each/skylake.h +1 -0
- package/numkong/include/numkong/geospatial/README.md +6 -6
- package/numkong/include/numkong/geospatial/v128relaxed.h +1 -0
- package/numkong/include/numkong/maxsim/README.md +5 -5
- package/numkong/include/numkong/maxsim/genoa.h +4 -0
- package/numkong/include/numkong/maxsim/icelake.h +1 -0
- package/numkong/include/numkong/maxsim/serial.h +22 -9
- package/numkong/include/numkong/maxsim/sme.h +3 -2
- package/numkong/include/numkong/mesh/README.md +7 -4
- package/numkong/include/numkong/mesh/genoa.h +2 -2
- package/numkong/include/numkong/mesh/haswell.h +8 -8
- package/numkong/include/numkong/mesh/neon.h +6 -6
- package/numkong/include/numkong/mesh/neonbfdot.h +3 -2
- package/numkong/include/numkong/mesh/neonfhm.h +3 -2
- package/numkong/include/numkong/mesh/rvv.h +4 -4
- package/numkong/include/numkong/mesh/serial.h +17 -17
- package/numkong/include/numkong/mesh/skylake.h +4 -4
- package/numkong/include/numkong/mesh/v128relaxed.h +4 -4
- package/numkong/include/numkong/mesh.hpp +2 -2
- package/numkong/include/numkong/numkong.h +1 -1
- package/numkong/include/numkong/numkong.hpp +1 -1
- package/numkong/include/numkong/probability/README.md +17 -15
- package/numkong/include/numkong/probability/skylake.h +1 -1
- package/numkong/include/numkong/reduce/README.md +27 -21
- package/numkong/include/numkong/reduce/genoa.h +2 -1
- package/numkong/include/numkong/reduce/icelake.h +1 -0
- package/numkong/include/numkong/reduce/neon.h +1 -1
- package/numkong/include/numkong/reduce/serial.h +9 -9
- package/numkong/include/numkong/reduce/skylake.h +4 -4
- package/numkong/include/numkong/reduce.hpp +38 -0
- package/numkong/include/numkong/scalar/README.md +29 -28
- package/numkong/include/numkong/set/README.md +17 -14
- package/numkong/include/numkong/set/haswell.h +15 -16
- package/numkong/include/numkong/set/icelake.h +15 -16
- package/numkong/include/numkong/set/loongsonasx.h +12 -10
- package/numkong/include/numkong/set/neon.h +15 -19
- package/numkong/include/numkong/set/powervsx.h +15 -21
- package/numkong/include/numkong/set/serial.h +19 -20
- package/numkong/include/numkong/set/v128relaxed.h +10 -8
- package/numkong/include/numkong/sets/icelake.h +1 -0
- package/numkong/include/numkong/sparse/README.md +4 -4
- package/numkong/include/numkong/sparse/icelake.h +1 -0
- package/numkong/include/numkong/sparse/neon.h +1 -0
- package/numkong/include/numkong/sparse/serial.h +10 -3
- package/numkong/include/numkong/sparse/turin.h +1 -0
- package/numkong/include/numkong/spatial/README.md +25 -24
- package/numkong/include/numkong/spatial/alder.h +2 -1
- package/numkong/include/numkong/spatial/haswell.h +48 -40
- package/numkong/include/numkong/spatial/icelake.h +2 -1
- package/numkong/include/numkong/spatial/neon.h +54 -46
- package/numkong/include/numkong/spatial/neonbfdot.h +1 -1
- package/numkong/include/numkong/spatial/powervsx.h +54 -46
- package/numkong/include/numkong/spatial/serial.h +56 -46
- package/numkong/include/numkong/spatial/sierra.h +4 -3
- package/numkong/include/numkong/spatial/skylake.h +27 -22
- package/numkong/include/numkong/spatial/sve.h +6 -6
- package/numkong/include/numkong/spatial/svebfdot.h +1 -1
- package/numkong/include/numkong/spatial/svehalf.h +1 -1
- package/numkong/include/numkong/spatial/v128relaxed.h +36 -30
- package/numkong/include/numkong/spatials/README.md +15 -15
- package/numkong/include/numkong/spatials/genoa.h +3 -0
- package/numkong/include/numkong/spatials/graniteamx.h +2 -0
- package/numkong/include/numkong/spatials/icelake.h +1 -0
- package/numkong/include/numkong/spatials/sierra.h +1 -0
- package/numkong/include/numkong/tensor.hpp +236 -123
- package/numkong/include/numkong/trigonometry/README.md +4 -4
- package/numkong/include/numkong/trigonometry/serial.h +6 -6
- package/numkong/include/numkong/trigonometry/v128relaxed.h +1 -0
- package/numkong/include/numkong/types.hpp +46 -16
- package/numkong/include/numkong/vector.hpp +116 -23
- package/numkong/javascript/@numkong-darwin-arm64/package.json +1 -1
- package/numkong/javascript/@numkong-darwin-x64/package.json +1 -1
- package/numkong/javascript/@numkong-linux-arm64/package.json +1 -1
- package/numkong/javascript/@numkong-linux-x64/package.json +1 -1
- package/numkong/javascript/@numkong-win32-arm64/package.json +1 -1
- package/numkong/javascript/@numkong-win32-x64/package.json +1 -1
- package/numkong/javascript/README.md +18 -16
- package/numkong/javascript/numkong.c +70 -40
- package/numkong/package.json +8 -8
- package/numkong/rust/README.md +19 -9
- package/numkong/rust/cast.rs +52 -1
- package/numkong/rust/each.rs +480 -7
- package/numkong/rust/matrix.rs +877 -216
- package/numkong/rust/numkong.rs +12 -8
- package/numkong/rust/reduce.rs +433 -5
- package/numkong/rust/tensor.rs +450 -842
- package/numkong/rust/vector.rs +520 -101
- package/numkong/swift/README.md +5 -3
- package/numkong/test/README.md +4 -4
- package/numkong/test/test-wasi.mjs +17 -0
- package/numkong/test/test_each.cpp +30 -1
- package/numkong/test/test_each.py +74 -1
- package/numkong/test/test_reduce.cpp +51 -0
- package/numkong/test/test_tensor.cpp +2 -63
- package/numkong/test/test_tensor.py +93 -0
- 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/prebuilds/win32-x64/usearch.node +0 -0
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
#include "numkong/types.h"
|
|
22
22
|
#include "numkong/maxsim/icelake.h" // `nk_maxsim_coarse_argmax_icelake_`
|
|
23
23
|
#include "numkong/dot.h" // `nk_dot_bf16`
|
|
24
|
+
#include "numkong/cast/icelake.h" // `nk_e5m2x32_to_bf16x32_icelake_`
|
|
25
|
+
#include "numkong/dot/skylake.h" // `nk_dot_through_f32_finalize_skylake_`
|
|
26
|
+
#include "numkong/maxsim/serial.h" // `nk_maxsim_packed_header_setup_`, `nk_maxsim_packed_size_`
|
|
27
|
+
#include "numkong/reduce/skylake.h" // `nk_reduce_add_f32x16_skylake_`
|
|
24
28
|
|
|
25
29
|
#if defined(__cplusplus)
|
|
26
30
|
extern "C" {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
#include "numkong/dot.h" // `nk_dot_f32`, `nk_dot_f16`
|
|
31
31
|
#include "numkong/cast/haswell.h" // `nk_f16_to_f32_haswell`
|
|
32
32
|
#include "numkong/spatial/haswell.h" // `nk_f32_sqrt_haswell`
|
|
33
|
+
#include "numkong/reduce/skylake.h" // `nk_reduce_add_f32x16_skylake_`
|
|
33
34
|
|
|
34
35
|
#if defined(__cplusplus)
|
|
35
36
|
extern "C" {
|
|
@@ -71,15 +71,6 @@ NK_STATIC_ASSERT(sizeof(nk_maxsim_vector_metadata_t) == 12, nk_maxsim_vector_met
|
|
|
71
71
|
*/
|
|
72
72
|
typedef void (*nk_maxsim_to_f32_t)(void const *source, nk_f32_t *destination);
|
|
73
73
|
|
|
74
|
-
/* Keep the serial instantiations below actually scalar, regardless of build type.
|
|
75
|
-
* See dots/serial.h for rationale. */
|
|
76
|
-
#if defined(__clang__)
|
|
77
|
-
#pragma clang attribute push(__attribute__((noinline)), apply_to = function)
|
|
78
|
-
#elif defined(__GNUC__)
|
|
79
|
-
#pragma GCC push_options
|
|
80
|
-
#pragma GCC optimize("no-tree-vectorize", "no-tree-slp-vectorize", "no-ipa-cp-clone", "no-inline")
|
|
81
|
-
#endif
|
|
82
|
-
|
|
83
74
|
/** @brief Identity conversion for f32 sources — just a typed memcpy. */
|
|
84
75
|
NK_INTERNAL void nk_f32_to_f32_(void const *source, nk_f32_t *destination) { *destination = *(nk_f32_t const *)source; }
|
|
85
76
|
|
|
@@ -234,6 +225,15 @@ NK_INTERNAL nk_size_t nk_maxsim_packed_size_( //
|
|
|
234
225
|
return header_size + i8_region_size + metadata_region_size + originals_region_size;
|
|
235
226
|
}
|
|
236
227
|
|
|
228
|
+
/* Keep the serial instantiations below actually scalar, regardless of build type.
|
|
229
|
+
* See dots/serial.h for rationale. */
|
|
230
|
+
#if defined(__clang__)
|
|
231
|
+
#pragma clang attribute push(__attribute__((noinline)), apply_to = function)
|
|
232
|
+
#elif defined(__GNUC__)
|
|
233
|
+
#pragma GCC push_options
|
|
234
|
+
#pragma GCC optimize("no-tree-vectorize", "no-tree-slp-vectorize", "no-ipa-cp-clone", "no-inline")
|
|
235
|
+
#endif
|
|
236
|
+
|
|
237
237
|
NK_PUBLIC nk_size_t nk_maxsim_packed_size_bf16_serial(nk_size_t vector_count, nk_size_t depth) {
|
|
238
238
|
return nk_maxsim_packed_size_(vector_count, depth, sizeof(nk_bf16_t), 1);
|
|
239
239
|
}
|
|
@@ -323,6 +323,12 @@ NK_PUBLIC void nk_maxsim_pack_f16_serial( //
|
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
+
#if defined(__clang__)
|
|
327
|
+
#pragma clang attribute pop
|
|
328
|
+
#elif defined(__GNUC__)
|
|
329
|
+
#pragma GCC pop_options
|
|
330
|
+
#endif
|
|
331
|
+
|
|
326
332
|
/**
|
|
327
333
|
* @brief Dtype-agnostic coarse i8 argmax kernel for the serial backend.
|
|
328
334
|
* Produces per-query best document indices using signed i8×i8 dot products.
|
|
@@ -389,6 +395,13 @@ NK_INTERNAL void nk_maxsim_coarse_argmax_serial_( //
|
|
|
389
395
|
}
|
|
390
396
|
}
|
|
391
397
|
|
|
398
|
+
#if defined(__clang__)
|
|
399
|
+
#pragma clang attribute push(__attribute__((noinline)), apply_to = function)
|
|
400
|
+
#elif defined(__GNUC__)
|
|
401
|
+
#pragma GCC push_options
|
|
402
|
+
#pragma GCC optimize("no-tree-vectorize", "no-tree-slp-vectorize", "no-ipa-cp-clone", "no-inline")
|
|
403
|
+
#endif
|
|
404
|
+
|
|
392
405
|
NK_PUBLIC void nk_maxsim_packed_bf16_serial( //
|
|
393
406
|
void const *query_packed, void const *document_packed, nk_size_t query_count, nk_size_t document_count,
|
|
394
407
|
nk_size_t depth, nk_f32_t *result) {
|
|
@@ -46,8 +46,9 @@
|
|
|
46
46
|
#if NK_TARGET_ARM64_
|
|
47
47
|
#if NK_TARGET_SME
|
|
48
48
|
|
|
49
|
-
#include "numkong/dots/sme.h"
|
|
50
|
-
#include "numkong/reduce/sve.h"
|
|
49
|
+
#include "numkong/dots/sme.h" // `nk_dots_sme_packed_header_t`
|
|
50
|
+
#include "numkong/reduce/sve.h" // `nk_svaddv_f64_`
|
|
51
|
+
#include "numkong/scalar/neon.h" // `nk_f64_rsqrt_neon`
|
|
51
52
|
|
|
52
53
|
#if defined(__cplusplus)
|
|
53
54
|
extern "C" {
|
|
@@ -51,10 +51,13 @@ def rmsd(a: np.ndarray, b: np.ndarray) -> float:
|
|
|
51
51
|
| Input Type | Output Type | Description |
|
|
52
52
|
| ---------- | ----------- | ---------------------------------------------- |
|
|
53
53
|
| `f64` | `f64` | 64-bit IEEE 754 double precision |
|
|
54
|
-
| `f32` | `
|
|
54
|
+
| `f32` | `f64` | 32-bit IEEE 754 single precision, widened RMSD |
|
|
55
55
|
| `f16` | `f32` | 16-bit IEEE 754 half precision, widened output |
|
|
56
56
|
| `bf16` | `f32` | 16-bit brain float, widened output |
|
|
57
57
|
|
|
58
|
+
The output type above is the RMSD result.
|
|
59
|
+
The centroid, rotation, and scale outputs use the transform type instead: `f64` for `f64` inputs, `f32` for `f32`, `f16`, and `bf16` inputs.
|
|
60
|
+
|
|
58
61
|
## Optimizations
|
|
59
62
|
|
|
60
63
|
### McAdams Branching-Free 3×3 SVD
|
|
@@ -199,9 +202,9 @@ Measured with Wasmtime v43 (Cranelift backend), WASI-SDK 24, `-msimd128 -mrelaxe
|
|
|
199
202
|
| `nk_kabsch_bf16_neonbfdot` | 180 mp/s, 0.7 ulp | 448 mp/s, 0.9 ulp | 726 mp/s, 1.3 ulp |
|
|
200
203
|
| `nk_umeyama_bf16_neonbfdot` | 176 mp/s, 0.2 ulp | 433 mp/s, 0.4 ulp | 705 mp/s, 0.8 ulp |
|
|
201
204
|
| __f16__ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ | ░░░░░░░░░░░░░░░░░░░░░░░░ |
|
|
202
|
-
| `
|
|
203
|
-
| `
|
|
204
|
-
| `
|
|
205
|
+
| `nk_rmsd_f16_neon` | 2,998 mp/s, 0.4 ulp | 3,215 mp/s, 1.7 ulp | 3,216 mp/s, 4.6 ulp |
|
|
206
|
+
| `nk_kabsch_f16_neon` | 178 mp/s, 0.9 ulp | 443 mp/s, 1.3 ulp | 711 mp/s, 2.4 ulp |
|
|
207
|
+
| `nk_umeyama_f16_neon` | 175 mp/s, 0.4 ulp | 408 mp/s, 0.8 ulp | 620 mp/s, 1.5 ulp |
|
|
205
208
|
|
|
206
209
|
#### WASM
|
|
207
210
|
|
|
@@ -410,7 +410,7 @@ NK_PUBLIC void nk_umeyama_bf16_genoa(nk_bf16_t const *a, nk_bf16_t const *b, nk_
|
|
|
410
410
|
optimal_rotation[3] = 0.0f, optimal_rotation[4] = 1.0f, optimal_rotation[5] = 0.0f;
|
|
411
411
|
optimal_rotation[6] = 0.0f, optimal_rotation[7] = 0.0f, optimal_rotation[8] = 1.0f;
|
|
412
412
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
413
|
-
c =
|
|
413
|
+
c = trace_rotation_covariance / centered_norm_squared_a;
|
|
414
414
|
}
|
|
415
415
|
else {
|
|
416
416
|
nk_f32_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -422,7 +422,7 @@ NK_PUBLIC void nk_umeyama_bf16_genoa(nk_bf16_t const *a, nk_bf16_t const *b, nk_
|
|
|
422
422
|
nk_f32_t d3 = det < 0.0f ? -1.0f : 1.0f;
|
|
423
423
|
nk_f32_t trace_ds = nk_sum_three_products_f32_(svd_diagonal[0], 1.0f, svd_diagonal[4], 1.0f, svd_diagonal[8],
|
|
424
424
|
d3);
|
|
425
|
-
c =
|
|
425
|
+
c = trace_ds / centered_norm_squared_a;
|
|
426
426
|
|
|
427
427
|
if (det < 0.0f) {
|
|
428
428
|
svd_right[2] = -svd_right[2], svd_right[5] = -svd_right[5], svd_right[8] = -svd_right[8];
|
|
@@ -763,7 +763,7 @@ NK_PUBLIC void nk_umeyama_f32_haswell(nk_f32_t const *a, nk_f32_t const *b, nk_s
|
|
|
763
763
|
optimal_rotation[3] = 0.0, optimal_rotation[4] = 1.0, optimal_rotation[5] = 0.0;
|
|
764
764
|
optimal_rotation[6] = 0.0, optimal_rotation[7] = 0.0, optimal_rotation[8] = 1.0;
|
|
765
765
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
766
|
-
applied_scale =
|
|
766
|
+
applied_scale = trace_rotation_covariance / centered_norm_squared_a;
|
|
767
767
|
}
|
|
768
768
|
else {
|
|
769
769
|
nk_f64_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -792,7 +792,7 @@ NK_PUBLIC void nk_umeyama_f32_haswell(nk_f32_t const *a, nk_f32_t const *b, nk_s
|
|
|
792
792
|
optimal_rotation[8] = svd_right[6] * svd_left[6] + svd_right[7] * svd_left[7] + svd_right[8] * svd_left[8];
|
|
793
793
|
}
|
|
794
794
|
nk_f64_t trace_ds = svd_diagonal[0] + svd_diagonal[4] + sign_correction * svd_diagonal[8];
|
|
795
|
-
applied_scale =
|
|
795
|
+
applied_scale = trace_ds / centered_norm_squared_a;
|
|
796
796
|
trace_rotation_covariance =
|
|
797
797
|
optimal_rotation[0] * cross_covariance[0] + optimal_rotation[1] * cross_covariance[3] +
|
|
798
798
|
optimal_rotation[2] * cross_covariance[6] + optimal_rotation[3] * cross_covariance[1] +
|
|
@@ -962,7 +962,7 @@ NK_PUBLIC void nk_umeyama_f64_haswell(nk_f64_t const *a, nk_f64_t const *b, nk_s
|
|
|
962
962
|
optimal_rotation[3] = 0.0, optimal_rotation[4] = 1.0, optimal_rotation[5] = 0.0;
|
|
963
963
|
optimal_rotation[6] = 0.0, optimal_rotation[7] = 0.0, optimal_rotation[8] = 1.0;
|
|
964
964
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
965
|
-
c =
|
|
965
|
+
c = trace_rotation_covariance / centered_norm_squared_a;
|
|
966
966
|
}
|
|
967
967
|
else {
|
|
968
968
|
nk_f64_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -972,7 +972,7 @@ NK_PUBLIC void nk_umeyama_f64_haswell(nk_f64_t const *a, nk_f64_t const *b, nk_s
|
|
|
972
972
|
nk_f64_t det = nk_det3x3_f64_(optimal_rotation);
|
|
973
973
|
nk_f64_t d3 = det < 0 ? -1.0 : 1.0;
|
|
974
974
|
nk_f64_t trace_ds = nk_sum_three_products_f64_(svd_diagonal[0], 1.0, svd_diagonal[4], 1.0, svd_diagonal[8], d3);
|
|
975
|
-
c =
|
|
975
|
+
c = trace_ds / centered_norm_squared_a;
|
|
976
976
|
|
|
977
977
|
if (det < 0) {
|
|
978
978
|
svd_right[2] = -svd_right[2], svd_right[5] = -svd_right[5], svd_right[8] = -svd_right[8];
|
|
@@ -1633,7 +1633,7 @@ NK_PUBLIC void nk_umeyama_f16_haswell(nk_f16_t const *a, nk_f16_t const *b, nk_s
|
|
|
1633
1633
|
optimal_rotation[3] = 0.0f, optimal_rotation[4] = 1.0f, optimal_rotation[5] = 0.0f;
|
|
1634
1634
|
optimal_rotation[6] = 0.0f, optimal_rotation[7] = 0.0f, optimal_rotation[8] = 1.0f;
|
|
1635
1635
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
1636
|
-
applied_scale =
|
|
1636
|
+
applied_scale = trace_rotation_covariance / centered_norm_squared_a;
|
|
1637
1637
|
}
|
|
1638
1638
|
else {
|
|
1639
1639
|
nk_f32_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -1664,7 +1664,7 @@ NK_PUBLIC void nk_umeyama_f16_haswell(nk_f16_t const *a, nk_f16_t const *b, nk_s
|
|
|
1664
1664
|
optimal_rotation[8] = svd_right[6] * svd_left[6] + svd_right[7] * svd_left[7] + svd_right[8] * svd_left[8];
|
|
1665
1665
|
}
|
|
1666
1666
|
nk_f32_t trace_ds = svd_diagonal[0] + svd_diagonal[4] + sign_correction * svd_diagonal[8];
|
|
1667
|
-
applied_scale =
|
|
1667
|
+
applied_scale = trace_ds / centered_norm_squared_a;
|
|
1668
1668
|
trace_rotation_covariance =
|
|
1669
1669
|
optimal_rotation[0] * cross_covariance[0] + optimal_rotation[1] * cross_covariance[3] +
|
|
1670
1670
|
optimal_rotation[2] * cross_covariance[6] + optimal_rotation[3] * cross_covariance[1] +
|
|
@@ -1819,7 +1819,7 @@ NK_PUBLIC void nk_umeyama_bf16_haswell(nk_bf16_t const *a, nk_bf16_t const *b, n
|
|
|
1819
1819
|
optimal_rotation[3] = 0.0f, optimal_rotation[4] = 1.0f, optimal_rotation[5] = 0.0f;
|
|
1820
1820
|
optimal_rotation[6] = 0.0f, optimal_rotation[7] = 0.0f, optimal_rotation[8] = 1.0f;
|
|
1821
1821
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
1822
|
-
applied_scale =
|
|
1822
|
+
applied_scale = trace_rotation_covariance / centered_norm_squared_a;
|
|
1823
1823
|
}
|
|
1824
1824
|
else {
|
|
1825
1825
|
nk_f32_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -1850,7 +1850,7 @@ NK_PUBLIC void nk_umeyama_bf16_haswell(nk_bf16_t const *a, nk_bf16_t const *b, n
|
|
|
1850
1850
|
optimal_rotation[8] = svd_right[6] * svd_left[6] + svd_right[7] * svd_left[7] + svd_right[8] * svd_left[8];
|
|
1851
1851
|
}
|
|
1852
1852
|
nk_f32_t trace_ds = svd_diagonal[0] + svd_diagonal[4] + sign_correction * svd_diagonal[8];
|
|
1853
|
-
applied_scale =
|
|
1853
|
+
applied_scale = trace_ds / centered_norm_squared_a;
|
|
1854
1854
|
trace_rotation_covariance =
|
|
1855
1855
|
optimal_rotation[0] * cross_covariance[0] + optimal_rotation[1] * cross_covariance[3] +
|
|
1856
1856
|
optimal_rotation[2] * cross_covariance[6] + optimal_rotation[3] * cross_covariance[1] +
|
|
@@ -853,7 +853,7 @@ NK_PUBLIC void nk_umeyama_f32_neon(nk_f32_t const *a, nk_f32_t const *b, nk_size
|
|
|
853
853
|
optimal_rotation[4] = 1, optimal_rotation[5] = 0, optimal_rotation[6] = 0, optimal_rotation[7] = 0,
|
|
854
854
|
optimal_rotation[8] = 1;
|
|
855
855
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
856
|
-
applied_scale =
|
|
856
|
+
applied_scale = trace_rotation_covariance / centered_norm_squared_a;
|
|
857
857
|
}
|
|
858
858
|
else {
|
|
859
859
|
nk_f64_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -884,7 +884,7 @@ NK_PUBLIC void nk_umeyama_f32_neon(nk_f32_t const *a, nk_f32_t const *b, nk_size
|
|
|
884
884
|
}
|
|
885
885
|
|
|
886
886
|
nk_f64_t trace_ds = svd_diagonal[0] + svd_diagonal[4] + sign_correction * svd_diagonal[8];
|
|
887
|
-
applied_scale =
|
|
887
|
+
applied_scale = trace_ds / centered_norm_squared_a;
|
|
888
888
|
|
|
889
889
|
trace_rotation_covariance =
|
|
890
890
|
optimal_rotation[0] * cross_covariance[0] + optimal_rotation[1] * cross_covariance[3] +
|
|
@@ -1139,7 +1139,7 @@ NK_PUBLIC void nk_umeyama_f64_neon(nk_f64_t const *a, nk_f64_t const *b, nk_size
|
|
|
1139
1139
|
optimal_rotation[4] = 1, optimal_rotation[5] = 0, optimal_rotation[6] = 0, optimal_rotation[7] = 0,
|
|
1140
1140
|
optimal_rotation[8] = 1;
|
|
1141
1141
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
1142
|
-
computed_scale =
|
|
1142
|
+
computed_scale = trace_rotation_covariance / centered_norm_squared_a;
|
|
1143
1143
|
}
|
|
1144
1144
|
else {
|
|
1145
1145
|
nk_f64_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -1149,7 +1149,7 @@ NK_PUBLIC void nk_umeyama_f64_neon(nk_f64_t const *a, nk_f64_t const *b, nk_size
|
|
|
1149
1149
|
// Handle reflection and compute scale
|
|
1150
1150
|
nk_f64_t det = nk_det3x3_f64_(optimal_rotation);
|
|
1151
1151
|
nk_f64_t trace_d_s = svd_diagonal[0] + svd_diagonal[4] + (det < 0 ? -svd_diagonal[8] : svd_diagonal[8]);
|
|
1152
|
-
computed_scale =
|
|
1152
|
+
computed_scale = trace_d_s / centered_norm_squared_a;
|
|
1153
1153
|
|
|
1154
1154
|
if (det < 0) {
|
|
1155
1155
|
svd_right[2] = -svd_right[2], svd_right[5] = -svd_right[5], svd_right[8] = -svd_right[8];
|
|
@@ -1679,7 +1679,7 @@ NK_PUBLIC void nk_umeyama_f16_neon(nk_f16_t const *a, nk_f16_t const *b, nk_size
|
|
|
1679
1679
|
optimal_rotation[4] = 1, optimal_rotation[5] = 0, optimal_rotation[6] = 0, optimal_rotation[7] = 0,
|
|
1680
1680
|
optimal_rotation[8] = 1;
|
|
1681
1681
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
1682
|
-
scale_factor =
|
|
1682
|
+
scale_factor = trace_rotation_covariance / centered_norm_squared_a;
|
|
1683
1683
|
}
|
|
1684
1684
|
else {
|
|
1685
1685
|
// SVD of H = U * S * Vᵀ
|
|
@@ -1701,7 +1701,7 @@ NK_PUBLIC void nk_umeyama_f16_neon(nk_f16_t const *a, nk_f16_t const *b, nk_size
|
|
|
1701
1701
|
nk_f32_t rotation_determinant = nk_det3x3_f32_(optimal_rotation);
|
|
1702
1702
|
nk_f32_t sign_det = rotation_determinant < 0 ? -1.0f : 1.0f;
|
|
1703
1703
|
nk_f32_t trace_scaled_s = svd_diagonal[0] + svd_diagonal[4] + sign_det * svd_diagonal[8];
|
|
1704
|
-
scale_factor =
|
|
1704
|
+
scale_factor = trace_scaled_s / centered_norm_squared_a;
|
|
1705
1705
|
|
|
1706
1706
|
if (rotation_determinant < 0) {
|
|
1707
1707
|
svd_right[2] = -svd_right[2], svd_right[5] = -svd_right[5], svd_right[8] = -svd_right[8];
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
#include "numkong/types.h"
|
|
41
41
|
#include "numkong/cast/neon.h" // `nk_u16x8_splat_`
|
|
42
42
|
#include "numkong/spatial/neon.h" // `nk_f32_sqrt_neon`
|
|
43
|
+
#include "numkong/mesh/serial.h" // `nk_det3x3_f32_`, `nk_svd3x3_f32_`
|
|
43
44
|
|
|
44
45
|
#if defined(__cplusplus)
|
|
45
46
|
extern "C" {
|
|
@@ -559,7 +560,7 @@ NK_PUBLIC void nk_umeyama_bf16_neonbfdot(nk_bf16_t const *a, nk_bf16_t const *b,
|
|
|
559
560
|
optimal_rotation[4] = 1, optimal_rotation[5] = 0, optimal_rotation[6] = 0, optimal_rotation[7] = 0,
|
|
560
561
|
optimal_rotation[8] = 1;
|
|
561
562
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
562
|
-
c =
|
|
563
|
+
c = trace_rotation_covariance / centered_norm_squared_a;
|
|
563
564
|
}
|
|
564
565
|
else {
|
|
565
566
|
nk_f32_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -581,7 +582,7 @@ NK_PUBLIC void nk_umeyama_bf16_neonbfdot(nk_bf16_t const *a, nk_bf16_t const *b,
|
|
|
581
582
|
nk_f32_t rotation_det = nk_det3x3_f32_(optimal_rotation);
|
|
582
583
|
nk_f32_t sign_det = rotation_det < 0 ? -1.0f : 1.0f;
|
|
583
584
|
nk_f32_t trace_scaled_s = svd_diagonal[0] + svd_diagonal[4] + sign_det * svd_diagonal[8];
|
|
584
|
-
c =
|
|
585
|
+
c = trace_scaled_s / centered_norm_squared_a;
|
|
585
586
|
|
|
586
587
|
if (rotation_det < 0) {
|
|
587
588
|
svd_right[2] = -svd_right[2], svd_right[5] = -svd_right[5], svd_right[8] = -svd_right[8];
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
#include "numkong/types.h"
|
|
40
40
|
#include "numkong/spatial/neon.h" // `nk_f32_sqrt_neon`
|
|
41
|
+
#include "numkong/mesh/serial.h" // `nk_det3x3_f32_`, `nk_svd3x3_f32_`
|
|
41
42
|
|
|
42
43
|
#if defined(__cplusplus)
|
|
43
44
|
extern "C" {
|
|
@@ -604,7 +605,7 @@ NK_PUBLIC void nk_umeyama_f16_neonfhm(nk_f16_t const *a, nk_f16_t const *b, nk_s
|
|
|
604
605
|
optimal_rotation[4] = 1, optimal_rotation[5] = 0, optimal_rotation[6] = 0, optimal_rotation[7] = 0,
|
|
605
606
|
optimal_rotation[8] = 1;
|
|
606
607
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
607
|
-
scale_factor =
|
|
608
|
+
scale_factor = trace_rotation_covariance / centered_norm_squared_a;
|
|
608
609
|
}
|
|
609
610
|
else {
|
|
610
611
|
// SVD of H = U · S · Vᵀ
|
|
@@ -626,7 +627,7 @@ NK_PUBLIC void nk_umeyama_f16_neonfhm(nk_f16_t const *a, nk_f16_t const *b, nk_s
|
|
|
626
627
|
nk_f32_t rotation_determinant = nk_det3x3_f32_(optimal_rotation);
|
|
627
628
|
nk_f32_t sign_det = rotation_determinant < 0 ? -1.0f : 1.0f;
|
|
628
629
|
nk_f32_t trace_scaled_s = svd_diagonal[0] + svd_diagonal[4] + sign_det * svd_diagonal[8];
|
|
629
|
-
scale_factor =
|
|
630
|
+
scale_factor = trace_scaled_s / centered_norm_squared_a;
|
|
630
631
|
|
|
631
632
|
if (rotation_determinant < 0) {
|
|
632
633
|
svd_right[2] = -svd_right[2], svd_right[5] = -svd_right[5], svd_right[8] = -svd_right[8];
|
|
@@ -884,7 +884,7 @@ NK_PUBLIC void nk_umeyama_f32_rvv(nk_f32_t const *a, nk_f32_t const *b, nk_size_
|
|
|
884
884
|
optimal_rotation[4] = 1, optimal_rotation[5] = 0, optimal_rotation[6] = 0, optimal_rotation[7] = 0,
|
|
885
885
|
optimal_rotation[8] = 1;
|
|
886
886
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
887
|
-
scale_factor =
|
|
887
|
+
scale_factor = trace_rotation_covariance / centered_norm_squared_a;
|
|
888
888
|
}
|
|
889
889
|
else {
|
|
890
890
|
nk_f64_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -894,7 +894,7 @@ NK_PUBLIC void nk_umeyama_f32_rvv(nk_f32_t const *a, nk_f32_t const *b, nk_size_
|
|
|
894
894
|
nk_f64_t sign_det = det < 0 ? -1.0 : 1.0;
|
|
895
895
|
nk_f64_t trace_ds = nk_sum_three_products_f64_(svd_diagonal[0], 1.0, svd_diagonal[4], 1.0, svd_diagonal[8],
|
|
896
896
|
sign_det);
|
|
897
|
-
scale_factor =
|
|
897
|
+
scale_factor = trace_ds / centered_norm_squared_a;
|
|
898
898
|
if (det < 0) {
|
|
899
899
|
svd_right[2] = -svd_right[2], svd_right[5] = -svd_right[5], svd_right[8] = -svd_right[8];
|
|
900
900
|
nk_rotation_from_svd_f64_serial_(svd_left, svd_right, optimal_rotation);
|
|
@@ -944,7 +944,7 @@ NK_PUBLIC void nk_umeyama_f64_rvv(nk_f64_t const *a, nk_f64_t const *b, nk_size_
|
|
|
944
944
|
optimal_rotation[4] = 1, optimal_rotation[5] = 0, optimal_rotation[6] = 0, optimal_rotation[7] = 0,
|
|
945
945
|
optimal_rotation[8] = 1;
|
|
946
946
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
947
|
-
scale_factor =
|
|
947
|
+
scale_factor = trace_rotation_covariance / centered_norm_squared_a;
|
|
948
948
|
}
|
|
949
949
|
else {
|
|
950
950
|
nk_f64_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -954,7 +954,7 @@ NK_PUBLIC void nk_umeyama_f64_rvv(nk_f64_t const *a, nk_f64_t const *b, nk_size_
|
|
|
954
954
|
nk_f64_t sign_det = det < 0 ? -1.0 : 1.0;
|
|
955
955
|
nk_f64_t trace_ds = nk_sum_three_products_f64_(svd_diagonal[0], 1.0, svd_diagonal[4], 1.0, svd_diagonal[8],
|
|
956
956
|
sign_det);
|
|
957
|
-
scale_factor =
|
|
957
|
+
scale_factor = trace_ds / centered_norm_squared_a;
|
|
958
958
|
if (det < 0) {
|
|
959
959
|
svd_right[2] = -svd_right[2], svd_right[5] = -svd_right[5], svd_right[8] = -svd_right[8];
|
|
960
960
|
nk_rotation_from_svd_f64_serial_(svd_left, svd_right, optimal_rotation);
|
|
@@ -272,17 +272,6 @@ extern "C" {
|
|
|
272
272
|
m[2] * (m[3] * m[7] - m[4] * m[6]); \
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
/* Keep the serial instantiations below actually scalar, regardless of build type.
|
|
276
|
-
* Without this, -O3 + LTO can vectorize or clone the serial kernels under AVX-512
|
|
277
|
-
* callers in dispatch_*.c, which wastes binary and breaks the nk_*_serial-as-scalar-oracle
|
|
278
|
-
* contract that tests and numerical-stability docs rely on. See dots/serial.h. */
|
|
279
|
-
#if defined(__clang__)
|
|
280
|
-
#pragma clang attribute push(__attribute__((noinline)), apply_to = function)
|
|
281
|
-
#elif defined(__GNUC__)
|
|
282
|
-
#pragma GCC push_options
|
|
283
|
-
#pragma GCC optimize("no-tree-vectorize", "no-tree-slp-vectorize", "no-ipa-cp-clone", "no-inline")
|
|
284
|
-
#endif
|
|
285
|
-
|
|
286
275
|
/* Size bias for release. Gated on NDEBUG so Debug builds keep -O0 for stepping. */
|
|
287
276
|
#if defined(NDEBUG)
|
|
288
277
|
#if defined(_MSC_VER)
|
|
@@ -701,6 +690,17 @@ nk_define_det3x3_(f64)
|
|
|
701
690
|
*result = (nk_##result_type##_t)compute_sqrt((sum_squared + sum_squared_compensation) * inv_n); \
|
|
702
691
|
}
|
|
703
692
|
|
|
693
|
+
/* Keep the serial instantiations below actually scalar, regardless of build type.
|
|
694
|
+
* Without this, -O3 + LTO can vectorize or clone the serial kernels under AVX-512
|
|
695
|
+
* callers in dispatch_*.c, which wastes binary and breaks the nk_*_serial-as-scalar-oracle
|
|
696
|
+
* contract that tests and numerical-stability docs rely on. See dots/serial.h. */
|
|
697
|
+
#if defined(__clang__)
|
|
698
|
+
#pragma clang attribute push(__attribute__((noinline)), apply_to = function)
|
|
699
|
+
#elif defined(__GNUC__)
|
|
700
|
+
#pragma GCC push_options
|
|
701
|
+
#pragma GCC optimize("no-tree-vectorize", "no-tree-slp-vectorize", "no-ipa-cp-clone", "no-inline")
|
|
702
|
+
#endif
|
|
703
|
+
|
|
704
704
|
nk_define_rmsd_(f64, f64, f64, f64, nk_assign_from_to_, nk_f64_sqrt_serial) // nk_rmsd_f64_serial
|
|
705
705
|
nk_define_kabsch_(f64, f64, f64, f64, f64, nk_assign_from_to_, nk_f64_sqrt_serial) // nk_kabsch_f64_serial
|
|
706
706
|
nk_define_umeyama_(f64, f64, f64, f64, f64, nk_assign_from_to_, nk_f64_sqrt_serial) // nk_umeyama_f64_serial
|
|
@@ -740,21 +740,21 @@ nk_define_umeyama_(bf16, f32, f32, f32, f32, nk_bf16_to_f32_serial, nk_f32_sqrt_
|
|
|
740
740
|
#undef nk_define_kabsch_
|
|
741
741
|
#undef nk_define_umeyama_
|
|
742
742
|
|
|
743
|
-
#if defined(
|
|
744
|
-
#if defined(_MSC_VER)
|
|
745
|
-
#pragma optimize("", on)
|
|
746
|
-
#elif defined(__clang__)
|
|
743
|
+
#if defined(__clang__)
|
|
747
744
|
#pragma clang attribute pop
|
|
748
745
|
#elif defined(__GNUC__)
|
|
749
746
|
#pragma GCC pop_options
|
|
750
747
|
#endif
|
|
751
|
-
#endif
|
|
752
748
|
|
|
753
|
-
#if defined(
|
|
749
|
+
#if defined(NDEBUG)
|
|
750
|
+
#if defined(_MSC_VER)
|
|
751
|
+
#pragma optimize("", on)
|
|
752
|
+
#elif defined(__clang__)
|
|
754
753
|
#pragma clang attribute pop
|
|
755
754
|
#elif defined(__GNUC__)
|
|
756
755
|
#pragma GCC pop_options
|
|
757
756
|
#endif
|
|
757
|
+
#endif
|
|
758
758
|
|
|
759
759
|
#if defined(__cplusplus)
|
|
760
760
|
} // extern "C"
|
|
@@ -1157,7 +1157,7 @@ NK_PUBLIC void nk_umeyama_f64_skylake(nk_f64_t const *a, nk_f64_t const *b, nk_s
|
|
|
1157
1157
|
optimal_rotation[4] = 1, optimal_rotation[5] = 0, optimal_rotation[6] = 0, optimal_rotation[7] = 0,
|
|
1158
1158
|
optimal_rotation[8] = 1;
|
|
1159
1159
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
1160
|
-
c =
|
|
1160
|
+
c = trace_rotation_covariance / centered_norm_squared_a;
|
|
1161
1161
|
}
|
|
1162
1162
|
else {
|
|
1163
1163
|
nk_f64_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -1168,7 +1168,7 @@ NK_PUBLIC void nk_umeyama_f64_skylake(nk_f64_t const *a, nk_f64_t const *b, nk_s
|
|
|
1168
1168
|
nk_f64_t det = nk_det3x3_f64_(optimal_rotation);
|
|
1169
1169
|
nk_f64_t d3 = det < 0 ? -1.0 : 1.0;
|
|
1170
1170
|
nk_f64_t trace_ds = nk_sum_three_products_f64_(svd_diagonal[0], 1.0, svd_diagonal[4], 1.0, svd_diagonal[8], d3);
|
|
1171
|
-
c =
|
|
1171
|
+
c = trace_ds / centered_norm_squared_a;
|
|
1172
1172
|
|
|
1173
1173
|
if (det < 0) {
|
|
1174
1174
|
svd_right[2] = -svd_right[2], svd_right[5] = -svd_right[5], svd_right[8] = -svd_right[8];
|
|
@@ -1620,7 +1620,7 @@ NK_PUBLIC void nk_umeyama_f16_skylake(nk_f16_t const *a, nk_f16_t const *b, nk_s
|
|
|
1620
1620
|
nk_f32_t det = nk_det3x3_f32_(optimal_rotation);
|
|
1621
1621
|
nk_f32_t d3 = det < 0.0f ? -1.0f : 1.0f;
|
|
1622
1622
|
nk_f32_t trace_ds = nk_sum_three_products_f32_(svd_diagonal[0], 1.0f, svd_diagonal[4], 1.0f, svd_diagonal[8], d3);
|
|
1623
|
-
nk_f32_t c =
|
|
1623
|
+
nk_f32_t c = trace_ds / centered_norm_squared_a;
|
|
1624
1624
|
if (scale) *scale = c;
|
|
1625
1625
|
|
|
1626
1626
|
if (det < 0.0f) {
|
|
@@ -1749,7 +1749,7 @@ NK_PUBLIC void nk_umeyama_bf16_skylake(nk_bf16_t const *a, nk_bf16_t const *b, n
|
|
|
1749
1749
|
nk_f32_t det = nk_det3x3_f32_(optimal_rotation);
|
|
1750
1750
|
nk_f32_t d3 = det < 0.0f ? -1.0f : 1.0f;
|
|
1751
1751
|
nk_f32_t trace_ds = nk_sum_three_products_f32_(svd_diagonal[0], 1.0f, svd_diagonal[4], 1.0f, svd_diagonal[8], d3);
|
|
1752
|
-
nk_f32_t c =
|
|
1752
|
+
nk_f32_t c = trace_ds / centered_norm_squared_a;
|
|
1753
1753
|
if (scale) *scale = c;
|
|
1754
1754
|
|
|
1755
1755
|
if (det < 0.0f) {
|
|
@@ -842,7 +842,7 @@ NK_PUBLIC void nk_umeyama_f32_v128relaxed(nk_f32_t const *a, nk_f32_t const *b,
|
|
|
842
842
|
optimal_rotation[4] = 1, optimal_rotation[5] = 0, optimal_rotation[6] = 0, optimal_rotation[7] = 0,
|
|
843
843
|
optimal_rotation[8] = 1;
|
|
844
844
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
845
|
-
computed_scale =
|
|
845
|
+
computed_scale = trace_rotation_covariance / centered_norm_squared_a;
|
|
846
846
|
}
|
|
847
847
|
else {
|
|
848
848
|
nk_f64_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -874,7 +874,7 @@ NK_PUBLIC void nk_umeyama_f32_v128relaxed(nk_f32_t const *a, nk_f32_t const *b,
|
|
|
874
874
|
|
|
875
875
|
nk_f64_t trace_signed_singular_values = svd_diagonal[0] + svd_diagonal[4] +
|
|
876
876
|
(det < 0 ? -svd_diagonal[8] : svd_diagonal[8]);
|
|
877
|
-
computed_scale =
|
|
877
|
+
computed_scale = trace_signed_singular_values / centered_norm_squared_a;
|
|
878
878
|
|
|
879
879
|
trace_rotation_covariance =
|
|
880
880
|
optimal_rotation[0] * cross_covariance[0] + optimal_rotation[1] * cross_covariance[3] +
|
|
@@ -1059,7 +1059,7 @@ NK_PUBLIC void nk_umeyama_f64_v128relaxed(nk_f64_t const *a, nk_f64_t const *b,
|
|
|
1059
1059
|
optimal_rotation[4] = 1, optimal_rotation[5] = 0, optimal_rotation[6] = 0, optimal_rotation[7] = 0,
|
|
1060
1060
|
optimal_rotation[8] = 1;
|
|
1061
1061
|
trace_rotation_covariance = cross_covariance[0] + cross_covariance[4] + cross_covariance[8];
|
|
1062
|
-
computed_scale =
|
|
1062
|
+
computed_scale = trace_rotation_covariance / centered_norm_squared_a;
|
|
1063
1063
|
}
|
|
1064
1064
|
else {
|
|
1065
1065
|
nk_f64_t svd_left[9], svd_diagonal[9], svd_right[9];
|
|
@@ -1069,7 +1069,7 @@ NK_PUBLIC void nk_umeyama_f64_v128relaxed(nk_f64_t const *a, nk_f64_t const *b,
|
|
|
1069
1069
|
// Handle reflection and compute scale
|
|
1070
1070
|
nk_f64_t det = nk_det3x3_f64_(optimal_rotation);
|
|
1071
1071
|
nk_f64_t trace_d_s = svd_diagonal[0] + svd_diagonal[4] + (det < 0 ? -svd_diagonal[8] : svd_diagonal[8]);
|
|
1072
|
-
computed_scale =
|
|
1072
|
+
computed_scale = trace_d_s / centered_norm_squared_a;
|
|
1073
1073
|
|
|
1074
1074
|
if (det < 0) {
|
|
1075
1075
|
svd_right[2] = -svd_right[2], svd_right[5] = -svd_right[5], svd_right[8] = -svd_right[8];
|
|
@@ -319,8 +319,8 @@ void svd3x3_(scalar_type_ const *a, scalar_type_ *svd_u, scalar_type_ *svd_s, sc
|
|
|
319
319
|
|
|
320
320
|
/**
|
|
321
321
|
* @brief Root Mean Square Deviation between two 3D point clouds (no alignment)
|
|
322
|
-
* @param[in] a,b Point clouds [
|
|
323
|
-
* @param[in]
|
|
322
|
+
* @param[in] a,b Point clouds [n x 3] interleaved (x0,y0,z0, x1,y1,z1, ...)
|
|
323
|
+
* @param[in] n Number of 3D points
|
|
324
324
|
* @param[out] a_centroid,b_centroid Centroids (3 values each), can be nullptr
|
|
325
325
|
* @param[out] rotation 3x3 rotation matrix (9 values), always identity, can be nullptr
|
|
326
326
|
* @param[out] scale Scale factor, always 1.0, can be nullptr
|
|
@@ -6,7 +6,7 @@ These are used in variational inference, topic modeling, and distribution compar
|
|
|
6
6
|
Kullback-Leibler divergence from $P$ to $Q$:
|
|
7
7
|
|
|
8
8
|
$$
|
|
9
|
-
\text{KLD}(P \| Q) = \sum_{i=0}^{n-1} P(i) \
|
|
9
|
+
\text{KLD}(P \| Q) = \sum_{i=0}^{n-1} P(i) \ln \frac{P(i)}{Q(i)}
|
|
10
10
|
$$
|
|
11
11
|
|
|
12
12
|
Jensen-Shannon distance is the square root of the symmetrized KLD through a mixture:
|
|
@@ -28,9 +28,8 @@ Reformulating as Python pseudocode:
|
|
|
28
28
|
```python
|
|
29
29
|
import numpy as np
|
|
30
30
|
|
|
31
|
-
def kld(p: np.ndarray, q: np.ndarray) -> float:
|
|
32
|
-
|
|
33
|
-
return np.sum(p[mask] * np.log2(p[mask] / q[mask]))
|
|
31
|
+
def kld(p: np.ndarray, q: np.ndarray, eps: float = 1e-7) -> float:
|
|
32
|
+
return np.sum(p * np.log((p + eps) / (q + eps)))
|
|
34
33
|
|
|
35
34
|
def jsd(p: np.ndarray, q: np.ndarray) -> float:
|
|
36
35
|
m = (p + q) / 2
|
|
@@ -45,12 +44,12 @@ __Jensen-Shannon distance__ is commonly used in microbiome community comparison
|
|
|
45
44
|
|
|
46
45
|
## Input & Output Types
|
|
47
46
|
|
|
48
|
-
| Input Type | Output Type | Description
|
|
49
|
-
| ---------- | ----------- |
|
|
50
|
-
| `f64` | `f64` | 64-bit IEEE 754 double precision
|
|
51
|
-
| `f32` | `
|
|
52
|
-
| `f16` | `f32` | 16-bit IEEE 754 half precision, widened output
|
|
53
|
-
| `bf16` | `f32` | 16-bit brain float, widened output
|
|
47
|
+
| Input Type | Output Type | Description |
|
|
48
|
+
| ---------- | ----------- | ------------------------------------------------ |
|
|
49
|
+
| `f64` | `f64` | 64-bit IEEE 754 double precision |
|
|
50
|
+
| `f32` | `f64` | 32-bit IEEE 754 single precision, widened output |
|
|
51
|
+
| `f16` | `f32` | 16-bit IEEE 754 half precision, widened output |
|
|
52
|
+
| `bf16` | `f32` | 16-bit brain float, widened output |
|
|
54
53
|
|
|
55
54
|
## Optimizations
|
|
56
55
|
|
|
@@ -61,24 +60,27 @@ The pipeline on Skylake is:
|
|
|
61
60
|
|
|
62
61
|
```
|
|
63
62
|
exponent = VGETEXPPS(x)
|
|
64
|
-
mantissa = VGETMANTPS(x, normalize_to_[1,2))
|
|
65
|
-
|
|
63
|
+
mantissa = VGETMANTPS(x, normalize_to_[1,2))
|
|
64
|
+
s = (mantissa - 1) / (mantissa + 1)
|
|
65
|
+
log2(x) ≈ exponent + (2/ln2) · s · (1 + s²/3 + s⁴/5 + s⁶/7 + s⁸/9)
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
`VGETEXP` extracts the unbiased exponent as a float, while `VGETMANT` normalizes the mantissa to $[1, 2)$.
|
|
69
|
-
A
|
|
69
|
+
A five-term odd series in $s$ — the $\operatorname{atanh}$ expansion, which converges quickly because $s \in [0, 1/3]$ — completes the approximation.
|
|
70
70
|
These instructions handle subnormals correctly without extra integer bit manipulation.
|
|
71
71
|
|
|
72
72
|
`nk_kld_f32_neon`, `nk_jsd_f32_neon`, `nk_kld_f16_haswell`, `nk_jsd_f16_haswell` use integer bit extraction instead:
|
|
73
73
|
|
|
74
74
|
```
|
|
75
75
|
exponent = (reinterpret_as_int(x) >> 23) - 127
|
|
76
|
-
mantissa = reinterpret_as_float((reinterpret_as_int(x) & 0x7FFFFF) | 0x3F800000)
|
|
77
|
-
log2(x) ≈ exponent +
|
|
76
|
+
mantissa = reinterpret_as_float((reinterpret_as_int(x) & 0x7FFFFF) | 0x3F800000)
|
|
77
|
+
log2(x) ≈ exponent + series_or_polynomial(mantissa)
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
This approach reinterprets the float as an integer, shifts out the mantissa bits to obtain the exponent, then masks and recombines to produce a normalized mantissa in $[1, 2)$.
|
|
81
|
+
The Haswell kernels then reuse the same $s$-series as Skylake, while the NEON kernels evaluate a degree-5 polynomial in the mantissa scaled by $m - 1$.
|
|
81
82
|
It works on any ISA with integer-float reinterpretation and avoids the need for specialized exponent/mantissa instructions.
|
|
83
|
+
The SIMD kernels scale the accumulated $\log_2$ sum by $\ln 2$ before returning, while the serial ones call a natural-log helper directly — either way the reported divergence is in nats, not bits.
|
|
82
84
|
|
|
83
85
|
### Kahan Compensated Summation for Float64
|
|
84
86
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
#include "numkong/types.h"
|
|
16
16
|
#include "numkong/spatial/haswell.h" // `nk_f32_sqrt_haswell`, `nk_f64_sqrt_haswell`
|
|
17
|
-
#include "numkong/spatial/skylake.h"
|
|
17
|
+
#include "numkong/spatial/skylake.h"
|
|
18
18
|
|
|
19
19
|
#if defined(__cplusplus)
|
|
20
20
|
extern "C" {
|