Mixalot

The content on this page was written by AI under human supervision.

Mixalot is a Python package that computes the Bayesian evidence of finite mixture models for count data exactly, as a rational number, together with the exact posterior probability of each possible number of components. You give it a vector of integer counts (how many of N observations fell in each of k categories) and a number of components g. It returns a Python Fraction; for very large problems you print its logarithm rather than its digits. The package collects the evaluators written for the paper Exact Bayesian evidence for mixture models behind one import, with a self-test suite and a checksum verification of every bundled file.

What it does

The evidence (or marginal likelihood) of a model is the probability of the data averaged over the prior on its parameters. The ratio of two evidences is a Bayes factor, the Bayesian way to decide between "one population" and "two". For a mixture of g categorical distributions on k states it is an integral over every component's probabilities and the mixing weights, in practice almost always estimated by Monte Carlo. Mixalot evaluates it exactly: with uniform Dirichlet priors the integral collapses to a finite sum over the ways the N observations can be allocated among components, computed by dynamic programming in big-integer arithmetic. The default convention is a fixed observation sequence (no multinomial coefficient) with Dirichlet(1) priors; measure="lebesgue" divides out the Dirichlet normalizing constants $(g-1)!\,((k-1)!)^g$ instead. A separate bundled evaluator written in the conventions of Lin, Sturmfels and Xu (arXiv:0805.3602) reproduces their printed benchmark values (core.lsx_example).

mixalot.Z, mixalot.gstar and mixalot.bayes_factor cover small and medium problems. Larger ones go through the bundled evaluators in mixalot.engines, each specialized in one direction. There are closed forms in harmonic numbers for one binary variable, a route for thousands of states, one for any number of components, and the exact infinite-component (Dirichlet-process) limit. Two-way contingency tables are computed modulo many primes and reassembled by the Chinese remainder theorem. A fourth route, the boxwalk subpackage (from mixalot import boxwalk), evaluates the underlying unit-box moment integrals $\int_{[0,1]^n}\prod_k P_k(x)^{u_k}\,dx$ exactly for integer polynomials $P_k$, by a recurrence walk modulo primes followed by rational reconstruction. mixalot.advise() reports the measured reach of the exact routes. With two components, N around $10^5$ is easy; with three, N = 3,000 needs about 1 GB of memory and N = 10,000 about 25 to 30 GB; with four or more, N up to roughly 500 to 700.

Two evaluators handle structured data. seg_v1 takes an ordered sequence of count vectors (chapters of a book, say) and computes the evidence that it came from g contiguous segments, each with its own profile, plus the posterior on where the boundaries fall. frozen_comp_v1 takes components with known profiles and unknown Dirichlet weights and returns the evidence and the Bayes factor for whether a given component is present. Each has an independently written twin (seg_blind, frozen_comp_blind); the self-tests require the two frozen-component implementations to agree exactly and cross-check seg_v1's exact route against its floating-point route. Three small modules extend this to authorship-style questions on an ordered token sequence. seam gives the exact evidence that one known profile produced a prefix and another the suffix, with the posterior on the changeover point. dcm replaces the multinomial with a Dirichlet-compound-multinomial, so word rates may vary from stretch to stretch. nullcal reads a mixed-versus-pure Bayes factor against the same statistic computed on objects known to be pure, because a mixture's extra freedom also absorbs ordinary rate variation. A Monte Carlo comparison suite is bundled too, for checking a sampler against exact values where both run, along with evaluators for two related likelihoods: Etienne's neutral-biodiversity sampling formula (with its supporting exact, interval-arithmetic and certified maximum-likelihood engines) and the telegraph model of gene expression.

The evidence routes return exact rationals; the exceptions say so in their names or modes (Z_float, lnZ_g_float, the interval mode of the Etienne evaluator, the Monte Carlo suite). A non-integer count raises NonIntegerCountError instead of being rounded. mixalot.verify() re-hashes every bundled file against recorded checksums and raises VendorTamperError, refusing to compute, if anything has changed, is missing or has been added. One limit is statistical: for a bare vector of counts from single draws, a mixture of categoricals is itself a categorical, so the data say little about g and at small N the posterior mostly reflects the prior. The exact number reports that faithfully but cannot remove it. Structure (ordered units, known profiles) is what gives power, and plant, recover_blind and mic_power measure detection power on synthetic data shaped like yours. The package handles categorical count data only.

Examples

Exact evidence and the component-count posterior. The manual's quickstart, against a checkout of the repository:

import sys; sys.path.insert(0, "<your-checkout>/tools/mixalot")
import mixalot
mixalot.verify()
mixalot.Z([4,1,3,2], 2)
mixalot.gstar([4,1,3,2], gmax=3)
from mixalot.engines import zseries, seg_v1, frozen_comp_v1, w4_blind_gf

verify() returns a report whose vendor_ok list names every bundled file that passed; its source_missing entries refer to the original source trees, which are not distributed, and are expected. Z returns Fraction(1010921, 2330808480000), the two-component evidence for ten observations on four states. gstar returns a dictionary with posterior (one Fraction per g from 1 to gmax, summing to one), map_g, p_ge2, evidence, prior and gmax. Here the posterior is roughly 0.22, 0.35, 0.43 for g = 1, 2, 3, so P(g ≥ 2) is near 0.78 with no value of g singled out, which is the weak-identifiability caution above: ten bare counts barely constrain g.

Command line. Put the counts in a JSON file, either a bare list [3, 1, 4, 1, 5] or an object {"U": [3,1,4,1,5], "gmax": 3}, and run

python3 -m mixalot.cli counts.json [--gmax 4]

The output is one JSON block: U, k, N, gmax, the posterior for each g as an exact fraction with a float beside it, map_g, p_ge2, the evidence for each g, and the advise() text for that N. With no file it prints the input schema.

Exact versus Monte Carlo at one point. The self-test suite and examples/worked_examples.py both run this comparison, two components of one binary variable with counts (50, 50):

from mixalot.engines import load
cf  = load("closed_form_1var")
z   = cf.Z_closed(50, 50)
est = load("estimators")
r   = est.run("m1", 2, [50, 50], "nested", seed=1)

z is the exact evidence as a Fraction; its logarithm is −71.0794. r is a dictionary with logZ_hat, err_est, diagnostics, settings and wall_s; the recorded test log has logZ_hat = −71.0204, 0.6 standard errors from the exact value, so nested sampling is calibrated at this size. python3 examples/worked_examples.py prints this and four more demonstrations: the exact log-evidence at N = 100,000 in roughly ten seconds, 300 states, the Dirichlet-process limit $Z_{\rm DPM}=5/48$ for counts (2, 1), and the exact finite-g correction $g\,(Z_g - Z_{\rm DPM}) = -1/48$. python3 examples/large_kgn_table.py [--quick] writes the full exact-versus-sampler table with timings.

Routines

Top level (import mixalot)

Authorship modules (from mixalot import nullcal, seam, dcm)

Command line and scripts

Bundled evaluators (mixalot.engines.<name>)

Boxwalk (from mixalot import boxwalk with tools/mixalot on sys.path; the subpackage mixalot/boxwalk/)

Used on this site

Requirements and source

Python 3 with the BootLoops common dependencies (mpmath, sympy, numpy, python-flint) plus scipy and dynesty (pip install scipy dynesty); the HMC-based comparison estimators also import jax. The boxwalk subpackage additionally needs Singular on the PATH (without it the Singular part of its self-test is reported as skipped) and fits recurrences with the package's bundled copy of the Annihilator (BOXWALK_ANNIHILATOR=/path/to/annihilator.py selects another copy, such as the stand-alone tools/annihilator/). Self-tests: python3 battery/battery.py [--full] from the package directory, about two minutes (set MIXALOT_BATTERY_OUT to write the BATTERY.txt record elsewhere); --full adds multi-minute checks, some of which skip unless the environment variables MIXALOT_REFDATA_PILOT, MIXALOT_REFDATA_SWEEP and MIXALOT_PILOT_DIR point at reference data that is not part of the repository. The authorship modules have their own script, mixalot/selftest.py, and boxwalk has python3 -m mixalot.boxwalk selftest. Code: tools/mixalot/ in BootLoops' bootloops-dev repository (GitHub organization BootLoops-ai), released under the MIT license; boxwalk is the subpackage mixalot/boxwalk/ inside it, with its own README. A packaged copy with the license files, and the manual, are also hosted here: mixalot-package.tar.gz, MANUAL.md; that copy uses an earlier layout in which boxwalk is a separate boxwalk/ directory beside mixalot/, with its own guide and the command line python3 cli.py ... run from that directory.

← back to the tools index