ladder
Small open models fine-tuned for competitive programming, trained entirely on free GPUs. A generated program is run against the problem’s actual test cases, and it either passes them or it does not.
Status
The pipeline is complete and tested end to end. No trained checkpoint is published yet, and there are no benchmark numbers to quote. The results table in the repository is empty on purpose; it gets filled in from a real run rather than an estimate. An early evaluation was inconclusive because the generation budget was set far below what the model needed, and it is being redone.
One constraint drives everything
Everything has to run on a free Kaggle or Colab T4. That rules out full fine-tuning, bf16 and flash-attention, and it decides most of the rest of the design. The interesting question is not what you can build with a cluster; it is what is still reachable without one.
- Data — Codeforces problems with reasoning traces, decontaminated against common benchmarks. Each row carries public, private and generated test cases
- Training — 4-bit QLoRA on
Qwen2.5-Coder-3B-Instruct, loss on the assistant turn only - Eval — generate a program, run it against up to 20 test cases in a subprocess with time and memory limits, report pass@k
The training data is not all correct
The reasoning traces in the corpus are model output, not verified solutions. Running each trace’s own solution against its own problem’s test cases:
| Sample | Traces whose solution passes |
|---|---|
| Easy problems (div2 A/B) | 16 / 16 |
| Mixed sample with div1 D/E/G/H | 36 / 50 |
Every failure was a wrong answer on a hard problem, not a crash or a timeout. These are confident, well-structured, incorrect solutions, concentrated on exactly the problems worth learning from.
So data prep runs the eval’s judge across the training set and drops the traces that fail. It costs about eight minutes of CPU and no GPU time at all, because data prep happens before you ever start a GPU session.
Why not a model judge
Because a model judge measures whether the output looks like a solution, and that is not the question. Competitive programming is one of the few domains where correctness is not a matter of opinion: there are test cases, and the program either passes them or it does not.
Getting that judge right turned out to be harder than expected. Three separate
bugs in it passed their unit tests while being wrong, including a sandboxing
flag that removed Python’s exit() builtin, and problems
with multiple valid outputs that cannot be exact-matched at all. A judge that
is quietly broken is worse than no judge, because it produces confident wrong
numbers.
Try it
Data prep is CPU-only, so do that part anywhere.
pip install -e . ladder build-data --config configs/smoke-1.5b.yaml --out data/smoke
Then on a GPU box:
pip install -r requirements-train.txt ladder train --config configs/smoke-1.5b.yaml --data data/smoke ladder eval --config configs/smoke-1.5b.yaml --adapter outputs/smoke-1.5b
The smoke config is a 20-step run on a 1.5B base. It exists to prove the whole pipeline works end to end in about fifteen minutes, before you spend free GPU hours on the real thing.