Skip to content

Commit 0daf00b

Browse files
DanielBoigkclaude
andcommitted
Vault: hand-written vs learned nonlinear factors; sharing types with RxInfer
Clarify that Lenticulum lacks factors from hand-written nonlinear functions (RxInfer's delta nodes), not nonlinear factors as such (I11, RxInfer as a Backend). New design note for later: wrap ExponentialFamily types as beliefs instead of converting, a MyceliumCore/Mycelium split, and reuse of ReactiveMP's linearisation/unscented code for a NonlinearFactor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQTuuzmdaTgshFtQCW3Bb9
1 parent 69bdf63 commit 0daf00b

4 files changed

Lines changed: 131 additions & 3 deletions

File tree

‎vault/Design/RxInfer as a Backend.md‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,14 @@ reaches well beyond conjugate models.
4141
| `combine`, densities, `moment_match` ([[Belief Algebra]]) | BayesBase / ExponentialFamily products and projections | far more families, tested |
4242
| Bethe free energy (`bethe_free_energy`) | constrained Bethe free energy | local constraints, VMP |
4343
| Gaussian and linear factors | a library of nodes and rules | breadth |
44-
| — | delta nodes, non-conjugate updates | nonlinear factors, which `Lenticulum` lacks |
44+
| — | delta nodes, non-conjugate updates | factors from **hand-written** nonlinear functions, $y = f(x)$ with $f$ given, which `Lenticulum` lacks (its nonlinear factors are learned: diffusion, DEQ, neural ODE) |
45+
46+
"Nonlinear" needs care here. A diffusion factor is as nonlinear as a factor gets, but its
47+
nonlinearity is learned and implicit (a relation, answered by root finding in any direction).
48+
A delta node is the other kind: the user writes $y = f(x)$, and RxInfer turns the known $f$ into
49+
Gaussian messages by linearisation, the unscented transform or CVI, which is how an extended or
50+
unscented Kalman filter is written there. A generic factor of that kind, `NonlinearFactor(f)`,
51+
is what `Lenticulum` is missing ([[Open Problems in Implicit Diffusion Learning]] I11).
4552

4653
Everything on the **probabilistic, specified-model** side of this project would run faster and
4754
more completely there. The localisation tutorial, for example, is a model RxInfer handles
@@ -95,5 +102,9 @@ Keep `Mycelium` as the small reference engine that implements the theory, and ad
95102
Item 3 is the real backend question answered in the useful direction: RxInfer as the engine
96103
*around* learned relations, with the relations staying in this project.
97104

98-
Related: [[Related Julia Projects]], [[Belief Algebra]], [[Inference Signatures]],
105+
Whether `Mycelium` could store RxInfer's own types instead of converting, and how a
106+
hand-written nonlinear factor could reuse RxInfer's approximations: [[Sharing Types with RxInfer]]
107+
(noted for later).
108+
109+
Related: [[Sharing Types with RxInfer]], [[Related Julia Projects]], [[Belief Algebra]], [[Inference Signatures]],
99110
[[Messages are Inversions]], [[Why Julia]], [[The Two-Part Diagram]]
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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]]

‎vault/Families/Diffusion/Open Problems in Implicit Diffusion Learning.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,10 @@ benchmark, and a problem with tens of coordinates, are the obvious next tests; I
179179
### I11. Nonlinear factors around the learner
180180

181181
Latent states with nonlinear dynamics (the particle tutorial's noisy positions, SLAM) need
182-
nonlinear Gaussian factors and Gauss–Newton in the graph, which Lenticulum does not have yet.
182+
nonlinear Gaussian factors and Gauss–Newton in the graph, which Lenticulum does not have yet:
183+
a factor from a **hand-written** function $y = f(x)$, with Gaussian messages by linearisation or
184+
the unscented transform (RxInfer's delta nodes, [[RxInfer as a Backend]] §2). The learned
185+
factors are nonlinear too, but their nonlinearity comes from data.
183186

184187
## Summary
185188

‎vault/Map of Content.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ some parts known and some fitted, and a residual that means something in the dom
264264
subgraph-as-factor operation the vault records as missing
265265
- [[RxInfer as a Backend]] — the nearest neighbour as an engine: gains for the probabilistic part,
266266
five things a replacement would lose, and a connector with learned relations as RxInfer nodes
267+
- [[Sharing Types with RxInfer]] — (later) wrap ExponentialFamily types as beliefs instead of
268+
converting; reuse RxInfer's linearisation and unscented code for hand-written nonlinear factors
267269
- [[Why Julia]] — why not C++, Rust, PyTorch, JAX or Mojo: in Julia ordinary code *is* the compiled,
268270
differentiable computation graph, with evidence from this repository and the costs stated
269271
- [[Parallelism and Compilation]] — parallelism, GPUs and XLA/MLIR in a *dynamic* SLAM

0 commit comments

Comments
 (0)