Clarity pass over the main text (36-item audit), Discussion rewrite and cut, acknowledgements, Souly et al. as ref 62, lettered SI panels, model section moved under Results; plus the untracked curriculum/society/compose/smol configs, runners, figures, stats and tests that the SI already cites. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y64o8FKP7rCuXzC48pxpMm
67 lines
2.9 KiB
Python
67 lines
2.9 KiB
Python
"""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, letter_axes # 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.tight_layout()
|
||
letter_axes(fig)
|
||
savefig(fig, results_dir, "E8")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main(*sys.argv[1:])
|