Skip to main content

math_audio_optimisation/
differential_evolution.rs

1use crate::{DEConfig, DEError, DEReport, DifferentialEvolution, Result};
2use ndarray::Array1;
3
4/// Runs Differential Evolution optimization on a function.
5///
6/// This is a convenience function that mirrors SciPy's `differential_evolution` API.
7/// It creates a DE optimizer with the given bounds and configuration, then runs
8/// the optimization to find the global minimum.
9///
10/// # Arguments
11///
12/// * `func` - The objective function to minimize, mapping `&Array1<f64>` to `f64`
13/// * `bounds` - Vector of (lower, upper) bound pairs for each dimension
14/// * `config` - DE configuration (use `DEConfigBuilder` to construct)
15///
16/// # Returns
17///
18/// Returns `Ok(DEReport)` containing the optimization result on success.
19///
20/// # Errors
21///
22/// Returns `DEError::InvalidBounds` if any bound pair has upper < lower.
23///
24/// # Example
25///
26/// ```rust
27/// use math_audio_optimisation::{differential_evolution, DEConfigBuilder};
28///
29/// let config = DEConfigBuilder::new()
30///     .maxiter(50)
31///     .seed(42)
32///     .build()
33///     .expect("invalid config");
34///
35/// let result = differential_evolution(
36///     &|x| x[0].powi(2) + x[1].powi(2),
37///     &[(-5.0, 5.0), (-5.0, 5.0)],
38///     config,
39/// ).expect("optimization failed");
40///
41/// assert!(result.fun < 0.01);
42/// ```
43pub fn differential_evolution<F>(
44    func: &F,
45    bounds: &[(f64, f64)],
46    config: DEConfig,
47) -> Result<DEReport>
48where
49    F: Fn(&Array1<f64>) -> f64 + Sync,
50{
51    let n = bounds.len();
52    let mut lower = Array1::<f64>::zeros(n);
53    let mut upper = Array1::<f64>::zeros(n);
54    for (i, (lo, hi)) in bounds.iter().enumerate() {
55        lower[i] = *lo;
56        upper[i] = *hi;
57        if hi < lo {
58            return Err(DEError::InvalidBounds {
59                index: i,
60                lower: *lo,
61                upper: *hi,
62            });
63        }
64    }
65    let mut de = DifferentialEvolution::new(func, lower, upper)?;
66    *de.config_mut() = config;
67    Ok(de.solve())
68}