Bayesian inference and probabilistic modeling for geospatial data. Use when building hierarchical models, computing posteriors with PyMC or TFP, performing variational inference, model comparison (LOO/WAIC/DIC), or spatial Gaussian processes.
Resources
13Install
npx skillscat add activeinferenceinstitute/geo-infer/geo-infer-bayes Install via the SkillsCat registry.
GEO-INFER-BAYES
Instructions
Core Capabilities
- Bayesian inference: Full posterior computation via MCMC and variational methods
- Model comparison: LOO-CV, WAIC, DIC, BIC, AIC (all real implementations)
- Gaussian processes: Exact Cholesky and batched inducing-point variational GPs
- Hierarchical models: Partial pooling via Cholesky LKJ decomposition
- Prior specification: Jeffreys, reference, unit-information priors
- ELBO computation: Real evidence lower bound (not placeholder)
Key Imports
from geo_infer_bayes.core.inference import BayesianInference
from geo_infer_bayes.core.model_comparison import ModelComparison
from geo_infer_bayes.models.spatial_gp import SparseSpatialGP, SpatialGP
from geo_infer_bayes.core.variational import VariationalInference
from geo_infer_bayes.utils.diagnostics import mcmc_diagnostics
from geo_infer_bayes.api.pymc_interface import PyMCInterface
from geo_infer_bayes.api.tfp_interface import TFPInterfaceExamples
Every snippet below runs against the current API.
Fit a spatial Gaussian process by MCMC. BayesianInference.run hands the data
back to the model afterwards, so the returned posterior can predict without a
separate fit call:
from geo_infer_bayes.core.inference import BayesianInference
from geo_infer_bayes.models.spatial_gp import SpatialGP
model = SpatialGP(kernel="rbf", lengthscale=1.0, variance=1.0, noise=0.5)
inference = BayesianInference(
model=model, method="mcmc", sampler_config={"n_chains": 4, "random_seed": 0}
)
posterior = inference.run(
data={"X": X, "y": y}, n_samples=2000, n_warmup=1000, progress_bar=False
)
print(posterior.summary()[["mean", "sd", "r_hat"]])Fit a large spatial dataset without constructing an observation-by-observation
covariance matrix. Supplied inducing locations are fixed; omit them to select a
deterministic maximin subset. fit optimizes the collapsed variational ELBO by
default:
from geo_infer_bayes.models import SparseSpatialGP
model = SparseSpatialGP(
inducing_points=inducing_locations,
kernel="matern",
degree=1.5,
batch_size=2048,
)
model.fit(X, y)
mean, std = model.predict(X_new, return_std=True)
print(model.elbo_)Convergence diagnostics. Samplers return draws pooled across chains, and R-hat
is a between-chain statistic, so split the chain axis back out first:
from geo_infer_bayes.utils.diagnostics import mcmc_diagnostics
diagnostics = mcmc_diagnostics(posterior.chain_samples())
worst_r_hat = max(stats["r_hat"] for stats in diagnostics.values())Predict with calibrated uncertainty. The returned std is the total predictive
standard deviation of the latent function -- the mean conditional GP variance
across posterior draws plus the variance of the per-draw means. It excludes
observation noise, which belongs to posterior_predictive:
mean, std = model.predict(X_new, posterior=posterior, samples=200, return_std=True)
draws = model.posterior_predictive(posterior, X=X_new, samples=500, random_seed=0)Model comparison by LOO. A GP likelihood is joint, sopointwise_log_likelihood supplies the per-observation terms LOO needs, via the
ordered-conditional decomposition of the marginal likelihood:
import numpy as np
from geo_infer_bayes.core.model_comparison import ModelComparison
names = ["lengthscale", "variance", "noise"]
draws = np.linspace(0, len(posterior.samples["noise"]) - 1, 100, dtype=int)
matrix = np.asarray([
model.pointwise_log_likelihood(
{name: float(posterior.samples[name][i]) for name in names},
{"X": X, "y": y},
)
for i in draws
])
results = ModelComparison([model]).compare_models(
{"log_likelihood_matrix": matrix}, method="loo", random_seed=0
)Reproducibility
Every sampler and predictive method takes a random_seed routed throughgeo_infer_bayes.utils.rng.resolve_rng, which accepts None, an int, aSeedSequence, a BitGenerator, a numpy.random.Generator, or a legacyRandomState, and always returns a Generator. Consequences worth knowing:
- Passing an
intmakes a chain replayable;0is a valid seed. - Passing a
Generatorthreads one stream through a whole pipeline. Nonemeans OS entropy, so results are not replayable. Callingnp.random.seed(...)does not make them so: this module never reads the
process-wide singleton, and never advances it either.- For independent parallel chains use
geo_infer_bayes.utils.rng.spawn_rng(seed, n)rather thanseed,seed + 1,
... which carries no independence guarantee. - At boundaries that accept only an
intseed, such as scikit-learn'srandom_state, usegeo_infer_bayes.utils.rng.derive_int_seed.
Guidelines
- GP uses actual Cholesky decomposition
- Sparse GP fit uses batched
NbyMkernel blocks andMbyM
sufficient statistics; it never builds a denseNbyNcovariance - TFP interface: real GP + Metropolis-Hastings sampling
- PyMC interface: posterior predictive sampling for predictions
- Variational: real ELBO computation with KL divergence
- Posterior predictions average over draws spread across the chain, not the
first N draws; the count comes from the draw axis, not from the number of
parameter names - LOO and WAIC need a posterior pointwise log-likelihood matrix. Passing data
without one falls back to prior draws, which is not cross-validation and whose
elpd is not comparable with a posterior-based one - Test:
uv run python -m pytest GEO-INFER-BAYES/tests/ -v
Integrations
- ACT → Active Inference belief updating and free energy
- MATH → Spatial statistics feeding Bayesian models
- SPM → Bayesian GLM fitting for parametric maps
- AI → Bayesian hyperparameter optimization
- RISK → Bayesian uncertainty quantification for risk