Extending mixle

Extend mixle by adding behavior to the existing contracts rather than by special-casing fit loops. A new family, engine, backend, or PPL constructor should make itself visible through the same estimator, encoder, capability, and inference surfaces as the built-in objects.

Add a Distribution Family

A full distribution family usually has five pieces:

Distribution

Stores fitted parameters. Implements log_density(x), sampler(), estimator(), and, for vectorized paths, seq_log_density(encoded) and dist_to_encoder().

Sampler

Produces seeded samples. Seed handling should be deterministic and local to the sampler.

Estimator

Declares the family to fit. Implements accumulator_factory() and estimate(nobs, suff_stat).

Accumulator

Collects sufficient statistics or training telemetry. Implements scalar and sequence update paths, combine, value, and from_value.

DataEncoder

Converts raw Python observations into encoded arrays or structured payloads.

Use a nearby family as the template. For example, start from another continuous univariate family when adding a scalar density, or from an existing combinator when the new object delegates to child distributions.

Minimum Family Checklist

  • scalar log_density agrees with vectorized seq_log_density;

  • sampler is reproducible under a fixed seed;

  • estimator recovers parameters on synthetic data;

  • accumulator values combine correctly across chunks;

  • encoder accepts the public observation shape;

  • str/serialization behavior is tested when the family supports it;

  • optional capability methods are present only when they are correct.

Capabilities

Capabilities are behavior contracts. Add them when the object truly supports the behavior:

Capability behavior

Implement when

enumeration

the support can be traversed in descending probability

finite support

support size is known and finite

rank/seek

the family supports structural unranking or count-budget indexes

conjugate update

priors and posterior updates are mathematically valid

conditioning/marginalization

the family can return exact conditional or marginal distributions

latent posterior

hidden assignments or paths can be queried

backend scoring

scoring can run safely on a compute engine

Use mixle.describe during development to confirm the object advertises the expected behavior.

For the full list of behavior contracts and predicates, see Capabilities And Contracts.

Add a Combinator

Combinators should preserve child capabilities when possible. For example, a composite can enumerate when its children can enumerate; a transform can expose density only when the change of variables is valid; a latent wrapper can expose posterior queries only when it can compute them.

Keep the observation shape obvious. A user should be able to look at one raw record and understand which child handles each part.

Add a Neural Leaf

Neural leaves still participate through estimators and distributions. The parent model should not need to know whether a child M-step is closed-form or gradient-based.

Practical rules:

  • keep the public observation shape explicit, such as (context, target);

  • keep module ownership and optimizer lifetime clear;

  • avoid buffering entire corpora in accumulators unless the design requires it;

  • expose telemetry separately from sufficient statistics when training is streamed;

  • test both scalar and encoded scoring paths.

Add a PPL Constructor

The PPL lowers symbolic RandomVariable expressions to concrete distributions, estimators, or inference targets. Add or register a lowering rule instead of branching inside the fit loop.

The PPL constructor should document:

  • fixed parameter slots;

  • free parameter slots;

  • prior-bearing slots;

  • constraints or named parameters;

  • the lowered distribution/estimator family.

Add an Engine or Backend

Engines own array math. Backends own where encoded data are folded.

For a new engine:

  • implement the ComputeEngine surface;

  • register array types with register_array_type;

  • make host/device boundaries explicit;

  • provide to_numpy behavior;

  • add precision and dtype normalization where needed.

For a new encoded-data backend:

  • preserve the [(count, payload)] contract;

  • support estimator encoders without changing model code;

  • keep partitioning compatible with data sample structure;

  • expose failures clearly when optional dependencies are missing.

The lower-level compute contracts, declaration metadata, encoded payloads, and kernel-selection machinery are documented in Compute Layer.

Public Surface Checklist

Public extensions should update:

  • the relevant guide page;

  • API Overview if a new public namespace or common import is added;

  • examples or tutorials when the behavior is user-facing;

  • generated API reference pages via make -C docs apidoc.

Testing Requirements

Add tests at the same level as the extension:

  • unit tests for the family or helper;

  • estimator recovery tests on synthetic data;

  • vectorized/scalar parity tests;

  • capability-specific tests;

  • optional-dependency skip markers where needed;

  • integration tests when the extension participates in optimize.

Design Rule

Prefer adding one capability-aware object over adding one new branch to a central algorithm. The rest of mixle should discover the behavior through the contract.