mixle.stats.directional.wrapped_normal module

Wrapped normal distribution – a Gaussian wrapped around the circle.

Wrapping X ~ N(mu, sigma^2) onto angles gives the wrapped normal WN(mu, sigma^2), the most common symmetric circular law and the maximum-entropy distribution for a fixed circular mean and resultant. Its density is the periodic sum of Gaussians,

f(theta; mu, sigma^2) = (1 / (sigma sqrt(2 pi))) sum_k exp(-(wrap(theta - mu) + 2 pi k)^2 / (2 sigma^2)),

which is uniform as sigma -> inf and concentrates at mu as sigma -> 0. Its first trigonometric moment is rho e^{i mu} with rho = exp(-sigma^2 / 2), so the mean direction and concentration are estimated in closed form from the mean resultant: mu = atan2(sum sin, sum cos) and sigma^2 = -2 log Rbar. It samples exactly by wrapping N(mu, sigma^2).

The density is summed over enough wraps (K set from sigma) that every retained Gaussian is machine-negligible past the window, which keeps the truncated sum strictly positive (unlike the trigonometric-series form, which can dip negative when truncated).

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

class WrappedNormalDistribution(mu, sigma2, name=None, keys=None)[source]

Bases: SequenceEncodableProbabilityDistribution

Wrapped normal distribution with mean direction mu and wrapped variance sigma2 > 0.

Parameters:
property K: int

Number of wraps summed on each side of the window.

density(x)[source]

Return the probability density at a single angle (radians).

Parameters:

x (float)

Return type:

float

log_density(x)[source]

Return the log-density at a single angle (radians).

Parameters:

x (float)

Return type:

float

seq_log_density(x)[source]

Return vectorized log-density for a sequence-encoded array of angles.

Parameters:

x (ndarray)

Return type:

ndarray

classmethod compute_capabilities()[source]
classmethod compute_declaration()[source]
static backend_legacy_sufficient_statistics(x, params, engine)[source]

Per-row circular moments (sum_cos, sum_sin, count) — uses the engine trig tier.

Parameters:
Return type:

tuple[Any, …]

backend_seq_log_density(x, engine)[source]

Engine-neutral vectorized log-density: the (2K+1)-branch wrapped logsumexp on engine ops.

Parameters:
Return type:

Any

sampler(seed=None)[source]

Return a sampler that wraps N(mu, sigma2) onto the circle.

Parameters:

seed (int | None)

Return type:

WrappedNormalSampler

estimator(pseudo_count=None)[source]

Return a closed-form (mean-resultant) estimator for mu and sigma2.

Parameters:

pseudo_count (float | None)

Return type:

WrappedNormalEstimator

dist_to_encoder()[source]

Return the data encoder used by this distribution (the angle itself).

Return type:

WrappedNormalDataEncoder

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

Bases: DistributionSampler

Draw angles by wrapping N(mu, sigma2) around the circle.

Parameters:
  • dist (WrappedNormalDistribution)

  • 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:

float | ndarray

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

Bases: SequenceEncodableStatisticAccumulator

Accumulate the weighted circular resultant (sum cos, sum sin, count).

Parameters:
  • name (str | None)

  • keys (str | None)

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

  • weight (float)

  • estimate (WrappedNormalDistribution | None)

Return type:

None

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

None

seq_update(x, weights, estimate)[source]
Parameters:
Return type:

None

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

None

combine(suff_stat)[source]
Parameters:

suff_stat (tuple[float, float, float])

Return type:

WrappedNormalAccumulator

value()[source]
Return type:

tuple[float, float, float]

from_value(x)[source]
Parameters:

x (tuple[float, float, float])

Return type:

WrappedNormalAccumulator

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:

WrappedNormalDataEncoder

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

Bases: StatisticAccumulatorFactory

Factory for WrappedNormalAccumulator.

Parameters:
  • name (str | None)

  • keys (str | None)

make()[source]
Return type:

WrappedNormalAccumulator

class WrappedNormalEstimator(sigma2_max=1.0e6, name=None, keys=None)[source]

Bases: ParameterEstimator

Estimate mu and sigma2 from the mean resultant (rho = exp(-sigma2/2)).

Parameters:
  • sigma2_max (float)

  • name (str | None)

  • keys (str | None)

accumulator_factory()[source]
Return type:

WrappedNormalAccumulatorFactory

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

WrappedNormalDistribution

class WrappedNormalDataEncoder[source]

Bases: DataSequenceEncoder

Encode angles as a float array (wrapped to (-pi, pi]).

seq_encode(x)[source]

Encode the iid observation sequence x for vectorized evaluation.

Parameters:

x (Sequence[float])

Return type:

ndarray