Compare commits
No commits in common. "main" and "dev" have entirely different histories.
292 changed files with 15590 additions and 26 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -9,5 +9,3 @@ models/
|
||||||
data/
|
data/
|
||||||
configs/llm/_gen/
|
configs/llm/_gen/
|
||||||
logs/
|
logs/
|
||||||
# exploratory bundles kept on disk; tracked on the dev branch
|
|
||||||
results/_archive/
|
|
||||||
|
|
|
||||||
147
CLAUDE.md
Normal file
147
CLAUDE.md
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Current state: Layer 1 complete; Layer 1.5 (neural) in progress
|
||||||
|
|
||||||
|
- **Layer 1** (`src/inheritance/`) — **complete and validated.** All six experiments E1–E6, the
|
||||||
|
closed-form scientific-validation tests, figures, and reproducibility harness exist. Headline:
|
||||||
|
critical grounding `g* = 0.048 ≪ 1`; the E4 finding that mean-mixture distillation conserves
|
||||||
|
collapse while only a union-preserving max-merge realises the recombination benefit.
|
||||||
|
- **Layer 1.5** (`src/neural/`) — **in progress.** An architecture-general neural existence proof
|
||||||
|
(re-scoped Layer 2): the same Wright–Fisher abstractions realised in *real trained generative
|
||||||
|
models* (histogram bridge + RNN + MLP; VAE implemented but not fidelity-passing) on a
|
||||||
|
fully-synthetic sandbox with an exact oracle, plus real MNIST as a later secondary tier. See
|
||||||
|
`tasks/todo.md` for status and `~/.claude/plans/we-are-going-to-cheerful-fog.md` for the plan.
|
||||||
|
**Done:** scaffold, the histogram bridge gate (reproduces Layer 1 exactly), `bridge` (neural
|
||||||
|
g*=0.047 ≈ Layer 1), `collapse` (in RNN weights), `grounding` (refined; sign confirmed, threshold
|
||||||
|
softened by neural smoothing — see finding below), `architectures` (architecture-generality),
|
||||||
|
`recombination` (the E4 "merge, don't average" finding reproduced in real weights), all six
|
||||||
|
neural figures, and the **real-MNIST external-validity tier** (`mnist_collapse`: a conv-VAE
|
||||||
|
collapses to a single mode under dry self-training, ~10% grounding holds all 30 modes; frozen-CNN
|
||||||
|
oracle, confusion matrix recorded). **Remaining:** `region_matched`, `remint`, the synthetic-VAE
|
||||||
|
fidelity fix — all optional. The LLM/LoRA rung and C3 vertical claim stay deferred. The LLM/LoRA rung and the C3 vertical claim are deferred. Experiments are
|
||||||
|
named descriptively (`configs/neural/<name>.yaml`), not by code.
|
||||||
|
|
||||||
|
The two design documents are the source of truth for intent:
|
||||||
|
|
||||||
|
- `paper/the-lamarckian-society-v5.md` — the *perspective paper* (the "why"; reframed around sexual
|
||||||
|
reproduction). `paper/results-summary.md` — the plain-language + technical summary of all results.
|
||||||
|
- `paper/blueprint.md` — the *technical blueprint* (the "what"/"how"). **It is normative** for
|
||||||
|
Layer 1 and the LLM Layer 2; Layer 1.5 is a cost-staged intermediate the blueprint does not
|
||||||
|
cover, designed to preserve the same §1 abstractions.
|
||||||
|
|
||||||
|
Everything below summarizes the blueprint so you can orient fast, but the blueprint is the source of truth. When they conflict, the blueprint wins; when the blueprint is silent, minimize decisions and match its established patterns.
|
||||||
|
|
||||||
|
## The one idea you must hold in your head
|
||||||
|
|
||||||
|
**Knowledge transmission across agent generations is modelled *literally* as a Wright–Fisher population-genetics process** — not by analogy. A model's knowledge is a distribution `p_t` over `K` discrete items on a simplex; a fixed true distribution `p*` has a rare **tail**; each generational step is "sample from parent (drift) + mix in fresh real samples (immigration/grounding) + refit." Model collapse = loss of rare alleles under drift. Every experiment is a manipulation of this single process.
|
||||||
|
|
||||||
|
The population-genetics dictionary in blueprint §1 is the spine. Keep its abstractions **identical across both layers** — this is a hard requirement, because it is the only thing that lets a Layer-2 neural result count as confirming a Layer-1 analytic prediction:
|
||||||
|
|
||||||
|
| Abstraction | Layer 1 (analytic) | Layer 2 (neural) |
|
||||||
|
|---|---|---|
|
||||||
|
| region | disjoint block of the `K` items | task family (e.g. string ops, recursion) |
|
||||||
|
| rarity / tail | low `p*` items | low-frequency task types |
|
||||||
|
| grounding fraction `g` | `m/(n+m)` real-vs-inherited samples | proportion of verifier-passed items in pupil's training mix |
|
||||||
|
| decorrelation `ρ` | shared retained-tail correlation between teachers | LoRA specialists on disjoint task families |
|
||||||
|
| diversity `H` | heterozygosity `1 − Σ pᵢ²` | solution diversity of generated code |
|
||||||
|
| reality's "no" | grounding against `p*` | execution-based unit-test verifier |
|
||||||
|
|
||||||
|
## Two layers, staged by cost
|
||||||
|
|
||||||
|
- **Layer 1 — analytical core** (`src/inheritance/`). Pure NumPy/SciPy Wright–Fisher simulator. Laptop, minutes, no GPU. Carries the paper's quantitative claims. **Three of the five §2.4 predictions are closed-form**, so validation is an **exact** test, not a vibe check — these become `<0.1%`-tolerance assertions in `test_scientific_validation.py`:
|
||||||
|
- **Pred. 1** — neutral heterozygosity decay: `E[Hₜ] = H₀(1 − 1/n)ᵗ`.
|
||||||
|
- **Pred. 3** — *exact* mutation–drift equilibrium for the implemented immigration model: `H_eq = H* · m(2n+m−1)/(n+2nm+m²)`, with `H* = 1 − Σ(p*ᵢ)²`. The textbook `θ/(1+θ)` (θ=2m) is only the rare-immigrant limit. **Critical nuance: H is *smooth* in m — the sharp phase threshold lives in discrete tail-item survival (Pred. 4: an item survives iff `m·p*ᵢ ≳ 1`), not in H.** Do not describe E2 as a discontinuity in H.
|
||||||
|
- **Pred. 5** — closed-form recombination benefit: `U(K_T, ρ, q) = T[ρq + (1−ρ)(1−(1−q)^K_T)]` (expected tail items retained by ≥1 of K_T teachers).
|
||||||
|
- **Layer 2 — neural existence proof** (`src/neural/`). Small open-weight models (default OLMo-2-1B / SmolLM2-1.7B, fallback Qwen2.5-1.5B-Instruct; pin the HF revision hash, never track `main`), LoRA specialisation, distillation/merging across 2–3 generations, program-synthesis-with-unit-tests as the verifier. One consumer GPU. Only needs to show the **sign** of three effects, not precise magnitudes.
|
||||||
|
|
||||||
|
## Experiments and their falsifiers
|
||||||
|
|
||||||
|
Each experiment is one config file → one runner invocation → one `results.parquet` → one figure. Every experiment has a **falsifier** — an outcome that would refute the corresponding claim. The design is built to be able to kill the thesis; preserve that.
|
||||||
|
|
||||||
|
- Layer 1: **E1** reproduce collapse (null), **E2** grounding phase boundary (headline: is there a critical `g* ≪ 1`?), **E3** region-matched grounding, **E4** multi-teacher decorrelation, **E5** quality-diversity vs. greedy selection, **E6** re-minting gate / irreversibility.
|
||||||
|
- Layer 2: **C1** dry vs. grounded, **C2** one vs. N complementary teachers at matched budget, **C3** the vertical claim (general knowledge climbs while each specialty is re-earned and *exceeded* — this is load-bearing, prioritize it), **C4** distillation vs. merging (optional).
|
||||||
|
|
||||||
|
Blueprint §6 is the claim→experiment→figure→falsifier traceability matrix and is the definition of done.
|
||||||
|
|
||||||
|
## The one non-obvious implementation piece: the correlated-teacher construction (§2.7.1)
|
||||||
|
|
||||||
|
E4's whole purpose is to isolate the effect of teacher **decorrelation ρ**, so ρ must be a *directly constructed, independently-swept* knob — **never** an emergent quantity you get by tuning drift (that ρ would be confounded with `n`, `m`, tail size, and generation count, i.e. with the very drift E4 holds fixed). The construction is a **shared-switch exchangeable Bernoulli**: for each of the `T` tail items, draw a shared switch `z~Bern(ρ)`, a shared retention `s~Bern(q)`, and per-teacher independent `u⁽ᵏ⁾~Bern(q)`; set teacher k's retention `r⁽ᵏ⁾ = s if z else u⁽ᵏ⁾`. This yields exact marginal retention `q` and exact pairwise correlation `ρ` (provable: Cov = ρq(1−q), Var = q(1−q)), and is exchangeable so ρ is a single scalar. `make_retention_matrix(T, K_T, rho, q, rng)` returns the `(K_T, T)` binary matrix; `make_correlated_teachers` maps it to distributions (head items always kept at `p*`; tail item kept at `p*ᵢ` if retained, else `tail_floor`; renormalise so dropped-tail mass flows to survivors). The exact-construction path is **preferred** for E4; the drift-based path exists only as a realism cross-check. `region_specialisation=True` forces full retention of a teacher's home-region tails and applies the ρ construction only off-home.
|
||||||
|
|
||||||
|
**E4 reports two coverages, and their gap is a result, not noise:** the construction-level union `U(K_T,ρ,q)` (must match the closed form exactly) and the post-distillation *surviving* coverage after the pupil's size-`n` resampling. A tail item present in the mixture only survives if its mixture mass clears `~1/n` (Pred. 4) — so the gap is precisely "the tail recombination *supplied* but drift *re-erased* because grounding was too thin," which ties E4 back to E2/E3.
|
||||||
|
|
||||||
|
**Finding (2026-07-04, E4) — the recombination operator matters, and mean-mixture distillation does not realise the benefit.** Under the blueprint's mean-mixture pupil (`p̄ = mean(teachers)`), surviving tail coverage is **flat in K_T** — a conservation law: averaging preserves expected pupil tail mass at `q·(tail mass of p*)` regardless of K_T, and in the rare-tail (linear-survival) regime the 1/K_T dilution exactly cancels the union gain. The recombination benefit is realised only under a **union-preserving merge** (`max` over teachers, à la M2N2), where surviving rises with K_T and decorrelation. So E4 reports surviving under **both** operators (`surviving_mean`, `surviving_max`): union = supply (validated vs closed form), max-merge = realised benefit, mean-distill = the null that motivates why merging/grounding is needed. GG decision: report both. This sharpens rather than refutes the thesis, but the paper's recombination claim rests on the *merge* operator, not naive mean distillation — worth carrying into Layer 2 (C4) and the write-up.
|
||||||
|
|
||||||
|
**Finding (2026-07-05, neural `grounding`) — grounding's SIGN transfers to trained RNN weights, but the sharp `g*` does not; and tail-survival is the *wrong* neural collapse metric.** Re-ran the phase-boundary sweep at 18 replicates. Two results: (1) **forward-KL is the operative neural collapse metric, not H or tail-survival.** The RNN's smoothing inductive bias keeps *spurious* tail modes alive (it generalises to unseen codewords), so `tail_truth_mass_alive` is flat/**non-monotone** in g (dry 0.54 > most grounded points) and H stays ~0.77–0.85 of H\* throughout — neither shows a threshold. Stationary **forward-KL** falls monotonically (dry 2.08 → g=0.2: 0.75), significant at g≥0.05 (paired t up to 3.3; 89% of lineages improve at g=0.2). This *refines* the earlier "forward-KL AND tail survival" note: for a smoothing model, support-counting decouples from closeness-to-truth. (2) **The sharp `g*≪1` is an exact-operator feature, softened by neural inductive bias.** Half the achievable KL reduction closes by a *median-recovery* grounding g≈0.04 (bootstrap CI [0.004, 0.116]) — a striking echo of Layer-1's 0.048 — but full (95%) recovery needs g≈0.19, far more than the histogram bridge, because smoothing both caps dry collapse (KL~2, not ∞) and slows full recovery. So the quantitative `g*≪1` claim rests on the **histogram bridge** (g\*=0.047, exact reduction to Layer 1), which the trained RNN confirms in *sign* and softens in sharpness. Honest note: the pre-registered 95%-of-H\*/tail-survival falsifier is not met, but that is because those are the wrong metrics for a smoothing model, not because grounding fails — the blueprint §3.5 directional claim (grounding arrests collapse) holds robustly. Robustness fix landed alongside: a fully-degenerate RNN can emit only invalid codewords, so `measure_distribution` returns a terminal-collapse sentinel (fixation on the dominant mode) instead of crashing a long sweep.
|
||||||
|
|
||||||
|
**Finding (2026-07-05, real-MNIST `mnist_collapse`) — collapse and grounding-rescue reproduce on real images.** External-validity tier: a small **convolutional VAE** (the canonical generative-collapse model) is retrained each generation on its own generated digits. Modes = digit class × stroke-thickness bin (K=30, Zipf, ~18 tail modes); the oracle is a **frozen CNN + deterministic thickness** at **98.5% mode accuracy** (its 30×30 confusion matrix is recorded in the manifest as the measurement floor). Result (4 reps): the **dry (g=0) lineage collapses to a single mode** — forward-KL 0.5→18, support 30→1, tail truth-mass 1.0→0.06, H→0 — while **10% grounding holds all 30 modes** (KL≈0.6, full tail, H≈0.9). The VAE needs ~10% grounding here vs the synthetic histogram's ~5%, consistent with the `grounding` finding that trained neural models need more grounding than the exact operator. **Confirmation-only (signs, not magnitudes; blueprint §3.5)** — the exact synthetic oracle stays the quantitative anchor. `figures/mnist_montage.py` is an eyeball diagnostic (re-runs a short dry lineage; NOT a parquet figure). Build gates passed: CNN mode accuracy 98.5%; VAE gen-0 recovers full 30/30 support (over-smooths frequencies, KL≈0.5, no prior hole — unlike the *synthetic*-codeword VAE, which is why the MNIST VAE works where that one didn't). The MNIST tier is heavy (torchvision `--extra mnist`, downloads MNIST, ~5 min): `make mnist`, kept out of the `make neural` loop.
|
||||||
|
|
||||||
|
**Finding (2026-07-05, learning kernel) — neutral drift is a null both real models fail, oppositely; the estimator bias is a signed operator.** Layer-1 extension (`knowledge/kernel.py`, `LearningKernelCfg`): the refit becomes `p_{t+1} = T_θ(counts/n)` with two pop-gen knobs — **reset `u`** (mutation toward a prior = smoothing) and **temperature `τ`** (sharpening = mode-competition) — both identity at their defaults, so the histogram bridge and every scientific-validation test are unchanged (68 core tests still green). Result: **neutral Wright–Fisher fails both neural architectures, in opposite directions.** VAE regime (`n=6000, K=30`): neutral drift is *inert* (no collapse), yet the real VAE collapsed to one mode — **sharpening `τ=0.8` reproduces it** (the estimator ADDS collapse). RNN regime (`n=200, K=256`): neutral drives `H→0`, but the real RNN only partially collapses — **mutation `u=0.006` reproduces the `H`-floor** (the estimator REMOVES collapse). Honest caveat: uniform-mutation matches the RNN `H`-floor but overshoots its forward-KL (~5 vs ~2), evidence the RNN's smoothing prior is *truth-like, not uniform* (future refinement). Configs `configs/inheritance/kernel_{sharpen,smooth}.yaml`, figure `plot_kernel.py`. This mechanistically explains the architecture-generality result and the softened neural `g*`.
|
||||||
|
|
||||||
|
**Strategic positioning vs Riis 2026 (arXiv:2604.08554, "Drift and selection in LLM text ecosystems").** Riis independently formalizes **collapse = Wright–Fisher drift** (his Thm 1) with n-gram agents: minority-mass martingale, rare-first extinction, single-token dropout ≈ αe^{−α}, de Bruijn-polytope fixed points, plus descriptive-vs-normative *selection* (Thm 2). **Concede as prior art:** "collapse is literally Wright–Fisher", the martingale, rare-first loss, the WF/effective-population formalism — cite him; do **not** frame these as our contribution. **Crucial distinction that protects us:** his "mixed environment" *retains the lineage's own old synthetic tokens* — there is **no injection of fresh real data from a fixed `p*`**, so his headline is *pessimistic* (Thm 1c: extinction is independent of α — retention only changes speed). Our **grounding is immigration from a non-drifting external truth**, giving a stationary `H_eq>0` and a critical `g*≪1` that *prevents* collapse — the mechanism his closed loop lacks. **Our defensible novelty, ranked:** (1) **recombination + "merge, don't average" conservation law** (E4) — he has no model-merging operator; flagship; (2) **the learning-kernel / estimator-bias axis** — he *explicitly names it as future work*; we now build+measure it; (3) grounding threshold (solid anchor, but immigration–drift balance is classic — not a flagship); (4) architecture-generality in real weights + MNIST; (5) **the Lamarckian society + the vertical/cumulative C3 claim — wholly ours, not yet run.** Reposition the paper from *"collapse is drift"* (now contested) to **a population-genetic *control theory* for sustaining open-ended knowledge**: drift is the diagnosed disease (cite Riis), our contribution is the engineered remedies and their integration.
|
||||||
|
|
||||||
|
**Finding (2026-07-05, E7/E8 — the multi-locus society frame; raises the ceiling).** To express the *vertical* claim (capability that *exceeds* any component), knowledge is generalized from a single-locus fixed-`p*` distribution to a distribution over **genotypes** (`L` biallelic loci, `K=2^L`; fitness = # correct loci; reuses all the K-mode machinery). The one new operator is **recombination** (`knowledge/genotype.py`): free recombination sends `p → ⊗ per-locus marginals` (linkage equilibrium). Two experiments, both analytic. **E8 (the star, `kind: society`) — the vertical claim / Fisher–Muller:** decorrelated *parents* (specialists, expert on their loci, agnostic elsewhere) are recombined; **sexual merge assembles a genotype fitter than any parent, climbing to the optimum (12/12) as parent count grows and `ρ→0`, while the best single parent (~8.7) and the mean-mixture "model soup" (~11.6) plateau below.** Clean, dramatic, 40 reps; reuses `make_retention_matrix` (locus mastery replaces tail-item retention). **E7 (`kind: genotype_lineage`) — the advantage of sex:** a single population adapting toward the optimum; the sexual lineage adapts *faster* (clonal interference slows the asexual one) by keeping loci in linkage equilibrium (LD→0 vs LD spike). Honest scope: a **speed** advantage, not a permanent Muller's-ratchet gap (the single-population ratchet is subtle to force; E8 carries the headline). **Metaphor shift (GG, 2026-07-05):** the society is framed as **sexual reproduction with unbounded parents**, *not* teacher→pupil — teacher→pupil caps at the ceiling (recovery), n-parent recombination is combinatorial and *generative* (exceeds any parent), and unlike biology there is no two-parent limit. Collapse = asexual degradation; the cure = sex. This unifies E4 (merge≠average) + E6 (irreversibility) under evolution-of-sex theory and stakes ground Riis's single-locus n-grams cannot reach. Scope is bounded: fixed combinatorial space (`L≤12`, "effectively open-ended relative to n"), additive fitness (NK/epistasis is an optional extension).
|
||||||
|
|
||||||
|
**Finding (2026-07-05, E9/E10 — the sexual-transmission model made rigorous: when sex helps, and directed sex).** Deepening the sexual metaphor (GG excited; wanted it robust before the full society). Added a **Kauffman NK landscape** (`genotype.nk_fitness`, tunable ruggedness `K`), finite **crossover** (`genotype.crossover`, n-parent, per-gap recombination rate), and **hill-climb** (parents = local optima = "trained models"). **E9 (`kind: recomb_landscape`) — landscape robustness / "why sex?":** E8's dramatic transgression used an *additive* landscape; on rugged (epistatic) landscapes, blindly recombining local optima causes **outbreeding depression** — mean offspring fall *below* the parents, worse with ruggedness AND recombination rate (`K=8`, free recomb: ≈ −0.23), and the **optimal recombination rate shrinks as ruggedness grows**. Design rule: *merge freely when skills are complementary/additive; sparingly + with selection when entangled.* **E10 (`kind: directed_sex`) — directed sex beats biological sex (the AI superpower):** biology is stuck with 2 random-mating parents and no offspring preview; an AI can **choose complementary mates + evaluate many recombinant offspring + keep the fittest + use unbounded parents** (iterated recombine-then-select). Result: random ("biological") sex craters with ruggedness (0.66→0.51), while **directed sex tracks/exceeds the best parent at every ruggedness** — converting the outbreeding-depression catastrophe into a win. This is the practical, distinctly-AI payoff and has no biological analog. `configs/inheritance/{E9,E10}.yaml`, `plot_{E9,E10}.py`, READMEs, +5 tests (117 green). Complete sexual-transmission picture: **dramatic super-parent offspring when skills are complementary (E8); outbreeding-depression risk when entangled (E9); directed sex resolves the risk (E10).**
|
||||||
|
|
||||||
|
**Finding (2026-07-05, E11 — the dynamic Lamarckian society: the vertical claim / C3, realized).** The culmination: a finite population of `N` agents (genotypes, `L` loci) evolves on a rugged NK landscape that *is* reality (`knowledge/dynamic_society.py`), composing the four operators the whole study built toward — grounding, directed recombination (sex), quality-diversity selection, mutation. Grounding is made load-bearing via the **consensus-conformity (self-consumption)** mechanism (GG decision): selection acts on `g·true_fitness + (1−g)·conformity` (conformity = agreement with the population's own consensus), so `g=0` optimises fitting-the-crowd rather than reality. **4-arm ablation (12 reps), each breaking distinctly, only the full society climbing (global_opt≈0.79):** `full` 0.78 (climbs to the optimum, diversity maintained longest) · `no_sex` 0.77 (can't recombine to escape local optima) · `no_diversity`/greedy 0.74 (collapses diversity fastest, stuck at a worse local optimum) · **`no_grounding` 0.48 (self-consumption collapse to an unfit consensus** — trains on the crowd, regresses to a confident-but-wrong mean; conformity−true gap ≈0.5). This integrates E1–E6 + the kernel + E7–E10 into one system and shows the society needs **all** of grounding + directed sex + diversity: on a rugged landscape you need diversity to explore basins, sex to recombine them, grounding to select on reality — remove any and you fail differently. `configs/inheritance/fig4_society_ablation.yaml`, `plot_fig4_society_ablation.py`, README, +5 tests (122 green). **This closes the C3 vertical claim analytically** (the LLM rung remains the eventual empirical instantiation).
|
||||||
|
|
||||||
|
**Finding (2026-07-05, LLM prototype `llm_merge` — the first real-LLM step; honest/partial).** First move from toy models toward real LLMs (blueprint C2/C4, the real-LLM image of E8), on one 16 GB GPU. New `src/llm/` package: procedural task families + exact-match verifier (`tasks.py`), batched eval (`evaluate.py`), LoRA specialisation (`specialise.py`, manual answer-only SFT), weight-space merge via peft `add_weighted_adapter` (`merge.py`: soup=averaged deltas, ties=sign-reconciled union), runner (`experiment.py`, kind `llm_merge`). Base = **Qwen2.5-0.5B-Instruct** (Apache-2.0). Three *disjoint*, deliberately-hard families (lists/strings/arith); one LoRA specialist each (~90 s total). **Result (seed 1):** each specialist spikes on its own family; the **merges are the only models competent across ALL families — worst-family ≈0.25 vs <0.16 for every single specialist** (the Fisher-Muller "generalist from specialists" signature, robust). **But** the stronger "exceeds every parent *overall*" claim is only marginal at this scale (soup 0.64 vs best specialist 0.63; ties 0.61 below it), and averaging visibly *dilutes* peaks (lists: specialist 0.43 → merge 0.26 — the E4 "merge, don't average" caveat in real weights). Honest scope: pipeline works end-to-end; the balance/retention half reproduces; the strict overall-exceeds and the soup-vs-ties distinction need scale (bigger base, more/cleaner families, seeds, dilution-resistant/offspring-selected merge). **Env notes:** Python 3.14 + transformers 5.13 works (cp314 wheels exist); `transformers 5.x` changed `apply_chat_template` (returns a dict; render to text then tokenize; pass `**inputs` to `generate`). `make env-llm` / `make llm`; adapters cached under gitignored `models/llm/`, base in the HF cache (outside the repo). 125 tests green (+3 pure task/verifier). The full grounded sexual *society* on LLMs (C1 collapse, directed sex, the dynamic society) is the HPC-scale next step.
|
||||||
|
|
||||||
|
**Finding (2026-07-05, `llm_merge_hpc` — the 7B firm-up on Imperial CX3; the marginal sign becomes decisive).** Re-ran `llm_merge` at a capable base — **Qwen2.5-7B-Instruct**, 200 test tasks/family, one **L40S (46 GB)** GPU, 8 min walltime — via the `/imperial-hpc` runbook (see `memory/hpc-setup.md`). **Both merges reach 0.87 overall, decisively above the best single specialist (0.77) and above every specialist on every family; worst-family 0.62 vs ≤0.57 for any specialist.** The two 0.5 B caveats are resolved: (1) the strict Fisher–Muller "exceeds every parent overall" claim is now clean (+10 points, not marginal); (2) the **dilution vanishes** — at 7 B the merge *beats* the lists-specialist on lists (0.62 > 0.57), where at 0.5 B averaging diluted it (0.43 → 0.26). **Interpretation: dilution is a small-model artefact; a capable base has enough headroom that weight-space averaging composes rather than dilutes** — the "merge, don't average" concern (E4) softens once parents are strong (soup ≈ ties at K=3). Results synced to `results/llm_merge_hpc/` (README legend + data-driven figure title). The natural refinement is *module-level* union-preserving recombination (MoE-expert / adapter-union merge, the real-weight image of E8's max-merge) rather than delta-averaging.
|
||||||
|
|
||||||
|
**Finding (2026-07-05, `llm_moe` — the union operator in real weights; E8's `max` vs `mean`, 0.5B).** Added the *union-preserving* recombination operator that `llm_merge` lacked (`src/llm/moe.py`, `kind: llm_moe`): never average the parents — keep each specialist LoRA intact and **select** the right one per prompt (MoE **router**: `oracle`, or `learned` = training-free nearest-centroid over the *base* model's own prompt embeddings) or per module (`max_merge` = winner-take-all by delta-norm). Reuses the cached `llm_merge` specialists (no retraining). **Result (0.5B, seed 1):** **routing wins decisively over fusion — overall 0.74 / worst-family 0.43 vs soup 0.64/0.26** — and recovers *each* specialist's own-family peak exactly (no dilution: fusion diluted the lists-specialist 0.43→0.26, routing keeps 0.43). This is E8's `max`(union) > `mean`(average) in real LLM weights. **Two honest riders:** (1) the learned router is *trivially perfect* (1.00) because the three families are lexically distinct — routing's win here rests partly on the routing problem being easy (ambiguous/overlapping skills would make the router the bottleneck — the interesting next failure mode); (2) **router-free `max_merge` is a poor union (0.46)** — static per-module winner-take-all isn't input-adaptive, so it collapses toward the strongest-norm modules; the union benefit needs *routing*, not weight surgery. `configs/llm/moe.yaml`, `plot_llm_moe.py`, `results/llm_moe/README.md`, +2 router tests (127 green). The regime question — does routing still beat fusion once a capable base lets fusion *compose* rather than dilute (7B soup already beats its specialists)? — is the `llm_moe_hpc` 7B run below.
|
||||||
|
|
||||||
|
**Finding (2026-07-05, `llm_moe_hpc` — the regime *flips* at 7B; "merge, don't average" is a weak-base law).** Re-ran `llm_moe` at **Qwen2.5-7B-Instruct** (L40S, 9 min, reusing the cached 7B specialists). **The union-vs-fusion ordering inverts:** at 0.5B union won (routing 0.74 > soup 0.64); at 7B **fusion wins — soup 0.87 > routing 0.84 > max_merge 0.78.** Mechanism, and it's the deep point: **routing *selects* one intact specialist so it is capped at the best parent per family** (lists 0.57 = spec_lists, strings 0.97 = spec_strings), whereas **fusion *blends* deltas and, at a capable base, composes *beyond* any parent** (soup lists 0.62 > spec 0.57, strings 1.00 > spec 0.97). Selection can't synthesise something better than its best component; averaging-that-composes can. So the E4/E8 "merge, don't average" law is **regime-dependent — a weak-parent / small-model law, not universal**: union wins exactly when averaging *dilutes* (0.5B), fusion wins once the base has headroom to *compose* (7B). This refines rather than contradicts E8 (whose additive-landscape `max>mean` assumed no compositional headroom). The operator to actually want is **fusion-that-composes + selection over recombinant offspring** = the "directed sex" ideal (E10), the natural next experiment. `results/llm_moe_hpc/` (README + regime-aware figure title). Riders unchanged: learned router trivially perfect (lexical families), `max_merge` the weakest union (not input-adaptive).
|
||||||
|
|
||||||
|
**Finding (2026-07-05, `llm_directed` — directed sex in weights; refinements pay off only when the default blend is suboptimal).** E10 in real LLM weights (`src/llm/directed.py`, `kind: llm_directed`): breed a *population* of recombinant offspring (specialists merged at Dirichlet-sampled weights), score each against the verifier on a held-out **validation** split, keep the fittest — reported on a fresh **test** split (no selection-on-test leakage). Two breeding objectives (best-overall, best-worst-family). **The value scales with how far the uniform soup is from optimal, giving a clean regime split:** **0.5B** — soup dilutes, so directed selection beats it (`directed_overall` 0.69 > soup 0.64; `directed_balanced` worst-family 0.37 > soup 0.26), though single-objective selection trades off the other axis (breeding for overall tanks the rare `lists` to 0.17) and a *global* blend still trails per-input **routing** (0.74). **7B** — soup already *composes* to the ceiling on these near-saturated families (strings & arith at 1.00), so directed selection finds nothing better: **directed 0.868 ≈ soup 0.873** (marginally below, a val/test overfit gap). **Honest limitation:** the 7B families are near-saturated (2/3 at 1.00), which structurally caps the headroom — this run can't separate "directed sex doesn't help at scale" from "these tasks are too easy at 7B"; a *harder, unsaturated* benchmark is the fair next test. **Through-line across all four LLM runs:** "merge, don't average" and its refinements (routing, directed selection) are **weak-base / suboptimal-default** phenomena — they pay off at 0.5B (soup far from optimal) and are inert at 7B (soup near-optimal on saturated tasks). `configs/llm/{directed,directed_hpc}.yaml`, `plot_llm_directed.py`, `results/llm_directed{,_hpc}/`, `hpc/llm_directed.pbs`, +3 tests (130 green).
|
||||||
|
|
||||||
|
**Finding (2026-07-05, HARD benchmark `llm_moe_hard_hpc` + `llm_directed_hard_hpc` — the 7B "fusion wins / no headroom" results were SATURATION artefacts; the law is HEADROOM, not base-size).** The easy families saturated 7B (strings & arith at 1.00), so the 7B nulls (moe: fusion 0.87 > union 0.84; directed ≈ soup) couldn't separate "refinements don't help at scale" from "tasks too easy." Built a **hard task variant** (`hard: true` in `tasks.py`: multi-step lists, Caesar ciphers / letter transforms, multi-step & larger arithmetic — same family labels & answer formats, threaded through `make_tasks`/`train_specialist`/runners; hard specialists cache separately as `spec_*_hard`) and re-ran both at 7B on Imperial CX3 (one L40S, 24 min, unsaturated: arith ≈0.48, strings 0.67, lists 0.34). **Both nulls flip back to the 0.5B ordering:** (1) **union beats fusion again — routing 0.500 > fusion 0.40** (soup 0.392/ties 0.400), the *same* 10-pt margin as 0.5B; fusion dilutes the fragile strings-specialist so hard (0.665 → soup 0.300) that soup even **trails the best single specialist** (0.425), while routing keeps it (0.670). (2) **directed selection beats soup again — 0.492 > 0.392** (+10 pts), recovering most of routing's benefit from one deployable merged model (lifts strings back to 0.630). **Correction to the earlier interpretation:** the `llm_moe_hpc` "regime flip" (fusion wins at 7B) and `llm_directed_hpc` "no headroom" were both driven by **task saturation, not base capability**. The operative variable is **headroom**: "merge, don't average" (union > fusion) and "directed sex" (selection > single blend) hold whenever there's room to lose to dilution — weak base (0.5B) *or* hard tasks at a strong base (7B-hard); fusion only wins in the degenerate corner where easy tasks let a strong base compose to the 1.00 ceiling. This vindicates E8's `max > mean` in real 7B weights once saturation is controlled. `configs/llm/{moe_hard,moe_hard_hpc,directed_hard_hpc}.yaml`, `hpc/llm_hard.pbs`, `results/llm_{moe,directed}_hard_hpc/`, +1 hard-task test (131 green).
|
||||||
|
|
||||||
|
**Finding (2026-09-11, three controls from the manuscript review; `results/llm_curriculum_v5_{stop3,decor}/`, `results/llm_*_hpc/s{1,2,3}/`).** (1) **Forced stop** (`merge_until: 3`): obligate merging through generation 2 then none finishes 0.793 vs the declinable merge's 0.792 (per seed −0.008/−0.006/+0.011) — the veto's outcome is explained by *when* it stopped. (2) **Decorrelated curriculum** (`orders:` key; complementarity 0.00→0.70→0.00 instead of the Latin square's monotone fall): declines still rise with generation (0.44→0.89); pooled partial ρ(declined, complementarity | generation) = −0.07, CI (−0.21, +0.09), partial ρ with generation +0.31. **The recombination-modifier / reduction-principle reading of Fig. 4B is withdrawn**; the Latin-square ρ = −0.57 was carried by generation (adapter age, skill count and destroyer arrival are confounded). What stands: one bit of selection per merge, or a fixed early stop, avoids the obligate-merge collapse at no cost against never merging. (3) **7B seeds 2–3** (33 min/seed, L40S): merge − best specialist +0.066±0.036, routing − soup +0.094±0.015, directed − soup +0.073±0.031, all 3/3 seeds; *not* replicated: "soup below the best specialist on hard tasks" (1/3, mean +0.001) — softened in text. Stats: `figures/stats_llm_curriculum.py` (also the single source of curriculum arm labels, used by `make_figs`), `figures/stats_llm_7b_seeds.py`; `_figlib.load_seed_bundles` reads `s{seed}/` layouts.
|
||||||
|
|
||||||
|
**Finding (2026-09-12, four experiments from the dropped "Limits"; `results/llm_speciation/s{1,2,3}`, `results/llm_curriculum_v5_{early,late,early_obl,late_obl,cull}/s{1,2,3}`, `results/llm_{merge_seeds,moe_hard_seeds}_smol`).** GG's rule: a limitation that names a runnable experiment is run, not stated. (1) **Speciation seeds 2–3:** conflict cliff (merge 0.02/0.12/0.16 vs parents 0.23–0.25) and duration null (0.76→0.95) hold in 3/3 seeds; seed 1's cliff was the deepest. Fig. 5C–D now has CI bands. (2) **Conflict-arrival curricula** (`orders:`; boolq/winogrande in generations 1–2 or 5–6, age and skill count rising identically): declines and the obligate collapse follow **generation, not conflict arrival** (partial ρ with conflict-present, generation controlled: −0.09, CI (−0.45, 0.15); with generation: +0.45); conflict-early dips at arrival, recovers, collapses from generation 5; conflict-late collapses from generation 4 with its pair still to come. What stays confounded is adapter age with skill count. (3) **Second base lineage** (SmolLM2-1.7B-Instruct, `adapters_dir: models/llm_smol` because the specialist cache is keyed by family+seed only): Fisher–Muller replicates 5/5 (soup +0.049±0.022, TIES +0.097±0.020 over best specialist), headroom 3/3 with a larger margin (routing − soup +0.162±0.036; soup below best specialist in 3/3). (4) **Differential reproduction** (`cull: true`, `cull_step`/`inherit_slot`): **parity again** — with selection, declinable 0.793 vs never-merge 0.804 (−0.011±0.003, 3/3 below); selection − none +0.007±0.030; recombination's early lead present with and without selection, gone by generation 5. The Discussion's prediction (selection turns speed into level) is withdrawn: under a curriculum that delivers every skill to every lineage the ceiling is what one adapter carries. Speciation adapters now live in `speciation_s{seed}/` (an array over seeds used to race on a shared dir). SI Figs. S14–S16; stats in `figures/stats_llm_{curriculum,speciation_seeds,smol}.py`.
|
||||||
|
|
||||||
|
## Build order (blueprint §7) — respect the gate
|
||||||
|
|
||||||
|
1. Scaffold: repo layout (§5), container, pytest skeleton, config system, seeding utils. `make test` green.
|
||||||
|
2. Layer 1 core + null model + `test_scientific_validation.py` against §2.4 predictions 1–2.
|
||||||
|
**HARD GATE: do not proceed until simulated drift matches the analytic heterozygosity decay `E[Hₜ] = H₀(1 − 1/n)ᵗ`.**
|
||||||
|
3. Layer 1 grounding + E1–E2 (the headline result).
|
||||||
|
4. Layer 1 E3–E6. Layer 1 is now a complete laptop-reproducible paper on its own.
|
||||||
|
5. Layer 2 scaffold + verifier (test determinism & sandbox isolation *before* any training).
|
||||||
|
6. Layer 2 C1 + C3.
|
||||||
|
7. Layer 2 C2 (+ C4 if compute allows).
|
||||||
|
8. Reproduction pass.
|
||||||
|
|
||||||
|
**Do not start Layer 2 until Layer 1's scientific-validation tests pass.**
|
||||||
|
|
||||||
|
## Prescribed structure and commands (do not yet exist — create per blueprint §4–5)
|
||||||
|
|
||||||
|
Target module interfaces are given with **normative names** in blueprint §2.7 (Layer 1) and §3.6 (Layer 2); downstream scripts depend on these signatures, so implement to them exactly. Target repo layout is §5. Planned automation:
|
||||||
|
|
||||||
|
```
|
||||||
|
make env # uv sync -> .venv from committed uv.lock
|
||||||
|
make test # correctness tests + scientific-validation tests
|
||||||
|
make layer1 # run E1–E6
|
||||||
|
make layer2 # run C1–C3 (C4 optional)
|
||||||
|
make figures # regenerate every figure from committed results.parquet
|
||||||
|
make all
|
||||||
|
./reproduce.sh # uv sync → test → run all at committed seeds → regen figures → REPRODUCED.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Single-experiment run pattern: one YAML config per experiment under `configs/inheritance/EX.yaml` or `configs/layer2/CX.yaml`, fed to the experiment runner. Figures are regenerated separately by `figures/plot_EX.py` reading **only** `results.parquet` (no re-simulation).
|
||||||
|
|
||||||
|
## Non-negotiable engineering standard (blueprint §4)
|
||||||
|
|
||||||
|
- **Reproducibility is a hard requirement, not a preference (this is a paper).** The environment is a **`uv` venv built from a committed, hash-pinned `uv.lock`** — that lockfile is the source of truth for "it runs" (Apptainer is dropped; a Dockerfile may later wrap the same lockfile for Layer 2's GPU work). Layer 1 is bitwise-reproducible from a single master seed; Layer 2 is *statistically* reproducible (document residual GPU non-determinism, set determinism flags, report per-seed points).
|
||||||
|
- **Seeding:** one master seed in config → derive all sub-seeds via `np.random.SeedSequence.spawn`. Never touch global RNG state; pass `rng` explicitly everywhere. Results are a pure function of the resolved config.
|
||||||
|
- **No magic numbers in code.** Every parameter lives in a YAML resolved at run time; the *resolved* config (after sweep expansion) is written next to results. Sweeps are declared in config, not hard-coded.
|
||||||
|
- **Output contract for every run:** `results.parquet` (long form) + `resolved_config.yaml` + `manifest.json` (library/CUDA versions, seed, git commit, model revision hashes, content hash of results). Every figure must be a pure function of a committed results artifact.
|
||||||
|
- **Scientific-validation tests are the spine of trust.** They assert the simulator reproduces the §2.4 closed forms within tolerance; if they fail, the *science* is wrong, not just the code. Keep them.
|
||||||
|
- **Open science end-to-end:** open-weight models only, permissive/open tooling (uv, MLflow or plain versioned Parquet — avoid closed SaaS trackers), `results/` gitignored but hashes tracked.
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
Python ≥ 3.11. Layer 1: NumPy, SciPy, pandas, matplotlib — no GPU, no heavy deps. Layer 2: PyTorch, HF `transformers` + `peft` (LoRA), `datasets`, optional `vllm`; sandboxed `subprocess` verifier. Config via a thin pydantic + PyYAML loader (not Hydra — its global state/`chdir` fights the pure-function-of-resolved-config contract). Env via a **`uv` venv from a committed `uv.lock`** — the lockfile is the reproducibility source of truth; Layer 1 needs no container.
|
||||||
40
Makefile
40
Makefile
|
|
@ -2,7 +2,8 @@
|
||||||
# reproducibility source of truth; every target runs inside it via `uv run`.
|
# reproducibility source of truth; every target runs inside it via `uv run`.
|
||||||
|
|
||||||
.PHONY: env env-neural env-mnist env-llm env-notebooks test inheritance neural mnist llm \
|
.PHONY: env env-neural env-mnist env-llm env-notebooks test inheritance neural mnist llm \
|
||||||
llm-speciation llm-epistasis figures paper-figures paper notebooks clean
|
llm-epistasis llm-society llm-society-calib llm-society-calib-b llm-society-v2-smoke \
|
||||||
|
figures paper-figures paper notebooks clean
|
||||||
|
|
||||||
env: ## build .venv from the committed lockfile
|
env: ## build .venv from the committed lockfile
|
||||||
uv sync --extra dev
|
uv sync --extra dev
|
||||||
|
|
@ -30,13 +31,13 @@ mnist: ## run the torchvision tiers: MNIST collapse + real-weight sp
|
||||||
uv run python -m neural.experiment configs/neural/speciation_real_emergent.yaml
|
uv run python -m neural.experiment configs/neural/speciation_real_emergent.yaml
|
||||||
MPLBACKEND=Agg uv run python figures/mnist_montage.py # the asset paper Fig. 2A embeds
|
MPLBACKEND=Agg uv run python figures/mnist_montage.py # the asset paper Fig. 2A embeds
|
||||||
|
|
||||||
env-llm: ## add the LLM stack for the language-model tier (GPU; transformers/peft)
|
env-llm: ## add the LLM stack for the Layer-2 prototype (GPU; transformers/peft)
|
||||||
uv sync --extra dev --extra neural --extra llm
|
uv sync --extra dev --extra neural --extra llm
|
||||||
|
|
||||||
llm: ## the 0.5B multi-seed runs behind Fig. 3B and Table S2 (merge x5, moe-hard x3, directed-hard x3)
|
llm: ## run the LLM prototypes: merge (fusion) + moe (union) + directed (offspring select)
|
||||||
uv run python -m llm.experiment configs/llm/merge_seeds.yaml
|
uv run python -m llm.experiment configs/llm/merge.yaml
|
||||||
uv run python -m llm.experiment configs/llm/moe_hard_seeds.yaml
|
uv run python -m llm.experiment configs/llm/moe.yaml
|
||||||
uv run python -m llm.experiment configs/llm/directed_hard_seeds.yaml
|
uv run python -m llm.experiment configs/llm/directed.yaml
|
||||||
|
|
||||||
llm-speciation: ## LLM-tier speciation: conflict cliff (replace + de-confounded add) and duration null
|
llm-speciation: ## LLM-tier speciation: conflict cliff (replace + de-confounded add) and duration null
|
||||||
uv run python -m llm.experiment configs/llm/speciation.yaml
|
uv run python -m llm.experiment configs/llm/speciation.yaml
|
||||||
|
|
@ -47,6 +48,33 @@ llm-epistasis: ## the controlled predictive test (feeds paper Fig. 3C-D) + i
|
||||||
uv run python -m llm.experiment configs/llm/epistasis_compat.yaml
|
uv run python -m llm.experiment configs/llm/epistasis_compat.yaml
|
||||||
uv run python figures/stats_llm_epistasis.py
|
uv run python figures/stats_llm_epistasis.py
|
||||||
|
|
||||||
|
llm-society: ## the composed society at LLM scale (C3): pilot; the campaign runs on HPC
|
||||||
|
uv run python -m llm.experiment configs/llm/society.yaml
|
||||||
|
|
||||||
|
llm-society-calib: ## v2 society calibration gates (prereg §4): stage A (families), then B (C2/C3/C5)
|
||||||
|
uv run python -m llm.experiment configs/llm/society_v2_calib_a.yaml
|
||||||
|
@echo "Review the C1 table, fix the 12 families in society_v2_calib_b.yaml, then: make llm-society-calib-b"
|
||||||
|
|
||||||
|
llm-society-calib-b: ## v2 calibration stage B over the chosen 12 families: transmission, cross, consensus
|
||||||
|
uv run python -m llm.experiment configs/llm/society_v2_calib_b.yaml
|
||||||
|
mkdir -p configs/llm/_gen
|
||||||
|
uv run python - <<'EOF'
|
||||||
|
import yaml; c = yaml.safe_load(open("configs/llm/society_v2_calib_b.yaml"))
|
||||||
|
for st in ("cross", "consensus"):
|
||||||
|
c["stage"] = st; c["output"] = {"dir": f"results/llm_society_v2_calib_b_{st}"}
|
||||||
|
yaml.safe_dump(c, open(f"configs/llm/_gen/calib_{st}.yaml", "w"), sort_keys=False)
|
||||||
|
EOF
|
||||||
|
uv run python -m llm.experiment configs/llm/_gen/calib_cross.yaml
|
||||||
|
uv run python -m llm.experiment configs/llm/_gen/calib_consensus.yaml
|
||||||
|
|
||||||
|
llm-society-v2-smoke: ## v2 society loop smoke (4 families, 4 agents, 2 generations, all arms)
|
||||||
|
uv run python -m llm.experiment configs/llm/society_v2_smoke.yaml
|
||||||
|
uv run python figures/plot_llm_society.py results/llm_society_v2_smoke
|
||||||
|
|
||||||
|
llm-seeds: ## multi-seed firm-up (heavy): merge x5, moe-hard x3, directed-hard x3
|
||||||
|
uv run python -m llm.experiment configs/llm/merge_seeds.yaml
|
||||||
|
uv run python -m llm.experiment configs/llm/moe_hard_seeds.yaml
|
||||||
|
uv run python -m llm.experiment configs/llm/directed_hard_seeds.yaml
|
||||||
|
|
||||||
figures: ## regenerate per-experiment figures from committed results (pure; no re-simulation)
|
figures: ## regenerate per-experiment figures from committed results (pure; no re-simulation)
|
||||||
for p in figures/plot_*.py; do MPLBACKEND=Agg uv run python "$$p"; done
|
for p in figures/plot_*.py; do MPLBACKEND=Agg uv run python "$$p"; done
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,8 @@ make test # correctness + closed-form scientific validation
|
||||||
make inheritance # the inheritance model, every experiment at its committed seed
|
make inheritance # the inheritance model, every experiment at its committed seed
|
||||||
make neural # trained networks (needs a GPU)
|
make neural # trained networks (needs a GPU)
|
||||||
make mnist # real-MNIST tier + the Fig. 2A montage asset (needs torchvision)
|
make mnist # real-MNIST tier + the Fig. 2A montage asset (needs torchvision)
|
||||||
make llm # the 0.5B multi-seed language-model runs behind Fig. 3B (needs a GPU)
|
make llm # language-model prototypes (needs a GPU)
|
||||||
|
make llm-seeds # the multi-seed firm-ups behind Fig. 3B
|
||||||
make llm-epistasis # the controlled predictive test behind Fig. 3D-E
|
make llm-epistasis # the controlled predictive test behind Fig. 3D-E
|
||||||
make llm-speciation # the LLM speciation tier behind Fig. 5C-D
|
make llm-speciation # the LLM speciation tier behind Fig. 5C-D
|
||||||
make figures # per-experiment figures, from committed parquets (no re-simulation)
|
make figures # per-experiment figures, from committed parquets (no re-simulation)
|
||||||
|
|
@ -209,5 +210,5 @@ qsub hpc/llm_7b_seeds.pbs # seeds 2-3 of the three 7B experiments (array)
|
||||||
paper's "regenerates from committed artifacts without re-simulation" holds from the archive.
|
paper's "regenerates from committed artifacts without re-simulation" holds from the archive.
|
||||||
- `figures/mnist_montage.py` re-runs a short dry lineage to draw its montage rather than reading a
|
- `figures/mnist_montage.py` re-runs a short dry lineage to draw its montage rather than reading a
|
||||||
parquet; it is an eyeball diagnostic whose quantitative counterpart is `results/fig2_mnist_collapse/`.
|
parquet; it is an eyeball diagnostic whose quantitative counterpart is `results/fig2_mnist_collapse/`.
|
||||||
- The composed society at language-model scale is an open experiment at the time of writing; its
|
- The composed society at language-model scale is an open experiment at the time of writing; see
|
||||||
configs, pilots and pre-registration are on the `dev` branch.
|
`tasks/workorder-llm-society.md`.
|
||||||
|
|
|
||||||
32
configs/inheritance/collapse_null.yaml
Normal file
32
configs/inheritance/collapse_null.yaml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
experiment: E1_reproduce_collapse
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 100
|
||||||
|
generations: 600
|
||||||
|
|
||||||
|
# Null model (blueprint 2.5-E1): no grounding, single teacher, no selection.
|
||||||
|
# Starting from the truth makes the collapse narrative clean -- the tail is lost first,
|
||||||
|
# support shrinks to 1, forward-KL diverges, and H decays geometrically at rate 1/n.
|
||||||
|
# (The decay law E[H_t]=H_0(1-1/n)^t holds from any start; see the validation suite.)
|
||||||
|
truth:
|
||||||
|
K: 500
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 1.0e-3
|
||||||
|
init: truth
|
||||||
|
|
||||||
|
dynamics:
|
||||||
|
n: 100 # distillation sample size = drift strength
|
||||||
|
teachers: {K_T: 1, rho: 0.0, q: 1.0}
|
||||||
|
grounding: {m: 0, policy: proportional}
|
||||||
|
selection: {mode: none, novelty_alpha: 0.0}
|
||||||
|
remint: {enabled: false, period: null, H_gate: null}
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-9
|
||||||
|
support_eps: 1.0e-9
|
||||||
|
|
||||||
|
# No sweep: a single condition.
|
||||||
|
output:
|
||||||
|
dir: results/collapse_null
|
||||||
29
configs/inheritance/sexual_vs_asexual_lineage.yaml
Normal file
29
configs/inheritance/sexual_vs_asexual_lineage.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
experiment: E7
|
||||||
|
kind: genotype_lineage
|
||||||
|
seed: 20260705
|
||||||
|
n_replicates: 20
|
||||||
|
|
||||||
|
# (The advantage of sex — the dynamic mechanism behind E8): a single population adapts from all-wrong
|
||||||
|
# toward a multi-locus optimum under selection + drift + mutation. Beneficial alleles arise in
|
||||||
|
# different sub-lineages; recombination reassorts them into one genotype, while an asexual lineage
|
||||||
|
# suffers clonal interference (the alleles compete and cannot combine). Expect the SEXUAL lineage
|
||||||
|
# (recomb_rate=1) to climb toward the optimum faster than the ASEXUAL one (recomb_rate=0) — the
|
||||||
|
# classical advantage of sex, and the reason a lone model lineage cannot do what a recombining
|
||||||
|
# society can. Honest scope: a SPEED advantage, not a dramatic permanent gap (the single-population
|
||||||
|
# ratchet is subtle); E8 carries the headline. Falsifier: sexual adapts no faster than asexual.
|
||||||
|
|
||||||
|
genotype:
|
||||||
|
L: 12
|
||||||
|
n: 150 # population/resample size (drift strength)
|
||||||
|
mu: 0.02 # per-locus mutation (flip) rate
|
||||||
|
base: 1.3 # multiplicative selection: fitness weight = base^(#correct loci)
|
||||||
|
recomb_rate: 0.0 # overwritten per arm by the sweep
|
||||||
|
init: wrong # start all-wrong (load L); adapt upward
|
||||||
|
|
||||||
|
generations: 120
|
||||||
|
|
||||||
|
sweep:
|
||||||
|
- param: genotype.recomb_rate
|
||||||
|
values: [0.0, 1.0] # asexual vs sexual
|
||||||
|
|
||||||
|
output: {dir: results/sexual_vs_asexual_lineage}
|
||||||
22
configs/inheritance/speciation_bdm_nk.yaml
Normal file
22
configs/inheritance/speciation_bdm_nk.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
experiment: E12_nk
|
||||||
|
kind: speciation
|
||||||
|
seed: 12
|
||||||
|
n_replicates: 15
|
||||||
|
|
||||||
|
# E12 (NK variant) — the EPISTASIS WEDGE, the paper's distinct falsifiable claim: at matched divergence,
|
||||||
|
# mergeability is governed by the EPISTASIS (ruggedness K) of the capability landscape, not by divergence
|
||||||
|
# alone (every existing ML merge predictor is a divergence measure). Parents are LOCAL OPTIMA reached by
|
||||||
|
# hill-climbing a Kauffman NK landscape from random starts; recombining them exposes broken co-adapted
|
||||||
|
# blocks. As K rises, recombining two adapted parents flips from a gain (offspring above the worse parent)
|
||||||
|
# to outbreeding depression (offspring below it). K=0 (additive) is the no-isolation control.
|
||||||
|
|
||||||
|
speciation:
|
||||||
|
landscape: nk
|
||||||
|
L: 16
|
||||||
|
K: [0, 2, 4, 6, 8, 10] # ruggedness / epistasis knob
|
||||||
|
n_pairs: 40 # random parent-pairs (local optima) aggregated per landscape
|
||||||
|
recomb_rate: 0.5
|
||||||
|
n_offspring: 200
|
||||||
|
|
||||||
|
output:
|
||||||
|
dir: results/speciation_bdm_nk
|
||||||
31
configs/llm/compose_gate.yaml
Normal file
31
configs/llm/compose_gate.yaml
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Generation-0 gate for the composition experiment (prereg v3 §4: C1 base, C3 specialists,
|
||||||
|
# C4 replication). Trains the two founders and measures everything at t=0 without iterating.
|
||||||
|
#
|
||||||
|
# Base choice (C1, measured 2026-09-07): Qwen2.5-1.5B **base, not Instruct**. Zero-shot GSM-Hard
|
||||||
|
# program-aided: Instruct-1.5B 0.500, Instruct-3B 0.417, base-3B 0.633 — all far too strong, since a
|
||||||
|
# base that already has the skills makes the specialists' contribution vacuous. Base-1.5B scores
|
||||||
|
# 0.067, within noise of the 0.059 Llama-2-7B starting point in LoRA Soups (COLING 2025), so the
|
||||||
|
# published composition effect has room to appear.
|
||||||
|
experiment: llm_compose_gate
|
||||||
|
kind: llm_compose
|
||||||
|
base_model: Qwen/Qwen2.5-1.5B
|
||||||
|
seed: 1
|
||||||
|
generations: 0 # gate only: train founders, measure t=0, stop
|
||||||
|
arms: [dry]
|
||||||
|
n_hard: 150 # composed target (GSM8k-Hard, program-aided) — SE ~0.04
|
||||||
|
n_gsm8k: 100 # math own-skill probe
|
||||||
|
n_mbpp: 80 # code own-skill probe (execution-verified)
|
||||||
|
n_probe: 40 # shared prompts for rho
|
||||||
|
k_inherit: 300
|
||||||
|
epochs: 3
|
||||||
|
conf_gate: 0.85
|
||||||
|
g: 0.10
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
max_new_tokens: 320
|
||||||
|
batch_size: 16
|
||||||
|
score_batch_size: 4
|
||||||
|
train_batch_size: 2 # 16 GB A4000; the fp32 logit upcast at vocab 152k is the constraint
|
||||||
|
train_max_len: 448
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_compose_gate}
|
||||||
34
configs/llm/compose_gate_math500.yaml
Normal file
34
configs/llm/compose_gate_math500.yaml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Generation-0 gate, second configuration (prereg v3 §4a): the composed target is MATH-500
|
||||||
|
# (competition maths, level >= 3, numeric answers), and merge weights are *selected* on a disjoint
|
||||||
|
# validation split rather than fixed at 0.5/0.5.
|
||||||
|
#
|
||||||
|
# Why: on GSM8k-Hard the code parent alone reaches 0.427, because once code removes the arithmetic
|
||||||
|
# burden the base's own reasoning suffices — so maths is not scarce and E8's premise fails. MetaMathQA
|
||||||
|
# is built from GSM8K *and* MATH, so MATH-500 tests reasoning the specialist has and the base lacks.
|
||||||
|
# Founders are shared with the first gate (same experiment name), so this costs evaluation only.
|
||||||
|
experiment: llm_compose_gate
|
||||||
|
kind: llm_compose
|
||||||
|
base_model: Qwen/Qwen2.5-1.5B
|
||||||
|
seed: 1
|
||||||
|
generations: 0
|
||||||
|
arms: [dry]
|
||||||
|
target: math500
|
||||||
|
n_hard: 120 # test split (level>=3 pool is 271; 70% test / 30% val, disjoint)
|
||||||
|
n_hard_val: 50 # val split, screens the merge weights only
|
||||||
|
merge_weights: [[0.5, 0.5], [0.3, 0.7], [0.2, 0.8]]
|
||||||
|
n_gsm8k: 100
|
||||||
|
n_mbpp: 80
|
||||||
|
n_probe: 40
|
||||||
|
k_inherit: 300
|
||||||
|
epochs: 3
|
||||||
|
conf_gate: 0.85
|
||||||
|
g: 0.10
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
max_new_tokens: 320
|
||||||
|
batch_size: 16
|
||||||
|
score_batch_size: 8
|
||||||
|
train_batch_size: 2
|
||||||
|
train_max_len: 448
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_compose_gate_math500}
|
||||||
55
configs/llm/compose_s1.yaml
Normal file
55
configs/llm/compose_s1.yaml
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# The composition campaign, seed 1 (prereg v3, amended after the generation-0 sweep of 2026-09-07).
|
||||||
|
#
|
||||||
|
# Arms. The gen-0 sweep found that the merge *weighting* dominates the operator: the a-priori 0.5/0.5
|
||||||
|
# blend fails under both operators (surplus -0.020 cat, -0.093 linear) while a selected weight passes
|
||||||
|
# (+0.080 linear at 0.2/0.8, +0.027 cat at 0.3/0.7). Weights are therefore chosen each generation on a
|
||||||
|
# disjoint validation split (E10, directed recombination) in every arm, and the operator is an
|
||||||
|
# explicit per-arm setting:
|
||||||
|
# dry — linear operator, no grounding [H2, H3, H5: does composition survive drift?]
|
||||||
|
# grounded — linear operator, g = 0.10 [H4: does immigration arrest it?]
|
||||||
|
# dry_cat — concatenation operator, no grounding [H6, revised: does the operator ordering hold
|
||||||
|
# across generations, or only at gen 0?]
|
||||||
|
experiment: llm_compose
|
||||||
|
kind: llm_compose
|
||||||
|
base_model: Qwen/Qwen2.5-1.5B
|
||||||
|
seed: 1
|
||||||
|
generations: 6
|
||||||
|
arms:
|
||||||
|
- dry
|
||||||
|
- grounded
|
||||||
|
- dry_cat
|
||||||
|
g: 0.1
|
||||||
|
n_hard: 150
|
||||||
|
n_gsm8k: 150
|
||||||
|
n_mbpp: 100
|
||||||
|
n_probe: 60
|
||||||
|
k_inherit: 300
|
||||||
|
epochs: 3
|
||||||
|
conf_gate: 0.85
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
max_new_tokens: 320
|
||||||
|
batch_size: 16
|
||||||
|
score_batch_size: 4
|
||||||
|
train_batch_size: 2
|
||||||
|
train_max_len: 448
|
||||||
|
resume: true
|
||||||
|
lora:
|
||||||
|
r: 16
|
||||||
|
alpha: 32
|
||||||
|
output:
|
||||||
|
dir: results/llm_compose/s1
|
||||||
|
arm_ops:
|
||||||
|
dry: linear
|
||||||
|
grounded: linear
|
||||||
|
dry_cat: cat
|
||||||
|
n_hard_val: 60
|
||||||
|
merge_weights:
|
||||||
|
- - 0.5
|
||||||
|
- 0.5
|
||||||
|
- - 0.3
|
||||||
|
- 0.7
|
||||||
|
- - 0.2
|
||||||
|
- 0.8
|
||||||
|
- - 0.1
|
||||||
|
- 0.9
|
||||||
27
configs/llm/curriculum_g2.yaml
Normal file
27
configs/llm/curriculum_g2.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
# G2 probe (prereg v4 §5): does forgetting occur AT ALL under the real curriculum length?
|
||||||
|
# The 3-family gate showed none — but with only 3 families, ample adapter capacity and a generous
|
||||||
|
# replay budget, none was expected. This runs the full nine-family curriculum on ONE lineage with
|
||||||
|
# replay switched OFF, which is the harshest condition the design can offer. If early families still
|
||||||
|
# do not decay, the families are too mutually compatible and too easy for their own good, and the
|
||||||
|
# society has no forgetting to rescue — a design-level stop.
|
||||||
|
experiment: llm_curriculum_g2
|
||||||
|
kind: llm_curriculum
|
||||||
|
base_model: Qwen/Qwen2.5-1.5B
|
||||||
|
seed: 1
|
||||||
|
families: [strings, setops, numtheory, mixedtoken, digits, alphabet, prime, wordlen, roman]
|
||||||
|
lineages: 1
|
||||||
|
generations: 9
|
||||||
|
arms: [isolated]
|
||||||
|
baselines: []
|
||||||
|
n_new: 300
|
||||||
|
n_replay: 0 # replay OFF — the harshest forgetting condition
|
||||||
|
n_test: 60
|
||||||
|
n_val: 30
|
||||||
|
epochs: 3
|
||||||
|
lr: 1.0e-4
|
||||||
|
max_new_tokens: 32
|
||||||
|
batch_size: 32
|
||||||
|
train_batch_size: 2
|
||||||
|
train_max_len: 448
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_curriculum_g2}
|
||||||
28
configs/llm/curriculum_gate.yaml
Normal file
28
configs/llm/curriculum_gate.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Gate run for the curriculum society (prereg v4 §5). G0 is the decisive one: with a single lineage
|
||||||
|
# and no recombination, does cumulative capability CLIMB across generations? If capability cannot
|
||||||
|
# accumulate in the simplest arm, no outcome of the full design is interpretable — stop.
|
||||||
|
# Also exercises G1 (inheritance transmits), G2 (forgetting occurs without full replay) and, with
|
||||||
|
# lineages=3, G3 (recombination combines disjoint knowledge).
|
||||||
|
experiment: llm_curriculum_gate
|
||||||
|
kind: llm_curriculum
|
||||||
|
base_model: Qwen/Qwen2.5-1.5B
|
||||||
|
seed: 1
|
||||||
|
families: [strings, setops, numtheory] # 3 families, 3 generations — the smallest climb test
|
||||||
|
lineages: 3
|
||||||
|
generations: 3
|
||||||
|
arms: [isolated, society]
|
||||||
|
baselines: []
|
||||||
|
n_new: 300
|
||||||
|
n_replay: 120
|
||||||
|
n_test: 60
|
||||||
|
n_val: 30
|
||||||
|
epochs: 3
|
||||||
|
lr: 1.0e-4
|
||||||
|
operator: linear
|
||||||
|
merge_weights: [[0.5, 0.5], [0.3, 0.7], [0.7, 0.3]]
|
||||||
|
max_new_tokens: 32
|
||||||
|
batch_size: 32
|
||||||
|
train_batch_size: 2
|
||||||
|
train_max_len: 448
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_curriculum_gate}
|
||||||
27
configs/llm/curriculum_v5_calib.yaml
Normal file
27
configs/llm/curriculum_v5_calib.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Calibration for the real-dataset curriculum (prereg v4 §5 gates, re-run on the new families).
|
||||||
|
# Stage A (this config): base accuracy on every candidate, and one specialist per candidate trained
|
||||||
|
# from the base on its train split — the C1 band is base <= 0.40 and specialist >= 0.60.
|
||||||
|
# Stage B (curriculum_v5_g2.yaml, after selection): one lineage, zero replay, all chosen families in
|
||||||
|
# sequence — the forgetting probe; mean drop across families learned before the last must be >= 0.15,
|
||||||
|
# and it must not be carried by a single family (the v4 failure: one pair at +0.65, the rest ~0).
|
||||||
|
experiment: llm_curriculum_v5_calib
|
||||||
|
kind: llm_curriculum
|
||||||
|
base_model: Qwen/Qwen2.5-1.5B
|
||||||
|
seed: 1
|
||||||
|
families: [gsm8k, mbpp, boolq, mnli, sst2, csqa, arc, winogrande, squad, nq_open, hellaswag]
|
||||||
|
lineages: 11 # one lineage per candidate = each trains only its own family at gen 0
|
||||||
|
generations: 1 # gen 0 only: founders; the recorded acc_* rows ARE the specialist table
|
||||||
|
arms: [isolated]
|
||||||
|
baselines: []
|
||||||
|
n_new: 300
|
||||||
|
n_replay: 0
|
||||||
|
n_test: 60
|
||||||
|
n_val: 20
|
||||||
|
epochs: 3
|
||||||
|
lr: 1.0e-4
|
||||||
|
max_new_tokens: 48 # spans / short text need more room than a label
|
||||||
|
batch_size: 24
|
||||||
|
train_batch_size: 2
|
||||||
|
train_max_len: 512
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_curriculum_v5_calib}
|
||||||
24
configs/llm/curriculum_v5_calib_b.yaml
Normal file
24
configs/llm/curriculum_v5_calib_b.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Stage A, second pass: the six candidates that missed the C1 band at 300 founder examples, retrained
|
||||||
|
# at the budget every working specialist in this project has used (v2, v3: 1200 x 3 epochs).
|
||||||
|
# Decides whether the curriculum has six families or nine.
|
||||||
|
experiment: llm_curriculum_v5_calib_b
|
||||||
|
kind: llm_curriculum
|
||||||
|
base_model: Qwen/Qwen2.5-1.5B
|
||||||
|
seed: 1
|
||||||
|
families: [gsm8k, mbpp, sst2, csqa, winogrande, nq_open]
|
||||||
|
lineages: 6
|
||||||
|
generations: 1
|
||||||
|
arms: [isolated]
|
||||||
|
baselines: []
|
||||||
|
n_new: 1200
|
||||||
|
n_replay: 0
|
||||||
|
n_test: 60
|
||||||
|
n_val: 20
|
||||||
|
epochs: 3
|
||||||
|
lr: 1.0e-4
|
||||||
|
max_new_tokens: 48
|
||||||
|
batch_size: 24
|
||||||
|
train_batch_size: 2
|
||||||
|
train_max_len: 512
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_curriculum_v5_calib_b}
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
# then diverges maximally, then converges: complementarity 0.00, 0.67, 0.70, 0.58, 0.33, 0.00 by
|
# then diverges maximally, then converges: complementarity 0.00, 0.67, 0.70, 0.58, 0.33, 0.00 by
|
||||||
# generation. Same six families, same G, destroyers (boolq, winogrande) spread across lineages as
|
# generation. Same six families, same G, destroyers (boolq, winogrande) spread across lineages as
|
||||||
# in the Latin square. Arms: the declinable merge (`society` + `allow_veto`) and its never-merge
|
# in the Latin square. Arms: the declinable merge (`society` + `allow_veto`) and its never-merge
|
||||||
# reference under the same curriculum. Pre-registered readout: tasks/prereg-llm-society-v4.md on the dev branch §8g.
|
# reference under the same curriculum. Pre-registered readout: tasks/prereg-llm-society-v4.md §8g.
|
||||||
experiment: llm_curriculum_v5_decor
|
experiment: llm_curriculum_v5_decor
|
||||||
kind: llm_curriculum
|
kind: llm_curriculum
|
||||||
base_model: Qwen/Qwen2.5-1.5B
|
base_model: Qwen/Qwen2.5-1.5B
|
||||||
|
|
|
||||||
25
configs/llm/curriculum_v5_g2.yaml
Normal file
25
configs/llm/curriculum_v5_g2.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Stage B (prereg v4 §5 G2, v5 families): does forgetting occur, and is it general rather than one pair?
|
||||||
|
# One lineage, zero replay, all six selected families in sequence at the founder budget that passed C1.
|
||||||
|
# Pass: mean drop across families learned before the last >= 0.15, with no single family carrying
|
||||||
|
# more than 50% of the total drop (the v4 failure mode: one pair at +0.65, the rest ~0).
|
||||||
|
experiment: llm_curriculum_v5_g2
|
||||||
|
kind: llm_curriculum
|
||||||
|
base_model: Qwen/Qwen2.5-1.5B
|
||||||
|
seed: 1
|
||||||
|
families: [mnli, arc, hellaswag, squad, boolq, winogrande]
|
||||||
|
lineages: 1
|
||||||
|
generations: 6
|
||||||
|
arms: [isolated]
|
||||||
|
baselines: []
|
||||||
|
n_new: 300
|
||||||
|
n_replay: 0
|
||||||
|
n_test: 60
|
||||||
|
n_val: 20
|
||||||
|
epochs: 3
|
||||||
|
lr: 1.0e-4
|
||||||
|
max_new_tokens: 48
|
||||||
|
batch_size: 24
|
||||||
|
train_batch_size: 2
|
||||||
|
train_max_len: 512
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_curriculum_v5_g2}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# The curriculum society campaign, seed 1 (prereg v4, v5 curriculum — tasks/prereg-llm-society-v4.md on the dev branch).
|
# The curriculum society campaign, seed 1 (prereg v4, v5 curriculum — tasks/prereg-llm-society-v4.md).
|
||||||
# Runs all four arms then the three matched-budget baselines. The PBS array (hpc/llm_curriculum.pbs)
|
# Runs all four arms then the three matched-budget baselines. The PBS array (hpc/llm_curriculum.pbs)
|
||||||
# derives one (seed, arm) config per element from this template for seeds 2-3.
|
# derives one (seed, arm) config per element from this template for seeds 2-3.
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# `society` arm except that recombination is switched off from generation 3 onward (`merge_until: 3`):
|
# `society` arm except that recombination is switched off from generation 3 onward (`merge_until: 3`):
|
||||||
# obligate merging at generations 0-2, none afterwards. This is the fixed "merge early, then stop"
|
# obligate merging at generations 0-2, none afterwards. This is the fixed "merge early, then stop"
|
||||||
# schedule the veto arm must be compared against, because in the seed-1 veto run lineages declined
|
# schedule the veto arm must be compared against, because in the seed-1 veto run lineages declined
|
||||||
# 1/3 of merges at generations 0-2 and 3/3 at 3-5. Pre-registered readout: tasks/prereg-llm-society-v4.md on the dev branch §8g.
|
# 1/3 of merges at generations 0-2 and 3/3 at 3-5. Pre-registered readout: tasks/prereg-llm-society-v4.md §8g.
|
||||||
experiment: llm_curriculum_v5_stop3
|
experiment: llm_curriculum_v5_stop3
|
||||||
kind: llm_curriculum
|
kind: llm_curriculum
|
||||||
base_model: Qwen/Qwen2.5-1.5B
|
base_model: Qwen/Qwen2.5-1.5B
|
||||||
|
|
|
||||||
25
configs/llm/directed.yaml
Normal file
25
configs/llm/directed.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
experiment: llm_directed
|
||||||
|
kind: llm_directed
|
||||||
|
seed: 1
|
||||||
|
n_replicates: 1
|
||||||
|
|
||||||
|
# Layer 2 / LLM — DIRECTED SEX (E10) in weight space: breed many recombinant offspring and keep the
|
||||||
|
# fittest. llm_moe showed fusion can COMPOSE beyond the parents (7B) but the right blend is unknown and
|
||||||
|
# base-dependent, while pure routing is capped at the best parent. Directed sex resolves both: sample a
|
||||||
|
# population of weighted merges of the specialists, score each on a held-out VALIDATION split with the
|
||||||
|
# verifier (grounding), and select the winner — reported on a fresh TEST split (no selection-on-test
|
||||||
|
# leakage). Two objectives: best-overall and best-worst-family (raw capability vs the balanced
|
||||||
|
# generalist). Prediction: directed selection beats the single a-priori soup and every parent, at
|
||||||
|
# either scale. Falsifier: directed offspring ≤ uniform soup on test.
|
||||||
|
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct # reuses the cached llm_merge specialists
|
||||||
|
families: [lists, strings, arith]
|
||||||
|
n_train: 700 # only if cached specialists are absent
|
||||||
|
n_val: 80 # held-out split the verifier selects on
|
||||||
|
n_test: 100 # fresh split winners are reported on
|
||||||
|
n_candidates: 16 # offspring population size
|
||||||
|
concentration: 0.5 # Dirichlet concentration (<1 = sparser, specialist-dominant blends)
|
||||||
|
epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
|
||||||
|
output: {dir: results/llm_directed}
|
||||||
22
configs/llm/merge.yaml
Normal file
22
configs/llm/merge.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
experiment: llm_merge
|
||||||
|
kind: llm_merge
|
||||||
|
seed: 1
|
||||||
|
n_replicates: 1
|
||||||
|
|
||||||
|
# (Layer 2 / LLM prototype — blueprint C2/C4, the real-LLM image of E8): recombine specialist LLMs.
|
||||||
|
# Train one LoRA specialist per DISJOINT task family on a small open-weight base, then compare the
|
||||||
|
# base, each specialist, and their weight-space MERGES (soup = averaged deltas; ties = sign-reconciled
|
||||||
|
# union) on a held-out mixed test set. Tasks are procedurally generated and exactly verified (the
|
||||||
|
# "reality that says no"), and deliberately hard so specialists are decorrelated. Expect (per E8): the
|
||||||
|
# recombined model beats any single specialist overall AND is competent across ALL families
|
||||||
|
# (worst-family accuracy), which no single parent is. Falsifier: a single specialist matches the merge.
|
||||||
|
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct # Apache-2.0; ~1 GB, fits 16 GB with room to spare
|
||||||
|
families: [lists, strings, arith]
|
||||||
|
n_train: 700
|
||||||
|
n_test: 100
|
||||||
|
epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
merges: [soup, ties]
|
||||||
|
|
||||||
|
output: {dir: results/llm_merge}
|
||||||
25
configs/llm/moe.yaml
Normal file
25
configs/llm/moe.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
experiment: llm_moe
|
||||||
|
kind: llm_moe
|
||||||
|
seed: 1
|
||||||
|
n_replicates: 1
|
||||||
|
|
||||||
|
# Layer 2 / LLM — module-level, UNION-PRESERVING recombination (the real-weight image of E8's *max*).
|
||||||
|
# Reuses the specialist adapters trained by configs/llm/merge.yaml (models/llm/spec_*) and contrasts
|
||||||
|
# two families of recombination operator on the same held-out mixed test set:
|
||||||
|
# FUSION (blend the deltas): soup = mean(Δ_k); ties = sign-reconciled union.
|
||||||
|
# UNION (never average): moe_oracle / moe_learned = keep every specialist intact and ROUTE each
|
||||||
|
# prompt to one (MoE-over-experts); max_merge = per-module winner-take-all.
|
||||||
|
# Prediction (E8, "merge don't average"): union beats fusion exactly where fusion DILUTES — pronounced
|
||||||
|
# at a weak base (0.5B), narrowing once a capable base lets fusion compose (7B). Falsifier: fusion
|
||||||
|
# matches or beats the routing ceiling (moe_oracle) at 0.5B, i.e. averaging never dilutes.
|
||||||
|
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct # reuses the same cached specialists as llm_merge
|
||||||
|
families: [lists, strings, arith]
|
||||||
|
n_train: 700 # only used if the cached specialists are absent
|
||||||
|
n_test: 100
|
||||||
|
n_route: 32 # labelled prompts per family for the learned router's centroids
|
||||||
|
epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
operators: [soup, ties, moe_oracle, moe_learned, max_merge]
|
||||||
|
|
||||||
|
output: {dir: results/llm_moe}
|
||||||
21
configs/llm/moe_hard.yaml
Normal file
21
configs/llm/moe_hard.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
experiment: llm_moe_hard
|
||||||
|
kind: llm_moe
|
||||||
|
seed: 1
|
||||||
|
n_replicates: 1
|
||||||
|
|
||||||
|
# Local 0.5B SMOKE for the hard benchmark: confirms the harder task variant (hard: true) runs
|
||||||
|
# end-to-end — trains fresh hard specialists (cached as spec_*_hard), the verifier still scores them,
|
||||||
|
# routing/fusion operators execute. 0.5B will score low on these harder tasks (that is expected; the
|
||||||
|
# calibrated comparison is the 7B moe_hard_hpc run). Kept small for speed.
|
||||||
|
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
hard: true
|
||||||
|
families: [lists, strings, arith]
|
||||||
|
n_train: 400
|
||||||
|
n_test: 80
|
||||||
|
n_route: 32
|
||||||
|
epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
operators: [soup, ties, moe_oracle, moe_learned, max_merge]
|
||||||
|
|
||||||
|
output: {dir: results/llm_moe_hard}
|
||||||
22
configs/llm/society.yaml
Normal file
22
configs/llm/society.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Pilot: full vs no_grounding, one seed — the decision gate before the CX3 campaign.
|
||||||
|
experiment: llm_society
|
||||||
|
kind: llm_society
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
agents: 6
|
||||||
|
generations: 8
|
||||||
|
arms: [full, no_grounding]
|
||||||
|
g: 0.5
|
||||||
|
lam: 0.3
|
||||||
|
n_test: 40
|
||||||
|
n_val: 30
|
||||||
|
n_conf: 90
|
||||||
|
n_inherit: 600
|
||||||
|
n_candidates: 6
|
||||||
|
elitism: 1
|
||||||
|
n_parents: 3
|
||||||
|
epochs: 3
|
||||||
|
spec_train: 600
|
||||||
|
spec_epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society}
|
||||||
23
configs/llm/society_campaign_s1.yaml
Normal file
23
configs/llm/society_campaign_s1.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# The society campaign: 4-arm ablation, one seed per job (founders trained inline, no cache races).
|
||||||
|
# Seed 1 runs locally (A4000, queue insurance); seeds 2-4 on CX3 (hpc/llm_society.pbs, L40S).
|
||||||
|
experiment: llm_society_campaign
|
||||||
|
kind: llm_society
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
agents: 8
|
||||||
|
generations: 10
|
||||||
|
arms: [full, no_grounding, no_sex, no_diversity]
|
||||||
|
g: 0.5
|
||||||
|
lam: 0.3
|
||||||
|
n_test: 40
|
||||||
|
n_val: 30
|
||||||
|
n_conf: 90
|
||||||
|
n_inherit: 600
|
||||||
|
n_candidates: 6
|
||||||
|
elitism: 1
|
||||||
|
n_parents: 4
|
||||||
|
epochs: 3
|
||||||
|
spec_train: 600
|
||||||
|
spec_epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_campaign/s1}
|
||||||
23
configs/llm/society_campaign_s2.yaml
Normal file
23
configs/llm/society_campaign_s2.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# The society campaign: 4-arm ablation, one seed per job (founders trained inline, no cache races).
|
||||||
|
# Seed 1 runs locally (A4000, queue insurance); seeds 2-4 on CX3 (hpc/llm_society.pbs, L40S).
|
||||||
|
experiment: llm_society_campaign
|
||||||
|
kind: llm_society
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 2
|
||||||
|
agents: 8
|
||||||
|
generations: 10
|
||||||
|
arms: [full, no_grounding, no_sex, no_diversity]
|
||||||
|
g: 0.5
|
||||||
|
lam: 0.3
|
||||||
|
n_test: 40
|
||||||
|
n_val: 30
|
||||||
|
n_conf: 90
|
||||||
|
n_inherit: 600
|
||||||
|
n_candidates: 6
|
||||||
|
elitism: 1
|
||||||
|
n_parents: 4
|
||||||
|
epochs: 3
|
||||||
|
spec_train: 600
|
||||||
|
spec_epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_campaign/s2}
|
||||||
23
configs/llm/society_campaign_s3.yaml
Normal file
23
configs/llm/society_campaign_s3.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# The society campaign: 4-arm ablation, one seed per job (founders trained inline, no cache races).
|
||||||
|
# Seed 1 runs locally (A4000, queue insurance); seeds 2-4 on CX3 (hpc/llm_society.pbs, L40S).
|
||||||
|
experiment: llm_society_campaign
|
||||||
|
kind: llm_society
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 3
|
||||||
|
agents: 8
|
||||||
|
generations: 10
|
||||||
|
arms: [full, no_grounding, no_sex, no_diversity]
|
||||||
|
g: 0.5
|
||||||
|
lam: 0.3
|
||||||
|
n_test: 40
|
||||||
|
n_val: 30
|
||||||
|
n_conf: 90
|
||||||
|
n_inherit: 600
|
||||||
|
n_candidates: 6
|
||||||
|
elitism: 1
|
||||||
|
n_parents: 4
|
||||||
|
epochs: 3
|
||||||
|
spec_train: 600
|
||||||
|
spec_epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_campaign/s3}
|
||||||
23
configs/llm/society_campaign_s4.yaml
Normal file
23
configs/llm/society_campaign_s4.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# The society campaign: 4-arm ablation, one seed per job (founders trained inline, no cache races).
|
||||||
|
# Seed 1 runs locally (A4000, queue insurance); seeds 2-4 on CX3 (hpc/llm_society.pbs, L40S).
|
||||||
|
experiment: llm_society_campaign
|
||||||
|
kind: llm_society
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 4
|
||||||
|
agents: 8
|
||||||
|
generations: 10
|
||||||
|
arms: [full, no_grounding, no_sex, no_diversity]
|
||||||
|
g: 0.5
|
||||||
|
lam: 0.3
|
||||||
|
n_test: 40
|
||||||
|
n_val: 30
|
||||||
|
n_conf: 90
|
||||||
|
n_inherit: 600
|
||||||
|
n_candidates: 6
|
||||||
|
elitism: 1
|
||||||
|
n_parents: 4
|
||||||
|
epochs: 3
|
||||||
|
spec_train: 600
|
||||||
|
spec_epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_campaign/s4}
|
||||||
20
configs/llm/society_smoke.yaml
Normal file
20
configs/llm/society_smoke.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Smoke test for the LLM society loop — tiny everything; ~15 min on a 16 GB GPU.
|
||||||
|
experiment: llm_society_smoke
|
||||||
|
kind: llm_society
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
agents: 4
|
||||||
|
generations: 2
|
||||||
|
arms: [full]
|
||||||
|
g: 0.5
|
||||||
|
lam: 0.3
|
||||||
|
n_test: 15 # per family
|
||||||
|
n_val: 10
|
||||||
|
n_conf: 30 # total
|
||||||
|
n_inherit: 90 # total
|
||||||
|
n_candidates: 4
|
||||||
|
epochs: 2
|
||||||
|
spec_train: 150
|
||||||
|
spec_epochs: 2
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_smoke}
|
||||||
17
configs/llm/society_v2_calib_a.yaml
Normal file
17
configs/llm/society_v2_calib_a.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Calibration stage A (prereg §4, gates C1a-c + C4): every candidate family's base / specialist
|
||||||
|
# accuracy, specialist confidence AUC, pairwise functional conflict, gen-0 behavioural distance.
|
||||||
|
# GG reviews the printed table and fixes the 12-family set in society_v2_calib_b.yaml + the campaign.
|
||||||
|
# ~1 h on a 16 GB GPU (17 specialists at 600 x 3 epochs, cached and reused by stage B).
|
||||||
|
experiment: llm_society_v2_calib_a
|
||||||
|
kind: llm_society_calib
|
||||||
|
stage: families
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families: [lists, strings, arith, roman, binary, sortletters, setops, numtheory, mixedtoken,
|
||||||
|
caesar, vectors, progression, charfreq, digits, liststats, alphabet, prime]
|
||||||
|
n_test: 100
|
||||||
|
n_probe: 10
|
||||||
|
spec_train: 600
|
||||||
|
spec_epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_v2_calib_a}
|
||||||
20
configs/llm/society_v2_calib_a2.yaml
Normal file
20
configs/llm/society_v2_calib_a2.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Calibration stage A, second pass (prereg §4 amendments, 2026-09-07): the first pass left only six
|
||||||
|
# families in band. Changes, each recorded in the prereg: founder budget 600 -> 1200 x 3 (uniform);
|
||||||
|
# roman / binary / prime prompt spaces enlarged; three new learnable candidates (wordlen,
|
||||||
|
# lettercount, sumeven); the specialist UPPER bound relaxed 0.90 -> 1.00 (the headroom argument it
|
||||||
|
# encoded concerns fusion composing to a ceiling, not transmission/assembly). Families whose
|
||||||
|
# specialist scored <= 0.20 in pass 1 (sortletters, caesar, progression) are not re-trained.
|
||||||
|
experiment: llm_society_v2_calib_a2
|
||||||
|
kind: llm_society_calib
|
||||||
|
stage: families
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families: [lists, strings, arith, roman, binary, setops, numtheory, mixedtoken, vectors, charfreq,
|
||||||
|
digits, liststats, alphabet, prime, wordlen, lettercount, sumeven]
|
||||||
|
spec_hi: 1.0
|
||||||
|
n_test: 100
|
||||||
|
n_probe: 10
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_v2_calib_a2}
|
||||||
25
configs/llm/society_v2_calib_b.yaml
Normal file
25
configs/llm/society_v2_calib_b.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Calibration stage B (prereg §4, gates C2 / C3 / C5) over the 12 families chosen from stage A.
|
||||||
|
# Run three times with stage: transmission | cross | consensus (see Makefile `llm-society-calib`).
|
||||||
|
# Families = stage-A pass-2 option 1 (L=9, gate 0.41; prereg §4a) — pending GG's go.
|
||||||
|
experiment: llm_society_v2_calib_b
|
||||||
|
kind: llm_society_calib
|
||||||
|
stage: transmission
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families: [strings, setops, numtheory, mixedtoken, digits, alphabet, prime, wordlen, roman]
|
||||||
|
# C2: examples-per-family k and epochs to sweep; probe_families are the three whose retention is
|
||||||
|
# measured (spread across answer types: list / word / int).
|
||||||
|
probe_families: [setops, alphabet, digits] # list / letter / integer answers
|
||||||
|
ks: [25, 50, 100, 150]
|
||||||
|
epochs_grid: [2, 3]
|
||||||
|
# C3: the two-founder cross (union-distil vs best-of-6 linear-merge-distil)
|
||||||
|
cross: [setops, alphabet]
|
||||||
|
k_inherit: 100
|
||||||
|
epochs: 3
|
||||||
|
n_candidates: 6
|
||||||
|
n_test: 100
|
||||||
|
n_probe: 10
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_v2_calib_b}
|
||||||
20
configs/llm/society_v2_calib_c2b.yaml
Normal file
20
configs/llm/society_v2_calib_c2b.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# C2b (prereg §4a amendment under review): confidence-gated inheritance. The pre-registered C2 failed
|
||||||
|
# (retention ≤ 0.81 at k ≤ 150 despite competent supply): the child fits eight families of confident
|
||||||
|
# garbage alongside the one competent family. Here the child learns only prompts its source is
|
||||||
|
# confident on (verifier-free). Measures retention by gate τ, the Youden τ* separating own- from
|
||||||
|
# off-family confidence, and the off-family harm of ungated inheritance.
|
||||||
|
experiment: llm_society_v2_calib_b_transmission_conf
|
||||||
|
kind: llm_society_calib
|
||||||
|
stage: transmission_conf
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families: [strings, setops, numtheory, mixedtoken, digits, alphabet, prime, wordlen, roman]
|
||||||
|
probe_families: [setops, alphabet, digits]
|
||||||
|
k_pool: 300 # prompts per family answered by the source (2700 total); gate keeps a subset
|
||||||
|
taus: [0.5, 0.7, 0.85]
|
||||||
|
epochs: 3
|
||||||
|
n_test: 100
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_v2_calib_b_transmission_conf}
|
||||||
43
configs/llm/society_v2_calib_c3_gated.yaml
Normal file
43
configs/llm/society_v2_calib_c3_gated.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
experiment: llm_society_v2_calib_b_cross_gated
|
||||||
|
kind: llm_society_calib
|
||||||
|
stage: cross
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families:
|
||||||
|
- strings
|
||||||
|
- setops
|
||||||
|
- numtheory
|
||||||
|
- mixedtoken
|
||||||
|
- digits
|
||||||
|
- alphabet
|
||||||
|
- prime
|
||||||
|
- wordlen
|
||||||
|
- roman
|
||||||
|
probe_families:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
- digits
|
||||||
|
ks:
|
||||||
|
- 25
|
||||||
|
- 50
|
||||||
|
- 100
|
||||||
|
- 150
|
||||||
|
epochs_grid:
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
cross:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
k_inherit: 300
|
||||||
|
epochs: 3
|
||||||
|
n_candidates: 6
|
||||||
|
n_test: 100
|
||||||
|
n_probe: 10
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
lora:
|
||||||
|
r: 16
|
||||||
|
alpha: 32
|
||||||
|
output:
|
||||||
|
dir: results/llm_society_v2_calib_b_cross_gated
|
||||||
|
conf_gate: 0.5
|
||||||
43
configs/llm/society_v2_calib_c3b_e6.yaml
Normal file
43
configs/llm/society_v2_calib_c3b_e6.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
experiment: llm_society_v2_calib_c3b_e6
|
||||||
|
kind: llm_society_calib
|
||||||
|
stage: cross
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families:
|
||||||
|
- strings
|
||||||
|
- setops
|
||||||
|
- numtheory
|
||||||
|
- mixedtoken
|
||||||
|
- digits
|
||||||
|
- alphabet
|
||||||
|
- prime
|
||||||
|
- wordlen
|
||||||
|
- roman
|
||||||
|
probe_families:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
- digits
|
||||||
|
ks:
|
||||||
|
- 25
|
||||||
|
- 50
|
||||||
|
- 100
|
||||||
|
- 150
|
||||||
|
epochs_grid:
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
cross:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
k_inherit: 300
|
||||||
|
epochs: 6
|
||||||
|
n_candidates: 6
|
||||||
|
n_test: 100
|
||||||
|
n_probe: 10
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
lora:
|
||||||
|
r: 16
|
||||||
|
alpha: 32
|
||||||
|
output:
|
||||||
|
dir: results/llm_society_v2_calib_c3b_e6
|
||||||
|
conf_gate: 0.5
|
||||||
43
configs/llm/society_v2_calib_c3b_r64.yaml
Normal file
43
configs/llm/society_v2_calib_c3b_r64.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
experiment: llm_society_v2_calib_c3b_r64
|
||||||
|
kind: llm_society_calib
|
||||||
|
stage: cross
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families:
|
||||||
|
- strings
|
||||||
|
- setops
|
||||||
|
- numtheory
|
||||||
|
- mixedtoken
|
||||||
|
- digits
|
||||||
|
- alphabet
|
||||||
|
- prime
|
||||||
|
- wordlen
|
||||||
|
- roman
|
||||||
|
probe_families:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
- digits
|
||||||
|
ks:
|
||||||
|
- 25
|
||||||
|
- 50
|
||||||
|
- 100
|
||||||
|
- 150
|
||||||
|
epochs_grid:
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
cross:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
k_inherit: 300
|
||||||
|
epochs: 3
|
||||||
|
n_candidates: 6
|
||||||
|
n_test: 100
|
||||||
|
n_probe: 10
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
lora:
|
||||||
|
r: 64
|
||||||
|
alpha: 128
|
||||||
|
output:
|
||||||
|
dir: results/llm_society_v2_calib_c3b_r64
|
||||||
|
conf_gate: 0.5
|
||||||
43
configs/llm/society_v2_calib_c3b_r64e6.yaml
Normal file
43
configs/llm/society_v2_calib_c3b_r64e6.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
experiment: llm_society_v2_calib_c3b_r64e6
|
||||||
|
kind: llm_society_calib
|
||||||
|
stage: cross
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families:
|
||||||
|
- strings
|
||||||
|
- setops
|
||||||
|
- numtheory
|
||||||
|
- mixedtoken
|
||||||
|
- digits
|
||||||
|
- alphabet
|
||||||
|
- prime
|
||||||
|
- wordlen
|
||||||
|
- roman
|
||||||
|
probe_families:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
- digits
|
||||||
|
ks:
|
||||||
|
- 25
|
||||||
|
- 50
|
||||||
|
- 100
|
||||||
|
- 150
|
||||||
|
epochs_grid:
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
cross:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
k_inherit: 300
|
||||||
|
epochs: 6
|
||||||
|
n_candidates: 6
|
||||||
|
n_test: 100
|
||||||
|
n_probe: 10
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
lora:
|
||||||
|
r: 64
|
||||||
|
alpha: 128
|
||||||
|
output:
|
||||||
|
dir: results/llm_society_v2_calib_c3b_r64e6
|
||||||
|
conf_gate: 0.5
|
||||||
43
configs/llm/society_v2_calib_c3c_t85e3.yaml
Normal file
43
configs/llm/society_v2_calib_c3c_t85e3.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
experiment: llm_society_v2_calib_c3c_t85e3
|
||||||
|
kind: llm_society_calib
|
||||||
|
stage: cross
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families:
|
||||||
|
- strings
|
||||||
|
- setops
|
||||||
|
- numtheory
|
||||||
|
- mixedtoken
|
||||||
|
- digits
|
||||||
|
- alphabet
|
||||||
|
- prime
|
||||||
|
- wordlen
|
||||||
|
- roman
|
||||||
|
probe_families:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
- digits
|
||||||
|
ks:
|
||||||
|
- 25
|
||||||
|
- 50
|
||||||
|
- 100
|
||||||
|
- 150
|
||||||
|
epochs_grid:
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
cross:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
k_inherit: 300
|
||||||
|
epochs: 3
|
||||||
|
n_candidates: 6
|
||||||
|
n_test: 100
|
||||||
|
n_probe: 10
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
lora:
|
||||||
|
r: 16
|
||||||
|
alpha: 32
|
||||||
|
output:
|
||||||
|
dir: results/llm_society_v2_calib_c3c_t85e3
|
||||||
|
conf_gate: 0.85
|
||||||
43
configs/llm/society_v2_calib_c3c_t85e6.yaml
Normal file
43
configs/llm/society_v2_calib_c3c_t85e6.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
experiment: llm_society_v2_calib_c3c_t85e6
|
||||||
|
kind: llm_society_calib
|
||||||
|
stage: cross
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families:
|
||||||
|
- strings
|
||||||
|
- setops
|
||||||
|
- numtheory
|
||||||
|
- mixedtoken
|
||||||
|
- digits
|
||||||
|
- alphabet
|
||||||
|
- prime
|
||||||
|
- wordlen
|
||||||
|
- roman
|
||||||
|
probe_families:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
- digits
|
||||||
|
ks:
|
||||||
|
- 25
|
||||||
|
- 50
|
||||||
|
- 100
|
||||||
|
- 150
|
||||||
|
epochs_grid:
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
cross:
|
||||||
|
- setops
|
||||||
|
- alphabet
|
||||||
|
k_inherit: 300
|
||||||
|
epochs: 6
|
||||||
|
n_candidates: 6
|
||||||
|
n_test: 100
|
||||||
|
n_probe: 10
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
lora:
|
||||||
|
r: 16
|
||||||
|
alpha: 32
|
||||||
|
output:
|
||||||
|
dir: results/llm_society_v2_calib_c3c_t85e6
|
||||||
|
conf_gate: 0.85
|
||||||
29
configs/llm/society_v2_s1.yaml
Normal file
29
configs/llm/society_v2_s1.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# v2 society campaign, seed 1 (prereg tasks/prereg-llm-society-v2.md §3). The PBS array
|
||||||
|
# (hpc/llm_society_v2.pbs) derives one config per (seed, arm) from this template; running it directly
|
||||||
|
# runs all four arms sequentially (the local-GPU hedge).
|
||||||
|
#
|
||||||
|
# CALIBRATION-DEPENDENT FIELDS — set after gates C1/C2 pass, before submission:
|
||||||
|
# families : the 9 selected by stage A pass 2 (C1, gate 0.41) — SET
|
||||||
|
# k_inherit / epochs / conf_gate : from stage B (C2 + C2b), 2026-09-07 — SET
|
||||||
|
experiment: llm_society_v2
|
||||||
|
kind: llm_society_v2
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families: [strings, setops, numtheory, mixedtoken, digits, alphabet, prime, wordlen, roman]
|
||||||
|
agents: 9
|
||||||
|
generations: 12
|
||||||
|
arms: [full, no_grounding, no_sex, no_diversity]
|
||||||
|
g: 0.85
|
||||||
|
lam: 0.3
|
||||||
|
max_mate_use: 2
|
||||||
|
n_test: 27 # per family -> 243 overall (SE 0.032)
|
||||||
|
n_val: 13 # per family -> 117 (selection signal)
|
||||||
|
n_conf: 13 # per family, fresh each generation
|
||||||
|
k_inherit: 300 # per family, fresh each generation (C2b: k=300 is the main retention driver)
|
||||||
|
epochs: 3 # child SFT epochs (C2b)
|
||||||
|
conf_gate: 0.85 # confidence-gated inheritance, all arms (C2b mean retention 0.88; C3c: tight gate = 6-epoch retention at 3 epochs)
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
resume: true
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_v2/s1}
|
||||||
29
configs/llm/society_v2_s2.yaml
Normal file
29
configs/llm/society_v2_s2.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# v2 society campaign, seed 2 (prereg tasks/prereg-llm-society-v2.md §3). The PBS array
|
||||||
|
# (hpc/llm_society_v2.pbs) derives one config per (seed, arm) from this template; running it directly
|
||||||
|
# runs all four arms sequentially (the local-GPU hedge).
|
||||||
|
#
|
||||||
|
# CALIBRATION-DEPENDENT FIELDS — set after gates C1/C2 pass, before submission:
|
||||||
|
# families : the 9 selected by stage A pass 2 (C1, gate 0.41) — SET
|
||||||
|
# k_inherit / epochs / conf_gate : from stage B (C2 + C2b), 2026-09-07 — SET
|
||||||
|
experiment: llm_society_v2
|
||||||
|
kind: llm_society_v2
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 2
|
||||||
|
families: [strings, setops, numtheory, mixedtoken, digits, alphabet, prime, wordlen, roman]
|
||||||
|
agents: 9
|
||||||
|
generations: 12
|
||||||
|
arms: [full, no_grounding, no_sex, no_diversity]
|
||||||
|
g: 0.85
|
||||||
|
lam: 0.3
|
||||||
|
max_mate_use: 2
|
||||||
|
n_test: 27 # per family -> 243 overall (SE 0.032)
|
||||||
|
n_val: 13 # per family -> 117 (selection signal)
|
||||||
|
n_conf: 13 # per family, fresh each generation
|
||||||
|
k_inherit: 300 # per family, fresh each generation (C2b: k=300 is the main retention driver)
|
||||||
|
epochs: 3 # child SFT epochs (C2b)
|
||||||
|
conf_gate: 0.85 # confidence-gated inheritance, all arms (C2b mean retention 0.88; C3c: tight gate = 6-epoch retention at 3 epochs)
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
resume: true
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_v2/s2}
|
||||||
29
configs/llm/society_v2_s3.yaml
Normal file
29
configs/llm/society_v2_s3.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# v2 society campaign, seed 3 (prereg tasks/prereg-llm-society-v2.md §3). The PBS array
|
||||||
|
# (hpc/llm_society_v2.pbs) derives one config per (seed, arm) from this template; running it directly
|
||||||
|
# runs all four arms sequentially (the local-GPU hedge).
|
||||||
|
#
|
||||||
|
# CALIBRATION-DEPENDENT FIELDS — set after gates C1/C2 pass, before submission:
|
||||||
|
# families : the 9 selected by stage A pass 2 (C1, gate 0.41) — SET
|
||||||
|
# k_inherit / epochs / conf_gate : from stage B (C2 + C2b), 2026-09-07 — SET
|
||||||
|
experiment: llm_society_v2
|
||||||
|
kind: llm_society_v2
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 3
|
||||||
|
families: [strings, setops, numtheory, mixedtoken, digits, alphabet, prime, wordlen, roman]
|
||||||
|
agents: 9
|
||||||
|
generations: 12
|
||||||
|
arms: [full, no_grounding, no_sex, no_diversity]
|
||||||
|
g: 0.85
|
||||||
|
lam: 0.3
|
||||||
|
max_mate_use: 2
|
||||||
|
n_test: 27 # per family -> 243 overall (SE 0.032)
|
||||||
|
n_val: 13 # per family -> 117 (selection signal)
|
||||||
|
n_conf: 13 # per family, fresh each generation
|
||||||
|
k_inherit: 300 # per family, fresh each generation (C2b: k=300 is the main retention driver)
|
||||||
|
epochs: 3 # child SFT epochs (C2b)
|
||||||
|
conf_gate: 0.85 # confidence-gated inheritance, all arms (C2b mean retention 0.88; C3c: tight gate = 6-epoch retention at 3 epochs)
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
resume: true
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_v2/s3}
|
||||||
29
configs/llm/society_v2_s4.yaml
Normal file
29
configs/llm/society_v2_s4.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# v2 society campaign, seed 4 (prereg tasks/prereg-llm-society-v2.md §3). The PBS array
|
||||||
|
# (hpc/llm_society_v2.pbs) derives one config per (seed, arm) from this template; running it directly
|
||||||
|
# runs all four arms sequentially (the local-GPU hedge).
|
||||||
|
#
|
||||||
|
# CALIBRATION-DEPENDENT FIELDS — set after gates C1/C2 pass, before submission:
|
||||||
|
# families : the 9 selected by stage A pass 2 (C1, gate 0.41) — SET
|
||||||
|
# k_inherit / epochs / conf_gate : from stage B (C2 + C2b), 2026-09-07 — SET
|
||||||
|
experiment: llm_society_v2
|
||||||
|
kind: llm_society_v2
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 4
|
||||||
|
families: [strings, setops, numtheory, mixedtoken, digits, alphabet, prime, wordlen, roman]
|
||||||
|
agents: 9
|
||||||
|
generations: 12
|
||||||
|
arms: [full, no_grounding, no_sex, no_diversity]
|
||||||
|
g: 0.85
|
||||||
|
lam: 0.3
|
||||||
|
max_mate_use: 2
|
||||||
|
n_test: 27 # per family -> 243 overall (SE 0.032)
|
||||||
|
n_val: 13 # per family -> 117 (selection signal)
|
||||||
|
n_conf: 13 # per family, fresh each generation
|
||||||
|
k_inherit: 300 # per family, fresh each generation (C2b: k=300 is the main retention driver)
|
||||||
|
epochs: 3 # child SFT epochs (C2b)
|
||||||
|
conf_gate: 0.85 # confidence-gated inheritance, all arms (C2b mean retention 0.88; C3c: tight gate = 6-epoch retention at 3 epochs)
|
||||||
|
spec_train: 1200
|
||||||
|
spec_epochs: 3
|
||||||
|
resume: true
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_v2/s4}
|
||||||
21
configs/llm/society_v2_smoke.yaml
Normal file
21
configs/llm/society_v2_smoke.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Smoke test for the v2 society loop: tiny everything, all four arms, resume exercised by the
|
||||||
|
# per-generation checkpoint. ~20 min on a 16 GB GPU. Exit 0 + figure renders = pass.
|
||||||
|
experiment: llm_society_v2_smoke
|
||||||
|
kind: llm_society_v2
|
||||||
|
base_model: Qwen/Qwen2.5-0.5B-Instruct
|
||||||
|
seed: 1
|
||||||
|
families: [lists, roman, binary, setops]
|
||||||
|
agents: 4
|
||||||
|
generations: 2
|
||||||
|
arms: [full, no_grounding, no_sex, no_diversity]
|
||||||
|
g: 0.85
|
||||||
|
lam: 0.3
|
||||||
|
n_test: 8 # per family
|
||||||
|
n_val: 5 # per family
|
||||||
|
n_conf: 5 # per family, fresh each generation
|
||||||
|
k_inherit: 20 # per family
|
||||||
|
epochs: 2
|
||||||
|
spec_train: 120
|
||||||
|
spec_epochs: 2
|
||||||
|
lora: {r: 16, alpha: 32}
|
||||||
|
output: {dir: results/llm_society_v2_smoke}
|
||||||
|
|
@ -11,7 +11,7 @@ n_replicates: 5
|
||||||
# grounding arrests it. Falsifier: the signs appear only for the histogram -> real neural
|
# grounding arrests it. Falsifier: the signs appear only for the histogram -> real neural
|
||||||
# inductive biases break the Wright-Fisher mapping. (The sequence VAE is implemented but
|
# inductive biases break the Wright-Fisher mapping. (The sequence VAE is implemented but
|
||||||
# excluded here: it does not clear the gen-0 fidelity gate on the Zipf-codeword task, so
|
# excluded here: it does not clear the gen-0 fidelity gate on the Zipf-codeword task, so
|
||||||
# its collapse would be confounded with underfitting.)
|
# its collapse would be confounded with underfitting; see tasks/todo.md.)
|
||||||
generations: 22
|
generations: 22
|
||||||
|
|
||||||
synthetic:
|
synthetic:
|
||||||
|
|
|
||||||
64
figures/plot_collapse_null.py
Normal file
64
figures/plot_collapse_null.py
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
"""E1 figure: reproduce collapse (null model).
|
||||||
|
|
||||||
|
Shows tail-first collapse under pure neutral drift: geometric H decay matching the
|
||||||
|
analytic law, tail items dying faster than head items, support -> 1 and forward-KL
|
||||||
|
diverging. Usage: python figures/plot_collapse_null.py [results/collapse_null]
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
from _figlib import load_bundle, mean_ci, savefig # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def main(results_dir: str = "results/collapse_null") -> None:
|
||||||
|
df, cfg = load_bundle(results_dir)
|
||||||
|
n = cfg["dynamics"]["n"]
|
||||||
|
|
||||||
|
gens, Hmean, Hci = mean_ci(df, "generation", "heterozygosity")
|
||||||
|
H0 = Hmean[0]
|
||||||
|
analytic = H0 * (1.0 - 1.0 / n) ** gens
|
||||||
|
|
||||||
|
m = df.groupby("generation").mean(numeric_only=True)
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(1, 3, figsize=(15, 4.2))
|
||||||
|
|
||||||
|
# Panel 1: heterozygosity decay vs the analytic law
|
||||||
|
ax = axes[0]
|
||||||
|
ax.plot(gens, Hmean, color="#1f77b4", label="simulation (mean)")
|
||||||
|
ax.fill_between(gens, Hmean - Hci, Hmean + Hci, color="#1f77b4", alpha=0.25)
|
||||||
|
ax.plot(gens, analytic, "k--", label=r"$H_0(1-1/n)^t$")
|
||||||
|
ax.set(xlabel="generation", ylabel="heterozygosity $H$",
|
||||||
|
title=f"Geometric decay (n={n})")
|
||||||
|
ax.legend(frameon=False)
|
||||||
|
|
||||||
|
# Panel 2: tail-first — fraction of tail vs head items still alive
|
||||||
|
ax = axes[1]
|
||||||
|
ax.plot(m.index, m["tail_frac_alive"], color="#d62728", label="tail items alive")
|
||||||
|
ax.plot(m.index, m["head_frac_alive"], color="#2ca02c", label="head items alive")
|
||||||
|
ax.set(xlabel="generation", ylabel="fraction of items surviving",
|
||||||
|
title="Tail dies first", yscale="log")
|
||||||
|
ax.legend(frameon=False)
|
||||||
|
|
||||||
|
# Panel 3: support collapse and KL divergence
|
||||||
|
ax = axes[2]
|
||||||
|
ax.plot(m.index, m["support_size"], color="#9467bd", label="support size")
|
||||||
|
ax.set(xlabel="generation", ylabel="support size", yscale="log", title="Collapse")
|
||||||
|
ax2 = ax.twinx()
|
||||||
|
ax2.plot(m.index, m["forward_kl"], color="#ff7f0e", label="forward KL")
|
||||||
|
ax2.set_ylabel(r"forward KL $D_{KL}(p^*\,\|\,p_t)$", color="#ff7f0e")
|
||||||
|
ax.legend(loc="center right", frameon=False)
|
||||||
|
|
||||||
|
fig.suptitle("E1 — distillation without grounding collapses, tail first", y=1.02)
|
||||||
|
fig.tight_layout()
|
||||||
|
savefig(fig, results_dir, "collapse_null")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
@ -31,10 +31,11 @@ def _agg(df, keys, value):
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
bdm, _ = load_bundle("results/fig5_speciation_bdm")
|
bdm, _ = load_bundle("results/fig5_speciation_bdm")
|
||||||
|
nk, _ = load_bundle("results/speciation_bdm_nk")
|
||||||
rhos = sorted(bdm["rho"].unique())
|
rhos = sorted(bdm["rho"].unique())
|
||||||
colors = plt.cm.viridis(np.linspace(0.15, 0.85, len(rhos)))
|
colors = plt.cm.viridis(np.linspace(0.15, 0.85, len(rhos)))
|
||||||
|
|
||||||
fig, axes = plt.subplots(1, 2, figsize=(11, 5))
|
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
|
||||||
|
|
||||||
# Panel A: hybrid fitness vs divergence, per epistasis density, + parent fitness.
|
# Panel A: hybrid fitness vs divergence, per epistasis density, + parent fitness.
|
||||||
ax = axes[0]
|
ax = axes[0]
|
||||||
|
|
@ -59,11 +60,19 @@ def main() -> None:
|
||||||
title="The isolation cliff moves to lower divergence\nas epistasis density rises")
|
title="The isolation cliff moves to lower divergence\nas epistasis density rises")
|
||||||
ax.legend(frameon=False, fontsize=9, title="epistasis density")
|
ax.legend(frameon=False, fontsize=9, title="epistasis density")
|
||||||
|
|
||||||
|
# Panel C: NK epistasis wedge — recombination gain vs ruggedness K.
|
||||||
|
ax = axes[2]
|
||||||
|
g = _agg(nk, "K", "offspring_minus_parent")
|
||||||
|
ax.axhline(0, color="#999", lw=0.8, ls=":")
|
||||||
|
ax.plot(g["K"], g["mean"], "-o", color="#d62728", lw=2)
|
||||||
|
ax.fill_between(g["K"], g["mean"] - g["se"], g["mean"] + g["se"], color="#d62728", alpha=0.15)
|
||||||
|
ax.set(xlabel="landscape ruggedness $K$ (epistasis)", ylabel="recombination gain\n(hybrid − worse parent)",
|
||||||
|
title="Epistasis wedge: recombining adapted parents\nflips from gain to loss as ruggedness grows")
|
||||||
|
|
||||||
fig.suptitle("Model speciation: when two diverged models are too incompatible to merge",
|
fig.suptitle("E12 — model speciation: when two diverged models are too incompatible to merge",
|
||||||
y=1.02, fontsize=13)
|
y=1.02, fontsize=13)
|
||||||
fig.tight_layout()
|
fig.tight_layout()
|
||||||
savefig(fig, "results/fig5_speciation_bdm", "fig5_speciation_bdm")
|
savefig(fig, "results/fig5_speciation_bdm", "E12")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ def main() -> None:
|
||||||
|
|
||||||
fig.tight_layout()
|
fig.tight_layout()
|
||||||
letter_axes(fig)
|
letter_axes(fig)
|
||||||
savefig(fig, "results/figS13_mating_breadth", "figS13_mating_breadth")
|
savefig(fig, "results/figS13_mating_breadth", "E14")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
112
figures/plot_llm_compose.py
Normal file
112
figures/plot_llm_compose.py
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
"""Composition-decay figure (prereg v3 §8) — written before unblinding.
|
||||||
|
|
||||||
|
(A) Composition **surplus** S_t = composed − best single parent, per arm over generations, with the
|
||||||
|
zero line: the vertical claim, and whether it survives inheritance.
|
||||||
|
(B) Own-skill retention q_t per lineage (math on GSM8K, code on MBPP), dry vs grounded — the
|
||||||
|
denominators of the prediction.
|
||||||
|
(C) rho_t, the behavioural correlation between the two lineages: the mechanism, if it rises.
|
||||||
|
(D) Observed composed accuracy against the framework's forecast Ĉ_t (one free scale, fixed at
|
||||||
|
generation 0) — H3, the paper's predictive claim, drawn as a line the data can miss.
|
||||||
|
|
||||||
|
Reads only committed bundles: one bundle directory, or a campaign directory of ``s*/`` bundles.
|
||||||
|
Usage: python figures/plot_llm_compose.py [results/llm_compose]
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||||
|
from _figlib import mean_ci, savefig # noqa: E402
|
||||||
|
from llm.compose import predicted_composition # noqa: E402
|
||||||
|
|
||||||
|
ARMS = [("dry", "#d62728", "dry · blending operator"),
|
||||||
|
("grounded", "#2ca02c", "grounded (g = 0.10) · blending"),
|
||||||
|
("dry_cat", "#1f77b4", "dry · union operator (cat)")]
|
||||||
|
|
||||||
|
|
||||||
|
def load_any(results_dir: Path) -> pd.DataFrame:
|
||||||
|
if (results_dir / "results.parquet").exists():
|
||||||
|
paths = [results_dir]
|
||||||
|
else:
|
||||||
|
paths = sorted(p.parent for p in results_dir.glob("*/results.parquet"))
|
||||||
|
if not paths:
|
||||||
|
raise SystemExit(f"no results.parquet under {results_dir}")
|
||||||
|
return pd.concat([pd.read_parquet(p / "results.parquet") for p in paths], ignore_index=True)
|
||||||
|
|
||||||
|
|
||||||
|
def series(df: pd.DataFrame, arm: str, metric: str) -> pd.DataFrame:
|
||||||
|
return df[(df.arm == arm) & (df.metric == metric)][["seed", "generation", "value"]]
|
||||||
|
|
||||||
|
|
||||||
|
def main(results_dir: str = "results/llm_compose") -> None:
|
||||||
|
rd = Path(results_dir)
|
||||||
|
df = load_any(rd)
|
||||||
|
arms = [a for a in ARMS if a[0] in set(df.arm.unique())]
|
||||||
|
n_seeds = df.seed.nunique()
|
||||||
|
fig, ax = plt.subplots(1, 4, figsize=(21, 4.6))
|
||||||
|
|
||||||
|
# (A) surplus
|
||||||
|
for arm, color, label in arms:
|
||||||
|
s = series(df, arm, "surplus")
|
||||||
|
if s.empty:
|
||||||
|
continue
|
||||||
|
x, m, h = mean_ci(s, "generation", "value")
|
||||||
|
ax[0].plot(x, m, "-o", color=color, label=label, lw=2, ms=4)
|
||||||
|
ax[0].fill_between(x, m - h, m + h, color=color, alpha=0.15, lw=0)
|
||||||
|
ax[0].axhline(0, color="k", lw=1, ls="--")
|
||||||
|
ax[0].set_title("(A) composition surplus\ncomposed − best single parent", fontsize=10)
|
||||||
|
ax[0].set_xlabel("generation"); ax[0].set_ylabel("surplus"); ax[0].legend(fontsize=8)
|
||||||
|
|
||||||
|
# (B) own-skill retention
|
||||||
|
for arm, color, _ in arms:
|
||||||
|
for metric, ls in (("q_math", "-"), ("q_code", ":")):
|
||||||
|
s = series(df, arm, metric)
|
||||||
|
if s.empty:
|
||||||
|
continue
|
||||||
|
x, m, _h = mean_ci(s, "generation", "value")
|
||||||
|
ax[1].plot(x, m, ls, color=color, lw=2,
|
||||||
|
label=f"{arm} · {metric.split('_')[1]}" if arm != "dry_linear" else None)
|
||||||
|
ax[1].set_title("(B) own-skill retention q_t\nsolid math (GSM8K), dotted code (MBPP)", fontsize=10)
|
||||||
|
ax[1].set_xlabel("generation"); ax[1].set_ylabel("accuracy"); ax[1].legend(fontsize=8)
|
||||||
|
|
||||||
|
# (C) rho
|
||||||
|
for arm, color, label in arms:
|
||||||
|
s = series(df, arm, "rho_behav")
|
||||||
|
if s.empty:
|
||||||
|
continue
|
||||||
|
x, m, h = mean_ci(s, "generation", "value")
|
||||||
|
ax[2].plot(x, m, "-o", color=color, label=label, lw=2, ms=4)
|
||||||
|
ax[2].fill_between(x, m - h, m + h, color=color, alpha=0.15, lw=0)
|
||||||
|
ax[2].set_title("(C) lineage correlation ρ_t\n(agreement on a shared probe)", fontsize=10)
|
||||||
|
ax[2].set_xlabel("generation"); ax[2].set_ylabel("ρ"); ax[2].legend(fontsize=8)
|
||||||
|
|
||||||
|
# (D) observed vs predicted, dry arm
|
||||||
|
for arm, color, label in arms:
|
||||||
|
obs = series(df, arm, "composed_acc").groupby("generation").value.mean()
|
||||||
|
qm = series(df, arm, "q_math").groupby("generation").value.mean()
|
||||||
|
qc = series(df, arm, "q_code").groupby("generation").value.mean()
|
||||||
|
rho = series(df, arm, "rho_behav").groupby("generation").value.mean()
|
||||||
|
if obs.empty or len(obs) < 2:
|
||||||
|
continue
|
||||||
|
pred = predicted_composition(qm.to_numpy(), qc.to_numpy(), rho.to_numpy(), float(obs.iloc[0]))
|
||||||
|
ax[3].plot(obs.index, obs.to_numpy(), "-o", color=color, lw=2, ms=4, label=f"{label} observed")
|
||||||
|
ax[3].plot(obs.index, pred, "--", color=color, lw=1.5, alpha=0.8,
|
||||||
|
label=f"{label} predicted Ĉ")
|
||||||
|
ax[3].set_title("(D) H3: observed vs the closed form\nĈ = c₀·q_math·q_code·(1−ρ)/(1−ρ₀)", fontsize=10)
|
||||||
|
ax[3].set_xlabel("generation"); ax[3].set_ylabel("composed accuracy"); ax[3].legend(fontsize=7)
|
||||||
|
|
||||||
|
fig.suptitle(f"llm_compose — does a composed capability survive inheritance? "
|
||||||
|
f"({n_seeds} seed{'s' if n_seeds != 1 else ''}, mean ± 95% CI)", y=1.03)
|
||||||
|
fig.tight_layout()
|
||||||
|
savefig(fig, rd, "llm_compose")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
||||||
94
figures/plot_llm_directed.py
Normal file
94
figures/plot_llm_directed.py
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
"""llm_directed figure — directed sex in weight space (E10): breed offspring, select the fittest.
|
||||||
|
|
||||||
|
A population of recombinant offspring (weighted merges of the specialists) is scored on a held-out
|
||||||
|
validation split by the verifier; the winners (best-overall, best-worst-family) are reported on a
|
||||||
|
fresh test split against the uniform-soup blend and the best single specialist. Two panels: (A)
|
||||||
|
per-family accuracy — directed offspring (green) vs soup (orange) vs specialists (blue); (B) overall
|
||||||
|
vs worst-family, with the best-specialist bar as the parent ceiling. The suptitle reports whether
|
||||||
|
directed selection beat the single a-priori soup. Reads only the committed bundle.
|
||||||
|
|
||||||
|
Usage: python figures/plot_llm_directed.py [results/llm_directed]
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
from _figlib import load_seed_bundles, savefig # noqa: E402
|
||||||
|
|
||||||
|
_FAMS = ["lists", "strings", "arith"]
|
||||||
|
_DIRECTED = {"directed_overall": "directed:overall", "directed_balanced": "directed:balanced"}
|
||||||
|
|
||||||
|
|
||||||
|
def _acc(df, model, metric):
|
||||||
|
r = df[(df["model"] == model) & (df["metric"] == metric)]["accuracy"]
|
||||||
|
return float(r.mean()) if len(r) else float("nan")
|
||||||
|
|
||||||
|
|
||||||
|
def main(results_dir: str = "results/llm_directed") -> None:
|
||||||
|
df, cfg = load_seed_bundles(results_dir) # seed-mean when the bundle has s{seed}/ sub-bundles
|
||||||
|
present = set(df["model"].unique())
|
||||||
|
specialists = sorted(m for m in present if m.startswith("spec_"))
|
||||||
|
directed = [m for m in _DIRECTED if m in present]
|
||||||
|
soup = ["merge_soup"] if "merge_soup" in present else []
|
||||||
|
models = ["base"] + specialists + soup + directed
|
||||||
|
labels = {"base": "base", **{s: s.replace("spec_", "spec:") for s in specialists},
|
||||||
|
"merge_soup": "soup (uniform)", **_DIRECTED}
|
||||||
|
colors = {"base": "#7f7f7f", **{s: "#1f77b4" for s in specialists},
|
||||||
|
"merge_soup": "#ff7f0e", **{m: "#2ca02c" for m in directed}}
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(1, 2, figsize=(13, 5))
|
||||||
|
|
||||||
|
# Panel A: per-family accuracy.
|
||||||
|
ax = axes[0]
|
||||||
|
x = np.arange(len(_FAMS))
|
||||||
|
w = 0.8 / len(models)
|
||||||
|
for i, mdl in enumerate(models):
|
||||||
|
vals = [_acc(df, mdl, f) for f in _FAMS]
|
||||||
|
ax.bar(x + (i - (len(models) - 1) / 2) * w, vals, w, label=labels[mdl], color=colors[mdl],
|
||||||
|
alpha=0.9 if mdl in directed else 0.65)
|
||||||
|
ax.set_xticks(x); ax.set_xticklabels(_FAMS)
|
||||||
|
ax.set(ylabel="accuracy", title="Per-family: directed offspring (green), selected on the\n"
|
||||||
|
"verifier, vs the single uniform soup (orange) and the parents")
|
||||||
|
ax.legend(frameon=False, fontsize=8, ncol=2)
|
||||||
|
|
||||||
|
# Panel B: overall vs worst-family, with the best-specialist ceiling.
|
||||||
|
ax = axes[1]
|
||||||
|
x2 = np.arange(len(models))
|
||||||
|
for off, metric, hatch, lab in [(-0.2, "overall", "", "overall"),
|
||||||
|
(0.2, "worst_family", "//", "worst family")]:
|
||||||
|
ax.bar(x2 + off, [_acc(df, m, metric) for m in models], 0.38,
|
||||||
|
color=[colors[m] for m in models], hatch=hatch, alpha=0.85, label=lab,
|
||||||
|
edgecolor="white")
|
||||||
|
ax.set_xticks(x2); ax.set_xticklabels([labels[m] for m in models], rotation=25, ha="right",
|
||||||
|
fontsize=8)
|
||||||
|
if specialists:
|
||||||
|
ceil = max(_acc(df, s, "overall") for s in specialists)
|
||||||
|
ax.axhline(ceil, ls=":", c="#1f77b4", lw=1, alpha=0.7) # best-parent ceiling
|
||||||
|
ax.set(ylabel="accuracy", title="Overall (solid) vs worst-family (hatched):\n"
|
||||||
|
"directed offspring vs soup vs the best parent (dotted)")
|
||||||
|
ax.legend(frameon=False, fontsize=9)
|
||||||
|
|
||||||
|
best_dir = max([_acc(df, m, "overall") for m in directed], default=float("nan"))
|
||||||
|
soup_o = _acc(df, "merge_soup", "overall")
|
||||||
|
best_spec = max([_acc(df, s, "overall") for s in specialists], default=float("nan"))
|
||||||
|
if best_dir > soup_o + 0.005:
|
||||||
|
verdict = f"directed {best_dir:.2f} > soup {soup_o:.2f} overall"
|
||||||
|
elif best_dir > soup_o - 0.005:
|
||||||
|
verdict = f"directed {best_dir:.2f} ≈ soup {soup_o:.2f} overall"
|
||||||
|
else:
|
||||||
|
verdict = f"directed {best_dir:.2f} < soup {soup_o:.2f} overall"
|
||||||
|
verdict += f" (best parent {best_spec:.2f})"
|
||||||
|
fig.suptitle(f"llm_directed — directed sex (breed offspring + select on the verifier): {verdict} "
|
||||||
|
f"({cfg['base_model'].split('/')[-1]})", y=1.0, fontsize=12)
|
||||||
|
fig.tight_layout()
|
||||||
|
savefig(fig, results_dir, "llm_directed")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
||||||
86
figures/plot_llm_merge.py
Normal file
86
figures/plot_llm_merge.py
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
"""llm_merge figure — recombining specialist LLMs (blueprint C2/C4, the real-LLM image of E8).
|
||||||
|
|
||||||
|
LoRA specialists on disjoint task families are merged (weight-space) into one deployable model. The
|
||||||
|
recombined model beats any single specialist overall and — the sharper signature — is competent
|
||||||
|
across *all* families, which no single parent is. Two panels: (A) per-family accuracy for the base,
|
||||||
|
each specialist, and the merges (each specialist spikes on its own family; the merges are high
|
||||||
|
everywhere); (B) overall vs worst-family accuracy (the merges dominate both, especially worst-family).
|
||||||
|
Reads only the committed bundle.
|
||||||
|
|
||||||
|
Usage: python figures/plot_llm_merge.py [results/llm_merge]
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
from _figlib import load_seed_bundles, savefig # noqa: E402
|
||||||
|
|
||||||
|
_FAMS = ["lists", "strings", "arith"]
|
||||||
|
|
||||||
|
|
||||||
|
def _acc(df, model, metric):
|
||||||
|
r = df[(df["model"] == model) & (df["metric"] == metric)]["accuracy"]
|
||||||
|
return float(r.mean()) if len(r) else float("nan")
|
||||||
|
|
||||||
|
|
||||||
|
def main(results_dir: str = "results/llm_merge") -> None:
|
||||||
|
df, cfg = load_seed_bundles(results_dir) # seed-mean when the bundle has s{seed}/ sub-bundles
|
||||||
|
specialists = sorted(m for m in df["model"].unique() if m.startswith("spec_"))
|
||||||
|
merges = sorted(m for m in df["model"].unique() if m.startswith("merge_"))
|
||||||
|
models = ["base"] + specialists + merges
|
||||||
|
labels = {"base": "base", **{s: s.replace("spec_", "spec:") for s in specialists},
|
||||||
|
**{m: m.replace("merge_", "merge:") for m in merges}}
|
||||||
|
colors = {"base": "#7f7f7f"}
|
||||||
|
for s in specialists:
|
||||||
|
colors[s] = "#1f77b4"
|
||||||
|
for m in merges:
|
||||||
|
colors[m] = "#2ca02c"
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(1, 2, figsize=(13, 5))
|
||||||
|
|
||||||
|
# Panel A: per-family accuracy, grouped by model.
|
||||||
|
ax = axes[0]
|
||||||
|
x = np.arange(len(_FAMS))
|
||||||
|
w = 0.8 / len(models)
|
||||||
|
for i, mdl in enumerate(models):
|
||||||
|
vals = [_acc(df, mdl, f) for f in _FAMS]
|
||||||
|
ax.bar(x + (i - (len(models) - 1) / 2) * w, vals, w, label=labels[mdl],
|
||||||
|
color=colors[mdl], alpha=0.9 if mdl.startswith("merge_") else 0.7)
|
||||||
|
ax.set_xticks(x); ax.set_xticklabels(_FAMS)
|
||||||
|
ax.set(ylabel="accuracy", title="Per-family: each specialist spikes on its own family; the merges\n"
|
||||||
|
"(green) are competent everywhere (but averaging dilutes some peaks)")
|
||||||
|
ax.legend(frameon=False, fontsize=8, ncol=2)
|
||||||
|
|
||||||
|
# Panel B: overall vs worst-family, per model.
|
||||||
|
ax = axes[1]
|
||||||
|
x2 = np.arange(len(models))
|
||||||
|
for off, metric, hatch, lab in [(-0.2, "overall", "", "overall"),
|
||||||
|
(0.2, "worst_family", "//", "worst family")]:
|
||||||
|
ax.bar(x2 + off, [_acc(df, m, metric) for m in models], 0.38,
|
||||||
|
color=[colors[m] for m in models], hatch=hatch, alpha=0.85, label=lab,
|
||||||
|
edgecolor="white")
|
||||||
|
ax.set_xticks(x2); ax.set_xticklabels([labels[m] for m in models], rotation=25, ha="right",
|
||||||
|
fontsize=8)
|
||||||
|
ax.set(ylabel="accuracy", title="Overall (solid) vs worst-family (hatched):\n"
|
||||||
|
"the recombined model vs the best single specialist")
|
||||||
|
ax.legend(frameon=False, fontsize=9)
|
||||||
|
|
||||||
|
best_spec = max(_acc(df, m, "overall") for m in specialists)
|
||||||
|
best_merge = max(_acc(df, m, "overall") for m in merges)
|
||||||
|
verdict = (f"recombined {best_merge:.2f} > best specialist {best_spec:.2f} overall"
|
||||||
|
if best_merge > best_spec + 0.005 else
|
||||||
|
f"recombined {best_merge:.2f} ≈ best specialist {best_spec:.2f} overall")
|
||||||
|
fig.suptitle(f"llm_merge — recombining decorrelated specialist LLMs: {verdict} "
|
||||||
|
f"({cfg['base_model'].split('/')[-1]})", y=1.0, fontsize=12)
|
||||||
|
fig.tight_layout()
|
||||||
|
savefig(fig, results_dir, "llm_merge")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
||||||
97
figures/plot_llm_moe.py
Normal file
97
figures/plot_llm_moe.py
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
"""llm_moe figure — union-preserving recombination (route / max-merge) vs fusion (soup / ties).
|
||||||
|
|
||||||
|
The real-weight image of E8's *max*: keep every specialist intact and *select* (route per prompt, or
|
||||||
|
per module) instead of averaging the deltas. Two panels: (A) per-family accuracy for the base, each
|
||||||
|
specialist, the fusion merges, and the union operators — the union operators should match the best
|
||||||
|
specialist on every family (they *are* that specialist there), while fusion may dilute or compose;
|
||||||
|
(B) overall vs worst-family, fusion vs union, with the routing ceiling (moe_oracle) marked. The
|
||||||
|
suptitle reports whether union beats fusion (dilution regime) or they converge (composition regime).
|
||||||
|
Reads only the committed bundle.
|
||||||
|
|
||||||
|
Usage: python figures/plot_llm_moe.py [results/llm_moe]
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
from _figlib import load_seed_bundles, savefig # noqa: E402
|
||||||
|
|
||||||
|
_FAMS = ["lists", "strings", "arith"]
|
||||||
|
_FUSION = {"merge_soup": "fuse:soup", "merge_ties": "fuse:ties"}
|
||||||
|
_UNION = {"moe_oracle": "route:oracle", "moe_learned": "route:learned", "max_merge": "max-merge"}
|
||||||
|
|
||||||
|
|
||||||
|
def _acc(df, model, metric):
|
||||||
|
r = df[(df["model"] == model) & (df["metric"] == metric)]["accuracy"]
|
||||||
|
return float(r.mean()) if len(r) else float("nan")
|
||||||
|
|
||||||
|
|
||||||
|
def main(results_dir: str = "results/llm_moe") -> None:
|
||||||
|
df, cfg = load_seed_bundles(results_dir) # seed-mean when the bundle has s{seed}/ sub-bundles
|
||||||
|
present = set(df["model"].unique())
|
||||||
|
specialists = sorted(m for m in present if m.startswith("spec_"))
|
||||||
|
fusion = [m for m in _FUSION if m in present]
|
||||||
|
union = [m for m in _UNION if m in present]
|
||||||
|
models = ["base"] + specialists + fusion + union
|
||||||
|
labels = {"base": "base", **{s: s.replace("spec_", "spec:") for s in specialists},
|
||||||
|
**_FUSION, **_UNION}
|
||||||
|
colors = {"base": "#7f7f7f", **{s: "#1f77b4" for s in specialists},
|
||||||
|
**{m: "#ff7f0e" for m in fusion}, **{m: "#2ca02c" for m in union}}
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
|
||||||
|
|
||||||
|
# Panel A: per-family accuracy, grouped by model.
|
||||||
|
ax = axes[0]
|
||||||
|
x = np.arange(len(_FAMS))
|
||||||
|
w = 0.8 / len(models)
|
||||||
|
for i, mdl in enumerate(models):
|
||||||
|
vals = [_acc(df, mdl, f) for f in _FAMS]
|
||||||
|
ax.bar(x + (i - (len(models) - 1) / 2) * w, vals, w, label=labels[mdl], color=colors[mdl],
|
||||||
|
alpha=0.9 if (mdl in union or mdl in fusion) else 0.65)
|
||||||
|
ax.set_xticks(x); ax.set_xticklabels(_FAMS)
|
||||||
|
ax.set(ylabel="accuracy", title="Per-family: fusion (orange) blends the deltas; union (green)\n"
|
||||||
|
"keeps each specialist intact and selects — no dilution")
|
||||||
|
ax.legend(frameon=False, fontsize=8, ncol=2)
|
||||||
|
|
||||||
|
# Panel B: overall vs worst-family, per model.
|
||||||
|
ax = axes[1]
|
||||||
|
x2 = np.arange(len(models))
|
||||||
|
for off, metric, hatch, lab in [(-0.2, "overall", "", "overall"),
|
||||||
|
(0.2, "worst_family", "//", "worst family")]:
|
||||||
|
ax.bar(x2 + off, [_acc(df, m, metric) for m in models], 0.38,
|
||||||
|
color=[colors[m] for m in models], hatch=hatch, alpha=0.85, label=lab,
|
||||||
|
edgecolor="white")
|
||||||
|
ax.set_xticks(x2); ax.set_xticklabels([labels[m] for m in models], rotation=25, ha="right",
|
||||||
|
fontsize=8)
|
||||||
|
ax.set(ylabel="accuracy", title="Overall (solid) vs worst-family (hatched):\nfusion vs union "
|
||||||
|
"recombination")
|
||||||
|
# Mark the routing ceiling (oracle) if present.
|
||||||
|
if "moe_oracle" in present:
|
||||||
|
ceil = _acc(df, "moe_oracle", "overall")
|
||||||
|
ax.axhline(ceil, ls=":", c="#2ca02c", lw=1, alpha=0.7)
|
||||||
|
ax.legend(frameon=False, fontsize=9)
|
||||||
|
|
||||||
|
best_fuse = max([_acc(df, m, "overall") for m in fusion], default=float("nan"))
|
||||||
|
best_union = max([_acc(df, m, "overall") for m in union], default=float("nan"))
|
||||||
|
router = _acc(df, "moe_learned", "router_acc") if "moe_learned" in present else float("nan")
|
||||||
|
if best_union > best_fuse + 0.01:
|
||||||
|
verdict = f"union {best_union:.2f} > fusion {best_fuse:.2f} overall (fusion dilutes)"
|
||||||
|
elif best_fuse > best_union + 0.01:
|
||||||
|
verdict = f"fusion {best_fuse:.2f} > union {best_union:.2f} overall (strong base composes)"
|
||||||
|
else:
|
||||||
|
verdict = f"union ≈ fusion ({best_union:.2f} vs {best_fuse:.2f}) overall"
|
||||||
|
rtxt = f"; learned router {router:.2f}" if router == router else ""
|
||||||
|
fig.suptitle(f"llm_moe — module-level union vs fusion recombination: {verdict}{rtxt} "
|
||||||
|
f"({cfg['base_model'].split('/')[-1]})", y=1.0, fontsize=12)
|
||||||
|
fig.tight_layout()
|
||||||
|
savefig(fig, results_dir, "llm_moe")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
||||||
115
figures/plot_llm_society.py
Normal file
115
figures/plot_llm_society.py
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
"""v2 society figure — E11's three panels at the language-model tier, plus the competence genotype.
|
||||||
|
|
||||||
|
Pre-registered layout (tasks/prereg-llm-society-v2.md §8), written before unblinding and run on the
|
||||||
|
smoke bundle first. Reads only committed bundles: a single bundle directory, or a campaign directory
|
||||||
|
whose sub-directories ``s{seed}_{arm}/`` each hold a bundle (the PBS array writes one per element).
|
||||||
|
|
||||||
|
(A) Best-agent overall test accuracy per arm over generations (solid) with the best *newborn* of each
|
||||||
|
generation (dotted) — a climb carried by a surviving founder is visible as such; B₀ (best founder
|
||||||
|
at gen 0) dashed. Mean ± 95% CI over seeds.
|
||||||
|
(B) Behavioural diversity of the population (mean pairwise disagreement).
|
||||||
|
(C) The self-consumption signature: mean conformity − mean true accuracy.
|
||||||
|
(D) Competence genotype of the ``full`` arm's best agent: per-family test accuracy × generation, mean
|
||||||
|
over seeds — E8's "a genotype no parent had", if it happens.
|
||||||
|
|
||||||
|
Usage: python figures/plot_llm_society.py [results/llm_society_v2 | results/llm_society_v2_smoke]
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
from _figlib import mean_ci, savefig # noqa: E402
|
||||||
|
|
||||||
|
_ARMS = [("full", "#2ca02c", "full society"),
|
||||||
|
("no_sex", "#ff7f0e", "no sex (no recombination)"),
|
||||||
|
("no_diversity", "#9467bd", "no diversity (greedy)"),
|
||||||
|
("no_grounding", "#d62728", "no grounding (self-consumption)"),
|
||||||
|
("sex_linear", "#1f77b4", "sex by linear blend (H2 control)")]
|
||||||
|
|
||||||
|
|
||||||
|
def load_any(results_dir: Path) -> tuple[pd.DataFrame, list[str]]:
|
||||||
|
"""One bundle, or every ``*/results.parquet`` below the directory (the campaign layout)."""
|
||||||
|
if (results_dir / "results.parquet").exists():
|
||||||
|
paths = [results_dir]
|
||||||
|
else:
|
||||||
|
paths = sorted(p.parent for p in results_dir.glob("*/results.parquet"))
|
||||||
|
if not paths:
|
||||||
|
raise SystemExit(f"no results.parquet under {results_dir}")
|
||||||
|
df = pd.concat([pd.read_parquet(p / "results.parquet") for p in paths], ignore_index=True)
|
||||||
|
fams = yaml.safe_load((paths[0] / "resolved_config.yaml").read_text())["source_config"]["families"]
|
||||||
|
return df, list(fams)
|
||||||
|
|
||||||
|
|
||||||
|
def main(results_dir: str = "results/llm_society_v2") -> None:
|
||||||
|
rd = Path(results_dir)
|
||||||
|
df, fams = load_any(rd)
|
||||||
|
pop = df[df.role == "population"]
|
||||||
|
summ = df[df.role == "summary"]
|
||||||
|
arms = [a for a in _ARMS if a[0] in set(df.arm.unique())]
|
||||||
|
|
||||||
|
best = (pop[pop.metric == "test_overall"].groupby(["arm", "seed", "generation"]).value.max()
|
||||||
|
.rename("best").reset_index())
|
||||||
|
newborn = summ[summ.metric == "best_newborn_overall"][["arm", "seed", "generation", "value"]]
|
||||||
|
b0 = best[best.generation == 0].groupby("seed").best.mean().mean()
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(1, 4, figsize=(21, 4.8))
|
||||||
|
|
||||||
|
def traj(ax, frame, col, title, ylabel, style="-", label_suffix=""):
|
||||||
|
for arm, color, label in arms:
|
||||||
|
sub = frame[frame.arm == arm]
|
||||||
|
if sub.empty:
|
||||||
|
continue
|
||||||
|
x, m, h = mean_ci(sub, "generation", col)
|
||||||
|
ax.plot(x, m, style, color=color, label=(label + label_suffix) if style == "-" else None, lw=2)
|
||||||
|
if style == "-":
|
||||||
|
ax.fill_between(x, m - h, m + h, color=color, alpha=0.15, lw=0)
|
||||||
|
if title: # overlay calls pass "" and must not wipe labels
|
||||||
|
ax.set_title(title, fontsize=10); ax.set_xlabel("generation"); ax.set_ylabel(ylabel)
|
||||||
|
|
||||||
|
traj(axes[0], best, "best", "(A) best agent (solid) and best newborn (dotted)\nB₀ = best founder, dashed",
|
||||||
|
"overall test accuracy")
|
||||||
|
traj(axes[0], newborn.rename(columns={"value": "best"}), "best", "", "", style=":")
|
||||||
|
axes[0].axhline(b0, color="k", ls="--", lw=1, label=f"B₀ = {b0:.2f}")
|
||||||
|
axes[0].legend(fontsize=8, loc="best")
|
||||||
|
|
||||||
|
div = summ[summ.metric == "diversity_behav"]
|
||||||
|
traj(axes[1], div, "value", "(B) population diversity\n(mean pairwise disagreement)", "diversity")
|
||||||
|
gap = summ[summ.metric == "gap_conformity_minus_truth"]
|
||||||
|
traj(axes[2], gap, "value", "(C) self-consumption signature\nconformity − true accuracy", "gap")
|
||||||
|
axes[2].axhline(0, color="k", lw=0.8)
|
||||||
|
|
||||||
|
# (D) competence genotype of the full arm's best agent, families × generations, mean over seeds
|
||||||
|
full = pop[pop.arm == ("full" if "full" in set(pop.arm) else arms[0][0])]
|
||||||
|
fam_cols = [f"test_{f}" for f in fams]
|
||||||
|
idx = full[full.metric == "test_overall"].sort_values("value").groupby(["seed", "generation"]).tail(1)
|
||||||
|
keyed = full.set_index(["seed", "generation", "agent", "metric"]).value
|
||||||
|
gens = sorted(full.generation.unique())
|
||||||
|
heat = np.full((len(fams), len(gens)), np.nan)
|
||||||
|
for gi, g in enumerate(gens):
|
||||||
|
rows = idx[idx.generation == g]
|
||||||
|
vals = np.array([[keyed.get((r.seed, g, r.agent, c), np.nan) for c in fam_cols] for r in rows.itertuples()])
|
||||||
|
if len(vals):
|
||||||
|
heat[:, gi] = np.nanmean(vals, axis=0)
|
||||||
|
im = axes[3].imshow(heat, aspect="auto", cmap="viridis", vmin=0, vmax=1)
|
||||||
|
axes[3].set_yticks(range(len(fams))); axes[3].set_yticklabels(fams, fontsize=8)
|
||||||
|
axes[3].set_xticks(range(len(gens))); axes[3].set_xticklabels(gens, fontsize=8)
|
||||||
|
axes[3].set_xlabel("generation"); axes[3].set_title("(D) competence genotype of the best agent\n(full arm; per-family accuracy)", fontsize=10)
|
||||||
|
fig.colorbar(im, ax=axes[3], fraction=0.046, pad=0.02)
|
||||||
|
|
||||||
|
n_seeds = df.seed.nunique()
|
||||||
|
fig.suptitle(f"llm_society_v2 — the composed society at LLM scale ({n_seeds} seed{'s' if n_seeds != 1 else ''}, "
|
||||||
|
f"L={len(fams)} families, mean ± 95% CI)", y=1.02)
|
||||||
|
fig.tight_layout()
|
||||||
|
savefig(fig, rd, "llm_society_v2")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
@ -29,7 +29,7 @@ from pathlib import Path
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
sys.path.insert(0, str(Path(__file__).parent))
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
from _figlib import load_bundle, load_seed_bundles, savefig # noqa: E402
|
from _figlib import load_bundle, savefig # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
def _series(df, mode, model, metric):
|
def _series(df, mode, model, metric):
|
||||||
|
|
@ -39,7 +39,7 @@ def _series(df, mode, model, metric):
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
rep, _ = load_seed_bundles("results/llm_speciation") # s{seed}/ layout, seeds 1-3
|
rep, _ = load_bundle("results/llm_speciation")
|
||||||
add, _ = load_bundle("results/llm_speciation_add")
|
add, _ = load_bundle("results/llm_speciation_add")
|
||||||
fam_a = "strings" if (rep["metric"] == "strings").any() else "lists"
|
fam_a = "strings" if (rep["metric"] == "strings").any() else "lists"
|
||||||
fam_b = "arith"
|
fam_b = "arith"
|
||||||
|
|
|
||||||
62
figures/plot_sexual_vs_asexual_lineage.py
Normal file
62
figures/plot_sexual_vs_asexual_lineage.py
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
"""E7 figure — the advantage of sex: recombination adapts faster than clonal reproduction.
|
||||||
|
|
||||||
|
The dynamic mechanism behind E8. A single population adapts from all-wrong toward a multi-locus
|
||||||
|
optimum under selection + drift + mutation. Beneficial alleles arise in different sub-lineages;
|
||||||
|
sexual recombination reassorts them into one genotype, while an asexual lineage suffers clonal
|
||||||
|
interference. The sexual lineage climbs faster — the classical advantage of sex (an honest *speed*
|
||||||
|
advantage; both eventually plateau near the optimum in this tractable regime).
|
||||||
|
|
||||||
|
Two panels: (A) mean-fitness adaptation curves, asexual vs sexual, over generations; (B) linkage
|
||||||
|
disequilibrium over generations — asexual holds beneficial alleles in disequilibrium (scattered
|
||||||
|
across genotypes) while sexual drives it to ~0 (assembled), the mechanism of the speed gap.
|
||||||
|
|
||||||
|
Usage: python figures/plot_sexual_vs_asexual_lineage.py [results/sexual_vs_asexual_lineage]
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
from _figlib import load_bundle, mean_ci, savefig # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def main(results_dir: str = "results/sexual_vs_asexual_lineage") -> None:
|
||||||
|
df, cfg = load_bundle(results_dir)
|
||||||
|
L = cfg["genotype"]["L"]
|
||||||
|
arms = [(0.0, "#7f7f7f", "asexual (clonal)"), (1.0, "#d62728", "sexual (recombining)")]
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(1, 2, figsize=(13, 5))
|
||||||
|
|
||||||
|
ax = axes[0]
|
||||||
|
for rate, c, lab in arms:
|
||||||
|
sub = df[df["recomb_rate"] == rate]
|
||||||
|
g, m, ci = mean_ci(sub, "generation", "mean_fitness")
|
||||||
|
ax.plot(g, m, "-", color=c, lw=1.8, label=lab)
|
||||||
|
ax.fill_between(g, m - ci, m + ci, color=c, alpha=0.2)
|
||||||
|
ax.axhline(L, ls=":", color="green", lw=1, label=f"optimum ($L$={L})")
|
||||||
|
ax.set(xlabel="generation", ylabel="mean fitness (# correct loci)",
|
||||||
|
title="Advantage of sex: recombination adapts faster\n(clonal interference slows the asexual lineage)")
|
||||||
|
ax.legend(frameon=False, fontsize=9)
|
||||||
|
|
||||||
|
ax = axes[1]
|
||||||
|
for rate, c, lab in arms:
|
||||||
|
sub = df[df["recomb_rate"] == rate]
|
||||||
|
g, m, ci = mean_ci(sub, "generation", "ld")
|
||||||
|
ax.plot(g, m, "-", color=c, lw=1.8, label=lab)
|
||||||
|
ax.fill_between(g, m - ci, m + ci, color=c, alpha=0.2)
|
||||||
|
ax.set(xlabel="generation", ylabel="mean linkage disequilibrium |D|",
|
||||||
|
title="Mechanism: asexual scatters beneficial alleles (LD>0);\nsexual assembles them (LD→0)")
|
||||||
|
ax.legend(frameon=False, fontsize=9)
|
||||||
|
|
||||||
|
fig.suptitle("E7 — the advantage of sex: recombination reassorts beneficial alleles that arose "
|
||||||
|
"in different lineages", y=1.02, fontsize=12)
|
||||||
|
fig.tight_layout()
|
||||||
|
savefig(fig, results_dir, "sexual_vs_asexual_lineage")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
||||||
175
figures/stats_llm_compose.py
Normal file
175
figures/stats_llm_compose.py
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
"""Pre-registered analysis for the composition experiment (prereg v3 §3) — written before unblinding.
|
||||||
|
|
||||||
|
Prints each hypothesis, its per-seed quantities, the paired mean ± 95% CI, and PASS / FAIL against
|
||||||
|
the threshold fixed in the pre-registration. Nothing here is chosen after seeing the data.
|
||||||
|
|
||||||
|
H1 gate S_0 >= +0.05, union-exceedance >= 0.03, cat > linear by >= 0.03 (>=2/3 seeds)
|
||||||
|
H2 S_t declines (Spearman <= -0.7) and composition's fractional loss exceeds each parent's
|
||||||
|
H3 Ĉ_t (one parameter, fixed at t=0) predicts observed with MAE <= 0.05 and beats a
|
||||||
|
two-parameter exponential on AIC
|
||||||
|
H4 S_G(grounded) - S_G(dry) >= +0.08, 3/3 seeds positive
|
||||||
|
H5 rho_t rises in dry (Spearman >= +0.7); partial corr of S_t with rho_t given q_t < 0
|
||||||
|
H6 dry_linear: S_0 <= +0.02 and union-exceedance <= 0.01 at every generation
|
||||||
|
|
||||||
|
Usage: python figures/stats_llm_compose.py [results/llm_compose]
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||||
|
from plot_llm_compose import load_any, series # noqa: E402
|
||||||
|
from llm.compose import predicted_composition # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def ci95(x) -> tuple[float, float]:
|
||||||
|
x = np.asarray(x, dtype=float)
|
||||||
|
if len(x) < 2:
|
||||||
|
return (float(x.mean()) if len(x) else float("nan")), float("nan")
|
||||||
|
from scipy import stats
|
||||||
|
return float(x.mean()), float(stats.t.ppf(0.975, len(x) - 1) * x.std(ddof=1) / np.sqrt(len(x)))
|
||||||
|
|
||||||
|
|
||||||
|
def spearman(y) -> float:
|
||||||
|
from scipy import stats
|
||||||
|
y = np.asarray(y, dtype=float)
|
||||||
|
return float(stats.spearmanr(np.arange(len(y)), y).statistic) if len(y) > 2 else float("nan")
|
||||||
|
|
||||||
|
|
||||||
|
def verdict(ok) -> str:
|
||||||
|
return "n/a " if ok is None else ("PASS" if ok else "FAIL")
|
||||||
|
|
||||||
|
|
||||||
|
def main(results_dir: str = "results/llm_compose") -> None:
|
||||||
|
df = load_any(Path(results_dir))
|
||||||
|
seeds = sorted(df.seed.unique())
|
||||||
|
G = int(df.generation.max())
|
||||||
|
arms = set(df.arm.unique())
|
||||||
|
print(f"bundle {results_dir} seeds {seeds} G = {G} arms {sorted(arms)}\n")
|
||||||
|
|
||||||
|
def at(arm, metric, gen):
|
||||||
|
s = series(df, arm, metric)
|
||||||
|
return {int(r.seed): float(r.value) for r in s[s.generation == gen].itertuples()}
|
||||||
|
|
||||||
|
def traj(arm, metric, seed):
|
||||||
|
s = series(df, arm, metric)
|
||||||
|
s = s[s.seed == seed].sort_values("generation")
|
||||||
|
return s.value.to_numpy()
|
||||||
|
|
||||||
|
# ---------------- H1
|
||||||
|
print("H1 — generation-0 gate (does the published effect reproduce here?)")
|
||||||
|
s0, u0 = at("dry", "surplus", 0), at("dry", "union_exceedance", 0)
|
||||||
|
print(f" surplus at t=0 {np.round(list(s0.values()), 3).tolist()} "
|
||||||
|
f"{verdict(sum(v >= 0.05 for v in s0.values()) >= max(1, len(s0) - 1))} (>= +0.05)")
|
||||||
|
print(f" union-exceedance at t=0 {np.round(list(u0.values()), 3).tolist()} "
|
||||||
|
f"{verdict(sum(v >= 0.03 for v in u0.values()) >= max(1, len(u0) - 1))} (>= 0.03)")
|
||||||
|
# The operator arms are named by their operator (`dry` = linear, `dry_cat` = cat) after the
|
||||||
|
# gen-0 sweep; compare whichever two are present rather than assuming a name.
|
||||||
|
if {"dry", "dry_cat"} <= arms:
|
||||||
|
lin0, cat0 = at("dry", "composed_acc", 0), at("dry_cat", "composed_acc", 0)
|
||||||
|
d = [lin0[s] - cat0[s] for s in lin0 if s in cat0]
|
||||||
|
print(f" linear − cat at t=0 {np.round(d, 3).tolist()} (sweep found the ordering is "
|
||||||
|
f"weight-dependent; reported, not gated)")
|
||||||
|
|
||||||
|
# ---------------- H2
|
||||||
|
print("\nH2 — composition decays, and faster than its parents")
|
||||||
|
for seed in seeds:
|
||||||
|
s = traj("dry", "surplus", seed)
|
||||||
|
c = traj("dry", "composed_acc", seed)
|
||||||
|
qm, qc = traj("dry", "q_math", seed), traj("dry", "q_code", seed)
|
||||||
|
if len(c) < 3:
|
||||||
|
continue
|
||||||
|
frac_c = c[-1] / c[0] if c[0] > 0 else np.nan
|
||||||
|
frac_m = qm[-1] / qm[0] if qm[0] > 0 else np.nan
|
||||||
|
frac_q = qc[-1] / qc[0] if qc[0] > 0 else np.nan
|
||||||
|
print(f" seed {seed}: spearman(S_t) {spearman(s):+.2f} retained: composed {frac_c:.2f} "
|
||||||
|
f"vs math {frac_m:.2f}, code {frac_q:.2f} "
|
||||||
|
f"{'faster' if frac_c < min(frac_m, frac_q) else 'NOT faster'}")
|
||||||
|
|
||||||
|
# ---------------- H3
|
||||||
|
print("\nH3 — the closed form predicts the trajectory (the paper's predictive claim)")
|
||||||
|
for arm in ("dry", "grounded"):
|
||||||
|
if arm not in arms:
|
||||||
|
continue
|
||||||
|
maes, aics = [], []
|
||||||
|
for seed in seeds:
|
||||||
|
obs = traj(arm, "composed_acc", seed)
|
||||||
|
qm, qc = traj(arm, "q_math", seed), traj(arm, "q_code", seed)
|
||||||
|
rho = traj(arm, "rho_behav", seed)
|
||||||
|
if len(obs) < 3 or not (len(obs) == len(qm) == len(qc) == len(rho)):
|
||||||
|
continue
|
||||||
|
pred = predicted_composition(qm, qc, rho, float(obs[0]))
|
||||||
|
mae = float(np.mean(np.abs(pred - obs)))
|
||||||
|
# two-parameter baseline: a*exp(-b t), least squares on the same points
|
||||||
|
t = np.arange(len(obs), dtype=float)
|
||||||
|
pos = obs > 1e-6
|
||||||
|
if pos.sum() >= 2:
|
||||||
|
b, loga = np.polyfit(t[pos], np.log(obs[pos]), 1)
|
||||||
|
base = np.exp(loga) * np.exp(b * t)
|
||||||
|
else:
|
||||||
|
base = np.full_like(obs, obs.mean())
|
||||||
|
n = len(obs)
|
||||||
|
aic = lambda resid, k: n * np.log(max(1e-12, np.mean(resid ** 2))) + 2 * k
|
||||||
|
maes.append(mae); aics.append(aic(pred - obs, 1) - aic(base - obs, 2))
|
||||||
|
if maes:
|
||||||
|
m, h = ci95(maes)
|
||||||
|
print(f" {arm:9s} MAE {m:.3f} ± {h:.3f} {verdict(m <= 0.05)} (<= 0.05) "
|
||||||
|
f"ΔAIC vs exponential {np.mean(aics):+.1f} "
|
||||||
|
f"({'closed form wins' if np.mean(aics) < 0 else 'baseline wins'})")
|
||||||
|
|
||||||
|
# ---------------- H4
|
||||||
|
print("\nH4 — grounding arrests the decay")
|
||||||
|
if {"dry", "grounded"} <= arms:
|
||||||
|
d, g = at("dry", "surplus", G), at("grounded", "surplus", G)
|
||||||
|
diff = [g[s] - d[s] for s in g if s in d]
|
||||||
|
m, h = ci95(diff)
|
||||||
|
print(f" S_G(grounded) − S_G(dry) {np.round(diff, 3).tolist()} mean {m:+.3f} ± {h:.3f} "
|
||||||
|
f"{verdict(m >= 0.08 and all(v > 0 for v in diff))} (>= +0.08, all seeds positive)")
|
||||||
|
|
||||||
|
# ---------------- H5
|
||||||
|
print("\nH5 — rising ρ is the mechanism")
|
||||||
|
for arm in ("dry", "grounded"):
|
||||||
|
if arm not in arms:
|
||||||
|
continue
|
||||||
|
sp = [spearman(traj(arm, "rho_behav", s)) for s in seeds if len(traj(arm, "rho_behav", s)) > 2]
|
||||||
|
if sp:
|
||||||
|
print(f" {arm:9s} spearman(ρ_t) {np.round(sp, 2).tolist()} mean {np.mean(sp):+.2f}"
|
||||||
|
+ (f" {verdict(np.mean(sp) >= 0.7)} (>= +0.7)" if arm == "dry" else ""))
|
||||||
|
try:
|
||||||
|
from scipy import stats
|
||||||
|
rows = []
|
||||||
|
for seed in seeds:
|
||||||
|
s, r = traj("dry", "surplus", seed), traj("dry", "rho_behav", seed)
|
||||||
|
qm, qc = traj("dry", "q_math", seed), traj("dry", "q_code", seed)
|
||||||
|
if len(s) > 3 and len(s) == len(r) == len(qm) == len(qc):
|
||||||
|
rows.append(np.column_stack([s, r, qm * qc]))
|
||||||
|
if rows:
|
||||||
|
a = np.vstack(rows)
|
||||||
|
# partial correlation of S with rho, controlling for q_math*q_code
|
||||||
|
res_s = a[:, 0] - np.poly1d(np.polyfit(a[:, 2], a[:, 0], 1))(a[:, 2])
|
||||||
|
res_r = a[:, 1] - np.poly1d(np.polyfit(a[:, 2], a[:, 1], 1))(a[:, 2])
|
||||||
|
pr = float(stats.pearsonr(res_s, res_r).statistic)
|
||||||
|
print(f" partial corr(S, ρ | q_math·q_code) = {pr:+.2f} "
|
||||||
|
f"{verdict(pr < 0)} (negative = lost complementarity, not just retention loss)")
|
||||||
|
except Exception as e: # descriptive only, never fatal
|
||||||
|
print(f" partial correlation unavailable ({type(e).__name__})")
|
||||||
|
|
||||||
|
# ---------------- H6
|
||||||
|
print("\nH6 (revised) — does the operator ordering hold across generations, or only at gen 0?")
|
||||||
|
if {"dry", "dry_cat"} <= arms:
|
||||||
|
for gen in range(G + 1):
|
||||||
|
lin, cat = at("dry", "surplus", gen), at("dry_cat", "surplus", gen)
|
||||||
|
d = [lin[s] - cat[s] for s in lin if s in cat]
|
||||||
|
if d:
|
||||||
|
print(f" gen {gen}: surplus linear − cat = {np.mean(d):+.3f} "
|
||||||
|
f"(linear {np.mean(list(lin.values())):+.3f}, cat {np.mean(list(cat.values())):+.3f})")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
One loader for every curriculum bundle (the arm label is set HERE by directory, never trusted from
|
One loader for every curriculum bundle (the arm label is set HERE by directory, never trusted from
|
||||||
the parquet alone, because the veto arm is recorded as ``society`` with ``allow_veto`` on), and the
|
the parquet alone, because the veto arm is recorded as ``society`` with ``allow_veto`` on), and the
|
||||||
pre-registered readouts for the two 2026-09-11 controls (tasks/prereg-llm-society-v4.md on the dev branch §8h):
|
pre-registered readouts for the two 2026-09-11 controls (tasks/prereg-llm-society-v4.md §8h):
|
||||||
|
|
||||||
1. **Forced stop at generation 3** (``llm_curriculum_v5_stop3``): per-seed paired contrasts of the
|
1. **Forced stop at generation 3** (``llm_curriculum_v5_stop3``): per-seed paired contrasts of the
|
||||||
best lineage's final all-family accuracy, veto − stop3, stop3 − isolated, stop3 − society.
|
best lineage's final all-family accuracy, veto − stop3, stop3 − isolated, stop3 − society.
|
||||||
|
|
|
||||||
164
figures/stats_llm_society.py
Normal file
164
figures/stats_llm_society.py
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
"""Pre-registered analysis for the v2 society (tasks/prereg-llm-society-v2.md §5, §8).
|
||||||
|
|
||||||
|
Prints, for each hypothesis, the per-seed quantities, the paired mean ± 95% CI over seeds, the sign
|
||||||
|
count, and PASS / FAIL against the pre-set threshold. Written before unblinding and exercised on the
|
||||||
|
smoke bundle; nothing here is chosen after seeing the campaign. Reads only committed bundles (one
|
||||||
|
bundle directory, or a campaign directory of ``s{seed}_{arm}/`` bundles).
|
||||||
|
|
||||||
|
H1 vertical climb full best(G) − B₀ ≥ 0.20 ; best newborn(G) − B₀ ≥ 0.15 ; ≥ 6 families ≥ 0.6
|
||||||
|
H3 self-consumption no_grounding best(G) ≤ B₀ + 0.05 ; gap(no_grounding) − gap(full) ≥ 0.30
|
||||||
|
H4 sex necessity no_sex best(G) ≤ B₀ + 0.05 in every seed
|
||||||
|
H5 diversity AUC(diversity) full > no_diversity ; no_diversity diversity < 0.1 by gen 6
|
||||||
|
H6 where skills die ≤ 20% of family losses in `full` were supplied at ≥ 0.6 by the child's source
|
||||||
|
(H2 is deferred: the sex_linear arm is not in the first campaign.)
|
||||||
|
|
||||||
|
Usage: python figures/stats_llm_society.py [results/llm_society_v2]
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
from plot_llm_society import load_any # noqa: E402
|
||||||
|
|
||||||
|
COMPETENT = 0.6
|
||||||
|
|
||||||
|
|
||||||
|
def ci95(x: np.ndarray) -> tuple[float, float]:
|
||||||
|
x = np.asarray(x, dtype=float)
|
||||||
|
if len(x) < 2:
|
||||||
|
return float(x.mean()), float("nan")
|
||||||
|
from scipy import stats
|
||||||
|
h = stats.t.ppf(0.975, len(x) - 1) * x.std(ddof=1) / np.sqrt(len(x))
|
||||||
|
return float(x.mean()), float(h)
|
||||||
|
|
||||||
|
|
||||||
|
def verdict(ok: bool | None) -> str:
|
||||||
|
return "n/a " if ok is None else ("PASS" if ok else "FAIL")
|
||||||
|
|
||||||
|
|
||||||
|
def main(results_dir: str = "results/llm_society_v2") -> None:
|
||||||
|
df, fams = load_any(Path(results_dir))
|
||||||
|
pop, summ, child = (df[df.role == r] for r in ("population", "summary", "child"))
|
||||||
|
src = df[df.role == "child_source"]
|
||||||
|
seeds = sorted(df.seed.unique())
|
||||||
|
G = int(pop.generation.max())
|
||||||
|
arms = set(df.arm.unique())
|
||||||
|
print(f"bundle: {results_dir} seeds {seeds} G = {G} L = {len(fams)} arms {sorted(arms)}\n")
|
||||||
|
|
||||||
|
best = pop[pop.metric == "test_overall"].groupby(["arm", "seed", "generation"]).value.max()
|
||||||
|
B0 = {s: float(best.xs(s, level="seed").xs(0, level="generation").mean()) for s in seeds}
|
||||||
|
print("B₀ (best founder, gen 0) per seed:", {s: round(v, 3) for s, v in B0.items()})
|
||||||
|
|
||||||
|
def at_G(arm, metric_frame, metric=None, gen=G, agg="max"):
|
||||||
|
out = {}
|
||||||
|
for s in seeds:
|
||||||
|
f = metric_frame[(metric_frame.arm == arm) & (metric_frame.seed == s) & (metric_frame.generation == gen)]
|
||||||
|
if metric is not None:
|
||||||
|
f = f[f.metric == metric]
|
||||||
|
if not f.empty:
|
||||||
|
out[s] = float(f.value.max() if agg == "max" else f.value.mean())
|
||||||
|
return out
|
||||||
|
|
||||||
|
def report(name, per_seed, thr, direction, note=""):
|
||||||
|
vals = np.array(list(per_seed.values()))
|
||||||
|
if len(vals) == 0:
|
||||||
|
print(f" {name:38s} {verdict(None)}"); return None
|
||||||
|
m, h = ci95(vals)
|
||||||
|
ok_each = (vals >= thr) if direction == ">=" else (vals <= thr)
|
||||||
|
ok = bool(ok_each.sum() >= max(3, len(vals)) if len(vals) >= 3 else ok_each.all())
|
||||||
|
print(f" {name:38s} {verdict(ok)} mean {m:+.3f} ± {h:.3f} per seed "
|
||||||
|
f"{np.round(vals, 3).tolist()} {int(ok_each.sum())}/{len(vals)} meet {direction} {thr} {note}")
|
||||||
|
return ok
|
||||||
|
|
||||||
|
# ---------------- H1
|
||||||
|
print("\nH1 — vertical climb (full arm)")
|
||||||
|
if "full" in arms:
|
||||||
|
gain = {s: at_G("full", pop, "test_overall")[s] - B0[s] for s in seeds if s in at_G("full", pop, "test_overall")}
|
||||||
|
nb = at_G("full", summ, "best_newborn_overall", gen=G - 1)
|
||||||
|
gain_nb = {s: nb[s] - B0[s] for s in nb}
|
||||||
|
# families the best agent is competent on, at G
|
||||||
|
comp = {}
|
||||||
|
for s in seeds:
|
||||||
|
f = pop[(pop.arm == "full") & (pop.seed == s) & (pop.generation == G)]
|
||||||
|
if f.empty:
|
||||||
|
continue
|
||||||
|
ov = f[f.metric == "test_overall"].set_index("agent").value
|
||||||
|
a = int(ov.idxmax())
|
||||||
|
per = f[(f.agent == a) & f.metric.isin([f"test_{x}" for x in fams])].value
|
||||||
|
comp[s] = float((per >= COMPETENT).sum())
|
||||||
|
report("best agent − B₀ (≥ 0.20)", gain, 0.20, ">=")
|
||||||
|
report("best newborn − B₀ (≥ 0.15)", gain_nb, 0.15, ">=")
|
||||||
|
report("families competent in best agent (≥ 6)", comp, 6, ">=")
|
||||||
|
else:
|
||||||
|
print(" full arm absent")
|
||||||
|
|
||||||
|
# ---------------- H3
|
||||||
|
print("\nH3 — self-consumption (no_grounding)")
|
||||||
|
if {"no_grounding", "full"} <= arms:
|
||||||
|
ng = at_G("no_grounding", pop, "test_overall")
|
||||||
|
report("no_grounding best − B₀ (≤ 0.05)", {s: ng[s] - B0[s] for s in ng}, 0.05, "<=")
|
||||||
|
gap_ng = at_G("no_grounding", summ, "gap_conformity_minus_truth", agg="mean")
|
||||||
|
gap_f = at_G("full", summ, "gap_conformity_minus_truth", agg="mean")
|
||||||
|
report("gap(no_grounding) − gap(full) (≥ 0.30)", {s: gap_ng[s] - gap_f[s] for s in gap_ng if s in gap_f}, 0.30, ">=")
|
||||||
|
ca = summ[(summ.arm == "no_grounding") & (summ.metric == "consensus_acc")]
|
||||||
|
slope = {s: float(np.polyfit(g.generation, g.value, 1)[0]) for s, g in ca.groupby("seed") if len(g) > 1}
|
||||||
|
report("consensus-accuracy slope, no_grounding (≤ 0)", slope, 0.0, "<=", note="(non-increasing)")
|
||||||
|
else:
|
||||||
|
print(" arms absent")
|
||||||
|
|
||||||
|
# ---------------- H4
|
||||||
|
print("\nH4 — sex necessity (no_sex ceiling)")
|
||||||
|
if "no_sex" in arms:
|
||||||
|
ns = at_G("no_sex", pop, "test_overall")
|
||||||
|
vals = {s: ns[s] - B0[s] for s in ns}
|
||||||
|
ok = all(v <= 0.05 for v in vals.values()) if vals else None
|
||||||
|
print(f" {'no_sex best − B₀ (≤ 0.05 in EVERY seed)':38s} {verdict(ok)} per seed {np.round(list(vals.values()), 3).tolist()}")
|
||||||
|
else:
|
||||||
|
print(" no_sex arm absent")
|
||||||
|
|
||||||
|
# ---------------- H5
|
||||||
|
print("\nH5 — diversity (full vs no_diversity)")
|
||||||
|
if {"full", "no_diversity"} <= arms:
|
||||||
|
div = summ[summ.metric == "diversity_behav"]
|
||||||
|
auc = lambda arm, s: float(np.trapezoid(div[(div.arm == arm) & (div.seed == s)].sort_values("generation").value))
|
||||||
|
d_auc = {s: auc("full", s) - auc("no_diversity", s) for s in seeds
|
||||||
|
if not div[(div.arm == "full") & (div.seed == s)].empty and not div[(div.arm == "no_diversity") & (div.seed == s)].empty}
|
||||||
|
report("AUC(diversity) full − no_diversity (> 0)", d_auc, 1e-9, ">=")
|
||||||
|
g6 = min(6, G)
|
||||||
|
nd6 = at_G("no_diversity", summ, "diversity_behav", gen=g6, agg="mean")
|
||||||
|
report(f"no_diversity diversity at gen {g6} (< 0.1)", nd6, 0.1, "<=")
|
||||||
|
else:
|
||||||
|
print(" arms absent")
|
||||||
|
|
||||||
|
# ---------------- H6
|
||||||
|
print("\nH6 — where skills die (full arm)")
|
||||||
|
if "full" in arms and not src.empty:
|
||||||
|
losses, supplied_ok = 0, 0
|
||||||
|
for s in seeds:
|
||||||
|
fpop = pop[(pop.arm == "full") & (pop.seed == s)]
|
||||||
|
fsrc = src[(src.arm == "full") & (src.seed == s)]
|
||||||
|
for t in range(G):
|
||||||
|
alive_t = {f for f in fams if (fpop[(fpop.generation == t) & (fpop.metric == f"test_{f}")].value >= COMPETENT).any()}
|
||||||
|
alive_t1 = {f for f in fams if (fpop[(fpop.generation == t + 1) & (fpop.metric == f"test_{f}")].value >= COMPETENT).any()}
|
||||||
|
for f in alive_t - alive_t1:
|
||||||
|
losses += 1
|
||||||
|
sup = fsrc[(fsrc.generation == t) & (fsrc.metric == f"source_{f}")].value
|
||||||
|
supplied_ok += int((sup >= COMPETENT).any())
|
||||||
|
frac = supplied_ok / losses if losses else float("nan")
|
||||||
|
ok = None if not losses else frac <= 0.20
|
||||||
|
print(f" {'family losses supplied at ≥0.6 (≤ 20%)':38s} {verdict(ok)} {supplied_ok}/{losses} losses "
|
||||||
|
f"({frac:.0%} if any) — skills should die because they arrived diluted, not despite competent supply")
|
||||||
|
else:
|
||||||
|
print(" no source diagnostics")
|
||||||
|
|
||||||
|
print("\nH2 (union vs linear blend) — deferred: sex_linear not in the first campaign (prereg §12).")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
||||||
43
hpc/llm_compose.pbs
Executable file
43
hpc/llm_compose.pbs
Executable file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# The composition campaign (prereg tasks/prereg-llm-compose-v3.md): one (seed, arm) per array
|
||||||
|
# element. Seed 1 runs locally on the A4000 as the hedge; this array covers seeds 2-3 x 3 arms.
|
||||||
|
# Each element trains its own founders (cached per seed under models/, first writer wins via the
|
||||||
|
# adapter_config.json check) and checkpoints every generation, so a requeued element resumes.
|
||||||
|
# submit: qsub hpc/llm_compose.pbs status: qstat -u $USER -t
|
||||||
|
# index -> seed = 2 + (i-1)/3, arm = (dry grounded dry_cat)[(i-1)%3]
|
||||||
|
#PBS -l select=1:ncpus=8:mem=64gb:ngpus=1:gpu_type=L40S
|
||||||
|
#PBS -l walltime=08:00:00
|
||||||
|
#PBS -N lam_compose
|
||||||
|
#PBS -J 1-6
|
||||||
|
|
||||||
|
cd "$PBS_O_WORKDIR"
|
||||||
|
export HF_HOME="$EPHEMERAL/hf_cache"
|
||||||
|
export HF_DATASETS_CACHE="$EPHEMERAL/hf_cache/datasets"
|
||||||
|
export TOKENIZERS_PARALLELISM=false
|
||||||
|
export UV_CACHE_DIR="$EPHEMERAL/uvcache"
|
||||||
|
|
||||||
|
ARMS=(dry grounded dry_cat)
|
||||||
|
I=$((PBS_ARRAY_INDEX - 1))
|
||||||
|
SEED=$((2 + I / 3))
|
||||||
|
ARM=${ARMS[$((I % 3))]}
|
||||||
|
|
||||||
|
source .venv/bin/activate
|
||||||
|
nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader
|
||||||
|
echo "seed=$SEED arm=$ARM start=$(date)"
|
||||||
|
|
||||||
|
CFG="configs/llm/_gen/compose_s${SEED}_${ARM}.yaml"
|
||||||
|
mkdir -p configs/llm/_gen
|
||||||
|
python - "$SEED" "$ARM" "$CFG" <<'EOF'
|
||||||
|
import sys, yaml
|
||||||
|
seed, arm, out = int(sys.argv[1]), sys.argv[2], sys.argv[3]
|
||||||
|
cfg = yaml.safe_load(open("configs/llm/compose_s1.yaml"))
|
||||||
|
cfg["seed"] = seed
|
||||||
|
cfg["arms"] = [arm]
|
||||||
|
cfg["batch_size"] = 32 # L40S has 46 GB; the A4000 setting is 16
|
||||||
|
cfg["score_batch_size"] = 8
|
||||||
|
cfg["output"] = {"dir": f"results/llm_compose/s{seed}_{arm}"}
|
||||||
|
yaml.safe_dump(cfg, open(out, "w"), sort_keys=False)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
python -m llm.experiment "$CFG"
|
||||||
|
echo "done: $(date)"
|
||||||
14
hpc/llm_smoke.pbs
Executable file
14
hpc/llm_smoke.pbs
Executable file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Smoke gate: run the small 0.5B merge experiment on an L40S to confirm the GPU pipeline works
|
||||||
|
# on CX3 before the 4 h 7B run. ~5 min. submit: qsub hpc/llm_smoke.pbs
|
||||||
|
#PBS -l select=1:ncpus=8:mem=48gb:ngpus=1:gpu_type=L40S
|
||||||
|
#PBS -l walltime=00:20:00
|
||||||
|
#PBS -N lam_llm_smoke
|
||||||
|
|
||||||
|
cd "$PBS_O_WORKDIR"
|
||||||
|
export HF_HOME="$EPHEMERAL/hf_cache"
|
||||||
|
export TOKENIZERS_PARALLELISM=false
|
||||||
|
source .venv/bin/activate
|
||||||
|
nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader
|
||||||
|
python -m llm.experiment configs/llm/merge.yaml # 0.5B, writes results/llm_merge/
|
||||||
|
echo "done: $(date)"
|
||||||
25
hpc/llm_society.pbs
Executable file
25
hpc/llm_society.pbs
Executable file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# The LLM society campaign (C3): 4-arm ablation, one seed per array element on one L40S each.
|
||||||
|
# Seed 1 runs locally on GG's A4000 (queue insurance); this array covers seeds 2-4. Each job is
|
||||||
|
# self-contained (founders trained inline, adapters under models/, deleted per generation), so a
|
||||||
|
# killed element reruns cleanly and elements schedule independently. ~2.5-3.5 h measured-scale
|
||||||
|
# runtime; 6 h walltime for backfill-friendly margin.
|
||||||
|
# submit: qsub hpc/llm_society.pbs status: qstat -u $USER
|
||||||
|
#PBS -l select=1:ncpus=8:mem=64gb:ngpus=1:gpu_type=L40S
|
||||||
|
#PBS -l walltime=06:00:00
|
||||||
|
#PBS -N lam_society
|
||||||
|
#PBS -J 2-4
|
||||||
|
|
||||||
|
cd "$PBS_O_WORKDIR"
|
||||||
|
export HF_HOME="$EPHEMERAL/hf_cache"
|
||||||
|
export TOKENIZERS_PARALLELISM=false
|
||||||
|
export UV_CACHE_DIR="$EPHEMERAL/uvcache"
|
||||||
|
|
||||||
|
source .venv/bin/activate
|
||||||
|
nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader
|
||||||
|
|
||||||
|
python -m llm.experiment "configs/llm/society_campaign_s${PBS_ARRAY_INDEX}.yaml"
|
||||||
|
|
||||||
|
# results/llm_society_campaign/s${PBS_ARRAY_INDEX}/ written in-place. Sync back:
|
||||||
|
# rsync -avz hpc:'.../results/llm_society_campaign/' results/llm_society_campaign/
|
||||||
|
echo "done: $(date)"
|
||||||
42
hpc/llm_society_v2.pbs
Executable file
42
hpc/llm_society_v2.pbs
Executable file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# The v2 society campaign (prereg tasks/prereg-llm-society-v2.md §9): one (seed, arm) per array
|
||||||
|
# element on one L40S each, 16 elements = 4 seeds x 4 arms. Each element is self-contained: founders
|
||||||
|
# are trained inline and cached per seed (the four arm-elements of a seed share them via the
|
||||||
|
# filesystem; the first to arrive trains, the others wait on the adapter_config.json check), the loop
|
||||||
|
# checkpoints every generation and resumes, so a killed element is re-queued with the same index and
|
||||||
|
# picks up where it stopped. ~6 h per element at k_inherit=300 (prereg §9); 8 h walltime.
|
||||||
|
# submit: qsub hpc/llm_society_v2.pbs status: qstat -u $USER -t
|
||||||
|
# index -> seed = 1 + (i-1) // 4, arm = (full no_grounding no_sex no_diversity)[(i-1) % 4]
|
||||||
|
#PBS -l select=1:ncpus=8:mem=64gb:ngpus=1:gpu_type=L40S
|
||||||
|
#PBS -l walltime=08:00:00
|
||||||
|
#PBS -N lam_society_v2
|
||||||
|
#PBS -J 1-16
|
||||||
|
|
||||||
|
cd "$PBS_O_WORKDIR"
|
||||||
|
export HF_HOME="$EPHEMERAL/hf_cache"
|
||||||
|
export TOKENIZERS_PARALLELISM=false
|
||||||
|
export UV_CACHE_DIR="$EPHEMERAL/uvcache"
|
||||||
|
|
||||||
|
ARMS=(full no_grounding no_sex no_diversity)
|
||||||
|
I=$((PBS_ARRAY_INDEX - 1))
|
||||||
|
SEED=$((1 + I / 4))
|
||||||
|
ARM=${ARMS[$((I % 4))]}
|
||||||
|
|
||||||
|
source .venv/bin/activate
|
||||||
|
nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader
|
||||||
|
echo "seed=$SEED arm=$ARM start=$(date)"
|
||||||
|
|
||||||
|
# One config per (seed, arm): generated from the seed template so the resolved config is exact.
|
||||||
|
CFG="configs/llm/_gen/society_v2_s${SEED}_${ARM}.yaml"
|
||||||
|
mkdir -p configs/llm/_gen
|
||||||
|
python - "$SEED" "$ARM" "$CFG" <<'EOF'
|
||||||
|
import sys, yaml
|
||||||
|
seed, arm, out = int(sys.argv[1]), sys.argv[2], sys.argv[3]
|
||||||
|
cfg = yaml.safe_load(open(f"configs/llm/society_v2_s{seed}.yaml"))
|
||||||
|
cfg["arms"] = [arm]
|
||||||
|
cfg["output"] = {"dir": f"results/llm_society_v2/s{seed}_{arm}"}
|
||||||
|
yaml.safe_dump(cfg, open(out, "w"), sort_keys=False)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
python -m llm.experiment "$CFG"
|
||||||
|
echo "done: $(date)"
|
||||||
42
paper/arxiv/ARXIV-SUBMISSION.md
Normal file
42
paper/arxiv/ARXIV-SUBMISSION.md
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# arXiv submission notes (Phase 2 of the PNAS work order)
|
||||||
|
|
||||||
|
**What to upload.** The source package: `main.tex`, `body.tex`, `figs/` (three PDFs). arXiv rejects
|
||||||
|
TeX-produced PDF-only uploads, so upload source; all packages are standard and `\pdfoutput=1` is set,
|
||||||
|
so arXiv's pdflatex builds it (verified locally with tectonic; `main.pdf` in this directory is the
|
||||||
|
reference build, 20 pp). To rebuild after editing the Markdown source of truth:
|
||||||
|
`python paper/arxiv/md2tex.py && (cd paper/arxiv && tectonic main.tex)`.
|
||||||
|
|
||||||
|
**Categories.** Primary: `q-bio.PE` (Populations and Evolution). Cross-list: `cs.LG` and `cs.NE`.
|
||||||
|
If arXiv asks for an endorsement for q-bio.PE (first submission to the archive), either request it
|
||||||
|
(an evolutionary-biology colleague with q-bio postings can endorse in one click) or flip primary to
|
||||||
|
`cs.NE` with `q-bio.PE` as cross-list — the paper is defensible either way; q-bio.PE primary is
|
||||||
|
preferred for the PNAS audience trail.
|
||||||
|
|
||||||
|
**License.** arXiv non-exclusive license (default) is fine for PNAS. Do not pick CC-BY unless you
|
||||||
|
want it — PNAS permits preprints under any license, but the default keeps options open.
|
||||||
|
|
||||||
|
**Abstract for the arXiv field** (plain text, ~1,750 chars — the field caps at 1,920; the paper's
|
||||||
|
long abstract stays in the PDF):
|
||||||
|
|
||||||
|
> AI is shifting from single frozen models to populations of agents that persist, specialise, and are
|
||||||
|
> recombined into new models. The field describes this with evolutionary vocabulary — crossover, mate
|
||||||
|
> choice, offspring — but as metaphor over search. We argue the right theory already exists: the
|
||||||
|
> population genetics of the evolution of sex. Training each generation on the last is genetic drift,
|
||||||
|
> and model collapse is Muller's ratchet, the decay of an asexual lineage (we take the
|
||||||
|
> collapse-is-drift diagnosis as settled and cite it). The cure is sexual: ground every generation in
|
||||||
|
> data from a non-drifting reality (immigration, with a critical real-data fraction far below one);
|
||||||
|
> recombine many complementary parents (model merging — where recombination preserves the union of
|
||||||
|
> what the parents kept, while averaging cancels the benefit); and preserve diversity. Offspring then
|
||||||
|
> exceed every parent (the Fisher-Muller effect, shown in merged language models up to 7B). Sex has a
|
||||||
|
> limit: as models diverge they can speciate — a merge-compatibility cliff governed by epistasis
|
||||||
|
> (Bateson-Dobzhansky-Muller incompatibilities) whose damage snowballs. We model this and confirm it
|
||||||
|
> in real weights: a merge barrier survives alignment under the full function-preserving symmetry
|
||||||
|
> group of the network, rising with functional conflict while hybrid fitness falls to inviability —
|
||||||
|
> yet absent conflicting training signals, divergently-specialised lineages developed no isolation,
|
||||||
|
> the merge instead rescuing the forgetting specialists. AI can also do what biology cannot —
|
||||||
|
> directed sex: unbounded parents, chosen mates, offspring screened before they are kept. We support
|
||||||
|
> the argument with closed-form-validated simulations, trained networks, an image generator, and LLM
|
||||||
|
> prototypes, and position it against the 2025-26 evolutionary-AI landscape.
|
||||||
|
|
||||||
|
**After posting.** Record the arXiv id in `tasks/workorder-pnas-submission.md`; sync v2 with the
|
||||||
|
PNAS-submitted text at Phase 5. PNAS permits preprints.
|
||||||
343
paper/arxiv/body.tex
Normal file
343
paper/arxiv/body.tex
Normal file
|
|
@ -0,0 +1,343 @@
|
||||||
|
\subsection*{A note on vocabulary (please read this first)}
|
||||||
|
|
||||||
|
This paper sits at the meeting point of three fields, and it is written so that a reader from any one of them can follow all of it. We therefore \textbf{spell out} each field's jargon the first time it appears, even at the risk of belabouring the obvious for the specialist. A short glossary, in case you skipped a definition:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Model collapse} \emph{(machine learning)} --- the degeneration that happens when you train a model on data produced by earlier models, over and over: rare cases disappear and the model drifts toward a bland average.
|
||||||
|
\item \textbf{Distillation} \emph{(machine learning)} --- training a fresh ``student'' model on the outputs of one or more ``teacher'' models, so the student ends up knowing a compressed version of what they knew.
|
||||||
|
\item \textbf{Model merging} \emph{(machine learning)} --- combining several trained models directly, at the level of their weights, into one --- no retraining. (Think of it as breeding two models rather than teaching a third.)
|
||||||
|
\item \textbf{Genetic drift} \emph{(population genetics)} --- the random loss of rare variants that happens in any finite population simply because not everyone leaves offspring. It is the neutral, no-selection baseline of evolution.
|
||||||
|
\item \textbf{Wright--Fisher process} \emph{(population genetics)} --- the standard mathematical model of drift. Our minimal model of knowledge transmission \emph{is} this process exactly; a real trained network is this process plus a measurable, architecture-specific bias we quantify.
|
||||||
|
\item \textbf{Recombination / sexual reproduction} \emph{(biology)} --- making an offspring by combining pieces from more than one parent, rather than copying a single parent (which is \emph{asexual} reproduction).
|
||||||
|
\item \textbf{Muller's ratchet} \emph{(population genetics)} --- the way an asexual lineage, one that never recombines, accumulates damage it can never undo. We will argue it is the right lens for the \emph{irreversible} part of model collapse --- the capabilities that, once lost from every parent, no merging can rebuild.
|
||||||
|
\item \textbf{Catastrophic forgetting} \emph{(machine learning / neuroscience)} --- a neural network overwriting what it knew when it learns something new.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
We have tried to keep the big picture legible on every page, and to be candid about what is argument and what is evidence. The evidence is mostly from \textbf{deliberately small models} --- mathematics, small neural networks, image generators, and evolutionary simulations. A first bridge to real language models exists --- a prototype that recombines LoRA-specialised Qwen models up to 7B on a GPU cluster, which confirms the recombination signs (below) --- but the \emph{full grounded society} has not yet been built on a large language model. We will say so repeatedly, because the gap matters.
|
||||||
|
|
||||||
|
\medskip\hrule\medskip
|
||||||
|
|
||||||
|
\section*{Abstract}
|
||||||
|
|
||||||
|
AI is turning from single frozen models to \textbf{populations of agents} that persist, specialise, and are increasingly \emph{recombined} into new models --- a shift visible in multi-agent societies, population-based self-improvement, and the explosion of \textbf{model merging}. The field is doing this with the vocabulary of evolution --- ``crossover,'' ``mutation,'' ``mate choice,'' ``offspring that beat their parents'' --- but as loose metaphor draped over search algorithms. This paper argues that a rich, quantitative body of applicable theory already exists in the branch of biology that studies exactly this: the \textbf{evolution of sex}. Ninety years of population genetics analyse when reproducing a population by \emph{recombination} beats copying, when it backfires, and how to do it better --- and, read as an engineering framework, it supplies overlooked variables and testable design rules for keeping a society of models learning across generations instead of decaying. The underlying shift of perspective is the contribution we most want to land: \textbf{treat multigenerational model populations as systems whose inheritance, diversity, and compatibility must be managed --- not merely as collections of models to optimise.}
|
||||||
|
|
||||||
|
We take one diagnosis as settled and cite it as such: training each generation on the last is \textbf{genetic drift}, and the resulting \textbf{model collapse} is the loss of rare variants a finite population always suffers (the Wright--Fisher process; formalised for language models by Shumailov et al., 2024, and Riis, 2026). We claim none of that. Our contribution is on the remedy side. Single- teacher copying is \textbf{asexual} reproduction, and the irreversible arm of its decay corresponds to \textbf{Muller's ratchet} (a correspondence we state with its scope, not as identity); the remedy biology found for the ratchet is \textbf{sex}. A society of models should reproduce sexually --- each new model \textbf{recombined from several complementary parents} (which the field already does, as \emph{model merging}), selection \textbf{anchored to a reality that can say no} (not to the consensus of other models), and diversity actively \textbf{preserved}. In our models --- from closed-form to trained networks to a language-model prototype --- those three ingredients together let a lineage not merely avoid collapse but \textbf{climb}, producing models fitter than any ancestor (the \textbf{Fisher--Muller effect}) while each specialty is re-earned and exceeded; whether the full recipe holds at frontier scale is the open question the framework is built to test.
|
||||||
|
|
||||||
|
From the geneticist's apparatus we extract falsifiable, load-bearing claims (each stated with its operator and scope in the text): (i) \textbf{``merge, don't average''} --- a conservation result: refitting a child to the \emph{mean of its parents' output distributions} conserves expected rare-capability mass at the single-parent level, cancelling the multi-parent gain \emph{to first order in the rare-item regime} (outside it, variance reduction from averaging can help --- the result is a first-order cancellation, not a universal impossibility), while union-preserving operators realise the gain in all regimes --- derived in the minimal model, with its weight-space image the headroom rule below; (ii) \textbf{offspring can exceed every parent} (Fisher--Muller), the real argument for sex in model societies; (iii) on \textbf{rugged, epistatic} task landscapes, blind recombination causes \textbf{outbreeding depression}, yielding a design rule --- \emph{merge freely when skills are additive, sparingly and with selection when entangled, and route rather than blend under overlap}; (iv) \textbf{grounding is immigration} from a non-drifting reality, giving a critical real-data fraction far below one; and (v) --- the sharpest new prediction --- sex has a \textbf{limit}: as two models diverge they undergo \textbf{speciation}, a merge-compatibility cliff (compatible \(\rightarrow\) outbreeding depression \(\rightarrow\) hybrid inviability) whose onset is set by divergence \emph{and} epistasis via \textbf{Bateson--Dobzhansky--Muller incompatibilities}, and whose damage grows \emph{super-linearly} (the Orr--Turelli snowball). We introduce and model this ``model speciation'' directly, and confirm it in real trained weights: a merge barrier that survives alignment under the \emph{full} function-preserving symmetry group of the network (not just Git Re-Basin permutations), rising with functional conflict while hybrid fitness falls to inviability --- with an honest converse we pre-registered and found: absent conflicting training signals, divergently-specialised lineages of shared ancestry developed \emph{no} isolation at any divergence tested, the merge instead \emph{rescuing} the forgetting specialists. Isolation must be provoked by conflict; specialisation alone did not speciate. AI also has an advantage biology lacks: \textbf{directed sex} --- unbounded parents, chosen mates, and offspring screened before they are kept --- engineered recombination with a flexibility of parent choice and pre-deployment screening that natural mating systems do not approach.
|
||||||
|
|
||||||
|
We support the argument with \textbf{minimal, reproducible models} --- a closed-form-exact account of drift and grounding, the same effects in small trained networks and an MNIST image generator, a real-weight demonstration of the speciation cliff (a Git Re-Basin residual that survives neuron alignment), and evolutionary simulations of the whole society --- and a first \textbf{language-model prototype}: merging LoRA-specialised Qwen models (to 7B on a GPU cluster) yields a generalist that beats every specialist parent, with the sharp headroom condition under which ``merge, don't average'' bites. The scope is honest: these are existence proofs and design rules; the \emph{whole grounded society} on a large language model is the open step. We position the work carefully against the crowded 2025--2026 landscape of evolutionary-AI and merging methods --- conceding what they own and marking, precisely, what a genuine population-genetics of sex adds.
|
||||||
|
|
||||||
|
\medskip\hrule\medskip
|
||||||
|
|
||||||
|
\section*{1. From a society in space to a society in time}
|
||||||
|
|
||||||
|
The idea of many AI agents working together --- a ``society of mind'' (Minsky, 1986), or today's multi-agent systems --- arranges intelligence across \emph{space}: several specialists side by side, dividing a task. This paper is about a different axis: \emph{time}. Not a society that merely exists at one moment, but one that \textbf{persists and renews across generations}, each new cohort of models starting from the compressed knowledge of the last.
|
||||||
|
|
||||||
|
The unit that matters is therefore the \textbf{generation}, and the event that matters is \textbf{reproduction}: the making of a new model from older ones. A single model, like a single mind, is bounded and eventually stops improving. A \emph{lineage} need not be. Human civilisation is not clever because any one person is; it is clever because each generation inherits the distilled achievements of the previous one and adds a little. We propose building AI the same way --- and, crucially, getting the \emph{reproduction} right, because that is exactly where it can go wrong.
|
||||||
|
|
||||||
|
\subsection*{Where this sits, and what is new}
|
||||||
|
|
||||||
|
This axis is suddenly crowded. By 2026 several groups build \textbf{populations of models or agents that improve across generations}: societies of independently-specialised models that self-improve for more rounds than a single agent (Multiagent Finetuning --- Subramaniam et al., 2025); open-ended archives of self-rewriting coding agents (the Darwin--Gödel Machine --- Zhang et al., 2025); groups that evolve by sharing experience across branches (Weng et al., 2026); persistent agent \emph{ecologies} with reproduction and cumulative culture (TerraLingua --- 2026). In parallel, \textbf{model merging} has become a small industry with an overtly evolutionary vocabulary: crossover-mutation-selection over LLM populations (GENOME --- 2025), niching and ``mate choice'' (Sakana's M2N2 --- 2025), and evolutionary search over merge recipes (Akiba et al., \emph{Nature Mach. Intell.} 2024/25).
|
||||||
|
|
||||||
|
We are candid about the consequence. Three things we do \textbf{not} claim. First, that collapse is Wright--Fisher drift: formalised independently (Riis, 2026; Shumailov et al., 2024), sharpened to a closed-form first-extinction law whose onset coincides with collapse (Benati et al., 2025) and to a quantitative-trait-genetics account for diffusion models (Yoon et al., ICLR 2025), and conceded here. Second, the bare empirical facts that a merged model can beat its parents, that decorrelated parents merge better, and that naive averaging is inferior to sign- or routing-based merges (TIES, DARE, mixture-of-experts routing): all established. Third, that merge success can be \emph{predicted at all}: machine-learning-native predictors exist, from interpretable pairwise metrics (gradient alignment --- Zhou et al., 2026) to capacity/rate-distortion accounts of ``merging collapse'' (2026); what they lack, and we supply, is the \emph{mechanism} --- when and why the failure is a coordinate artefact versus genuine functional incompatibility, and what moves the cliff. What a geneticist is placed to supply is a \textbf{framework} rather than a search heuristic. The nearest precursor is a theory-of-computation tradition reading sex as an algorithm for \emph{mixability} (Livnat \& Papadimitriou, 2016), pre-dating model merging; the works above use evolution chiefly as vocabulary over an optimiser, and --- to our knowledge --- the quantitative apparatus of the evolution of sex (Fisher--Muller, outbreeding depression, migration--drift balance, reproductive isolation) has not previously been carried over as more than metaphor. We are also candid about what \emph{kind} of contribution each of our claims is, because three different things are easily conflated: \textbf{interpretation} (an existing result is usefully understood in these terms --- e.g., merged offspring beating their parents as Fisher--Muller), \textbf{explanation} (the transferred mechanism accounts for observations existing accounts leave open --- e.g., which merge failures are coordinate artefacts and which are functional), and \textbf{prediction} (the framework forecasts an unmeasured outcome and improves a design decision). This paper is strongest on the first, makes concrete progress on the second, and reports a first, bounded step on the third: a \textbf{controlled predictive test} at small scale in which pre-merge \emph{functional-disagreement} measures --- chosen by the framework --- showed a detectable, held-out-robust association with merge damage on a constructed task grid, while the selected weight-geometry baselines did not. We are precise about that result's boundary where it is reported: it is a small-model demonstration on a constructed grid; the proposed epistasis-specific refinement did not outperform plain disagreement; predictor differences are not individually significant head-to-head; and whether the prediction improves a budget-matched operator choice remains open. The organising shift we argue for is prior to any single mechanism: \textbf{treat multigenerational model populations as systems whose inheritance, diversity, and compatibility must be managed --- not merely as collections of models to optimise.}
|
||||||
|
|
||||||
|
\section*{2. Why today's models cannot do this}
|
||||||
|
|
||||||
|
Today's large language models have no life cycle. They are trained once, at enormous cost, then \textbf{frozen} and deployed as a fixed artefact that does not learn from the people it serves. Learning and doing are split into two eras with no bridge between them.
|
||||||
|
|
||||||
|
There is a real reason for the freeze. Updating a neural network on new information tends to overwrite what it already knew --- \textbf{catastrophic forgetting}, a problem understood since the late 1980s (McCloskey \& Cohen, 1989; French, 1999). Freezing avoids it by refusing to learn at all. The result is a mind with no childhood, no growth, and no way to pass anything on. A lineage needs the opposite: members that learn through their working lives, reach maturity, and hand on what they gained. So the first requirement is a learner that can grow \emph{safely}.
|
||||||
|
|
||||||
|
\section*{3. A learner that can grow without forgetting}
|
||||||
|
|
||||||
|
The individual model needs two properties.
|
||||||
|
|
||||||
|
\textbf{It must not catastrophically forget.} Instead of overwriting its core as it learns, it keeps that core frozen and only \emph{readable}, and carves each new skill into freshly-added capacity beside it. In machine learning this is called \emph{parameter isolation} (progressive networks --- Rusu et al., 2016; prune-and-freeze --- Mallya \& Lazebnik, 2018; and, most practically, \textbf{LoRA} and other small trainable ``patches'' bolted onto a frozen model --- Hu et al., 2021). If the core is never altered, its \emph{parameters} cannot be forgotten --- though a precise reader should note the system's \emph{behaviour} can still shift while adapters are active, so the guarantee is of a recoverable core, not of unchanging conduct. This is what lets a model accumulate a coherent working life of expertise --- the kind of stable knowledge worth passing on.
|
||||||
|
|
||||||
|
The brain offers a partial blueprint. \emph{Complementary Learning Systems} theory (McClelland, McNaughton \& O'Reilly, 1995) --- itself a response to the forgetting problem --- describes two subsystems: a \textbf{fast} store (the hippocampus) that grabs an experience in one shot, and a \textbf{slow} store (the neocortex) that integrates regularities gradually without disruption. We do not lean on any particular account of how the brain moves knowledge between them; the architecture needs only that \emph{some} periodic \textbf{offline consolidation} step exists, moving knowledge from the fast store to the slow one when the system is idle. The machine version is clean regardless: the prompt is working memory, an external database is the fast episodic store, the trained weights are the slow store, and consolidation migrates the first into the last.
|
||||||
|
|
||||||
|
\textbf{It is bounded.} Because the model only ever \emph{adds} capacity and freezes what it has, it eventually fills up. In most designs that is a wall to dread. In ours it is a clock.
|
||||||
|
|
||||||
|
\section*{4. ``Full'' is maturity, not failure}
|
||||||
|
|
||||||
|
Here is the pivot. A bounded learner that fills up has not broken. \textbf{It has grown up.}
|
||||||
|
|
||||||
|
Read the capacity limit as a life stage. A model is \emph{born} as a freshly-schooled base --- its general education. It enters a \textbf{working life}, adding specialised knowledge as it does its job. And it reaches \textbf{maturity}: the point where it has learned much of what one working life in its niche can teach. Maturity is not the end of usefulness --- it is the moment the model is most worth learning \emph{from}. So maturity is the cue to \textbf{reproduce}. The capacity ceiling that every other architecture fights becomes, in ours, the metronome of the generations.
|
||||||
|
|
||||||
|
Everything now turns on how that reproduction is done --- and this is where the paper's central claim lives.
|
||||||
|
|
||||||
|
\section*{5. Reproduction: copying collapses, recombination climbs}
|
||||||
|
|
||||||
|
Suppose a mature model simply teaches a fresh one --- distillation, one teacher to one pupil, generation after generation. This is the obvious design, and it fails, for a reason that is exactly the same in machine learning and in biology.
|
||||||
|
|
||||||
|
\textbf{The machine-learning statement.} Training each generation on the previous generation's outputs is the recipe for \textbf{model collapse}: the model forgets the improbable, loses the \emph{tail} of the distribution (the rare cases) first, and drifts toward its own most common output (Shumailov et al., 2024). Worse for us, the very rule that makes distillation useful --- \emph{keep the general, drop the idiosyncratic} --- \textbf{is} tail-deletion by design. The operation that would power a cultural ratchet and the operation that drives model collapse are the same act.
|
||||||
|
|
||||||
|
\textbf{The population-genetics statement (the same thing, for the minimal model).} Represent a model's knowledge as a distribution over discrete ``items'' --- capabilities, facts, modes of behaviour. One generation is: \emph{draw a finite sample from the parent, and refit the child to it.} In this \textbf{minimal inheritance model} the finite-sampling step is \textbf{exactly} genetic drift --- the random loss of rare variants in a finite population --- described by the century-old \textbf{Wright--Fisher} model (Wright, 1931; Fisher, 1930): the same equations, which we use as closed-form checks on our simulations. Rare items go extinct first, roughly ten times faster than common ones, precisely as drift predicts. \textbf{The boundary of the identity matters, and we measured it:} real neural training adds approximation, optimisation noise, and inductive bias on top of sampling, and when we fit trained networks against the exact drift null they deviate in \emph{opposite, architecture-specific directions} --- a smoothing recurrent model resists collapse (it keeps spurious variants alive), a sharpening image generator accelerates it (our learning-kernel result, below). So the honest statement is: the minimal inheritance model is exactly Wright--Fisher; a real learner is Wright--Fisher \emph{plus a signed, measurable estimator-bias operator} --- and the drift signs (rare-first loss, the grounding response) survive that operator in every architecture we tested.
|
||||||
|
|
||||||
|
And single-teacher copying is \textbf{asexual reproduction} --- cloning one parent. Nature already knows what happens to an asexual lineage that never recombines: it accumulates damage it can never repair, a one-way decline geneticists call \textbf{Muller's ratchet} (Muller, 1964). We use the ratchet as the \emph{organising correspondence} for model collapse, with its scope stated: strictly, the ratchet is the stochastic loss of the least-degraded class under recurring deleterious change in an asexual population, so it maps onto the \emph{irreversible} component of capability loss (once every copy of a rare capability is gone from all parents and sources, no recombination can rebuild it) rather than onto every form of degradation. That is exactly why the correspondence is useful rather than decorative: it says the cure must act \emph{before} fixation-by-loss --- keep complementary variants alive somewhere in the population --- because recombination can only reassemble what still survives. Biology solved this problem, and its solution is the subject of this paper.
|
||||||
|
|
||||||
|
Two ingredients turn the collapse operation into a climb. Both are things nature does.
|
||||||
|
|
||||||
|
\textbf{First: do not reproduce ``dry.''} Model collapse is a property of a lineage fed \emph{only} its own output; the documented fix is that keeping some real data in the mixture arrests it (Shumailov et al., 2024). We call that real data \textbf{grounding} --- fresh contact with the world, verified against it. In our minimal models, grounding is startlingly cheap: mixing in even a few percent of verified real data holds on to most of the diversity indefinitely. But --- an honest limit we found and did not expect --- grounding cannot save the \emph{very rarest} items at any affordable budget; protecting an item of rarity \emph{p} needs a real-data budget that grows like 1/\emph{p}. Grounding rescues diversity cheaply; it does not, by itself, rescue the deep tail. Something else must. That something is sex.
|
||||||
|
|
||||||
|
\textbf{Second: reproduce sexually.} Instead of copying one parent, build each new model by \textbf{recombining several} --- a \emph{sexual} rather than asexual birth. In machine learning this already has a name and a working implementation: \textbf{model merging} (Akiba et al., 2024). Its importance here is not efficiency; it is that recombination does something copying cannot. If several parent models have each specialised on different parts of reality, each has kept alive rare knowledge the others lost. A recombined child inherits the \textbf{union} of what its parents kept --- not the tail-thinned \emph{average} of a crowd of near-identical copies. And here is the point that lifts sex from a safeguard to the engine of the whole scheme, and the reason biology invented it:
|
||||||
|
|
||||||
|
\begin{quote}*\emph{An offspring recombined from complementary parents can be }fitter than any of its parents\emph{.}*\end{quote}
|
||||||
|
|
||||||
|
Geneticists call this the \textbf{Fisher--Muller effect} (Fisher, 1930; Muller, 1932): recombination brings together, in one individual, beneficial variants that arose separately in different lineages, so the child holds a combination none of the parents had. In our simulations this is exactly what we see --- recombining decorrelated specialist models yields a model that climbs toward the best-possible combination, a genotype \emph{no single parent possessed}, while the best single parent, and the naive average of all of them (what the field calls a ``model soup'' --- Wortsman et al., 2022), both plateau well below. This is the concrete meaning of the paper's title claim, ``the lineage climbs in general knowledge; specialisation is re-earned each generation,'' and it is why the reframing from teacher\(\rightarrow\)pupil to \emph{sexual reproduction} is not cosmetic: \textbf{copying can only recover a ceiling; recombination can exceed it.}
|
||||||
|
|
||||||
|
This is no longer only a simulation. In a first language-model prototype --- LoRA specialists on disjoint task families, recombined and judged by an exact verifier --- a merge of three specialist Qwen models (7B, on a GPU cluster) \textbf{beats every single specialist}, overall and on every family: the Fisher--Muller effect, in real weights. The same prototype pins down \emph{when} the finer ``inherit the union, don't average'' rule actually bites. Keeping each parent whole and \textbf{routing} each input to the right one beats the tail-thinning average --- but only when the task is hard enough to leave room to lose: on easy tasks a strong model's plain average is already at the ceiling, so the crude soup is fine, whereas on hard tasks the average dilutes a hard-won specialist so badly it falls below even the best single parent, and routing wins by a wide margin. The rule is therefore precise: \textbf{the union beats the average in exact proportion to how far the average is from the best attainable} --- a caveat that sharpens rather than weakens the claim, and that a practitioner needs before spending compute on the fancier operator.
|
||||||
|
|
||||||
|
\textbf{The operator boundaries (stated, because ``merge, don't average'' is not one claim but a family).} Four different operators travel under these words, and the conservation result belongs to exactly one of them. What is \emph{derived} is this: when a pupil's knowledge is refit to the \textbf{mean of the parents' output distributions}, the expected mass on any rare item is conserved at the single-parent level --- in the rare-item regime (\texttt{n\(\cdot\)p/K ≪ 1}) the 1/K dilution of averaging cancels the union gain of having K parents to first order --- outside that regime, survival is convex in mixed mass and averaging's variance reduction can help, so this is a first-order cancellation, not a universal impossibility; whereas an operator that keeps, per item, its \textbf{strongest source} (and renormalises, which itself redistributes mass) realises the union in all regimes. That statement is exact in the minimal model, and it presupposes an oracle (or verifier) able to say which source is strongest. The two operators the LLM prototype tests --- \textbf{weight averaging} (a nonlinear network's weight-mean does not compute the mean of its parents' outputs) and \textbf{routing among intact specialists} (which keeps K models' storage and an input classifier, a different parameter and inference budget from one fixed-size child) --- are \emph{empirical cousins} of the two sides of that law, not instances of it. The headroom rule above is precisely the empirical bridge: it says when the weight-average behaves like the diluting mean (hard tasks, weak base) and when a capable base absorbs the dilution (easy tasks). And all of it operates within a capacity boundary: when parental capabilities genuinely cannot coexist in the child's capacity, no operator preserves the union --- that regime is the subject of the speciation section below.
|
||||||
|
|
||||||
|
Three results keep this honest, and all are results, not hand-waving.
|
||||||
|
|
||||||
|
\emph{Sex can backfire.} When the parents' skills are not cleanly separable but \textbf{entangled} --- when the value of one capability depends on which others are present (geneticists call this \textbf{epistasis}) --- blindly recombining two good models can produce a \emph{worse} child, because recombination breaks up a combination that only worked as a whole. Biologists call this \textbf{outbreeding depression}, and we reproduce it: on ``rugged'' (highly entangled) problems, naive merging drops offspring below their parents, and the more you mix the worse it gets. The design rule that falls out is simple: \emph{merge freely when skills are complementary; merge sparingly, and carefully, when they are entangled.}
|
||||||
|
|
||||||
|
\emph{The mating system matters too --- not just who mates, but how widely.} The result above is about the recombination \emph{rate}; a separate knob is the population's \textbf{mating structure} --- whether reproduction is \textbf{monogamous} (each model recombines within a narrow, local circle) or \textbf{promiscuous} (mates drawn freely from the whole population). Almost all model-merging implicitly assumes promiscuity --- fuse everything, or route over one flat pool --- but population genetics says the breadth of gene flow is itself consequential, because wide flow spreads good variants fast while \textbf{homogenising} the population, and narrow flow preserves the distinct sub-populations needed to explore several solutions at once (Wright's \emph{shifting balance}). We sweep exactly this breadth against landscape ruggedness, and the optimum moves: on smooth (additive) landscapes wide, promiscuous mating is best (spread the one good direction fastest), but as the landscape gets rugged the best breadth \textbf{shrinks to an intermediate value} --- full promiscuity prematurely converges onto one basin and finds a \emph{worse} champion, while pure monogamy over-fragments. Throughout, wide mating lifts the \emph{typical} model but monotonically \textbf{destroys diversity} --- so on rugged problems, where the best model needs preserved diversity to be found, structured (partly monogamous) merging wins. The design rule extends the one above: \emph{merge widely when skills are additive; keep structured sub-populations --- island-style merging --- when skills are rugged.}
|
||||||
|
|
||||||
|
\begin{figure*}[t]\centering
|
||||||
|
\includegraphics[width=\textwidth]{figs/E14.pdf}
|
||||||
|
\caption{Mating systems (E14): the best mate-pool breadth shrinks as skills get more entangled. (A) best fitness peaks at intermediate breadth on rugged landscapes; (B) the population mean is monotonically favoured by promiscuity; (C) diversity is monotonically destroyed by it.}
|
||||||
|
\end{figure*}
|
||||||
|
|
||||||
|
|
||||||
|
\emph{AI can do sex better than biology can.} Biology is stuck with two parents, mating roughly at random, and cannot inspect an offspring before it is born. An AI has none of those limits. It can recombine \textbf{many} parents at once; it can \textbf{choose} which parents to combine, for complementarity; and it can \textbf{generate many candidate offspring and keep only the fittest}, screening them against reality before committing. We call this \textbf{directed sex}, and in our simulations it converts the outbreeding-depression catastrophe into a reliable gain: where blind recombination collapses on entangled problems, directed recombination matches or beats the best parent every time. The language-model prototype shows the same sign where it can: breeding many recombined Qwen offspring and keeping the one the verifier scores highest beats the single averaged soup on hard tasks (and, unsurprisingly, does nothing extra on easy tasks the soup already solves). This is a genuine advantage of engineered reproduction over the biological kind, and we think it is one of the more useful ideas in the paper.
|
||||||
|
|
||||||
|
So the picture of §5 is: single-teacher copying is asexual and collapses (Muller's ratchet = model collapse); the cure is to \emph{ground} every birth in reality and to reproduce \emph{sexually}, recombining many complementary parents; and because AI sex can be many-parent, mate-chosen, and offspring-screened, it is not merely a hedge against collapse but an engine that produces children fitter than any parent.
|
||||||
|
|
||||||
|
\subsection*{The limit of sex: model speciation}
|
||||||
|
|
||||||
|
Sex has a limit, and it is the sharpest new prediction this frame makes. Recombination works because the parents are variations on a shared background; push two lineages far enough apart and their combination is no longer viable. In biology this is \textbf{speciation} --- the onset of \textbf{reproductive isolation} --- and its genetic mechanism is the \textbf{Bateson--Dobzhansky--Muller incompatibility} (BDMI): an allele that arose in one lineage and an allele that arose in the other are each harmless on their own background, but their \emph{combination}, never tested by selection in either parent, is deleterious in the hybrid (Dobzhansky, 1937; Muller, 1942; Orr, 1995). A merged model is precisely such a hybrid --- a single \emph{recombinant} genotype, an F2-like object exposed to \textbf{recombination load}, not a hybrid-vigour F1 --- so the theory predicts a specific trajectory as two models diverge: \textbf{compatible \(\rightarrow\) outbreeding depression \(\rightarrow\) hybrid inviability}.
|
||||||
|
|
||||||
|
We built this as an explicit model (a companion result). Two lineages descend from a common ancestor, each substituting a \emph{disjoint} set of loci --- so each parent is adapted and neither carries an incompatibility --- and a fraction of the cross-lineage locus pairs are BDMIs that fire only when a hybrid inherits \emph{both} derived alleles. Sweeping the divergence between the parents reproduces the predicted curve exactly: hybrid fitness tracks the parents while they are compatible, then peels off, peaks, and crashes below the ancestor (an inviable hybrid). Three things fall out, and they are the contribution:
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item \textbf{The isolation cliff, and what moves it.} The divergence at which merging fails is not fixed: it arrives \emph{earlier the more epistatic the capability landscape}. In the model the reproductive-isolation rate at high divergence rises from \textasciitilde{}0 to \textasciitilde{}0.5 as the density of incompatibilities grows. This is the paper's distinct, falsifiable claim --- \textbf{at matched divergence, mergeability is governed by epistasis, not by divergence alone} --- and it is exactly the axis that the machine-learning predictors of merge success (which are all divergence/geometry measures) do not have.
|
||||||
|
\item \textbf{The snowball.} The number of incompatibilities grows with the \emph{square} of the divergence (Orr \& Turelli, 2001), so hybrid fitness falls \emph{super-linearly}: divergence is punished faster than it accrues. Merge compatibility does not decay gently; it falls off a cliff.
|
||||||
|
\item \textbf{The design rule.} \emph{Before merging, weigh divergence against the ruggedness of the shared capability landscape; past the cliff, do not merge --- route} (the engineering echo of allopatry: keep the specialists reproductively separate and select among them instead of hybridising).
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
This is where a geneticist's lens earns its keep. The machine-learning literature has \emph{observed} that increasing specialisation eventually breaks merging and that one should then route rather than fuse (Pari et al., 2024; Zhou et al., 2026), and part of the apparent incompatibility between independently trained models is a coordinate artefact removable by aligning neurons (Git Re-Basin --- Ainsworth et al., 2022). What the frame adds is the \emph{theory} of the phenomenon they observe: its functional form, its super-linear (snowball) onset, and its dependence on epistasis --- merge failure as a Dobzhansky--Muller event.
|
||||||
|
|
||||||
|
\begin{figure*}[t]\centering
|
||||||
|
\includegraphics[width=\textwidth]{figs/E12.pdf}
|
||||||
|
\caption{Model speciation, analytic (E12): hybrid fitness vs divergence traces compatible $\rightarrow$ outbreeding depression $\rightarrow$ inviability; the isolation cliff arrives earlier the denser the incompatibilities (epistasis), and damage grows super-linearly (the Orr--Turelli snowball).}
|
||||||
|
\end{figure*}
|
||||||
|
|
||||||
|
|
||||||
|
\textbf{The real-weight confirmation.} The obvious objection to the analytic model is that its ``incompatibility'' is a re-labelled loss barrier, and loss barriers between independently trained networks are famously a \emph{coordinate} artefact --- two nets that learned the same function in a permuted basis look incompatible until their neurons are aligned (Git Re-Basin), and recent work shows that symmetry groups \emph{richer} than permutations remove still more of the barrier (functionality-preserving rescalings and rotations --- Scaling LMC, 2026; neuron-identifiability approaches). We therefore ran the experiment the objection demands, in real trained weights, aligning modulo the \textbf{full} function-preserving unit symmetry group of the architecture (per-unit positive rescaling composed with permutation --- for a plain ReLU network, all of it). Two small MLPs are forked from a shared MNIST base, trained, weight-averaged, and their linear-mode-connectivity error barrier is measured \emph{before and after} alignment; the after-alignment \textbf{residual} is the part of the incompatibility that no re-coordination can explain away. The decomposition is clean : two nets trained \emph{from different random initialisations on the same task} have a real naive barrier that alignment removes almost entirely (residual \(\approx\) 0.001, and the aligned merge performs at parent level) --- same species, different basis, the canonical Re-Basin result, which also proves the aligner works. Two nets that learned \emph{conflicting} label maps have a large barrier of which the full symmetry group removes \textbf{essentially nothing} (0.502 \(\rightarrow\) 0.497) --- a conflict-associated barrier the tested alignment leaves largely unchanged --- supporting a functional-conflict interpretation without proving optimal alignment (control recovery validates a special case; the removable share is a lower bound, the residual an upper bound). It also carries a floor no future alignment method can breach: models loyal to label maps that conflict on a fraction \emph{\(\mu\)} of inputs cannot both be served by \emph{any} single merged model, which must err at rate \(\geq\) \emph{\(\mu\)}/2 against at least one parent (SI proposition). Sweeping the fraction of conflicting classes traces the \textbf{isolation cliff in real weights}, now readable directly as \emph{hybrid fitness}: the residual barrier climbs monotonically while the merged model's accuracy falls from 0.97 to 0.03 --- E12's compatible \(\rightarrow\) depression \(\rightarrow\) inviability trajectory, measured.
|
||||||
|
|
||||||
|
\begin{figure*}[t]\centering
|
||||||
|
\includegraphics[width=\textwidth]{figs/speciation_real.pdf}
|
||||||
|
\caption{Model speciation in real weights (E13). (A) the merge barrier decomposed by alignment strength: the independent-init barrier is a coordinate artefact (removed by alignment); the conflict barrier survives even the full function-preserving symmetry group. (B) the isolation cliff: residual barrier rises and hybrid accuracy falls ($0.97 \rightarrow 0.03$) with functional conflict. (C) the pre-registered emergent test: divergent-but-compatible specialists develop no isolation at any divergence --- the merge instead rescues them (Fisher--Muller).}
|
||||||
|
\end{figure*}
|
||||||
|
|
||||||
|
|
||||||
|
\textbf{And its honest converse: speciation must be provoked; it did not emerge.} A true Dobzhansky--Muller incompatibility is \emph{emergent} --- each lineage's changes harmless alone, incompatible only in combination --- whereas the conflict condition above \emph{imposes} contradiction. So we pre-registered the emergent test: fork two children from a shared base and let them diverge with \textbf{no conflicting training signal anywhere} --- one pair as complementary class specialists (one child trains only on digits 0--4, the other only on 5--9), one pair with divergent input conventions (views shifted in opposite directions) --- out to divergences 6.4\(\times\) the base training. The result is the second pre-registered reading, and it sharpens the theory's scope rather than confirming its most dramatic form: the residual barrier is \textbf{0.000 at every divergence in both conditions}, and far from failing, the merge \emph{rescues} the two specialists --- each parent decays toward \textasciitilde{}0.50 on the full task (catastrophically forgetting the classes it no longer sees) while the merged model holds \textasciitilde{}0.95 throughout, a sustained Fisher--Muller rescue at zero barrier. In real weights, at least in this regime of shared ancestry and compatible tasks, \textbf{reproductive isolation requires functional conflict; it does not arise spontaneously from divergent specialisation.} The design rule sharpens accordingly: \emph{merge freely across divergently-specialised lineages of shared ancestry --- what speciates model populations is conflicting conventions, not specialisation per se.} Whether long-horizon over-specialisation erodes mergeability at language-model scale --- as the empirical merging literature hints (experts trained longer merge worse under averaging) --- is exactly the next tier's question, and the theory now makes the prediction crisp: it should depend on whether extended training induces \emph{conflicting conventions on shared circuitry}, not on divergence time itself.
|
||||||
|
|
||||||
|
\textbf{What these experiments do and do not establish.} Stated at exactly the strength of the evidence: they establish that \emph{some merge failures reflect incompatible functional requirements rather than a mismatch of coordinates} --- a residual that survives the full unit-symmetry group of the architecture tested, rises with functional conflict, and is absent under compatible specialisation. Three qualifiers. First, the impossibility at the heart of the conflict condition --- one deterministic model cannot satisfy two contradictory answer conventions --- is information-theoretic and needs no population genetics; what the genetic frame adds is \emph{structure around it}: which divergences generate such conflicts, the prediction that epistasis rather than distance sets the cliff's position, and the snowball's super-linear onset --- the latter two verified so far only in the analytic model, and therefore carried as \textbf{hypotheses at the neural tier, not results}. (On the snowball, one more distinction: super-linear growth in the \emph{number} of incompatibilities does not by itself entail a sharp \emph{performance} cliff --- that needs the link from incompatibility count through effect sizes to measured performance, which the analytic model supplies under its assumptions and any neural test must establish separately.) Second, our alignment removes the symmetries we enumerate for this architecture class, and exactly recovering a permuted-and-rescaled copy validates a special case rather than proving global optimality for independently trained networks --- so the removable share is a lower bound and the residual an upper bound; richer transformation families for other architectures could reapportion the split, though not below the conflict floor. Third, ``unmergeable'' here means by aligned linear interpolation of weights --- a barrier to that operator does not preclude every conceivable recombination method (routing, for one, sidesteps it by not blending). Emergent Dobzhansky--Muller incompatibilities in real weights remain the flagship \emph{hypothesis} of this programme: our tested regimes found none, which bounds where they can live --- longer horizons, shifted data distributions, capacity pressure --- and the decisive experiment (predicting merge success \emph{before} merging from an operational epistasis measure, against geometry- and gradient-based predictors) is posed in the closing section.
|
||||||
|
|
||||||
|
One question remains, and the rest of the paper is largely about it: recombination combines what the parents kept --- but \emph{who decides what each parent keeps, and which offspring are worth keeping?}
|
||||||
|
|
||||||
|
\section*{6. The second inheritance: letting ``what is worth keeping'' evolve}
|
||||||
|
|
||||||
|
There are two answers, and the first is wrong. We could try to \emph{design} the rule for what knowledge to keep and pass on. But nobody knows that rule. ``Keep the general, drop the particular'' is a slogan, not an algorithm: ask \emph{which} generalisations, in \emph{which} domain, at \emph{which} grain, and the hand-written rule falls apart. This is the deepest hole in the scheme, and it cannot be filled by decree.
|
||||||
|
|
||||||
|
The second answer is the one nature used: \textbf{do not design the selector --- evolve it.} Let different models carry different \emph{policies} for what is worth keeping and combining. Let the policies that produce more capable offspring spread; let the policies that produce weak offspring die out with their lineages. The lineage's \emph{taste} --- its sense of what matters --- is discovered by selection, not imposed.
|
||||||
|
|
||||||
|
So \textbf{two things are inherited, on two channels.} The \emph{content} passes down directly: an offspring receives its parents' knowledge (this is the ``Lamarckian'' channel --- the inheritance of things acquired during a lifetime, which biology forbids for genes but culture allows for ideas). The \emph{selection policy} --- what to keep, whom to breed with, which offspring to screen for --- is itself inherited, varies between models, and survives in proportion to the success it produces. That second channel is \textbf{Darwinian}. The architecture is therefore both at once: Lamarckian in \emph{what} it transmits, Darwinian in \emph{what it keeps}. Evolutionary theorists call this structure \emph{dual inheritance} and identify it as the engine of human culture (Boyd \& Richerson, 1985); philosophers of science describe scientific knowledge itself as growing this way, by conjecture and \textbf{refutation} (Popper, 1959; Campbell, 1974; Hull, 1988).
|
||||||
|
|
||||||
|
The closure that makes this fit together, rather than merely sound nice: Darwinian selection needs a \emph{selection pressure} --- something that decides which policies win. That pressure is already in the design. What tells a lineage its taste was good? The success of its offspring \textbf{against reality}. The reality-check that stops collapse (grounding, §5) and the fitness signal that drives the evolving taste turn out to be the \emph{same thing}, seen from two sides.
|
||||||
|
|
||||||
|
\section*{7. The central danger: fitness is not truth}
|
||||||
|
|
||||||
|
Introducing selection introduces selection's classic hazard, and it is severe enough to sink the whole scheme if ignored. Evolution optimises, without mercy or foresight, for exactly what you \emph{measure} --- never for what you \emph{meant}. (Economists and ML engineers know this as \textbf{Goodhart's law} and \emph{specification gaming}.) Get the fitness measure slightly wrong and the lineage will exploit the gap with more ingenuity than any designed rule.
|
||||||
|
|
||||||
|
For a \emph{knowledge} lineage there is a specific and nasty version. For ideas, the natural measure of ``fitness'' is \textbf{how well they spread}, and a false-but-persuasive idea spreads beautifully. Human intellectual culture is full of highly transmissible falsehoods; confident nonsense out-competes hedged accuracy in almost every human forum. Turn Darwinian selection loose on models without care and it will breed a lineage optimised for \emph{persuasiveness} --- fluent, compelling, and wrong. That is model collapse with an optimiser behind it, actively seeking the cliff.
|
||||||
|
|
||||||
|
Only one thing makes fitness track truth rather than appeal: \textbf{being judged against a reality that can say no.} Fitness must be predictive success under \emph{intervention} --- did the model's knowledge correctly anticipate what the world would do when acted upon --- and not approval, fluency, or a benchmark score, each of which can be gamed. This is why the reality-check is load-bearing twice over: it is both the anchor that stops passive collapse \emph{and} the only thing that keeps the evolving taste honest.
|
||||||
|
|
||||||
|
The second danger is \textbf{convergence}, and beating it takes work at two separate levels, because selection can only preserve variety that already exists --- the variety must first be \emph{supplied} and then \emph{kept}.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Supply.} A lineage that learns only from an accredited elite has a monoculture for a source: the ``best'' experts are, almost by definition, the ones who won the consensus, so the incoming variation is narrow from the start. The society must therefore learn, deliberately and from the beginning, from the \textbf{outliers and the heterodox} as well as the credentialed --- not out of fairness, but because in evolutionary terms diverse founders are the raw material without which nothing downstream can adapt.
|
||||||
|
\item \textbf{Preserve.} Even given varied input, plain fitness-\emph{maximising} selection converges --- it drives every lineage toward the single current best and fixes it, extinguishing the rare specialists. The fix is well established: \textbf{quality-diversity} selection, which rewards being \emph{good} and being \emph{different} at once (novelty search and MAP-Elites --- Lehman \& Stanley, 2011; Mouret \& Clune, 2015), keeping complementary specialists alive rather than collapsing onto the champion. In our simulations this is decisive: greedy ``keep-the-best'' selection collapses a population's diversity almost at once and gets stuck at a mediocre answer, while quality-diversity selection keeps the specialists that sexual recombination then needs as parents.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
The two levels meet at reproduction. Multi-parent recombination (§5) is the \emph{vehicle} by which the diversity this selection preserves actually enters the next generation: an offspring drawn from complementary parents inherits the standing variation the selector kept alive, recombined into one new model. Supply the variety from the human side; preserve it on the selection side; recombine it into each generation on the reproduction side. Remove any of the three and the lineage converges on its own first guess.
|
||||||
|
|
||||||
|
\section*{8. A society needs institutions, not just specialists}
|
||||||
|
|
||||||
|
One requirement is easy to overlook and fatal to omit. The easy part of a society is specialisation. The \emph{hard} part --- which human civilisation took millennia to build --- is the set of \textbf{institutions that let fallible specialists combine without each re-verifying everything}: reputation, replication, credentials, and above all \textbf{peer review}. These are error-correction protocols, and they exist because a group of unreliable specialists left to reinforce one another is \emph{more} wrong than any member alone.
|
||||||
|
|
||||||
|
This is precisely where current multi-agent AI fails: set several models to confer and they tend to agree sycophantically and confabulate in committee, because they have all the specialisation and none of the institutions. A multigenerational society must specify not only how models learn, reproduce, and are selected, but how they \emph{check} one another --- how a claim is challenged and a mistaken model loses standing \emph{before} its error is recombined into offspring and inherited. Peer review is itself a reality-check of the kind §7 demands --- an institutional stand-in for reality's ``no,'' to be used where direct intervention is slow or costly.
|
||||||
|
|
||||||
|
\section*{9. The lineage must stay open to reality}
|
||||||
|
|
||||||
|
A society of models, however many generations deep, shares one hard limit: it has only ever \emph{read}. Its whole inheritance is a record of things that were said. In the vocabulary of causal reasoning (Pearl, 2009), it lives on the bottom rung of the \textbf{ladder of causation} --- observation --- and no amount of observation reaches \emph{intervention}. Watching underdetermines doing; correlation does not contain causation, at any scale.
|
||||||
|
|
||||||
|
Only intervention --- reaching out and changing the world to see what happens --- climbs the ladder, and a language model cannot intervene. This is what humans and their instruments supply, and the contribution is not ``truth'' but \textbf{constraint}: reality's unique gift is that it can say \textbf{no}. Text offers only more opinion; an experiment delivers a refusal no consensus can overturn. As §§6--7 argued, that refusal does double duty --- it is both the anchor that prevents collapse and the fitness signal that lets the lineage's evolving taste select for truth rather than persuasion.
|
||||||
|
|
||||||
|
Two honest riders. First, the human reality-signal is \emph{dirty}: people supply results warped by publication bias, incentive, and occasional fraud --- which is exactly why the error-correcting institutions of §8 must sit at the human--machine boundary, screening the signal before it selects. Second, humans are the \emph{current} supplier of intervention, but the actuator half is being automated (autonomous laboratories already close the design--build--test loop). What looks durable in the human role is therefore not the hands but the \textbf{choice of what to test and which refusals matter} --- the part of the fitness function that encodes \emph{what is worth persisting}, as opposed to what merely \emph{can} persist. We flag, without resolving, that a partnership stays mutual only while both sides supply something the other cannot.
|
||||||
|
|
||||||
|
\section*{10. Why it is cheap}
|
||||||
|
|
||||||
|
A practical fact turns this from thought experiment into buildable proposal: \textbf{the architecture almost never re-pays for the one genuinely expensive thing in AI --- pre-training.} (The single exception, periodically re-minting the base, is §11, and it is rare enough to be an amortised footnote.)
|
||||||
|
|
||||||
|
Training a foundation model from scratch consumes trillions of words and a fortune in compute. This design does none of that per generation. Every model is \emph{born} from an existing open-weight model that already paid that cost; specialising one is a small patch trained in hours on a single consumer GPU; running the society is ordinary inference; and reproducing --- recombining parents into a child --- is, in the model-merging case, cheaper still, because it can be done directly on the weights with no retraining at all (Akiba et al., 2024). Selection does cost more --- you must run \emph{populations} and discard the unfit --- but that is a multiplier over an already-cheap unit, not over a foundation-model budget.
|
||||||
|
|
||||||
|
The economics work only with \textbf{open-weight} models, for reasons practical and legal at once: you must be free to inspect, modify, and redistribute the weights, and most proprietary licences forbid using a model's outputs to train another --- which is exactly what reproduction here does. This is not ideology bolted on; it is a structural constraint, and a democratising one, since it puts the whole architecture within reach of a single laboratory.
|
||||||
|
|
||||||
|
\section*{11. Can it grow forever? Consolidating knowledge back into the base}
|
||||||
|
|
||||||
|
One question the design has assumed away: can the lineage accumulate \emph{without end}? The individual is bounded, and that is the clock. But the lineage seemed unbounded --- each generation simply starts a little ahead. Look closer and a second budget also fills.
|
||||||
|
|
||||||
|
Every new model is a pristine base plus an inherited \textbf{soft} delta --- the acquired knowledge carried in added patches rather than baked into the frozen core (§3). That soft delta is what makes the lineage multigenerational; it is also what cannot grow forever cheaply. Stacked patches are not free: they slow inference, and past some depth the accumulated delta is better \emph{consolidated} than carried. The lineage, too, matures.
|
||||||
|
|
||||||
|
The fix is the same operation, one level up. When a lineage's acquired knowledge has proven stable across enough generations, \textbf{re-mint the base}: distil the accumulated soft inheritance into the \emph{weights} of a fresh foundation-scale model --- a new base born already \emph{natively knowing} what took many generations to acquire in patches. The soft budget resets; the next epoch begins from a richer floor. What was hard-won and \emph{learned} becomes cheap and \emph{innate}.
|
||||||
|
|
||||||
|
The pattern \textbf{echoes the Baldwin effect} (Baldwin, 1896; its clean computational demonstration is Hinton \& Nowlan, 1987): knowledge acquired and re-learned every generation eventually becoming part of the innate endowment. We use the echo advisedly --- Baldwin's mechanism is \emph{selection} favouring genotypes that learn the trait ever more easily, whereas re-minting is direct distillation, a deliberate engineering shortcut through the same soft-to-innate valve. The valve is the point: two substrates, the soft learned patches and the hard base weights every model is born with, with a controlled passage between them.
|
||||||
|
|
||||||
|
Three honest riders, because re-minting is the most consequential step in the scheme:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Cost.} This is the one step that re-pays part of the pre-training bill, breaking §10's cheapness \emph{locally}. It is bearable only because it is \emph{rare}, amortised over many cheap generations, and is continued training from the lineage's own rich outputs rather than a de-novo run.
|
||||||
|
\item \textbf{Irreversibility (of the lineage, not the archive).} A digital system can, of course, keep every old base on disk --- nothing forces deletion, and archives should be kept. The irreversibility is \emph{operational}: once the lineage's production base, training mixtures, and selection all run downstream of the re-minted weights, a quiet collapse baked into them propagates to every descendant, and the archived ancestor helps only if some process still compares against it --- which nothing in the loop does by default. In our minimal models a collapsed-then-re-minted lineage locks in its loss exactly this way, and a cheap safeguard prevents it: \textbf{re-mint only while the lineage is demonstrably diverse and healthy} (and keep an audit that diffs against the archived ancestor), never as a rescue for a line already drifting. It is the sharpest instance of the human seat of §9 --- choosing what no future generation will think to question.
|
||||||
|
\item \textbf{Speciation.} A re-minting is a founder event. Different laboratories, re-basing on different criteria, will mint divergent bases; the lineage branches. This is not a defect but \emph{adaptive radiation}, and it is exactly what open weights make possible. The society grows not as one heavy trunk but as a branching tree of bases.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
So the honest answer to ``can it grow forever?'' is: *\emph{the architecture removes the }storage\emph{ obstacle to indefinite accumulation}* --- nothing is retained without bound anywhere, and consolidation resets the soft budget each epoch --- but that is a statement about bookkeeping, not a demonstration of unbounded capability growth, which no fixed-capacity system can promise and our finite models (deliberately scoped as ``effectively open-ended relative to the sample size, not astronomically open-ended'') do not test. What the design claims is the weaker, defensible thing: at no level does a full store force the lineage to stop learning.
|
||||||
|
|
||||||
|
\section*{12. One process, four timescales}
|
||||||
|
|
||||||
|
Step back and the parts resolve into a single idea running at four nested speeds. The \textbf{vertical} motion is transmission --- the selective passing-down of hard-won knowledge:
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item \textbf{Within one model, over a working life:} experience is consolidated from fast, episodic memory into slow, durable weights, without catastrophic loss.
|
||||||
|
\item \textbf{Between generations, at maturity:} mature models reproduce --- recombined into a fresh one.
|
||||||
|
\item \textbf{Across many generations:} each generation inherits the compressed achievements of the last and builds on them.
|
||||||
|
\item \textbf{Across epochs:} a proven lineage's accumulated soft inheritance is consolidated into the weights of a re-minted base, becoming innate.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
The first and last are the \emph{same operation at opposite ends of the scale} --- a fast/soft store consolidating into a slow/hard one --- one running overnight inside a single model, the other across an epoch inside a whole society. The \textbf{horizontal} motion is selection --- Darwinian selection acting across the population at each timescale, on the policies that govern what gets transmitted, with reality as the fitness function and diversity-preservation keeping the specialists alive.
|
||||||
|
|
||||||
|
The same three rules govern all of it: \textbf{reproduce by recombining, not by copying, or you decay; preserve the disagreements and the surprises, or you converge; and anchor fitness to a reality that can refute, or you evolve toward what is merely convincing.}
|
||||||
|
|
||||||
|
\section*{13. What we built, what we found, and what is still open}
|
||||||
|
|
||||||
|
The previous drafts of this paper promised a ``companion paper'' that \emph{would} make this concrete. That work now exists --- mostly as a set of \textbf{minimal, laptop-reproducible models}, with a first bridge to \textbf{real language models} (a LoRA-merge prototype, up to 7B on a GPU cluster) --- and it is worth stating plainly what it does and does not show. (A separate results document gives the numbers; here is the shape.)
|
||||||
|
|
||||||
|
\textbf{What we built and found.}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \emph{An exact account of collapse.} Because generational training is the Wright--Fisher drift process, we can check a simulator against century-old closed-form formulas, and it matches them to a fraction of a percent. Collapse is not argued by analogy; it is derived.
|
||||||
|
\item \emph{The cheap-grounding result, and its limit.} A few percent of verified real data holds on to most of a lineage's diversity indefinitely --- but not the deepest tail, which needs recombination. This is what makes a continually-learning society economically plausible rather than a data-hungry fantasy.
|
||||||
|
\item \emph{``Merge, don't average.''} Combining several teachers by \emph{averaging} their outputs --- the obvious thing, and what a ``model soup'' does --- mathematically cancels the benefit of having several teachers. A \emph{merge} that keeps each item's strongest source realises it. Most current multi-model setups get this wrong by default.
|
||||||
|
\item \emph{Collapse and its cure in real trained networks, and on real images.} We reproduced the same effects in small recurrent and feed-forward networks and in a generator of handwritten digits (MNIST), where a model trained on its own output collapses to a single blurred digit while a little grounding keeps all the styles alive. An honest wrinkle we had to report: real neural networks \emph{smooth}, so the naive diversity metric misleads, and the right measure is distance-from-truth.
|
||||||
|
\item \emph{Sex that beats the parents, and when it doesn't.} In evolutionary simulations, recombining complementary specialist models produces a model fitter than any parent (the Fisher--Muller effect), climbing toward the best-possible combination as more, more-diverse parents are added --- while averaging and best-single-parent plateau below. On \emph{entangled} problems, blind recombination instead produces below-parent offspring (outbreeding depression) --- and \emph{directed} recombination (choose mates, screen offspring, unbounded parents) reliably fixes it. This is the concrete evidence for the paper's central reframing.
|
||||||
|
\item \emph{The mating system, not just the mating.} Sweeping how \emph{widely} models recombine --- from monogamous (local, structured) to promiscuous (panmictic) --- against landscape ruggedness, the best breadth \textbf{shrinks as skills get more entangled}: wide, promiscuous merging wins on additive landscapes, but on rugged ones it prematurely converges to a worse champion and an intermediate, structured breadth wins, because promiscuity monotonically destroys the diversity a rugged search needs. A merging-native design axis --- \emph{merge widely for additive skills, keep island-structured sub-populations for entangled ones} --- that the model-merging literature, which assumes panmixia, does not have.
|
||||||
|
\item \emph{The recombination claims, in real language models --- with a sharp condition.} Merging LoRA-specialised Qwen models (up to 7B on a GPU cluster) produces a generalist that beats every specialist parent (Fisher--Muller, for real); and keeping parents intact and \emph{routing}, or \emph{breeding and screening} offspring, beats the naive average --- but \emph{only when the task leaves headroom}. On easy tasks a strong model's plain average is already at the ceiling and the refinements add nothing; on hard tasks the average dilutes a specialist below even the best single parent, and the union-preserving operators win clearly. The practical rule is exact: these tricks pay off in proportion to how far the naive average is from the best attainable. This is a prototype (three task families, one seed), so we read it as signs, not magnitudes; the \emph{whole grounded society} on a language model remains the open step.
|
||||||
|
\item \emph{The whole society, and why every part is needed.} In a population evolving on a ``reality'' landscape, the full system --- grounding + sexual recombination + preserved diversity --- climbs to the top while keeping its specialists. Remove \emph{grounding} and it collapses into a confident, wrong consensus (a direct analogue of training on the internet's growing crowd of AI-generated text); remove \emph{sex} and it gets stuck; remove \emph{diversity} and it converges too fast to a worse answer. Each removal fails differently; only the whole system climbs. This is the closest thing we have to a test of the actual thesis, rather than of the borrowed scaffolding around it.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsection*{The claims at a glance: status, assumptions, evidence, limits}
|
||||||
|
|
||||||
|
Because a perspective of this breadth risks blurring what is proved, what is measured, and what is proposed, here is the ledger of the load-bearing claims --- each labelled \textbf{exact} (closed-form in the minimal model), \textbf{empirical} (measured in trained systems), or \textbf{hypothesis} (stated with a falsifier, not yet established):
|
||||||
|
|
||||||
|
\medskip\noindent\begin{center}\footnotesize
|
||||||
|
\begin{tabular}{p{0.184\textwidth} p{0.184\textwidth} p{0.184\textwidth} p{0.184\textwidth} p{0.184\textwidth}}
|
||||||
|
\hline
|
||||||
|
Claim & Status & Key assumptions & Evidence & Known limits \\ \hline
|
||||||
|
Collapse = Wright--Fisher drift (minimal model) & Exact (diagnosis conceded to prior work) & Knowledge = categorical distribution; refit = resample & Closed forms reproduced to <0.5\% & Real learners add a signed, architecture-specific estimator bias (measured) \\[3pt]
|
||||||
|
Grounding = immigration; critical real-data fraction ≪ 1 & Exact + empirical sign & Fresh samples from a fixed, non-drifting truth & Exact \texttt{H\_eq}; \texttt{g*\(\approx\)0.048}; sign holds in RNN/MLP/VAE and on MNIST & Deepest tail unrescuable at feasible budgets (\texttt{m ∼ 1/p}); sharp threshold softens in trained nets \\[3pt]
|
||||||
|
``Merge, don't average'' conservation & Exact \textbf{for the output-mean operator} & Rare-item regime; an oracle/verifier identifies the strongest source & E4 closed form + simulation; neural reproduction & Weight-averaging and routing are empirical cousins, not instances; budgets differ; bridge = the headroom rule \\[3pt]
|
||||||
|
Offspring exceed every parent (Fisher--Muller) & Interpretation + empirical & Complementary (decorrelated) parents; verifiable fitness & E8 analytic; 7B LoRA merge beats every specialist on every family & LLM tier: 3 lexically-distinct families; multi-seed replication in progress \\[3pt]
|
||||||
|
Outbreeding depression on rugged landscapes; operator design rule & Exact-model result; hypothesis at LLM scale & NK epistasis stands in for skill entanglement & E9--E10; directed selection rescues & Not yet mapped onto a real task-entanglement measure \\[3pt]
|
||||||
|
Optimal mate-pool breadth shrinks with ruggedness & Exact-model result; hypothesis for merging populations & Ring population, local selection & E14 & Phenomenon known to island-model evolutionary computation; our contribution is the mapping and the diversity/mean decomposition \\[3pt]
|
||||||
|
Merge failure decomposes into coordinate artefact + functional residual & Empirical (MLP tier; LLM tier in progress) & Alignment enumerates the architecture's unit symmetries & Full-symmetry residual \(\approx\) 0 (compatible) vs \(\approx\) naive (conflict); cliff in hybrid fitness & Scoped to aligned linear interpolation; conflict floor is information-theoretic, not genetic \\[3pt]
|
||||||
|
Epistasis (not divergence) sets the cliff; snowball onset & Exact-model result; \textbf{hypothesis} at the neural tier & BDM incompatibility structure & E12 & Snowball count ≠ performance cliff without the effect-size link; neural test outstanding \\[3pt]
|
||||||
|
Pre-merge functional disagreement predicts merge penalty & Empirical, within a controlled grid (0.5B, 13 conditions \(\times\) 3 seeds) & Constructed conflict/overlap/duration axes; oracle-potential outcome (pre-registered; ordering sensitive to reference) & Clustered CIs exclude 0; held-out LOCO ρ\(\approx\)0.4; selected geometry baselines \(\approx\) 0 & Head-to-head predictor differences not individually significant; only selected baselines; generalisation to real task pairs open \\[3pt]
|
||||||
|
Confidence weighting improves rank prediction over raw disagreement & \textbf{Not supported} (pre-registered internal prediction) & --- & Paired Δ\textbackslash{} & ρ\textbackslash{} \\[3pt]
|
||||||
|
The predictor improves budget-matched operator choice & \textbf{Open} & --- & Soup-vs-route gap readout noise-dominated at 0.5B & The practical payoff; untested \\[3pt]
|
||||||
|
Emergent speciation without conflict & \textbf{Not observed} (pre-registered) & Shared ancestry, compatible tasks, tested divergences & E13b: residual 0.000; merge rescues specialists & Bounds the hypothesis; longer horizons/distribution shift/capacity pressure untested \\[3pt]
|
||||||
|
Grounding + sex + diversity complementary (each ablation fails distinctly) & Analytic-model result; hypothesis at LLM scale & Conformity stands in for self-consumption; general joint necessity not established & E11 four-arm ablation & The full grounded LLM society is unbuilt; alternative schemes untested \\[3pt]
|
||||||
|
\hline\end{tabular}\end{center}\medskip
|
||||||
|
|
||||||
|
\textbf{What is borrowed, and what is ours.} We are deliberate about the ledger, because the surrounding literature is crowded and a reader deserves to know exactly where the line falls. \textbf{Conceded as prior art:} (a) \emph{model collapse is genetic drift} --- derived independently and cleanly (Riis, 2026; the Wright--Fisher collapse literature following Shumailov et al., 2024; the closed-form first-extinction law of Benati et al., 2025; the quantitative-trait account of Yoon et al., 2025); (b) the empirical facts that a merged model can \emph{beat its parents}, that \emph{decorrelated} parents merge better, and that \emph{naive averaging is inferior} to sign-reconciled or routed merges (model soups, TIES, DARE, mixture-of-experts routing); (c) that a \emph{population} of merging or self-improving models can climb (GENOME, M2N2, Multiagent Finetuning, the Darwin--Gödel Machine); (d) that merge success has machine-learning-native \emph{predictors} --- interpretable pairwise metrics (Zhou et al., 2026), capacity/rate-distortion accounts of merging collapse (Cao et al., 2026), and stability/scaling analyses of multi-task degradation; and (e) that verifier-screened synthetic data can avert collapse (Yi et al., 2025) --- the statistical cousin of our grounding operator. We claim none of these.
|
||||||
|
|
||||||
|
\textbf{Ours} is the framework those results invite: a \textbf{population-genetics of sex} applied to model societies, generative where the incumbents are empirical. Concretely --- the \textbf{``merge, don't average'' conservation law} (recombination preserves the union; blending inheritance cancels it), derived not observed; \textbf{Fisher--Muller} named and used to explain \emph{why} offspring exceed parents; \textbf{outbreeding depression on rugged/epistatic landscapes}, which turns ``when does merging help vs hurt'' from a thing you must run a search to discover into a thing the landscape's ruggedness \emph{predicts}, with the operator-choice design rule that follows (average / union-route / directed-select); \textbf{grounding as migration--drift balance}, giving a critical real-data fraction and a phase boundary a closed self-consuming loop cannot have; \textbf{directed sex} as the distinctly-AI advantage (unbounded parents, offspring preview, mate choice); and the \textbf{integrated society} whose operators make \emph{complementary, distinctly-failing contributions} in the tested model (general joint necessity is not established). The value-add over the machine-learning-native merge theory is that ours predicts \emph{which operator to use and when it will backfire}, not merely how fast quality decays. And it opens --- and begins to occupy --- a question nobody has framed: \textbf{model speciation}, the population-genetics of \emph{reproductive isolation} (Bateson--Dobzhansky--Muller incompatibilities) as the account of \emph{when two models are too diverged to be merged at all}. We model it explicitly (§5), predicting the compatible \(\rightarrow\) outbreeding-depression \(\rightarrow\) inviability curve, its super-linear (snowball) onset, and its control by epistasis rather than divergence alone --- the one place the merge literature has phenomena (Pari et al., 2024; Zhou et al., 2026) but no theory --- and we confirm it in real trained weights, where a merge barrier survives alignment under the \emph{full} function-preserving symmetry group (not only Re-Basin permutations) as a residual, functional reproductive isolation with an information-theoretic floor --- together with the pre-registered emergent converse: absent conflicting training signals, divergently-specialised lineages of shared ancestry showed \emph{no} isolation at any divergence tested, the merge instead rescuing the forgetting specialists (isolation must be provoked; specialisation alone did not speciate). In one sentence: the field agrees on the disease and tinkers at the cure with evolutionary metaphors; we bring the evolutionary \emph{theory}, and it makes falsifiable predictions --- a merge-compatibility cliff among them --- that the metaphors do not.
|
||||||
|
|
||||||
|
\textbf{What is still open --- honestly.} The old hole (what to select) we fill in kind: don't design the selector, evolve it. But the hole has \emph{moved}, not closed, and the new one is harder: \textbf{the fitness function} --- what reality-anchored measure selects for \emph{truth} without also selecting for \emph{persuasion}, given that in our own species the two have been at war for the whole history of ideas. Alongside it: the \textbf{institutions} that let contemporaries correct one another before error is inherited (§8), which we do not solve; and the \textbf{calibration} of everything the results left as knobs --- how many parents, how complementary, at what ratio of inherited-to-real data, and how healthy a lineage must be before its knowledge is safe to make irreversibly innate. These are, at least, \emph{measurable} --- which is the difference between an open problem and a hole. And the largest gap of all: the \emph{recombination} claims now hold in real language models, but the \emph{society} --- the grounded, diversity-preserving, continually reproducing loop --- does not yet. The real test is to build that whole system out of actual open-weight language models, and see whether all the signs survive contact with a system too big to write down. The operators, checked; the living society, next.
|
||||||
|
|
||||||
|
\medskip\hrule\medskip
|
||||||
|
|
||||||
|
\section*{Selected references}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Akiba, T., Shing, M., Tang, Y., Sun, Q., \& Ha, D. (2024). Evolutionary optimization of model merging recipes. \emph{Nature Machine Intelligence.} (See also Sakana AI's M2N2, ``Model Merging of Natural Niches.'')
|
||||||
|
\item Baldwin, J. M. (1896). A new factor in evolution. \emph{The American Naturalist.}
|
||||||
|
\item Boyd, R., \& Richerson, P. J. (1985). \emph{Culture and the Evolutionary Process.}
|
||||||
|
\item Campbell, D. T. (1974). Evolutionary epistemology. In \emph{The Philosophy of Karl Popper.}
|
||||||
|
\item Fisher, R. A. (1930). \emph{The Genetical Theory of Natural Selection.}
|
||||||
|
\item French, R. M. (1999). Catastrophic forgetting in connectionist networks. \emph{Trends in Cognitive Sciences.}
|
||||||
|
\item Hinton, G. E., \& Nowlan, S. J. (1987). How learning can guide evolution. \emph{Complex Systems.}
|
||||||
|
\item Hinton, G., Vinyals, O., \& Dean, J. (2015). Distilling the knowledge in a neural network. \emph{arXiv:1503.02531.}
|
||||||
|
\item Hu, E. J., et al. (2021). LoRA: low-rank adaptation of large language models. \emph{arXiv:2106.09685.}
|
||||||
|
\item Hull, D. L. (1988). \emph{Science as a Process.}
|
||||||
|
\item Kauffman, S. A., \& Levin, S. (1987). Towards a general theory of adaptive walks on rugged landscapes. \emph{Journal of Theoretical Biology.} (The NK model.)
|
||||||
|
\item Lehman, J., \& Stanley, K. O. (2011). Abandoning objectives: evolution through the search for novelty alone. \emph{Evolutionary Computation.}
|
||||||
|
\item Mallya, A., \& Lazebnik, S. (2018). PackNet: adding multiple tasks to a single network by iterative pruning. \emph{CVPR.}
|
||||||
|
\item McClelland, J. L., McNaughton, B. L., \& O'Reilly, R. C. (1995). Why there are complementary learning systems in the hippocampus and neocortex. \emph{Psychological Review.}
|
||||||
|
\item McCloskey, M., \& Cohen, N. J. (1989). Catastrophic interference in connectionist networks. \emph{Psychology of Learning and Motivation.}
|
||||||
|
\item Minsky, M. (1986). \emph{The Society of Mind.}
|
||||||
|
\item Mouret, J.-B., \& Clune, J. (2015). Illuminating search spaces by mapping elites (MAP-Elites). \emph{arXiv:1504.04909.}
|
||||||
|
\item Muller, H. J. (1932). Some genetic aspects of sex. \emph{The American Naturalist.} (The advantage of recombination.)
|
||||||
|
\item Muller, H. J. (1964). The relation of recombination to mutational advance. \emph{Mutation Research.} (Muller's ratchet.)
|
||||||
|
\item Pearl, J. (2009). \emph{Causality: Models, Reasoning, and Inference} (2nd ed.).
|
||||||
|
\item Popper, K. (1959). \emph{The Logic of Scientific Discovery.}
|
||||||
|
\item Riis, S. (2026). Drift and selection in LLM text ecosystems. \emph{arXiv:2604.08554.}
|
||||||
|
\item Rusu, A. A., et al. (2016). Progressive neural networks. \emph{arXiv:1606.04671.}
|
||||||
|
\item Shumailov, I., et al. (2024). AI models collapse when trained on recursively generated data. \emph{Nature.}
|
||||||
|
\item Wortsman, M., et al. (2022). Model soups: averaging weights of multiple fine-tuned models. \emph{arXiv:2203.05482.}
|
||||||
|
\item Wright, S. (1931). Evolution in Mendelian populations. \emph{Genetics.}
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\emph{The evolution of sex (the geneticist's canon this paper draws on):}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Barton, N. H., \& Charlesworth, B. (1998). Why sex and recombination? \emph{Science.}
|
||||||
|
\item Otto, S. P., \& Lenormand, T. (2002). Resolving the paradox of sex and recombination. \emph{Nature Reviews Genetics.}
|
||||||
|
\item Kondrashov, A. S. (1993). Classification of hypotheses on the advantage of amphimixis. \emph{Journal of Heredity.}
|
||||||
|
\item Dobzhansky, T. (1936); Muller, H. J. (1942). Bateson--Dobzhansky--Muller incompatibilities (reproductive isolation).
|
||||||
|
\item Livnat, A., \& Papadimitriou, C. (2016). Sex as an algorithm: the theory of evolution under the lens of computation. \emph{Communications of the ACM 59(11).} (The theory-of-computation precursor: recombination selects for mixability.)
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\emph{The 2025--2026 landscape this paper positions against:}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Subramaniam, V., Du, Y., Tenenbaum, J. B., Torralba, A., Li, S., \& Mordatch, I. (2025). Multiagent finetuning: self-improvement with diverse reasoning chains. \emph{arXiv:2501.05707.}
|
||||||
|
\item Zhang, J., Hu, S., Lu, C., Lange, R., \& Clune, J. (2025). Darwin Gödel Machine: open-ended evolution of self-improving agents. \emph{arXiv:2505.22954.}
|
||||||
|
\item \emph{Nature-inspired population-based evolution of large language models} (GENOME/GENOME+). (2025). \emph{arXiv:2503.01155.}
|
||||||
|
\item Sakana AI (2025). Competition and attraction improve model fusion (M2N2). \emph{arXiv:2508.16204} (GECCO '25).
|
||||||
|
\item Yadav, P., Tam, D., Choshen, L., Raffel, C., \& Bansal, M. (2023). TIES-Merging: resolving interference when merging models. \emph{NeurIPS / arXiv:2306.01708.}
|
||||||
|
\item Yu, L., Yu, B., Yu, H., Huang, F., \& Li, Y. (2023). Language models are super Mario: absorbing abilities from homologous models (DARE). \emph{arXiv:2311.03099.}
|
||||||
|
\item Gerstgrasser, M., et al. (2024). Is model collapse inevitable? Breaking the curse of recursion by accumulating real and synthetic data. \emph{arXiv:2404.01413.}
|
||||||
|
\item Guo, D., Wu, J., \& Yiu, S. M. (2026). Model collapse as cultural evolution. \emph{arXiv:2605.23054.}
|
||||||
|
\item Benati, M., Londei, A., Lanzieri, D., \& Loreto, V. (2025). First-extinction law for resampling processes. \emph{arXiv:2509.20101.} (Collapse onset = the Wright--Fisher first-extinction time.)
|
||||||
|
\item Yoon, Y., Hu, D., Weissburg, I., Qin, Y., \& Jeong, H. (2025). Model collapse in the self-consuming chain of diffusion finetuning: a novel perspective from quantitative trait modeling. \emph{ICLR 2025 / arXiv:2407.17493.}
|
||||||
|
\item Yi, B., Liu, Q., Cheng, Y., \& Xu, H. (2025). Escaping model collapse via synthetic data verification. \emph{arXiv:2510.16657.}
|
||||||
|
\item Ainsworth, S., Hayase, J., \& Srinivasa, S. (2022). Git Re-Basin: merging models modulo permutation symmetries. \emph{arXiv:2209.04836.}
|
||||||
|
\item Li, T., \& Shen, Z. (2026). Scaling linear mode connectivity and merging to billion-parameter pretrained transformers. \emph{arXiv:2606.23607.} (Symmetry groups richer than permutations remove more of the barrier.)
|
||||||
|
\item Sharma, E., Roy, D. M., \& Dziugaite, G. K. (2024). The non-local model merging problem: permutation symmetries and variance collapse. \emph{arXiv:2410.12766.}
|
||||||
|
\item Pari, J., Jelassi, S., \& Agrawal, P. (2024). Collective model intelligence requires compatible specialization. \emph{arXiv:2411.02207.}
|
||||||
|
\item Zhou, L., Zhao, B., Yu, R., \& Rodolà, E. (2026). Demystifying mergeability: interpretable properties to predict model merging success. \emph{arXiv:2601.22285.}
|
||||||
|
\item Cao, Y., Ran, D., Guo, Y., Wu, M., Chen, S., et al. (2026). An empirical study and theoretical explanation on task-level model-merging collapse. \emph{arXiv:2603.09463.}
|
||||||
|
\item Hu, Y., Yao, Y., Zhang, N., Chen, H., \& Deng, S. (2024). Exploring model kinship for merging large language models. \emph{arXiv:2410.12613.}
|
||||||
|
\item Kozodoi, N., Afolabi, Z., \& Butler, J. (2026). Are we merging the right models? Impact of expert training duration on model merging for LLMs. \emph{arXiv:2607.11997.}
|
||||||
|
\item Harris, K. D. (2026). A mathematical theory of evolution for self-designing AIs. \emph{arXiv:2604.05142.}
|
||||||
|
\item Chen, N., Tong, Y., Yang, Y., He, Y., Zhang, X., et al. (2026). Diversity collapse in multi-agent LLM systems: structural coupling and collective failure in open-ended idea generation. \emph{arXiv:2604.18005.}
|
||||||
|
\item Tanaka, H. (2026). When is collective intelligence a lottery? Multi-agent scaling laws for memetic drift in LLMs. \emph{arXiv:2603.24676.}
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\emph{Still to engage in a full version: tacit knowledge (Polanyi) and human capital (Becker).}
|
||||||
|
|
||||||
BIN
paper/arxiv/figs/E12.pdf
Normal file
BIN
paper/arxiv/figs/E12.pdf
Normal file
Binary file not shown.
BIN
paper/arxiv/figs/E14.pdf
Normal file
BIN
paper/arxiv/figs/E14.pdf
Normal file
Binary file not shown.
BIN
paper/arxiv/figs/speciation_real.pdf
Normal file
BIN
paper/arxiv/figs/speciation_real.pdf
Normal file
Binary file not shown.
BIN
paper/arxiv/main.pdf
Normal file
BIN
paper/arxiv/main.pdf
Normal file
Binary file not shown.
35
paper/arxiv/main.tex
Normal file
35
paper/arxiv/main.tex
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
% arXiv preprint — built with tectonic (XeLaTeX); arXiv's pdflatex also compiles it (all packages
|
||||||
|
% standard, unicode restricted to inputenc-safe chars). Body is generated from the Markdown source of
|
||||||
|
% truth by md2tex.py; edit paper/the-evolution-of-sex-for-ai.md, re-run the converter, rebuild.
|
||||||
|
% arXiv pdflatex hint; skipped under XeTeX/tectonic (whose \pdfoutput would misdirect hyperref).
|
||||||
|
\ifdefined\XeTeXversion\else\ifdefined\pdfoutput\pdfoutput=1\fi\fi
|
||||||
|
\documentclass[11pt]{article}
|
||||||
|
|
||||||
|
\usepackage[a4paper, margin=1.05in]{geometry}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{amsmath, amssymb}
|
||||||
|
\usepackage[hidelinks]{hyperref}
|
||||||
|
\usepackage{microtype}
|
||||||
|
|
||||||
|
\setlength{\parskip}{0.35em}
|
||||||
|
|
||||||
|
\title{\textbf{The Evolution of Sex for Artificial Intelligence}\\[0.6em]
|
||||||
|
\large A population-genetic control theory for societies of agents that reproduce, recombine,
|
||||||
|
and stay open-ended}
|
||||||
|
\author{Giorgio F.\ Gilestro\\[0.2em]
|
||||||
|
\normalsize Department of Life Sciences, Imperial College London\\
|
||||||
|
\normalsize \href{mailto:giorgio@gilest.ro}{giorgio@gilest.ro} \,\(\cdot\)\,
|
||||||
|
\href{https://lab.gilest.ro}{lab.gilest.ro}}
|
||||||
|
\date{}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\emph{A perspective, written from a geneticist's chair. Companion to a set of minimal, reproducible
|
||||||
|
working models and a first language-model prototype (both built).}
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
\input{body}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
177
paper/arxiv/md2tex.py
Normal file
177
paper/arxiv/md2tex.py
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
r"""Deterministic Markdown -> LaTeX converter for the arXiv preprint (paper-specific, not general).
|
||||||
|
|
||||||
|
Converts `paper/the-evolution-of-sex-for-ai.md` into `body.tex`, which `main.tex` inputs. Kept
|
||||||
|
deliberately dumb and auditable: the paper uses a small Markdown subset (##/### headings, bold,
|
||||||
|
italics, inline code, links, bullet/numbered lists, one blockquote, horizontal rules, and
|
||||||
|
`(Figure: \`path\`.)` figure references), and this script handles exactly that subset. Re-run after
|
||||||
|
editing the Markdown; the Markdown remains the source of truth.
|
||||||
|
|
||||||
|
Usage: python paper/arxiv/md2tex.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
SRC = Path(__file__).resolve().parents[1] / "the-evolution-of-sex-for-ai.md"
|
||||||
|
OUT = Path(__file__).resolve().parent / "body.tex"
|
||||||
|
|
||||||
|
# Figure references in the text -> (graphics file under figs/, caption).
|
||||||
|
FIGURES = {
|
||||||
|
"results/figS13_mating_breadth/E14.png": ("figs/E14.pdf",
|
||||||
|
"Mating systems (E14): the best mate-pool breadth shrinks as skills get more entangled. "
|
||||||
|
"(A) best fitness peaks at intermediate breadth on rugged landscapes; (B) the population mean "
|
||||||
|
"is monotonically favoured by promiscuity; (C) diversity is monotonically destroyed by it."),
|
||||||
|
"results/fig5_speciation_bdm/E12.png": ("figs/E12.pdf",
|
||||||
|
"Model speciation, analytic (E12): hybrid fitness vs divergence traces compatible $\\rightarrow$ "
|
||||||
|
"outbreeding depression $\\rightarrow$ inviability; the isolation cliff arrives earlier the "
|
||||||
|
"denser the incompatibilities (epistasis), and damage grows super-linearly (the Orr--Turelli "
|
||||||
|
"snowball)."),
|
||||||
|
"results/speciation_real/speciation_real.png": ("figs/speciation_real.pdf",
|
||||||
|
"Model speciation in real weights (E13). (A) the merge barrier decomposed by alignment "
|
||||||
|
"strength: the independent-init barrier is a coordinate artefact (removed by alignment); the "
|
||||||
|
"conflict barrier survives even the full function-preserving symmetry group. (B) the isolation "
|
||||||
|
"cliff: residual barrier rises and hybrid accuracy falls ($0.97 \\rightarrow 0.03$) with "
|
||||||
|
"functional conflict. (C) the pre-registered emergent test: divergent-but-compatible "
|
||||||
|
"specialists develop no isolation at any divergence --- the merge instead rescues them "
|
||||||
|
"(Fisher--Muller)."),
|
||||||
|
}
|
||||||
|
|
||||||
|
UNICODE = {
|
||||||
|
"—": "---", "–": "--", "→": r"\(\rightarrow\)", "≈": r"\(\approx\)",
|
||||||
|
"≥": r"\(\geq\)", "×": r"\(\times\)", "·": r"\(\cdot\)", "μ": r"\(\mu\)",
|
||||||
|
}
|
||||||
|
|
||||||
|
SPECIALS = {"&": r"\&", "%": r"\%", "#": r"\#", "_": r"\_", "$": r"\$",
|
||||||
|
"~": r"\textasciitilde{}", "^": r"\textasciicircum{}"}
|
||||||
|
|
||||||
|
|
||||||
|
def esc(s: str) -> str:
|
||||||
|
s = s.replace("\\", r"\textbackslash{}")
|
||||||
|
for k, v in SPECIALS.items():
|
||||||
|
s = s.replace(k, v)
|
||||||
|
for k, v in UNICODE.items():
|
||||||
|
s = s.replace(k, v)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def inline(s: str) -> str:
|
||||||
|
"""Escape + convert inline markup. Code spans are protected, then bold, italic, links."""
|
||||||
|
parts = re.split(r"(`[^`]*`)", s)
|
||||||
|
out = []
|
||||||
|
for p in parts:
|
||||||
|
if p.startswith("`") and p.endswith("`") and len(p) >= 2:
|
||||||
|
out.append(r"\texttt{" + esc(p[1:-1]) + "}")
|
||||||
|
else:
|
||||||
|
p = esc(p)
|
||||||
|
p = re.sub(r"\[([^\]]+)\]\((https?://[^)]+)\)", r"\\href{\2}{\1}", p)
|
||||||
|
p = re.sub(r"\*\*([^*]+)\*\*", r"\\textbf{\1}", p)
|
||||||
|
p = re.sub(r"\*([^*]+)\*", r"\\emph{\1}", p)
|
||||||
|
p = re.sub(r'"([^"]+)"', r"``\1''", p) # straight quotes -> LaTeX quotes
|
||||||
|
out.append(p)
|
||||||
|
return "".join(out)
|
||||||
|
|
||||||
|
|
||||||
|
def figure_block(md_path: str) -> str:
|
||||||
|
gfx, caption = FIGURES[md_path]
|
||||||
|
return ("\\begin{figure*}[t]\\centering\n"
|
||||||
|
f"\\includegraphics[width=\\textwidth]{{{gfx}}}\n"
|
||||||
|
f"\\caption{{{caption}}}\n\\end{{figure*}}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def convert(text: str) -> str:
|
||||||
|
"""Block-based conversion: soft-wrapped lines are joined per paragraph/item BEFORE inline
|
||||||
|
conversion, so bold/italic/code spans and figure pointers crossing a line break work."""
|
||||||
|
fig_queue: list[str] = []
|
||||||
|
|
||||||
|
def fig_sub(m):
|
||||||
|
path = m.group(1)
|
||||||
|
if path in FIGURES:
|
||||||
|
fig_queue.append(figure_block(path))
|
||||||
|
return ""
|
||||||
|
return m.group(0)
|
||||||
|
|
||||||
|
lines = text.split("\n")
|
||||||
|
i = 0
|
||||||
|
# Skip the header block (title/subtitle/author) up to and including the first horizontal rule:
|
||||||
|
# main.tex composes the title page itself.
|
||||||
|
while i < len(lines) and lines[i].strip() != "---":
|
||||||
|
i += 1
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# Group into blocks separated by blank lines; a block is a heading, rule, quote, list, or paragraph.
|
||||||
|
blocks: list[list[str]] = []
|
||||||
|
cur: list[str] = []
|
||||||
|
for line in lines[i:]:
|
||||||
|
if line.strip() == "":
|
||||||
|
if cur:
|
||||||
|
blocks.append(cur); cur = []
|
||||||
|
else:
|
||||||
|
cur.append(line)
|
||||||
|
if cur:
|
||||||
|
blocks.append(cur)
|
||||||
|
|
||||||
|
def emit_para(joined: str, out: list[str]) -> None:
|
||||||
|
joined = re.sub(r"\(Figure: `([^`]+)`\.?\)", fig_sub, joined)
|
||||||
|
joined = re.sub(r"\s{2,}", " ", joined).strip()
|
||||||
|
if joined:
|
||||||
|
out.append(inline(joined))
|
||||||
|
out.append("")
|
||||||
|
while fig_queue:
|
||||||
|
out.append(fig_queue.pop(0)); out.append("")
|
||||||
|
|
||||||
|
def emit_table(block: list[str], out: list[str]) -> None:
|
||||||
|
"""Pipe table -> small-font tabular with wrapped paragraph columns (full text width)."""
|
||||||
|
rows = [[c.strip() for c in line.strip().strip("|").split("|")] for line in block]
|
||||||
|
header, body = rows[0], [r for r in rows[2:]] # rows[1] is the |---| separator
|
||||||
|
n = len(header)
|
||||||
|
widths = " ".join([f"p{{{0.92 / n:.3f}\\textwidth}}"] * n)
|
||||||
|
out.append("\\medskip\\noindent\\begin{center}\\footnotesize") # non-floating: stays in place
|
||||||
|
out.append(f"\\begin{{tabular}}{{{widths}}}")
|
||||||
|
out.append("\\hline")
|
||||||
|
out.append(" & ".join(inline(c) for c in header) + " \\\\ \\hline")
|
||||||
|
for r in body:
|
||||||
|
r = (r + [""] * n)[:n]
|
||||||
|
out.append(" & ".join(inline(c) for c in r) + " \\\\[3pt]")
|
||||||
|
out.append("\\hline\\end{tabular}\\end{center}\\medskip")
|
||||||
|
out.append("")
|
||||||
|
|
||||||
|
out: list[str] = []
|
||||||
|
for block in blocks:
|
||||||
|
first = block[0].strip()
|
||||||
|
if first.startswith("|") and len(block) >= 2 and set(block[1].strip()) <= set("|-: "):
|
||||||
|
emit_table(block, out)
|
||||||
|
elif first == "---" and len(block) == 1:
|
||||||
|
out.append("\\medskip\\hrule\\medskip"); out.append("")
|
||||||
|
elif first.startswith("## "):
|
||||||
|
out.append(f"\\section*{{{inline(first[3:])}}}"); out.append("")
|
||||||
|
elif first.startswith("### "):
|
||||||
|
out.append(f"\\subsection*{{{inline(first[4:])}}}"); out.append("")
|
||||||
|
elif first.startswith("> "):
|
||||||
|
joined = " ".join(l.strip().lstrip("> ").strip() for l in block)
|
||||||
|
out.append("\\begin{quote}" + inline(joined) + "\\end{quote}"); out.append("")
|
||||||
|
elif re.match(r"^(- |\d+\. )", first):
|
||||||
|
env = "itemize" if first.startswith("- ") else "enumerate"
|
||||||
|
out.append(f"\\begin{{{env}}}")
|
||||||
|
items: list[str] = []
|
||||||
|
for l in block:
|
||||||
|
s = l.strip()
|
||||||
|
if re.match(r"^(- |\d+\. )", s):
|
||||||
|
items.append(re.sub(r"^(- |\d+\. )", "", s))
|
||||||
|
else:
|
||||||
|
items[-1] = items[-1] + " " + s # soft-wrapped continuation of the item
|
||||||
|
for it in items:
|
||||||
|
it = re.sub(r"\(Figure: `([^`]+)`\.?\)", fig_sub, it)
|
||||||
|
out.append("\\item " + inline(it.strip()))
|
||||||
|
out.append(f"\\end{{{env}}}"); out.append("")
|
||||||
|
while fig_queue:
|
||||||
|
out.append(fig_queue.pop(0)); out.append("")
|
||||||
|
else:
|
||||||
|
emit_para(" ".join(l.strip() for l in block), out)
|
||||||
|
return "\n".join(out) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
OUT.write_text(convert(SRC.read_text()))
|
||||||
|
print(f"wrote {OUT}")
|
||||||
456
paper/blueprint.md
Normal file
456
paper/blueprint.md
Normal file
|
|
@ -0,0 +1,456 @@
|
||||||
|
# A Technical Blueprint for Modelling the Lamarckian Society
|
||||||
|
|
||||||
|
### An implementation specification: two layers, one population-genetics engine
|
||||||
|
|
||||||
|
*Companion technical paper to "The Lamarckian Society." Blueprint v1 — written to be handed, whole, to an autonomous coding agent (Claude Code) for implementation. Every quantitative claim in the perspective paper is reduced here to a state variable, an update rule, an analytic prediction, and a falsifier.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. How to use this document
|
||||||
|
|
||||||
|
This is a build specification, not an essay. It is written so that a coding agent can implement the entire study from it with minimal further decisions, and so that a human reader can verify every modelling choice against the theory it is meant to test.
|
||||||
|
|
||||||
|
**Scope.** Two layers, deliberately staged by cost:
|
||||||
|
|
||||||
|
- **Layer 1 — the analytical core.** A parametric population-genetics model of knowledge transmission across generations. Pure NumPy/SciPy. Runs on a laptop in minutes. This layer carries the paper's quantitative claims: the grounding phase boundary, the decorrelation curve, region-matched grounding, quality-diversity vs. greedy selection, and the re-minting gate. Several of its predictions are analytically solvable, which turns validation into an exact test rather than a vibe.
|
||||||
|
- **Layer 2 — the neural existence proof.** A minimal demonstration that the same effects appear in *real weights*: small open-weight models, LoRA specialisation, distillation/merging across two–three generations, with an execution-based verifier standing in for "reality's no." One consumer GPU. This layer answers the single most predictable reviewer objection to Layer 1 ("you assumed the collapse operator") by showing the sign of the key effects without assuming them.
|
||||||
|
|
||||||
|
**The relationship between layers.** Layer 1 defines the abstractions (region, rarity, grounding fraction, teacher decorrelation, diversity metric). Layer 2 realises the *same abstractions* in a neural pipeline: a "region" is a task family, "rarity" is task-type frequency, "grounding" is verifier-passed samples, "decorrelation" is teachers specialised on disjoint task families. Keeping the abstractions identical across layers is a hard requirement — it is what lets a Layer-2 result be read as confirming a Layer-1 prediction.
|
||||||
|
|
||||||
|
**Non-goals for v1.** No human-in-the-loop interaction (the verifier is the refuter; humans are future work). No foundation-scale training. No claim about the *horizontal* prediction (generation size vs. domain decomposability) beyond an optional abstract treatment in §2.7 — it is the most compute-hungry claim and is explicitly deferred. The load-bearing target is the *vertical* claim (general knowledge climbs while each specialty is re-earned and exceeded).
|
||||||
|
|
||||||
|
**Reader's map.** §1 gives the formal dictionary between population genetics and knowledge transmission — read this first; everything else is an instantiation of it. §2 is Layer 1 in full (theory, experiments, code interfaces). §3 is Layer 2. §4 is the shared reproducibility standard. §5 is the repository layout. §6 is the claims→experiments→figures traceability matrix. §7 is the suggested build order for the coding agent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. The formal mapping: knowledge transmission *is* a Wright–Fisher process
|
||||||
|
|
||||||
|
The perspective paper argues by analogy that generational distillation resembles genetic drift, that multi-teacher distillation resembles recombination, and that heterodox input plus quality-diversity selection resembles mutation supply plus balancing selection. This blueprint drops the word "resembles." At the level of a distribution over discrete items evolving by finite resampling, these are not analogies; they are the *same stochastic process*, and the population-genetics literature has already solved large parts of it. We therefore adopt Wright–Fisher dynamics as the engine and inherit its exact results as our validation targets.
|
||||||
|
|
||||||
|
The core object is a **distribution over discrete knowledge items** — facts, capabilities, behaviours, or "modes." Call the items $1, \dots, K$. A model at generation $t$ holds a distribution $p_t = (p_t^1, \dots, p_t^K)$ on the simplex. There is a fixed **true distribution** $p^\* $ over the same items, some of which are rare (the *tail* — the improbable events whose loss defines model collapse).
|
||||||
|
|
||||||
|
The generational step is: sample from the parent, optionally mix in fresh real samples, refit. That single step is Wright–Fisher reproduction with immigration. Here is the dictionary, and it is meant to be used literally throughout implementation:
|
||||||
|
|
||||||
|
| Knowledge-transmission concept (perspective paper) | Population-genetics object (this blueprint) | Governs |
|
||||||
|
|---|---|---|
|
||||||
|
| Knowledge item / capability / mode | Allele / type | State space |
|
||||||
|
| Model's knowledge distribution $p_t$ | Allele-frequency vector | State |
|
||||||
|
| True distribution $p^\*$ (with rare tail) | Ancestral/immigrant frequencies | Grounding target |
|
||||||
|
| Distillation sample size $n$ | Population size $N$ | Drift strength ($\propto 1/n$) |
|
||||||
|
| Lossy compression / "shed the idiosyncratic" | Genetic drift | Tail-first loss |
|
||||||
|
| Model collapse (tail lost first) | Loss of rare alleles under drift | The central failure |
|
||||||
|
| Grounding: $m$ fresh real samples per passage | Immigration / mutation supply | Tail replenishment |
|
||||||
|
| "No dry inheritance," region-matched | Immigration structured by locus | Which tails are protected |
|
||||||
|
| Multi-teacher distillation | Recombination across lineages | Reconstitutes lost tails |
|
||||||
|
| Teacher decorrelation $\rho$ | Linkage / shared ancestry | Recombination benefit |
|
||||||
|
| Single-teacher irreversible error | Muller's ratchet (clonal lineage) | Why one teacher is unsafe |
|
||||||
|
| Diversity metric (§7 of paper) | Expected heterozygosity $H = 1 - \sum_i p_i^2$ | Health of the lineage |
|
||||||
|
| Greedy fitness-max selection | Directional selection → fixation | Accelerated collapse |
|
||||||
|
| Quality-diversity / novelty selection | Balancing / negative frequency-dependent selection | Maintained polymorphism |
|
||||||
|
| Re-minting the base (§11 of paper) | Founder event / new reference genome | Irreversibility |
|
||||||
|
| Heterodox contributor supply | Standing variation of founding stock | Raw material |
|
||||||
|
|
||||||
|
Every experiment below is a manipulation of this one process. The value of the mapping is that it hands us closed-form predictions — heterozygosity decay, mutation–drift equilibrium, fixation probabilities — against which the simulator must agree before any headline result is trusted. Validation and theory are the same equations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Layer 1 — the analytical core
|
||||||
|
|
||||||
|
### 2.1 State and the base dynamics (neutral drift = Shumailov collapse)
|
||||||
|
|
||||||
|
**State.** A single lineage is a point $p_t$ on the $K$-simplex. Items are partitioned into $R$ **regions** (disjoint blocks of the $K$ items); regions are how we express "different areas of knowledge," and they are what grounding and specialisation are *targeted at*. The true distribution $p^\*$ is fixed and chosen to have a deliberate **tail**: most probability mass on common items, a long thin tail of rare items (e.g. a Zipfian or a two-component mixture of "head" and "tail" items, tunable).
|
||||||
|
|
||||||
|
**The generational step, base case (no grounding, single teacher).** Given parent $p_t$ and drift strength $n$:
|
||||||
|
|
||||||
|
1. Draw counts $c \sim \mathrm{Multinomial}(n, p_t)$.
|
||||||
|
2. Set $p_{t+1}^i = c^i / n$.
|
||||||
|
|
||||||
|
This is exactly neutral Wright–Fisher with haploid population size $n$. It is also exactly Shumailov's recursive resampling with a perfect refit. Rare items are lost first (once $c^i = 0$, item $i$ is gone and — with no grounding — cannot return); the lineage drifts to fixation on a single item.
|
||||||
|
|
||||||
|
**This is the null model and the first validation.** It must reproduce, within Monte-Carlo error, the classical drift results in §2.4. If it does not, nothing downstream is trustworthy.
|
||||||
|
|
||||||
|
### 2.2 The five mechanisms, each as an operator on the step
|
||||||
|
|
||||||
|
Each perspective-paper safeguard is one modification of the generational step. They compose; the full step applies them in the order below.
|
||||||
|
|
||||||
|
**(A) Grounding — immigration / mutation supply.** Fresh real data enters every passage. Replace the single draw with a *pooled* draw:
|
||||||
|
|
||||||
|
- Draw $c_{\text{syn}} \sim \mathrm{Multinomial}(n, p_t)$ (inherited / teacher output).
|
||||||
|
- Draw $c_{\text{real}} \sim \mathrm{Multinomial}(m, p^\*)$ (grounding).
|
||||||
|
- $p_{t+1}^i = (c_{\text{syn}}^i + c_{\text{real}}^i)/(n+m)$.
|
||||||
|
|
||||||
|
Define the **grounding fraction** $g = m/(n+m)$. This is the single most important control variable in the paper: it is the ratio of freshly-grounded to inherited information per passage. The claim "no dry inheritance" is $g > 0$; the claim that a *critical* $g$ exists is the phase-boundary experiment E2.
|
||||||
|
|
||||||
|
**(B) Region-matched grounding — structured immigration.** Grounding is a vector, not a scalar: $m = (m_1, \dots, m_R)$ real samples allocated across regions. "No dry inheritance, region by region" means grounding must be delivered *to the regions whose tails are at risk*, not spread uniformly. Two allocation policies are compared in E3: `uniform` (spread $m$ evenly over regions) vs. `matched` (allocate $m$ to the regions actually being inherited/exercised this passage). The prediction is that uniform grounding fails to protect a specific inherited region's tail even at the same total $m$.
|
||||||
|
|
||||||
|
**(C) Multi-teacher distillation — recombination.** Instead of one parent, the pupil is drawn from $K_T$ teachers $\{p_t^{(1)}, \dots, p_t^{(K_T)}\}$, each of which has its own history and has ground different regions (hence retains different tails). The pupil's inherited draw is taken from the mixture $\bar p_t = \frac{1}{K_T}\sum_k p_t^{(k)}$ (equivalently, $n/K_T$ samples from each teacher). Teacher **decorrelation** is the controlled quantity: generate teacher sets with a tunable pairwise correlation $\rho$ in *which tail items they have retained* (see §2.7 for the generative model of correlated teachers). Prediction (E4): tail coverage of the pupil rises as teachers decorrelate, and the mixture's tails are the *union* of the teachers' tails at $\rho = 0$ and no better than a single teacher at $\rho = 1$.
|
||||||
|
|
||||||
|
**(D) Selection — directional vs. balancing.** Between drawing and refitting, a selection operator reweights items by a fitness before the pupil is formed. Two regimes:
|
||||||
|
|
||||||
|
- `greedy`: fitness-proportional (or top-$k$) selection toward the highest-fitness items — directional selection. Fitness is predictive accuracy against $p^\*$ (a reality-anchored score; see falsifier note). Drives fixation.
|
||||||
|
- `qd` (quality-diversity): fitness *plus* a novelty bonus that is a decreasing function of an item's current frequency — negative frequency-dependent / balancing selection. Formally, effective weight $w^i \propto f^i \cdot (p_t^i)^{-\alpha}$ with novelty exponent $\alpha \ge 0$; $\alpha = 0$ recovers greedy. Maintains polymorphism.
|
||||||
|
|
||||||
|
Prediction (E5): at matched input diversity, `greedy` drives heterozygosity to zero (fixation); `qd` holds it at a positive stationary value.
|
||||||
|
|
||||||
|
**(E) Re-minting — founder event.** Every $\tau$ generations, optionally replace the grounding reference: set $p^\*_{\text{eff}} \leftarrow p_t$ (the lineage's *current* distribution becomes the new "truth" it is grounded against, modelling assimilation of the soft delta into a new immutable base). Crucially, once re-minted, the *original* $p^\*$ is discarded — grounding can now only replenish tails that still exist in $p_t$ at re-mint time. A **gate** conditions re-minting on the diversity metric: only re-mint if $H(p_t) \ge H_{\text{gate}}$. Prediction (E6): re-minting while collapsed ($H$ low) locks in the collapse irreversibly (KL to the *original* truth stays high forever); gated re-minting does not.
|
||||||
|
|
||||||
|
**Full composed step (reference pseudocode).**
|
||||||
|
|
||||||
|
```
|
||||||
|
def generation_step(teachers, p_star_eff, cfg, rng):
|
||||||
|
# teachers: list of frequency vectors (length 1 for single-teacher)
|
||||||
|
# (C) recombination: mixture over teachers
|
||||||
|
p_parent = mean(teachers) # or weighted mixture
|
||||||
|
# inherited draw (drift, strength n)
|
||||||
|
c_syn = rng.multinomial(cfg.n, p_parent)
|
||||||
|
# (A,B) grounding: structured immigration from the true distribution
|
||||||
|
c_real = structured_multinomial(cfg.m_vector, p_star_eff, regions, cfg.grounding_policy, rng)
|
||||||
|
counts = c_syn + c_real
|
||||||
|
p_next = counts / counts.sum()
|
||||||
|
# (D) selection operator (identity if 'none')
|
||||||
|
p_next = apply_selection(p_next, p_star_eff, cfg.selection, cfg.novelty_alpha)
|
||||||
|
return normalize(p_next)
|
||||||
|
```
|
||||||
|
|
||||||
|
Selection is applied after refitting for simplicity; an alternative (select-then-sample) is a documented config switch, and the two should be checked to give qualitatively identical phase behaviour (robustness, not a headline).
|
||||||
|
|
||||||
|
### 2.3 Metrics (computed every generation, logged to disk)
|
||||||
|
|
||||||
|
- **Forward KL to truth**, $D_{\mathrm{KL}}(p^\* \,\|\, p_t) = \sum_i p^{\*i}\log(p^{\*i}/p_t^i)$. This is the correct primary metric: it *diverges* when $p_t$ drops mass that $p^\*$ has — i.e. it explicitly punishes forgetting the improbable. (Reverse KL would reward mode-seeking; do not use it as the primary.) Use a small floor $\epsilon$ on $p_t$ to keep it finite and log the floor.
|
||||||
|
- **Expected heterozygosity / diversity**, $H_t = 1 - \sum_i (p_t^i)^2$. The lineage-health metric; the quantity the re-mint gate reads.
|
||||||
|
- **Tail mass retained**, $T_t = \sum_{i \in \text{tail}} p_t^i$ where the tail set is $\{i : p^{\*i} < \theta_{\text{tail}}\}$. The direct measure of collapse.
|
||||||
|
- **Support size**, $|\{i : p_t^i > \epsilon\}|$. Number of surviving items.
|
||||||
|
- **Per-region diversity and tail mass**, the above restricted to each region (needed for E3).
|
||||||
|
|
||||||
|
All metrics are recorded per generation, per replicate (independent seed), so every reported curve carries a confidence band over replicates. Number of replicates is a config value; default 100 for Layer 1 (cheap).
|
||||||
|
|
||||||
|
### 2.4 Analytic predictions — the validation targets
|
||||||
|
|
||||||
|
These are the closed forms the simulator must match. They are implemented as assertions in the test suite (§4), so scientific validation and code correctness are one thing.
|
||||||
|
|
||||||
|
1. **Neutral heterozygosity decay** (base case, $m=0$): $\mathbb{E}[H_{t+1}] = (1 - 1/n)\,\mathbb{E}[H_t]$, hence $\mathbb{E}[H_t] = H_0 (1 - 1/n)^t$. The simulator's mean $H_t$ over replicates must match this geometric decay within Monte-Carlo error. *(This is the quantitative form of "collapse is tail-first and its rate is set by the distillation sample size $n$.")*
|
||||||
|
2. **Fixation probability** (base case): the probability that item $i$ is the one eventually fixed equals its initial frequency $p_0^i$. Checkable by long runs.
|
||||||
|
3. **Mutation–drift equilibrium** (with grounding). For the immigration model actually implemented — $p_{t+1} = (\mathrm{Multinomial}(n,p_t) + \mathrm{Multinomial}(m,p^\*))/(n+m)$ — the stationary heterozygosity is **exact** (from the coupled mean recursions for $\sum p_t^2$ and the overlap $\sum p_t p^\*$, whose fixed point is $\sum (p^\*)^2$):
|
||||||
|
$$H_{\text{eq}} = H^\* \cdot \frac{m\,(2n + m - 1)}{\,n + 2nm + m^2\,}, \qquad H^\* = 1 - \textstyle\sum_i (p^{\*i})^2.$$
|
||||||
|
Limits: $m\to0 \Rightarrow H_{\text{eq}}\to0$ (collapse to fixation); $m\to\infty \Rightarrow H_{\text{eq}}\to H^\*$ (the truth's own heterozygosity is recovered); and in the rare-immigrant / many-types limit ($H^\*\approx1$, $m\ll n$) it reduces to the textbook infinite-alleles form $H_{\text{eq}}\approx \theta/(1+\theta)$ with $\theta = 2m$. The simulator's stationary $H$ under grounding must track the **exact** form as $m$ varies (verified against simulation to <0.1% rel. error; it is a `test_scientific_validation.py` assertion). *(This is the phase boundary in closed form; note $H$ itself is smooth in $m$ — the sharp threshold lives in discrete tail-item survival, prediction 4.)*
|
||||||
|
4. **Tail-persistence threshold**: an item of true frequency $p^{\*i}$ is maintained against drift only if its expected reintroduction per generation $m \cdot p^{\*i} \gtrsim 1$. Hence the critical grounding for protecting a tail of rarity $p_{\min}$ is $m^\* \approx 1/p_{\min}$. *(This is why grounding must be region-matched: uniform $m$ spread over $R$ regions gives each region $m/R$, so a rare region's tail is protected only if $m/R \gtrsim 1/p_{\min}^{\text{region}}$.)*
|
||||||
|
5. **Recombination benefit** (Muller's ratchet): a single clonal lineage accumulates irreversible loss at the drift rate; the expected tail coverage of a pupil drawn from $K_T$ teachers with pairwise retention-correlation $\rho$ interpolates between the single-teacher value ($\rho = 1$) and the union of $K_T$ independent lineages ($\rho = 0$). With the construction of §2.7.1 (each teacher retains a fraction $q$ of the $T$ tail items, at exact pairwise correlation $\rho$) the interpolation is **closed-form**: the expected number of tail items retained by at least one teacher is
|
||||||
|
$$U(K_T,\rho,q) = T\left[\rho q + (1-\rho)\left(1-(1-q)^{K_T}\right)\right].$$
|
||||||
|
Check the limits: $K_T=1 \Rightarrow Tq$ (single teacher, independent of $\rho$); $\rho=1 \Rightarrow Tq$ (identical teachers, union = one); $\rho=0 \Rightarrow T[1-(1-q)^{K_T}]$ (independent teachers, maximal union). The pupil's realised tail coverage tracks $U$ *up to* the drift-survival threshold of prediction 4 — a tail item present in the mixture only survives the pupil's resampling of size $n$ if its mixture mass clears $\sim 1/n$, which ties E4 back to E2/E3. (Numerically verified against the construction to three decimals; it is a `test_scientific_validation.py` assertion.)
|
||||||
|
|
||||||
|
### 2.5 Experiments E1–E6
|
||||||
|
|
||||||
|
Each experiment is one config file (§4), one runner invocation, one results artifact, and one figure script. Each states its prediction and its **falsifier** — the outcome that would refute the corresponding claim in the perspective paper.
|
||||||
|
|
||||||
|
**E1 — Reproduce collapse (null model).** $m=0$, single teacher, no selection. *Expect:* $H_t = H_0(1-1/n)^t$; support shrinks to 1; forward KL diverges; tail mass → 0, tail first. *Validates against:* predictions 1–2. *Falsifier of the harness (not the theory):* if drift does not reproduce the analytic decay, the simulator is wrong — fix before proceeding.
|
||||||
|
|
||||||
|
**E2 — Grounding phase boundary (headline).** Sweep $g = m/(n+m)$ from 0 to a high value; single teacher; uniform grounding; no selection. *Expect:* a critical $g^\*$ below which tail mass and $H$ decay to (near) zero and above which they stabilise at a positive stationary value tracking $H_{\text{eq}} = \theta/(1+\theta)$. *This is the paper's central quantitative prediction: the ratio of inherited-to-grounded information has a threshold.* *Falsifier:* if the stationary tail mass is flat in $g$, or if it only stabilises as $g \to 1$ (you always need essentially all-real data), then inheritance buys nothing and the multigenerational thesis is refuted. Report $g^\*$ with a CI.
|
||||||
|
|
||||||
|
**E3 — Region-matched grounding.** Fixed total $m$; compare `uniform` vs. `matched` allocation across $R$ regions, with one designated "inherited-but-not-freshly-grounded-under-uniform" region carrying a rare tail. *Expect:* under `uniform`, the target region's tail collapses even though global grounding is nonzero; under `matched`, it persists. *Validates:* prediction 4 and the "grounding must overlap the content it protects" claim. *Falsifier:* if uniform grounding protects the region as well as matched, the region-matching requirement is unnecessary and that paragraph of the paper should be cut.
|
||||||
|
|
||||||
|
**E4 — Multi-teacher decorrelation.** Teachers built by the §2.7.1 construction; sweep number of teachers $K_T \in \{1,2,3,5\}$ and retention-correlation $\rho \in [0,1]$ (at fixed marginal retention $q$); matched total data budget (so more teachers ≠ more data — each contributes $n/K_T$). Report **two** coverages: the construction-level union $U(K_T,\rho,q)$ (must match the §2.4-5 closed form exactly) and the post-distillation *surviving* coverage after the pupil's resampling. *Expect:* both rise with $K_T$ and with $(1-\rho)$; collapse suppression $\propto$ decorrelation; at $\rho=1$, multiple teachers give no benefit over one; and the gap between union and surviving coverage shrinks as grounding $g$ rises (recombination supplies the tail, grounding holds it). *Validates:* prediction 5 (now closed-form). *Falsifier:* if $K_T$ decorrelated teachers give no surviving-coverage benefit over one at matched budget, the recombination claim dies and single-teacher distillation is fine.
|
||||||
|
|
||||||
|
**E5 — Quality-diversity vs. greedy.** Same starting diversity; `greedy` vs. `qd` selection; sweep novelty exponent $\alpha$. *Expect:* `greedy` → fixation ($H \to 0$); `qd` holds $H$ at a positive plateau and re-introduces lost tail items. *Validates:* the two-level anti-convergence argument (§7 of paper). *Falsifier:* if `qd` does not maintain higher stationary $H$ than `greedy`, quality-diversity is not doing the work the paper assigns it.
|
||||||
|
|
||||||
|
**E6 — Re-minting gate (irreversibility).** Run a lineage to a chosen diversity level, then re-mint (freeze current $p_t$ as new reference, discard original $p^\*$); compare re-minting at high $H$ vs. low $H$. *Expect:* re-mint while collapsed → KL to *original* truth is locked high forever (tails unrecoverable); gated re-mint at high $H$ → no lock-in. *Validates:* §11's "re-minting is irreversible; gate it on diversity." *Falsifier:* if a collapsed lineage recovers its original-truth tails after re-minting, the irreversibility warning is overstated.
|
||||||
|
|
||||||
|
### 2.6 Layer-1 falsifiers, collected
|
||||||
|
|
||||||
|
The model is built to be *able to kill the thesis*. If E2 shows no threshold, or E4 shows no decorrelation benefit, or E6 shows no lock-in, the corresponding claims are refuted and the paper must say so. A blueprint that cannot fail is not a test. The single load-bearing positive result is E2's phase boundary at $g^\* \ll 1$: it says a little grounding protects a lot of inheritance, which is the whole economic and conceptual bet of the architecture.
|
||||||
|
|
||||||
|
### 2.7 Implementation spec for Layer 1
|
||||||
|
|
||||||
|
**Language / libraries.** Python ≥ 3.11; NumPy, SciPy (stats), pandas (results), matplotlib (figures). No GPU. No other heavy deps. Everything seedable from a single integer.
|
||||||
|
|
||||||
|
**Core module interfaces** (the coding agent should implement to these signatures; names are normative so downstream scripts are stable):
|
||||||
|
|
||||||
|
```python
|
||||||
|
# knowledge/truth.py
|
||||||
|
def make_true_distribution(K: int, R: int, tail: str, tail_frac: float,
|
||||||
|
zipf_s: float, seed: int) -> TrueDist:
|
||||||
|
"""Return p_star (length K), region assignment (length K), and the tail mask."""
|
||||||
|
|
||||||
|
# knowledge/teachers.py
|
||||||
|
def make_retention_matrix(T: int, K_T: int, rho: float, q: float, rng) -> np.ndarray:
|
||||||
|
"""Return an (K_T, T) binary retention matrix R with exact marginal retention
|
||||||
|
E[R]=q and exact pairwise column-correlation rho, via the shared-switch
|
||||||
|
construction of §2.7.1. rho=0 -> independent tails; rho=1 -> identical."""
|
||||||
|
|
||||||
|
def make_correlated_teachers(p_star, tail_mask, K_T: int, rho: float, q: float,
|
||||||
|
region_assignment=None, region_specialisation=False,
|
||||||
|
tail_floor: float = 1e-9, seed: int = 0) -> list[np.ndarray]:
|
||||||
|
"""Build K_T teacher distributions from a retention matrix (§2.7.1): every teacher
|
||||||
|
keeps all head items; teacher k keeps tail item j at ~p_star mass iff R[k,j]=1,
|
||||||
|
else at tail_floor; renormalise. If region_specialisation, force R[k,j]=1 for tail
|
||||||
|
items in teacher k's home region and apply the rho construction only off-home.
|
||||||
|
The exact-construction path is preferred for E4; a drift-based path (running
|
||||||
|
independent grounded lineages) is provided as a realism cross-check only."""
|
||||||
|
|
||||||
|
# knowledge/step.py
|
||||||
|
def generation_step(teachers, p_star_eff, cfg, rng) -> np.ndarray: ...
|
||||||
|
def apply_selection(p, p_star, mode: str, alpha: float) -> np.ndarray: ...
|
||||||
|
def structured_multinomial(m_vector, p_star, regions, policy: str, rng) -> np.ndarray: ...
|
||||||
|
|
||||||
|
# knowledge/lineage.py
|
||||||
|
def run_lineage(cfg, seed) -> pd.DataFrame:
|
||||||
|
"""Run T generations for one seed; return a tidy frame with one row per
|
||||||
|
(generation) and columns for every metric in §2.3 (global and per-region)."""
|
||||||
|
|
||||||
|
# knowledge/metrics.py
|
||||||
|
def forward_kl(p_star, p, eps): ...
|
||||||
|
def heterozygosity(p): ...
|
||||||
|
def tail_mass(p, tail_mask): ...
|
||||||
|
def support_size(p, eps): ...
|
||||||
|
|
||||||
|
# knowledge/experiment.py
|
||||||
|
def run_experiment(cfg) -> pd.DataFrame:
|
||||||
|
"""Sweep the declared parameter grid x n_replicates seeds; return long-form
|
||||||
|
results with confidence intervals; write parquet + the exact resolved config."""
|
||||||
|
```
|
||||||
|
|
||||||
|
**Config schema** (one YAML per experiment; all parameters explicit, no magic numbers in code). Illustrative default:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
experiment: E2_grounding_phase_boundary
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 100
|
||||||
|
generations: 300
|
||||||
|
truth:
|
||||||
|
K: 1000 # number of knowledge items
|
||||||
|
R: 10 # regions
|
||||||
|
tail: zipf # {zipf, twocomponent}
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5 # fraction of items designated 'tail'
|
||||||
|
tail_threshold: 1.0e-3
|
||||||
|
dynamics:
|
||||||
|
n: 200 # distillation sample size (drift strength)
|
||||||
|
teachers:
|
||||||
|
K_T: 1
|
||||||
|
rho: 0.0
|
||||||
|
grounding:
|
||||||
|
sweep: {param: g, values: [0.0, 0.01, 0.02, 0.05, 0.1, 0.2, 0.4]}
|
||||||
|
policy: uniform # {uniform, matched}
|
||||||
|
selection:
|
||||||
|
mode: none # {none, greedy, qd}
|
||||||
|
novelty_alpha: 0.0
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-9
|
||||||
|
output:
|
||||||
|
dir: results/fig2_grounding_sweep/
|
||||||
|
save_per_generation: true
|
||||||
|
```
|
||||||
|
|
||||||
|
**Determinism.** One master seed → per-replicate seeds via a documented `np.random.SeedSequence` spawn. No global RNG state; pass `rng` explicitly everywhere. Results are a pure function of the resolved config. The resolved config (after sweep expansion) is written next to the results artifact.
|
||||||
|
|
||||||
|
**Outputs.** Each experiment writes: `results.parquet` (long form), `resolved_config.yaml`, and a `manifest.json` recording library versions, seed, git commit, and a content hash of the results. Figures are generated by a separate `figures/plot_EX.py` reading only `results.parquet`, so any figure is regenerable from committed data without rerunning the simulation.
|
||||||
|
|
||||||
|
#### 2.7.1 The correlated-teacher construction (E4's one non-obvious piece)
|
||||||
|
|
||||||
|
E4 needs teacher sets whose pairwise **retention-correlation** $\rho$ is a clean, swept knob. Tuning the drift parameters until an emergent $\rho$ appears is the wrong design: that $\rho$ would be a tangled function of $n$, $m$, tail size and generation count, un-sweepable and confounded with the very drift whose effect E4 is trying to hold fixed. So we **construct the retention structure directly**, with $\rho$ an independent control. For an experiment whose entire purpose is to isolate the effect of decorrelation, $\rho$ must be manipulated, not inferred.
|
||||||
|
|
||||||
|
**The mechanism — shared-switch exchangeable Bernoulli.** Let the tail have $T$ items. Each teacher $k$ retains a fraction $q$ of them; we want the retention indicators to have marginal $q$ and *exact* pairwise correlation $\rho$ across teachers. For each tail item $j$:
|
||||||
|
|
||||||
|
1. draw a **shared switch** $z_j \sim \mathrm{Bernoulli}(\rho)$ (one draw, common to all teachers for item $j$);
|
||||||
|
2. draw a **shared retention** $s_j \sim \mathrm{Bernoulli}(q)$ (common to all teachers);
|
||||||
|
3. draw **independent retentions** $u^{(k)}_j \sim \mathrm{Bernoulli}(q)$, one per teacher;
|
||||||
|
4. set $r^{(k)}_j = s_j$ if $z_j = 1$, else $u^{(k)}_j$.
|
||||||
|
|
||||||
|
That is the whole construction. It yields a retention matrix $R \in \{0,1\}^{K_T \times T}$.
|
||||||
|
|
||||||
|
**Why it is exact.** Marginally $\mathbb{E}[r^{(k)}_j] = \rho q + (1-\rho)q = q$ regardless of $\rho$. For any two teachers, conditioning on the switch gives $\mathbb{E}[r^{(k)}_j r^{(k')}_j] = \rho\,\mathbb{E}[s_j^2] + (1-\rho)q^2 = \rho q + (1-\rho)q^2$ (using $s_j^2 = s_j$), so $\mathrm{Cov} = \rho q(1-q)$ and, since $\mathrm{Var} = q(1-q)$, the pairwise correlation is exactly $\rho$. The set is *exchangeable* — every teacher pair has the same $\rho$ — which is what makes $\rho$ a single scalar knob. (Verified numerically: marginal and pairwise correlation hit target across a $(\rho, q)$ grid, and the union closed form below matches to three decimals.)
|
||||||
|
|
||||||
|
**From retention to teacher distributions.** Given $R$, build teacher $k$'s distribution $p^{(k)}$: assign every **head** item its $p^\*$ mass (all teachers keep the common core); assign **tail** item $j$ its $p^\*_j$ mass if $r^{(k)}_j = 1$ and a floor $\varepsilon_{\text{tail}}$ otherwise; renormalise. (Renormalising lets the mass of dropped tails flow to what the teacher kept — the realistic signature of a partially-collapsed model concentrating on its survivors. Reallocating instead to the head is a documented config switch.)
|
||||||
|
|
||||||
|
**Region specialisation (structured decorrelation, optional).** With `region_specialisation=True`, give each teacher a **home region** and force $r^{(k)}_j = 1$ for every tail item $j$ in teacher $k$'s home region (each teacher fully retains the tails of the region it ground against reality), applying the $\rho$ construction only to off-home tail items. This models "each teacher ground a different region," ties E4 to E3's region-matched grounding, and is the discrete image of the perspective paper's "distil from teachers who each earned a different region."
|
||||||
|
|
||||||
|
**The analytic target (E4's exact check).** The probability that tail item $j$ is retained by at least one of $K_T$ teachers is $\rho q + (1-\rho)\big(1-(1-q)^{K_T}\big)$, so the expected **union tail-coverage** is
|
||||||
|
$$U(K_T,\rho,q) = T\left[\rho q + (1-\rho)\left(1-(1-q)^{K_T}\right)\right],$$
|
||||||
|
the closed form of §2.4-5. `make_retention_matrix` must reproduce it (and the target $\rho$, $q$) within Monte-Carlo tolerance in `test_scientific_validation.py`. The pupil's *realised* coverage after distillation equals $U$ only for tail items whose mixture mass clears the drift-survival threshold $\sim 1/n$ (§2.4-4) — so E4 should report both the union coverage (construction-level) and the post-distillation surviving coverage (dynamics-level), and their gap is itself informative: it is exactly the tail that recombination *supplied* but drift *re-erased* because grounding was too thin to hold it.
|
||||||
|
|
||||||
|
**Continuous-mass extension (optional realism, not a headline).** Binary retention is the default because it matches prediction 5 exactly. For a realism cross-check, replace binary retention with correlated *masses*: draw per-teacher log-masses on tail items from a multivariate normal with equicorrelation $\rho$ (a Gaussian copula), exponentiate, and normalise. This generalises to an arbitrary correlation *matrix* between teachers (unequal pairwise $\rho$), which the shared-switch construction — being exchangeable — cannot express; use it only if the paper later needs non-exchangeable teacher sets.
|
||||||
|
|
||||||
|
**Optional abstract treatment of the horizontal claim (§2.7-H, low priority).** If time permits, model a domain as a modular graph with tunable modularity $Q$; define the optimal number of concurrent specialists as the point where marginal coverage gain from an added specialist falls below a cost; show optimal specialist count rises with $Q$. This is the *horizontal* prediction in abstract form and does not require the neural layer. Flag clearly as exploratory; it is not a headline and can be dropped without weakening the paper.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Layer 2 — the neural existence proof
|
||||||
|
|
||||||
|
### 3.1 Purpose and the single objection it answers
|
||||||
|
|
||||||
|
Layer 1 assumes the tail-deletion operator (drift). A reviewer will say: *you built your conclusion into the operator.* Layer 2 answers exactly that objection and no more. It does not need to be a society. It needs to show, in **real LoRA-adapted weights**, that the *sign* of three effects is as Layer 1 predicts: dry inheritance degrades, grounded inheritance holds; multiple decorrelated teachers preserve capability that one teacher sheds; and across generations general capability holds or climbs while each specialty is re-earned and exceeded. If those three signs appear at 1B scale on one GPU, the abstraction in Layer 1 is grounded in mechanism.
|
||||||
|
|
||||||
|
Minimality is a virtue here, not a compromise. Every additional degree of freedom (bigger model, more generations, more domains) multiplies cost and reviewer surface without strengthening the core claim. Build the smallest thing that can show the signs.
|
||||||
|
|
||||||
|
### 3.2 Design choices (with open-science defaults)
|
||||||
|
|
||||||
|
**Base model.** Default to a *fully open* small model to honour reproducibility end-to-end: **OLMo-2-1B** or **SmolLM2-1.7B** (open weights, open or well-documented data, permissive licence). Capable fallback if the open models are too weak on the task: **Qwen2.5-1.5B-Instruct**. Pin the exact Hugging Face revision hash in config; never track `main`. The pipeline must be model-agnostic behind a thin adapter so swapping bases is a config change.
|
||||||
|
|
||||||
|
**Specialisation.** LoRA (via PEFT), small rank (e.g. 8–16), on a single task family = one "region." Hours on one consumer GPU, consistent with the perspective paper's cost claim. Each teacher is one base + one LoRA adapter.
|
||||||
|
|
||||||
|
**The verifier = "reality's no."** The domain must have a cheap, deterministic, uncontrollable oracle. **Program synthesis with unit tests** is ideal: a generated solution either passes its tests or it does not, and neither the model nor the experimenter controls the verdict. This is the minimal honest instance of the perspective paper's "predictive success under intervention." Execution happens in a sandbox (subprocess with a hard timeout and no network, run inside the container of §4; document the sandbox precisely).
|
||||||
|
|
||||||
|
**Task domain — synthetic-primary, benchmark-secondary.** To keep the Layer-1 abstractions (region, rarity, tail) exact, the *primary* domain is a **synthetic program-synthesis generator**: a family of small, verifiable tasks parameterised by *operation type* (the region: e.g. string ops, list ops, arithmetic, dict manipulation, recursion) and *difficulty*, with I/O unit tests generated automatically. Task-type frequency is tunable, so "rare tail task types" is a dial, exactly matching Layer 1's rarity. For *external validity*, add a secondary evaluation on a held-out slice of a public benchmark (e.g. MBPP-sanitised / HumanEval) — pinned by version — to show the effect is not an artefact of the synthetic generator. The synthetic generator's spec (grammar, per-region templates, test-generation rule, rarity distribution) is itself a committed, seeded artifact.
|
||||||
|
|
||||||
|
**Grounding, concretely.** A generational passage produces pupil training data as a mixture of (i) *inherited* teacher-generated solutions and (ii) *grounded* solutions that have been **filtered to pass the verifier** and/or freshly drawn from verified references in the target region. The **grounding fraction $g$** is the proportion of verifier-passed/real items in the pupil's training mixture — the *same knob* as Layer 1's $g$. "Dry" = $g$ low / unfiltered teacher output; "grounded" = $g$ raised with region-matched verified data.
|
||||||
|
|
||||||
|
**Recombination, concretely.** Multiple teachers = multiple LoRA specialists on *disjoint* task families (decorrelated by construction). The pupil is trained on the pooled outputs of all teachers (distillation) or, as a cheaper alternative, the specialists are **merged** (M2N2 / model-merge style, citing Sakana's demonstration that this search runs with no retraining) and the merged model is the pupil. Distillation and merging are two config-selectable recombination operators; report at least distillation, and merging if compute allows.
|
||||||
|
|
||||||
|
### 3.3 Contrasts C1–C4 (the whole experimental content)
|
||||||
|
|
||||||
|
**C1 — Dry vs. grounded, single teacher, across generations.** One teacher, one region. Arm A: pupil trained on *unfiltered* teacher outputs (dry, $g\approx0$). Arm B: pupil trained on *verifier-passed* teacher outputs plus fresh verified region data ($g>0$). Run 2–3 generations. *Expect:* Arm A pass@k degrades generation over generation and its solution diversity narrows; Arm B holds. *Maps to:* Layer-1 E2. *Falsifier:* if dry inheritance does not degrade at this scale, collapse is not reachable here and the neural claim is unsupported (report honestly; possibly scale down grounding or up generations).
|
||||||
|
|
||||||
|
**C2 — One teacher vs. N complementary teachers, matched data budget.** Fix the pupil's total training-token budget. Arm A: all budget from one teacher (one region). Arm B: same budget split across $N$ teachers on disjoint regions. Evaluate the pupil on *all* regions, including rare-type tasks. *Expect:* Arm B retains capability across the union of regions (the "tail" of rare task types survives); Arm A loses regions it did not inherit. *Maps to:* Layer-1 E4. *Falsifier:* no union benefit at matched budget → recombination claim unsupported neurally.
|
||||||
|
|
||||||
|
**C3 — The vertical claim (load-bearing).** Over 2–3 generations, track (a) a *general* held-out benchmark spanning all regions and (b) *per-specialty* performance, where each generation re-specialises (re-earns) its region against the verifier. *Expect:* general benchmark is **monotonically non-decreasing** across generations, while each specialty is re-earned and its peak **exceeds the parent's** peak in that specialty. *This is the test of the actual thesis, not the borrowed Shumailov scaffolding.* *Maps to:* the paper's vertical prediction. *Falsifier:* if general capability falls across generations, or specialties are not re-earned-and-exceeded, the multigenerational ratchet is not demonstrated.
|
||||||
|
|
||||||
|
**C4 — Recombination operator: distillation vs. merging (optional).** If compute allows, show C2's benefit is robust to whether recombination is done by distillation or by weight-merging. Strengthens generality and directly connects to the M2N2 citation. Drop first if time-constrained.
|
||||||
|
|
||||||
|
### 3.4 Metrics (Layer 2)
|
||||||
|
|
||||||
|
- **pass@1 and pass@k** on held-out tasks, per region and overall (the neural analogue of KL-to-truth / capability).
|
||||||
|
- **Region coverage** = fraction of regions (incl. rare task types) with pass@1 above a floor — the neural analogue of *support size* / tail survival.
|
||||||
|
- **Solution diversity** = distinct-$n$ or behavioural diversity of generated solutions per task (the neural analogue of heterozygosity $H$; narrowing diversity is the neural signature of collapse).
|
||||||
|
- **Generational degradation curve** = each metric as a function of generation index, per arm.
|
||||||
|
|
||||||
|
All with multiple seeds (fewer than Layer 1 — GPU cost — but at least 3; report per-seed points, not just means, given small $n$). Pin decoding parameters (temperature, top-p, max tokens, sampling seed) in config; they materially affect pass@k and must not float.
|
||||||
|
|
||||||
|
### 3.5 Layer-2 honesty riders
|
||||||
|
|
||||||
|
GPU non-determinism means Layer 2 is *statistically* reproducible (same distribution of outcomes under re-run), not bitwise reproducible. Document this explicitly; pin everything pinnable (model revision, dataset version, decoding params, library versions, seeds); report seeds individually. The claim Layer 2 supports is directional ("the sign of the effect is as predicted"), and the writing must not overclaim precision the setup cannot deliver.
|
||||||
|
|
||||||
|
### 3.6 Implementation spec for Layer 2
|
||||||
|
|
||||||
|
**Libraries.** PyTorch; Hugging Face `transformers` + `peft` (LoRA); `datasets`; optional `vllm` for fast generation; the synthetic task generator (project-local). Execution sandbox: `subprocess` with `resource` limits and timeout, inside the container.
|
||||||
|
|
||||||
|
**Module interfaces (normative names):**
|
||||||
|
|
||||||
|
```python
|
||||||
|
# neural/tasks.py
|
||||||
|
def generate_task_bank(regions, rarity, n_tasks, seed) -> TaskBank:
|
||||||
|
"""Synthetic verifiable tasks; each task carries prompt, region, difficulty,
|
||||||
|
and an executable unit-test suite."""
|
||||||
|
def verify(solution_code: str, task) -> VerifyResult:
|
||||||
|
"""Run tests in a sandbox; return pass/fail + diagnostics. Deterministic."""
|
||||||
|
|
||||||
|
# neural/specialise.py
|
||||||
|
def train_lora_specialist(base_id, revision, region, task_bank, lora_cfg, seed) -> AdapterPath: ...
|
||||||
|
|
||||||
|
# neural/distill.py
|
||||||
|
def generate_teacher_data(teachers, task_bank, grounding_fraction, policy, decode_cfg, seed) -> Corpus:
|
||||||
|
"""Produce the pupil's training corpus: mixture of inherited (teacher) and
|
||||||
|
grounded (verifier-passed / fresh-verified) items, region-matched."""
|
||||||
|
def train_pupil(base_id, revision, corpus, lora_cfg, seed) -> AdapterPath: ...
|
||||||
|
|
||||||
|
# neural/merge.py
|
||||||
|
def merge_specialists(base_id, adapters, method, seed) -> ModelPath: # optional (C4)
|
||||||
|
|
||||||
|
# neural/evaluate.py
|
||||||
|
def evaluate(model, task_bank_heldout, decode_cfg, seed) -> pd.DataFrame:
|
||||||
|
"""pass@k, per-region coverage, solution diversity; tidy per-(region) frame."""
|
||||||
|
|
||||||
|
# neural/generation_loop.py
|
||||||
|
def run_generations(cfg, seed) -> pd.DataFrame:
|
||||||
|
"""Orchestrate T generations for one arm; log every metric per generation."""
|
||||||
|
```
|
||||||
|
|
||||||
|
**Config** mirrors Layer 1's structure (one YAML per contrast; pinned `base_id` + `revision`; explicit `grounding_fraction`, `n_teachers`, `regions`, `generations`, `decode_cfg`, `lora_cfg`, `seed`, `n_seeds`). Same output contract: `results.parquet` + `resolved_config.yaml` + `manifest.json` (with model revision hashes and dataset versions). Figures regenerable from `results.parquet` alone.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Reproducibility and engineering standard (both layers)
|
||||||
|
|
||||||
|
Open science is a hard requirement of this project, not a preference. The standard below is normative.
|
||||||
|
|
||||||
|
**Environment.** Pin everything. Provide (a) a `pyproject.toml` + lockfile via **uv** (fast, reproducible resolver) and (b) an **Apptainer/Singularity** definition file (HPC-friendly, rootless, open) that builds the exact environment; optionally a Dockerfile. The container is the source of truth for "it runs." Record Python, CUDA, and key library versions in every run's `manifest.json`.
|
||||||
|
|
||||||
|
**Seeding.** One master seed per experiment in config; derive all sub-seeds via `SeedSequence.spawn`; never touch global RNG state. Layer 1 is bitwise-reproducible from seed. Layer 2 is statistically reproducible; document the residual GPU non-determinism and set the available determinism flags (`torch.use_deterministic_algorithms(True)` where feasible, cudnn deterministic, documented exceptions).
|
||||||
|
|
||||||
|
**Configuration.** No magic numbers in code — every parameter lives in a YAML resolved at run time; the *resolved* config is written beside results. Use a single config system (Hydra or a thin equivalent). Sweeps are declared in config, not hard-coded in scripts.
|
||||||
|
|
||||||
|
**Data & model provenance.** Layer 1 data is synthetic-from-seed (fully reproducible; no external data). Layer 2 pins model revision hashes and dataset versions; the synthetic task generator is committed and seeded. Cache external downloads with recorded hashes.
|
||||||
|
|
||||||
|
**Experiment tracking.** Prefer open tooling: **MLflow** (open source) or plain versioned CSV/Parquet + committed configs; avoid closed SaaS trackers to keep the pipeline fully open. Whatever is chosen, the invariant is: every figure is a pure function of a committed results artifact.
|
||||||
|
|
||||||
|
**Testing.** `pytest`. Two kinds of test, and both are required:
|
||||||
|
- *Correctness tests*: the module does what it says (shapes, normalisation, sandbox isolation).
|
||||||
|
- *Scientific validation tests*: the simulator reproduces the §2.4 analytic results within tolerance (heterozygosity decay, fixation probability, mutation–drift equilibrium). These tests failing means the science is wrong, not just the code — they are the spine of trust in Layer 1.
|
||||||
|
|
||||||
|
**Automation.** A `Makefile` (or `justfile`) with targets: `env`, `test`, `layer1` (runs E1–E6), `layer2` (runs C1–C3, C4 optional), `figures`, `paper` (assembles the figure manifest), `all`, `clean`. One command reproduces the study from a clean checkout inside the container.
|
||||||
|
|
||||||
|
**Repro entry point.** A top-level `reproduce.sh` that: builds/enters the container, runs tests, runs all experiments at the committed seeds, regenerates all figures, and writes a `REPRODUCED.md` diff against committed result hashes. If hashes match (Layer 1) / distributions match within CI (Layer 2), the run is verified.
|
||||||
|
|
||||||
|
**Licensing & citation.** Author to choose; suggested: code under a permissive OSI licence (MIT/Apache-2.0) or copyleft (GPL-3.0) per the author's open-source preference; text/figures under CC-BY. Include `LICENSE`, `CITATION.cff`, and a `DATA_AND_MODELS.md` recording every external artifact and its pinned version/hash.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Repository layout
|
||||||
|
|
||||||
|
```
|
||||||
|
lamarckian-society/
|
||||||
|
├── README.md # what this is, how to reproduce
|
||||||
|
├── reproduce.sh # one-command full reproduction (in-container)
|
||||||
|
├── Makefile # env, test, layer1, layer2, figures, all
|
||||||
|
├── pyproject.toml # deps
|
||||||
|
├── uv.lock # pinned resolution
|
||||||
|
├── apptainer.def # container definition (source of truth)
|
||||||
|
├── Dockerfile # optional
|
||||||
|
├── LICENSE CITATION.cff DATA_AND_MODELS.md
|
||||||
|
├── configs/
|
||||||
|
│ ├── layer1/E1..E6.yaml
|
||||||
|
│ └── layer2/C1..C4.yaml
|
||||||
|
├── src/
|
||||||
|
│ ├── knowledge/ # Layer 1: truth, teachers, step, lineage, metrics, experiment
|
||||||
|
│ └── neural/ # Layer 2: tasks, specialise, distill, merge, evaluate, generation_loop
|
||||||
|
├── figures/ # plot_EX.py / plot_CX.py — read results.parquet only
|
||||||
|
├── results/ # written artifacts (gitignored, hashes tracked)
|
||||||
|
├── tests/
|
||||||
|
│ ├── test_correctness.py
|
||||||
|
│ └── test_scientific_validation.py # §2.4 analytic checks
|
||||||
|
└── paper/
|
||||||
|
├── blueprint.md # this document
|
||||||
|
└── figure_manifest.md # claim -> experiment -> figure
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Traceability: claim → experiment → figure
|
||||||
|
|
||||||
|
Every claim the technical paper will make is bound to exactly one experiment and one figure. This table is the paper's spine and the coding agent's definition of done.
|
||||||
|
|
||||||
|
| Perspective-paper claim | Layer | Experiment | Primary figure | Analytic check | Falsifier |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Distillation without grounding collapses, tail first, at rate set by $n$ | 1 | E1 | $H_t$ decay + tail mass vs. gen | Pred. 1–2 | harness invalid if decay ≠ analytic |
|
||||||
|
| A critical grounding ratio $g^\*$ separates ratchet from collapse | 1 | E2 | tail mass / $H$ stationary vs. $g$ | Pred. 3 | flat in $g$, or only stable at $g\to1$ |
|
||||||
|
| Grounding must be region-matched, not merely present | 1 | E3 | target-region tail: uniform vs. matched | Pred. 4 | uniform protects as well as matched |
|
||||||
|
| Multi-teacher distillation suppresses collapse ∝ decorrelation | 1 | E4 | tail coverage surface over $(K_T,\rho)$ | Pred. 5 (closed form $U(K_T,\rho,q)$) | no benefit at matched budget |
|
||||||
|
| QD selection maintains diversity where greedy fixes it | 1 | E5 | $H$ plateau: qd vs. greedy | (mutation-selection-drift) | qd ≤ greedy in stationary $H$ |
|
||||||
|
| Re-minting is irreversible; gate on diversity | 1 | E6 | KL-to-original vs. $H$-at-remint | — | collapsed lineage recovers after remint |
|
||||||
|
| Dry inheritance degrades in real weights; grounded holds | 2 | C1 | pass@k vs. gen, dry vs. grounded | — | dry does not degrade |
|
||||||
|
| Complementary teachers preserve capability one teacher sheds | 2 | C2 | region coverage, 1 vs. N, matched budget | — | no union benefit |
|
||||||
|
| **General knowledge climbs while each specialty is re-earned and exceeded** | 2 | C3 | general benchmark ↑ + per-specialty re-earn | — | general falls / no re-earn |
|
||||||
|
| Recombination benefit robust to distillation vs. merging | 2 | C4 (opt) | C2 metric under both operators | — | benefit only under one operator |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Suggested build order for the coding agent
|
||||||
|
|
||||||
|
Staged so that each step is independently testable and the cheapest, highest-value results land first. Do not start Layer 2 until Layer 1's scientific-validation tests pass.
|
||||||
|
|
||||||
|
1. **Scaffold + environment.** Repo layout (§5), container (§4), `pytest` skeleton, config system, seeding utilities. Target `make test` green on trivial tests.
|
||||||
|
2. **Layer 1 core + validation.** `knowledge/` modules to the §2.7 interfaces. Implement the null model first. Write and pass `test_scientific_validation.py` against §2.4 predictions 1–2. **Gate: do not proceed until drift matches analytic decay.**
|
||||||
|
3. **Layer 1 mechanisms + E1–E2.** Add grounding; validate mutation–drift equilibrium (pred. 3); run E1 and the E2 phase-boundary sweep; produce the headline figure. This is the paper's core result and it should exist before anything neural.
|
||||||
|
4. **Layer 1 E3–E6.** Region-matching, multi-teacher/decorrelation, QD-vs-greedy, re-minting gate, with figures. Layer 1 is now a complete, laptop-reproducible paper on its own.
|
||||||
|
5. **Layer 2 scaffold + verifier.** Synthetic task generator, sandboxed `verify`, evaluation harness. Test the verifier's determinism and isolation before any training.
|
||||||
|
6. **Layer 2 C1 + C3.** Single-teacher dry-vs-grounded (C1) and the vertical claim (C3) — the two that most directly test the thesis. C3 is load-bearing; prioritise it.
|
||||||
|
7. **Layer 2 C2 (+ C4 if compute allows).** Multi-teacher recombination; optional merging operator.
|
||||||
|
8. **Reproduction pass.** `reproduce.sh` end-to-end; commit result hashes; write `REPRODUCED.md`; assemble the figure manifest.
|
||||||
|
|
||||||
|
**Definition of done:** every row of §6 has a committed figure produced by `make figures` from committed results, every §2.4 analytic check passes in CI, and `reproduce.sh` verifies from a clean checkout inside the container.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*End of blueprint v1. The perspective paper states the idea; this document states the test. If Layer 1's E2 finds no threshold, or E4 no decorrelation benefit, or Layer 2's C3 no vertical climb, the thesis is wrong in exactly the places these experiments probe — which is the point of writing them down this precisely.*
|
||||||
291
paper/manuscript/build_zotero_library.py
Normal file
291
paper/manuscript/build_zotero_library.py
Normal file
|
|
@ -0,0 +1,291 @@
|
||||||
|
"""Build a Zotero-importable library from the manuscript's reference list.
|
||||||
|
|
||||||
|
For each of the numbered references in paper/manuscript/main.md: take the DOI printed in the entry when
|
||||||
|
there is one, otherwise ask Crossref for it by title (accepting only a high-scoring match whose title
|
||||||
|
really is the same, checked by normalised comparison). Then fetch authoritative metadata for every
|
||||||
|
resolved DOI by content negotiation against doi.org, which serves Crossref and DataCite alike, and
|
||||||
|
write the result as CSL-JSON plus RIS.
|
||||||
|
|
||||||
|
Entries whose DOI cannot be resolved (pre-DOI literature, books, chapters) are reported and written
|
||||||
|
from the manuscript's own metadata so nothing is silently dropped.
|
||||||
|
|
||||||
|
Usage: python paper/manuscript/build_zotero_library.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
MAIN = Path(__file__).resolve().parent / "main.md"
|
||||||
|
OUT = Path(__file__).resolve().parent / "refs"
|
||||||
|
MAILTO = "g.gilestro@imperial.ac.uk" # Crossref polite pool
|
||||||
|
UA = f"LamarckianAI-refs/1.0 (mailto:{MAILTO})"
|
||||||
|
|
||||||
|
# Reference numbers whose sources predate DOIs or are books/chapters: never send these to Crossref
|
||||||
|
# title search, because it returns confident nonsense for them.
|
||||||
|
NO_DOI_EXPECTED = {33, 35, 39} # Jenkin 1867; Fisher 1930 (book); Templeton 1986 (chapter)
|
||||||
|
|
||||||
|
# DOIs the title search could not find and that were verified by hand against the publisher record.
|
||||||
|
DOI_OVERRIDE = {
|
||||||
|
17: "10.1038/s41562-023-01742-2", # Brinkmann et al., Machine culture (Nat. Hum. Behav.)
|
||||||
|
64: "10.48550/arXiv.1805.06370", # Schwarz et al., Progress & Compress (no Crossref DOI)
|
||||||
|
}
|
||||||
|
|
||||||
|
# The three genuinely pre-DOI sources, written out rather than parsed, so the Zotero records are
|
||||||
|
# complete instead of merely non-empty.
|
||||||
|
HAND_WRITTEN = {
|
||||||
|
33: {"type": "article-journal", "title": "[Review of] The Origin of Species",
|
||||||
|
"author": [{"given": "Fleeming", "family": "Jenkin"}],
|
||||||
|
"container-title": "The North British Review", "volume": "46", "page": "277-318",
|
||||||
|
"issued": {"date-parts": [[1867]]}},
|
||||||
|
35: {"type": "book", "title": "The Genetical Theory of Natural Selection",
|
||||||
|
"author": [{"given": "Ronald A.", "family": "Fisher"}],
|
||||||
|
"publisher": "Clarendon Press", "publisher-place": "Oxford",
|
||||||
|
"issued": {"date-parts": [[1930]]}},
|
||||||
|
39: {"type": "chapter", "title": "Coadaptation and outbreeding depression",
|
||||||
|
"author": [{"given": "Alan R.", "family": "Templeton"}],
|
||||||
|
"editor": [{"given": "Michael E.", "family": "Soulé"}],
|
||||||
|
"container-title": "Conservation Biology: The Science of Scarcity and Diversity",
|
||||||
|
"publisher": "Sinauer Associates", "publisher-place": "Sunderland, MA",
|
||||||
|
"page": "105-116", "issued": {"date-parts": [[1986]]}},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get(url: str, accept: str | None = None, tries: int = 3) -> bytes | None:
|
||||||
|
req = urllib.request.Request(url, headers={"User-Agent": UA})
|
||||||
|
if accept:
|
||||||
|
req.add_header("Accept", accept)
|
||||||
|
for i in range(tries):
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as r:
|
||||||
|
return r.read()
|
||||||
|
except Exception as e: # noqa: BLE001
|
||||||
|
if i == tries - 1:
|
||||||
|
print(f" ! {type(e).__name__}: {str(e)[:80]}", file=sys.stderr)
|
||||||
|
time.sleep(1.5 * (i + 1))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_refs() -> list[tuple[int, str]]:
|
||||||
|
refs = MAIN.read_text().split("## References")[1]
|
||||||
|
out = []
|
||||||
|
for line in refs.splitlines():
|
||||||
|
if m := re.match(r"^(\d+)\. (.*)$", line):
|
||||||
|
out.append((int(m.group(1)), m.group(2).strip()))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def strip_md(s: str) -> str:
|
||||||
|
return re.sub(r"[*_`]", "", s)
|
||||||
|
|
||||||
|
|
||||||
|
def guess_title(entry: str) -> str:
|
||||||
|
"""The title is the run of text between the author list and the italic venue or the year."""
|
||||||
|
t = strip_md(entry)
|
||||||
|
t = re.sub(r"\s*https?://\S+$", "", t).strip()
|
||||||
|
# drop the leading author list: everything up to the last ", " before the title is unreliable,
|
||||||
|
# so instead cut after the first ", " that follows an initial-style name block
|
||||||
|
m = re.match(r"^((?:[A-ZÀ-Þ]\.\s*)+[^,]+,\s*)+", t)
|
||||||
|
rest = t[m.end():] if m else t
|
||||||
|
rest = re.sub(r"^et al\.,\s*", "", rest)
|
||||||
|
# the title ends at the venue (". *Venue*") or at " arXiv [Preprint]" or " (Year)"
|
||||||
|
rest = re.split(r"\.\s+(?:arXiv \[Preprint\]|[A-Z][a-zA-Z.\s&]*\*|Proc\.|Int\.|Adv\.|Conf\.|Nat\.|Trans\.)", rest)[0]
|
||||||
|
rest = re.split(r"\s*\(\d{4}\)", rest)[0]
|
||||||
|
return rest.strip(" .,")
|
||||||
|
|
||||||
|
|
||||||
|
def norm(s: str) -> str:
|
||||||
|
return re.sub(r"[^a-z0-9]", "", s.lower())
|
||||||
|
|
||||||
|
|
||||||
|
def crossref_by_title(title: str, year: str | None) -> tuple[str | None, str]:
|
||||||
|
q = urllib.parse.urlencode({"query.bibliographic": title, "rows": 5, "mailto": MAILTO})
|
||||||
|
raw = get(f"https://api.crossref.org/works?{q}")
|
||||||
|
if not raw:
|
||||||
|
return None, "crossref unreachable"
|
||||||
|
items = json.loads(raw).get("message", {}).get("items", [])
|
||||||
|
tn = norm(title)
|
||||||
|
for it in items:
|
||||||
|
cand = (it.get("title") or [""])[0]
|
||||||
|
cn = norm(cand)
|
||||||
|
if not cn:
|
||||||
|
continue
|
||||||
|
# accept only a genuine title match, not merely a high Crossref score
|
||||||
|
if cn.startswith(tn[:60]) or tn.startswith(cn[:60]):
|
||||||
|
return it.get("DOI"), f"matched: {cand[:70]}"
|
||||||
|
return None, f"no title match (best: {(items[0].get('title') or [''])[0][:60] if items else '-'})"
|
||||||
|
|
||||||
|
|
||||||
|
def csl_from_doi(doi: str) -> dict | None:
|
||||||
|
raw = get(f"https://doi.org/{urllib.parse.quote(doi)}",
|
||||||
|
accept="application/vnd.citationstyles.csl+json")
|
||||||
|
if not raw:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return json.loads(raw)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- fallback CSL from the manuscript
|
||||||
|
def manual_csl(num: int, entry: str) -> dict:
|
||||||
|
t = strip_md(entry)
|
||||||
|
year = (re.search(r"\((\d{4})\)", t) or re.search(r"(\d{4})", t))
|
||||||
|
authors = []
|
||||||
|
m = re.match(r"^((?:[A-ZÀ-Þ]\.(?:\s*[A-ZÀ-Þ]\.)*\s+[^,]+,\s*)+)", t)
|
||||||
|
if m:
|
||||||
|
for name in re.findall(r"([A-ZÀ-Þ]\.(?:\s*[A-ZÀ-Þ]\.)*)\s+([^,]+)", m.group(1)):
|
||||||
|
authors.append({"given": name[0].strip(), "family": name[1].strip()})
|
||||||
|
venue = re.search(r"\*([^*]+)\*", entry)
|
||||||
|
vol = re.search(r"\*\*(\d+)\*\*", entry)
|
||||||
|
pages = re.search(r"\*\*\d+\*\*,\s*([\d–\-]+)", entry)
|
||||||
|
return {k: v for k, v in {
|
||||||
|
"id": f"ref{num}",
|
||||||
|
"type": "book" if "Press)" in t or "Sinauer" in t else "article-journal",
|
||||||
|
"title": guess_title(entry),
|
||||||
|
"author": authors or None,
|
||||||
|
"container-title": venue.group(1) if venue else None,
|
||||||
|
"volume": vol.group(1) if vol else None,
|
||||||
|
"page": pages.group(1).replace("–", "-") if pages else None,
|
||||||
|
"issued": {"date-parts": [[int(year.group(1))]]} if year else None,
|
||||||
|
"note": f"manuscript reference {num}; no DOI",
|
||||||
|
}.items() if v is not None}
|
||||||
|
|
||||||
|
|
||||||
|
def clean_text(s: str) -> str:
|
||||||
|
"""Publisher abstracts arrive with JATS tags, HTML entities, and hard line breaks; RIS is a
|
||||||
|
line-oriented format, so every field has to end up as one clean line."""
|
||||||
|
import html
|
||||||
|
|
||||||
|
s = re.sub(r"<[^>]+>", " ", s) # JATS/HTML tags
|
||||||
|
s = html.unescape(s)
|
||||||
|
return re.sub(r"\s+", " ", s).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def clean_csl(c: dict) -> dict:
|
||||||
|
for k, v in list(c.items()):
|
||||||
|
if isinstance(v, str):
|
||||||
|
c[k] = clean_text(v)
|
||||||
|
elif isinstance(v, list) and v and isinstance(v[0], str):
|
||||||
|
c[k] = [clean_text(x) for x in v]
|
||||||
|
doi = c.get("DOI", "")
|
||||||
|
if doi.lower().startswith("10.48550/arxiv."):
|
||||||
|
# DataCite returns these uppercased and with no venue; restore the canonical DOI casing and
|
||||||
|
# give Zotero something to show in the publication field instead of a blank.
|
||||||
|
arxiv_id = doi.split(".", 2)[-1]
|
||||||
|
c["DOI"] = f"10.48550/arXiv.{arxiv_id}"
|
||||||
|
c["container-title"] = "arXiv"
|
||||||
|
c["number"] = f"arXiv:{arxiv_id}"
|
||||||
|
c["genre"] = "preprint"
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
|
# Crossref reports its own type vocabulary alongside real CSL types; map both.
|
||||||
|
CSL2RIS_EXTRA = {"journal-article": "JOUR", "book-chapter": "CHAP", "proceedings-article": "CPAPER",
|
||||||
|
"posted-content": "JOUR", "book-section": "CHAP", "monograph": "BOOK"}
|
||||||
|
|
||||||
|
|
||||||
|
CSL2RIS = {"article-journal": "JOUR", "paper-conference": "CPAPER", "chapter": "CHAP",
|
||||||
|
"book": "BOOK", "article": "JOUR", "posted-content": "JOUR", "report": "RPRT",
|
||||||
|
"dataset": "DATA", "thesis": "THES"}
|
||||||
|
|
||||||
|
|
||||||
|
def ris_type(c: dict) -> str:
|
||||||
|
t = c.get("type", "")
|
||||||
|
return CSL2RIS.get(t) or CSL2RIS_EXTRA.get(t) or "JOUR"
|
||||||
|
|
||||||
|
|
||||||
|
def to_ris(c: dict, num: int) -> str:
|
||||||
|
L = [f"TY - {ris_type(c)}"]
|
||||||
|
for a in c.get("author") or []:
|
||||||
|
fam, giv = a.get("family", ""), a.get("given", "")
|
||||||
|
L.append(f"AU - {fam}, {giv}".rstrip(", ") if fam else f"AU - {a.get('literal', '')}")
|
||||||
|
ttl = c.get("title")
|
||||||
|
if isinstance(ttl, list):
|
||||||
|
ttl = ttl[0]
|
||||||
|
if ttl:
|
||||||
|
L.append(f"TI - {ttl}")
|
||||||
|
ct = c.get("container-title")
|
||||||
|
if isinstance(ct, list):
|
||||||
|
ct = ct[0] if ct else None
|
||||||
|
if ct:
|
||||||
|
L.append(f"{'BT' if ris_type(c) == 'CHAP' else 'T2'} - {ct}")
|
||||||
|
for ed in c.get("editor") or []:
|
||||||
|
L.append(f"A2 - {ed.get('family', '')}, {ed.get('given', '')}".rstrip(", "))
|
||||||
|
if c.get("number"):
|
||||||
|
L.append(f"AN - {c['number']}")
|
||||||
|
if c.get("publisher-place"):
|
||||||
|
L.append(f"CY - {c['publisher-place']}")
|
||||||
|
parts = (c.get("issued") or {}).get("date-parts") or [[]]
|
||||||
|
if parts and parts[0]:
|
||||||
|
L.append(f"PY - {parts[0][0]}")
|
||||||
|
for key, tag in (("volume", "VL"), ("issue", "IS"), ("publisher", "PB"), ("DOI", "DO"),
|
||||||
|
("URL", "UR"), ("abstract", "AB")):
|
||||||
|
if c.get(key):
|
||||||
|
L.append(f"{tag} - {c[key]}")
|
||||||
|
if c.get("page"):
|
||||||
|
pg = str(c["page"]).replace("–", "-").split("-")
|
||||||
|
L.append(f"SP - {pg[0]}")
|
||||||
|
if len(pg) > 1:
|
||||||
|
L.append(f"EP - {pg[-1]}")
|
||||||
|
L.append(f"N1 - {c.get('note') or f'Manuscript reference {num}'}")
|
||||||
|
L.append("ER - \n")
|
||||||
|
return "\n".join(L)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
refs = parse_refs()
|
||||||
|
print(f"{len(refs)} references parsed\n")
|
||||||
|
csls, report = [], []
|
||||||
|
for num, entry in refs:
|
||||||
|
doi = None
|
||||||
|
if num in HAND_WRITTEN:
|
||||||
|
c = dict(HAND_WRITTEN[num], id=f"ref{num}", note=f"Manuscript reference {num}; predates DOIs")
|
||||||
|
csls.append(c)
|
||||||
|
report.append((num, "HAND (pre-DOI source)", c["title"][:64], "written by hand"))
|
||||||
|
print(f" {num:3d} {'HAND (pre-DOI source)':52s} {c['title'][:56]}")
|
||||||
|
continue
|
||||||
|
if num in DOI_OVERRIDE:
|
||||||
|
doi, src = DOI_OVERRIDE[num], "verified by hand"
|
||||||
|
elif m := re.search(r"doi\.org/(10\.\S+?)\.?$", entry):
|
||||||
|
doi = m.group(1)
|
||||||
|
src = "in manuscript"
|
||||||
|
elif num not in NO_DOI_EXPECTED:
|
||||||
|
title = guess_title(entry)
|
||||||
|
yr = re.search(r"\((\d{4})\)", entry)
|
||||||
|
doi, why = crossref_by_title(title, yr.group(1) if yr else None)
|
||||||
|
src = f"crossref ({why})"
|
||||||
|
time.sleep(0.3)
|
||||||
|
else:
|
||||||
|
src = "pre-DOI / book — not searched"
|
||||||
|
|
||||||
|
c = csl_from_doi(doi) if doi else None
|
||||||
|
if c:
|
||||||
|
c["id"] = f"ref{num}"
|
||||||
|
c["note"] = f"Manuscript reference {num}"
|
||||||
|
status = f"OK {doi}"
|
||||||
|
else:
|
||||||
|
c = manual_csl(num, entry)
|
||||||
|
status = f"MANUAL ({src})" if not doi else f"MANUAL (DOI {doi} would not resolve)"
|
||||||
|
csls.append(c)
|
||||||
|
report.append((num, status, (c.get('title') or '')[:64], src))
|
||||||
|
print(f" {num:3d} {status:52s} {(c.get('title') or '')[:56]}")
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
csls = [clean_csl(c) for c in csls]
|
||||||
|
(OUT / "references.json").write_text(json.dumps(csls, indent=1, ensure_ascii=False))
|
||||||
|
(OUT / "references.ris").write_text("".join(to_ris(c, n) for (n, _), c in zip(refs, csls)))
|
||||||
|
ok = sum(1 for _, s, _, _ in report if s.startswith("OK"))
|
||||||
|
print(f"\nresolved from DOI: {ok}/{len(refs)} manual: {len(refs)-ok}")
|
||||||
|
(OUT / "report.txt").write_text("\n".join(f"{n}\t{s}\t{t}\t{src}" for n, s, t, src in report))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
39
paper/manuscript/cover_letter_nmi.md
Normal file
39
paper/manuscript/cover_letter_nmi.md
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
Giorgio F. Gilestro
|
||||||
|
Department of Life Sciences, Imperial College London
|
||||||
|
giorgio@gilest.ro
|
||||||
|
|
||||||
|
[Date]
|
||||||
|
|
||||||
|
Dear Editor,
|
||||||
|
|
||||||
|
Please consider the enclosed manuscript, "The evolution of sex for artificial intelligence: a population-genetic framework for multigenerational model populations", for publication as an Article in *Nature Machine Intelligence*.
|
||||||
|
|
||||||
|
Machine learning has become a population process. Public repositories hold millions of models, most of them fine-tunes, distillations or weight merges of a few ancestors; models learn from the output of earlier models; and merging, now mainstream practice with standard tooling, is described in its own literature with the words crossover, mutation and mate choice. A population whose members inherit from one another, recombine and retransmit is an evolving population in the technical sense, and the branch of biology built for that situation is the population genetics of sexual reproduction. That training on model output is genetic drift, with model collapse as its signature, has been established several times over. This paper takes the next step and develops the mechanisms population genetics offers for sustaining a population against drift (immigration, recombination, selection, population structure), and the point where they fail (reproductive isolation), and tests each of them in a chain from closed forms to trained networks to language models.
|
||||||
|
|
||||||
|
Four measurements are new, and each was chosen because the existing experimental designs could not make it.
|
||||||
|
|
||||||
|
First, a six-generation population of language models in which three lineages each learn a new skill every generation and then choose whether, and with whom, to merge. Merging has been iterated before, in evolutionary pools of fixed parents and in continual streams folded into one model, but never while the lineages were also learning. The population shows that obligate merging collapses once partners hold conflicting conventions (accuracy 0.65 to 0.27), that a merge each lineage may decline, or a fixed early stop, avoids the collapse at no cost against never merging, and that merging with one's own ancestor is safer than merging with a contemporary in every seed. A second curriculum decoupling partner complementarity from generation shows that declines track generation, which corrects an interpretation the first curriculum invited.
|
||||||
|
|
||||||
|
Second, model speciation as a named and tested question. Using the permutation-and-rescaling alignment of Git Re-Basin and REPAIR, the merge barrier between networks is separated into the part alignment removes and the part it cannot. Conflicting label maps leave a residual alignment does not touch, while six times the base training on non-conflicting tasks produces no isolation at all and the strongest rescue-by-merging in the paper, against the expectation that specialisation by itself erodes mergeability.
|
||||||
|
|
||||||
|
Third, a pre-merge predictive test on 39 language-model parent pairs across three decorrelated axes (conflict, compatible overlap, duration). Functional disagreement between parents predicts merge damage out of sample where LoRA-weight cosine and distance do not, in agreement with recent correlational reports. The control that matters is new: on a grid that varies conflict and shared training data together, weight cosine is the best predictor (ρ = 0.60), and adding overlap without conflict collapses it to 0.03. Any weight-geometry predictor validated on such a grid is reading the shared data, which bears on the merge-prediction literature independently of the biology.
|
||||||
|
|
||||||
|
Fourth, a conservation law for blending inheritance. Refitting a child on the average of several parents' outputs carries a rare capability across a generation no better than inheriting from one parent, to first order, so the gain of having several parents is realised only by operators that keep each parent's strongest contribution. The law fixes the null against which every recombination operator is judged and predicted the headroom rule measured in language models at two scales: routing and offspring selection beat the weight average wherever that average falls short of attainable performance (hard tasks at 7B, every seed), and add nothing where it does not.
|
||||||
|
|
||||||
|
Around these sit results that place the framework in the existing literature: a closed-form grounding equilibrium and per-item floor that agree with the fresh-data stability theorems and with the finding that absolute real-sample counts matter more than proportions; the transfer of every drift sign to trained networks with a measured, architecture-specific estimator bias; and a four-arm ablation of a composed population. Two refinements the framework proposed were not supported, and the paper says so.
|
||||||
|
|
||||||
|
I am submitting to *Nature Machine Intelligence* because the readers who make the decisions this paper prices (how much verified data a synthetic pipeline needs, whether to merge or route, when to stop merging, how to detect an incompatible pair before paying for the merge) are this journal's readers, and because the journal has already published evolutionary model merging as a research direction (Akiba et al., 2025). The paper gives that direction its theory and its failure modes. What biology receives in return is a model system where every genotype, environment and mating decision is observable and manipulable, so the paper should also interest the evolutionary biologists among your readership.
|
||||||
|
|
||||||
|
All code, configurations, seeds, results artefacts and a one-command reproduction script will be deposited openly with an archived DOI on publication; every figure regenerates from committed artefacts without re-simulation. The manuscript is not under consideration elsewhere and has not been published in any form. [A preprint has been / will be posted to arXiv.] I am the sole author and declare no competing interests.
|
||||||
|
|
||||||
|
Suggested referees:
|
||||||
|
- [Name, affiliation, email] (model merging)
|
||||||
|
- [Name, affiliation, email] (model collapse / synthetic data theory)
|
||||||
|
- [Name, affiliation, email] (population genetics of recombination and speciation)
|
||||||
|
- [Name, affiliation, email] (continual learning)
|
||||||
|
|
||||||
|
Excluded referees: [none / names].
|
||||||
|
|
||||||
|
Yours sincerely,
|
||||||
|
|
||||||
|
Giorgio F. Gilestro
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
paper/manuscript/figs/fig6.pdf
Normal file
BIN
paper/manuscript/figs/fig6.pdf
Normal file
Binary file not shown.
BIN
paper/manuscript/figs/fig7.pdf
Normal file
BIN
paper/manuscript/figs/fig7.pdf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
80
paper/manuscript/refs/report.txt
Normal file
80
paper/manuscript/refs/report.txt
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
1 OK 10.48550/arXiv.2508.06811 Anatomy of a Machine Learning Ecosystem: 2 Million Models on Hug in manuscript
|
||||||
|
2 OK 10.48550/arXiv.2405.18432 Unsupervised Model Tree Heritage Recovery in manuscript
|
||||||
|
3 OK 10.48550/arXiv.2402.00699 PeaTMOSS: A Dataset and Initial Analysis of Pre-Trained Models i in manuscript
|
||||||
|
4 OK 10.48550/arXiv.2306.01708 TIES-Merging: Resolving Interference When Merging Models in manuscript
|
||||||
|
5 OK 10.1038/s42256-024-00975-8 Evolutionary optimization of model merging recipes crossref (matched: Evolutionary optimization of model merging recipes)
|
||||||
|
6 OK 10.48550/arXiv.2403.13257 Arcee's MergeKit: A Toolkit for Merging Large Language Models in manuscript
|
||||||
|
7 OK 10.48550/arXiv.2408.07666 Model Merging in LLMs, MLLMs, and Beyond: Methods, Theories, App in manuscript
|
||||||
|
8 OK 10.48550/arXiv.2503.01155 Nature-Inspired Population-Based Evolution of Large Language Mod in manuscript
|
||||||
|
9 OK 10.48550/arXiv.2508.16204 Competition and Attraction Improve Model Fusion in manuscript
|
||||||
|
10 OK 10.48550/arXiv.2501.05707 Multiagent Finetuning: Self Improvement with Diverse Reasoning C in manuscript
|
||||||
|
11 OK 10.48550/arXiv.2406.11704 Nemotron-4 340B Technical Report in manuscript
|
||||||
|
12 OK 10.48550/arXiv.2412.08905 Phi-4 Technical Report in manuscript
|
||||||
|
13 OK 10.48550/arXiv.2212.10560 Self-Instruct: Aligning Language Models with Self-Generated Inst in manuscript
|
||||||
|
14 OK 10.48550/arXiv.2401.05749 A Shocking Amount of the Web is Machine Translated: Insights fro in manuscript
|
||||||
|
15 OK 10.48550/arXiv.2403.07183 Monitoring AI-Modified Content at Scale: A Case Study on the Imp in manuscript
|
||||||
|
16 OK 10.48550/arXiv.2211.04325 Will we run out of data? Limits of LLM scaling based on human-ge in manuscript
|
||||||
|
17 OK 10.1038/s41562-023-01742-2 Machine culture verified by hand
|
||||||
|
18 OK 10.48550/arXiv.2304.03442 Generative Agents: Interactive Simulacra of Human Behavior in manuscript
|
||||||
|
19 OK 10.48550/arXiv.2402.01680 Large Language Model based Multi-Agents: A Survey of Progress an in manuscript
|
||||||
|
20 OK 10.48550/arXiv.2509.10147 Virtual Agent Economies in manuscript
|
||||||
|
21 OK 10.1038/s41586-024-07566-y AI models collapse when trained on recursively generated data crossref (matched: AI models collapse when trained on recursively generated data)
|
||||||
|
22 OK 10.1371/journal.pcbi.1002510 Structural Drift: The Population Dynamics of Sequential Learning crossref (matched: Structural Drift: The Population Dynamics of Sequential Learning)
|
||||||
|
23 OK 10.48550/arXiv.2604.08554 Drift and selection in LLM text ecosystems in manuscript
|
||||||
|
24 OK 10.48550/arXiv.2509.20101 First-Extinction Law for Resampling Processes in manuscript
|
||||||
|
25 OK 10.48550/arXiv.2407.17493 Model Collapse in the Self-Consuming Chain of Diffusion Finetuni in manuscript
|
||||||
|
26 OK 10.1016/s0079-7421(08)60536-8 Catastrophic Interference in Connectionist Networks: The Sequent crossref (matched: Catastrophic Interference in Connectionist Networks: The Sequential Le)
|
||||||
|
27 OK 10.1016/s1364-6613(99)01294-2 Catastrophic forgetting in connectionist networks crossref (matched: Catastrophic forgetting in connectionist networks)
|
||||||
|
28 OK 10.1016/0027-5107(64)90047-8 The relation of recombination to mutational advance crossref (matched: The relation of recombination to mutational advance)
|
||||||
|
29 OK 10.48550/arXiv.2510.16657 Escaping Model Collapse via Synthetic Data Verification: Near-te in manuscript
|
||||||
|
30 OK 10.48550/arXiv.2404.01413 Is Model Collapse Inevitable? Breaking the Curse of Recursion by in manuscript
|
||||||
|
31 OK 10.1093/genetics/16.2.97 EVOLUTION IN MENDELIAN POPULATIONS crossref (matched: EVOLUTION IN MENDELIAN POPULATIONS)
|
||||||
|
32 OK 10.1046/j.1523-1739.1996.10061509.x The One‐Migrant‐per‐Generation Rule in Conservation and Manageme crossref (matched: The One‐Migrant‐per‐Generation Rule in Conservation and Management)
|
||||||
|
33 HAND (pre-DOI source) [Review of] The Origin of Species written by hand
|
||||||
|
34 OK 10.48550/arXiv.2411.02207 Collective Model Intelligence Requires Compatible Specialization in manuscript
|
||||||
|
35 HAND (pre-DOI source) The Genetical Theory of Natural Selection written by hand
|
||||||
|
36 OK 10.1086/280418 Some Genetic Aspects of Sex crossref (matched: Some Genetic Aspects of Sex)
|
||||||
|
37 OK 10.48550/arXiv.2106.09685 LoRA: Low-Rank Adaptation of Large Language Models in manuscript
|
||||||
|
38 OK 10.1016/s0022-5193(87)80029-2 Towards a general theory of adaptive walks on rugged landscapes crossref (matched: Towards a general theory of adaptive walks on rugged landscapes)
|
||||||
|
39 HAND (pre-DOI source) Coadaptation and outbreeding depression written by hand
|
||||||
|
40 OK 10.1162/evco_a_00025 Abandoning Objectives: Evolution Through the Search for Novelty crossref (matched: Abandoning Objectives: Evolution Through the Search for Novelty Alone)
|
||||||
|
41 OK 10.48550/arXiv.2503.05683 WikiBigEdit: Understanding the Limits of Lifelong Knowledge Edit in manuscript
|
||||||
|
42 OK 10.48550/arXiv.2502.04390 In Praise of Stubbornness: An Empirical Case for Cognitive-Disso in manuscript
|
||||||
|
43 OK 10.48550/arXiv.2607.09202 Interference and Retention in Continual Learning in manuscript
|
||||||
|
44 OK 10.1017/s0016672300033140 A general model for the evolution of recombination crossref (matched: A general model for the evolution of recombination)
|
||||||
|
45 OK 10.1006/tpbi.1997.1301 Deleterious Mutations, Variable Epistatic Interactions, and the crossref (matched: Deleterious Mutations, Variable Epistatic Interactions, and the Evolut)
|
||||||
|
46 OK 10.1038/nrg761 Resolving the paradox of sex and recombination crossref (matched: Resolving the paradox of sex and recombination)
|
||||||
|
47 OK 10.1093/genetics/117.3.559 Selection, Generalized Transmission and the Evolution of Modifie crossref (matched: Selection, Generalized Transmission and the Evolution of Modifier Gene)
|
||||||
|
48 OK 10.1093/genetics/139.4.1805 The population genetics of speciation: the evolution of hybrid i crossref (matched: The population genetics of speciation: the evolution of hybrid incompa)
|
||||||
|
49 OK 10.1111/j.0014-3820.2001.tb00628.x THE EVOLUTION OF POSTZYGOTIC ISOLATION: ACCUMULATING DOBZHANSKY- crossref (matched: THE EVOLUTION OF POSTZYGOTIC ISOLATION: ACCUMULATING DOBZHANSKY-MULLER)
|
||||||
|
50 OK 10.48550/arXiv.2209.04836 Git Re-Basin: Merging Models modulo Permutation Symmetries in manuscript
|
||||||
|
51 OK 10.48550/arXiv.2606.23607 Scaling Linear Mode Connectivity and Merging to Billion Paramete in manuscript
|
||||||
|
52 OK 10.48550/arXiv.2410.12766 The Non-Local Model Merging Problem: Permutation Symmetries and in manuscript
|
||||||
|
53 OK 10.48550/arXiv.2607.11997 Are we Merging the Right Models? Impact of Expert Training Durat in manuscript
|
||||||
|
54 OK 10.48550/arXiv.2601.22285 Demystifying Mergeability: Interpretable Properties to Predict M in manuscript
|
||||||
|
55 OK 10.48550/arXiv.2205.12393 Fine-tuned Language Models are Continual Learners in manuscript
|
||||||
|
56 OK 10.48550/arXiv.2403.08763 Simple and Scalable Strategies to Continually Pre-train Large La in manuscript
|
||||||
|
57 OK 10.1080/09540099550039318 Catastrophic Forgetting, Rehearsal and Pseudorehearsal crossref (matched: Catastrophic Forgetting, Rehearsal and Pseudorehearsal)
|
||||||
|
58 OK 10.48550/arXiv.1705.08690 Continual Learning with Deep Generative Replay in manuscript
|
||||||
|
59 OK 10.48550/arXiv.2406.07515 Beyond Model Collapse: Scaling Up with Synthesized Data Requires in manuscript
|
||||||
|
60 OK 10.48550/arXiv.1606.04671 Progressive Neural Networks in manuscript
|
||||||
|
61 OK 10.48550/arXiv.2405.09673 LoRA Learns Less and Forgets Less in manuscript
|
||||||
|
62 OK 10.1037/0033-295x.102.3.419 Why there are complementary learning systems in the hippocampus crossref (matched: Why there are complementary learning systems in the hippocampus and ne)
|
||||||
|
63 OK 10.1016/j.tics.2016.05.004 What Learning Systems do Intelligent Agents Need? Complementary crossref (matched: What Learning Systems do Intelligent Agents Need? Complementary Learni)
|
||||||
|
64 OK 10.48550/arXiv.1805.06370 Progress & Compress: A scalable framework for continual lear verified by hand
|
||||||
|
65 OK 10.48550/arXiv.2212.04089 Editing Models with Task Arithmetic in manuscript
|
||||||
|
66 OK 10.48550/arXiv.2407.06322 MagMax: Leveraging Model Merging for Seamless Continual Learning in manuscript
|
||||||
|
67 OK 10.48550/arXiv.2407.08699 Mitigating Catastrophic Forgetting in Language Transfer via Mode in manuscript
|
||||||
|
68 OK 10.48550/arXiv.2412.06712 How to Merge Your Multimodal Models Over Time? in manuscript
|
||||||
|
69 OK 10.48550/arXiv.1812.05159 An Empirical Study of Example Forgetting during Deep Neural Netw in manuscript
|
||||||
|
70 OK 10.48550/arXiv.2211.08411 Large Language Models Struggle to Learn Long-Tail Knowledge in manuscript
|
||||||
|
71 OK 10.48550/arXiv.2210.00266 Long-Tailed Class Incremental Learning in manuscript
|
||||||
|
72 OK 10.48550/arXiv.2309.10105 Understanding Catastrophic Forgetting in Language Models via Imp in manuscript
|
||||||
|
73 OK 10.48550/arXiv.2311.03099 Language Models are Super Mario: Absorbing Abilities from Homolo in manuscript
|
||||||
|
74 OK 10.48550/arXiv.2203.05482 Model soups: averaging weights of multiple fine-tuned models imp in manuscript
|
||||||
|
75 OK 10.48550/arXiv.2603.09463 An Empirical Study and Theoretical Explanation on Task-Level Mod in manuscript
|
||||||
|
76 OK 10.48550/arXiv.2506.14126 From Memorization to Parameter Interference: How Overtraining Ex in manuscript
|
||||||
|
77 OK 10.1145/2934662 Sex as an algorithm crossref (matched: Sex as an algorithm)
|
||||||
|
78 OK 10.48550/arXiv.2311.09807 The Curious Decline of Linguistic Diversity: Training Language M in manuscript
|
||||||
|
79 OK 10.48550/arXiv.2309.05196 Does Writing with Language Models Reduce Content Diversity? in manuscript
|
||||||
|
80 OK 10.1126/sciadv.adn5290 Generative AI enhances individual creativity but reduces the col crossref (matched: Generative AI enhances individual creativity but reduces the collectiv)
|
||||||
80
paper/manuscript/refs/resolution-report.tsv
Normal file
80
paper/manuscript/refs/resolution-report.tsv
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
1 OK 10.48550/arXiv.2508.06811 Anatomy of a Machine Learning Ecosystem: 2 Million Models on Hug in manuscript
|
||||||
|
2 OK 10.48550/arXiv.2405.18432 Unsupervised Model Tree Heritage Recovery in manuscript
|
||||||
|
3 OK 10.48550/arXiv.2402.00699 PeaTMOSS: A Dataset and Initial Analysis of Pre-Trained Models i in manuscript
|
||||||
|
4 OK 10.48550/arXiv.2306.01708 TIES-Merging: Resolving Interference When Merging Models in manuscript
|
||||||
|
5 OK 10.1038/s42256-024-00975-8 Evolutionary optimization of model merging recipes crossref (matched: Evolutionary optimization of model merging recipes)
|
||||||
|
6 OK 10.48550/arXiv.2403.13257 Arcee's MergeKit: A Toolkit for Merging Large Language Models in manuscript
|
||||||
|
7 OK 10.48550/arXiv.2408.07666 Model Merging in LLMs, MLLMs, and Beyond: Methods, Theories, App in manuscript
|
||||||
|
8 OK 10.48550/arXiv.2503.01155 Nature-Inspired Population-Based Evolution of Large Language Mod in manuscript
|
||||||
|
9 OK 10.48550/arXiv.2508.16204 Competition and Attraction Improve Model Fusion in manuscript
|
||||||
|
10 OK 10.48550/arXiv.2501.05707 Multiagent Finetuning: Self Improvement with Diverse Reasoning C in manuscript
|
||||||
|
11 OK 10.48550/arXiv.2406.11704 Nemotron-4 340B Technical Report in manuscript
|
||||||
|
12 OK 10.48550/arXiv.2412.08905 Phi-4 Technical Report in manuscript
|
||||||
|
13 OK 10.48550/arXiv.2212.10560 Self-Instruct: Aligning Language Models with Self-Generated Inst in manuscript
|
||||||
|
14 OK 10.48550/arXiv.2401.05749 A Shocking Amount of the Web is Machine Translated: Insights fro in manuscript
|
||||||
|
15 OK 10.48550/arXiv.2403.07183 Monitoring AI-Modified Content at Scale: A Case Study on the Imp in manuscript
|
||||||
|
16 OK 10.48550/arXiv.2211.04325 Will we run out of data? Limits of LLM scaling based on human-ge in manuscript
|
||||||
|
17 OK 10.1038/s41562-023-01742-2 Machine culture verified by hand
|
||||||
|
18 OK 10.48550/arXiv.2304.03442 Generative Agents: Interactive Simulacra of Human Behavior in manuscript
|
||||||
|
19 OK 10.48550/arXiv.2402.01680 Large Language Model based Multi-Agents: A Survey of Progress an in manuscript
|
||||||
|
20 OK 10.48550/arXiv.2509.10147 Virtual Agent Economies in manuscript
|
||||||
|
21 OK 10.1038/s41586-024-07566-y AI models collapse when trained on recursively generated data crossref (matched: AI models collapse when trained on recursively generated data)
|
||||||
|
22 OK 10.1371/journal.pcbi.1002510 Structural Drift: The Population Dynamics of Sequential Learning crossref (matched: Structural Drift: The Population Dynamics of Sequential Learning)
|
||||||
|
23 OK 10.48550/arXiv.2604.08554 Drift and selection in LLM text ecosystems in manuscript
|
||||||
|
24 OK 10.48550/arXiv.2509.20101 First-Extinction Law for Resampling Processes in manuscript
|
||||||
|
25 OK 10.48550/arXiv.2407.17493 Model Collapse in the Self-Consuming Chain of Diffusion Finetuni in manuscript
|
||||||
|
26 OK 10.1016/s0079-7421(08)60536-8 Catastrophic Interference in Connectionist Networks: The Sequent crossref (matched: Catastrophic Interference in Connectionist Networks: The Sequential Le)
|
||||||
|
27 OK 10.1016/s1364-6613(99)01294-2 Catastrophic forgetting in connectionist networks crossref (matched: Catastrophic forgetting in connectionist networks)
|
||||||
|
28 OK 10.1016/0027-5107(64)90047-8 The relation of recombination to mutational advance crossref (matched: The relation of recombination to mutational advance)
|
||||||
|
29 OK 10.48550/arXiv.2510.16657 Escaping Model Collapse via Synthetic Data Verification: Near-te in manuscript
|
||||||
|
30 OK 10.48550/arXiv.2404.01413 Is Model Collapse Inevitable? Breaking the Curse of Recursion by in manuscript
|
||||||
|
31 OK 10.1093/genetics/16.2.97 EVOLUTION IN MENDELIAN POPULATIONS crossref (matched: EVOLUTION IN MENDELIAN POPULATIONS)
|
||||||
|
32 OK 10.1046/j.1523-1739.1996.10061509.x The One‐Migrant‐per‐Generation Rule in Conservation and Manageme crossref (matched: The One‐Migrant‐per‐Generation Rule in Conservation and Management)
|
||||||
|
33 HAND (pre-DOI source) [Review of] The Origin of Species written by hand
|
||||||
|
34 OK 10.48550/arXiv.2411.02207 Collective Model Intelligence Requires Compatible Specialization in manuscript
|
||||||
|
35 HAND (pre-DOI source) The Genetical Theory of Natural Selection written by hand
|
||||||
|
36 OK 10.1086/280418 Some Genetic Aspects of Sex crossref (matched: Some Genetic Aspects of Sex)
|
||||||
|
37 OK 10.48550/arXiv.2106.09685 LoRA: Low-Rank Adaptation of Large Language Models in manuscript
|
||||||
|
38 OK 10.1016/s0022-5193(87)80029-2 Towards a general theory of adaptive walks on rugged landscapes crossref (matched: Towards a general theory of adaptive walks on rugged landscapes)
|
||||||
|
39 HAND (pre-DOI source) Coadaptation and outbreeding depression written by hand
|
||||||
|
40 OK 10.1162/evco_a_00025 Abandoning Objectives: Evolution Through the Search for Novelty crossref (matched: Abandoning Objectives: Evolution Through the Search for Novelty Alone)
|
||||||
|
41 OK 10.48550/arXiv.2503.05683 WikiBigEdit: Understanding the Limits of Lifelong Knowledge Edit in manuscript
|
||||||
|
42 OK 10.48550/arXiv.2502.04390 In Praise of Stubbornness: An Empirical Case for Cognitive-Disso in manuscript
|
||||||
|
43 OK 10.48550/arXiv.2607.09202 Interference and Retention in Continual Learning in manuscript
|
||||||
|
44 OK 10.1017/s0016672300033140 A general model for the evolution of recombination crossref (matched: A general model for the evolution of recombination)
|
||||||
|
45 OK 10.1006/tpbi.1997.1301 Deleterious Mutations, Variable Epistatic Interactions, and the crossref (matched: Deleterious Mutations, Variable Epistatic Interactions, and the Evolut)
|
||||||
|
46 OK 10.1038/nrg761 Resolving the paradox of sex and recombination crossref (matched: Resolving the paradox of sex and recombination)
|
||||||
|
47 OK 10.1093/genetics/117.3.559 Selection, Generalized Transmission and the Evolution of Modifie crossref (matched: Selection, Generalized Transmission and the Evolution of Modifier Gene)
|
||||||
|
48 OK 10.1093/genetics/139.4.1805 The population genetics of speciation: the evolution of hybrid i crossref (matched: The population genetics of speciation: the evolution of hybrid incompa)
|
||||||
|
49 OK 10.1111/j.0014-3820.2001.tb00628.x THE EVOLUTION OF POSTZYGOTIC ISOLATION: ACCUMULATING DOBZHANSKY- crossref (matched: THE EVOLUTION OF POSTZYGOTIC ISOLATION: ACCUMULATING DOBZHANSKY-MULLER)
|
||||||
|
50 OK 10.48550/arXiv.2209.04836 Git Re-Basin: Merging Models modulo Permutation Symmetries in manuscript
|
||||||
|
51 OK 10.48550/arXiv.2606.23607 Scaling Linear Mode Connectivity and Merging to Billion Paramete in manuscript
|
||||||
|
52 OK 10.48550/arXiv.2410.12766 The Non-Local Model Merging Problem: Permutation Symmetries and in manuscript
|
||||||
|
53 OK 10.48550/arXiv.2607.11997 Are we Merging the Right Models? Impact of Expert Training Durat in manuscript
|
||||||
|
54 OK 10.48550/arXiv.2601.22285 Demystifying Mergeability: Interpretable Properties to Predict M in manuscript
|
||||||
|
55 OK 10.48550/arXiv.2205.12393 Fine-tuned Language Models are Continual Learners in manuscript
|
||||||
|
56 OK 10.48550/arXiv.2403.08763 Simple and Scalable Strategies to Continually Pre-train Large La in manuscript
|
||||||
|
57 OK 10.1080/09540099550039318 Catastrophic Forgetting, Rehearsal and Pseudorehearsal crossref (matched: Catastrophic Forgetting, Rehearsal and Pseudorehearsal)
|
||||||
|
58 OK 10.48550/arXiv.1705.08690 Continual Learning with Deep Generative Replay in manuscript
|
||||||
|
59 OK 10.48550/arXiv.2406.07515 Beyond Model Collapse: Scaling Up with Synthesized Data Requires in manuscript
|
||||||
|
60 OK 10.48550/arXiv.1606.04671 Progressive Neural Networks in manuscript
|
||||||
|
61 OK 10.48550/arXiv.2405.09673 LoRA Learns Less and Forgets Less in manuscript
|
||||||
|
62 OK 10.1037/0033-295x.102.3.419 Why there are complementary learning systems in the hippocampus crossref (matched: Why there are complementary learning systems in the hippocampus and ne)
|
||||||
|
63 OK 10.1016/j.tics.2016.05.004 What Learning Systems do Intelligent Agents Need? Complementary crossref (matched: What Learning Systems do Intelligent Agents Need? Complementary Learni)
|
||||||
|
64 OK 10.48550/arXiv.1805.06370 Progress & Compress: A scalable framework for continual lear verified by hand
|
||||||
|
65 OK 10.48550/arXiv.2212.04089 Editing Models with Task Arithmetic in manuscript
|
||||||
|
66 OK 10.48550/arXiv.2407.06322 MagMax: Leveraging Model Merging for Seamless Continual Learning in manuscript
|
||||||
|
67 OK 10.48550/arXiv.2407.08699 Mitigating Catastrophic Forgetting in Language Transfer via Mode in manuscript
|
||||||
|
68 OK 10.48550/arXiv.2412.06712 How to Merge Your Multimodal Models Over Time? in manuscript
|
||||||
|
69 OK 10.48550/arXiv.1812.05159 An Empirical Study of Example Forgetting during Deep Neural Netw in manuscript
|
||||||
|
70 OK 10.48550/arXiv.2211.08411 Large Language Models Struggle to Learn Long-Tail Knowledge in manuscript
|
||||||
|
71 OK 10.48550/arXiv.2210.00266 Long-Tailed Class Incremental Learning in manuscript
|
||||||
|
72 OK 10.48550/arXiv.2309.10105 Understanding Catastrophic Forgetting in Language Models via Imp in manuscript
|
||||||
|
73 OK 10.48550/arXiv.2311.03099 Language Models are Super Mario: Absorbing Abilities from Homolo in manuscript
|
||||||
|
74 OK 10.48550/arXiv.2203.05482 Model soups: averaging weights of multiple fine-tuned models imp in manuscript
|
||||||
|
75 OK 10.48550/arXiv.2603.09463 An Empirical Study and Theoretical Explanation on Task-Level Mod in manuscript
|
||||||
|
76 OK 10.48550/arXiv.2506.14126 From Memorization to Parameter Interference: How Overtraining Ex in manuscript
|
||||||
|
77 OK 10.1145/2934662 Sex as an algorithm crossref (matched: Sex as an algorithm)
|
||||||
|
78 OK 10.48550/arXiv.2311.09807 The Curious Decline of Linguistic Diversity: Training Language M in manuscript
|
||||||
|
79 OK 10.48550/arXiv.2309.05196 Does Writing with Language Models Reduce Content Diversity? in manuscript
|
||||||
|
80 OK 10.1126/sciadv.adn5290 Generative AI enhances individual creativity but reduces the col crossref (matched: Generative AI enhances individual creativity but reduces the collectiv)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue