Skip to main content

SmoothTerm

Struct SmoothTerm 

Source
pub struct SmoothTerm {
    pub name: String,
    pub coeff_range: Range<usize>,
    pub shape: ShapeConstraint,
    pub penalties_local: Vec<Array2<f64>>,
    pub nullspace_dims: Vec<usize>,
    pub penaltyinfo_local: Vec<PenaltyInfo>,
    pub metadata: BasisMetadata,
    pub lower_bounds_local: Option<Array1<f64>>,
    pub linear_constraints_local: Option<LinearInequalityConstraints>,
    pub kronecker_factored: Option<KroneckerFactoredBasis>,
    pub joint_null_rotation: Option<JointNullRotation>,
    pub unabsorbed_global_orthogonality: Option<Array2<f64>>,
}

Fields§

§name: String§coeff_range: Range<usize>§shape: ShapeConstraint§penalties_local: Vec<Array2<f64>>§nullspace_dims: Vec<usize>§penaltyinfo_local: Vec<PenaltyInfo>§metadata: BasisMetadata§lower_bounds_local: Option<Array1<f64>>

Optional term-local lower bounds for constrained coefficients. -inf means unconstrained.

§linear_constraints_local: Option<LinearInequalityConstraints>

Optional term-local inequality constraints in local coefficient coordinates. A_local * beta_local >= b_local.

§kronecker_factored: Option<KroneckerFactoredBasis>

Optional factored tensor-product representation preserved for operator-backed assembly in the main design builder.

§joint_null_rotation: Option<JointNullRotation>

Joint-null absorption rotation. Some(Q) records the orthonormal (p_local × p_local) matrix that was applied to this term’s design and per-block penalties at construction time: term_design ← X_raw · Q, penalties_local[k] ← Qᵀ · S_raw · Q. The smooth’s coefficient vector therefore lives in the rotated (γ) coordinate system, with β_raw = Q · γ recovering the raw pre-rotation parameterization. None means either no joint null space (penalty already full-rank) or rotation was suppressed — suppression fires when the smooth carries shape constraints (lower bounds or local linear inequalities) that would lose their cone geometry under a general orthogonal rotation.

Prediction-side replay: callers building a new-data design X_new_raw from the raw basis must call SmoothTerm::apply_rotation_to_predict (or equivalent) to obtain X_new = X_new_raw · Q matching this term’s coefficient system.

Persistence replay: freeze_term_collection_from_design copies this rotation into SmoothTermSpec, which is serialized with fitted-model payloads and reused by the predict-time basis builder. Saved models therefore replay the same X_new_raw · Q transform as in-memory prediction.

§unabsorbed_global_orthogonality: Option<Array2<f64>>

Global-orthogonality transform that apply_global_smooth_identifiability applied to this term’s design but could NOT embed into metadata (factor-smooth kinds: sz metadata is per-marginal, fs metadata has no transform slot — #978). freeze_term_collection_from_design copies it onto the term’s basis spec (frozen_global_orthogonality) so the predict-side rebuild replays it instead of emitting the unresidualized (wider) design that the fitted coefficients no longer match. Chart convention is per kind: post-Q Z for sz (the raw rebuild reapplies Q itself, #700), full Q·Z chart for fs.

Implementations§

Source§

impl SmoothTerm

Source

pub fn apply_rotation_to_predict( &self, x_new_raw: Array2<f64>, ) -> Result<Array2<f64>, BasisError>

Apply the joint-null absorption rotation to a raw new-data design matrix, returning X_new_raw · Q when this term was rotated at fit time, or X_new_raw unchanged when no rotation was applied.

Callers in the prediction path: after building the smooth’s basis at new data via the raw basis builder (the same builder used at fit time, applied to x_new instead of the training rows), call this method on the resulting matrix before forming X · β. The fitted β lives in γ-coordinates if Q was applied; multiplying the un-rotated X_new_raw by β would give a wrong η.

Returns an error if the raw design’s column count does not match the rotation’s p_local. The width invariant must hold: the raw basis builder MUST emit the same p_local columns that the fit-time builder did, and the rotation is (p_local × p_local).

Source

pub fn wald_unpenalized_dim(&self) -> usize

Dimension of the joint null space of this term’s active penalties: the coefficient directions penalized by no penalty. The smooth-component Wald test (crate::inference::smooth_test::wood_smooth_test) treats this many leading coefficients as genuine unpenalized fixed effects and tests them at full rank; the remainder is the penalized sub-block tested with a rank-≈EDF truncated pseudo-inverse.

Because every penalty block S_k is positive semi-definite, vᵀ(Σ_k S_k)v = Σ_k vᵀ S_k v = 0 iff S_k v = 0 for every k; the joint null space is therefore exactly null(Σ_k S_k), of dimension p_local − rank(Σ_k S_k). This is the intersection of the per-penalty null spaces, not their sum.

Summing the per-penalty nullspace_dims instead (the historical defect behind #1360) unions the null spaces and badly over-counts: a double-penalty smooth carries a bending penalty (null space = its polynomial part) plus a complementary null-space ridge (which penalizes exactly that polynomial part), so the two null spaces are disjoint and the joint null space is empty — yet the per-penalty dims sum to nearly p_local. Feeding that inflated count to the Wald test makes it test almost the whole shrunk block at full rank, manufacturing overwhelming “significance” for a term the fit drove to ~0 EDF.

Trait Implementations§

Source§

impl Clone for SmoothTerm

Source§

fn clone(&self) -> SmoothTerm

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SmoothTerm

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Allocation for T
where T: RefUnwindSafe + Send + Sync,

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> ByRef<T> for T

Source§

fn by_ref(&self) -> &T

Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DistributionExt for T
where T: ?Sized,

Source§

fn rand<T>(&self, rng: &mut (impl Rng + ?Sized)) -> T
where Self: Distribution<T>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Imply<T> for U
where T: ?Sized, U: ?Sized,

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V