society: multi-locus recombination frame — the vertical claim (E7/E8)
Enter the Lamarckian society with a robust theoretical frame. The single-
locus, fixed-p* model can only express recovery toward a ceiling; the
society's load-bearing claim is vertical -- capability that EXCEEDS any
component. Generalize knowledge to a distribution over genotypes (L
biallelic loci, K=2^L, additive fitness = # correct loci), reusing all the
K-mode machinery. The one new operator is recombination: free recombination
sends p -> product of per-locus marginals (linkage equilibrium).
E8 (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, a genotype no parent had) as parent count
grows and rho->0, while the best single parent (~8.7) and the mean-mixture
"model soup" (~11.6) plateau below. Reuses make_retention_matrix (locus
mastery replaces tail-item retention).
E7 (kind: genotype_lineage) -- the advantage of sex: a single population
adapts toward the optimum; the sexual lineage adapts faster than asexual
(clonal interference) by keeping loci in linkage equilibrium (LD->0 vs LD
spike). Honest scope: a speed advantage, not a permanent Muller's-ratchet
gap (subtle to force); E8 carries the headline.
Metaphor shift (per GG): the society is sexual reproduction with UNBOUNDED
parents, not teacher->pupil. Teacher->pupil caps at the ceiling; n-parent
recombination is combinatorial and generative, 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 reaches ground Riis's single-locus n-grams cannot.
New: knowledge/{genotype,genotype_lineage,society}.py, configs/layer1/{E7,
E8}.yaml, figures/plot_{E7,E8}.py, READMEs, tests/test_genotype.py (+7).
experiment.py dispatch (kind in {genotype_lineage, society}); make layer1
wired. 112 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
871bc39ec6
commit
62c68d6c8c
22 changed files with 879 additions and 3 deletions
62
figures/plot_E7.py
Normal file
62
figures/plot_E7.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_E7.py [results/E7]
|
||||
"""
|
||||
|
||||
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/E7") -> 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, "E7")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(*sys.argv[1:])
|
||||
68
figures/plot_E8.py
Normal file
68
figures/plot_E8.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
"""E8 figure — the vertical claim: n-parent recombination exceeds any parent (Fisher–Muller).
|
||||
|
||||
The society headline. Decorrelated *parents* are specialists (expert on some loci, agnostic on the
|
||||
rest); an *offspring* recombined from all of them can be fitter than any parent — capability that
|
||||
*exceeds* every component, not just recovers a ceiling. Unlike biological sex there is no two-parent
|
||||
limit, so capability climbs toward the optimum as the parent pool grows and decorrelates.
|
||||
|
||||
Two panels: (A) deployed capability (mode-genotype fitness) vs parent count at ρ=0 — sexual
|
||||
recombination reaches the optimum (a genotype no parent had) while the best single parent and the
|
||||
mean-mixture "model soup" plateau below; (B) the decorrelation control — sexual capability vs parent
|
||||
count for ρ ∈ {0, 0.5, 1}: decorrelated parents (ρ=0) climb to the optimum, identical parents (ρ=1)
|
||||
buy nothing. Reads only the committed bundle.
|
||||
|
||||
Usage: python figures/plot_E8.py [results/E8]
|
||||
"""
|
||||
|
||||
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/E8") -> None:
|
||||
df, cfg = load_bundle(results_dir)
|
||||
L = cfg["society"]["L"]
|
||||
rhos = sorted(df["rho"].unique())
|
||||
|
||||
fig, axes = plt.subplots(1, 2, figsize=(13, 5))
|
||||
|
||||
# Panel A: best-parent vs average vs sexual, at rho=0.
|
||||
ax = axes[0]
|
||||
d0 = df[df["rho"] == 0.0]
|
||||
for col, c, lab in [("best_parent", "#7f7f7f", "best single parent"),
|
||||
("average", "#1f77b4", "average (model soup)"),
|
||||
("sexual", "#d62728", "sexual recombination")]:
|
||||
k, m, ci = mean_ci(d0, "K_T", col)
|
||||
ax.errorbar(k, m, yerr=ci, fmt="-o", color=c, capsize=3, label=lab)
|
||||
ax.axhline(L, ls=":", color="green", lw=1, label=f"optimum ($L$={L})")
|
||||
ax.set(xlabel="number of parents $K_T$", ylabel="deployed capability (mode fitness)",
|
||||
title="Recombination exceeds any parent (ρ=0):\nsexual reaches the optimum; soup & best-parent plateau")
|
||||
ax.legend(frameon=False, fontsize=9)
|
||||
|
||||
# Panel B: sexual capability vs K_T for each rho (decorrelation control).
|
||||
ax = axes[1]
|
||||
colors = plt.cm.viridis(np.linspace(0, 0.8, len(rhos)))
|
||||
for rho, c in zip(rhos, colors):
|
||||
sub = df[df["rho"] == rho]
|
||||
k, m, ci = mean_ci(sub, "K_T", "sexual")
|
||||
ax.errorbar(k, m, yerr=ci, fmt="-o", color=c, capsize=3, label=fr"ρ={rho:g}")
|
||||
ax.axhline(L, ls=":", color="green", lw=1, label=f"optimum ($L$={L})")
|
||||
ax.set(xlabel="number of parents $K_T$", ylabel="sexual-recombination capability",
|
||||
title="Decorrelation is the fuel:\nρ=0 climbs to the optimum; ρ=1 (clones) buy nothing")
|
||||
ax.legend(frameon=False, fontsize=9)
|
||||
|
||||
fig.suptitle("E8 — the vertical claim: an offspring recombined from many decorrelated parents "
|
||||
"is fitter than any parent (Fisher–Muller; no two-parent limit)", y=1.02, fontsize=12)
|
||||
fig.tight_layout()
|
||||
savefig(fig, results_dir, "E8")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(*sys.argv[1:])
|
||||
Loading…
Add table
Add a link
Reference in a new issue