MachineSex/CLAUDE.md
Giorgio Gilestro d22dd9d535 recombination: reproduce the E4 "merge, don't average" finding in real weights
src/neural/recombine.py mirrors Layer-1 run_coverage but trains K_T specialist RNNs on
assignments from the exact shared-switch retention construction (K_T/rho/q clean; union
matches the closed form), then recombines the measured teacher distributions two ways:
mean (naive pooling) vs oracle-guided max-merge (per-mode strongest teacher, M2N2-style),
each followed by size-n resampling.

Result (8 reps): at rho=0, union rises 0.49->0.96 (supply matches closed form); analytic
surviving_max rises 0.043->0.087 while surviving_mean stays flat ~0.045 — the conservation
law (averaging cancels the union gain, max-merge realises it). At rho=1 (identical
teachers) union and max are flat. The lesson holds in the neural setting; trained-weight
columns show the same signs but noisier (smoothing inflates baseline; deep tail barely
clears n=200 resampling). torch-gated test added. 93 tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 21:49:44 +01:00

13 KiB
Raw Blame History

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/knowledge/) — complete and validated. All six experiments E1E6, 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 WrightFisher 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 (neural phase boundary), architectures (architecture-generality), recombination (the E4 "merge, don't average" finding reproduced in real weights). Remaining: grounding refinement, region_matched, remint, figures, the MNIST tier. 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-v4.md — the perspective paper (the "why").
  • 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 WrightFisher 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/knowledge/). Pure NumPy/SciPy WrightFisher 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. 3exact mutationdrift equilibrium for the implemented immigration model: H_eq = H* · m(2n+m1)/(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(1q)^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 23 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(1q), Var = q(1q)), 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.

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 12. HARD GATE: do not proceed until simulated drift matches the analytic heterozygosity decay E[Hₜ] = H₀(1 1/n)ᵗ.
  3. Layer 1 grounding + E1E2 (the headline result).
  4. Layer 1 E3E6. 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 §45)

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 E1E6
make layer2     # run C1C3 (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/layer1/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.