Tensor product basis with full penalty decomposition (t2()-style interaction smooth).
TensorProductBasisT2(marginals)
TensorProductBasisT2 builds exactly the same basis matrix as TensorProductBasis (te()) — the row-wise Kronecker product of the marginal bases — but replaces te()’s d per-direction penalties with a richer decomposition that has one penalty for every non-empty subset of the marginal directions, following the “type 2” tensor product construction of Wood, Scheipl and Faraway (2013). Because it uses ordinary quadratic penalties throughout (rather than the null-space/range-space projections used internally by te()), the resulting penalties are positive semi-definite by construction and combine cleanly with random-effects representations of the smooth, and it never has negative smoothing-parameter degeneracies that can occasionally affect te(). Prefer TensorProductBasisT2 over plain TensorProductBasis when a strictly additive quadratic-penalty structure is needed (e.g. for mixed-model / REML-based fitting), or when the interaction component of the surface is believed to need noticeably different smoothing from any single marginal direction on its own and a dedicated smoothing parameter for that pure-interaction subset is wanted.
Notes
For d marginals with penalties S_1, \ldots, S_d and basis dimensions k_1, \ldots, k_d, every non-empty subset \sigma \subseteq \{1, \ldots, d\} of marginal directions gets its own penalty
\mathbf{S}_\sigma = \mathbf{M}_1 \otimes \mathbf{M}_2 \otimes \cdots \otimes \mathbf{M}_d,
\qquad \mathbf{M}_j = \begin{cases} \mathbf{S}_j & j \in \sigma \\ \mathbf{I}_{k_j} & j \notin
\sigma \end{cases},
giving 2^d - 1 penalties in total (compared to the d penalties of TensorProductBasis). For two marginals with penalties S_1, S_2, this is
\{\, \mathbf{S}_1 \otimes \mathbf{I},\ \ \mathbf{I} \otimes \mathbf{S}_2,\ \ \mathbf{S}_1
\otimes \mathbf{S}_2 \,\},
the usual two te()-style main-effect-direction penalties plus one additional penalty, S_1 ⊗ S_2, that penalizes roughness jointly in both directions at once — this extra term is what lets the pure two-way-interaction component of the surface have its own smoothing parameter, separate from either marginal direction’s smoothness. As the number of marginals d grows, the number of penalties grows exponentially (2^d - 1), so this construction is practical mainly for d = 2 or d = 3; null_space_dimension() and identifiability_constraints() are inherited unchanged from TensorProductBasis, since the basis matrix itself does not change — only the penalty is decomposed further.
Examples
import numpy as np
from whittaker.smooths.tensor import TensorProductBasisT2
from whittaker.smooths.tprs import TPRS
rng = np.random.default_rng(0)
x = rng.uniform(0, 1, (100, 2))
basis = TensorProductBasisT2([TPRS(k=6), TPRS(k=5)]).fit(x)
B = basis.basis_matrix(x)
penalties = basis.penalty_matrices()
B.shape, len(penalties) # 3 penalties for 2 marginals (2^2 - 1)
Methods
|
Name
|
Description
|
|
penalty_matrices()
|
Return one penalty per non-empty subset of marginal directions.
|
penalty_matrices()
Return one penalty per non-empty subset of marginal directions.
Returns
list[NDArray]
-
List of
2^d - 1 matrices, each of shape (k, k) where d is the number of marginals and k is the total basis dimension. Ordered by increasing subset bitmask (single-direction penalties first, higher-order interaction penalties last).