rogermt commited on
Commit
35fef90
·
verified ·
1 Parent(s): b003c8b

Add 19 new transforms: Kronecker, mirror tiles, upscale, gravity, color ops, crop, transpose

Browse files
Files changed (1) hide show
  1. itt_solver/transforms.py +222 -10
itt_solver/transforms.py CHANGED
@@ -1,18 +1,21 @@
1
  import numpy as np
2
  from .solver_core import tile_transform, fill_enclosed, Transform
3
 
4
- def tile_to_target_shifted(shift=(1,1), tile_factor=3):
5
- """
6
- Tile the input to a canvas tile_factor x tile_factor times, apply a small roll,
7
- and return the tiled canvas. This guarantees the transform output differs from input.
8
- """
 
 
9
  def fn(phi):
10
  h_in, w_in = phi.shape
11
  out_shape = (h_in * tile_factor, w_in * tile_factor)
12
  tiled = tile_transform(phi, out_shape)
13
- tiled = np.roll(tiled, shift=shift, axis=(0,1))
14
  return tiled
15
- return Transform(fn, f"tile_to_target_shift{shift}")
 
16
 
17
  def FillEnclosedHarmonic(boundary_mask=None):
18
  def fn(phi):
@@ -20,10 +23,12 @@ def FillEnclosedHarmonic(boundary_mask=None):
20
  return fill_enclosed(phi, bm)
21
  return Transform(fn, "FillEnclosedHarmonic")
22
 
 
23
  def Rotate(k=1):
24
  def fn(phi):
25
  return np.rot90(phi, k)
26
- return Transform(fn, f"Rotate_{90*k}")
 
27
 
28
  def Reflect(axis='h'):
29
  def fn(phi):
@@ -32,10 +37,217 @@ def Reflect(axis='h'):
32
  return np.fliplr(phi)
33
  return Transform(fn, f"Reflect_{axis}")
34
 
 
35
  def ColorMap(mapping):
36
  def fn(phi):
37
  out = phi.copy()
38
- for k,v in mapping.items():
39
- out[phi==k] = v
40
  return out
41
  return Transform(fn, f"ColorMap_{mapping}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import numpy as np
2
  from .solver_core import tile_transform, fill_enclosed, Transform
3
 
4
+
5
+ # ---------------------------------------------------------------------------
6
+ # Existing transforms (cleaned up to import Transform from solver_core)
7
+ # ---------------------------------------------------------------------------
8
+
9
+ def tile_to_target_shifted(shift=(1, 1), tile_factor=3):
10
+ """Tile the input tile_factor×tile_factor times, then roll by shift."""
11
  def fn(phi):
12
  h_in, w_in = phi.shape
13
  out_shape = (h_in * tile_factor, w_in * tile_factor)
14
  tiled = tile_transform(phi, out_shape)
15
+ tiled = np.roll(tiled, shift=shift, axis=(0, 1))
16
  return tiled
17
+ return Transform(fn, f"ShiftedTile_s{shift}_f{tile_factor}")
18
+
19
 
20
  def FillEnclosedHarmonic(boundary_mask=None):
21
  def fn(phi):
 
23
  return fill_enclosed(phi, bm)
24
  return Transform(fn, "FillEnclosedHarmonic")
25
 
26
+
27
  def Rotate(k=1):
28
  def fn(phi):
29
  return np.rot90(phi, k)
30
+ return Transform(fn, f"Rotate_{90 * k}")
31
+
32
 
33
  def Reflect(axis='h'):
34
  def fn(phi):
 
37
  return np.fliplr(phi)
38
  return Transform(fn, f"Reflect_{axis}")
39
 
40
+
41
  def ColorMap(mapping):
42
  def fn(phi):
43
  out = phi.copy()
44
+ for k, v in mapping.items():
45
+ out[phi == k] = v
46
  return out
47
  return Transform(fn, f"ColorMap_{mapping}")
48
+
49
+
50
+ # ---------------------------------------------------------------------------
51
+ # NEW transforms — Kronecker / self‑similar family
52
+ # ---------------------------------------------------------------------------
53
+
54
+ def KroneckerSelfSimilar():
55
+ """output = kron((input != 0).astype(int), input)
56
+
57
+ This is the exact transform for ARC task 007bbfb7 and the general
58
+ family of "use the input's own nonzero pattern as a meta‑layout
59
+ for placing copies of itself". Works for any 2‑color grid.
60
+ """
61
+ def fn(phi):
62
+ mask = (phi != 0).astype(phi.dtype)
63
+ return np.kron(mask, phi)
64
+ return Transform(fn, "KroneckerSelfSimilar")
65
+
66
+
67
+ def KroneckerSelfSimilarInv():
68
+ """output = kron(input, (input != 0).astype(int))
69
+
70
+ Mirror variant — identical result for symmetric inputs, differs
71
+ for asymmetric ones.
72
+ """
73
+ def fn(phi):
74
+ mask = (phi != 0).astype(phi.dtype)
75
+ return np.kron(phi, mask)
76
+ return Transform(fn, "KroneckerSelfSimilarInv")
77
+
78
+
79
+ # ---------------------------------------------------------------------------
80
+ # NEW transforms — mirror / kaleidoscope tiling
81
+ # ---------------------------------------------------------------------------
82
+
83
+ def MirrorTileH():
84
+ """Horizontal mirror tile: [abc] → [abc|cba]"""
85
+ def fn(phi):
86
+ return np.hstack([phi, np.fliplr(phi)])
87
+ return Transform(fn, "MirrorTileH")
88
+
89
+
90
+ def MirrorTileV():
91
+ """Vertical mirror tile: stack input then its vertical reflection."""
92
+ def fn(phi):
93
+ return np.vstack([phi, np.flipud(phi)])
94
+ return Transform(fn, "MirrorTileV")
95
+
96
+
97
+ def MirrorTile4Way():
98
+ """Full kaleidoscope: 2×2 mirror tile (D4 reflections)."""
99
+ def fn(phi):
100
+ top = np.hstack([phi, np.fliplr(phi)])
101
+ return np.vstack([top, np.flipud(top)])
102
+ return Transform(fn, "MirrorTile4Way")
103
+
104
+
105
+ # ---------------------------------------------------------------------------
106
+ # NEW transforms — upscale / zoom
107
+ # ---------------------------------------------------------------------------
108
+
109
+ def Upscale(k=2):
110
+ """Pixel‑repeat upscale: each pixel becomes a k×k block."""
111
+ def fn(phi):
112
+ return np.kron(phi, np.ones((k, k), dtype=phi.dtype))
113
+ return Transform(fn, f"Upscale_{k}x")
114
+
115
+
116
+ def Downscale(k=2):
117
+ """Downsample by taking every k‑th pixel (inverse of Upscale)."""
118
+ def fn(phi):
119
+ return phi[::k, ::k].copy()
120
+ return Transform(fn, f"Downscale_{k}x")
121
+
122
+
123
+ # ---------------------------------------------------------------------------
124
+ # NEW transforms — stacking
125
+ # ---------------------------------------------------------------------------
126
+
127
+ def StackH(n=2):
128
+ """Tile horizontally n times: [A] → [A|A|...|A]."""
129
+ def fn(phi):
130
+ return np.tile(phi, (1, n))
131
+ return Transform(fn, f"StackH_{n}")
132
+
133
+
134
+ def StackV(n=2):
135
+ """Tile vertically n times."""
136
+ def fn(phi):
137
+ return np.tile(phi, (n, 1))
138
+ return Transform(fn, f"StackV_{n}")
139
+
140
+
141
+ # ---------------------------------------------------------------------------
142
+ # NEW transforms — color manipulation
143
+ # ---------------------------------------------------------------------------
144
+
145
+ def RetainColor(color):
146
+ """Keep only pixels of the given color; zero the rest."""
147
+ def fn(phi):
148
+ out = np.zeros_like(phi)
149
+ out[phi == color] = color
150
+ return out
151
+ return Transform(fn, f"RetainColor_{color}")
152
+
153
+
154
+ def RemoveColor(color):
155
+ """Zero out all pixels of the given color."""
156
+ def fn(phi):
157
+ out = phi.copy()
158
+ out[phi == color] = 0
159
+ return out
160
+ return Transform(fn, f"RemoveColor_{color}")
161
+
162
+
163
+ def InvertColors():
164
+ """Swap black (0) with the most common non‑zero color."""
165
+ def fn(phi):
166
+ nonzero = phi[phi != 0]
167
+ if nonzero.size == 0:
168
+ return phi.copy()
169
+ from collections import Counter
170
+ top_color = Counter(nonzero.flatten().astype(int).tolist()).most_common(1)[0][0]
171
+ out = phi.copy()
172
+ mask_zero = (phi == 0)
173
+ mask_top = (phi == top_color)
174
+ out[mask_zero] = top_color
175
+ out[mask_top] = 0
176
+ return out
177
+ return Transform(fn, "InvertColors")
178
+
179
+
180
+ # ---------------------------------------------------------------------------
181
+ # NEW transforms — gravity
182
+ # ---------------------------------------------------------------------------
183
+
184
+ def GravityDown():
185
+ """Non‑zero pixels fall to the bottom within each column."""
186
+ def fn(phi):
187
+ out = np.zeros_like(phi)
188
+ h, w = phi.shape
189
+ for c in range(w):
190
+ col = phi[:, c]
191
+ nonzero = col[col != 0]
192
+ if nonzero.size > 0:
193
+ out[h - nonzero.size:, c] = nonzero
194
+ return out
195
+ return Transform(fn, "GravityDown")
196
+
197
+
198
+ def GravityUp():
199
+ """Non‑zero pixels rise to the top within each column."""
200
+ def fn(phi):
201
+ out = np.zeros_like(phi)
202
+ h, w = phi.shape
203
+ for c in range(w):
204
+ col = phi[:, c]
205
+ nonzero = col[col != 0]
206
+ if nonzero.size > 0:
207
+ out[:nonzero.size, c] = nonzero
208
+ return out
209
+ return Transform(fn, "GravityUp")
210
+
211
+
212
+ # ---------------------------------------------------------------------------
213
+ # NEW transforms — overlay / composition
214
+ # ---------------------------------------------------------------------------
215
+
216
+ def OverlayTransparent(background):
217
+ """Overlay: background pixels are replaced by foreground where foreground != 0."""
218
+ bg = np.array(background, dtype=float)
219
+ def fn(phi):
220
+ out = bg.copy()
221
+ mask = (phi != 0)
222
+ if phi.shape != out.shape:
223
+ p = tile_transform(phi, out.shape)
224
+ m = (p != 0)
225
+ out[m] = p[m]
226
+ else:
227
+ out[mask] = phi[mask]
228
+ return out
229
+ return Transform(fn, "OverlayTransparent")
230
+
231
+
232
+ # ---------------------------------------------------------------------------
233
+ # NEW transforms — border / crop helpers
234
+ # ---------------------------------------------------------------------------
235
+
236
+ def CropToContent():
237
+ """Crop grid to bounding box of non‑zero content."""
238
+ def fn(phi):
239
+ rows = np.any(phi != 0, axis=1)
240
+ cols = np.any(phi != 0, axis=0)
241
+ if not rows.any():
242
+ return phi.copy()
243
+ rmin, rmax = np.where(rows)[0][[0, -1]]
244
+ cmin, cmax = np.where(cols)[0][[0, -1]]
245
+ return phi[rmin:rmax + 1, cmin:cmax + 1].copy()
246
+ return Transform(fn, "CropToContent")
247
+
248
+
249
+ def Transpose():
250
+ """Matrix transpose."""
251
+ def fn(phi):
252
+ return phi.T.copy()
253
+ return Transform(fn, "Transpose")