Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reference counted ScipPtr #139

Closed
wants to merge 15 commits into from
4 changes: 2 additions & 2 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ jobs:
args: '--verbose --all --out Xml'

- name: Upload to codecov.io
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
fail_ci_if_error: false

linux-conda-test:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Unreleased
### Added
### Fixed
- Reference counted ScipPtr.
### Changed
### Remove

Expand Down
7 changes: 7 additions & 0 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1787,4 +1787,11 @@ mod tests {
assert_eq!(second_solved.status(), Status::Optimal);
assert!((second_solved.obj_val() - expected_obj).abs() <= 1e-6);
}

#[test]
fn solution_after_model_drop() {
let model = create_model();
let sol = model.solve().best_sol().unwrap(); // Temporary value returned from `model.solve()` is dropped.
dbg!(sol);
}
}
38 changes: 31 additions & 7 deletions src/scip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ use crate::{
};
use crate::{scip_call, HeurTiming, Heuristic};
use core::panic;

use std::collections::BTreeMap;
use std::ffi::{c_int, CStr, CString};
use std::mem::MaybeUninit;
use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;

#[non_exhaustive]
#[derive(Debug)]
pub(crate) struct ScipPtr {
pub(crate) raw: *mut ffi::SCIP,
consumed: bool,
uses: Arc<AtomicUsize>,
vars_added_in_solving: Vec<*mut ffi::SCIP_VAR>,
}

Expand All @@ -26,15 +29,17 @@ impl ScipPtr {
let scip_ptr = unsafe { scip_ptr.assume_init() };
ScipPtr {
raw: scip_ptr,
consumed: false,
uses: Arc::new(AtomicUsize::new(1)),
vars_added_in_solving: Vec::new(),
}
}

pub(crate) fn clone(&self) -> Self {
let uses = self.uses.clone();
uses.fetch_add(1, Ordering::Relaxed);
ScipPtr {
raw: self.raw,
consumed: true,
uses,
vars_added_in_solving: Vec::new(),
}
}
Expand Down Expand Up @@ -115,7 +120,7 @@ impl ScipPtr {

pub(crate) fn status(&self) -> Status {
let status = unsafe { ffi::SCIPgetStatus(self.raw) };
status.try_into().expect("Unknown SCIP status")
status.into()
}

pub(crate) fn print_version(&self) {
Expand Down Expand Up @@ -184,7 +189,7 @@ impl ScipPtr {
let sol = unsafe { ffi::SCIPgetBestSol(self.raw) };

Solution {
scip_ptr: self.raw,
scip_ptr: self.clone(),
raw: sol,
}
}
Expand Down Expand Up @@ -489,7 +494,7 @@ impl ScipPtr {
scip_call! { ffi::SCIPcreateSol(self.raw, sol.as_mut_ptr(), std::ptr::null_mut()) }
let sol = unsafe { sol.assume_init() };
Ok(Solution {
scip_ptr: self.raw,
scip_ptr: self.clone(),
raw: sol,
})
}
Expand Down Expand Up @@ -992,7 +997,8 @@ impl ScipPtr {

impl Drop for ScipPtr {
fn drop(&mut self) {
if self.consumed {
self.uses.fetch_sub(1, Ordering::Relaxed);
if self.uses.load(Ordering::Relaxed) > 0 {
return;
}
// Rust Model struct keeps at most one copy of each variable and constraint pointers
Expand Down Expand Up @@ -1037,3 +1043,21 @@ impl Drop for ScipPtr {
unsafe { ffi::SCIPfree(&mut self.raw) };
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn uses() {
let mut scip = ScipPtr::new();
assert_eq!(scip.uses.load(Ordering::Relaxed), 1);
let scip2 = scip.clone();
assert_eq!(scip.uses.load(Ordering::Relaxed), 2);
drop(scip2);
assert_eq!(scip.uses.load(Ordering::Relaxed), 1);
let uses = scip.uses.clone();
drop(scip);
assert_eq!(uses.load(Ordering::Relaxed), 0);
}
}
24 changes: 15 additions & 9 deletions src/solution.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
use std::fmt;
use std::rc::Rc;

use crate::scip::ScipPtr;
use crate::variable::Variable;
use crate::{ffi, scip_call_panic};

/// A wrapper for a SCIP solution.
#[derive(PartialEq, Eq)]

pub struct Solution {
pub(crate) scip_ptr: *mut ffi::SCIP,
pub(crate) scip_ptr: ScipPtr,
pub(crate) raw: *mut ffi::SCIP_SOL,
}

impl Solution {
/// Returns the objective value of the solution.
pub fn obj_val(&self) -> f64 {
unsafe { ffi::SCIPgetSolOrigObj(self.scip_ptr, self.raw) }
unsafe { ffi::SCIPgetSolOrigObj(self.scip_ptr.raw, self.raw) }
}

/// Returns the value of a variable in the solution.
pub fn val(&self, var: Rc<Variable>) -> f64 {
unsafe { ffi::SCIPgetSolVal(self.scip_ptr, self.raw, var.raw) }
unsafe { ffi::SCIPgetSolVal(self.scip_ptr.raw, self.raw, var.raw) }
}

/// Sets the value of a variable in the solution.
pub fn set_val(&self, var: Rc<Variable>, val: f64) {
scip_call_panic!(ffi::SCIPsetSolVal(self.scip_ptr, self.raw, var.raw, val));
scip_call_panic!(ffi::SCIPsetSolVal(
self.scip_ptr.raw,
self.raw,
var.raw,
val
));
}
}

Expand All @@ -33,12 +39,12 @@ 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::SCIPgetVars(self.scip_ptr) };
let n_vars = unsafe { ffi::SCIPgetNVars(self.scip_ptr) };
let vars = unsafe { ffi::SCIPgetVars(self.scip_ptr.raw) };
let n_vars = unsafe { ffi::SCIPgetNVars(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, self.raw, var) };
let eps = unsafe { ffi::SCIPepsilon(self.scip_ptr) };
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) };
// from CString
Expand Down
Loading