mixle.inference.jit module¶
JIT-compile a model’s whole-tree log-density to a single XLA program (A2).
mixle’s per-distribution backend_seq_log_density is engine-neutral: a Composite/Mixture/… combines
its children’s scores through ComputeEngine ops, so the entire model tree is one pure array
computation. Run it on the JAX engine under jax.jit and the whole tree lowers to a single compiled
XLA program – the “compile the model once” trick that makes JAX-backed PPLs fast.
score = jit_seq_log_density(model) # compiles lazily, per data shape ll = score(data) # bit-identical to model.seq_log_density(encode(data)), faster
This is ideal for repeated scoring of a fixed model – prediction, held-out / cross-validation
log-likelihoods, importance weights, bootstrap, large-batch scoring – where the same compiled program
is reused across calls. The result is bit-identical to model.seq_log_density (verified) and, on a
large composite tree, several-fold faster than vectorized NumPy.
Scope note: this compiles with the model’s parameters baked into the program (the structure lowers to
one XLA function). Reusing a single compiled program ACROSS EM iterations as the parameters update
needs the parameters threaded as traced inputs (a tree-level backend_log_density_from_params); that
is the next increment and is not done here.
- class JittedScorer(model, engine=None)[source]
Bases:
objectA
jax.jit-compiled whole-tree log-density scorer for a fixed model.Calling the scorer encodes
data, runs the engine-neutralbackend_seq_log_densityover the whole model tree on the JAX engine underjax.jit, and returns host log-densities. The compiled program is cached and reused across calls with the same data shape.- Parameters:
model (Any)
engine (Any)
- jit_seq_log_density(model, engine=None)[source]
Return a
JittedScorer: the whole-tree log-density ofmodelcompiled to one XLA program.jit_seq_log_density(model)(data)is bit-identical tomodel.seq_log_density(encode(data))but runs as a singlejax.jitXLA program over the entire composite tree – fast for repeated scoring of a fixed model. Requires the JAX optional extra and that every leaf inmodeldeclares JAX support (raisesEngineNotSupportedError-style errors from the engine layer otherwise).
- jit_em_mixture(model, data, *, max_its=100, engine=None)[source]
Fit a finite mixture of same-family scalar exponential-family leaves by EM, with the ENTIRE EM loop (every E-step + closed-form weighted M-step iteration) compiled to ONE
jax.jitXLA program vialax.scan– the parameters are traced inputs threaded through the loop on-device, so there is no per-iteration recompile and no per-iteration host sync. This is roadmap A2 bullet 1 (“repeated EM iterations run as one XLA program”) realized literally.modelis the initial mixture (its components seed the EM); supported leaves: Gaussian, Poisson, Exponential. Runs a fixedmax_itsiterations (no host-side early stop – that is the point: the loop stays on-device). Returns a fittedMixtureDistribution, bit-close to the host EM from the same start (it is the same EM update). RaisesNotImplementedErrorfor unsupported structure.SPEED – measured, honest: the payoff is GPU/TPU and large scale, where XLA parallelizes the E-step over millions of points and many components. On an Apple M4 GPU (via jax-metal) this kernel runs ~21x faster than mixle’s vectorized NumPy EM (K=10, N=1e6, 50 iters: 156 ms vs 3254 ms) with identical estimates, and ~12x faster than the same jitted loop on CPU. On CPU it is *not* a speedup – mixle’s host EM is already vectorized NumPy (+ a fused path) and a long sequential
scanof small steps loses to it (~0.1-0.8x for K up to 40). So: GPU -> big win, CPU -> use the host EM. (The CPU win from A2 is the single-pass scoring jit,jit_seq_log_density(), ~8x.) Note: jax-metal is version-pinned – the GPU result above used Python 3.11 + jax/jaxlib 0.4.34 + jax-metal 0.1.1; newer jaxlib emits StableHLO that jax-metal 0.1.1 cannot compile.