1. System of linear equations
Where there is a system of linear equations (i.e. no terms like etc), the equations can be represented as matrix-vector multiplication.
- To solve , means to find the vector . To do this, we must find the inverse of . (This is not always possible).
Geometric intuition
There are two ways to interpret the above system of equations, and find the solution, :
- Vector interpretation - Find the values of that correctly scale the columns of matrix to produce the vector
- Matrix linearly transforms the vector space in such a way that the vector gets transformed into .
- i.e. find the linear combination of the columns of that equals :
- Planar interpretation - Find the intersection of three planes. Each equation represents a plane in .
- The solution to the system of equations is the set of points that lie on all three planes.
- If the solution is unique, then the planes intersect at a single point.
- Otherwise, the planes are parallel (no solution) or coincide as a line or plane (infinitely many solutions).
2. Matrix inverse
2.1. Is it invertible?
- All non-square matrices are not invertible (they have no determinant). Some square matrices are also not invertible:
- Singular matrices are those which have no inverses (like how 0 has no inverse)
- : No inverse, because the matrix collapses the vector space (dimensionality is lost)
- Non-singular matrices are those which have an inverse
- : Inverse exists, because the matrix preserves the vector space (dimensionality is maintained)
- The inverse of a matrix is unique: An invertible matrix only has one inverse.
2.1.1. Caveat:
- Solutions may exist even when the matrix is singular (i.e. ).
- In this case, the system of equations is underdetermined (i.e. there are more unknowns than equations).
- Geometrically, despite a singular matrix collapsing the vector space, the following solutions may still exist:
- If a 2D vector space is collapsed into a 1D line, the solution space is a 1D line
- or if a 3D vector space is collapsed into a 2D plane (or 1D line), the solution space is a 2D plane (or a 1D line)
2.2. Finding the inverse
- Only square, non-singular matrices () are invertible.
- is invertible if there exists a matrix such that .
- akin to how how
- Geometric intuition: If you apply and then apply its inverse , you get back to the original vector. In other words, you did “nothing” to the vector, hence the identity matrix .
- For a matrix, the inverse is:
- Complicated: Where is the adjugate matrix of a matrix (i.e. the transpose of the cofactor matrix of ). See link for computation details: Adjugate matrix
2.3. Using the inverse to solve a system of linear equations
Using the earlier example:
We can solve for by multiplying both sides by the inverse of :
Finding the inverse of :
2.3.1. Notes on computational complexity and numerical stability
- Computational complexity and memory-intensive: Inverting a matrix is computationally expensive. Inverting large matrices may cause memory issues. Use the inverse of a matrix only when necessary.
- Numerical stability: Inverting a matrix can lead to numerical instability. Use other methods (e.g. LU decomposition, iterative methods) for solving systems of linear equations.
import numpy as np
from numpy.linalg import det, inv
A = np.array([[2, 5, 3], [4, 0, 8], [1, 3, 0]])
b = np.array([[-3], [0], [2]])
# b = np.array([-3, 0, 2]) # This is also valid because python is smart and can infer the required array shape
print("Problem statement: Ax = b. Find x, given:\nA =\n", A, "\nb =\n", b)
# Calculate the determinant of A
print(f"\ndet(A) = {det(A):.1f}") # A has non-zero determinant so is invertible
# Find the inverse of A (round to 2 dp)
print("\nInv(A): A^-1 =\n", np.round(inv(A), 3))
# Demonstrate that A^-1 * A = I, and also A * A^-1 = I (round to 2 dp)
print("\nDemonstrate that A^-1 is in fact the inverse of A:")
print("A^-1 * A =\n", np.round(inv(A) @ A, 10))
print("\n(And also):")
print("A * A^-1 =\n", np.round(A @ inv(A), 10))
# Find x in Ax = b
x = inv(A) @ b
print("\nRecall, problem statement: Ax = b. Therefore:")
print("x = A^-1 * b =\n", np.round(x, 3))
# Check the solution
print("\nCheck the solution: Ax =\n", A @ x)
Problem statement: Ax = b. Find x, given:
A =
[[2 5 3]
[4 0 8]
[1 3 0]]
b =
[[-3]
[ 0]
[ 2]]
det(A) = 28.0
Inv(A): A^-1 =
[[-0.857 0.321 1.429]
[ 0.286 -0.107 -0.143]
[ 0.429 -0.036 -0.714]]
Demonstrate that A^-1 is in fact the inverse of A:
A^-1 * A =
[[ 1. -0. 0.]
[ 0. 1. 0.]
[ 0. 0. 1.]]
(And also):
A * A^-1 =
[[ 1. 0. -0.]
[ 0. 1. 0.]
[-0. 0. 1.]]
Recall, problem statement: Ax = b. Therefore:
x = A^-1 * b =
[[ 5.429]
[-1.143]
[-2.714]]
Check the solution: Ax =
[[-3.]
[ 0.]
[ 2.]]# Demonstrate what happens when square matrix A is singular (non-invertible, i.e. determinant is zero)
P = np.array([[0, 1, 0], [0, 0, 0], [1, 0, 1]])
print("det(p):", det(P))
print("Inv P:\n", inv(P)) # <-- LinAlgError thrown because P is Singular (not invertible)
det(p): 0.0---------------------------------------------------------------------------
LinAlgError Traceback (most recent call last)
Cell In[2], line 4
2 P = np.array([[0, 1, 0], [0, 0, 0], [1, 0, 1]])
3 print("det(p):", det(P))
----> 4 print("Inv P:\n", inv(P)) # <-- LinAlgError thrown because P is Singular (not invertible)
File ~/Code/notes/.venv/lib/python3.13/site-packages/numpy/linalg/_linalg.py:609, in inv(a)
606 signature = 'D->D' if isComplexType(t) else 'd->d'
607 with errstate(call=_raise_linalgerror_singular, invalid='call',
608 over='ignore', divide='ignore', under='ignore'):
--> 609 ainv = _umath_linalg.inv(a, signature=signature)
610 return wrap(ainv.astype(result_t, copy=False))
File ~/Code/notes/.venv/lib/python3.13/site-packages/numpy/linalg/_linalg.py:104, in _raise_linalgerror_singular(err, flag)
103 def _raise_linalgerror_singular(err, flag):
--> 104 raise LinAlgError("Singular matrix")
LinAlgError: Singular matrix2.4. Ill-Conditioned Matrices
- An ill-conditioned matrix is one which is close to being singular
- Its determinant will be close to 0 (problematic in the same way dividing by a tiny number is)
- Computation errors (overflow, underflow, round-off errors) may occur
- Condition number: Higher number means the matrix is more ill-conditioned (i.e. closer to being singular)
# Compute the condition numbers, determinants, and matrix inverses for the following 3 matrices A_1, A_2, A_3
from numpy.linalg import cond
A_1 = np.array([[1, 1], [2, 3]])
A_2 = np.array([[4.1, 2.8], [9.7, 6.6]])
A_3 = np.array([[4.1, 2.8], [9.6760, 6.6080]])
print("\nMatrix A_1:")
print("Condition number(A_1):", cond(A_1))
print("det(A_1):", det(A_1))
print("Inv A_1:\n", inv(A_1))
print("\nMatrix A_2:")
print("Condition number(A_2):", cond(A_2))
print("det(A_2):", det(A_2))
print("Inv A_2:\n", inv(A_2))
print("\nMatrix A_3 -- This matrix is close to singular or badly scaled. It is ill-conditioned:")
print("Condition number(A_3):", cond(A_3)) # Note that the condition number is very high
print("det(A_3):", det(A_3)) # Note that the determinant is very close to zero
print("Inv A_3:\n", inv(A_3)) # Note that the inverse is very large
Matrix A_1:
Condition number(A_1): 14.933034373659225
det(A_1): 1.0
Inv A_1:
[[ 3. -1.]
[-2. 1.]]
Matrix A_2:
Condition number(A_2): 1622.9993838565622
det(A_2): -0.0999999999999999
Inv A_2:
[[-66. 28.]
[ 97. -41.]]
Matrix A_3 -- This matrix is close to singular or badly scaled. It is ill-conditioned:
Condition number(A_3): 1.1149221731402912e+16
det(A_3): -4.461167435465537e-15
Inv A_3:
[[-1.53781451e+15 6.51616316e+14]
[ 2.25179981e+15 -9.54152463e+14]]