paper (Phase 2): fold hardened E13 into both versions, citation refresh, arXiv package

Speciation section rewritten around the hardened results: alignment
modulo the full function-preserving symmetry group (answers 2606.23607
preemptively), the hybrid-fitness cliff (0.97 -> 0.03), the mu(S)/2
floor, and the pre-registered emergent converse (no isolation without
functional conflict; the merge rescues forgetting specialists) — in the
abstract, §5, §13 ledger, and the accessible version.

Citation refresh (author names verified via arXiv API): concede
First-Extinction Law (Benati 2509.20101) and quantitative-trait collapse
(Yoon 2407.17493) alongside Riis; add verifier-injection (Yi 2510.16657),
Livnat & Papadimitriou (CACM 2016) as the sex-as-computation precursor,
and the adjacent 2024-26 merge/LMC/multi-agent literature (Ainsworth,
Pari, Zhou, Cao, Sharma, Hu, Kozodoi, Li & Shen, Harris, Chen, Tanaka).

arXiv package (paper/arxiv/): md2tex.py — a small block-based
Markdown->LaTeX converter keeping the Markdown as source of truth —
main.tex, generated body.tex, 3 vector figures; builds clean under
tectonic (20 pp; pdflatex hint guarded for arXiv); ARXIV-SUBMISSION.md
carries categories, license note, and a <=1,920-char abstract. 149 tests
green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkRLcc18rwT2Lysu6PbG7v
This commit is contained in:
Giorgio Gilestro 2026-09-06 12:49:52 +01:00
parent ea051a5f92
commit d6a5c5cacd
11 changed files with 671 additions and 44 deletions

159
paper/arxiv/md2tex.py Normal file
View file

@ -0,0 +1,159 @@
r"""Deterministic Markdown -> LaTeX converter for the arXiv preprint (paper-specific, not general).
Converts `paper/the-evolution-of-sex-for-ai.md` into `body.tex`, which `main.tex` inputs. Kept
deliberately dumb and auditable: the paper uses a small Markdown subset (##/### headings, bold,
italics, inline code, links, bullet/numbered lists, one blockquote, horizontal rules, and
`(Figure: \`path\`.)` figure references), and this script handles exactly that subset. Re-run after
editing the Markdown; the Markdown remains the source of truth.
Usage: python paper/arxiv/md2tex.py
"""
from __future__ import annotations
import re
from pathlib import Path
SRC = Path(__file__).resolve().parents[1] / "the-evolution-of-sex-for-ai.md"
OUT = Path(__file__).resolve().parent / "body.tex"
# Figure references in the text -> (graphics file under figs/, caption).
FIGURES = {
"results/E14/E14.png": ("figs/E14.pdf",
"Mating systems (E14): the best mate-pool breadth shrinks as skills get more entangled. "
"(A) best fitness peaks at intermediate breadth on rugged landscapes; (B) the population mean "
"is monotonically favoured by promiscuity; (C) diversity is monotonically destroyed by it."),
"results/E12/E12.png": ("figs/E12.pdf",
"Model speciation, analytic (E12): hybrid fitness vs divergence traces compatible $\\rightarrow$ "
"outbreeding depression $\\rightarrow$ inviability; the isolation cliff arrives earlier the "
"denser the incompatibilities (epistasis), and damage grows super-linearly (the Orr--Turelli "
"snowball)."),
"results/speciation_real/speciation_real.png": ("figs/speciation_real.pdf",
"Model speciation in real weights (E13). (A) the merge barrier decomposed by alignment "
"strength: the independent-init barrier is a coordinate artefact (removed by alignment); the "
"conflict barrier survives even the full function-preserving symmetry group. (B) the isolation "
"cliff: residual barrier rises and hybrid accuracy falls ($0.97 \\rightarrow 0.03$) with "
"functional conflict. (C) the pre-registered emergent test: divergent-but-compatible "
"specialists develop no isolation at any divergence --- the merge instead rescues them "
"(Fisher--Muller)."),
}
UNICODE = {
"": "---", "": "--", "": r"\(\rightarrow\)", "": r"\(\approx\)",
"": r"\(\geq\)", "×": r"\(\times\)", "·": r"\(\cdot\)", "μ": r"\(\mu\)",
}
SPECIALS = {"&": r"\&", "%": r"\%", "#": r"\#", "_": r"\_", "$": r"\$",
"~": r"\textasciitilde{}", "^": r"\textasciicircum{}"}
def esc(s: str) -> str:
s = s.replace("\\", r"\textbackslash{}")
for k, v in SPECIALS.items():
s = s.replace(k, v)
for k, v in UNICODE.items():
s = s.replace(k, v)
return s
def inline(s: str) -> str:
"""Escape + convert inline markup. Code spans are protected, then bold, italic, links."""
parts = re.split(r"(`[^`]*`)", s)
out = []
for p in parts:
if p.startswith("`") and p.endswith("`") and len(p) >= 2:
out.append(r"\texttt{" + esc(p[1:-1]) + "}")
else:
p = esc(p)
p = re.sub(r"\[([^\]]+)\]\((https?://[^)]+)\)", r"\\href{\2}{\1}", p)
p = re.sub(r"\*\*([^*]+)\*\*", r"\\textbf{\1}", p)
p = re.sub(r"\*([^*]+)\*", r"\\emph{\1}", p)
p = re.sub(r'"([^"]+)"', r"``\1''", p) # straight quotes -> LaTeX quotes
out.append(p)
return "".join(out)
def figure_block(md_path: str) -> str:
gfx, caption = FIGURES[md_path]
return ("\\begin{figure*}[t]\\centering\n"
f"\\includegraphics[width=\\textwidth]{{{gfx}}}\n"
f"\\caption{{{caption}}}\n\\end{{figure*}}\n")
def convert(text: str) -> str:
"""Block-based conversion: soft-wrapped lines are joined per paragraph/item BEFORE inline
conversion, so bold/italic/code spans and figure pointers crossing a line break work."""
fig_queue: list[str] = []
def fig_sub(m):
path = m.group(1)
if path in FIGURES:
fig_queue.append(figure_block(path))
return ""
return m.group(0)
lines = text.split("\n")
i = 0
# Skip the header block (title/subtitle/author) up to and including the first horizontal rule:
# main.tex composes the title page itself.
while i < len(lines) and lines[i].strip() != "---":
i += 1
i += 1
# Group into blocks separated by blank lines; a block is a heading, rule, quote, list, or paragraph.
blocks: list[list[str]] = []
cur: list[str] = []
for line in lines[i:]:
if line.strip() == "":
if cur:
blocks.append(cur); cur = []
else:
cur.append(line)
if cur:
blocks.append(cur)
def emit_para(joined: str, out: list[str]) -> None:
joined = re.sub(r"\(Figure: `([^`]+)`\.?\)", fig_sub, joined)
joined = re.sub(r"\s{2,}", " ", joined).strip()
if joined:
out.append(inline(joined))
out.append("")
while fig_queue:
out.append(fig_queue.pop(0)); out.append("")
out: list[str] = []
for block in blocks:
first = block[0].strip()
if first == "---" and len(block) == 1:
out.append("\\medskip\\hrule\\medskip"); out.append("")
elif first.startswith("## "):
out.append(f"\\section*{{{inline(first[3:])}}}"); out.append("")
elif first.startswith("### "):
out.append(f"\\subsection*{{{inline(first[4:])}}}"); out.append("")
elif first.startswith("> "):
joined = " ".join(l.strip().lstrip("> ").strip() for l in block)
out.append("\\begin{quote}" + inline(joined) + "\\end{quote}"); out.append("")
elif re.match(r"^(- |\d+\. )", first):
env = "itemize" if first.startswith("- ") else "enumerate"
out.append(f"\\begin{{{env}}}")
items: list[str] = []
for l in block:
s = l.strip()
if re.match(r"^(- |\d+\. )", s):
items.append(re.sub(r"^(- |\d+\. )", "", s))
else:
items[-1] = items[-1] + " " + s # soft-wrapped continuation of the item
for it in items:
it = re.sub(r"\(Figure: `([^`]+)`\.?\)", fig_sub, it)
out.append("\\item " + inline(it.strip()))
out.append(f"\\end{{{env}}}"); out.append("")
while fig_queue:
out.append(fig_queue.pop(0)); out.append("")
else:
emit_para(" ".join(l.strip() for l in block), out)
return "\n".join(out) + "\n"
if __name__ == "__main__":
OUT.write_text(convert(SRC.read_text()))
print(f"wrote {OUT}")