MachineSex/figures/plot_E8.py
Giorgio Gilestro 62c68d6c8c 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>
2026-07-05 10:51:41 +01:00

68 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""E8 figure — the vertical claim: n-parent recombination exceeds any parent (FisherMuller).
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 (FisherMuller; no two-parent limit)", y=1.02, fontsize=12)
fig.tight_layout()
savefig(fig, results_dir, "E8")
if __name__ == "__main__":
main(*sys.argv[1:])