Repeated sparse indices accumulate before nonlinear updates
Derive a gather's scatter-add adjoint, distinguish duplicate storage entries from gradient identity, and budget sparse values and indices under supported runtime layouts.
On this page
The question for today
What you’ll understand
- Accumulate repeated gather-index cotangents into the correct parameter rows.
- Explain uncoalesced COO duplicates and why nonlinear operations need semantic care.
- Compare sparse and dense payload including index overhead and runtime support.
An embedding lookup can touch only a few rows of a large table, so its input gradient has useful structure. The same row may be selected several times, and reverse mode must add every occurrence's contribution. A sparse storage format can represent those contributions separately before combining them. Distinguish the mathematical gradient from its physical entries before interpreting a duplicate index as a duplicate parameter or applying a nonlinear optimizer update to each fragment.
The adjoint of gathering is accumulation
For a table E with one row per identifier, a gather output y_j takes row E at index i_j. A reverse cotangent for each occurrence contributes to that same table row. If the row appears twice, both vectors add. Rows never selected receive zero under this simple gather-only model. This is a sparse linear map with respect to the table values at fixed indices, but the integer indices are not ordinary differentiable coordinates. A program that wants gradients for selecting an index needs a different explicitly defined relaxation or estimator.
A naive reverse implementation that assigns rather than adds can lose earlier occurrences. Parallel scatter-add implementations must also respect accumulation semantics, even though floating-point summation order can affect final rounding. PyTorch 2.9 documents operation-specific shape and backward restrictions and possible device nondeterminism for scatter operations. Those implementation details are separate from the exact real-valued adjoint. Test repeated indices and nontrivial vector cotangents; a dataset with unique indices can conceal a fundamentally wrong gather derivative.
Storage duplicates can represent one summed gradient
PyTorch 2.9's sparse COO documentation permits uncoalesced duplicate coordinates whose values contribute by addition. Coalescing combines duplicates and orders the representation under its contract. The mathematical tensor is the sum at each coordinate, not whichever duplicate appears last. A nonlinear operation on separate fragments can differ from the operation on their sum: squaring values one and two separately gives a represented total five, while squaring the combined value three gives nine. An optimizer or custom kernel must honor the layout's semantics instead of assuming every stored coordinate is unique.
Sparse gradient support depends on the operation, layout, device, and optimizer. Embedding can request sparse gradients under its documented options, but not every optimizer accepts that representation or implements every combination. Do not infer support from the fact that a sparse tensor can be constructed. A zero stored value can also remain structurally present after cancellation; numerical zero and absent storage are different concepts. Verify supported transformations, coalescing requirements, accumulation, and optimizer state rather than declaring that sparse automatically means less memory or fewer arithmetic operations.
Verify structure and numerical behavior together
Compare a sparse derivative with a small dense reference after converting both to the same mathematical layout. Include repeated indices, canceled contributions, unused rows, different feature widths, and several accumulation steps. Measure index traffic and coalescing work alongside value payload, especially when the selected set is not very sparse. A representation that saves gradient storage can still require dense parameter or optimizer state. Keep the promised memory category explicit and test whether batching changes duplicate frequency or backend behavior.
The vector lab illustrates componentwise addition on paper rather than sparse storage. Reset, explicitly set BOTH angle 0 degrees and magnitude one, inspect vector (1,0), then Play or Step. Reset and re-set BOTH angle 90 degrees and magnitude two for vector (0,2). Their paper sum is (1,2), not either last assignment. Edits retain t; playback traces one selected vector and never stores both, scatters rows, or coalesces duplicates. Use the example only to inspect accumulation components; the worked gather trace determines the real indexed gradient.
Work it through
Two occurrences of one embedding row must add
Let E have rows (0,1), (2,3), and (4,5). Gather indices (1,1,2), with output cotangents (1,2), (3,−1), and (−2,4). Indices are zero-based and fixed.
The first two occurrences both refer to row one, so add their cotangents: (1,2)+(3,−1)=(4,1). Overwriting with the second would lose the first contribution.
Row two receives (−2,4), and unused row zero receives (0,0). The full table gradient is therefore rows (0,0),(4,1),(−2,4).
As a scalar check, the weighted gathered output is (2,3)·(1,2)+(2,3)·(3,−1)+(4,5)·(−2,4)=8+3+12=23. Its table derivative has the accumulated rows above.
Repeated indices represent repeated uses of the same parameter row, requiring addition rather than assignment or separate nonlinear updates.
The living notebook
Explore the model
Interactive lab · vectors
Accumulation adds components, not the last writer
For EACH run Reset, set BOTH angle and magnitude, inspect, then Play or Step. Compare (0°,1), vector (1,0), with (90°,2), vector (0,2), and add them on paper. Edits retain t. This instrument displays one vector at a time and implements no sparse table, scatter, duplicate storage, or coalescing.
- x component
- 1.393
- y component
- 0.975
- Magnitude
- 1.7
Playback traces a fixed vector and its projections. Angle and magnitude set the geometry; the moving marker does not change them. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forRepeated derivative contributions add in the same coordinates; an indexed runtime must preserve that semantics independently of storage format.
Pause & reason
Check your understanding
Your turn
Try it yourself
A row-sparse gradient touches three unique rows of a 1,000×8 table. Values use four bytes and one row index uses eight bytes. Compare ideal sparse payload with dense gradient payload, excluding all overhead and optimizer state.
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Count eight values plus one index per stored row, not just the nonzero values.
Compare with the solution
Sparse payload is 3×(8×4+8)=120 bytes. Dense gradient payload is 1,000×8×4=32,000 bytes. This comparison concerns gradient payload only; duplicate entries, metadata, parameter storage, optimizer state, and operation support remain separate costs.
Keep the idea
Take it with you
- A gather's reverse rule accumulates every occurrence into its indexed row.
- Uncoalesced duplicate storage denotes a summed tensor, not separate parameters.
- Sparse savings depend on indices, layouts, optimizer support, and the measured memory category.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.