docs/embedding-stability.md
Embedding stability across revisions
src/lib/projection/align.ts, tests/align.test.ts, src/lib/world/diff.ts
The question was not "can we animate?" It was:
Can two successive embeddings be aligned so that only semantic change remains as motion?
Answer: partly, and the boundary is measurable. Gauge freedoms are removable exactly. MDS's diffuse response to a changed metric is not. The result is enough for a stable visual identity and not enough for motion to be a change channel.
What alignment removes, and what it must not
Classical MDS does not return a layout. It returns an equivalence class:
translate, rotate or reflect a solution and it solves the metric exactly as
well. Nothing in the facts picks a member. The one actually returned is decided
by arithmetic accident — layout.ts:leadingEigen seeds from sin(i + 1 + seed)
where i is a node's position in the list, and the sign a power iteration
converges to is arbitrary. Insert one file, indices shift, axes may flip.
| freedom | gauge? | removed |
|---|---|---|
| translation | yes | yes |
| rotation | yes | yes |
| reflection | yes | yes |
| uniform scale | yes here — from-facts rescales by 300 / span |
yes |
| per-point motion | no — this is the signal | never |
Nothing beyond a similarity transform is permitted. Shear or per-point warping would manufacture agreement rather than reveal it. If similarity alignment cannot reduce false motion, that is a finding about MDS, not a licence to fit harder.
Attribution must precede alignment
Found by the instrument, not by design. Least squares has no notion of which points deserve to agree, so it rotates the frame to chase whichever subject moved most and charges the cost to everyone else.
Measured on a synthetic case with one subject of five genuinely relocating:
| fit | real motion reported | smeared onto stationary subjects |
|---|---|---|
| unrestricted | 2.6 of 55.5 | up to 9.8 |
| anchored on the stationary subjects | 55.5 of 55.5 (exact) | 0.000 |
This imposes an ordering constraint on the whole pipeline. A reference frame can
only be built from subjects known to be semantically stationary, and knowing
which those are is exactly what world/diff.ts computes. Alignment cannot be
a rendering-time afterthought over two anonymous point clouds.
The measurement — 7 adjacent revisions of this repository
1056 stationary subject-pairs. False motion = a subject whose bytes, commits and own couplings are all unchanged, displaced more than 3 units on a 300-unit city.
| pair | stationary | median move raw | aligned | false raw | false aligned |
|---|---|---|---|---|---|
| 339c→7725 | 148 | 0.0 | 0.0 | 0 | 0 |
| 7725→48b7 | 148 | 2.3 | 2.3 | 64 | 61 |
| 48b7→1604 | 151 | 102.2 | 0.8 | 147 | 5 |
| 1604→421f | 154 | 0.0 | 0.0 | 0 | 0 |
| 421f→6ce2 | 152 | 148.1 | 0.5 | 148 | 0 |
| 6ce2→c35f | 148 | 1.0 | 1.0 | 32 | 25 |
| c35f→b995 | 155 | 102.9 | 0.9 | 151 | 26 |
| false motions | ||
|---|---|---|
| raw | 542 / 1056 | 51.3% |
| aligned | 117 / 1056 | 11.1% |
| reduction | 78.4% |
Held-out control: fitting on half the stationary subjects and measuring the other half gives residuals of 0.4–1.0 units against in-sample 0.5–1.0. The transform generalises; 4 parameters over ~150 points is not overfitting.
Two regimes are visible. Pairs with a median raw move near 100 units are axis flips — pure gauge, and alignment removes them essentially completely (147→5, 148→0, 151→26). Pairs with a median raw move of 1–2 units never had a flip; their discrepancy is diffuse local re-solving, and alignment does nothing for it (64→61, 32→25).
Retracted: the unreachable stand-in is not the cause
symmetrise sets unreachable pairs to span × 2, where span is the largest
finite distance observed. That is a world-scoped normalisation of exactly the
same shape as peakBytes, and it looked like the obvious culprit. It measurably
is not.
When span moved 90 → 105 between two revisions, 7122 of 11476 distances between stationary subjects changed; holding the stand-in fixed reduced that to 187, a 97% reduction in input perturbation. The effect on the output:
| false motions | |
|---|---|
| observed-peak stand-in | 117 / 1056 |
| shared stand-in | 114 / 1056 |
2.6%. The 7122 changed distances moved almost uniformly, so alignment was already absorbing them as scale. Hypothesis refuted; the residual instability is not the stand-in.
Why animation is still not licensed
Aligned displacement, 1056 stationary versus 21 genuinely-changed subjects:
| quantile | false (stationary) | true (changed) |
|---|---|---|
| p50 | 0.46 | 3.59 |
| p75 | 1.26 | 7.36 |
| p90 | 3.09 | 73.94 |
| p95 | 4.12 | 122.09 |
| max | 51.88 | 122.83 |
The median true motion (3.59) sits at the false p90 (3.09). The distributions interpenetrate, and no threshold separates them:
| floor | false motions shown | real motions hidden |
|---|---|---|
| 1 | 29.8% | 14.3% |
| 3 | 11.1% | 38.1% |
| 5 | 3.4% | 66.7% |
| 10 | 0.4% | 76.2% |
Most real changes are small — a few bytes, one commit — and produce genuine motion indistinguishable from solver residue. Only large structural moves (>~50 units) are cleanly separable, and they are a minority of real changes.
So: position is now trustworthy as identity, not as a change channel. A building holds still when nothing about it changed, which is what makes the city recognisable between revisions. But animating position would still spend the display's strongest signal on solver residue for the majority of real edits.
Animation remains unlicensed. The finding is not that alignment failed — it cut false motion by 78.4% and is retained — but that the remaining error is inherent to MDS as a temporal representation, not to its gauge. A temporal representation would need positions that are stable by construction, e.g. solved incrementally against the previous embedding rather than independently. That is a different claim and is not yet measured.
Falsified: constraining the embedding does not help
The follow-up hypothesis was that the problem is aligning unstable embeddings after the fact, and that solving B constrained by A would be temporally stable by construction. That is a different optimisation problem, so it was measured the same way.
Classical MDS is spectral and has no natural constraint. The candidate is therefore SMACOF stress majorisation, which is a local optimiser and so can be warm-started at A's solution, optionally with an anchor term pulling common subjects toward their previous positions with weight λ.
An instrument error had to be fixed first: SMACOF-B was initially compared against a classical reference A, which measured the difference between two methods rather than stability within one. Each method is now scored against its own reference.
Same 7 adjacent revisions, same 1056 stationary subject-pairs. AUC is the
scale-invariant measure — the probability that a genuinely-changed subject moves
further than an unchanged one — added precisely because a ratio of quantiles can
be flattered by an anchor that merely shrinks everything.
| method | false > 3u | false p90 | true p50 | AUC | stress-1 |
|---|---|---|---|---|---|
| classical + align (incumbent) | 11.1% | 3.09 | 3.59 | 0.831 | 0.923 |
| SMACOF cold + align | 97.7% | 108.68 | 22.83 | 0.450 | 0.393 |
| SMACOF warm (constrained) | 75.1% | 31.01 | 8.46 | 0.539 | 0.392 |
| SMACOF warm λ=0.1 | 29.5% | 8.15 | 3.19 | 0.674 | 0.393 |
| SMACOF warm λ=0.3 | 18.8% | 6.04 | 2.51 | 0.679 | 0.394 |
| SMACOF warm λ=1 | 11.9% | 3.49 | 1.43 | 0.675 | 0.395 |
| SMACOF warm λ=3 | 5.3% | 1.69 | 0.66 | 0.675 | 0.396 |
| SMACOF warm λ=10 | 0.0% | 0.62 | 0.23 | 0.675 | 0.397 |
| SMACOF warm λ=30 | 0.0% | 0.22 | 0.08 | 0.675 | 0.398 |
Rejected. False motion can be driven to exactly zero, and it buys nothing: AUC plateaus at 0.675 from λ=0.3 upward and never approaches the incumbent's 0.831. The anchor scales false and true motion together — at λ=10 the median true motion (0.23) sits below the false p90 (0.62). Constraining the embedding does not make motion informative; it makes the city stiff.
By the stated criterion — continue only on a measurable improvement in false motion, true motion, and attribution separation — the candidate fails the third outright and wins the first only by destroying the second.
The reason, which is worth keeping
SMACOF fits the metric far better than classical MDS: stress-1 0.39 vs 0.92. And it is far less temporally stable. That is not a coincidence.
Classical MDS is stable because it is lossy. It keeps only the two leading eigenvectors, which are coarse and robust to small perturbations of the distance matrix. SMACOF fits the fine structure — and the fine structure is exactly what changes between revisions. Fidelity and temporal stability are in tension here, and the incumbent sits at the stable, unfaithful end.
A separate finding falls out: stress-1 = 0.92 means the shipped embedding barely fits its own metric. That is a fidelity problem for position-as-identity, independent of anything temporal, and is recorded rather than acted on.
What shipped
alignEmbedding(reference, target, { fitOn })— exact closed-form similarity fit in 2D, both reflection branches evaluated, applied to every target point so new buildings land in the same frame.diffWorldsaligns internally, anchored on the subjects its own attribution found stationary, and reports position in that frame.- Position marks below
MOTION_FLOOR(3 units) are not reported, with the cost of that floor stated above rather than hidden. - Three causes, never collapsed:
fact,normalisation,projection— plusunattributableas a refusal.
On the 13-commit span 58af62d → b99546a: 87 buildings report a change, of
which 11 are fact, 76 are projection, and 0 are normalisation. Before
alignment the same span reported 146.