198812 Session 5: Matrix algebra (cont.)
Math to code mapping.
The purpose of the document it to show how mathematical conpects learned in the Computer Programming Prerequisites course could be implemented in a programming language. This supplementary material should help you to transfer theoretical concepts to programming. If you have programming skills you may use the material during the course. If not, just come back to it, once you are advanced enough in programming.
- This document is basesd on the lecture notes by Sébastien Court. It structured in the same way as the lecture notes. It consists only of sections, where numeric examples were given.
- It provides code in Python and R for examples used there. The code serves only demonstration purposes to show a possible implementation. We assumed the correctness of input data.
- For multiple examples of the same calculation, only one is taken. You can easily adapt code for missing calculations.
For this session, code in basic Python, Python with NumPy package and in R is given. Vector and matrix algebra is relatively easy to implement using numpy
(some courses from module 3) or R (198803). In most of the cases just by calling dedicated functions. In basic Python (198801), the required functionality in many cases must be implemented from scratch.
1 Symmetric matrices
1.1 Transposition
\[ \begin{eqnarray*} A = \left(\begin{matrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{matrix} \right) \quad \Rightarrow \quad A^T = \left(\begin{matrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{matrix} \right) \\ \end{eqnarray*} \]
This time we will define a function, as we will need this code later to check symmetry of a matrix.
def transpose(a):
= len(a)
nrow = len(a[0])
ncol
= []
result for j in range(ncol):
= [0] * nrow
row
result.append(row[:])
for i in range(nrow):
for j in range(ncol):
= a[i][j]
result[j][i] return result
Let us check how it works for our example.
a
[[1, 2, 3], [4, 5, 6]]
transpose(a)
[[1, 4], [2, 5], [3, 6]]
A
array([[1, 2, 3],
[4, 5, 6]])
A.T
array([[1, 4],
[2, 5],
[3, 6]])
a
[,1] [,2] [,3]
[1,] 1 2 3
[2,] 4 5 6
t(a)
[,1] [,2]
[1,] 1 4
[2,] 2 5
[3,] 3 6
1.2 Symmetric matrices
\[ \begin{eqnarray*} A = A^T = \left(\begin{matrix} 1 & 8 & 7 \\ 8 & 5 & 3 \\ 7 & 3 & 5 \end{matrix} \right) \\ \end{eqnarray*} \]
We will use our transpose()
function.
a
[[1, 8, 7], [8, 5, 3], [7, 3, 5]]
transpose(a)
[[1, 8, 7], [8, 5, 3], [7, 3, 5]]
== transpose(a) a
True
A
array([[1, 8, 7],
[8, 5, 3],
[7, 3, 5]])
A.T
array([[1, 8, 7],
[8, 5, 3],
[7, 3, 5]])
all(A == A.T) numpy.
True
a
[,1] [,2] [,3]
[1,] 1 8 7
[2,] 8 5 3
[3,] 7 3 5
t(a)
[,1] [,2] [,3]
[1,] 1 8 7
[2,] 8 5 3
[3,] 7 3 5
all(a==t(a))
[1] TRUE
1.3 Skew-symmetric matrices
\[ \begin{eqnarray*} A = -A^T = \left(\begin{matrix} 0 & -8 & 7 \\ 8 & 0 & -3 \\ -7 & 3 & 0 \end{matrix} \right) \\ \end{eqnarray*} \]
At first, we will define our negate()
function.
def negate(a):
= len(a)
nrow = len(a[0])
ncol = []
result for i in range(nrow):
= []
row for j in range(ncol):
-a[i][j])
row.append(
result.append(row)return result
Let us check how our transpose()
and negate()
functions work for our example.
a
[[0, -8, 7], [8, 0, -3], [-7, 3, 0]]
= transpose(a); at at
[[0, 8, -7], [-8, 0, 3], [7, -3, 0]]
= negate(at); atn atn
[[0, -8, 7], [8, 0, -3], [-7, 3, 0]]
== atn a
True
A
array([[ 0, -8, 7],
[ 8, 0, -3],
[-7, 3, 0]])
-A.T
array([[ 0, -8, 7],
[ 8, 0, -3],
[-7, 3, 0]])
all(A == -A.T) numpy.
True
a
[,1] [,2] [,3]
[1,] 0 -8 7
[2,] 8 0 -3
[3,] -7 3 0
-t(a)
[,1] [,2] [,3]
[1,] 0 -8 7
[2,] 8 0 -3
[3,] -7 3 0
all(a==-t(a))
[1] TRUE
2 Diagonalization
2.1 Diagonal matrices
\[ \begin{equation*} D_1 = \left(\begin{matrix} -1 & 0 & 0 \\ 0 & 7 & 0 \\ 0 & 0 & 5\end{matrix}\right) \\ \end{equation*} \]
= [-1, 7, 5]
x
= ncol = len(x)
nrow = []
result = [0] * ncol
row for i in range(nrow):
result.append(row[:]) = x[i]
result[i][i]
result
[[-1, 0, 0], [0, 7, 0], [0, 0, 5]]
-1, 7, 5]) numpy.diag([
array([[-1, 0, 0],
[ 0, 7, 0],
[ 0, 0, 5]])
diag(c(-1, 7, 5))
[,1] [,2] [,3]
[1,] -1 0 0
[2,] 0 7 0
[3,] 0 0 5
2.2 Eigenvalues and eigenvectors
For
\[ \begin{equation*} A = \left( \begin{matrix} -3 & 2 & 4 \\ -2 & 1 & 4 \\ -2 & 2 & 3 \end{matrix} \right), \quad X = \left( \begin{matrix} 1\\1\\1 \end{matrix} \right). \end{equation*} \]
we can verify \(AX = 3X\).
a
[[-3, 2, 4], [-2, 1, 4], [-2, 2, 3]]
x
[1, 1, 1]
a * x =
= len(x)
ncol = len(a)
nrow = [0] * ncol
result_1
for i in range(nrow):
for j in range(ncol):
+= a[i][j] * x[j]
result_1[i] result_1
[3, 3, 3]
3 * x =
= [3*n for n in x]; result_2 result_2
[3, 3, 3]
== result_2 result_1
True
A
array([[-3, 2, 4],
[-2, 1, 4],
[-2, 2, 3]])
X
array([1, 1, 1])
numpy.dot(A, X)
array([3, 3, 3])
3 * X
array([3, 3, 3])
all(numpy.dot(A, X) == 3 * X) numpy.
True
a
[,1] [,2] [,3]
[1,] -3 2 4
[2,] -2 1 4
[3,] -2 2 3
x
[,1]
[1,] 1
[2,] 1
[3,] 1
%*% x a
[,1]
[1,] 3
[2,] 3
[3,] 3
3 * x
[,1]
[1,] 3
[2,] 3
[3,] 3
all(a %*% x == 3 * x)
[1] TRUE
2.3 Matrices that are diagonalizable
\[ \begin{equation*} A = \left(\begin{matrix}1 & 2 \\ 3 & 2 \end{matrix}\right) = PDP^{-1} = \left(\begin{matrix}2 & 1 \\ 3 & -1 \end{matrix}\right) \left(\begin{matrix}4 & 0 \\ 0 & -1 \end{matrix}\right) \left(\begin{matrix}1/5 & 1/5 \\ 3/5 & -2/5 \end{matrix}\right) . \end{equation*} \]
a
[[1, 2], [3, 2]]
p
[[2, 1], [3, -1]]
d
[[4, 0], [0, -1]]
pi
[[0.2, 0.2], [0.6, -0.4]]
We use our multiply()
function from Session 2 with rounding added.
= multiply(multiply(p, d), pi); result result
[[1.0, 2.0], [3.0, 2.0]]
== result a
True
A
array([[1, 2],
[3, 2]])
P
array([[ 2, 1],
[ 3, -1]])
D
array([[ 4, 0],
[ 0, -1]])
= numpy.linalg.inv(P); Pi Pi
array([[ 0.2, 0.2],
[ 0.6, -0.4]])
= numpy.dot(numpy.dot(P, D), Pi)
result result
array([[1., 2.],
[3., 2.]])
As elements of result
are real numbers and elements of A
are whole numbers, we will use the allclose()
function to check if two arrays are element-wise equal within a tolerance.
numpy.allclose(A, result)
True
library(matlib)
a
[,1] [,2]
[1,] 1 2
[2,] 3 2
p
[,1] [,2]
[1,] 2 1
[2,] 3 -1
d
[,1] [,2]
[1,] 4 0
[2,] 0 -1
= inv(p)
pi %*% d %*% pi p
[,1] [,2]
[1,] 1 2
[2,] 3 2
all(a == p %*% d %*% pi)
[1] FALSE
2.4 Orthogonal matrices
For
\[ \begin{eqnarray*} R_1 = \left(\begin{matrix}0.6 & -0.8 \\ 0.8 & 0.6 \end{matrix} \right) \\ \end{eqnarray*} \]
we can verify \(RR^T=I\).
We use our multiply()
and transpose()
functions.
r1
[[0.6, -0.8], [0.8, 0.6]]
multiply(r1, transpose(r1))
[[1.0, 0.0], [0.0, 1.0]]
R1
array([[ 0.6, -0.8],
[ 0.8, 0.6]])
numpy.dot(R1, R1.T)
array([[ 1.00000000e+00, -2.66453526e-17],
[-2.66453526e-17, 1.00000000e+00]])
=15) numpy.around(numpy.dot(R1, R1.T), decimals
array([[ 1., -0.],
[-0., 1.]])
r1
[,1] [,2]
[1,] 0.6 -0.8
[2,] 0.8 0.6
%*% t(r1) r1
[,1] [,2]
[1,] 1 0
[2,] 0 1