ARC-AGI / itt_solver /transforms.py
rogermt's picture
Add 19 new transforms: Kronecker, mirror tiles, upscale, gravity, color ops, crop, transpose
35fef90 verified
raw
history blame
7.99 kB
import numpy as np
from .solver_core import tile_transform, fill_enclosed, Transform
# ---------------------------------------------------------------------------
# Existing transforms (cleaned up to import Transform from solver_core)
# ---------------------------------------------------------------------------
def tile_to_target_shifted(shift=(1, 1), tile_factor=3):
"""Tile the input tile_factor×tile_factor times, then roll by shift."""
def fn(phi):
h_in, w_in = phi.shape
out_shape = (h_in * tile_factor, w_in * tile_factor)
tiled = tile_transform(phi, out_shape)
tiled = np.roll(tiled, shift=shift, axis=(0, 1))
return tiled
return Transform(fn, f"ShiftedTile_s{shift}_f{tile_factor}")
def FillEnclosedHarmonic(boundary_mask=None):
def fn(phi):
bm = (phi != 0) if boundary_mask is None else boundary_mask
return fill_enclosed(phi, bm)
return Transform(fn, "FillEnclosedHarmonic")
def Rotate(k=1):
def fn(phi):
return np.rot90(phi, k)
return Transform(fn, f"Rotate_{90 * k}")
def Reflect(axis='h'):
def fn(phi):
if axis == 'h':
return np.flipud(phi)
return np.fliplr(phi)
return Transform(fn, f"Reflect_{axis}")
def ColorMap(mapping):
def fn(phi):
out = phi.copy()
for k, v in mapping.items():
out[phi == k] = v
return out
return Transform(fn, f"ColorMap_{mapping}")
# ---------------------------------------------------------------------------
# NEW transforms — Kronecker / self‑similar family
# ---------------------------------------------------------------------------
def KroneckerSelfSimilar():
"""output = kron((input != 0).astype(int), input)
This is the exact transform for ARC task 007bbfb7 and the general
family of "use the input's own nonzero pattern as a meta‑layout
for placing copies of itself". Works for any 2‑color grid.
"""
def fn(phi):
mask = (phi != 0).astype(phi.dtype)
return np.kron(mask, phi)
return Transform(fn, "KroneckerSelfSimilar")
def KroneckerSelfSimilarInv():
"""output = kron(input, (input != 0).astype(int))
Mirror variant — identical result for symmetric inputs, differs
for asymmetric ones.
"""
def fn(phi):
mask = (phi != 0).astype(phi.dtype)
return np.kron(phi, mask)
return Transform(fn, "KroneckerSelfSimilarInv")
# ---------------------------------------------------------------------------
# NEW transforms — mirror / kaleidoscope tiling
# ---------------------------------------------------------------------------
def MirrorTileH():
"""Horizontal mirror tile: [abc] → [abc|cba]"""
def fn(phi):
return np.hstack([phi, np.fliplr(phi)])
return Transform(fn, "MirrorTileH")
def MirrorTileV():
"""Vertical mirror tile: stack input then its vertical reflection."""
def fn(phi):
return np.vstack([phi, np.flipud(phi)])
return Transform(fn, "MirrorTileV")
def MirrorTile4Way():
"""Full kaleidoscope: 2×2 mirror tile (D4 reflections)."""
def fn(phi):
top = np.hstack([phi, np.fliplr(phi)])
return np.vstack([top, np.flipud(top)])
return Transform(fn, "MirrorTile4Way")
# ---------------------------------------------------------------------------
# NEW transforms — upscale / zoom
# ---------------------------------------------------------------------------
def Upscale(k=2):
"""Pixel‑repeat upscale: each pixel becomes a k×k block."""
def fn(phi):
return np.kron(phi, np.ones((k, k), dtype=phi.dtype))
return Transform(fn, f"Upscale_{k}x")
def Downscale(k=2):
"""Downsample by taking every k‑th pixel (inverse of Upscale)."""
def fn(phi):
return phi[::k, ::k].copy()
return Transform(fn, f"Downscale_{k}x")
# ---------------------------------------------------------------------------
# NEW transforms — stacking
# ---------------------------------------------------------------------------
def StackH(n=2):
"""Tile horizontally n times: [A] → [A|A|...|A]."""
def fn(phi):
return np.tile(phi, (1, n))
return Transform(fn, f"StackH_{n}")
def StackV(n=2):
"""Tile vertically n times."""
def fn(phi):
return np.tile(phi, (n, 1))
return Transform(fn, f"StackV_{n}")
# ---------------------------------------------------------------------------
# NEW transforms — color manipulation
# ---------------------------------------------------------------------------
def RetainColor(color):
"""Keep only pixels of the given color; zero the rest."""
def fn(phi):
out = np.zeros_like(phi)
out[phi == color] = color
return out
return Transform(fn, f"RetainColor_{color}")
def RemoveColor(color):
"""Zero out all pixels of the given color."""
def fn(phi):
out = phi.copy()
out[phi == color] = 0
return out
return Transform(fn, f"RemoveColor_{color}")
def InvertColors():
"""Swap black (0) with the most common non‑zero color."""
def fn(phi):
nonzero = phi[phi != 0]
if nonzero.size == 0:
return phi.copy()
from collections import Counter
top_color = Counter(nonzero.flatten().astype(int).tolist()).most_common(1)[0][0]
out = phi.copy()
mask_zero = (phi == 0)
mask_top = (phi == top_color)
out[mask_zero] = top_color
out[mask_top] = 0
return out
return Transform(fn, "InvertColors")
# ---------------------------------------------------------------------------
# NEW transforms — gravity
# ---------------------------------------------------------------------------
def GravityDown():
"""Non‑zero pixels fall to the bottom within each column."""
def fn(phi):
out = np.zeros_like(phi)
h, w = phi.shape
for c in range(w):
col = phi[:, c]
nonzero = col[col != 0]
if nonzero.size > 0:
out[h - nonzero.size:, c] = nonzero
return out
return Transform(fn, "GravityDown")
def GravityUp():
"""Non‑zero pixels rise to the top within each column."""
def fn(phi):
out = np.zeros_like(phi)
h, w = phi.shape
for c in range(w):
col = phi[:, c]
nonzero = col[col != 0]
if nonzero.size > 0:
out[:nonzero.size, c] = nonzero
return out
return Transform(fn, "GravityUp")
# ---------------------------------------------------------------------------
# NEW transforms — overlay / composition
# ---------------------------------------------------------------------------
def OverlayTransparent(background):
"""Overlay: background pixels are replaced by foreground where foreground != 0."""
bg = np.array(background, dtype=float)
def fn(phi):
out = bg.copy()
mask = (phi != 0)
if phi.shape != out.shape:
p = tile_transform(phi, out.shape)
m = (p != 0)
out[m] = p[m]
else:
out[mask] = phi[mask]
return out
return Transform(fn, "OverlayTransparent")
# ---------------------------------------------------------------------------
# NEW transforms — border / crop helpers
# ---------------------------------------------------------------------------
def CropToContent():
"""Crop grid to bounding box of non‑zero content."""
def fn(phi):
rows = np.any(phi != 0, axis=1)
cols = np.any(phi != 0, axis=0)
if not rows.any():
return phi.copy()
rmin, rmax = np.where(rows)[0][[0, -1]]
cmin, cmax = np.where(cols)[0][[0, -1]]
return phi[rmin:rmax + 1, cmin:cmax + 1].copy()
return Transform(fn, "CropToContent")
def Transpose():
"""Matrix transpose."""
def fn(phi):
return phi.T.copy()
return Transform(fn, "Transpose")