The Question a Scatter Plot Can't Answer
Suppose ISRO gives you the (x, y) coordinates of 500 boulders scattered across a small patch of the lunar surface near a suspected impact crater. You plot the points and, because you have eyes, you instantly see it: the boulders trace out a ring, thrown outward when the crater formed, with an empty patch of regolith in the middle where the impactor itself dug in. A human glances at the picture and says "ring" in a fraction of a second.
Now take away the picture. All a computer ever receives is a list of 500 number pairs — no image, no perception, nothing "shaped" about it at all. How would you write a program that looks only at those coordinates (or, more generally, only at the distances between points — which is all you get in higher dimensions where you can't plot anything) and correctly outputs "these points form a loop with a hole in the middle," as opposed to "these points form a solid disc" or "these points are two separate blobs"? Clustering algorithms like k-means will happily draw a circle-shaped cluster around the ring and call it one blob, missing the hole entirely — k-means has no concept of "hole." You need a different kind of mathematics, one built specifically to answer "what is the shape of this data, and which features of that shape are real versus just measurement noise?" That mathematics is topological data analysis (TDA), and its central tool is persistent homology. This chapter builds it from first principles, with a fully worked numerical example you can check by hand.
From Points to Shapes: Building a Simplicial Complex
The first move is deceptively simple: turn a point cloud into a connected shape by picking a distance threshold, call it epsilon (ε), and joining every pair of points whose distance is at most ε. This alone gives you a graph — vertices and edges. But a graph can only ever be zero- or one-dimensional (points and lines); it can never enclose a 2-dimensional patch of "filled-in" surface. To capture that, TDA generalizes the idea of "join nearby points" one dimension further: whenever three points are all pairwise within ε of each other, fill in the solid triangle connecting them, not just its three edges. Whenever four points are all pairwise within ε, fill in the solid tetrahedron. And so on.
Formally: a k-simplex is the shape spanned by k + 1 points — a 0-simplex is a single vertex, a 1-simplex is an edge, a 2-simplex is a filled triangle, a 3-simplex is a filled tetrahedron. A collection of simplices that fit together consistently (every face of an included simplex is also included) is a simplicial complex. The specific rule "include the simplex on any set of points whose pairwise distances are all ≤ ε" defines the Vietoris–Rips complex at scale ε, written Rips(ε) — the workhorse construction of TDA, because it needs nothing but a table of pairwise distances to build, which is exactly the kind of data you can compute in any dimension, not just 2D or 3D you can draw.
One subtlety that trips people up the first time they see it: a simplicial complex is an abstract object, defined purely by which sets of points satisfy the distance condition. When you draw it on paper in 2D, two triangles that both satisfy the Rips condition can end up geometrically overlapping in your drawing even though, abstractly, they are two separate, perfectly valid 2-simplices. The topology is decided by the distance table, not by whether your sketch of it looks tidy — a point we will need later in this chapter.
Betti Numbers: Counting Holes, Precisely
Once you have a simplicial complex, you can count its holes rigorously using Betti numbers, denoted b0, b1, b2, and so on:
- b0 = the number of connected components (separate pieces).
- b1 = the number of independent 1-dimensional holes — loops that don't bound any filled-in 2-dimensional patch inside the complex. A hexagonal ring of edges with no triangles filling it has b1 = 1. Fill in the triangles and b1 drops to 0.
- b2 = the number of independent enclosed 2-dimensional voids (think: the hollow air pocket inside a hollow sphere made of triangular faces, like a geodesic dome with nothing filling its interior). We won't compute b2 by hand in this chapter, but the pattern continues in exactly this way to arbitrarily high dimensions — the same machinery that finds a ring in a 2D point cloud can find a 7-dimensional void in a 7-dimensional dataset, which is precisely why TDA is useful beyond pictures you can draw.
A single point has b0 = 1, b1 = 0. A triangle's three edges with no filled interior (just the boundary) has b0 = 1, b1 = 1 — it's a loop. Fill in the 2-simplex and b1 collapses to 0, because now the loop is the boundary of solid material, not a path around empty space. This last observation is the seed of everything that follows.
A Worked Example: Growing ε on Seven Points
Take seven points in the plane: six arranged as the vertices of a regular hexagon with circumradius 2 units, labelled P1 through P6 going around the ring, plus one outlier P7 sitting far off to one side, well outside the hexagon's neighbourhood. Using the standard distance formula d = √((x₂−x₁)² + (y₂−y₁)²) from CBSE coordinate geometry, three distances matter for the hexagon points:
- Adjacent vertices (P1–P2, P2–P3, …, P6–P1): distance = 2 (the hexagon's "ring edges").
- Vertices two apart, i.e. skipping one (P1–P3, P2–P4, …): distance = 2√3 ≈ 3.464 (the "short diagonals").
- Opposite vertices (P1–P4, P2–P5, P3–P6): distance = 4 (the diameter).
P7's nearest hexagon neighbour sits about 12.3 units away — far outside anything we'll examine in this chapter.
Now grow ε from 0 and watch the Rips complex change:
Panel 1 (ε ≈ 1): every distance in the set is at least 2, so no edges exist at all. Seven isolated vertices, b0 = 7, b1 = 0. If you stopped analysis here — which is exactly what happens if you pick a single arbitrary threshold instead of sweeping through all of them — you would wrongly conclude the data is seven disconnected specks with no structure.
Panel 2 (ε = 2): all six ring edges appear simultaneously, since every adjacent pair is exactly distance 2 apart. The six hexagon points fuse into a single connected component; P7, whose nearest hexagon neighbour is 12.3 units away, stays isolated. So b0 drops from 7 to 2 — five of the six ring edges did the merging (six components collapsing to one needs exactly five successful merges), and the sixth ring edge, arriving at the same instant, closes the loop instead of merging anything further: it is born as a new one-dimensional cycle. b1 jumps from 0 to 1.
Panel 3 (ε = 2√3 ≈ 3.464): the six "short diagonal" edges appear, each connecting two vertices that skip one hexagon vertex. This also makes several new triples pairwise-close enough to qualify as filled 2-simplices — for instance P1, P2, P3 are now all mutually within 2√3, so triangle P1P2P3 is filled in, and the same happens for every other consecutive triple, plus the two larger triangles P1P3P5 and P2P4P6 (each of those three points is pairwise 2√3 apart too). Once enough of the ring's interior is triangulated this way, the boundary loop stops being "a path around empty space" and becomes "the edge of filled-in material" — b1 falls back to 0. b0 stays at 2, because none of this affects whether P7 is connected to anything.
Notice something important: b0 alone cannot tell the loop story at all — it reads exactly "2" in both panel 2 and panel 3, whether the hole is open or filled. Only b1 registers the loop being born and then dying. This is why real shape analysis needs the whole family of Betti numbers together, not just component counting.
(And about that overlap: yes, triangles P1P3P5 and P2P4P6 visually cross each other in the drawing above — that's the "abstract complex" subtlety from the previous section in action. Each is a perfectly valid 2-simplex because its three vertices satisfy the distance condition; the drawing looks tangled, but the algebra that computes b1 doesn't care how the picture looks.)
The Persistence Pairing Principle — and a Common Misconception
It is tempting, after seeing a hole get filled in by triangles, to generalise sloppily to: "adding material can only ever destroy holes, never create new ones." The worked example above already disproves that as a blanket statement — at ε = 2, adding the six ring edges did the opposite: it created a brand-new hole (b1 jumped from 0 to 1) out of nothing. Edges routinely birth loops; that is exactly what a ring of edges with no interior is.
The statement that is true, and is the actual engine behind persistent homology, is more precise: when you add a single k-simplex to a growing filtration, it does exactly one of two things — it either births a brand-new homology class in dimension k, or it kills (pairs off with) an existing class in dimension k − 1. This is called the persistence pairing principle (sometimes "the Elder Rule"). Concretely for the dimensions you'll actually compute by hand:
- Adding a vertex (0-simplex) always births a new component (b0 goes up by 1) — unless it's already present.
- Adding an edge (1-simplex) either merges two separate components (killing a b0 class — two pieces become one) or, if both its endpoints are already in the same component, closes a new loop (birthing a b1 class). It can never do both, and which one happens depends entirely on whether the edge's endpoints were already connected.
- Adding a triangle (2-simplex) either births a new b2 void or, far more commonly in 2D data, kills an existing b1 loop by filling in a cycle that was already there.
Every birth is either permanent (survives forever) or eventually paired with a later death of one dimension lower — that pairing, tracked across the entire sweep of ε, is what a persistence diagram or barcode records. Get this principle right and the whole subject becomes mechanical bookkeeping; get it wrong (as the sloppy "adding material only destroys holes" version does) and you'll mispredict exactly the case that matters most — the moment a real loop is born.
The Persistence Barcode
A persistence barcode plots, for every topological feature the filtration ever produces, a horizontal bar starting at its birth ε and ending at its death ε (or continuing forever, if it never dies). Long bars are features that survive across a wide range of scales — real structure. Short bars are features that appear and vanish almost immediately as ε ticks up by a hair — usually just noise from how densely the points happen to be spaced, not genuine shape.
Read the H1 bar directly off the axis: it starts at ε = 2 and ends at ε = 2√3 ≈ 3.464, a horizontal span of 2√3 − 2 ≈ 1.464 epsilon-units. That span is the loop's persistence — a substantial, unambiguous bar, telling you this is a genuine ring in the data, not an artefact of one unlucky threshold choice. Contrast that with how fragile the "pick one ε and hope" approach from Panel 1 was: choose ε = 1 and you see nothing; choose ε = 0.5 higher and the entire hole story would already be irrelevant. The barcode sidesteps the guesswork by showing you every scale at once and letting the bar lengths tell you what's real.
Computing It: Union–Find and the Distance Formula
Tracking b0 across a filtration is exactly the classic union–find (disjoint-set) algorithm from graph theory, applied to edges sorted by length — which is also, not coincidentally, the algorithm behind single-linkage hierarchical clustering. Here it is, computing connected components at a given ε using nothing but the coordinate distance formula:
import math
points = {
"P1": (0, 2), "P2": (1.732, 1), "P3": (1.732, -1),
"P4": (0, -2), "P5": (-1.732, -1), "P6": (-1.732, 1),
"P7": (14, 1),
}
def dist(a, b):
(x1, y1), (x2, y2) = a, b
return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
names = list(points)
parent = {n: n for n in names}
def find(n):
while parent[n] != n:
parent[n] = parent[parent[n]]
n = parent[n]
return n
def union(a, b):
ra, rb = find(a), find(b)
if ra != rb:
parent[ra] = rb
def components(epsilon):
for n in names:
parent[n] = n
for i in range(len(names)):
for j in range(i + 1, len(names)):
a, b = names[i], names[j]
if dist(points[a], points[b]) <= epsilon:
union(a, b)
return len({find(n) for n in names})
for eps in (0.5, 2, 3.47, 5):
print(eps, components(eps))
Trace it by hand and it matches the barcode exactly. At ε = 0.5, the smallest possible distance in the set is 2 (the ring edges), so no pair triggers union; all seven points remain their own root, giving 0.5 7. At ε = 2, the six ring-edge pairs (each distance exactly 2) all pass the test and get unioned, collapsing P1–P6 into one root while P7 (distance ≈ 12.3 to its nearest hexagon neighbour) stays untouched, giving 2 2. At ε = 3.47 (deliberately just past 2√3 ≈ 3.464 to avoid a floating-point tie at the boundary), the six short-diagonal pairs also pass, but they only add edges inside a component that was already fused at ε = 2 — component count doesn't change, giving 3.47 2. At ε = 5, still 5 2, since P7 needs ε past 12.3 to join.
Notice this code gives you b0 correctly but says nothing about b1 — union–find only ever sees the graph, not the triangles. You can get an upper bound on b1 for free from the same graph, using the standard cycle-rank identity for a 1-dimensional complex: independent cycles = edges − vertices + components.
def cycle_rank(epsilon):
for n in names:
parent[n] = n
edges = 0
for i in range(len(names)):
for j in range(i + 1, len(names)):
a, b = names[i], names[j]
if dist(points[a], points[b]) <= epsilon:
union(a, b)
edges += 1
comps = len({find(n) for n in names})
return edges - len(names) + comps
print(cycle_rank(2)) # 1 -> matches the true b1 (no triangles exist at eps=2 yet)
print(cycle_rank(3.47)) # 7 -> NOT the true b1 once triangles are counted
At ε = 2, this returns 6 − 7 + 2 = 1, exactly matching the true b1, because no 2-simplices exist yet — the graph's cycle count and the complex's b1 genuinely agree at this stage. But at ε = 3.47, it returns 12 − 7 + 2 = 7, wildly overshooting the true b1 of 0. The gap between 7 and 0 is exactly the seven independent loops in the raw graph that got filled in — killed — by the 2-simplices this edge-only code never looks at. This is a direct, computable illustration of the persistence pairing principle from the previous section: those triangle additions are the deaths that pair off with earlier edge-births. Any tool that only builds a graph (however popular "network analysis" is) is structurally blind to this — it's why genuine TDA software (Ripser, GUDHI, Dionysus) builds the full simplicial complex, not just a graph.
Why This Matters: Genuine Uses of Persistent Homology
This isn't a purely academic curiosity. In a widely cited 2011 study, Nicolau, Levine and Carlsson applied topological methods to gene-expression data from breast cancer patients and identified a previously unrecognised subgroup with a distinct mutational signature and notably better survival outcomes — a pattern that had been sitting in the data all along but was invisible to standard clustering because it wasn't a "blob," it was a shape clustering methods aren't built to see. Separately, de Silva and Ghrist showed how persistent homology can verify that a network of motion sensors has no coverage gaps using nothing but which sensors can detect each other — no GPS, no coordinates — by checking whether a computed b1/b2 pattern in the sensor network's simplicial complex is topologically trivial. Both results share the exact mechanism you just traced by hand on seven points: distances in, Betti numbers and their persistence out, real structure separated from noise by bar length.
CBSE and Competitive-Exam Connections
Everything computed in this chapter rests on tools already in your Class 10 syllabus, pushed further than the textbook usually takes them. The distance formula is the entire engine of the Rips complex — every ε threshold in the worked example came directly from it. The union–find / connected-components idea is standard graph theory, relevant to JEE and Olympiad combinatorics problems about connectivity and spanning structures. And the cycle-rank formula (edges − vertices + components) is a direct cousin of Euler's formula for planar graphs (V − E + F = 2), a recurring tool in Olympiad geometry and combinatorics and a useful check anywhere you're asked to reason about faces, edges, and connected pieces of a graph or polyhedron. Persistent homology itself sits beyond the CBSE board syllabus and is graduate-level material in its full generality, but if you're aiming for research-adjacent competitions or want a head start into computational topology, the way this chapter built it — filtration, Betti numbers, birth/death pairing — is the standard route every textbook on the subject takes, just compressed and made concrete.
Practice: Test Your Understanding
Q1 (concept check). True or false: "Adding an edge to a Rips complex can only merge two components — it can never create a new loop." Explain using the ε = 2 step of the worked example.
Answer: False. Whether an added edge merges components or births a loop depends on whether its two endpoints were already in the same component. At ε = 2, five of the six ring edges merge previously-separate hexagon vertices (killing b0 classes), but the sixth ring edge connects two endpoints that are, by then, already in the same component — so instead of merging anything, it closes the ring and births a new b1 class. Same simplex type (an edge), two completely different effects, depending purely on the state of the complex at the moment it's added.
Q2 (computation). Consider a square with vertices A(0, 0), B(4, 0), C(4, 4), D(0, 4). Using the distance formula, find the ε at which the boundary loop is born, the ε at which it dies, and its persistence.
Answer: Adjacent sides (AB, BC, CD, DA) all have length 4. The two diagonals (AC, BD) both have length √(4² + 4²) = √32 = 4√2 ≈ 5.657. At ε = 4, all four sides appear simultaneously, closing the square into a loop: b1 is born. Nothing else happens until ε reaches 4√2, when diagonal AC appears; check triangle ABC: AB = 4, BC = 4, AC = 4√2, all ≤ 4√2, so this 2-simplex is filled — likewise triangle ACD. Two filled triangles cover the whole square, and the loop dies at ε = 4√2 ≈ 5.657. Persistence = 4√2 − 4 ≈ 1.657.
Q3 (misconception check). A classmate says: "Since b0 stayed at 2 the whole time from ε = 2 to ε = 5 in the hexagon example, nothing topologically interesting happened in that range." What's wrong with this claim?
Answer: b0 only tracks connected components; it is blind to loops entirely. Across that exact range, b1 went from 1 down to 0 — the hole was born and then filled — a real, significant topological event that b0 alone cannot register. You always need to track every Betti number the problem calls for, not just the easiest one to compute.
Summary
Persistent homology answers a question raw coordinates can't answer on their own: which shapes in a point cloud are genuine structure, and which are accidents of a single arbitrary distance threshold? Build a Vietoris–Rips complex at every scale ε by connecting points within distance ε and filling in triangles (and higher simplices) wherever every pair in a group qualifies. Track the Betti numbers b0 (components), b1 (loops), b2 (voids) as ε grows. Every simplex you add either births a new class in its own dimension or kills a class one dimension down — never the reverse, and there is no dimension-agnostic shortcut like "more edges only closes holes." Record every birth and death as a bar in a persistence barcode; long bars are signal, short bars are noise. The same machinery that separated a real ring from an outlier in seven hand-traceable points is, unmodified in principle, what found a clinically meaningful cancer subtype hiding in gene-expression data and what verifies sensor coverage without a single GPS reading — because it never needed a picture to begin with, only distances.
Think About It
Think about this: How would you explain topological data analysis: persistent homology and shape discovery to a friend who has never seen a computer? What real-world analogy would you use? Imagine you had to build a system using these concepts — what would be your first step? Try this: before moving on, write down three things you learned and one question you still have.