1use crate::common::IntegrateFloat;
8use crate::error::IntegrateResult;
9use scirs2_core::ndarray::{Array1, ArrayView1};
10use std::collections::VecDeque;
11
12pub struct AdvancedErrorEstimator<F: IntegrateFloat> {
14 pub tolerance: F,
16 pub max_richardson_order: usize,
18 solution_history: VecDeque<Array1<F>>,
20 step_size_history: VecDeque<F>,
22 error_history: VecDeque<F>,
24}
25
26pub struct RichardsonExtrapolator<F: IntegrateFloat> {
28 pub order: usize,
30 pub step_ratios: Vec<F>,
32 solutions: Vec<Array1<F>>,
34}
35
36pub struct SpectralErrorIndicator<F: IntegrateFloat> {
38 pub window_size: usize,
40 pub decay_threshold: F,
42 history: VecDeque<Array1<F>>,
44}
45
46pub struct DefectCorrector<F: IntegrateFloat> {
48 pub max_iterations: usize,
50 pub tolerance: F,
52 pub iterative: bool,
54}
55
56#[derive(Debug, Clone)]
58pub struct ErrorAnalysisResult<F: IntegrateFloat> {
59 pub primary_estimate: F,
61 pub richardson_error: Option<F>,
63 pub spectral_error: Option<F>,
65 pub defect_error: Option<F>,
67 pub quality_metrics: SolutionQualityMetrics<F>,
69 pub recommended_step_size: F,
71 pub confidence: F,
73 pub error_distribution: ErrorDistribution<F>,
75}
76
77#[derive(Debug, Clone)]
79pub struct SolutionQualityMetrics<F: IntegrateFloat> {
80 pub smoothness: F,
82 pub regularity: F,
84 pub conservation_error: Option<F>,
86 pub monotonicity_violations: usize,
88 pub oscillation_index: F,
90 pub signal_noise_ratio: F,
92}
93
94#[derive(Debug, Clone)]
96pub struct ErrorDistribution<F: IntegrateFloat> {
97 pub mean_error: F,
99 pub std_deviation: F,
101 pub max_error: F,
103 pub skewness: F,
105 pub component_errors: Vec<F>,
107}
108
109impl<F: IntegrateFloat> AdvancedErrorEstimator<F> {
110 pub fn new(tolerance: F, max_richardsonorder: usize) -> Self {
112 Self {
113 tolerance,
114 max_richardson_order: max_richardsonorder,
115 solution_history: VecDeque::new(),
116 step_size_history: VecDeque::new(),
117 error_history: VecDeque::new(),
118 }
119 }
120
121 pub fn analyze_error<Func>(
123 &mut self,
124 current_solution: &Array1<F>,
125 step_size: F,
126 ode_function: Func,
127 embedded_error: Option<F>,
128 ) -> IntegrateResult<ErrorAnalysisResult<F>>
129 where
130 Func: Fn(F, &ArrayView1<F>) -> Array1<F>,
131 {
132 self.solution_history.push_back(current_solution.clone());
134 self.step_size_history.push_back(step_size);
135
136 while self.solution_history.len() > 20 {
138 self.solution_history.pop_front();
139 self.step_size_history.pop_front();
140 }
141
142 let mut result = ErrorAnalysisResult {
143 primary_estimate: embedded_error
144 .unwrap_or(F::from(1e-8).expect("Failed to convert constant to float")),
145 richardson_error: None,
146 spectral_error: None,
147 defect_error: None,
148 quality_metrics: self.assess_solution_quality()?,
149 recommended_step_size: step_size,
150 confidence: F::from(0.5).expect("Failed to convert constant to float"), error_distribution: self.analyze_error_distribution(current_solution)?,
152 };
153
154 if self.solution_history.len() >= 3 {
156 result.richardson_error = self.richardson_extrapolation()?;
157 }
158
159 if self.solution_history.len() >= 5 {
161 result.spectral_error = self.spectral_error_analysis()?;
162 }
163
164 result.defect_error = self.defect_based_error(current_solution, &ode_function)?;
166
167 result.confidence = Self::compute_confidence(&result);
169 result.recommended_step_size = self.recommend_step_size(&result, step_size);
170
171 self.error_history.push_back(result.primary_estimate);
173 while self.error_history.len() > 20 {
174 self.error_history.pop_front();
175 }
176
177 Ok(result)
178 }
179
180 fn richardson_extrapolation(&self) -> IntegrateResult<Option<F>> {
182 if self.solution_history.len() < 3 || self.step_size_history.len() < 3 {
183 return Ok(None);
184 }
185
186 let n = self.solution_history.len();
187 let y2 = &self.solution_history[n - 1]; let y1 = &self.solution_history[n - 2]; let _y0 = &self.solution_history[n - 3]; let h2 = self.step_size_history[n - 1];
192 let h1 = self.step_size_history[n - 2];
193
194 let r = h1 / h2;
196 if (r - F::one()).abs() < F::from(0.1).expect("Failed to convert constant to float") {
197 return Ok(None);
199 }
200
201 let extrapolated_error = (y2 - y1).mapv(|x| x.abs()).sum() / (r.powi(2) - F::one());
203
204 Ok(Some(extrapolated_error))
205 }
206
207 fn spectral_error_analysis(&self) -> IntegrateResult<Option<F>> {
209 if self.solution_history.len() < 5 {
210 return Ok(None);
211 }
212
213 let _n = self.solution_history.len();
214 let recent_solutions = &self.solution_history;
215
216 let mut spectral_norm = F::zero();
218 let mut total_norm = F::zero();
219
220 for component in 0..recent_solutions[0].len() {
221 let values: Vec<F> = recent_solutions.iter().map(|sol| sol[component]).collect();
222
223 if values.len() >= 3 {
225 for i in 1..values.len() - 1 {
226 let second_diff = values[i + 1]
227 - F::from(2.0).expect("Failed to convert constant to float") * values[i]
228 + values[i - 1];
229 spectral_norm += second_diff.abs();
230 total_norm += values[i].abs();
231 }
232 }
233 }
234
235 if total_norm > F::zero() {
236 let spectral_indicator = spectral_norm / total_norm;
237 Ok(Some(spectral_indicator))
238 } else {
239 Ok(None)
240 }
241 }
242
243 fn defect_based_error<Func>(
245 &self,
246 current_solution: &Array1<F>,
247 ode_function: &Func,
248 ) -> IntegrateResult<Option<F>>
249 where
250 Func: Fn(F, &ArrayView1<F>) -> Array1<F>,
251 {
252 if self.solution_history.len() < 2 || self.step_size_history.is_empty() {
253 return Ok(None);
254 }
255
256 let h = *self.step_size_history.back().expect("Operation failed");
257 let t = F::zero(); let f_current = ode_function(t, ¤t_solution.view());
262 let defect_norm = f_current.mapv(|x| x.abs()).sum() * h;
263
264 Ok(Some(defect_norm))
265 }
266
267 fn assess_solution_quality(&self) -> IntegrateResult<SolutionQualityMetrics<F>> {
269 let mut metrics = SolutionQualityMetrics {
270 smoothness: F::zero(),
271 regularity: F::zero(),
272 conservation_error: None,
273 monotonicity_violations: 0,
274 oscillation_index: F::zero(),
275 signal_noise_ratio: F::one(),
276 };
277
278 if self.solution_history.len() < 3 {
279 return Ok(metrics);
280 }
281
282 let n = self.solution_history.len();
283 let solutions = &self.solution_history;
284
285 let mut total_variation = F::zero();
287 let mut total_magnitude = F::zero();
288
289 for i in 1..n {
290 let diff = &solutions[i] - &solutions[i - 1];
291 total_variation += diff.mapv(|x| x.abs()).sum();
292 total_magnitude += solutions[i].mapv(|x| x.abs()).sum();
293 }
294
295 if total_magnitude > F::zero() {
296 metrics.smoothness = F::one() / (F::one() + total_variation / total_magnitude);
297 }
298
299 if n >= 3 {
301 let mut oscillations = 0;
302 for comp in 0..solutions[0].len() {
303 for i in 1..n - 1 {
304 let prev = solutions[i - 1][comp];
305 let curr = solutions[i][comp];
306 let next = solutions[i + 1][comp];
307
308 if (curr - prev) * (next - curr) < F::zero() {
310 oscillations += 1;
311 }
312 }
313 }
314 metrics.oscillation_index = F::from(oscillations).expect("Failed to convert to float")
315 / F::from(n * solutions[0].len()).expect("Operation failed");
316 }
317
318 if n >= 4 {
320 let mut signal_power = F::zero();
321 let mut noise_power = F::zero();
322
323 for comp in 0..solutions[0].len() {
324 let values: Vec<F> = solutions.iter().map(|sol| sol[comp]).collect();
325 let mean = values.iter().fold(F::zero(), |acc, &x| acc + x)
326 / F::from(values.len()).expect("Operation failed");
327
328 for &val in &values {
330 signal_power += (val - mean).powi(2);
331 }
332
333 for i in 1..values.len() - 1 {
335 let second_diff = values[i + 1]
336 - F::from(2.0).expect("Failed to convert constant to float") * values[i]
337 + values[i - 1];
338 noise_power += second_diff.powi(2);
339 }
340 }
341
342 if noise_power > F::zero() {
343 metrics.signal_noise_ratio = signal_power / noise_power;
344 }
345 }
346
347 Ok(metrics)
348 }
349
350 fn analyze_error_distribution(
352 &self,
353 solution: &Array1<F>,
354 ) -> IntegrateResult<ErrorDistribution<F>> {
355 let n_components = solution.len();
356 let mut component_errors = vec![F::zero(); n_components];
357
358 if self.solution_history.len() >= 2 {
360 let prev_solution = self.solution_history.back().expect("Operation failed");
361 for i in 0..n_components {
362 component_errors[i] = (solution[i] - prev_solution[i]).abs();
363 }
364 }
365
366 let mean_error = component_errors.iter().fold(F::zero(), |acc, &x| acc + x)
368 / F::from(n_components).expect("Failed to convert to float");
369
370 let variance = component_errors
371 .iter()
372 .map(|&err| (err - mean_error).powi(2))
373 .fold(F::zero(), |acc, x| acc + x)
374 / F::from(n_components).expect("Failed to convert to float");
375
376 let std_deviation = variance.sqrt();
377 let max_error = component_errors
378 .iter()
379 .fold(F::zero(), |acc, &x| acc.max(x));
380
381 let skewness = if std_deviation > F::zero() {
383 component_errors
384 .iter()
385 .map(|&err| ((err - mean_error) / std_deviation).powi(3))
386 .fold(F::zero(), |acc, x| acc + x)
387 / F::from(n_components).expect("Failed to convert to float")
388 } else {
389 F::zero()
390 };
391
392 Ok(ErrorDistribution {
393 mean_error,
394 std_deviation,
395 max_error,
396 skewness,
397 component_errors,
398 })
399 }
400
401 fn compute_confidence(result: &ErrorAnalysisResult<F>) -> F {
403 let mut confidence_factors = Vec::new();
404
405 let estimates = [
407 Some(result.primary_estimate),
408 result.richardson_error,
409 result.spectral_error,
410 result.defect_error,
411 ]
412 .iter()
413 .filter_map(|&est| est)
414 .collect::<Vec<_>>();
415
416 if estimates.len() >= 2 {
417 let mean_est = estimates.iter().fold(F::zero(), |acc, &x| acc + x)
418 / F::from(estimates.len()).expect("Operation failed");
419 let relative_std = estimates
420 .iter()
421 .map(|&est| ((est - mean_est) / mean_est).abs())
422 .fold(F::zero(), |acc, x| acc + x)
423 / F::from(estimates.len()).expect("Operation failed");
424
425 confidence_factors.push(F::one() / (F::one() + relative_std));
427 }
428
429 confidence_factors.push(result.quality_metrics.smoothness);
431 confidence_factors.push(F::one() / (F::one() + result.quality_metrics.oscillation_index));
432
433 if !confidence_factors.is_empty() {
435 confidence_factors.iter().fold(F::zero(), |acc, &x| acc + x)
436 / F::from(confidence_factors.len()).expect("Operation failed")
437 } else {
438 F::from(0.5).expect("Failed to convert constant to float") }
440 }
441
442 fn recommend_step_size(&self, result: &ErrorAnalysisResult<F>, currentstep: F) -> F {
444 let target_error = self.tolerance;
445 let current_error = result.primary_estimate;
446
447 if current_error <= F::zero() {
448 return currentstep;
449 }
450
451 let safety_factor = F::from(0.8).expect("Failed to convert constant to float")
453 + F::from(0.15).expect("Failed to convert constant to float") * result.confidence;
454
455 let ratio = (target_error / current_error)
457 .powf(F::from(0.5).expect("Failed to convert constant to float"));
458 let _basic_recommendation = currentstep * ratio * safety_factor;
459
460 let quality_factor = if result.quality_metrics.oscillation_index
462 > F::from(0.1).expect("Failed to convert constant to float")
463 {
464 F::from(0.7).expect("Failed to convert constant to float") } else if result.quality_metrics.smoothness
466 > F::from(0.8).expect("Failed to convert constant to float")
467 {
468 F::from(1.2).expect("Failed to convert constant to float") } else {
470 F::one()
471 };
472
473 let min_factor = F::from(0.2).expect("Failed to convert constant to float");
475 let max_factor = F::from(5.0).expect("Failed to convert constant to float");
476 let final_factor = (ratio * safety_factor * quality_factor)
477 .max(min_factor)
478 .min(max_factor);
479
480 currentstep * final_factor
481 }
482}
483
484impl<F: IntegrateFloat> RichardsonExtrapolator<F> {
485 pub fn new(order: usize) -> Self {
487 Self {
488 order,
489 step_ratios: vec![
490 F::from(0.5).expect("Failed to convert constant to float"),
491 F::from(0.25).expect("Failed to convert constant to float"),
492 ],
493 solutions: Vec::new(),
494 }
495 }
496
497 pub fn add_solution(&mut self, solution: Array1<F>) {
499 self.solutions.push(solution);
500 if self.solutions.len() > self.order + 1 {
501 self.solutions.remove(0);
502 }
503 }
504
505 pub fn extrapolate(&self) -> IntegrateResult<Option<Array1<F>>> {
507 if self.solutions.len() < 2 {
508 return Ok(None);
509 }
510
511 let n = self.solutions.len();
512 let mut tableau = Vec::new();
513
514 for sol in &self.solutions {
516 tableau.push(vec![sol.clone()]);
517 }
518
519 for col in 1..n {
521 for row in 0..n - col {
522 let default_ratio = F::from(0.5).expect("Failed to convert constant to float");
523 let r = self.step_ratios.get(col - 1).unwrap_or(&default_ratio);
524 let r_power = r.powi(self.order as i32);
525
526 let numerator = &tableau[row + 1][col - 1] * r_power - &tableau[row][col - 1];
527 let denominator = r_power - F::one();
528
529 if denominator.abs() > F::from(1e-12).expect("Failed to convert constant to float")
530 {
531 let extrapolated = numerator / denominator;
532 tableau[row].push(extrapolated);
533 } else {
534 return Ok(None);
535 }
536 }
537 }
538
539 Ok(Some(tableau[0][n - 1].clone()))
541 }
542}
543
544impl<F: IntegrateFloat> SpectralErrorIndicator<F> {
545 pub fn new(window_size: usize, decaythreshold: F) -> Self {
547 Self {
548 window_size,
549 decay_threshold: decaythreshold,
550 history: VecDeque::new(),
551 }
552 }
553
554 pub fn add_solution(&mut self, solution: Array1<F>) {
556 self.history.push_back(solution);
557 while self.history.len() > self.window_size {
558 self.history.pop_front();
559 }
560 }
561
562 pub fn compute_indicator(&self) -> IntegrateResult<Option<F>> {
564 if self.history.len() < self.window_size {
565 return Ok(None);
566 }
567
568 let mut total_indicator = F::zero();
571 let n_components = self.history[0].len();
572
573 for comp in 0..n_components {
574 let values: Vec<F> = self.history.iter().map(|sol| sol[comp]).collect();
575
576 let mut diff_norms = Vec::new();
578 let mut current_values = values;
579
580 for _order in 0..3 {
581 if current_values.len() < 2 {
583 break;
584 }
585
586 let mut new_values = Vec::new();
587 let mut norm = F::zero();
588
589 for i in 0..current_values.len() - 1 {
590 let diff = current_values[i + 1] - current_values[i];
591 new_values.push(diff);
592 norm += diff.abs();
593 }
594
595 diff_norms.push(norm);
596 current_values = new_values;
597 }
598
599 if diff_norms.len() >= 2 {
601 for i in 1..diff_norms.len() {
602 if diff_norms[i - 1] > F::zero() {
603 let decay_rate = diff_norms[i] / diff_norms[i - 1];
604 if decay_rate > self.decay_threshold {
605 total_indicator += decay_rate;
606 }
607 }
608 }
609 }
610 }
611
612 Ok(Some(
613 total_indicator / F::from(n_components).expect("Failed to convert to float"),
614 ))
615 }
616}
617
618#[cfg(test)]
619mod tests {
620 use super::*;
621
622 #[test]
623 fn test_advanced_error_estimator() {
624 let mut estimator = AdvancedErrorEstimator::<f64>::new(1e-6, 3);
625
626 let solution = Array1::from_vec(vec![1.0, 2.0, 3.0]);
627 let step_size = 0.01;
628 let ode_fn =
629 |_t: f64, y: &ArrayView1<f64>| Array1::from_vec(y.iter().map(|&yi| -yi).collect());
630
631 let result = estimator.analyze_error(&solution, step_size, ode_fn, Some(1e-8));
632 assert!(result.is_ok());
633
634 let error_analysis = result.expect("Test: error analysis failed");
635 assert!(error_analysis.primary_estimate > 0.0);
636 assert!(error_analysis.confidence >= 0.0 && error_analysis.confidence <= 1.0);
637 }
638
639 #[test]
640 fn test_richardson_extrapolator() {
641 let mut extrapolator = RichardsonExtrapolator::<f64>::new(2);
642
643 extrapolator.add_solution(Array1::from_vec(vec![1.0, 2.0]));
644 extrapolator.add_solution(Array1::from_vec(vec![1.01, 2.01]));
645 extrapolator.add_solution(Array1::from_vec(vec![1.005, 2.005]));
646
647 let result = extrapolator.extrapolate();
648 assert!(result.is_ok());
649 }
650
651 #[test]
652 fn test_spectral_error_indicator() {
653 let mut indicator = SpectralErrorIndicator::<f64>::new(5, 0.5);
654
655 for i in 0..6 {
656 let solution = Array1::from_vec(vec![i as f64, (i as f64).sin()]);
657 indicator.add_solution(solution);
658 }
659
660 let result = indicator.compute_indicator();
661 assert!(result.is_ok());
662 }
663}