UNPKG

brent-local-min-generator

Version:

Brent's local minimum finding algorithm, as a generator function.

137 lines (123 loc) 3.49 kB
/** * local_min() finds an approximation x to the point at which f(x) * attains its minimum (or the appropriate limit point), and returns * the value of f() at x. * @param {number} a - The lower value used to bracket root finding into the function. * @param {number} b - The upper value used to bracket root finding into the function. * @param {number} tolerance - Used to determine how close the bounds bracketing * root finding must converge before quitting. (default: 0.0) * @param {number=} epsilon - The epsilon (default: Number.EPSILON) * @returns Returns a input value resulting in a suitable root if successful. Returns false on failure. */ function* local_min_generator(a, b, t=0.0, eps=2.0*Number.EPSILON) { const c = (3.0 - Math.sqrt(5.0)) / 2.0; let x = a + c * (b - a); let w = x; let v = w; let e = 0.0; let fx = yield x; let fw = fx; let fv = fw; // Main loop let d, u; for (;;) { // loop: const m = 0.5 * (a + b); const tol = eps * Math.abs(x) + t; const t2 = 2.0 * tol; // Check stopping criterion if (Math.abs(x - m) <= t2 - 0.5 * (b - a)) return {x, fx}; let r = 0.; let q = r; let p = q; if (Math.abs(e) > tol) { r = (x - w) * (fx - fv); q = (x - v) * (fx - fw); p = (x - v) * q - (x - w) * r; q = 2.0 * (q - r); if (q > 0.0) p = -p; else q = -q; r = e; e = d; } // the Algol 60 version in all printed versions is wrong, // the code is like the FORTRAN version if ( Math.abs(p) < Math.abs(0.5 * q * r) && p > q * (a - x) && p < q * (b - x)) { // A "parabolic interpolation" step d = p / q; u = x + d; // f must not be evaluated too close to a or b if (u - a < t2 || b - u < t2) d = (x < m ? tol : -tol); } else { // A "golden section" step e = (x < m ? b : a) - x; d = c * e; } // f must not be evaluated too close to x u = x + (Math.abs(d) >= tol ? d : (d > 0.0 ? tol : -tol)); const fu = yield u; // Update a, b, v, w, and x if (fu <= fx) { if (u < x) b = x; else a = x; v = w; fv = fw; w = x; fw = fx; x = u; fx = fu; } else { if (u < x) a = u; else b = u; if (fu <= fw || w == x) { v = w; fv = fw; w = u; fw = fu; } else { if (fu <= fv || v == x || v == w) { v = u; fv = fu; } } } } } // local_min_generator /** * local_min() finds an approximation x to the point at which f(x) * attains its minimum (or the appropriate limit point), and returns * the value of f() at x. * @param {(x:number)=>number} f - The function for which roots are desired. * @param {number} a - The lower value used to bracket root finding into the function. * @param {number} b - The upper value used to bracket root finding into the function. * @param {number} tolerance - Used to determine how close the bounds bracketing * root finding must converge before quitting. (default: 0.0) * @param {number=} epsilon - The epsilon (default: Number.EPSILON) * @returns Returns a input value resulting in a suitable root if successful. Returns false on failure. */ function local_min(f, a, b, tolerance=0.0, epsilon=Number.EPSILON) { const gen = local_min_generator(a, b, tolerance, epsilon); let result = gen.next(); while ( ! result.done ) { const x = result.value; const y = f(x); result = gen.next(y) } return result.value; } module.exports = { local_min_generator, local_min, };