Lenticulum.jl

Implicit machine learning on factor graphs: learning relations $R_\theta \subseteq X_1\times\cdots\times X_n$ rather than functions $f_\theta : X \to Y$.

Under development

Interfaces are not stable. Several packages document known gaps rather than hiding them; the per-package pages say what is missing.

The idea in one page

An ordinary neural network is a function. You feed it an input, you get an output, and the direction is fixed when you build the layer. Lenticulum's unit of computation is a factor: a relation among several named channels, with no distinguished input or output. Which channels are inputs is decided per call, and the same factor can be run in whichever direction the graph needs.

A diffusion model of points on a circle as a relation: its field (left) and three queries answered by one model (right)

Left: the field of a diffusion model of points on a circle; its stable roots are the relation. Right: "given $x$, find $y$" has two answers, "given $y$, find $x$" uses the same model the other way round, and a query just off the circle returns the nearest ridge point. The first tutorial builds exactly this picture.

# a Lux layer knows which side is the input
Dense(3 => 5)

# a factor does not — you tell it, when you use it
GaussianFactor(1 => 1; noise = Q, channels = (:x, :y))

Factors are wired into a factor graph — a bipartite graph of factors and variables — and inference is message passing rather than a forward pass. That is the whole design:

Lux.jlLenticulum.jl
unita layer: a functiona factor: a relation
directionfixed at constructionchosen per message
wiringa DAGany connected graph
running itone forward passscheduled messages until convergence
backward passa gradienta posterior belief

The packages

The project is one umbrella package over five smaller ones. Most users need the top two rows.

packagewhat it gives you
LenticulumCorewhat a factor is: channels, polarities, beliefs, energies
Myceliumhow factors are wired and scheduled: graphs, messages, free energy
Lenticulumthe linear-Gaussian factors, and Gaussian beliefs
VariationalDiffusiona diffusion model as a factor (VP-SDE, RED-Diff)
ImplicitLayersDEQs and neural ODEs as factors
Adversarialimplicit generative models — generators and density ratios

LenticulumCore and Mycelium between them define the framework; the other three are factor libraries built on it, and each can be ignored if you do not need that model family.

Installation

Not registered. From a clone:

using Pkg
Pkg.develop(path = "/path/to/Lenticulum.jl")

# the factor libraries live under lib/ and are separate packages
Pkg.develop(path = "/path/to/Lenticulum.jl/lib/Mycelium.jl")
Pkg.develop(path = "/path/to/Lenticulum.jl/lib/ImplicitLayers.jl")

Every package depends on LuxCore, not Lux, so wrapping a Lux model costs nothing and pulls in nothing. Neither Lux nor any automatic-differentiation package is a dependency of anything here.

Where to go next

Where the theory is

Not here. This repository is also an Obsidian vault, and the mathematics — the categorical foundations, the papers, the derivations, and an honest account of what does not work — lives there. It is rendered as a website and deployed beside this one:

The theory vault →

See the Theory vault page for a map of it.

Per-file implementation notes sit next to the source they describe, as *.md beside *.jl, and are part of the vault too.

These docs describe the code. If a docstring below cites something in double brackets like [[Bethe Free Energy]], that is a link into the vault, not a broken link on this site.