mixle.stats.directional.von_mises module

Von Mises distributions for circular angular data.

Observations are angles in radians. A Von Mises distribution with mean direction mu and concentration kappa >= 0 has log-density

log(f(theta; mu, kappa)) = kappa * cos(theta - mu) - log(2*pi*I_0(kappa)),

the circular analogue of a Gaussian (kappa = 0 is uniform on the circle; large kappa concentrates near mu). This is the one-dimensional companion to von_mises_fisher (the von Mises-Fisher distribution on a sphere).

Wrap-around semantics – read this before comparing scores across families. Any finite real x is accepted and interpreted modulo 2*pi: only cos(x) and sin(x) ever enter the density, the sufficient statistics, and the estimator, so x, x + 2*pi and x + 200*pi are indistinguishable observations of the same angle. The density integrates to one over a single period (-pi, pi], not over the real line – integrated over a wider interval it exceeds one. Two consequences for non-angular data:

  • Fitting a real-line column (money, durations, counts) silently wraps it onto the circle; the fit is a valid circular density of x mod 2*pi but says nothing about x itself.

  • The log-density is not comparable to real-line families’ log-densities. A column spanning k periods overlays its mass onto one period, so the wrapped model can “win” a likelihood comparison against any real-line family by roughly log(k) nats per observation without modeling the data at all. Compare von Mises scores only against other circular models, on data that is genuinely angular.

It is a two-parameter exponential family with sufficient statistics (cos theta, sin theta):

log(f) = eta1*cos(theta) + eta2*sin(theta) + log_const,

eta1 = kappa*cos(mu), eta2 = kappa*sin(mu), log_const = -log(2*pi*I_0(kappa)).

The natural parameters and normalizer are precomputed (the Bessel term I_0 lives only in the scalar log_const), so the per-row score is linear in the encoded cos/sin fields and lowers cleanly to generated NumPy, Torch, and Numba kernels.

Reference: Mardia & Jupp, Directional Statistics (Wiley, 2000).

exception VonMisesFitError[source]

Bases: RuntimeError

Raised when circular moments have no finite von Mises fit.

class VonMisesDistribution(mu, kappa, name=None, keys=None)[source]

Bases: SequenceEncodableProbabilityDistribution

Von Mises distribution on the circle with mean direction mu and concentration kappa >= 0.

Observations are angles in radians, interpreted modulo 2*pi: log_density(x) equals log_density(x + 2*pi*k) for every integer k, and the density normalizes over one period, not over the real line. Scores are therefore comparable only to other circular models – see the module docstring for why a wrapped fit of real-line data can spuriously out-score every real-line family.

Parameters:
classmethod compute_capabilities()[source]

Describe backend support for generated von Mises kernels.

classmethod compute_declaration()[source]

Return the structured compute declaration for von Mises distributions.

static backend_legacy_sufficient_statistics(x, params, engine)[source]

Return per-row (count, cos, sin) sufficient statistics in accumulator order.

Parameters:
Return type:

tuple[Any, …]

static exp_family_sufficient_statistics(x, engine)[source]

Return von Mises sufficient statistics T(x) = (cos x, sin x).

Parameters:
Return type:

tuple[Any, …]

static exp_family_natural_parameters(params, engine)[source]

Return von Mises natural parameters eta = (kappa cos mu, kappa sin mu).

Parameters:
Return type:

tuple[Any, …]

static exp_family_log_partition(params, engine)[source]

Return von Mises log partition A = log(2 pi I_0(kappa)) = -log_const.

Parameters:
Return type:

Any

static exp_family_from_natural(eta)[source]

Return the von Mises with natural parameters eta = (kappa cos mu, kappa sin mu).

Parameters:

eta (Any)

Return type:

VonMisesDistribution

static backend_log_density_from_params(cos_t, sin_t, eta1, eta2, log_const, engine)[source]

Engine-neutral von Mises log-density from natural parameters (linear in cos/sin).

Parameters:
Return type:

Any

density(x)[source]

Return the probability density at a single angle.

Parameters:

x (float)

Return type:

float

log_density(x)[source]

Return the log-density at a single angle (radians), wrapping x modulo 2*pi.

Any finite real is accepted; values outside (-pi, pi] are folded onto the circle, so this is the density of x mod 2*pi, normalized over one period only.

Parameters:

x (float)

Return type:

float

seq_log_density(x)[source]

Return vectorized log-density values for sequence-encoded (cos, sin) observations.

Parameters:

x (tuple[ndarray, ndarray])

Return type:

ndarray

backend_seq_log_density(x, engine)[source]

Engine-neutral vectorized log-density for encoded data.

Parameters:
Return type:

Any

classmethod backend_stacked_params(dists, engine)[source]

Return stacked natural parameters for a homogeneous mixture kernel.

Parameters:
Return type:

dict[str, Any]

classmethod backend_stacked_log_density(x, params, engine)[source]

Return an (n, k) matrix of von Mises log densities.

Parameters:
Return type:

Any

classmethod backend_stacked_sufficient_statistics(x, weights, params, engine)[source]

Return stacked sufficient statistics using engine-resident arrays.

Parameters:
Return type:

tuple[Any, Any, Any]

sampler(seed=None)[source]

Return a sampler for drawing angles from this distribution.

Parameters:

seed (int | None)

Return type:

VonMisesSampler

estimator(pseudo_count=None)[source]

Return an estimator for fitting this distribution from data.

Parameters:

pseudo_count (float | None)

Return type:

VonMisesEstimator

dist_to_encoder()[source]

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

Return type:

VonMisesDataEncoder

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

Bases: DistributionSampler

Draw iid angles from a von Mises distribution.

Parameters:
  • dist (VonMisesDistribution)

  • seed (int | None)

sample(size=None, *, batched=True)[source]

Draw size iid angles in (-pi, pi] (a float when size is None).

Parameters:
Return type:

float | ndarray

class VonMisesAccumulator(name=None, keys=None)[source]

Bases: SequenceEncodableStatisticAccumulator

Accumulate weighted count and circular moments (sum of cos / sin) for von Mises estimation.

Parameters:
  • name (str | None)

  • keys (str | None)

update(x, weight, estimate)[source]

Accumulate one weighted circular moment contribution.

Parameters:
  • x (float)

  • weight (float)

  • estimate (VonMisesDistribution | None)

Return type:

None

initialize(x, weight, rng)[source]

Initialize statistics from one angle.

Parameters:
Return type:

None

seq_update(x, weights, estimate)[source]

Accumulate circular moments from encoded cos/sin values.

Parameters:
Return type:

None

seq_initialize(x, weights, rng)[source]

Initialize statistics from encoded angles.

Parameters:
Return type:

None

combine(suff_stat)[source]

Merge another von Mises sufficient-statistic tuple.

Parameters:

suff_stat (tuple[float, float, float])

Return type:

VonMisesAccumulator

value()[source]

Return count, cosine sum, and sine sum.

Return type:

tuple[float, float, float]

from_value(x)[source]

Replace accumulator contents from circular-moment statistics.

Parameters:

x (tuple[float, float, float])

Return type:

VonMisesAccumulator

scale(c)[source]

Scale linear von Mises sufficient statistics.

Parameters:

c (float)

Return type:

VonMisesAccumulator

acc_to_encoder()[source]

Return the encoder used by this accumulator.

Return type:

VonMisesDataEncoder

class VonMisesAccumulatorFactory(name=None, keys=None)[source]

Bases: StatisticAccumulatorFactory

Factory for VonMisesAccumulator.

Parameters:
  • name (str | None)

  • keys (str | None)

make()[source]

Create a fresh von Mises accumulator.

Return type:

VonMisesAccumulator

class VonMisesEstimator(pseudo_count=None, suff_stat=None, name=None, keys=None)[source]

Bases: ParameterEstimator

Maximum-likelihood estimator for the von Mises mean direction and concentration.

The MLE is mu = atan2(sum sin, sum cos) and kappa = A^{-1}(R) where R is the mean resultant length and A(kappa) = I_1(kappa) / I_0(kappa).

The data must be angles in radians. Any finite real is accepted and wrapped modulo 2*pi (only cos/sin enter the statistics), so fitting a non-angular real-line column does not fail – it silently fits the circular law of x mod 2*pi, whose log-likelihood is normalized over one period and is not comparable to real-line families (see the module docstring).

Parameters:
accumulator_factory()[source]

Return an accumulator factory for von Mises circular moments.

Return type:

VonMisesAccumulatorFactory

estimate(nobs, suff_stat)[source]

Estimate mean direction and concentration from weighted circular moments.

Parameters:
Return type:

VonMisesDistribution

class VonMisesDataEncoder[source]

Bases: DataSequenceEncoder

Encode angle observations as (cos, sin) pairs.

seq_encode(x)[source]

Encode angles as cosine and sine arrays.

Parameters:

x (Sequence[float])

Return type:

tuple[ndarray, ndarray]

row_count(x)[source]

Return the encoded row count after unit-circle validation.

Parameters:

x (tuple[ndarray, ndarray])

Return type:

int