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 fraiman_muniz;
14pub mod modal;
15pub mod random_projection;
16pub mod random_tukey;
17pub mod rpd;
18pub mod spatial;
19
20#[cfg(test)]
21mod tests;
22
23pub use band::{band_1d, modified_band_1d, modified_epigraph_index_1d};
25pub use dispatch::{functional_boxplot, functional_depth, DepthMethod, FunctionalBoxplotResult};
26pub use fraiman_muniz::{fraiman_muniz_1d, fraiman_muniz_2d};
27pub use modal::{modal_1d, modal_2d};
28pub use random_projection::{
29 random_projection_1d, random_projection_1d_seeded, random_projection_2d,
30};
31pub use random_tukey::{random_tukey_1d, random_tukey_1d_seeded, random_tukey_2d};
32pub use rpd::{rpd_depth_1d, rpd_depth_1d_seeded};
33pub use spatial::{
34 functional_spatial_1d, functional_spatial_2d, kernel_functional_spatial_1d,
35 kernel_functional_spatial_2d,
36};
37
38pub(super) fn generate_random_projections(nproj: usize, m: usize, seed: Option<u64>) -> Vec<f64> {
49 let mut rng: Box<dyn RngCore> = match seed {
50 Some(s) => Box::new(StdRng::seed_from_u64(s)),
51 None => Box::new(rand::thread_rng()),
52 };
53 let mut projections = vec![0.0; nproj * m];
54 for p_idx in 0..nproj {
55 let base = p_idx * m;
56 let mut norm_sq = 0.0;
57 for t in 0..m {
58 let v: f64 = rng.sample(StandardNormal);
59 projections[base + t] = v;
60 norm_sq += v * v;
61 }
62 let inv_norm = 1.0 / norm_sq.sqrt();
63 for t in 0..m {
64 projections[base + t] *= inv_norm;
65 }
66 }
67 projections
68}
69
70pub(super) fn project_and_sort_reference(
75 data_ori: &FdMatrix,
76 projections: &[f64],
77 nproj: usize,
78 nori: usize,
79 m: usize,
80) -> Vec<f64> {
81 let mut sorted = vec![0.0; nproj * nori];
82 maybe_par_chunks_mut_enumerate!(sorted, nori, |(p_idx, spo): (usize, &mut [f64])| {
83 let proj = &projections[p_idx * m..(p_idx + 1) * m];
84 for j in 0..nori {
85 let mut dot = 0.0;
86 for t in 0..m {
87 dot += data_ori[(j, t)] * proj[t];
88 }
89 spo[j] = dot;
90 }
91 spo.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
92 });
93 sorted
94}
95
96pub(super) fn random_depth_core(
103 data_obj: &FdMatrix,
104 data_ori: &FdMatrix,
105 nproj: usize,
106 seed: Option<u64>,
107 init: f64,
108 aggregate: impl Fn(f64, f64) -> f64 + Sync,
109 finalize: impl Fn(f64, usize) -> f64 + Sync,
110) -> Vec<f64> {
111 use crate::iter_maybe_parallel;
112 #[cfg(feature = "parallel")]
113 use rayon::iter::ParallelIterator;
114
115 let nobj = data_obj.nrows();
116 let nori = data_ori.nrows();
117 let m = data_obj.ncols();
118
119 if nobj == 0 || nori == 0 || m == 0 || nproj == 0 {
120 return Vec::new();
121 }
122
123 let projections = generate_random_projections(nproj, m, seed);
124 let sorted_proj_ori = project_and_sort_reference(data_ori, &projections, nproj, nori, m);
125 let denom = nori as f64 + 1.0;
126
127 iter_maybe_parallel!(0..nobj)
128 .map(|i| {
129 let mut acc = init;
130 for p_idx in 0..nproj {
131 let proj = &projections[p_idx * m..(p_idx + 1) * m];
132 let sorted_ori = &sorted_proj_ori[p_idx * nori..(p_idx + 1) * nori];
133
134 let mut proj_i = 0.0;
135 for t in 0..m {
136 proj_i += data_obj[(i, t)] * proj[t];
137 }
138
139 let below = sorted_ori.partition_point(|&v| v < proj_i);
140 let above = nori - sorted_ori.partition_point(|&v| v <= proj_i);
141 let depth = (below.min(above) as f64 + 1.0) / denom;
142 acc = aggregate(acc, depth);
143 }
144 finalize(acc, nproj)
145 })
146 .collect()
147}