mod cobyla;
use crate::cobyla::raw_cobyla;
mod cobyla_solver;
mod cobyla_state;
pub use crate::cobyla_solver::*;
pub use crate::cobyla_state::*;
use std::os::raw::c_void;
use std::slice;
pub trait ObjFn<U>: Fn(&[f64], &mut U) -> f64 {}
impl<F, U> ObjFn<U> for F where F: Fn(&[f64], &mut U) -> f64 {}
pub trait CstrFn: Fn(&[f64]) -> f64 {}
impl<F> CstrFn for F where F: Fn(&[f64]) -> f64 {}
struct FunctionCfg<'a, F: ObjFn<U>, G: CstrFn, U> {
pub func: F,
pub cons: &'a [G],
pub data: U,
}
fn function_raw_callback<F: ObjFn<U>, G: CstrFn, U>(
n: ::std::os::raw::c_long,
m: ::std::os::raw::c_long,
x: *const f64,
con: *mut f64,
data: *mut ::std::os::raw::c_void,
) -> f64 {
let argument = unsafe { slice::from_raw_parts(x, n as usize) };
let f = unsafe { &mut *(data as *mut FunctionCfg<F, G, U>) };
let res = (f.func)(argument, &mut f.data);
for i in 0..m as isize {
unsafe {
*con.offset(i) = (f.cons[i as usize])(argument);
}
}
#[allow(clippy::forget_ref)]
std::mem::forget(f);
res
}
#[allow(clippy::useless_conversion)]
#[allow(clippy::too_many_arguments)]
pub fn fmin_cobyla<'a, F: ObjFn<U>, G: CstrFn, U>(
func: F,
x0: &'a mut [f64],
cons: &[G],
args: U,
rhobeg: f64,
rhoend: f64,
maxfun: i32,
iprint: i32,
) -> (i32, &'a [f64]) {
let n: i32 = x0.len() as i32;
let m: i32 = cons.len() as i32;
let fn_cfg = Box::new(FunctionCfg {
func,
cons,
data: args, });
let fn_cfg_ptr = Box::into_raw(fn_cfg) as *mut c_void;
let x = x0;
let mut maxfun = maxfun.into();
let mut w = vec![0.; (n * (3 * n + 2 * m + 11) + 4 * m + 6) as usize];
let mut iact = vec![0; (m + 1) as usize];
let status = unsafe {
raw_cobyla(
n.into(),
m.into(),
Some(function_raw_callback::<F, G, U>),
fn_cfg_ptr,
x.as_mut_ptr(),
rhobeg,
rhoend,
iprint.into(),
&mut maxfun,
w.as_mut_ptr(),
iact.as_mut_ptr(),
)
};
unsafe { Box::from_raw(fn_cfg_ptr) };
(status, x)
}
#[cfg(test)]
mod tests {
use approx::assert_abs_diff_eq;
use super::*;
fn paraboloid(x: &[f64], _data: &mut ()) -> f64 {
10. * (x[0] + 1.).powf(2.) + x[1].powf(2.)
}
#[test]
fn test_fmin_cobyla() {
let mut x = vec![1., 1.];
#[allow(bare_trait_objects)]
let mut cons: Vec<&CstrFn> = vec![];
let cstr1 = |x: &[f64]| x[0];
cons.push(&cstr1);
let (status, x_opt) = fmin_cobyla(paraboloid, &mut x, &cons, (), 0.5, 1e-4, 200, 1);
println!("status = {}", status);
println!("x = {:?}", x_opt);
assert_abs_diff_eq!(x.as_slice(), [0., 0.].as_slice(), epsilon = 1e-4);
}
unsafe fn calcfc(
_n: ::std::os::raw::c_long,
_m: ::std::os::raw::c_long,
x: *const f64,
_con: *mut f64,
_data: *mut ::std::os::raw::c_void,
) -> f64 {
let r1 = *x.offset(0) + 1.0;
let r2 = *x.offset(1);
10.0 * (r1 * r1) + (r2 * r2)
}
#[test]
fn test_cobyla() {
let n = 2;
let m = 0;
let mut x = vec![1.0, 1.0];
let rhobeg = 0.5;
let rhoend = 1e-4;
let iprint = 0;
let mut maxfun = 2000;
let mut w: Vec<_> = vec![0.; 3000];
let mut iact: Vec<_> = vec![0; 51];
let null = std::ptr::null_mut::<c_void>();
unsafe {
raw_cobyla(
n,
m,
Some(calcfc),
null,
x.as_mut_ptr(),
rhobeg,
rhoend,
iprint,
&mut maxfun,
w.as_mut_ptr(),
iact.as_mut_ptr(),
);
}
assert_abs_diff_eq!(x.as_slice(), [-1., 0.].as_slice(), epsilon = 1e-3);
}
fn fletcher9115(x: &[f64], _data: &mut ()) -> f64 {
-x[0] - x[1]
}
#[allow(clippy::vec_init_then_push)]
#[test]
fn test_fmin_cobyla2() {
let mut x = vec![1., 1.];
let mut cons: Vec<&dyn CstrFn> = vec![];
cons.push(&|x: &[f64]| x[1] - x[0] * x[0]);
cons.push(&|x: &[f64]| 1. - x[0] * x[0] - x[1] * x[1]);
let (status, x_opt) = fmin_cobyla(fletcher9115, &mut x, &cons, (), 0.5, 1e-4, 200, 1);
println!("status = {}", status);
println!("x = {:?}", x_opt);
let sqrt_0_5: f64 = 0.5_f64.sqrt();
assert_abs_diff_eq!(x_opt, [sqrt_0_5, sqrt_0_5].as_slice(), epsilon = 1e-4);
}
unsafe fn calcfc_cstr(
_n: ::std::os::raw::c_long,
_m: ::std::os::raw::c_long,
x: *const f64,
con: *mut f64,
_data: *mut ::std::os::raw::c_void,
) -> f64 {
let r1 = *x.offset(0);
let r2 = *x.offset(1);
let fc = -r1 - r2;
*con.offset(0) = r2 - r1 * r1;
*con.offset(1) = 1.0 - r1 * r1 - r2 * r2;
fc
}
#[test]
fn test_cobyla_cstr() {
let n = 2;
let m = 2;
let mut x = vec![1.0, 1.0];
let rhobeg = 0.5;
let rhoend = 1e-4;
let iprint = 0;
let mut maxfun = 2000;
let mut w: Vec<_> = vec![0.; 3000];
let mut iact: Vec<_> = vec![0; 51];
let null = std::ptr::null_mut::<c_void>();
unsafe {
raw_cobyla(
n,
m,
Some(calcfc_cstr),
null,
x.as_mut_ptr(),
rhobeg,
rhoend,
iprint,
&mut maxfun,
w.as_mut_ptr(),
iact.as_mut_ptr(),
);
}
let sqrt_0_5: f64 = 0.5_f64.sqrt();
assert_abs_diff_eq!(
x.as_slice(),
[sqrt_0_5, sqrt_0_5].as_slice(),
epsilon = 1e-4
);
}
}