The order of a model, kept only where noise can't explain it.
Classical information criteria next to Fine–Hwang threshold estimators: grow the memory with the sample, studentize every statistic, clip what white noise could produce, report the last surviving lag.
Two families, one honest comparison
Information criteria score every candidate order; threshold estimators keep the last lag whose statistic survives a vanishing cut-off. tsorder computes both from one pass over the data, so they can be compared on equal footing — in accuracy and in time.
One data path
Sample autocovariance (by FFT for large lags) → Levinson–Durbin recursion. AIC, AICc, BIC, HQ, FPE, CAT and MDL, the PACF and the long AR fit all come from the same O(K²) pass — with Burg and conditional-OLS variants a flag away.
Studentized by construction
The raw Fine–Hwang threshold is not scale-invariant and degrades with n. Every statistic here returns its null standard error, and every threshold lives on the studentized scale.
Pluggable statistics
PACF for AR, Bartlett-studentized ACF for MA, long-AR coefficients by Yule–Walker or OLS. The statistic and the cut-off rule are orthogonal axes of the API.
Calibrated thresholds
BIC-like, iterated-logarithm, max-of-K and Gumbel rules. The Gumbel threshold controls the family-wise over-selection level: α = 5% means ≈ 5% false positives on white noise, verified in the tests.
Benchmarked, defaults included
Every default was decided by a reproducible benchmark shipped in the repository — including the negative results: the mode-by-consensus ensemble never beat its best member and was dropped.
No dependencies
Pure Rust on the standard library, deterministic, MSRV 1.81. Errors are values (Result), never a panic on user data — ready for thin R and Python bindings.
An AR(2), read straight off the PACF
Simulated AR(2) with φ = (1.2, −0.6) and n = 400. The studentized partial autocorrelations at lags 1 and 2 tower over the threshold τₙ = 3.0; everything after them is what white noise produces. The last crossing gives the order — the same answer BIC reaches by scoring all eleven candidate models.
Studentized PACF and the Fine–Hwang threshold
|PACF(k)|·√n for k = 1…p_n, with the default memory p_n = ⌈2 log 400⌉ = 12 and the default threshold τₙ = √(1.5 log n).
Values as a table
| Lag | |studentized PACF| | Decision |
|---|
Selected order: 2 (last lag above the threshold). BIC on the same series: 2.
Reproducible: cargo run --release --example pacf_path prints these numbers.
How it works
Fine & Hwang (1979) proved the first consistent order estimator: no likelihood, no Gaussianity, no maximum order to guess. tsorder is that idea with the one repair it needed — studentization.
Grow the memory
Fit a long model of order p_n that increases with the sample size n.
One recursion
Autocovariances and one Levinson–Durbin pass give every statistic and every criterion.
Studentize
Each statistic is divided by its null standard error, so the threshold is scale-free.
Clip and report
Zero everything below τₙ; the last surviving lag is the order. The full path is returned for plotting.
Install & run
Add the crate and pass any &[f64]. Rust 1.81 or later; changes between versions in the changelog. R (extendr) and Python (PyO3) bindings are planned on top of this same core.
$ cargo add tsorder-core
// classical criteria and the default Fine–Hwang
use tsorder_core::*;
let bic = ic_order(&x, Criterion::Bic, None)?;
let fh = fh_order(&x, FhConfig::default())?;
println!("BIC {} / FH {}", bic.order, fh.order);
// the whole studentized path, for plots
for (k, t) in fh.studentized.iter().enumerate() {
println!("lag {} |t| {:.2}", k + 1, t.abs());
}
// MA order, level-calibrated threshold, ensemble
let ma = fh_order(&x, FhConfig {
statistic: Statistic::AcfBartlett,
threshold: Threshold::Gumbel { alpha: 0.05 },
..Default::default()
})?;
let e = ensemble_order(&x, &[
FhConfig::default(),
FhConfig { statistic: Statistic::LongArOls,
..Default::default() },
], EnsembleRule::Majority)?;
Built on the literature, tested against it
Each piece implements a published result and says so. Where a convention mattered, it follows the R reference implementation, so numbers are comparable across ecosystems.
| Component | Foundation |
|---|---|
| Threshold-and-clip order estimation | Fine & Hwang (1979); Hwang (1978) |
| PACF null distribution (self-studentized) | Quenouille (1949) |
| ACF standard errors for MA | Bartlett (1946) |
| Inverse autocorrelations from a long AR fit | Cleveland (1972) |
| Bands over a growing Yule–Walker fit, Gumbel calibration | Jirak (2012, Ann. Statist.) |
| Q_n scale and robust autocorrelations | Rousseeuw & Croux (1993); Croux & Rousseeuw (1992); Ma & Genton (2000) |
| Innovation variances & criteria conventions | R's stats::ar.yw; AICc as in forecast |
| Ensemble (model by consensus) | García-Hiernaux, Jerez & Casals (2012) |
| McMillan degree: SVC via canonical correlations (Table 1 reproduced), Hankel SVT, sequential rank test | Bauer (2001); Zheng, Jedra & Proutière (2025); Baram & Porat (1988); Robin & Smith (2000) |
| ARMA (p, q): EACF table (matches R's TSA::eacf) and recursive regression | Tsay & Tiao (1984); Hannan & Rissanen (1982) |
Tested properties
Order recovery on long simulated AR, MA and subset-AR series; affine invariance of every statistic and criterion; empirical level of the Gumbel rule on white noise; errors instead of silent NaN.
CI on every platform
fmt · clippy -D warnings · tests and docs on Linux, macOS and Windows, plus the MSRV build (Rust 1.81).
MIT, nothing attached
No dependencies, no license to reconcile, no supply chain to watch. Pre-alpha: the API is still settling.