# TensorProductBasisT2


Tensor product basis with full penalty decomposition (`t2()`-style interaction smooth).


Usage

``` python
TensorProductBasisT2(marginals)
```


[TensorProductBasisT2](TensorProductBasisT2.md#whittaker.TensorProductBasisT2) builds exactly the same basis matrix as [TensorProductBasis](TensorProductBasis.md#whittaker.TensorProductBasis) (`te()`) -- the row-wise Kronecker product of the marginal bases -- but replaces `te()`'s [d](TPRS.md#whittaker.TPRS.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](TensorProductBasisT2.md#whittaker.TensorProductBasisT2) over plain [TensorProductBasis](TensorProductBasis.md#whittaker.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](TPRS.md#whittaker.TPRS.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](TPRS.md#whittaker.TPRS.d) penalties of [TensorProductBasis](TensorProductBasis.md#whittaker.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](TPRS.md#whittaker.TPRS.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](TensorProductBasis.md#whittaker.TensorProductBasis), since the basis matrix itself does not change -- only the penalty is decomposed further.


## Examples


``` python
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)
```


    ((100, 30), 3)


## Methods

| Name | Description |
|----|----|
| [penalty_matrices()](#penalty_matrices) | Return one penalty per non-empty subset of marginal directions. |

------------------------------------------------------------------------


### penalty_matrices()


Return one penalty per non-empty subset of marginal directions.


Usage

``` python
penalty_matrices()
```


#### Returns


`list[NDArray]`  
List of `2^d - 1` matrices, each of shape `(k, k)` where [d](TPRS.md#whittaker.TPRS.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).
