Put W&B code back into run_task
Browse files- neurogolf_solver.py +179 -114
neurogolf_solver.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
| 2 |
"""
|
| 3 |
ARC-AGI NeuroGolf Championship - Complete Solver v5
|
| 4 |
Format: [1,10,30,30] one-hot input/output, opset 17, IR version 8.
|
| 5 |
-
|
| 6 |
v5 CHANGES (from v4):
|
| 7 |
- Opset 10 → 17, IR 10 → 8
|
| 8 |
- s_flip: Slice(step=-1) replaces Gather — 0 MACs (was ~165K)
|
|
@@ -11,7 +10,6 @@ v5 CHANGES (from v4):
|
|
| 11 |
- All Pad nodes: tensor-based pads input (opset 17 requirement)
|
| 12 |
- All ReduceSum nodes: axes as tensor input (opset 13+ requirement)
|
| 13 |
- All other solvers unchanged from v4
|
| 14 |
-
|
| 15 |
Solvers:
|
| 16 |
- Analytical: identity, constant, color_map, transpose, flip, rotate, tile, upscale,
|
| 17 |
concat, concat_enhanced, spatial_gather, varshape_spatial_gather,
|
|
@@ -19,7 +17,6 @@ Solvers:
|
|
| 19 |
- Conv (fixed shape): Slice -> Conv -> ArgMax -> Equal+Cast -> Pad
|
| 20 |
- Conv (variable shape): Conv(30x30) -> ArgMax -> Equal+Cast -> Mul(mask)
|
| 21 |
- Conv (diff shape): Slice -> Conv -> Slice(crop) -> ArgMax -> Equal+Cast -> Pad
|
| 22 |
-
|
| 23 |
Usage:
|
| 24 |
python neurogolf_solver.py --data_dir ARC-AGI/data/training/ --output_dir submission
|
| 25 |
python neurogolf_solver.py --data_dir ARC-AGI/data/training/ --output_dir submission --conv_budget 60 --arcgen_dir ARC-GEN-100K/
|
|
@@ -1170,130 +1167,198 @@ ANALYTICAL_SOLVERS = [
|
|
| 1170 |
('varshape_spatial_gather', s_varshape_spatial_gather),
|
| 1171 |
]
|
| 1172 |
|
| 1173 |
-
|
| 1174 |
-
|
| 1175 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1176 |
try:
|
| 1177 |
-
model =
|
| 1178 |
-
|
| 1179 |
-
if verbose: print(f" {name}: ERROR {e}")
|
| 1180 |
-
continue
|
| 1181 |
-
if model is not None:
|
| 1182 |
onnx.save(model, path)
|
| 1183 |
-
if validate(path, td):
|
| 1184 |
-
|
| 1185 |
-
|
| 1186 |
-
|
| 1187 |
-
|
| 1188 |
-
|
| 1189 |
-
|
| 1190 |
-
|
| 1191 |
-
|
| 1192 |
-
|
| 1193 |
-
|
| 1194 |
-
|
| 1195 |
-
|
| 1196 |
-
|
| 1197 |
-
|
| 1198 |
-
|
| 1199 |
-
|
| 1200 |
-
|
| 1201 |
-
|
| 1202 |
-
if verbose: print(f" {name}: ERROR {e}")
|
| 1203 |
-
continue
|
| 1204 |
if result is not None:
|
| 1205 |
-
|
| 1206 |
-
|
| 1207 |
-
macs, mem, par = score_network(path)
|
| 1208 |
-
if macs is not None:
|
| 1209 |
-
cost = macs + mem + par
|
| 1210 |
-
score = max(1.0, 25.0 - math.log(cost)) if cost > 0 else 25.0
|
| 1211 |
-
if verbose: print(f" {solver_type}: PASS cost={cost} score={score:.2f}")
|
| 1212 |
-
return solver_type, score
|
| 1213 |
-
return None
|
| 1214 |
-
|
| 1215 |
-
def main():
|
| 1216 |
-
parser = argparse.ArgumentParser(description='NeuroGolf Solver v5')
|
| 1217 |
-
parser.add_argument('--data_dir', type=str, default=None)
|
| 1218 |
-
parser.add_argument('--kaggle_dir', type=str, default=None)
|
| 1219 |
-
parser.add_argument('--arcgen_dir', type=str, default=None)
|
| 1220 |
-
parser.add_argument('--output_dir', type=str, default='submission')
|
| 1221 |
-
parser.add_argument('--conv_budget', type=float, default=30.0)
|
| 1222 |
-
parser.add_argument('--task', type=int, default=None)
|
| 1223 |
-
parser.add_argument('--verbose', action='store_true', default=True)
|
| 1224 |
-
parser.add_argument('--quiet', action='store_true', default=False)
|
| 1225 |
-
args = parser.parse_args()
|
| 1226 |
-
if args.quiet:
|
| 1227 |
-
args.verbose = False
|
| 1228 |
-
os.makedirs(args.output_dir, exist_ok=True)
|
| 1229 |
-
if args.kaggle_dir:
|
| 1230 |
-
tasks = load_tasks_kaggle(args.kaggle_dir)
|
| 1231 |
-
elif args.data_dir:
|
| 1232 |
-
tasks = load_tasks_dir(args.data_dir, args.arcgen_dir)
|
| 1233 |
else:
|
| 1234 |
-
|
| 1235 |
-
|
| 1236 |
-
|
| 1237 |
-
|
| 1238 |
-
|
| 1239 |
-
|
| 1240 |
-
|
| 1241 |
-
|
| 1242 |
-
|
| 1243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1244 |
results = {}
|
| 1245 |
-
|
| 1246 |
-
|
| 1247 |
-
t_total = time.time()
|
| 1248 |
-
task_nums = [args.task] if args.task else sorted(tasks.keys())
|
| 1249 |
for tn in task_nums:
|
| 1250 |
-
if tn in EXCLUDED_TASKS:
|
| 1251 |
-
if args.verbose: print(f"Task {tn:3d}: EXCLUDED")
|
| 1252 |
-
continue
|
| 1253 |
if tn not in tasks:
|
| 1254 |
-
if args.verbose: print(f"Task {tn:3d}: NOT FOUND")
|
| 1255 |
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1256 |
td = tasks[tn]['data']
|
| 1257 |
-
|
| 1258 |
-
|
| 1259 |
-
|
| 1260 |
-
|
| 1261 |
-
|
| 1262 |
-
|
|
|
|
|
|
|
| 1263 |
total_score += score
|
| 1264 |
-
|
|
|
|
|
|
|
|
|
|
| 1265 |
else:
|
| 1266 |
-
|
| 1267 |
-
|
| 1268 |
-
|
| 1269 |
-
|
| 1270 |
-
|
| 1271 |
-
|
| 1272 |
-
|
| 1273 |
-
|
| 1274 |
-
|
| 1275 |
-
|
| 1276 |
-
|
| 1277 |
-
|
| 1278 |
-
|
| 1279 |
-
|
| 1280 |
-
|
| 1281 |
-
|
| 1282 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1283 |
with open(csv_path, 'w', newline='') as f:
|
| 1284 |
w = csv.writer(f)
|
| 1285 |
-
w.writerow(['
|
| 1286 |
-
for tn in sorted(
|
| 1287 |
-
|
| 1288 |
-
|
| 1289 |
-
|
| 1290 |
-
|
| 1291 |
-
|
| 1292 |
-
|
| 1293 |
-
|
| 1294 |
-
|
| 1295 |
-
print(f"
|
| 1296 |
-
print(f"
|
| 1297 |
|
| 1298 |
if __name__ == '__main__':
|
| 1299 |
main()
|
|
|
|
|
|
| 2 |
"""
|
| 3 |
ARC-AGI NeuroGolf Championship - Complete Solver v5
|
| 4 |
Format: [1,10,30,30] one-hot input/output, opset 17, IR version 8.
|
|
|
|
| 5 |
v5 CHANGES (from v4):
|
| 6 |
- Opset 10 → 17, IR 10 → 8
|
| 7 |
- s_flip: Slice(step=-1) replaces Gather — 0 MACs (was ~165K)
|
|
|
|
| 10 |
- All Pad nodes: tensor-based pads input (opset 17 requirement)
|
| 11 |
- All ReduceSum nodes: axes as tensor input (opset 13+ requirement)
|
| 12 |
- All other solvers unchanged from v4
|
|
|
|
| 13 |
Solvers:
|
| 14 |
- Analytical: identity, constant, color_map, transpose, flip, rotate, tile, upscale,
|
| 15 |
concat, concat_enhanced, spatial_gather, varshape_spatial_gather,
|
|
|
|
| 17 |
- Conv (fixed shape): Slice -> Conv -> ArgMax -> Equal+Cast -> Pad
|
| 18 |
- Conv (variable shape): Conv(30x30) -> ArgMax -> Equal+Cast -> Mul(mask)
|
| 19 |
- Conv (diff shape): Slice -> Conv -> Slice(crop) -> ArgMax -> Equal+Cast -> Pad
|
|
|
|
| 20 |
Usage:
|
| 21 |
python neurogolf_solver.py --data_dir ARC-AGI/data/training/ --output_dir submission
|
| 22 |
python neurogolf_solver.py --data_dir ARC-AGI/data/training/ --output_dir submission --conv_budget 60 --arcgen_dir ARC-GEN-100K/
|
|
|
|
| 1167 |
('varshape_spatial_gather', s_varshape_spatial_gather),
|
| 1168 |
]
|
| 1169 |
|
| 1170 |
+
|
| 1171 |
+
def solve_task(tn, td, outdir, conv_budget=30.0):
|
| 1172 |
+
t_start = time.time()
|
| 1173 |
+
os.makedirs(outdir, exist_ok=True)
|
| 1174 |
+
path = os.path.join(outdir, f"task{tn:03d}.onnx")
|
| 1175 |
+
|
| 1176 |
+
# Skip excluded tasks
|
| 1177 |
+
if tn in EXCLUDED_TASKS:
|
| 1178 |
+
return False, 'excluded', None, time.time() - t_start, path
|
| 1179 |
+
|
| 1180 |
+
# 1. Try analytical solvers (fast, tiny models)
|
| 1181 |
+
for sname, sfn in ANALYTICAL_SOLVERS:
|
| 1182 |
try:
|
| 1183 |
+
model = sfn(td)
|
| 1184 |
+
if model is None: continue
|
|
|
|
|
|
|
|
|
|
| 1185 |
onnx.save(model, path)
|
| 1186 |
+
if validate(path, td):
|
| 1187 |
+
return True, sname, os.path.getsize(path), time.time() - t_start, path
|
| 1188 |
+
except: pass
|
| 1189 |
+
|
| 1190 |
+
# 2. Determine task shape category and try conv solvers
|
| 1191 |
+
exs = get_exs(td)
|
| 1192 |
+
same_shape = all(inp.shape == out.shape for inp, out in exs)
|
| 1193 |
+
shapes = set(inp.shape for inp, _ in exs)
|
| 1194 |
+
fixed_in = len(shapes) == 1
|
| 1195 |
+
|
| 1196 |
+
conv_time = conv_budget
|
| 1197 |
+
|
| 1198 |
+
if same_shape:
|
| 1199 |
+
if fixed_in:
|
| 1200 |
+
result = solve_conv_fixed(td, path, time_budget=conv_time/2)
|
| 1201 |
+
if result is not None:
|
| 1202 |
+
sname, model = result
|
| 1203 |
+
return True, sname, os.path.getsize(path), time.time() - t_start, path
|
| 1204 |
+
result = solve_conv_variable(td, path, time_budget=conv_time)
|
|
|
|
|
|
|
| 1205 |
if result is not None:
|
| 1206 |
+
sname, model = result
|
| 1207 |
+
return True, sname, os.path.getsize(path), time.time() - t_start, path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1208 |
else:
|
| 1209 |
+
sp = fixed_shapes(td)
|
| 1210 |
+
if sp is not None:
|
| 1211 |
+
(IH,IW),(OH,OW) = sp
|
| 1212 |
+
if OH <= IH and OW <= IW:
|
| 1213 |
+
result = solve_conv_diffshape(td, path, time_budget=conv_time)
|
| 1214 |
+
if result is not None:
|
| 1215 |
+
sname, model = result
|
| 1216 |
+
return True, sname, os.path.getsize(path), time.time() - t_start, path
|
| 1217 |
+
|
| 1218 |
+
# Try variable diff-shape conv (output within input bounds)
|
| 1219 |
+
result = solve_conv_var_diff(td, path, time_budget=conv_time)
|
| 1220 |
+
if result is not None:
|
| 1221 |
+
sname, model = result
|
| 1222 |
+
return True, sname, os.path.getsize(path), time.time() - t_start, path
|
| 1223 |
+
|
| 1224 |
+
return False, None, None, time.time() - t_start, path
|
| 1225 |
+
|
| 1226 |
+
def run_tasks(task_nums, tasks, output_dir, conv_budget, use_wandb):
|
| 1227 |
results = {}
|
| 1228 |
+
costs_dict = {}
|
| 1229 |
+
total_score = 0
|
|
|
|
|
|
|
| 1230 |
for tn in task_nums:
|
|
|
|
|
|
|
|
|
|
| 1231 |
if tn not in tasks:
|
|
|
|
| 1232 |
continue
|
| 1233 |
+
if tn in EXCLUDED_TASKS:
|
| 1234 |
+
print(f"Task {tn:3d}: EXCLUDED (officially)")
|
| 1235 |
+
continue
|
| 1236 |
+
|
| 1237 |
td = tasks[tn]['data']
|
| 1238 |
+
ok, sname, sz, t_task, model_path = solve_task(tn, td, output_dir, conv_budget)
|
| 1239 |
+
|
| 1240 |
+
if ok:
|
| 1241 |
+
macs, memory, params = score_network(model_path)
|
| 1242 |
+
if macs is None:
|
| 1243 |
+
macs, memory, params = 0, 0, 0
|
| 1244 |
+
cost = macs + memory + params
|
| 1245 |
+
score = max(1.0, 25.0 - math.log(max(1, cost)))
|
| 1246 |
total_score += score
|
| 1247 |
+
|
| 1248 |
+
results[tn] = (sname, t_task, sz)
|
| 1249 |
+
costs_dict[tn] = cost
|
| 1250 |
+
print(f"Task {tn:3d}: {sname:25s} {score:7.3f} {cost:>12} {t_task:7.3f}s ({sz:>8,} bytes)")
|
| 1251 |
else:
|
| 1252 |
+
print(f"Task {tn:3d}: UNSOLVED {t_task:7.3f}s")
|
| 1253 |
+
cost = 0
|
| 1254 |
+
|
| 1255 |
+
if use_wandb and wandb is not None:
|
| 1256 |
+
wandb.log({
|
| 1257 |
+
"task_id": tn,
|
| 1258 |
+
"solver": sname if ok else "unsolved",
|
| 1259 |
+
"onnx_bytes": sz if ok else 0,
|
| 1260 |
+
"task_time_sec": t_task,
|
| 1261 |
+
"cost": cost,
|
| 1262 |
+
"score": score if ok else 0,
|
| 1263 |
+
})
|
| 1264 |
+
|
| 1265 |
+
return results, costs_dict, total_score
|
| 1266 |
+
|
| 1267 |
+
|
| 1268 |
+
def main():
|
| 1269 |
+
parser = argparse.ArgumentParser()
|
| 1270 |
+
parser.add_argument('--data_dir', default='ARC-AGI/data/training/')
|
| 1271 |
+
parser.add_argument('--arcgen_dir', default='', help='Path to ARC-GEN-100K/ directory')
|
| 1272 |
+
parser.add_argument('--output_dir', default='/kaggle/working/submission')
|
| 1273 |
+
parser.add_argument('--kaggle', action='store_true')
|
| 1274 |
+
parser.add_argument('--conv_budget', type=float, default=30.0)
|
| 1275 |
+
parser.add_argument('--tasks', type=str, default='')
|
| 1276 |
+
parser.add_argument('--device', type=str, default='auto', choices=['auto','cpu','cuda'])
|
| 1277 |
+
parser.add_argument('--use_wandb', action='store_true')
|
| 1278 |
+
args = parser.parse_args()
|
| 1279 |
+
global ORT_PROVIDERS
|
| 1280 |
+
config = {
|
| 1281 |
+
"device": args.device,
|
| 1282 |
+
"conv_budget": args.conv_budget,
|
| 1283 |
+
"data_dir": args.data_dir,
|
| 1284 |
+
"arcgen_dir": args.arcgen_dir,
|
| 1285 |
+
"tasks": args.tasks,
|
| 1286 |
+
}
|
| 1287 |
+
|
| 1288 |
+
if args.device == 'cuda':
|
| 1289 |
+
ORT_PROVIDERS = ['CUDAExecutionProvider', 'CPUExecutionProvider']
|
| 1290 |
+
elif args.device == 'cpu':
|
| 1291 |
+
ORT_PROVIDERS = ['CPUExecutionProvider']
|
| 1292 |
+
|
| 1293 |
+
ort.set_default_logger_severity(3)
|
| 1294 |
+
print(f"Using providers: {ORT_PROVIDERS}")
|
| 1295 |
+
|
| 1296 |
+
if args.kaggle:
|
| 1297 |
+
tasks = load_tasks_kaggle(args.data_dir)
|
| 1298 |
+
else:
|
| 1299 |
+
arcgen = args.arcgen_dir if args.arcgen_dir else None
|
| 1300 |
+
tasks = load_tasks_dir(args.data_dir, arcgen_dir=arcgen)
|
| 1301 |
+
|
| 1302 |
+
# Count arc-gen examples
|
| 1303 |
+
total_arcgen = sum(len(t['data'].get('arc-gen', [])) for t in tasks.values())
|
| 1304 |
+
print(f"Loaded {len(tasks)} tasks ({total_arcgen} ARC-GEN examples)")
|
| 1305 |
+
print(f"Excluded tasks: {sorted(EXCLUDED_TASKS)}")
|
| 1306 |
+
|
| 1307 |
+
task_nums = [int(t) for t in args.tasks.split(',')] if args.tasks else sorted(tasks.keys())
|
| 1308 |
+
active_tasks = [t for t in task_nums if t not in EXCLUDED_TASKS]
|
| 1309 |
+
print(f"Solving {len(active_tasks)} active tasks (skipping {len(task_nums) - len(active_tasks)} excluded)")
|
| 1310 |
+
print(f"Conv budget: {args.conv_budget}s per task")
|
| 1311 |
+
print("=" * 70)
|
| 1312 |
+
t0 = time.time()
|
| 1313 |
+
|
| 1314 |
+
if args.use_wandb and wandb is not None:
|
| 1315 |
+
with wandb.init(project="neurogolf", name="solver_run", config=config):
|
| 1316 |
+
results, costs_dict, total_score = run_tasks(task_nums, tasks, args.output_dir, args.conv_budget, use_wandb=True)
|
| 1317 |
+
else:
|
| 1318 |
+
results, costs_dict, total_score = run_tasks(task_nums, tasks, args.output_dir, args.conv_budget, use_wandb=False)
|
| 1319 |
+
|
| 1320 |
+
elapsed = time.time() - t0
|
| 1321 |
+
print(f"\n{'='*70}")
|
| 1322 |
+
print(f"Solved: {len(results)}/{len(active_tasks)} active tasks in {elapsed:.0f}s")
|
| 1323 |
+
solver_names = [v[0] for v in results.values()]
|
| 1324 |
+
sc = Counter(solver_names)
|
| 1325 |
+
for s, c in sc.most_common(): print(f" {s}: {c}")
|
| 1326 |
+
|
| 1327 |
+
# Generate submission
|
| 1328 |
+
outdir = args.output_dir
|
| 1329 |
+
n_files = len([f for f in os.listdir(outdir) if f.endswith('.onnx')])
|
| 1330 |
+
total_size = sum(os.path.getsize(os.path.join(outdir, f))
|
| 1331 |
+
for f in os.listdir(outdir) if f.endswith('.onnx'))
|
| 1332 |
+
|
| 1333 |
+
# Create submission.zip
|
| 1334 |
+
zip_path = os.path.join(os.path.dirname(outdir) or '/kaggle/working/', 'submission.zip')
|
| 1335 |
+
buf = io.BytesIO()
|
| 1336 |
+
with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf:
|
| 1337 |
+
for f in sorted(os.listdir(outdir)):
|
| 1338 |
+
if f.endswith('.onnx'):
|
| 1339 |
+
zf.write(os.path.join(outdir, f), f)
|
| 1340 |
+
zip_bytes = buf.getvalue()
|
| 1341 |
+
with open(zip_path, 'wb') as f:
|
| 1342 |
+
f.write(zip_bytes)
|
| 1343 |
+
zip_size = len(zip_bytes)
|
| 1344 |
+
|
| 1345 |
+
# Create submission.csv
|
| 1346 |
+
csv_path = os.path.join(os.path.dirname(outdir) or '.', 'submission.csv')
|
| 1347 |
with open(csv_path, 'w', newline='') as f:
|
| 1348 |
w = csv.writer(f)
|
| 1349 |
+
w.writerow(['task_id', 'total_cost'])
|
| 1350 |
+
for tn in sorted(costs_dict.keys()):
|
| 1351 |
+
w.writerow([f'task{tn:03d}', costs_dict[tn]])
|
| 1352 |
+
|
| 1353 |
+
# Estimate LB score: solved tasks get their score, unsolved get 1.0
|
| 1354 |
+
unsolved_count = len(active_tasks) - len(results)
|
| 1355 |
+
est_lb = total_score + unsolved_count * 1.0
|
| 1356 |
+
|
| 1357 |
+
print(f"\n{n_files} ONNX files, {total_size/1024:.1f} KB uncompressed")
|
| 1358 |
+
print(f"ZIP size: {zip_size/1024:.1f} KB / {MAX_FILESIZE/1024:.0f} KB limit {'OK' if zip_size <= MAX_FILESIZE else 'OVER!'}")
|
| 1359 |
+
print(f"Estimated LB score: {est_lb:.1f} (solved: {total_score:.1f} + unsolved: {unsolved_count}×1.0)")
|
| 1360 |
+
print(f"Written: {zip_path} | {csv_path}")
|
| 1361 |
|
| 1362 |
if __name__ == '__main__':
|
| 1363 |
main()
|
| 1364 |
+
|