use crate::options::SolverOptions;
use crate::problem::{LpProblem, SolveRoute, SolveStatus, SolverResult};
pub fn solve_lp_with(problem: &LpProblem, options: &SolverOptions) -> SolverResult {
let mut result = crate::simplex::solve_with(problem, options);
result.stats.route = SolveRoute::LpDirect;
result.stats.deadline_triggered = matches!(result.status, SolveStatus::Timeout);
result
}
pub(crate) fn solve_lp_forwarded_from_qp(problem: &LpProblem, options: &SolverOptions) -> SolverResult {
let mut result = crate::simplex::solve_with(problem, options);
result.stats.route = SolveRoute::LpForwardedFromQp;
result.stats.deadline_triggered = matches!(result.status, SolveStatus::Timeout);
result
}
#[cfg(test)]
mod tests {
use super::*;
use crate::problem::ConstraintType;
use crate::sparse::CscMatrix;
fn make_trivial_lp() -> LpProblem {
let a = CscMatrix::from_triplets(&[0], &[0], &[1.0], 1, 1).unwrap();
LpProblem::new_general(
vec![1.0],
a,
vec![5.0],
vec![ConstraintType::Le],
vec![(0.0, f64::INFINITY)],
None,
)
.unwrap()
}
#[test]
fn invalid_options_rejected_at_lp_entry() {
let lp = make_trivial_lp();
let cases: &[(&str, SolverOptions)] = &[
("nan primal_tol", SolverOptions { primal_tol: f64::NAN, ..Default::default() }),
("inf primal_tol", SolverOptions { primal_tol: f64::INFINITY, ..Default::default() }),
("neg timeout_secs", SolverOptions { timeout_secs: Some(-0.5), ..Default::default() }),
("zero threads", SolverOptions { threads: 0, ..Default::default() }),
("nan dual_tol", SolverOptions { dual_tol: f64::NAN, ..Default::default() }),
];
for (label, opts) in cases {
let result = solve_lp_with(&lp, opts);
assert_eq!(
result.status,
SolveStatus::NumericalError,
"solve_lp_with with {label} must return NumericalError"
);
}
}
}