// Revelations:
//  1. In common implementations of the algorithm (such as those on wikipedia)
//     the values of the function at the endpoints are never used.
//     Hence **it is only necessary to save one y value.**
//     However, we save more because we don't trust the function's accuracy.
//  2. TECHNICALLY the step function doesn't even even need to use phi;
//      one could record 'b' and derive the second endpoint as 'c = d - b + a'.
//     But I don't know if that is numerically stable, so we will do what
//     the wikipedia implementations do and recompute b and c every iter.
1. In common implementations of the algorithm (such as those on wikipedia)
the values of the function at the endpoints are never used.
Hence **it is only necessary to save one y value.
However, we save more because we don't trust the function's accuracy.
2.
1 . In common implementations of the algorithm ( such as those on wikipedia ) the values of the function at the endpoints are never used . Hence ** it is only necessary to save one y value . However we save more because we don ' t trust the function ' s accuracy . 2 .
pub fn run<E, F>(
        &self,
        interval: (f64, f64),
        mut compute: F,
    // NOTE: cannot return a bound due to issue mentioned in body
    ) -> Result<Result<f64, E>, GoldenSearchError>
    where F: FnMut(f64) -> Result<Value, E>
    {
        nest_err(|| {
            // early wrapping:
            //  - ValueBound for internal use
            //  - Result<Value, Result<TheirError, OurError>> for easy short-circuiting
            let mut compute = move |alpha| {
                let value = compute(alpha).map_err(Ok)?;
                if !value.0.is_finite() {
                    return Err(Err(ErrorKind::FunctionOutput(value.0).into()));
                }
                trace!("GS-iter:  a: {:<23e}  v: {:<23e}", alpha, value.0);
                Ok(ValueBound { alpha, value: value.0 })
            };
            let phi: f64 = (1.0 + 5f64.sqrt()) / 2.0;
            let get_mid_xs = |a, d| {
                let dist = (d - a) / (1.0 + phi);
                (a + dist, d - dist)
            };
            let (mut state, mut history) = {
                // endpoints. (note: we allow d.alpha < a.alpha)
                let a = compute(interval.0)?;
                let d = compute(interval.1)?;
                // inner point closer to a
                let b = compute(get_mid_xs(a.alpha, d.alpha).0)?;
                let history = vec![a, d, b];
                ((a, b, d), history)
            };
            let mut iterations = 0;
            let stop_reason = loop {
                // Golden search will usually stop long before this,
                // after squeezing every last bit out of the f64 mantissa.
                if iterations > 300 { panic!("GS never stopped!"); }
                let (a, mut b, d) = state;
                // user-supplied stop condition
                {
                    use crate::stop_condition::ShouldStop;
                    let objectives = golden::Objectives {
                        alphas: (a.alpha, b.alpha, d.alpha),
                        iterations: iterations,
                    };
                    if self.stop_condition.should_stop(&objectives) {
                        break "met user-supplied stop condition";
                    }
                }
                // Forcibly stop when it is obvious that the value is no longer numerically reliable.
                // (our interval looks like it has the wrong curvature)
                if b.value > a.value.max(d.value) {
                    break "noise > signal, or wrong curvature";
                }
                // re-adjust b, purportedly to avoid systematic issues with precision
                // that can cause infinite loops. (I dunno. ask whoever edits wikipedia)
                //
                // NOTE: Technically this desynchronizes the alpha of our Bounds from
                //  the values, so at the end we cannot return a bound.
                let (b_alpha, c_alpha) = get_mid_xs(a.alpha, d.alpha);
                b.alpha = b_alpha;
                // Forcibly stop if the interval has shrunk to nothing.
                {
                    let mut test = false;
                    // Has b crossed c?
                    test = test || (c_alpha - b.alpha) * (d.alpha - a.alpha) <= 0.0;
                    // Are any two alphas equal?
                    test = test || a.alpha == b_alpha;
                    test = test || c_alpha == d.alpha;
                    if test {
                        break "empty interval";
                    }
                }
                let c = compute(c_alpha)?;
                history.push(c);
                state = match b.value < c.value {
                    true => (c, b, a),
                    false => (b, c, d),
                };
                iterations += 1;
            }; // let stop_reason = { ... }
            //history.sort_on_key(|bound| NotNan::new(bound.alpha).unwrap());
            let (_a, b, _d) = state;
            trace!("GS-stop:  a: {:<23e}  ({})", b.alpha, stop_reason);
            Ok(b.alpha)
        })
    }
pub fn run < E , F > ( & self , interval : ( f64 , f64 ) , mut compute : F , // NOTE: cannot return a bound due to issue mentioned in body ) -> Result < Result < f64 , E > , GoldenSearchError > where F : FnMut ( f64 ) -> Result < Value , E > { nest_err ( | | { // early wrapping: //  - ValueBound for internal use //  - Result<Value, Result<TheirError, OurError>> for easy short-circuiting let mut compute = move | alpha | { let value = compute ( alpha ) . map_err ( Ok ) ? ; if ! value . 0 . is_finite ( ) { return Err ( Err ( ErrorKind :: FunctionOutput ( value . 0 ) . into ( ) ) ) ; } trace ! ( " " alpha value 0 ) ; Ok ( ValueBound { alpha , value : value . 0 } ) } ; let phi : f64 = ( 1.0 + 5f64 . sqrt ( ) ) / 2.0 ; let get_mid_xs = | a , d | { let dist = ( d - a ) / ( 1.0 + phi ) ; ( a + dist , d - dist ) } ; let ( mut state , mut history ) = { // endpoints. (note: we allow d.alpha < a.alpha) let a = compute ( interval . 0 ) ? ; let d = compute ( interval . 1 ) ? ; // inner point closer to a let b = compute ( get_mid_xs ( a . alpha , d . alpha ) . 0 ) ? ; let history = vec ! [ a d b ] ; ( ( a , b , d ) , history ) } ; let mut iterations = 0 ; let stop_reason = loop { // Golden search will usually stop long before this, // after squeezing every last bit out of the f64 mantissa. if iterations > 300 { panic ! ( " " ) ; } let ( a , mut b , d ) = state ; // user-supplied stop condition { use crate :: stop_condition :: ShouldStop ; let objectives = golden :: Objectives { alphas : ( a . alpha , b . alpha , d . alpha ) , iterations : iterations , } ; if self . stop_condition . should_stop ( & objectives ) { break " " ; } } // Forcibly stop when it is obvious that the value is no longer numerically reliable. // (our interval looks like it has the wrong curvature) if b . value > a . value . max ( d . value ) { break " " ; } // re-adjust b, purportedly to avoid systematic issues with precision // that can cause infinite loops. (I dunno. ask whoever edits wikipedia) // // NOTE: Technically this desynchronizes the alpha of our Bounds from //  the values, so at the end we cannot return a bound. let ( b_alpha , c_alpha ) = get_mid_xs ( a . alpha , d . alpha ) ; b . alpha = b_alpha ; // Forcibly stop if the interval has shrunk to nothing. { let mut test = false ; // Has b crossed c? test = test || ( c_alpha - b . alpha ) * ( d . alpha - a . alpha ) <= 0.0 ; // Are any two alphas equal? test = test || a . alpha == b_alpha ; test = test || c_alpha == d . alpha ; if test { break " " ; } } let c = compute ( c_alpha ) ? ; history . push ( c ) ; state = match b . value < c . value { true => ( c , b , a ) , false => ( b , c , d ) , } ; iterations += 1 ; } ; // let stop_reason = { ... } //history.sort_on_key(|bound| NotNan::new(bound.alpha).unwrap()); let ( _a , b , _d ) = state ; trace ! ( " " b alpha stop_reason ) ; Ok ( b . alpha ) } ) }
Revelations:
1.
Revelations : 1 .
&self
interval
(f64, f64)
compute
F