Part I - Foundations
1.1 What is a Vector, Really?
In programming, a vector is usually treated as a simple 1D array of numbers. Whereas in linear algebra, a vector is a geometric object in space with both magnitude and direction (or equivalently, a point in space relative to an origin).
To describe that geometric arrow using numbers, we need a coordinate system, which is defined by a set of reference vectors called a basis. The basis vectors define the fundamental directions and step sizes of our grid, while the numbers inside our vector simply tell us how many steps to take along each basis vector.
For example, in the standard 2D Cartesian grid where our basis vectors are and , writing a coordinate vector as is shorthand for:
What happens if we switch to a new coordinate system where each unit step on the axes is twice as long i.e. and ?
The underlying geometric arrow in space does not change in direction or physical length at all. However, its coordinate representation changes to , because 1 step along our new axes covers 2 units of magnitude:
A vector is the underlying geometric object in space. The list of numbers we write down is merely its coordinate representation relative to a chosen basis. Change the basis, and the coordinates change even though the vector itself stays the same.
1.2 Scalars, Vectors, Matrices and Tensors
Before diving into operations, we should know the following terminology:
-
Scalar (): A single real number (like ). It has magnitude but no direction (-dimensional).
-
Vector (): A 1D ordered collection of scalars, written by default as a column vector:
-
Matrix (): A 2D rectangular grid of numbers with rows and columns. An individual entry at row , column is written as or .
Part II - Operations & Their Meaning
We can understand every vector and matrix operation through two complementary lenses: data accounting (algebraic) and movements in space (geometric).
2.1 Vector Addition, Subtraction and Scalar Multiplication
Vector Addition ()
Algebraically, adding two vectors means adding their matching components. Imagine a 2D vector as a receipt tracking two quantities: .
- On your first trip to a fruit stand, you buy 2 apples for $1: .
- On a second trip to a wholesale stall, you buy 6 apples for $2: .
To find your combined haul and total spend, you add apples to apples and dollars to dollars:
Geometrically, if we plot Apples on the horizontal axis and Dollars on the vertical axis, this is the tip-to-tail rule: start at the origin , walk along arrow to , and then place the tail of arrow at the arrowhead of . Drawing a straight line from the origin to the final arrowhead lands us at .
Scalar Multiplication ()
Multiplying a vector by a scalar scales every component proportionally.
Using our first receipt (2 apples for $1), scaling linearly means the rate stays constant:
- If I hand you $3 instead of $1 (), you get (6 apples for $3).
- If I hand you $1.50 (), you get (3 apples for $1.50).
Geometrically, scalar multiplication stretches or shrinks the vector along the exact same straight line passing through the origin:
- stretches the arrow.
- shrinks the arrow.
- flips the arrow's direction by (like returning the apples for a refund).
Vector Subtraction ()
Subtraction is simply adding the opposite vector: .
Geometrically, there are two ways to visualize :
- Flip and add tip-to-tail: Reverse the direction of and attach its tail to the tip of .
- Tip-to-tip displacement: Draw both and starting from the origin. The vector is the arrow starting at the tip of and pointing straight to the tip of (because ).
2.2 Modulus (Length) and the Dot Product
Modulus (Vector Length)
The modulus of a vector, written as or , is the straight-line distance from the origin to the vector's tip. It captures pure magnitude and ignores direction.
By the Pythagorean theorem in dimensions:
Because each component is squared, negative signs vanish meaning and have the exact same modulus.
The Dot Product ()
The dot product takes two vectors of the same dimension and collapses them into a single scalar.
Algebraically, we multiply matching coordinates and sum them up:
Analogy: Suppose contains the quantities of apples and oranges in your bag (4 apples, 3 oranges), and contains the unit prices ($2 per apple, $5 per orange). Dotting them together computes your total bill:
Because lengths and are always positive, the sign of the dot product is controlled entirely by :
- ( is less than ): They point in generally the same direction.
- (): The vectors are orthogonal (perpendicular).
- ( is greater than ): They point in opposing directions.
2.3 Scalar and Vector Projections
Imagine shining a flashlight directly perpendicular to vector . Vector will cast a shadow onto the line of . Finding that shadow is called projection.
1. Scalar Projection (Length of the Shadow)
From basic right-triangle trigonometry, if is the angle between and , the signed length of the shadow of along is:
Since we know , we can substitute to eliminate the angle completely:
2. Vector Projection (The Shadow as an Arrow)
The scalar projection gives us a number (how long the shadow is). To turn that length into an actual vector pointing in the direction of , we multiply the scalar length by the unit vector in the direction of (which is ):
Notice that the fraction is just a single scalar that scales vector .
2.4 Matrix Multiplication, Transpose, Identity and Inverses
Matrix Multiplication
Earlier, we dotted a quantity vector with a price vector to get a single bill.
Now suppose two people go shopping for apples and oranges, and they want to compare their total bills across two different stores (Store 1 and Store 2):
- Quantities Matrix (): Each row is a person's shopping list .
- Prices Matrix (): Each column is a store's price list .
To find every person's bill at every store, we compute by taking the dot product of each row of with each column of :
To multiply matrix () by matrix (), the inner dimensions must match (), because we are dotting rows of length with columns of length . The output has the outer dimensions (). Furthermore, order matters i.e. .
Transpose ()
The transpose of a matrix , written , flips the matrix over its main diagonal - turning rows into columns and columns into rows: .
- It also connects dot products to matrix multiplication: for column vectors , turning into a row vector lets us write the dot product as .
- Reversal rule: When transposing a product, the order reverses: .
Identity Matrix () and Matrix Inverse ()
- Identity Matrix (): A square matrix with s on the main diagonal and s everywhere else. It acts like the number in multiplication: for any vector , (it leaves space untouched).
- Inverse Matrix (): If a matrix transforms vector into (), the inverse matrix is the "undo button" that takes back to : Not all matrices have an inverse! As we will see in Part IV, if a matrix squashes 2D space flat onto a 1D line, you cannot "un-squash" it uniquely.
Part III - Vector Spaces, Basis and Transformations
3.1 Linear Combinations and Span
Given a set of vectors , any expression where we scale each vector by a scalar and add them together is called a linear combination:
The span of those vectors is the set of all possible vectors you can reach by trying every possible combination of scalars :
- The span of a single non-zero vector in is a straight line through the origin.
- The span of two non-collinear vectors in is a flat 2D plane (a subspace) slicing through the origin.
- The span of three vectors that don't lie on the same plane fills the entire 3D space .
3.2 Linear Independence vs Dependence
Suppose you are in 2D space with two vectors: and . With these two arrows, you can already reach every single point in .
What if someone hands you a third vector ? Does allow you to reach any new territory? No, because you could already build using and :
When one vector in a set can be written as a linear combination of the others, the set is linearly dependent. It contains redundant information.
Formally, a set of vectors is linearly independent if no vector in the set can be built from the others or equivalently, the only way to combine them to get the zero vector is to set every weight to zero:
3.3 Basis, Dimension and Matrices as Transformations
A basis of a vector space is a set of vectors that satisfies two strict conditions simultaneously:
- They span the space (enough vectors to reach everywhere).
- They are linearly independent (no redundant vectors).
The number of vectors in a basis is the dimension of the space.
Matrices as Linear Transformations
Why do we care so much about basis vectors? Because multiplying a matrix by a vector is actually a change of basis / space transformation.
Consider multiplying a matrix by our standard basis vectors and :
The columns of any transformation matrix literally tell you where the original basis vectors and land after the transformation!
Consequently, for any vector , multiplying just takes that exact same recipe ( steps of the first column, plus steps of the second column) using the new landed basis vectors:
Common geometric transformations include:
- Scaling by along :
- Horizontal Shear: ( stays put, tilts to )
- Counterclockwise Rotation by angle :
3.4 Changing Basis ()
Suppose a friend uses a custom coordinate grid with basis vectors and (measured in our standard grid). We pack their basis vectors into the columns of a basis matrix :
- From their grid to our standard grid: If a vector has coordinates in their grid, what is it in our standard grid? It's , which is simply matrix multiplication by :
- From our standard grid to their grid: What if we have in standard coordinates and want to find its coordinates in their basis ? We multiply by the inverse to run the translation backward:
3.5 Orthogonal Matrices & The Gram-Schmidt Process
Changing basis with requires computing a matrix inverse, which is computationally expensive when dimensions get huge.
Is there a "dream basis" where we never have to compute a messy inverse? Yes: an orthonormal basis, where every basis vector has unit length () and is mutually orthogonal (perpendicular) to every other basis vector ( for ).
When we pack orthonormal basis vectors into the columns of a matrix , we get an orthogonal matrix. Look what happens when we multiply :
- Diagonal entries are .
- Off-diagonal entries are .
Therefore:
For an orthogonal matrix, inverting it is as effortless as taking its transpose! Geometrically, orthogonal matrices represent pure rigid rotations and reflections i.e. they preserve all vector lengths () and angles.
The Gram-Schmidt Process
If we are given a set of linearly independent vectors that are tilted at awkward angles, the Gram-Schmidt process constructs a clean orthonormal basis one vector at a time using projections:
- First vector: Just normalize to unit length:
- Second vector: Take and subtract off its shadow (projection) onto . Whatever is left over is guaranteed to be strictly perpendicular to ! Then normalize it:
- Third vector: Take and subtract off its shadows onto both and , then normalize:
Part IV - Solving Linear Systems
4.1 Two Ways to Read
Consider a system of linear equations:
We can interpret in two ways:
- The Row Picture (Intersecting Lines/Planes): Each row is the equation of a line (in 2D) or a plane (in 3D). Solving the system means finding the intersection point that satisfies all equations at once.
- The Column Picture (Linear Combination of Columns): We are looking for the weights to combine the column vectors of to land on target :
Here, works!
4.2 Gaussian Elimination & Row Echelon Form
For larger systems, we solve systematically using Gaussian Elimination. We write an augmented matrix and apply valid row operations (swapping rows, scaling a row, or subtracting a multiple of one row from another) until the matrix is in Row Echelon Form (staircase of zeros below the diagonal).
Let's solve our system step by step:
- Subtract from to eliminate the bottom-left entry:
- Divide by to get a leading (pivot):
- Back-substitute into Row 1 ().
(If we continue eliminating entries above the diagonal until the left side becomes the Identity matrix , we get Reduced Row Echelon Form. Performing the exact same steps on until it becomes is how we compute the inverse matrix )
4.3 Determinants, Rank and Existence of Solutions
What is the Determinant ( or )?
Geometrically, the determinant of a transformation matrix measures the factor by which the transformation scales area (in 2D) or volume (in 3D).
- Start with a unit square formed by and (Area ).
- After transforming by , that unit square becomes a parallelogram spanned by and , whose signed area is:
- If , space was flipped inside-out (like a mirror reflection reversing the orientation of the axes).
- Crucial Case (): If the area of the transformed parallelogram is zero, it means the two columns of are collinear (linearly dependent)! The transformation has squashed 2D space flat onto a 1D line (or a single point). Once space is flattened, information is permanently lost i.e you cannot reverse the transformation. Such a matrix is called singular (non-invertible).
Rank and Solution Cases
The rank of a matrix is the number of linearly independent columns (or rows) which equals the number of non-zero pivot rows after Gaussian elimination. Geometrically, rank is the true dimension of the output space after transformation.
For an system :
- Full Rank (, ): The columns span the entire space. There is exactly one unique solution for every target .
- Rank Deficient (, ): Space collapses onto a lower-dimensional subspace (like a plane collapsing into a line):
- No solution (Inconsistent): Target lies outside the collapsed line/plane that can reach.
- Infinitely many solutions: Target happens to lie on the collapsed line/plane, so an entire line or plane of input vectors all get squashed onto .
Part V - Eigenvalues and Eigenvectors
5.1 The Eigenvalue Equation & Geometric Meaning
When you apply a linear transformation matrix to space, almost every vector gets knocked off its original line of action changing both its direction and its length.
However, certain special vectors refuse to get knocked off their line. When multiplies one of these special vectors , the output points along the exact same line as it only gets stretched, shrunk or flipped by a scalar factor :
- (any non-zero vector satisfying this) is called an eigenvector ("characteristic vector").
- is its corresponding eigenvalue (the scaling factor along that invariant direction).
Geometric Intuition:
- Pure Scaling along and : The eigenvectors are simply the -axis and -axis themselves.
- Horizontal Shear : Every vector with a vertical component gets tilted sideways. Only vectors lying strictly along the horizontal -axis stay on their own line ().
- 3D Rotation: If you spin a globe in 3D space, almost every vector moves to a new direction except vectors lying along the axis of rotation, which stay completely fixed ()!
5.2 Deriving the Characteristic Polynomial
How do we algebraically solve when both and are unknown?
- First, insert the identity matrix so we can subtract the right side from the left (since you cannot subtract a scalar directly from a matrix , we write ):
- Factor out :
- Look closely at . Obviously, works, but the zero vector has no direction, so we ban by definition. For a matrix to squash a non-zero vector onto the origin , that matrix must collapse space meaning it must be singular!
- A matrix is singular if and only if its determinant is zero:
This equation, , is called the characteristic equation.
Worked Numerical Example
Let's find the eigenvalues and eigenvectors of:
Step 1: Solve for .
Factoring the quadratic:
You can always verify your eigenvalues instantly using two identities:
- Sum of eigenvalues Trace of (sum of diagonal entries): , and .
- Product of eigenvalues Determinant of : and .
Step 2: Substitute each back into to find the eigenvectors.
-
For : Both rows state that . Picking gives our first eigenvector: (Any non-zero multiple of lies on the same line and is an equally valid eigenvector).
-
For : This states . Picking gives our second eigenvector:
5.3 Diagonalization () and Matrix Powers
Why are eigenvectors the crown jewel of this course? Because if an matrix has linearly independent eigenvectors, we can use those eigenvectors as our new coordinate basis!
Let be the basis matrix whose columns are the eigenvectors , and let be the diagonal matrix containing the eigenvalues on the diagonal:
In the eigenvector basis, the matrix doesn't rotate or shear anything it just scales along the axes by and . Therefore, applying is equivalent to:
- : Change coordinates from the standard basis into the eigenvector basis.
- : Scale each coordinate directly by .
- : Convert coordinates back from the eigenvector basis to the standard basis.
Written as an equation, this is Eigendecomposition (Diagonalization):
The Superpower: Computing Instantly
Suppose we need to apply transformation times () for example, simulating a system over time steps or running Google's PageRank web-surfer algorithm to steady state. Multiplying a dense matrix times is slow and messy, but in diagonalized form, the interior terms cancel out like dominoes:
Raising a diagonal matrix to the -th power simply raises its individual diagonal numbers to the -th power!
(Note: When a matrix is symmetric (), its eigenvectors are guaranteed to be mutually orthogonal, meaning our eigenvector basis matrix becomes an orthogonal matrix where , giving known as the Spectral Theorem).
5.4 Verifying Everything in Python (NumPy)
Here is a brief, self-contained script verifying our projections, Gram-Schmidt orthogonalization and eigendecomposition from the examples above:
import numpy as np
np.set_printoptions(precision=4, suppress=True)
A = np.array([[4.0, 1.0],
[2.0, 3.0]])
eigenvalues, P = np.linalg.eig(A)
# Sort descending so the order matches the hand calculation (5, then 2)
order = np.argsort(eigenvalues)[::-1]
eigenvalues, P = eigenvalues[order], P[:, order]
D = np.diag(eigenvalues)
P_inv = np.linalg.inv(P)
print("Eigenvalues:", eigenvalues) # [5. 2.]
print("A = P D P^-1 holds:", np.allclose(A, P @ D @ P_inv))
# Trace / determinant sanity check
print("Sum of eigenvalues == trace:", np.isclose(eigenvalues.sum(), np.trace(A)))
print("Product of eigenvalues == det:", np.isclose(eigenvalues.prod(), np.linalg.det(A)))
# Compare with hand-computed eigenvectors [1, 1] and [1, -2].
# NumPy normalises to unit length (and may flip sign), so compare directions only.
P_hand = np.array([[1.0, 1.0],
[1.0, -2.0]])
for i in range(2):
v = P_hand[:, i]
is_eigvec = np.allclose(A @ v, eigenvalues[i] * v)
same_dir = np.allclose(np.abs(v / np.linalg.norm(v)), np.abs(P[:, i]))
print(f"Hand eigenvector {v}: A v = lambda v -> {is_eigvec}, same direction as NumPy's -> {same_dir}")
# Fast matrix powers: A^k = P D^k P^-1
k = 5
A_direct = np.linalg.matrix_power(A, k)
A_eigen = P @ np.diag(eigenvalues**k) @ P_inv
print(f"\nDirect A^{k}:\n{A_direct}")
print(f"Eigen-based A^{k}:\n{A_eigen}")
print("Match:", np.allclose(A_direct, A_eigen))
# Special cases
print("\n--- Special cases ---")
# Uniform scaling: every vector is an eigenvector
S = 2 * np.eye(2)
vals, vecs = np.linalg.eig(S)
print("Uniform scaling 2I -> eigenvalues:", vals) # [2. 2.]
# 90-degree rotation: no real eigenvectors (complex eigenvalues)
R = np.array([[0.0, -1.0],
[1.0, 0.0]])
vals, _ = np.linalg.eig(R)
print("Rotation 90° -> eigenvalues:", vals) # [0.+1.j 0.-1.j]
# Shear: repeated eigenvalue, only one eigenvector direction
Sh = np.array([[1.0, 1.0],
[0.0, 1.0]])
vals, vecs = np.linalg.eig(Sh)
print("Shear -> eigenvalues:", vals) # [1. 1.]
print("Shear -> eigenvector matrix:\n", vecs)
print("|det(P)| ≈ 0 (columns are parallel, so NOT diagonalizable):",
abs(np.linalg.det(vecs)) < 1e-10)
# PageRank via the power method
print("\n--- PageRank ---")
# 4 pages A, B, C, D. Column j = where page j sends its "vote".
# A -> B, C, D | B -> C, D | C -> A | D -> A, C
L = np.array([
[0, 0, 1, 1/2],
[1/3, 0, 0, 0 ],
[1/3, 1/2, 0, 1/2],
[1/3, 1/2, 0, 0 ],
])
print("Columns sum to 1:", np.allclose(L.sum(axis=0), 1))
n = L.shape[0]
# Power method: start with equal rank, repeatedly apply L
r = np.ones(n) / n
for _ in range(100):
r_next = L @ r
if np.linalg.norm(r_next - r) < 1e-10:
break
r = r_next
print("Power method ranks:", r)
# Same answer as the eigenvector with eigenvalue 1 (normalised to sum to 1)
vals, vecs = np.linalg.eig(L)
idx = np.argmin(np.abs(vals - 1))
r_eig = np.real(vecs[:, idx])
r_eig = r_eig / r_eig.sum()
print("Eigenvector (lambda=1):", r_eig)
print("Match:", np.allclose(r, r_eig, atol=1e-6))
# With damping factor d = 0.85
d = 0.85
M = d * L + (1 - d) / n * np.ones((n, n))
r = np.ones(n) / n
for _ in range(100):
r = M @ r
print("With damping (d=0.85):", r)
