Basic matrix operations

12. Basic matrix operations#

There are many matrix operations in Python (NumPy) that make finding solutions to systems equations much easier than doing so by hand. In order to use these operations, the matrices and vectors must first be stored in memory. Consider the following matrices and vectors:

\[\begin{split} A = \begin{bmatrix} 3 & 5 & 2 \\ 0 & 1 & 2 \\ 3 & 6 & 1 \end{bmatrix}, \qquad B = \begin{bmatrix} 2 & 5 & 4 \\ 4 & 6 & 3 \\ 4 & 10 & 8 \end{bmatrix}, \qquad C = \begin{bmatrix} 1 & 3 \\ 3 & 2 \\ 4 & 5 \end{bmatrix}, \qquad \vec{x} = \begin{bmatrix} 3 \\ 2 \\ 1 \end{bmatrix} \end{split}\]

Enter the matrices into Python and perform the following operations:
(a) \(AB\)
(b) \(AC\)
(c) \(CA\)
(d) \(B\vec{x}\)
(e) \(B\vec{x}^T\)

# import packages
import numpy as np

# define matrices and vectors
A = np.array([ [3, 5, 2], [0, 1, 2], [3, 6, 1] ])
B = np.array([ [2, 5, 4], [4, 6, 3], [4, 10, 8] ])
C = np.array([ [1, 3], [3, 2], [4, 5] ])
x = np.array([ [3], [2], [1] ])

We can print out these variables to see if we did it correctly.

print(A)
print()
print(x, x.shape)
[[3 5 2]
 [0 1 2]
 [3 6 1]]

[[3]
 [2]
 [1]] (3, 1)

If it all looks good, we can proceed with the computation. In NumPy, matrix multiplication (successive row-column products) is performed with the @ operator. If you use the * operator, it might still work, but it will likely be wrong, as this will perform element-wise multiplication.

# part a: AB
print("AB:")
print(A @ B)

# part b: AC
print("\nAC:")
print(A @ C)

# part c: CA
print("\nCA:")
print(C @ A)
AB:
[[34 65 43]
 [12 26 19]
 [34 61 38]]

AC:
[[26 29]
 [11 12]
 [25 26]]

CA:
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[3], line 11
      9 # part c: CA
     10 print("\nCA:")
---> 11 print(C @ A)

ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 3 is different from 2)

As expected, \(CA\) gave an error because the inner dimensions do not match. In this next cell, notice how we take the transpose of \(\vec{x}\) by appending a .T to the array.

# part d: Bx
print("\nBx:")
print(B @ x)

# part e: Bx^T
print("\nBx^T:")
print(B @ x.T)
Bx:
[[20]
 [27]
 [40]]

Bx^T:
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[4], line 7
      5 # part e: Bx^T
      6 print("\nBx^T:")
----> 7 print(B @ x.T)

ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 1 is different from 3)

Reduced row echelon form#

Next we will reduce \(A\) to reduced row echelon form using the rref() method from the SymPy library. Unfortunately there’s no built-in method in NumPy, but the SymPy library has its own advantages (look at that \(\LaTeX\)-ified output!).

from sympy import Matrix

A2 = Matrix(A)
display(A2)

A2.rref()[0]
\[\begin{split}\displaystyle \left[\begin{matrix}3 & 5 & 2\\0 & 1 & 2\\3 & 6 & 1\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0 & 0\\0 & 1 & 0\\0 & 0 & 1\end{matrix}\right]\end{split}\]

We will also reduce the augmented matrix \([A\ |\ \vec{x}]\). To create this, we will use the concatenate() function from NumPy, where the first argument is the list of matrices/vectors we want to augment, and the axis parameter is the dimension to concatenate along (0 refers to rows = vertical stacking, 1 refers to columns = horizontal stacking).

Ax = Matrix(np.concatenate([A, x], axis=1))
display(Ax)

Ax.rref()[0]
\[\begin{split}\displaystyle \left[\begin{matrix}3 & 5 & 2 & 3\\0 & 1 & 2 & 2\\3 & 6 & 1 & 1\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0 & 0 & \frac{11}{9}\\0 & 1 & 0 & - \frac{2}{3}\\0 & 0 & 1 & \frac{4}{3}\end{matrix}\right]\end{split}\]