Manuscript revision and pending experiment work, snapshot before restructuring

Clarity pass over the main text (36-item audit), Discussion rewrite and cut,
acknowledgements, Souly et al. as ref 62, lettered SI panels, model section
moved under Results; plus the untracked curriculum/society/compose/smol
configs, runners, figures, stats and tests that the SI already cites.

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 16:54:09 +01:00
parent e4804adabc
commit 84124de143
450 changed files with 52813 additions and 1202 deletions

110
tests/test_llm_compose.py Normal file
View file

@ -0,0 +1,110 @@
"""Tests for the composition experiment's pure pieces and its execution verifier (prereg v3 §7)."""
import numpy as np
import pytest
from llm.compose import predicted_composition, score_composed, score_gsm8k, union_exceedance
from llm.compose_data import ProgTask
from llm.execute import ExecResult, extract_code, numeric_match, run_solution, verify_program
from llm.tasks import Task
# ---------------------------------------------------------------- execution verifier
def test_executes_and_matches_reference():
ok, res = verify_program("```python\ndef solution():\n return 6*7\n```", 42.0)
assert ok and res.ok and res.value == 42.0 and res.status == "ok"
def test_rejects_wrong_answer_but_still_ran():
ok, res = verify_program("def solution():\n return 41", 42.0)
assert not ok and res.ok and res.value == 41.0
@pytest.mark.parametrize("code,status", [
("def solution():\n while True: pass", "timeout"),
("def solution():\n return len([0]*10**9)", "memory"),
("x = 1", "no_solution"),
("def solution(:\n return 1", "error:SyntaxError"),
("def solution():\n return solution()", "recursion"),
("", "no_code"),
])
def test_hazards_are_contained(code, status):
assert run_solution(code, timeout_s=8.0).status == status
def test_sandbox_blocks_writes_outside_jail_and_network():
assert run_solution(
"def solution():\n open('/tmp/_llm_compose_escape','w').write('x'); return 1"
).status == "error:PermissionError"
assert run_solution(
"import socket\ndef solution():\n socket.socket().connect(('1.1.1.1',80)); return 1"
).status == "error:PermissionError"
import os
assert not os.path.exists("/tmp/_llm_compose_escape")
def test_sandbox_allows_writes_inside_its_own_jail():
# scratch files are fine — the jail is a fresh temp dir destroyed after the run
assert run_solution("def solution():\n open('s.txt','w').write('x'); return 1").ok
def test_extract_code_prefers_last_fence_and_drops_prose():
got = extract_code("Sure:\n```python\ndef solution():\n return 1\n```\nHope that helps!")
assert got == "def solution():\n return 1"
assert extract_code("def solution():\n return 2") == "def solution():\n return 2"
def test_numeric_match_tolerances():
assert numeric_match(1e6, 1e6 + 1) # relative tolerance
assert numeric_match(0.0, 0.0)
assert not numeric_match(None, 1.0)
assert not numeric_match(1.0, 2.0)
def test_execution_is_deterministic():
code = "def solution():\n return sum(range(1000))"
assert len({run_solution(code).value for _ in range(3)}) == 1
# ---------------------------------------------------------------- scoring and the prediction
def test_score_composed_counts_correct_and_executable_separately():
tasks = [ProgTask("p", 42.0, 0), ProgTask("p", 42.0, 1), ProgTask("p", 42.0, 2)]
comps = ["def solution():\n return 42", # correct
"def solution():\n return 7", # ran, wrong
"def solution(:\n"] # did not run
acc, ok, ran = score_composed(comps, tasks)
assert acc == pytest.approx(1 / 3) and ok.tolist() == [True, False, False]
assert ran == pytest.approx(2 / 3)
def test_score_gsm8k_reads_after_the_hash_marker():
tasks = [Task("math", "p", "18"), Task("math", "p", "7")]
assert score_gsm8k(["reasoning blah\n#### 18", "the answer is 9"], tasks) == pytest.approx(0.5)
def test_union_exceedance_is_solved_by_merge_and_no_parent():
merged = np.array([True, True, True, False])
pa = np.array([True, False, False, False])
pb = np.array([False, True, False, True])
assert union_exceedance(merged, [pa, pb]) == pytest.approx(0.25) # only item 2
assert union_exceedance(merged, [merged]) == 0.0
def test_predicted_composition_is_anchored_at_generation_zero():
q_m = np.array([0.9, 0.8, 0.7]); q_c = np.array([0.9, 0.85, 0.8])
rho = np.array([0.2, 0.4, 0.6])
pred = predicted_composition(q_m, q_c, rho, observed0=0.30)
assert pred[0] == pytest.approx(0.30) # one free scale, fixed at t=0
assert pred[1] > pred[2] # decays with q and rho
# decays faster than either parent alone: product form plus the decorrelation term
assert pred[2] / pred[0] < min(q_m[2] / q_m[0], q_c[2] / q_c[0])
def test_predicted_composition_handles_degenerate_start():
pred = predicted_composition(np.array([0.0, 0.0]), np.array([0.0, 0.0]),
np.array([0.0, 0.0]), observed0=0.0)
assert np.all(np.isfinite(pred))