gam_problem/
monotone_root_error.rs1#[derive(Clone, Debug)]
4pub enum MonotoneRootError {
5 EvalFailed {
7 label: String,
8 a: f64,
9 source: String,
10 },
11 NonFiniteEval {
13 label: String,
14 a: f64,
15 f: f64,
16 fp: f64,
17 fpp: f64,
18 },
19 DegenerateDerivative { label: String, a: f64, fp: f64 },
22 BracketingExhausted {
24 label: String,
25 iters: usize,
26 a_lo: f64,
27 a_hi: f64,
28 },
29 RefinementDidNotConverge {
31 label: String,
32 iters: usize,
33 last_residual: f64,
34 },
35}
36
37impl MonotoneRootError {
41 pub fn exact_root_degenerate(label: &str, a: f64) -> Self {
42 MonotoneRootError::RefinementDidNotConverge {
44 label: format!("__EXACT_ROOT__{label}"),
45 iters: usize::MAX,
46 last_residual: a,
47 }
48 }
49
50 pub fn converged_root_degenerate(label: &str, a: f64) -> Self {
51 MonotoneRootError::RefinementDidNotConverge {
52 label: format!("__CONVERGED__{label}"),
53 iters: 0,
54 last_residual: a,
55 }
56 }
57
58 pub fn analytic_bracket_invalid(label: &str, lo: f64, hi: f64) -> Self {
59 MonotoneRootError::BracketingExhausted {
60 label: format!("__ANALYTIC_INVALID__{label}"),
61 iters: 0,
62 a_lo: lo,
63 a_hi: hi,
64 }
65 }
66
67 pub fn analytic_bracket_no_straddle(label: &str, f_lo: f64, f_hi: f64) -> Self {
68 MonotoneRootError::BracketingExhausted {
69 label: format!("__ANALYTIC_NOSTRADDLE__{label}"),
70 iters: 0,
71 a_lo: f_lo,
72 a_hi: f_hi,
73 }
74 }
75
76 pub fn search_exhausted(label: &str, step_sign: f64, a_init: f64) -> Self {
77 MonotoneRootError::BracketingExhausted {
78 label: format!("__SEARCH__{label}"),
79 iters: 0,
80 a_lo: a_init,
81 a_hi: step_sign,
82 }
83 }
84}
85
86impl std::fmt::Display for MonotoneRootError {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 match self {
89 MonotoneRootError::EvalFailed { source, .. } => f.write_str(source),
90 MonotoneRootError::NonFiniteEval { label, a, .. } => {
91 write!(f, "{label}: non-finite evaluation at a={a:.6}")
92 }
93 MonotoneRootError::DegenerateDerivative { label, a, .. } => {
94 write!(
95 f,
96 "{label}: initial derivative is zero or non-finite at a={a:.6}"
97 )
98 }
99 MonotoneRootError::BracketingExhausted {
100 label, a_lo, a_hi, ..
101 } => {
102 if let Some(real_label) = label.strip_prefix("__ANALYTIC_INVALID__") {
103 write!(
104 f,
105 "{real_label}: invalid analytic bracket [{a_lo:.6}, {a_hi:.6}]"
106 )
107 } else if let Some(real_label) = label.strip_prefix("__ANALYTIC_NOSTRADDLE__") {
108 let f_lo = a_lo;
109 let f_hi = a_hi;
110 write!(
111 f,
112 "{real_label}: analytic bracket does not straddle root (f_lo={f_lo:.3e}, f_hi={f_hi:.3e})"
113 )
114 } else if let Some(real_label) = label.strip_prefix("__SEARCH__") {
115 let step_sign = *a_hi;
116 let a_init = *a_lo;
117 write!(
118 f,
119 "{real_label}: failed to bracket root (searched {step_sign:+.0} from a={a_init:.6})"
120 )
121 } else {
122 write!(
123 f,
124 "{label}: failed to bracket root (a_lo={a_lo:.6}, a_hi={a_hi:.6})"
125 )
126 }
127 }
128 MonotoneRootError::RefinementDidNotConverge {
129 label,
130 last_residual,
131 ..
132 } => {
133 if let Some(real_label) = label.strip_prefix("__EXACT_ROOT__") {
134 let a = last_residual;
135 write!(
136 f,
137 "{real_label}: zero or non-finite derivative at exact root a={a:.6}"
138 )
139 } else if let Some(real_label) = label.strip_prefix("__CONVERGED__") {
140 let a = last_residual;
141 write!(
142 f,
143 "{real_label}: zero or non-finite derivative at converged root a={a:.6}"
144 )
145 } else {
146 write!(
147 f,
148 "{label}: refinement did not converge (last residual={last_residual:.3e})"
149 )
150 }
151 }
152 }
153 }
154}
155
156impl std::error::Error for MonotoneRootError {}