EA-as-PPL: two-layer refactor — standalone classic EC + fugue-native inference (0.2.0) - #19
Merged
Merged
Conversation
…ce layer Phase 0: EvolutionaryGenome loses to_trace/from_trace/trace_prefix; they move to the TraceGenome extension trait (genome/trace_genome.rs) behind the new `ppl` feature. fugue-ppl becomes an optional dependency; the classic layer (all algorithms, operators, wasm crate) compiles fugue-free with --no-default-features --features std,parallel,checkpoint (595 tests pass). Phases 1-3: fugue_integration is rewritten as `inference` (deprecated alias kept): - GenomePrior replaces the closed Prior enum: the prior is an arbitrary fugue Model<G> returning the decoded genome. Ships UniformBoxPrior, GaussianPrior, BitStringPrior, PermutationPrior. All density code is deleted; scoring is ScoreGivenTrace replay. - Permutation's trace encoding switches to Lehmer-code ranks so single-site moves decode to valid distinct permutations (a value encoding makes every single-site change a duplicate and freezes the chain). - EvolutionModel<P, F> builds the Boltzmann target as a literal program: prior.model().bind(|g| factor(beta*f(g))). target_model() for MH (fixed beta), smc_model() untempered for SMC (beta applied exactly once by fugue's tempering - fixes the old double-counting). - EvolutionStep deleted; EvolutionChain delegates to fugue adaptive_single_site_mh (typed proposals move every site kind). New dead-chain regressions: test_bitstring_chain_moves, test_permutation_chain_moves. - EvolutionarySMC deleted; EvolutionSMC::run delegates to fugue adaptive_smc_with_kernel with the CrossoverKernel population move. EvolutionPosterior readouts recover genomes by decode-replay; log-evidence comes free and is checked against the analytic marginal likelihood. - BayesianAdaptiveGA repointed to the new model API; fugue Beta/Gamma swapped for rand_distr. Anchors kept green: EV-16 (conjugate SMC posterior), EV-52 (weighted trace = beta*f), EV-90 (MH bounds/truncated mean), EV-53 (conjugate updates/Thompson). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HmWLBR9Zq6hPG5o7URDuNJ
…ioning Phase 4 (inference/grammar.rs): ArithmeticGrammarPrior - expression trees as a probabilistic context-free grammar program with tree-path addresses (node/0/1#leaf, #func, #tkind, #var, #const). Because an execution's structure is encoded in its own choices, fugue's generic machinery becomes genetic programming: single-site MH on a structural site births/kills subtrees with automatic RJMCMC corrections (subtree regeneration), and subtree_crossover_mask() + fugue::CrossoverKernel grafts the subtrees under one shared node path between particles (subtree crossover). Parsimony is the grammar prior. EvolutionSMC::run_with_kernel added for explicit kernels. Flagship: examples/symbolic_regression_inference.rs - symbolic regression of x^2+1 posed as exact Bayesian inference (PCFG prior, Gaussian likelihood factor, tempered SMC with both genetic moves, MAP by decode-replay, posterior predictive, grammar comparison by Bayes factor). Pinned by test_symreg_recovers_known_expression plus grammar log-prior and depth-parsimony analytic tests. Phase 5: two-layer repositioning - lib.rs crate docs, README tagline and architecture sections, CLAUDE.md (with inference-layer invariants), website architecture page rewritten as "Evolution as inference" (replacing stale ConditioningHandler/ResamplingHandler content), CHANGELOG 0.2.0 entry with full breaking-change migration notes, version 0.2.0, crate description updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HmWLBR9Zq6hPG5o7URDuNJ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the fugue-evo half of the cross-repo EA-as-PPL plan (#18), consuming the upstream primitives from alexnodeland/fugue#45 (fugue-ppl 0.2.1). Two commits: Phases 0–3, then Phases 4–5.
Phase 0 — trait split +
pplfeature gateEvolutionaryGenomelosesto_trace/from_trace/trace_prefix; they move to the newTraceGenomeextension trait (genome::trace_genome, behind thepplfeature, default on).ChoiceValuere-export moves with them.fugue-pplbecomes optional:--no-default-features --features std,parallel,checkpointbuilds the entire classic layer (all 8 algorithms, operators, wasm crate) with zero fugue dependency — 595 tests pass in that config.Phases 1–3 — the inference layer, rebuilt on fugue (
fugue_integration→inference, deprecated alias kept)GenomePrior::model() -> fugue::Model<G>returns the decoded genome (the model's return value is the decode). Built-ins:UniformBoxPrior,GaussianPrior,BitStringPrior,PermutationPrior. ThePriorenum and all hand-written density code (log_prior_densityetc.) are deleted — every density is aScoreGivenTracereplay of the target program.prior.model().bind(|g| factor(β·f(g))).target_model()(fixed β) drives MH;smc_model()(untempered) drives SMC, where fugue's adaptive tempering applies β exactly once — fixing the old hand-rolled SMC's β double-counting.EvolutionStepdeleted →EvolutionChainoverfugue::adaptive_single_site_mh. The old proposal only perturbedF64choices, so BitString/Permutation chains silently never moved; typed proposals now move every site kind. New regressions:test_bitstring_chain_moves,test_permutation_chain_moves.Permutation's trace encoding is now the Lehmer code (ranks vs the shrinking available list), matching the sequential-categoricalPermutationPrior— this is what makes single-site permutation moves live (a value encoding turns every single-site change into a duplicate; a design refinement over the plan's ambiguous "Categorical(remaining)" wording).EvolutionarySMCdeleted →EvolutionSMC::run/run_with_kerneloverfugue::adaptive_smc_with_kernel: adaptive ESS-driven β ladder, per-particle rejuvenation, the population-coupledCrossoverKernel, and an unbiased log-evidence estimate.EvolutionPosteriorrecovers genomes by decode-replay.BayesianAdaptiveGArepointed to the new model API; conjugateBeta/Gammabacked byrand_distr.Phase 4 — genetic programming as exact inference
ArithmeticGrammarPrior(inference/grammar.rs): a PCFG over expression trees as a fugue program with tree-path addresses. Structure lives in the choices, so subtree regeneration is fugue's ordinary single-site MH (a#leafflip births/kills the subtree with automatic RJMCMC corrections) and subtree crossover is the crossover kernel with a shared-node-path union mask. Parsimony is the grammar prior itself.Flagship
examples/symbolic_regression_inference.rs: fits x²+1 by sampling the posterior over programs — MAP recovered by decode-replay (predictions match truth to 3 decimals), posterior predictive, and grammar comparison by Bayes factor (the parsimonious {Add,Sub,Mul} grammar beats the 12-function set by exp(10.6)). Pinned bytest_symreg_recovers_known_expression.Phase 5 — repositioning
Two-layer story in lib.rs docs, README, CLAUDE.md (with inference-layer invariants), and the website architecture page (rewritten from the stale
ConditioningHandlercontent). CHANGELOG 0.2.0 entry carries the full migration guide. Version0.1.1 → 0.2.0(pre-1.0 breaking-minor).Validation
--no-default-features --features std,parallel,checkpoint(fugue absent): 9/9 suites, 0 failures.CHECKPOINT_VERSION = 1, no genome struct fields changed).Depends on fugue#45 (path dependency already points at the sibling checkout;
version = "0.2.1"is honored on registry resolution).🤖 Generated with Claude Code
https://claude.ai/code/session_01HmWLBR9Zq6hPG5o7URDuNJ