Appendix C — Python and numpy Setup Guide
Every chapter of this book pairs the mathematics with a short numpy snippet, and the
progressive project builds a from-scratch toolkit/ you verify against numpy and
scipy. None of that requires you to be a Python expert — but it does require a working
Python with the right libraries installed, in a place that will not collide with the
rest of your system. This appendix gets you there in about fifteen minutes, then hands
you a "hello, linear algebra" snippet and a short list of the gotchas that trip up
nearly everyone the first time mathematics meets code.
You do not need to run anything to follow the book — every snippet shows its output so you can read along on paper. But linear algebra is a subject you learn with your hands, and the fastest way to believe a theorem is to watch numpy confirm it. Set this up once; you will use it in all forty chapters.
C.1 Installing Python 3.10 or newer
The book targets Python 3.10+ (the snippets use modern syntax, and the libraries below have dropped support for older versions). Check what you have first — open a terminal (Terminal on macOS/Linux, PowerShell or Command Prompt on Windows) and run:
python --version # or: python3 --version
If it prints Python 3.10.x or higher, you are set. If the command is missing or the
version is older, install a current Python:
- Windows / macOS: download the official installer from
python.org/downloads and run it. On Windows,
tick the box "Add Python to PATH" during installation — skipping it is the most
common first-day snag, because then
pythonis not found in a fresh terminal. - macOS (Homebrew):
brew install python. - Linux: use your package manager, e.g.
sudo apt install python3 python3-pip python3-venvon Debian/Ubuntu.
Common Pitfall — On many systems
pythonstill points at a legacy Python 2, while Python 3 ispython3. Ifpython --versionsurprises you, trypython3. The same split applies to the package installer:pipversuspip3. Inside a virtual environment (next section) this ambiguity disappears —pythonandpipmean the environment's versions — which is one more reason to always work in one.
C.2 Creating a virtual environment (do this once per project)
A virtual environment ("venv") is a private, self-contained Python that belongs to this project alone. Installing the book's libraries into a venv keeps them from clashing with other projects or with your operating system's Python. This is standard practice, not a nicety; get in the habit now.
From the folder where you keep your linear-algebra work (for instance, the folder
containing the book's toolkit/), create and activate a venv:
# Create a venv named ".venv" in the current folder (once)
python -m venv .venv
# Activate it (every time you open a new terminal for this project)
source .venv/bin/activate # macOS / Linux
.venv\Scripts\activate # Windows PowerShell / cmd
Once activated, your prompt shows (.venv) and python/pip refer to the
environment. To leave it, type deactivate. Re-activate it whenever you return — the
packages stay installed; only the activation is per-session.
C.3 Installing the libraries
With the venv active, install the four libraries the book uses. Upgrade pip first so
you get current wheels:
python -m pip install --upgrade pip
python -m pip install numpy scipy matplotlib pillow
Here is what each one is for in this book:
| Library | Role | Where it appears |
|---|---|---|
| numpy | arrays and core linear algebra (np.linalg) |
every chapter |
| scipy | extra factorizations (scipy.linalg: LU, expm, null_space, Cholesky) |
Ch. 10, 20, 28, 37, 38 |
| matplotlib | all figures, including the 2D transformation visualizer | every geometric chapter |
| pillow | loading/saving images as arrays | the SVD image-compression chapters (30–31) |
Confirm everything imports and report its version:
# check_install.py — confirm the toolchain is ready
import numpy as np, scipy, matplotlib, PIL
print("numpy", np.__version__, "| scipy", scipy.__version__)
print("matplotlib", matplotlib.__version__, "| pillow", PIL.__version__)
# Example output:
# numpy 2.3.5 | scipy 1.17.0
# matplotlib 3.x | pillow 11.x
If those three lines print without error, you are ready for Chapter 1. (Exact version numbers will differ; any recent release works — the book avoids bleeding-edge API.)
C.4 Using the book's from-scratch toolkit/
The progressive project lives in a package called toolkit/. Each chapter's "Build
Your Toolkit" callout asks you to implement one operation from scratch in pure
Python (no numpy inside the implementation — that is the point), and the matching
test in toolkit/tests/ checks your work against numpy/scipy. The layout looks like
this:
toolkit/
├── __init__.py
├── vectors.py # Ch. 2: add, scale, dot, norm, angle
├── matrices.py # Ch. 7/8: matmul (as composition), transpose
├── linear_systems.py # Ch. 4: row_reduce, gaussian_elimination, back_substitute
├── ... # inverse, lu, determinant, projection, gram_schmidt, eigen, svd, pca
└── tests/ # numpy/scipy live here ONLY, as the trusted reference
Run a chapter's tests from the folder that contains toolkit/, using the
-m/module form so Python finds the package on its path:
# from the directory that contains the toolkit/ folder
python -m pytest toolkit/tests/test_linear_systems.py # one chapter's tests
python -m pytest toolkit/tests/ # the whole suite
A test file can also be run directly (python toolkit/tests/test_linear_systems.py)
for plain pass/fail output. The division of labor is the book's third recurring theme
in miniature: your pure-Python code builds understanding; numpy verifies it at
scale. Implement, then verify — never the other way around.
Build Your Toolkit — Before Chapter 2, create the folder, drop an empty
__init__.pyinside it, and runpython -m pytest toolkit/tests/from the parent directory. Seeing "no tests ran" (rather than an import error) confirms Python can find the package — your scaffold is ready for the first real function.
C.5 Hello, linear algebra
Here is a complete first program. It builds a $2\times2$ matrix, applies it to a vector, solves a system, and confirms the solution — touching the operations of Chapters 2 through 9 in nine lines.
# hello_linalg.py — the first thing to run
import numpy as np
A = np.array([[2.0, 1.0],
[1.0, 3.0]]) # a 2x2 matrix (italic capital A in the book)
x = np.array([1.0, 2.0]) # a vector (bold lowercase x in the book)
print("A @ x =", A @ x) # matrix-vector product -> [4. 7.]
b = np.array([4.0, 7.0])
solution = np.linalg.solve(A, b) # solve A x = b
print("solve =", solution) # -> [1. 2.], recovering x
print("match? =", np.allclose(solution, x)) # -> True
Save it, activate your venv, and run python hello_linalg.py. Three lines of output,
the last of which is True, mean your installation is sound and you have just
performed the central act of the whole subject: applied a matrix to a vector and
inverted the operation.
C.6 The gotchas everyone hits (read this once, save yourself hours)
These are the recurring traps where the mathematics of the book and the mechanics of numpy disagree. The book flags each one the first time it bites in a chapter; here they are collected so you meet them on purpose.
-
Math indexes from 1; numpy indexes from 0. The mathematician's first component $v_1$ is
v[0]in code, and the entry $a_{11}$ (top-left) isA[0, 0]. The rule, stated once: $a_{ij}$ in math isA[i-1, j-1]in numpy. This single off-by-one causes more confusion than any other detail when you check a hand computation against code. -
Never compare floats with
==. Floating-point arithmetic is approximate:0.1 + 0.2 == 0.3evaluates toFalsein Python. Comparing computed matrices, eigenvalues, or norms with==will betray you. Use a tolerance instead:np.allclose(x, y)for arrays,np.isclose(a, b)for scalars. Throughout the book, "the hand result matches numpy" always meansnp.allclose, never==. -
@is matrix multiplication;*is elementwise. This is the error that produces wrong answers silently, because both run without complaint.A @ Bis the matrix product of Chapter 8 (composition of transformations).A * Bmultiplies entry by entry (the Hadamard product), which is almost never what a linear-algebra formula means. LikewiseA @ xis the matrix–vector product;A * xbroadcasts and is usually a bug. When you transcribe a formula like $A^{\mathsf{T}}A$, writeA.T @ A. -
np.linalg.eigversusnp.linalg.eigh. Useeighfor symmetric/Hermitian matrices andeigfor everything else.eighexploits symmetry to return real eigenvalues sorted in ascending order with guaranteed orthonormal eigenvectors — matching the Spectral Theorem (Ch. 27).eigis general and returns eigenvalues in no guaranteed order, possibly complex (a rotation matrix yields a complex pair), with eigenvectors that are merely scaled to unit length. Callingeigon a symmetric matrix still works but throws away the symmetry guarantees and can introduce tiny imaginary parts from rounding. Match the tool to the matrix. -
SVD and eigenvectors have sign and order conventions.
U, s, Vt = np.linalg.svd(A)returns the singular valuessin descending order (good — matches Eckart–Young, Ch. 31), andVtis already $V^{\mathsf{T}}$, not $V$ (so reconstruct withU @ np.diag(s) @ Vt, and the right singular vectors are the rows ofVt). But the sign of each singular vector is not unique: $(\mathbf{u}_i,\mathbf{v}_i)$ and $(-\mathbf{u}_i,-\mathbf{v}_i)$ are equally valid, so different machines or library versions may flip a column. Eigenvectors fromeig/eighcarry the same sign ambiguity (andeig's ordering is not even fixed). The cure is to compare quantities that are unique — the singular values, the subspace a vector spans, or the reconstructed matrix $A=U\Sigma V^{\mathsf{T}}$ — rather than the raw signed vectors. Appendix D returns to this in the cheatsheet. -
Trig functions take radians, not degrees.
np.cos,np.sinexpect radians, so a $90^\circ$ rotation usesnp.cos(np.pi/2), andnp.deg2rad(90)converts if you think in degrees. Feeding degrees tonp.sinis a quiet source of nonsense rotations (Ch. 21).
The Key Insight — Almost every numpy surprise in this book traces to one of three roots: an index that starts at 0 instead of 1, a float compared exactly instead of with a tolerance, or
*written where@was meant. Internalize those three and the code will stop fighting you — leaving you free to watch it confirm the mathematics, which is the whole reason it is here.