Course Webpage

We import the numpy module.

In [1]:
import numpy as np

Given the linear system $Ax=b$ where the coefficient matrix $A$ and the constant matrix $b$ are

In [2]:
n = 7 

A1 = np.diag(np.ones(n))*3
A2 = np.diag(np.ones(n-1),1) 
A = A1 + A2 + A2.T 
b = np.arange(n,2*n)*1.

print('A')
print(A)
print('b')
print(b)
A
[[3. 1. 0. 0. 0. 0. 0.]
 [1. 3. 1. 0. 0. 0. 0.]
 [0. 1. 3. 1. 0. 0. 0.]
 [0. 0. 1. 3. 1. 0. 0.]
 [0. 0. 0. 1. 3. 1. 0.]
 [0. 0. 0. 0. 1. 3. 1.]
 [0. 0. 0. 0. 0. 1. 3.]]
b
[ 7.  8.  9. 10. 11. 12. 13.]

We can solve this system with the generic function

In [3]:
x = np.linalg.solve(A,b)

print('x')
print(x)
x
[1.86119554 1.41641337 1.88956434 1.91489362 2.36575481 1.98784195
 3.67071935]

Let us see an specific method to solve these type of linear systems, the tridiagonal systems.

Gauss method for tridiagonal systems

The coefficient matrix of a tridiagonal systems is tridiagonal. This is a matrix where all the elements are zero, except the elements in the main diagonal and the adyacent ones that are over and under. For example, the following linear system

$$ \begin{array}{rrrrrrrrrrrr} a_{11} x_1 & +a_{12} x_2& & & & & &=& b_1 \\ a_{21} x_1 & +a_{22} x_2& +a_{23}x_3 & & & & &=&b_2\\ & a_{32}x_2 &+ a_{33}x_3 & +a_{34}x_4 & & & &=&b_3\\ & & a_{43}x_3 & +a_{44}x_4 & +a_{45}x_5 & & &=&b_4\\ & & & a_{54}x_4 & +a_{55}x_5& +a_{56}x_6& &=&b_5\\ & & & & a_{65}x_5& +a_{66}x_6&+a_{67}x_7&=&b_6\\ & & & & & a_{76}x_6&+a_{77}x_7&=&b_7 \end{array} $$

has a $7\times7$ tridiagonal coefficient matrix

$$ A = \left( \begin{matrix} a_{11} & a_{12} & 0 & 0 & 0 & 0& 0\\ a_{21} & a_{22} & a_{23} & 0 & 0 & 0& 0\\ 0 & a_{32} & a_{33} & a_{34} & 0 & 0& 0\\ 0 & 0 & a_{43} & a_{44} & a_{45} & 0& 0\\ 0 & 0 & 0 & a_{54} & a_{55} & a_{56}& 0\\ 0 & 0 & 0 & 0 & a_{65} & a_{66}& a_{67}\\ 0 & 0 & 0 & 0 & 0 & a_{76}& a_{77} \end{matrix} \right) \quad \quad b = \left( \begin{matrix} b_1\\ b_2\\ b_3\\ b_4\\ b_5\\ b_6\\ b_7 \end{matrix} \right) $$

Gauss method has two steps:

  • Triangularization of the augmented matrix.
  • Back-substitution.

Triangularization

We must obtain zeros under the main diagonal using row operations. In the actual example we do

$$ \begin{matrix} \mathrm{If}\;k=1\quad f \leftarrow \dfrac{a_{21}}{a_{11}}\;\mathrm{then}\;& & a_{21}\leftarrow a_{21}-f\, a_{11}=0 & & a_{22}\leftarrow a_{22} -f\,a_{12} & & b_2\leftarrow b_2 -f\,b_1\\[0.3cm] \mathrm{If}\;k=2\quad f \leftarrow \dfrac{a_{32}}{a_{22}}\;\mathrm{then}\;& & a_{32}\leftarrow a_{32}-f\, a_{22}=0 & & a_{33}\leftarrow a_{33} -f\,a_{23} & & b_3\leftarrow b_3 -f\,b_2\\[0.3cm] \mathrm{If}\;k=3\quad f \leftarrow \dfrac{a_{43}}{a_{33}}\;\mathrm{then}\;& & a_{43}\leftarrow a_{43}-f\, a_{33}=0 & & a_{44}\leftarrow a_{44} -f\,a_{34} & & b_4\leftarrow b_4 -f\,b_3\\[0.3cm] \mathrm{If}\;k=4\quad f \leftarrow \dfrac{a_{54}}{a_{44}}\;\mathrm{then}\;& & a_{54}\leftarrow a_{54}-f\, a_{44}=0 & & a_{55}\leftarrow a_{55} -f\,a_{45} & & b_5\leftarrow b_5 -f\,b_4\\[0.3cm] \mathrm{If}\;k=5\quad f \leftarrow \dfrac{a_{65}}{a_{55}}\;\mathrm{then}\;& & a_{65}\leftarrow a_{65}-f\, a_{55}=0 & & a_{66}\leftarrow a_{66} -f\,a_{56} & & b_6\leftarrow b_6 -f\,b_5\\[0.3cm] \mathrm{If}\;k=6\quad f \leftarrow \dfrac{a_{76}}{a_{66}}\;\mathrm{then}\;& & a_{76}\leftarrow a_{76}-f\, a_{66}=0 & & a_{77}\leftarrow a_{77} -f\,a_{67} & & b_7\leftarrow b_7 -f\,b_6 \end{matrix} $$

Exercise 1

Given a tridiagonal linear system, write a function At, bt = triangular(A,b) that returns matrices At and bt that are the transformed matrices after the triangularization process. Use one and only one for loop.

  • To read better the matrices, use
np.set_printoptions(precision = 2)   # only two decimals
np.set_printoptions(suppress = True) # do not use exponential format
  • Obtain the number of rows and columns of A with shape. For instance, m, n = A.shape. You can also obtain the number of elements of a vector with len. For instance, n = len(b).

  • Initialize the triangular matrix with At = np.copy(A) if you do not want to modify A. Similarly with b.

  • Triangularize $Ax=b$, where matrices A and b can be generated with the code

n = 7 

A1 = np.diag(np.ones(n))*3
A2 = np.diag(np.ones(n-1),1) 
A = A1 + A2 + A2.T 

b = np.arange(n,2*n)*1.
  • Leave the function as it is and triangularize also A and b generated with the code
n = 8 

np.random.seed(3)
A1 = np.diag(np.random.rand(n))
A2 = np.diag(np.random.rand(n-1),1)
A = A1 + A2 + A2.T 

b = np.random.rand(n)
In [1]:
%run Exercise1.py
-------------  DATA 1  -------------
A
[[3. 1. 0. 0. 0. 0. 0.]
 [1. 3. 1. 0. 0. 0. 0.]
 [0. 1. 3. 1. 0. 0. 0.]
 [0. 0. 1. 3. 1. 0. 0.]
 [0. 0. 0. 1. 3. 1. 0.]
 [0. 0. 0. 0. 1. 3. 1.]
 [0. 0. 0. 0. 0. 1. 3.]]
b
[ 7.  8.  9. 10. 11. 12. 13.]

-------  TRIANGULAR SYSTEM 1 -------
At
[[3.   1.   0.   0.   0.   0.   0.  ]
 [0.   2.67 1.   0.   0.   0.   0.  ]
 [0.   0.   2.62 1.   0.   0.   0.  ]
 [0.   0.   0.   2.62 1.   0.   0.  ]
 [0.   0.   0.   0.   2.62 1.   0.  ]
 [0.   0.   0.   0.   0.   2.62 1.  ]
 [0.   0.   0.   0.   0.   0.   2.62]]
bt
[7.   5.67 6.88 7.38 8.18 8.88 9.61]

-------------  DATA 2  -------------
A
[[0.55 0.05 0.   0.   0.   0.   0.   0.  ]
 [0.05 0.71 0.44 0.   0.   0.   0.   0.  ]
 [0.   0.44 0.29 0.03 0.   0.   0.   0.  ]
 [0.   0.   0.03 0.51 0.46 0.   0.   0.  ]
 [0.   0.   0.   0.46 0.89 0.65 0.   0.  ]
 [0.   0.   0.   0.   0.65 0.9  0.28 0.  ]
 [0.   0.   0.   0.   0.   0.28 0.13 0.68]
 [0.   0.   0.   0.   0.   0.   0.68 0.21]]
b
[0.59 0.02 0.56 0.26 0.42 0.28 0.69 0.44]

-------  TRIANGULAR SYSTEM 2 -------
At
[[ 0.55  0.05  0.    0.    0.    0.    0.    0.  ]
 [ 0.    0.7   0.44  0.    0.    0.    0.    0.  ]
 [ 0.    0.    0.01  0.03  0.    0.    0.    0.  ]
 [ 0.    0.    0.    0.45  0.46  0.    0.    0.  ]
 [ 0.    0.    0.    0.    0.43  0.65  0.    0.  ]
 [ 0.    0.    0.    0.    0.   -0.09  0.28  0.  ]
 [ 0.    0.    0.    0.    0.    0.    1.03  0.68]
 [ 0.    0.    0.    0.    0.    0.    0.   -0.24]]
bt
[ 0.59 -0.03  0.58 -0.92  1.35 -1.76 -5.01  3.74]

Back-substitution

After the triangularization, we have the equivalent system $A^*x=b^*$

$$ \begin{array}{rrrrrrrrrrrr} a_{11}^* x_1 & +a_{12}^* x_2& & & & & &=& b^*_1 \\ & a_{22}^* x_2& +a_{23}^*x_3 & & & & &=&b^*_2\\ & & a_{33}^*x_3 & +a_{34}^*x_4 & & & &=&b^*_3\\ & & & a_{44}^*x_4 & +a^*_{45}x_5 & & &=&b^*_4\\ & & & & a^*_{55}x_5& +a^*_{56}x_6& &=&b^*_5\\ & & & & & a^*_{66}x_6&+a^*_{67}x_7&=&b^*_6\\ & & & & & & a^*_{77}x_7&=&b^*_7 \end{array} $$

We solve the equations from the bottom to the top

$$ \begin{matrix} \mathrm{If}\;k=7\quad & & x_7 \leftarrow \dfrac{b_7^*}{a_{77}^*}\\[0.3cm] \mathrm{If}\;k=6\quad & & x_6 \leftarrow \dfrac{b_6^*-a_{67}^*x_7}{a_{66}^*}\\[0.3cm] \mathrm{If}\;k=5\quad & & x_5 \leftarrow \dfrac{b_5^*-a_{56}^*x_6}{a_{55}^*}\\[0.3cm] \mathrm{If}\;k=4\quad & & x_4 \leftarrow \dfrac{b_4^*-a_{45}^*x_5}{a_{44}^*}\\[0.3cm] \mathrm{If}\;k=3\quad & & x_3 \leftarrow \dfrac{b_3^*-a_{34}^*x_4}{a_{33}^*}\\[0.3cm] \mathrm{If}\;k=2\quad & & x_2 \leftarrow \dfrac{b_2^*-a_{23}^*x_3}{a_{22}^*}\\[0.3cm] \mathrm{If}\;k=1\quad & & x_1 \leftarrow \dfrac{b_1^*-a_{12}^*x_2}{a_{11}^*}\\[0.3cm] \end{matrix} $$

Exercise 2

Write a function x = back_subs(At,bt) that solves a triangular system with back-substitution. Use one and only one for loop. Use it with the function triangular to solve the linear systems of the previous exercise.

Hint:

  • You can use a loop with a negative step. For instance
    for k in range(n1,n2,-1):
    
    where n1 is the initial value, n2 the next value to the final one and -1 the step.
In [2]:
%run Exercise2.py
-------------  SOLUTION SYSTEM 1  -------------
x
[1.86 1.42 1.89 1.91 2.37 1.99 3.67]

-------------  SOLUTION SYSTEM 2  -------------
x
[ -3.    43.59 -69.63  53.46 -54.66  38.2    5.47 -15.72]

Storing the coefficients in a rectangular matrix of three columns

Let us store now the coefficients of the coefficient matrix in a rectangular matrix. The linear system was

$$ \begin{array}{rrrrrrrrrrrr} a_{11} x_1 & +a_{12} x_2& & & & & &=& b_1 \\ a_{21} x_1 & +a_{22} x_2& +a_{23}x_3 & & & & &=&b_2\\ & a_{32}x_2 &+ a_{33}x_3 & +a_{34}x_4 & & & &=&b_3\\ & & a_{43}x_3 & +a_{44}x_4 & +a_{45}x_5 & & &=&b_4\\ & & & a_{54}x_4 & +a_{55}x_5& +a_{56}x_6& &=&b_5\\ & & & & a_{65}x_5& +a_{66}x_6&+a_{67}x_7&=&b_6\\ & & & & & a_{76}x_6&+a_{77}x_7&=&b_7 \end{array} $$

If we store the coefficients in a rectangular matrix

$$ A_r = \left( \begin{matrix} 0 & a_{11} & a_{12}\\ a_{21} & a_{22} & a_{23}\\ a_{32} & a_{33} & a_{34}\\ a_{43} & a_{44} & a_{45}\\ a_{54} & a_{55} & a_{56}\\ a_{65} & a_{66} & a_{67}\\ a_{76} & a_{77} & 0 \end{matrix} \right)\quad = \quad \left( \begin{matrix} 0 & r_{12} & r_{13}\\ r_{21} & r_{22} & r_{23}\\ r_{31} & r_{32} & r_{33}\\ r_{41} & r_{42} & r_{43}\\ r_{51} & r_{52} & r_{53}\\ r_{61} & r_{62} & r_{63}\\ r_{71} & r_{72} & 0 \end{matrix} \right) $$
$$ \begin{matrix} \mathrm{If}\;k=1\quad f \leftarrow \dfrac{a_{21}}{a_{11}}\;\mathrm{then}\;& & a_{21}\leftarrow a_{21}-f\, a_{11}=0 & & a_{22}\leftarrow a_{22} -f\,a_{12} & & b_2\leftarrow b_2 -f\,b_1\\[0.3cm] \mathrm{If}\;k=2\quad f \leftarrow \dfrac{a_{32}}{a_{22}}\;\mathrm{then}\;& & a_{32}\leftarrow a_{32}-f\, a_{22}=0 & & a_{33}\leftarrow a_{33} -f\,a_{23} & & b_3\leftarrow b_3 -f\,b_2\\[0.3cm] \mathrm{If}\;k=3\quad f \leftarrow \dfrac{a_{43}}{a_{33}}\;\mathrm{then}\;& & a_{43}\leftarrow a_{43}-f\, a_{33}=0 & & a_{44}\leftarrow a_{44} -f\,a_{34} & & b_4\leftarrow b_4 -f\,b_3\\[0.3cm] \mathrm{If}\;k=4\quad f \leftarrow \dfrac{a_{54}}{a_{44}}\;\mathrm{then}\;& & a_{54}\leftarrow a_{54}-f\, a_{44}=0 & & a_{55}\leftarrow a_{55} -f\,a_{45} & & b_5\leftarrow b_5 -f\,b_4\\[0.3cm] \mathrm{If}\;k=5\quad f \leftarrow \dfrac{a_{65}}{a_{55}}\;\mathrm{then}\;& & a_{65}\leftarrow a_{65}-f\, a_{55}=0 & & a_{66}\leftarrow a_{66} -f\,a_{56} & & b_6\leftarrow b_6 -f\,b_5\\[0.3cm] \mathrm{If}\;k=6\quad f \leftarrow \dfrac{a_{76}}{a_{66}}\;\mathrm{then}\;& & a_{76}\leftarrow a_{76}-f\, a_{66}=0 & & a_{77}\leftarrow a_{77} -f\,a_{67} & & b_7\leftarrow b_7 -f\,b_6 \end{matrix} $$

Now, taking into account the new storing

$$ \begin{matrix} \mathrm{Si}\;k=1\quad f \leftarrow \dfrac{r_{21}}{r_{12}}\;\mathrm{then}\;& & r_{21}\leftarrow r_{21}-f\, r_{12}=0 & & r_{22}\leftarrow r_{22} -f\,r_{13} & & b_2\leftarrow b_2 -f\,b_1\\[0.3cm] \mathrm{Si}\;k=2\quad f \leftarrow \dfrac{r_{31}}{r_{22}}\;\mathrm{then}\;& & r_{31}\leftarrow r_{31}-f\, r_{22}=0 & & r_{32}\leftarrow r_{32} -f\,r_{23} & & b_3\leftarrow b_3 -f\,b_2\\[0.3cm] \mathrm{Si}\;k=3\quad f \leftarrow \dfrac{r_{41}}{r_{32}}\;\mathrm{then}\;& & r_{41}\leftarrow r_{41}-f\, r_{32}=0 & & r_{42}\leftarrow r_{42} -f\,r_{33} & & b_4\leftarrow b_4 -f\,b_3\\[0.3cm] \mathrm{Si}\;k=4\quad f \leftarrow \dfrac{r_{51}}{r_{42}}\;\mathrm{then}\;& & r_{51}\leftarrow r_{51}-f\, r_{42}=0 & & r_{52}\leftarrow r_{52} -f\,r_{43} & & b_5\leftarrow b_5 -f\,b_4\\[0.3cm] \mathrm{Si}\;k=5\quad f \leftarrow \dfrac{r_{61}}{r_{52}}\;\mathrm{then}\;& & r_{61}\leftarrow r_{61}-f\, r_{52}=0 & & r_{62}\leftarrow r_{62} -f\,r_{53} & & b_6\leftarrow b_6 -f\,b_5\\[0.3cm] \mathrm{Si}\;k=6\quad f \leftarrow \dfrac{r_{71}}{r_{62}}\;\mathrm{then}\;& & r_{71}\leftarrow r_{71}-f\, r_{62}=0 & & r_{72}\leftarrow r_{72} -f\,r_{63} & & b_7\leftarrow b_7 -f\,b_6 \end{matrix} $$

And now, the triangular matrix is stored

$$ \left( \begin{matrix} 0 & r_{12}^* & r_{13}^*\\ 0 & r_{22}^* & r_{23}^*\\ 0 & r_{32}^* & r_{33}^*\\ 0 & r_{42}^* & r_{43}^*\\ 0 & r_{52}^* & r_{53}^*\\ 0 & r_{62}^* & r_{63}^*\\ 0 & r_{72}^* & 0 \end{matrix} \right) $$

And the back-substitution will be

$$ \begin{matrix} \mathrm{Si}\;k=7\quad & & x_7 \leftarrow \dfrac{b_7^*}{r_{72}^*}\\[0.3cm] \mathrm{Si}\;k=6\quad & & x_6 \leftarrow \dfrac{b_6^*-r_{63}^*x_7}{r_{62}^*}\\[0.3cm] \mathrm{Si}\;k=5\quad & & x_5 \leftarrow \dfrac{b_5^*-r_{53}^*x_6}{r_{52}^*}\\[0.3cm] \mathrm{Si}\;k=4\quad & & x_4 \leftarrow \dfrac{b_4^*-r_{43}^*x_5}{r_{42}^*}\\[0.3cm] \mathrm{Si}\;k=3\quad & & x_3 \leftarrow \dfrac{b_3^*-r_{33}^*x_4}{r_{32}^*}\\[0.3cm] \mathrm{Si}\;k=2\quad & & x_2 \leftarrow \dfrac{b_2^*-r_{23}^*x_3}{r_{22}^*}\\[0.3cm] \mathrm{Si}\;k=1\quad & & x_1 \leftarrow \dfrac{b_1^*-r_{13}^*x_2}{r_{12}^*}\\[0.3cm] \end{matrix} $$

Exercise 3

Linear systems with tridiagonal matrices are quite common in physics problems (heat equation, wave equation). Thus, common algorithms to solve linear systems are adapted so these kind of probles can be solved more efficiently. For example, we can store the diagonals of the $A$ in a rectangular matrix $A_r$ with only three columns. We can store the square matrix of Exercise 1 in a rectangular matrix

n = 7 

Ar = np.zeros((n,3))
Ar[:,0] = np.concatenate((np.array([0]),np.ones((n-1),)))
Ar[:,1] = np.ones((n),)*3
Ar[:,2] = np.concatenate((np.ones((n-1),),np.array([0])))

b = np.arange(n,2*n)*1.

Modify the code from the two previous exercises son we can use $A_r$ instead of $A.$

  • Without modifying the functions, apply them also to the data
n = 8

np.random.seed(3)
Ar = np.zeros((n,3))
Ar[:,1] = np.random.rand(n)
Ar[:,0] = np.concatenate((np.array([0]),np.random.rand(n-1)))
Ar[0:n-1,2] = Ar[1:n,0]

b = np.random.rand(n)
In [4]:
%run Exercise3.py
-------------  DATA 1 -------------
Ar
[[0. 3. 1.]
 [1. 3. 1.]
 [1. 3. 1.]
 [1. 3. 1.]
 [1. 3. 1.]
 [1. 3. 1.]
 [1. 3. 0.]]
b
[ 7.  8.  9. 10. 11. 12. 13.]

-------  TRIANGULAR SYSTEM 1 -------
At
[[0.   3.   1.  ]
 [0.   2.67 1.  ]
 [0.   2.62 1.  ]
 [0.   2.62 1.  ]
 [0.   2.62 1.  ]
 [0.   2.62 1.  ]
 [0.   2.62 0.  ]]
bt
[7.   5.67 6.88 7.38 8.18 8.88 9.61]

-----------  SOLUTION 1 -----------
x
[1.86 1.42 1.89 1.91 2.37 1.99 3.67]



-------------  DATA 2 -------------
Ar
[[0.   0.55 0.05]
 [0.05 0.71 0.44]
 [0.44 0.29 0.03]
 [0.03 0.51 0.46]
 [0.46 0.89 0.65]
 [0.65 0.9  0.28]
 [0.28 0.13 0.68]
 [0.68 0.21 0.  ]]
b
[0.59 0.02 0.56 0.26 0.42 0.28 0.69 0.44]

-------  TRIANGULAR SYSTEM 2 -------
At
[[ 0.    0.55  0.05]
 [ 0.    0.7   0.44]
 [ 0.    0.01  0.03]
 [ 0.    0.45  0.46]
 [ 0.    0.43  0.65]
 [ 0.   -0.09  0.28]
 [ 0.    1.03  0.68]
 [ 0.   -0.24  0.  ]]
bt
[ 0.59 -0.03  0.58 -0.92  1.35 -1.76 -5.01  3.74]

-----------  SOLUTION 2 -----------
x
[ -3.    43.59 -69.63  53.46 -54.66  38.2    5.47 -15.72]

Proposed exercises

Matrix product

We use three numpy arrays $A_{m\times n}$, $B_{n\times p}$ and $C_{m\times p}$.

As we are multipliying $A$ by $B$, the number of columns in $A$, $n$, must be the same as the number of rows in $B$. And the result matrix, $C$, must have the same number of rows as $A$ and the same number of columns as $B$.

An element of $C$ is given by

$$ c_{ij} = a_{i1}\,b_{1j}+a_{i2}\,b_{2j}+\dots+a_{in}\,b_{nj} = \sum_{k=1}^{n}a_{ik}\,b_{kj}\quad \mathrm{for} \; i = 1,\ldots,m \quad j = 1,\ldots,p \quad (1) $$

(By File:Matrix multiplication diagram.svg)


Exercise 4

Taking into account the equation (1), write a function C = multiply_3loops(A,B) that multiplies matrices A and B and returns the result C. Use three for loops. One with index i for the row, another with index j for the column and a last index k for the cumulative addition.

  • Use shape to get the dimensions of A and B. For instance, m, n = A.shape
  • Initialize C = np.zeros((m,p)) being m the number of A rows and p the number of B columns.
  • Then, every C element has been initialized to zero and we get its value as a cumulative addition. That is, we start with $c_{ij}=0$, and then $$k= 1\qquad c_{ij}\leftarrow c_{ij}+a_{i1}b_{1j}$$ $$k= 2\qquad c_{ij}\leftarrow c_{ij}+a_{i2}b_{2j}$$ $$\ldots$$ $$k= n\qquad c_{ij}\leftarrow c_{ij}+a_{in}b_{nj}$$
  • Use the function to multiply the matrices
    A = np.array([[-3.,2],[-2,0],[-4,4],[4,-4]])
    B = np.array([[4.,-3,1],[-2,1,1]])
    
  • Without modifying the function, multiply the matrices
    A1 = np.array([[-3.,2],[-2,0],[-4,4],[4,-4],[1,1]])
    B1 = np.array([[4.,-3,1,1],[-2,1,1,1]])
    
In [5]:
%run Exercise4.py
C
 [[-16.  11.  -1.]
 [ -8.   6.  -2.]
 [-24.  16.   0.]
 [ 24. -16.   0.]]

C1
 [[-16.  11.  -1.  -1.]
 [ -8.   6.  -2.  -2.]
 [-24.  16.   0.   0.]
 [ 24. -16.   0.   0.]
 [  2.  -2.   2.   2.]]

Exercise 5

Write a function C = multiply_2loops(A,B) that multiplies matrices A and B using two loops. Multiply the matrices of Exercise 4. We can remove a look if we consider that

  • A[i,:] is a vector of n components that contains the row i from A.
  • B[:,j] is a vector of n components that contains the column j from B.
  • A[i,:]*B[:,j] is the element-wise product of these two vectors and it is also a vector with n components.
  • np.sum(A[i,:]*B[:,j]) is the addition of the elements of this vector.
In [6]:
%run Exercise5.py
C
 [[-16.  11.  -1.]
 [ -8.   6.  -2.]
 [-24.  16.   0.]
 [ 24. -16.   0.]]

C1
 [[-16.  11.  -1.  -1.]
 [ -8.   6.  -2.  -2.]
 [-24.  16.   0.   0.]
 [ 24. -16.   0.   0.]
 [  2.  -2.   2.   2.]]

We can improve the vectorization getting a column (or a row) of the matrix C with only a code line. That way, we can remove one more loop.

  • B[:,j] is a vector of n components that contains the column j from B.
  • In D = A*B[:,j] every row of D is the element-wise product of each row of A with the vector B[:,j]. Thus, D is the same size as A.
  • C[:,j] = np.sum(D,axis=1) is a column matrix where each element is the addition of the elements of a row of D and it is the matrix product of matrix A by column matrix B[:,j].
In [7]:
A = np.array([[-3.,2],[-2,0],[-4,4],[4,-4]])
B = np.array([[4.,-3,1],[-2,1,1]])

m = A.shape[0]
p = B.shape[1]

C = np.zeros((m,p))

for j in range(p):
    C[:,j] = np.sum(A*B[:,j],axis=1)

print(C)
[[-16.  11.  -1.]
 [ -8.   6.  -2.]
 [-24.  16.   0.]
 [ 24. -16.   0.]]

Exercise 6

Write a function C = multiply_1loop(A,B) that multiplies matrices A and B using a loop. Multiply the matrices of Exercise 4.

In [8]:
%run Exercise6.py
C
 [[-16.  11.  -1.]
 [ -8.   6.  -2.]
 [-24.  16.   0.]
 [ 24. -16.   0.]]

C1
 [[-16.  11.  -1.  -1.]
 [ -8.   6.  -2.  -2.]
 [-24.  16.   0.   0.]
 [ 24. -16.   0.   0.]
 [  2.  -2.   2.   2.]]

Exercise 7

Compare the execution time for the three functions that multiply matrices and the numpy command np.dot(A,B) and A@B. Use matrices $A_{m\times n}$ and $B_{n\times p}$ with random elements. Use the command time.time() from the previous lab. np.random.rand(m,n) is a $m\times n$ matrix with random elements from $[0,1]$.

m = 300; n = 200; p = 400
A = np.random.rand(m,n)
B = np.random.rand(n,p)
In [11]:
%run Exercise7.py
Execution time with 3 loops
6.2343010902404785
Execution time with 2 loops
0.34038805961608887
Execution time with 1 loop
0.02167987823486328
Execution time with dot command
0.00031447410583496094
Execution time with @
0.0001678466796875

Exercise 8

If we multiply a column matrix $A_{m\times 1}$ by a row matrix $B_{1 \times n}$ the result is a matrix $C_{m\times n}$. For instance

$$ AB = \left( \begin{array}{c} a_1\\ a_2\\ a_3\\ a_4 \end{array} \right) \left( \begin{array}{ccc} b_1 & b_2 & b_3 \end{array} \right) = \left( \begin{array}{ccc} a_1b_1 & a_1b_2 & a_1b_3 \\ a_2b_1 & a_2b_2 & a_2b_3 \\ a_3b_1 & a_3b_2 & a_3b_3 \\ a_4b_1 & a_4b_2 & a_4b_3 \end{array} \right) = \left( \begin{array}{ccc} c_{11} & c_{12} & c_{13} \\ c_{21} & c_{22} & c_{23} \\ c_{31} & c_{32} & c_{33} \\ c_{41} & c_{42} & c_{43} \end{array} \right) = C $$

Using two for loops, write a function C = multiply_col_row(a,b) that multiplies a column matrix by a row matrix a return the product matrix. The parameters a and b are given as 1D numpy arrays, while C will be a 2D numpy array.

Multipliy

a1 = np.array([1,3,4])
b1 = np.array([2,1])

And also

a2 = np.array([1,3])
b2 = np.array([2,1,0,1])
In [1]:
%run Exercise8
C1
 [[2. 1.]
 [6. 3.]
 [8. 4.]]

C2
 [[2. 1. 0. 1.]
 [6. 3. 0. 3.]]

Exercise 9

Given the matrices $A_{m\times n}$ and $B_{n\times p}$ where $A_1$, $A_2$, $\ldots$, $A_n$ represent the columns of the matrix $A$ and $B_1$, $B_2$, $\ldots$, $B_n$ the rows of matrix $B$, we can obtain the product of these two matrices

$$ AB = \left( \begin{array}{cccc} A_1 & A_2 & \ldots & A_n \end{array} \right) \left( \begin{array}{c} B_1\\ B_2\\ \dots\\ B_n \end{array} \right) = A_1B_1+A_2B_2+\dots+A_nB_n=C_1+C_2+\dots+C_n=C $$

Where $C_1$, $C_2$, $\ldots$, $C_n$ are $m\times p$ matrices

Using a for loop, write a function C = multiply(A,B) that multiplies matrices A and B and returns matrix C. using this algorithm. To multiply column matrix by row matrix use the function Ci = multiply_col_row(A,B)

Multiply

A1 = np.array([[-3,2],[-2,0],[-4,4],[4,-4]])
B1 = np.array([[4,-3,1],[-2,1,1]])

And also

A2 = np.array([[-3,2],[-2,0],[-4,4],[4,-4],[1,1]])
B2 = np.array([[4,-3,1,1],[-2,1,1,1]])
In [4]:
%run Exercise9
C1
 [[-16.  11.  -1.]
 [ -8.   6.  -2.]
 [-24.  16.   0.]
 [ 24. -16.   0.]]

C2
 [[-16.  11.  -1.  -1.]
 [ -8.   6.  -2.  -2.]
 [-24.  16.   0.   0.]
 [ 24. -16.   0.   0.]
 [  2.  -2.   2.   2.]]