Embeddings & Geometry

Hyperbolic Embeddings

Every embedding you have ever trained lives in a space you did not choose. This lesson makes that choice visible — and shows that a two-dimensional space with the right curvature holds a tree that a two-hundred-dimensional flat space cannot.

Prerequisites: a vector is a list of numbers + distance between vectors means something. Curvature, hyperbolic geometry and the distance formula are all built here from nothing.
10
Chapters
9
Simulations
0
Assumed Knowledge

Chapter 0: Trees Do Not Fit in Flat Space

You have a taxonomy. Maybe it is WordNet — dalmatian is a kind of dog is a kind of canine is a kind of carnivore, all the way up to entity. Maybe it is your company’s product catalogue, or a biological tree of life, or the folder structure of a codebase, or the reply tree of a forum. It does not matter. It is a tree, and you would like to turn each node into a vector so that the usual machinery — nearest neighbour search, clustering, a downstream classifier — can operate on it.

So you do the obvious thing. You pick a dimension, say 100, initialise a vector per node, and train with a loss that says parents should be near their children. It works, sort of. The top of the tree looks sensible. The bottom is mush: dalmatian, dachshund and tabby cat all end up roughly the same distance from each other, which is exactly the distinction you needed.

The natural reaction is “more dimensions, more epochs, better loss.” That reaction is wrong, and it is wrong for a reason that has nothing to do with optimisation. The problem is geometric: the space you chose does not contain enough room. Not “not enough room for your data” in a vague sense — not enough room in a sense we are about to compute exactly, with arithmetic you can check on paper.

Count the room, count the demand

Strip the problem to its bones. Take a complete binary tree — every node has exactly two children — of depth d. Insist that every edge has length 1, because that is what “parent and child are one step apart” means. And insist that two different nodes never sit on top of each other; give each one a little personal space.

Now place it in the ordinary flat plane, the one you have been using your whole life. Put the root at the origin. Its children are at distance 1, their children at distance 2, and the nodes at depth k sit somewhere on a circle of radius k.

Here are the two numbers that decide everything.

demand at depth k:   2k nodes   ——   supply at depth k:   a circle of circumference 2πk

The demand doubles every level. The supply grows by a constant 2π ≈ 6.28 every level. One is exponential, the other is linear, and no amount of training changes that. Divide to get the room available per node:

depth knodes 2kcircumference 2πkarc per nodeverdict
2412.573.14roomy
3818.852.36fine
41625.131.57getting tight
53231.420.98siblings now closer than an edge
66437.700.59crowding
101,02462.830.06116× too close
201,048,576125.660.00012hopeless

Read the fifth row again, because it is the whole chapter. At depth five — thirty-two leaves, a tree you could draw on a napkin — the flat plane has already run out of room. Two leaves that are ten edges apart in the tree (five up to the root, five back down) are being squeezed to within 0.98 of each other, and it only gets worse.

Define distortion as the ratio between the distance the tree asks for and the distance the embedding delivers. At depth 10 the tree says two leaves in different halves are 20 apart; the plane gives them 0.061. That is a distortion of

20 ÷ 0.061 ≈ 328×

At depth 20 the same calculation gives 40 ÷ 0.00012 ≈ 333,000×. The embedding is not a slightly lossy picture of your tree. It is a completely different object wearing your tree’s labels.

The misconception this lesson exists to kill. “It is crowded in 2D, so use 100D and it will be fine.” It will not. In D flat dimensions the room inside radius r grows like rD — a polynomial. Your tree grows like 2d — an exponential. A polynomial in r chases an exponential in d and loses, for every fixed D, once the tree is deep enough. Raising D buys you a few more levels and then the same wall.
Where the room runs out

Top: how much room each leaf gets, level by level, on a logarithmic scale — the flat plane in orange, the hyperbolic plane (Chapter 1) in teal. The dashed line is one edge length: below it, siblings are closer to each other than a parent is to its child, which means the embedding is lying. Bottom: the leaves of the current level laid out on the available circumference, drawn to scale. Drag the slider and watch the orange ticks fuse into a solid bar.

tree depth5

Why the tree is the hard case, and not a weird one

It would be comforting if trees were an exotic corner case. They are not; they are the shape of almost every organising relation you care about. Taxonomies, org charts, file systems, ontologies, class hierarchies, citation lineages, biological phylogeny, the “is-a” edges of a knowledge graph. Anything with the words parent, subtype, contains or specialises in its schema.

And it is not just perfect trees. What matters is a property called tree-likeness: does the number of things within k steps of a node grow exponentially in k? A social graph with communities, a web graph, a road network with hubs — many real graphs pass this test at least locally. Chapter 7 gives you a number to measure it with, so you can stop guessing.

The theorem, so you stop hoping

This is not folklore. In 1986 Jean Bourgain proved that embedding the complete binary tree of depth h into any Euclidean space — any number of dimensions, a Hilbert space if you like — requires distortion that grows like √(log h). It grows slowly, but it grows without bound, and crucially the bound does not depend on the dimension at all. You cannot dimension your way out.

And in 2011 Rik Sarkar proved the counterpart: any tree embeds into the hyperbolic plane — two dimensions — with distortion 1 + ε for any ε you like. Two dimensions. Not two hundred.

The pair of results is the entire motivation. Flat space: unbounded distortion no matter how many dimensions you buy. Hyperbolic space: arbitrarily good with two. Something about hyperbolic space is structurally right for trees, and the rest of this lesson is finding out exactly what.

Concept → realization: what actually changes in your code

Before we build any geometry, look at how small the code change is. This is a normal embedding training step — the kind you have written many times:

python
# EUCLIDEAN: the default, and the thing that quietly fails on trees
emb = nn.Embedding(n_nodes, dim)              # (n, d) free parameters
u, v = emb(idx_u), emb(idx_v)                 # (B, d), (B, d)
dist = (u - v).pow(2).sum(-1).sqrt()          # (B,)  straight-line distance
loss = ranking_loss(dist, dist_negatives)
loss.backward(); opt.step()                   # plain SGD / Adam

And this is the hyperbolic version. Three lines change, and every one of them will be derived in this lesson:

python
# HYPERBOLIC: same parameters, different distance, different step rule
emb = nn.Embedding(n_nodes, dim)              # SAME (n, d) parameters — the ball is an open subset of R^d
u, v = emb(idx_u), emb(idx_v)
dist = poincare_dist(u, v)                    # 1. arcosh(...) instead of L2   → Chapter 1
loss = ranking_loss(dist, dist_negatives)
loss.backward()
riemannian_sgd_step(emb, lr)                  # 2. rescale gradient by (1-|x|^2)^2/4 → Chapter 3
project_into_ball(emb, eps=1e-5)              # 3. never let |x| reach 1        → Chapter 3

Notice what did not change: the parameter tensor. A d-dimensional hyperbolic embedding is still d floats per node. You are not adding capacity. You are reinterpreting the same numbers under a different rule for measuring distance — and that reinterpretation is worth, on WordNet, roughly a forty-fold reduction in dimension. Chapter 3 puts real figures on that claim.

What “curvature” is going to mean

One word of orientation so the next chapter does not feel like a rabbit from a hat. Curvature is not a mysterious property of a space; it is answerable by a measurement you could take with a tape measure: walk out a distance r in every direction and measure the circumference of the circle you traced.

What you measureCurvatureEveryday nameRoom
circumference less than 2πrpositivethe sphere — walk far enough and paths reconvergefinite; runs out
circumference exactlyrzerothe flat planepolynomial
circumference more than 2πrnegativehyperbolic — a saddle, a ruffled lettuce leafexponential

That is the whole idea. A space with negative curvature has more perimeter at radius r than flat space does, and if it is negatively curved everywhere, that surplus compounds. Chapter 1 builds such a space out of nothing but a unit disk and one rule about how long a ruler is. Chapter 2 proves the circumference is 2π sinh r, which is exponential, which is exactly the growth rate of a tree.

Then, and only then, will we train anything.

Why does adding more dimensions fail to fix the crowding of a deep tree in Euclidean space?

Chapter 1: The Poincaré Disk, Built From One Rule

We need a space with more room than the plane. The obstacle is that we also need to store points in it as ordinary tuples of floats, and draw them on ordinary screens. Those two demands look contradictory: how do you fit a space that is bigger than the plane inside a picture that is smaller than the plane?

The answer is the oldest trick in cartography. A world map fits an entire sphere onto a rectangle by distorting scale — on a Mercator map, Greenland is the size of Africa because the map stretches things near the poles. Nobody is confused by this. You read the map knowing that a centimetre near the equator and a centimetre near the pole mean different real distances.

We are going to do the same thing in reverse. We will take an infinite space and squeeze it into the open unit disk, and we will pay for the squeeze by declaring that a centimetre near the rim represents an enormous real distance. The disk is a map. The space it depicts is unbounded.

The one rule

Let B be the open unit disk: all points x with ‖x‖ < 1. (Open: the boundary circle itself is not in the space. We will see why in a moment.) Now the single definition that generates everything:

a tiny step of Euclidean length δ taken at the point x
counts as a true distance of   λx · δ,   where   λx = 2 / (1 − ‖x2)

That factor λx is called the conformal factor. Everything about hyperbolic embeddings — the distance formula, the exploding gradients, the numerical failures, the reason generality lives at the centre — is that one fraction and its consequences.

Read it as a shrinking ruler. At the centre λ = 2, so your ruler is normal-ish. As you drift outward the denominator 1 − ‖x2 collapses toward zero and the ruler shrinks toward nothing, so it takes more and more of them to cross the same Euclidean gap.

x1 − ‖x2λx = 2 / (1 − ‖x2)meaning
012.00the centre; a Euclidean step is worth 2
0.50.752.67barely stretched yet
0.90.1910.53a Euclidean millimetre is now a centimetre
0.990.0199100.5fifty times the centre’s scale
0.9990.0019991,000.5the rim is a long way away
0.99990.0001999910,000.5and getting further, fast

Notice the pattern in the last column: for ‖x‖ close to 1, since 1 − r2 = (1−r)(1+r) ≈ 2(1−r), we get λ ≈ 1/(1−r). Each extra nine in the radius multiplies the scale by ten. Hold on to that, because in Chapter 7 it becomes the reason float32 dies.

Deriving the distance from the centre — the whole derivation, by hand

Now we can compute a real distance instead of asserting one. Walk in a straight line from the centre out to a point at Euclidean radius r. Chop the walk into tiny steps dt. Each step at radius t counts as λ(t) dt. Add them up:

d(0, x) = ∫0r 2 dt / (1 − t2)

That integral is elementary. Split the fraction with partial fractions — check the algebra yourself by putting the right-hand side over a common denominator:

2 / (1 − t2) = 1/(1 − t) + 1/(1 + t)

Each piece integrates to a logarithm: ∫ dt/(1−t) = −ln(1−t) and ∫ dt/(1+t) = ln(1+t). Evaluate from 0 to r, and both terms vanish at 0 because ln 1 = 0:

d(0, x) = ln(1 + r) − ln(1 − r) = ln( (1+r) / (1−r) ) = 2 artanh(r)

That is the entire derivation. No hand-waving, no appeal to authority — one substitution and two logarithms. And it immediately answers the question we opened with.

The boundary is infinitely far away. Let r → 1. Then 1 − r → 0, so ln((1+r)/(1−r)) → ∞. The rim of the disk is not the edge of the space; it is the horizon — the set of directions you could walk forever without arriving. This is why the disk is open. A point with ‖x‖ = 1 is not a point of the space at all, which is why every implementation has a projection step that shoves points back inside.

Four distances you should be able to produce from memory

Plug numbers into ln((1+r)/(1−r)) and something delightful happens.

r = 0.5:   ln(1.5 / 0.5) = ln 3 = 1.0986
r = 0.9:   ln(1.9 / 0.1) = ln 19 = 2.9444
r = 0.99:   ln(1.99 / 0.01) = ln 199 = 5.2933
r = 0.999:   ln(1.999 / 0.001) = ln 1999 = 7.6004

Look at the gaps: 2.9444 → 5.2933 is a jump of 2.349; 5.2933 → 7.6004 is a jump of 2.307. Both are converging on ln 10 = 2.3026. Each additional nine in the Euclidean radius is worth a fixed 2.30 units of true distance. The Euclidean radius is a logarithmic gauge of the real radius, in exactly the way a decibel is a logarithmic gauge of power.

Invert it and you get the formula you will actually use when placing things by hand:

x‖ = tanh( d(0,x) / 2 )

Want a node at true radius 6.3? Then ‖x‖ = tanh(3.15) = 0.99633. Want one at 9.8? tanh(4.9) = 0.99989. Two nodes 3.5 units apart in the hierarchy differ in stored radius by 0.0036. Chapter 4 makes that uncomfortable fact into a design lesson, and Chapter 7 makes it into a bug report.

The general two-point distance

Distances from the centre are the easy case because the path is a straight radial line. For two arbitrary points the shortest path bends (we will see why in a moment), and the closed form is:

d(u, v) = arcosh  ( 1 + 2 · ‖uv2  /   ((1 − ‖u2)(1 − ‖v2))  )

where arcosh(z) = ln(z + √(z2 − 1)) is the inverse hyperbolic cosine. Do not take it on faith — check that it reduces correctly. Set u = 0. Then ‖uv2 = r2 and 1−‖u2 = 1, so the argument is

1 + 2r2/(1 − r2) = (1 − r2 + 2r2)/(1 − r2) = (1 + r2)/(1 − r2)

and the standard identity cosh s = (1 + tanh2(s/2))/(1 − tanh2(s/2)) says that arcosh of that is exactly 2 artanh(r). The general formula contains the radial one. Good.

Three structural things to notice in that formula, because each one shows up later as an engineering consequence:

Feature of the formulaWhat it causes downstream
The numerator is the plain Euclidean gap ‖uv2Two points at the same Euclidean offset can have wildly different hyperbolic distances — position matters, not just displacement
The denominator has a factor per point that vanishes at the rimDistance blows up near the boundary; this is the source of both the room and the numerical pain
arcosh(1) = 0 and arcosh grows like ln(2z)Hyperbolic distance is essentially the logarithm of the blown-up ratio, which is why it stays a manageable number like 9.8 even when the ratio is 19,801

Worked example 1: the same shape, twice, at two radii

This is the calculation that makes hyperbolic space click. Take two points at the same radius, ninety degrees apart. Then do it again, further out. Nothing about the shape changes — only where it sits.

Near the centre. u = (0.5, 0), v = (0, 0.5).

uv2 = 0.25 + 0.25 = 0.5
1 − ‖u2 = 1 − 0.25 = 0.75, and the same for v
argument = 1 + 2(0.5) / (0.75 × 0.75) = 1 + 1.0 / 0.5625 = 1 + 1.7778 = 2.7778
d = arcosh(2.7778) = ln(2.7778 + √(7.7160 − 1)) = ln(2.7778 + 2.5915) = ln 5.3693 = 1.6807

For comparison, the distance out to each of them from the centre is ln 3 = 1.0986, so travelling u → centre → v would cost 2.1972. The direct route (1.6807) is a clear shortcut. So far this behaves like an ordinary plane.

Far out. u = (0.99, 0), v = (0, 0.99). Same angle, same shape, bigger radius.

uv2 = 0.9801 + 0.9801 = 1.9602
1 − ‖u2 = 0.0199, so the product of the two factors is 0.00039601
argument = 1 + 2(1.9602) / 0.00039601 = 1 + 9,899.7 = 9,900.7
d = arcosh(9,900.7) ≈ ln(2 × 9,900.7) = ln 19,801 = 9.8935

Now compare with the through-the-centre route: 2 × ln 199 = 2 × 5.2933 = 10.5866. The direct path saves only

9.8935 ÷ 10.5866 = 0.9345  —  a 6.5% saving

against 23% at radius 0.5. Far from the centre, going “through the root” is almost free. That is precisely how distance behaves in a tree: to get from one leaf to a distant leaf you climb to the common ancestor and come back down, and there is no shortcut across. Hyperbolic space reproduces that behaviour without anybody programming it in. It falls out of the shrinking ruler.

The sentence to remember from this chapter. In hyperbolic space, the shortest path between two far-apart points is almost the path through their common ancestor. Trees have that property exactly; hyperbolic space has it approximately, everywhere, for free. Everything else — the loss functions, the cones, MERU — is exploiting that one fact.

Why straight lines bow inward

If the ruler is longer near the centre, then a path that detours toward the centre gets to use the cheap ruler for part of its journey. So shortest paths — geodesics — bow inward. It is the same reason a flight from London to Tokyo goes over Siberia rather than along the latitude line: the map is not the territory.

The exact shape is beautiful and easy to compute. Every geodesic of the Poincaré disk is either a diameter (a straight line through the centre — no detour available, it is already using the cheapest route) or an arc of a circle that meets the boundary circle at right angles. A circle with centre c and radius ρ is orthogonal to the unit circle exactly when ‖c2 = 1 + ρ2. Combine that with “the circle passes through u and v” and you get two linear equations for the centre:

c, u⟩ = (1 + ‖u2)/2   and   ⟨c, v⟩ = (1 + ‖v2)/2

Derivation, in one line: ‖cu2 = ρ2 = ‖c2 − 1, expand the left side, and the ‖c2 terms cancel. That is exactly what the simulation below solves, twice per frame, to draw the arcs you are about to drag around.

Poincaré disk explorer — drag the two dots

The faint rings are the true circles of hyperbolic radius 1, 2, 3, 4 and 5 — note how they bunch toward the rim even though they are evenly spaced in reality. The solid curve is the geodesic (shortest path); the dashed straight line is the Euclidean chord you would have drawn by mistake. Drag either dot outward and watch the ratio at the bottom climb toward 1, which is the tree property appearing.

Concept → realization: the distance function you will actually ship

python
import torch

def poincare_dist(u, v, eps=1e-7):
    """u, v: (..., d) tensors with ||x|| < 1.  Returns (...,) distances."""
    su = u.pow(2).sum(-1)                 # ||u||^2
    sv = v.pow(2).sum(-1)                 # ||v||^2
    duv = (u - v).pow(2).sum(-1)          # ||u - v||^2
    # clamp keeps the denominator away from 0 when a point drifts to the rim
    alpha = (1 - su).clamp_min(eps)
    beta  = (1 - sv).clamp_min(eps)
    z = 1 + 2 * duv / (alpha * beta)
    return torch.acosh(z.clamp_min(1 + eps))   # acosh is undefined below 1

Two clamps, and both are load-bearing. The first stops a point that has crept to ‖x‖ = 1 from producing a division by zero. The second stops floating-point round-off from handing acosh an argument of 0.9999999 — which is mathematically impossible but numerically routine when u and v are nearly equal — and getting nan back. A single nan in an embedding table poisons every subsequent batch through the shared parameters. Chapter 7 returns to this with the arithmetic of why it happens.

Two points sit at Euclidean radius 0.99, ninety degrees apart. Their hyperbolic distance is 9.89, while going out to each of them from the centre costs 5.29 each. What does the near-equality of 9.89 and 10.59 tell you?

Chapter 2: Exponential Room

Chapter 0 said the plane fails because supply grows linearly while demand grows exponentially. Chapter 1 built a space with a shrinking ruler. Now we cash the cheque: we compute the supply in that space and find that it, too, grows exponentially. Then a binary tree fits, and we will know exactly how much edge length to give it.

Deriving the circumference of a hyperbolic circle

Take the circle of true radius s centred at the origin. On the map it appears as an ordinary Euclidean circle of radius r = tanh(s/2) — we derived that inversion in Chapter 1. Its drawn circumference is 2πr. But every bit of that drawn arc sits at radius r, where the conformal factor is λ = 2/(1 − r2). So the true length is the drawn length times the factor:

L(s) = 2πr · 2/(1 − r2) = 4π · r / (1 − r2)   with   r = tanh(s/2)

Now use the identity 1 − tanh2u = 1/cosh2u (which is just cosh2 − sinh2 = 1 divided through by cosh2). Substituting u = s/2:

r/(1 − r2) = tanh(s/2) · cosh2(s/2) = sinh(s/2) · cosh(s/2) = ½ sinh s

where the last step is the double-angle identity sinh 2u = 2 sinh u cosh u. Put it together:

L(s) = 4π · ½ sinh s = 2π sinh s

Three lines of algebra, no magic. And by the same argument the area enclosed is A(s) = ∫0s 2π sinh t dt = 2π(cosh s − 1).

Why sinh is the whole point

Recall sinh s = (es − es)/2. For small s this is approximately s, so hyperbolic space looks flat close up — as any curved space does. For large s the second term dies and

L(s) ≈ 2π · es/2 = π es

The circumference grows exponentially with the radius. Every additional unit of radius multiplies the available perimeter by e ≈ 2.718. That is the supply curve we were missing.

radius sflat: 2πshyperbolic: 2π sinh sratio
16.287.381.2×
212.5722.791.8×
318.8562.943.3×
531.42466.214.8×
1062.8369,2001,101×
20125.71.52 × 10912,100,000×

At radius 20 — walk twenty steps from the root — a hyperbolic plane offers you a billion and a half units of perimeter to arrange things on. The flat plane offers 126. Same two coordinates per point.

Sanity check on the units. None of this is “adding dimensions in disguise.” A point in the Poincaré disk is still exactly two floats. The extra room comes from how those two floats are interpreted, not from how many there are. In a real embedding you store d floats and get the d-dimensional Poincaré ball, whose sphere area grows like e(d−1)s.

The fitting rule: e versus b

Now put demand and supply side by side, per level, for a tree with branching factor b and edge length .

demand at level k: bk nodes   ——   supply at radius kℓ: ≈ π ekℓ

Room per node is therefore ≈ π ekℓ / bk = π (e/b)k. Everything hinges on whether the base e/b exceeds 1:

room per node grows with depth  ⇔  e > b  ⇔  > ln b

That is the design rule, and it is actionable. A binary tree needs edges longer than ln 2 = 0.693. A ten-ary tree needs ln 10 = 2.303. A tree where one node has a thousand children needs ln 1000 = 6.91 per level. Choose the edge length above that threshold and the tree fits with room to spare, forever, at any depth.

Check it against a concrete case. Binary tree, = 1, so e/2 = 1.359 — room per node should grow by 36% per level:

depthleavesflat room per leafhyperbolic room per leaf
382.367.87
5320.9814.57
82560.19636.58
101,0240.06167.58
201,048,5760.000121,453.6

Read the two right-hand columns as two different worlds. One is collapsing toward zero; the other is expanding. And 14.57 / 7.87 = 1.85 = 1.3592, exactly the compounding the formula predicted over two levels. The arithmetic holds.

The image to keep. Hyperbolic space is not “a plane with more room stuffed into it.” It is a space that is shaped like a tree already. Sarkar’s construction, which achieves distortion 1 + ε in two dimensions, is barely a construction at all: give the root a full circle of angle, give each child an equal slice of its parent’s slice, and step outward by per level. That is it. The geometry does the packing for you.
The same tree, two geometries

Both panels place the identical tree by the identical rule — equal angular slice per child, one step outward per level. Left is flat, right is hyperbolic (drawn on the Poincaré map, so it looks crowded at the rim; the numbers underneath are the true distances). Watch the “closest pair of leaves” readout: flat collapses, hyperbolic does not.

depth4
branching b2
edge length ℓ1.0

Play with the edge-length slider until the rule bites

Set branching to 4 and edge length to 0.7. Since ln 4 = 1.386 and 0.7 < 1.386, the rule says this must fail — and the readout confirms it: the closest pair of leaves shrinks level by level, exactly like the flat case, just more slowly. Now push the slider past 1.4 and the collapse reverses. There is no gentle degradation around the threshold; it is a phase change between “shrinking geometrically” and “growing geometrically.”

This is the first genuinely useful engineering knob in the lesson. If your hierarchy has high-fan-out nodes — a category with 2,000 direct children is common in product catalogues — then a model that learns short edges near the root will crush those children together no matter how long you train. The fix is not more data; it is more radius, which in practice means letting norms grow (Chapter 3) or raising the curvature magnitude.

Why the pictures in the papers all look the same

Every Poincaré embedding figure you have seen has the same look: a few words floating near the middle and a dense confetti of words smeared around the rim. Newcomers read that as “the model collapsed everything to the boundary.” It did not. That appearance is required by the map.

Here is the arithmetic. Suppose two leaves at true radius 9.8 are 4.2 apart — three levels up to their common ancestor and three back down at = 0.7. Their stored radius is tanh(4.9) = 0.999889, so 1 − r2 = 0.000222. Two points at the same radius separated by angle φ satisfy

cosh d = 1 + 8r2 sin2(φ/2) / (1 − r2)2

(that is the general formula with ‖uv2 = 4r2 sin2(φ/2), the chord of an isoceles triangle). Solve for φ with d = 4.2, so cosh d = 33.35:

sin2(φ/2) = 32.35 × (0.000222)2 / 8 = 1.993 × 10−7
φ = 2 × arcsin(4.464 × 10−4) = 8.93 × 10−4 rad = 0.051°

Two concepts a comfortable four units apart are drawn one twentieth of a degree apart. On a 1000-pixel figure that is under a pixel. The confetti is the map projection, not the model. The same fact, seen from the other side, is Chapter 7’s numerical catastrophe: if 0.05° of angle carries four units of meaning, then losing a few bits of angular precision loses whole levels of the hierarchy.

Concept → realization: place a tree by hand, no training

You do not always need to learn an embedding. If you already have the tree, you can place it combinatorially in a few lines — this is Sarkar’s construction, and it is a good baseline and a good initialiser.

python
import numpy as np

def place(node, angle, half_width, depth, ell, out):
    """angle: centre of this node's angular slice (radians)
       half_width: half the slice this node owns
       depth: how many edges from the root; true radius = depth * ell"""
    r = np.tanh(depth * ell / 2)              # true radius -> stored radius
    out[node] = (r * np.cos(angle), r * np.sin(angle))
    kids = children[node]
    if not kids: return
    step = 2 * half_width / len(kids)         # split the slice evenly
    for i, k in enumerate(kids):
        a = angle - half_width + (i + 0.5) * step
        place(k, a, step / 2, depth + 1, ell, out)

out = {}
place(root, angle=0.0, half_width=np.pi, depth=0, ell=1.0, out=out)
# out[node] is now a 2-vector inside the unit disk. No gradients involved.

Trace the data flow: the tree structure goes in as children, an adjacency dict; what comes out is a dict of 2-vectors, each with norm strictly below 1 because tanh never reaches it. The only free parameter is ell, and Chapter 2’s rule tells you how to set it: above ln(max branching factor). Every parent–child pair ends up at true distance close to ell, and every leaf-to-leaf distance ends up close to the tree distance times ell. That is distortion near 1, in two dimensions, with no optimiser at all.

Why then does anyone train? Because most real data is not a clean tree given to you in advance. It is a noisy partial order with cycles, missing edges and multiple parents — and that is what Chapter 3 handles.

Your catalogue has a category with 500 direct children. Using edge length ℓ = 1.0 in a hyperbolic embedding, what does the fitting rule predict?

Chapter 3: Embedding WordNet

Time to train something. The 2017 paper by Maximilian Nickel and Douwe Kiela — Poincaré Embeddings for Learning Hierarchical Representations — is the origin of this whole line of work, and its setup is small enough to hold in your head entirely.

The data, exactly

WordNet is a hand-built lexical database. Its noun hierarchy is a hypernymy graph: an edge uv means “u is a kind of v”, as in dalmatiandog. Take the transitive closure — if dalmatian is a dog and a dog is a carnivore, add the edge dalmatiancarnivore directly. That gives

82,115 nouns   and   743,241 hypernymy pairs

Two remarks on that choice, because it is not innocent. First, taking the closure means the model sees dalmatianentity as a positive pair, so it is trained to place remote ancestors nearby too — which is what pulls generic words to the centre. Second, WordNet nouns are not a perfect tree; a few synsets have more than one parent. The model does not care. It never assumes a tree; it just fits distances to a set of pairs.

The objective, and why it is a ranking loss

For every observed pair (u, v) we want d(u, v) to be small relative to the distance from u to things that are not its hypernyms. Sample a handful of such non-neighbours — the negatives — and write a softmax over the negated distances:

ℓ(u,v) = − log   ed(u,v)  /  ∑v' ∈ N(u) ed(u,v')

where N(u) is the true v plus ten sampled negatives. If you have met InfoNCE or word2vec’s negative sampling, this is the same object with one substitution: the score is −d instead of a dot product. That substitution is the entire hyperbolic contribution at the loss level.

It is a relative criterion, which matters. Nothing in the loss says “put dalmatian at distance 0.7 from dog.” It only says “closer than to trombone.” The absolute scale — how far out the leaves end up — is decided by the geometry and the optimiser, not by you. That is a feature: the model discovers how much radius the hierarchy needs.

Worked example: one loss term, by hand

Take a positive pair currently at d = 1.2 and five sampled negatives at distances 2.8, 3.0, 3.5, 4.0 and 5.0. Exponentiate the negated distances:

e−1.2 = 0.30119   (the positive)
e−2.8 = 0.06081,   e−3.0 = 0.04979,   e−3.5 = 0.03020,   e−4.0 = 0.01832,   e−5.0 = 0.00674
negatives sum to 0.06081 + 0.04979 + 0.03020 + 0.01832 + 0.00674 = 0.16585
denominator = 0.30119 + 0.16585 = 0.46704
p = 0.30119 / 0.46704 = 0.6449  →  ℓ = −ln 0.6449 = 0.4387

Now suppose the positive drifts outward so d = 2.5. Then e−2.5 = 0.08209, the denominator becomes 0.24793, p = 0.3311 and the loss is −ln 0.3311 = 1.1054. A drift of 1.3 units cost us a factor of 2.5 in loss. The gradient will pull it back.

And notice the shape of the push on the negatives. Their gradient weights are their softmax shares:

nearest negative (d = 2.8): 0.06081 / 0.46704 = 0.1302
farthest negative (d = 5.0): 0.00674 / 0.46704 = 0.0144

The nearest negative gets a push nine times stronger than the farthest. The loss spends almost all of its effort on the confusions that are actually happening and essentially ignores the obviously-wrong candidates. This is why ten negatives is enough on a graph with 82,115 nodes: you do not need to push everything away, only the current intruders.

Riemannian gradient: why the plain gradient is the wrong direction

Here is the part that trips people up. Your autograd gives you ∂ℓ/∂θ — the derivative with respect to the stored coordinates. But coordinates are map coordinates, and we established in Chapter 1 that the map is distorted. Following the coordinate gradient is like navigating by a Mercator map and concluding that walking one centimetre near the pole covers the same ground as one centimetre at the equator.

The correction is exact. In the Poincaré ball the metric is λ2 times the identity, so converting a coordinate gradient into a true direction of steepest ascent means dividing by λ2:

gradR ℓ = (1 / λθ2) ∇E ℓ = ((1 − ‖θ2)2 / 4) · ∇E

because λ = 2/(1 − ‖θ2) means 1/λ2 = (1 − ‖θ2)2/4. Write α = 1 − ‖θ2 from here on. The update rule is then

θ ← proj ( θη · (α2/4) · ∇E ℓ ),    proj(θ) = θ/‖θ‖ · (1 − ε) if ‖θ‖ ≥ 1, else θ

with ε = 10−5. That is the whole optimiser. Two lines on top of SGD.

The rescale factor is enormous, and it has to be

The distance function’s own gradient has Euclidean norm 2/α — you can verify this on the radial case, where d = ln((1+r)/(1−r)) and dd/dr = 2/(1−r2). So the coordinate gradient blows up near the rim, and the factor α2/4 shrinks there. Multiply them:

Euclidean displacement per step = η · (α2/4) · (2/α) = η · α/2
true (hyperbolic) displacement = λ × that = (2/α) · ηα/2 = η

The true step length is η, everywhere, at every radius. That is the whole design goal of a Riemannian optimiser, achieved exactly. In coordinates, with η = 0.1:

θαrescale α2/4Euclidean steptrue step
0.50.750.14060.03750.1
0.90.190.009030.00950.1
0.990.01999.90 × 10−50.0009950.1
0.9990.0019999.99 × 10−70.00010.1

What happens if you skip it — two concrete disasters

Disaster one: plain SGD. At ‖θ‖ = 0.999 the raw gradient has norm 2/0.001999 = 1,000.5. A step of η = 0.1 moves the point 100 units in coordinates — a hundred times the radius of the entire disk. The projection catches it and slams it to 1 − 10−5. Every deep node ends up pinned at the rim in a single epoch, all of them at the same radius, hierarchy annihilated. The loss will not even look bad, because a rim full of points can still satisfy many relative ranking constraints.

Disaster two: plain Adam. Adam normalises away the gradient magnitude, so its coordinate step is about the learning rate regardless of radius — say 0.001. Convert to true distance by multiplying by λ:

at ‖θ‖ = 0.5:   0.001 × 2.667 = 0.0027 units
at ‖θ‖ = 0.999:   0.001 × 1000.5 = 1.0005 units

The same nominal learning rate moves outer nodes 375× faster in real distance than inner ones. At = 0.7 per level, one Adam step teleports a leaf 1.4 levels. Your leaves thrash while your root barely moves. This is the single most common reason a first hyperbolic experiment produces garbage, and the fix is one line: use a Riemannian optimiser (geoopt.RiemannianAdam and friends do the rescaling and the parallel transport of momentum for you).

One optimiser step, at your chosen radius

The dot is a node; the two arrows are the step that plain SGD would take (orange, coordinate gradient, unscaled) and the step Riemannian SGD takes (teal). Slide the node outward and watch the orange arrow explode out of the disk while the teal one shrinks to a speck — and the readout confirms the teal step covers the same true distance every time.

true radius of the node2.9
learning rate η0.10

Burn-in: the trick that looks like a hack and is not

Nickel and Kiela initialise every embedding uniformly in [−0.001, 0.001] — essentially all 82,115 nouns stacked on the origin — and then run ten epochs at one tenth of the learning rate before training properly. That is called the burn-in, and the reason it is necessary is a direct consequence of the geometry.

Ask how hard it is to change a node’s angle once it has drifted outward. Moving by Δφ radians at true radius s covers an arc of true length sinh(s) · Δφ. So to swing 0.1 radians:

true radius sθsinh sarc for 0.1 radsteps at η = 0.1
1.10.501.340.131.3
2.90.909.060.919
5.30.99100.010.0100
7.60.9991,000100.01,000

Angular mobility falls by roughly 750× between radius 1.1 and radius 7.6. Once a node has travelled out, it is angularly frozen — it can still slide radially, but it can no longer change which branch of the tree it belongs to. The angular layout is therefore decided early or not at all. Burn-in is a deliberate period in which everything stays near the origin, cheap to rotate, while the loss sorts out who belongs beside whom. Only then are norms allowed to grow and the hierarchy to stretch out.

Generalise the lesson. In hyperbolic training, radius is commitment. A node that has moved outward has bought specificity and sold mobility. Every practical recipe in this literature — burn-in, low learning rates, careful initialisation near the origin, curriculum from coarse to fine — is a variation on “decide the angles before you spend the radius.”

Concept → realization: the full training step

python
import torch

EPS = 1e-5

def project(x, eps=EPS):
    """Force every row strictly inside the unit ball. Shape in = shape out."""
    norm = x.norm(dim=-1, keepdim=True).clamp_min(1e-12)
    factor = (1 - eps) / norm
    return torch.where(norm >= 1, x * factor, x)

def rsgd_step(emb, lr):
    """Riemannian SGD on nn.Embedding weights living in the Poincare ball."""
    with torch.no_grad():
        w, g = emb.weight, emb.weight.grad          # both (n, d)
        alpha = 1 - w.pow(2).sum(-1, keepdim=True)  # (n, 1)
        w -= lr * (alpha.pow(2) / 4) * g              # THE metric correction
        emb.weight.data = project(w)
        emb.weight.grad = None

# ---- one training iteration ----------------------------------------
u  = emb(idx_u)                       # (B, d)      the child
v  = emb(idx_v)                       # (B, d)      the true hypernym
vn = emb(idx_neg)                     # (B, K, d)   K sampled non-hypernyms

d_pos = poincare_dist(u, v)                          # (B,)
d_neg = poincare_dist(u.unsqueeze(1), vn)             # (B, K)
logits = torch.cat([-d_pos.unsqueeze(1), -d_neg], 1)  # (B, 1+K)
loss = torch.nn.functional.cross_entropy(
           logits, torch.zeros(len(u), dtype=torch.long))  # target = index 0
loss.backward()
rsgd_step(emb, lr=0.1 if epoch >= burn_in else 0.01)

Trace the shapes once and the whole method is demystified. In: two index tensors and a negative-sample tensor. Out: a scalar. The only non-standard lines are poincare_dist (Chapter 1), the alpha.pow(2)/4 rescale, and project. Everything else is a vanilla contrastive setup.

The result that started the field

On WordNet noun reconstruction — embed the full transitive closure, then for each pair rank the true hypernym against all non-neighbours — the paper reports roughly:

Modeldimparametersmean rankMAP
Euclidean5410,575≈ 3,500≈ 0.02
Euclidean20016,423,000≈ 1,160≈ 0.17
Poincaré5410,575≈ 5≈ 0.82
Poincaré20016,423,000≈ 4≈ 0.87

The comparison to stare at is row 2 against row 3. Forty times fewer parameters, roughly five times the MAP. And the Poincaré model has already saturated at dimension 5 — going to 200 buys it almost nothing, because the hierarchy simply does not need more than a couple of dimensions once the curvature is right. The mean rank of about 5 means that on average the true hypernym is the fifth-closest point out of 82,115.

Chapter 4 opens that trained model up and reads it.

Why does Nickel & Kiela’s training run ten “burn-in” epochs at a tenth of the learning rate before real training?

Chapter 4: Reading a Poincaré Embedding

Training finished. You have 82,115 five-dimensional vectors, every one with norm below 1. What do they mean? A Euclidean embedding gives you one readout — distance means relatedness — and nothing else. A hyperbolic embedding gives you two, and the second one is free.

The two readouts. Distance between two points measures how related they are, and it is symmetric. Distance from the origin measures how specific a concept is, and it is not shared — it is a property of a single point. Generic sits at the centre; specific sits far out. Nobody supervised this. It emerges.

Why generality drifts to the centre — the mechanism, not the slogan

It is easy to say “the root ends up in the middle” and hard to say why, so here is the argument.

The training loss requires mammal to be close to all of its thousands of descendants. Those descendants are spread over a wide range of directions, because they are also being pushed apart from each other. So mammal must find a location that is simultaneously close to points spread over a wide angular fan.

In hyperbolic space that location is uniquely determined, and there is a formula for it. For two points at true radii su, sv separated by angle φ, once both radii are more than about 3, the distance is very accurately

d(u, v) ≈ su + sv + 2 ln sin(φ/2)

Check it against the exact calculation from Chapter 1: two points at r = 0.99 (true radius 5.2933) and φ = 90°. The formula gives 5.2933 + 5.2933 + 2 ln(0.7071) = 10.5866 − 0.6931 = 9.8935. The exact arcosh computation gave 9.89351. Agreement to five significant figures.

Now rewrite it. Define h = −ln sin(φ/2), a positive number that gets larger as the angle gets smaller. Then

d(u, v) ≈ su + sv − 2h

That is literally the distance formula in a tree: depth(u) + depth(v) − 2 × depth(lowest common ancestor). Hyperbolic geometry has handed us an LCA, and its depth is −ln sin(φ/2) — determined entirely by the angle between the two points. Points that share a direction share a deep ancestor; points on opposite sides share only the root.

So the embedding factorises. Angle encodes which branch. Radius encodes how far down that branch. Nobody designed this split; it is forced by the metric. And it tells you what the model is really learning: a soft assignment of directions to subtrees.

Worked example: placing a WordNet chain by hand

Take the real hypernym chain for dalmatian and give each level a true radius of 0.7 × its depth. Convert with ‖x‖ = tanh(s/2):

levelsynsettrue radius sstored norm tanh(s/2)
0entity0.00.000000
1physical entity0.70.336376
2object1.40.604368
5organism3.50.941376
9mammal6.30.996334
11carnivore7.70.999095
13dog9.10.999777
14dalmatian9.80.999889

Stare at the right-hand column. mammal and dalmatian are five levels apart — five whole steps of the hierarchy — and their stored norms differ by 0.999889 − 0.996334 = 0.0036. Between dog and dalmatian, one level, the difference is 0.000112.

Do not read the raw norm. The stored norm is a logarithmically compressed gauge; the top nine levels of the hierarchy live in the first 0.996 of it and the bottom five are crammed into the last 0.004. If you print ‖x‖ and eyeball it, everything below level 8 looks identical. Always convert: 2 artanh(‖x‖). That is the number that is linear in hierarchy depth, and it is one line of code.

Worked example: recovering the common ancestor from the angle

Put dalmatian and siamese cat in the picture. Both are at level 14, so true radius 9.8. Their lowest common ancestor in WordNet is carnivore at level 11, so a correct embedding should give them

d = 9.8 + 9.8 − 2(7.7) = 19.6 − 15.4 = 4.2

What angle does that require? Invert the tree formula: h = −ln sin(φ/2) = 7.7, so sin(φ/2) = e−7.7 = 4.5 × 10−4, giving

φ ≈ 2 × 4.5 × 10−4 = 9.0 × 10−4 rad = 0.051°

which is exactly the number Chapter 2 got by the other route, from the exact distance formula. Two independent calculations, same answer — the asymptotic tree formula and the exact metric agree.

And now the practical reading: an ancestor three levels up costs you a factor of e3×0.7 = 8 in angular resolution. Every level of depth you want to resolve divides the angular budget by e. That is the same exponential from Chapter 2, seen from inside the trained model.

Generality is radius — but not the radius you stored

The same fifteen-node chain shown three ways. Top: the Poincaré map, where the deep nodes pile onto the rim. Middle: the stored norm on a 0–1 ruler, which is useless below level 8. Bottom: the true radius 2 artanh(‖x‖), evenly spaced, one tick per level. Slide to select a node and read all three at once.

select level9
edge length ℓ0.7

Turning two readouts into a directed prediction

Distance is symmetric, so it alone cannot answer “is u a kind of v, or the other way round?” Nickel and Kiela combine the two readouts into an asymmetric score:

score(u is-a v) = − ( 1 + α(‖v‖ − ‖u‖) ) · d(u, v)

with a large constant α (they used 1000). Read it as: start from the negated distance, then penalise the hypothesis if the claimed parent v is further out than the claimed child u. Work an example with the chain above. dog has norm 0.999777, dalmatian 0.999889, and suppose their distance is 0.7.

“dalmatian is-a dog”:   −(1 + 1000(0.999777 − 0.999889))(0.7) = −(1 − 0.112)(0.7) = −0.622
“dog is-a dalmatian”:   −(1 + 1000(0.999889 − 0.999777))(0.7) = −(1 + 0.112)(0.7) = −0.778

Higher is better, so the correct direction wins by 0.156. It works — but look how thin the margin is, because the norm difference is only 0.000112 and needs a multiplier of a thousand to matter at all. This is a patch, not a principle: the geometry gives you an order, and we are reconstructing it from a scalar with a hand-tuned constant. Chapter 5 replaces the patch with a construction that encodes the order directly.

How the model is actually scored

Two protocols, and it is worth knowing exactly what they measure.

Reconstruction embeds the complete relation set and asks whether the geometry can represent it at all — a capacity test. For each observed pair (u, v) you rank v against all of u’s non-neighbours by distance, and record the rank.

Link prediction holds out a slice of edges and asks whether the geometry generalises — whether unseen is-a relations land in the right place. Reconstruction can be gamed by capacity; link prediction cannot.

Both report MAP, mean average precision. Compute an average precision by hand so the number stops being an abstraction. Suppose a node has three true hypernyms and, after sorting all candidates by distance, they appear at positions 1, 3 and 6:

at the 1st hit (rank 1): precision = 1/1 = 1.0000
at the 2nd hit (rank 3): precision = 2/3 = 0.6667
at the 3rd hit (rank 6): precision = 3/6 = 0.5000
AP = (1.0000 + 0.6667 + 0.5000) / 3 = 0.7222

MAP is that, averaged over all nodes. A MAP of 0.82 with mean rank 5 on 82,115 candidates means the true parent is essentially always in the top handful — the top 0.006% of the vocabulary.

What the norm does not tell you

Be honest about the limits of the readout, because “norm equals generality” is repeated far more often than it is qualified.

ConfoundWhat happensWhat to do
Frequency in the closureA node appearing in many pairs is pulled inward by sheer gradient volume, whether or not it is conceptually genericCompare norms only within a subtree, or normalise by node degree before interpreting
Multiple parentsA synset with two unrelated parents must sit between two angular regions, so it is dragged toward the centre and looks more generic than it isExpect DAG nodes to read as over-general; check the graph, not just the geometry
Unbalanced branchesA branch with 40,000 descendants claims more angle than one with 12, so absolute angles are not comparable across branchesOnly angular differences within a branch are meaningful
Nothing anchors the rootThe loss is invariant to the isometries of the ball, so a run can place the whole hierarchy off-centreIf you rely on norms, add a term pinning a known root at the origin, or re-centre after training

That last row deserves emphasis. The Poincaré ball has a rich group of distance-preserving maps (Möbius transformations) that move the origin anywhere. The loss cannot see them, so “the origin is the root” is a convention that training only approximately respects — it holds because initialisation starts everyone at the origin, not because the objective demands it. If your downstream code reads norms as depths, pin the root.

You print the norms of a trained 5-dim Poincaré embedding and find that mammal, dog and dalmatian all read as “about 1.00”. What went wrong?

Chapter 5: Entailment Cones — Order as Geometry

Chapter 4 ended on an awkward note. Hierarchy is an order: dalmatian is under dog, and dog is emphatically not under dalmatian. But distance is symmetricd(u,v) = d(v,u) by definition — so the direction had to be smuggled back in through a hand-tuned norm term.

Octavian Ganea, Gary Bécigneul and Thomas Hofmann asked the better question in 2018: what geometric object is asymmetric by construction? Their answer is a cone. Attach to every point x a region C(x) spreading outward from it, and define

v is a descendant of u  ⇔  vC(u)

Membership in a set is not symmetric. The direction is now built into the geometry rather than bolted onto a score, and “list everything under mammal” becomes an actual geometric query instead of a threshold on a distance.

What the cone field must satisfy

You cannot draw arbitrary cones. If this is to model a hierarchy, the relation must be a partial order, and the crucial requirement is transitivity: if w is under v and v is under u, then w must be under u. In cone language that says

vC(u)  ⇒  C(v) ⊆ C(u)

— the cones must nest. A cone can never poke out of a cone it sits inside. That is a strong constraint, and it is what determines the shape. If you also ask for cones that are symmetric about the outward radial direction (so that “downward” means “away from the root”), the nesting requirement pins the half-aperture to a single functional form:

ψ(x) = arcsin  ( K (1 − ‖x2) / ‖x‖ )

with a single free constant K; the paper uses K = 0.1. Everything about the behaviour of entailment cones is in that fraction (1 − r2)/r, which is large near the origin and small far out.

xK(1−r2)/rhalf-aperture ψreading
0.100.99081.9°near the root: the cone is almost a half-plane
0.200.48028.7°a broad category
0.500.1508.63°a mid-level concept
0.900.02111.21°a specific one
0.990.002010.115°a leaf: a needle that contains almost nothing

That table is the hierarchy, written as a picture. Generic concepts own wide cones and therefore many descendants; specific concepts own needles and therefore almost none. And you get the aperture for free from the position — there is no per-node width parameter to learn.

Why the aperture must shrink. Suppose it did not, and a point far out had a wide cone. Its cone would spill outside the cone of its own ancestor, and the relation would stop being transitive: you would be able to find a w under v under u with w not under u. Shrinking apertures are not a modelling choice; they are the price of a consistent order.

There is a hole at the centre, and it is unavoidable

For arcsin to be defined we need K(1 − r2)/r ≤ 1. Solve Kr2 + rK ≥ 0 for the positive root:

rε = 2K / (1 + √(1 + 4K2)) = 0.2 / (1 + 1.0198) = 0.0990

So with K = 0.1 no point may sit within about 0.099 of the origin — there is a small forbidden disk in the middle. The interpretation is honest and worth stating: there is no single top element. The cone construction cannot represent a node that entails literally everything, because that would need an aperture of 180°, which the formula caps at 90° near the hole’s edge. In practice you place the root just outside the hole and accept that it is an extremely general concept rather than a universal one.

The angle you have to measure

To test membership you need the angle at x between two directions: the outward radial direction (the cone’s axis) and the geodesic heading from x toward y. Call it Ξ(x, y). Because geodesics are circular arcs, the closed form is not pretty, but it is elementary:

Ξ(x,y) = arccos   ⟨x,y⟩(1 + ‖x2) − ‖x2(1 + ‖y2)  /  ‖x‖ ‖xy‖ √(1 + ‖x2y2 − 2⟨x,y⟩)

Do not accept a formula like that without testing it. Take x = (0.3, 0) and y = (0.6, 0) — y is directly outward from x, so the answer must be exactly zero.

x,y⟩ = 0.18,   ‖x2 = 0.09,   ‖y2 = 0.36,   ‖xy‖ = 0.3
numerator = 0.18(1.09) − 0.09(1.36) = 0.1962 − 0.1224 = 0.0738
denominator = 0.3 × 0.3 × √(1 + 0.0324 − 0.36) = 0.09 × √0.6724 = 0.09 × 0.82 = 0.0738
ratio = 1.0000  →  Ξ = arccos(1) =  ✓

The formula survives its own sanity check, numerator and denominator agreeing to four digits by hand.

The energy, and a worked pair

Membership is the test Ξ(x,y) ≤ ψ(x). Turn it into something differentiable by measuring the violation:

E(x, y) = max ( 0,   Ξ(x,y) − ψ(x) )

Zero when y is inside x’s cone; otherwise the number of radians y would have to swing to get in. Work an example. With x = (0.3, 0) the aperture is ψ = arcsin(0.1 × 0.91 / 0.3) = arcsin(0.3033) = 0.3082 rad = 17.66°.

candidate yΞ(x,y)E = max(0, Ξ − 0.3082)verdict
(0.60, 0.00)0.00°0.000inside — a true descendant
(0.50, 0.20)49.0°0.548 rad (31.4°)outside by 31°
(0.35, 0.30)86.3°1.198 rad (68.6°)far outside — sideways, not below

Note the third row: (0.35, 0.30) is at a larger radius than x and quite close to it in ordinary distance, yet it is emphatically not a descendant. Radius alone would have said “more specific, therefore probably below.” The cone says “more specific, but down a different branch,” which is the distinction Chapter 4’s norm-based score could only approximate.

The loss

Positives should have zero energy; negatives should have energy above a margin γ:

L = ∑(u,v) positive E(u,v)  +  ∑(u',v') negative max (0, γE(u',v'))

This is a max-margin hinge, and it behaves differently from Chapter 3’s softmax in a way that matters. The softmax always has gradient — every negative contributes something forever. The hinge switches off: once a positive is inside the cone and a negative is γ outside, they produce exactly zero gradient. The model stops pushing things it has already got right, which is why cone training converges to a crisp configuration instead of a perpetual tug-of-war.

The cost of that crispness is a cold start. If the initial layout is random, most positives are wildly outside their cones and most negatives are too, so the gradient signal is dominated by noise and the geometry has no reason to organise. Ganea et al. therefore initialise from a trained Poincaré embedding and let the cone loss refine it. Read that as a two-stage recipe: distance-based training to arrange the space, then cone-based training to sharpen the order.

Cones, nesting, and the energy readout

Drag the orange point to move the cone — its aperture is computed from its radius, not chosen. The teal point is the candidate; its label shows Ξ, ψ and the energy. The faint inner cone belongs to a point on the axis further out, drawn so you can watch it stay swallowed by the outer cone at every position: that containment is transitivity, made visible. The grey disk at the centre is the forbidden region of radius 0.099.

Cones versus distance, honestly

Poincaré distance (Ch 3)Entailment cones (Ch 5)
Encodes orderindirectly, via a norm term with a tuned constantdirectly, as set membership
Transitivityapproximate, emergentexact, by the nesting property
“All descendants of xno clean query — you threshold a distance and hopea geometric region; test each candidate’s angle
Gradient behaviouralways on (softmax)switches off once satisfied (hinge)
Initialisationrandom near the origin worksneeds a pretrained distance embedding
Top elementthe origin works fineimpossible — a forbidden hole of radius 0.099
Where it winsgeneral relatedness, retrieval by similaritypredicting unseen is-a edges from few observed ones

The reported gains are concentrated exactly where you would predict from that table: in link prediction with sparse supervision. Train on only the direct WordNet edges — no transitive closure — and ask the model to infer the closure. A distance model has to be told that dalmatian is close to entity; a cone model gets it from nesting, because dalmatianC(dog) ⊆ … ⊆ C(entity) follows from the geometry. As you reveal more of the closure during training, the advantage narrows — the distance model can eventually be shown everything the cone model deduced.

Why must the half-aperture of an entailment cone shrink as the point moves away from the origin?

Chapter 6: MERU — Hyperbolic Vision-Language

Everything so far has been about explicit hierarchies: WordNet, a taxonomy, a graph you were handed. The 2023 paper Hyperbolic Image-Text Representations — the model is called MERU — asks a bolder question. Images and their captions form a hierarchy too, one nobody wrote down. Can a vision-language model discover it?

The hierarchy nobody labelled

Look at a photograph of a dalmatian asleep on a red sofa in a sunlit room. Now list some true captions:

CaptionHow many images does it describe?
“a photo”essentially all of them
“a dog”millions
“a dalmatian sleeping”thousands
“a dalmatian asleep on a red sofa in the sun”a handful
the image itselfexactly one

That is a chain from general to specific, and it points one way. Text is more generic than the images it describes — a caption always underspecifies, because it omits the exact lighting, the exact pose, the exact fabric of the sofa. An image is a leaf; a caption is an interior node.

Why CLIP cannot represent that

CLIP encodes an image and a caption, L2-normalises both, and trains with a contrastive loss on cosine similarity. That normalisation is not a detail — it is a geometric commitment. It forces every embedding onto the surface of a unit sphere.

The consequence, stated plainly: on a sphere, every point has the same norm. There is no “further out.” There is no room for “more specific.” CLIP’s space is perfectly symmetric between the two modalities — the caption “a dog” and a specific photo of a dog occupy structurally identical positions, and the only thing the model can say about them is an angle. A relation that is inherently asymmetric has been embedded in a space with no way to express asymmetry.

You see the symptom in practice. Ask CLIP for the images nearest to “a dog” and you get dogs; ask for the captions nearest to a dog photo and you get dog captions. Fine. But there is no operation that says “give me the more general concept” or “is this caption a valid generalisation of that one?” The space simply does not have that axis.

MERU’s change: one space, one root, two encoders

MERU keeps the two encoders (a vision transformer and a text transformer) and keeps the contrastive objective. It changes the space and adds one loss term.

image / text encoder
ViT or text transformer → a vector in Rn
treat as a tangent vector at the root
the vector is a direction and a length measured from the origin of the space
exponential map
exp0(v) lands it on the hyperboloid; the length of v becomes the distance from the root
contrastive loss + entailment loss
logits from −d/τ instead of cosine/τ; plus a cone term making text entail its images

The Lorentz model, and why they switched off the disk

MERU does not use the Poincaré ball. It uses the Lorentz model (also called the hyperboloid model), and the reason is numerical — a preview of Chapter 7.

In the Lorentz model a point of n-dimensional hyperbolic space is a vector in Rn+1 with one special “time” coordinate x0, constrained to lie on the sheet

x02 + ‖xspace2 = −1/c,    x0 > 0

where c > 0 is the curvature magnitude (MERU makes it learnable). Only the n space coordinates are stored; the time coordinate is recomputed as x0 = √(1/c + ‖xspace2). The distance is

d(x,y) = (1/√c) arcosh ( −cx,yL ),    ⟨x,yL = −x0y0 + ⟨xspace, yspace

The contrast with the disk is the point. In the Poincaré ball, moving far from the root drives ‖x‖ toward 1 and the quantity 1 − ‖x2 toward zero — a catastrophic cancellation, two nearly equal numbers subtracted, losing precision exactly where your leaves live. In the Lorentz model, moving far from the root makes the coordinates grow: a point at distance s has x0 = cosh(s)/√c. Large numbers keep their relative precision. Nothing is cancelling.

Worked example: the tangent norm is the specificity

The exponential map at the root, with c = 1, is

exp0(v) =  ( cosh‖v‖,   sinh‖v‖ · v/‖v‖ )

Take an encoder output with ‖v‖ = 0.8. Then cosh(0.8) = 1.33743 and sinh(0.8) = 0.88811. Check the constraint first — it should land on the sheet:

−(1.33743)2 + (0.88811)2 = −1.78872 + 0.78874 = −0.99998  ✓

(exactly −1 up to rounding, since cosh2 − sinh2 = 1). Now its distance from the root, which is the point (1, 0, …, 0):

x, root⟩L = −1.33743 × 1 + 0 = −1.33743
d = arcosh(1.33743) = 0.8
The clean statement. The norm of the encoder’s raw output vector is exactly the distance from the root, which is exactly the specificity. Your ViT’s output length — a number you previously threw away by normalising — becomes the model’s estimate of how specific the input is. That is the single most elegant consequence of the whole design.

The entailment term

Contrastive loss alone would place matching pairs near each other and say nothing about order, so MERU adds a cone term of exactly Chapter 5’s shape, transplanted to the hyperboloid. The half-aperture at x is

aper(x) = arcsin ( 2K / (√c · ‖xspace‖) ),   K = 0.1

— again shrinking as you move outward, again with a forbidden region near the root (you need ‖xspace‖ ≥ 2K = 0.2 for the arcsin to exist). At ‖v‖ = 0.8 the space norm is sinh(0.8) = 0.888, so

aper = arcsin(0.2 / 0.888) = arcsin(0.2252) = 0.2271 rad = 13.0°
and at ‖xspace‖ = 5:   arcsin(0.04) = 2.29°
It is the same formula as Chapter 5. Substitute ‖x‖ = tanh(s/2) into Ganea’s Poincaré aperture: (1 − tanh2(s/2))/tanh(s/2) = sech2(s/2)/tanh(s/2) = 1/(sinh(s/2)cosh(s/2)) = 2/sinh s. So ψ = arcsin(2K/sinh s) — exactly MERU’s aperture, since ‖xspace‖ = sinh s on the hyperboloid. Two papers, two models, one function. Check the arithmetic: at s = 0.8 the Lorentz version gave 13.01°, and the disk version at r = tanh(0.4) = 0.37995 gives arcsin(0.1 × 0.85564 / 0.37995) = arcsin(0.22520) = 13.01°.

The loss then penalises max(0, exterior angle − aperture) for each (text, image) pair, with the text as the apex. In words: every caption must contain its images inside its cone. Since a narrower cone means a smaller radius is needed to contain a given spread, the pressure pushes text inward and images outward, and the general-to-specific axis organises itself.

MERU’s space, and what CLIP’s cannot do

Left: CLIP — everything L2-normalised onto one circle (pink = captions, blue = images), so a generic caption and a specific photo sit at the same radius and only angle distinguishes them. Right: MERU — the root at the centre, the caption at a radius set by its specificity, images further out, and the caption’s cone containing the images it entails. Slide the specificity and watch the cone narrow and shed images. The cone constant is drawn at K = 0.45 rather than the real 0.1, purely so the wedge is visible at this scale; with K = 0.1 every cone here would be a needle.

caption specificity (distance from root)1.20

What it bought, stated without hype

MERU was trained on RedCaps, a public corpus of roughly 12 million image–text pairs, against a CLIP baseline trained on the identical data with the identical encoders — the only honest way to attribute a difference to the geometry. Two findings:

ClaimVerdict
Better zero-shot accuracy across the boardNo. Broadly comparable to the matched CLIP baseline — competitive, sometimes better on retrieval, not a landslide
An interpretable general-to-specific axisYes, and this is the real result. Walk an image embedding toward the root and you pass through progressively more generic captions from the corpus — a traversal that has no analogue in CLIP
Better dimension efficiency, as on WordNetNot demonstrated at scale. Image-text data is not a clean tree, and the room advantage that dominated WordNet does not obviously transfer

That is the mature reading. The geometry did not make the model smarter; it made the model’s representation structured, and gave it an operation — “generalise” — that a spherical space cannot express at all. Whether that structure is worth the engineering is exactly the question of Chapter 7.

Concept → realization: the encoder head that changes

python
import torch

# ---- CLIP head: normalise onto the sphere, score with cosine -------
z_img = torch.nn.functional.normalize(img_enc(pixels), dim=-1)   # (B, n), ||z|| = 1
z_txt = torch.nn.functional.normalize(txt_enc(tokens), dim=-1)   # (B, n), ||z|| = 1
logits = z_img @ z_txt.T / tau                                 # (B, B)

# ---- MERU head: do NOT normalise; lift onto the hyperboloid -------
v_img = img_enc(pixels)            # (B, n) — the NORM now carries specificity
v_txt = txt_enc(tokens)            # (B, n)

def exp_map0(v, c):
    """Tangent vector at the root -> space coords on the hyperboloid. (B, n)"""
    n = v.norm(dim=-1, keepdim=True).clamp_min(1e-8)
    return torch.sinh(c.sqrt() * n) * v / (c.sqrt() * n)

def lorentz_dist(x, y, c):
    x0 = (1/c + x.pow(2).sum(-1)).sqrt()     # time coord, recomputed not stored
    y0 = (1/c + y.pow(2).sum(-1)).sqrt()
    ip = -x0 * y0 + (x * y).sum(-1)         # Lorentzian inner product
    return torch.acosh((-c * ip).clamp_min(1 + 1e-7)) / c.sqrt()

x_img, x_txt = exp_map0(v_img, c), exp_map0(v_txt, c)
logits = -lorentz_dist(x_img.unsqueeze(1), x_txt.unsqueeze(0), c) / tau   # (B, B)
loss = contrastive(logits) + w * entailment(x_txt, x_img, c)   # text is the apex

The diff is four lines and one deletion. The deletion is the important one: removing normalize. Everything else follows from letting the encoder’s output length mean something.

Why does MERU work in the Lorentz (hyperboloid) model rather than the Poincaré ball?

Chapter 7: When It Wins, and When It Is a Gimmick

Six chapters of enthusiasm. Now the audit. Hyperbolic embeddings are one of the few ideas in representation learning with a genuinely principled motivation, and they are also one of the most over-applied. This chapter gives you a measurement to run before you commit, an honest accounting of what it costs, and the arithmetic of the failure mode that catches everyone.

The measurement: is your data actually tree-like?

“Tree-like” is not a vibe; it is a measurable quantity called Gromov δ-hyperbolicity. Take any four points w, x, y, z and form the three ways of pairing them up:

S1 = d(w,x) + d(y,z)    S2 = d(w,y) + d(x,z)    S3 = d(w,z) + d(x,y)

The four-point condition says: the two largest of those three sums differ by at most 2δ. The smallest δ that works for every quadruple is the space’s δ-hyperbolicity. Two hand calculations settle what it means.

Case 1 — four leaves of a binary tree. Leaves a, b are siblings; c, d are siblings in the other half. So d(a,b) = 2, d(c,d) = 2, and every cross pair is 4.

S1 = 2 + 2 = 4,   S2 = 4 + 4 = 8,   S3 = 4 + 4 = 8
two largest: 8 and 8  →  difference 0  →  δ = 0

Case 2 — a four-cycle. Four nodes in a square with unit edges: adjacent pairs are 1 apart, opposite pairs are 2 apart.

S1 = 1 + 1 = 2,   S2 = 2 + 2 = 4,   S3 = 1 + 1 = 2
two largest: 4 and 2  →  difference 2  →  δ = 1

Normalise by the diameter to compare across datasets: δrel = 2δ/diameter. The tree gives 0. The square gives 2 × 1 / 2 = 1, the maximum. A square is the purest possible non-tree: there are two equally good routes between opposite corners, and a tree never offers a choice of routes.

The pre-flight check. Sample a few thousand quadruples from your graph or your existing embedding, compute δrel, and look at the distribution. Near 0 ⇒ the negative-curvature story applies and you should expect real gains at low dimension. Near 1 ⇒ your data is full of squares — grids, meshes, dense communities, generic feature similarity — and hyperbolic geometry has nothing to offer it. Run this before the experiment, not after it disappoints you.

Where the win actually is: dimension, not accuracy

The most common overclaim is “hyperbolic embeddings are more accurate.” The precise claim is narrower and more useful: hyperbolic embeddings reach a given quality at far lower dimension. Plot WordNet reconstruction MAP against dimension and the shape is unmistakable.

Quality against dimension — where the advantage lives

WordNet noun reconstruction, as reported by Nickel & Kiela. The Poincaré curve is essentially flat: it has already won at five dimensions and two hundred adds nothing. The Euclidean curve is still climbing at two hundred and has not reached where hyperbolic started. Toggle to see the same axes on data that is not tree-like, where the two geometries converge and the extra machinery is pure cost.

Read the practical consequence off the chart. If you are storing 82,115 vectors, the difference between 5 and 200 dimensions is 410,575 floats versus 16,423,000 — 1.6 MB versus 66 MB, and a fortieth of the memory bandwidth on every lookup. For a taxonomy served at query time inside a recommender or a retrieval reranker, that is the entire business case. For a research benchmark where you were going to use 768 dimensions anyway, there is no business case at all.

The engineering tax, itemised

Every one of these is real, and none of them appears in the abstract of a paper.

What breaksWhyCost
Your optimiserPlain SGD and Adam ignore the metric (Chapter 3); momentum needs parallel transport between tangent spacesLow — use geoopt or write twenty lines
Your averagingThere is no closed-form mean. The Fréchet mean minimises ∑d(x,xi)2 and must be found by iterationMedium — no mean-pooling, no k-means centroids, no batch-norm without redefining it
Your vector databaseHNSW, IVF, ScaNN and pgvector index inner products or L2. Hyperbolic distance is neither, and no monotone rewriting turns it into oneHigh — you lose approximate nearest neighbour search entirely, which for many products is the whole point
Your linear layersA matrix multiply does not map the ball to the ball. Hyperbolic networks use Möbius operations or map to the tangent space and backMedium — every layer type needs a hyperbolic analogue, and the tangent-space version is an approximation
Your precisionSee below. This is the one that produces silent wrongness rather than an errorHigh

The precision ceiling, computed

At true radius s, the quantity everything depends on is α = 1 − ‖x2 = sech2(s/2) ≈ 4es. It decays exponentially. Meanwhile your float type can only resolve numbers near 1 down to its machine epsilon. Set them equal:

float32:   4es = 1.19 × 10−7  →  smax = ln(3.36 × 107) = 17.3
float64:   4es = 2.22 × 10−16  →  smax = ln(1.80 × 1016) = 37.4

Past that radius, 1 − ‖x2 rounds to exactly zero and your distances become inf and then nan. And it is worse than a hard wall, because the relative error in α is εmachine/α = ε es/4, which degrades long before the wall:

true radius sα ≈ 4esfloat32 relative error in αusable?
52.7 × 10−24 × 10−6fine
101.8 × 10−47 × 10−4fine
151.2 × 10−60.1010% error — distances are noise
17.31.2 × 10−71.0dead

Now convert to hierarchy levels, which is what you actually care about. Chapter 2 said a tree with branching factor b needs edge length > ln b. So the number of levels you can afford is smax / :

branching factorminimum = ln blevels in float32 (17.3)levels in float64 (37.4)
20.692554
102.30716
1004.6138
This is the trap. Wide hierarchies force long edges, and long edges eat the precision budget. A product taxonomy with a hundred children per node gets three usable levels in float32. Everything below that is numerically indistinguishable, and your model will report a healthy-looking loss the whole time, because the loss is computed from the same broken distances. Symptoms: nan appearing mid-epoch; deep nodes all reading the same norm; two obviously different leaves returning identical distances. Mitigations: compute the distance in float64 even if you store in float32, or use the Lorentz model, or reduce the curvature magnitude so the same tree needs less radius.

And be clear that the Lorentz model is a different trade, not a free lunch. There the coordinates grow like cosh(s) so far-apart points are fine — but the distance is arcosh(−cx,yL), and for two nearby points that inner product is a difference of two huge and nearly equal numbers. The cancellation moved from “far from the origin” to “close together.” No model of hyperbolic space is uniformly well-conditioned; pick the one whose bad case you do not have.

The decision procedure

QuestionAnswer that says goAnswer that says stop
Is the relation an order (is-a, part-of, contains, reports-to)?Yes — and you need the directionIt is plain similarity
What is δrel on a sample of quadruples?LowNear 1
Is low dimension a real constraint (serving cost, memory, an on-device model)?Yes — you want 8 dims, not 768You were going to use 768 anyway
Do you need approximate nearest neighbour search at scale?No, or the catalogue is small enough for brute forceYes — this alone is usually decisive
How deep and how wide is the hierarchy?Depth × ln(branching) comfortably under 17Over it — budget float64 and expect pain
Do you have a Euclidean baseline at the same dimension?Yes, and it is clearly worseNo — then you do not have a result

That last row is the one people skip. The literature is full of hyperbolic variants reported against Euclidean baselines at a different dimension, a different optimiser, or a different training budget. The geometry is a hypothesis about the data, and hypotheses need controls. Run the matched baseline before you believe your own result — the way MERU ran a CLIP baseline on identical data and reported honest parity.

Your product taxonomy has depth 12 and up to 100 children per node, and you plan to train a float32 Poincaré embedding. What does the arithmetic predict?

Chapter 8: Geometry Is a Design Axis

The narrow lesson of this course is “use hyperbolic space for hierarchies.” The broad lesson is much more useful: the space is a hyperparameter, you have always been setting it, and until now you were setting it by default.

One formula for all three

Chapter 2 measured a space by the circumference of a circle of radius r. That single measurement organises every constant-curvature geometry into one family:

L(r) = 2π sK(r),    sK(r) = sin(√K r)/√K  (K > 0),    r  (K = 0),    sinh(√−K r)/√−K  (K < 0)

Three formulas, one knob. Turn K positive and the circumference eventually turns around and shrinks back to zero — you are on a sphere and you have come back to the far side. Set it to zero and you get the straight line 2πr. Turn it negative and you get exponential growth.

CurvatureRoom at radius rWhat fits naturallyWhere you have already seen it
Positive (sphere)2π sin r — grows, peaks, shrinks; total volume finiteDirections, cyclic quantities, anything where “opposite” is meaningful and there is a bounded amount of stuffEvery L2-normalised embedding you have ever trained. Cosine similarity is spherical geometry
Zero (Euclidean)r — linear; volume polynomialAdditive composition, translations, independent factorsword2vec analogies, PCA, most feature spaces
Negative (hyperbolic)2π sinh r — exponentialTrees, taxonomies, anything whose neighbourhoods grow exponentiallyThis lesson
The realisation that reframes your whole stack. You have never used a Euclidean embedding for retrieval. The moment you L2-normalise and score with cosine, you moved onto a sphere — a positively-curved space with finite total volume. Nobody calls it a geometric choice, but it is one, and it is the reason a sphere is a bad home for a deep hierarchy: on a sphere there is no “further out” at all, which is precisely the complaint Chapter 6 made about CLIP.
One knob, three geometries

The circumference of a circle of radius r, as a function of curvature. Drag K from positive (the curve bends over and returns to zero — a sphere closes up) through zero (the straight line) to negative (exponential blow-up). The dashed marker reads off how much room you get at radius 5, which is the number that decides whether your tree fits.

curvature K-1.00

Two more geometries that are not curvature at all

Curvature is one axis. There is a second family that encodes order without any curvature, and it is worth knowing because it often beats hyperbolic space on the specific job of predicting order.

Order embeddings (Vendrov et al., 2016) put every concept in the non-negative orthant and define

uv  (“u is a kind of v”)  ⇔  uivi for every coordinate i

with the violation penalty E(u,v) = ‖max(0, vu)‖2. Work it: let dalmatian = (3.0, 5.0, 2.0) and dog = (1.0, 4.0, 2.0).

E(dalmatian, dog) = ‖max(0, (−2, −1, 0))‖2 = ‖(0,0,0)‖2 = 0  — satisfied
E(dog, dalmatian) = ‖max(0, (2, 1, 0))‖2 = 4 + 1 + 0 = 5  — violated

Asymmetric, transitive by construction (coordinatewise ≥ is a partial order), and the origin is a universal top element with no forbidden hole. Notice what this is, geometrically: an entailment cone with a fixed aperture, axis-aligned, the same everywhere. Chapter 5’s hyperbolic cone is the same idea with the aperture allowed to depend on position — which is what buys the exponential room.

Box embeddings (Vilnis et al., 2018) go one step further and represent each concept as an axis-aligned box, with containment as the order and volume as probability:

P(u | v) = vol(box(u) ∩ box(v)) / vol(box(v))

Worked: let box(dog) = [0,4] × [0,3], area 12, and box(dalmatian) = [1,2] × [0,3], area 3, entirely inside it. The intersection has area 3, so

P(dalmatian | dog) = 3/12 = 0.25     P(dog | dalmatian) = 3/3 = 1.00

A calibrated conditional probability, in both directions, from a volume ratio. Neither the Poincaré distance nor the cone energy can give you that — they give an order, not a measure. The price is a notorious optimisation problem: when two boxes are disjoint the intersection volume is exactly zero, so the gradient is exactly zero and they never find each other. The fix is smoothed or Gumbel boxes, which soften the corners so a gradient survives.

Product spaces (Gu et al., 2019) refuse to choose. Represent a point as a tuple across several factors — say two hyperbolic planes, a sphere and eight Euclidean dimensions — and define the squared distance as the sum of the squared distances in each factor. Real data is rarely uniformly one shape: a social graph is tree-like around hubs and grid-like inside dense communities. The signature (how many factors of each curvature) becomes something you search over, guided by the local δrel from Chapter 7.

The choosing table

If your data’s structure is……useBecause
Direction or similarity, bounded amount of stuffSphere (L2-normalise + cosine)Finite volume matches finite variety; the default for good reason
Additive, compositional, translation-likeEuclideanVector addition means something there and nowhere else
A deep tree or taxonomy, and you need low dimensionHyperbolicExponential room matches exponential branching
A partial order where you must predict unseen edgesEntailment cones or order embeddingsTransitivity is enforced by the geometry, so the closure comes free
A partial order where you need calibrated probabilitiesBox embeddingsVolume is a measure, so ratios are probabilities
A mixture, or you genuinely do not knowProduct space, and measure δrelLet the signature be a hyperparameter rather than an assumption
The transferable habit. Before choosing a loss, ask one question about your data: how fast does the number of things within distance k of a point grow with k? Linear or polynomial → flat. Saturating → spherical. Exponential → hyperbolic. That single measurement tells you which space can hold your data without lying, and it costs an afternoon to run. Nearly every “the embedding is mushy at the bottom” bug is a mismatch between that growth rate and the geometry you defaulted into.
Your team L2-normalises all embeddings and scores with cosine similarity, then complains that the model cannot express “this caption is more general than that one”. What is the geometric diagnosis?

Chapter 9: Connections

Nothing new here — just the map of where this sits, what it does not solve, and where to go next.

The lesson in eight sentences

ChapterThe one thing
0A tree’s demand grows exponentially with depth; flat space’s supply grows polynomially with radius. Depth 5 already breaks 2D, and no dimension count fixes it.
1Declare that a ruler at radius r is 2/(1−r2) times longer than it looks. Everything else follows — including d(0,x) = ln((1+r)/(1−r)) and an infinitely distant rim.
2Circumference is 2π sinh r, so room grows by a factor of e per unit radius. A b-ary tree fits precisely when the edge length exceeds ln b.
3Same parameters, new distance, and a gradient rescaled by (1−‖θ2)2/4 so that every step covers a constant true distance. Five dimensions beat Euclidean two hundred on WordNet.
4Angle encodes which branch; radius encodes how far down it. Distance decomposes as su + sv − 2h, the tree formula, with h = −ln sin(φ/2).
5Distance is symmetric but hierarchy is not. Cones make the order geometric, and transitivity forces the aperture to shrink as arcsin(K(1−r2)/r).
6Text is more generic than the images it describes. Stop normalising, and the encoder’s output norm becomes the distance from the root — the specificity.
7–8Measure δrel first. The win is dimension efficiency on tree-like data, not accuracy in general, and the bill includes your optimiser, your averaging, your ANN index and your float type.

Where to go next on this site

LessonWhy it follows
Vector EmbeddingsThe Euclidean and spherical baseline this lesson is arguing with. Read it first if “embedding” is still fuzzy.
Similarity MetricsCosine, dot product, L2 — and now you can see each of them as a commitment to a geometry rather than a formula choice.
Contrastive Learning & CLIPThe exact model MERU modifies. Chapter 6 only makes full sense once you have seen the sphere-and-temperature setup in detail.
Vector DatabasesThe ANN indexes — HNSW, IVF, product quantisation — that hyperbolic distance cannot use. Chapter 7’s heaviest cost lives here.
CS224W — Node EmbeddingsDeepWalk and node2vec, the graph-embedding tradition that Poincaré embeddings were competing with.
CS224W — Knowledge GraphsTransE, RotatE and friends: another family that encodes relations as geometric operations, and where box and hyperbolic variants both show up.
Graph Neural NetworksHyperbolic GCNs put message passing in this space; understanding why aggregation needs a tangent-space detour requires this lesson first.
Optimization on ManifoldsRiemannian gradients, retractions and parallel transport in general — Chapter 3’s rescale is the simplest instance of a large subject.
Embedding BenchmarksHow to run the matched baseline Chapter 7 insists on before you believe a geometry claim.

Honest limitations

Four things this lesson taught you to do, and the reasons each might not be the right move.

The moveThe catch
Embed the taxonomy in the Poincaré ballIf the taxonomy is a DAG with many multi-parent nodes, they get dragged to the centre and read as falsely generic (Chapter 4). Check how tree-like it really is.
Use 5 dimensions instead of 200You lose the ability to encode anything besides the hierarchy. Real systems usually want hierarchy and topical similarity and popularity in the same vector — which is an argument for a product space, not a pure hyperbolic one.
Add an entailment cone lossCones need a pretrained distance embedding to start from, and they cannot represent a universal top element. Budget two training stages.
Adopt hyperbolic CLIPMERU bought structure and interpretability at parity, not a leap in accuracy. If your metric is zero-shot accuracy alone, the honest expected gain is roughly zero.

References

  1. Nickel, M. & Kiela, D. “Poincaré Embeddings for Learning Hierarchical Representations.” NeurIPS, 2017. arXiv:1705.08039 — Chapters 3–4.
  2. Nickel, M. & Kiela, D. “Learning Continuous Hierarchies in the Lorentz Model of Hyperbolic Geometry.” ICML, 2018. arXiv:1806.03417 — the numerical argument of Chapters 6–7.
  3. Ganea, O., Bécigneul, G. & Hofmann, T. “Hyperbolic Entailment Cones for Learning Hierarchical Embeddings.” ICML, 2018. arXiv:1804.01882 — Chapter 5, including the aperture and angle formulas.
  4. Desai, K., Nickel, M., Rajpal, T., Johnson, J. & Vedantam, R. “Hyperbolic Image-Text Representations” (MERU). ICML, 2023. arXiv:2304.09172 — Chapter 6.
  5. Sala, F., De Sa, C., Gu, A. & Ré, C. “Representation Tradeoffs for Hyperbolic Embeddings.” ICML, 2018. arXiv:1804.03329 — the precision analysis behind Chapter 7’s ceiling.
  6. Chami, I., Ying, R., Ré, C. & Leskovec, J. “Hyperbolic Graph Convolutional Neural Networks.” NeurIPS, 2019. arXiv:1910.12933 — δ-hyperbolicity used as a model-selection signal.
  7. Khrulkov, V. et al. “Hyperbolic Image Embeddings.” CVPR, 2020. arXiv:1904.02239 — measuring δ on vision datasets.
  8. Vendrov, I., Kiros, R., Fidler, S. & Urtasun, R. “Order-Embeddings of Images and Language.” ICLR, 2016. arXiv:1511.06361 — Chapter 8.
  9. Vilnis, L., Li, X., Murty, S. & McCallum, A. “Probabilistic Embedding of Knowledge Graphs with Box Lattice Measures.” ACL, 2018. arXiv:1805.06627 — box embeddings and calibrated conditionals.
  10. Gu, A., Sala, F., Gunel, B. & Ré, C. “Learning Mixed-Curvature Representations in Product Spaces.” ICLR, 2019 — product manifolds and signature search.
  11. Sarkar, R. “Low Distortion Delaunay Embedding of Trees in Hyperbolic Plane.” Graph Drawing, 2011 — the 1+ε construction of Chapter 2.
  12. Bourgain, J. “The Metrical Interpretation of Superreflexivity in Banach Spaces.” Israel J. Math., 1986 — the √(log h) lower bound on Euclidean tree distortion quoted in Chapter 0.
  13. geoopt — Riemannian optimisation for PyTorch (Riemannian Adam, the Poincaré ball and Lorentz manifolds). github.com/geoopt/geoopt
“Geometry is not true, it is advantageous.”
— Henri Poincaré, whose disk you have been dragging points around for the last ten chapters. He meant it about physics. It turns out to be operational advice for embedding design: no space is the correct one, and the job is to pick the one whose growth rate matches your data.
One last diagnostic. A colleague reports that switching a 768-dimensional sentence-similarity retriever to hyperbolic space gave “no improvement, and ANN search stopped working”. What should you tell them?