use std::fmt;
use std::rc::Rc;
use crate::scip::ScipPtr;
use crate::variable::Variable;
use crate::{ffi, scip_call_panic};
#[derive(Clone)]
pub struct Solution {
pub(crate) raw: *mut ffi::SCIP_SOL,
pub(crate) scip_ptr: Rc<ScipPtr>,
}
impl Solution {
pub fn inner(&self) -> *mut ffi::SCIP_SOL {
self.raw
}
pub fn obj_val(&self) -> f64 {
unsafe { ffi::SCIPgetSolOrigObj(self.scip_ptr.raw, self.raw) }
}
pub fn val(&self, var: &Variable) -> f64 {
unsafe { ffi::SCIPgetSolVal(self.scip_ptr.raw, self.raw, var.raw) }
}
pub fn set_val(&self, var: &Variable, val: f64) {
scip_call_panic!(ffi::SCIPsetSolVal(
self.scip_ptr.raw,
self.raw,
var.raw,
val
));
}
pub fn as_name_map(&self) -> std::collections::HashMap<String, f64> {
let vars = unsafe { ffi::SCIPgetVars(self.scip_ptr.raw) };
let n_vars = unsafe { ffi::SCIPgetNVars(self.scip_ptr.raw) };
let mut map = std::collections::HashMap::new();
for i in 0..n_vars {
let var = unsafe { *vars.offset(i as isize) };
let val = unsafe { ffi::SCIPgetSolVal(self.scip_ptr.raw, self.raw, var) };
let eps = unsafe { ffi::SCIPepsilon(self.scip_ptr.raw) };
if val > eps || val < -eps {
let name_ptr = unsafe { ffi::SCIPvarGetName(var) };
let name = unsafe { std::ffi::CStr::from_ptr(name_ptr).to_str().unwrap() };
map.insert(name.to_string(), val);
}
}
map
}
pub fn as_id_map(&self) -> std::collections::HashMap<i32, f64> {
let vars = unsafe { ffi::SCIPgetVars(self.scip_ptr.raw) };
let n_vars = unsafe { ffi::SCIPgetNVars(self.scip_ptr.raw) };
let mut map = std::collections::HashMap::new();
for i in 0..n_vars {
let var = unsafe { *vars.offset(i as isize) };
let val = unsafe { ffi::SCIPgetSolVal(self.scip_ptr.raw, self.raw, var) };
let eps = unsafe { ffi::SCIPepsilon(self.scip_ptr.raw) };
if val > eps || val < -eps {
let id = unsafe { ffi::SCIPvarGetProbindex(var) };
map.insert(id, val);
}
}
map
}
}
impl fmt::Debug for Solution {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let obj_val = self.obj_val();
writeln!(f, "Solution with obj val: {obj_val}")?;
let vars = unsafe { ffi::SCIPgetOrigVars(self.scip_ptr.raw) };
let n_vars = unsafe { ffi::SCIPgetNOrigVars(self.scip_ptr.raw) };
for i in 0..n_vars {
let var = unsafe { *vars.offset(i as isize) };
let val = unsafe { ffi::SCIPgetSolVal(self.scip_ptr.raw, self.raw, var) };
let eps = unsafe { ffi::SCIPepsilon(self.scip_ptr.raw) };
if val > eps || val < -eps {
let name_ptr = unsafe { ffi::SCIPvarGetName(var) };
let name = unsafe { std::ffi::CStr::from_ptr(name_ptr).to_str().unwrap() };
writeln!(f, "Var {name}={val}")?;
}
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum SolError {
Infeasible,
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn sol_methods() {
let model = Model::new()
.hide_output()
.include_default_plugins()
.read_prob("data/test/simple.lp")
.unwrap()
.solve();
let status = model.status();
assert_eq!(status, Status::Optimal);
let sol = model.best_sol().unwrap();
assert!(!sol.inner().is_null());
let debug_str = format!("{:?}", sol);
assert!(debug_str.contains("Solution with obj val: 200"));
assert!(debug_str.contains("Var x1=40"));
assert!(debug_str.contains("Var x2=20"));
let vars = model.vars();
assert_eq!(sol.val(&vars[0]), 40.);
assert_eq!(sol.val(&vars[1]), 20.);
assert_eq!(sol.obj_val(), model.obj_val());
let sol_name_map = sol.as_name_map();
assert_eq!(sol_name_map.get("t_x1").unwrap(), &40.);
assert_eq!(sol_name_map.get("t_x2").unwrap(), &20.);
let sol_id_map = sol.as_id_map();
assert_eq!(sol_id_map.get(&0).unwrap(), &40.);
assert_eq!(sol_id_map.get(&1).unwrap(), &20.);
}
}