brent-local-min-generator
Version:
Brent's local minimum finding algorithm, as a generator function.
137 lines (123 loc) • 3.49 kB
JavaScript
/**
* 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,
};