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

@ -1,11 +1,11 @@
"""Eyeball diagnostic: watch a dry MNIST lineage collapse, generation by generation.
Unlike the `plot_mnist` figure (a pure function of committed parquet), this **re-runs** a short
Unlike the `plot_fig2_mnist_collapse` figure (a pure function of committed parquet), this **re-runs** a short
dry VAE lineage and saves a grid of freshly-generated digits at a few generations, so the
collapse is visible directly early generations show varied digits, late generations degenerate
toward a single blurry mode. Diagnostic only; not part of the reproducible figure set.
Usage: python figures/mnist_montage.py [results/mnist_collapse]
Usage: python figures/mnist_montage.py [results/fig2_mnist_collapse]
"""
from __future__ import annotations
@ -28,7 +28,7 @@ _SNAP_GENS = [0, 4, 8, 12, 15] # generations to snapshot
_COLS = 12 # sample digits per row
def main(out_dir: str = "results/mnist_collapse") -> None:
def main(out_dir: str = "results/fig2_mnist_collapse") -> None:
cfg = MnistCfg()
data = load_mnist(cfg.data_root)
td = make_mnist_truth(cfg)

View file

@ -24,8 +24,8 @@ sys.path.insert(0, str(Path(__file__).parent))
from _figlib import load_bundle, mean_ci, savefig # noqa: E402
sys.path.insert(0, str(Path(__file__).parents[1] / "src"))
from knowledge.analysis import critical_grounding, reduce_to_stationary # noqa: E402
from knowledge.metrics import heterozygosity # noqa: E402
from inheritance.analysis import critical_grounding, reduce_to_stationary # noqa: E402
from inheritance.metrics import heterozygosity # noqa: E402
from neural.config import SyntheticCfg # noqa: E402
from neural.synthetic import make_mode_truth # noqa: E402

View file

@ -24,7 +24,7 @@ sys.path.insert(0, str(Path(__file__).parent))
from _figlib import load_bundle, mean_ci, savefig # noqa: E402
sys.path.insert(0, str(Path(__file__).parents[1] / "src"))
from knowledge.metrics import heterozygosity # noqa: E402
from inheritance.metrics import heterozygosity # noqa: E402
from neural.config import SyntheticCfg # noqa: E402
from neural.synthetic import make_mode_truth # noqa: E402

View file

@ -2,7 +2,7 @@
Shows tail-first collapse under pure neutral drift: geometric H decay matching the
analytic law, tail items dying faster than head items, support -> 1 and forward-KL
diverging. Usage: python figures/plot_E1.py [results/E1]
diverging. Usage: python figures/plot_collapse_null.py [results/collapse_null]
"""
from __future__ import annotations
@ -17,7 +17,7 @@ sys.path.insert(0, str(Path(__file__).parent))
from _figlib import load_bundle, mean_ci, savefig # noqa: E402
def main(results_dir: str = "results/E1") -> None:
def main(results_dir: str = "results/collapse_null") -> None:
df, cfg = load_bundle(results_dir)
n = cfg["dynamics"]["n"]
@ -57,7 +57,7 @@ def main(results_dir: str = "results/E1") -> None:
fig.suptitle("E1 — distillation without grounding collapses, tail first", y=1.02)
fig.tight_layout()
savefig(fig, results_dir, "E1")
savefig(fig, results_dir, "collapse_null")
if __name__ == "__main__":

View file

@ -5,7 +5,7 @@ stationary H vs g tracking the exact H_eq, with an operational g* (where H first
0.95·H*) and its bootstrap CI, and g=0 marked as a finite-time artifact; (C) tail coverage
by item-count vs truth-mass both stay low, the deep tail is largely unrescuable at
feasible grounding; (D) per-rarity-band survival the m·p*_i1 threshold made visible
(deep bands lag, motivating E4/E6). Usage: python figures/plot_E2.py [results/E2]
(deep bands lag, motivating E4/E6). Usage: python figures/plot_fig2_grounding_sweep.py [results/fig2_grounding_sweep]
"""
from __future__ import annotations
@ -20,12 +20,12 @@ sys.path.insert(0, str(Path(__file__).parent))
from _figlib import load_bundle, mean_ci, savefig, letter_axes # noqa: E402
sys.path.insert(0, str(Path(__file__).parents[1] / "src"))
from knowledge.analysis import critical_grounding, reduce_to_stationary # noqa: E402
from knowledge.metrics import heterozygosity # noqa: E402
from knowledge.truth import make_true_distribution # noqa: E402
from inheritance.analysis import critical_grounding, reduce_to_stationary # noqa: E402
from inheritance.metrics import heterozygosity # noqa: E402
from inheritance.truth import make_true_distribution # noqa: E402
def main(results_dir: str = "results/E2") -> None:
def main(results_dir: str = "results/fig2_grounding_sweep") -> None:
df, cfg = load_bundle(results_dir)
n = cfg["dynamics"]["n"]
td = make_true_distribution(cfg["truth"]["K"], 1, "zipf", cfg["truth"]["tail_frac"],
@ -108,7 +108,7 @@ def main(results_dir: str = "results/E2") -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "E2")
savefig(fig, results_dir, "fig2_grounding_sweep")
if __name__ == "__main__":

View file

@ -10,7 +10,7 @@ Four panels, dry (g=0) vs grounded, mean ± 95% CI across replicates: (A) forwar
(B) support size (distinct modes alive); (C) tail truth-mass alive; (D) heterozygosity. Reads the
committed bundle (parquet) + manifest.json only.
Usage: python figures/plot_mnist.py [results/mnist_collapse]
Usage: python figures/plot_fig2_mnist_collapse.py [results/fig2_mnist_collapse]
"""
from __future__ import annotations
@ -26,7 +26,7 @@ sys.path.insert(0, str(Path(__file__).parent))
from _figlib import load_bundle, savefig, letter_axes # noqa: E402
sys.path.insert(0, str(Path(__file__).parents[1] / "src"))
from knowledge.metrics import heterozygosity # noqa: E402
from inheritance.metrics import heterozygosity # noqa: E402
from neural.config import MnistCfg # noqa: E402
from neural.mnist_data import make_mnist_truth # noqa: E402
@ -39,7 +39,7 @@ def _traj(df, g, col):
return gens, grp.mean().to_numpy(), 1.96 * grp.sem().to_numpy()
def main(results_dir: str = "results/mnist_collapse") -> None:
def main(results_dir: str = "results/fig2_mnist_collapse") -> None:
df, cfg = load_bundle(results_dir)
syn = MnistCfg(**cfg["mnist"])
H_star = heterozygosity(make_mnist_truth(syn).p_star)
@ -72,7 +72,7 @@ def main(results_dir: str = "results/mnist_collapse") -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "mnist_collapse")
savefig(fig, results_dir, "fig2_mnist_collapse")
if __name__ == "__main__":

View file

@ -13,7 +13,7 @@ almost immediately; (C) the self-consumption signature — conformity minus true
population's mutual agreement exceeds its real capability), largest for no_grounding. Reads only the
committed bundle.
Usage: python figures/plot_E11.py [results/E11]
Usage: python figures/plot_fig4_society_ablation.py [results/fig4_society_ablation]
"""
from __future__ import annotations
@ -32,7 +32,7 @@ _ARMS = [("full", "#2ca02c", "full society"),
("no_grounding", "#d62728", "no grounding (self-consumption)")]
def main(results_dir: str = "results/E11") -> None:
def main(results_dir: str = "results/fig4_society_ablation") -> None:
df, _ = load_bundle(results_dir)
arms = [a for a in _ARMS if a[0] in set(df["arm"].unique())]
g_opt = df["global_opt"].mean()
@ -61,7 +61,7 @@ def main(results_dir: str = "results/E11") -> None:
fig.suptitle("E11 — the dynamic Lamarckian society: grounding + directed sex + diversity climb to "
"the optimum; remove any one and it breaks (the vertical claim, C3)", y=1.02, fontsize=12)
fig.tight_layout()
savefig(fig, results_dir, "E11")
savefig(fig, results_dir, "fig4_society_ablation")
if __name__ == "__main__":

View file

@ -8,7 +8,7 @@ vs divergence — the isolation cliff, moving to lower divergence as epistasis d
epistasis wedge as landscape ruggedness K grows, recombining two adapted local-optimum parents flips
from a gain to outbreeding depression.
Usage: python figures/plot_E12.py
Usage: python figures/plot_fig5_speciation_bdm.py
"""
from __future__ import annotations
@ -30,8 +30,8 @@ def _agg(df, keys, value):
def main() -> None:
bdm, _ = load_bundle("results/E12")
nk, _ = load_bundle("results/E12_nk")
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)))
@ -72,7 +72,7 @@ def main() -> None:
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/E12", "E12")
savefig(fig, "results/fig5_speciation_bdm", "E12")
if __name__ == "__main__":

View file

@ -12,7 +12,7 @@ Two panels: (A) the risk — mean offspring fitness minus best-parent vs recombi
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_E9.py [results/E9]
Usage: python figures/plot_figS10_rugged_landscapes.py [results/figS10_rugged_landscapes]
"""
from __future__ import annotations
@ -27,7 +27,7 @@ sys.path.insert(0, str(Path(__file__).parent))
from _figlib import load_bundle, savefig, letter_axes # noqa: E402
def main(results_dir: str = "results/E9") -> None:
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())
@ -58,7 +58,7 @@ def main(results_dir: str = "results/E9") -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "E9")
savefig(fig, results_dir, "figS10_rugged_landscapes")
if __name__ == "__main__":

View file

@ -10,7 +10,7 @@ Two panels: (A) deployed capability vs landscape ruggedness — best single pare
directed sex, and the global optimum; (B) each strategy's edge over the best parent, making the
random-sex collapse and the directed-sex rescue explicit. Reads only the committed bundle.
Usage: python figures/plot_E10.py [results/E10]
Usage: python figures/plot_figS11_directed_recombination.py [results/figS11_directed_recombination]
"""
from __future__ import annotations
@ -24,7 +24,7 @@ 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/E10") -> None:
def main(results_dir: str = "results/figS11_directed_recombination") -> None:
df, _ = load_bundle(results_dir)
fig, axes = plt.subplots(1, 2, figsize=(13, 5))
@ -57,7 +57,7 @@ def main(results_dir: str = "results/E10") -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "E10")
savefig(fig, results_dir, "figS11_directed_recombination")
if __name__ == "__main__":

View file

@ -3,7 +3,7 @@
At matched grounding, greedy (directional) selection drives the lineage toward the
fittest items and collapses diversity, while quality-diversity selection (a novelty bonus
w_i f_i·p_i^{-alpha}) maintains a high stationary heterozygosity that rises with the
novelty exponent alpha. Usage: python figures/plot_E5.py [results/E5]
novelty exponent alpha. Usage: python figures/plot_figS12_quality_diversity.py [results/figS12_quality_diversity]
"""
from __future__ import annotations
@ -18,7 +18,7 @@ sys.path.insert(0, str(Path(__file__).parent))
from _figlib import load_bundle, savefig, letter_axes # noqa: E402
def main(results_dir: str = "results/E5") -> None:
def main(results_dir: str = "results/figS12_quality_diversity") -> None:
df, cfg = load_bundle(results_dir)
last = int(cfg["generations"] * 0.8)
@ -72,7 +72,7 @@ def main(results_dir: str = "results/E5") -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "E5")
savefig(fig, results_dir, "figS12_quality_diversity")
if __name__ == "__main__":

View file

@ -15,7 +15,7 @@ panmictic), one line per landscape ruggedness K:
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
Usage: python figures/plot_figS13_mating_breadth.py
"""
from __future__ import annotations
@ -30,7 +30,7 @@ from _figlib import load_bundle, savefig, letter_axes # noqa: E402
def main() -> None:
df, _ = load_bundle("results/E14")
df, _ = load_bundle("results/figS13_mating_breadth")
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"]
@ -60,7 +60,7 @@ def main() -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, "results/E14", "E14")
savefig(fig, "results/figS13_mating_breadth", "E14")
if __name__ == "__main__":

View file

@ -10,7 +10,7 @@ Three panels: (A) forward-KL trajectories per architecture, dry (solid) vs groun
(C) tail-item survival, dry vs grounded, grouped by architecture (all rise). Reads only the
committed bundle.
Usage: python figures/plot_architectures.py [results/architectures]
Usage: python figures/plot_figS1_architectures.py [results/figS1_architectures]
"""
from __future__ import annotations
@ -29,7 +29,7 @@ _ARCH_LABEL = {"histogram": "histogram\n(exact)", "rnn": "GRU\n(autoregressive)"
"mlp": "MLP\n(causal-masked)"}
def main(results_dir: str = "results/architectures") -> None:
def main(results_dir: str = "results/figS1_architectures") -> None:
df, cfg = load_bundle(results_dir)
kinds = [k for k in _ARCH_ORDER if k in set(df["kind"].unique())]
g_dry, g_wet = min(df["g"].unique()), max(df["g"].unique())
@ -74,7 +74,7 @@ def main(results_dir: str = "results/architectures") -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "architectures")
savefig(fig, results_dir, "figS1_architectures")
if __name__ == "__main__":

View file

@ -4,7 +4,7 @@ Re-minting freezes the current distribution as the new grounding reference and d
the original truth. Re-minting a collapsed lineage locks in the collapse: KL to the
original truth diverges, because the lost original tails can no longer be grounded.
Gating re-mint on diversity refuses to re-mint while collapsed and keeps KL bounded;
re-minting a healthy lineage is harmless. Usage: python figures/plot_E6.py [results/E6]
re-minting a healthy lineage is harmless. Usage: python figures/plot_figS3_rebaselining.py [results/figS3_rebaselining]
"""
from __future__ import annotations
@ -26,7 +26,7 @@ STYLE = {
}
def main(results_dir: str = "results/E6") -> None:
def main(results_dir: str = "results/figS3_rebaselining") -> None:
df, cfg = load_bundle(results_dir)
period = cfg["dynamics"]["remint"]["period"]
G = cfg["generations"]
@ -69,7 +69,7 @@ def main(results_dir: str = "results/E6") -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "E6")
savefig(fig, results_dir, "figS3_rebaselining")
if __name__ == "__main__":

View file

@ -3,7 +3,7 @@
Shows that grounding must *overlap* the content it protects. At the same total budget,
uniform grounding spreads thin and lets the exercised region's tail collapse, while
matched grounding concentrates on that region and keeps its rare items alive (at the cost
of the regions it does not touch). Usage: python figures/plot_E3.py [results/E3]
of the regions it does not touch). Usage: python figures/plot_figS5_aimed_grounding.py [results/figS5_aimed_grounding]
Metric: per-region tail-item survival. (Per-region *heterozygosity* is confounded by
region mass under matched grounding, so it is deliberately not used here.)
@ -21,7 +21,7 @@ sys.path.insert(0, str(Path(__file__).parent))
from _figlib import load_bundle, savefig, letter_axes # noqa: E402
def main(results_dir: str = "results/E3") -> None:
def main(results_dir: str = "results/figS5_aimed_grounding") -> None:
df, cfg = load_bundle(results_dir)
R = cfg["truth"]["R"]
exercised = cfg["dynamics"]["grounding"]["exercised"]
@ -66,7 +66,7 @@ def main(results_dir: str = "results/E3") -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "E3")
savefig(fig, results_dir, "figS5_aimed_grounding")
if __name__ == "__main__":

View file

@ -14,7 +14,7 @@ recovery grounding (≈Layer-1's 0.048) and the note that full recovery needs mu
smoothing model; (D) the metric-choice panel H and tail-survival are flat/non-monotone
while forward-KL responds. Reads only the committed bundle.
Usage: python figures/plot_grounding.py [results/grounding]
Usage: python figures/plot_figS6_grounding_rnn.py [results/figS6_grounding_rnn]
"""
from __future__ import annotations
@ -29,8 +29,8 @@ sys.path.insert(0, str(Path(__file__).parent))
from _figlib import load_bundle, mean_ci, savefig, letter_axes # noqa: E402
sys.path.insert(0, str(Path(__file__).parents[1] / "src"))
from knowledge.analysis import reduce_to_stationary # noqa: E402
from knowledge.metrics import heterozygosity # noqa: E402
from inheritance.analysis import reduce_to_stationary # noqa: E402
from inheritance.metrics import heterozygosity # noqa: E402
from neural.config import SyntheticCfg # noqa: E402
from neural.synthetic import make_mode_truth # noqa: E402
@ -63,7 +63,7 @@ def _recovery_gstar(piv: np.ndarray, gs: np.ndarray, frac: float, seed: int = 7)
return float(pt), float(lo), float(hi)
def main(results_dir: str = "results/grounding") -> None:
def main(results_dir: str = "results/figS6_grounding_rnn") -> None:
df, cfg = load_bundle(results_dir)
syn = SyntheticCfg(**cfg["synthetic"])
H_star = heterozygosity(make_mode_truth(syn).p_star)
@ -133,7 +133,7 @@ def main(results_dir: str = "results/grounding") -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "grounding")
savefig(fig, results_dir, "figS6_grounding_rnn")
if __name__ == "__main__":

View file

@ -4,7 +4,7 @@ Three panels tell the honest story: (A) union coverage rises with K_T and decorr
matching the exact closed form (recombination *supplies* the tail); (B) that supply is
realised in the pupil only under a union-preserving merge mean-mixture distillation
dilutes it away (flat in K_T) while max-merge keeps it; (C) the union-surviving gap.
Usage: python figures/plot_E4.py [results/E4]
Usage: python figures/plot_figS8_multiparent_union.py [results/figS8_multiparent_union]
"""
from __future__ import annotations
@ -23,7 +23,7 @@ def U_closed(K_T, rho, q):
return rho * q + (1 - rho) * (1 - (1 - q) ** K_T)
def main(results_dir: str = "results/E4") -> None:
def main(results_dir: str = "results/figS8_multiparent_union") -> None:
df, cfg = load_bundle(results_dir)
q = cfg["coverage"]["q"]
K_Ts = sorted(df["K_T"].unique())
@ -71,7 +71,7 @@ def main(results_dir: str = "results/E4") -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "E4")
savefig(fig, results_dir, "figS8_multiparent_union")
if __name__ == "__main__":

View file

@ -11,7 +11,7 @@ mean-mixture "model soup" plateau below; (B) the decorrelation control — sexua
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]
Usage: python figures/plot_figS9_specialist_superparent.py [results/figS9_specialist_superparent]
"""
from __future__ import annotations
@ -26,7 +26,7 @@ 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:
def main(results_dir: str = "results/figS9_specialist_superparent") -> None:
df, cfg = load_bundle(results_dir)
L = cfg["society"]["L"]
rhos = sorted(df["rho"].unique())
@ -60,7 +60,7 @@ def main(results_dir: str = "results/E8") -> None:
fig.tight_layout()
letter_axes(fig)
savefig(fig, results_dir, "E8")
savefig(fig, results_dir, "figS9_specialist_superparent")
if __name__ == "__main__":

View file

@ -13,7 +13,7 @@ one knob of the learning kernel:
estimator REMOVES collapse pressure.
Analytic arms are read from results/kernel_{sharpen,smooth}; the neural reference endpoints
(dashed) are read from the committed results/mnist_collapse and results/grounding parquets so the
(dashed) are read from the committed results/fig2_mnist_collapse and results/figS6_grounding_rnn parquets so the
figure is a pure function of committed artifacts.
Usage: python figures/plot_kernel.py
@ -31,8 +31,8 @@ sys.path.insert(0, str(Path(__file__).parent))
from _figlib import load_bundle, savefig, letter_axes # noqa: E402
sys.path.insert(0, str(Path(__file__).parents[1] / "src"))
from knowledge.metrics import heterozygosity # noqa: E402
from knowledge.truth import make_true_distribution # noqa: E402
from inheritance.metrics import heterozygosity # noqa: E402
from inheritance.truth import make_true_distribution # noqa: E402
def _mean_traj(df, knob, val, col):
@ -52,8 +52,8 @@ def _neural_dry(results_dir, col, stationary_frac=0.0):
def main() -> None:
sh, sh_cfg = load_bundle("results/kernel_sharpen")
sm, sm_cfg = load_bundle("results/kernel_smooth")
sh, sh_cfg = load_bundle("results/figS2_kernel_sharpen")
sm, sm_cfg = load_bundle("results/figS2_kernel_smooth")
Hstar_sh = heterozygosity(make_true_distribution(
sh_cfg["truth"]["K"], 1, "zipf", 0.5, sh_cfg["truth"]["zipf_s"], 0,
tail_threshold=sh_cfg["truth"]["tail_threshold"]).p_star)
@ -62,10 +62,10 @@ def main() -> None:
tail_threshold=sm_cfg["truth"]["tail_threshold"]).p_star)
# Neural reference endpoints (dashed) from the committed neural runs.
vae_H = _neural_dry("results/mnist_collapse", "heterozygosity")
vae_sup = _neural_dry("results/mnist_collapse", "support_size")
rnn_H = _neural_dry("results/grounding", "heterozygosity", stationary_frac=0.4)
rnn_KL = _neural_dry("results/grounding", "forward_kl", stationary_frac=0.4)
vae_H = _neural_dry("results/fig2_mnist_collapse", "heterozygosity")
vae_sup = _neural_dry("results/fig2_mnist_collapse", "support_size")
rnn_H = _neural_dry("results/figS6_grounding_rnn", "heterozygosity", stationary_frac=0.4)
rnn_KL = _neural_dry("results/figS6_grounding_rnn", "forward_kl", stationary_frac=0.4)
fig, axes = plt.subplots(2, 2, figsize=(13, 9))
NEU, KER = "#1f77b4", "#d62728"
@ -111,7 +111,7 @@ def main() -> None:
ax.legend(frameon=False, fontsize=8)
fig.tight_layout()
for d in ("results/kernel_sharpen", "results/kernel_smooth"):
for d in ("results/figS2_kernel_sharpen", "results/figS2_kernel_smooth"):
letter_axes(fig)
savefig(fig, d, "kernel")

View file

@ -10,7 +10,7 @@ Two panels: (A) mean-fitness adaptation curves, asexual vs sexual, over generati
disequilibrium over generations asexual holds beneficial alleles in disequilibrium (scattered
across genotypes) while sexual drives it to ~0 (assembled), the mechanism of the speed gap.
Usage: python figures/plot_E7.py [results/E7]
Usage: python figures/plot_sexual_vs_asexual_lineage.py [results/sexual_vs_asexual_lineage]
"""
from __future__ import annotations
@ -24,7 +24,7 @@ sys.path.insert(0, str(Path(__file__).parent))
from _figlib import load_bundle, mean_ci, savefig # noqa: E402
def main(results_dir: str = "results/E7") -> None:
def main(results_dir: str = "results/sexual_vs_asexual_lineage") -> None:
df, cfg = load_bundle(results_dir)
L = cfg["genotype"]["L"]
arms = [(0.0, "#7f7f7f", "asexual (clonal)"), (1.0, "#d62728", "sexual (recombining)")]
@ -55,7 +55,7 @@ def main(results_dir: str = "results/E7") -> None:
fig.suptitle("E7 — the advantage of sex: recombination reassorts beneficial alleles that arose "
"in different lineages", y=1.02, fontsize=12)
fig.tight_layout()
savefig(fig, results_dir, "E7")
savefig(fig, results_dir, "sexual_vs_asexual_lineage")
if __name__ == "__main__":