align / code /tests /test_audit_g7_p2_nonfrozen_fixes.py
viai957's picture
Add files using upload-large-folder tool
201dc77 verified
Raw History Blame Contribute Delete
13.7 kB
"""Regression tests for the 2026-10-14 audit, group G7 (p2 non-frozen files).
U12-1 --redo must delete the stage's artefacts (and every downstream one), not just the markers.
U12-5 the dev split must be decontaminated against test/MOBench/pool by INFORMAL text too.
U12-7 `align.model.weight_average` main() must take the heavy lock / preflight / thread cap.
U06-1 Qwen3 `_attn_bias` returns None (flash path) for all-ones and right-padded masks.
U06-2 weight_average fsyncs shards/index/marker/dirs before the rename, and a re-run verifies shards.
CPU only, nothing takes the real heavy lock.
Run: CUDA_VISIBLE_DEVICES= .venv/bin/python -m pytest tests/test_audit_g7_p2_nonfrozen_fixes.py -q
"""
from __future__ import annotations
import importlib.util
import json
import os
from pathlib import Path
import pytest
import torch
from test_expert_iteration import EI, FakeRunner, World, stages_done
from test_weight_average import _tensors, _write_ckpt
ROOT = Path(__file__).resolve().parents[1]
# ======================================================================== U12-1
def _round_artefacts(run: Path, r: int = 1):
rd = run / f"round_{r:02d}"
return {
"gen": rd / "gen" / "gen_r0.jsonl", "ver": rd / "gen" / "ver_r0.jsonl", "rundir": rd / "gen" / "run_r0",
"pass_rates": rd / "pass_rates.json", "data": rd / "sft_data", "sft": rd / "sft", "export": rd / "export",
"avg": rd / "avg", "eval": rd / "eval", "report": rd / "report", "manifest": rd / "round_manifest.json",
"select": rd / "select",
}
def _plant_stale(art: dict):
"""Artefacts the fake children do not write but a real run leaves behind (pipeline run dirs, a stale eval file
under a different tag)."""
art["rundir"].mkdir(parents=True, exist_ok=True)
(art["rundir"] / "pipeline.jsonl").write_text("{}")
(art["eval"] / "gen_n4_c999.jsonl").write_text("{}")
def test_redo_train_removes_the_checkpoint_and_every_downstream_artefact(world):
assert EI.main(world.argv(), FakeRunner(world)) == 0
art = _round_artefacts(world.d / "run")
_plant_stale(art)
cfg = EI.parse_args(world.argv(redo="1:train"))
EI.apply_redo(cfg, cfg.redo)
for k in ("sft", "export", "avg", "eval", "report", "manifest"):
assert not art[k].exists(), k
assert not (art["export"].parent / "export_check").exists()
for k in ("gen", "ver", "pass_rates", "data", "select"): # upstream of train: untouched
assert art[k].exists(), k
assert stages_done(world.d / "run") == {"select", "gen_verify", "measure", "sft_data"}
def test_redo_gen_verify_removes_samples_and_everything_after_it(world):
assert EI.main(world.argv(), FakeRunner(world)) == 0
art = _round_artefacts(world.d / "run")
_plant_stale(art)
cfg = EI.parse_args(world.argv(redo="1:gen_verify"))
EI.apply_redo(cfg, cfg.redo)
for k in ("gen", "ver", "rundir", "pass_rates", "data", "sft", "export", "avg", "eval", "report", "manifest"):
assert not art[k].exists(), k
assert art["select"].exists()
def test_redo_reruns_train_from_scratch_and_regenerates_eval(world):
"""End to end: the children must not find the previous model's checkpoint or eval samples."""
assert EI.main(world.argv(), FakeRunner(world)) == 0
seen = {}
class Watching(FakeRunner):
def run(self, argv, env, log):
k = self.key(argv)
L = EI.Layout(cfg, 1)
if k == "train":
seen["ckpt_at_train"] = EI.latest_checkpoint(L.sft)
if k == "eval":
seen["eval_files_at_eval"] = sorted(p.name for p in L.eval.glob("*")) if L.eval.exists() else []
return super().run(argv, env, log)
cfg = EI.parse_args(world.argv(redo="1:train"))
assert EI.main(world.argv(redo="1:train"), Watching(world)) == 0
assert seen["ckpt_at_train"] is None
assert seen["eval_files_at_eval"] == []
def test_redo_of_a_model_stage_also_wipes_later_rounds_but_a_cosmetic_redo_does_not(world):
w = World(world.d)
assert EI.main(w.argv(rounds=2), FakeRunner(w)) == 0
r2 = _round_artefacts(w.d / "run", 2)
assert r2["sft"].exists() and r2["eval"].exists()
cfg = EI.parse_args(w.argv(rounds=2, redo="1:report"))
EI.apply_redo(cfg, cfg.redo)
assert r2["sft"].exists() and r2["eval"].exists() # round 2 consumed round 1's model, which a report redo leaves intact
cfg = EI.parse_args(w.argv(rounds=2, redo="1:train"))
EI.apply_redo(cfg, cfg.redo)
for k in ("gen", "ver", "data", "sft", "export", "avg", "eval", "manifest"):
assert not r2[k].exists(), k
@pytest.fixture
def world(tmp_workdir):
return World(Path(tmp_workdir))
# ======================================================================== U12-5
def _load_bv():
spec = importlib.util.spec_from_file_location("build_valid_g7", ROOT / "scripts" / "p2" / "build_valid.py")
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
INFORMAL = ("Let n be a positive integer such that the sum of the first n odd numbers equals the square of the "
"number of its divisors plus seventeen hundred and forty two")
def _prob(pid, split, informal, sig):
C = EI.C
stmt = f"import Mathlib\n\ntheorem t_{pid.replace('-', '_')} {sig} := by sorry"
return {"problem_id": pid, "source": "s", "split": split, "formal_statement": stmt,
"informal_prefix": f"/-- {informal}-/\n" if informal else None, "statement_sha": C.statement_sha(stmt)}
def test_build_valid_drops_valid_problems_whose_informal_text_overlaps_the_pool(tmp_workdir):
bv = _load_bv()
d = Path(tmp_workdir)
C = bv.C
pd = d / "problems"
pd.mkdir()
C.write_jsonl_atomic(pd / "minif2f.jsonl", [_prob("mf-test", "test", "unrelated words about triangles and circles", "(x : Nat) : x = x")])
C.write_jsonl_atomic(pd / "mobench.jsonl", [_prob("mo", "test", None, "(y : Nat) : y = y")])
# same informal problem, DIFFERENT formalisation (different signature/name): no sha/alpha/name layer sees it
C.write_jsonl_atomic(pd / "train_pool.jsonl", [_prob("Goedel-Pset-1", "train", INFORMAL, "(a : Nat) (h : a = 1) : a + 1 = 2")])
src = d / "valid.jsonl"
rows = [
{"id": "valid_leak", "formal_statement": "theorem valid_leak (n : Nat) (h : n = 5) : n * 2 = 10 := sorry", "informal_stmt": INFORMAL},
{"id": "valid_ok", "formal_statement": "theorem valid_ok (m : Nat) (h : m = 6) : m * 2 = 12 := sorry",
"informal_stmt": "Show that twice six is twelve when m is six"},
]
C.write_jsonl_atomic(src, rows)
out = d / "valid_out.jsonl"
assert bv.main(["--src", str(src), "--problems-dir", str(pd), "--out", str(out), "--no-elab"]) == 0
kept = [r["problem_id"] for r in C.iter_jsonl(out)]
assert kept == ["valid_ok"]
man = json.loads(Path(str(out) + ".manifest.json").read_text())
assert [x["id"] for x in man["dropped"]] == ["valid_leak"] and "informal" in man["dropped"][0]["reason"]
def test_driver_refuses_a_pool_whose_informal_text_overlaps_the_eval_problems(world):
from test_p2_support import write_jsonl
ev = json.loads((world.d / "minif2f.jsonl").read_text().splitlines()[0])
ev["informal_prefix"] = f"/-- {INFORMAL}-/\n"
ev_rows = [ev] + [json.loads(x) for x in (world.d / "minif2f.jsonl").read_text().splitlines()[1:]]
write_jsonl(world.d / "minif2f.jsonl", ev_rows)
cfg = EI.parse_args(world.argv())
assert EI.split_problems(cfg) is None # the pool is clean
pool = [json.loads(x) for x in (world.d / "pool.jsonl").read_text().splitlines()]
pool[0]["informal_prefix"] = f"/-- {INFORMAL}-/\n"
write_jsonl(world.d / "pool.jsonl", pool)
err = EI.split_problems(cfg)
assert err and "informal" in err and pool[0]["problem_id"] in err
# ======================================================================== U12-7
def test_weight_average_main_takes_the_heavy_guards_and_caps_threads(tmp_workdir, monkeypatch):
from align.model import weight_average as wa
from align.utils import guard
calls = []
monkeypatch.setattr(guard, "apply_resource_guards", lambda *a, **k: calls.append((a, k)))
threads = []
monkeypatch.setattr(torch, "set_num_threads", lambda n: threads.append(n))
A, B = _tensors(1, torch.float32), _tensors(2, torch.float32)
_write_ckpt(f"{tmp_workdir}/a", A)
_write_ckpt(f"{tmp_workdir}/b", B)
argv = ["--base", f"{tmp_workdir}/a", "--ft", f"{tmp_workdir}/b", "--ft-weight", "0.5", "--out", f"{tmp_workdir}/o"]
assert wa.main(argv) == 0
assert len(calls) == 1 and calls[0][1].get("heavy") is True and calls[0][1].get("label") == "weight-average"
assert threads and max(threads) <= calls[0][1]["cpu_threads"] <= 8
def test_weight_average_guards_run_before_any_checkpoint_is_read(tmp_workdir, monkeypatch):
"""A refused preflight must leave the output untouched (no .partial, no reads)."""
from align.model import weight_average as wa
from align.utils import guard
def refuse(*a, **k):
raise guard.PreflightRefused("host busy")
monkeypatch.setattr(guard, "apply_resource_guards", refuse)
A, B = _tensors(1, torch.float32), _tensors(2, torch.float32)
_write_ckpt(f"{tmp_workdir}/a", A)
_write_ckpt(f"{tmp_workdir}/b", B)
with pytest.raises(guard.PreflightRefused):
wa.main(["--base", f"{tmp_workdir}/a", "--ft", f"{tmp_workdir}/b", "--ft-weight", "0.5", "--out", f"{tmp_workdir}/o"])
assert not os.path.exists(f"{tmp_workdir}/o") and not os.path.exists(f"{tmp_workdir}/o.partial")
# ======================================================================== U06-2
def test_weight_average_fsyncs_everything_before_the_rename(tmp_workdir, monkeypatch):
from align.model import weight_average as wa
A, B = _tensors(1, torch.bfloat16), _tensors(2, torch.bfloat16)
_write_ckpt(f"{tmp_workdir}/a", A)
_write_ckpt(f"{tmp_workdir}/b", B)
events = []
real_fsync, real_rename = os.fsync, os.rename
def fsync(fd):
try:
events.append(("fsync", os.readlink(f"/proc/self/fd/{fd}")))
except OSError:
events.append(("fsync", "?"))
return real_fsync(fd)
def rename(src, dst, *a, **k):
events.append(("rename", str(src), str(dst)))
return real_rename(src, dst, *a, **k)
monkeypatch.setattr(os, "fsync", fsync)
monkeypatch.setattr(os, "rename", rename)
out = f"{tmp_workdir}/o"
rep = wa.average_checkpoints(f"{tmp_workdir}/a", f"{tmp_workdir}/b", out, 0.5)
ri = next(i for i, e in enumerate(events) if e[0] == "rename" and e[2] == out)
before = {e[1] for e in events[:ri] if e[0] == "fsync"}
after = {e[1] for e in events[ri + 1:] if e[0] == "fsync"}
part = out + ".partial"
shards = [f"{part}/model-{i:05d}-of-{rep['shards']:05d}.safetensors" for i in range(1, rep["shards"] + 1)]
for p in shards + [f"{part}/{wa.INDEX_NAME}", f"{part}/{wa.MARKER}", part]:
assert p in before, f"{p} not fsynced before the rename"
assert os.path.dirname(out) in after, "parent directory not fsynced after the rename"
def _finished(tmp_workdir):
from align.model import weight_average as wa
A, B = _tensors(1, torch.bfloat16), _tensors(2, torch.bfloat16)
_write_ckpt(f"{tmp_workdir}/a", A)
_write_ckpt(f"{tmp_workdir}/b", B)
out = f"{tmp_workdir}/o"
wa.average_checkpoints(f"{tmp_workdir}/a", f"{tmp_workdir}/b", out, 0.5)
return wa, out
def test_rerun_on_a_finished_dir_is_a_noop_when_intact(tmp_workdir):
wa, out = _finished(tmp_workdir)
assert wa.average_checkpoints(f"{tmp_workdir}/a", f"{tmp_workdir}/b", out, 0.5)["skipped"] is True
@pytest.mark.parametrize("damage", ["truncate", "zero_length", "missing"])
def test_rerun_refuses_a_marked_dir_whose_shard_is_damaged(tmp_workdir, damage):
wa, out = _finished(tmp_workdir)
shard = sorted(f for f in os.listdir(out) if f.endswith(".safetensors"))[0]
p = os.path.join(out, shard)
if damage == "missing":
os.remove(p)
else:
size = os.path.getsize(p)
with open(p, "r+b") as f:
f.truncate(0 if damage == "zero_length" else size - 7)
with pytest.raises(wa.AverageError, match="incomplete|damaged|missing|size"):
wa.average_checkpoints(f"{tmp_workdir}/a", f"{tmp_workdir}/b", out, 0.5)
# ======================================================================== U06-1
def _q3():
from align.model import qwen3 as q3
return q3
def test_attn_bias_is_none_for_all_ones_and_right_padded_masks():
q3 = _q3()
bias = q3.Qwen3Model._attn_bias
t = 6
ones = torch.ones(2, t, dtype=torch.long)
right = ones.clone()
right[1, 4:] = 0
right[0, 5:] = 0
assert bias(ones, t, torch.float32, "cpu") is None
assert bias(right, t, torch.float32, "cpu") is None
assert bias(ones.bool(), t, torch.float32, "cpu") is None
def test_attn_bias_is_kept_for_left_padding_and_interior_holes():
q3 = _q3()
bias = q3.Qwen3Model._attn_bias
t = 6
left = torch.ones(2, t, dtype=torch.long)
left[1, :2] = 0
hole = torch.ones(1, t, dtype=torch.long)
hole[0, 3] = 0
for m in (left, hole):
b = bias(m, t, torch.float32, "cpu")
assert b is not None and b.shape == (m.shape[0], 1, t, t)
# a masked key is really masked for a real query
assert bias(left, t, torch.float32, "cpu")[1, 0, 5, 0] < -1e30
def test_attn_bias_still_validates_the_mask_length():
q3 = _q3()
with pytest.raises(ValueError, match="attention_mask covers"):
q3.Qwen3Model._attn_bias(torch.ones(1, 5, dtype=torch.long), 6, torch.float32, "cpu")