Assignment 01: Tensors and Einsum

In this assignment you will implement several tensor operations from scratch using Python and PyTorch tensors. All code should be written in src/assignment_01.py. Starter code with vector/tensor initializations, function headers, and assertion-based tests is already provided. Please do not modify the test code.


Task 1: Dot Product

The dot product of two vectors $\mathbf{a}, \mathbf{b} \in \mathbb{R}^n$ is defined as

$$ \mathbf{a} \cdot \mathbf{b} = \sum_{k=0}^{n-1} a_k \cdot b_k $$

Your task is to implement the function dot_product(a, b) in src/assignment_01.py. Use a python for loop to iterate over the elements of the input vectors. The function should be written in a way that it can handle vectors of any length (not just the provided length of 100).


Task 2: Matrix-Matrix Multiplication

The matrix product $C = A \cdot B$ for $A \in \mathbb{R}^{m \times k}$, $B \in \mathbb{R}^{k \times n}$ and $C \in \mathbb{R}^{m \times n}$ is defined element-wise as

$$ c_{pr} = \sum_{q=0}^{k-1} a_{pq} \cdot b_{qr} $$

This corresponds to the einsum pq, qr -> pr.

Your task consists of two parts:

  1. Implement the function matmul_loops(A, B): Compute $C = A \cdot B$ using nested for loops. No PyTorch matrix-multiply operations are allowed inside this function.

  2. Implement the function matmul_dot(A, B): Compute the same product but reuse your dot_product function from Task 1. To do so, use loops over the dot_product function that uses slices of the input matrices as 1-D views.


Task 3: Einsum acsxp, bspy -> abcxy

Given tensors

$$ A \in \mathbb{R}^{a \times c \times s \times x \times p}, \quad B \in \mathbb{R}^{b \times s \times p \times y} $$

the target einsum acsxp, bspy -> abcxy computes

$$ C_{abcxy} = \sum_{s} \sum_{p} A_{acsxp} \cdot B_{bspy} $$

You can assume a static shape for the tensors, A: (2, 4, 5, 4, 3), B: (3, 5, 3, 5) and C: (2, 3, 4, 4, 5)

Your task consists of two parts:

  1. Implement the function einsum_loops(A, B): Compute the einsum expression by using for loops to iterate over all index dimensions, accumulating the products into a pre-allocated output tensor.

  2. Implement the function einsum_gemm(A, B): Compute the same einsum but reuse one of your matrix multiplication implementations from Task 2. To do so, use loops over the matrix multiplication function that uses slices of the input tensors as 2-D views.


Optional Task: Tensor Permutation and Reshaping

Look up torch.permute and torch.reshape in the PyTorch documentation and experiment with them inside a new python file. Try to answer the following questions:

  • How do .shape and .stride() change after a torch.permute or torch.reshape call? Does the underlying data move in memory?

  • How does torch.reshape differ from torch.view?

  • Why can reshaping a permuted tensor be handled differently than reshaping a freshly created tensor? (Hint: check .is_contiguous() before and after torch.permute).