mixle.stats.matrix.matrix_normal module

Matrix normal distribution MN(M, U, V) – a distribution over n-by-p real matrices.

The matrix normal is the Gaussian on matrices with a separable (Kronecker) covariance: a draw X has mean matrix M (n, p), an among-row covariance U (n, n) and an among-column covariance V (p, p), and vec(X) ~ N(vec(M), V (x) U) (column-stacking vec). Equivalently X = M + A Z B^T with U = A A^T, V = B B^T and Z standard normal. Its density is

log p(X) = -np/2 log(2pi) - n/2 log|V| - p/2 log|U| - 1/2 tr(U^{-1} (X-M) V^{-1} (X-M)^T).

U and V are identifiable only through their Kronecker product U (x) V (scaling U by c and V by 1/c is the same law), so the estimator anchors V[0,0] = 1. It is fit by the standard flip-flop MLE: alternate the closed-form updates of U given V and V given U to convergence – here from a fixed sufficient statistic (the row-blocked second moment), so it converges inside a single estimate call.

Reference: Dawid, ‘Some matrix-variate distribution theory’, Biometrika (1981).

class MatrixNormalDistribution(mean, row_covar, col_covar, name=None, keys=None)[source]

Bases: SequenceEncodableProbabilityDistribution

Matrix normal distribution over (n, p) matrices with row covariance U and column covariance V.

Parameters:
density(x)[source]

Return the matrix-normal density at a single (n, p) matrix.

Parameters:

x (ndarray)

Return type:

float

log_density(x)[source]

Return the log-density at a single (n, p) matrix.

Parameters:

x (ndarray)

Return type:

float

seq_log_density(x)[source]

Vectorized log-density for a stack of matrices, shape (N, n, p).

Parameters:

x (ndarray)

Return type:

ndarray

sampler(seed=None)[source]

Return a sampler for drawing matrices from this distribution.

Parameters:

seed (int | None)

Return type:

MatrixNormalSampler

estimator(pseudo_count=None)[source]

Return a flip-flop MLE estimator for the mean and the two covariance factors.

Parameters:

pseudo_count (float | None)

Return type:

MatrixNormalEstimator

dist_to_encoder()[source]

Return the data encoder used by this distribution for vectorized methods.

Return type:

MatrixNormalDataEncoder

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

Bases: DistributionSampler

Draw matrices by X = M + chol(U) Z chol(V)^T with Z standard normal.

Parameters:
  • dist (MatrixNormalDistribution)

  • seed (int | None)

sample(size=None)[source]

Draw observations.

Combinator samplers (mixture/sequence/…) accept batched. With batched=True (the default) each child stream is drawn in one vectorized call instead of a per-draw Python loop – far faster. Because every child sampler owns an independent RandomState, batching consumes each stream in the same order as the loop, so the draws are identical to the legacy path. batched=False forces that legacy per-draw loop as a guaranteed- stable reference. Leaf samplers are already vectorized and ignore the flag.

Parameters:

size (int | None)

Return type:

ndarray

class MatrixNormalAccumulator(n, p, name=None, keys=None)[source]

Bases: SequenceEncodableStatisticAccumulator

Accumulate the mean and the row-blocked second moment T[a,b,c,d] = sum_i X_i[a,c] X_i[b,d].

Parameters:
update(x, weight, estimate)[source]
Parameters:
  • x (ndarray)

  • weight (float)

  • estimate (MatrixNormalDistribution | None)

Return type:

None

initialize(x, weight, rng)[source]
Parameters:
Return type:

None

seq_update(x, weights, estimate)[source]
Parameters:
  • x (ndarray)

  • weights (ndarray)

  • estimate (MatrixNormalDistribution | None)

Return type:

None

seq_initialize(x, weights, rng)[source]
Parameters:
Return type:

None

combine(suff_stat)[source]
Parameters:

suff_stat (tuple[ndarray, ndarray, float])

Return type:

MatrixNormalAccumulator

value()[source]
Return type:

tuple[ndarray, ndarray, float]

from_value(x)[source]
Parameters:

x (tuple[ndarray, ndarray, float])

Return type:

MatrixNormalAccumulator

key_merge(stats_dict)[source]

Pool this accumulator’s statistics into stats_dict under its merge key.

The structural default implements the common single-key pattern: store the accumulator under self.keys the first time the key is seen, else combine into the one already there. Accumulators with several named keys (e.g. an HMM’s init/trans/state keys) or a non-accumulator stats payload override this. A keys of None (the default) is a no-op.

Parameters:

stats_dict (dict[str, Any])

Return type:

None

key_replace(stats_dict)[source]

Replace this accumulator’s statistics from the pooled stats_dict entry (see key_merge).

Parameters:

stats_dict (dict[str, Any])

Return type:

None

acc_to_encoder()[source]
Return type:

MatrixNormalDataEncoder

class MatrixNormalAccumulatorFactory(n, p, name=None, keys=None)[source]

Bases: StatisticAccumulatorFactory

Factory for MatrixNormalAccumulator.

Parameters:
make()[source]
Return type:

MatrixNormalAccumulator

class MatrixNormalEstimator(n, p, max_iter=100, tol=1.0e-9, name=None, keys=None)[source]

Bases: ParameterEstimator

Flip-flop maximum-likelihood estimator for the matrix-normal parameters.

Parameters:
accumulator_factory()[source]
Return type:

MatrixNormalAccumulatorFactory

estimate(nobs, suff_stat)[source]
Parameters:
Return type:

MatrixNormalDistribution

class MatrixNormalDataEncoder[source]

Bases: DataSequenceEncoder

Encode a sequence of (n, p) matrices as an (N, n, p) float array.

seq_encode(x)[source]

Encode the iid observation sequence x for vectorized evaluation.

Parameters:

x (Sequence[ndarray])

Return type:

ndarray