Skip to content

← Indexing and Selection step 1 of 9

Medium Primitives

The update that goes nowhere

PyTorch has two indexing systems and they share one syntax. Telling them apart is the single most useful thing in this track, because one returns a window onto your tensor and the other returns a photocopy.

Basic indexing gives you a view

Integers and slices. The result is a shape and a stride over the same buffer:

a = torch.zeros(5)
a[1:3] += 1
a                    # [0, 1, 1, 0, 0]      the original changed

Advanced indexing gives you a copy

A list or tensor of indices, or a boolean mask. There is no stride that describes “elements 1 and 2 and 7”, so PyTorch allocates and gathers:

b = torch.zeros(5)
part = b[[1, 2]]
part += 1
b                    # [0, 0, 0, 0, 0]      nothing happened

part was a fresh tensor. You incremented it. It then went out of scope.

Why this one is nasty

It does not raise. It does not warn. It returns the right-looking thing, and the update lands in a tensor nobody keeps. Code like this appears in every codebase, usually as “zero out the padded positions” or “clamp these specific rows”, and it silently does nothing at all.

Written as one statement it does work, because PyTorch turns it into an index_put_ rather than a read followed by a discarded write:

b[[1, 2]] += 1       # works. b is [0, 1, 1, 0, 0]
part = b[[1, 2]]
part += 1            # does not. part is a copy.

The difference is whether the indexing appears on the left of the assignment. That is a subtle rule to carry around, and the reliable habit is simpler: if you took a slice out into a variable and want the original to change, check that you took a view.

Your task

def add_to_rows(x: torch.Tensor, rows: list[int], amount: float) -> torch.Tensor

Add amount to every element of the named rows of x, in place, and return x. Rows may repeat, and a repeated row should be added to once per mention.

The starter pulls the rows out first, which is where it goes wrong.