mixle.stats.combinator.copula module

Copula combinator: glue arbitrary marginal distributions to a dependence structure (Sklar’s theorem).

GaussianCopulaDistribution (mixle.stats.multivariate) models only dependence, on the unit cube (0,1)^d – it assumes its inputs are already the uniform scores u_i. This combinator is the piece that makes copulas composable: give it your marginals (any mixle leaves exposing cdf – a Gamma, a StudentT, a VonMises, mixed freely) and a copula core, and it forms the joint

f(x_1, …, x_d) = c(F_1(x_1), …, F_d(x_d)) * prod_i f_i(x_i) (Sklar)

where each F_i = marginals[i].cdf is the probability-integral transform (PIT) and c is the copula density. That is the whole point of copulas: pick any marginals you like and couple them through one dependence object, instead of hand-writing a bespoke multivariate leaf for every marginal combination.

Estimation is IFM (Inference Functions for Margins): fit each marginal on its own column, PIT the data through the fitted marginals, then fit the copula on the uniform scores. This is exact in a single M-step (the accumulator buffers the raw columns, the same pattern the neural leaves use), not an approximate coupled iteration.

The copula core is pluggable: any distribution on (0,1)^d implementing the mixle five-piece contract works. GaussianCopulaDistribution is the first supported core; Clayton/Frank/t cores can be dropped in later with no change here.

Reference: Nelsen, An Introduction to Copulas (2nd ed., Springer, 2006); Joe, Dependence Modeling with Copulas (CRC, 2014).

class CopulaDistribution(marginals, copula, name=None, keys=None)[source]

Bases: SequenceEncodableProbabilityDistribution

Joint over d scalar fields = d marginals coupled by a copula core (Sklar’s theorem).

marginals is a length-d sequence of mixle leaves, each exposing log_density, cdf, a sampler() with sample(), and the estimator/encoder contract. copula is a distribution on (0,1)^d (e.g. GaussianCopulaDistribution). An observation is a length-d tuple/array of scalars (x_1, ..., x_d).

Parameters:
  • marginals (Sequence[SequenceEncodableProbabilityDistribution])

  • copula (SequenceEncodableProbabilityDistribution)

  • name (str | None)

  • keys (str | None)

density(x)[source]

Return the joint density at one raw observation.

Parameters:

x (Sequence[float])

Return type:

float

log_density(x)[source]

Sklar’s decomposition: sum of marginal log-densities + the copula log-density at the PIT scores.

Parameters:

x (Sequence[float])

Return type:

float

seq_log_density(enc)[source]

Vectorized: per-column marginal log-densities (summed) plus the copula log-density at the PIT scores.

Parameters:

enc (Any)

Return type:

ndarray

sampler(seed=None)[source]

Return a sampler that draws copula scores and inverts the marginals.

Parameters:

seed (int | None)

Return type:

CopulaSampler

estimator(pseudo_count=None)[source]

Return an IFM estimator for marginals followed by the copula core.

Parameters:

pseudo_count (float | None)

Return type:

CopulaEstimator

dist_to_encoder()[source]

Return an encoder that preserves marginal encodings and raw columns.

Return type:

CopulaDataEncoder

class CopulaSampler(dist, seed=None)[source]

Bases: DistributionSampler

Sample by drawing uniform scores from the copula, then inverting each marginal by its quantile.

Requires each marginal’s sampler to expose quantile OR the marginal itself to expose ppf/quantile; falls back to inverse-CDF root finding on cdf when neither is present, so any cdf-bearing marginal is sampleable.

Parameters:
  • dist (CopulaDistribution)

  • seed (int | None)

sample(size=None)[source]

Draw one joint observation or size iid observations.

Parameters:

size (int | None)

Return type:

Any

class CopulaDataEncoder(marginal_encoders)[source]

Bases: DataSequenceEncoder

Encode a batch as (per-marginal encodings, raw (n, d) column array).

The raw columns ride along because the copula’s uniform scores u = F(x) depend on the marginals’ current parameters (they change during fitting), so they cannot be baked in at encode time – they are recomputed by CopulaDistribution._pit_columns() against whatever distribution is scoring/estimating.

Parameters:

marginal_encoders (Sequence[DataSequenceEncoder])

seq_encode(x)[source]

Encode each marginal column while retaining raw columns for PIT recomputation.

Parameters:

x (Sequence[Sequence[float]])

Return type:

tuple[tuple[Any, …], ndarray]

class CopulaAccumulator(marginal_accumulators, dim, keys=None)[source]

Bases: SequenceEncodableStatisticAccumulator

Delegate per-column sufficient stats to marginal sub-accumulators; buffer raw columns for the copula.

The copula stage is fit AFTER the marginals (IFM): it needs the marginals’ fitted CDFs to PIT the data, so the raw columns are buffered (weighted) here and PIT-ed inside CopulaEstimator.estimate(). This is the same buffer-the-rows pattern the neural leaves use, and it makes the IFM fit exact in one M-step.

Parameters:
  • marginal_accumulators (Sequence[Any])

  • dim (int)

  • keys (str | None)

update(x, weight, estimate)[source]

Update marginal accumulators and buffer one raw observation for IFM copula fitting.

Parameters:
Return type:

None

initialize(x, weight, rng)[source]

Initialize marginal accumulators and buffer one raw observation.

Parameters:
Return type:

None

seq_update(enc, weights, estimate)[source]

Update marginal accumulators and buffer encoded raw columns for IFM.

Parameters:
  • enc (Any)

  • weights (ndarray)

  • estimate (CopulaDistribution | None)

Return type:

None

seq_initialize(enc, weights, rng)[source]

Initialize marginal accumulators and buffer encoded raw columns.

Parameters:
Return type:

None

combine(suff_stat)[source]

Merge marginal sufficient statistics and buffered raw columns.

Parameters:

suff_stat (tuple[tuple[Any, ...], ndarray, ndarray])

Return type:

CopulaAccumulator

value()[source]

Return marginal stats, buffered raw columns, and buffered weights.

Return type:

tuple[tuple[Any, …], ndarray, ndarray]

from_value(x)[source]

Restore marginal stats and raw-column buffers from value output.

Parameters:

x (tuple[tuple[Any, ...], ndarray, ndarray])

Return type:

CopulaAccumulator

key_merge(stats_dict)[source]

Delegate keyed merges to marginal accumulators.

Parameters:

stats_dict (dict[str, Any])

Return type:

None

key_replace(stats_dict)[source]

Delegate keyed replacements to marginal accumulators.

Parameters:

stats_dict (dict[str, Any])

Return type:

None

acc_to_encoder()[source]

Return an encoder composed from the marginal accumulator encoders.

Return type:

CopulaDataEncoder

class CopulaAccumulatorFactory(marginal_factories, dim, keys=None)[source]

Bases: StatisticAccumulatorFactory

Create accumulators for IFM copula estimation.

Parameters:
  • marginal_factories (Sequence[Any])

  • dim (int)

  • keys (str | None)

make()[source]

Create an empty copula accumulator.

Return type:

CopulaAccumulator

class CopulaEstimator(marginal_estimators, copula_estimator, dim, name=None, keys=None)[source]

Bases: ParameterEstimator

IFM estimator: fit each marginal from its sub-stats, PIT the buffered data, fit the copula on the scores.

Parameters:
  • marginal_estimators (Sequence[ParameterEstimator])

  • copula_estimator (ParameterEstimator)

  • dim (int)

  • name (str | None)

  • keys (str | None)

accumulator_factory()[source]

Return a factory for IFM copula sufficient-statistic accumulators.

Return type:

CopulaAccumulatorFactory

estimate(nobs, suff_stat)[source]

Estimate marginals, transform buffered data by PIT, and estimate the copula core.

Parameters:
Return type:

CopulaDistribution

copula_estimator_prototype()[source]

A copula instance usable for PIT-encoding before the copula is refit – the estimator’s own default.

Return type:

SequenceEncodableProbabilityDistribution