Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,13 @@ class MetaCognition:
Date: 2025-09-12
Test Result: pytest tests/test_recolor_fix.py passed
Notes: Standardised 'mapping' parameter across heuristics; episodic loader normalises keys

[X] Step 4.3 UPDATE2 - Translate parameter mismatch fixed preventing training warnings
Date: 2025-09-13
Test Result: pytest tests/test_translate_fix.py passed; python tools/train_guidance_on_arc.py --epochs 1
Notes: Canonicalised 'fill' parameter for translate; legacy 'fill_value' still accepted


```

---
Expand Down
27 changes: 22 additions & 5 deletions arc_solver/dsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def op_transpose(a: Array) -> Array:
return transpose_grid(a)


def op_translate(a: Array, dy: int, dx: int, fill: Optional[int] = None) -> Array:
def op_translate(a: Array, dy: int, dx: int, fill: Optional[int] = None, *, fill_value: Optional[int] = None) -> Array:
"""Translate the grid by ``(dy, dx)`` filling uncovered cells.

Parameters
Expand All @@ -69,11 +69,13 @@ def op_translate(a: Array, dy: int, dx: int, fill: Optional[int] = None) -> Arra
Input grid.
dy, dx:
Translation offsets. Positive values move content down/right.
fill:
Optional fill value for uncovered cells. If ``None`` the background
colour of ``a`` is used.
fill, fill_value:
Optional fill value for uncovered cells. ``fill_value`` is an alias for
backward compatibility. When both are ``None`` the background colour of
``a`` is used.
"""
fill_val = 0 if fill is None else fill
chosen = fill if fill is not None else fill_value
fill_val = 0 if chosen is None else chosen
return translate_grid(a, dy, dx, fill=fill_val)


Expand Down Expand Up @@ -114,6 +116,20 @@ def op_pad(a: Array, out_h: int, out_w: int) -> Array:
_sem_cache: Dict[Tuple[bytes, str, Tuple[Tuple[str, Any], ...]], Array] = {}


def _canonical_params(name: str, params: Dict[str, Any]) -> Dict[str, Any]:
"""Return a copy of ``params`` with legacy aliases normalised."""
if name == "recolor" and "mapping" not in params and "color_map" in params:
new_params = dict(params)
new_params["mapping"] = new_params.pop("color_map")
return new_params
if name == "translate" and "fill" not in params and "fill_value" in params:
new_params = dict(params)
new_params["fill"] = new_params.pop("fill_value")
return new_params
return params



def _norm_params(params: Dict[str, Any]) -> Tuple[Tuple[str, Any], ...]:
"""Normalise parameters to a hashable tuple."""
items: List[Tuple[str, Any]] = []
Expand All @@ -127,6 +143,7 @@ def _norm_params(params: Dict[str, Any]) -> Tuple[Tuple[str, Any], ...]:

def apply_op(a: Array, name: str, params: Dict[str, Any]) -> Array:
"""Apply a primitive operation with semantic caching."""
params = _canonical_params(name, params)
key = (a.tobytes(), name, _norm_params(params))
cached = _sem_cache.get(key)
if cached is not None:
Expand Down
15 changes: 10 additions & 5 deletions arc_solver/dsl_complete.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,20 @@ def transpose(grid: Array) -> Array:
return grid.T


def translate(grid: Array, dx: int = 0, dy: int = 0, fill_value: int = 0) -> Array:
"""Translate grid by (dx, dy) with wraparound or filling."""
def translate(grid: Array, dy: int = 0, dx: int = 0, fill: int = 0, *, fill_value: int | None = None) -> Array:
"""Translate grid by (dy, dx) with wraparound or filling.

``fill_value`` is accepted for backward compatibility.
"""
if fill_value is not None and fill == 0:
fill = fill_value
H, W = grid.shape
result = np.full_like(grid, fill_value)
result = np.full_like(grid, fill)

# Source bounds
src_y_start = max(0, -dy)
src_y_end = min(H, H - dy)
src_x_start = max(0, -dx)
src_x_start = max(0, -dx)
src_x_end = min(W, W - dx)

# Destination bounds
Expand Down Expand Up @@ -693,7 +698,7 @@ def get_operation_signatures() -> Dict[str, List[str]]:
'rotate': ['k'],
'flip': ['axis'],
'transpose': [],
'translate': ['dx', 'dy', 'fill_value'],
'translate': ['dy', 'dx', 'fill'],
'crop': ['top', 'bottom', 'left', 'right'],
'pad': ['top', 'bottom', 'left', 'right', 'fill_value'],
'resize': ['new_height', 'new_width', 'method'],
Expand Down
6 changes: 3 additions & 3 deletions arc_solver/heuristics_complete.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def detect_basic_transformations(inp: Array, out: Array) -> List[List[Tuple[str,

translated = translate_safe(inp, dx, dy)
if np.array_equal(translated, out):
programs.append([('translate', {'dx': dx, 'dy': dy, 'fill_value': 0})])
programs.append([('translate', {'dy': dy, 'dx': dx, 'fill': 0})])

return programs

Expand Down Expand Up @@ -497,8 +497,8 @@ def infer_color_mapping(inp: Array, out: Array) -> Optional[Dict[int, int]]:

for i in range(inp.shape[0]):
for j in range(inp.shape[1]):
inp_color = inp[i, j]
out_color = out[i, j]
inp_color = int(inp[i, j])
out_color = int(out[i, j])

if inp_color in color_map:
if color_map[inp_color] != out_color:
Expand Down
21 changes: 21 additions & 0 deletions tests/test_translate_fix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from pathlib import Path
import sys

import numpy as np

sys.path.append(str(Path(__file__).resolve().parents[1]))
from arc_solver.dsl import apply_program
from arc_solver.heuristics_complete import detect_basic_transformations


def test_translate_fill_value_alias_and_detection():
inp = np.array([[1, 0], [0, 0]])
expected = np.array([[0, 0], [0, 1]])

# legacy alias still works
legacy_prog = [("translate", {"dy": 1, "dx": 1, "fill_value": 0})]
assert np.array_equal(apply_program(inp, legacy_prog), expected)

# heuristics now emit canonical parameter name
detected = detect_basic_transformations(inp, expected)
assert [("translate", {"dy": 1, "dx": 1, "fill": 0})] in detected
Loading