rogermt commited on
Commit
3f3d372
·
verified ·
1 Parent(s): 9b68947

Put W&B code back into run_task

Browse files
Files changed (1) hide show
  1. 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
- def solve_task(tn, td, output_dir, conv_budget=30.0, verbose=True):
1174
- path = os.path.join(output_dir, f"task{tn:03d}.onnx")
1175
- for name, solver in ANALYTICAL_SOLVERS:
 
 
 
 
 
 
 
 
 
1176
  try:
1177
- model = solver(td)
1178
- except Exception as e:
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
- macs, mem, par = score_network(path)
1185
- if macs is not None:
1186
- cost = macs + mem + par
1187
- score = max(1.0, 25.0 - math.log(cost)) if cost > 0 else 25.0
1188
- if verbose: print(f" {name}: PASS cost={cost} score={score:.2f}")
1189
- return name, score
1190
- else:
1191
- if verbose: print(f" {name}: model built but FAILED validation")
1192
- conv_solvers = [
1193
- ('conv_fixed', solve_conv_fixed),
1194
- ('conv_variable', solve_conv_variable),
1195
- ('conv_diffshape', solve_conv_diffshape),
1196
- ('conv_var_diff', solve_conv_var_diff),
1197
- ]
1198
- for name, solver in conv_solvers:
1199
- try:
1200
- result = solver(td, path, time_budget=conv_budget)
1201
- except Exception as e:
1202
- if verbose: print(f" {name}: ERROR {e}")
1203
- continue
1204
  if result is not None:
1205
- solver_type, model = result
1206
- onnx.save(model, path)
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
- for p in ['/kaggle/input/competitions/neurogolf-2026/', 'ARC-AGI/data/training/']:
1235
- if os.path.exists(p):
1236
- if 'kaggle' in p:
1237
- tasks = load_tasks_kaggle(p)
1238
- else:
1239
- tasks = load_tasks_dir(p, args.arcgen_dir)
1240
- break
1241
- else:
1242
- print("ERROR: No data directory found. Use --data_dir or --kaggle_dir")
1243
- sys.exit(1)
 
 
 
 
 
 
 
 
1244
  results = {}
1245
- total_score = 0.0
1246
- solved = 0
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
- hex_id = tasks[tn]['hex']
1258
- if args.verbose: print(f"\nTask {tn:3d} ({hex_id}):")
1259
- result = solve_task(tn, td, args.output_dir, args.conv_budget, args.verbose)
1260
- if result is not None:
1261
- solver_type, score = result
1262
- results[tn] = {'solver': solver_type, 'score': score, 'hex': hex_id}
 
 
1263
  total_score += score
1264
- solved += 1
 
 
 
1265
  else:
1266
- total_score += 1.0
1267
- if args.verbose: print(f" UNSOLVED")
1268
- elapsed = time.time() - t_total
1269
- print(f"\n{'='*60}")
1270
- print(f"RESULTS: {solved}/{len(task_nums)} tasks solved")
1271
- print(f"Total score: {total_score:.1f}")
1272
- print(f"Time: {elapsed:.1f}s")
1273
- print(f"{'='*60}")
1274
- solver_counts = Counter(r['solver'] for r in results.values())
1275
- solver_scores = {}
1276
- for tn, r in results.items():
1277
- st = r['solver']
1278
- solver_scores[st] = solver_scores.get(st, 0) + r['score']
1279
- print("\nSolver breakdown:")
1280
- for st in sorted(solver_counts.keys()):
1281
- print(f" {st}: {solver_counts[st]} tasks, total score {solver_scores[st]:.1f}, avg {solver_scores[st]/solver_counts[st]:.2f}")
1282
- csv_path = os.path.join(args.output_dir, 'submission.csv')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1283
  with open(csv_path, 'w', newline='') as f:
1284
  w = csv.writer(f)
1285
- w.writerow(['task_num', 'hex_id', 'solver', 'score', 'onnx_file'])
1286
- for tn in sorted(results.keys()):
1287
- r = results[tn]
1288
- w.writerow([tn, r['hex'], r['solver'], f"{r['score']:.3f}", f"task{tn:03d}.onnx"])
1289
- zip_path = os.path.join(args.output_dir, 'submission.zip')
1290
- with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
1291
- for tn in sorted(results.keys()):
1292
- onnx_path = os.path.join(args.output_dir, f"task{tn:03d}.onnx")
1293
- if os.path.exists(onnx_path):
1294
- zf.write(onnx_path, f"task{tn:03d}.onnx")
1295
- print(f"\nSubmission files: {csv_path}, {zip_path}")
1296
- print(f"Models in zip: {len(results)}")
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
+