1use nalgebra::{DMatrix, DVector};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
18pub enum HessianUpdateMethod {
19 #[default]
21 Bfgs,
22 Bofill,
24 BfgsPure,
26 Powell,
28 BfgsPowellMix,
30}
31
32
33const SMALL: f64 = 1e-14;
35const RMIN2: f64 = 1e-12;
36
37pub fn update_hessian_with_method(
53 hessian: &DMatrix<f64>,
54 delta_x: &DVector<f64>,
55 delta_g: &DVector<f64>,
56 method: HessianUpdateMethod,
57) -> DMatrix<f64> {
58 match method {
59 HessianUpdateMethod::Bfgs => update_hessian_bfgs(hessian, delta_x, delta_g),
60 HessianUpdateMethod::Bofill => update_hessian_bofill(hessian, delta_x, delta_g),
61 HessianUpdateMethod::BfgsPure => update_hessian_bfgs_pure(hessian, delta_x, delta_g),
62 HessianUpdateMethod::Powell => update_hessian_powell(hessian, delta_x, delta_g),
63 HessianUpdateMethod::BfgsPowellMix => update_hessian_bfgs_powell_mix(hessian, delta_x, delta_g),
64 }
65}
66
67pub fn update_hessian_bfgs(
79 hessian: &DMatrix<f64>,
80 delta_x: &DVector<f64>,
81 delta_g: &DVector<f64>,
82) -> DMatrix<f64> {
83 let mut h_new = hessian.clone();
84
85 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
87 return h_new;
88 }
89
90 let dx_dg = delta_x.dot(delta_g); if dx_dg <= SMALL {
94 return h_new;
95 }
96
97 let h_dx = hessian * delta_x;
99 let dx_h_dx = delta_x.dot(&h_dx); if dx_h_dx.abs() <= SMALL {
102 return h_new;
103 }
104
105 let n = hessian.nrows();
107 for i in 0..n {
108 for j in 0..=i {
109 let update = delta_g[i] * delta_g[j] / dx_dg
110 - h_dx[i] * h_dx[j] / dx_h_dx;
111 h_new[(i, j)] += update;
112 if i != j {
113 h_new[(j, i)] += update;
114 }
115 }
116 }
117
118 h_new
119}
120
121pub fn update_hessian_bfgs_pure(
126 hessian: &DMatrix<f64>,
127 delta_x: &DVector<f64>,
128 delta_g: &DVector<f64>,
129) -> DMatrix<f64> {
130 let mut h_new = hessian.clone();
131
132 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
133 return h_new;
134 }
135
136 let dx_dg = delta_x.dot(delta_g);
137
138 if dx_dg.abs() <= SMALL {
139 return h_new;
140 }
141
142 let h_dx = hessian * delta_x;
143 let dx_h_dx = delta_x.dot(&h_dx);
144
145 if dx_h_dx.abs() <= SMALL {
146 return h_new;
147 }
148
149 let n = hessian.nrows();
150 for i in 0..n {
151 for j in 0..=i {
152 let update = delta_g[i] * delta_g[j] / dx_dg
153 - h_dx[i] * h_dx[j] / dx_h_dx;
154 h_new[(i, j)] += update;
155 if i != j {
156 h_new[(j, i)] += update;
157 }
158 }
159 }
160
161 h_new
162}
163
164pub fn update_hessian_powell(
174 hessian: &DMatrix<f64>,
175 delta_x: &DVector<f64>,
176 delta_g: &DVector<f64>,
177) -> DMatrix<f64> {
178 let mut h_new = hessian.clone();
179
180 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
181 return h_new;
182 }
183
184 let dx_norm_sq = delta_x.norm_squared();
185 if dx_norm_sq < RMIN2 {
186 return h_new;
187 }
188
189 let h_dx = hessian * delta_x;
191 let diff = delta_g - &h_dx;
192
193 let denom = diff.dot(delta_x);
195
196 if denom.abs() <= SMALL {
197 return h_new;
198 }
199
200 let n = hessian.nrows();
202 for i in 0..n {
203 for j in 0..=i {
204 let update = diff[i] * diff[j] / denom;
205 h_new[(i, j)] += update;
206 if i != j {
207 h_new[(j, i)] += update;
208 }
209 }
210 }
211
212 h_new
213}
214
215pub fn update_hessian_bofill(
230 hessian: &DMatrix<f64>,
231 delta_x: &DVector<f64>,
232 delta_g: &DVector<f64>,
233) -> DMatrix<f64> {
234 let mut h_new = hessian.clone();
235
236 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
237 return h_new;
238 }
239
240 let dx_norm_sq = delta_x.norm_squared();
241 if dx_norm_sq < RMIN2 {
242 return h_new;
243 }
244
245 let h_dx = hessian * delta_x;
247 let diff = delta_g - &h_dx; let diff_norm_sq = diff.norm_squared();
249
250 if diff_norm_sq < SMALL {
251 return h_new;
252 }
253
254 let dx_dg = delta_x.dot(delta_g);
255 let dx_h_dx = delta_x.dot(&h_dx);
256
257 let r_num = dx_dg - dx_h_dx;
259 let r_denom = dx_norm_sq * diff_norm_sq;
260
261 let phi = if r_num.abs() > SMALL && r_denom.abs() > SMALL {
262 1.0 - (r_num * r_num) / r_denom
263 } else {
264 1.0 };
266
267 let n = hessian.nrows();
269 for i in 0..n {
270 for j in 0..=i {
271 let powell = (diff[i] * delta_x[j] + delta_x[i] * diff[j]) / dx_norm_sq
274 - r_num * delta_x[i] * delta_x[j] / (dx_norm_sq * dx_norm_sq);
275
276 let ms = if r_num.abs() > SMALL {
278 diff[i] * diff[j] / r_num
279 } else {
280 0.0
281 };
282
283 let update = (1.0 - phi) * ms + phi * powell;
284 h_new[(i, j)] += update;
285 if i != j {
286 h_new[(j, i)] += update;
287 }
288 }
289 }
290
291 h_new
292}
293
294pub fn update_hessian_bfgs_powell_mix(
299 hessian: &DMatrix<f64>,
300 delta_x: &DVector<f64>,
301 delta_g: &DVector<f64>,
302) -> DMatrix<f64> {
303 let mut h_new = hessian.clone();
304
305 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
306 return h_new;
307 }
308
309 let dx_norm_sq = delta_x.norm_squared();
310 if dx_norm_sq < RMIN2 {
311 return h_new;
312 }
313
314 let h_dx = hessian * delta_x;
315 let diff = delta_g - &h_dx;
316 let diff_norm_sq = diff.norm_squared();
317
318 if diff_norm_sq < SMALL {
319 return h_new;
320 }
321
322 let dx_dg = delta_x.dot(delta_g);
323 let dx_h_dx = delta_x.dot(&h_dx);
324
325 let r_num = dx_dg - dx_h_dx;
327 let r_denom = dx_norm_sq * diff_norm_sq;
328
329 let phi = if r_num.abs() > SMALL && r_denom.abs() > SMALL {
330 (1.0 - (r_num * r_num) / r_denom).clamp(0.0, 1.0)
331 } else {
332 0.5
333 };
334
335 let bfgs_valid = dx_dg.abs() > SMALL && dx_h_dx.abs() > SMALL;
337
338 let powell_denom = diff.dot(delta_x);
340 let powell_valid = powell_denom.abs() > SMALL;
341
342 let n = hessian.nrows();
343 for i in 0..n {
344 for j in 0..=i {
345 let mut update = 0.0;
346
347 if bfgs_valid {
349 let bfgs = delta_g[i] * delta_g[j] / dx_dg
350 - h_dx[i] * h_dx[j] / dx_h_dx;
351 update += (1.0 - phi) * bfgs;
352 }
353
354 if powell_valid {
356 let powell = diff[i] * diff[j] / powell_denom;
357 update += phi * powell;
358 }
359
360 h_new[(i, j)] += update;
361 if i != j {
362 h_new[(j, i)] += update;
363 }
364 }
365 }
366
367 h_new
368}
369
370pub fn update_inverse_hessian_bfgs(
386 h_inv: &DMatrix<f64>,
387 delta_x: &DVector<f64>,
388 delta_g: &DVector<f64>,
389) -> DMatrix<f64> {
390 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
391 return h_inv.clone();
392 }
393 if !h_inv.iter().all(|v| v.is_finite()) {
394 return h_inv.clone();
395 }
396
397 let mut h_inv_new = h_inv.clone();
398
399 let h_del_g = h_inv * delta_g;
401
402 let fac_denom = delta_g.dot(delta_x); let fae = delta_g.dot(&h_del_g); if fac_denom.abs() < SMALL || fae.abs() < SMALL {
406 return h_inv_new;
407 }
408
409 let fac = 1.0 / fac_denom;
410 let fad = 1.0 / fae;
411
412 let w = delta_x * fac - &h_del_g * fad;
414
415 let term1 = (delta_x * delta_x.transpose()) * fac;
417 let term2 = (&h_del_g * h_del_g.transpose()) * fad;
418 let term3 = (&w * w.transpose()) * fae;
419
420 h_inv_new += term1 - term2 + term3;
421
422 h_inv_new = 0.5 * (&h_inv_new + h_inv_new.transpose());
424
425 for v in h_inv_new.iter_mut() {
427 if !v.is_finite() {
428 *v = 0.0;
429 }
430 }
431
432 h_inv_new
433}