xrtoolz.prob — API¶
Proposed public surface. Names track numpyro where possible to keep the user's mental model intact.
Imports¶
import xrtoolz.prob as xpr # Function API + distributions
from xrtoolz.prob import ( # Distributions
Normal, HalfNormal, LogNormal, StudentT, Cauchy,
Beta, Gamma, Exponential, Uniform,
Bernoulli, Categorical, Poisson, Binomial,
MultivariateNormal, Dirichlet,
)
from xrtoolz.prob import ( # Sampling / log-prob primitives
sample, log_prob, plate, deterministic, factor,
)
from xrtoolz.prob import ( # Pipeline operators
Sample, LogProb, PriorPredictive, PosteriorPredictive,
)
from xrtoolz.prob import ( # I/O
to_dataset, from_dataset,
)
from xrtoolz.prob import ( # pyrox re-exports
PyroxModule, PyroxParam, PyroxSample, Parameterized, pyrox_method,
)
import xrtoolz does not pull numpyro or pyrox.
Distributions¶
Each labeled distribution is a subclass of DistributionBridge and
exposes the same three methods plus distribution-specific
constructor kwargs.
Univariate¶
class Normal(DistributionBridge):
"""Normal distribution parameterized by loc and scale.
Args:
loc: mean. Scalar or DataArray.
scale: stddev. Scalar or DataArray.
Shapes:
batch_dims = union of loc.dims and scale.dims.
event_dims = ().
"""
def __init__(self, loc, scale): ...
class HalfNormal(DistributionBridge):
def __init__(self, scale): ...
class LogNormal(DistributionBridge):
def __init__(self, loc, scale): ...
class StudentT(DistributionBridge):
def __init__(self, df, loc=0.0, scale=1.0): ...
class Cauchy(DistributionBridge):
def __init__(self, loc, scale): ...
class Beta(DistributionBridge):
def __init__(self, concentration1, concentration0): ...
class Gamma(DistributionBridge):
def __init__(self, concentration, rate): ...
class Exponential(DistributionBridge):
def __init__(self, rate): ...
class Uniform(DistributionBridge):
def __init__(self, low, high): ...
Discrete¶
class Bernoulli(DistributionBridge):
def __init__(self, *, probs=None, logits=None): ...
class Categorical(DistributionBridge):
def __init__(self, *, probs=None, logits=None, dim: str = "category"): ...
# `dim` declares the name of the categorical event axis on probs/logits.
class Poisson(DistributionBridge):
def __init__(self, rate): ...
class Binomial(DistributionBridge):
def __init__(self, total_count, *, probs=None, logits=None): ...
Multivariate¶
class MultivariateNormal(DistributionBridge):
"""MVN parameterized by loc DataArray and covariance.
Args:
loc: mean DataArray. The trailing ``event_dims`` are the MVN
event axes.
covariance_matrix: NamedOperator (preferred) or DataArray
with row/col dims for a dense (N, N) covariance.
event_dims: tuple of dim names on ``loc`` that are the MVN
event axes. Remaining dims are batch.
"""
def __init__(
self,
loc: xr.DataArray,
*,
covariance_matrix: NamedOperator | xr.DataArray | None = None,
scale_tril: NamedOperator | xr.DataArray | None = None,
precision_matrix: NamedOperator | xr.DataArray | None = None,
event_dims: tuple[str, ...] | None = None,
): ...
class Dirichlet(DistributionBridge):
def __init__(self, concentration: xr.DataArray, *, dim: str = "category"): ...
Common methods¶
class DistributionBridge:
def sample(
self, key: jax.Array, *,
sample_shape: tuple[str, ...] | tuple[int, ...] = (),
) -> xr.DataArray:
"""Draw a labeled sample.
``sample_shape`` accepts ``("draw=100",)``-style tokens
(named, recommended) or ``(100,)``-style ints (numpyro
convention; auto-names the dim and warns once).
"""
def log_prob(self, x: xr.DataArray) -> jax.Array:
"""Element-wise log-prob; sums broadcast over event_dims.
``x.dims`` must include the distribution's batch_dims +
event_dims; extra dims are treated as sample dims.
"""
def to_numpyro(self) -> numpyro.distributions.Distribution:
"""Return the underlying numpyro distribution.
Use inside ``numpyro.sample`` / ``pyrox_sample`` / model
bodies where you want numpyro semantics.
"""
@property
def batch_dims(self) -> tuple[str, ...]: ...
@property
def event_dims(self) -> tuple[str, ...]: ...
@property
def batch_coords(self) -> dict[str, xr.DataArray]: ...
Module-level primitives¶
def sample(
name: str,
distribution: DistributionBridge,
*,
obs: xr.DataArray | None = None,
sample_shape: tuple[str, ...] = (),
) -> xr.DataArray:
"""Register a sample site with numpyro (labeled version).
Equivalent to ``numpyro.sample(name, distribution.to_numpyro(), obs=obs)``
when called inside a numpyro / pyrox model context; ``sample_shape``
is only honored outside a model context.
Returns a DataArray with the distribution's batch_dims + event_dims
+ (optional) sample_dims; the return value is also registered in the
numpyro trace if one is active.
"""
def deterministic(name: str, value: xr.DataArray) -> xr.DataArray: ...
def factor(name: str, log_factor: jax.Array) -> None: ...
@contextmanager
def plate(
name: str,
size_or_array: int | xr.DataArray | None = None,
*,
subsample_size: int | None = None,
) -> Iterator[numpyro.plate]: ...
Predictive helpers¶
def prior_predictive(
model: Callable,
key: jax.Array,
*,
num_samples: int = 1,
sample_dim: str = "draw",
dims_per_site: Mapping[str, tuple[str, ...]] | None = None,
**model_kwargs: Any,
) -> xr.Dataset:
"""Draw from the prior predictive of a numpyro / pyrox model.
Wraps ``numpyro.infer.Predictive``; converts the result to a
Dataset via ``to_dataset``.
"""
def posterior_predictive(
model: Callable,
posterior_samples: Mapping[str, jax.Array] | xr.Dataset,
key: jax.Array,
*,
sample_dim: str = "draw",
dims_per_site: Mapping[str, tuple[str, ...]] | None = None,
**model_kwargs: Any,
) -> xr.Dataset: ...
I/O¶
def to_dataset(
samples: Mapping[str, jax.Array],
*,
dims_per_site: Mapping[str, tuple[str, ...]],
coords: Mapping[str, xr.DataArray] | None = None,
chain_dim: str = "chain",
draw_dim: str = "draw",
) -> xr.Dataset:
"""Convert raw numpyro samples to an xarray Dataset."""
def from_dataset(
ds: xr.Dataset,
*,
chain_dim: str = "chain",
draw_dim: str = "draw",
) -> dict[str, jax.Array]:
"""Inverse: Dataset -> {site: jax_array}.
Useful for feeding posterior samples back into
``posterior_predictive`` after manipulating them in xarray.
"""
Layer-1 operators¶
| Function | Operator | Notes |
|---|---|---|
dist.sample |
Sample |
Generator op; no input. Constructor takes dist + key + sample_shape. |
dist.log_prob |
LogProb |
Single-input; returns 0-d DataArray. |
prior_predictive |
PriorPredictive |
Generator op; runs the model under Predictive. |
posterior_predictive |
PosteriorPredictive |
Generator op; takes a posterior Dataset as init kwarg. |
pyrox re-exports¶
The full pyrox _core API is re-exported from xrtoolz.prob:
These are direct re-exports — no wrapping. Users who already know pyrox get the same surface; users who came in via xrtoolz get a single namespace for the probabilistic stack.
Exception hierarchy¶
class ProbBridgeError(Exception):
"""Base for xrtoolz.prob errors."""
class CoordMismatch(ProbBridgeError):
"""Two parameter DataArrays disagree on coords for a shared dim."""
class DimMismatch(ProbBridgeError, ValueError):
"""log_prob input dims don't match distribution batch + event dims."""
class PlateMisuse(ProbBridgeError, ValueError):
"""Plate name doesn't match any inner distribution's dims."""
Upstream numpyro / pyrox / JAX exceptions propagate unchanged.
What this API explicitly does not include¶
- arviz
InferenceDataconverters.to_datasetproduces a plain xarray Dataset; arviz can ingest it. Promote to first-class if users ask. - MCMC / SVI wrappers. Users call
MCMC(NUTS(model)).run(...)directly; the bridge converts the inputs (DataArrays) and outputs (raw samples). - A
Distributionbase class users subclass to add new distributions. Users who need a custom distribution write a numpyroDistributionand wrap withto_numpyro-compatible glue; we don't ship a custom-distribution kit. - Custom guides. numpyro AutoGuides + pyrox
Parameterizedcover the guide story. The bridge doesn't reimplement guide construction.