This is the fifth post in Knowledge Graphs as Geometry, a weekly series that builds one idea, chapter by chapter: a knowledge graph stores facts as relations, every relation is an operation in space and predicting a missing fact is geometry. The last four posts built up a small zoo of models. This one shows that the zoo is an illusion.
By now we have built five models. TransE adds a vector, DistMult multiplies coordinate by coordinate, ComplEx does the same multiplication over complex numbers, RotatE constrains those complex numbers to rotations and QuatE jumps to quaternions. Five names, five papers and five sets of notation. The natural reaction after meeting them one at a time is that there are a lot of these things to keep straight.
But look back at how each one arrived. Every time, the new model differed from the last by exactly one decision. ComplEx is DistMult with a complex number system instead of a real one, RotatE is ComplEx with a distance score instead of a matching score and QuatE is RotatE moved up to quaternions. We kept noticing this in passing, and the point of this post is to stop noticing it in passing and give it a name.
Here is the claim, and it is the whole chapter in one sentence. Every model in the zoo is three independent choices:
Carrier: the number system the coordinates live in.
Operator: the map the relation applies to the head.
Comparator: the rule that turns a transformed head and a candidate tail into a single score.
Pick one from each column and you have named a model. The five famous ones are five points in that grid, and the grid has empty cells nobody bothered to name.
That table turns a reading list into a coordinate system. Instead of memorizing five architectures, you reason along three axes. So let us define the axes, watch the famous models fall out as configurations of them, and then build all four from a single class by changing arguments, because a good abstraction should make the code collapse and not just the prose.
Strip any of the multiplicative models down to its moving parts and the same operator appears every time. Give each entity a vector of coordinates grouped into channels. The relation r also gets a vector. The operator is the channelwise product, meaning you multiply the head channel by channel with the relation:
\(\widehat{\mathbf{t}} \;=\; \mathbf{h} \odot \mathbf{r}\)
That single line ⊙ is DistMult, ComplEx, RotatE and QuatE all at once. What changes between them is what a channel is and what you are allowed to put in r, which is the carrier and the operator’s one constraint. Then a comparator turns the transformed head into a score against a tail. So the full template is three nested choices around one product:
\(s(h, r, t) \;=\; \mathrm{compare}\big(\,\mathbf{h} \odot_{\text{carrier}} \mathbf{r},\ \mathbf{t}\,\big)\)
Let us take the three choices one at a time, on this formula.
The carrier is the number system one channel lives in, together with the rule for how two channels multiply. We have used three. The real numbers ℝ give one real value per channel under ordinary multiplication. The complex numbers ℂ give two reals per channel, multiplied the complex way, which is what lets a channel rotate. The quaternions ℍ give four reals per channel. The carrier decides what algebra the relation acts in, and as we saw in the earlier posts it also decides what relation patterns the model can represent at all. A real carrier forces every relation to be symmetric, and a complex carrier breaks that because complex multiplication has a direction.
Keep the family graph from the first post in mind, because the carrier choice is exactly what lets the model tell parentOf from its reverse. With a real carrier, multiplying Ann’s coordinate by the parentOf channel gives the same answer in both directions, so there is no way to encode that Ann is the parent and Carol the child rather than the other way around. Switch to a complex carrier and each channel can carry a phase, an angle, so the relation can turn Ann’s vector one way to reach a child and the opposite way to reach a parent. The carrier is where the directionality of a relation lives. That is not a detail, because for a graph of mostly directed relations it is the difference between a model that works and one that cannot get off the ground.
The operator is the map applied to the head. Across the entire multiplicative family it is the one product ⊙ above, and the models differ only in a constraint placed on the relation vector r. A free relation can be anything, so it scales and flips, which is the DistMult and ComplEx setting. A unit relation has every channel normalized to length one, so it is a pure rotation with no stretching, which is the QuatE setting. A phase relation is the complex special case of unit, where each channel is a point on the unit circle, an angle, which is exactly RotatE. Free, unit and phase are three settings of one knob.
The comparator is how the transformed head meets the tail to produce a number, and we have used two. Matching is the inner product ⟨h ⊙ r, t⟩, which is high when the transformed head points the same way as the tail. Distance is the negative norm γ − ‖h ⊙ r − t‖, which is high when the transformed head lands on the tail. Matching asks whether the two align, and distance asks whether we arrived.
A model is now a point in carrier × operator-constraint × comparator. DistMult is (ℝ, free, matching), ComplEx is (ℂ, free, matching), RotatE is (ℂ, phase, distance) and QuatE is (ℍ, unit, matching). The zoo is not five inventions but five points in one space.
A good abstraction earns its keep when the code collapses to match it. If these four models really are three arguments to one object, then we should be able to write the object once and instantiate the four by changing nothing but the arguments, and have each one come out identical to the hand-written, separately published version.
That is exactly what the library does. There is one class, DiagonalProductModel, that takes a carrier, a comparator and a relation mode, and implements the shared forward pass once. It looks up the head, applies the carrier product with the relation and hands the result and the tail to the comparator. The named models are two-line subclasses that pick the three knobs. To check that the abstraction is real and not just suggestive, we build each model both ways, from the generic class and from its named subclass, copy the weights across so that only the configuration is being compared, and score the same batch of triples.
Every row says yes. The generic class configured as (ℝ, free, matching) computes the same scores as the published DistMult to floating-point precision, (ℂ, phase, distance) reproduces RotatE, and so on. This is the difference between an abstraction that is merely suggestive and one that is exact. We are not saying these models are like configurations of a common core. We are checking that the configured core and the hand-written model produce bit-for-bit the same forward pass on the same weights. The named subclasses add nothing but the three argument choices.
Reading the ComplEx and RotatE rows next to each other is the cleanest possible statement of the thesis. They share a carrier, ℂ, and they share an operator, the product ⊙. They are separated by a single argument, the comparator flipping from matching to distance, and that one flip drags a second change behind it, which is the subject of the next demonstration. So the answer to how RotatE differs from ComplEx is no longer a comparison of two papers. It is to change one argument and accept the constraint that argument forces.
The lens is not only tidy. It is also what makes one piece of code score all candidates cheaply for every model in the family. This matters because link prediction is ranking. To answer (Ann, parentOf, ?) you score every possible tail, and to answer (?, parentOf, Carol) you score every possible head. Done naively, head prediction re-applies the relation to all the candidate entities one at a time, which is slow and repeated for every query.
Two algebraic identities collapse that loop into a single operation. They are properties of the carrier rather than of any one model, so they cover the whole family at once.
For the matching comparator, the relation can be moved off the head and onto the tail by conjugating it:
\(\langle\, \mathbf{h} \odot \mathbf{r},\ \mathbf{t} \,\rangle \;=\; \langle\, \mathbf{h},\ \mathbf{t} \odot \overline{\mathbf{r}} \,\rangle\)
So to score every head, you transform the one tail by the conjugate relation and take an inner product against the whole entity table, which is a single matrix multiply. For the distance comparator with a unit relation, the rotation can be undone on the tail instead of applied to the head:
\(\lVert\, \mathbf{h} \odot \mathbf{r} - \mathbf{t} \,\rVert \;=\; \lVert\, \mathbf{h} - \mathbf{t} \odot \mathbf{r}^{-1} \,\rVert\)
So head prediction is one product of the tail with the inverse relation, then one distance against the entity table, which is a single cdist. We can check the fast path against brute force by scoring every head the slow way, scoring them the fast way and measuring the largest disagreement.
The gap is at the level of floating-point rounding, around 10⁻⁶, so the two paths are the same computation. But notice the fine print on the second identity. It needs ‖r‖ = 1. That identity holds only when the relation preserves length, which is what a mathematician calls an isometry, so a free relation that stretches breaks it and the fast head prediction would silently return wrong rankings.
This is the coupling that the ComplEx-to-RotatE flip dragged behind it. A distance comparator requires a length-preserving relation. The comparator and the operator constraint are not independent after all, because choosing distance forces you to choose unit or phase. The library makes the coupling load-bearing. Ask it for a distance comparator with a free relation and it refuses, rather than hand back numbers that look fine and are wrong.
rejected: Distance comparator needs relation_mode 'unit' or 'phase'
so that head prediction is exact (the relation must be an isometry).The geometry of the operator forces the comparator. That is a small instance of a general principle the book proves later, and it is the first hint that the three choices are not quite as independent as the grid makes them look.
If the four models really are one class differing by three knobs, then training them under an identical recipe isolates what each choice buys. The objective, the dimension, the schedule and the data are all held fixed, and only the model class changes. We do this on UMLS, a small biomedical knowledge graph of 135 entities and 46 relations that trains in seconds, and read the four off as a single leaderboard.
The spread is the lens at work, and it lines up exactly with the axes. DistMult is the weakest at MRR 0.665. Its real carrier forces every relation to be symmetric, and UMLS is full of directed relations like causes and treats that simply are not symmetric, so DistMult cannot represent them no matter how long it trains. That is a carrier ceiling rather than a tuning problem. Move to the complex carrier and the ceiling lifts, so ComplEx jumps to MRR 0.841 because complex multiplication has a direction and can finally tell causes from is-caused-by. RotatE keeps the complex carrier and swaps to the distance comparator with a rotation constraint, reaching MRR 0.860 and an almost-perfect Hits@10 of 0.995. QuatE moves up to the quaternion carrier at MRR 0.853.
The class and the training are the same across the four, so the carrier and comparator choices alone account for the whole gap from 0.665 to 0.860. The lens did not just organize a reading list. It turned the question of which model is better and why into one you answer by pointing at an axis.
It is worth being precise about what this experiment does and does not show. It is not a claim that one of these models is best for all graphs, since UMLS is small and on a different graph the ordering can shift. The point is narrower and stronger. Because the four share a class, a training recipe and a dimension, any difference in the bars is attributable to the three knobs and nothing else. There is no hidden architecture advantage to argue about and no separate hyperparameter sweep tilting one model over another. When DistMult trails the others by 0.18 of MRR, that gap is the price of the real carrier, paid in full view. The lens does not just predict models. It makes ablations clean, because the thing you vary is the only thing that changed.
The three choices organize the zoo completely. But each axis is still narrow, and seeing the narrowness is what motivates the rest of the book. Two limits stand out, and both are bridges to what comes next.
First, the carrier ladder ℝ, ℂ, ℍ is not an arbitrary list. It is the start of one sequence, since each is a Clifford algebra, the first three rungs of a ladder that keeps going. The next part of the book builds that sequence from scratch and finds a general carrier, of any dimension, whose multiplication is a single precomputed table. The channelwise product becomes the geometric product, and the four named models become four signatures of one algebra.
Second, and this is the bigger gap, the operator is only ever a product. It can rotate, it can scale and it can do both, but it cannot translate, which is precisely why TransE, the add-a-vector model, sits outside the family in that opening table. The product can never be singular either, because multiplying by a nonzero relation is always invertible, so a diagonal model can never send two distinct heads to one tail. It structurally cannot represent a genuinely many-to-one relation like parentOf, where Ann and Bob both map to Carol. We saw in the very first post that real relations demand that collapse, and the product cannot give it.
A richer operator would do all three at once, rotating, stretching and translating, with the freedom to collapse a dimension when the relation is many-to-one. That operator is
\(F_r(\mathbf{x}) \;=\; U_r\, \Sigma_r\, V_r^{\mathsf{T}}\, \mathbf{x} \;+\; \mathbf{c}_r\)
a rotation Uᵣ, then a stretch Σᵣ that can scale each direction differently and can shrink one to zero, then another rotation Vᵣᵀ, and finally a translation cᵣ. The diagonal models are its special case where the stretch is forced full-rank and the translation is zero. That operator is where this series is heading, and the three choices survive into it unchanged in spirit and widened in scope, as a general carrier, a general rotate-stretch-translate operator and the same two comparators bound to it by the same length-preserving coupling we just met.
A note on the framing, since it matters and the book holds the line on it. Geometric algebra is the design language that says these models should be one object and tells you what the general operator looks like. Linear algebra is the implementation, so that operator runs as the ordinary matrix product Uᵣ Σᵣ Vᵣᵀ x + cᵣ, on a GPU, with no special engine. The singular value decomposition is the seam, where the rotations Uᵣ, Vᵣ and the translation are the part geometry hands you cleanly, while the stretch Σᵣ is a plain symmetric map. We claim no magic for the geometry and no speedup beyond what the matrix form already gives. The geometry tells us why the zoo is one object, and the matrices are the object.
The whole point fits in a few lines, with four famous models built from one class by three arguments each.
from kge.carrier import REAL, COMPLEX, QUATERNION
from kge.comparators import Matching, Distance
from kge.models import DiagonalProductModel
E, R, dim = 30, 6, 8
# Each model is three arguments to ONE class: carrier, comparator, relation mode.
DistMult = DiagonalProductModel(E, R, REAL, dim, Matching(), "free")
ComplEx = DiagonalProductModel(E, R, COMPLEX, dim, Matching(), "free")
RotatE = DiagonalProductModel(E, R, COMPLEX, dim, Distance(margin=6.0), "phase")
QuatE = DiagonalProductModel(E, R, QUATERNION, dim, Matching(), "unit")
# Try the forbidden pairing (the library refuses it):
DiagonalProductModel(E, R, COMPLEX, dim, Distance(), "free")
# -> ValueError: Distance comparator needs relation_mode 'unit' or 'phase'The companion notebook builds all four from the generic class, confirms each is identical to its named subclass, checks both all-candidate identities against brute force to floating-point precision, shows the library rejecting the distance-plus-free pairing and trains the four on UMLS to reproduce the leaderboard above.
▶ Run the notebook in Colab (no install)
📦 Code: github.com/asudjianto-xml/Knowledge-Graph-Geometry. Install it with pip install "kge-geometric @ git+https://github.com/asudjianto-xml/Knowledge-Graph-Geometry.git"
The model zoo is an illusion. Every diagonal model is three independent choices, a carrier, an operator and a comparator, around one product, and the famous names are points in that grid. Naming the axes turns a list to memorize into a space to reason in, and reading a leaderboard becomes reading the axes. But the grid has two narrow edges. The carrier ladder is the start of one sequence, and the operator can only ever multiply. Widen both and the four points become the diagonal slice of a single operator that can rotate, stretch and translate. Next week: building geometric algebra out of the linear algebra we already have.
Knowledge Graphs as Geometry is a free weekly series adapted from my book Knowledge Graph Embeddings as Geometric Operators. The posts carry the intuition and the runnable code, while the book carries the full derivations. Subscribe to follow the whole argument, from a single translation to one operator that contains the entire model zoo.
Check also the free package for agentic AI: https://knowlytix.ai/

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.