|
| 1 | +#design #later |
| 2 | + |
| 3 | +> Could `Mycelium` store its beliefs (and perhaps its factors) as the types RxInfer.jl already |
| 4 | +> uses, instead of its own structs that are converted on the way in and out? **For beliefs, |
| 5 | +> yes, by wrapping**: keep this project's abstract types and dispatch, store an ExponentialFamily |
| 6 | +> object inside. **For factors, no**: the two packages mean different things by a factor, and the |
| 7 | +> useful sharing is RxInfer's approximation code inside a hand-written nonlinear factor. Noted |
| 8 | +> for later; nothing here is built. |
| 9 | +
|
| 10 | +> Sources: original to this vault (design); the source of ReactiveMP.jl 5.x (`src/approximations/`: linearization, unscented, Laplace, CVI) and of BayesBase.jl / ExponentialFamily.jl, read October 2026; Bagaev, Podusenko & de Vries, *RxInfer: A Julia package for reactive real-time Bayesian inference*, JOSS 2023 |
| 11 | +> |
| 12 | +> Bibliography: [[Bibliography#^bagaev2023rxinfer|Bagaev et al. 2023]] |
| 13 | +> |
| 14 | +> Theory: [[RxInfer as a Backend]] · [[Belief Algebra]] · [[interop]] |
| 15 | +
|
| 16 | +## 1. Where things stand |
| 17 | + |
| 18 | +Beliefs are this project's own structs (`GaussianBelief`, `CategoricalBelief`, …, in |
| 19 | +`LenticulumCore`), with their rules in `Mycelium`. `as_distribution` / `as_belief` convert to |
| 20 | +and from the BayesBase / ExponentialFamily types RxInfer passes ([[interop]]), allocating a new |
| 21 | +object each way. |
| 22 | + |
| 23 | +The allocation is not the problem: copying an $n \times n$ precision is cheap next to anything |
| 24 | +done with it, and `as_distribution` could share the arrays instead of copying them. What a |
| 25 | +shared type would really buy is **shared rules**: ExponentialFamily already has products, |
| 26 | +entropies, densities and natural-parameter arithmetic for dozens of families (Gamma, Wishart, |
| 27 | +Beta, Dirichlet, von Mises, …), which `Mycelium` would otherwise re-implement one by one. |
| 28 | + |
| 29 | +## 2. Three ways to hold a belief |
| 30 | + |
| 31 | +| | own struct (today) | use the RxInfer type directly | wrap the RxInfer type | |
| 32 | +|---|---|---|---| |
| 33 | +| example | `GaussianBelief(η, Λ)` | `MvNormalWeightedMeanPrecision` *is* the belief | `GaussianBelief{D}(d::D)`, `d` an ExponentialFamily object | |
| 34 | +| conversion | allocate | none | unwrap / wrap, no copy | |
| 35 | +| own supertype and dispatch (`AbstractBelief`) | yes | **no**: an external type cannot subtype ours | yes | |
| 36 | +| families available | the six we wrote | all of ExponentialFamily's | all, one thin wrapper each | |
| 37 | +| our extras (improper messages, labels, `TrivialBelief`, polarity-aware rules) | yes | must be bolted on outside the type | yes, in the wrapper | |
| 38 | +| dependency of the belief layer | `LinearAlgebra` | ExponentialFamily (heavy) | ExponentialFamily (heavy) | |
| 39 | + |
| 40 | +Using the types directly loses the one thing the project's design rests on: a belief type |
| 41 | +hierarchy it controls (`AbstractBelief`, `isexact`, `combine` rules with polarity in mind). |
| 42 | +Wrapping keeps it and still gets every rule for free by delegation: `combine` on two wrapped |
| 43 | +Gaussians calls BayesBase's `prod`; entropy calls `entropy`; a family with no wrapper yet can |
| 44 | +use a generic `ExpFamBelief{D}`. |
| 45 | + |
| 46 | +## 3. Where the wrapped beliefs would live |
| 47 | + |
| 48 | +Not in `LenticulumCore`: ExponentialFamily pulls in ForwardDiff, SpecialFunctions, HCubature |
| 49 | +and more, and the core is meant to be light, like LuxCore. This is the `MyceliumCore` / |
| 50 | +`Mycelium` split discussed earlier ([[Belief Algebra]] §6, item 3), now with a reason: |
| 51 | + |
| 52 | +| package | content | dependencies | |
| 53 | +|---|---|---| |
| 54 | +| `LenticulumCore` | abstract types, the factor interface, today's light beliefs | light | |
| 55 | +| `MyceliumCore` (new) | graphs, schedules, `combine` for the light beliefs | light | |
| 56 | +| `Mycelium` | wrapped ExponentialFamily beliefs, their rules by delegation, the RxInfer bridges | ExponentialFamily, BayesBase | |
| 57 | + |
| 58 | +Factor packages that only need Gaussians and points keep depending on the light layers. |
| 59 | + |
| 60 | +## 4. Factors: share the approximations, not the structs |
| 61 | + |
| 62 | +A Lenticulum factor and an RxInfer node are different objects: |
| 63 | + |
| 64 | +| | Lenticulum factor | RxInfer node | |
| 65 | +|---|---|---| |
| 66 | +| is | a struct with parameters and state (`ps`, `st`), like a Lux layer | a declaration (`@node`) plus one rule per interface (`@rule`) | |
| 67 | +| direction | chosen per call by a `Polarity` | one rule per direction, written in advance | |
| 68 | +| learned | yes, by gradients through inference | parameters by inference over them | |
| 69 | + |
| 70 | +A common struct would fit neither. What can be shared is the bridge in both directions, step 3 |
| 71 | +of [[RxInfer as a Backend]] §4 (a learned relation as an RxInfer node), and, the part that |
| 72 | +matters most, **RxInfer's approximation code inside a hand-written nonlinear factor**. |
| 73 | + |
| 74 | +ReactiveMP's delta nodes already implement, as reusable functions, the Gaussian approximations |
| 75 | +through a known function $f$ (`src/approximations/`): `Linearization` (first-order Taylor), |
| 76 | +`Unscented` (sigma points), Gauss–Hermite and spherical-radial cubature, `Laplace`, and CVI. A |
| 77 | +`NonlinearFactor(f; method = Unscented())` in `Mycelium` could call these for its messages, |
| 78 | +which gives Lenticulum the factor it lacks ([[Open Problems in Implicit Diffusion Learning]] |
| 79 | +I11) without writing a sigma-point library. The cost is a ReactiveMP dependency (heavier than |
| 80 | +ExponentialFamily, and its internals change between versions); the fallback is writing |
| 81 | +linearisation and the unscented transform here, about a hundred lines with |
| 82 | +DifferentiationInterface. |
| 83 | + |
| 84 | +A hand-written nonlinear factor and a learned one would then sit side by side in one graph: |
| 85 | +known physics ($y = h(x)$, a measurement model) next to a learned relation. That is the |
| 86 | +combination real problems need (the particle and SLAM examples). |
| 87 | + |
| 88 | +## 5. Risks |
| 89 | + |
| 90 | +- **Load time and weight.** ExponentialFamily and ReactiveMP are large; time to first |
| 91 | + inference grows. Hence the split in §3. |
| 92 | +- **Improper messages.** This project's likelihood messages often have a singular precision. |
| 93 | + ExponentialFamily represents them, but its `mean`, `cov` and `entropy` assume proper |
| 94 | + distributions; the wrapper must keep this project's checks. |
| 95 | +- **API stability.** BayesBase and ExponentialFamily are stable packages; ReactiveMP's |
| 96 | + approximation internals are less so. Depend on the former; treat the latter as optional. |
| 97 | +- **Automatic differentiation.** The implicit adjoints differentiate through beliefs only in |
| 98 | + a few places, but Zygote or Enzyme through ExponentialFamily types is untested here. |
| 99 | +- **Labels and polarity.** Categorical labels and polarity-aware rules have no counterpart |
| 100 | + upstream; they stay in the wrappers. |
| 101 | + |
| 102 | +## 6. When to do it |
| 103 | + |
| 104 | +The trigger is the **first non-Gaussian exponential family** `Mycelium` needs: a Gamma belief |
| 105 | +for a learned noise precision, a Wishart for a covariance, a Dirichlet for categorical |
| 106 | +parameters. Writing those by hand would duplicate ExponentialFamily, so at that point wrap |
| 107 | +instead (§2) and split the package (§3). The nonlinear factor (§4) can come earlier and |
| 108 | +independently, with ReactiveMP's approximations as an optional extension and a small built-in |
| 109 | +linearisation as the default. |
| 110 | + |
| 111 | +Related: [[RxInfer as a Backend]], [[Belief Algebra]], [[interop]], [[gaussian_belief]], |
| 112 | +[[Related Julia Projects]], [[Why Julia]] |
0 commit comments