use pounce_cli::cbf;
use pounce_convex::{solve_socp_ipm, QpOptions, QpStatus};
use pounce_feral::FeralSolverInterface;
use pounce_linsol::SparseSymLinearSolverInterface;
fn backend() -> Box<dyn SparseSymLinearSolverInterface> {
Box::new(FeralSolverInterface::new())
}
fn solve_instance(text: &str) -> (QpStatus, f64) {
let model = cbf::parse(text).expect("parse CBF");
let cp = model.to_conic().expect("map to conic");
let opts = QpOptions {
max_iter: 500,
..QpOptions::default()
};
let sol = solve_socp_ipm(&cp.prob, &cp.cones, &opts, backend);
let obj = cp.cbf_objective(sol.obj, model.minimize);
(sol.status, obj)
}
fn assert_solved(status: QpStatus, label: &str) {
assert!(
matches!(status, QpStatus::Optimal | QpStatus::OptimalInaccurate),
"{label} status: {status:?}"
);
}
const DEMB761: &str = include_str!("data/cblib/demb761.cbf");
const BECK751: &str = include_str!("data/cblib/beck751.cbf");
const FANG88: &str = include_str!("data/cblib/fang88.cbf");
const POW3: &str = include_str!("data/cblib/pow3_synthetic.cbf");
const SDP: &str = include_str!("data/cblib/sdp_synthetic.cbf");
#[test]
fn demb761_solves_to_optimum() {
let (status, obj) = solve_instance(DEMB761);
assert_solved(status, "demb761");
assert!(obj.is_finite(), "demb761 objective finite: {obj}");
}
#[test]
fn beck751_solves_to_optimum() {
let (status, obj) = solve_instance(BECK751);
assert_solved(status, "beck751");
assert!(obj.is_finite(), "beck751 objective finite: {obj}");
}
#[test]
fn fang88_solves_to_optimum() {
let (status, obj) = solve_instance(FANG88);
assert_solved(status, "fang88");
assert!(obj.is_finite(), "fang88 objective finite: {obj}");
}
#[test]
fn power_cone_synthetic_hits_known_optimum() {
let (status, obj) = solve_instance(POW3);
assert_solved(status, "pow3");
assert!((obj - 1.0).abs() < 1e-6, "pow3 objective {obj} vs 1");
}
#[test]
fn sdp_psdcon_synthetic_hits_known_optimum() {
let (status, obj) = solve_instance(SDP);
assert_eq!(status, QpStatus::Optimal, "sdp status");
assert!((obj - 2.0).abs() < 1e-5, "sdp objective {obj} vs 2");
}