# Chapter 5 Exercise Set 3: Vectors and Matrices¶

## Vectors¶

1. Lists can be used to represent mathematical vectors. In this exercise and several that follow you will write functions to perform standard operations on vectors. Create a file named `vectors.py` and write Python code to make the doctests for each function pass.

Write a function `add_vectors(u, v)` that takes two lists of numbers of the same length, and returns a new list containing the sums of the corresponding elements of each.

```def add_vectors(u, v):
"""
[2, 1]
[2, 6]
>>> add_vectors([1, 2, 1], [1, 4, 3])
[2, 6, 4]
>>> add_vectors([11, 0, -4, 5], [2, -4, 17, 0])
[13, -4, 13, 5]
>>> a = [1, 2, 3]
>>> b = [1, 1, 1]
[2, 3, 4]
>>> a
[1, 2, 3]
>>> b
[1, 1, 1]
"""
```

`add_vectors` should pass the doctests above.

2. Write a function `scalar_mult(s, v)` that takes a number, `s`, and a list, `v` and returns the scalar multiple of `v` by `s`.

```def scalar_mult(s, v):
"""
>>> scalar_mult(5, [1, 2])
[5, 10]
>>> scalar_mult(3, [1, 0, -1])
[3, 0, -3]
>>> scalar_mult(7, [3, 0, 5, 11, 2])
[21, 0, 35, 77, 14]
>>> a = [1, 2, 3]
>>> scalar_mult(4, a)
[4, 8, 12]
>>> a
[1, 2, 3]
"""
```
3. Write a function `dot_product(u, v)` that takes two lists of numbers of the same length, and returns the sum of the products of the corresponding elements of each (the dot_product).

```def dot_product(u, v):
"""
>>> dot_product([1, 1], [1, 1])
2
>>> dot_product([1, 2], [1, 4])
9
>>> dot_product([1, 2, 1], [1, 4, 3])
12
>>> dot_product([2, 0, -1, 1], [1, 5, 2, 0])
0
"""
```

Verify that `dot_product` passes the doctests above.

## Matrices¶

1. Create a new module named `matrices.py` and add the following function, which returns a copy of nested lists of numbers such that the lists are not aliases:

```def copy_matrix(matrix):
"""
>>> copy_matrix([[1, 2], [3, 4]])
[[1, 2], [3, 4]]
>>> copy_matrix([[1, 2, 3], [4, 5, 6]])
[[1, 2, 3], [4, 5, 6]]
>>> copy_matrix([[1, 2], [3, 4], [5, 6], [7, 8]])
[[1, 2], [3, 4], [5, 6], [7, 8]]
>>> m = [[1, 0, 0], [0, 2, 0], [0, 0, 3]]
>>> copyofm = copy_matrix(m)
>>> copyofm
[[1, 0, 0], [0, 2, 0], [0, 0, 3]]
>>> for row_num, row in enumerate(copyofm):
...     for col_num, col_val in enumerate(row):
...         copyofm[row_num][col_num] = 42
...
>>> copyofm
[[42, 42, 42], [42, 42, 42], [42, 42, 42]]
>>> m
[[1, 0, 0], [0, 2, 0], [0, 0, 3]]
"""
```
2. Then add each of the following functions to the `matrices.py` module, one at a time, making sure the doctests pass for each one before adding the next.

```def add_row(matrix):
"""
>>> m = [[0, 0], [0, 0]]
[[0, 0], [0, 0], [0, 0]]
>>> n = [[3, 2, 5], [1, 4, 7]]
[[3, 2, 5], [1, 4, 7], [0, 0, 0]]
>>> n
[[3, 2, 5], [1, 4, 7]]
"""
```
```def add_column(matrix):
"""
>>> m = [[0, 0], [0, 0]]
[[0, 0, 0], [0, 0, 0]]
>>> n = [[3, 2], [5, 1], [4, 7]]
[[3, 2, 0], [5, 1, 0], [4, 7, 0]]
>>> n
[[3, 2], [5, 1], [4, 7]]
"""
```

Your new functions should pass the doctests. Note that the last doctest in each function assures that `add_row` and `add_column` are pure functions. ( note: Python has a `copy` module with a function named `deepcopy` that could make the task easier here, or you could use your own `copy_matrix` function.)

3. Write a function `add_matrices(m1, m2)` that adds `m1` and `m2` and returns a new matrix containing their sum. You can assume that `m1` and `m2` are the same size. You add two matrices by adding their corresponding values.

```def add_matrices(m1, m2):
"""
>>> a = [[1, 2], [3, 4]]
>>> b = [[2, 2], [2, 2]]
[[3, 4], [5, 6]]
>>> c = [[8, 2], [3, 4], [5, 7]]
>>> d = [[3, 2], [9, 2], [10, 12]]
[[11, 4], [12, 6], [15, 19]]
>>> c
[[8, 2], [3, 4], [5, 7]]
>>> d
[[3, 2], [9, 2], [10, 12]]
"""
```

Add your new function to `matrices.py` and be sure it passes the doctests above. The last two doctests confirm that `add_matrices` is a pure function.

4. Write a function `scalar_mult(s, m)` that multiplies a matrix, `m`, by a scalar, `s`.

```def scalar_mult(s, m):
"""
>>> a = [[1, 2], [3, 4]]
>>> scalar_mult(3, a)
[[3, 6], [9, 12]]
>>> b = [[3, 5, 7], [1, 1, 1], [0, 2, 0], [2, 2, 3]]
>>> scalar_mult(10, b)
[[30, 50, 70], [10, 10, 10], [0, 20, 0], [20, 20, 30]]
>>> b
[[3, 5, 7], [1, 1, 1], [0, 2, 0], [2, 2, 3]]
"""
```

Add your new function to `matrices.py` and be sure it passes the doctests above.

5. Write functions `row_times_column` and `matrix_mult`:

```def row_times_column(m1, row, m2, column):
"""
>>> row_times_column([[1, 2], [3, 4]], 0, [[5, 6], [7, 8]], 0)
19
>>> row_times_column([[1, 2], [3, 4]], 0, [[5, 6], [7, 8]], 1)
22
>>> row_times_column([[1, 2], [3, 4]], 1, [[5, 6], [7, 8]], 0)
43
>>> row_times_column([[1, 2], [3, 4]], 1, [[5, 6], [7, 8]], 1)
50
"""
```
```def matrix_mult(m1, m2):
"""
>>> matrix_mult([[1, 2], [3,  4]], [[5, 6], [7, 8]])
[[19, 22], [43, 50]]
>>> matrix_mult([[1, 2, 3], [4,  5, 6]], [[7, 8], [9, 1], [2, 3]])
[[31, 19], [85, 55]]
>>> matrix_mult([[7, 8], [9, 1], [2, 3]], [[1, 2, 3], [4, 5, 6]])
[[39, 54, 69], [13, 23, 33], [14, 19, 24]]
"""
```

Add your new functions to `matrices.py` and be sure it passes the doctests above.

6. Write a function `transpose` that takes a matrix as an argument and returns is transpose:

```def transpose(m):
"""
>>> m = [[3, 4, 6]]
>>> transpose(m)
[[3], [4], [6]]
>>> m
[3, 4, 6]
>>> m = [[3, 4, 6], [1, 5, 9]]
>>> transpose(m)
[[3, 1], [4, 5], [6, 9]]
"""
```
7. Extra challenge for the mathematically inclined: Add a function `cross_product(u, v)` to your `vectors.py` module that takes two lists of numbers of length 3 and returns their cross product. You should write your own doctests and use the test driven development process described in the chapter.