MachineSex/figures/plot_figS10_rugged_landscapes.py
Giorgio Gilestro ab3dc10587 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
2026-09-13 17:00:40 +01:00

65 lines
3 KiB
Python
Raw Permalink 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.

"""E9 figure — landscape robustness: when recombination helps, and the outbreeding-depression risk.
The credibility test for the sexual metaphor. E8 used an additive landscape where recombination
trivially helps; here parents are local optima ("trained models") of a Kauffman NK landscape whose
ruggedness (epistasis) is tunable. Blindly recombining entangled models breaks co-adapted allele
blocks and offspring fall *below* the parents — outbreeding depression — worse the more rugged the
landscape and the higher the recombination rate. With selection (best offspring), a nonzero optimal
recombination rate re-emerges. Design rule: merge freely when skills are complementary; merge
sparingly (and always select) when they are entangled.
Two panels: (A) the risk — mean offspring fitness minus best-parent vs recombination rate, one curve
per ruggedness K (all ≤0, steeper as K grows); (B) with offspring selection — best-of-brood fitness
vs rate per K, showing an intermediate optimum on rugged landscapes. Reads only the bundle.
Usage: python figures/plot_figS10_rugged_landscapes.py [results/figS10_rugged_landscapes]
"""
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, letter_axes # noqa: E402
def main(results_dir: str = "results/figS10_rugged_landscapes") -> None:
df, _ = load_bundle(results_dir)
Ks = sorted(df["K"].unique())
rates = sorted(df["rate"].unique())
colors = plt.cm.viridis(np.linspace(0, 0.85, len(Ks)))
bp = df.groupby("K")["best_parent"].mean()
fig, axes = plt.subplots(1, 2, figsize=(13, 5))
# Panel A: the risk — mean offspring minus best parent vs rate, per K.
ax = axes[0]
for K, c in zip(Ks, colors):
s = df[df["K"] == K].groupby("rate")["mean_offspring"].mean() - bp[K]
ax.plot(s.index, s.values, "-o", color=c, ms=4, label=f"K={K}")
ax.axhline(0, ls=":", color="gray", lw=1)
ax.set(xlabel="recombination rate", ylabel="mean offspring best parent",
title="The risk: outbreeding depression\n(worse with ruggedness K and recombination rate)")
ax.legend(frameon=False, fontsize=8, title="ruggedness")
# Panel B: with selection — best offspring vs rate, per K (intermediate optimum on rugged).
ax = axes[1]
for K, c in zip(Ks, colors):
s = df[df["K"] == K].groupby("rate")["best_offspring"].mean()
ax.plot(s.index, s.values, "-o", color=c, ms=4, label=f"K={K}")
ax.axhline(bp[K], ls=":", color=c, lw=0.8, alpha=0.6)
ax.set(xlabel="recombination rate", ylabel="best-of-brood fitness (with selection)",
title="With offspring selection, an optimal\nrecombination rate re-emerges (dotted = parents)")
ax.legend(frameon=False, fontsize=8, title="ruggedness")
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "figS10_rugged_landscapes")
if __name__ == "__main__":
main(*sys.argv[1:])