use crate::core::math::{MatrixIdentity, MatrixIndex, Scalar, VectorLen};
pub trait InitialInverseHessian<V, M, F: Scalar = f64> {
fn initialize(&self, param: &V, gradient: &V) -> M;
fn rescale_on_first_update(&self) -> bool {
false
}
}
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct IdentityInverseHessian;
impl<V: VectorLen, M: MatrixIdentity, F: Scalar> InitialInverseHessian<V, M, F>
for IdentityInverseHessian
{
fn initialize(&self, param: &V, _: &V) -> M {
M::identity(param.vec_len())
}
fn rescale_on_first_update(&self) -> bool {
true
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FixedInverseHessian<M>(pub(super) M);
impl<V: VectorLen, M: Clone + MatrixIndex<F>, F: Scalar>
InitialInverseHessian<V, M, F> for FixedInverseHessian<M>
{
fn initialize(&self, param: &V, _: &V) -> M {
validate::<M, F>(&self.0, param.vec_len());
self.0.clone()
}
}
impl<V: VectorLen, M: MatrixIndex<F>, F: Scalar, Init: Fn(&V, &V) -> M>
InitialInverseHessian<V, M, F> for Init
{
fn initialize(&self, param: &V, gradient: &V) -> M {
let matrix = self(param, gradient);
validate::<M, F>(&matrix, param.vec_len());
matrix
}
}
fn validate<M: MatrixIndex<F>, F: Scalar>(matrix: &M, n: usize) {
assert!(
matrix.matrix_rows() == n && matrix.matrix_cols() == n,
"BFGS initial inverse Hessian must match the parameter dimension"
);
for i in 0..n {
for j in 0..=i {
let a = matrix.matrix_entry(i, j);
let b = matrix.matrix_entry(j, i);
assert!(
a.is_finite() && b.is_finite() && a == b,
"BFGS initial inverse Hessian must be finite and symmetric"
);
}
}
let mut l = vec![F::zero(); n * n];
for i in 0..n {
for j in 0..=i {
let mut value = matrix.matrix_entry(i, j);
for k in 0..j {
value = value - l[i * n + k] * l[j * n + k];
}
assert!(
value.is_finite() && (i != j || value > F::zero()),
"BFGS initial inverse Hessian must be positive definite"
);
l[i * n + j] = if i == j {
value.sqrt()
} else {
value / l[j * n + j]
};
assert!(
l[i * n + j].is_finite(),
"BFGS initial inverse Hessian factorization must be finite"
);
}
}
}