Skip to content

← Indexing and Selection step 3 of 9

Medium Primitives

When two writes land on one slot

Jump to the task

When several values target the same destination, decide whether to replace the stored value or add the contributions. Counting requires addition.

import torch

index = torch.tensor([1, 1, 3], dtype=torch.long)
source = torch.tensor([10.0, 20.0, 30.0])
out = torch.zeros(5)
out.scatter_add_(0, index, source)
print(out.tolist())  # [0.0, 30.0, 0.0, 30.0, 0.0]

Position 1 receives both 10 and 20. Position 3 receives 30. scatter_ instead writes source values, and with duplicate indices its choice of surviving value is unspecified. Do not rely on “the last write wins.”

Build a histogram

Give each label a contribution of one and add it to that label's bin:

labels = torch.tensor([1, 1, 3], dtype=torch.long)
counts = torch.zeros(5)
counts.scatter_add_(0, labels, torch.ones(3))
print(counts.tolist())  # [0.0, 2.0, 0.0, 1.0, 0.0]

Replacing rather than adding these identical ones would leave every occupied bin at one, losing the number of occurrences. Repeated labels are central to a histogram, so the collision behavior matters.

scatter_add_ specifies addition, but does not guarantee identical floating-point results on every device. CUDA accumulation can be nondeterministic because contributions may be combined in a different order. The small integer counts in this CPU exercise are exactly representable.

torch.bincount is another option for nonnegative integer labels. It produces integer counts for unweighted input; this exercise asks for a floating-point output and illustrates accumulation through an index.

Your task

def bin_counts(labels: list[int], n_bins: int) -> torch.Tensor

n_bins is positive and every label lies in 0 .. n_bins-1. Return a one-dimensional floating-point tensor of length n_bins, where entry i is the number of occurrences of i. Unused bins are zero. An empty label list produces all zeros.

The starter writes ones without accumulating repeated labels.