differential_equations/
stats.rs

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