1use crate::matrix::FdMatrix;
7use crate::maybe_par_chunks_mut_enumerate;
8use rand::prelude::*;
9use rand_distr::StandardNormal;
10
11pub mod band;
12pub mod dispatch;
13pub mod erl;
14pub mod extremal;
15pub mod fraiman_muniz;
16pub mod half_region;
17pub mod hypo_epi;
18pub mod linf;
19pub mod modal;
20pub mod random_projection;
21pub mod random_tukey;
22pub mod rpd;
23pub mod spatial;
24pub mod tvd;
25
26#[cfg(test)]
27mod tests;
28
29pub use band::{band_1d, modified_band_1d, modified_epigraph_index_1d};
31pub use dispatch::{functional_boxplot, functional_depth, DepthMethod, FunctionalBoxplotResult};
32pub use erl::extreme_rank_length_depth_1d;
33pub use extremal::extremal_depth_1d;
34pub use fraiman_muniz::{fraiman_muniz_1d, fraiman_muniz_2d};
35pub use half_region::{half_region_depth_1d, modified_half_region_depth_1d};
36pub use hypo_epi::{epigraph_index_1d, hypograph_index_1d, modified_hypograph_index_1d};
37pub use linf::linfinity_depth_1d;
38pub use modal::{modal_1d, modal_2d};
39pub use random_projection::{
40 random_projection_1d, random_projection_1d_seeded, random_projection_2d,
41};
42pub use random_tukey::{random_tukey_1d, random_tukey_1d_seeded, random_tukey_2d};
43pub use rpd::{rpd_depth_1d, rpd_depth_1d_seeded};
44pub use spatial::{
45 functional_spatial_1d, functional_spatial_2d, kernel_functional_spatial_1d,
46 kernel_functional_spatial_2d,
47};
48pub use tvd::{total_variation_depth_1d, TvdMssResult};
49
50pub(super) fn generate_random_projections(nproj: usize, m: usize, seed: Option<u64>) -> Vec<f64> {
61 let mut rng: Box<dyn RngCore> = match seed {
62 Some(s) => Box::new(StdRng::seed_from_u64(s)),
63 None => Box::new(rand::thread_rng()),
64 };
65 let mut projections = vec![0.0; nproj * m];
66 for p_idx in 0..nproj {
67 let base = p_idx * m;
68 let mut norm_sq = 0.0;
69 for t in 0..m {
70 let v: f64 = rng.sample(StandardNormal);
71 projections[base + t] = v;
72 norm_sq += v * v;
73 }
74 let inv_norm = 1.0 / norm_sq.sqrt();
75 for t in 0..m {
76 projections[base + t] *= inv_norm;
77 }
78 }
79 projections
80}
81
82pub(super) fn project_and_sort_reference(
87 data_ori: &FdMatrix,
88 projections: &[f64],
89 nproj: usize,
90 nori: usize,
91 m: usize,
92) -> Vec<f64> {
93 let mut sorted = vec![0.0; nproj * nori];
94 maybe_par_chunks_mut_enumerate!(sorted, nori, |(p_idx, spo): (usize, &mut [f64])| {
95 let proj = &projections[p_idx * m..(p_idx + 1) * m];
96 for j in 0..nori {
97 let mut dot = 0.0;
98 for t in 0..m {
99 dot += data_ori[(j, t)] * proj[t];
100 }
101 spo[j] = dot;
102 }
103 spo.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
104 });
105 sorted
106}
107
108pub(super) fn random_depth_core(
115 data_obj: &FdMatrix,
116 data_ori: &FdMatrix,
117 nproj: usize,
118 seed: Option<u64>,
119 init: f64,
120 aggregate: impl Fn(f64, f64) -> f64 + Sync,
121 finalize: impl Fn(f64, usize) -> f64 + Sync,
122) -> Vec<f64> {
123 use crate::iter_maybe_parallel;
124 #[cfg(feature = "parallel")]
125 use rayon::iter::ParallelIterator;
126
127 let nobj = data_obj.nrows();
128 let nori = data_ori.nrows();
129 let m = data_obj.ncols();
130
131 if nobj == 0 || nori == 0 || m == 0 || nproj == 0 {
132 return Vec::new();
133 }
134
135 let projections = generate_random_projections(nproj, m, seed);
136 let sorted_proj_ori = project_and_sort_reference(data_ori, &projections, nproj, nori, m);
137 let denom = nori as f64 + 1.0;
138
139 iter_maybe_parallel!(0..nobj)
140 .map(|i| {
141 let mut acc = init;
142 for p_idx in 0..nproj {
143 let proj = &projections[p_idx * m..(p_idx + 1) * m];
144 let sorted_ori = &sorted_proj_ori[p_idx * nori..(p_idx + 1) * nori];
145
146 let mut proj_i = 0.0;
147 for t in 0..m {
148 proj_i += data_obj[(i, t)] * proj[t];
149 }
150
151 let below = sorted_ori.partition_point(|&v| v < proj_i);
152 let above = nori - sorted_ori.partition_point(|&v| v <= proj_i);
153 let depth = (below.min(above) as f64 + 1.0) / denom;
154 acc = aggregate(acc, depth);
155 }
156 finalize(acc, nproj)
157 })
158 .collect()
159}