time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

halley_iterate.js (6068B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2018 The Stdlib Authors.
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *    http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 *
     18 *
     19 * ## Notice
     20 *
     21 * The original C++ code and copyright notice are from the [Boost library]{@link http://www.boost.org/doc/libs/1_62_0/boost/math/tools/roots.hpp}. The implementation has been modified for JavaScript.
     22 *
     23 * ```text
     24 * Copyright John Maddock 2006.
     25 *
     26 * Use, modification and distribution are subject to the
     27 * Boost Software License, Version 1.0. (See accompanying file
     28 * LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
     29 * ```
     30 */
     31 
     32 'use strict';
     33 
     34 // MODULES //
     35 
     36 var abs = require( './../../../../base/special/abs' );
     37 var ldexp = require( './../../../../base/special/ldexp' );
     38 var sign = require( './../../../../base/special/signum' );
     39 var max = require( './../../../../base/special/max' );
     40 var MAX_VALUE = require( '@stdlib/constants/float64/max' );
     41 
     42 
     43 // MAIN //
     44 
     45 /**
     46 * Performs root finding via third order Halley iteration.
     47 *
     48 * @private
     49 * @param {Array} fun - array of function and its first two derivatives
     50 * @param {number} guess - initial starting value
     51 * @param {number} minimum - minimum possible value for the result, used as initial lower bracket
     52 * @param {number} maximum - maximum possible value for the result, used as initial upper bracket
     53 * @param {PositiveInteger} digits - desired number of binary digits
     54 * @param {PositiveInteger} maxIter - maximum number of iterations
     55 * @returns {number} function value
     56 */
     57 function halleyIterate( fun, guess, minimum, maximum, digits, maxIter ) {
     58 	var convergence;
     59 	var outOfBounds;
     60 	var delta1;
     61 	var delta2;
     62 	var factor;
     63 	var result;
     64 	var f0Last;
     65 	var count;
     66 	var delta;
     67 	var denom;
     68 	var diff;
     69 	var num;
     70 	var res;
     71 	var f0;
     72 	var f1;
     73 	var f2;
     74 
     75 	f0 = 0.0;
     76 	outOfBounds = false;
     77 	result = guess;
     78 	factor = ldexp( 1.0, 1.0-digits );
     79 	delta = max( 10000000*guess, 10000000 );  // Arbitrarily large delta...
     80 	f0Last = 0;
     81 	delta1 = delta;
     82 	delta2 = delta;
     83 
     84 	count = maxIter;
     85 	do {
     86 		f0Last = f0;
     87 		delta2 = delta1;
     88 		delta1 = delta;
     89 		res = fun( result);
     90 		f0 = res[ 0 ];
     91 		f1 = res[ 1 ];
     92 		f2 = res[ 2 ];
     93 		count -= 1;
     94 
     95 		if ( f0 === 0.0 ) {
     96 			break;
     97 		}
     98 		if ( f1 === 0.0 ) {
     99 			// Oops zero derivative!!!
    100 			if ( f0Last === 0.0 ) {
    101 				// Must be the first iteration, pretend that we had a previous one at either min or max:
    102 				if ( result === minimum ) {
    103 					guess = maximum;
    104 				} else {
    105 					guess = minimum;
    106 				}
    107 				f0Last = fun( guess );
    108 				delta = guess - result;
    109 			}
    110 			if ( sign( f0Last ) * sign( f0 ) < 0 ) {
    111 				// We've crossed over so move in opposite direction to last step:
    112 				if ( delta < 0 ) {
    113 					delta = ( result-minimum ) / 2.0;
    114 				} else {
    115 					delta = ( result-maximum ) / 2.0;
    116 				}
    117 			// Move in same direction as last step:
    118 			} else if ( delta < 0 ) {
    119 				delta = (result-maximum) / 2.0;
    120 			} else {
    121 				delta = (result-minimum) / 2.0;
    122 			}
    123 		} else if ( f2 === 0.0 ) {
    124 			delta = f0 / f1;
    125 		} else {
    126 			denom = 2.0 * f0;
    127 			num = ( 2.0 * f1 ) - ( f0 * ( f2 / f1 ) );
    128 			if ( abs(num) < 1.0 && ( abs(denom) >= abs(num) * MAX_VALUE ) ) {
    129 				// Possible overflow, use Newton step:
    130 				delta = f0 / f1;
    131 			} else {
    132 				delta = denom / num;
    133 			}
    134 			if ( delta * f1 / f0 < 0.0 ) {
    135 				// Probably cancellation error, try a Newton step instead:
    136 				delta = f0 / f1;
    137 				if ( abs(delta) > 2.0 * abs(guess) ) {
    138 					delta = ( (delta < 0.0) ? -1.0 : 1.0 ) * 2.0 * abs( guess );
    139 				}
    140 			}
    141 		}
    142 		convergence = abs( delta / delta2 );
    143 		if ( convergence > 0.8 && convergence < 2.0 ) {
    144 			// Last two steps haven't converged, try bisection:
    145 			delta = ( delta > 0.0 ) ? ( result-minimum )/2.0 : ( result-maximum )/2.0; // eslint-disable-line max-len
    146 			if ( abs(delta) > result ) {
    147 				delta = sign( delta ) * result; // Protect against huge jumps!
    148 			}
    149 			// Reset delta2 so that this branch will *not* be taken on the next iteration:
    150 			delta2 = delta * 3.0;
    151 		}
    152 		guess = result;
    153 		result -= delta;
    154 
    155 		// Check for out of bounds step:
    156 		if ( result < minimum ) {
    157 			if (
    158 				abs(minimum) < 1 &&
    159 				abs(result) > 1 &&
    160 				( MAX_VALUE / abs(result) < abs(minimum) )
    161 			) {
    162 				diff = 1000.0;
    163 			} else {
    164 				diff = result / minimum;
    165 			}
    166 			if ( abs(diff) < 1.0 ) {
    167 				diff = 1.0 / diff;
    168 			}
    169 			if ( !outOfBounds && diff > 0.0 && diff < 3.0 ) {
    170 				// Only a small out of bounds step, let's assume that the result is probably approximately at minimum:
    171 				delta = 0.99 * (guess - minimum);
    172 				result = guess - delta;
    173 				outOfBounds = true; // Only take this branch once!
    174 			} else {
    175 				delta = (guess - minimum) / 2.0;
    176 				result = guess - delta;
    177 				if ( result === minimum || result === maximum ) {
    178 					break;
    179 				}
    180 			}
    181 		} else if ( result > maximum ) {
    182 			if (
    183 				abs(maximum) < 1.0 &&
    184 				abs(result) > 1.0 &&
    185 				MAX_VALUE / abs(result) < abs(maximum)
    186 			) {
    187 				diff = 1000.0;
    188 			} else {
    189 				diff = result / maximum;
    190 			}
    191 			if ( abs(diff) < 1.0 ) {
    192 				diff = 1.0 / diff;
    193 			}
    194 			if ( !outOfBounds && diff > 0.0 && diff < 3.0 ) {
    195 				// Only a small out of bounds step, let's assume that the result is probably approximately at minimum:
    196 				delta = 0.99 * (guess - maximum);
    197 				result = guess - delta;
    198 				outOfBounds = true; // Only take this branch once!
    199 			} else {
    200 				delta = ( guess - maximum ) / 2.0;
    201 				result = guess - delta;
    202 				if ( result === minimum || result === maximum ) {
    203 					break;
    204 				}
    205 			}
    206 		}
    207 		// Update brackets:
    208 		if ( delta > 0.0 ) {
    209 			maximum = guess;
    210 		} else {
    211 			minimum = guess;
    212 		}
    213 	} while ( count && ( abs(result * factor) < abs(delta) ) );
    214 
    215 	return result;
    216 }
    217 
    218 
    219 // EXPORTS //
    220 
    221 module.exports = halleyIterate;