use ffi;
use libc::{c_void, malloc, free};
use std::intrinsics::{fabsf64, sqrtf64};
use num::Float;
static REL_ERR_VAL : f64 = 1.0e-06f64;
static ABS_ERR_VAL : f64 = 1.0e-10f64;
static GOLDEN_MEAN : f64 = 0.3819660112501052f64;
fn safe_func_call<T>(f: ::function<T>, arg: &mut T, x: f64, yp: &mut f64) {
*yp = f(x, arg);
if !yp.is_finite() {
rgsl_error!("computed function value is infinite or NaN", ::Value::BadFunc);
}
}
fn compute_f_values<T>(f: ::function<T>, arg: &mut T, x_minimum: f64, f_minimum: &mut f64, x_lower: f64, f_lower: &mut f64,
x_upper: f64, f_upper: &mut f64) -> ::Value {
safe_func_call(f, arg, x_lower, f_lower);
safe_func_call(f, arg, x_upper, f_upper);
safe_func_call(f, arg, x_minimum, f_minimum);
::Value::Success
}
pub struct Minimizer<T> {
type_: MinimizerType<T>,
function: Option<::function<T>>,
arg: Option<*mut c_void>,
x_minimum: f64,
x_lower: f64,
x_upper: f64,
f_minimum: f64,
f_lower: f64,
f_upper: f64,
state: *mut c_void
}
impl<T> Minimizer<T> {
pub fn new(t: &MinimizerType<T>) -> Option<Minimizer<T>> {
let state = unsafe { malloc(t.size) };
if state.is_null() {
None
} else {
Some(Minimizer {
type_: t.clone(),
function: None,
arg: None,
x_minimum: 0f64,
x_lower: 0f64,
x_upper: 0f64,
f_minimum: 0f64,
f_lower: 0f64,
f_upper: 0f64,
state: state
})
}
}
pub fn set(&mut self, f: ::function<T>, arg: &mut T, x_minimum: f64, x_lower: f64, x_upper: f64) -> ::Value {
let mut f_minimum = 0f64;
let mut f_lower = 0f64;
let mut f_upper = 0f64;
let status = compute_f_values(f, arg, x_minimum, &mut f_minimum, x_lower, &mut f_lower, x_upper, &mut f_upper);
if status != ::Value::Success {
status
} else {
self.set_with_values(f, arg, x_minimum, f_minimum, x_lower, f_lower, x_upper, f_upper)
}
}
pub fn set_with_values(&mut self, f: ::function<T>, arg: &mut T, x_minimum: f64, f_minimum: f64, x_lower: f64, f_lower: f64,
x_upper: f64, f_upper: f64) -> ::Value {
self.function = Some(f);
self.arg = unsafe { Some(::std::mem::transmute(arg)) };
self.x_minimum = x_minimum;
self.x_lower = x_lower;
self.x_upper = x_upper;
if x_lower > x_upper {
rgsl_error!("invalid interval (lower > upper)", ::Value::Inval);
}
if x_minimum >= x_upper || x_minimum <= x_lower {
rgsl_error!("x_minimum must lie inside interval (lower < x < upper)", ::Value::Inval);
}
self.f_upper = f_upper;
self.f_minimum = f_minimum;
self.f_lower = f_lower;
if f_minimum >= f_lower || f_minimum >= f_upper {
rgsl_error!("endpoints do not enclose a minimum", ::Value::Inval);
}
unsafe {
(self.type_.set)(self.state, self.function.unwrap(), ::std::mem::transmute(self.arg.unwrap()), x_minimum, f_minimum, x_lower,
f_lower, x_upper, f_upper)
}
}
pub fn name(&self) -> String {
self.type_.name.clone()
}
pub fn x_minimum(&self) -> f64 {
self.x_minimum
}
pub fn x_lower(&self) -> f64 {
self.x_lower
}
pub fn x_upper(&self) -> f64 {
self.x_upper
}
pub fn f_minimum(&self) -> f64 {
self.f_minimum
}
pub fn f_lower(&self) -> f64 {
self.f_lower
}
pub fn f_upper(&self) -> f64 {
self.f_upper
}
pub fn iterate(&mut self) -> ::Value {
unsafe {
(self.type_.iterate)(self.state, self.function.unwrap(), ::std::mem::transmute(self.arg.unwrap()), &mut self.x_minimum,
&mut self.f_minimum, &mut self.x_lower, &mut self.f_lower, &mut self.x_upper, &mut self.f_upper)
}
}
}
impl<T> Drop for Minimizer<T> {
fn drop(&mut self) {
unsafe { free(self.state) };
self.state = ::std::ptr::null_mut();
}
}
pub struct MinimizerType<T> {
pub name: String,
size: u64,
set: fn(state: *mut c_void, f: ::function<T>, arg: &mut T, x_minimum: f64, f_minimum: f64, x_lower: f64, f_lower: f64, x_upper: f64,
f_upper: f64) -> ::Value,
iterate: fn(state: *mut c_void, f: ::function<T>, arg: &mut T, x_minimum: &mut f64, f_minimum: &mut f64, x_lower: &mut f64,
f_lower: &mut f64, x_upper: &mut f64, f_upper: &mut f64) -> ::Value
}
impl<T> MinimizerType<T> {
pub fn golden_section() -> MinimizerType<T> {
MinimizerType {
name: "goldensection".to_string(),
size: ::std::mem::size_of::<goldensection_state_t>() as u64,
set: goldensection_init,
iterate: goldensection_iterate
}
}
pub fn brent() -> MinimizerType<T> {
MinimizerType {
name: "brent".to_string(),
size: ::std::mem::size_of::<brent_state_t>() as u64,
set: brent_init,
iterate: brent_iterate
}
}
pub fn quad_golden() -> MinimizerType<T> {
MinimizerType {
name: "quad-golden".to_string(),
size: ::std::mem::size_of::<quad_golden_state_t>() as u64,
set: quad_golden_init,
iterate: quad_golden_iterate
}
}
}
impl<T> Clone for MinimizerType<T> {
fn clone(&self) -> MinimizerType<T> {
MinimizerType {
name: self.name.clone(),
size: self.size,
set: self.set,
iterate: self.iterate
}
}
}
struct goldensection_state_t {
dummy: f64
}
#[allow(unused_variables)]
fn goldensection_init<T>(vstate: *mut c_void, f: ::function<T>, arg: &mut T, x_minimum: f64, f_minimum: f64, x_lower: f64, f_lower: f64,
x_upper: f64, f_upper: f64) -> ::Value {
let state : &mut goldensection_state_t = unsafe { ::std::mem::transmute(vstate) };
state.dummy = 0f64;
::Value::Success
}
#[allow(unused_variables)]
fn goldensection_iterate<T>(vstate: *mut c_void, f: ::function<T>, arg: &mut T, x_minimum: &mut f64, f_minimum: &mut f64, x_lower: &mut f64,
f_lower: &mut f64, x_upper: &mut f64, f_upper: &mut f64) -> ::Value {
let x_center = *x_minimum;
let x_left = *x_lower;
let x_right = *x_upper;
let f_min = *f_minimum;
let golden = 0.3819660f64;
let w_lower = x_center - x_left;
let w_upper = x_right - x_center;
let mut f_new = 0f64;
let x_new = x_center + golden * if w_upper > w_lower { w_upper } else { -w_lower };
safe_func_call(f, arg, x_new, &mut f_new);
if f_new < f_min {
*x_minimum = x_new;
*f_minimum = f_new;
::Value::Success
} else if x_new < x_center && f_new > f_min {
*x_lower = x_new;
*f_lower = f_new;
::Value::Success
} else if x_new > x_center && f_new > f_min {
*x_upper = x_new;
*f_upper = f_new;
::Value::Success
} else {
::Value::Failure
}
}
struct brent_state_t {
d: f64,
e: f64,
v: f64,
w: f64,
f_v: f64,
f_w: f64
}
#[allow(unused_variables)]
fn brent_init<T>(vstate: *mut c_void, f: ::function<T>, arg: &mut T, x_minimum: f64, f_minimum: f64, x_lower: f64, f_lower: f64, x_upper: f64,
f_upper: f64) -> ::Value {
let state : &mut brent_state_t = unsafe { ::std::mem::transmute(vstate) };
let golden = 0.3819660f64;
state.v = x_lower + golden * (x_upper - x_lower);
state.w = state.v;
let mut f_vw = 0f64;
state.d = 0f64;
state.e = 0f64;
safe_func_call(f, arg, state.v, &mut f_vw);
state.f_v = f_vw;
state.f_w = f_vw;
::Value::Success
}
#[allow(unused_assignments)]
fn brent_iterate<T>(vstate: *mut c_void, f: ::function<T>, arg: &mut T, x_minimum: &mut f64, f_minimum: &mut f64, x_lower: &mut f64,
f_lower: &mut f64, x_upper: &mut f64, f_upper: &mut f64) -> ::Value {
unsafe {
let state : &mut brent_state_t = ::std::mem::transmute(vstate);
let x_left = *x_lower;
let x_right = *x_upper;
let z = *x_minimum;
let mut d = state.e;
let mut e = state.d;
let mut u = 0f64;
let mut f_u = 0f64;
let v = state.v;
let w = state.w;
let f_v = state.f_v;
let f_w = state.f_w;
let f_z = *f_minimum;
let golden = 0.3819660f64;
let w_lower = z - x_left;
let w_upper = x_right - z;
let tolerance = ::SQRT_DBL_EPSILON * fabsf64(z);
let mut p = 0f64;
let mut q = 0f64;
let mut r = 0f64;
let midpoint = 0.5f64 * (x_left + x_right);
if fabsf64(e) > tolerance {
r = (z - w) * (f_z - f_v);
q = (z - v) * (f_z - f_w);
p = (z - v) * q - (z - w) * r;
q = 2f64 * (q - r);
if q > 0f64 {
p = -p;
} else {
q = -q;
}
r = e;
e = d;
}
if fabsf64(p) < fabsf64(0.5f64 * q * r) && p < q * w_lower && p < q * w_upper {
let t2 = 2f64 * tolerance;
d = p / q;
u = z + d;
if (u - x_left) < t2 || (x_right - u) < t2 {
d = if z < midpoint { tolerance } else { -tolerance };
}
} else {
e = if z < midpoint { x_right - z } else { -(z - x_left) };
d = golden * e;
}
if fabsf64(d) >= tolerance {
u = z + d;
} else {
u = z + if d > 0f64 { tolerance } else { -tolerance };
}
state.e = e;
state.d = d;
safe_func_call(f, arg, u, &mut f_u);
if f_u <= f_z {
if u < z {
*x_upper = z;
*f_upper = f_z;
} else {
*x_lower = z;
*f_lower = f_z;
}
state.v = w;
state.f_v = f_w;
state.w = z;
state.f_w = f_z;
*x_minimum = u;
*f_minimum = f_u;
::Value::Success
} else {
if u < z {
*x_lower = u;
*f_lower = f_u;
} else {
*x_upper = u;
*f_upper = f_u;
}
if f_u <= f_w || w == z {
state.v = w;
state.f_v = f_w;
state.w = u;
state.f_w = f_u;
::Value::Success
} else if f_u <= f_v || v == z || v == w {
state.v = u;
state.f_v = f_u;
::Value::Success
} else {
::Value::Success
}
}
}
}
struct quad_golden_state_t {
step_size: f64,
stored_step: f64,
prev_stored_step: f64,
x_prev_small: f64,
f_prev_small: f64,
x_small: f64,
f_small: f64,
num_iter: i32
}
#[allow(unused_variables)]
fn quad_golden_init<T>(vstate: *mut c_void, f: ::function<T>, arg: &mut T, x_minimum: f64, f_minimum: f64, x_lower: f64, f_lower: f64, x_upper: f64,
f_upper: f64) -> ::Value {
let state : &mut quad_golden_state_t = unsafe { ::std::mem::transmute(vstate) };
state.x_prev_small = x_minimum;
state.x_small = x_minimum;
state.f_prev_small = f_minimum;
state.f_small = f_minimum;
state.step_size = 0f64;
state.stored_step = 0f64;
state.prev_stored_step = 0f64;
state.num_iter = 0;
::Value::Success
}
#[allow(unused_assignments)]
fn quad_golden_iterate<T>(vstate: *mut c_void, f: ::function<T>, arg: &mut T, x_minimum: &mut f64, f_minimum: &mut f64, x_lower: &mut f64,
f_lower: &mut f64, x_upper: &mut f64, f_upper: &mut f64) -> ::Value {
unsafe {
let state : &mut quad_golden_state_t = ::std::mem::transmute(vstate);
let x_m = *x_minimum;
let f_m = *f_minimum;
let x_l = *x_lower;
let x_u = *x_upper;
let x_small = state.x_small;
let f_small = state.f_small;
let x_prev_small = state.x_prev_small;
let f_prev_small = state.f_prev_small;
let mut stored_step = state.stored_step;
let mut prev_stored_step = state.prev_stored_step;
let mut step_size = state.step_size;
let mut quad_step_size = prev_stored_step;
let mut f_eval = 0f64;
let x_midpoint = 0.5f64 * (x_l + x_u);
let tol = REL_ERR_VAL * fabsf64(x_m) + ABS_ERR_VAL;
if fabsf64(stored_step) - tol > -2.0f64 * ::DBL_EPSILON {
let c3 = (x_m - x_small) * (f_m - f_prev_small);
let mut c2 = (x_m - x_prev_small) * (f_m - f_small);
let mut c1 = (x_m - x_prev_small) * c2 - (x_m - x_small) * c3;
c2 = 2f64 * (c2 - c3);
if fabsf64(c2) > ::DBL_EPSILON {
if c2 > 0f64 {
c1 = -c1;
}
c2 = fabsf64(c2);
quad_step_size = c1 / c2;
} else {
quad_step_size = stored_step;
}
prev_stored_step = stored_step;
stored_step = step_size;
}
let x_trial = x_m + quad_step_size;
if fabsf64(quad_step_size) < fabsf64(0.5f64 * prev_stored_step) && x_trial > x_l && x_trial < x_u {
step_size = quad_step_size;
if (x_trial - x_l) < 2.0 * tol || (x_u - x_trial) < 2f64 * tol {
step_size = if x_midpoint >= x_m { 1f64 } else { -1f64 } * fabsf64(tol);
}
}
else if (x_small != x_prev_small && x_small < x_m && x_prev_small < x_m) ||
(x_small != x_prev_small && x_small > x_m && x_prev_small > x_m) {
let mut outside_interval = 0f64;
let mut inside_interval = 0f64;
if x_small < x_m {
outside_interval = x_l - x_m;
inside_interval = x_u - x_m;
} else {
outside_interval = x_u - x_m;
inside_interval = x_l - x_m;
}
if fabsf64(inside_interval) <= tol {
let tmp = outside_interval;
outside_interval = inside_interval;
inside_interval = tmp;
}
{
let step = inside_interval;
let scale_factor;
if fabsf64(outside_interval) < fabsf64(inside_interval) {
scale_factor = 0.5f64 * sqrtf64(-outside_interval / inside_interval);
} else {
scale_factor = (5f64 / 11f64) * (0.1f64 - inside_interval / outside_interval);
}
state.stored_step = step;
step_size = scale_factor * step;
}
} else {
let step = if x_m < x_midpoint {
x_u - x_m
} else {
x_l - x_m
};
state.stored_step = step;
step_size = GOLDEN_MEAN * step;
}
let x_eval = if fabsf64(step_size) > tol {
x_m + step_size
} else {
x_m + if step_size >= 0f64 { 1f64 } else { -1f64 } * fabsf64(tol)
};
safe_func_call(f, arg, x_eval, &mut f_eval);
if f_eval <= f_m {
if x_eval < x_m {
*x_upper = x_m;
*f_upper = f_m;
} else {
*x_lower = x_m;
*f_upper = f_m;
}
state.x_prev_small = x_small;
state.f_prev_small = f_small;
state.x_small = x_m;
state.f_small = f_m;
*x_minimum = x_eval;
*f_minimum = f_eval;
} else {
if x_eval < x_m {
*x_lower = x_eval;
*f_lower = f_eval;
} else {
*x_upper = x_eval;
*f_upper = f_eval;
}
if f_eval <= f_small || fabsf64(x_small - x_m) < 2f64 * ::DBL_EPSILON {
state.x_prev_small = x_small;
state.f_prev_small = f_small;
state.x_small = x_eval;
state.f_small = f_eval;
} else if f_eval <= f_prev_small || fabsf64(x_prev_small - x_m) < 2f64 * ::DBL_EPSILON ||
fabsf64(x_prev_small - x_small) < 2f64 * ::DBL_EPSILON {
state.x_prev_small = x_eval;
state.f_prev_small = f_eval;
}
}
state.stored_step = stored_step;
state.prev_stored_step = prev_stored_step;
state.step_size = step_size;
state.num_iter += 1;
::Value::Success
}
}