scirs2_integrate/ode/ensemble/
types.rs1#[non_exhaustive]
5#[derive(Debug, Clone)]
6pub struct EnsembleConfig {
7 pub n_ensemble: usize,
10 pub n_threads: usize,
12 pub rtol: f64,
14 pub atol: f64,
16 pub t_span: (f64, f64),
18 pub max_steps: usize,
20 pub h_init: f64,
22}
23
24impl Default for EnsembleConfig {
25 fn default() -> Self {
26 let n_threads = num_cpus::get().max(1);
27 Self {
28 n_ensemble: 100,
29 n_threads,
30 rtol: 1e-6,
31 atol: 1e-9,
32 t_span: (0.0, 1.0),
33 max_steps: 100_000,
34 h_init: 0.0,
35 }
36 }
37}
38
39#[derive(Debug, Clone)]
41pub struct EnsembleResult {
42 pub trajectories: Vec<Vec<Vec<f64>>>,
45 pub times: Vec<Vec<f64>>,
47 pub converged: Vec<bool>,
49 pub n_steps: Vec<usize>,
51}
52
53impl EnsembleResult {
54 pub fn mean_trajectory(&self) -> Option<Vec<Vec<f64>>> {
63 if self.trajectories.is_empty() {
64 return None;
65 }
66
67 let ref_len = self.trajectories[0].len();
69 if ref_len == 0 {
70 return None;
71 }
72
73 let n_state = self.trajectories[0][0].len();
74 let n_members = self.trajectories.len();
75
76 let min_len = self
78 .trajectories
79 .iter()
80 .map(|traj| traj.len())
81 .min()
82 .unwrap_or(0);
83
84 if min_len == 0 {
85 return None;
86 }
87
88 let mut mean = vec![vec![0.0_f64; n_state]; min_len];
89 for traj in &self.trajectories {
90 for (k, step) in traj.iter().take(min_len).enumerate() {
91 for (j, &val) in step.iter().enumerate() {
92 mean[k][j] += val;
93 }
94 }
95 }
96 let n_f = n_members as f64;
97 for step in mean.iter_mut() {
98 for val in step.iter_mut() {
99 *val /= n_f;
100 }
101 }
102 Some(mean)
103 }
104
105 pub fn std_trajectory(&self) -> Option<Vec<Vec<f64>>> {
109 if self.trajectories.len() < 2 {
110 return None;
111 }
112 let mean = self.mean_trajectory()?;
113 let min_len = mean.len();
114 let n_state = mean[0].len();
115 let n_members = self.trajectories.len();
116
117 let mut variance = vec![vec![0.0_f64; n_state]; min_len];
118 for traj in &self.trajectories {
119 for (k, step) in traj.iter().take(min_len).enumerate() {
120 for (j, &val) in step.iter().enumerate() {
121 let diff = val - mean[k][j];
122 variance[k][j] += diff * diff;
123 }
124 }
125 }
126 let n_f = (n_members - 1) as f64;
127 for step in variance.iter_mut() {
128 for val in step.iter_mut() {
129 *val = (*val / n_f).sqrt();
130 }
131 }
132 Some(variance)
133 }
134
135 pub fn quantile_trajectories(&self, q: f64) -> Option<Vec<Vec<f64>>> {
140 if self.trajectories.is_empty() {
141 return None;
142 }
143 let min_len = self.trajectories.iter().map(|t| t.len()).min().unwrap_or(0);
144 if min_len == 0 {
145 return None;
146 }
147 let n_state = self.trajectories[0][0].len();
148 let n_members = self.trajectories.len();
149
150 let mut result = vec![vec![0.0_f64; n_state]; min_len];
151 for k in 0..min_len {
152 for j in 0..n_state {
153 let mut vals: Vec<f64> = self
154 .trajectories
155 .iter()
156 .filter(|traj| traj.len() > k)
157 .map(|traj| traj[k][j])
158 .collect();
159 vals.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
160 let idx = ((q * (n_members - 1) as f64).round() as usize).min(n_members - 1);
161 result[k][j] = vals[idx];
162 }
163 }
164 Some(result)
165 }
166}