|
| 1 | +"""V7-F56: symbolic-dim validation — incompatible (H, nh, head_dim).""" |
| 2 | + |
| 3 | +from __future__ import annotations |
| 4 | + |
| 5 | +import pytest |
| 6 | + |
| 7 | +from cppmega_v4.jsonrpc.schema import VerifyParams |
| 8 | +from cppmega_v4.runner import Pipeline, run_pipeline |
| 9 | + |
| 10 | + |
| 11 | +def _spec(H: int, num_heads: int, head_dim: int) -> VerifyParams: |
| 12 | + return VerifyParams.model_validate({ |
| 13 | + "graph": { |
| 14 | + "nodes": [ |
| 15 | + {"id": "attn", "kind": "attention", |
| 16 | + "params": {"num_heads": num_heads, |
| 17 | + "head_dim": head_dim}}, |
| 18 | + {"id": "mlp", "kind": "mlp", "params": {}}, |
| 19 | + ], |
| 20 | + "edges": [{"src": "attn", "dst": "mlp"}], |
| 21 | + }, |
| 22 | + "dim_env": {"B": 1, "S": 8, "H": H, |
| 23 | + "nh": num_heads, "nkv": 1, "head_dim": head_dim}, |
| 24 | + "loss": {"kind": "cross_entropy", "head_outputs": ["mlp"]}, |
| 25 | + "optim": {"kind": "adamw", |
| 26 | + "groups": [{"matcher": "all", "lr": 1e-3, |
| 27 | + "weight_decay": 0.01, |
| 28 | + "betas": [0.9, 0.95]}]}, |
| 29 | + }) |
| 30 | + |
| 31 | + |
| 32 | +def _verify(spec: VerifyParams): |
| 33 | + return run_pipeline(spec, Pipeline.from_dict({ |
| 34 | + "stages": ["parse", "verify_build_spec"], |
| 35 | + })) |
| 36 | + |
| 37 | + |
| 38 | +def test_v7_f56_compatible_combo_h128_nh8_hd16_passes(): |
| 39 | + """H=128 = nh(8) * head_dim(16). verify ok.""" |
| 40 | + rep = _verify(_spec(H=128, num_heads=8, head_dim=16)) |
| 41 | + vbs = next(s for s in rep.stages if s.name == "verify_build_spec") |
| 42 | + assert vbs.status == "ok", f"compatible combo rejected: {vbs.error}" |
| 43 | + |
| 44 | + |
| 45 | +def test_v7_f56_compatible_combo_h512_nh8_hd64_passes(): |
| 46 | + rep = _verify(_spec(H=512, num_heads=8, head_dim=64)) |
| 47 | + vbs = next(s for s in rep.stages if s.name == "verify_build_spec") |
| 48 | + assert vbs.status == "ok" |
| 49 | + |
| 50 | + |
| 51 | +@pytest.mark.xfail(strict=True, reason=( |
| 52 | + "V7-F56 honest finding: build_model accepts H=128 with nh=3, " |
| 53 | + "head_dim=50 silently (num_heads*head_dim ≠ H). The constructor " |
| 54 | + "lacks a symbolic-dim validator at verify_build_spec. Marked xfail " |
| 55 | + "to track the bug; flipping to non-xfail when the validator lands." |
| 56 | +)) |
| 57 | +def test_v7_f56_incompatible_h128_nh3_hd50_train_fails_loudly(): |
| 58 | + """Should fail with a clear error at verify_build_spec or |
| 59 | + build_model — today it silently produces a broken model.""" |
| 60 | + spec = _spec(H=128, num_heads=3, head_dim=50) |
| 61 | + rep = run_pipeline(spec, Pipeline.from_dict({ |
| 62 | + "stages": ["parse", "verify_build_spec", "build_model", |
| 63 | + "dry_forward"], |
| 64 | + })) |
| 65 | + statuses = {s.name: s.status for s in rep.stages} |
| 66 | + assert "fail" in statuses.values(), ( |
| 67 | + f"incompatible combo passed silently: {statuses}" |
| 68 | + ) |
| 69 | + |
| 70 | + |
| 71 | +def test_v7_f56_dim_env_H_mismatched_with_nh_times_head_dim_observable(): |
| 72 | + """Build the spec and verify the bookkeeping math at the |
| 73 | + Python level — proves the contract the validator SHOULD enforce |
| 74 | + is observable to the gate.""" |
| 75 | + spec = _spec(H=128, num_heads=3, head_dim=50) |
| 76 | + de = spec.dim_env if isinstance( |
| 77 | + spec.dim_env, dict) else spec.dim_env.model_dump() |
| 78 | + H = de["H"] |
| 79 | + nh = de["nh"] |
| 80 | + hd = de["head_dim"] |
| 81 | + assert nh * hd != H, ( |
| 82 | + f"V7-F56 premise broken: nh*hd ({nh}*{hd}) accidentally " |
| 83 | + f"equals H ({H})" |
| 84 | + ) |
0 commit comments