use crate::types::{Basis, RowBatch, SolutionView, SolverError, SolverStatistics, StageTemplate};
pub trait SolverInterface: Send {
fn load_model(&mut self, template: &StageTemplate);
fn add_rows(&mut self, cuts: &RowBatch);
fn set_row_bounds(&mut self, indices: &[usize], lower: &[f64], upper: &[f64]);
fn set_col_bounds(&mut self, indices: &[usize], lower: &[f64], upper: &[f64]);
fn solve(&mut self) -> Result<SolutionView<'_>, SolverError>;
fn reset(&mut self);
fn get_basis(&mut self, out: &mut Basis);
fn solve_with_basis(&mut self, basis: &Basis) -> Result<SolutionView<'_>, SolverError>;
fn statistics(&self) -> SolverStatistics;
fn name(&self) -> &'static str;
fn solver_name_version(&self) -> String;
}
#[cfg(test)]
mod tests {
use super::SolverInterface;
fn accepts_solver<S: SolverInterface>(_: &S) {}
struct NoopSolver;
impl SolverInterface for NoopSolver {
fn load_model(&mut self, _template: &crate::types::StageTemplate) {}
fn add_rows(&mut self, _cuts: &crate::types::RowBatch) {}
fn set_row_bounds(&mut self, _indices: &[usize], _lower: &[f64], _upper: &[f64]) {}
fn set_col_bounds(&mut self, _indices: &[usize], _lower: &[f64], _upper: &[f64]) {}
fn solve(&mut self) -> Result<crate::types::SolutionView<'_>, crate::types::SolverError> {
Err(crate::types::SolverError::InternalError {
message: "noop".to_string(),
error_code: None,
})
}
fn reset(&mut self) {}
fn get_basis(&mut self, _out: &mut crate::types::Basis) {}
fn solve_with_basis(
&mut self,
_basis: &crate::types::Basis,
) -> Result<crate::types::SolutionView<'_>, crate::types::SolverError> {
Err(crate::types::SolverError::InternalError {
message: "noop".to_string(),
error_code: None,
})
}
fn statistics(&self) -> crate::types::SolverStatistics {
crate::types::SolverStatistics::default()
}
fn name(&self) -> &'static str {
"Noop"
}
fn solver_name_version(&self) -> String {
"NoopSolver 0.0.0".to_string()
}
}
fn assert_send<T: Send>() {}
#[test]
fn test_trait_compiles_as_generic_bound() {
accepts_solver(&NoopSolver);
}
#[test]
fn test_solver_interface_send_bound() {
assert_send::<NoopSolver>();
}
#[test]
fn test_noop_solver_name() {
let name = NoopSolver.name();
assert_eq!(name, "Noop");
assert!(!name.is_empty());
}
#[test]
fn test_noop_solver_statistics_initial() {
let stats = NoopSolver.statistics();
assert_eq!(stats.solve_count, 0);
assert_eq!(stats.success_count, 0);
assert_eq!(stats.failure_count, 0);
assert_eq!(stats.total_iterations, 0);
assert_eq!(stats.retry_count, 0);
assert_eq!(stats.total_solve_time_seconds, 0.0);
}
#[test]
fn test_noop_solver_get_basis_noop() {
use crate::types::Basis;
let mut solver = NoopSolver;
let mut raw = Basis::new(3, 2);
raw.col_status.iter_mut().for_each(|v| *v = 99_i32);
raw.row_status.iter_mut().for_each(|v| *v = 99_i32);
solver.get_basis(&mut raw);
assert!(raw.col_status.iter().all(|&v| v == 99_i32));
assert!(raw.row_status.iter().all(|&v| v == 99_i32));
}
#[test]
fn test_noop_solver_solve_with_basis_returns_internal_error() {
use crate::types::{Basis, SolverError};
let mut solver = NoopSolver;
let raw = Basis::new(0, 0);
let result = solver.solve_with_basis(&raw);
assert!(matches!(result, Err(SolverError::InternalError { .. })));
}
#[test]
fn test_noop_solver_all_methods() {
use crate::types::{RowBatch, SolverError, StageTemplate};
let template = StageTemplate {
num_cols: 1,
num_rows: 0,
num_nz: 0,
col_starts: vec![0_i32, 0],
row_indices: vec![],
values: vec![],
col_lower: vec![0.0],
col_upper: vec![1.0],
objective: vec![1.0],
row_lower: vec![],
row_upper: vec![],
n_state: 0,
n_transfer: 0,
n_dual_relevant: 0,
n_hydro: 0,
max_par_order: 0,
col_scale: Vec::new(),
row_scale: Vec::new(),
};
let batch = RowBatch {
num_rows: 0,
row_starts: vec![0_i32],
col_indices: vec![],
values: vec![],
row_lower: vec![],
row_upper: vec![],
};
let mut solver = NoopSolver;
solver.load_model(&template);
solver.add_rows(&batch);
solver.set_row_bounds(&[], &[], &[]);
solver.set_col_bounds(&[], &[], &[]);
let result = solver.solve();
assert!(matches!(result, Err(SolverError::InternalError { .. })));
solver.reset();
}
}