mixle.reason.model module¶
Trainable cross-modal reasoning model with a shared latent.
This module learns per-modality encoders and decoders jointly from unlabeled multimodal records. The latent is not observed; training maximizes a Product-of-Experts variational lower bound for a multimodal VAE-style model.
Each modality
mhas an encoderq_m(z | x_m) = N(mu_m, diag(sig_m^2)).The belief given any subset of modalities is the product of experts with the prior: precisions add, so more modalities produce a sharper belief. This matches
mixle.inference.belief.GaussianBelief.fuse()with learned experts.Each modality has a decoder
p(x_m | z); training reconstructs every modality from the fused latent. Modality-subset subsampling lets inference work from one modality, all modalities, or any subset between them.
After training: belief(obs) returns q(z | available modalities) as a
GaussianBelief, and predict(obs, target)
generates a missing modality from the available ones. Uncertainty remains part
of the object: the returned belief is a distribution, sharpened by each
modality in proportion to its learned precision.
Torch is imported lazily; mixle.reason exposes this via a deferred attribute.
- class CrossModalModel(latent_dim, *, seed=0)[source]
Bases:
objectA multimodal Product-of-Experts VAE: one shared latent learned from many modalities, unsupervised.
- add_modality(name, in_dim, *, hidden=(64,))[source]
Register a NEW modality with a learned encoder
q(z|x)and decoderp(x|z).MXR-080-0276: a duplicate
nameis rejected rather than silently replaced. Silently replacing a registered modality’s encoder/decoder previously left_fittedand any stored conformal radii intact, so a later prediction could combine a fresh, untrained replacement encoder/decoder with calibration computed against the OLD one and still advertise calibrated coverage. Usereplace_modality()to deliberately replace an existing modality – it invalidates fitted/calibration state as part of the same operation.- Raises:
ValueError –
nameis already registered.- Parameters:
- Return type:
CrossModalModel
- replace_modality(name, in_dim, *, hidden=(64,))[source]
Deliberately replace a registered modality with a fresh, untrained encoder/decoder pair.
MXR-080-0276: unlike
add_modality()(which rejects a duplicate name outright), this method exists specifically to allow overwriting an existing modality’s encoder/decoder – but doing so invalidates every dependent fitted/calibration artifact in the SAME operation (see_invalidate_fitted_state()):_fittedis cleared, training metadata is cleared, and every stored conformal radius is cleared, since each was computed against this modality’s prior encoder/decoder. Callfit()(and re-calibrate()any target) again before relying on beliefs/predictions/intervals.
- fit(data, *, epochs=600, lr=3e-3, beta=0.5, subsample=True)[source]
Train encoders and decoders jointly on unlabeled multimodal data.
datamaps each registered modality name to an(N, in_dim)array (all modalities share the sameNrows – rowiis one record’s several views).betaweights the KL rate; withsubsample=Truethe ELBO is also evaluated on each single-modality subset so the model can inferzfrom any one modality alone (the MVAE training trick).MXR-080-0277: the complete aligned training table – equal row counts across every modality, each modality’s declared
in_dimmatching its data’s actual width, non-empty data, and finite values – is validated up front, before any per-modality normalization statistic or the model’s fitted state is touched.epochsis validated as a genuine positiveint: a zero or negative value previously still set_fitted = Trueon encoders/decoders that were never actually updated (their random init weights, dressed up as a “trained” model).- Raises:
ValueError –
data’s modalities don’t match the registered set; a modality’s data is empty, has the wrong width, contains non-finite values, or has a row count that disagrees with the other modalities; orepochsis not positive.TypeError –
epochsis not a plainint(aboolor a float, whole-valued or not, is rejected).
- Parameters:
- Return type:
CrossModalModel
- belief(obs)[source]
The belief
q(z | available modalities)as aGaussianBelief(product of experts).Requires a prior
fit()call._fittedwas tracked but never checked: a freshly constructed model’s encoders carry their random init weights, so every downstream consumer of this method –encode(),predict(),calibrate(),predict_interval()– could silently return a meaningless, randomly-initialized “belief” with no indication anything was wrong. Checked here once, since all four route through this method.obscarries exactly ONE observation per modality: each value must be a 1-D array of lengthin_dim(MXR-080-0278). Previouslynp.atleast_2dsilently accepted a(B, in_dim)batch, fused it through the product-of-experts as if it were legitimate – modalities with unequal batch sizes could broadcast into unintended cross-row pairings – and then returned only row 0, discarding the rest with no signal anything was dropped. No caller in this codebase passes a genuine batch here; call this once per observation.- Raises:
ValueError – some modality’s observation is not a 1-D array of the registered width.
- Parameters:
- Return type:
GaussianBelief
- encode(obs)[source]
The posterior-mean latent code for
obs(a shared-space embedding usable as store keys).
- predict(obs, target)[source]
Generate the
targetmodality from the modalities inobs(cross-modal generation).obstakes exactly one observation per modality; seebelief()(MXR-080-0278) – this method inherits that contract entirely through its call below.
- calibrate(cal_data, target, *, alpha=0.1)[source]
Calibrate cross-modal prediction of
targetfor finite-sample coverage (split conformal).The holdout is SPLIT internally: the first half estimates the per-dimension residual scales, and only the second half is scored and ranked against those FIXED scales – the same fixed scales a fresh query is scored with, which is what makes the query’s score exchangeable with the ranked ones. Estimating the scales on the same residuals that get ranked broke that exchangeability, and the break was not cosmetic: with i.i.d. residuals and every stated assumption holding, measured joint coverage at nominal 0.90 was 0.854 in two dimensions, 0.584 in ten, and 0.179 in fifty (STAT-RR21-06) – each calibration residual helped set its own yardstick, so query scores ran systematically larger than the ranked ones. The radius is the standard conformal
ceil((n_rank+1)(1-alpha))-th SMALLEST ranked score (ascending order statistic; an earlier docstring said “largest”, contradicting the implementation). Using the max over dimensions makes the guarantee simultaneous:predict_interval()returns a box whose joint coverage over the whole target vector is>= 1 - alpha– distribution-free, regardless of model specification (unlike the Gaussian posterior interval). Scope: that coverage is marginal over the calibration draw and the query jointly, under exchangeability of the calibration pairs and served traffic; it is not a per-query certainty, and distribution shift voids the statement silently – re-measure on drifted traffic. The split halves the effective ranking sample, so small holdouts reach+infradii (an unbounded box) sooner – that is the honest price of a guarantee that actually holds.MXR-080-0279: every split-conformal precondition is validated and the method fails closed – raises without storing anything – before a single score is computed. Previously,
alphaoutside(0, 1)could select a negative-indexed or otherwise unrelated order statistic instead of raising; an empty or wrong-width holdout produced an opaque broadcastingValueError(or worse, silently proceeded on a mismatched pairing); and non-finite holdout values propagated into a NaN radius stored exactly like a valid one. A stored radius is later presented, viapredict_interval(), as carrying a genuine finite-sample coverage guarantee, so it must never come from anything but a valid computation on a valid holdout.- Raises:
KeyError –
targetis not registered, orcal_datais missing a modality needed to predict it.ValueError –
alphais not strictly between 0 and 1; the holdout is empty; a modality’s holdout width doesn’t match its registeredin_dim; a modality’s holdout row count disagrees with the others; or any holdout value is non-finite.
- Parameters:
- Return type:
CrossModalModel
- predict_interval(obs, target)[source]
A conformally-calibrated prediction box
(lower, upper)fortargetgivenobs.Requires a prior
calibrate()call fortarget. Coverage is distribution-free and simultaneous:P(y in box) >= 1 - alphajointly over the whole target vector – FOR THE PREDICTOR THAT PRODUCED THE CALIBRATION SCORES. Refitting clears the stored radii (STAT-RR22-08); the guarantee is additionally conditional on the encoders/decoders not being mutated in place between calibration and serving, which no runtime check can detect – recalibrate after any parameter update.