Diagnose how an autoregressive surrogate's rollout fails, not just how much.
Every neural PDE surrogate accumulates error over a long rollout. Relative L² tells you the error is growing. It does not tell you whether the model is tracing the right attractor at the wrong rate, drifting off the attractor entirely, or just degrading into noise — three failures with three different fixes.
rolldrift decomposes rollout error by direction relative to the reference
trajectory's motion, and reports a dimension-normalised statistic that is
comparable across problems.
import rolldrift as rd
report = rd.diagnose(pred, true, dt=0.01, weights=cell_volumes)
print(report)DriftReport
kappa (tangential concentration) : +0.8138
interpretation : mostly tangential drift, with a secondary off-path component
kappa over first fifth : +0.9916 (aggregate is diluted by accumulated drift)
raw tangential fraction f_par : 0.8145 (isotropic null = 0.003906)
enrichment vs chance : 209x
transverse fraction : 0.1855
state dimension : 256
...
| surrogate defect | κ | enrichment | reading |
|---|---|---|---|
| wrong wave speed | +0.81 | 209× | tangential — right path, wrong rate |
| amplitude decay | −0.004 | 1.8e-10× | transverse — leaving the attractor |
| isotropic noise | −0.0002 | 0.95× | no directional structure |
Note that κ is ≈0 for both of the last two. That is expected: κ's lower bound is −1/(n−1), so it is blind on the transverse side in high dimensions. The enrichment ratio resolves them. Read both.
Because it isn't comparable across problems. Under isotropic error in n
dimensions the tangential fraction is 1/n, not 0.5 — so a 2-D system scores
0.5 and a 1000-D system scores 0.001 for identical (absent) structure.
kappa = (n * f_par - 1) / (n - 1) normalises against that null: 0 under
isotropy, 1 under pure tangential error, at any n. Verified — identical
physics embedded in n = 2…1000 gives f_par drifting 0.899 → 0.800 while
kappa holds at 0.799–0.802.
Beyond the analytic test cases, the decomposition has been cross-checked against an independently written implementation used in published work on latent-space ROMs of bluff-body wakes (arXiv:2608.07189).
With matched tangent estimators, the two agree to machine precision:
| case | published implementation | rolldrift |
difference |
|---|---|---|---|
| Re=300, n_z=4 | 0.978845 | 0.978845 | 0.00e+00 |
| Re=400, n_z=4 | 0.962065 | 0.962065 | 0.00e+00 |
| Re=800, n_z=8 | 0.957033 | 0.957033 | 0.00e+00 |
| square Re=101, n_z=4 | 0.975297 | 0.975297 | 2.22e-16 |
Two implementations written independently, agreeing exactly. That is the strongest available check that the definition is what it claims to be.
The cross-check also found a bug in the published pipeline, which is the
more useful outcome. Its tangent was estimated by a central difference over ±5
steps, spanning 0.54–0.93 of a shedding period. Since tangent misalignment
biases the tangential share downward as cos²(α), that stencil was understating
the published result. rolldrift's narrower default recovers it:
| case | at ±5 steps | at ±1 step | understated by | s₃/s₁ |
|---|---|---|---|---|
| Re=400 | 0.9621 | 0.9870 | +0.0249 | 0.0754 |
| Re=800 | 0.9570 | 0.9650 | +0.0080 | 0.0390 |
| Re=300 | 0.9788 | 0.9857 | +0.0069 | 0.0303 |
| square Re=101 | 0.9753 | 0.9758 | +0.0005 | 0.0051 |
The size of the penalty is monotone in the energy outside the leading POD pair, across a factor of fifteen — which is the predicted mechanism, since a wide stencil damages harmonic content specifically.
pip install -e .Requires numpy. Tests need pytest and scipy.
Time is always axis 0. Everything after it is state and gets flattened.
(T, n), (T, H, W) and (T, C, H, W) are all valid.
On a non-uniform mesh the correct inner product is <a,b>_w = Σ wᵢ aᵢ bᵢ. Pass
cell volumes or areas as weights. Passing None on gridded input is allowed
but warns, because silently assuming a uniform mesh is a good way to get a
plausible wrong answer.
1. Tangential ≠ phase, transverse ≠ amplitude. This is a decomposition of error direction. It coincides with the intuitive phase/amplitude split only when the radius vector is normal to the velocity, i.e. for near-circular orbits. On an asymmetric orbit a pure amplitude error carries a real tangential component:
| orbit | f_∥ from a pure 2% dilation |
|---|---|
| circle | 0.000 |
| Van der Pol μ=1 | 0.180 |
| Van der Pol μ=5 | 0.749 |
| Van der Pol μ=10 | 0.884 |
Test this on your own system before interpreting a high κ as phase drift: run
diagnose on a dilated copy of the reference and check the result is far below
your measured value.
2. κ is horizon-dependent, and it can move in either direction. Two competing effects:
- Dilution. Once accumulated drift exceeds the feature scale, a pure phase error reads as partly transverse — a long chord departs from the tangent. This pulls κ down with horizon.
- Emergence. If the transverse component is a fixed floor while the tangential component grows, the ratio improves over time. This pushes κ up.
Which dominates is a property of your system. On travelling-wave data dilution wins; on the bluff-body ROM data above, emergence wins and κ rises monotonically across the rollout (+0.952 → +0.993).
The library detects and warns about the dilution case only. Always inspect
report.kappa_t rather than trusting the aggregate, and treat
report.kappa_early as one point on a curve, not as a correction.
3. κ is blind on the transverse side at large n. Floor is −1/(n−1). Use
report.enrichment.
4. Tangent estimation must be resolved. rolldrift uses a two-point central
difference, which is correct for adequately sampled data. But if you pass
pre-smoothed derivatives, decimated trajectories, or data sampled at fewer than
~10 points per dominant period, the tangent rotates and the share is understated
— see the published-pipeline table above for how large that can get. The library
warns on under-sampling but cannot detect that you smoothed the data upstream.
5. Effective dimension is ambient, not intrinsic. For a field on a 128×128 grid, n = 16384, but the dynamics live on a far lower-dimensional attractor. The isotropic null used here is over the ambient space. A null over the attractor's tangent space would be sharper. Open question, deliberately not guessed at in v0.1.
6. Mode A only. A ground-truth reference trajectory is required. Monitoring without ground truth, against a fitted attractor model, is planned for v0.2 and will use the same call signature.
Tangent-estimation error biases the tangential fraction downward
(f_par_measured ≈ cos²(α) · f_par_true for a tangent misaligned by α).
Under-resolved data cannot manufacture a tangential-drift result — the reported
value is a lower bound. Sloppy data works against the claim, not for it.
This is why limitation 4 matters in only one direction: a bad tangent will cost you a real result, but it will not hand you a false one.
Every claim above is checked in tests/ (63 tests) against cases with
analytically known answers. See docs/verification_notes.md for the record,
including two adversarial probes that failed and what they turned out to mean.