|
|
@ -6,7 +6,7 @@ use crate::{
|
|
|
|
|
|
|
|
|
|
|
|
use super::line_search::LineSearch;
|
|
|
|
use super::line_search::LineSearch;
|
|
|
|
|
|
|
|
|
|
|
|
pub fn steepest_descent<T: XVar<E> + Clone, E>(
|
|
|
|
pub fn steepest_descent<T: XVar<E> + Clone, E: std::fmt::Debug>(
|
|
|
|
fun: &dyn ObjectiveFun<T, E>,
|
|
|
|
fun: &dyn ObjectiveFun<T, E>,
|
|
|
|
x0: &T,
|
|
|
|
x0: &T,
|
|
|
|
max_iters: usize,
|
|
|
|
max_iters: usize,
|
|
|
@ -22,11 +22,10 @@ pub fn steepest_descent<T: XVar<E> + Clone, E>(
|
|
|
|
let mut f = 0.0;
|
|
|
|
let mut f = 0.0;
|
|
|
|
let mut i = 0;
|
|
|
|
let mut i = 0;
|
|
|
|
for _ in 0..max_iters {
|
|
|
|
for _ in 0..max_iters {
|
|
|
|
let primes = fun.prime(&xs);
|
|
|
|
let direction = T::scale_prime(&fun.prime(&xs), -1.0);
|
|
|
|
let learning_rate = line_search.get_learning_rate(fun, &xs, &T::scale_prime(&primes, -1.0));
|
|
|
|
let learning_rate = line_search.get_learning_rate(fun, &xs, &direction);
|
|
|
|
xs = xs.update(direction * learning_rate, &primes);
|
|
|
|
xs = xs.update(learning_rate, &direction);
|
|
|
|
f = fun.eval(&xs);
|
|
|
|
f = fun.eval(&xs);
|
|
|
|
|
|
|
|
|
|
|
|
if (f - f_iminus1).abs() < tolerance {
|
|
|
|
if (f - f_iminus1).abs() < tolerance {
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -66,21 +65,52 @@ mod test {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
LineSearch::BackTrack {
|
|
|
|
LineSearch::BackTrack {
|
|
|
|
max_iterations: 100,
|
|
|
|
max_iterations: 100,
|
|
|
|
gamma: 0.5,
|
|
|
|
gamma: 0.9,
|
|
|
|
c: 0.1,
|
|
|
|
c: 0.3,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
];
|
|
|
|
];
|
|
|
|
for line_search in line_searches {
|
|
|
|
for line_search in line_searches {
|
|
|
|
let res = steepest_descent(&obj, &vec![20.0], 1000, 1e-12, &line_search, -1.0);
|
|
|
|
let res = steepest_descent(&obj, &vec![20.0, 20.0], 1000, 1e-12, &line_search, -1.0);
|
|
|
|
|
|
|
|
|
|
|
|
if let ExitCondition::MaxIter = res.exit_con {
|
|
|
|
if let ExitCondition::MaxIter = res.exit_con {
|
|
|
|
panic!("Failed to converge to minima");
|
|
|
|
panic!("Failed to converge to minima");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
println!(
|
|
|
|
println!(
|
|
|
|
"{:?} on iteration {}\n{}",
|
|
|
|
"{:?} on iteration {} has value:\n{}",
|
|
|
|
res.best_xs, res.iters, res.best_fun_val
|
|
|
|
res.best_xs, res.iters, res.best_fun_val
|
|
|
|
);
|
|
|
|
);
|
|
|
|
assert!(res.best_fun_val < 1e-8);
|
|
|
|
assert!(res.best_fun_val < 1e-8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
|
|
pub fn basic_beale_test() {
|
|
|
|
|
|
|
|
let fun = Box::new(|x: &Vec<f64>| {
|
|
|
|
|
|
|
|
(1.5 - x[0] + x[0] * x[1]).powi(2)
|
|
|
|
|
|
|
|
+ (2.25 - x[0] + x[0] * x[1].powi(2)).powi(2)
|
|
|
|
|
|
|
|
+ (2.625 - x[0] + x[0] * x[1].powi(3)).powi(2)
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
let prime = Box::new(|x: &Vec<f64>| {
|
|
|
|
|
|
|
|
vec![
|
|
|
|
|
|
|
|
2.0 * (1.5 - x[0] + x[0] * x[1]) * (x[1] - 1.0)
|
|
|
|
|
|
|
|
+ 2.0 * (2.25 - x[0] + x[0] * x[1].powi(2)) * (x[1].powi(2) - 1.0)
|
|
|
|
|
|
|
|
+ 2.0 * (2.625 - x[0] + x[0] * x[1].powi(3)) * (x[1].powi(3) - 1.0),
|
|
|
|
|
|
|
|
2.0 * (1.5 - x[0] + x[0] * x[1]) * (x[0])
|
|
|
|
|
|
|
|
+ 2.0 * (2.25 - x[0] + x[0] * x[1].powi(2)) * (2.0 * x[0] * x[1])
|
|
|
|
|
|
|
|
+ 2.0 * (2.625 - x[0] + x[0] * x[1].powi(3)) * (3.0 * x[0] * x[1].powi(3)),
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
let obj = Fun::new(fun, prime);
|
|
|
|
|
|
|
|
let line_search = LineSearch::BackTrack {
|
|
|
|
|
|
|
|
max_iterations: 1000,
|
|
|
|
|
|
|
|
gamma: 0.9,
|
|
|
|
|
|
|
|
c: 0.01,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
let res = steepest_descent(&obj, &vec![3.1, 0.5], 10000, 1e-12, &line_search, -1.0);
|
|
|
|
|
|
|
|
println!(
|
|
|
|
|
|
|
|
"Best val is {:?} for xs {:?}",
|
|
|
|
|
|
|
|
res.best_fun_val, res.best_xs
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(res.best_fun_val < 1e-7);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|