# ShrinkageCRS


Shrinkage Cubic Regression Spline.


Usage

``` python
ShrinkageCRS(k=10)
```


Equivalent to mgcv's `bs="cs"` basis. [CRS](CRS.md#whittaker.CRS) penalizes only the curvature of a natural cubic spline, leaving its 2-dimensional null space of constant and linear functions completely free -- a large smoothing parameter flattens the fit to a line, but never removes it from the model. [ShrinkageCRS](ShrinkageCRS.md#whittaker.ShrinkageCRS) adds a second penalty, built directly from the eigenstructure of the ordinary CRS penalty, that specifically targets this null space. Following the double-penalty approach of Marra & Wood (2011), the two penalties are given independent smoothing parameters during fitting, so that both the wiggly and the linear/constant parts of the term can be shrunk simultaneously, letting the term drop out of the model entirely. Prefer [ShrinkageCRS](ShrinkageCRS.md#whittaker.ShrinkageCRS) over plain [CRS](CRS.md#whittaker.CRS) for univariate terms whose presence in the model is uncertain and where automatic selection via GCV or REML is preferred over an explicit inclusion/exclusion test.

The basis functions are identical to [CRS](CRS.md#whittaker.CRS) (`bs="cr"`); only the penalty structure differs.


## Parameters


`k: int = ``10`  
Number of basis functions (equal to the number of knots). Must be at least `3`. The default is `10`. See [CRS](CRS.md#whittaker.CRS) for guidance on choosing `k`.


## Notes

[ShrinkageCRS](ShrinkageCRS.md#whittaker.ShrinkageCRS) reuses the exact basis construction of [CRS](CRS.md#whittaker.CRS): `k` knots at evenly-spaced quantiles of the training data, with the design matrix built from natural-cubic-spline basis functions (see [CRS](CRS.md#whittaker.CRS) for the full construction). What changes is the penalty. The ordinary CRS penalty is

 \mathbf{S}\_{\text{wiggle}} = \mathbf{Q} \mathbf{R}^{-1} \mathbf{Q}^\top, 

which is positive semi-definite with rank `k - 2` and a 2-dimensional null space spanned by the constant and linear functions evaluated at the knots. [ShrinkageCRS](ShrinkageCRS.md#whittaker.ShrinkageCRS) eigendecomposes `S_wiggle`, identifies the eigenvectors `U_null` whose eigenvalues are numerically zero (below `1e-10` times the largest eigenvalue), and builds a second penalty matrix from their outer product:

 \mathbf{S}\_{\text{null}} = \mathbf{U}\_{\text{null}} \mathbf{U}\_{\text{null}}^\top. 

`S_null` is positive semi-definite with rank `2`, non-zero exactly on the subspace that `S_wiggle` leaves unpenalized. During fitting each matrix is scaled by its own smoothing parameter and the two contributions are summed:

 \lambda\_{\text{wiggle}} \\ \boldsymbol{\beta}^\top \mathbf{S}\_{\text{wiggle}} \boldsymbol{\beta} + \lambda\_{\text{null}} \\ \boldsymbol{\beta}^\top \mathbf{S}\_{\text{null}} \boldsymbol{\beta}. 

Because `S_wiggle + S_null` is strictly positive definite, the combined penalty has no null space, so `null_space_dimension()` returns `0`: none of the `k` basis functions escapes penalization once both smoothing parameters are positive.


## Examples


``` python
import numpy as np
from whittaker.smooths import ShrinkageCRS

rng = np.random.default_rng(0)
x = rng.uniform(0, 1, 100)

basis = ShrinkageCRS(k=10).fit(x)
B = basis.basis_matrix(x)
S_wiggle, S_null = basis.penalty_matrices()
B.shape, S_wiggle.shape, S_null.shape
```


    ((100, 10), (10, 10), (10, 10))


## Methods

| Name | Description |
|----|----|
| [null_space_dimension()](#null_space_dimension) | Return `0`: the combined double penalty leaves no unpenalized null space. |
| [penalty_matrices()](#penalty_matrices) | Return the two penalty matrices used for shrinkage. |

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


### null_space_dimension()


Return `0`: the combined double penalty leaves no unpenalized null space.


Usage

``` python
null_space_dimension()
```


#### Returns


`int`  
Always `0` for [ShrinkageCRS](ShrinkageCRS.md#whittaker.ShrinkageCRS), since `S_wiggle + S_null` is positive definite.


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


### penalty_matrices()


Return the two penalty matrices used for shrinkage.


Usage

``` python
penalty_matrices()
```


#### Returns


`list[NDArray]`  
A two-element list `[S_wiggle, S_null]`, each of shape `(k, k)`. `S_wiggle` is the ordinary [CRS](CRS.md#whittaker.CRS) wiggliness penalty `Q R^{-1} Q'`. `S_null` is a projection penalty built from the eigenvectors of `S_wiggle` with (numerically) zero eigenvalue, targeting the constant and linear null space. Each matrix is intended to be paired with its own independently-estimated smoothing parameter during fitting.
