mixle.enumeration.quantization.semiring module¶
The two enumeration primitives and the bridges between them (step-1 contract).
The enumeration system has two orthogonal computational modes:
- Axis A – aggregate computation: fold a distribution’s complete-probability reduction into a
summary. Density, structural counts, and bounds all live here. This is captured by
DecomposableSemiring: a family expresses its reduction withleaf/plus/timesand the carrier decides what is computed. The carrier must be witness-retaining – its elements keep enough structure to invert a rank back to a value (unranking) – which a plain scalar semiring cannot do.CountSemiringis the witness-retaining carrier overmixle.enumeration.quantization.CountIndexthat yields the count-budget seek index.- Axis B – ordered access: produce values in strict descending probability order, lazily. This is
a best-first search, not a fold, and a semiring cannot generate it; it is the existing
enumerator()machinery (mixle.stats.compute.pdist.DistributionEnumerator), aliased here asOrderedStreamfor symmetry.
The two axes trade in one currency – (value, log_prob) pairs under a shared
mixle.enumeration.quantization.Quantizer – connected by two coercion bridges:
enumerate_and_bin()(Axis B -> Axis A): the universal fallback. AnyOrderedStreamcan be tabulated into a bounded count index; O(count), so only viable for small budgets.
ordered_stream_from_count_index()(Axis A -> Axis B): unrank in bin order to get an approximately-ordered stream when exact best-first is too expensive.
So a family implements whichever axis is natural and the bridges synthesize the other (lossily).
This module is the contract; CountSemiring is the witness-retaining carrier for everything
count-shaped, and TropicalSemiring is the (max, +) carrier that computes the Viterbi
configuration and its log-prob – an exact bound that factors through the non-decomposable families
(HMM/Mixture) by swapping only the carrier.
For those non-decomposable families the state-augmented count DP IS implemented – as a conservative
upper bound – by HiddenMarkovModelDistribution.quantized_count_index (a forward count DP over the
(state-path, observation) trellis) and MixtureDistribution.quantized_count_index (a plus-fold of
weight-scaled component indices). What is not available is the EXACT MARGINAL count/rank, which sums
over the latent and is provably hard (mixture/HMM marginal rank is k-SUM- / W[1]-hard); the exact head
plus a certified unbiased Monte-Carlo estimator for the tail lives in
mixle.enumeration.density_rank (density_rank), and exact HMM path enumeration in
mixle.enumeration.hmm_paths.
- class DecomposableSemiring[source]
Bases:
ABCA witness-retaining semiring over which a likelihood reduction is evaluated.
zero/oneare the additive/multiplicative identities;pluspools mutually exclusive alternatives (e.g. different sequence lengths or Markov end-states);timescomposes independent factors (whose log-probabilities add);productis n-arytimes.leaflifts a single scored atom into the carrier. Implementations choose the carrierE; for counting it isCountIndex, whose per-bucket structure is the witness needed to unrank.- abstractmethod zero()[source]
- Return type:
E
- abstractmethod one()[source]
- Return type:
E
- abstractmethod leaf(value, log_prob, quantizer)[source]
- abstractmethod plus(a, b)[source]
- Parameters:
a (E)
b (E)
- Return type:
E
- class CountSemiring[source]
Bases:
DecomposableSemiringWitness-retaining carrier over reified nodes / CountIndex: structural counting + unranking.
leaf/plus/scale/map_valuesbuild_CNodetrees unranked by the iterative_unrank()(bounded call-stack regardless of chain depth);times/productconvolve viamixle.enumeration.quantization.convolve_indices()(flat unranker – identical bin counts and within-bucket order to the previous path). Both element kinds expose.histand.get_in_bucket, so they interoperate freely.- zero()[source]
- Return type:
_CNode
- one()[source]
- Return type:
_CNode
- leaf(value, log_prob, quantizer)[source]
- from_enumerator(enum, quantizer, max_fine_bucket)[source]
Lift an atomic distribution’s exact enumerator into a carrier element (depth-bounded).
- plus(a, b)[source]
- Return type:
_CNode
- times(a, b, quantizer, max_fine_bucket)[source]
- Parameters:
quantizer (Quantizer)
max_fine_bucket (int)
- Return type:
CountIndex
- product(elements, quantizer, max_fine_bucket)[source]
n-ary
times. Default foldstimes; carriers may override for efficiency/order.
- scale(a, log_prob, quantizer, max_fine_bucket=None)[source]
Multiply by a constant probability factor: shift buckets by the factor, value unchanged.
This is the action of the base log-prob monoid on the carrier (e.g. a sequence length term, a Markov transition/initial term). Optionally truncate the shifted histogram to a depth bound.
- map_values(a, fn)[source]
Relabel values (pushforward) without touching counts or buckets – e.g. tuple -> list.
- power_prefix(a, max_k, quantizer, max_fine_bucket)[source]
Return [a^(times 0), …, a^(times K)] (K <= max_k), the k-fold self-products.
The histograms are built incrementally (O(max_k) convolutions, shared), so this is the count side of an iid sequence. Each element’s unranker is the flat product of k copies, built lazily and cached – identical bin counts and within-bucket order to calling
product([a]*k)directly, but without the O(k^2) eager cost. Stops early if the element mass is exhausted within the depth bound.
- class TropicalSemiring[source]
Bases:
DecomposableSemiringMax-plus (Viterbi) carrier: the single best configuration and its log-probability.
The second realization of
DecomposableSemiring– it swaps only the carrier (the contract this module was built around). WhereCountSemiringcounts the support in each quantized bin, the tropical carrier folds the sameleaf/plus/timesreduction over the(max, +)semiring:pluskeeps the higher-probability alternative,times/productadd log-probabilities and concatenate witnesses. The result is the Viterbi (most-probable) configuration and its exact log-probability – the top of the descending-probability order, and hence an exact bound usable by any family expressible inleaf/plus/times.Crucially this includes the non-decomposable families (HMM / Mixture): exact counting couples across structure (which is why the count index does not serve them), but the Viterbi bound factors cleanly through the tropical fold. (Exact bounded counting for those families is the state-augmented count DP – a further step; for HMM the exact descending enumeration is already available via
mixle.enumeration.hmm_paths.hmm_best_paths().)The witness is the flat tuple of leaf atoms along the best configuration (the tropical carrier keeps a single witness, not the per-child structured value the count carrier reconstructs on unrank), which is exactly what a Viterbi decode wants.
quantizer/max_fine_bucketare accepted for interface symmetry but unused – the tropical fold needs no quantization.- zero()[source]
- Return type:
_Trop
- one()[source]
- Return type:
_Trop
- leaf(value, log_prob, quantizer=None)[source]
- plus(a, b)[source]
- Parameters:
a (_Trop)
b (_Trop)
- Return type:
_Trop
- OrderedStream
Exact strict-descending lazy stream of
(value, log_prob)– produced byenumerator().
- enumerate_and_bin(stream, quantizer, max_fine_bucket)[source]
Axis B -> Axis A: tabulate an ordered stream into a bounded count index.
The universal fallback for distributions that can only enumerate. O(number of in-bound values), so feasible only for small budgets. Returns
(CountIndex, truncated).
- bounded_dedup_stream(stream, max_entries=1 << 16, key=None)[source]
Deduplicate an (approximately) descending
(value, log_prob)stream in O(max_entries) memory.The structural BoundedCount index for a MARGINAL family (Mixture / HMM) emits a value once per contributing component / state-path; every copy reports the same exact
log_density(it is path/component-independent), so the duplicates are exact repeats of the value. This wrapper keeps a least-recently-seen window of at mostmax_entrieskeys and suppresses repeats within it.Memory is hard-capped at
max_entries– never the full (up to 2**M) support. The trade: a duplicate whose two occurrences are more thanmax_entriesdistinct values apart in the stream (i.e. its second copy is far deeper / effectively outside the bound of interest) may survive. Setmax_entriesto bound how far apart duplicates can be and still be removed.
- ordered_stream_from_count_index(index, max_items=None)[source]
Axis A -> Axis B: unrank a built count index in coarse-bin order (approximately descending).
indexis a built LazyQuantizedEnumerationIndex (fromcount_budget_index). The order is exact across coarse bins but unspecified within a bin – a good-enough stream when exact best-first enumeration is too expensive.