1use crate::dim::Dim;
8use crate::error::FdarError;
9use crate::iter_maybe_parallel;
10use crate::matrix::FdMatrix;
11#[cfg(feature = "parallel")]
12use rayon::iter::ParallelIterator;
13
14#[must_use = "expensive computation whose result should not be discarded"]
34pub(crate) fn linfinity_depth_1d(
35 data_obj: &FdMatrix,
36 data_ori: &FdMatrix,
37) -> Result<Vec<f64>, FdarError> {
38 let (nobj, nori, m) = (data_obj.nrows(), data_ori.nrows(), data_obj.ncols());
39 if nobj == 0 || nori == 0 || m == 0 {
40 return Err(FdarError::InvalidDimension {
41 parameter: "data_obj",
42 expected: "non-empty matrices".to_string(),
43 actual: format!("{nobj}x{m}"),
44 });
45 }
46 if data_ori.ncols() != m {
47 return Err(FdarError::InvalidDimension {
48 parameter: "data_ori",
49 expected: format!("same number of columns as data_obj ({m})"),
50 actual: format!("{}", data_ori.ncols()),
51 });
52 }
53
54 let depths: Vec<f64> = iter_maybe_parallel!(0..nobj)
55 .map(|i| {
56 let mut total = 0.0_f64;
57 for j in 0..nori {
58 let mut sup = 0.0_f64;
59 for t in 0..m {
60 let d = (data_obj[(i, t)] - data_ori[(j, t)]).abs();
61 if d > sup {
62 sup = d;
63 }
64 }
65 total += sup;
66 }
67 let mean_dist = total / nori as f64;
68 1.0 / (1.0 + mean_dist)
69 })
70 .collect();
71
72 Ok(depths)
73}
74
75#[must_use = "expensive computation whose result should not be discarded"]
80pub fn linfinity_depth(
81 data_obj: &FdMatrix,
82 data_ori: &FdMatrix,
83 dim: Dim,
84) -> Result<Vec<f64>, FdarError> {
85 match dim {
86 Dim::One | Dim::Two => linfinity_depth_1d(data_obj, data_ori),
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::depth::dispatch::{functional_depth, DepthMethod};
94
95 fn sample(n: usize, m: usize) -> FdMatrix {
96 let mut col_major = vec![0.0; n * m];
97 for i in 0..n {
98 for t in 0..m {
99 let x = t as f64 / (m as f64 - 1.0);
100 col_major[i + t * n] = (x * std::f64::consts::PI).sin() + 0.05 * i as f64;
101 }
102 }
103 FdMatrix::from_column_major(col_major, n, m).unwrap()
104 }
105
106 fn sample_with_outlier(n: usize, m: usize, outlier_idx: usize) -> FdMatrix {
107 let mut col_major = vec![0.0; n * m];
108 for i in 0..n {
109 for t in 0..m {
110 let x = t as f64 / (m as f64 - 1.0);
111 let base = (x * std::f64::consts::PI).sin();
112 let val = if i == outlier_idx {
113 base + 100.0
114 } else {
115 base + 0.01 * i as f64
116 };
117 col_major[i + t * n] = val;
118 }
119 }
120 FdMatrix::from_column_major(col_major, n, m).unwrap()
121 }
122
123 #[test]
124 fn depths_in_unit_interval() {
125 let data = sample(8, 20);
126 let ld = linfinity_depth_1d(&data, &data).unwrap();
127 assert_eq!(ld.len(), 8);
128 for &v in &ld {
129 assert!(v > 0.0 && v <= 1.0 + 1e-12, "L∞ depth out of range: {v}");
130 }
131 }
132
133 #[test]
134 fn closest_to_sample_is_deepest_and_outlier_shallow() {
135 let outlier_idx = 3usize;
136 let data = sample_with_outlier(8, 20, outlier_idx);
137 let ld = linfinity_depth_1d(&data, &data).unwrap();
138 let mut shallowest = 0usize;
140 let mut deepest = 0usize;
141 for i in 1..ld.len() {
142 if ld[i] < ld[shallowest] {
143 shallowest = i;
144 }
145 if ld[i] > ld[deepest] {
146 deepest = i;
147 }
148 }
149 assert_eq!(
150 shallowest, outlier_idx,
151 "outlier should be shallowest by L∞ depth"
152 );
153 assert_ne!(deepest, outlier_idx);
154 assert!(ld[deepest] > ld[outlier_idx]);
155 }
156
157 #[test]
158 fn monotone_decreasing_in_mean_distance() {
159 let n = 9usize;
161 let data = sample(n, 25);
162 let ld = linfinity_depth_1d(&data, &data).unwrap();
163 let mut deepest = 0usize;
164 for i in 1..n {
165 if ld[i] > ld[deepest] {
166 deepest = i;
167 }
168 }
169 assert_eq!(
170 deepest,
171 n / 2,
172 "central curve should be deepest by L∞ depth"
173 );
174 }
175
176 #[test]
177 fn single_curve_self_depth_is_one() {
178 let one = sample(1, 8);
179 let ld = linfinity_depth_1d(&one, &one).unwrap();
180 assert_eq!(ld.len(), 1);
181 assert!(
182 (ld[0] - 1.0).abs() < 1e-12,
183 "n=1 self-depth should be 1.0, got {}",
184 ld[0]
185 );
186 }
187
188 #[test]
189 fn dispatch_round_trip_and_empty_err() {
190 let data = sample(6, 12);
191 let got = functional_depth(&data, DepthMethod::LInfinity).unwrap();
192 assert_eq!(got, linfinity_depth_1d(&data, &data).unwrap());
193 let empty = FdMatrix::from_column_major(vec![], 0, 0).unwrap();
194 assert!(linfinity_depth_1d(&empty, &empty).is_err());
195 }
196}