Skip to content

feat(kernels): nonstationary (VaryingAmplitude, Gibbs) and space–time (Gneiting) kernels - #815

Open
thomaspinder wants to merge 2 commits into
mainfrom
feat/nonstationary-kernels
Open

thomaspinder wants to merge 2 commits into
mainfrom
feat/nonstationary-kernels

Conversation

@thomaspinder

Copy link
Copy Markdown
Collaborator

Summary

Three kernels for climate fields, as agreed in the design on #812. Fixes #812.

 gpjax/kernels/
+├── location_functions.py        # AbstractLocationFunction, Constant, Linear
 ├── nonstationary/
+│   ├── varying_amplitude.py     # k = σ(x) σ(y) k0(x, y)
+│   └── gibbs.py                 # lengthscale ℓ(x) via input scaling of k0
 ├── stationary/
+│   ├── gneiting.py              # nonseparable space–time kernel
 │   └── base.py                  # + isotropic_radial: ClassVar[bool]
 └── approximations/rff.py        # clearer error for kernels with no spectral density
+GLOSSARY.md
+docs/adr/0001-location-functions.md
+docs/examples/nonstationary_terrain.py
+docs/examples/spacetime_temperature.py

A location function gives a kernel parameter that changes with location. It evaluates one point, selects its own columns with active_dims, and returns a log value. It is a separate type from mean functions; ADR 0001 gives the reason.

kernel = gpx.kernels.VaryingAmplitude(
    gpx.kernels.Gibbs(
        gpx.kernels.Matern32(active_dims=[0, 1]),                    # distance columns
        lengthscale=location_functions.Linear(active_dims=[2]),      # covariate column
    ),
    amplitude=location_functions.Linear(active_dims=[2]),
)
kernel = gpx.kernels.Gneiting(space_dims=[1, 2], time_dim=0)

Gibbs needs no refactor of the stationary kernels:

gibbs(x, y)
  a, b   = ℓ-function(x), ℓ-function(y)           # log lengthscales
  c      = sqrt(2 / (e^{2a} + e^{2b}))
  return  (2 e^{a+b} / (e^{2a} + e^{2b}))^{d/2} * base_kernel(c·x, c·y)

vmap batches only the operations that depend on the mapped input, so a location function runs once for each row ($N + M$ times for an $N \times M$ matrix), also inside sum and product kernels.

Evidence

Tests: uv run poe test gives 3304 passed and 1 skipped. Lint and doctests pass. The CI docs build (-E -W) gives 0 warnings.

  • Gibbs matches a direct Paciorek–Schervish formula with an ARD Matérn-3/2 base, to 1e-15.
  • Hypothesis checks that the Gram matrices are positive definite for random weights and parameters.
  • A constant location function gives exactly the base kernel.
  • Gneiting with β = 0 equals the separable product. At lag 0 it equals a powered exponential kernel.
  • Invalid bases and invalid columns raise errors. jit, grad and fit work.

Colorado precipitation normals, 247 stations (5-fold cross-validation):

Model Log ML Held-out NLPD 90% coverage, mountains / plains RMSE (log)
Before: stationary −283.1 1.095 0.82 / 0.99 0.196
After: Gibbs −217.9 0.876 0.87 / 0.82 0.174

Europe, daily temperature anomalies, January–February 2019 (held-out gap of 140 points):

Model Log ML Held-out NLPD RMSE (K)
Before: separable (β = 0) −1845.0 1.033 2.17
After: nonseparable (β = 1.00) −1799.4 0.996 2.09

On Irish wind (Haslett & Raftery, 1989), the profile likelihood over β was flat (Δ ≈ 1.5), because the interaction there comes from advection, which a symmetric kernel cannot model. The notebook states this limit.

Merge Danger

Door: two-way

All the changes add new API. The only change to existing behaviour is the text of the RFF TypeError, and RFF's base_kernel parameter annotation, which is now AbstractKernel so that the clear message is reached under beartype.

Blast Radius: small

Follow-up: #813 (constrain PoweredExponential.power).

🤖 Generated with Claude Code

thomaspinder and others added 2 commits October 4, 2026 16:30
…e kernels

Add three kernels for climate fields (#812):

- VaryingAmplitude(base_kernel, amplitude): sigma(x) sigma(y) k0(x, y).
- Gibbs(base_kernel, lengthscale): the Paciorek-Schervish construction. It
  scales the inputs of an existing isotropic radial kernel, so all radial
  kernels and their ARD lengthscales work without a refactor. A new
  `isotropic_radial` class flag marks the valid base kernels.
- Gneiting(space_dims, time_dim): the nonseparable space-time kernel of
  Gneiting (2002, eq. 14), with interaction parameter beta.

The new gpjax.kernels.location_functions module (Constant, Linear and an
abstract base class) gives parameters that change with location. They are a
separate type from mean functions; ADR 0001 records why.

RFF now names the kernel that it cannot approximate and points users of
sample_approx to the predictive distribution.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…examples

- Nonstationary Kernels over Complex Terrain: Colorado precipitation normals
  1991-2020 (NOAA, public domain) with elevation as the covariate. Gibbs
  improves the log marginal likelihood from -283 to -218 and the held-out
  NLPD from 1.10 to 0.88.
- Space-Time Modelling of Winter Temperature: daily NCEP-NCAR R1 temperature
  anomalies over Europe, Jan-Feb 2019. The nonseparable Gneiting kernel
  improves the log marginal likelihood by 45.6 and the held-out NLPD from
  1.033 to 0.996 against its separable version.

Both datasets are small netCDF3 files made by new functions in
_pull_reference_datasets.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown

📖 Docs preview: https://pr-815--endearing-crepe-c2d5fe.netlify.app

Smoke render — the expensive notebooks run with reduced budgets, so
figures are not publication fidelity. /render-mode.txt says smoke.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci Continuous Integration documentation Improvements or additions to documentation enhancement New feature or request examples kernels release size/xl tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(kernels): nonstationary kernels for climate data (amplitude, Gibbs/Paciorek, Gneiting)

1 participant