mixle.stats.combinator.truncated module

Truncated-support combinator: restrict a base distribution to an allowed set, renormalized.

TruncatedDistribution wraps a base distribution and conditions it on a support restriction:

p(x) = p_base(x) / Z for x in the allowed support, else 0,

where Z = sum_{y allowed} p_base(y) is the retained mass. The restriction is given either as a finite forbidden set to exclude (Z = 1 - sum_f p_base(f) – works for an infinite base) or a finite allowed set to keep (Z = sum_a p_base(a)). It pairs with the Phase-1c support tools: the renormalizer is exactly the truncated tail/total the enumeration bounds reason about.

allowed/forbidden are deduplicated (by value, not by position), so a repeated entry cannot double-count its mass. The point-mass sums above assume a discrete (enumerable) base, where p_base(v) at a single value v is itself a probability. For a continuous base, any single point has zero measure, so a finite point list can only ever remove or retain zero mass: forbidding one is therefore a no-op (Z = 1) and allowing one necessarily retains nothing (Z = 0, which raises – see below).

class TruncatedProjectionFitReceipt(accepted_weight, rejected_weight, rejected_fraction, likelihood_aware)[source]

Bases: NamedTuple

Evidence accounting for the explicit untruncated-base projection fit.

Parameters:
  • accepted_weight (float)

  • rejected_weight (float)

  • rejected_fraction (float)

  • likelihood_aware (bool)

accepted_weight: float

Alias for field number 0

rejected_weight: float

Alias for field number 1

rejected_fraction: float

Alias for field number 2

likelihood_aware: bool

Alias for field number 3

class TruncatedStatistics(schema_version, child, accepted_weight, rejected_weight)[source]

Bases: NamedTuple

Versioned sufficient statistics for support-filtered projection fitting.

Parameters:
  • schema_version (int)

  • child (Any)

  • accepted_weight (float)

  • rejected_weight (float)

schema_version: int

Alias for field number 0

child: Any

Alias for field number 1

accepted_weight: float

Alias for field number 2

rejected_weight: float

Alias for field number 3

class TruncatedDistribution(base, allowed=None, forbidden=None, name=None, keys=None, fit_receipt=None)[source]

Bases: SequenceEncodableProbabilityDistribution

A base distribution restricted to an allowed support and renormalized.

Parameters:
  • base (SequenceEncodableProbabilityDistribution)

  • allowed (Sequence[Any] | None)

  • forbidden (Sequence[Any] | None)

  • name (str | None)

  • keys (str | None)

  • fit_receipt (TruncatedProjectionFitReceipt | None)

density(x)[source]

Return the renormalized probability/density at x.

Parameters:

x (Any)

Return type:

float

log_density(x)[source]

Return log p_base(x) - log Z for allowed x, else -inf.

Parameters:

x (Any)

Return type:

float

seq_log_density(x)[source]

Return per-row truncated log-densities for an encoded batch.

Parameters:

x (tuple[Any, ndarray])

Return type:

ndarray

sampler(seed=None)[source]

Return a rejection sampler over the allowed support.

Parameters:

seed (int | None)

Return type:

TruncatedSampler

estimator(pseudo_count=None)[source]

Return the explicit projection estimator, not a truncated-likelihood MLE.

The projection fits the child to retained observations and then reapplies the support restriction. It deliberately omits the parameter-dependent -n log Z(theta) term, so the returned model carries a receipt with likelihood_aware=False.

Parameters:

pseudo_count (float | None)

Return type:

TruncatedProjectionEstimator

dist_to_encoder()[source]

Return the data encoder (base encoding + an allowed-membership mask).

Return type:

TruncatedDataEncoder

support_size()[source]

Cardinality of the retained support (None if infinite).

Return type:

int | None

enumerator()[source]

Enumerate the allowed support in descending (renormalized) probability order.

Return type:

TruncatedEnumerator

class TruncatedEnumerator(dist)[source]

Bases: DistributionEnumerator

Filter the base enumeration to the allowed support, renormalized by -log Z.

Parameters:

dist (TruncatedDistribution)

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

Bases: DistributionSampler

Rejection sampler: draw from the base, keep only allowed values.

Parameters:
  • dist (TruncatedDistribution)

  • seed (int | None)

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

Draw one allowed value (or a list of size) by rejection.

With batched=True (default) base draws are taken in blocks sized from the running accept rate, instead of one per draw – far faster when the retained mass is small, with a clear diagnostic (accept rate, attempts) if the budget is exhausted. Set batched=False for the per-draw reference path. Batched draws differ in RNG-call order from the per-draw loop.

Parameters:
class TruncatedAccumulator(base_accumulator, *, allowed, forbidden, keys=None)[source]

Bases: SequenceEncodableStatisticAccumulator

Accumulate accepted child statistics and account explicitly for rejected evidence.

Parameters:
  • base_accumulator (SequenceEncodableStatisticAccumulator)

  • allowed (Sequence[Any] | None)

  • forbidden (Sequence[Any] | None)

  • keys (str | None)

update(x, weight, estimate)[source]

Accumulate one observation under the same support rule as batch accumulation.

Parameters:
  • x (Any)

  • weight (float)

  • estimate (TruncatedDistribution | None)

Return type:

None

seq_update(x, weights, estimate)[source]

Accumulate encoded observations, zeroing weights outside the retained support.

Parameters:
Return type:

None

initialize(x, weight, rng)[source]

Initialize under the same support rule as batch initialization.

Parameters:
Return type:

None

seq_initialize(x, weights, rng)[source]

Initialize from encoded observations, zeroing weights outside the retained support.

Parameters:
Return type:

None

combine(suff_stat)[source]

Merge serialized sufficient statistics into this accumulator.

Parameters:

suff_stat (TruncatedStatistics)

Return type:

TruncatedAccumulator

value()[source]

Return this accumulator’s serialized sufficient statistics.

Return type:

TruncatedStatistics

from_value(x)[source]

Restore this accumulator from serialized sufficient statistics.

Parameters:

x (TruncatedStatistics)

Return type:

TruncatedAccumulator

scale(c)[source]

Scale linear sufficient statistics in-place by c.

The structural default is correct for ordinary weighted sums, nested tuples/lists/dicts, and numeric arrays. Families whose value() payload includes non-linear metadata such as support bounds must override this method and leave that metadata unscaled.

Parameters:

c (float)

Return type:

TruncatedAccumulator

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 whose key does not cover their whole state (several named keys, a wrapped child accumulator, a non-accumulator payload) override this – and ONLY those: as of 2026-07-14 every whole-state family uses this default, after a sweep found sixteen hand-rolled copies that had drifted into three broken shapes. If you are tempted to reimplement this per family, don’t; extend the base or override with super() calls. A keys of None (the default) is a no-op.

Why this exact shape is correct – and the drifted shapes are not: the POOL must live in the dict. Combining INTO the dict-held accumulator (below) mutates the pool in place, so after all sites merge, the dict entry holds everyone’s statistics and key_replace hands that pool to every site. The observed broken variants each violate one half: pulling the dict’s stats into self without writing back (the dict keeps the FIRST site only – later sites’ data silently discarded); merging only when the key is already present (nobody ever inserts: both passes are no-ops and tying does nothing); and replacing by pushing self INTO the dict (the LAST site overwrites the pool). The keyed-protocol sweep (keyed_protocol_sweep_test.py) enforces this behaviorally for every catalog family.

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 an encoder carrying the accumulator’s exact support restriction.

Return type:

TruncatedDataEncoder

class TruncatedAccumulatorFactory(base_factory, *, allowed, forbidden, keys=None)[source]

Bases: StatisticAccumulatorFactory

Factory for TruncatedAccumulator.

Parameters:
  • base_factory (StatisticAccumulatorFactory)

  • allowed (Sequence[Any] | None)

  • forbidden (Sequence[Any] | None)

  • keys (str | None)

make()[source]

Create an empty truncated-data accumulator.

Return type:

TruncatedAccumulator

class TruncatedProjectionEstimator(base_estimator, allowed=None, forbidden=None, name=None, keys=None)[source]

Bases: ParameterEstimator

Approximate projection that fits the base only to retained observations.

This is intentionally not called a truncated MLE: it omits the parameter-dependent normalizer from the objective and marks every fitted model with likelihood_aware=False.

Parameters:
accumulator_factory()[source]

Return a factory for truncated-data sufficient-statistic accumulators.

Return type:

TruncatedAccumulatorFactory

estimate(nobs, suff_stat)[source]

Project retained evidence onto the base and report excluded evidence.

Parameters:
Return type:

TruncatedDistribution

TruncatedEstimator

alias of TruncatedProjectionEstimator

class TruncatedDataEncoder(dist)[source]

Bases: MaskedBaseEncoder

Encode observations via the base encoder, plus a boolean allowed-membership mask.

Parameters:

dist (TruncatedDistribution)