rust_poly/poly/roots/single_root/
newton.rs1use super::{line_search_accelerate, line_search_decelerate, LazyDerivatives};
2use crate::{
3 num::{Complex, Zero},
4 poly::roots,
5 roots::initial_guess::initial_guess_smallest,
6 scalar::SafeConstants,
7 util::{
8 self,
9 complex::c_from_f64,
10 doc_macros::{errors_no_converge, panic_t_from_f64},
11 },
12 Poly, RealScalar,
13};
14use num::One;
15
16#[doc = errors_no_converge!()]
20#[doc = panic_t_from_f64!()]
23#[allow(clippy::similar_names)]
24#[allow(clippy::items_after_statements)]
25#[allow(clippy::type_complexity)]
26pub fn newton<T: RealScalar>(
27 poly: &Poly<T>,
28 epsilon: T,
29 max_iter: Option<usize>,
30 initial_guess: Option<Complex<T>>,
32) -> std::result::Result<(Vec<Complex<T>>, u128), roots::Error<Vec<Complex<T>>>> {
33 log::trace!("starting with arguments: {{poly: \"{poly}\", epsilon: {epsilon}, max_iter: \"{max_iter:?}\", initial_guess: \"{initial_guess:?}\"}}");
34
35 let mut eval_counter = 0;
36 let mut guess = initial_guess.unwrap_or_else(|| initial_guess_smallest(poly));
37 let mut guess_old = guess.clone();
38 let mut guess_old_old = guess.clone();
39 let mut guess_delta = Complex::one();
40
41 const CYCLE_COUNT_THRESHOLD: usize = 17;
44 let mut cycle_counter = 0;
45 let mut best_guess = guess.clone();
46 let mut best_px_norm = guess.norm_sqr();
47
48 let mut diffs = LazyDerivatives::new(poly);
49
50 for i in util::iterator::saturating_counter() {
52 let px = poly.eval(guess.clone());
53 eval_counter += 1;
54
55 log::trace!("best_guess: \"{best_guess:?}\"");
56
57 if px.norm_sqr() <= epsilon {
59 log::trace!("stopping because target precision reached");
60 return Ok((vec![best_guess], eval_counter));
61 }
62
63 if i > 3
65 && super::stopping_criterion_garwick(
66 guess.clone(),
67 guess_old.clone(),
68 guess_old_old.clone(),
69 )
70 {
71 log::trace!("stopping because garwick heuristic says no improvement is possible");
72 return Ok((vec![best_guess], eval_counter));
73 }
74
75 if max_iter.is_some_and(|max| i >= max) {
77 log::trace!("did not converge {{best_guess: {best_guess}, poly: {poly}}}");
78 return Err(roots::Error::NoConverge(vec![best_guess]));
79 }
80
81 if px.norm_sqr() >= best_px_norm {
83 cycle_counter += 1;
84 if cycle_counter > CYCLE_COUNT_THRESHOLD {
85 const ROTATION_RADIANS: f64 = 0.925_024_5;
87 const SCALE: f64 = 5.0;
88
89 cycle_counter = 0;
90 log::trace!("cycle detected, backing off {{current_guess: {guess}, best_guess: {best_guess}}}");
91
92 let backoff = c_from_f64(Complex::from_polar(SCALE, ROTATION_RADIANS));
95 guess = best_guess.clone() - guess_delta.clone() * backoff;
97 }
98 } else {
99 cycle_counter = 0;
100 best_guess = guess.clone();
101 best_px_norm = px.norm_sqr();
102 }
103
104 let pdx = diffs.get_nth_derivative(1).eval(guess.clone());
105 eval_counter += 1;
106
107 guess_delta = compute_delta(px.clone(), pdx.clone(), guess_delta);
108
109 let mut guess_new = guess.clone() - guess_delta.clone();
111 let px_new = poly.eval(guess_new.clone());
112 let pdx_new = diffs.get_nth_derivative(1).eval(guess_new.clone());
113
114 if !check_will_converge(
115 guess_new.clone(),
116 guess.clone(),
117 px_new.clone(),
118 pdx_new,
119 pdx,
120 ) {
121 if px_new.norm_sqr() > px.norm_sqr() {
126 let res = line_search_decelerate(poly, guess.clone(), guess_delta.clone());
127 guess_new = res.0;
128 eval_counter += res.1;
129 } else {
130 let res = line_search_accelerate(poly, guess.clone(), guess_delta.clone());
131 guess_new = res.0;
132 eval_counter += res.1;
133 }
134 }
135
136 guess_old_old = guess_old;
137 guess_old = guess;
138 guess = guess_new;
139 }
140 unreachable!()
141}
142
143fn compute_delta<T: RealScalar>(
144 px: Complex<T>,
145 pdx: Complex<T>,
146 delta_old: Complex<T>,
147) -> Complex<T> {
148 const EXPLODE_THRESHOLD: f64 = 5.0;
149
150 if pdx.is_zero() {
151 const ROTATION_RADIANS: f64 = 0.925_024_5;
153 const SCALE: f64 = 5.0;
154 let backoff = c_from_f64(Complex::from_polar(SCALE, ROTATION_RADIANS));
157 return delta_old * backoff;
158 }
159
160 let delta = px / pdx;
161
162 if delta.norm_sqr() > delta_old.norm_sqr() * T::from_f64(EXPLODE_THRESHOLD).expect("overflow") {
163 const ROTATION_RADIANS: f64 = 0.925_024_5;
165 const SCALE: f64 = 5.0;
166 let backoff = c_from_f64(Complex::from_polar(SCALE, ROTATION_RADIANS))
170 .scale(delta_old.norm_sqr() / delta.norm_sqr());
171
172 return delta * backoff;
173 }
174
175 delta
176}
177
178fn check_will_converge<T: RealScalar>(
185 guess: Complex<T>,
186 guess_old: Complex<T>,
187 px: Complex<T>,
188 pdx: Complex<T>,
189 pdx_old: Complex<T>,
190) -> bool {
191 !(px.clone() * pdx.clone()).is_small()
192 && ((px / pdx.clone()).norm_sqr()
193 * T::from_u8(2).expect("overflow")
194 * ((pdx_old - pdx.clone()) / (guess_old - guess)).norm_sqr()
195 <= pdx.norm_sqr())
196}
197
198#[cfg(test)]
199mod test {
200 use crate::{num::One, roots::newton_deflate, util::__testing::check_roots, Poly64};
201
202 #[test]
203 pub fn degree_0() {
204 let mut p = Poly64::one();
205 let roots = newton_deflate(&mut p, Some(1E-14), Some(100), &[]).unwrap();
206 assert!(roots.is_empty());
207 assert!(p.is_one());
208 }
209
210 #[test]
211 fn degree_1() {
212 let roots_expected = vec![complex!(1.0)];
213 let mut p = crate::Poly::from_roots(&roots_expected);
214 let roots = newton_deflate(&mut p, Some(1E-14), Some(100), &[]).unwrap();
215 assert!(check_roots(roots, roots_expected, 1E-12));
216 }
217
218 #[test]
219 fn degree_2() {
220 let roots_expected = vec![complex!(1.0), complex!(2.0)];
221 let mut p = crate::Poly::from_roots(&roots_expected);
222 let roots = newton_deflate(&mut p, Some(1E-14), Some(100), &[]).unwrap();
223 assert!(check_roots(roots, roots_expected, 1E-12));
224 }
225
226 #[test]
227 fn degree_3() {
228 let roots_expected = vec![complex!(1.0), complex!(2.0), complex!(3.0)];
229 let mut p = crate::Poly::from_roots(&roots_expected);
230 let roots = newton_deflate(&mut p, Some(1E-14), Some(100), &[]).unwrap();
231 assert!(check_roots(roots, roots_expected, 1E-5));
232 }
233
234 #[test]
235 fn degree_3_complex() {
236 let roots_expected = vec![complex!(1.0), complex!(0.0, 1.0), complex!(0.0, -1.0)];
237 let mut p = crate::Poly::from_roots(&roots_expected);
238 let roots = newton_deflate(&mut p, Some(1E-14), Some(100), &[]).unwrap();
239 assert!(check_roots(roots, roots_expected, 1E-4));
240 }
241
242 #[test]
243 fn degree_5_multiplicity_3() {
244 let roots_expected = vec![
245 complex!(1.0),
246 complex!(2.0),
247 complex!(2.0),
248 complex!(2.0),
249 complex!(3.0),
250 ];
251 let mut p = crate::Poly::from_roots(&roots_expected);
252 let roots = newton_deflate(&mut p, Some(1E-14), Some(100), &[]).unwrap();
253 assert!(check_roots(roots.clone(), roots_expected, 0.2), "{roots:?}");
254 }
255
256 #[test]
257 fn degree_5_2_zeros() {
258 let roots_expected = vec![
259 complex!(0.0),
260 complex!(0.0),
261 complex!(1.0),
262 complex!(2.0),
263 complex!(3.0),
264 ];
265 let mut p = crate::Poly::from_roots(&roots_expected);
266 let roots = newton_deflate(&mut p, Some(1E-14), Some(100), &[]).unwrap();
267 assert!(check_roots(roots, roots_expected, 1E-5));
268 }
269}