scirs2_optimize/
roots_anderson.rs1use crate::error::OptimizeResult;
2use crate::result::OptimizeResults;
3use crate::roots::Options;
4use scirs2_core::ndarray::{Array1, Array2, ArrayBase, Data, Ix1};
5
6#[allow(dead_code)]
12pub fn root_anderson<F, J, S>(
13 func: F,
14 x0: &ArrayBase<S, Ix1>,
15 _jacobian_fn: Option<J>,
16 options: &Options,
17) -> OptimizeResult<OptimizeResults<f64>>
18where
19 F: Fn(&[f64]) -> Array1<f64>,
20 J: Fn(&[f64]) -> Array2<f64>,
21 S: Data<Elem = f64>,
22{
23 let xtol = options.xtol.unwrap_or(1e-8);
25 let ftol = options.ftol.unwrap_or(1e-8);
26 let maxfev = options.maxfev.unwrap_or(100 * x0.len());
27
28 let m = options.m_anderson.unwrap_or(5).min(x0.len()); let beta = options.beta_anderson.unwrap_or(0.5); let n = x0.len();
34 let mut x = x0.to_owned();
35 let mut f = func(x.as_slice().unwrap());
36 let mut nfev = 1;
37
38 let mut residuals: Vec<Array1<f64>> = Vec::with_capacity(m + 1);
40 let mut steps: Vec<Array1<f64>> = Vec::with_capacity(m + 1);
41
42 let mut iter = 0;
44 let mut converged = false;
45
46 while iter < maxfev {
47 let f_norm = f.iter().map(|&fi| fi.powi(2)).sum::<f64>().sqrt();
49 if f_norm < ftol {
50 converged = true;
51 break;
52 }
53
54 let mut x_simple = x.clone();
56 for i in 0..n {
57 x_simple[i] = x[i] - f[i];
58 }
59
60 let x_new = if iter >= 1 {
62 residuals.push(f.clone());
64
65 if residuals.len() > m {
67 residuals.remove(0);
68 steps.remove(0);
69 }
70
71 let mk = residuals.len() - 1;
73
74 if mk >= 1 {
75 let mut df = Array2::zeros((n, mk));
77 for j in 0..mk {
78 for i in 0..n {
79 df[[i, j]] = residuals[j + 1][i] - residuals[0][i];
80 }
81 }
82
83 let alpha = match solve_least_squares(&df, &(-&residuals[0])) {
86 Some(a) => a,
87 None => {
88 let mut x_next = x.clone();
90 for i in 0..n {
91 x_next[i] = (1.0 - beta) * x[i] + beta * x_simple[i];
92 }
93 x_next
94 }
95 };
96
97 let mut x_next = x.clone();
99
100 for i in 0..n {
102 x_next[i] = x_simple[i] * (1.0 - alpha.iter().sum::<f64>());
103 }
104
105 for j in 0..mk {
107 for i in 0..n {
108 x_next[i] += alpha[j] * steps[j][i];
109 }
110 }
111
112 for i in 0..n {
114 x_next[i] = (1.0 - beta) * x[i] + beta * x_next[i];
115 }
116
117 x_next
118 } else {
119 let mut x_next = x.clone();
121 for i in 0..n {
122 x_next[i] = (1.0 - beta) * x[i] + beta * x_simple[i];
123 }
124 x_next
125 }
126 } else {
127 let mut x_next = x.clone();
129 for i in 0..n {
130 x_next[i] = (1.0 - beta) * x[i] + beta * x_simple[i];
131 }
132 x_next
133 };
134
135 steps.push(x_simple);
137
138 let f_new = func(x_new.as_slice().unwrap());
140 nfev += 1;
141
142 let step_norm = (0..n)
144 .map(|i| (x_new[i] - x[i]).powi(2))
145 .sum::<f64>()
146 .sqrt();
147 let x_norm = (0..n).map(|i| x[i].powi(2)).sum::<f64>().sqrt();
148
149 if step_norm < xtol * (1.0 + x_norm) {
150 converged = true;
151 x = x_new;
152 f = f_new;
153 break;
154 }
155
156 x = x_new;
158 f = f_new;
159
160 iter += 1;
161 }
162
163 let mut result = OptimizeResults::default();
165 result.x = x;
166 result.fun = f.iter().map(|&fi| fi.powi(2)).sum::<f64>();
167 result.nfev = nfev;
168 result.nit = iter;
169 result.success = converged;
170
171 if converged {
172 result.message = "Root finding converged successfully".to_string();
173 } else {
174 result.message = "Maximum number of function evaluations reached".to_string();
175 }
176
177 Ok(result)
178}
179
180#[allow(dead_code)]
182pub fn solve_least_squares(a: &Array2<f64>, b: &Array1<f64>) -> Option<Array1<f64>> {
183 let (m, n) = (a.nrows(), a.ncols());
184 if m < n {
185 return None; }
187
188 let mut q: Array2<f64> = Array2::eye(m);
190 let mut r = a.clone();
191
192 for k in 0..n {
193 let mut x = Array1::zeros(m - k);
195 for i in k..m {
196 x[i - k] = r[[i, k]];
197 }
198
199 let x_norm = x.iter().map(|&xi| xi.powi(2)).sum::<f64>().sqrt();
200 if x_norm < 1e-10 {
201 continue; }
203
204 let mut v = x.clone();
206 v[0] += if x[0] >= 0.0 { x_norm } else { -x_norm };
207
208 let v_norm = v.iter().map(|&vi| vi.powi(2)).sum::<f64>().sqrt();
209 if v_norm < 1e-10 {
210 continue;
211 }
212
213 for i in 0..v.len() {
215 v[i] /= v_norm;
216 }
217
218 for j in k..n {
220 let mut dot_product = 0.0;
221 for i in 0..v.len() {
222 dot_product += v[i] * r[[i + k, j]];
223 }
224
225 let factor = 2.0 * dot_product;
226 for i in 0..v.len() {
227 r[[i + k, j]] -= factor * v[i];
228 }
229 }
230
231 for j in 0..m {
233 let mut dot_product = 0.0;
234 for i in 0..v.len() {
235 dot_product += v[i] * q[[j, i + k]];
236 }
237
238 let factor = 2.0 * dot_product;
239 for i in 0..v.len() {
240 q[[j, i + k]] -= factor * v[i];
241 }
242 }
243 }
244
245 let mut y = Array1::zeros(m);
250 for i in 0..m {
251 for j in 0..m {
252 y[i] += q[[i, j]] * b[j];
253 }
254 }
255
256 let mut x = Array1::zeros(n);
258
259 for i in (0..n).rev() {
260 let mut sum: f64 = y[i];
261 for j in (i + 1)..n {
262 sum -= r[[i, j]] * x[j];
263 }
264
265 if r[[i, i]].abs() < 1e-10 {
266 return None; }
268
269 x[i] = sum / r[[i, i]];
270 }
271
272 Some(x)
273}