Skip to main content

fdars_core/fts/
mod.rs

1//! Functional time series serial-dependence diagnostics.
2//!
3//! # R baselines
4//!
5//! * [`functional_acf`] / [`functional_pacf`] — `fdaACF::facf`
6//!   (Mestre et al. 2021, *Computational Statistics & Data Analysis*).
7//! * [`stationarity_test`] — `ftsa::T_stationary`
8//!   (Horváth, Kokoszka, Rice 2014, *Journal of Econometrics* 179:66–82).
9//! * [`long_run_covariance`] — `ftsa::long_run_covariance_estimation`
10//!   (Bartlett HAC kernel-sandwich estimator).
11//! * [`functional_difference`] — `ftsa::diff.fts` (functional first-difference).
12//!
13//! # Conventions
14//!
15//! Entry points take an explicit deterministic `seed` (`StdRng::seed_from_u64(seed)`)
16//! and default Monte-Carlo replications of 999. All public functions return
17//! `Result<_, FdarError>` and validate inputs at entry. Result structs derive
18//! `Debug, Clone, PartialEq` and are serde-gated.
19
20mod acf;
21mod forecast;
22mod spectral;
23
24pub use acf::{
25    functional_acf, functional_difference, functional_pacf, long_run_covariance, stationarity_test,
26};
27pub use forecast::{fplsr, ftsm, ftsm_forecast, ftsm_forecast_multistep, ftsm_update};
28pub use spectral::{dpca, dpca_reconstruct, spectral_density};
29
30use crate::matrix::FdMatrix;
31
32/// Result of the spectral density operator estimator.
33///
34/// Produced by [`spectral_density`] (plan 41-01, FTS-03-01). Holds the complex
35/// m×m spectral density operator at each of the `n_curves` Fourier frequencies
36/// `θ_j = 2πj/N`. The operator at frequency `k` is stored as separate real
37/// (`re[k]`) and imaginary (`im[k]`) flat column-major m×m matrices — element
38/// `(j1, j2)` lives at index `j1 + j2 * m`. The operator is Hermitian:
39/// `im[k][j1 + j2*m] == -im[k][j2 + j1*m]`.
40///
41/// # Divergence from `freqdom`
42///
43/// The `1/2π` pre-factor is omitted (matching the crate's `long_run_covariance`,
44/// which does not divide by `2π`), so eigenvalues are `2π` larger than `freqdom`
45/// output. Filter shapes (eigenvectors) are unaffected.
46#[derive(Debug, Clone, PartialEq)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48#[non_exhaustive]
49pub struct SpectralDensityResult {
50    /// Fourier frequencies `θ_j = 2πj/N`, length `n_curves`.
51    pub freqs: Vec<f64>,
52    /// Real part of the operator at each frequency; `re[k]` is a flat column-major m×m matrix.
53    pub re: Vec<Vec<f64>>,
54    /// Imaginary part of the operator at each frequency; `im[k]` is a flat column-major m×m matrix.
55    pub im: Vec<Vec<f64>>,
56    /// Grid dimension m (each `re[k]`/`im[k]` is m×m).
57    pub m: usize,
58    /// Number of curves N (= number of Fourier frequencies).
59    pub n_curves: usize,
60    /// Bartlett lag-window bandwidth used.
61    pub bandwidth: usize,
62}
63
64/// Result of dynamic functional PCA (DPCA).
65///
66/// Produced by [`dpca`] (plan 41-01, FTS-03-02). Holds the time-domain dynamic
67/// eigen-filters and the dynamic score series.
68///
69/// # Divergence from `freqdom`
70///
71/// Eigenvectors are computed from `Re(f̂(θ))` via [`nalgebra::SymmetricEigen`]
72/// (nalgebra 0.33 has no stable complex Hermitian path without `faer`); this is
73/// exact for the leading dynamic subspace of a Bartlett-windowed estimator.
74/// Filters and scores use the Simpson-weighted L2 inner product consistently,
75/// so the estimator/score/reconstruction metric matches.
76#[derive(Debug, Clone, PartialEq)]
77#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
78#[non_exhaustive]
79pub struct DpcaResult {
80    /// Dynamic eigen-filters, one per component. `filters[c]` is a `(2L+1) × m`
81    /// [`FdMatrix`]: row `l_idx` holds the filter tap at lag `l_idx - L`, column `j`
82    /// is the grid point.
83    pub filters: Vec<FdMatrix>,
84    /// Dynamic scores, shape `(N - 2L) × ncomp` (interior time points only).
85    pub scores: FdMatrix,
86    /// Per-component eigenvalue trajectory across frequencies: `eigenvalues[c]`
87    /// has length `n_freqs` (negative finite-sample eigenvalues clipped to 0).
88    pub eigenvalues: Vec<Vec<f64>>,
89    /// Number of Fourier frequencies N.
90    pub n_freqs: usize,
91    /// Filter lag support L (window is `[-L, L]`).
92    pub filter_lag: usize,
93    /// Number of retained dynamic components.
94    pub ncomp: usize,
95    /// Inclusive interior time range `(L, N-1-L)` for which scores are defined.
96    pub valid_range: (usize, usize),
97}
98
99/// Result of DPCA curve reconstruction from dynamic scores.
100///
101/// Produced by [`dpca_reconstruct`] (plan 41-01, FTS-03-03). The
102/// `reconstruction_error` is monotone non-increasing in the number of retained
103/// components (integrated-L2 error over the fully-defined interior).
104#[derive(Debug, Clone, PartialEq)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106#[non_exhaustive]
107pub struct DpcaReconstruction {
108    /// Reconstructed curves over the interior, shape `(N - 2L) × m`.
109    pub fitted: FdMatrix,
110    /// Integrated-L2 reconstruction error using K = 1..=ncomp components
111    /// (`reconstruction_error[K-1]`); monotone non-increasing in K.
112    pub reconstruction_error: Vec<f64>,
113    /// Inclusive interior time range `(L, N-1-L)` matching the source [`DpcaResult`].
114    pub valid_range: (usize, usize),
115}
116
117/// Result of functional ACF/PACF estimation.
118///
119/// Produced by [`functional_acf`] and [`functional_pacf`].
120/// Lag values run from 1 to `max_lag`; `acf`, `pacf`, and `upper_band`
121/// all have length `max_lag`.
122#[derive(Debug, Clone, PartialEq)]
123#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
124#[non_exhaustive]
125pub struct FacfResult {
126    /// Lag values (1..=max_lag).
127    pub lags: Vec<u32>,
128    /// Functional autocorrelation ρ_h at each lag (L2-norm, fdaACF convention).
129    pub acf: Vec<f64>,
130    /// Functional partial autocorrelation (scalar Durbin-Levinson approximation).
131    pub pacf: Vec<f64>,
132    /// Upper confidence band under the strong-white-noise null (Monte-Carlo quantile).
133    pub upper_band: Vec<f64>,
134}
135
136/// Result of the functional stationarity test.
137///
138/// Produced by `stationarity_test` (plan 34-02).
139#[derive(Debug, Clone, PartialEq)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
141#[non_exhaustive]
142pub struct StationarityResult {
143    /// Test statistic T (KPSS-style partial-sum L2 norm).
144    pub statistic: f64,
145    /// Monte-Carlo permutation p-value.
146    pub p_value: f64,
147    /// Number of permutations used.
148    pub n_perm: usize,
149}
150
151/// Result of the Bartlett kernel-sandwich long-run covariance estimator.
152///
153/// Produced by `long_run_covariance` (plan 34-02).
154#[derive(Debug, Clone, PartialEq)]
155#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
156#[non_exhaustive]
157pub struct LongRunCovResult {
158    /// Estimated m×m long-run covariance matrix (column-major flat Vec).
159    pub cov_matrix: Vec<f64>,
160    /// Grid dimension m (cov_matrix is m×m).
161    pub m: usize,
162    /// Bandwidth used.
163    pub bandwidth: usize,
164    /// Number of curves N.
165    pub n_curves: usize,
166}
167
168/// Diagnostics for a single fitted FPC-score AR(p) model.
169///
170/// One per retained component in [`FtsmResult::ar_models`]. Produced by [`ftsm`].
171#[derive(Debug, Clone, PartialEq)]
172#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
173#[non_exhaustive]
174pub struct ArModelResult {
175    /// Selected AR order p (0 = white noise), chosen by AIC.
176    pub order: usize,
177    /// AR coefficients phi_1..phi_p (0-indexed: `phi[0]` is the lag-1 coefficient).
178    pub phi: Vec<f64>,
179    /// Innovation (residual) variance from the Yule-Walker fit.
180    pub sigma2: f64,
181}
182
183/// Result of fitting the FPCA-based functional time-series model.
184///
185/// Produced by [`ftsm`]. Carries the FPCA decomposition (mean, loadings,
186/// score-time-series, reconstructed fitted curves, integration weights) plus the
187/// per-component AR-model diagnostics used for forecasting.
188#[derive(Debug, Clone, PartialEq)]
189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190#[non_exhaustive]
191pub struct FtsmResult {
192    /// Mean curve μ(u), length m.
193    pub mean: Vec<f64>,
194    /// FPC loadings φ_k, shape m × ncomp.
195    pub rotation: crate::matrix::FdMatrix,
196    /// FPC score time-series β_{t,k}, shape n × ncomp.
197    pub scores: crate::matrix::FdMatrix,
198    /// Reconstructed fitted curves, shape n × m.
199    pub fitted: crate::matrix::FdMatrix,
200    /// Simpson integration weights, length m.
201    pub weights: Vec<f64>,
202    /// Effective number of retained components (clamped to min(ncomp, n, m)).
203    pub ncomp: usize,
204    /// Per-component fitted AR-model diagnostics (length = ncomp).
205    pub ar_models: Vec<ArModelResult>,
206}
207
208/// Result of an FPC-score-AR curve forecast.
209///
210/// Produced by [`ftsm_forecast`]. `forecast` is an h × m matrix whose row `i`
211/// holds the forecast curve for horizon `i + 1`.
212#[derive(Debug, Clone, PartialEq)]
213#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
214#[non_exhaustive]
215pub struct FtsmForecastResult {
216    /// Forecast curves, shape h × m (row i = horizon i+1).
217    pub forecast: crate::matrix::FdMatrix,
218    /// Forecast horizon (number of steps ahead).
219    pub h: usize,
220}
221
222/// Result of the functional PLS forecasting variant.
223///
224/// Produced by [`fplsr`]. A lag-1 PLS design (predictor = current curve,
225/// response = next curve) yields a one-step-ahead forecast curve plus the
226/// in-sample lag-1 fitted curves.
227#[derive(Debug, Clone, PartialEq)]
228#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
229#[non_exhaustive]
230pub struct FplsrResult {
231    /// One-step-ahead forecast of the next curve, shape 1 × m.
232    pub forecast: crate::matrix::FdMatrix,
233    /// In-sample lag-1 fitted curves, shape (n-1) × m.
234    pub fitted: crate::matrix::FdMatrix,
235    /// Number of PLS components used (clamped to min(ncomp, n-1, m)).
236    pub ncomp: usize,
237}