Skip to content

Commit

Permalink
Merge pull request #15 from adwhit/fix-bounds-dangling-ptr
Browse files Browse the repository at this point in the history
fix dangling ptr in various bounds getters
  • Loading branch information
adwhit authored Dec 10, 2023
2 parents 656762b + cd75dff commit 0fe62f9
Showing 2 changed files with 56 additions and 34 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[package]
name = "nlopt"
version = "0.6.0"
version = "0.7.0"
authors = ["Lucas L. Treffenstädt <lucas@treffenstaedt.de>", "Alex Whitney <adwhit@fastmail.com>"]
description = "Wrapper for the nlopt library"
license = "MIT"
repository = "https://github.com/adwhit/rust-nlopt"
readme = "README.md"
keywords = ["nonlinear", "optimization", "lbfgs", "bobyqa"]
keywords = ["nonlinear", "optimization", "lbfgs", "bobyqa", "milp"]
categories = ["science", "algorithms"]
edition = "2018"

86 changes: 54 additions & 32 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -386,29 +386,19 @@ impl<F: ObjFn<T>, T> Nlopt<F, T> {
}

/// Retrieve the current upper bonds on `x`
pub fn get_upper_bounds(&self) -> Option<&[f64]> {
pub fn get_upper_bounds(&self) -> Option<Vec<f64>> {
let mut bound: Vec<f64> = vec![0.0_f64; self.n_dims];
let b = bound.as_mut_ptr();
unsafe {
let ret = sys::nlopt_get_upper_bounds(self.nloptc_obj.0, b as *mut f64);
match ret {
x if x < 0 => None,
_ => Some(slice::from_raw_parts(b as *mut f64, self.n_dims)),
}
}
let res = unsafe { sys::nlopt_get_upper_bounds(self.nloptc_obj.0, b as *mut f64) };
result_from_outcome(res).ok().map(|_| bound)
}

/// Retrieve the current lower bonds on `x`
pub fn get_lower_bounds(&self) -> Option<&[f64]> {
pub fn get_lower_bounds(&self) -> Option<Vec<f64>> {
let mut bound: Vec<f64> = vec![0.0_f64; self.n_dims];
let b = bound.as_mut_ptr();
unsafe {
let ret = sys::nlopt_get_lower_bounds(self.nloptc_obj.0, b as *mut f64);
match ret {
x if x < 0 => None,
_ => Some(slice::from_raw_parts(b as *mut f64, self.n_dims)),
}
}
let res = unsafe { sys::nlopt_get_lower_bounds(self.nloptc_obj.0, b as *mut f64) };
result_from_outcome(res).ok().map(|_| bound)
}

/// Several of the algorithms in NLopt (MMA, COBYLA, and ORIG_DIRECT) also support arbitrary
@@ -642,14 +632,11 @@ impl<F: ObjFn<T>, T> Nlopt<F, T> {
self.set_xtol_abs(tol)
}

pub fn get_xtol_abs(&mut self) -> Option<&[f64]> {
pub fn get_xtol_abs(&mut self) -> Option<Vec<f64>> {
let mut tol: Vec<f64> = vec![0.0_f64; self.n_dims];
let b = tol.as_mut_ptr();
let ret = unsafe { sys::nlopt_get_xtol_abs(self.nloptc_obj.0, b as *mut f64) };
match ret {
x if x < 0 => None,
_ => Some(unsafe { slice::from_raw_parts(b as *mut f64, self.n_dims) }),
}
let res = unsafe { sys::nlopt_get_xtol_abs(self.nloptc_obj.0, b as *mut f64) };
result_from_outcome(res).ok().map(|_| tol)
}

/// Stop when the number of function evaluations exceeds `maxeval`. (This is not a strict maximum:
@@ -759,16 +746,12 @@ impl<F: ObjFn<T>, T> Nlopt<F, T> {
/// depend on the initial `x`, which is why you must pass it here. Both `x`
/// and the return value are arrays of
/// length `n`.
pub fn get_initial_step(&mut self, x: &[f64]) -> Option<&[f64]> {
pub fn get_initial_step(&mut self, x: &[f64]) -> Option<Vec<f64>> {
let mut dx: Vec<f64> = vec![0.0_f64; self.n_dims];
unsafe {
let b = dx.as_mut_ptr();
let ret = sys::nlopt_get_initial_step(self.nloptc_obj.0, x.as_ptr(), b as *mut f64);
match ret {
x if x < 0 => None,
_ => Some(slice::from_raw_parts(b as *mut f64, self.n_dims)),
}
}
let b = dx.as_mut_ptr();
let res =
unsafe { sys::nlopt_get_initial_step(self.nloptc_obj.0, x.as_ptr(), b as *mut f64) };
result_from_outcome(res).ok().map(|_| dx)
}

// Stochastic Population
@@ -949,11 +932,29 @@ mod tests {
let xl = vec![2.0; 25];
let xu = vec![4.0; 25];

// check bounds unset
assert!(opt
.get_upper_bounds()
.unwrap()
.into_iter()
.all(|v| v == f64::INFINITY));
assert!(opt
.get_lower_bounds()
.unwrap()
.into_iter()
.all(|v| v == f64::NEG_INFINITY));

// set the bounds
opt.set_upper_bounds(&xu).unwrap();
opt.set_lower_bounds(&xl).unwrap();
// set it twice, why not
opt.set_lower_bounds(&xl).unwrap();
opt.set_xtol_rel(1e-8).unwrap();

// check the bounds were set
assert_eq!(opt.get_upper_bounds().unwrap(), xu);
assert_eq!(opt.get_lower_bounds().unwrap(), xl);

opt.set_xtol_rel(1e-8).unwrap();
let mut expect = vec![2.0; 25];
expect[23] = 2.1090933511928247;
expect[24] = 4.;
@@ -979,6 +980,10 @@ mod tests {
Target::Maximize,
(),
);
assert_eq!(
opt.get_upper_bounds().unwrap(),
&[f64::INFINITY, f64::INFINITY]
);
let mut input = vec![3.0, -2.0];
let (_s, val) = opt.optimize(&mut input).unwrap();
assert_eq!(val, 4.);
@@ -1006,6 +1011,15 @@ mod tests {
opt.add_equality_constraint(heq, (), 1e-6).unwrap();
opt.set_xtol_rel(1e-6).unwrap();

assert_eq!(
opt.get_upper_bounds().unwrap(),
&[f64::INFINITY, f64::INFINITY]
);
assert_eq!(
opt.get_lower_bounds().unwrap(),
&[f64::NEG_INFINITY, f64::NEG_INFINITY]
);

let mut local_opt = opt.get_local_optimizer(Algorithm::Cobyla);
local_opt.set_xtol_rel(1e-6).unwrap();
opt.set_local_optimizer(local_opt).unwrap();
@@ -1064,7 +1078,11 @@ mod tests {
opt.set_xtol_rel(1e-6).unwrap();

let mut local_opt = opt.get_local_optimizer(Algorithm::Bobyqa);

assert_eq!(local_opt.get_xtol_rel().unwrap(), 0.0);
local_opt.set_xtol_rel(1e-6).unwrap();
assert_eq!(local_opt.get_xtol_rel().unwrap(), 1e-6);

opt.set_local_optimizer(local_opt).unwrap();

let mut input = vec![1., 1.];
@@ -1090,7 +1108,11 @@ mod tests {
Target::Minimize,
user_data.clone(),
);

assert_eq!(nlopt.get_xtol_abs().unwrap(), &[0.0]);
nlopt.set_xtol_abs1(1e-15).unwrap();
assert_eq!(nlopt.get_xtol_abs().unwrap(), &[1e-15]);

let mut input = [0.0];
let (_s, v) = nlopt.optimize(&mut input).unwrap();
assert_eq!(input[0], 3.0);

0 comments on commit 0fe62f9

Please sign in to comment.