summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorVoid Agent <void@jayrup.hermes>2026-08-14 13:21:03 +0100
committerVoid Agent <void@jayrup.hermes>2026-08-14 13:21:03 +0100
commit9fecbe58cc0750e9e39b261a672b0c6e85e6ed4e (patch)
treecfcf24545a0249b7b206a66eaae7f44c587f9709 /tests
parent9c50f31c66e788ff08eeac83f84e90e9bc1a921e (diff)
fix codex BLOCKERs: global fixed layout invariance, min_steps off-by-one, integers EOS alias, rerun guard, eval prereg-literal codes + dual-checkpoint honesty; +7 regression tests
Diffstat (limited to 'tests')
-rw-r--r--tests/test_codex_fixes.py104
-rw-r--r--tests/test_data.py9
2 files changed, 110 insertions, 3 deletions
diff --git a/tests/test_codex_fixes.py b/tests/test_codex_fixes.py
new file mode 100644
index 0000000..9e14113
--- /dev/null
+++ b/tests/test_codex_fixes.py
@@ -0,0 +1,104 @@
+"""Regression tests for codex-review fixes (design/reviews/codex-review.md)."""
+import torch
+
+from src.config import Config
+from src.data import build_examples, decode_tokens, make_batch
+from src.model_api import build_model, greedy_decode
+
+
+def test_mixed_batch_vs_singleton_invariance_rnn():
+ cfg = Config(model="rnn")
+ m = build_model(cfg)
+ ex = build_examples([2, 42, 99], cfg)
+ batch = make_batch(ex, cfg)
+ out_batch = m(batch["x"], batch["y_in"])
+ for i in range(3):
+ single = make_batch([ex[i]], cfg)
+ out_single = m(single["x"], single["y_in"])
+ assert torch.allclose(out_batch["logits"][i], out_single["logits"][0], atol=1e-6), f"row {i}"
+ assert torch.allclose(out_batch["halt_steps"][i], out_single["halt_steps"][0], atol=1e-6)
+
+
+def test_mixed_batch_vs_singleton_invariance_transformer():
+ cfg = Config(model="transformer")
+ m = build_model(cfg)
+ ex = build_examples([2, 42, 99], cfg)
+ batch = make_batch(ex, cfg)
+ out_batch = m(batch["x"], batch["y_in"])
+ for i in range(3):
+ single = make_batch([ex[i]], cfg)
+ out_single = m(single["x"], single["y_in"])
+ assert torch.allclose(out_batch["logits"][i], out_single["logits"][0], atol=1e-6), f"row {i}"
+
+
+def test_greedy_decode_layout_invariant():
+ cfg = Config(model="transformer")
+ m = build_model(cfg)
+ ex = build_examples([42], cfg)
+ single = make_batch(ex, cfg)
+ out_a = greedy_decode(m, single["x"], cfg)
+ raw = torch.tensor([[4, 2]], dtype=torch.long) # unpadded — greedy must left-pad internally
+ out_b = greedy_decode(m, raw, cfg)
+ assert torch.equal(out_a, out_b)
+
+
+def test_min_steps_floor_reachable():
+ """min_steps off-by-one fix: earliest halt is step min_steps (forced-1 halt prob -> exactly floor)."""
+ cfg = Config(model="rnn", min_steps=2)
+ m = build_model(cfg)
+ with torch.no_grad():
+ m.halt_head.bias.fill_(50.0) # p -> 1 at the first free step
+ ex = build_examples([2, 7, 42], cfg)
+ b = make_batch(ex, cfg)
+ out = m(b["x"], b["y_in"])
+ assert torch.allclose(out["halt_steps"], torch.full_like(out["halt_steps"], float(cfg.min_steps)), atol=1e-3)
+
+
+def test_pad_logits_cannot_change_loss():
+ """Loss and grads must be insensitive to logits at padded target positions."""
+ cfg = Config(model="rnn")
+ m = build_model(cfg)
+ b = make_batch(build_examples([2, 7], cfg), cfg)
+ out = m(b["x"], b["y_in"])
+ y_safe = b["y"].clamp(max=cfg.vocab - 1)
+ loss = torch.nn.functional.cross_entropy(out["logits"].reshape(-1, cfg.vocab),
+ y_safe.reshape(-1), reduction="none")
+ mask = b["y_mask"].float().reshape(-1)
+ l1 = (loss * mask).sum() / mask.sum()
+ l1.backward()
+ grads = {k: v.grad.clone() for k, v in m.named_parameters() if v.grad is not None}
+ m.zero_grad()
+ out2 = m(b["x"], b["y_in"])
+ logits2 = out2["logits"].clone()
+ pad_positions = ~b["y_mask"] # (B,T_out)
+ logits2[pad_positions] += 100.0 # huge perturbation on pad positions only
+ loss2 = torch.nn.functional.cross_entropy(logits2.reshape(-1, cfg.vocab),
+ y_safe.reshape(-1), reduction="none")
+ l2 = (loss2 * mask).sum() / mask.sum()
+ assert torch.allclose(l1.detach(), l2.detach())
+ l2.backward()
+ for k, v in m.named_parameters():
+ if v.grad is not None:
+ assert torch.allclose(grads[k], v.grad, atol=1e-6), k
+
+
+def test_integers_vocab_eos_not_aliased():
+ """Value 101 (= next_prime(100)) must not collide with EOS in integers mode."""
+ cfg = Config(vocab_mode="integers", range_end=100)
+ ex = build_examples([100], cfg)
+ _, target = ex[0]
+ assert target[0] == 101 and target[1] == cfg.eos_id
+ assert cfg.eos_id != 101
+ assert cfg.vocab == 103
+ assert decode_tokens(target, cfg) == 101
+
+
+def test_act_weights_sum_to_one():
+ """ACT mass conservation: steps must always lie in [min_steps, K] for random states."""
+ cfg = Config(model="rnn", max_steps=20, min_steps=2)
+ m = build_model(cfg)
+ b = make_batch(build_examples([2, 7, 42, 99, 3, 15, 60, 97], cfg), cfg)
+ out = m(b["x"], b["y_in"])
+ s = out["halt_steps"]
+ assert bool((s >= cfg.min_steps).all())
+ assert bool((s <= cfg.max_steps).all())
diff --git a/tests/test_data.py b/tests/test_data.py
index fe84e94..cd4e620 100644
--- a/tests/test_data.py
+++ b/tests/test_data.py
@@ -30,7 +30,7 @@ def test_encode_decode_roundtrip_integers():
cfg = Config(vocab_mode="integers", range_end=100)
for n in [2, 42, 100]:
assert decode_tokens(encode_int(n, cfg) + [cfg.eos_id], cfg) == n
- assert cfg.vocab == 102 # 0..101 + EOS
+ assert cfg.vocab == 103 # values 0..101, EOS=102, pad=103 (no alias with target 101)
def test_splits_sizes_and_no_overlap():
@@ -60,8 +60,8 @@ def test_make_batch_shapes_and_padding():
cfg = Config()
ex = build_examples([2, 7, 42, 99], cfg)
b = make_batch(ex, cfg)
- assert b["x"].shape == (4, 2) # max input len 2 (42, 99)
- assert b["y"].shape == (4, 4) # max output len 4 ("101"+EOS)
+ assert b["x"].shape == (4, 3) # GLOBAL layout: left-padded to 3 digits (max of [2,100])
+ assert b["y"].shape == (4, 4) # GLOBAL layout: right-padded to 4 ("101"+EOS)
assert b["y_in"].shape == (4, 4)
assert b["y_mask"].shape == (4, 4)
# pad positions masked out
@@ -69,6 +69,9 @@ def test_make_batch_shapes_and_padding():
assert b["y_mask"][0, 1]
# BOS position of y_in is eos_id
assert (b["y_in"][:, 0] == cfg.eos_id).all()
+ # LEFT-padding: input 2 sits at the last column, pads at the front
+ assert b["x"][0, 2] == 2 and b["x"][0, 0] == cfg.pad_id
+ assert b["x"][2, 1] == 4 and b["x"][2, 2] == 2
def test_flagged_composites_are_composite():