1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//! Per-axis input standardization and length-scale compensation helpers for
//! the spatial smooth arm.
//!
//! Pure numeric helpers relocated verbatim from `smooth.rs` (issue #780
//! decomposition): per-column variance scales, in-place standardization, the
//! geometric-mean scale, and the kernel length-scale compensation maps that
//! keep the Matérn/Duchon/thin-plate range in original coordinates after
//! standardization. No behavior change — bodies are byte-identical and the
//! parent re-imports each name so every call site is unchanged.
use ;
/// Compute per-column standard deviations for spatial inputs.
///
/// Standardizing each covariate axis to unit spread makes the
/// Matérn/Duchon/thin-plate kernel — and the `ψ = log κ = −log ℓ` REML
/// length-scale optimizer that refines it — operate in scale-free coordinates,
/// so the fit is invariant to an affine covariate rescale `x → a·x + b`. This
/// matters in **one dimension too** (issue #1215): a 1-D `s(x, bs="tp")` whose
/// kernel ran in raw covariate units seeded and bounded its `ψ`-optimizer off
/// the raw magnitude, landing in a scale-dependent basin (a clean bimodal step
/// across `|a| ⋛ 1`). Standardizing the single axis the same way as the d > 1
/// axes removes that magnitude from the optimizer's view, so the selected `ψ̂`
/// (hence the fitted curve) is scale-invariant. The frozen scale is replayed at
/// predict, so original-unit queries map onto the same standardized geometry.
///
/// Returns `None` only when there is no axis or too few rows to estimate a
/// spread, or when the caller already supplies frozen scales (prediction path).
pub
/// Apply per-column standardization to a data matrix using precomputed scales.
pub
/// Geometric mean of strictly positive scales: `(∏ s_a)^(1/d)`.
///
/// Computed via log-sum-divide to avoid overflow / underflow when d is large
/// or when individual scales are small. The Matérn / Duchon / thin-plate
/// auto-standardization paths use this to compensate the user's
/// `length_scale` so the kernel range remains expressed in *original* data
/// coordinates after per-axis division by σ_a:
///
/// ‖x_std − c_std‖ / L_eff with L_eff = L_user / σ_geom
///
/// matches `‖x − c‖ / L_user` exactly for uniform σ_a (= σ_geom) and reduces
/// to the natural anisotropic-Mahalanobis preconditioning when σ_a vary —
/// the convention σ_geom = (∏σ_a)^(1/d) preserves the kernel volume scale.
pub
pub