differential_equations/
stats.rs

1//! Statistics and performance tracking for Numerical methods
2
3use std::{
4    ops::{Add, AddAssign},
5    time::Instant,
6};
7
8use crate::traits::Real;
9
10/// Number of evaluations
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12pub struct Evals {
13    /// Number of function evaluations
14    pub function: usize,
15    /// Number of jacobian evaluations
16    pub jacobian: usize,
17    /// Number of Newton iterations
18    pub newton: usize,
19    /// Number of matrix decompositions
20    pub decompositions: usize,
21    /// Number of matrix system solves
22    pub solves: usize,
23}
24
25impl Evals {
26    /// Create a new Evals struct with zeroed fields
27    pub fn new() -> Self {
28        Self {
29            function: 0,
30            jacobian: 0,
31            newton: 0,
32            decompositions: 0,
33            solves: 0,
34        }
35    }
36}
37
38impl Add for Evals {
39    type Output = Self;
40
41    fn add(self, other: Self) -> Self {
42        Self {
43            function: self.function + other.function,
44            jacobian: self.jacobian + other.jacobian,
45            newton: self.newton + other.newton,
46            decompositions: self.decompositions + other.decompositions,
47            solves: self.solves + other.solves,
48        }
49    }
50}
51
52impl AddAssign for Evals {
53    fn add_assign(&mut self, other: Self) {
54        self.function += other.function;
55        self.jacobian += other.jacobian;
56        self.newton += other.newton;
57        self.decompositions += other.decompositions;
58        self.solves += other.solves;
59    }
60}
61
62/// Number of Steps
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
64pub struct Steps {
65    /// Number of accepted steps
66    pub accepted: usize,
67    /// Number of rejected steps
68    pub rejected: usize,
69}
70
71impl Steps {
72    /// Create a new Steps struct
73    pub fn new() -> Self {
74        Self {
75            accepted: 0,
76            rejected: 0,
77        }
78    }
79
80    /// Get the total number of steps (accepted + rejected)
81    pub fn total(&self) -> usize {
82        self.accepted + self.rejected
83    }
84}
85
86impl Add for Steps {
87    type Output = Self;
88
89    fn add(self, other: Self) -> Self {
90        Self {
91            accepted: self.accepted + other.accepted,
92            rejected: self.rejected + other.rejected,
93        }
94    }
95}
96
97impl AddAssign for Steps {
98    fn add_assign(&mut self, other: Self) {
99        self.accepted += other.accepted;
100        self.rejected += other.rejected;
101    }
102}
103
104/// Timer for tracking solution time
105#[derive(Debug, Clone)]
106pub enum Timer<T: Real> {
107    Off,
108    Running(Instant),
109    Completed(T),
110}
111
112impl<T: Real> Timer<T> {
113    /// Starts the timer
114    pub fn start(&mut self) {
115        *self = Timer::Running(Instant::now());
116    }
117
118    /// Returns the elapsed time in seconds
119    pub fn elapsed(&self) -> T {
120        match self {
121            Timer::Off => T::zero(),
122            Timer::Running(start_time) => T::from_f64(start_time.elapsed().as_secs_f64()).unwrap(),
123            Timer::Completed(t) => *t,
124        }
125    }
126
127    /// Complete the running timer and convert it to a completed state
128    pub fn complete(&mut self) {
129        match self {
130            Timer::Off => {}
131            Timer::Running(start_time) => {
132                *self = Timer::Completed(T::from_f64(start_time.elapsed().as_secs_f64()).unwrap());
133            }
134            Timer::Completed(_) => {}
135        }
136    }
137}