Without compaction, one rollout is one row, so its reward, its advantage and its row are the same thing.
This PR
With context compaction (CC), one rollout can become several rows. Every row carries the same reward, GRPO uses it once per rollout, and its one advantage is copied to every row.
Status
Proposed — open PR, not merged. The numbers below come from a real run of the PR's code, with a stand-in model.
Nine pages on this PR, one question each. This one: how one reward maps onto several rows.
Background. In GRPO each prompt is run several times; each run is a rollout, and the runs of one prompt form a group.
A rollout's advantage is its reward minus the group's average reward (the baseline).
Every token the model generated in that rollout is then pushed up or down by that advantage.
Dotted words have a short explanation: hover or tap them.
Here the two rollouts of one group split differently: g0 was compacted 3 times and became 4 rows, while g1 was not compacted and stayed 1 row.
The reassembler writes the rollout's single reward onto each of its rows (rollout_reassembler.py L1350-1376).
Before GRPO runs, RL checks that all rows of a rollout agree, and keeps just one of them (logical_advantage.py L133-148).
GRPO therefore sees 2 rewards, not 5. The resulting advantage is copied back to every row (single_controller.py L5378-5393).
Two rollouts, five rows, two rewards
Why it is built this way
Question
Answer
Is the reward stored once or many times?
Many: each row carries a copy, because rows are trained one at a time. RL refuses to train if the copies disagree.
Does a rollout with more rows get more say?
No. GRPO sees one reward per rollout, so compaction cannot change any rollout's advantage. Above, g0 and g1 get +0.5 and −0.5 even though g0 has 4 rows and g1 has 1.
Does a rollout with more rows get more gradient?
No more than it would without compaction. Each generated token gets its rollout's advantage, and the loss divides by the total token count. This is why CC requires a token-level loss and rejects sequence-level ones, which would divide by rows (config.py L858-879).
What if one row is bad?
All rows of that rollout are masked. A rollout's validity is the lowest validity of its rows (logical_advantage.py L148).
What changes, then?
Only the context each token is scored against. A token in row g0_s2 is scored against the compacted prompt that row starts with, which is the prompt the model actually saw when it generated that token.
So what. Compaction changes how many rows a rollout has, not how much it counts.
The reward is one number per rollout. It is copied onto each row for storage, used once for the group baseline, and its advantage is spread over the rollout's generated tokens, wherever they landed.
One metric does count rows: num_mask_sample_filtered counts each row and padding row (review comment on single_controller.py).