Skip to content

Commit d609fc6

Browse files
authored
Merge pull request #14 from tylerbessire/codex/outline-arc-training-and-evaluation-process
fix: canonicalise translate/recolor parameters
2 parents 0521227 + 8e51a1e commit d609fc6

File tree

4 files changed

+49
-10
lines changed

4 files changed

+49
-10
lines changed

AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,11 @@ class MetaCognition:
489489
Date: 2025-09-13
490490
Test Result: pytest tests/test_translate_fix.py passed; python tools/train_guidance_on_arc.py --epochs 1
491491
Notes: Canonicalised 'fill' parameter for translate; legacy 'fill_value' still accepted
492+
[X] Step 4.3 UPDATE3 - Translate/recolor params normalised to integers preventing training failures
493+
Date: 2025-09-13
494+
Test Result: pytest tests/test_translate_fix.py tests/test_recolor_fix.py -q
495+
Notes: Episode loader and DSL cast dy/dx/fill and mapping entries to int
496+
492497

493498

494499
```

arc_solver/dsl.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,19 @@ def op_pad(a: Array, out_h: int, out_w: int) -> Array:
117117

118118

119119
def _canonical_params(name: str, params: Dict[str, Any]) -> Dict[str, Any]:
120-
"""Return a copy of ``params`` with legacy aliases normalised."""
121-
if name == "recolor" and "mapping" not in params and "color_map" in params:
122-
new_params = dict(params)
123-
new_params["mapping"] = new_params.pop("color_map")
124-
return new_params
125-
if name == "translate" and "fill" not in params and "fill_value" in params:
126-
new_params = dict(params)
127-
new_params["fill"] = new_params.pop("fill_value")
128-
return new_params
129-
return params
120+
"""Return a copy of ``params`` with legacy aliases normalised and typed."""
121+
new_params = dict(params)
122+
if name == "recolor":
123+
mapping = new_params.get("mapping") or new_params.pop("color_map", {})
124+
if mapping:
125+
new_params["mapping"] = {int(k): int(v) for k, v in mapping.items()}
126+
elif name == "translate":
127+
if "fill" not in new_params and "fill_value" in new_params:
128+
new_params["fill"] = new_params.pop("fill_value")
129+
for key in ("dy", "dx", "fill"):
130+
if key in new_params and new_params[key] is not None:
131+
new_params[key] = int(new_params[key])
132+
return new_params
130133

131134

132135

arc_solver/neural/episodic.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ def from_dict(cls, data: Dict[str, Any]) -> "Episode":
9595
if op == "recolor":
9696
mapping = params.get("mapping") or params.get("color_map") or {}
9797
params = {"mapping": {int(k): int(v) for k, v in mapping.items()}}
98+
elif op == "translate":
99+
clean = {k: int(v) for k, v in params.items() if v is not None}
100+
if "fill_value" in clean and "fill" not in clean:
101+
clean["fill"] = clean.pop("fill_value")
102+
params = clean
103+
98104
prog_ops.append((op, params))
99105
programs.append(prog_ops)
100106

tests/test_translate_fix.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
import json
2+
13
from pathlib import Path
24
import sys
35

46
import numpy as np
7+
from hypothesis import given, strategies as st
8+
59

610
sys.path.append(str(Path(__file__).resolve().parents[1]))
711
from arc_solver.dsl import apply_program
812
from arc_solver.heuristics_complete import detect_basic_transformations
13+
from arc_solver.neural.episodic import Episode
14+
915

1016

1117
def test_translate_fill_value_alias_and_detection():
@@ -19,3 +25,22 @@ def test_translate_fill_value_alias_and_detection():
1925
# heuristics now emit canonical parameter name
2026
detected = detect_basic_transformations(inp, expected)
2127
assert [("translate", {"dy": 1, "dx": 1, "fill": 0})] in detected
28+
29+
30+
@given(st.integers(-3, 3), st.integers(-3, 3), st.integers(0, 9))
31+
def test_episode_translate_roundtrip(dy: int, dx: int, fill: int) -> None:
32+
"""Episode serialisation normalises translate parameters to ints."""
33+
inp = np.array([[1]])
34+
out = apply_program(inp, [("translate", {"dy": dy, "dx": dx, "fill": fill})])
35+
episode = Episode(
36+
task_signature="sig",
37+
programs=[[('translate', {'dy': str(dy), 'dx': str(dx), 'fill_value': str(fill)})]],
38+
train_pairs=[(inp, out)],
39+
)
40+
data = json.loads(json.dumps(episode.to_dict()))
41+
loaded = Episode.from_dict(data)
42+
op, params = loaded.programs[0][0]
43+
assert op == 'translate'
44+
assert all(isinstance(params[k], int) for k in ('dy', 'dx', 'fill'))
45+
assert np.array_equal(apply_program(inp, [(op, params)]), out)
46+

0 commit comments

Comments
 (0)