SFI.diagnostics package¶
SFI diagnostics — residual-consistency checks for inference results.
Top-level user-facing entry point:
from SFI.diagnostics import assess
report = assess(inferer, level="standard")
report.print_summary()
The module reuses the fitted force and the inferred constant diffusion to
recompute standardised residuals (innovations) and test whether they look
like an independent N(0, 1) sample:
mean, variance, skewness and kurtosis (pooled and per spatial component), plus the per-component covariance;
Ljung–Box autocorrelation of the residuals and their squares;
Kolmogorov–Smirnov normality against
N(0, 1);predicted-vs-realised normalised mean squared error of the force (a sampling-noise-aware chi-square check).
See SFI.diagnostics.residual_tests. Two preset levels are exposed:
"minimal"— residual moments only;"standard"— adds autocorrelation, normality, and MSE consistency.
All backends (overdamped / underdamped, single / multi-particle, SPDE) share a unified residual definition: the Euler–Maruyama innovation \(r_t = \Delta x_t - F(x_t)\,\Delta t\) (overdamped) or the secant-velocity innovation \(r_t = \Delta v_t - F(x_t, v_t)\,\Delta t\) (underdamped), both whitened by \((2 \bar D \Delta t)^{-1/2}\).
References
Ljung & Box (1978); Diebold, Gunther & Tay (1998).
- class SFI.diagnostics.DiagnosticsReport(residuals=<factory>, meta=<factory>)[source]¶
Bases:
objectContainer for residual-consistency test results.
- Variables:
residuals (dict) – Test results. Always holds
"moments"; atlevel="standard"also"autocorr","normality"and"mse_consistency".meta (dict) – Backend tag, regime,
n_obs,n_particles,d, level.
- Parameters:
residuals (dict)
meta (dict)
- flag_issues(alpha=0.01, *, hints=True)[source]¶
List human-readable warnings.
Returns one line per test whose p-value is below
alphaor whose statistic crosses a sane threshold (residual mean off zero, std far from one, MSE-consistency|z| > 5).- Parameters:
alpha (float) – Significance level for the p-value tests.
hints (bool) – When True (default), each message carries a one-line action hint (” — <what to do>”); set False for bare statistics (machine parsing).
- Return type:
list[str]
- meta: dict¶
- print_summary(alpha=0.01, *, hints=True)[source]¶
Print a human-readable summary of the diagnostic report.
Each flagged issue carries a one-line action hint unless
hints=False.- Parameters:
alpha (float)
hints (bool)
- Return type:
None
- residuals: dict¶
- class SFI.diagnostics.DynamicsOrderReport(verdict, fit=<factory>, scaling=<factory>, scan=<factory>, cross_check=None, meta=<factory>)[source]¶
Bases:
objectResult of
classify_dynamics().- Variables:
verdict (str) –
"OD","UD"or"inconclusive".fit (dict) – Parametric fit output (
params=sigma2, D, V, gamma,tau_v,stderr,V_z,delta_aiccand the raw SSR / AICc values).scaling (dict) – Model-free statistics (
rho2,K,beta).scan (dict) – Lag-0/1/2 covariances across the dt scan (
strides,dt,C0,C1,C2,n_eff).cross_check (dict or None) – Overdamped-fit Ljung–Box result, or
Noneif disabled.meta (dict) – Sampling summary (
d,n_datasets,strides,dt_min/max,gamma_dt_min).
- Parameters:
verdict (str)
fit (dict)
scaling (dict)
scan (dict)
cross_check (dict | None)
meta (dict)
- cross_check: dict | None = None¶
- fit: dict¶
- meta: dict¶
- scaling: dict¶
- scan: dict¶
- verdict: str¶
- SFI.diagnostics.assess(inferer, *, level='standard', n_lags=None, data=None)[source]¶
Run residual-consistency checks on a fitted inference object.
Recomputes the standardised residuals (Euler–Maruyama innovations, whitened by the inferred constant diffusion) and tests whether they look like an independent
N(0, 1)sample.- Parameters:
inferer – A fitted
OverdampedLangevinInferenceorUnderdampedLangevinInference. Must have a callableforce_inferred(run e.g.infer_force_linearfirst) and an inferred constant diffusion (A_inv).level (str) –
"minimal"— pooled residual moments only;"standard"(default) — adds autocorrelation, normality, and predicted-vs-realised MSE consistency.n_lags (int | None) – Number of autocorrelation lags. Default
min(20, n_eff // 5).data – Optional independent
TrajectoryCollectionon which to evaluate the residuals (held-out diagnostics). Default: the training data attached to the inferer.
- Returns:
Container with a
residualssection andmeta.- Return type:
Notes
The Euler-style residual is exact for the linear estimators; for the parametric estimators it is an approximation that is still asymptotically consistent under correct specification, so the autocorrelation and normality tests remain valid for flagging misspecification.
- SFI.diagnostics.classify_dynamics(data, *, strides=None, cross_check=True)[source]¶
Classify trajectory data as overdamped, underdamped, or inconclusive.
Robust to high localization noise (lag >= 2 covariances are noise-immune) and reports inconclusive at coarse sampling where the momentum is unresolved (
gamma * dt >~ 1), rather than guessing.- Parameters:
data (TrajectoryCollection) – Raw trajectories (positions). Velocities are not required — the test reconstructs the dynamics order from position increments alone.
strides (sequence of int, optional) – Coarse-graining factors for the dt scan. Default: a geometric set
1, 2, 4, ...capped so the coarsest still has enough samples.cross_check (bool) – Run the Layer-3 overdamped-fit residual-autocorrelation corroboration (default
True).
- Return type:
Notes
Assumes white localization noise; spatially uniform, isotropic pooling of components. Strong memory (a generalized Langevin / viscoelastic bath) can also produce velocity persistence without inertia — out of scope; the test detects trajectory smoothness, which inertia produces.
Examples
>>> report = classify_dynamics(collection) >>> report.verdict 'UD'
- SFI.diagnostics.parametric_four_point_diagnostic(data, drift_fn, dt, n_substeps=4)[source]¶
Four-point diagnostic: test that Cov[r_i, r_{i+2}] = 0.
Under correct model specification and i.i.d. measurement noise, midpoint flow residuals separated by two steps share no measurement noise source, so their cross-covariance should vanish. Deviations indicate model misspecification, correlated measurement noise, or higher-order effects.
- Parameters:
data (TrajectoryCollection or TrajectoryDataset) – Observed trajectory data.
drift_fn (callable (d,) → (d,)) – Drift function with parameters already closed over.
dt (float) – Observation time step.
n_substeps (int) – RK4 micro-steps per interval.
- Returns:
result –
C_02: (d, d) empirical cross-covariance of r_0 and r_2.frobenius_norm: ||C_02||_F.n_quadruplets: number of quadruplets used.- Return type:
dict
- SFI.diagnostics.plot_dynamics_order(report, *, axes=None)[source]¶
Visualise an OD-vs-UD classification (
classify_dynamics()).Two panels versus the sampling step
dt:lag-2 persistence
rho2 = C2/(C0+2C1)(noise-immune): tends to 0 for overdamped data, to a positive plateau for inertia;apparent kinetic energy
K = (C0+2C1)/dt^2on log-log axes, with reference slopes-1(overdamped,K ~ 2D/dt) and0(underdamped); the fitted log-log slopebetais annotated.
The parametric-fit prediction is overlaid on both panels. Accepts a
DynamicsOrderReport.
- SFI.diagnostics.plot_qq(report_or_inferer, *, ax=None, level='standard')[source]¶
Normal Q–Q plot of pooled whitened residuals.
- Parameters:
level (str)
- SFI.diagnostics.plot_residual_acf(report_or_inferer, *, ax=None, level='standard')[source]¶
Autocorrelation of the residuals (and of $z^2$).
Reads the ACF computed by
autocorrelation_tests()(stored on the report) rather than recomputing it.- Parameters:
level (str)
- SFI.diagnostics.plot_residual_histogram(report_or_inferer, *, ax=None, bins=60, level='standard')[source]¶
Histogram of pooled residuals overlaid with N(0,1) density.
- Parameters:
bins (int)
level (str)
- SFI.diagnostics.plot_summary(report_or_inferer, *, level='standard', figsize=(13.0, 4.0))[source]¶
1×3 summary figure: Q–Q plot, residual histogram, and ACF.
Returns the matplotlib
Figure.- Parameters:
level (str)
Submodules¶
- SFI.diagnostics.dynamics_order module
- SFI.diagnostics.plotting module
- SFI.diagnostics.report module
- SFI.diagnostics.residual_tests module
- SFI.diagnostics.residuals module
- Measurement-noise-aware, banded whitening
- Residual conventions
ResidualBundleResidualBundle.backendResidualBundle.dResidualBundle.force_quadratic_formResidualBundle.mean_dtResidualBundle.n_obsResidualBundle.n_particlesResidualBundle.nmse_excess_factorResidualBundle.regimeResidualBundle.whitenedResidualBundle.zResidualBundle.z_componentsResidualBundle.z_squared_norms
build_overdamped_residuals()build_residuals()build_underdamped_residuals()