Remove original files from root (now in own-solver/)
Browse files- neurogolf_solver/__init__.py +0 -7
- neurogolf_solver/config.py +0 -14
- neurogolf_solver/constants.py +0 -26
- neurogolf_solver/data_loader.py +0 -96
- neurogolf_solver/gather_helpers.py +0 -63
- neurogolf_solver/main.py +0 -136
- neurogolf_solver/onnx_helpers.py +0 -67
- neurogolf_solver/profiler.py +0 -84
- neurogolf_solver/solvers/WAVE2_SCAN.md +0 -48
- neurogolf_solver/solvers/__init__.py +0 -6
- neurogolf_solver/solvers/analytical.py +0 -78
- neurogolf_solver/solvers/conv.py +0 -544
- neurogolf_solver/solvers/edge.py +0 -99
- neurogolf_solver/solvers/geometric.py +0 -177
- neurogolf_solver/solvers/gravity.py +0 -140
- neurogolf_solver/solvers/mode.py +0 -63
- neurogolf_solver/solvers/solver_registry.py +0 -163
- neurogolf_solver/solvers/tiling.py +0 -429
- neurogolf_solver/solvers/wave1.py +0 -277
- neurogolf_solver/submission.py +0 -150
- neurogolf_solver/validators.py +0 -125
neurogolf_solver/__init__.py
DELETED
|
@@ -1,7 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
ARC-AGI NeuroGolf Championship - Complete Solver v5
|
| 4 |
-
Refactored into modular components.
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
__version__ = '5.0.0'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/config.py
DELETED
|
@@ -1,14 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Runtime configuration management."""
|
| 3 |
-
|
| 4 |
-
from onnx import helper
|
| 5 |
-
|
| 6 |
-
def get_providers(device='auto'):
|
| 7 |
-
"""Get ONNX Runtime execution providers based on device."""
|
| 8 |
-
if device == 'cuda':
|
| 9 |
-
return ['CUDAExecutionProvider', 'CPUExecutionProvider']
|
| 10 |
-
return ['CPUExecutionProvider']
|
| 11 |
-
|
| 12 |
-
def make_opset(version=17):
|
| 13 |
-
"""Create ONNX opset identifier."""
|
| 14 |
-
return [helper.make_opsetid("", version)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/constants.py
DELETED
|
@@ -1,26 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Constants and configuration values for ARC-AGI NeuroGolf Championship."""
|
| 3 |
-
|
| 4 |
-
import numpy as np
|
| 5 |
-
from onnx import TensorProto
|
| 6 |
-
|
| 7 |
-
# Grid dimensions
|
| 8 |
-
BATCH, CH, GH, GW = 1, 10, 30, 30
|
| 9 |
-
GRID_SHAPE = [BATCH, CH, GH, GW]
|
| 10 |
-
|
| 11 |
-
# ONNX settings
|
| 12 |
-
DT = TensorProto.FLOAT
|
| 13 |
-
IR = 8
|
| 14 |
-
OPSET_VERSION = 17
|
| 15 |
-
|
| 16 |
-
# Limits
|
| 17 |
-
INT64_MIN = int(np.iinfo(np.int64).min)
|
| 18 |
-
BANNED_OPS = {'Loop', 'Scan', 'NonZero', 'Unique', 'Script', 'Function'}
|
| 19 |
-
MAX_ONNX_FILESIZE = int(1.44 * 1024 * 1024) # per .onnx file, NOT submission zip
|
| 20 |
-
|
| 21 |
-
# Task exclusions — NONE. All 400 tasks count.
|
| 22 |
-
EXCLUDED_TASKS = set()
|
| 23 |
-
|
| 24 |
-
# ARC-GEN limits
|
| 25 |
-
MAX_ARCGEN_VALIDATE = 30
|
| 26 |
-
MAX_ARCGEN_FIT = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/data_loader.py
DELETED
|
@@ -1,96 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Data loading utilities for ARC-AGI tasks."""
|
| 3 |
-
|
| 4 |
-
import json
|
| 5 |
-
import os
|
| 6 |
-
import numpy as np
|
| 7 |
-
from .constants import CH, GH, GW
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def load_tasks_dir(data_dir, arcgen_dir=None):
|
| 11 |
-
"""Load tasks from directory structure."""
|
| 12 |
-
files = sorted(f for f in os.listdir(data_dir) if f.endswith('.json'))
|
| 13 |
-
tasks = {}
|
| 14 |
-
for i, f in enumerate(files):
|
| 15 |
-
with open(os.path.join(data_dir, f)) as fh:
|
| 16 |
-
data = json.load(fh)
|
| 17 |
-
hex_id = f.replace('.json', '')
|
| 18 |
-
if arcgen_dir and os.path.exists(os.path.join(arcgen_dir, f)):
|
| 19 |
-
with open(os.path.join(arcgen_dir, f)) as fh:
|
| 20 |
-
arcgen_examples = json.load(fh)
|
| 21 |
-
if isinstance(arcgen_examples, list):
|
| 22 |
-
data['arc-gen'] = arcgen_examples
|
| 23 |
-
if 'arc-gen' not in data:
|
| 24 |
-
data['arc-gen'] = []
|
| 25 |
-
tasks[i + 1] = {'hex': hex_id, 'data': data}
|
| 26 |
-
return tasks
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
def load_tasks_kaggle(data_dir):
|
| 30 |
-
"""Load tasks from Kaggle format."""
|
| 31 |
-
tasks = {}
|
| 32 |
-
for tn in range(1, 401):
|
| 33 |
-
path = os.path.join(data_dir, f"task{tn:03d}.json")
|
| 34 |
-
if os.path.exists(path):
|
| 35 |
-
with open(path) as f:
|
| 36 |
-
data = json.load(f)
|
| 37 |
-
if 'arc-gen' not in data:
|
| 38 |
-
data['arc-gen'] = []
|
| 39 |
-
tasks[tn] = {'hex': f'task{tn:03d}', 'data': data}
|
| 40 |
-
return tasks
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
def to_onehot(grid):
|
| 44 |
-
"""Convert grid to one-hot encoding."""
|
| 45 |
-
arr = np.zeros((1, CH, GH, GW), dtype=np.float32)
|
| 46 |
-
for r, row in enumerate(grid):
|
| 47 |
-
for c, v in enumerate(row):
|
| 48 |
-
if r < GH and c < GW and 0 <= v < CH:
|
| 49 |
-
arr[0, v, r, c] = 1.0
|
| 50 |
-
return arr
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
def get_exs(td):
|
| 54 |
-
"""Get examples as numpy arrays."""
|
| 55 |
-
return [(np.array(ex['input'], dtype=np.int64), np.array(ex['output'], dtype=np.int64))
|
| 56 |
-
for ex in td['train'] + td['test']]
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
def get_exs_for_fitting(td):
|
| 60 |
-
"""Get examples for fitting with ARC-GEN augmentation."""
|
| 61 |
-
base_exs = [(np.array(ex['input'], dtype=np.int64), np.array(ex['output'], dtype=np.int64))
|
| 62 |
-
for ex in td['train'] + td['test']]
|
| 63 |
-
if not base_exs:
|
| 64 |
-
return base_exs
|
| 65 |
-
base_shapes = {inp.shape for inp, _ in base_exs}
|
| 66 |
-
if len(base_shapes) != 1:
|
| 67 |
-
return base_exs
|
| 68 |
-
base_shape = list(base_shapes)[0]
|
| 69 |
-
ag_exs = []
|
| 70 |
-
for ex in td.get('arc-gen', []):
|
| 71 |
-
inp = np.array(ex['input'], dtype=np.int64)
|
| 72 |
-
out = np.array(ex['output'], dtype=np.int64)
|
| 73 |
-
if inp.shape == base_shape and out.shape == base_exs[0][1].shape:
|
| 74 |
-
ag_exs.append((inp, out))
|
| 75 |
-
return base_exs + ag_exs[:10]
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
def get_exs_for_fitting_variable(td):
|
| 79 |
-
"""Get examples for variable-shape fitting."""
|
| 80 |
-
base_exs = [(np.array(ex['input'], dtype=np.int64), np.array(ex['output'], dtype=np.int64))
|
| 81 |
-
for ex in td['train'] + td['test']]
|
| 82 |
-
ag_exs = []
|
| 83 |
-
for ex in td.get('arc-gen', []):
|
| 84 |
-
inp = np.array(ex['input'], dtype=np.int64)
|
| 85 |
-
out = np.array(ex['output'], dtype=np.int64)
|
| 86 |
-
if inp.shape == out.shape and inp.shape[0] <= 30 and inp.shape[1] <= 30:
|
| 87 |
-
ag_exs.append((inp, out))
|
| 88 |
-
return base_exs + ag_exs[:20]
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
def fixed_shapes(td):
|
| 92 |
-
"""Check if task has fixed input/output shapes."""
|
| 93 |
-
shapes = set()
|
| 94 |
-
for inp, out in get_exs(td):
|
| 95 |
-
shapes.add((inp.shape, out.shape))
|
| 96 |
-
return list(shapes)[0] if len(shapes) == 1 else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/gather_helpers.py
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Gather-based model building utilities."""
|
| 3 |
-
|
| 4 |
-
import numpy as np
|
| 5 |
-
from onnx import numpy_helper, helper
|
| 6 |
-
from .onnx_helpers import mk
|
| 7 |
-
from .constants import GH, GW
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def _build_gather_model(OH, OW, idx):
|
| 11 |
-
"""Build gather model from index mapping."""
|
| 12 |
-
flat_idx = np.zeros((GH * GW,), dtype=np.int64)
|
| 13 |
-
mask = np.zeros((1, 1, GH, GW), dtype=np.float32)
|
| 14 |
-
for oi in range(OH):
|
| 15 |
-
for oj in range(OW):
|
| 16 |
-
flat_idx[oi * GW + oj] = idx[oi, oj, 0] * GW + idx[oi, oj, 1]
|
| 17 |
-
mask[0, 0, oi, oj] = 1.0
|
| 18 |
-
inits = [
|
| 19 |
-
numpy_helper.from_array(np.array([1, 10, GH * GW], dtype=np.int64), 'fs'),
|
| 20 |
-
numpy_helper.from_array(flat_idx, 'idx'),
|
| 21 |
-
numpy_helper.from_array(np.array([1, 10, GH, GW], dtype=np.int64), 'os'),
|
| 22 |
-
numpy_helper.from_array(mask, 'mask'),
|
| 23 |
-
]
|
| 24 |
-
nodes = [
|
| 25 |
-
helper.make_node('Reshape', ['input', 'fs'], ['flat']),
|
| 26 |
-
helper.make_node('Gather', ['flat', 'idx'], ['g'], axis=2),
|
| 27 |
-
helper.make_node('Reshape', ['g', 'os'], ['raw']),
|
| 28 |
-
helper.make_node('Mul', ['raw', 'mask'], ['output']),
|
| 29 |
-
]
|
| 30 |
-
return mk(nodes, inits)
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
def _build_gather_model_with_const(IH, IW, OH, OW, idx, cst):
|
| 34 |
-
"""Build gather model with constant values."""
|
| 35 |
-
flat_idx = np.zeros((GH * GW,), dtype=np.int64)
|
| 36 |
-
gather_mask = np.zeros((1, 1, GH, GW), dtype=np.float32)
|
| 37 |
-
const_oh = np.zeros((1, 10, GH, GW), dtype=np.float32)
|
| 38 |
-
for oi in range(OH):
|
| 39 |
-
for oj in range(OW):
|
| 40 |
-
if idx[oi, oj, 0] >= 0:
|
| 41 |
-
flat_idx[oi * GW + oj] = idx[oi, oj, 0] * GW + idx[oi, oj, 1]
|
| 42 |
-
gather_mask[0, 0, oi, oj] = 1.0
|
| 43 |
-
elif cst[oi, oj] >= 0:
|
| 44 |
-
const_oh[0, cst[oi, oj], oi, oj] = 1.0
|
| 45 |
-
has_const = np.any(const_oh > 0)
|
| 46 |
-
inits = [
|
| 47 |
-
numpy_helper.from_array(np.array([1, 10, GH * GW], dtype=np.int64), 'fs'),
|
| 48 |
-
numpy_helper.from_array(flat_idx, 'idx'),
|
| 49 |
-
numpy_helper.from_array(np.array([1, 10, GH, GW], dtype=np.int64), 'os'),
|
| 50 |
-
numpy_helper.from_array(gather_mask, 'gmask'),
|
| 51 |
-
]
|
| 52 |
-
nodes = [
|
| 53 |
-
helper.make_node('Reshape', ['input', 'fs'], ['flat']),
|
| 54 |
-
helper.make_node('Gather', ['flat', 'idx'], ['g'], axis=2),
|
| 55 |
-
helper.make_node('Reshape', ['g', 'os'], ['raw']),
|
| 56 |
-
helper.make_node('Mul', ['raw', 'gmask'], ['masked']),
|
| 57 |
-
]
|
| 58 |
-
if has_const:
|
| 59 |
-
inits.append(numpy_helper.from_array(const_oh, 'cst'))
|
| 60 |
-
nodes.append(helper.make_node('Add', ['masked', 'cst'], ['output']))
|
| 61 |
-
else:
|
| 62 |
-
nodes[-1] = helper.make_node('Mul', ['raw', 'gmask'], ['output'])
|
| 63 |
-
return mk(nodes, inits)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/main.py
DELETED
|
@@ -1,136 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
ARC-AGI NeuroGolf Championship - Main Entry Point
|
| 4 |
-
|
| 5 |
-
Usage:
|
| 6 |
-
python -m neurogolf_solver.main --data_dir ARC-AGI/data/training/ --output_dir submission
|
| 7 |
-
python -m neurogolf_solver.main --kaggle --output_dir /kaggle/working/submission
|
| 8 |
-
python -m neurogolf_solver.main --data_dir ARC-AGI/data/training/ --arcgen_dir ARC-GEN-100K/ --use_wandb
|
| 9 |
-
"""
|
| 10 |
-
|
| 11 |
-
import argparse
|
| 12 |
-
import os
|
| 13 |
-
import sys
|
| 14 |
-
import time
|
| 15 |
-
import onnxruntime as ort
|
| 16 |
-
from .config import get_providers
|
| 17 |
-
from .data_loader import load_tasks_dir, load_tasks_kaggle
|
| 18 |
-
from .submission import run_tasks, generate_submission, print_summary
|
| 19 |
-
from .profiler import score_network
|
| 20 |
-
from .constants import EXCLUDED_TASKS, MAX_ONNX_FILESIZE
|
| 21 |
-
|
| 22 |
-
try:
|
| 23 |
-
import wandb
|
| 24 |
-
except ImportError:
|
| 25 |
-
wandb = None
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
def check_all_models(output_dir, strict_size, strict_score):
|
| 29 |
-
"""Check all .onnx files for size limit and scoreability.
|
| 30 |
-
strict_size=True → halt on oversized files.
|
| 31 |
-
strict_score=False → warn on unscorable but don't halt."""
|
| 32 |
-
size_problems = []
|
| 33 |
-
score_problems = []
|
| 34 |
-
for f in sorted(os.listdir(output_dir)):
|
| 35 |
-
if not f.endswith('.onnx'):
|
| 36 |
-
continue
|
| 37 |
-
fpath = os.path.join(output_dir, f)
|
| 38 |
-
fsize = os.path.getsize(fpath)
|
| 39 |
-
|
| 40 |
-
if fsize > MAX_ONNX_FILESIZE:
|
| 41 |
-
size_problems.append((f, fsize))
|
| 42 |
-
|
| 43 |
-
macs, memory, params = score_network(fpath)
|
| 44 |
-
if macs is None or memory is None or params is None:
|
| 45 |
-
score_problems.append(f)
|
| 46 |
-
|
| 47 |
-
if size_problems:
|
| 48 |
-
print(f"\n{'!'*70}")
|
| 49 |
-
print(f"FATAL: {len(size_problems)} .onnx files exceed 1.44MB limit:")
|
| 50 |
-
for f, sz in size_problems:
|
| 51 |
-
print(f" {f}: {sz:,} bytes ({sz/1024:.1f} KB)")
|
| 52 |
-
print(f"{'!'*70}")
|
| 53 |
-
if strict_size:
|
| 54 |
-
sys.exit(1)
|
| 55 |
-
|
| 56 |
-
if score_problems:
|
| 57 |
-
print(f"\nWARNING: {len(score_problems)} .onnx files unscorable by onnx_tool:")
|
| 58 |
-
for f in score_problems:
|
| 59 |
-
print(f" {f}")
|
| 60 |
-
if strict_score:
|
| 61 |
-
print("Stopping (--strict_score is on).")
|
| 62 |
-
sys.exit(1)
|
| 63 |
-
|
| 64 |
-
if not size_problems and not score_problems:
|
| 65 |
-
print(f"\nAll .onnx files pass size and score checks.")
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
def main():
|
| 69 |
-
parser = argparse.ArgumentParser(description='NeuroGolf Solver v5')
|
| 70 |
-
parser.add_argument('--data_dir', default='ARC-AGI/data/training/')
|
| 71 |
-
parser.add_argument('--arcgen_dir', default='', help='Path to ARC-GEN-100K/ directory')
|
| 72 |
-
parser.add_argument('--output_dir', default='/kaggle/working/submission')
|
| 73 |
-
parser.add_argument('--kaggle', action='store_true', help='Use Kaggle task format')
|
| 74 |
-
parser.add_argument('--conv_budget', type=float, default=30.0, help='Seconds per conv solver per task')
|
| 75 |
-
parser.add_argument('--tasks', type=str, default='', help='Comma-separated task numbers')
|
| 76 |
-
parser.add_argument('--device', type=str, default='auto', choices=['auto', 'cpu', 'cuda'])
|
| 77 |
-
parser.add_argument('--use_wandb', action='store_true', help='Enable W&B logging')
|
| 78 |
-
parser.add_argument('--strict_size', type=bool, default=True, help='Halt if any .onnx > 1.44MB (default: True)')
|
| 79 |
-
parser.add_argument('--strict_score', type=bool, default=False, help='Halt if any model unscorable (default: False)')
|
| 80 |
-
args = parser.parse_args()
|
| 81 |
-
|
| 82 |
-
providers = get_providers(args.device)
|
| 83 |
-
|
| 84 |
-
config = {
|
| 85 |
-
"device": args.device,
|
| 86 |
-
"conv_budget": args.conv_budget,
|
| 87 |
-
"data_dir": args.data_dir,
|
| 88 |
-
"arcgen_dir": args.arcgen_dir,
|
| 89 |
-
"tasks": args.tasks,
|
| 90 |
-
}
|
| 91 |
-
|
| 92 |
-
ort.set_default_logger_severity(3)
|
| 93 |
-
print(f"Using providers: {providers}")
|
| 94 |
-
print(f"Strict size: {args.strict_size} | Strict score: {args.strict_score}")
|
| 95 |
-
print(f"Max .onnx file size: {MAX_ONNX_FILESIZE:,} bytes")
|
| 96 |
-
|
| 97 |
-
# Load tasks
|
| 98 |
-
if args.kaggle:
|
| 99 |
-
tasks = load_tasks_kaggle(args.data_dir)
|
| 100 |
-
else:
|
| 101 |
-
arcgen = args.arcgen_dir if args.arcgen_dir else None
|
| 102 |
-
tasks = load_tasks_dir(args.data_dir, arcgen_dir=arcgen)
|
| 103 |
-
|
| 104 |
-
total_arcgen = sum(len(t['data'].get('arc-gen', [])) for t in tasks.values())
|
| 105 |
-
print(f"Loaded {len(tasks)} tasks ({total_arcgen} ARC-GEN examples)")
|
| 106 |
-
|
| 107 |
-
task_nums = [int(t) for t in args.tasks.split(',')] if args.tasks else sorted(tasks.keys())
|
| 108 |
-
print(f"Solving {len(task_nums)} tasks")
|
| 109 |
-
print(f"Conv budget: {args.conv_budget}s per task")
|
| 110 |
-
print("=" * 70)
|
| 111 |
-
|
| 112 |
-
t0 = time.time()
|
| 113 |
-
|
| 114 |
-
if args.use_wandb and wandb is not None:
|
| 115 |
-
with wandb.init(project="neurogolf", name="solver_run", config=config):
|
| 116 |
-
results, costs_dict, total_score = run_tasks(
|
| 117 |
-
task_nums, tasks, args.output_dir, providers,
|
| 118 |
-
args.conv_budget, EXCLUDED_TASKS, use_wandb=True
|
| 119 |
-
)
|
| 120 |
-
else:
|
| 121 |
-
results, costs_dict, total_score = run_tasks(
|
| 122 |
-
task_nums, tasks, args.output_dir, providers,
|
| 123 |
-
args.conv_budget, EXCLUDED_TASKS, use_wandb=False
|
| 124 |
-
)
|
| 125 |
-
|
| 126 |
-
elapsed = time.time() - t0
|
| 127 |
-
|
| 128 |
-
# Check all output files BEFORE generating submission
|
| 129 |
-
check_all_models(args.output_dir, args.strict_size, args.strict_score)
|
| 130 |
-
|
| 131 |
-
submission_info = generate_submission(args.output_dir, results, costs_dict, task_nums)
|
| 132 |
-
print_summary(results, submission_info, elapsed)
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
if __name__ == '__main__':
|
| 136 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/onnx_helpers.py
DELETED
|
@@ -1,67 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""ONNX model building helper functions (opset 17)."""
|
| 3 |
-
|
| 4 |
-
import numpy as np
|
| 5 |
-
from onnx import helper, TensorProto, numpy_helper
|
| 6 |
-
from .constants import DT, IR, GRID_SHAPE, INT64_MIN, GH, GW
|
| 7 |
-
from .config import make_opset
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def _make_int64_init(name, values):
|
| 11 |
-
"""Create int64 initializer."""
|
| 12 |
-
return numpy_helper.from_array(np.array(values, dtype=np.int64), name)
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
def _build_pad_node(input_name, output_name, pad_h, pad_w, inits, suffix=''):
|
| 16 |
-
"""Pad with tensor-based pads input (opset 11+)."""
|
| 17 |
-
pads_name = f'pads{suffix}'
|
| 18 |
-
cv_name = f'pad_cv{suffix}'
|
| 19 |
-
pads_arr = np.array([0, 0, 0, 0, 0, 0, pad_h, pad_w], dtype=np.int64)
|
| 20 |
-
inits.append(numpy_helper.from_array(pads_arr, pads_name))
|
| 21 |
-
inits.append(numpy_helper.from_array(np.array(0.0, dtype=np.float32), cv_name))
|
| 22 |
-
return helper.make_node('Pad', [input_name, pads_name, cv_name], [output_name], mode='constant')
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
def _build_slice_crop(input_name, output_name, IH, IW, inits, suffix=''):
|
| 26 |
-
"""Slice to crop [1,10,30,30] to [1,10,IH,IW]."""
|
| 27 |
-
st_name = f'crop_st{suffix}'
|
| 28 |
-
en_name = f'crop_en{suffix}'
|
| 29 |
-
inits.append(_make_int64_init(st_name, [0, 0, 0, 0]))
|
| 30 |
-
inits.append(_make_int64_init(en_name, [1, 10, IH, IW]))
|
| 31 |
-
return helper.make_node('Slice', [input_name, st_name, en_name], [output_name])
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
def _build_slice_reverse(input_name, output_name, axis, dim_size, inits, suffix=''):
|
| 35 |
-
"""Slice(step=-1) to reverse one axis. Zero MACs."""
|
| 36 |
-
st_name = f'rev_st{suffix}'
|
| 37 |
-
en_name = f'rev_en{suffix}'
|
| 38 |
-
ax_name = f'rev_ax{suffix}'
|
| 39 |
-
sp_name = f'rev_sp{suffix}'
|
| 40 |
-
inits.append(_make_int64_init(st_name, [dim_size - 1]))
|
| 41 |
-
inits.append(_make_int64_init(en_name, [INT64_MIN]))
|
| 42 |
-
inits.append(_make_int64_init(ax_name, [axis]))
|
| 43 |
-
inits.append(_make_int64_init(sp_name, [-1]))
|
| 44 |
-
return helper.make_node('Slice', [input_name, st_name, en_name, ax_name, sp_name], [output_name])
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
def _build_reducesum(input_name, output_name, axes_list, inits, suffix=''):
|
| 48 |
-
"""ReduceSum with axes as tensor input (opset 13+). keepdims=1."""
|
| 49 |
-
axes_name = f'rs_axes{suffix}'
|
| 50 |
-
inits.append(_make_int64_init(axes_name, axes_list))
|
| 51 |
-
return helper.make_node('ReduceSum', [input_name, axes_name], [output_name], keepdims=1)
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
def mk(nodes, inits=None, opset_version=17):
|
| 55 |
-
"""Create ONNX model from nodes and initializers."""
|
| 56 |
-
x = helper.make_tensor_value_info("input", DT, GRID_SHAPE)
|
| 57 |
-
y = helper.make_tensor_value_info("output", DT, GRID_SHAPE)
|
| 58 |
-
g = helper.make_graph(nodes, "g", [x], [y], initializer=inits or [])
|
| 59 |
-
return helper.make_model(g, ir_version=IR, opset_imports=make_opset(opset_version))
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
def add_onehot_block(nodes, inits, am_name, oh_name):
|
| 63 |
-
"""Add ArgMax one-hot conversion block."""
|
| 64 |
-
classes = np.arange(10, dtype=np.int64).reshape(1, 10, 1, 1)
|
| 65 |
-
inits.append(numpy_helper.from_array(classes, 'classes'))
|
| 66 |
-
nodes.append(helper.make_node('Equal', [am_name, 'classes'], ['eq']))
|
| 67 |
-
nodes.append(helper.make_node('Cast', ['eq'], [oh_name], to=TensorProto.FLOAT))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/profiler.py
DELETED
|
@@ -1,84 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Static profiling for ONNX models.
|
| 3 |
-
|
| 4 |
-
Uses neurogolf_utils.score_network() (onnx_tool) when available — this is
|
| 5 |
-
the ONLY scoring that matches Kaggle. The static fallback is approximate
|
| 6 |
-
and prints a WARNING. If onnx_tool returns (None, None, None), the model
|
| 7 |
-
is REJECTED — do not submit it.
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
-
import onnx
|
| 11 |
-
from onnx import numpy_helper
|
| 12 |
-
from .constants import BANNED_OPS, GH, GW
|
| 13 |
-
|
| 14 |
-
try:
|
| 15 |
-
from neurogolf_utils import score_network as _score_network_official
|
| 16 |
-
HAS_ONNX_TOOL = True
|
| 17 |
-
except ImportError:
|
| 18 |
-
HAS_ONNX_TOOL = False
|
| 19 |
-
|
| 20 |
-
_WARNED_NO_ONNX_TOOL = False
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
def score_network(path):
|
| 24 |
-
"""Score network. Returns (macs, memory, params) or (None, None, None).
|
| 25 |
-
|
| 26 |
-
If onnx_tool is available: uses official scorer. (None,None,None) = REJECTED.
|
| 27 |
-
If onnx_tool is NOT available: uses static fallback with WARNING.
|
| 28 |
-
"""
|
| 29 |
-
global _WARNED_NO_ONNX_TOOL
|
| 30 |
-
if HAS_ONNX_TOOL:
|
| 31 |
-
# Official scorer — trust its result. Do NOT catch exceptions silently.
|
| 32 |
-
try:
|
| 33 |
-
result = _score_network_official(path)
|
| 34 |
-
except Exception as e:
|
| 35 |
-
print(f"WARNING: onnx_tool score_network failed on {path}: {e}")
|
| 36 |
-
return None, None, None
|
| 37 |
-
return result
|
| 38 |
-
else:
|
| 39 |
-
if not _WARNED_NO_ONNX_TOOL:
|
| 40 |
-
print("WARNING: onnx_tool not installed. Scores are APPROXIMATE and may not match Kaggle.")
|
| 41 |
-
print("WARNING: Models that fail onnx_tool profiling will be REJECTED on Kaggle.")
|
| 42 |
-
print("WARNING: Run neurogolf_utils.verify_network() in a Kaggle notebook before submitting.")
|
| 43 |
-
_WARNED_NO_ONNX_TOOL = True
|
| 44 |
-
return _static_profile(path)
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
def _static_profile(path):
|
| 48 |
-
"""Static profiling fallback. APPROXIMATE — does not match Kaggle scoring.
|
| 49 |
-
Only used when onnx_tool is not installed."""
|
| 50 |
-
try:
|
| 51 |
-
model = onnx.load(path)
|
| 52 |
-
except:
|
| 53 |
-
return None, None, None
|
| 54 |
-
tensors = {}
|
| 55 |
-
params = 0
|
| 56 |
-
nbytes = 0
|
| 57 |
-
macs = 0
|
| 58 |
-
for init in model.graph.initializer:
|
| 59 |
-
a = numpy_helper.to_array(init)
|
| 60 |
-
tensors[init.name] = a
|
| 61 |
-
params += a.size
|
| 62 |
-
nbytes += a.nbytes
|
| 63 |
-
for nd in model.graph.node:
|
| 64 |
-
if nd.op_type == 'Constant':
|
| 65 |
-
for attr in nd.attribute:
|
| 66 |
-
if attr.t and attr.t.ByteSize() > 0:
|
| 67 |
-
try:
|
| 68 |
-
a = numpy_helper.to_array(attr.t)
|
| 69 |
-
if nd.output:
|
| 70 |
-
tensors[nd.output[0]] = a
|
| 71 |
-
params += a.size
|
| 72 |
-
nbytes += a.nbytes
|
| 73 |
-
except:
|
| 74 |
-
pass
|
| 75 |
-
# Banned op check — UPPERCASE to match Kaggle
|
| 76 |
-
if nd.op_type.upper() in {op.upper() for op in BANNED_OPS}:
|
| 77 |
-
print(f"WARNING: Banned op '{nd.op_type}' found in {path}")
|
| 78 |
-
return None, None, None
|
| 79 |
-
if nd.op_type == 'Conv' and len(nd.input) >= 2 and nd.input[1] in tensors:
|
| 80 |
-
w = tensors[nd.input[1]]
|
| 81 |
-
if w.ndim == 4:
|
| 82 |
-
co, ci, kh, kw = w.shape
|
| 83 |
-
macs += co * ci * kh * kw * GH * GW
|
| 84 |
-
return int(macs), int(nbytes), int(params)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/solvers/WAVE2_SCAN.md
DELETED
|
@@ -1,48 +0,0 @@
|
|
| 1 |
-
# Wave 2 + Flood Fill Scan Results (2026-04-27)
|
| 2 |
-
|
| 3 |
-
## Wave 2 — Composition & Mode Extensions
|
| 4 |
-
|
| 5 |
-
| Pattern | Matches |
|
| 6 |
-
|---------|---------|
|
| 7 |
-
| transform_then_recolor (flip/rot/transpose + color map) | 0 |
|
| 8 |
-
| recolor_then_transform (reverse order) | 0 |
|
| 9 |
-
| row_mode_fill (each row → dominant color) | 0 |
|
| 10 |
-
| col_mode_fill (each col → dominant color) | 0 |
|
| 11 |
-
| fill_bg_with_mode (zeros → global mode) | 0 |
|
| 12 |
-
| fill_bg_with_color (zeros → fixed color, all examples) | 0 |
|
| 13 |
-
|
| 14 |
-
## Flood Fill
|
| 15 |
-
|
| 16 |
-
| Pattern | Matches |
|
| 17 |
-
|---------|---------|
|
| 18 |
-
| flood_fill_replace (seed spreads into passable, all become fill_color) | 0 |
|
| 19 |
-
| flood_fill_keep_seed (seed stays, passable neighbors become fill_color) | 0 |
|
| 20 |
-
|
| 21 |
-
## Pattern Inpainting
|
| 22 |
-
|
| 23 |
-
| Pattern | Matches |
|
| 24 |
-
|---------|---------|
|
| 25 |
-
| Tile inpainting (output = perfect tile, input = tile with holes) | 0 |
|
| 26 |
-
|
| 27 |
-
## What the tasks ACTUALLY need (from manual inspection):
|
| 28 |
-
|
| 29 |
-
- **Task 5**: Pattern stamping at positions indicated by markers
|
| 30 |
-
- **Task 17**: Wallpaper defect restoration (NOT simple tile inpainting)
|
| 31 |
-
- **Task 20**: Diamond symmetry completion with color-specific rules
|
| 32 |
-
- **Task 27**: Shape-relative region filling (notch detection)
|
| 33 |
-
|
| 34 |
-
These require **object-level reasoning**: detect shapes, understand spatial relationships
|
| 35 |
-
between objects, apply context-dependent rules. Cannot be solved by pixel-level operations
|
| 36 |
-
(flood fill, mode fill, color mapping) alone.
|
| 37 |
-
|
| 38 |
-
## Conclusion:
|
| 39 |
-
|
| 40 |
-
Simple analytical solvers (Waves 1-2) and pixel-level propagation (flood fill)
|
| 41 |
-
have reached their ceiling. The remaining 349 tasks need:
|
| 42 |
-
1. Object detection/segmentation
|
| 43 |
-
2. Spatial relationship reasoning
|
| 44 |
-
3. Context-dependent rule application
|
| 45 |
-
4. Pattern recognition beyond tiling
|
| 46 |
-
|
| 47 |
-
These are fundamentally in the domain of learned models (conv lstsq already does this
|
| 48 |
-
for some tasks) or much more complex hand-crafted solvers.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/solvers/__init__.py
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Solvers package for ARC-AGI NeuroGolf Championship."""
|
| 3 |
-
|
| 4 |
-
from .solver_registry import ANALYTICAL_SOLVERS, solve_task
|
| 5 |
-
|
| 6 |
-
__all__ = ['ANALYTICAL_SOLVERS', 'solve_task']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/solvers/analytical.py
DELETED
|
@@ -1,78 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Basic analytical solvers: identity, constant, color_map, transpose."""
|
| 3 |
-
|
| 4 |
-
import numpy as np
|
| 5 |
-
from onnx import helper, numpy_helper, TensorProto
|
| 6 |
-
from ..onnx_helpers import mk, _make_int64_init
|
| 7 |
-
from ..data_loader import get_exs, fixed_shapes
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def s_identity(td):
|
| 11 |
-
"""Identity solver."""
|
| 12 |
-
for ex in td['train'] + td['test']:
|
| 13 |
-
if ex['input'] != ex['output']:
|
| 14 |
-
return None
|
| 15 |
-
return mk([helper.make_node('Identity', ['input'], ['output'])])
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def s_color_map(td):
|
| 19 |
-
"""Color mapping solver."""
|
| 20 |
-
cm = {}
|
| 21 |
-
for ex in td['train'] + td['test']:
|
| 22 |
-
inp, out = np.array(ex['input']), np.array(ex['output'])
|
| 23 |
-
if inp.shape != out.shape:
|
| 24 |
-
return None
|
| 25 |
-
for iv, ov in zip(inp.flat, out.flat):
|
| 26 |
-
iv, ov = int(iv), int(ov)
|
| 27 |
-
if iv in cm and cm[iv] != ov:
|
| 28 |
-
return None
|
| 29 |
-
cm[iv] = ov
|
| 30 |
-
is_permutation = (set(cm.keys()) == set(cm.values()))
|
| 31 |
-
if is_permutation:
|
| 32 |
-
gather_ch = np.arange(10, dtype=np.int32)
|
| 33 |
-
for src, dst in cm.items():
|
| 34 |
-
if 0 <= src < 10 and 0 <= dst < 10:
|
| 35 |
-
gather_ch[dst] = src
|
| 36 |
-
inits = [numpy_helper.from_array(gather_ch, 'gi')]
|
| 37 |
-
nodes = [helper.make_node('Gather', ['input', 'gi'], ['output'], axis=1)]
|
| 38 |
-
return mk(nodes, inits)
|
| 39 |
-
else:
|
| 40 |
-
W = np.zeros((10, 10, 1, 1), dtype=np.float32)
|
| 41 |
-
for ic in range(10):
|
| 42 |
-
W[cm.get(ic, ic), ic, 0, 0] = 1.0
|
| 43 |
-
return mk([helper.make_node('Conv', ['input', 'W'], ['output'], kernel_shape=[1, 1])],
|
| 44 |
-
[numpy_helper.from_array(W, 'W')])
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
def s_transpose(td):
|
| 48 |
-
"""Transpose solver."""
|
| 49 |
-
for ex in td['train'] + td['test']:
|
| 50 |
-
if not np.array_equal(np.array(ex['output']), np.array(ex['input']).T):
|
| 51 |
-
return None
|
| 52 |
-
return mk([helper.make_node('Transpose', ['input'], ['output'], perm=[0, 1, 3, 2])])
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
def s_constant(td):
|
| 56 |
-
"""Constant output solver using opset 17 ReduceSum."""
|
| 57 |
-
sp = fixed_shapes(td)
|
| 58 |
-
if sp is None:
|
| 59 |
-
return None
|
| 60 |
-
exs = get_exs(td)
|
| 61 |
-
outs = [out for _, out in exs]
|
| 62 |
-
if not all(np.array_equal(outs[0], o) for o in outs[1:]):
|
| 63 |
-
return None
|
| 64 |
-
const = np.zeros((1, 10, 30, 30), dtype=np.float32)
|
| 65 |
-
for r, row in enumerate(outs[0]):
|
| 66 |
-
for c, v in enumerate(row):
|
| 67 |
-
const[0, int(v), r, c] = 1.0
|
| 68 |
-
inits = [
|
| 69 |
-
numpy_helper.from_array(np.array(0.0, dtype=np.float32), 'z'),
|
| 70 |
-
numpy_helper.from_array(const, 'c'),
|
| 71 |
-
_make_int64_init('rs_axes_cst', [1, 2, 3]),
|
| 72 |
-
]
|
| 73 |
-
nodes = [
|
| 74 |
-
helper.make_node('Mul', ['input', 'z'], ['zd']),
|
| 75 |
-
helper.make_node('ReduceSum', ['zd', 'rs_axes_cst'], ['s'], keepdims=1),
|
| 76 |
-
helper.make_node('Add', ['s', 'c'], ['output']),
|
| 77 |
-
]
|
| 78 |
-
return mk(nodes, inits)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/solvers/conv.py
DELETED
|
@@ -1,544 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Convolutional solvers with least squares fitting.
|
| 3 |
-
|
| 4 |
-
v5.1: Refactored into composable primitives (_build_patch_matrix, _solve_weights,
|
| 5 |
-
_extract_weights) + PCR (PCA regression) fallback via _solve_weights_pcr.
|
| 6 |
-
PCR tested on 400 tasks: 0 new solves but no regressions. Code kept for
|
| 7 |
-
future experiments (Lasso, Ridge can reuse the same _solve_weights interface).
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
-
import time
|
| 11 |
-
import numpy as np
|
| 12 |
-
import onnx
|
| 13 |
-
from onnx import helper, numpy_helper
|
| 14 |
-
from ..onnx_helpers import mk, _make_int64_init, _build_pad_node, add_onehot_block
|
| 15 |
-
from ..data_loader import get_exs, get_exs_for_fitting, get_exs_for_fitting_variable, fixed_shapes
|
| 16 |
-
from ..validators import validate
|
| 17 |
-
from ..constants import GH, GW
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
# ---------------------------------------------------------------------------
|
| 21 |
-
# Core fitting primitives (composable: mix _build_patch_matrix with any solver)
|
| 22 |
-
# ---------------------------------------------------------------------------
|
| 23 |
-
|
| 24 |
-
def _build_patch_matrix(exs_raw, ks, use_bias, use_full_30=False):
|
| 25 |
-
"""Build patch matrix P and target matrix T_oh from examples.
|
| 26 |
-
Returns (P, T, T_oh) or None if infeasible."""
|
| 27 |
-
pad = ks // 2
|
| 28 |
-
feat = 10 * ks * ks + (1 if use_bias else 0)
|
| 29 |
-
if feat > 20000:
|
| 30 |
-
return None
|
| 31 |
-
patches, targets = [], []
|
| 32 |
-
for inp_g, out_g in exs_raw:
|
| 33 |
-
ih, iw = inp_g.shape
|
| 34 |
-
if use_full_30:
|
| 35 |
-
oh_full = np.zeros((10, GH, GW), dtype=np.float64)
|
| 36 |
-
for c in range(10):
|
| 37 |
-
oh_full[c, :ih, :iw] = (inp_g == c)
|
| 38 |
-
oh_pad = np.pad(oh_full, ((0, 0), (pad, pad), (pad, pad)))
|
| 39 |
-
else:
|
| 40 |
-
oh_enc = np.zeros((10, ih, iw), dtype=np.float64)
|
| 41 |
-
for c in range(10):
|
| 42 |
-
oh_enc[c] = (inp_g == c)
|
| 43 |
-
oh_pad = np.pad(oh_enc, ((0, 0), (pad, pad), (pad, pad)))
|
| 44 |
-
oh, ow = out_g.shape
|
| 45 |
-
for r in range(oh):
|
| 46 |
-
for c in range(ow):
|
| 47 |
-
p = oh_pad[:, r:r + ks, c:c + ks].flatten()
|
| 48 |
-
if use_bias:
|
| 49 |
-
p = np.append(p, 1.0)
|
| 50 |
-
patches.append(p)
|
| 51 |
-
targets.append(int(out_g[r, c]))
|
| 52 |
-
n_patches = len(patches)
|
| 53 |
-
if feat > 5000 and n_patches > 2000:
|
| 54 |
-
return None
|
| 55 |
-
P = np.array(patches, dtype=np.float64)
|
| 56 |
-
T = np.array(targets, dtype=np.int64)
|
| 57 |
-
T_oh = np.zeros((len(T), 10), dtype=np.float64)
|
| 58 |
-
for i, t in enumerate(T):
|
| 59 |
-
T_oh[i, t] = 1.0
|
| 60 |
-
return P, T, T_oh
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
def _solve_weights(P, T, T_oh):
|
| 64 |
-
"""Raw lstsq solve. Returns WT (p×10) or None."""
|
| 65 |
-
try:
|
| 66 |
-
WT = np.linalg.lstsq(P, T_oh, rcond=None)[0]
|
| 67 |
-
except (np.linalg.LinAlgError, ValueError):
|
| 68 |
-
return None
|
| 69 |
-
if not np.array_equal(np.argmax(P @ WT, axis=1), T):
|
| 70 |
-
return None
|
| 71 |
-
return WT
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
def _solve_weights_pcr(P, T, T_oh, var_thresholds=(0.999, 0.99, 0.95)):
|
| 75 |
-
"""PCA/Truncated SVD regression. Try multiple variance thresholds.
|
| 76 |
-
Returns WT (p×10) or None.
|
| 77 |
-
Only attempted when p/n > 0.5 (potential overfitting zone).
|
| 78 |
-
|
| 79 |
-
Tested 2026-04-26: improves arc-gen accuracy by 3-9% on 4/345 unsolved
|
| 80 |
-
tasks but never reaches 100% required for validation. Kept as fallback
|
| 81 |
-
for marginal cases and for future combination with more arc-gen data."""
|
| 82 |
-
n, p = P.shape
|
| 83 |
-
if p / max(n, 1) <= 0.5:
|
| 84 |
-
return None # lstsq is safe here, no need for PCR
|
| 85 |
-
try:
|
| 86 |
-
U, s, Vt = np.linalg.svd(P, full_matrices=False)
|
| 87 |
-
except (np.linalg.LinAlgError, ValueError):
|
| 88 |
-
return None
|
| 89 |
-
cumvar = np.cumsum(s**2) / np.sum(s**2)
|
| 90 |
-
for thresh in var_thresholds:
|
| 91 |
-
k = int(np.searchsorted(cumvar, thresh)) + 1
|
| 92 |
-
k = max(k, 5)
|
| 93 |
-
k = min(k, min(n, p))
|
| 94 |
-
P_red = U[:, :k] * s[:k]
|
| 95 |
-
try:
|
| 96 |
-
w_red = np.linalg.lstsq(P_red, T_oh, rcond=None)[0]
|
| 97 |
-
except (np.linalg.LinAlgError, ValueError):
|
| 98 |
-
continue
|
| 99 |
-
if not np.array_equal(np.argmax(P_red @ w_red, axis=1), T):
|
| 100 |
-
continue
|
| 101 |
-
# Map back to full p-dimensional weights for ONNX conv
|
| 102 |
-
WT = Vt[:k].T @ w_red
|
| 103 |
-
# Verify full-space predictions match
|
| 104 |
-
if np.array_equal(np.argmax(P @ WT, axis=1), T):
|
| 105 |
-
return WT
|
| 106 |
-
return None
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
def _extract_weights(WT, ks, use_bias):
|
| 110 |
-
"""Extract Wconv and B from weight matrix WT."""
|
| 111 |
-
if use_bias:
|
| 112 |
-
Wconv = WT[:-1].T.reshape(10, 10, ks, ks).astype(np.float32)
|
| 113 |
-
B = WT[-1].astype(np.float32)
|
| 114 |
-
else:
|
| 115 |
-
Wconv = WT.T.reshape(10, 10, ks, ks).astype(np.float32)
|
| 116 |
-
B = None
|
| 117 |
-
return Wconv, B
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
# ---------------------------------------------------------------------------
|
| 121 |
-
# Convenience wrappers (combine primitives into single-call fitting)
|
| 122 |
-
# ---------------------------------------------------------------------------
|
| 123 |
-
|
| 124 |
-
def _lstsq_conv(exs_raw, ks, use_bias, use_full_30=False):
|
| 125 |
-
"""Least squares convolutional weight fitting.
|
| 126 |
-
Returns (Wconv, B) or None."""
|
| 127 |
-
ptm = _build_patch_matrix(exs_raw, ks, use_bias, use_full_30)
|
| 128 |
-
if ptm is None:
|
| 129 |
-
return None
|
| 130 |
-
P, T, T_oh = ptm
|
| 131 |
-
WT = _solve_weights(P, T, T_oh)
|
| 132 |
-
if WT is None:
|
| 133 |
-
return None
|
| 134 |
-
return _extract_weights(WT, ks, use_bias)
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
def _lstsq_conv_pcr(exs_raw, ks, use_bias, use_full_30=False):
|
| 138 |
-
"""PCA regression convolutional weight fitting.
|
| 139 |
-
Returns (Wconv, B) or None. Fallback when raw lstsq overfits."""
|
| 140 |
-
ptm = _build_patch_matrix(exs_raw, ks, use_bias, use_full_30)
|
| 141 |
-
if ptm is None:
|
| 142 |
-
return None
|
| 143 |
-
P, T, T_oh = ptm
|
| 144 |
-
WT = _solve_weights_pcr(P, T, T_oh)
|
| 145 |
-
if WT is None:
|
| 146 |
-
return None
|
| 147 |
-
return _extract_weights(WT, ks, use_bias)
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
# ---------------------------------------------------------------------------
|
| 151 |
-
# Solver functions (called from solver_registry.py)
|
| 152 |
-
# ---------------------------------------------------------------------------
|
| 153 |
-
|
| 154 |
-
def _build_and_validate_conv_fixed(fit_fn, fit_exs, ks, use_bias, IH, IW, td, path, providers):
|
| 155 |
-
"""Build ONNX model with given fit function, validate it. Returns (tag, model) or None."""
|
| 156 |
-
result = fit_fn(fit_exs, ks, use_bias, use_full_30=False)
|
| 157 |
-
if result is None:
|
| 158 |
-
return None
|
| 159 |
-
Wconv, B = result
|
| 160 |
-
pad = ks // 2
|
| 161 |
-
pad_h, pad_w = GH - IH, GW - IW
|
| 162 |
-
inits = [
|
| 163 |
-
_make_int64_init('sl_st', [0, 0, 0, 0]),
|
| 164 |
-
_make_int64_init('sl_en', [1, 10, IH, IW]),
|
| 165 |
-
numpy_helper.from_array(Wconv, 'W'),
|
| 166 |
-
]
|
| 167 |
-
conv_inputs = ['grid', 'W']
|
| 168 |
-
if B is not None:
|
| 169 |
-
inits.append(numpy_helper.from_array(B, 'B'))
|
| 170 |
-
conv_inputs.append('B')
|
| 171 |
-
nodes = [
|
| 172 |
-
helper.make_node('Slice', ['input', 'sl_st', 'sl_en'], ['grid']),
|
| 173 |
-
helper.make_node('Conv', conv_inputs, ['co'], kernel_shape=[ks, ks], pads=[pad] * 4),
|
| 174 |
-
helper.make_node('ArgMax', ['co'], ['am'], axis=1, keepdims=1),
|
| 175 |
-
]
|
| 176 |
-
add_onehot_block(nodes, inits, 'am', 'oh_out')
|
| 177 |
-
nodes.append(_build_pad_node('oh_out', 'output', pad_h, pad_w, inits))
|
| 178 |
-
model = mk(nodes, inits)
|
| 179 |
-
onnx.save(model, path)
|
| 180 |
-
if validate(path, td, providers):
|
| 181 |
-
tag = 'conv_fixed' if fit_fn == _lstsq_conv else 'conv_fixed_pcr'
|
| 182 |
-
return tag, model
|
| 183 |
-
return None
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
def solve_conv_fixed(td, path, providers, time_budget=30.0):
|
| 187 |
-
"""Fixed-shape convolutional solver. Tries lstsq first, PCR as second pass."""
|
| 188 |
-
exs = get_exs(td)
|
| 189 |
-
for inp, out in exs:
|
| 190 |
-
if inp.shape != out.shape:
|
| 191 |
-
return None
|
| 192 |
-
shapes = set(inp.shape for inp, _ in exs)
|
| 193 |
-
if len(shapes) != 1:
|
| 194 |
-
return None
|
| 195 |
-
IH, IW = shapes.pop()
|
| 196 |
-
fit_exs = get_exs_for_fitting(td)
|
| 197 |
-
fit_exs = [(i, o) for i, o in fit_exs if i.shape == o.shape and i.shape == (IH, IW)]
|
| 198 |
-
t_start = time.time()
|
| 199 |
-
# Pass 1: raw lstsq (same as baseline)
|
| 200 |
-
failed_ks = [] # (ks, use_bias) pairs where lstsq fit train but failed validation
|
| 201 |
-
for use_bias in [False, True]:
|
| 202 |
-
for ks in [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]:
|
| 203 |
-
if time.time() - t_start > time_budget:
|
| 204 |
-
return None
|
| 205 |
-
result = _lstsq_conv(fit_exs, ks, use_bias, use_full_30=False)
|
| 206 |
-
if result is None:
|
| 207 |
-
continue
|
| 208 |
-
Wconv, B = result
|
| 209 |
-
pad = ks // 2
|
| 210 |
-
pad_h, pad_w = GH - IH, GW - IW
|
| 211 |
-
inits = [
|
| 212 |
-
_make_int64_init('sl_st', [0, 0, 0, 0]),
|
| 213 |
-
_make_int64_init('sl_en', [1, 10, IH, IW]),
|
| 214 |
-
numpy_helper.from_array(Wconv, 'W'),
|
| 215 |
-
]
|
| 216 |
-
conv_inputs = ['grid', 'W']
|
| 217 |
-
if B is not None:
|
| 218 |
-
inits.append(numpy_helper.from_array(B, 'B'))
|
| 219 |
-
conv_inputs.append('B')
|
| 220 |
-
nodes = [
|
| 221 |
-
helper.make_node('Slice', ['input', 'sl_st', 'sl_en'], ['grid']),
|
| 222 |
-
helper.make_node('Conv', conv_inputs, ['co'], kernel_shape=[ks, ks], pads=[pad] * 4),
|
| 223 |
-
helper.make_node('ArgMax', ['co'], ['am'], axis=1, keepdims=1),
|
| 224 |
-
]
|
| 225 |
-
add_onehot_block(nodes, inits, 'am', 'oh_out')
|
| 226 |
-
nodes.append(_build_pad_node('oh_out', 'output', pad_h, pad_w, inits))
|
| 227 |
-
model = mk(nodes, inits)
|
| 228 |
-
onnx.save(model, path)
|
| 229 |
-
if validate(path, td, providers):
|
| 230 |
-
return 'conv_fixed', model
|
| 231 |
-
# lstsq fit train but failed validation — candidate for PCR
|
| 232 |
-
failed_ks.append((ks, use_bias))
|
| 233 |
-
# Pass 2: PCR on failed ks values (only if time remains)
|
| 234 |
-
for ks, use_bias in failed_ks:
|
| 235 |
-
if time.time() - t_start > time_budget:
|
| 236 |
-
return None
|
| 237 |
-
r = _build_and_validate_conv_fixed(_lstsq_conv_pcr, fit_exs, ks, use_bias, IH, IW, td, path, providers)
|
| 238 |
-
if r is not None:
|
| 239 |
-
return r
|
| 240 |
-
return None
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
def _build_and_validate_conv_var(fit_fn, fit_exs, ks, use_bias, td, path, providers):
|
| 244 |
-
"""Build variable-shape ONNX model with given fit function. Returns (tag, model) or None."""
|
| 245 |
-
result = fit_fn(fit_exs, ks, use_bias, use_full_30=True)
|
| 246 |
-
if result is None:
|
| 247 |
-
return None
|
| 248 |
-
Wconv, B = result
|
| 249 |
-
pad = ks // 2
|
| 250 |
-
inits = [
|
| 251 |
-
numpy_helper.from_array(Wconv, 'W'),
|
| 252 |
-
_make_int64_init('rs_axes_var', [1]),
|
| 253 |
-
]
|
| 254 |
-
conv_inputs = ['input', 'W']
|
| 255 |
-
if B is not None:
|
| 256 |
-
inits.append(numpy_helper.from_array(B, 'B'))
|
| 257 |
-
conv_inputs.append('B')
|
| 258 |
-
nodes = [
|
| 259 |
-
helper.make_node('ReduceSum', ['input', 'rs_axes_var'], ['mask'], keepdims=1),
|
| 260 |
-
helper.make_node('Conv', conv_inputs, ['co'], kernel_shape=[ks, ks], pads=[pad] * 4),
|
| 261 |
-
helper.make_node('ArgMax', ['co'], ['am'], axis=1, keepdims=1),
|
| 262 |
-
]
|
| 263 |
-
add_onehot_block(nodes, inits, 'am', 'oh_out')
|
| 264 |
-
nodes.append(helper.make_node('Mul', ['oh_out', 'mask'], ['output']))
|
| 265 |
-
model = mk(nodes, inits)
|
| 266 |
-
onnx.save(model, path)
|
| 267 |
-
if validate(path, td, providers):
|
| 268 |
-
tag = 'conv_var' if fit_fn == _lstsq_conv else 'conv_var_pcr'
|
| 269 |
-
return tag, model
|
| 270 |
-
return None
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
def solve_conv_variable(td, path, providers, time_budget=30.0):
|
| 274 |
-
"""Variable-shape conv. Tries lstsq first, PCR as second pass."""
|
| 275 |
-
exs = get_exs(td)
|
| 276 |
-
for inp, out in exs:
|
| 277 |
-
if inp.shape != out.shape:
|
| 278 |
-
return None
|
| 279 |
-
fit_exs = get_exs_for_fitting_variable(td)
|
| 280 |
-
fit_exs = [(i, o) for i, o in fit_exs if i.shape == o.shape]
|
| 281 |
-
t_start = time.time()
|
| 282 |
-
# Pass 1: raw lstsq
|
| 283 |
-
failed_ks = []
|
| 284 |
-
for use_bias in [False, True]:
|
| 285 |
-
for ks in [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]:
|
| 286 |
-
if time.time() - t_start > time_budget:
|
| 287 |
-
return None
|
| 288 |
-
result = _lstsq_conv(fit_exs, ks, use_bias, use_full_30=True)
|
| 289 |
-
if result is None:
|
| 290 |
-
continue
|
| 291 |
-
Wconv, B = result
|
| 292 |
-
pad = ks // 2
|
| 293 |
-
inits = [
|
| 294 |
-
numpy_helper.from_array(Wconv, 'W'),
|
| 295 |
-
_make_int64_init('rs_axes_var', [1]),
|
| 296 |
-
]
|
| 297 |
-
conv_inputs = ['input', 'W']
|
| 298 |
-
if B is not None:
|
| 299 |
-
inits.append(numpy_helper.from_array(B, 'B'))
|
| 300 |
-
conv_inputs.append('B')
|
| 301 |
-
nodes = [
|
| 302 |
-
helper.make_node('ReduceSum', ['input', 'rs_axes_var'], ['mask'], keepdims=1),
|
| 303 |
-
helper.make_node('Conv', conv_inputs, ['co'], kernel_shape=[ks, ks], pads=[pad] * 4),
|
| 304 |
-
helper.make_node('ArgMax', ['co'], ['am'], axis=1, keepdims=1),
|
| 305 |
-
]
|
| 306 |
-
add_onehot_block(nodes, inits, 'am', 'oh_out')
|
| 307 |
-
nodes.append(helper.make_node('Mul', ['oh_out', 'mask'], ['output']))
|
| 308 |
-
model = mk(nodes, inits)
|
| 309 |
-
onnx.save(model, path)
|
| 310 |
-
if validate(path, td, providers):
|
| 311 |
-
return 'conv_var', model
|
| 312 |
-
failed_ks.append((ks, use_bias))
|
| 313 |
-
# Pass 2: PCR on failed ks values
|
| 314 |
-
for ks, use_bias in failed_ks:
|
| 315 |
-
if time.time() - t_start > time_budget:
|
| 316 |
-
return None
|
| 317 |
-
r = _build_and_validate_conv_var(_lstsq_conv_pcr, fit_exs, ks, use_bias, td, path, providers)
|
| 318 |
-
if r is not None:
|
| 319 |
-
return r
|
| 320 |
-
return None
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
def solve_conv_diffshape(td, path, providers, time_budget=30.0):
|
| 324 |
-
"""Different-shape convolutional solver. Tries lstsq first, PCR as second pass."""
|
| 325 |
-
sp = fixed_shapes(td)
|
| 326 |
-
if sp is None:
|
| 327 |
-
return None
|
| 328 |
-
(IH, IW), (OH, OW) = sp
|
| 329 |
-
if IH == OH and IW == OW:
|
| 330 |
-
return None
|
| 331 |
-
if OH > IH or OW > IW:
|
| 332 |
-
return None
|
| 333 |
-
if OH > 30 or OW > 30:
|
| 334 |
-
return None
|
| 335 |
-
exs = get_exs(td)
|
| 336 |
-
t_start = time.time()
|
| 337 |
-
failed_configs = [] # (P, T, T_oh, ks, use_bias, dr_off, dc_off) for PCR retry
|
| 338 |
-
for dr_off, dc_off in [(0, 0), ((IH - OH) // 2, (IW - OW) // 2)]:
|
| 339 |
-
for use_bias in [False, True]:
|
| 340 |
-
for ks in [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]:
|
| 341 |
-
if time.time() - t_start > time_budget:
|
| 342 |
-
break
|
| 343 |
-
pad = ks // 2
|
| 344 |
-
feat = 10 * ks * ks + (1 if use_bias else 0)
|
| 345 |
-
if feat > 10000:
|
| 346 |
-
continue
|
| 347 |
-
patches, targets = [], []
|
| 348 |
-
valid = True
|
| 349 |
-
for inp_g, out_g in exs:
|
| 350 |
-
oh_enc = np.zeros((10, IH, IW), dtype=np.float64)
|
| 351 |
-
for c in range(10):
|
| 352 |
-
oh_enc[c] = (inp_g == c)
|
| 353 |
-
oh_pad = np.pad(oh_enc, ((0, 0), (pad, pad), (pad, pad)))
|
| 354 |
-
for r in range(OH):
|
| 355 |
-
for c in range(OW):
|
| 356 |
-
sr, sc = r + dr_off, c + dc_off
|
| 357 |
-
if sr < 0 or sr >= IH or sc < 0 or sc >= IW:
|
| 358 |
-
valid = False
|
| 359 |
-
break
|
| 360 |
-
p = oh_pad[:, sr:sr + ks, sc:sc + ks].flatten()
|
| 361 |
-
if use_bias:
|
| 362 |
-
p = np.append(p, 1.0)
|
| 363 |
-
patches.append(p)
|
| 364 |
-
targets.append(int(out_g[r, c]))
|
| 365 |
-
if not valid:
|
| 366 |
-
break
|
| 367 |
-
if not valid:
|
| 368 |
-
break
|
| 369 |
-
if not valid:
|
| 370 |
-
continue
|
| 371 |
-
n_patches = len(patches)
|
| 372 |
-
if feat > 5000 and n_patches > 2000:
|
| 373 |
-
continue
|
| 374 |
-
P = np.array(patches, dtype=np.float64)
|
| 375 |
-
T = np.array(targets, dtype=np.int64)
|
| 376 |
-
T_oh = np.zeros((len(T), 10), dtype=np.float64)
|
| 377 |
-
for i, t in enumerate(T):
|
| 378 |
-
T_oh[i, t] = 1.0
|
| 379 |
-
# Pass 1: raw lstsq
|
| 380 |
-
WT = _solve_weights(P, T, T_oh)
|
| 381 |
-
if WT is None:
|
| 382 |
-
continue
|
| 383 |
-
Wconv, B = _extract_weights(WT, ks, use_bias)
|
| 384 |
-
pad_h, pad_w = GH - OH, GW - OW
|
| 385 |
-
inits = [
|
| 386 |
-
_make_int64_init('sl_st', [0, 0, 0, 0]),
|
| 387 |
-
_make_int64_init('sl_en', [1, 10, IH, IW]),
|
| 388 |
-
numpy_helper.from_array(Wconv, 'W'),
|
| 389 |
-
_make_int64_init('cr_st', [0, 0, dr_off, dc_off]),
|
| 390 |
-
_make_int64_init('cr_en', [1, 10, dr_off + OH, dc_off + OW]),
|
| 391 |
-
]
|
| 392 |
-
conv_inputs = ['grid', 'W']
|
| 393 |
-
if B is not None:
|
| 394 |
-
inits.append(numpy_helper.from_array(B, 'B'))
|
| 395 |
-
conv_inputs.append('B')
|
| 396 |
-
nodes = [
|
| 397 |
-
helper.make_node('Slice', ['input', 'sl_st', 'sl_en'], ['grid']),
|
| 398 |
-
helper.make_node('Conv', conv_inputs, ['co'], kernel_shape=[ks, ks], pads=[pad] * 4),
|
| 399 |
-
helper.make_node('Slice', ['co', 'cr_st', 'cr_en'], ['co_crop']),
|
| 400 |
-
helper.make_node('ArgMax', ['co_crop'], ['am'], axis=1, keepdims=1),
|
| 401 |
-
]
|
| 402 |
-
add_onehot_block(nodes, inits, 'am', 'oh_out')
|
| 403 |
-
nodes.append(_build_pad_node('oh_out', 'output', pad_h, pad_w, inits))
|
| 404 |
-
model = mk(nodes, inits)
|
| 405 |
-
onnx.save(model, path)
|
| 406 |
-
if validate(path, td, providers):
|
| 407 |
-
return 'conv_diff', model
|
| 408 |
-
# Failed validation — save for PCR retry
|
| 409 |
-
failed_configs.append((P, T, T_oh, ks, use_bias, dr_off, dc_off))
|
| 410 |
-
# Pass 2: PCR on failed configs
|
| 411 |
-
for P, T, T_oh, ks, use_bias, dr_off, dc_off in failed_configs:
|
| 412 |
-
if time.time() - t_start > time_budget:
|
| 413 |
-
return None
|
| 414 |
-
WT = _solve_weights_pcr(P, T, T_oh)
|
| 415 |
-
if WT is None:
|
| 416 |
-
continue
|
| 417 |
-
Wconv, B = _extract_weights(WT, ks, use_bias)
|
| 418 |
-
pad_h, pad_w = GH - OH, GW - OW
|
| 419 |
-
inits = [
|
| 420 |
-
_make_int64_init('sl_st', [0, 0, 0, 0]),
|
| 421 |
-
_make_int64_init('sl_en', [1, 10, IH, IW]),
|
| 422 |
-
numpy_helper.from_array(Wconv, 'W'),
|
| 423 |
-
_make_int64_init('cr_st', [0, 0, dr_off, dc_off]),
|
| 424 |
-
_make_int64_init('cr_en', [1, 10, dr_off + OH, dc_off + OW]),
|
| 425 |
-
]
|
| 426 |
-
conv_inputs = ['grid', 'W']
|
| 427 |
-
if B is not None:
|
| 428 |
-
inits.append(numpy_helper.from_array(B, 'B'))
|
| 429 |
-
conv_inputs.append('B')
|
| 430 |
-
nodes = [
|
| 431 |
-
helper.make_node('Slice', ['input', 'sl_st', 'sl_en'], ['grid']),
|
| 432 |
-
helper.make_node('Conv', conv_inputs, ['co'], kernel_shape=[ks, ks], pads=[pad] * 4),
|
| 433 |
-
helper.make_node('Slice', ['co', 'cr_st', 'cr_en'], ['co_crop']),
|
| 434 |
-
helper.make_node('ArgMax', ['co_crop'], ['am'], axis=1, keepdims=1),
|
| 435 |
-
]
|
| 436 |
-
add_onehot_block(nodes, inits, 'am', 'oh_out')
|
| 437 |
-
nodes.append(_build_pad_node('oh_out', 'output', pad_h, pad_w, inits))
|
| 438 |
-
model = mk(nodes, inits)
|
| 439 |
-
onnx.save(model, path)
|
| 440 |
-
if validate(path, td, providers):
|
| 441 |
-
return 'conv_diff_pcr', model
|
| 442 |
-
return None
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
def solve_conv_var_diff(td, path, providers, time_budget=30.0):
|
| 446 |
-
"""Variable diff-shape conv. Tries lstsq first, PCR as second pass."""
|
| 447 |
-
exs = get_exs(td)
|
| 448 |
-
t_start = time.time()
|
| 449 |
-
failed_configs = [] # (P, T, T_oh, ks, use_bias) for PCR retry
|
| 450 |
-
for use_bias in [False, True]:
|
| 451 |
-
for ks in [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]:
|
| 452 |
-
if time.time() - t_start > time_budget:
|
| 453 |
-
break
|
| 454 |
-
pad = ks // 2
|
| 455 |
-
feat = 10 * ks * ks + (1 if use_bias else 0)
|
| 456 |
-
if feat > 20000:
|
| 457 |
-
continue
|
| 458 |
-
patches, targets = [], []
|
| 459 |
-
for inp_g, out_g in exs:
|
| 460 |
-
ih, iw = inp_g.shape
|
| 461 |
-
oh, ow = out_g.shape
|
| 462 |
-
oh_full = np.zeros((10, GH, GW), dtype=np.float64)
|
| 463 |
-
for c in range(10):
|
| 464 |
-
oh_full[c, :ih, :iw] = (inp_g == c)
|
| 465 |
-
oh_pad = np.pad(oh_full, ((0, 0), (pad, pad), (pad, pad)))
|
| 466 |
-
for r in range(oh):
|
| 467 |
-
for c in range(ow):
|
| 468 |
-
p = oh_pad[:, r:r + ks, c:c + ks].flatten()
|
| 469 |
-
if use_bias:
|
| 470 |
-
p = np.append(p, 1.0)
|
| 471 |
-
patches.append(p)
|
| 472 |
-
targets.append(int(out_g[r, c]))
|
| 473 |
-
n_patches = len(patches)
|
| 474 |
-
if feat > 5000 and n_patches > 2000:
|
| 475 |
-
continue
|
| 476 |
-
P = np.array(patches, dtype=np.float64)
|
| 477 |
-
T = np.array(targets, dtype=np.int64)
|
| 478 |
-
T_oh = np.zeros((len(T), 10), dtype=np.float64)
|
| 479 |
-
for i, t in enumerate(T):
|
| 480 |
-
T_oh[i, t] = 1.0
|
| 481 |
-
# Pass 1: raw lstsq
|
| 482 |
-
WT = _solve_weights(P, T, T_oh)
|
| 483 |
-
if WT is None:
|
| 484 |
-
continue
|
| 485 |
-
Wconv, B = _extract_weights(WT, ks, use_bias)
|
| 486 |
-
all_output_within_input = all(
|
| 487 |
-
out_g.shape[0] <= inp_g.shape[0] and out_g.shape[1] <= inp_g.shape[1]
|
| 488 |
-
for inp_g, out_g in exs
|
| 489 |
-
)
|
| 490 |
-
if all_output_within_input:
|
| 491 |
-
inits = [
|
| 492 |
-
numpy_helper.from_array(Wconv, 'W'),
|
| 493 |
-
_make_int64_init('rs_axes_vd', [1]),
|
| 494 |
-
]
|
| 495 |
-
conv_inputs = ['input', 'W']
|
| 496 |
-
if B is not None:
|
| 497 |
-
inits.append(numpy_helper.from_array(B, 'B'))
|
| 498 |
-
conv_inputs.append('B')
|
| 499 |
-
nodes = [
|
| 500 |
-
helper.make_node('ReduceSum', ['input', 'rs_axes_vd'], ['mask'], keepdims=1),
|
| 501 |
-
helper.make_node('Conv', conv_inputs, ['co'], kernel_shape=[ks, ks], pads=[pad] * 4),
|
| 502 |
-
helper.make_node('ArgMax', ['co'], ['am'], axis=1, keepdims=1),
|
| 503 |
-
]
|
| 504 |
-
add_onehot_block(nodes, inits, 'am', 'oh_out')
|
| 505 |
-
nodes.append(helper.make_node('Mul', ['oh_out', 'mask'], ['output']))
|
| 506 |
-
model = mk(nodes, inits)
|
| 507 |
-
onnx.save(model, path)
|
| 508 |
-
if validate(path, td, providers):
|
| 509 |
-
return 'conv_var_diff', model
|
| 510 |
-
# Failed validation — save for PCR
|
| 511 |
-
failed_configs.append((P, T, T_oh, ks, use_bias))
|
| 512 |
-
# Pass 2: PCR on failed configs
|
| 513 |
-
for P, T, T_oh, ks, use_bias in failed_configs:
|
| 514 |
-
if time.time() - t_start > time_budget:
|
| 515 |
-
return None
|
| 516 |
-
WT = _solve_weights_pcr(P, T, T_oh)
|
| 517 |
-
if WT is None:
|
| 518 |
-
continue
|
| 519 |
-
Wconv, B = _extract_weights(WT, ks, use_bias)
|
| 520 |
-
all_output_within_input = all(
|
| 521 |
-
out_g.shape[0] <= inp_g.shape[0] and out_g.shape[1] <= inp_g.shape[1]
|
| 522 |
-
for inp_g, out_g in exs
|
| 523 |
-
)
|
| 524 |
-
if all_output_within_input:
|
| 525 |
-
inits = [
|
| 526 |
-
numpy_helper.from_array(Wconv, 'W'),
|
| 527 |
-
_make_int64_init('rs_axes_vd', [1]),
|
| 528 |
-
]
|
| 529 |
-
conv_inputs = ['input', 'W']
|
| 530 |
-
if B is not None:
|
| 531 |
-
inits.append(numpy_helper.from_array(B, 'B'))
|
| 532 |
-
conv_inputs.append('B')
|
| 533 |
-
nodes = [
|
| 534 |
-
helper.make_node('ReduceSum', ['input', 'rs_axes_vd'], ['mask'], keepdims=1),
|
| 535 |
-
helper.make_node('Conv', conv_inputs, ['co'], kernel_shape=[ks, ks], pads=[pad] * 4),
|
| 536 |
-
helper.make_node('ArgMax', ['co'], ['am'], axis=1, keepdims=1),
|
| 537 |
-
]
|
| 538 |
-
add_onehot_block(nodes, inits, 'am', 'oh_out')
|
| 539 |
-
nodes.append(helper.make_node('Mul', ['oh_out', 'mask'], ['output']))
|
| 540 |
-
model = mk(nodes, inits)
|
| 541 |
-
onnx.save(model, path)
|
| 542 |
-
if validate(path, td, providers):
|
| 543 |
-
return 'conv_var_diff_pcr', model
|
| 544 |
-
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/solvers/edge.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Edge/boundary detection solver — Laplacian Conv.
|
| 3 |
-
|
| 4 |
-
v5.2: 0 matches in current task set (edge definition too strict).
|
| 5 |
-
Kept for future variants (per-color edges, interior-only edges, etc.).
|
| 6 |
-
"""
|
| 7 |
-
|
| 8 |
-
import numpy as np
|
| 9 |
-
from onnx import helper, numpy_helper
|
| 10 |
-
from ..onnx_helpers import mk, _make_int64_init, _build_pad_node
|
| 11 |
-
from ..data_loader import get_exs, fixed_shapes
|
| 12 |
-
from ..constants import GH, GW
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
def _has_edges(inp, out, edge_color, bg_color=0):
|
| 16 |
-
"""Check if output is edge detection of input."""
|
| 17 |
-
h, w = inp.shape
|
| 18 |
-
for r in range(h):
|
| 19 |
-
for c in range(w):
|
| 20 |
-
pix = inp[r, c]
|
| 21 |
-
is_edge = False
|
| 22 |
-
if pix != bg_color:
|
| 23 |
-
for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
|
| 24 |
-
nr, nc = r + dr, c + dc
|
| 25 |
-
if 0 <= nr < h and 0 <= nc < w:
|
| 26 |
-
if inp[nr, nc] != pix:
|
| 27 |
-
is_edge = True
|
| 28 |
-
break
|
| 29 |
-
else:
|
| 30 |
-
is_edge = True
|
| 31 |
-
break
|
| 32 |
-
expected = edge_color if is_edge else bg_color
|
| 33 |
-
if out[r, c] != expected:
|
| 34 |
-
return False
|
| 35 |
-
return True
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
def _build_edge_model(IH, IW, edge_color, bg_color=0):
|
| 39 |
-
"""Build ONNX model for edge detection via Laplacian conv."""
|
| 40 |
-
pad_h, pad_w = GH - IH, GW - IW
|
| 41 |
-
|
| 42 |
-
ch_sel = np.zeros((1, 10, 1, 1), dtype=np.float32)
|
| 43 |
-
for c in range(10):
|
| 44 |
-
if c != bg_color:
|
| 45 |
-
ch_sel[0, c, 0, 0] = 1.0
|
| 46 |
-
|
| 47 |
-
lap_k = np.array([[0, -1, 0],
|
| 48 |
-
[-1, 4, -1],
|
| 49 |
-
[0, -1, 0]], dtype=np.float32).reshape(1, 1, 3, 3)
|
| 50 |
-
|
| 51 |
-
edge_oh = np.zeros((1, 10, 1, 1), dtype=np.float32)
|
| 52 |
-
edge_oh[0, edge_color, 0, 0] = 1.0
|
| 53 |
-
bg_oh = np.zeros((1, 10, 1, 1), dtype=np.float32)
|
| 54 |
-
bg_oh[0, bg_color, 0, 0] = 1.0
|
| 55 |
-
|
| 56 |
-
inits = [
|
| 57 |
-
_make_int64_init('sl_st', [0, 0, 0, 0]),
|
| 58 |
-
_make_int64_init('sl_en', [1, 10, IH, IW]),
|
| 59 |
-
numpy_helper.from_array(ch_sel, 'ch_sel'),
|
| 60 |
-
numpy_helper.from_array(lap_k, 'lap_k'),
|
| 61 |
-
numpy_helper.from_array(np.float32(0.5), 'thresh'),
|
| 62 |
-
numpy_helper.from_array(edge_oh, 'edge_oh'),
|
| 63 |
-
numpy_helper.from_array(bg_oh, 'bg_oh'),
|
| 64 |
-
]
|
| 65 |
-
|
| 66 |
-
nodes = [
|
| 67 |
-
helper.make_node('Slice', ['input', 'sl_st', 'sl_en'], ['cropped']),
|
| 68 |
-
helper.make_node('Conv', ['cropped', 'ch_sel'], ['occ'], kernel_shape=[1, 1]),
|
| 69 |
-
helper.make_node('Conv', ['occ', 'lap_k'], ['lap_out'], kernel_shape=[3, 3], pads=[1, 1, 1, 1]),
|
| 70 |
-
helper.make_node('Abs', ['lap_out'], ['lap_abs']),
|
| 71 |
-
helper.make_node('Greater', ['lap_abs', 'thresh'], ['is_edge_raw']),
|
| 72 |
-
helper.make_node('Greater', ['occ', 'thresh'], ['is_occ']),
|
| 73 |
-
helper.make_node('And', ['is_edge_raw', 'is_occ'], ['is_edge']),
|
| 74 |
-
helper.make_node('Where', ['is_edge', 'edge_oh', 'bg_oh'], ['result_small']),
|
| 75 |
-
]
|
| 76 |
-
nodes.append(_build_pad_node('result_small', 'output', pad_h, pad_w, inits))
|
| 77 |
-
return mk(nodes, inits)
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
def s_edge_detect(td):
|
| 81 |
-
"""Edge detection solver: output = boundary pixels of input shapes."""
|
| 82 |
-
exs = get_exs(td)
|
| 83 |
-
sp = fixed_shapes(td)
|
| 84 |
-
if sp is None:
|
| 85 |
-
return None
|
| 86 |
-
(IH, IW), (OH, OW) = sp
|
| 87 |
-
if (IH, IW) != (OH, OW):
|
| 88 |
-
return None
|
| 89 |
-
|
| 90 |
-
for bg_color in [0]:
|
| 91 |
-
out_colors = set()
|
| 92 |
-
for _, out in exs:
|
| 93 |
-
out_colors.update(out.flatten())
|
| 94 |
-
for edge_color in out_colors:
|
| 95 |
-
if edge_color == bg_color:
|
| 96 |
-
continue
|
| 97 |
-
if all(_has_edges(inp, out, edge_color, bg_color) for inp, out in exs):
|
| 98 |
-
return _build_edge_model(IH, IW, edge_color, bg_color)
|
| 99 |
-
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/solvers/geometric.py
DELETED
|
@@ -1,177 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Geometric transformation solvers: flip, rotate, shift, crop."""
|
| 3 |
-
|
| 4 |
-
import numpy as np
|
| 5 |
-
from onnx import helper
|
| 6 |
-
from ..onnx_helpers import mk, _build_slice_crop, _build_slice_reverse, _build_pad_node
|
| 7 |
-
from ..data_loader import get_exs, fixed_shapes
|
| 8 |
-
from ..gather_helpers import _build_gather_model, _build_gather_model_with_const
|
| 9 |
-
from ..constants import GH, GW
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
def s_flip(td):
|
| 13 |
-
"""Flip using Slice(step=-1) — zero MACs."""
|
| 14 |
-
exs = get_exs(td)
|
| 15 |
-
sp = fixed_shapes(td)
|
| 16 |
-
if sp is None:
|
| 17 |
-
return None
|
| 18 |
-
(IH, IW), (OH, OW) = sp
|
| 19 |
-
if (IH, IW) != (OH, OW):
|
| 20 |
-
return None
|
| 21 |
-
for axis, flip_fn in [(0, np.flipud), (1, np.fliplr)]:
|
| 22 |
-
if all(np.array_equal(out, flip_fn(inp)) for inp, out in exs):
|
| 23 |
-
onnx_axis = 2 if axis == 0 else 3
|
| 24 |
-
dim_size = IH if axis == 0 else IW
|
| 25 |
-
pad_h, pad_w = GH - IH, GW - IW
|
| 26 |
-
inits = []
|
| 27 |
-
nodes = []
|
| 28 |
-
nodes.append(_build_slice_crop('input', 'cropped', IH, IW, inits))
|
| 29 |
-
nodes.append(_build_slice_reverse('cropped', 'flipped', onnx_axis, dim_size, inits))
|
| 30 |
-
nodes.append(_build_pad_node('flipped', 'output', pad_h, pad_w, inits))
|
| 31 |
-
return mk(nodes, inits)
|
| 32 |
-
return None
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
def s_rotate(td):
|
| 36 |
-
"""Rotate using Slice+Transpose — zero MACs for square grids and k=2.
|
| 37 |
-
Gather fallback for non-square k=1,3."""
|
| 38 |
-
exs = get_exs(td)
|
| 39 |
-
sp = fixed_shapes(td)
|
| 40 |
-
if sp is None:
|
| 41 |
-
return None
|
| 42 |
-
(IH, IW), (OH, OW) = sp
|
| 43 |
-
for k in [1, 2, 3]:
|
| 44 |
-
if not all(np.array_equal(out, np.rot90(inp, k)) for inp, out in exs):
|
| 45 |
-
continue
|
| 46 |
-
if k == 2:
|
| 47 |
-
pad_h, pad_w = GH - OH, GW - OW
|
| 48 |
-
inits = []
|
| 49 |
-
nodes = []
|
| 50 |
-
nodes.append(_build_slice_crop('input', 'cropped', IH, IW, inits))
|
| 51 |
-
nodes.append(_build_slice_reverse('cropped', 'flip_h', 2, IH, inits, suffix='_h'))
|
| 52 |
-
nodes.append(_build_slice_reverse('flip_h', 'rotated', 3, IW, inits, suffix='_w'))
|
| 53 |
-
nodes.append(_build_pad_node('rotated', 'output', pad_h, pad_w, inits))
|
| 54 |
-
return mk(nodes, inits)
|
| 55 |
-
elif k == 1 and IH == IW:
|
| 56 |
-
pad_h, pad_w = GH - IH, GW - IW
|
| 57 |
-
inits = []
|
| 58 |
-
nodes = []
|
| 59 |
-
nodes.append(_build_slice_crop('input', 'cropped', IH, IW, inits))
|
| 60 |
-
nodes.append(helper.make_node('Transpose', ['cropped'], ['transposed'], perm=[0, 1, 3, 2]))
|
| 61 |
-
nodes.append(_build_slice_reverse('transposed', 'rotated', 2, IH, inits))
|
| 62 |
-
nodes.append(_build_pad_node('rotated', 'output', pad_h, pad_w, inits))
|
| 63 |
-
return mk(nodes, inits)
|
| 64 |
-
elif k == 3 and IH == IW:
|
| 65 |
-
pad_h, pad_w = GH - IH, GW - IW
|
| 66 |
-
inits = []
|
| 67 |
-
nodes = []
|
| 68 |
-
nodes.append(_build_slice_crop('input', 'cropped', IH, IW, inits))
|
| 69 |
-
nodes.append(_build_slice_reverse('cropped', 'flipped', 2, IH, inits))
|
| 70 |
-
nodes.append(helper.make_node('Transpose', ['flipped'], ['rotated'], perm=[0, 1, 3, 2]))
|
| 71 |
-
nodes.append(_build_pad_node('rotated', 'output', pad_h, pad_w, inits))
|
| 72 |
-
return mk(nodes, inits)
|
| 73 |
-
else:
|
| 74 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 75 |
-
for r in range(OH):
|
| 76 |
-
for c in range(OW):
|
| 77 |
-
if k == 1:
|
| 78 |
-
sr, sc = c, IH - 1 - r
|
| 79 |
-
elif k == 3:
|
| 80 |
-
sr, sc = IW - 1 - c, r
|
| 81 |
-
idx[r, c] = [sr, sc]
|
| 82 |
-
return _build_gather_model(OH, OW, idx)
|
| 83 |
-
return None
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
def s_shift(td):
|
| 87 |
-
"""Shift transformation solver."""
|
| 88 |
-
exs = get_exs(td)
|
| 89 |
-
sp = fixed_shapes(td)
|
| 90 |
-
if sp is None:
|
| 91 |
-
return None
|
| 92 |
-
(IH, IW), (OH, OW) = sp
|
| 93 |
-
if (IH, IW) != (OH, OW):
|
| 94 |
-
return None
|
| 95 |
-
for dr in range(-5, 6):
|
| 96 |
-
for dc in range(-5, 6):
|
| 97 |
-
if dr == 0 and dc == 0:
|
| 98 |
-
continue
|
| 99 |
-
ok = True
|
| 100 |
-
for inp, out in exs:
|
| 101 |
-
shifted = np.zeros_like(inp)
|
| 102 |
-
r0, r1 = max(0, dr), min(IH, IH + dr)
|
| 103 |
-
c0, c1 = max(0, dc), min(IW, IW + dc)
|
| 104 |
-
if r1 > r0 and c1 > c0:
|
| 105 |
-
sr0, sc0 = max(0, -dr), max(0, -dc)
|
| 106 |
-
shifted[r0:r1, c0:c1] = inp[sr0:sr0 + (r1 - r0), sc0:sc0 + (c1 - c0)]
|
| 107 |
-
if not np.array_equal(shifted, out):
|
| 108 |
-
ok = False
|
| 109 |
-
break
|
| 110 |
-
if not ok:
|
| 111 |
-
continue
|
| 112 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 113 |
-
cst = np.full((OH, OW), 0, dtype=np.int64)
|
| 114 |
-
for r in range(OH):
|
| 115 |
-
for c in range(OW):
|
| 116 |
-
sr, sc = r - dr, c - dc
|
| 117 |
-
if 0 <= sr < IH and 0 <= sc < IW:
|
| 118 |
-
idx[r, c] = [sr, sc]
|
| 119 |
-
else:
|
| 120 |
-
idx[r, c] = [-1, -1]
|
| 121 |
-
return _build_gather_model_with_const(IH, IW, OH, OW, idx, cst)
|
| 122 |
-
return None
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
def s_fixed_crop(td):
|
| 126 |
-
"""Fixed crop solver."""
|
| 127 |
-
exs = get_exs(td)
|
| 128 |
-
sp = fixed_shapes(td)
|
| 129 |
-
if sp is None:
|
| 130 |
-
return None
|
| 131 |
-
(IH, IW), (OH, OW) = sp
|
| 132 |
-
if OH > IH or OW > IW or (OH == IH and OW == IW):
|
| 133 |
-
return None
|
| 134 |
-
for r0 in range(IH - OH + 1):
|
| 135 |
-
for c0 in range(IW - OW + 1):
|
| 136 |
-
if all(np.array_equal(inp[r0:r0 + OH, c0:c0 + OW], out) for inp, out in exs):
|
| 137 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 138 |
-
for r in range(OH):
|
| 139 |
-
for c in range(OW):
|
| 140 |
-
idx[r, c] = [r0 + r, c0 + c]
|
| 141 |
-
return _build_gather_model(OH, OW, idx)
|
| 142 |
-
return None
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
def s_gravity(td):
|
| 146 |
-
"""Detect gravity-like compaction (detection only, no ONNX model built)."""
|
| 147 |
-
exs = get_exs(td)
|
| 148 |
-
sp = fixed_shapes(td)
|
| 149 |
-
if sp is None:
|
| 150 |
-
return None
|
| 151 |
-
(IH, IW), (OH, OW) = sp
|
| 152 |
-
if (IH, IW) != (OH, OW):
|
| 153 |
-
return None
|
| 154 |
-
|
| 155 |
-
def _gravity(grid, direction):
|
| 156 |
-
r = np.zeros_like(grid)
|
| 157 |
-
h, w = grid.shape
|
| 158 |
-
if direction in ('down', 'up'):
|
| 159 |
-
for c in range(w):
|
| 160 |
-
nz = grid[:, c][grid[:, c] != 0]
|
| 161 |
-
if direction == 'down':
|
| 162 |
-
r[h - len(nz):h, c] = nz
|
| 163 |
-
else:
|
| 164 |
-
r[:len(nz), c] = nz
|
| 165 |
-
else:
|
| 166 |
-
for rr in range(h):
|
| 167 |
-
nz = grid[rr, :][grid[rr, :] != 0]
|
| 168 |
-
if direction == 'right':
|
| 169 |
-
r[rr, w - len(nz):w] = nz
|
| 170 |
-
else:
|
| 171 |
-
r[rr, :len(nz)] = nz
|
| 172 |
-
return r
|
| 173 |
-
|
| 174 |
-
for d in ('down', 'up', 'left', 'right'):
|
| 175 |
-
if all(np.array_equal(_gravity(inp, d), out) for inp, out in exs):
|
| 176 |
-
return None
|
| 177 |
-
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/solvers/gravity.py
DELETED
|
@@ -1,140 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Gravity solver — unrolled bubble-sort via Conv + Where.
|
| 3 |
-
|
| 4 |
-
v5.2: Solves Task 78 (direction=up, bg=0, score 8.399).
|
| 5 |
-
Tries all 4 directions × 10 bg colors. Fixed-shape only.
|
| 6 |
-
"""
|
| 7 |
-
|
| 8 |
-
import numpy as np
|
| 9 |
-
from onnx import helper, numpy_helper
|
| 10 |
-
from ..onnx_helpers import mk, _make_int64_init, _build_pad_node, add_onehot_block
|
| 11 |
-
from ..data_loader import get_exs, fixed_shapes
|
| 12 |
-
from ..constants import GH, GW
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
def _gravity_np(grid, direction, bg_color=0):
|
| 16 |
-
"""Apply gravity in numpy for verification."""
|
| 17 |
-
r = np.full_like(grid, bg_color)
|
| 18 |
-
h, w = grid.shape
|
| 19 |
-
if direction == 'down':
|
| 20 |
-
for c in range(w):
|
| 21 |
-
nz = grid[:, c][grid[:, c] != bg_color]
|
| 22 |
-
r[h - len(nz):h, c] = nz
|
| 23 |
-
elif direction == 'up':
|
| 24 |
-
for c in range(w):
|
| 25 |
-
nz = grid[:, c][grid[:, c] != bg_color]
|
| 26 |
-
r[:len(nz), c] = nz
|
| 27 |
-
elif direction == 'right':
|
| 28 |
-
for rr in range(h):
|
| 29 |
-
nz = grid[rr, :][grid[rr, :] != bg_color]
|
| 30 |
-
r[rr, w - len(nz):w] = nz
|
| 31 |
-
elif direction == 'left':
|
| 32 |
-
for rr in range(h):
|
| 33 |
-
nz = grid[rr, :][grid[rr, :] != bg_color]
|
| 34 |
-
r[rr, :len(nz)] = nz
|
| 35 |
-
return r
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
def _build_gravity_model(IH, IW, direction, bg_color=0):
|
| 39 |
-
"""Build ONNX model for gravity via unrolled bubble-sort.
|
| 40 |
-
|
| 41 |
-
Each step compares adjacent cells and swaps if needed:
|
| 42 |
-
- If current cell is bg AND source neighbor is non-bg → fill with source
|
| 43 |
-
- If current cell is non-bg AND destination neighbor is bg → vacate to bg
|
| 44 |
-
After max(IH,IW) passes, all non-bg pixels settle in the gravity direction.
|
| 45 |
-
"""
|
| 46 |
-
pad_h, pad_w = GH - IH, GW - IW
|
| 47 |
-
n_steps = max(IH, IW)
|
| 48 |
-
|
| 49 |
-
pull_above = np.zeros((10, 10, 3, 3), dtype=np.float32)
|
| 50 |
-
pull_below = np.zeros((10, 10, 3, 3), dtype=np.float32)
|
| 51 |
-
for ch in range(10):
|
| 52 |
-
if direction == 'down':
|
| 53 |
-
pull_above[ch, ch, 0, 1] = 1.0
|
| 54 |
-
pull_below[ch, ch, 2, 1] = 1.0
|
| 55 |
-
elif direction == 'up':
|
| 56 |
-
pull_above[ch, ch, 2, 1] = 1.0
|
| 57 |
-
pull_below[ch, ch, 0, 1] = 1.0
|
| 58 |
-
elif direction == 'right':
|
| 59 |
-
pull_above[ch, ch, 1, 0] = 1.0
|
| 60 |
-
pull_below[ch, ch, 1, 2] = 1.0
|
| 61 |
-
elif direction == 'left':
|
| 62 |
-
pull_above[ch, ch, 1, 2] = 1.0
|
| 63 |
-
pull_below[ch, ch, 1, 0] = 1.0
|
| 64 |
-
|
| 65 |
-
bg_sel = np.zeros((1, 10, 1, 1), dtype=np.float32)
|
| 66 |
-
bg_sel[0, bg_color, 0, 0] = 1.0
|
| 67 |
-
bg_oh = np.zeros((1, 10, 1, 1), dtype=np.float32)
|
| 68 |
-
bg_oh[0, bg_color, 0, 0] = 1.0
|
| 69 |
-
|
| 70 |
-
inits = [
|
| 71 |
-
_make_int64_init('sl_st', [0, 0, 0, 0]),
|
| 72 |
-
_make_int64_init('sl_en', [1, 10, IH, IW]),
|
| 73 |
-
numpy_helper.from_array(pull_above, 'pull_src'),
|
| 74 |
-
numpy_helper.from_array(pull_below, 'pull_dst'),
|
| 75 |
-
numpy_helper.from_array(bg_sel, 'bg_sel'),
|
| 76 |
-
numpy_helper.from_array(bg_oh, 'bg_oh'),
|
| 77 |
-
numpy_helper.from_array(np.float32(0.5), 'half'),
|
| 78 |
-
]
|
| 79 |
-
|
| 80 |
-
nodes = [
|
| 81 |
-
helper.make_node('Slice', ['input', 'sl_st', 'sl_en'], ['cur_0']),
|
| 82 |
-
]
|
| 83 |
-
|
| 84 |
-
cur = 'cur_0'
|
| 85 |
-
for i in range(n_steps):
|
| 86 |
-
src = f'src_{i}'
|
| 87 |
-
nodes.append(helper.make_node('Conv', [cur, 'pull_src'], [src],
|
| 88 |
-
kernel_shape=[3, 3], pads=[1, 1, 1, 1]))
|
| 89 |
-
|
| 90 |
-
nodes.append(helper.make_node('Mul', [cur, 'bg_sel'], [f'cbg_{i}']))
|
| 91 |
-
inits.append(_make_int64_init(f'ax1_{i}', [1]))
|
| 92 |
-
nodes.append(helper.make_node('ReduceSum', [f'cbg_{i}', f'ax1_{i}'], [f'cbgsum_{i}'], keepdims=1))
|
| 93 |
-
nodes.append(helper.make_node('Greater', [f'cbgsum_{i}', 'half'], [f'cur_is_bg_{i}']))
|
| 94 |
-
|
| 95 |
-
nodes.append(helper.make_node('Mul', [src, 'bg_sel'], [f'sbg_{i}']))
|
| 96 |
-
inits.append(_make_int64_init(f'ax2_{i}', [1]))
|
| 97 |
-
nodes.append(helper.make_node('ReduceSum', [f'sbg_{i}', f'ax2_{i}'], [f'sbgsum_{i}'], keepdims=1))
|
| 98 |
-
nodes.append(helper.make_node('Not', [f'cur_is_bg_{i}'], [f'cur_not_bg_{i}']))
|
| 99 |
-
|
| 100 |
-
nodes.append(helper.make_node('Greater', [f'sbgsum_{i}', 'half'], [f'src_is_bg_{i}']))
|
| 101 |
-
nodes.append(helper.make_node('Not', [f'src_is_bg_{i}'], [f'src_not_bg_{i}']))
|
| 102 |
-
nodes.append(helper.make_node('And', [f'cur_is_bg_{i}', f'src_not_bg_{i}'], [f'fill_{i}']))
|
| 103 |
-
|
| 104 |
-
dst = f'dst_{i}'
|
| 105 |
-
nodes.append(helper.make_node('Conv', [cur, 'pull_dst'], [dst],
|
| 106 |
-
kernel_shape=[3, 3], pads=[1, 1, 1, 1]))
|
| 107 |
-
nodes.append(helper.make_node('Mul', [dst, 'bg_sel'], [f'dbg_{i}']))
|
| 108 |
-
inits.append(_make_int64_init(f'ax3_{i}', [1]))
|
| 109 |
-
nodes.append(helper.make_node('ReduceSum', [f'dbg_{i}', f'ax3_{i}'], [f'dbgsum_{i}'], keepdims=1))
|
| 110 |
-
nodes.append(helper.make_node('Greater', [f'dbgsum_{i}', 'half'], [f'dst_is_bg_{i}']))
|
| 111 |
-
nodes.append(helper.make_node('And', [f'cur_not_bg_{i}', f'dst_is_bg_{i}'], [f'vacate_{i}']))
|
| 112 |
-
|
| 113 |
-
nxt = f'cur_{i+1}'
|
| 114 |
-
nodes.append(helper.make_node('Where', [f'fill_{i}', src, cur], [f'tmp_{i}']))
|
| 115 |
-
nodes.append(helper.make_node('Where', [f'vacate_{i}', 'bg_oh', f'tmp_{i}'], [nxt]))
|
| 116 |
-
cur = nxt
|
| 117 |
-
|
| 118 |
-
nodes.append(helper.make_node('ArgMax', [cur], ['grav_am'], axis=1, keepdims=1))
|
| 119 |
-
add_onehot_block(nodes, inits, 'grav_am', 'grav_oh')
|
| 120 |
-
nodes.append(_build_pad_node('grav_oh', 'output', pad_h, pad_w, inits))
|
| 121 |
-
return mk(nodes, inits)
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
def s_gravity_unrolled(td):
|
| 125 |
-
"""Gravity solver with unrolled Conv+Where steps.
|
| 126 |
-
Tries all 4 directions × bg colors 0-9."""
|
| 127 |
-
exs = get_exs(td)
|
| 128 |
-
sp = fixed_shapes(td)
|
| 129 |
-
if sp is None:
|
| 130 |
-
return None
|
| 131 |
-
(IH, IW), (OH, OW) = sp
|
| 132 |
-
if (IH, IW) != (OH, OW):
|
| 133 |
-
return None
|
| 134 |
-
|
| 135 |
-
for bg_color in range(10):
|
| 136 |
-
for direction in ('down', 'up', 'left', 'right'):
|
| 137 |
-
if all(np.array_equal(_gravity_np(inp, direction, bg_color), out)
|
| 138 |
-
for inp, out in exs):
|
| 139 |
-
return _build_gravity_model(IH, IW, direction, bg_color)
|
| 140 |
-
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/solvers/mode.py
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Mode fill solver — output = solid fill of most common input color.
|
| 3 |
-
|
| 4 |
-
v5.2: Solves Task 129 (score 19.451).
|
| 5 |
-
Uses runtime ReduceSum→ArgMax→Expand for variable mode across inputs.
|
| 6 |
-
Falls through to s_constant when mode is fixed across all examples.
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
import numpy as np
|
| 10 |
-
from onnx import helper, numpy_helper, TensorProto
|
| 11 |
-
from ..onnx_helpers import mk, _make_int64_init, _build_pad_node
|
| 12 |
-
from ..data_loader import get_exs, fixed_shapes
|
| 13 |
-
from ..constants import GH, GW
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
def s_mode_fill(td):
|
| 17 |
-
"""Mode fill: output is entirely the most common color from input.
|
| 18 |
-
Uses runtime ArgMax to handle variable mode across inputs."""
|
| 19 |
-
exs = get_exs(td)
|
| 20 |
-
|
| 21 |
-
for inp, out in exs:
|
| 22 |
-
if inp.shape != out.shape:
|
| 23 |
-
return None
|
| 24 |
-
vals, counts = np.unique(inp, return_counts=True)
|
| 25 |
-
mode = vals[np.argmax(counts)]
|
| 26 |
-
if not np.all(out == mode):
|
| 27 |
-
return None
|
| 28 |
-
|
| 29 |
-
# Check if mode is always the same color
|
| 30 |
-
modes = set()
|
| 31 |
-
for inp, out in exs:
|
| 32 |
-
vals, counts = np.unique(inp, return_counts=True)
|
| 33 |
-
modes.add(vals[np.argmax(counts)])
|
| 34 |
-
|
| 35 |
-
if len(modes) == 1:
|
| 36 |
-
return None # Let s_constant handle it
|
| 37 |
-
|
| 38 |
-
sp = fixed_shapes(td)
|
| 39 |
-
if sp is None:
|
| 40 |
-
return None
|
| 41 |
-
(IH, IW), (OH, OW) = sp
|
| 42 |
-
if (IH, IW) != (OH, OW):
|
| 43 |
-
return None
|
| 44 |
-
|
| 45 |
-
pad_h, pad_w = GH - IH, GW - IW
|
| 46 |
-
|
| 47 |
-
inits = [
|
| 48 |
-
_make_int64_init('sl_st', [0, 0, 0, 0]),
|
| 49 |
-
_make_int64_init('sl_en', [1, 10, IH, IW]),
|
| 50 |
-
_make_int64_init('rs_axes_mode', [2, 3]),
|
| 51 |
-
numpy_helper.from_array(np.arange(10, dtype=np.int64).reshape(1, 10, 1, 1), 'classes'),
|
| 52 |
-
]
|
| 53 |
-
|
| 54 |
-
nodes = [
|
| 55 |
-
helper.make_node('Slice', ['input', 'sl_st', 'sl_en'], ['cropped']),
|
| 56 |
-
helper.make_node('ReduceSum', ['cropped', 'rs_axes_mode'], ['hist'], keepdims=1),
|
| 57 |
-
helper.make_node('ArgMax', ['hist'], ['mode_idx'], axis=1, keepdims=1),
|
| 58 |
-
helper.make_node('Equal', ['mode_idx', 'classes'], ['eq']),
|
| 59 |
-
helper.make_node('Cast', ['eq'], ['mode_oh'], to=TensorProto.FLOAT),
|
| 60 |
-
helper.make_node('Expand', ['mode_oh', 'sl_en'], ['expanded']),
|
| 61 |
-
]
|
| 62 |
-
nodes.append(_build_pad_node('expanded', 'output', pad_h, pad_w, inits))
|
| 63 |
-
return mk(nodes, inits)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/solvers/solver_registry.py
DELETED
|
@@ -1,163 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Solver registry and task solving orchestration."""
|
| 3 |
-
|
| 4 |
-
import os
|
| 5 |
-
import time
|
| 6 |
-
import onnx
|
| 7 |
-
from .analytical import s_identity, s_constant, s_color_map, s_transpose
|
| 8 |
-
from .geometric import s_flip, s_rotate, s_shift, s_fixed_crop, s_gravity
|
| 9 |
-
from .tiling import (s_tile, s_upscale, s_kronecker, s_nonuniform_scale, s_diagonal_tile,
|
| 10 |
-
s_mirror_h, s_mirror_v, s_quad_mirror, s_concat, s_concat_enhanced,
|
| 11 |
-
s_spatial_gather, s_varshape_spatial_gather)
|
| 12 |
-
from .gravity import s_gravity_unrolled
|
| 13 |
-
from .edge import s_edge_detect
|
| 14 |
-
from .mode import s_mode_fill
|
| 15 |
-
from .wave1 import (s_downsample_stride, s_symmetry_complete, s_extract_inner,
|
| 16 |
-
s_add_border, s_sparse_fill, s_channel_filter)
|
| 17 |
-
from .conv import solve_conv_fixed, solve_conv_variable, solve_conv_diffshape, solve_conv_var_diff
|
| 18 |
-
from ..data_loader import get_exs, fixed_shapes
|
| 19 |
-
from ..validators import validate
|
| 20 |
-
from ..profiler import score_network
|
| 21 |
-
from ..constants import EXCLUDED_TASKS, MAX_ONNX_FILESIZE
|
| 22 |
-
|
| 23 |
-
# Analytical solvers registry — order matters (cheaper first)
|
| 24 |
-
ANALYTICAL_SOLVERS = [
|
| 25 |
-
('identity', s_identity),
|
| 26 |
-
('constant', s_constant),
|
| 27 |
-
('color_map', s_color_map),
|
| 28 |
-
('transpose', s_transpose),
|
| 29 |
-
('flip', s_flip),
|
| 30 |
-
('rotate', s_rotate),
|
| 31 |
-
('shift', s_shift),
|
| 32 |
-
('tile', s_tile),
|
| 33 |
-
('upscale', s_upscale),
|
| 34 |
-
('kronecker', s_kronecker),
|
| 35 |
-
('nonuniform_scale', s_nonuniform_scale),
|
| 36 |
-
('mirror_h', s_mirror_h),
|
| 37 |
-
('mirror_v', s_mirror_v),
|
| 38 |
-
('quad_mirror', s_quad_mirror),
|
| 39 |
-
('concat', s_concat),
|
| 40 |
-
('concat_enhanced', s_concat_enhanced),
|
| 41 |
-
('diagonal_tile', s_diagonal_tile),
|
| 42 |
-
('fixed_crop', s_fixed_crop),
|
| 43 |
-
('spatial_gather', s_spatial_gather),
|
| 44 |
-
('varshape_spatial_gather', s_varshape_spatial_gather),
|
| 45 |
-
('gravity_unrolled', s_gravity_unrolled),
|
| 46 |
-
('edge_detect', s_edge_detect),
|
| 47 |
-
('mode_fill', s_mode_fill),
|
| 48 |
-
('downsample_stride', s_downsample_stride),
|
| 49 |
-
('symmetry_complete', s_symmetry_complete),
|
| 50 |
-
('extract_inner', s_extract_inner),
|
| 51 |
-
('add_border', s_add_border),
|
| 52 |
-
('sparse_fill', s_sparse_fill),
|
| 53 |
-
('channel_filter', s_channel_filter),
|
| 54 |
-
]
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
def _check_size(path):
|
| 58 |
-
"""Return True if file is within 1.44MB limit."""
|
| 59 |
-
try:
|
| 60 |
-
return os.path.getsize(path) <= MAX_ONNX_FILESIZE
|
| 61 |
-
except OSError:
|
| 62 |
-
return False
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
def _check_scoreable(path):
|
| 66 |
-
"""Return True if score_network returns valid scores (not None).
|
| 67 |
-
A model that can't be scored will be REJECTED by Kaggle."""
|
| 68 |
-
macs, memory, params = score_network(path)
|
| 69 |
-
if macs is None or memory is None or params is None:
|
| 70 |
-
return False
|
| 71 |
-
return True
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
def _accept_model(path, td, providers):
|
| 75 |
-
"""Full acceptance check: size + validate (outputs) + scoreable.
|
| 76 |
-
Returns True only if model would be accepted by Kaggle."""
|
| 77 |
-
if not _check_size(path):
|
| 78 |
-
return False
|
| 79 |
-
if not validate(path, td, providers):
|
| 80 |
-
return False
|
| 81 |
-
if not _check_scoreable(path):
|
| 82 |
-
return False
|
| 83 |
-
return True
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
def _cleanup_failed(path):
|
| 87 |
-
"""Delete leftover .onnx file from failed solve attempts.
|
| 88 |
-
Prevents bad files from ending up in submission zip."""
|
| 89 |
-
try:
|
| 90 |
-
if os.path.exists(path):
|
| 91 |
-
os.remove(path)
|
| 92 |
-
except OSError:
|
| 93 |
-
pass
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
def solve_task(tn, td, outdir, providers, conv_budget=30.0, excluded_tasks=None):
|
| 97 |
-
"""Solve a single ARC-AGI task.
|
| 98 |
-
|
| 99 |
-
Returns: (ok, solver_name, file_size, elapsed, model_path)
|
| 100 |
-
If unsolved, deletes any leftover .onnx file.
|
| 101 |
-
"""
|
| 102 |
-
if excluded_tasks is None:
|
| 103 |
-
excluded_tasks = EXCLUDED_TASKS
|
| 104 |
-
|
| 105 |
-
t_start = time.time()
|
| 106 |
-
os.makedirs(outdir, exist_ok=True)
|
| 107 |
-
path = os.path.join(outdir, f"task{tn:03d}.onnx")
|
| 108 |
-
|
| 109 |
-
if tn in excluded_tasks:
|
| 110 |
-
return False, 'excluded', None, time.time() - t_start, path
|
| 111 |
-
|
| 112 |
-
# 1. Try analytical solvers (fast, tiny models)
|
| 113 |
-
for sname, sfn in ANALYTICAL_SOLVERS:
|
| 114 |
-
try:
|
| 115 |
-
model = sfn(td)
|
| 116 |
-
if model is None:
|
| 117 |
-
continue
|
| 118 |
-
onnx.save(model, path)
|
| 119 |
-
if _accept_model(path, td, providers):
|
| 120 |
-
return True, sname, os.path.getsize(path), time.time() - t_start, path
|
| 121 |
-
except:
|
| 122 |
-
pass
|
| 123 |
-
|
| 124 |
-
# 2. Determine task shape category and try conv solvers
|
| 125 |
-
exs = get_exs(td)
|
| 126 |
-
same_shape = all(inp.shape == out.shape for inp, out in exs)
|
| 127 |
-
shapes = set(inp.shape for inp, _ in exs)
|
| 128 |
-
fixed_in = len(shapes) == 1
|
| 129 |
-
|
| 130 |
-
conv_time = conv_budget
|
| 131 |
-
|
| 132 |
-
if same_shape:
|
| 133 |
-
if fixed_in:
|
| 134 |
-
result = solve_conv_fixed(td, path, providers, time_budget=conv_time / 2)
|
| 135 |
-
if result is not None:
|
| 136 |
-
if _check_size(path) and _check_scoreable(path):
|
| 137 |
-
sname, model = result
|
| 138 |
-
return True, sname, os.path.getsize(path), time.time() - t_start, path
|
| 139 |
-
result = solve_conv_variable(td, path, providers, time_budget=conv_time)
|
| 140 |
-
if result is not None:
|
| 141 |
-
if _check_size(path) and _check_scoreable(path):
|
| 142 |
-
sname, model = result
|
| 143 |
-
return True, sname, os.path.getsize(path), time.time() - t_start, path
|
| 144 |
-
else:
|
| 145 |
-
sp = fixed_shapes(td)
|
| 146 |
-
if sp is not None:
|
| 147 |
-
(IH, IW), (OH, OW) = sp
|
| 148 |
-
if OH <= IH and OW <= IW:
|
| 149 |
-
result = solve_conv_diffshape(td, path, providers, time_budget=conv_time)
|
| 150 |
-
if result is not None:
|
| 151 |
-
if _check_size(path) and _check_scoreable(path):
|
| 152 |
-
sname, model = result
|
| 153 |
-
return True, sname, os.path.getsize(path), time.time() - t_start, path
|
| 154 |
-
|
| 155 |
-
result = solve_conv_var_diff(td, path, providers, time_budget=conv_time)
|
| 156 |
-
if result is not None:
|
| 157 |
-
if _check_size(path) and _check_scoreable(path):
|
| 158 |
-
sname, model = result
|
| 159 |
-
return True, sname, os.path.getsize(path), time.time() - t_start, path
|
| 160 |
-
|
| 161 |
-
# All solvers failed — delete leftover .onnx file
|
| 162 |
-
_cleanup_failed(path)
|
| 163 |
-
return False, None, None, time.time() - t_start, path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/solvers/tiling.py
DELETED
|
@@ -1,429 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Tiling, upscaling, mirror, concat, and spatial gather solvers."""
|
| 3 |
-
|
| 4 |
-
import numpy as np
|
| 5 |
-
from onnx import helper
|
| 6 |
-
from itertools import product as iproduct
|
| 7 |
-
from ..onnx_helpers import mk, _make_int64_init, _build_pad_node
|
| 8 |
-
from ..data_loader import get_exs, fixed_shapes
|
| 9 |
-
from ..gather_helpers import _build_gather_model, _build_gather_model_with_const
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
def s_tile(td):
|
| 13 |
-
"""Tiling solver."""
|
| 14 |
-
exs = get_exs(td)
|
| 15 |
-
in_shapes = set(inp.shape for inp, _ in exs)
|
| 16 |
-
if len(in_shapes) != 1:
|
| 17 |
-
return None
|
| 18 |
-
IH, IW = in_shapes.pop()
|
| 19 |
-
tiles = set()
|
| 20 |
-
for inp, out in exs:
|
| 21 |
-
OH, OW = out.shape
|
| 22 |
-
if OH % IH or OW % IW:
|
| 23 |
-
return None
|
| 24 |
-
rH, rW = OH // IH, OW // IW
|
| 25 |
-
if rH < 1 or rW < 1 or (rH == 1 and rW == 1):
|
| 26 |
-
return None
|
| 27 |
-
tiles.add((rH, rW))
|
| 28 |
-
if len(tiles) != 1:
|
| 29 |
-
return None
|
| 30 |
-
rH, rW = tiles.pop()
|
| 31 |
-
OH, OW = IH * rH, IW * rW
|
| 32 |
-
if OH > 30 or OW > 30:
|
| 33 |
-
return None
|
| 34 |
-
for inp, out in exs:
|
| 35 |
-
if not np.array_equal(out, np.tile(inp, (rH, rW))):
|
| 36 |
-
return None
|
| 37 |
-
pad_h, pad_w = 30 - OH, 30 - OW
|
| 38 |
-
inits = [
|
| 39 |
-
_make_int64_init('st', [0, 0, 0, 0]),
|
| 40 |
-
_make_int64_init('en', [1, 10, IH, IW]),
|
| 41 |
-
_make_int64_init('rp', [1, 1, rH, rW]),
|
| 42 |
-
]
|
| 43 |
-
nodes = [
|
| 44 |
-
helper.make_node('Slice', ['input', 'st', 'en'], ['cr']),
|
| 45 |
-
helper.make_node('Tile', ['cr', 'rp'], ['tl']),
|
| 46 |
-
]
|
| 47 |
-
nodes.append(_build_pad_node('tl', 'output', pad_h, pad_w, inits))
|
| 48 |
-
return mk(nodes, inits)
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
def s_upscale(td):
|
| 52 |
-
"""Upscaling solver."""
|
| 53 |
-
exs = get_exs(td)
|
| 54 |
-
in_shapes = set(inp.shape for inp, _ in exs)
|
| 55 |
-
if len(in_shapes) != 1:
|
| 56 |
-
return None
|
| 57 |
-
IH, IW = in_shapes.pop()
|
| 58 |
-
scales = set()
|
| 59 |
-
for inp, out in exs:
|
| 60 |
-
OH, OW = out.shape
|
| 61 |
-
if OH % IH or OW % IW:
|
| 62 |
-
return None
|
| 63 |
-
sH, sW = OH // IH, OW // IW
|
| 64 |
-
if sH < 2 or sW < 2:
|
| 65 |
-
return None
|
| 66 |
-
scales.add((sH, sW))
|
| 67 |
-
if len(scales) != 1:
|
| 68 |
-
return None
|
| 69 |
-
sH, sW = scales.pop()
|
| 70 |
-
OH, OW = IH * sH, IW * sW
|
| 71 |
-
if OH > 30 or OW > 30:
|
| 72 |
-
return None
|
| 73 |
-
for inp, out in exs:
|
| 74 |
-
if not np.array_equal(out, np.repeat(np.repeat(inp, sH, 0), sW, 1)):
|
| 75 |
-
return None
|
| 76 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 77 |
-
for r in range(OH):
|
| 78 |
-
for c in range(OW):
|
| 79 |
-
idx[r, c] = [r // sH, c // sW]
|
| 80 |
-
return _build_gather_model(OH, OW, idx)
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
def s_kronecker(td):
|
| 84 |
-
"""Kronecker product solver."""
|
| 85 |
-
exs = get_exs(td)
|
| 86 |
-
sp = fixed_shapes(td)
|
| 87 |
-
if sp is None:
|
| 88 |
-
return None
|
| 89 |
-
(IH, IW), (OH, OW) = sp
|
| 90 |
-
if OH % IH != 0 or OW % IW != 0:
|
| 91 |
-
return None
|
| 92 |
-
sH, sW = OH // IH, OW // IW
|
| 93 |
-
if sH < 2 or sW < 2:
|
| 94 |
-
return None
|
| 95 |
-
if OH > 30 or OW > 30:
|
| 96 |
-
return None
|
| 97 |
-
for inp, out in exs:
|
| 98 |
-
if not np.array_equal(out, np.kron(inp, np.ones((sH, sW), dtype=np.int64))):
|
| 99 |
-
return None
|
| 100 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 101 |
-
for r in range(OH):
|
| 102 |
-
for c in range(OW):
|
| 103 |
-
idx[r, c] = [r // sH, c // sW]
|
| 104 |
-
return _build_gather_model(OH, OW, idx)
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
def s_nonuniform_scale(td):
|
| 108 |
-
"""Non-uniform scaling solver."""
|
| 109 |
-
exs = get_exs(td)
|
| 110 |
-
sp = fixed_shapes(td)
|
| 111 |
-
if sp is None:
|
| 112 |
-
return None
|
| 113 |
-
(IH, IW), (OH, OW) = sp
|
| 114 |
-
for fh, fw in [(1, 2), (2, 1), (1, 3), (3, 1), (2, 3), (3, 2), (1, 4), (4, 1), (2, 4), (4, 2)]:
|
| 115 |
-
if OH != IH * fh or OW != IW * fw:
|
| 116 |
-
continue
|
| 117 |
-
if OH > 30 or OW > 30:
|
| 118 |
-
continue
|
| 119 |
-
if all(np.array_equal(np.repeat(np.repeat(inp, fh, 0), fw, 1), out) for inp, out in exs):
|
| 120 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 121 |
-
for r in range(OH):
|
| 122 |
-
for c in range(OW):
|
| 123 |
-
idx[r, c] = [r // fh, c // fw]
|
| 124 |
-
return _build_gather_model(OH, OW, idx)
|
| 125 |
-
return None
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
def s_diagonal_tile(td):
|
| 129 |
-
"""Diagonal tiling solver."""
|
| 130 |
-
exs = get_exs(td)
|
| 131 |
-
sp = fixed_shapes(td)
|
| 132 |
-
if sp is None:
|
| 133 |
-
return None
|
| 134 |
-
(IH, IW), (OH, OW) = sp
|
| 135 |
-
if OH % IH != 0 or OW % IW != 0:
|
| 136 |
-
return None
|
| 137 |
-
rH, rW = OH // IH, OW // IW
|
| 138 |
-
if rH != rW or rH < 2:
|
| 139 |
-
return None
|
| 140 |
-
if OH > 30 or OW > 30:
|
| 141 |
-
return None
|
| 142 |
-
for inp, out in exs:
|
| 143 |
-
for bi in range(rH):
|
| 144 |
-
for bj in range(rW):
|
| 145 |
-
block = out[bi * IH:(bi + 1) * IH, bj * IW:(bj + 1) * IW]
|
| 146 |
-
if bi == bj:
|
| 147 |
-
if not np.array_equal(block, inp):
|
| 148 |
-
return None
|
| 149 |
-
else:
|
| 150 |
-
if not np.all(block == 0):
|
| 151 |
-
return None
|
| 152 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 153 |
-
cst = np.full((OH, OW), -1, dtype=np.int64)
|
| 154 |
-
for bi in range(rH):
|
| 155 |
-
for bj in range(rW):
|
| 156 |
-
for lr in range(IH):
|
| 157 |
-
for lc in range(IW):
|
| 158 |
-
oi, oj = bi * IH + lr, bj * IW + lc
|
| 159 |
-
if bi == bj:
|
| 160 |
-
idx[oi, oj] = [lr, lc]
|
| 161 |
-
else:
|
| 162 |
-
idx[oi, oj] = [-1, -1]
|
| 163 |
-
cst[oi, oj] = 0
|
| 164 |
-
return _build_gather_model_with_const(IH, IW, OH, OW, idx, cst)
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
def s_mirror_h(td):
|
| 168 |
-
"""Horizontal mirror solver."""
|
| 169 |
-
exs = get_exs(td)
|
| 170 |
-
sp = fixed_shapes(td)
|
| 171 |
-
if sp is None:
|
| 172 |
-
return None
|
| 173 |
-
(IH, IW), (OH, OW) = sp
|
| 174 |
-
if OH != IH or OW != 2 * IW:
|
| 175 |
-
return None
|
| 176 |
-
if OW > 30:
|
| 177 |
-
return None
|
| 178 |
-
for inp, out in exs:
|
| 179 |
-
if not np.array_equal(np.concatenate([inp, np.flip(inp, 1)], 1), out):
|
| 180 |
-
return None
|
| 181 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 182 |
-
for r in range(OH):
|
| 183 |
-
for c in range(OW):
|
| 184 |
-
sc = c if c < IW else 2 * IW - 1 - c
|
| 185 |
-
idx[r, c] = [r, sc]
|
| 186 |
-
return _build_gather_model(OH, OW, idx)
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
def s_mirror_v(td):
|
| 190 |
-
"""Vertical mirror solver."""
|
| 191 |
-
exs = get_exs(td)
|
| 192 |
-
sp = fixed_shapes(td)
|
| 193 |
-
if sp is None:
|
| 194 |
-
return None
|
| 195 |
-
(IH, IW), (OH, OW) = sp
|
| 196 |
-
if OW != IW or OH != 2 * IH:
|
| 197 |
-
return None
|
| 198 |
-
if OH > 30:
|
| 199 |
-
return None
|
| 200 |
-
for inp, out in exs:
|
| 201 |
-
if not np.array_equal(np.concatenate([inp, np.flip(inp, 0)], 0), out):
|
| 202 |
-
return None
|
| 203 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 204 |
-
for r in range(OH):
|
| 205 |
-
for c in range(OW):
|
| 206 |
-
sr = r if r < IH else 2 * IH - 1 - r
|
| 207 |
-
idx[r, c] = [sr, c]
|
| 208 |
-
return _build_gather_model(OH, OW, idx)
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
def s_quad_mirror(td):
|
| 212 |
-
"""Quad mirror solver."""
|
| 213 |
-
exs = get_exs(td)
|
| 214 |
-
sp = fixed_shapes(td)
|
| 215 |
-
if sp is None:
|
| 216 |
-
return None
|
| 217 |
-
(IH, IW), (OH, OW) = sp
|
| 218 |
-
if OH != 2 * IH or OW != 2 * IW:
|
| 219 |
-
return None
|
| 220 |
-
if OH > 30 or OW > 30:
|
| 221 |
-
return None
|
| 222 |
-
for inp, out in exs:
|
| 223 |
-
expected = np.block([[inp, np.flip(inp, 1)],
|
| 224 |
-
[np.flip(inp, 0), np.flip(np.flip(inp, 0), 1)]])
|
| 225 |
-
if not np.array_equal(expected, out):
|
| 226 |
-
return None
|
| 227 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 228 |
-
for r in range(OH):
|
| 229 |
-
for c in range(OW):
|
| 230 |
-
sr = r if r < IH else 2 * IH - 1 - r
|
| 231 |
-
sc = c if c < IW else 2 * IW - 1 - c
|
| 232 |
-
idx[r, c] = [sr, sc]
|
| 233 |
-
return _build_gather_model(OH, OW, idx)
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
def s_concat(td):
|
| 237 |
-
"""Concatenation solver with transformations."""
|
| 238 |
-
exs = get_exs(td)
|
| 239 |
-
sp = fixed_shapes(td)
|
| 240 |
-
if sp is None:
|
| 241 |
-
return None
|
| 242 |
-
(IH, IW), (OH, OW) = sp
|
| 243 |
-
transforms = [
|
| 244 |
-
('id', lambda x: x), ('fliplr', lambda x: np.fliplr(x)),
|
| 245 |
-
('flipud', lambda x: np.flipud(x)), ('rot180', lambda x: np.rot90(x, 2)),
|
| 246 |
-
]
|
| 247 |
-
if OH == IH and OW % IW == 0 and OW > IW:
|
| 248 |
-
n = OW // IW
|
| 249 |
-
if 2 <= n <= 4:
|
| 250 |
-
for combo in iproduct(range(4), repeat=n):
|
| 251 |
-
if all(np.array_equal(out, np.concatenate([transforms[t][1](inp) for t in combo], axis=1))
|
| 252 |
-
for inp, out in exs):
|
| 253 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 254 |
-
for oi in range(OH):
|
| 255 |
-
for oj in range(OW):
|
| 256 |
-
bj = oj // IW
|
| 257 |
-
lr, lc = oi, oj % IW
|
| 258 |
-
t = transforms[combo[bj]][0]
|
| 259 |
-
if t == 'id':
|
| 260 |
-
sr, sc = lr, lc
|
| 261 |
-
elif t == 'fliplr':
|
| 262 |
-
sr, sc = lr, IW - 1 - lc
|
| 263 |
-
elif t == 'flipud':
|
| 264 |
-
sr, sc = IH - 1 - lr, lc
|
| 265 |
-
elif t == 'rot180':
|
| 266 |
-
sr, sc = IH - 1 - lr, IW - 1 - lc
|
| 267 |
-
idx[oi, oj] = [sr, sc]
|
| 268 |
-
return _build_gather_model(OH, OW, idx)
|
| 269 |
-
if OW == IW and OH % IH == 0 and OH > IH:
|
| 270 |
-
n = OH // IH
|
| 271 |
-
if 2 <= n <= 4:
|
| 272 |
-
for combo in iproduct(range(4), repeat=n):
|
| 273 |
-
if all(np.array_equal(out, np.concatenate([transforms[t][1](inp) for t in combo], axis=0))
|
| 274 |
-
for inp, out in exs):
|
| 275 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 276 |
-
for oi in range(OH):
|
| 277 |
-
for oj in range(OW):
|
| 278 |
-
bi = oi // IH
|
| 279 |
-
lr, lc = oi % IH, oj
|
| 280 |
-
t = transforms[combo[bi]][0]
|
| 281 |
-
if t == 'id':
|
| 282 |
-
sr, sc = lr, lc
|
| 283 |
-
elif t == 'fliplr':
|
| 284 |
-
sr, sc = lr, IW - 1 - lc
|
| 285 |
-
elif t == 'flipud':
|
| 286 |
-
sr, sc = IH - 1 - lr, lc
|
| 287 |
-
elif t == 'rot180':
|
| 288 |
-
sr, sc = IH - 1 - lr, IW - 1 - lc
|
| 289 |
-
idx[oi, oj] = [sr, sc]
|
| 290 |
-
return _build_gather_model(OH, OW, idx)
|
| 291 |
-
return None
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
def s_concat_enhanced(td):
|
| 295 |
-
"""Enhanced concatenation with all 8 dihedral transforms."""
|
| 296 |
-
exs = get_exs(td)
|
| 297 |
-
sp = fixed_shapes(td)
|
| 298 |
-
if sp is None:
|
| 299 |
-
return None
|
| 300 |
-
(IH, IW), (OH, OW) = sp
|
| 301 |
-
if IH == OH and IW == OW:
|
| 302 |
-
return None
|
| 303 |
-
if OH % IH != 0 or OW % IW != 0:
|
| 304 |
-
return None
|
| 305 |
-
rH, rW = OH // IH, OW // IW
|
| 306 |
-
if rH * rW > 16 or rH * rW < 2:
|
| 307 |
-
return None
|
| 308 |
-
if OH > 30 or OW > 30:
|
| 309 |
-
return None
|
| 310 |
-
transforms = [
|
| 311 |
-
('id', lambda x: x), ('fliplr', lambda x: np.fliplr(x)),
|
| 312 |
-
('flipud', lambda x: np.flipud(x)), ('rot180', lambda x: np.rot90(x, 2)),
|
| 313 |
-
('rot90', lambda x: np.rot90(x, 1)), ('rot270', lambda x: np.rot90(x, 3)),
|
| 314 |
-
('T', lambda x: x.T), ('T_fliplr', lambda x: np.fliplr(x.T)),
|
| 315 |
-
]
|
| 316 |
-
block_transforms = {}
|
| 317 |
-
for bi in range(rH):
|
| 318 |
-
for bj in range(rW):
|
| 319 |
-
found = None
|
| 320 |
-
for tidx, (tname, tfn) in enumerate(transforms):
|
| 321 |
-
ok = True
|
| 322 |
-
for inp, out in exs:
|
| 323 |
-
block = out[bi * IH:(bi + 1) * IH, bj * IW:(bj + 1) * IW]
|
| 324 |
-
expected = tfn(inp)
|
| 325 |
-
if expected.shape != (IH, IW) or not np.array_equal(block, expected):
|
| 326 |
-
ok = False
|
| 327 |
-
break
|
| 328 |
-
if ok:
|
| 329 |
-
found = (tidx, tname)
|
| 330 |
-
break
|
| 331 |
-
if found is None:
|
| 332 |
-
return None
|
| 333 |
-
block_transforms[(bi, bj)] = found
|
| 334 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 335 |
-
for bi in range(rH):
|
| 336 |
-
for bj in range(rW):
|
| 337 |
-
_, tname = block_transforms[(bi, bj)]
|
| 338 |
-
for lr in range(IH):
|
| 339 |
-
for lc in range(IW):
|
| 340 |
-
oi, oj = bi * IH + lr, bj * IW + lc
|
| 341 |
-
if tname == 'id':
|
| 342 |
-
sr, sc = lr, lc
|
| 343 |
-
elif tname == 'fliplr':
|
| 344 |
-
sr, sc = lr, IW - 1 - lc
|
| 345 |
-
elif tname == 'flipud':
|
| 346 |
-
sr, sc = IH - 1 - lr, lc
|
| 347 |
-
elif tname == 'rot180':
|
| 348 |
-
sr, sc = IH - 1 - lr, IW - 1 - lc
|
| 349 |
-
elif tname == 'rot90':
|
| 350 |
-
sr, sc = IW - 1 - lc, lr
|
| 351 |
-
elif tname == 'rot270':
|
| 352 |
-
sr, sc = lc, IH - 1 - lr
|
| 353 |
-
elif tname == 'T':
|
| 354 |
-
sr, sc = lc, lr
|
| 355 |
-
elif tname == 'T_fliplr':
|
| 356 |
-
sr, sc = IW - 1 - lc, lr
|
| 357 |
-
idx[oi, oj] = [sr, sc]
|
| 358 |
-
for inp, out in exs:
|
| 359 |
-
reconstructed = np.zeros_like(out)
|
| 360 |
-
for oi in range(OH):
|
| 361 |
-
for oj in range(OW):
|
| 362 |
-
reconstructed[oi, oj] = inp[idx[oi, oj, 0], idx[oi, oj, 1]]
|
| 363 |
-
if not np.array_equal(reconstructed, out):
|
| 364 |
-
return None
|
| 365 |
-
return _build_gather_model(OH, OW, idx)
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
def s_spatial_gather(td):
|
| 369 |
-
"""Spatial gather solver."""
|
| 370 |
-
sp = fixed_shapes(td)
|
| 371 |
-
if sp is None:
|
| 372 |
-
return None
|
| 373 |
-
(IH, IW), (OH, OW) = sp
|
| 374 |
-
exs = get_exs(td)
|
| 375 |
-
idx = np.full((OH, OW, 2), -1, dtype=np.int64)
|
| 376 |
-
cst = np.full((OH, OW), -1, dtype=np.int64)
|
| 377 |
-
for oi in range(OH):
|
| 378 |
-
for oj in range(OW):
|
| 379 |
-
vals = set(int(out[oi, oj]) for _, out in exs)
|
| 380 |
-
if len(vals) == 1:
|
| 381 |
-
cst[oi, oj] = vals.pop()
|
| 382 |
-
found = False
|
| 383 |
-
for ri in range(IH):
|
| 384 |
-
for rj in range(IW):
|
| 385 |
-
if all(int(inp[ri, rj]) == int(out[oi, oj]) for inp, out in exs):
|
| 386 |
-
idx[oi, oj] = [ri, rj]
|
| 387 |
-
found = True
|
| 388 |
-
break
|
| 389 |
-
if found:
|
| 390 |
-
break
|
| 391 |
-
if not found and cst[oi, oj] < 0:
|
| 392 |
-
return None
|
| 393 |
-
return _build_gather_model_with_const(IH, IW, OH, OW, idx, cst)
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
def s_varshape_spatial_gather(td):
|
| 397 |
-
"""Variable shape spatial gather solver."""
|
| 398 |
-
sp = fixed_shapes(td)
|
| 399 |
-
if sp is not None:
|
| 400 |
-
return None
|
| 401 |
-
exs = get_exs(td)
|
| 402 |
-
exs_30 = []
|
| 403 |
-
for inp, out in exs:
|
| 404 |
-
ih, iw = inp.shape
|
| 405 |
-
oh, ow = out.shape
|
| 406 |
-
inp30 = np.zeros((30, 30), dtype=np.int64)
|
| 407 |
-
out30 = np.zeros((30, 30), dtype=np.int64)
|
| 408 |
-
inp30[:ih, :iw] = inp
|
| 409 |
-
out30[:oh, :ow] = out
|
| 410 |
-
exs_30.append((inp30, out30))
|
| 411 |
-
idx = np.full((30, 30, 2), -1, dtype=np.int64)
|
| 412 |
-
cst = np.full((30, 30), -1, dtype=np.int64)
|
| 413 |
-
for oi in range(30):
|
| 414 |
-
for oj in range(30):
|
| 415 |
-
vals = set(int(out30[oi, oj]) for _, out30 in exs_30)
|
| 416 |
-
if len(vals) == 1:
|
| 417 |
-
cst[oi, oj] = vals.pop()
|
| 418 |
-
found = False
|
| 419 |
-
for ri in range(30):
|
| 420 |
-
for rj in range(30):
|
| 421 |
-
if all(int(inp30[ri, rj]) == int(out30[oi, oj]) for inp30, out30 in exs_30):
|
| 422 |
-
idx[oi, oj] = [ri, rj]
|
| 423 |
-
found = True
|
| 424 |
-
break
|
| 425 |
-
if found:
|
| 426 |
-
break
|
| 427 |
-
if not found and cst[oi, oj] < 0:
|
| 428 |
-
return None
|
| 429 |
-
return _build_gather_model_with_const(30, 30, 30, 30, idx, cst)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/solvers/wave1.py
DELETED
|
@@ -1,277 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Wave 1 static spatial remapping solvers.
|
| 3 |
-
|
| 4 |
-
A4: downsample_stride — strided sampling of input
|
| 5 |
-
A7: symmetry_complete — mirror to complete L-R or T-B symmetry
|
| 6 |
-
A1: extract_inner — remove border frame
|
| 7 |
-
A2: add_border — add constant border
|
| 8 |
-
A6: sparse_fill — pixel to block expansion
|
| 9 |
-
B1: channel_filter — keep only certain colors
|
| 10 |
-
|
| 11 |
-
Scan results (2026-04-27): 0 arc-gen validated matches.
|
| 12 |
-
Kept for future tasks and as building blocks.
|
| 13 |
-
"""
|
| 14 |
-
|
| 15 |
-
import numpy as np
|
| 16 |
-
from ..data_loader import get_exs, fixed_shapes
|
| 17 |
-
from ..gather_helpers import _build_gather_model, _build_gather_model_with_const
|
| 18 |
-
from ..onnx_helpers import mk, _make_int64_init, _build_pad_node, add_onehot_block
|
| 19 |
-
from ..constants import GH, GW
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
def s_downsample_stride(td):
|
| 23 |
-
"""out[r,c] = inp[r*sH + oH, c*sW + oW] for integer strides."""
|
| 24 |
-
exs = get_exs(td)
|
| 25 |
-
sp = fixed_shapes(td)
|
| 26 |
-
if sp is None:
|
| 27 |
-
return None
|
| 28 |
-
(IH, IW), (OH, OW) = sp
|
| 29 |
-
if OH >= IH or OW >= IW:
|
| 30 |
-
return None
|
| 31 |
-
|
| 32 |
-
for sh in range(2, 6):
|
| 33 |
-
for sw in range(2, 6):
|
| 34 |
-
for oh_off in range(sh):
|
| 35 |
-
for ow_off in range(sw):
|
| 36 |
-
ok = True
|
| 37 |
-
for inp, out in exs:
|
| 38 |
-
sampled = inp[oh_off::sh, ow_off::sw]
|
| 39 |
-
if sampled.shape != out.shape or not np.array_equal(sampled, out):
|
| 40 |
-
ok = False
|
| 41 |
-
break
|
| 42 |
-
if ok:
|
| 43 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 44 |
-
for r in range(OH):
|
| 45 |
-
for c in range(OW):
|
| 46 |
-
idx[r, c] = [r * sh + oh_off, c * sw + ow_off]
|
| 47 |
-
return _build_gather_model(OH, OW, idx)
|
| 48 |
-
return None
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
def s_symmetry_complete(td):
|
| 52 |
-
"""Complete partial T-B symmetry by adding mirrored + original via Gather."""
|
| 53 |
-
from onnx import helper, numpy_helper
|
| 54 |
-
|
| 55 |
-
exs = get_exs(td)
|
| 56 |
-
sp = fixed_shapes(td)
|
| 57 |
-
if sp is None:
|
| 58 |
-
return None
|
| 59 |
-
(IH, IW), (OH, OW) = sp
|
| 60 |
-
if (IH, IW) != (OH, OW):
|
| 61 |
-
return None
|
| 62 |
-
if IH < 2:
|
| 63 |
-
return None
|
| 64 |
-
|
| 65 |
-
# T-B symmetry: out[r,c] = max(inp[r,c], inp[IH-1-r,c])
|
| 66 |
-
ok = True
|
| 67 |
-
for inp, out in exs:
|
| 68 |
-
exp = inp.copy()
|
| 69 |
-
for r in range(IH // 2):
|
| 70 |
-
for c in range(IW):
|
| 71 |
-
v = max(int(inp[r, c]), int(inp[IH - 1 - r, c]))
|
| 72 |
-
exp[r, c] = v
|
| 73 |
-
exp[IH - 1 - r, c] = v
|
| 74 |
-
if not np.array_equal(out, exp):
|
| 75 |
-
ok = False
|
| 76 |
-
break
|
| 77 |
-
|
| 78 |
-
if ok:
|
| 79 |
-
# Build: Gather(self) + Gather(mirror) → Add → ArgMax → one-hot
|
| 80 |
-
pad_h, pad_w = GH - OH, GW - OW
|
| 81 |
-
mirror_idx = np.zeros((GH * GW,), dtype=np.int64)
|
| 82 |
-
mask = np.zeros((1, 1, GH, GW), dtype=np.float32)
|
| 83 |
-
self_idx = np.zeros((GH * GW,), dtype=np.int64)
|
| 84 |
-
for r in range(OH):
|
| 85 |
-
for c in range(OW):
|
| 86 |
-
self_idx[r * GW + c] = r * GW + c
|
| 87 |
-
mirror_idx[r * GW + c] = (IH - 1 - r) * GW + c
|
| 88 |
-
mask[0, 0, r, c] = 1.0
|
| 89 |
-
|
| 90 |
-
inits = [
|
| 91 |
-
numpy_helper.from_array(np.array([1, 10, GH * GW], dtype=np.int64), 'fs'),
|
| 92 |
-
numpy_helper.from_array(self_idx, 'self_idx'),
|
| 93 |
-
numpy_helper.from_array(mirror_idx, 'mirror_idx'),
|
| 94 |
-
numpy_helper.from_array(np.array([1, 10, GH, GW], dtype=np.int64), 'os'),
|
| 95 |
-
numpy_helper.from_array(mask, 'mask'),
|
| 96 |
-
]
|
| 97 |
-
nodes = [
|
| 98 |
-
helper.make_node('Reshape', ['input', 'fs'], ['flat']),
|
| 99 |
-
helper.make_node('Gather', ['flat', 'self_idx'], ['g_self'], axis=2),
|
| 100 |
-
helper.make_node('Gather', ['flat', 'mirror_idx'], ['g_mirror'], axis=2),
|
| 101 |
-
helper.make_node('Add', ['g_self', 'g_mirror'], ['combined']),
|
| 102 |
-
helper.make_node('Reshape', ['combined', 'os'], ['combined_2d']),
|
| 103 |
-
helper.make_node('ArgMax', ['combined_2d'], ['am'], axis=1, keepdims=1),
|
| 104 |
-
]
|
| 105 |
-
add_onehot_block(nodes, inits, 'am', 'oh_out')
|
| 106 |
-
nodes.append(helper.make_node('Mul', ['oh_out', 'mask'], ['output']))
|
| 107 |
-
return mk(nodes, inits)
|
| 108 |
-
|
| 109 |
-
# L-R symmetry: out[r,c] = max(inp[r,c], inp[r,IW-1-c])
|
| 110 |
-
if IW < 2:
|
| 111 |
-
return None
|
| 112 |
-
ok = True
|
| 113 |
-
for inp, out in exs:
|
| 114 |
-
exp = inp.copy()
|
| 115 |
-
for r in range(IH):
|
| 116 |
-
for c in range(IW // 2):
|
| 117 |
-
v = max(int(inp[r, c]), int(inp[r, IW - 1 - c]))
|
| 118 |
-
exp[r, c] = v
|
| 119 |
-
exp[r, IW - 1 - c] = v
|
| 120 |
-
if not np.array_equal(out, exp):
|
| 121 |
-
ok = False
|
| 122 |
-
break
|
| 123 |
-
|
| 124 |
-
if ok:
|
| 125 |
-
mirror_idx = np.zeros((GH * GW,), dtype=np.int64)
|
| 126 |
-
mask = np.zeros((1, 1, GH, GW), dtype=np.float32)
|
| 127 |
-
self_idx = np.zeros((GH * GW,), dtype=np.int64)
|
| 128 |
-
for r in range(OH):
|
| 129 |
-
for c in range(OW):
|
| 130 |
-
self_idx[r * GW + c] = r * GW + c
|
| 131 |
-
mirror_idx[r * GW + c] = r * GW + (IW - 1 - c)
|
| 132 |
-
mask[0, 0, r, c] = 1.0
|
| 133 |
-
|
| 134 |
-
inits = [
|
| 135 |
-
numpy_helper.from_array(np.array([1, 10, GH * GW], dtype=np.int64), 'fs'),
|
| 136 |
-
numpy_helper.from_array(self_idx, 'self_idx'),
|
| 137 |
-
numpy_helper.from_array(mirror_idx, 'mirror_idx'),
|
| 138 |
-
numpy_helper.from_array(np.array([1, 10, GH, GW], dtype=np.int64), 'os'),
|
| 139 |
-
numpy_helper.from_array(mask, 'mask'),
|
| 140 |
-
]
|
| 141 |
-
nodes = [
|
| 142 |
-
helper.make_node('Reshape', ['input', 'fs'], ['flat']),
|
| 143 |
-
helper.make_node('Gather', ['flat', 'self_idx'], ['g_self'], axis=2),
|
| 144 |
-
helper.make_node('Gather', ['flat', 'mirror_idx'], ['g_mirror'], axis=2),
|
| 145 |
-
helper.make_node('Add', ['g_self', 'g_mirror'], ['combined']),
|
| 146 |
-
helper.make_node('Reshape', ['combined', 'os'], ['combined_2d']),
|
| 147 |
-
helper.make_node('ArgMax', ['combined_2d'], ['am'], axis=1, keepdims=1),
|
| 148 |
-
]
|
| 149 |
-
add_onehot_block(nodes, inits, 'am', 'oh_out')
|
| 150 |
-
nodes.append(helper.make_node('Mul', ['oh_out', 'mask'], ['output']))
|
| 151 |
-
return mk(nodes, inits)
|
| 152 |
-
|
| 153 |
-
return None
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
def s_extract_inner(td):
|
| 157 |
-
"""Remove N-pixel border frame → smaller output."""
|
| 158 |
-
exs = get_exs(td)
|
| 159 |
-
sp = fixed_shapes(td)
|
| 160 |
-
if sp is None:
|
| 161 |
-
return None
|
| 162 |
-
(IH, IW), (OH, OW) = sp
|
| 163 |
-
|
| 164 |
-
for b in range(1, min(IH, IW) // 2):
|
| 165 |
-
if OH != IH - 2 * b or OW != IW - 2 * b:
|
| 166 |
-
continue
|
| 167 |
-
if all(np.array_equal(inp[b:IH-b, b:IW-b], out) for inp, out in exs):
|
| 168 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 169 |
-
for r in range(OH):
|
| 170 |
-
for c in range(OW):
|
| 171 |
-
idx[r, c] = [r + b, c + b]
|
| 172 |
-
return _build_gather_model(OH, OW, idx)
|
| 173 |
-
return None
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
def s_add_border(td):
|
| 177 |
-
"""Add constant-color border frame → larger output."""
|
| 178 |
-
exs = get_exs(td)
|
| 179 |
-
sp = fixed_shapes(td)
|
| 180 |
-
if sp is None:
|
| 181 |
-
return None
|
| 182 |
-
(IH, IW), (OH, OW) = sp
|
| 183 |
-
|
| 184 |
-
for b in range(1, 5):
|
| 185 |
-
if OH != IH + 2 * b or OW != IW + 2 * b:
|
| 186 |
-
continue
|
| 187 |
-
if OH > 30 or OW > 30:
|
| 188 |
-
continue
|
| 189 |
-
for bc in range(10):
|
| 190 |
-
ok = True
|
| 191 |
-
for inp, out in exs:
|
| 192 |
-
exp = np.full((OH, OW), bc, dtype=np.int64)
|
| 193 |
-
exp[b:b+IH, b:b+IW] = inp
|
| 194 |
-
if not np.array_equal(out, exp):
|
| 195 |
-
ok = False
|
| 196 |
-
break
|
| 197 |
-
if ok:
|
| 198 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 199 |
-
cst = np.full((OH, OW), -1, dtype=np.int64)
|
| 200 |
-
for r in range(OH):
|
| 201 |
-
for c in range(OW):
|
| 202 |
-
if b <= r < b + IH and b <= c < b + IW:
|
| 203 |
-
idx[r, c] = [r - b, c - b]
|
| 204 |
-
else:
|
| 205 |
-
idx[r, c] = [-1, -1]
|
| 206 |
-
cst[r, c] = bc
|
| 207 |
-
return _build_gather_model_with_const(IH, IW, OH, OW, idx, cst)
|
| 208 |
-
return None
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
def s_sparse_fill(td):
|
| 212 |
-
"""Each input pixel becomes an NxN block in output."""
|
| 213 |
-
exs = get_exs(td)
|
| 214 |
-
sp = fixed_shapes(td)
|
| 215 |
-
if sp is None:
|
| 216 |
-
return None
|
| 217 |
-
(IH, IW), (OH, OW) = sp
|
| 218 |
-
|
| 219 |
-
for bh in range(2, 10):
|
| 220 |
-
for bw in range(2, 10):
|
| 221 |
-
if OH != IH * bh or OW != IW * bw:
|
| 222 |
-
continue
|
| 223 |
-
if OH > 30 or OW > 30:
|
| 224 |
-
continue
|
| 225 |
-
ok = True
|
| 226 |
-
for inp, out in exs:
|
| 227 |
-
exp = np.zeros((OH, OW), dtype=np.int64)
|
| 228 |
-
for r in range(IH):
|
| 229 |
-
for c in range(IW):
|
| 230 |
-
exp[r*bh:(r+1)*bh, c*bw:(c+1)*bw] = inp[r, c]
|
| 231 |
-
if not np.array_equal(out, exp):
|
| 232 |
-
ok = False
|
| 233 |
-
break
|
| 234 |
-
if ok:
|
| 235 |
-
idx = np.zeros((OH, OW, 2), dtype=np.int64)
|
| 236 |
-
for r in range(OH):
|
| 237 |
-
for c in range(OW):
|
| 238 |
-
idx[r, c] = [r // bh, c // bw]
|
| 239 |
-
return _build_gather_model(OH, OW, idx)
|
| 240 |
-
return None
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
def s_channel_filter(td):
|
| 244 |
-
"""Keep only certain colors, rest → background (0)."""
|
| 245 |
-
from onnx import helper, numpy_helper
|
| 246 |
-
|
| 247 |
-
exs = get_exs(td)
|
| 248 |
-
sp = fixed_shapes(td)
|
| 249 |
-
if sp is None:
|
| 250 |
-
return None
|
| 251 |
-
(IH, IW), (OH, OW) = sp
|
| 252 |
-
if (IH, IW) != (OH, OW):
|
| 253 |
-
return None
|
| 254 |
-
|
| 255 |
-
in_colors = set()
|
| 256 |
-
out_colors = set()
|
| 257 |
-
for inp, out in exs:
|
| 258 |
-
in_colors.update(inp.flatten())
|
| 259 |
-
out_colors.update(out.flatten())
|
| 260 |
-
|
| 261 |
-
if not (out_colors < in_colors):
|
| 262 |
-
return None
|
| 263 |
-
|
| 264 |
-
keep = out_colors
|
| 265 |
-
for inp, out in exs:
|
| 266 |
-
exp = np.where(np.isin(inp, list(keep)), inp, 0)
|
| 267 |
-
if not np.array_equal(out, exp):
|
| 268 |
-
return None
|
| 269 |
-
|
| 270 |
-
ch_mask = np.zeros((1, 10, 1, 1), dtype=np.float32)
|
| 271 |
-
for c in keep:
|
| 272 |
-
if 0 <= c < 10:
|
| 273 |
-
ch_mask[0, c, 0, 0] = 1.0
|
| 274 |
-
|
| 275 |
-
inits = [numpy_helper.from_array(ch_mask, 'ch_mask')]
|
| 276 |
-
nodes = [helper.make_node('Mul', ['input', 'ch_mask'], ['output'])]
|
| 277 |
-
return mk(nodes, inits)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/submission.py
DELETED
|
@@ -1,150 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Submission file generation and task running with W&B logging."""
|
| 3 |
-
|
| 4 |
-
import os
|
| 5 |
-
import csv
|
| 6 |
-
import io
|
| 7 |
-
import math
|
| 8 |
-
import zipfile
|
| 9 |
-
from collections import Counter
|
| 10 |
-
from .profiler import score_network
|
| 11 |
-
from .constants import MAX_ONNX_FILESIZE, EXCLUDED_TASKS
|
| 12 |
-
|
| 13 |
-
try:
|
| 14 |
-
import wandb
|
| 15 |
-
except ImportError:
|
| 16 |
-
wandb = None
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
def run_tasks(task_nums, tasks, output_dir, providers, conv_budget, excluded_tasks, use_wandb):
|
| 20 |
-
"""Run all tasks and collect results.
|
| 21 |
-
|
| 22 |
-
Returns: (results, costs_dict, total_score)
|
| 23 |
-
"""
|
| 24 |
-
from .solvers.solver_registry import solve_task
|
| 25 |
-
|
| 26 |
-
results = {}
|
| 27 |
-
costs_dict = {}
|
| 28 |
-
total_score = 0
|
| 29 |
-
|
| 30 |
-
for tn in task_nums:
|
| 31 |
-
if tn not in tasks:
|
| 32 |
-
continue
|
| 33 |
-
|
| 34 |
-
td = tasks[tn]['data']
|
| 35 |
-
ok, sname, sz, t_task, model_path = solve_task(
|
| 36 |
-
tn, td, output_dir, providers, conv_budget, excluded_tasks
|
| 37 |
-
)
|
| 38 |
-
|
| 39 |
-
if ok:
|
| 40 |
-
macs, memory, params = score_network(model_path)
|
| 41 |
-
if macs is None:
|
| 42 |
-
macs, memory, params = 0, 0, 0
|
| 43 |
-
cost = macs + memory + params
|
| 44 |
-
score = max(1.0, 25.0 - math.log(max(1, cost)))
|
| 45 |
-
total_score += score
|
| 46 |
-
|
| 47 |
-
# Check per-file size limit
|
| 48 |
-
if sz and sz > MAX_ONNX_FILESIZE:
|
| 49 |
-
print(f"Task {tn:3d}: {sname:25s} OVER SIZE LIMIT ({sz:,} > {MAX_ONNX_FILESIZE:,})")
|
| 50 |
-
continue
|
| 51 |
-
|
| 52 |
-
results[tn] = (sname, t_task, sz)
|
| 53 |
-
costs_dict[tn] = cost
|
| 54 |
-
print(f"Task {tn:3d}: {sname:25s} {score:7.3f} {cost:>12} {t_task:7.3f}s ({sz:>8,} bytes)")
|
| 55 |
-
else:
|
| 56 |
-
score = 0
|
| 57 |
-
cost = 0
|
| 58 |
-
print(f"Task {tn:3d}: UNSOLVED {t_task:7.3f}s")
|
| 59 |
-
|
| 60 |
-
if use_wandb and wandb is not None:
|
| 61 |
-
wandb.log({
|
| 62 |
-
"task_id": tn,
|
| 63 |
-
"solver": sname if ok else "unsolved",
|
| 64 |
-
"onnx_bytes": sz if ok else 0,
|
| 65 |
-
"task_time_sec": t_task,
|
| 66 |
-
"cost": cost,
|
| 67 |
-
"score": score,
|
| 68 |
-
})
|
| 69 |
-
|
| 70 |
-
return results, costs_dict, total_score
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
def generate_submission(output_dir, results, costs_dict, active_tasks):
|
| 74 |
-
"""Generate submission.zip and submission.csv.
|
| 75 |
-
|
| 76 |
-
Returns dict with submission info.
|
| 77 |
-
"""
|
| 78 |
-
n_files = len([f for f in os.listdir(output_dir) if f.endswith('.onnx')])
|
| 79 |
-
total_size = sum(os.path.getsize(os.path.join(output_dir, f))
|
| 80 |
-
for f in os.listdir(output_dir) if f.endswith('.onnx'))
|
| 81 |
-
|
| 82 |
-
# Check per-file size limits
|
| 83 |
-
oversized = []
|
| 84 |
-
for f in os.listdir(output_dir):
|
| 85 |
-
if f.endswith('.onnx'):
|
| 86 |
-
fsize = os.path.getsize(os.path.join(output_dir, f))
|
| 87 |
-
if fsize > MAX_ONNX_FILESIZE:
|
| 88 |
-
oversized.append((f, fsize))
|
| 89 |
-
|
| 90 |
-
# Create submission.zip
|
| 91 |
-
parent_dir = os.path.dirname(output_dir) or '/kaggle/working/'
|
| 92 |
-
zip_path = os.path.join(parent_dir, 'submission.zip')
|
| 93 |
-
buf = io.BytesIO()
|
| 94 |
-
with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf:
|
| 95 |
-
for f in sorted(os.listdir(output_dir)):
|
| 96 |
-
if f.endswith('.onnx'):
|
| 97 |
-
zf.write(os.path.join(output_dir, f), f)
|
| 98 |
-
zip_bytes = buf.getvalue()
|
| 99 |
-
with open(zip_path, 'wb') as f:
|
| 100 |
-
f.write(zip_bytes)
|
| 101 |
-
zip_size = len(zip_bytes)
|
| 102 |
-
|
| 103 |
-
# Create submission.csv
|
| 104 |
-
csv_path = os.path.join(parent_dir, 'submission.csv')
|
| 105 |
-
with open(csv_path, 'w', newline='') as f:
|
| 106 |
-
w = csv.writer(f)
|
| 107 |
-
w.writerow(['task_id', 'total_cost'])
|
| 108 |
-
for tn in sorted(costs_dict.keys()):
|
| 109 |
-
w.writerow([f'task{tn:03d}', costs_dict[tn]])
|
| 110 |
-
|
| 111 |
-
unsolved_count = len(active_tasks) - len(results)
|
| 112 |
-
total_score = sum(max(1.0, 25.0 - math.log(max(1, cost))) for cost in costs_dict.values())
|
| 113 |
-
total_cost = sum(costs_dict.values())
|
| 114 |
-
|
| 115 |
-
return {
|
| 116 |
-
'n_files': n_files,
|
| 117 |
-
'total_size': total_size,
|
| 118 |
-
'zip_path': zip_path,
|
| 119 |
-
'zip_size': zip_size,
|
| 120 |
-
'csv_path': csv_path,
|
| 121 |
-
'total_score': total_score,
|
| 122 |
-
'total_cost': total_cost,
|
| 123 |
-
'unsolved_count': unsolved_count,
|
| 124 |
-
'oversized': oversized,
|
| 125 |
-
}
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
def print_summary(results, submission_info, elapsed):
|
| 129 |
-
"""Print summary statistics."""
|
| 130 |
-
active_count = submission_info['unsolved_count'] + len(results)
|
| 131 |
-
|
| 132 |
-
print(f"\n{'=' * 70}")
|
| 133 |
-
print(f"Solved: {len(results)}/{active_count} tasks in {elapsed:.0f}s")
|
| 134 |
-
solver_names = [v[0] for v in results.values()]
|
| 135 |
-
sc = Counter(solver_names)
|
| 136 |
-
for s, c in sc.most_common():
|
| 137 |
-
print(f" {s}: {c}")
|
| 138 |
-
|
| 139 |
-
print(f"\n{submission_info['n_files']} ONNX files, {submission_info['total_size'] / 1024:.1f} KB uncompressed")
|
| 140 |
-
print(f"ZIP size: {submission_info['zip_size'] / 1024:.1f} KB")
|
| 141 |
-
|
| 142 |
-
if submission_info['oversized']:
|
| 143 |
-
print(f"WARNING: {len(submission_info['oversized'])} files exceed 1.44MB limit:")
|
| 144 |
-
for f, sz in submission_info['oversized']:
|
| 145 |
-
print(f" {f}: {sz / 1024:.1f} KB")
|
| 146 |
-
|
| 147 |
-
print(f"\nEstimated LB score: {submission_info['total_score']:.1f}")
|
| 148 |
-
print(f"Total cost: {submission_info['total_cost']:,}")
|
| 149 |
-
print(f"Solved: {len(results)} | Unsolved: {submission_info['unsolved_count']}")
|
| 150 |
-
print(f"Written: {submission_info['zip_path']} | {submission_info['csv_path']}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
neurogolf_solver/validators.py
DELETED
|
@@ -1,125 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Model validation utilities.
|
| 3 |
-
|
| 4 |
-
Validation order (matches Kaggle's verify_network):
|
| 5 |
-
1. File size ≤ 1.44MB
|
| 6 |
-
2. onnx.checker.check_model() — catches malformed graphs
|
| 7 |
-
3. No banned ops (UPPERCASE check to match Kaggle)
|
| 8 |
-
4. All tensor shapes are static (no dynamic dims)
|
| 9 |
-
5. onnxruntime.InferenceSession loads successfully
|
| 10 |
-
6. Correct outputs on train + test + arc-gen
|
| 11 |
-
"""
|
| 12 |
-
|
| 13 |
-
import os
|
| 14 |
-
import numpy as np
|
| 15 |
-
import onnx
|
| 16 |
-
import onnxruntime as ort
|
| 17 |
-
from .data_loader import to_onehot
|
| 18 |
-
from .constants import MAX_ARCGEN_VALIDATE, MAX_ONNX_FILESIZE, BANNED_OPS
|
| 19 |
-
|
| 20 |
-
_BANNED_OPS_UPPER = {op.upper() for op in BANNED_OPS}
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
def check_model_structure(path):
|
| 24 |
-
"""Check ONNX model structure: size, valid graph, no banned ops, static shapes.
|
| 25 |
-
Returns (ok, error_message)."""
|
| 26 |
-
# 1. File size
|
| 27 |
-
try:
|
| 28 |
-
fsize = os.path.getsize(path)
|
| 29 |
-
except OSError:
|
| 30 |
-
return False, f"File not found: {path}"
|
| 31 |
-
if fsize > MAX_ONNX_FILESIZE:
|
| 32 |
-
return False, f"File size {fsize} exceeds {MAX_ONNX_FILESIZE} ({fsize/1024:.1f} KB)"
|
| 33 |
-
|
| 34 |
-
# 2. ONNX checker
|
| 35 |
-
try:
|
| 36 |
-
model = onnx.load(path)
|
| 37 |
-
onnx.checker.check_model(model)
|
| 38 |
-
except Exception as e:
|
| 39 |
-
return False, f"onnx.checker failed: {e}"
|
| 40 |
-
|
| 41 |
-
# 3. Banned ops (UPPERCASE comparison — matches Kaggle)
|
| 42 |
-
for node in model.graph.node:
|
| 43 |
-
if node.op_type.upper() in _BANNED_OPS_UPPER:
|
| 44 |
-
return False, f"Banned op: {node.op_type}"
|
| 45 |
-
|
| 46 |
-
# 4. Static shapes — all tensors must have fully defined shapes
|
| 47 |
-
for inp in model.graph.input:
|
| 48 |
-
if inp.type.HasField('tensor_type'):
|
| 49 |
-
shape = inp.type.tensor_type.shape
|
| 50 |
-
if shape:
|
| 51 |
-
for dim in shape.dim:
|
| 52 |
-
if not dim.dim_value and dim.dim_value != 0:
|
| 53 |
-
if not dim.dim_param: # symbolic dim is also not static
|
| 54 |
-
pass # dim_value=0 is valid (means unknown in some contexts)
|
| 55 |
-
return False, f"Dynamic shape on input '{inp.name}': {[d.dim_value or d.dim_param for d in shape.dim]}"
|
| 56 |
-
|
| 57 |
-
for out in model.graph.output:
|
| 58 |
-
if out.type.HasField('tensor_type'):
|
| 59 |
-
shape = out.type.tensor_type.shape
|
| 60 |
-
if shape:
|
| 61 |
-
for dim in shape.dim:
|
| 62 |
-
if not dim.dim_value and dim.dim_value != 0:
|
| 63 |
-
if not dim.dim_param:
|
| 64 |
-
pass
|
| 65 |
-
return False, f"Dynamic shape on output '{out.name}': {[d.dim_value or d.dim_param for d in shape.dim]}"
|
| 66 |
-
|
| 67 |
-
return True, None
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
def validate(path, td, providers):
|
| 71 |
-
"""Full validation: structure check + correct outputs on all splits.
|
| 72 |
-
Returns False immediately if any check fails."""
|
| 73 |
-
# Structure checks first
|
| 74 |
-
ok, err = check_model_structure(path)
|
| 75 |
-
if not ok:
|
| 76 |
-
return False
|
| 77 |
-
|
| 78 |
-
# Load and run inference
|
| 79 |
-
try:
|
| 80 |
-
opts = ort.SessionOptions()
|
| 81 |
-
opts.log_severity_level = 3
|
| 82 |
-
sess = ort.InferenceSession(path, sess_options=opts, providers=providers)
|
| 83 |
-
except:
|
| 84 |
-
return False
|
| 85 |
-
|
| 86 |
-
examples = td['train'] + td['test']
|
| 87 |
-
if 'arc-gen' in td:
|
| 88 |
-
examples = examples + td['arc-gen'][:MAX_ARCGEN_VALIDATE]
|
| 89 |
-
for ex in examples:
|
| 90 |
-
inp = to_onehot(ex['input'])
|
| 91 |
-
exp = to_onehot(ex['output'])
|
| 92 |
-
try:
|
| 93 |
-
out = sess.run(['output'], {'input': inp})[0]
|
| 94 |
-
out = (out > 0.0).astype(np.float32)
|
| 95 |
-
except:
|
| 96 |
-
return False
|
| 97 |
-
if not np.array_equal(out, exp):
|
| 98 |
-
return False
|
| 99 |
-
return True
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
def validate_raw(raw_bytes, td, providers):
|
| 103 |
-
"""Validate ONNX model from raw bytes."""
|
| 104 |
-
if len(raw_bytes) > MAX_ONNX_FILESIZE:
|
| 105 |
-
return False
|
| 106 |
-
try:
|
| 107 |
-
opts = ort.SessionOptions()
|
| 108 |
-
opts.log_severity_level = 3
|
| 109 |
-
sess = ort.InferenceSession(raw_bytes, sess_options=opts, providers=providers)
|
| 110 |
-
except:
|
| 111 |
-
return False
|
| 112 |
-
examples = td['train'] + td['test']
|
| 113 |
-
if 'arc-gen' in td:
|
| 114 |
-
examples = examples + td['arc-gen'][:MAX_ARCGEN_VALIDATE]
|
| 115 |
-
for ex in examples:
|
| 116 |
-
inp = to_onehot(ex['input'])
|
| 117 |
-
exp = to_onehot(ex['output'])
|
| 118 |
-
try:
|
| 119 |
-
out = sess.run(['output'], {'input': inp})[0]
|
| 120 |
-
out = (out > 0.0).astype(np.float32)
|
| 121 |
-
except:
|
| 122 |
-
return False
|
| 123 |
-
if not np.array_equal(out, exp):
|
| 124 |
-
return False
|
| 125 |
-
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|