Loselen

001 - Vectors & Vector Operations

Challenge

Implement vector addition, subtraction, dot product, and L1/L2 norms using only basic arrays (no linear algebra library calls). Verify each against a library (e.g., NumPy), since Python does not have built-in vector operations.

My Approach

Firstly, I chose how to define a vector in Python. For this first project, I wanted to keep it simple, as a list of numbers.

For all the binary vector operations (operations that need two inputs), I checked the sizes first. If they were the same size, I proceeded; if not, I raised a ValueError. This was done using this function:

def _check_sizes(a, b):
    if len(a) != len(b):
        raise ValueError("Both vectors must have the same size.")

Vector Addition

Vector addition definition:

[a1a2an]+[b1b2bn]=[a1+b1a2+b2an+bn]\begin{bmatrix} a_1 \\ a_2 \\ \vdots \\ a_n \end{bmatrix} + \begin{bmatrix} b_1 \\ b_2 \\ \vdots \\ b_n \end{bmatrix} = \begin{bmatrix} a_1 + b_1 \\ a_2 + b_2 \\ \vdots \\ a_n + b_n \end{bmatrix}

For vector addition, I just added every corresponding element (element-wise). I used a normal loop like this:

def vec_add(a, b):
    _check_sizes(a, b)
    res = []
    for single_a, single_b in zip(a, b):
        res.append(single_a + single_b)
    return res

Notice that, at the beginning, I used the private function _check_sizes() to ensure the operation was valid (it raises a ValueError if not).

Vector Subtraction

Vector subtraction definition:

[a1a2an][b1b2bn]=[a1b1a2b2anbn]\begin{bmatrix} a_1 \\ a_2 \\ \vdots \\ a_n \end{bmatrix} - \begin{bmatrix} b_1 \\ b_2 \\ \vdots \\ b_n \end{bmatrix} = \begin{bmatrix} a_1 - b_1 \\ a_2 - b_2 \\ \vdots \\ a_n - b_n \end{bmatrix}

For vector subtraction, the idea was still the same as vector addition, except every element was subtracted (as opposed to addition).

def vec_sub(a, b):
    _check_sizes(a, b)
    res = []
    for single_a, single_b in zip(a, b):
        res.append(single_a - single_b)
    return res

Vector Dot product

Vector dot product definition:

[a1a2an][b1b2bn]=a1b1+a2b2++anbn\begin{bmatrix} a_1 \\ a_2 \\ \vdots \\ a_n \end{bmatrix} \cdot \begin{bmatrix} b_1 \\ b_2 \\ \vdots \\ b_n \end{bmatrix} = a_1b_1 + a_2b_2 + \ldots + a_nb_n

For dot product, I multiplied the corresponding elements and summed them into one scalar. There was a caveat for this function: it assumed real number input. If the input was a complex number, I handled it like a normal real number case.

Why did I state this? Because there is a different dot product definition called the Hermitian Product, which states we take the conjugate of one vector before multiplying. Since I wanted this to be simple, I handled it as a normal real number case.

def vec_dot(a, b):
    _check_sizes(a, b)
    res = 0
    for single_a, single_b in zip(a, b):
        res += single_a * single_b
    return res

Vector L1 and L2 Norm

For this, I used the general definition of the LpL_p norm, which is:

ap=i=1naipp\lVert a \rVert_p = \sqrt[p]{\sum_{i=1}^{n} |a_i|^p}

There is something to keep in mind: the LpL_p norm is defined for any real number p1p \geq 1. So I added a check to ensure p1p \geq 1 before proceeding.

def vec_norm(a, order):
    if not order >= 1:
        raise ValueError("Order must be greater than or equal to 1.")
    res = 0

    for single_a in a:
        res += abs(single_a) ** order
    res = res ** (1 / order)

    return res

Verifying Against a Library

Since Python doesn't have a built-in vector library, I checked my implementation against the NumPy library on several cases. For testing, I used the pytest library.

For vector addition, I checked it against these cases:

  1. simple addition
  2. commutative check
  3. integer and float addition
  4. size doesn't match case

For the implementation, it looked like this:

import numpy as np
import pytest
from vector import vec_add

def test_vec_add():
    a = [3, 4, 5]
    b = [-4, 0, 3]
    c = [1.5, 2.75, -1.2]
    d = [3, -4]
    
    assert vec_add(a, b) == np.add(np.array(a), np.array(b)).tolist() # simple addition check
    assert vec_add(b, c) == np.add(np.array(c), np.array(b)).tolist() # commutative check
    with pytest.raises(ValueError): # size doesn't match check
        vec_add(a, d)

Then I also did the same for all other functions implemented here. The testing implementation basically looked the same as the vector addition ones. Here is the list of what I checked:

For vector subtraction:

  1. simple subtraction
  2. integer and float subtraction
  3. size doesn't match

For vector dot product:

  1. simple dot product
  2. commutative check
  3. integer and float dot product
  4. size doesn't match check

For L1 and L2 norm:

  1. checking known pythagorean triples and quadruples
  2. float Lp norm check
  3. float order ValueError check
  4. less than 1 order ValueError check

Source Code

You can find the source code here:

Source Code Link

In the source code you can find additional things like type hintings, docstrings, and file structure that I omit in this explanation.

On this page