implementation

DiffusionFactor: the point where ImplicitREDDiff’s selection matrices stop being notation and become a LenticulumCore.Polarity.

Sources: code: factor.jl, gaussian.jl

Theory (CT-ML wiki): Bayesian Inversion · Variational Free Energy

1. P is derived from the polarity, not configured

The note asks for diagonal selection matrices with and . All of that already exists in LenticulumCore.channels:

ImplicitREDDiff.mdLenticulumCore
the channels with Observed()
the channels with Unobserved()
the channels with Latent()
ispartition, guaranteed by the NamedTuple
channel_precision(p, name)
default_precision(Observed()) == Inf

So precision_vector(f, p) is a reading of the polarity, and the factor has no configuration of its own for it. The channels tile one in declaration order, and blockranges returns index ranges — a diagonal 0/1 matrix is a wasteful way to write a range.

Note which way round the default falls: the hard clamp is the default and the soft clamp is what you opt into. That is the right way round — an observation is evidence unless you say otherwise — and it is LenticulumCore’s choice, inherited for free.

2. One network for the joint state

predictor is a NoisePredictor over the whole , not one per channel. That is what makes this a relation rather than a bundle of conditionals: the diffusion model knows the joint distribution of all channels, so any subset can be inpainted from any other. A per-channel model could not do that, and would be an explicit learner wearing a costume.

3. supported_polarities under-reports on purpose

supports_polarity accepts any assignment over the channel set with at least one Unobserved() channel — including Latent() ones — because a joint diffusion prior really can answer all of them. supported_polarities enumerates only the “one unobserved, rest observed” cases.

The gap is deliberate: a scheduler needs a listable set, and the full set has elements. The predicate is the truth; the enumeration is a usable subset. channels.md notes that the two exist precisely so they can differ, and this is the first factor where they do.

4. The energy signature does not fit, again

LenticulumCore.energy(factor, x, a, y, ps, st) takes AutoBayes’ split. This factor needs the full state x, the reference , and the polarity (because depends on it), so the method here reads a as the polarity — using the latent-space slot to carry something that is not a latent value.

That is ugly and it is the same mismatch constraint.md §3 records from the acausal side: the core’s energy signature presumes a causal factor whose / split is fixed before energy is called. Two independent factor families have now hit it, which is the point at which it stops being a quirk and becomes an interface bug. Recorded, not fixed — the signature is used by Mycelium and Lenticulum and changing it for two callers is a LenticulumCore decision.

5. Implementation difficulties

5.1 The message is a posterior, not a likelihood

Every other factor in this project returns a likelihood from factor_message, with the prior divided out, because a variable of degree would otherwise count the prior times (gaussian.jl’s invert docstring is explicit about this).

A diffusion factor cannot divide its prior out. The prior is a neural network; there is no subtraction available. So this message double-counts whenever the target variable has degree

1.

Correct at degree 1, approximate otherwise

The usual inverse-problem setting — one diffusion prior, one measurement — is degree 1 and is fine. Put two diffusion factors on one variable and each will re-assert its own prior. Nothing detects this.

5.2 A Dirac message dominates everything it meets

invert returns a DiracBelief, faithfully to RED-Diff’s family. But Mycelium.combine gives a Dirac absolute precedence over any other belief (it is the limit). So a diffusion factor in a graph does not negotiate with its neighbours — it overrides them, and two diffusion factors disagreeing on one variable throw a “contradictory hard clamps” error rather than averaging.

The fix is the paper’s own general case: keep and return a GaussianBelief with finite precision. RED-Diff’s Section 3 derives it; the experiments drop it. Doing so here would need a variance update in the inner loop and is the most valuable missing piece in this file.

5.3 The free energy is Monte-Carlo noisy and its entropy term is not an entropy

local_free_energy re-runs the prox and evaluates (clamp, score) at the solution. Two consequences:

  • it is stochastic — calling it twice gives different numbers. Every other factor’s free energy is a closed form. Anything in Mycelium that compares free energies across iterations (a convergence check, a line search) will see noise.
  • the score summand plays the role of in the Bethe sum, but it is not the entropy of : is a Dirac and its differential entropy is . The substitution follows the vault’s first reading of the score-matching term as the entropy, because it depends on the learned distribution rather than the data point; that reading is revised in The Implicit Diffusion Factor as a Statistical Game §2 (it is the prior game’s energy). Either way it is not the Bethe formula’s .

So the counting correction of Bethe Free Energy does not apply to this factor in the way it applies to GaussianFactor, and mixing the two in one graph produces a total that is not for anything.

5.4 Running the prox inside local_free_energy is expensive and re-does work

The inversion has usually just been computed by factor_message; local_free_energy runs it again because the message store keeps beliefs, not the internal state the prox converged to. Caching the solution on st is the obvious fix and is not done.

5.5 assemble_state silently zero-fills

A channel with no incoming message contributes zeros to . That is safe only because such a channel’s precision is zero and the entry is multiplied out — but the invariant is implicit, and a future polarity that gives a message-less channel nonzero would read the zeros as data.

Related: schedule, predictor, reddiff, The Diffusion Factor, Channels and Polarity, Implicit Learners, Bethe Free Energy