Graph theory
A graph is things plus the relationships between them — nodes and edges. It's where discrete structure meets linear algebra: the adjacency matrix turns a graph into something you can multiply, and that one move powers GNNs, PageRank, spectral clustering, and knowledge graphs.
Written and reviewed by Shreyash Prashu Last reviewed
What you'll learn
- Graphs, edges, degree, and the adjacency matrix
- Why powers of the adjacency matrix count walks
- The graph Laplacian and spectral methods
- Random walks, PageRank, and the ML hooks (GNNs, knowledge graphs)
Before you start
The last lesson made a promise it could not keep on a straight line: it claimed the eigenvectors of a graph Laplacian generalise the Fourier basis to networks — yet we had no graph, no Laplacian, not even a definition of either. Time to build them, and in doing so close the whole math chapter where it has secretly been heading all along: turn structure into a matrix, and let linear algebra do the rest.
A graph is the most general way to write down things and the relationships between them — a set of nodes (vertices) joined by edges. Social networks, molecules, the web, knowledge bases, recommendation data: all graphs. And graphs are increasingly the structure ML itself runs on — a graph neural network passes messages along edges, and even attention can be read as a soft, fully-connected graph. The single object that makes all of it computable is the adjacency matrix.
From a graph to a matrix
Number the nodes 0 … n−1. The adjacency matrix A is n × n with A[i, j] = 1
when there’s an edge from i to j (and 0 otherwise; for weighted graphs, the weight).
For an undirected graph A is symmetric. A node’s degree — how many edges touch
it — is just the sum of its row.
Powers of A count walks
Here’s the payoff of turning the graph into a matrix: (A^k)[i, j] is the number of
walks of length k from i to j. Squaring the adjacency matrix counts two-step
paths; cubing it (and reading the diagonal) counts triangles:
import numpy as np
A = np.array([[0, 1, 0, 0], # edges: 0-1, 1-2, 1-3, 2-3
[1, 0, 1, 1],
[0, 1, 0, 1],
[0, 1, 1, 0]])
print("degrees:", A.sum(axis=1))
print("2-step walks (A @ A):")
print(A @ A)
print("triangles per node (diag(A^3)//2):", np.diag(A @ A @ A) // 2)
degrees: [1 3 2 2]
2-step walks (A @ A):
[[1 0 1 1]
[0 3 1 1]
[1 1 2 1]
[1 1 1 2]]
triangles per node (diag(A^3)//2): [0 1 1 1]
A²[1,1] = 3 says there are three two-step walks from node 1 back to itself (out to each
neighbor and back). And diag(A³)//2 = [0,1,1,1] says nodes 1, 2, 3 each sit on exactly
one triangle — they form one. Matrix algebra is answering graph questions.
The Laplacian and random walks
Two more constructions carry most of graph ML:
- The graph Laplacian
L = D − A, whereDis the diagonal degree matrix. Its eigenvalues and eigenvectors (eigen-decomposition) encode connectivity: the number of zero eigenvalues equals the number of connected components, and the eigenvector of the second-smallest eigenvalue (the Fiedler vector) splits the graph into two well-separated halves — the basis of spectral clustering. - Random walks and PageRank. Normalize the adjacency matrix into a transition matrix and you have a Markov chain on the graph. Its stationary distribution is PageRank: a node is important if a random walker spends a lot of time there. The famous web-ranking algorithm is just the stationary distribution of a random surfer.
In one breath
- A graph is nodes + edges; the adjacency matrix
A(A[i,j]=1for an edge, symmetric if undirected) turns it into something you can multiply, and a node’s degree is its row sum. - Powers of A count walks:
(A^k)[i,j]is the number of length-kwalks fromitoj(soA²counts 2-step paths,diag(A³)/2counts triangles). - The graph Laplacian
L = D − Ahas a spectrum that encodes connectivity (zero eigenvalues = components; the Fiedler vector = a 2-way cut) — the engine of spectral clustering. - A random walk on a graph is a Markov chain; its stationary distribution is PageRank (importance = where a random surfer dwells).
- ML hooks: GNNs (message passing = multiply by adjacency/Laplacian), knowledge graphs, spectral clustering, PageRank ranking, and recommendation.
Practice
Quick check
A question to carry forward
That closes the whole math chapter — on its quietest, most powerful habit. Take anything — a
transformation, a probability cloud, a signal, now a network — turn it into a matrix or an
array of numbers, and let linear algebra and calculus do the work. Vectors, gradients,
covariance, the Fourier basis, the adjacency matrix: every one was an array, and every worked
example in every lesson was computed the same way — import numpy as np.
Which finally exposes the thing we have leaned on in nearly forty lessons and never once
examined. We kept writing A @ A, rng.normal(...), np.fft.rfft, .sum(axis=1) as though
arrays were free. They are not — a plain Python list of a million numbers is slow and bloated,
yet NumPy multiplies a million of them in a blink. So the next section turns to the engine
itself. What is a NumPy array, why is it so much faster and leaner than a Python list, and
what does “vectorised” actually mean — the foundation that Pandas, scikit-learn, PyTorch,
and JAX are every one of them quietly built upon?
Practice this in an interview
All questionsA neural network can be built without activations, but stacked affine layers collapse into one affine transformation, so depth adds no functional expressive power. Nonlinear activations let the network represent curved or piecewise decision boundaries and feature interactions, while the right choice depends on the layer and gradient behavior.
An embedding is a dense, learned vector representation of a discrete or high-dimensional object — a word, image, user, product — in a continuous low-dimensional space. Proximity in embedding space reflects semantic or behavioural similarity, making embeddings a universal interface between raw data and neural networks.