Source code for pymc_extras.inference.INLA.inla

import warnings

import pymc as pm

from pytensor.tensor import TensorLike, TensorVariable, as_tensor
from xarray import DataTree

from pymc_extras.model.marginal.marginalize import marginalize


[docs] def fit_INLA( x: TensorVariable, Q: TensorLike, minimizer_seed: int = 42, model: pm.Model | None = None, minimizer_kwargs: dict = {"method": "L-BFGS-B", "optimizer_kwargs": {"tol": 1e-8}}, return_latent_posteriors: bool = False, **sampler_kwargs, ) -> DataTree: r""" Performs inference over a linear mixed model using Integrated Nested Laplace Approximations (INLA). Assumes a model of the form: .. math:: \theta \rightarrow x \rightarrow y Where the prior on the hyperparameters :math:`\pi(\theta)` is arbitrary, the prior on the latent field is Gaussian (and in precision form): :math:`\pi(x) = N(\mu, Q^{-1})` and the latent field is linked to the observables $y$ through some linear map. As it stands, INLA in PyMC Extras is currently experimental. Parameters ---------- x: TensorVariable The latent gaussian to marginalize out. Q: TensorLike Precision matrix of the latent field. minimizer_seed: int Seed for random initialisation of the minimum point x*. Currently unused — the initialization is deterministic. model: pm.Model PyMC model. minimizer_kwargs: Kwargs to pass to pytensor.optimize.minimize during the optimization step maximizing logp(x | y, params). returned_latent_posteriors: If True, also return posteriors for the latent Gaussian field (currently unsupported). sampler_kwargs: Kwargs to pass to pm.sample. Returns ------- DataTree The inference data containing the results of the INLA algorithm. Examples -------- .. code:: ipython In [1]: rng = np.random.default_rng(123) ...: n = 10000 ...: d = 3 ...: mu_mu = 10 * rng.random(d) ...: mu_true = rng.random(d) ...: tau = np.identity(d) ...: cov = np.linalg.inv(tau) ...: y_obs = rng.multivariate_normal(mean=mu_true, cov=cov, size=n) In [2]: with pm.Model() as model: ...: mu = pm.MvNormal("mu", mu=mu_mu, tau=tau) ...: x = pm.MvNormal("x", mu=mu, tau=tau) ...: y = pm.MvNormal("y", mu=x, tau=tau, observed=y_obs) ...: idata = pmx.fit( ...: method="INLA", ...: x=x, ...: Q=tau, ...: return_latent_posteriors=False, ...: ) In[3]: posterior_mean_true = (mu_mu + mu_true) / 2 ...: posterior_mean_inla = idata.posterior.mu.mean(axis=(0, 1)).values ...: print(posterior_mean_true) ...: print(posterior_mean_inla) Out[3]: [3.50394522 0.35705804 1.50784662] [3.48732847 0.35738072 1.46851421] """ warnings.warn( "INLA is currently experimental. Please see the INLA Roadmap for more info: https://github.com/pymc-devs/pymc-extras/issues/340.", UserWarning, ) model = pm.modelcontext(model) # Marginalize out the latent field marginal_model = marginalize( model, laplace_approx={x: as_tensor(Q)}, minimizer_kwargs=minimizer_kwargs, ) # Sample over the hyperparameters if not return_latent_posteriors: idata = pm.sample(model=marginal_model, **sampler_kwargs) return idata # Unmarginalize stuff raise NotImplementedError( "Inference over the latent field with INLA is currently unsupported. Set return_latent_posteriors to False" )