| """Build optimized ONNX model for Task 025. |
| |
| Algorithm (266/266 verified): |
| - Detect h/v lines for each color |
| - For each dot of color c, project it adjacent to nearest same-color line |
| - Only one line per color (never both h and v for same color) |
| |
| Score target: ~11-12 pts (from base 8.71) |
| """ |
| import sys |
| sys.path.insert(0, '/app/repo/medal-solvers') |
|
|
| from onnx import TensorProto |
| import numpy as np |
| from onnx_builder import OnnxBuilder |
|
|
| H, W, C = 30, 30, 10 |
|
|
|
|
| def build_task025(): |
| b = OnnxBuilder() |
| const, nd = b.const, b.nd |
|
|
| |
| const('c_half', [0.5]) |
| const('c_one', [1.0]) |
| const('c_zero', [0.0]) |
| |
| const('axes1', [1], 'i') |
| const('axes2', [2], 'i') |
| const('axes3', [3], 'i') |
| |
| const('shape_1_1_30_30', [1, 1, 30, 30], 'i') |
| const('shape_1_1_30_1', [1, 1, 30, 1], 'i') |
| const('shape_1_1_1_30', [1, 1, 1, 30], 'i') |
| const('shape_1_1_1_1', [1, 1, 1, 1], 'i') |
| const('shape_1_10_1_1', [1, 10, 1, 1], 'i') |
| |
| |
| row_grid = np.arange(30, dtype=np.float32).reshape(1, 1, 30, 1) * np.ones((1, 1, 1, 30), dtype=np.float32) |
| col_grid = np.arange(30, dtype=np.float32).reshape(1, 1, 1, 30) * np.ones((1, 1, 30, 1), dtype=np.float32) |
| const('row_grid', row_grid) |
| const('col_grid', col_grid) |
| |
| const('depth_10', [10.0]) |
| const('oh_vals', [0.0, 1.0]) |
| |
| for i in range(10): |
| const(f'idx_{i}', [i], 'i') |
|
|
| |
| active_sum = nd('ReduceSum', ['input', 'axes1'], [[1, 1, 30, 30]], keepdims=1) |
| active_b = nd('Greater', [active_sum, 'c_half'], [([1, 1, 30, 30], TensorProto.BOOL)]) |
| active = nd('Cast', [active_b], [[1, 1, 30, 30]], to=1) |
| |
| |
| grid_w = nd('ReduceSum', [active, 'axes3'], [[1, 1, 30, 1]], keepdims=1) |
| |
| grid_h = nd('ReduceSum', [active, 'axes2'], [[1, 1, 1, 30]], keepdims=1) |
|
|
| |
| const('zeros_1_10_30_30', np.zeros((1, 10, 30, 30), dtype=np.float32)) |
| delta = 'zeros_1_10_30_30' |
| |
| for c in range(1, 10): |
| |
| ch = nd('Gather', ['input', f'idx_{c}'], [[1, 1, 30, 30]], axis=1) |
| |
| |
| ch_row_sum = nd('ReduceSum', [ch, 'axes3'], [[1, 1, 30, 1]], keepdims=1) |
| |
| ch_col_sum = nd('ReduceSum', [ch, 'axes2'], [[1, 1, 1, 30]], keepdims=1) |
| |
| |
| diff_h = nd('Sub', [ch_row_sum, grid_w], [[1, 1, 30, 1]]) |
| abs_diff_h = nd('Abs', [diff_h], [[1, 1, 30, 1]]) |
| h_match_b = nd('Less', [abs_diff_h, 'c_half'], [([1, 1, 30, 1], TensorProto.BOOL)]) |
| gw_pos_b = nd('Greater', [grid_w, 'c_half'], [([1, 1, 30, 1], TensorProto.BOOL)]) |
| h_line_b = nd('And', [h_match_b, gw_pos_b], [([1, 1, 30, 1], TensorProto.BOOL)]) |
| h_line = nd('Cast', [h_line_b], [[1, 1, 30, 1]], to=1) |
| |
| |
| diff_v = nd('Sub', [ch_col_sum, grid_h], [[1, 1, 1, 30]]) |
| abs_diff_v = nd('Abs', [diff_v], [[1, 1, 1, 30]]) |
| v_match_b = nd('Less', [abs_diff_v, 'c_half'], [([1, 1, 1, 30], TensorProto.BOOL)]) |
| gh_pos_b = nd('Greater', [grid_h, 'c_half'], [([1, 1, 1, 30], TensorProto.BOOL)]) |
| v_line_b = nd('And', [v_match_b, gh_pos_b], [([1, 1, 1, 30], TensorProto.BOOL)]) |
| v_line = nd('Cast', [v_line_b], [[1, 1, 1, 30]], to=1) |
| |
| |
| has_h = nd('ReduceMax', [h_line, 'axes2'], [[1, 1, 1, 1]], keepdims=1) |
| has_v = nd('ReduceMax', [v_line, 'axes3'], [[1, 1, 1, 1]], keepdims=1) |
| |
| |
| |
| |
| |
| const(f'row_idx_col_{c}', np.arange(30, dtype=np.float32).reshape(1, 1, 30, 1)) |
| h_pos = nd('ReduceSum', [nd('Mul', [h_line, f'row_idx_col_{c}'], [[1, 1, 30, 1]]), 'axes2'], |
| [[1, 1, 1, 1]], keepdims=1) |
| |
| |
| const(f'col_idx_row_{c}', np.arange(30, dtype=np.float32).reshape(1, 1, 1, 30)) |
| v_pos = nd('ReduceSum', [nd('Mul', [v_line, f'col_idx_row_{c}'], [[1, 1, 1, 30]]), 'axes3'], |
| [[1, 1, 1, 1]], keepdims=1) |
| |
| |
| on_line_mask = nd('Max', [h_line, v_line], [[1, 1, 30, 30]]) |
| on_line_clip = nd('Clip', [on_line_mask, 'c_zero', 'c_one'], [[1, 1, 30, 30]]) |
| not_on_line = nd('Sub', ['c_one', on_line_clip], [[1, 1, 30, 30]]) |
| |
| |
| dots = nd('Mul', [ch, not_on_line], [[1, 1, 30, 30]]) |
| |
| |
| |
| above_b = nd('Less', ['row_grid', nd('Sub', [h_pos, 'c_half'], [[1, 1, 1, 1]])], |
| [([1, 1, 30, 30], TensorProto.BOOL)]) |
| above_f = nd('Cast', [above_b], [[1, 1, 30, 30]], to=1) |
| below_f = nd('Sub', ['c_one', above_f], [[1, 1, 30, 30]]) |
| |
| |
| dots_above = nd('Mul', [dots, above_f], [[1, 1, 30, 30]]) |
| dots_below = nd('Mul', [dots, below_f], [[1, 1, 30, 30]]) |
| |
| |
| cols_above = nd('ReduceMax', [dots_above, 'axes2'], [[1, 1, 1, 30]], keepdims=1) |
| cols_below = nd('ReduceMax', [dots_below, 'axes2'], [[1, 1, 1, 30]], keepdims=1) |
| |
| |
| h_target_above = nd('Sub', [h_pos, 'c_one'], [[1, 1, 1, 1]]) |
| h_target_below = nd('Add', [h_pos, 'c_one'], [[1, 1, 1, 1]]) |
| |
| |
| at_above_row = nd('Less', [nd('Abs', [nd('Sub', ['row_grid', h_target_above], [[1, 1, 30, 30]])], [[1, 1, 30, 30]]), |
| 'c_half'], [([1, 1, 30, 30], TensorProto.BOOL)]) |
| at_above_row_f = nd('Cast', [at_above_row], [[1, 1, 30, 30]], to=1) |
| |
| at_below_row = nd('Less', [nd('Abs', [nd('Sub', ['row_grid', h_target_below], [[1, 1, 30, 30]])], [[1, 1, 30, 30]]), |
| 'c_half'], [([1, 1, 30, 30], TensorProto.BOOL)]) |
| at_below_row_f = nd('Cast', [at_below_row], [[1, 1, 30, 30]], to=1) |
| |
| |
| h_proj_above = nd('Mul', [at_above_row_f, cols_above], [[1, 1, 30, 30]]) |
| h_proj_below = nd('Mul', [at_below_row_f, cols_below], [[1, 1, 30, 30]]) |
| h_proj = nd('Add', [h_proj_above, h_proj_below], [[1, 1, 30, 30]]) |
| h_proj_gated = nd('Mul', [h_proj, has_h], [[1, 1, 30, 30]]) |
| |
| |
| left_b = nd('Less', ['col_grid', nd('Sub', [v_pos, 'c_half'], [[1, 1, 1, 1]])], |
| [([1, 1, 30, 30], TensorProto.BOOL)]) |
| left_f = nd('Cast', [left_b], [[1, 1, 30, 30]], to=1) |
| right_f = nd('Sub', ['c_one', left_f], [[1, 1, 30, 30]]) |
| |
| dots_left = nd('Mul', [dots, left_f], [[1, 1, 30, 30]]) |
| dots_right = nd('Mul', [dots, right_f], [[1, 1, 30, 30]]) |
| |
| rows_left = nd('ReduceMax', [dots_left, 'axes3'], [[1, 1, 30, 1]], keepdims=1) |
| rows_right = nd('ReduceMax', [dots_right, 'axes3'], [[1, 1, 30, 1]], keepdims=1) |
| |
| v_target_left = nd('Sub', [v_pos, 'c_one'], [[1, 1, 1, 1]]) |
| v_target_right = nd('Add', [v_pos, 'c_one'], [[1, 1, 1, 1]]) |
| |
| at_left_col = nd('Less', [nd('Abs', [nd('Sub', ['col_grid', v_target_left], [[1, 1, 30, 30]])], [[1, 1, 30, 30]]), |
| 'c_half'], [([1, 1, 30, 30], TensorProto.BOOL)]) |
| at_left_col_f = nd('Cast', [at_left_col], [[1, 1, 30, 30]], to=1) |
| |
| at_right_col = nd('Less', [nd('Abs', [nd('Sub', ['col_grid', v_target_right], [[1, 1, 30, 30]])], [[1, 1, 30, 30]]), |
| 'c_half'], [([1, 1, 30, 30], TensorProto.BOOL)]) |
| at_right_col_f = nd('Cast', [at_right_col], [[1, 1, 30, 30]], to=1) |
| |
| v_proj_left = nd('Mul', [at_left_col_f, rows_left], [[1, 1, 30, 30]]) |
| v_proj_right = nd('Mul', [at_right_col_f, rows_right], [[1, 1, 30, 30]]) |
| v_proj = nd('Add', [v_proj_left, v_proj_right], [[1, 1, 30, 30]]) |
| v_proj_gated = nd('Mul', [v_proj, has_v], [[1, 1, 30, 30]]) |
| |
| |
| proj = nd('Add', [h_proj_gated, v_proj_gated], [[1, 1, 30, 30]]) |
| |
| |
| |
| net_change = nd('Sub', [proj, dots], [[1, 1, 30, 30]]) |
| |
| |
| c_oh = nd('OneHot', [f'idx_{c}', 'depth_10', 'oh_vals'], [[1, 10]], axis=1) |
| c_oh_4d = nd('Reshape', [c_oh, 'shape_1_10_1_1'], [[1, 10, 1, 1]]) |
| ch0_oh = nd('OneHot', ['idx_0', 'depth_10', 'oh_vals'], [[1, 10]], axis=1) |
| ch0_oh_4d = nd('Reshape', [ch0_oh, 'shape_1_10_1_1'], [[1, 10, 1, 1]]) |
| |
| |
| oh_diff = nd('Sub', [c_oh_4d, ch0_oh_4d], [[1, 10, 1, 1]]) |
| color_delta = nd('Mul', [net_change, oh_diff], [[1, 10, 30, 30]]) |
| |
| delta = nd('Add', [delta, color_delta], [[1, 10, 30, 30]]) |
| |
| |
| final = nd('Add', ['input', delta], [[1, 10, 30, 30]]) |
| final_clipped = nd('Clip', [final, 'c_zero', 'c_one'], [[1, 10, 30, 30]]) |
| |
| return b.finish('task025', last_tensor=final_clipped) |
|
|
|
|
| if __name__ == '__main__': |
| import os, json, math |
| import numpy as np |
| import onnx |
| import onnxruntime as ort |
| |
| print("Building Task 025 ONNX model...") |
| model = build_task025() |
| |
| os.makedirs('/app/repo/medal-solvers/optimized', exist_ok=True) |
| output_path = '/app/repo/medal-solvers/optimized/task025.onnx' |
| onnx.save(model, output_path) |
| |
| fsize = os.path.getsize(output_path) |
| print(f" Nodes: {len(model.graph.node)}") |
| print(f" File size: {fsize:,} bytes (limit: 1,509,949)") |
| |
| |
| sess = ort.InferenceSession(output_path) |
| |
| with open('/app/task-data/task025.json') as f: |
| data = json.load(f) |
| |
| all_examples = data['train'] + data['test'] + data['arc-gen'] |
| right_count, wrong_count = 0, 0 |
| |
| for i, ex in enumerate(all_examples): |
| inp_grid = ex['input'] |
| inp = np.zeros((1, 10, 30, 30), dtype=np.float32) |
| for r, row in enumerate(inp_grid): |
| for ci, v in enumerate(row): |
| if r < 30 and ci < 30: |
| inp[0][v][r][ci] = 1.0 |
| |
| result = sess.run(['output'], {'input': inp}) |
| out = (result[0] > 0.0).astype(float) |
| |
| exp = np.zeros((1, 10, 30, 30), dtype=np.float32) |
| for r, row in enumerate(ex['output']): |
| for ci, v in enumerate(row): |
| if r < 30 and ci < 30: |
| exp[0][v][r][ci] = 1.0 |
| |
| if np.array_equal(out, exp): |
| right_count += 1 |
| else: |
| wrong_count += 1 |
| if wrong_count <= 5: |
| diff_locs = np.where(out != exp) |
| print(f" FAIL {i}: {len(diff_locs[0])} diffs") |
| for d in range(min(3, len(diff_locs[0]))): |
| ch_d, r_d, c_d = diff_locs[1][d], diff_locs[2][d], diff_locs[3][d] |
| print(f" ch={ch_d} ({r_d},{c_d}): pred={out[0,ch_d,r_d,c_d]} exp={exp[0,ch_d,r_d,c_d]}") |
| |
| print(f"\nResults: {right_count} pass, {wrong_count} fail out of {len(all_examples)}") |
| |
| if wrong_count == 0: |
| params = sum(int(np.prod(init.dims)) for init in model.graph.initializer) |
| mem_est = sum(int(np.prod([d.dim_value for d in vi.type.tensor_type.shape.dim])) * 4 |
| for vi in model.graph.value_info |
| if vi.type.tensor_type.shape.dim) |
| score_est = max(1.0, 25.0 - math.log(max(1.0, mem_est + params))) |
| print(f" Params: {params:,}, Memory est: {mem_est:,}, Score est: {score_est:.3f}") |
|
|