mixle.inference.torsion module¶
Twisted composition for mixture components with a shared base density.
Mixture components can share one base density modulo a declared group action,
instead of each independently learning a density from scratch on its own slice
of data. Concretely, CyclicGroup
acts on a periodic coordinate by rotation of its (cos, sin) embedding (an exact, Jacobian-1 change of
variables, so a fitted embedding density scores identically whichever group element aligned a point into it);
fit_twisted_mixture() pools every group’s data into ONE shared base density after undoing each group’s
twist, so the shared density is fit on the union – effectively |groups| times the data for the same
parameter count as fitting one group alone.
The rotation is Jacobian-1, but the (cos, sin) embedding is not a change of variables at all: it
maps the 1-D coordinate onto a measure-zero circle in R^2. An ambient 2-D density evaluated on that
circle is therefore NOT a density over x – it integrates over one period to an arbitrary constant that
differs from fit to fit. Every scoring entry point here (TwistedMixtureResult.log_density(),
independent_log_density()) divides out that constant via log_circular_normalizer(), so the
returned values are genuine log densities on [0, period) and the twisted-vs-independent held-out
likelihood comparison this module exists to make is between comparable quantities.
Use this as an experimental modeling option. If the shared-base model does not beat independently fit per-group models at matched per-component capacity on held-out per-group log likelihood, keep the independent baseline as the default.
- NORMALIZER_GRID = 4096
Quadrature points per period used by
log_circular_normalizer(). The integrand is smooth and periodic, so the uniform-grid (periodic trapezoid) rule converges spectrally – a few thousand nodes pin the normalizer far below any difference the twisted/independent comparison turns on.
- class CyclicGroup(order, period=1.0)[source]
Bases:
objectZ_``order`` acting on a periodic real-valued coordinate of period
periodby rotation.Each group element
kin{0, ..., order - 1}is realized concretely as a rotation of the coordinate’s(cos, sin)embedding by angle2*pi*k/order– an orthogonal (norm- and Jacobian-preserving) transform, so composing group elements is exactly addition modorder(compose()), and a density fit on the embedding is unaffected by which element aligned a point into it (the twist is undone before scoring, not baked into the density).ordermust be an exact positive integer andperioda finite positive number: a zero order divides by zero in the rotation law, a negative or fractional one describes no cyclic group at all, and a zero/non-finite period makes every embedding NaN.{0, ..., order - 1}is the whole element set, and it is checked – an out-of-rangekused to be silently wrapped into a different element, so-1,order,order + 1andTrueall acted as undeclared aliases.- element(k)[source]
Validate
kas a declared group element and return it as a plainint.
- embed(x)[source]
The periodic coordinate’s
(cos, sin)embedding, shape(..., 2).
- act(embedded, k)[source]
Rotate an
(..., 2)embedding by group elementk(the forward twist).
- inverse_act(embedded, k)[source]
Undo group element
k’s twist –act(inverse_act(v, k), k) == v.
- log_circular_normalizer(density, group, *, grid=NORMALIZER_GRID)[source]
log ∫_0^period q(embed(t)) dt– what turns an ambient embedding score into a density onx.embedplaces the periodic coordinate on a measure-zero circle inR^2, so an ambient 2-D densityqrestricted to it does not integrate to one over a period; it integrates to a fit-specific constant. Subtracting this log constant fromlog q(embed(x))yields a normalized density on[0, period).The value does not depend on the group element:
inverse_act(embed(x), k) == embed(x - k*period/order), and integrating over a whole period is invariant to that shift – so one normalizer serves everyk.
- class TwistedMixtureResult(base_density, group)[source]
Bases:
objectA single shared base density plus the group whose elements twist it into each group’s local factor.
- Parameters:
base_density (Any)
group (CyclicGroup)
- log_normalizer()[source]
The (cached,
k-independent) log constant that normalizes the shared base density over a period.- Return type:
- log_density(x, k)[source]
log p(x | group=k): undok’s twist, score under the shared base density, normalize.A normalized density on the periodic coordinate –
expof this integrates to 1 over one period – so values are comparable againstindependent_log_density()and across fits.
- fit_twisted_mixture(group, data_by_group, *, n_components=2, seed=0, max_its=50)[source]
Fit ONE base density on every group’s data pooled together after undoing each group’s twist.
data_by_groupmaps a group elementkto that group’s (small) sample of the periodic coordinate. Every sample is embedded and rotated back by its own group’sinverse_actbefore pooling – so the fittedn_components-component density seessum(len(v) for v in data_by_group.values())points, not just one group’s slice, for the same parameter count asfit_independent_mixtures()spends on a SINGLE group.
- fit_independent_mixtures(group, data_by_group, *, n_components=2, seed=0, max_its=50)[source]
The untwisted baseline: one independently-fit
n_components-component density per group.Same per-group parameter count as the shared base density in
fit_twisted_mixture(), but|groups|times the total parameters overall, and each fit sees only its own group’s (small) sample – the comparisonfit_twisted_mixture()is measured against.
- independent_log_density(models, group, x, k)[source]
Score
xunder groupk’s independently-fit density (the baseline sibling ofTwistedMixtureResult.log_density()).Normalized over one period by that model’s own
log_circular_normalizer()– each independent fit has a different embedding normalizer, so without this the baseline comparison would be decided by normalization rather than by fit quality.