MachineSex/paper/arxiv/md2tex.py
Giorgio Gilestro d6a5c5cacd 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
2026-09-06 12:49:52 +01:00

159 lines
6.7 KiB
Python
Raw 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.

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}")