Removed from main (all preserved on the dev branch): the arXiv build and
its sources, design documents (blueprint, results summary, review responses,
essay drafts), tasks/ and CLAUDE.md, the cover letter and reference tooling,
two unused manuscript figures, and every experiment that feeds no figure or
number in the paper: the collapse null, the sexual-vs-asexual lineage, the
NK speciation variant, the 0.5B single-seed LLM prototypes, the compose and
society experiments with their calibration and pilot runs, and their
configs, runners, tests, figure scripts and PBS jobs. Their result bundles
are moved to results/_archive/ (ignored) so the parquets stay on disk.
Also: plot_llm_speciation reads the s{seed}/ layout; the mating-breadth
plot writes under its bundle name; Makefile targets reduced to the kept
experiments; REPRODUCING.md and README point to dev for the rest.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y64o8FKP7rCuXzC48pxpMm
70 lines
3.1 KiB
Python
70 lines
3.1 KiB
Python
"""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")
|
||
rhos = sorted(bdm["rho"].unique())
|
||
colors = plt.cm.viridis(np.linspace(0.15, 0.85, len(rhos)))
|
||
|
||
fig, axes = plt.subplots(1, 2, figsize=(11, 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")
|
||
|
||
|
||
fig.suptitle("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", "fig5_speciation_bdm")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|