use crate::row::BasisStatus;
use crate::row::Row;
use crate::scip::ScipPtr;
use crate::{ffi, Variable};
use std::rc::Rc;
#[derive(Debug, Clone)]
pub struct Col {
pub(crate) raw: *mut ffi::SCIP_COL,
pub(crate) scip: Rc<ScipPtr>,
}
impl Col {
pub fn inner(&self) -> *mut ffi::SCIP_COL {
self.raw
}
pub fn index(&self) -> usize {
let id = unsafe { ffi::SCIPcolGetIndex(self.raw) };
assert!(id >= 0);
id as usize
}
pub fn obj(&self) -> f64 {
unsafe { ffi::SCIPcolGetObj(self.raw) }
}
pub fn lb(&self) -> f64 {
unsafe { ffi::SCIPcolGetLb(self.raw) }
}
pub fn ub(&self) -> f64 {
unsafe { ffi::SCIPcolGetUb(self.raw) }
}
pub fn best_bound(&self) -> f64 {
unsafe { ffi::SCIPcolGetBestBound(self.raw) }
}
pub fn var(&self) -> Variable {
let var_ptr = unsafe { ffi::SCIPcolGetVar(self.raw) };
Variable {
raw: var_ptr,
scip: Rc::clone(&self.scip),
}
}
pub fn primal_sol(&self) -> f64 {
unsafe { ffi::SCIPcolGetPrimsol(self.raw) }
}
pub fn min_primal_sol(&self) -> f64 {
unsafe { ffi::SCIPcolGetMinPrimsol(self.raw) }
}
pub fn max_primal_sol(&self) -> f64 {
unsafe { ffi::SCIPcolGetMaxPrimsol(self.raw) }
}
pub fn basis_status(&self) -> BasisStatus {
unsafe { ffi::SCIPcolGetBasisStatus(self.raw) }.into()
}
pub fn var_probindex(&self) -> Option<usize> {
let probindex = unsafe { ffi::SCIPcolGetVarProbindex(self.raw) };
if probindex < 0 {
None
} else {
Some(probindex as usize)
}
}
pub fn is_integral(&self) -> bool {
(unsafe { ffi::SCIPcolIsIntegral(self.raw) }) != 0
}
pub fn is_removable(&self) -> bool {
(unsafe { ffi::SCIPcolIsRemovable(self.raw) }) != 0
}
pub fn lp_pos(&self) -> Option<usize> {
let pos = unsafe { ffi::SCIPcolGetLPPos(self.raw) };
if pos < 0 {
None
} else {
Some(pos as usize)
}
}
pub fn lp_depth(&self) -> Option<usize> {
let depth = unsafe { ffi::SCIPcolGetLPDepth(self.raw) };
if depth < 0 {
None
} else {
Some(depth as usize)
}
}
pub fn is_in_lp(&self) -> bool {
(unsafe { ffi::SCIPcolIsInLP(self.raw) }) != 0
}
pub fn n_non_zeros(&self) -> usize {
let n_non_zeros = unsafe { ffi::SCIPcolGetNNonz(self.raw) };
assert!(n_non_zeros >= 0);
n_non_zeros as usize
}
pub fn n_lp_non_zeros(&self) -> usize {
let n_lp_non_zeros = unsafe { ffi::SCIPcolGetNLPNonz(self.raw) };
assert!(n_lp_non_zeros >= 0);
n_lp_non_zeros as usize
}
pub fn rows(&self) -> Vec<Row> {
let n_non_zeros = self.n_non_zeros();
let rows_ptr = unsafe { ffi::SCIPcolGetRows(self.raw) };
let rows = unsafe { std::slice::from_raw_parts(rows_ptr, n_non_zeros) };
rows.iter()
.map(|&row_ptr| Row {
raw: row_ptr,
scip: Rc::clone(&self.scip),
})
.collect()
}
pub fn vals(&self) -> Vec<f64> {
let n_non_zeros = self.n_non_zeros();
let vals_ptr = unsafe { ffi::SCIPcolGetVals(self.raw) };
let vals = unsafe { std::slice::from_raw_parts(vals_ptr, n_non_zeros) };
vals.to_vec()
}
pub fn strong_branching_node(&self) -> Option<i64> {
let node = unsafe { ffi::SCIPcolGetStrongbranchNode(self.raw) };
if node < 0 {
None
} else {
Some(node)
}
}
pub fn n_strong_branches(&self) -> usize {
let n_strong_branches = unsafe { ffi::SCIPcolGetNStrongbranchs(self.raw) };
assert!(n_strong_branches >= 0);
n_strong_branches as usize
}
pub fn age(&self) -> usize {
let age = unsafe { ffi::SCIPcolGetAge(self.raw) };
assert!(age >= 0);
age as usize
}
}
impl PartialEq for Col {
fn eq(&self, other: &Self) -> bool {
self.index() == other.index() && self.raw == other.raw
}
}
#[cfg(test)]
mod tests {
use crate::prelude::eventhdlr;
use crate::{
minimal_model, BasisStatus, Event, EventMask, Eventhdlr, Model, ModelWithProblem,
ProblemOrSolving, SCIPEventhdlr, Solving, VarType,
};
struct ColTesterEventHandler;
impl Eventhdlr for ColTesterEventHandler {
fn get_type(&self) -> EventMask {
EventMask::FIRST_LP_SOLVED
}
fn execute(&mut self, model: Model<Solving>, _eventhdlr: SCIPEventhdlr, event: Event) {
assert_eq!(event.event_type(), EventMask::FIRST_LP_SOLVED);
let vars = model.vars();
let first_var = vars[0].clone();
let col = first_var.col().unwrap();
assert_eq!(col.index(), 0);
assert_eq!(col.index(), 0);
assert_eq!(col.index(), 0);
assert_eq!(col.obj(), 1.0);
assert_eq!(col.lb(), 0.0);
assert_eq!(col.ub(), 1.0);
assert_eq!(col.best_bound(), 0.0);
assert_eq!(col.primal_sol(), 1.0);
assert_eq!(col.min_primal_sol(), 1.0);
assert_eq!(col.max_primal_sol(), 1.0);
assert_eq!(col.basis_status(), BasisStatus::Basic);
assert_eq!(col.var_probindex(), Some(0));
assert!(col.is_integral());
assert!(!col.is_removable());
assert_eq!(col.lp_pos(), Some(0));
assert_eq!(col.lp_depth(), Some(0));
assert!(col.is_in_lp());
assert_eq!(col.n_non_zeros(), 1);
assert_eq!(col.n_lp_non_zeros(), 1);
assert_eq!(col.vals(), vec![1.0]);
assert_eq!(col.strong_branching_node(), None);
assert_eq!(col.n_strong_branches(), 0);
assert_eq!(col.age(), 0);
}
}
#[test]
fn test_col() {
let mut model = minimal_model();
let x = model.add_var(0.0, 1.0, 1.0, "x", VarType::Binary);
let cons = model.add_cons(vec![&x], &[1.0], 1.0, 1.0, "cons1");
model.set_cons_modifiable(&cons, true);
let e = ColTesterEventHandler;
model.add(eventhdlr(e).name("ColTesterEventHandler"));
model.solve();
}
}