Skip to content

← Matmul and einsum step 1 of 6

Easy Primitives

One weight matrix, every item

Jump to the task

A single weight matrix can transform every item in a batch. There is no need to copy the weights or write a loop:

x:      (batch, time, in_features)
w:      (in_features, out_features)
x @ w:  (batch, time, out_features)

Each row x[b, t] is multiplied by the same w.

Matrix axes and batch axes

When both operands have at least two dimensions, @ treats their last two axes as matrix axes. The left matrix's last dimension must match the right matrix's second-to-last dimension. Any earlier axes are batch axes and must be broadcast-compatible.

(5, 2, 3) @    (3, 4) -> (5, 2, 4)
(1, 2, 3) @ (5, 3, 4) -> (5, 2, 4)

In the first line, one weight matrix is reused for five input matrices. In the second, one input matrix is paired with five weight matrices. The shared size 3 is the dimension summed over, not an output axis.

import torch

x = torch.arange(12, dtype=torch.float32).reshape(2, 3, 2)
w = torch.tensor([[1.0], [2.0]])
y = x @ w
print(list(y.shape))  # [2, 3, 1]
print(y.tolist())  # [[[2.0], [8.0], [14.0]], [[20.0], [26.0], [32.0]]]

Why avoid a loop here?

The starter computes each batch item separately, then stacks the results. One expression lets PyTorch handle the batch and avoids those separate Python calls. This can improve performance, but the benefit depends on shapes, device and execution mode.

Flattening the batch and time axes first is also a valid implementation if you restore the output shape. It is unnecessary for this expression. reshape may share storage or copy depending on strides; adjacent axes alone do not guarantee a view.

What the exercise measures

The harness counts dispatched PyTorch operations, including view operations. For its contiguous inputs, the direct matrix product fits the three-operation limit. That count describes this exercise's execution path, not a promise about every PyTorch version or tensor layout.

Dispatched operations are not the same as GPU kernel launches, elapsed time or allocated bytes. Use a benchmark when choosing an implementation for a real workload.

Your task

def project(x: torch.Tensor, w: torch.Tensor) -> torch.Tensor

x has shape (batch, time, in_features) and w has shape (in_features, out_features). Return shape (batch, time, out_features) using at most three dispatched operations on the harness inputs.