Skip to content
← Documentation

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.
  • diffWorlds aligns 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 — plus unattributable as 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.