# Evolution as inference
*This page describes the `ppl` feature (on by default). Without it, fugue-evo
is a standalone evolutionary-computation library with no
probabilistic-programming dependency at all.*
fugue-evo's inference layer makes "evolutionary algorithms as probabilistic
programs" literal. Given a fitness `f` and a prior over genomes, the
Boltzmann/Gibbs posterior
```text
pi_beta(x) ∝ p(x) · exp(beta · f(x))
```
is not merely a mathematical analogy — it is a [fugue](https://fugue.run)
program, and every sampler in the layer is fugue's own inference machinery run
against that program.
<div class="fugue-explorable" data-viz="smc-inference" data-seed="11" data-pop="240" data-rungs="18" data-betamax="1"></div>
*This is the whole idea, live: fugue-evo's real inference layer (compiled to
WASM) runs tempered SMC against a twin-peaks Boltzmann target. The yellow heat
is the exact tempered density `π_β ∝ p(x)·exp(β·f(x))`, recomputed each rung
as β climbs from the prior (β = 0, the blue rings) to the posterior (β = 1);
the green particles are the SMC population — reweighted, resampled when the
ESS drops (watch the ESS readout flash), rejuvenated by typed MH, and mixed
across the two modes by the crossover kernel (a product-target Metropolis
swap of x-coordinates). Push β MAX past 1 to watch inference become an
annealed optimizer, and drag the seed to replay a different history. The
log Z readout is the running evidence estimate — a model score no classic GA
can report.*
## Priors are programs
The `GenomePrior` trait replaces any notion of a built-in prior enum:
```rust,ignore
pub trait GenomePrior: Clone + Send + Sync + 'static {
type Genome: TraceGenome;
fn model(&self) -> fugue::Model<Self::Genome>;
}
```
The model samples the genome's canonical trace sites (`gene#i`, `bit#i`,
`perm#i`, …) and returns the assembled genome — the model's return value *is*
the decode. Anything expressible as a fugue model is a valid prior:
correlated coordinates, hierarchical scales, variable-length genomes.
Built-ins: `UniformBoxPrior`, `GaussianPrior`, `BitStringPrior`,
`PermutationPrior` (a Fisher–Yates/Lehmer-code program whose single-site
moves always decode to valid permutations), and `ArithmeticGrammarPrior`
(below).
There is deliberately **no hand-written density code** anywhere in the layer:
prior mass, tempered joints, and MH acceptance ratios are all obtained by
running or replaying the target program through fugue's handlers.
## The target as a program
`EvolutionModel::new(prior, fitness)` assembles
```rust,ignore
factor(beta * fit).map(move |_| g)
})
```
Two builders exist because MH wants a fixed-beta target (`target_model()`)
while tempered SMC must receive the *untempered* factor (`smc_model()`):
fugue's `adaptive_smc` supplies beta by likelihood-tempering, applying it
exactly once.
## Samplers
- **`EvolutionChain`** — Metropolis–Hastings via fugue's
`adaptive_single_site_mh`. Typed proposals move every site kind: Gaussian /
log-space walks for reals, flips for bits, prior-resample for categorical
sites (permutation ranks), with reversible-jump corrections when a proposal
changes the model's structure.
- **`EvolutionSMC`** — tempered SMC via fugue's `adaptive_smc_with_kernel`:
an adaptive ESS-driven beta ladder from the prior to the posterior,
systematic resampling, per-particle MH rejuvenation, an optional
population-coupled **crossover kernel** (a product-target Metropolis move
that swaps an address block between two particles), and an unbiased
**log-evidence** estimate — a genuine Bayesian model score. Genomes are
recovered from bare particle traces by decode-replay.
## Genetic programming as exact inference
`ArithmeticGrammarPrior` is a probabilistic context-free grammar over
expression trees, written as a fugue program with tree-path addresses
(`node#leaf`, `node/0#func`, `node/0/1#const`, …). Because an execution's
structure is encoded in its own choices:
- **subtree regeneration mutation** is fugue's ordinary single-site MH — a
flip of one `#leaf` bit births or kills the subtree below it, fresh
structure drawn from the grammar, reversible-jump corrections applied
automatically;
- **subtree crossover** is the crossover kernel with a mask that grafts the
subtrees under one shared node path between two particles;
- **parsimony** is the grammar prior itself — deeper trees pay more mass, no
ad-hoc penalty needed.
The flagship example, `examples/symbolic_regression_inference.rs`, fits
`x² + 1` by sampling the posterior over programs and compares function-set
grammars by Bayes factor. The regression test
`test_symreg_recovers_known_expression` pins the recovery.
## The layer boundary
The `TraceGenome` extension trait (`genome::trace_genome`) is the boundary:
classic algorithms require only `EvolutionaryGenome`; genomes that also
implement `TraceGenome` can be driven by the inference layer. The canonical
trace encoding is pure data (zero stored log-probabilities); probability mass
always comes from scoring under a prior program.