mixle.reason.inference_program module¶
Multi-hop inference programs over composed M0/L2 conditioning queries (roadmap M5).
L2’s CrossModalJoint answers one conditioning query exactly:
condition on any subset of a joint’s named modalities, infer any other subset, all within a SINGLE
shared latent regime. Real cross-modal reasoning needs to chain THROUGH modalities that are not all
tied by one joint – the card’s own example is (image field -> shared latent -> predicted field ->
text field), where the image<->latent relationship and the latent<->text relationship are two
separately-fit joints. This module adds exactly that composition and nothing else: a small, explicit,
LINEAR chain of infer() calls (a path DAG – no
free-form planning, no branching/merging in v1; see notes/designs/M5.md part (a)).
The one real design problem a chain introduces that a single hop does not have: hop i’s posterior
over the field hop i+1 needs to condition on is a full DISTRIBUTION, not a single value, but
CrossModalJoint.infer demands a concrete observed value. Two receipted ways to bridge that gap are
implemented (notes/designs/M5.md part (b)):
propagation="sampled"(default) – Monte-Carlo particles carried hop to hop, exactly the “one particle, one draw, carry the weight” pattern already proven out bymixle.reason.cycle_consistency.joint_cycle_consistency_receipt()’s round trip. Unbiased; converges to the exact marginal asn_samplesgrows.propagation="moment"– collapse each non-final hop’s posterior to its own point estimate (analytic mean for a Gaussian-like leaf, arg-max of the component-weightedpmapfor a categorical leaf) and carry that one point forward. Cheap (oneinferper hop instead ofn_samples), and HONESTLY the wrong choice whenever a downstream hop is sensitive to the intermediate’s uncertainty, not just its central tendency – seemixle/tests/inference_program_test.py::InferenceProgramTwoHopVsNaiveTestfor a fixture where this mode measurably diverges from the closed-form answer that"sampled"recovers.
- class InferenceHop(joint, target, carry=<factory>, extra_evidence=<factory>)[source]
Bases:
objectOne
CrossModalJoint.infercall in a chain.targetis the tuple of modality names this hop infers.carryrenames a value produced by the PREVIOUS hop’stargetinto this hop’s own joint’s modality-name space ({prior_target_name: this_hop_evidence_name}) – the two joints need not share a naming convention. Ignored (and must be empty) for the first hop in a program, which conditions on the program’s externalevidenceinstead.extra_evidenceis fixed evidence local to this hop (observed independently of anything carried down the chain).
- class ProgramReceipt(propagation, n_hops, hop_targets, n_particles)[source]
Bases:
objectWhat
run_inference_program()actually did – the M5 analogue of M0’sConditionReceipt.
- class ProgramPosterior(mixture, target, receipt)[source]
Bases:
objectA completed inference program’s result: the same
sample/log_density/mean-shaped contract M0’s ownPosteriorexposes, over the final hop’stargetfields, so a program’s output composes with the same downstream code (e.g. the language<->belief bridge) that already consumes a single-hop posterior.
- run_inference_program(evidence, hops, *, propagation='sampled', n_samples=500, seed=0)[source]
Run a linear chain of
InferenceHopconditioning queries, propagating uncertainty (or not – see module docstring) between hops.evidenceconditions the FIRST hop only; later hops condition onextra_evidenceplus whatever theircarrymapping pulls from the previous hop’s posterior overtarget.