Scaffold plus the Layer 1 analytical core and the first two experiments. - knowledge/: truth, metrics, teachers (2.7.1 shared-switch construction), step, lineage, experiment, config, seeding (imported as `knowledge`). - Validation spine green: neutral decay (Pred 1), fixation (Pred 2), exact mutation-drift equilibrium (Pred 3), union coverage (Pred 5). 68 tests pass. - E1 reproduces tail-first collapse. E2 delivers the headline: a grounding phase boundary g* << 1, with stationary H tracking the exact H_eq closed form (g=0.005 -> 68% of truth diversity; g=0.05 -> 96%). - Reproducibility: uv venv from a hash-pinned uv.lock is the source of truth; every run writes results.parquet + resolved_config.yaml + manifest.json (lib versions, git commit, sha256). Figures and manifests tracked; the large regenerable parquet is gitignored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
14 KiB
Layer 1 Execution Plan — The Lamarckian Society (analytical core)
Created 2026-07-04. Scope: blueprint §7 build-order steps 1–4 (scaffold → Layer 1 complete, laptop-reproducible). Layer 2 is explicitly out of scope here and gated on Layer 1's scientific-validation tests passing.
Source of truth: lamarckian-society-technical-blueprint-v1.md. Where it is silent I record a decision below rather than improvise silently.
Design decisions to pin before coding (blueprint leaves these open)
These are the only places the spec is genuinely underdetermined. Recommendation given for each; flagged for sign-off.
-
Config framework — recommend thin pydantic + PyYAML, not Hydra. Blueprint says "Hydra or a thin equivalent." Hydra is heavyweight and its global-state/
os.chdirbehaviour fights the "passrngexplicitly, results are a pure function of resolved config" requirement. A thin loader (pydantic models for validation + a small sweep-expander) is dependency-light, aligns with the stdlib-first preference, and makes the "write resolved config beside results" contract trivial. Decision: thin pydantic loader. -
Selection fitness
f_i(the reality-anchored score). Blueprint: "fitness is predictive accuracy againstp*" but gives no formula for the discrete model. Decision:f_i = p*_eff_i(truth frequency = fitness; reality-anchored by construction). Post-selection distributionp'_i ∝ p_i^(1−α) · f_i, matching the blueprint's statedw_i ∝ f_i·(p_i)^(−α)withα=0recovering fitness-proportional greedy. Document as a modelling choice;select-then-samplevssample-then-selectis the documented robustness switch (§2.2). -
tail_maskdefinition. Two knobs exist (tail_frac,tail_threshold). Decision: the metric-bearing tail set is the §2.3 definition{i : p*_i < tail_threshold}.tail_fraconly drives thetwocomponentconstruction (fraction of items placed in the low-mass component). Document that forzipf,tail_fracis unused. -
TrueDisttype. Decision: a frozen dataclassTrueDist(p_star: np.ndarray, regions: np.ndarray, tail_mask: np.ndarray)— immutable, sop*cannot be mutated in place (except the deliberate re-mint path, which produces a new object). -
Region partition. Decision: contiguous blocks; require
K % R == 0(assert with a clear error) to keep per-region math clean for v1. -
Re-mint semantics (E6). Decision: re-mint replaces
p_star_effwith a newTrueDistbuilt from currentp_t(fresh tail_mask recomputed onp_t), and the originalTrueDistis retained only for the KL-to-original metric, never for grounding. This is the irreversibility the experiment tests.
Phase 0 — Scaffold & environment
- Install
uv(curl -LsSf https://astral.sh/uv/install.sh | sh; lands in~/.local/bin, no sudo). - Reproducibility path =
uvvenv from a committed, hash-pinneduv.lock(GG decision, 2026-07-04; no Apptainer/Docker for Layer 1).pyproject.toml(Python ≥3.11; deps: numpy, scipy, pandas, pyarrow, matplotlib, pydantic, pyyaml; dev: pytest). Commituv.lock. Create.venvviauv sync. - Repo layout per §5:
src/knowledge/,configs/layer1/,figures/,results/(gitignored),tests/,paper/. Addsrc/lamarckian/package root or makeknowledgeimportable (decide package name — recommendsrc/lamarckian/knowledge/...withsrc-layout). .gitignore(.venv/,results/,__pycache__/,*.parquetunder results but keep hashes).- Seeding util
lamarckian/utils/seeding.py: master seed →np.random.SeedSequence(seed).spawn(n)→ per-replicatenp.random.default_rng(child). No global RNG anywhere. - Config loader
lamarckian/config.py: pydantic schema mirroring the §2.7 YAML, aload_config(path), aexpand_sweeps(cfg) -> list[ResolvedConfig], andwrite_resolved(cfg, dir). - Manifest util:
write_manifest(dir, results_df)recording lib versions, master seed,git rev-parse HEAD, content hash ofresults.parquet. Makefiletargets (env,test,layer1,figures,clean) +pytestskeleton. Gate:make testgreen on a trivial test.- Move
lamarckian-society-technical-blueprint-v1.md→paper/blueprint.mdper §5 (confirm with GG first — it's referenced by name elsewhere).
Phase 1 — Core primitives + null model + VALIDATION GATE
Implement to the normative signatures in §2.7. Order chosen so each piece is unit-testable before the next depends on it.
knowledge/truth.py::make_true_distribution→TrueDist. Supporttail ∈ {zipf, twocomponent}. Unit tests: normalisation, region block sizes, tail_mask matches threshold, determinism from seed.knowledge/metrics.py:forward_kl(withepsfloor, logged),heterozygosity,tail_mass,support_size, plus per-region variants. Unit tests on hand-computed small vectors.knowledge/step.py::generation_step— null path first (single teacher,m=0, selectionnone):c ~ Multinomial(n, p_t),p_{t+1}=c/n. Exactly neutral Wright–Fisher.knowledge/lineage.py::run_lineage(cfg, seed)→ tidy per-generation DataFrame (all §2.3 metrics, global + per-region).tests/test_scientific_validation.py— the spine:- Pred. 1 heterozygosity decay: mean
H_tover replicates matchesH₀(1−1/n)^t. Prefer testing the full deterministic mean-recursion trajectory (subsumes the fixed point), within Monte-Carlo CI. - Pred. 2 fixation probability = initial frequency (long runs, statistical tolerance).
- Pred. 1 heterozygosity decay: mean
- HARD GATE: do not proceed until Pred. 1–2 pass. If drift ≠ analytic decay, the harness is wrong — fix here.
Phase 2 — Grounding + E1 + E2 (the headline)
knowledge/step.py::structured_multinomial(m_vector, p_star, regions, policy, rng)— per-region immigration draws fromp*restricted+renormalised to each region;uniformspreadsmevenly,matchedconcentrates on exercised regions. Returns length-K counts.- Extend
generation_stepwith grounding (pooled draw,g = m/(n+m)). - Pred. 3 validation — exact equilibrium
H_eq = H*·m(2n+m−1)/(n+2nm+m²): run to stationarity (burn-in + late-generation + replicate averaging), assert<0.1%rel. error vs closed form across anmgrid. Also assert them→0andm→∞limits. - Pred. 4 validation — tail-persistence: item of freq
p*_imaintained iffm·p*_i ≳ 1; verify the survival transition location statistically. knowledge/experiment.py::run_experiment(cfg)— sweep grid ×n_replicates; long-form results + CIs; writeresults.parquet+resolved_config.yaml+manifest.json.- E1 config + run:
m=0, single teacher, no selection. ExpectHgeometric decay, support→1, KL diverges, tail-first loss. - E2 config + run: sweep
g, single teacher, uniform grounding, no selection. Locate criticalg*(transition in tail mass / support, since H is smooth in m — the sharp threshold is in discrete tail survival). Reportg*with CI. This is the load-bearing result. figures/plot_E1.py,plot_E2.py— readresults.parquetonly.
Phase 3 — E3–E6
- E3 region-matched grounding. Fixed total
m;uniformvsmatched; one designated inherited-but-unwatered region with a rare tail. Expect uniform lets that region's tail collapse; matched holds it. Per-region metrics essential.plot_E3.py. - §2.7.1 correlated-teacher construction —
knowledge/teachers.py:make_retention_matrix(T, K_T, rho, q, rng)— shared-switch exchangeable Bernoulli.make_correlated_teachers(...)— retention→distributions (head kept atp*; tail atp*_iif retained elsetail_floor; renormalise).region_specialisationoption.- Pred. 5 validation:
make_retention_matrixreproduces marginalq, pairwiseρ, and union coverageU(K_T,ρ,q)=T[ρq+(1−ρ)(1−(1−q)^K_T)]to 3 decimals over a(ρ,q)grid.
- E4 multi-teacher decorrelation. Sweep
K_T∈{1,2,3,5},ρ∈[0,1]at fixedq, matched budget (n/K_Teach). Report both unionUand post-distillation surviving coverage; show their gap shrinks asgrises.plot_E4.py(coverage surface over(K_T,ρ)). - E5 QD vs greedy.
apply_selection(none/greedy/qd, pinned fitness form). Sweep noveltyα. Expect greedy→fixation (H→0), qd holdsHplateau + re-introduces tails.plot_E5.py. - E6 re-mint gate. Re-mint at high vs low
H; track KL to original truth. Expect collapsed re-mint locks KL high forever; gated (high-H) does not.plot_E6.py.
Phase 4 — Reproducibility polish (Layer 1 slice)
configs/layer1/E1..E6.yamlall committed with explicit params (no magic numbers in code).paper/figure_manifest.md— the §6 claim→experiment→figure rows for Layer 1.make layer1runs E1–E6;make figuresregenerates all figures from committed parquet.- Full
test_correctness.py(shapes, normalisation, determinism) +test_scientific_validation.py(Pred. 1–5) green in CI. reproduce.sh(uv sync→make test→make layer1→make figures→ writeREPRODUCED.mddiffing committed result hashes) +README.mdreproduce section. No container — the committeduv.lockis the reproducibility source of truth (per GG, 2026-07-04); a Dockerfile may later wrap the same lockfile for Layer 2's GPU work.
Definition of done (Layer 1)
Every Layer-1 row of blueprint §6 has a committed figure produced by make figures from committed results; all §2.4 analytic checks (Pred. 1–5) pass; make layer1 && make figures reproduces from a clean .venv. Then — and only then — Layer 2 may begin.
Falsifier watch (report honestly if hit)
- E2 tail mass flat in
g, or only stabilises asg→1→ multigenerational thesis refuted. - E3 uniform protects as well as matched → region-matching claim dies.
- E4 no surviving-coverage benefit at matched budget → recombination claim dies.
- E5 qd ≤ greedy stationary
H→ QD does no work. - E6 collapsed lineage recovers original-truth tails after re-mint → irreversibility overstated.
Review — progress log
2026-07-04 — Phases 0 & 1 complete; hard gate PASSED.
- Reorg: docs →
paper/(blueprint.md,the-lamarckian-society-v4.md). src-layout undersrc/knowledge/. - A pre-existing
tests/test_scientific_validation.py(author-supplied, 22 KB) turned out to hard-specify the package contract — implemented to it rather than inventing interfaces. Key contracts it locked (now honoured): package imports asknowledge.*;run_lineage(cfg_dict, seed)returns a tidy per-gen frame with aheterozygositycolumn, rows 0..T;p_0initialises uniform (H_0=1−1/K);metrics.heterozygosityandteachers.make_retention_matrixmatch the reference to 1e-12 / closed form. - Env:
uv0.11.26 installed;pyproject.toml+uv.lockcommitted; numpy 2.5, pandas 3.0, scipy 1.18, pydantic 2.13, pytest 9.1. - Modules written:
metrics,seeding,config(dataclasses +from_dict),truth,teachers,step,lineage. Config is dataclass-based (not pydantic) — the conformance test passes a raw dict; dataclasses validate cleanly and stay stdlib-simple. Pydantic still a dep for the Phase-2 YAML/experiment layer. - Results:
make testgreen — 68 passed (48 scientific-validation, 20 correctness). Conformance tests RAN (not skipped): Pred. 1 (neutral decay), Pred. 3 (exactH_eq), Pred. 5 (union coverage) all pass against the real package. The Pred. 1–2 hard gate is passed, and grounding already conforms to the exact equilibrium.
Design decisions #1 (dataclasses now / pydantic at YAML layer), #2 (fitness f_i=p*_i), #3 (threshold tail_mask), #6 (re-mint discard) all implemented as planned. Region design: each region an identical 1/R-mass block (symmetric; reduces to global Zipf at R=1, matching the reference).
2026-07-04 — Phase 2 complete (E1 + E2).
experiment.py: sweep expansion (Cartesian grid; special-casesg→m), paired replicate seeds (shared across grid points), output contract (results.parquet+resolved_config.yaml+manifest.jsonwith lib versions + git commit + sha256). CLIpython -m knowledge.experiment <cfg>.- E1 (null collapse) — reproduces tail-first collapse: H geometric decay matches
H₀(1−1/n)ᵗwithin CI; tail items die ~10× faster than head items; support 500→1; forward-KL diverges. Figureresults/E1/E1.png. - E2 (headline) —
H_simtracks the exactH_eqclosed form across the sweep; phase boundary atg* ≪ 1: g=0.005 (m=1 real sample vs n=200) → 68% of truth H; g=0.05 → 96%. g=0 slides to ~0.10 over 500 gens. Figureresults/E2/E2.png. Headline result achieved. - Metric subtlety found & fixed: aggregate
tail_massis a drift martingale (mean-conserved), so it's a poor collapse indicator. Addedtail_support/head_support/tail_frac_alive/head_frac_alive; E1 & E2 figures now use tail-item survival, which is honest and monotone. - E2 extended 300→500 generations (GG-approved) so the g=0 arm visibly approaches 0 while g>0 arms sit on plateaus.
- Makefile
layer1/figureswired to E1–E2.make teststill green (68).
Discovered during work
- E2 grounding policy vs. the analytic H_eq: Pred. 3's closed form is derived for plain immigration
Multinomial(m, p*). Implemented aspolicy="proportional", and every policy reduces to it atR=1. E2 should therefore run atR=1(orproportional) so the phase-boundary sweep tracks the exactH_eq; region structure is E3's concern. Decide E2'sinit(uniform vs truth) when building its config. init: {uniform|truth}added toTruthCfg(uniform default, mandated by the decay conformance test). E1/E2 may wanttruthstart for a clean "tail collapses from the truth" story — revisit in Phase 2.
Potential agents
(none proposed yet)