Restructure: descriptive tier and experiment names, paper/manuscript

- paper/pnas -> paper/manuscript (venue-neutral)
- configs/layer1 -> configs/inheritance, src/knowledge -> src/inheritance
  (imported as `inheritance`), make layer1 -> make inheritance; layer2 alias dropped
- inheritance and trained-network bundles named after the manuscript figure
  they feed (fig2_grounding_sweep, figS3_rebaselining, ...), or descriptively
  where they feed none; configs keep their `experiment:` value so parquet
  hashes are unchanged, only output.dir moves
- figure scripts, SI figure sources, notebooks, REPRODUCING.md, README and the
  SI Methods/tables updated; make clean no longer deletes tracked manifests;
  reproduce.sh hashes the s{seed}/ layouts too

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y64o8FKP7rCuXzC48pxpMm
This commit is contained in:
Giorgio Gilestro 2026-09-13 17:00:40 +01:00
parent 84124de143
commit ab3dc10587
240 changed files with 477 additions and 476 deletions

View file

@ -0,0 +1,79 @@
"""E12 figure — model speciation: the merge-compatibility limit of the sexual society.
Three panels, reading only the committed bundles. (A) BDM: mean recombinant (hybrid) fitness vs
parental divergence, one line per epistasis density rho, against the rising parent fitness the
compatible -> outbreeding-depression -> hybrid-inviability trajectory, peaking then crashing sooner the
denser the epistasis. (B) BDM: the reproductive-isolation rate (fraction of hybrids below the ancestor)
vs divergence the isolation cliff, moving to lower divergence as epistasis density rises. (C) NK: the
epistasis wedge as landscape ruggedness K grows, recombining two adapted local-optimum parents flips
from a gain to outbreeding depression.
Usage: python figures/plot_fig5_speciation_bdm.py
"""
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, savefig # noqa: E402
def _agg(df, keys, value):
g = df.groupby(keys)[value].agg(["mean", "std", "count"]).reset_index()
g["se"] = g["std"] / np.sqrt(g["count"].clip(lower=1))
return g
def main() -> None:
bdm, _ = load_bundle("results/fig5_speciation_bdm")
nk, _ = load_bundle("results/speciation_bdm_nk")
rhos = sorted(bdm["rho"].unique())
colors = plt.cm.viridis(np.linspace(0.15, 0.85, len(rhos)))
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
# Panel A: hybrid fitness vs divergence, per epistasis density, + parent fitness.
ax = axes[0]
par = _agg(bdm, "divergence", "parent_fitness")
ax.plot(par["divergence"], par["mean"], "k--", lw=1.6, label="parent fitness")
for rho, c in zip(rhos, colors):
g = _agg(bdm[bdm["rho"] == rho], "divergence", "offspring_fitness")
ax.plot(g["divergence"], g["mean"], "-o", color=c, lw=2, label=f"hybrid, ρ={rho}")
ax.fill_between(g["divergence"], g["mean"] - g["se"], g["mean"] + g["se"], color=c, alpha=0.15)
ax.axhline(0, color="#999", lw=0.8, ls=":")
ax.set(xlabel="parental divergence (substitutions $d$)", ylabel="fitness",
title="Hybrid fitness collapses as lineages diverge\n(compatible → outbreeding depression → inviability)")
ax.legend(frameon=False, fontsize=8)
# Panel B: reproductive-isolation rate vs divergence, per epistasis density.
ax = axes[1]
for rho, c in zip(rhos, colors):
g = _agg(bdm[bdm["rho"] == rho], "divergence", "isolation")
ax.plot(g["divergence"], g["mean"], "-o", color=c, lw=2, label=f"ρ={rho}")
ax.set(xlabel="parental divergence (substitutions $d$)", ylabel="reproductive isolation\n(P hybrid inviable)",
ylim=(-0.02, 1.02),
title="The isolation cliff moves to lower divergence\nas epistasis density rises")
ax.legend(frameon=False, fontsize=9, title="epistasis density")
# Panel C: NK epistasis wedge — recombination gain vs ruggedness K.
ax = axes[2]
g = _agg(nk, "K", "offspring_minus_parent")
ax.axhline(0, color="#999", lw=0.8, ls=":")
ax.plot(g["K"], g["mean"], "-o", color="#d62728", lw=2)
ax.fill_between(g["K"], g["mean"] - g["se"], g["mean"] + g["se"], color="#d62728", alpha=0.15)
ax.set(xlabel="landscape ruggedness $K$ (epistasis)", ylabel="recombination gain\n(hybrid worse parent)",
title="Epistasis wedge: recombining adapted parents\nflips from gain to loss as ruggedness grows")
fig.suptitle("E12 — model speciation: when two diverged models are too incompatible to merge",
y=1.02, fontsize=13)
fig.tight_layout()
savefig(fig, "results/fig5_speciation_bdm", "E12")
if __name__ == "__main__":
main()