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
3 KiB
Python
67 lines
3 KiB
Python
"""E14 figure — mating systems: monogamy vs promiscuity (mate-pool breadth) across ruggedness.
|
|
|
|
Three panels, each vs mate-pool breadth (log x: 0.03 = monogamous/structured -> 1.0 = promiscuous/
|
|
panmictic), one line per landscape ruggedness K:
|
|
|
|
(A) best fitness / global optimum — the *champion*. On smooth landscapes (low K) it is maximised by
|
|
wide breadth; as ruggedness rises the peak shifts to an INTERMEDIATE breadth (full promiscuity
|
|
prematurely converges below it) — the mating-system image of E9's "optimal recombination rate
|
|
shrinks with ruggedness".
|
|
(B) mean fitness / global optimum — the *typical* individual. Monotonically favoured by breadth at
|
|
every K: panmixia lifts the whole population toward a good consensus.
|
|
(C) diversity (mean normalised pairwise Hamming) — monotonically DESTROYED by breadth at every K
|
|
(promiscuity homogenises), the reservoir largest under monogamy and on rugged landscapes.
|
|
|
|
The tension between (A)/(C) is the result: promiscuity maximises the typical model and kills diversity;
|
|
on rugged landscapes the best model needs preserved diversity, so an intermediate breadth wins.
|
|
|
|
Usage: python figures/plot_E14.py
|
|
"""
|
|
|
|
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, savefig, letter_axes # noqa: E402
|
|
|
|
|
|
def main() -> None:
|
|
df, _ = load_bundle("results/E14")
|
|
last = df[df["generation"] == df["generation"].max()].copy()
|
|
last["best_n"] = last["best_fitness"] / last["global_opt"]
|
|
last["mean_n"] = last["mean_fitness"] / last["global_opt"]
|
|
Ks = sorted(last["K"].unique())
|
|
cmap = plt.get_cmap("viridis")
|
|
colors = {K: cmap(i / max(1, len(Ks) - 1)) for i, K in enumerate(Ks)}
|
|
|
|
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
|
|
panels = [
|
|
("best_n", "best fitness / global optimum",
|
|
"Best model peaks at intermediate breadth on rugged\nlandscapes (the peak shifts left as $K$ rises)"),
|
|
("mean_n", "mean fitness / global optimum",
|
|
"Population mean rises monotonically with breadth\n(panmixia lifts the whole population)"),
|
|
("diversity", "diversity (mean pairwise Hamming)",
|
|
"Standing diversity falls monotonically with breadth\n(promiscuity homogenises; monogamy preserves)"),
|
|
]
|
|
for ax, (col, ylab, title) in zip(axes, panels):
|
|
for K in Ks:
|
|
g = (last[last["K"] == K].groupby("breadth")[col]
|
|
.agg(["mean", "sem"]).reset_index())
|
|
ax.errorbar(g["breadth"], g["mean"], yerr=1.96 * g["sem"].fillna(0.0),
|
|
marker="o", lw=1.8, capsize=2, color=colors[K], label=f"K={K}")
|
|
ax.set_xscale("log")
|
|
ax.set(xlabel="mate-pool breadth (monogamous ← → promiscuous)", ylabel=ylab)
|
|
ax.set_title(title, fontsize=9)
|
|
ax.legend(title="ruggedness", frameon=False, fontsize=8)
|
|
|
|
fig.tight_layout()
|
|
letter_axes(fig)
|
|
savefig(fig, "results/E14", "E14")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|