#![cfg(feature = "nalgebra")]
use basin::problems::ConstrainedQuadratic;
use basin::{
Backtracking, BarrierMethod, BasicState, Executor, GradientDescent, GradientState,
TerminationReason, BFGS,
};
use nalgebra::{DMatrix, DVector};
fn active_problem() -> ConstrainedQuadratic<DMatrix<f64>, DVector<f64>> {
ConstrainedQuadratic::new(
DVector::from_vec(vec![2.0, 2.0]),
DMatrix::from_row_slice(1, 2, &[1.0, 1.0]),
DVector::from_vec(vec![2.0]),
)
}
#[test]
fn active_constraint_converges_to_projection() {
let problem = active_problem();
let initial = DVector::from_vec(vec![0.0, 0.0]);
let result = Executor::new(
problem,
BarrierMethod::new(GradientDescent::with_line_search(Backtracking::new())),
BasicState::new(initial),
)
.max_iter(50)
.run();
assert_eq!(result.reason, TerminationReason::SolverConverged);
assert!(
(result.param()[0] - 1.0).abs() < 1e-4 && (result.param()[1] - 1.0).abs() < 1e-4,
"expected (1, 1), got {:?}",
result.param()
);
}
#[test]
fn inactive_constraint_recovers_unconstrained_minimum() {
let problem = ConstrainedQuadratic::new(
DVector::from_vec(vec![0.5, 0.5]),
DMatrix::from_row_slice(1, 2, &[1.0, 1.0]),
DVector::from_vec(vec![2.0]),
);
let initial = DVector::from_vec(vec![0.0, 0.0]);
let result = Executor::new(
problem,
BarrierMethod::new(GradientDescent::with_line_search(Backtracking::new())),
BasicState::new(initial),
)
.max_iter(50)
.run();
assert_eq!(result.reason, TerminationReason::SolverConverged);
assert!(
(result.param()[0] - 0.5).abs() < 1e-4 && (result.param()[1] - 0.5).abs() < 1e-4,
"expected (0.5, 0.5), got {:?}",
result.param()
);
}
#[test]
fn infeasible_start_is_reported_as_failure() {
let problem = active_problem();
let initial = DVector::from_vec(vec![2.0, 2.0]);
let result = Executor::new(
problem,
BarrierMethod::new(GradientDescent::with_line_search(Backtracking::new())),
BasicState::new(initial),
)
.max_iter(50)
.run();
assert_eq!(result.reason, TerminationReason::SolverFailed);
}
#[test]
fn eval_counts_are_recorded() {
let problem = active_problem();
let initial = DVector::from_vec(vec![0.0, 0.0]);
let result = Executor::new(
problem,
BarrierMethod::new(GradientDescent::with_line_search(Backtracking::new())),
BasicState::new(initial),
)
.max_iter(50)
.run();
assert!(result.cost_evals() > 0, "no cost evals recorded");
assert!(
result.state.gradient_evals() > 0,
"no gradient evals recorded"
);
}
#[test]
fn two_constraints_both_active() {
let problem = ConstrainedQuadratic::new(
DVector::from_vec(vec![2.0, 2.0]),
DMatrix::from_row_slice(2, 2, &[1.0, 1.0, 1.0, 0.0]),
DVector::from_vec(vec![2.0, 0.5]),
);
let initial = DVector::from_vec(vec![0.0, 0.0]);
let result = Executor::new(
problem,
BarrierMethod::new(GradientDescent::with_line_search(Backtracking::new())),
BasicState::new(initial),
)
.max_iter(50)
.run();
assert_eq!(result.reason, TerminationReason::SolverConverged);
assert!(
(result.param()[0] - 0.5).abs() < 1e-4 && (result.param()[1] - 1.5).abs() < 1e-4,
"expected (0.5, 1.5), got {:?}",
result.param()
);
}
#[test]
fn bfgs_inner_converges_to_projection() {
let problem = active_problem();
let initial = DVector::from_vec(vec![0.0, 0.0]);
let result = Executor::new(
problem,
BarrierMethod::new(BFGS::with_line_search(Backtracking::new())),
BasicState::new(initial),
)
.max_iter(50)
.run();
assert_eq!(result.reason, TerminationReason::SolverConverged);
assert!(
(result.param()[0] - 1.0).abs() < 1e-4 && (result.param()[1] - 1.0).abs() < 1e-4,
"expected (1, 1), got {:?}",
result.param()
);
}