New Fig. 1 (experimental-programme schematic); Table 2 to SI; figures in citation order; Fig. 2B legible labels
Replaces the results table with a pipeline figure: five questions x three architecture tiers (exact Wright-Fisher simulator, trained networks, language models), filled cells naming the experiments, dashed cells the honest gaps. Table 1 (the dictionary) stays; Table 2 moves to SI Appendix Table S2. The renumber surfaced a pre-existing citation-order violation (the LLM figure was cited in the recombination section before Figs. 3-6), so figures are renumbered to strict first-citation order (LLM tier is now Fig. 3). Fig. 2B: the montage's baked-in raster labels are cropped away and replaced with vector row numbers under a rotated "generation" header. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkRLcc18rwT2Lysu6PbG7v
This commit is contained in:
parent
073fc33509
commit
0159e2839a
13 changed files with 208 additions and 109 deletions
|
|
@ -1,7 +1,7 @@
|
|||
"""Publication figures for the PNAS draft — unified, lettered, codename-free.
|
||||
|
||||
Re-plots every panel directly from the committed results artifacts into six single-file figures
|
||||
(figs/fig1.pdf .. fig6.pdf): no experiment codenames, no suptitles, no per-panel headline titles
|
||||
Renders fig1 (the experimental-programme schematic) and re-plots every data panel directly from the
|
||||
committed results artifacts (figs/fig2.pdf .. fig7.pdf): no experiment codenames, no suptitles, no per-panel headline titles
|
||||
(interpretation lives in the captions), bold panel letters, one consistent style. The per-experiment
|
||||
figures under results/ remain the exploratory versions; these are the manuscript's.
|
||||
|
||||
|
|
@ -42,8 +42,93 @@ def save(fig, name):
|
|||
print("wrote", OUT / f"{name}.pdf")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- fig 1: grounding + MNIST
|
||||
# ---------------------------------------------------------------- fig 1: experimental programme
|
||||
def fig1():
|
||||
from matplotlib.patches import FancyBboxPatch
|
||||
|
||||
TIERS = [
|
||||
("Exact model", "Wright\u2013Fisher simulator (NumPy)", "closed forms \u00b7 bitwise-reproducible",
|
||||
"#4292c6", "#eaf2fa"),
|
||||
("Trained networks", "RNN \u00b7 MLP \u00b7 VAE on a synthetic oracle;\nconvolutional VAE on MNIST",
|
||||
"sign-level tests \u00b7 exact oracles", "#41ab5d", "#edf8ea"),
|
||||
("Language models", "LoRA specialists on Qwen 0.5B & 7B;\nexact-match verifier",
|
||||
"seed-replicated signs", "#e6550d", "#fdf0e6"),
|
||||
]
|
||||
ROWS = [
|
||||
("Grounding", "how much real data?",
|
||||
["immigration\u2013drift equilibrium:\n$g \\approx 0.05$ retains $\\geq$95% diversity;\nobservation floor $1-e^{-mp}$",
|
||||
"collapse & rescue in every\narchitecture; MNIST: dry 30$\\to$1 modes,\n10% grounding holds 30/30;\nestimator-bias learning kernel",
|
||||
None]),
|
||||
("Recombination", "blend or merge?",
|
||||
["blending conservation law\n(first-order cancellation);\nunion-operator gain; Fisher\u2013Muller",
|
||||
"merge rescues two forgetting\nspecialists ($\\approx$0.50 $\\to$ 0.955)",
|
||||
"merged specialists beat every parent\n(5 seeds at 0.5B; 7B); routing vs\naveraging: the headroom rule"]),
|
||||
("Entangled skills", "who merges with whom?",
|
||||
["NK landscapes: outbreeding\ndepression; directed sex restores\nthe gain; mate-pool breadth optimum",
|
||||
None,
|
||||
"bred-and-screened offspring beat\nthe blind blend in every seed\n(hard, unsaturated tasks)"]),
|
||||
("The composed society", "can the loop sustain itself?",
|
||||
["four-arm ablation: grounding, sex,\ndiversity each removed\n$\\to$ three distinct failures",
|
||||
None,
|
||||
"OPEN"]),
|
||||
("Speciation & prediction", "when does merging fail?",
|
||||
["BDM incompatibility model:\nisolation cliff; quadratic snowball",
|
||||
"barrier decomposition under\npermutation+rescaling; conflict\nsweep 0.97$\\to$0.03; emergent null",
|
||||
"convention conflict $\\to$ hybrid\nbreakdown; duration null; pre-merge\npredictive test (13 cond. $\\times$ 3 seeds)"]),
|
||||
]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(11.4, 5.4))
|
||||
ax.set_axis_off()
|
||||
ax.set_xlim(0, 1)
|
||||
ax.set_ylim(0, 1)
|
||||
x0, gap = 0.16, 0.008
|
||||
cw = (1.0 - x0) / 3
|
||||
row_h, row_top = 0.152, 0.79
|
||||
|
||||
ax.annotate("", xy=(0.995, 0.975), xytext=(x0 + 0.02, 0.975),
|
||||
arrowprops=dict(arrowstyle="->", color="#555", lw=1.1))
|
||||
ax.text(x0 + (1 - x0) / 2, 0.988, "the same population-genetic abstractions (Table 1), increasing realism",
|
||||
ha="center", va="bottom", fontsize=8, style="italic", color="#333")
|
||||
|
||||
for j, (name, arch, guarantee, edge, face) in enumerate(TIERS):
|
||||
x = x0 + j * cw
|
||||
ax.add_patch(FancyBboxPatch((x + gap, 0.795), cw - 2 * gap, 0.16,
|
||||
boxstyle="round,pad=0.004", fc=face, ec=edge, lw=1.4))
|
||||
ax.text(x + cw / 2, 0.944, name, ha="center", va="top", fontsize=9, fontweight="bold", color=edge)
|
||||
ax.text(x + cw / 2, 0.902, arch, ha="center", va="top", fontsize=6.8, linespacing=1.3)
|
||||
ax.text(x + cw / 2, 0.803, guarantee, ha="center", va="bottom", fontsize=6.4,
|
||||
style="italic", color="#555")
|
||||
|
||||
for i, (label, question, cells) in enumerate(ROWS):
|
||||
y1 = row_top - i * row_h
|
||||
y0 = y1 - row_h + 2 * gap
|
||||
yc = (y0 + y1) / 2
|
||||
ax.text(0.0, yc + 0.012, label, ha="left", va="center", fontsize=8, fontweight="bold")
|
||||
ax.text(0.0, yc - 0.022, question, ha="left", va="center", fontsize=6.8, style="italic", color="#555")
|
||||
for j, cell in enumerate(cells):
|
||||
x = x0 + j * cw
|
||||
edge, face = TIERS[j][3], TIERS[j][4]
|
||||
if cell is None:
|
||||
ax.add_patch(FancyBboxPatch((x + gap, y0), cw - 2 * gap, y1 - y0,
|
||||
boxstyle="round,pad=0.004", fc="white", ec="#bbbbbb",
|
||||
lw=0.8, ls=(0, (3, 2))))
|
||||
ax.text(x + cw / 2, yc, "not tested at this tier", ha="center", va="center",
|
||||
fontsize=6.4, style="italic", color="#999")
|
||||
elif cell == "OPEN":
|
||||
ax.add_patch(FancyBboxPatch((x + gap, y0), cw - 2 * gap, y1 - y0,
|
||||
boxstyle="round,pad=0.004", fc="white", ec=edge,
|
||||
lw=0.8, ls=(0, (3, 2))))
|
||||
ax.text(x + cw / 2, yc, "open \u2014 the stated gap", ha="center", va="center",
|
||||
fontsize=6.6, style="italic", color=edge)
|
||||
else:
|
||||
ax.add_patch(FancyBboxPatch((x + gap, y0), cw - 2 * gap, y1 - y0,
|
||||
boxstyle="round,pad=0.004", fc=face, ec=edge, lw=0.9))
|
||||
ax.text(x + cw / 2, yc, cell, ha="center", va="center", fontsize=6.4, linespacing=1.35)
|
||||
save(fig, "fig1")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- fig 2: grounding + MNIST
|
||||
def fig2():
|
||||
from knowledge.analysis import critical_grounding, reduce_to_stationary
|
||||
from knowledge.metrics import heterozygosity
|
||||
from knowledge.truth import make_true_distribution
|
||||
|
|
@ -84,15 +169,21 @@ def fig1():
|
|||
ax = axes[1]
|
||||
from PIL import Image
|
||||
im = np.asarray(Image.open("results/mnist_collapse/mnist_montage.png"))
|
||||
crop = int(im.shape[0] * 0.085) # remove the baked-in title band
|
||||
ax.imshow(im[crop:], interpolation="bilinear")
|
||||
# Strip the baked-in title band and left label margin (raster text is unreadable at panel
|
||||
# size); measured on the committed montage: boxes span y >= 69, x >= 75, row centres below.
|
||||
top, left = 60, 68
|
||||
ax.imshow(im[top:, left:], interpolation="bilinear")
|
||||
for yc, g in zip((101.5, 191.5, 282.0, 372.5, 462.5), (0, 4, 8, 12, 15)):
|
||||
ax.text(-10, yc - top, str(g), ha="right", va="center", fontsize=8.5)
|
||||
ax.text(-0.055, 0.5, "generation", transform=ax.transAxes, rotation=90,
|
||||
ha="center", va="center", fontsize=8.5)
|
||||
ax.set_axis_off()
|
||||
letter(ax, "B", x=-0.02)
|
||||
save(fig, "fig1")
|
||||
save(fig, "fig2")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- fig 2: blending vs union + Fisher–Muller
|
||||
def fig2():
|
||||
# ---------------------------------------------------------------- fig 4: blending vs union + Fisher–Muller
|
||||
def fig4():
|
||||
fig, axes = plt.subplots(1, 2, figsize=(10.6, 3.5))
|
||||
|
||||
df, _ = load_bundle("results/E4")
|
||||
|
|
@ -122,11 +213,11 @@ def fig2():
|
|||
ax.set(xlabel="number of parents", ylabel="offspring capability")
|
||||
ax.legend()
|
||||
letter(ax, "B")
|
||||
save(fig, "fig2")
|
||||
save(fig, "fig4")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- fig 3: rugged landscapes
|
||||
def fig3():
|
||||
# ---------------------------------------------------------------- fig 5: rugged landscapes
|
||||
def fig5():
|
||||
fig, axes = plt.subplots(2, 2, figsize=(10.6, 6.8))
|
||||
|
||||
df9, _ = load_bundle("results/E9")
|
||||
|
|
@ -173,11 +264,11 @@ def fig3():
|
|||
ax.set(xlabel="mate-pool breadth (monogamous → panmictic)", ylabel=ylab)
|
||||
ax.legend(title="ruggedness")
|
||||
letter(ax, L)
|
||||
save(fig, "fig3")
|
||||
save(fig, "fig5")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- fig 4: the society
|
||||
def fig4():
|
||||
# ---------------------------------------------------------------- fig 6: the society
|
||||
def fig6():
|
||||
df, _ = load_bundle("results/E11")
|
||||
arms = [("full", "#2ca02c", "full system"),
|
||||
("no_sex", "#ff7f0e", "no recombination"),
|
||||
|
|
@ -201,11 +292,11 @@ def fig4():
|
|||
ax.legend()
|
||||
ax.set(xlabel="generation", ylabel=ylab)
|
||||
letter(ax, L)
|
||||
save(fig, "fig4")
|
||||
save(fig, "fig6")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- fig 5: speciation, three tiers
|
||||
def fig5():
|
||||
# ---------------------------------------------------------------- fig 7: speciation, three tiers
|
||||
def fig7():
|
||||
fig, axes = plt.subplots(2, 3, figsize=(11.4, 6.6))
|
||||
|
||||
bdm, _ = load_bundle("results/E12")
|
||||
|
|
@ -294,11 +385,11 @@ def fig5():
|
|||
ax.set(xlabel="specialist training (epochs)", ylabel="accuracy", ylim=(0, 1.02))
|
||||
ax.legend()
|
||||
letter(ax, "F")
|
||||
save(fig, "fig5")
|
||||
save(fig, "fig7")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- fig 6: the language-model tier
|
||||
def fig6():
|
||||
# ---------------------------------------------------------------- fig 3: the language-model tier
|
||||
def fig3():
|
||||
import pandas as pd
|
||||
from scipy.stats import spearmanr
|
||||
|
||||
|
|
@ -373,9 +464,9 @@ def fig6():
|
|||
ax.set_xticklabels([l for _, l in preds], fontsize=6.5)
|
||||
ax.set(ylabel="|Spearman ρ| vs merge penalty", ylim=(0, 0.8))
|
||||
letter(ax, "D")
|
||||
save(fig, "fig6")
|
||||
save(fig, "fig3")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for f in (fig1, fig2, fig3, fig4, fig5, fig6):
|
||||
for f in (fig1, fig2, fig3, fig4, fig5, fig6, fig7):
|
||||
f()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue