1use nalgebra::{DMatrix, DVector};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
23pub enum HessianUpdateMethod {
24 #[default]
26 Bfgs,
27 Bofill,
29 BfgsPure,
31 Powell,
33 BfgsPowellMix,
35}
36
37
38const SMALL: f64 = 1e-14;
40const RMIN2: f64 = 1e-12;
41
42pub fn update_hessian_with_method(
58 hessian: &DMatrix<f64>,
59 delta_x: &DVector<f64>,
60 delta_g: &DVector<f64>,
61 method: HessianUpdateMethod,
62) -> DMatrix<f64> {
63 match method {
64 HessianUpdateMethod::Bfgs => update_hessian_bfgs(hessian, delta_x, delta_g),
65 HessianUpdateMethod::Bofill => update_hessian_bofill(hessian, delta_x, delta_g),
66 HessianUpdateMethod::BfgsPure => update_hessian_bfgs_pure(hessian, delta_x, delta_g),
67 HessianUpdateMethod::Powell => update_hessian_powell(hessian, delta_x, delta_g),
68 HessianUpdateMethod::BfgsPowellMix => update_hessian_bfgs_powell_mix(hessian, delta_x, delta_g),
69 }
70}
71
72pub fn update_hessian_bfgs(
84 hessian: &DMatrix<f64>,
85 delta_x: &DVector<f64>,
86 delta_g: &DVector<f64>,
87) -> DMatrix<f64> {
88 let mut h_new = hessian.clone();
89
90 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
92 return h_new;
93 }
94
95 let dx_dg = delta_x.dot(delta_g); if dx_dg <= SMALL {
99 return h_new;
100 }
101
102 let h_dx = hessian * delta_x;
104 let dx_h_dx = delta_x.dot(&h_dx); if dx_h_dx.abs() <= SMALL {
107 return h_new;
108 }
109
110 let n = hessian.nrows();
112 for i in 0..n {
113 for j in 0..=i {
114 let update = delta_g[i] * delta_g[j] / dx_dg
115 - h_dx[i] * h_dx[j] / dx_h_dx;
116 h_new[(i, j)] += update;
117 if i != j {
118 h_new[(j, i)] += update;
119 }
120 }
121 }
122
123 h_new
124}
125
126pub fn update_hessian_bfgs_pure(
131 hessian: &DMatrix<f64>,
132 delta_x: &DVector<f64>,
133 delta_g: &DVector<f64>,
134) -> DMatrix<f64> {
135 let mut h_new = hessian.clone();
136
137 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
138 return h_new;
139 }
140
141 let dx_dg = delta_x.dot(delta_g);
142
143 if dx_dg.abs() <= SMALL {
144 return h_new;
145 }
146
147 let h_dx = hessian * delta_x;
148 let dx_h_dx = delta_x.dot(&h_dx);
149
150 if dx_h_dx.abs() <= SMALL {
151 return h_new;
152 }
153
154 let n = hessian.nrows();
155 for i in 0..n {
156 for j in 0..=i {
157 let update = delta_g[i] * delta_g[j] / dx_dg
158 - h_dx[i] * h_dx[j] / dx_h_dx;
159 h_new[(i, j)] += update;
160 if i != j {
161 h_new[(j, i)] += update;
162 }
163 }
164 }
165
166 h_new
167}
168
169pub fn update_hessian_powell(
179 hessian: &DMatrix<f64>,
180 delta_x: &DVector<f64>,
181 delta_g: &DVector<f64>,
182) -> DMatrix<f64> {
183 let mut h_new = hessian.clone();
184
185 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
186 return h_new;
187 }
188
189 let dx_norm_sq = delta_x.norm_squared();
190 if dx_norm_sq < RMIN2 {
191 return h_new;
192 }
193
194 let h_dx = hessian * delta_x;
196 let diff = delta_g - &h_dx;
197
198 let denom = diff.dot(delta_x);
200
201 if denom.abs() <= SMALL {
202 return h_new;
203 }
204
205 let n = hessian.nrows();
207 for i in 0..n {
208 for j in 0..=i {
209 let update = diff[i] * diff[j] / denom;
210 h_new[(i, j)] += update;
211 if i != j {
212 h_new[(j, i)] += update;
213 }
214 }
215 }
216
217 h_new
218}
219
220pub fn update_hessian_bofill(
235 hessian: &DMatrix<f64>,
236 delta_x: &DVector<f64>,
237 delta_g: &DVector<f64>,
238) -> DMatrix<f64> {
239 let mut h_new = hessian.clone();
240
241 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
242 return h_new;
243 }
244
245 let dx_norm_sq = delta_x.norm_squared();
246 if dx_norm_sq < RMIN2 {
247 return h_new;
248 }
249
250 let h_dx = hessian * delta_x;
252 let diff = delta_g - &h_dx; let diff_norm_sq = diff.norm_squared();
254
255 if diff_norm_sq < SMALL {
256 return h_new;
257 }
258
259 let dx_dg = delta_x.dot(delta_g);
260 let dx_h_dx = delta_x.dot(&h_dx);
261
262 let r_num = dx_dg - dx_h_dx;
264 let r_denom = dx_norm_sq * diff_norm_sq;
265
266 let phi = if r_num.abs() > SMALL && r_denom.abs() > SMALL {
267 1.0 - (r_num * r_num) / r_denom
268 } else {
269 1.0 };
271
272 let n = hessian.nrows();
274 for i in 0..n {
275 for j in 0..=i {
276 let powell = (diff[i] * delta_x[j] + delta_x[i] * diff[j]) / dx_norm_sq
279 - r_num * delta_x[i] * delta_x[j] / (dx_norm_sq * dx_norm_sq);
280
281 let ms = if r_num.abs() > SMALL {
283 diff[i] * diff[j] / r_num
284 } else {
285 0.0
286 };
287
288 let update = (1.0 - phi) * ms + phi * powell;
289 h_new[(i, j)] += update;
290 if i != j {
291 h_new[(j, i)] += update;
292 }
293 }
294 }
295
296 h_new
297}
298
299pub fn update_hessian_bfgs_powell_mix(
304 hessian: &DMatrix<f64>,
305 delta_x: &DVector<f64>,
306 delta_g: &DVector<f64>,
307) -> DMatrix<f64> {
308 let mut h_new = hessian.clone();
309
310 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
311 return h_new;
312 }
313
314 let dx_norm_sq = delta_x.norm_squared();
315 if dx_norm_sq < RMIN2 {
316 return h_new;
317 }
318
319 let h_dx = hessian * delta_x;
320 let diff = delta_g - &h_dx;
321 let diff_norm_sq = diff.norm_squared();
322
323 if diff_norm_sq < SMALL {
324 return h_new;
325 }
326
327 let dx_dg = delta_x.dot(delta_g);
328 let dx_h_dx = delta_x.dot(&h_dx);
329
330 let r_num = dx_dg - dx_h_dx;
332 let r_denom = dx_norm_sq * diff_norm_sq;
333
334 let phi = if r_num.abs() > SMALL && r_denom.abs() > SMALL {
335 (1.0 - (r_num * r_num) / r_denom).clamp(0.0, 1.0)
336 } else {
337 0.5
338 };
339
340 let bfgs_valid = dx_dg.abs() > SMALL && dx_h_dx.abs() > SMALL;
342
343 let powell_denom = diff.dot(delta_x);
345 let powell_valid = powell_denom.abs() > SMALL;
346
347 let n = hessian.nrows();
348 for i in 0..n {
349 for j in 0..=i {
350 let mut update = 0.0;
351
352 if bfgs_valid {
354 let bfgs = delta_g[i] * delta_g[j] / dx_dg
355 - h_dx[i] * h_dx[j] / dx_h_dx;
356 update += (1.0 - phi) * bfgs;
357 }
358
359 if powell_valid {
361 let powell = diff[i] * diff[j] / powell_denom;
362 update += phi * powell;
363 }
364
365 h_new[(i, j)] += update;
366 if i != j {
367 h_new[(j, i)] += update;
368 }
369 }
370 }
371
372 h_new
373}
374
375pub fn update_inverse_hessian_bfgs(
391 h_inv: &DMatrix<f64>,
392 delta_x: &DVector<f64>,
393 delta_g: &DVector<f64>,
394) -> DMatrix<f64> {
395 if !delta_x.iter().all(|v| v.is_finite()) || !delta_g.iter().all(|v| v.is_finite()) {
396 return h_inv.clone();
397 }
398 if !h_inv.iter().all(|v| v.is_finite()) {
399 return h_inv.clone();
400 }
401
402 let mut h_inv_new = h_inv.clone();
403
404 let h_del_g = h_inv * delta_g;
406
407 let fac_denom = delta_g.dot(delta_x); let fae = delta_g.dot(&h_del_g); if fac_denom.abs() < SMALL || fae.abs() < SMALL {
411 return h_inv_new;
412 }
413
414 let fac = 1.0 / fac_denom;
415 let fad = 1.0 / fae;
416
417 let w = delta_x * fac - &h_del_g * fad;
419
420 let term1 = (delta_x * delta_x.transpose()) * fac;
422 let term2 = (&h_del_g * h_del_g.transpose()) * fad;
423 let term3 = (&w * w.transpose()) * fae;
424
425 h_inv_new += term1 - term2 + term3;
426
427 h_inv_new = 0.5 * (&h_inv_new + h_inv_new.transpose());
429
430 for v in h_inv_new.iter_mut() {
432 if !v.is_finite() {
433 *v = 0.0;
434 }
435 }
436
437 h_inv_new
438}