mgh 0.1.12

A Collection of Moré-Garbow-Hilstrom https://dl.acm.org/doi/pdf/10.1145/355934.355936
Documentation
pub fn broyden_banded(x: &[f64]) -> f64 {
    let n = x.len();
    let ml = 5;
    let mu = 1;

    // store the results f_i(x) for i = 1 to n.
    let mut f = Vec::with_capacity(n);

    // Loop to calculate each f_i(x).
    // Note: Rust uses 0-based indexing (0 to n-1), while the formula
    // uses 1-based indexing (1 to n). We will use `i` as the 0-based index.
    for i in 0..n {
        let xi = x[i];

        // first part of the formula: x_i(2 + 5x_i^2) + 1
        let first_part = xi * (2.0 + 5.0 * xi.powi(2)) + 1.0;

        // Calculate the summation part: Σ_{j ∈ J_i} x_j(1 + x_j)
        let mut sum = 0.0;

        // Determine the bounds for the inner loop based on J_i.
        // We convert the 1-based math formula to 0-based Rust indices.
        // Math: max(1, i - m_l)  -> Rust: (i + 1).saturating_sub(m_l).saturating_sub(1)
        // A simpler way is to work with isize or use saturating_sub.
        let lower_j = i.saturating_sub(ml);

        // Math: min(n, i + m_u)  -> Rust: (i + 1 + m_u).min(n) - 1
        let upper_j = (i + mu).min(n - 1);

        // Loop over the "band" of j indices.
        for (j, item) in x.iter().enumerate().take(upper_j + 1).skip(lower_j) {
            // The condition `j ≠ i` from the definition of J_i.
            if i == j {
                continue;
            }

            let xj = item;
            sum += xj * (1.0 + xj);
        }

        // Combine the parts to get the final f_i(x)
        f.push(first_part - sum);
    }

    f.iter().map(|&fi| fi.powi(2)).sum()
}

pub fn init(n: usize) -> Vec<f64> {
    vec![-1.; n]
}

pub fn min(n: usize) -> Vec<f64> {
    vec![-1.; n] // Broyden banded min is roughly -1? No, usually close to 0 but let's check. Actually not 0.
    // The previous implementation didn't have a min for broyden banded in MGHMin.
    // We omit it for now or assume close to 0 if we added it?
    // User request "examine one by one" suggests just moving what exists.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_broyden_banded() {
        let n = 10;
        let x = init(n);
        let val = broyden_banded(&x);
        assert!(val.is_finite());
    }
}