tsorder_core/
integration.rs1use crate::criteria::{ic_order, Criterion};
12use crate::dist::{df_tau_c_critical, kpss_critical};
13use crate::linalg::ols;
14use crate::{check_input, Error, Result};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum UnitRootTest {
19 Kpss,
22 Adf,
25}
26
27#[derive(Debug, Clone, Copy)]
29pub struct IntegrationConfig {
30 pub max_d: usize,
32 pub alpha: Option<f64>,
35 pub test: UnitRootTest,
36}
37
38impl Default for IntegrationConfig {
39 fn default() -> Self {
40 IntegrationConfig {
41 max_d: 2,
42 alpha: None,
43 test: UnitRootTest::Kpss,
44 }
45 }
46}
47
48#[derive(Debug, Clone)]
50pub struct IntegrationResult {
51 pub d: usize,
54 pub stats: Vec<f64>,
56 pub crits: Vec<f64>,
58 pub alpha: f64,
59 pub test: UnitRootTest,
60}
61
62pub fn integration_order(x: &[f64], config: IntegrationConfig) -> Result<IntegrationResult> {
66 let n = x.len();
67 check_input(x, 20 + 4 * config.max_d)?;
68 let alpha = config
69 .alpha
70 .unwrap_or(0.05 * 50f64.ln() / (n as f64).ln().max(1.0));
71 let mut series: Vec<f64> = x.to_vec();
72 let mut stats = Vec::new();
73 let mut crits = Vec::new();
74 let mut d = config.max_d;
75 for stage in 0..=config.max_d {
76 let (stat, crit, unit_root) = match config.test {
77 UnitRootTest::Kpss => {
78 let s = kpss_stat(&series)?;
79 let c = kpss_critical(alpha);
80 (s, c, s > c) }
82 UnitRootTest::Adf => {
83 let s = adf_stat(&series)?;
84 let c = df_tau_c_critical(alpha);
85 (s, c, s >= c) }
87 };
88 stats.push(stat);
89 crits.push(crit);
90 if !unit_root {
91 d = stage;
92 break;
93 }
94 if stage < config.max_d {
95 series = series.windows(2).map(|w| w[1] - w[0]).collect();
96 }
97 }
98 Ok(IntegrationResult {
99 d,
100 stats,
101 crits,
102 alpha,
103 test: config.test,
104 })
105}
106
107fn kpss_stat(x: &[f64]) -> Result<f64> {
109 let t = x.len();
110 let mean = x.iter().sum::<f64>() / t as f64;
111 let e: Vec<f64> = x.iter().map(|v| v - mean).collect();
112 let mut s = 0.0;
113 let mut sum_s2 = 0.0;
114 for v in &e {
115 s += v;
116 sum_s2 += s * s;
117 }
118 let bandwidth = (4.0 * (t as f64 / 100.0).powf(0.25)).floor() as usize;
119 let mut lr = e.iter().map(|v| v * v).sum::<f64>() / t as f64;
120 for j in 1..=bandwidth.min(t - 1) {
121 let w = 1.0 - j as f64 / (bandwidth as f64 + 1.0);
122 let g = (0..t - j).map(|i| e[i] * e[i + j]).sum::<f64>() / t as f64;
123 lr += 2.0 * w * g;
124 }
125 if lr <= 0.0 {
126 return Err(Error::Singular);
127 }
128 Ok(sum_s2 / (t as f64 * t as f64 * lr))
129}
130
131fn adf_stat(x: &[f64]) -> Result<f64> {
134 let t = x.len();
135 let dx: Vec<f64> = x.windows(2).map(|w| w[1] - w[0]).collect();
136 let k = ic_order(&dx, Criterion::Bic, None)?.order;
137 let start = k + 1; let rows = dx.len() - start;
139 let cols = 2 + k;
140 if rows <= cols + 2 {
141 return Err(Error::TooShort {
142 n: t,
143 needed: cols + start + 3,
144 });
145 }
146 let mut design = vec![0.0; rows * cols];
147 let mut y = vec![0.0; rows];
148 for r in 0..rows {
149 let ti = start + r; y[r] = dx[ti];
151 design[r * cols] = 1.0;
152 design[r * cols + 1] = x[ti];
153 for j in 0..k {
154 design[r * cols + 2 + j] = dx[ti - j - 1];
155 }
156 }
157 let (beta, _s2, se) = ols(&design, rows, cols, &y)?;
158 Ok(beta[1] / se[1])
159}