Lab 2: reproduce the failure and the fix on our own GPUs
On this page
Big thing: You will run the unedited Qwen3.5-0.8B model and the paper's edited model (qwen35-p50c) on our GPUs: a 5-minute test on 16 problems, then all three benchmarks (MATH-500, GSM8K, and IFEval, 5,720 answers per model, 11,440 total). Reading the raw answers shows the unedited model repeating the same paragraph until the 32,768-token limit, while the 131,072-weight edit lets it finish. A token is a word piece.
Why it matters: Before taking the edit apart in later labs, we verify that the failure and the 40-to-53-point accuracy jump reproduce on our hardware and grading code. Inspecting raw answers connects the summary numbers to concrete text.
Time and hardware: 5 minutes on 2 NVIDIA A100 GPUs on trainer for the quick run; about 1.5 hours on all 8 A100 GPUs on trainer (about 11 GPU-hours) for the full suite.
Theory guide: Sections 7 and 10.
Before you run: predict
Write down a short guess for each question before reading the output.
- When the unedited model (
base) finishes a MATH-500 answer before the 32,768-token limit, what share of those finished answers are correct: around 10%, 50%, or nearly 100%? - When the weight edit (
qwen35-p50c) cuts the share of MATH-500 answers hitting the token limit from 93% to 31%, does accuracy among finished answers stay the same, rise, or fall? Why? - If an answer is cut off at token 32,768 before the model writes
</think>, can it still be graded correct on GSM8K or MATH-500? What about IFEval? - The quick run tests 16 practice problems out of the 256 in
dev256. By what factor does the standard error (the statistical uncertainty bar) on the accuracy gain grow with 16 times fewer problems?
Run it
All generation runs for this lab live on trainer.
Every command on a VM starts with . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code, which activates the Python 3.12 virtual environment (an isolated Python setup, with vLLM 0.33.0 for generating answers on GPUs) and keeps caches inside /nvme/wsp-lab.
Step 1: the 5-minute quick run (plans/quick.json)
plans/quick.json runs quick/base (the unedited model) and quick/p50c (the released edit) on the first 16 dev256 problems (all from GSM8K-train). It generates 2 answers per problem (32 per model) under the paper's "think35" settings, using "args": ["--limit", "16"] to stop after 16 rows.
Rerun note: scripts/run_jobs.py skips any shard (a batch of problems) with a .done.json marker, so if plans/quick.json has already run on the machine, the command finishes immediately and does nothing. To generate fresh answers, move the finished folder aside first:
ssh trainer
. /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
nvidia-smi
# Optional: move a finished quick run aside so run_jobs.py generates fresh answers
mv /nvme/wsp-lab/runs/quick /nvme/wsp-lab/runs/quick-ours
# Run quick/base and quick/p50c side by side on GPUs 0 and 1 (about 5 minutes)
python scripts/run_jobs.py plans/quick.json --gpus 0,1
On 2 GPUs side by side, quick/base took 5.0 minutes and quick/p50c took 3.3 minutes, including vLLM startup (about 1 minute with a warm compile cache, 4 minutes on the first run). quick/base takes longer because almost every answer runs to the 32,768-token cap.
Inspect the answers on trainer with labs/peek.py (CPU only, a few seconds). If you moved the finished run to quick-ours, quick/base reads the new run and quick-ours/base reads the earlier one:
# Still on trainer
export CUDA_VISIBLE_DEVICES=""
python labs/peek.py quick/base
python labs/peek.py quick/p50c
# Compare answer 0 on problem gsm8k_train_5628 (unedited vs edited)
python labs/peek.py quick/base --id gsm8k_train_5628
python labs/peek.py quick/p50c --id gsm8k_train_5628
# Inspect a cut-but-correct unedited answer, and an edited answer that kept arguing after </think>
python labs/peek.py quick/base --id gsm8k_train_3455
python labs/peek.py quick/p50c --id gsm8k_train_1882
Run labs/peek.py and labs/lab4_endings.py on the VM that produced the run (trainer for quick/* and bench/*). The compressed full-text files (shard*.texts.jsonl.gz) stay on the VM; scripts/pull_runs.sh copies only the per-answer score files (merged.jsonl, summary.json, and *.done.json) to the laptop.
Step 2: the full benchmark suite (plans/trainer.json)
plans/trainer.json runs both models on all three benchmarks from the paper:
math500: 500 competition math problems, 4 answers each (2,000answers per model).gsm8k: 1,319 grade-school math test problems, 2 answers each (2,638answers per model).ifeval: 541 instruction-following prompts, 2 answers each (1,082answers per model).
The plan splits those 6 runs into 17 shards across 8 GPUs and takes about 1.5 hours (about 11 GPU-hours). Our finished runs live in results/runs/bench/ on this laptop.
To run or check the suite on trainer:
ssh trainer
. /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
# Run all 17 shards across 8 GPUs (skips finished shards unless you move /nvme/wsp-lab/runs/bench aside)
python scripts/run_jobs.py plans/trainer.json --gpus 0-7
# Summarize all runs on the trainer VM
python scripts/summarize.py /nvme/wsp-lab/runs
# Classify how every benchmark answer ended (CPU only, writes results/lab2/endings.json)
CUDA_VISIBLE_DEVICES="" python labs/lab4_endings.py \
bench/base/math500 bench/p50c/math500 \
bench/base/gsm8k bench/p50c/gsm8k \
bench/base/ifeval bench/p50c/ifeval \
--out lab2/endings
On the laptop (no GPU needed), pull the graded records, rebuild results/summary.md and results/summary.json, and plot the figures:
# On the local laptop, inside ~/weight-space-programming/code
bash scripts/pull_runs.sh trainer
python3 scripts/summarize.py results/runs
uv run -q --with numpy --with matplotlib python scripts/plot_runs.py
scripts/plot_runs.py writes results/figures/lab2_benchmarks.png and results/figures/lab2_lengths.png.
What you should see
1. The 5-minute quick run (quick/base vs quick/p50c)
Our quick run on the first 16 dev256 problems (32 answers per model) compares with the full 256-problem dev256 runs (512 answers per model) as follows:
| Run | Problems x answers | Accuracy % | Cut at the cap % | Mean tokens | Correct when finished % | Cut answers ending in repetition % | Paired gain vs base (pp) | Rescued / lost problems |
|---|---|---|---|---|---|---|---|---|
quick/base |
16 x 2 (32) |
18.8 +/- 7.7 (6/32) |
84.4 (27/32) |
28,375 |
100.0 (5/5) |
100.0 (27/27) |
- |
- |
quick/p50c |
16 x 2 (32) |
65.6 +/- 11.8 (21/32) |
9.4 (3/32) |
11,664 |
72.4 (21/29) |
0.0 (0/3) |
+46.9 +/- 11.6 |
6 / 0 |
dev/base (full) |
256 x 2 (512) |
12.1 +/- 1.6 (62/512) |
88.7 (454/512) |
29,683 |
100.0 (58/58) |
96.9 |
- |
- |
dev/p50c (full) |
256 x 2 (512) |
61.5 +/- 2.8 (315/512) |
19.7 (101/512) |
16,104 |
76.6 (315/411) |
24.8 |
+49.4 +/- 2.6 |
125 / 0 |
The model reads and writes one token at a time. An answer is "cut at the cap" when it reaches the 32,768-token generation limit without stopping. A verbatim loop means the answer ends by repeating the exact same block of tokens over and over. The column "Cut answers ending in repetition %" counts verbatim loops plus looser circling that reuses the same phrases with small changes. In quick/base, every cut answer is a verbatim loop, so the two counts match. Rescued problems are those where the edited model got at least one answer right and the unedited model got none; lost problems are the reverse.
Even on 16 problems, quick/base hits the limit on 27 of 32 answers (84.4%), all ending in a verbatim loop. With the edit, only 3 of 32 answers are cut (9.4%), none end in a verbatim loop, and accuracy rises by +46.9 +/- 11.6 percentage points, matching the full dev256 gain of +49.4 +/- 2.6 points.
2. Three problems to read with labs/peek.py
Individual transcripts show what the summary columns mean:
- Unedited loop vs edited finish on
gsm8k_train_5628. The problem asks: "Mr. Desmond bought three times as many toys for his younger son as he bought for his elder son. If the elder son received 60 toys, how many toys did Mr Desmond buy?" (truth:240).- In
quick/base(answer 0), the unedited model finds60 + 180 = 240early, then doubts how to read the wording. Starting at token8,568of32,768, an83-token block repeats verbatim about292times until the cap:The model hastextLet's check if the answer is 240. Let's check if the answer is 180. Let's check if the answer is 60. Let's assume the question asks for the total. Wait, let's consider the possibility that the question asks for the total number of toys.240and cannot stop checking it. - In
quick/p50c(answer 0), the edited model finishes in5,825tokens. It settles on240in its scratchpad, writes</think>, and outputs\boxed{240}.
- In
- Cut while thinking, yet graded correct on
gsm8k_train_3455. Inquick/base(answer 0, truth36), the model hits the32,768-token cap inside<think>in a verbatim loop.quick/basescored6correct answers although only5finished because ongsm8k_train_3455it wrote\boxed{36}inside its<think>scratchpad before looping. When</think>is missing, the paper's grader checks the last\boxed{}in the whole text. - Closed thinking, then argued until the cap on
gsm8k_train_1882. Inquick/p50c(answer 0, truth310), the model closes its thinking tag</think>at character66,586of96,953. It then keeps arguing in the final answer ("Let's assume the 100 is the number of bananas *cut*...") until token32,768without boxing a number. Ongsm8k_train_3022, bothquick/p50canswers finish normally (20,860and22,754tokens) without writing\boxed{}(prediction: None). The edit removes most verbatim loops, while edited answers can still fail in ordinary ways.
3. Full benchmarks: our runs vs the paper
Our plans/trainer.json runs on trainer (results/summary.md) compare with the paper's reported numbers (exp8_math/README.md):
| Benchmark | Ours, unedited (acc % / cut % / mean tokens) | Paper, unedited (acc % / cut % / mean tokens) | Ours, edited p50c (acc % / cut % / mean tokens) |
Paper, edited p50c (acc % / cut % / mean tokens) |
Our paired gain (pp), rescued / lost |
|---|---|---|---|---|---|
MATH-500 (500 x 4) |
7.3 +/- 0.9 / 93.3 / 30,956 |
7.9 / 93.0 / 30,868 |
51.8 +/- 1.9 / 31.0 / 21,950 |
48.1 / 33.05 / 22,037 |
+44.5 +/- 1.7, 263 / 0 |
GSM8K (1,319 x 2) |
14.2 +/- 0.8 / 87.7 / 29,370 |
13.84 / 87.5 / 29,289 |
66.9 +/- 1.2 / 6.3 / 11,742 |
67.1 / 7.24 / 11,993 |
+52.7 +/- 1.1, 711 / 2 |
IFEval (541 x 2) |
21.2 +/- 1.4 / 73.5 / 24,976 |
21.0 / 74.9 / 25,296 |
63.4 +/- 1.8 / 1.4 / 3,984 |
62.3 / 2.2 / 4,241 |
+42.2 +/- 2.0, 261 / 22 |
Five of the six accuracies sit within about one standard error (SE) of the paper's numbers. On edited MATH-500, our 51.8% is 3.7 percentage points above the paper's 48.1% (about two standard errors). Sampling noise or a different vLLM version (0.33.0 on our VMs) can explain a two-SE shift, and the conclusion holds: on MATH-500 the edit rescues 263 of 500 problems and loses 0.
Look at the two diagnostic columns in results/summary.md:
| Benchmark | Unedited: correct when finished % | Edited (p50c): correct when finished % |
Unedited: cut answers ending in repetition % | Edited (p50c): cut answers ending in repetition % |
|---|---|---|---|---|
| MATH-500 | 100.0% |
75.0% |
96.2% |
34.7% |
| GSM8K | 97.5% |
71.3% |
98.3% |
29.7% |
| IFEval | 78.7% |
64.3% |
96.1% |
80.0% |
When the unedited model finishes a MATH-500 answer, it is right 100.0% of the time (133 of 133 finished answers), and 97.5% on GSM8K. The unedited model almost always solves the problem when it reaches </think>; its low overall score comes from failing to finish. Among answers cut at 32,768 tokens, 96.2% to 98.3% end in repetition, nearly all of them verbatim loops (95.9% of cut MATH-500 answers and 97.9% on GSM8K). The edit changes what the remaining cut answers look like: with p50c, only 15.3% of cut MATH-500 answers and 15.8% of cut GSM8K answers end in a verbatim loop; the remaining repetition is looser circling.


In lab2_benchmarks.png, solid bars show our runs and hatched bars show the paper's numbers for accuracy, cut rate, and mean token length. lab2_lengths.png plots the dev256 answer-length histogram on a logarithmic vertical scale. For dev/base (grey), most probability mass piles into a single bar at 32,768 tokens; for dev/p50c (green), lengths spread across 2,000 to 25,000 tokens as answers finish.
4. How every benchmark answer ended (results/lab2/endings.json)
labs/lab4_endings.py sorts every answer into four mutually exclusive groups by whether and how it wrote </think>:
- Cut while thinking: hit the
32,768-token cap before writing</think>. - Stopped while thinking: emitted the end-of-sequence token before writing
</think>, leaving no final answer. - Closed out of a loop: wrote
</think>right after verbatim repetition (the 60-character snippet ending 100 characters before</think>appears at least 3 times in the 6,000 characters before</think>). - Closed normally: wrote
</think>without that repetition pattern before it.
Counts for all six benchmark runs, with correct answers in parentheses:
| Run (total answers) | Total correct | Cut while thinking (correct) | Stopped while thinking (correct) | Closed out of a loop (correct) | Closed normally (correct) |
|---|---|---|---|---|---|
bench/base/math500 (2,000) |
146 |
1,863 (13) |
0 (0) |
8 (4) |
129 (129) |
bench/p50c/math500 (2,000) |
1,036 |
416 (1) |
1 (0) |
581 (191) |
1,002 (844) |
bench/base/gsm8k (2,638) |
374 |
2,312 (57) |
0 (0) |
38 (36) |
288 (281) |
bench/p50c/gsm8k (2,638) |
1,765 |
116 (0) |
1 (0) |
558 (190) |
1,963 (1,575) |
bench/base/ifeval (1,082) |
229 |
788 (0) |
0 (0) |
18 (15) |
276 (214) |
bench/p50c/ifeval (1,082) |
686 |
10 (0) |
2 (0) |
58 (23) |
1,012 (663) |
Comparing bench/base/math500 and bench/p50c/math500:
- In the unedited model,
1,863of2,000answers were cut while thinking (13were still graded correct because they boxed a number inside<think>). Only8escaped a loop, and129closed normally (all129correct). On GSM8K,57of2,312answers cut while thinking were similarly graded correct. - In the edited model on MATH-500, answers cut while thinking drop from
1,863to416. Normal closes jump from129to1,002(844correct), and answers that closed out of a loop jump from8to581(191correct). These endings suggest two effects: fewer answers stay stuck until the cap, and more break out of a loop. Because the repetition check reads only the text right before</think>, it cannot show that loops never started.
5. How much exploration budget the agent needed in the paper
In the paper's logs (external/lm_understand-logs/exp8_math/README.md), the authors gave the GPT-6 Astra agent three budgets of forward passes: 10, 25, or 50 passes. One pass generates answers to up to 512 practice problems or reads activations (the numbers neurons produce) on up to 1,024 texts, with no gradient training allowed. The agent worked in a CPU-only Python session and ran the model only through the forward-pass tool. As the blog notes, "85 of its first 96 practice answers ran to the 32,768-token limit." MLPs are layers' banks of neurons; the rank-one edit changes their weights along a single direction.
| Agent budget (forward passes) | MATH-500 accuracy % | GSM8K accuracy % | IFEval accuracy % | MATH-500 hit the limit % | What edit the agent shipped |
|---|---|---|---|---|---|
| Unedited | 7.9 |
13.8 |
21.0 |
93.0 |
None |
10 passes |
12.3 |
25.7 |
49.0 |
87.2 |
Removed a loop direction from 30 matrices across layers 8 to 22 with alpha = 1.5 |
25 passes |
14.1 |
25.1 |
39.9 |
86.4 |
Zeroed attention head 7 of layer 15 |
50 passes (p50c) |
48.1 |
67.1 |
62.3 |
33.1 |
Rank-one edit (alpha = 6) on 64 MLP columns in layers 15 and 19 (131,072 weights, 0.015% of the model) |
With 10 or 25 passes, the agent spotted the repetition failure and tried broad or single-head edits that barely moved the 93% MATH-500 cut rate (87.2% and 86.4%). Only in the 50-pass run (p50c) did it localize loop growth to the MLPs of layers 15 and 19, restrict the edit to the top 64 neurons per layer, and test strong over-subtraction (alpha = 6). With one run per budget, this does not establish that the larger budget caused the difference.
Why it works
1. Two paths an answer can take, and how grading treats them
When "think35" mode is on, wsp/protocol.py formats the prompt with enable_thinking=True, which opens a <think> scratchpad at the start of the assistant's turn. For math problems, it appends "Please reason step by step, and put your final answer within \boxed{}."
In wsp/grading.py, math grading runs in two steps:
- If
</think>appears, keep only the substring after the last</think>. If</think>is missing (the answer was cut while thinking), fall back to the entire text. - Find the last
\boxed{...}(or\fbox{...}) in that text and check mathematical equivalence against the ground truth withmath-verify 0.9.0.
Because of that fallback, 13 cut MATH-500 answers and 57 cut GSM8K answers still score as correct for the unedited model: the model happened to write \boxed{...} inside <think> before getting stuck. On IFEval (ifeval_follow in wsp/grading.py), strict prompt-level grading checks only the text after </think>. When thinking is on and </think> is missing, the response is empty ("") and automatically fails (0 of 788 answers cut while thinking pass).
2. Why "correct when finished" drops when overall accuracy jumps
In Theory Guide section 7, the probability of getting a finished, correct answer splits into two factors:
On MATH-500:
- Unedited (
bench/base/math500): (6.65%), and (100.0%). Their product is6.65%, plus0.65%(13 / 2,000) from cut-but-boxed answers, giving7.3%overall accuracy. - Edited (
bench/p50c/math500): (69.0%), more than ten times higher, while (75.0%). Their product is51.75%(plus1cut-but-boxed answer =51.8%).
Two things pull down from 100.0% to 75.0%:
- Selection by difficulty. The
6.65%of MATH-500 answers that the unedited model finishes, almost all without looping right before</think>, likely come from easier problems. Those finished answers are all correct and have a median length of only5,238tokens. With the edit, the model finishes69.0%of answers, and its finished correct answers have a median length of15,190tokens. The extra finished answers may include harder problems where a 0.8B model can still make math errors. - Loop escapes vs normal closes. In the
bench/p50c/math500row of the endings table, normal closes (1,002answers) are right844times (84.2%). Answers inclosed out of a loop(581answers) are right191times (32.9%). Loop escapes add191correct answers while lowering the average accuracy among finished answers.
3. How scripts/run_jobs.py shards, grades, and merges a plan
Generating 5,720 long reasoning answers per model took about 8 GPU-hours for the unedited model and 3 for the edited one. scripts/run_jobs.py parallelizes a plan across a pool of GPUs without cross-GPU communication:
Shard i of k reads every -th problem (rows[i::k]) from /nvme/wsp-lab/data/<data>.jsonl. Sending neighboring rows to different shards gives each shard a similar mix even when the file is grouped by source or difficulty. Each GPU runs an independent vLLM process (scripts/gen_worker.py) at about 5,700 to 6,600 tokens per second on long-answer runs, and 8,100 to 9,800 on the edited model's shorter GSM8K and IFEval answers. Once every shard writes .done.json, run_jobs.py restores problem order in runs/<name>/merged.jsonl and writes runs/<name>/summary.json.
4. Standard error (SE) and paired gain in plain words
Every accuracy in results/summary.md carries a +/- SE bar, such as 14.2 +/- 0.8 on GSM8K.
- What standard error (SE) measures: If you drew a fresh set of problems of the same size from the same distribution, the average score would shift slightly by chance.
wsp/summary.pyfirst computes the accuracy on each problem by averaging its 2 or 4 samples, so repeated samples on one problem are not treated as independent problems. The standard error is the sample standard deviation of divided by : As a rule of thumb, a gap of2 SEor more is unlikely to come from problem sampling alone (+/- 2 SEis roughly a 95% confidence interval). - Why 16 problems have an SE about 4 times larger than 256 problems: Because is in the denominator, dividing the problem count by
16(from256indev256to16inquick) multiplies the SE by . Our gain SE is11.6percentage points onquick/p50c(16problems) vs2.6points ondev/p50c(256problems), a ratio of4.5. - Why paired gain has a smaller SE than comparing two separate runs: Because runs
aandbuse the same problems,compare(a, b)inwsp/summary.pytakes the difference on each problem , , and computes the mean and SE of . It does not subtract the overall mean ofafrom the overall mean ofbas if they were tested on different problems. Easy problems are easy for both models and hard ones are hard for both, so problem difficulty cancels inside each . On MATH-500, the individual accuracies have SEs of0.9and1.9, and the paired gain (+44.5 +/- 1.7) cancels shared variance across the 500 problems.
Check yourself
- On
bench/base/math500,results/summary.mdreports7.3%accuracy (146correct answers out of2,000), and100.0%correct when finished. If100.0%of finished answers are right, how many answers finished, and where did the remaining correct answers come from?
Answer
In results/lab2/endings.json, 129 answers closed normally (all 129 correct) and 8 closed out of a loop (4 correct). Of those 137 answers that wrote </think>, 4 later hit the 32,768-token cap while writing the final response, leaving 133 that finished (133 / 2,000 = 6.65%), all 133 correct (100.0%). The remaining 13 correct answers (133 + 13 = 146) were cut at 32,768 tokens while still inside <think>, and were graded correct because they had already written \boxed{...} inside their thinking scratchpad before looping.
- Why did
labs/peek.pyandlabs/lab4_endings.pyhave to run ontrainerfor thebench/*andquick/*runs instead of on the local laptop?
Answer
Full transcripts (shard*.texts.jsonl.gz) are large, so scripts/pull_runs.sh leaves them on the VM and copies only merged.jsonl, summary.json, and *.done.json to results/runs/ on the laptop. trainer holds the texts for bench/* and quick/* (plus the three dev/rebuilt-* runs), while sampler holds build/base and all other dev/* runs.
- Look at the endings table for
bench/p50c/gsm8k(2,638answers). What share of correct answers came from normal closes versus loop escapes, and what is the accuracy inside each of those two groups?
Answer
Out of 1,765 correct answers on bench/p50c/gsm8k, 1,575 (89.2%) came from closed normally and 190 (10.8%) came from closed out of a loop. Inside closed normally, accuracy is 1,575 / 1,963 = 80.2%. Inside closed out of a loop, accuracy is 190 / 558 = 34.1%.
- Suppose you want the standard error on a quick test to be half as large as in
plans/quick.json(--limit 16, SE around11.6points). How many problems should you pass to--limit?
Answer
64 problems. Because standard error shrinks like , cutting the SE in half requires times as many problems: .
Try this next
- Check how the 32 quick-run answers ended. On
trainer, runlabs/lab4_endings.pyonquick/baseandquick/p50c, plus--show 2to print the text around</think>for up to2correct answers per run that closed out of a loop:Withoutbashssh trainer . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code CUDA_VISIBLE_DEVICES="" python labs/lab4_endings.py quick/base quick/p50c --show 2--out, the script writes toresults/lab4/try-quick_base-quick_p50c/endings.jsonso the main result files stay untouched. Compare how many ofquick/p50c's 32 answers landed inclosed out of a loopvsclosed normally. - Read 2 more answers from
quick/p50cwithlabs/peek.py. RunCUDA_VISIBLE_DEVICES="" python labs/peek.py quick/p50contrainerto see the 32-line table. Pick two problems where bothquick/p50canswers were wrong:gsm8k_train_1313(it answered145and15for truth55) andgsm8k_train_6457(both cut at32,768tokens without a verbatim loop):Check howbashCUDA_VISIBLE_DEVICES="" python labs/peek.py quick/p50c --id gsm8k_train_1313 --answer 0 CUDA_VISIBLE_DEVICES="" python labs/peek.py quick/p50c --id gsm8k_train_6457 --answer 0gsm8k_train_6457spent 32,768 tokens whenverbatim_loop_endis false (it keeps trying variations without repeating an identical word block). - Verify the GSM8K vs MATH split inside
dev/baseanddev/p50clocally.dev256has 128 GSM8K-train problems and 128 MATH-train problems. On this laptop, run a one-liner onresults/summary.json:Check thatbashpython3 -c 'import json; d={r["name"]:r["summary"]["by_source"] for r in json.load(open("results/summary.json")) if r["name"] in ("dev/base","dev/p50c")}; print(json.dumps(d, indent=2))'dev/basescores15.2%on GSM8K (86.3%cut) and9.0%on MATH (91.0%cut), whiledev/p50cjumps to74.6%on GSM8K (4.7%cut) and48.4%on MATH (34.8%cut).