|
|
|
@ -11,167 +11,80 @@ namespace MathNet.Numerics.Optimization |
|
|
|
public class BrentOptions |
|
|
|
{ |
|
|
|
public int MaximumIterations = 1000; |
|
|
|
|
|
|
|
public double FunctionTolerance = 1e-4; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Result of Brent Minimization.
|
|
|
|
/// </summary>
|
|
|
|
public class BrentResult |
|
|
|
{ |
|
|
|
public int NumberOfIterations; |
|
|
|
public double MinimumPoint; |
|
|
|
public double MinimumFunctionValue; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Minimizes f(p) where p is a model parameter scalar, i.e. a line-search.
|
|
|
|
/// Inspired by the SciPy implementation.
|
|
|
|
/// </summary>
|
|
|
|
public class BrentMinimizer |
|
|
|
{ |
|
|
|
const double verySmallNumber = 1e-21, goldenRatio = 1.618034, minimumTolerance = 1.0e-11; |
|
|
|
const double growLimit = 110.0, conjugateGradient = 0.3819660; |
|
|
|
const int maxIterations = 500; |
|
|
|
|
|
|
|
int bracketFunctionCalls; |
|
|
|
public int FunctionCalls, Iterations; |
|
|
|
double minPoint, minFunction; |
|
|
|
Func<double, double> function; |
|
|
|
double pointA, pointB, pointC; |
|
|
|
double functionA, functionB, functionC; |
|
|
|
|
|
|
|
public double Tolerance { get; set; } |
|
|
|
|
|
|
|
public BrentMinimizer(Func<double, double> function) |
|
|
|
public struct Bracket |
|
|
|
{ |
|
|
|
this.function = function; |
|
|
|
Tolerance = 1e-4; |
|
|
|
public double PointA; |
|
|
|
public double PointB; |
|
|
|
public double PointC; |
|
|
|
public double FunctionA; |
|
|
|
public double FunctionB; |
|
|
|
public double FunctionC; |
|
|
|
} |
|
|
|
|
|
|
|
public BrentResult Result { get; private set; } |
|
|
|
|
|
|
|
public int Search(out double minPoint, out double minFunction) |
|
|
|
{ |
|
|
|
bracketFunctionCalls = 0; |
|
|
|
UpdateBracketInterval(0, 1); |
|
|
|
FunctionCalls = 0; |
|
|
|
Iterations = 0; |
|
|
|
int result = BrentMinimize(); |
|
|
|
FunctionCalls += bracketFunctionCalls; |
|
|
|
minPoint = this.minPoint; |
|
|
|
minFunction = this.minFunction; |
|
|
|
return result; |
|
|
|
} |
|
|
|
public readonly BrentOptions Options = new BrentOptions(); |
|
|
|
|
|
|
|
const double verySmallNumber = 1e-21, goldenRatio = 1.618034, minimumTolerance = 1.0e-11; |
|
|
|
const double growLimit = 110.0, conjugateGradient = 0.3819660; |
|
|
|
|
|
|
|
private int UpdateBracketInterval(double pointAStart, double pointBStart) |
|
|
|
/// <summary>
|
|
|
|
/// Find the minimum of the supplied function using the Brent method.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="function"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
public double Minimize(Func<double, double> function) |
|
|
|
{ |
|
|
|
Bracket bracket; |
|
|
|
UpdateBracketInterval(function, new Bracket() { PointA = 0, PointB = 1 }, out bracket); |
|
|
|
int iterations = 0; |
|
|
|
pointA = pointAStart; |
|
|
|
pointB = pointBStart; |
|
|
|
int maxIterations; |
|
|
|
maxIterations = 1000; |
|
|
|
functionA = function(pointA); |
|
|
|
functionB = function(pointB); |
|
|
|
double temp; |
|
|
|
if (functionA < functionB) // Swap points over
|
|
|
|
{ |
|
|
|
temp = functionA; functionA = functionB; functionB = temp; |
|
|
|
temp = pointA; pointA = pointB; pointB = temp; |
|
|
|
} |
|
|
|
pointC = pointB + goldenRatio * (pointB - pointA); |
|
|
|
functionC = function(pointC); |
|
|
|
bracketFunctionCalls = 3; iterations = 0; |
|
|
|
double temp1, temp2, value, denom; |
|
|
|
while (functionC < functionB) |
|
|
|
{ |
|
|
|
double pointW, functionW, wlim; |
|
|
|
temp1 = (pointB - pointA) * (functionB - functionC); |
|
|
|
temp2 = (pointB - pointC) * (functionB - functionA); |
|
|
|
value = temp2 - temp1; |
|
|
|
if (Math.Abs(value) < verySmallNumber) denom = 2.0 * verySmallNumber; |
|
|
|
else denom = 2.0 * value; |
|
|
|
pointW = pointB - ((pointB - pointC) * temp2 - (pointB - pointA) * temp1) / denom; |
|
|
|
wlim = pointB + growLimit * (pointC - pointB); |
|
|
|
if (iterations > maxIterations) return 1; |
|
|
|
iterations++; |
|
|
|
if ((pointW - pointC) * (pointB - pointW) > 0.0) |
|
|
|
{ |
|
|
|
functionW = function(pointW); |
|
|
|
bracketFunctionCalls++; |
|
|
|
if (functionW < functionC) |
|
|
|
{ |
|
|
|
pointA = pointB; pointB = pointW; |
|
|
|
functionA = functionB; functionB = functionW; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
else if (functionW > functionB) |
|
|
|
{ |
|
|
|
pointC = pointW; functionC = functionW; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
pointW = pointC + goldenRatio * (pointC - pointB); |
|
|
|
functionW = function(pointW); |
|
|
|
bracketFunctionCalls++; |
|
|
|
} |
|
|
|
else if ((pointW - wlim) * (wlim - pointC) >= 0.0) |
|
|
|
{ |
|
|
|
pointW = wlim; |
|
|
|
functionW = function(pointW); |
|
|
|
bracketFunctionCalls++; |
|
|
|
} |
|
|
|
else if ((pointW - wlim) * (pointC - pointW) > 0.0) |
|
|
|
{ |
|
|
|
functionW = function(pointW); |
|
|
|
bracketFunctionCalls++; |
|
|
|
if (functionW < functionC) |
|
|
|
{ |
|
|
|
pointB = pointC; pointC = pointW; |
|
|
|
pointW = pointC + goldenRatio * (pointC - pointB); |
|
|
|
functionB = functionC; functionC = functionW; |
|
|
|
functionW = function(pointW); |
|
|
|
bracketFunctionCalls++; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
pointW = pointC + goldenRatio * (pointC - pointB); |
|
|
|
functionW = function(pointW); |
|
|
|
bracketFunctionCalls++; |
|
|
|
} |
|
|
|
pointA = pointB; pointB = pointC; pointC = pointW; |
|
|
|
functionA = functionB; functionB = functionC; functionC = functionW; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
// Find the minimum of the function using the Brent method with the current
|
|
|
|
// bracketing interval.
|
|
|
|
private int BrentMinimize() |
|
|
|
{ |
|
|
|
int result = 0; |
|
|
|
double x, w, v, fx, fw, fv; |
|
|
|
double a, b, deltax, rat; |
|
|
|
double cg = conjugateGradient; |
|
|
|
x = w = v = pointB; // x is the point with lowest function value encountered
|
|
|
|
x = w = v = bracket.PointB; // x is the point with lowest function value encountered
|
|
|
|
fw = fv = fx = function(x); |
|
|
|
if (pointA < pointC) |
|
|
|
if (bracket.PointA < bracket.PointC) |
|
|
|
{ |
|
|
|
a = pointA; b = pointC; |
|
|
|
a = bracket.PointA; b = bracket.PointC; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
a = pointC; b = pointA; |
|
|
|
a = bracket.PointC; b = bracket.PointA; |
|
|
|
} |
|
|
|
deltax = 0.0; |
|
|
|
FunctionCalls = 1; |
|
|
|
Iterations = 0; |
|
|
|
rat = 0; |
|
|
|
while (Iterations < maxIterations) |
|
|
|
while (iterations < Options.MaximumIterations) |
|
|
|
{ |
|
|
|
double tol1, tol2, xmin, fval, xmid; |
|
|
|
double tol1, tol2, xmid; |
|
|
|
double temp1, temp2, p; |
|
|
|
double u, fu, dx_temp; |
|
|
|
tol1 = Tolerance * Math.Abs(x) + minimumTolerance; |
|
|
|
tol1 = Options.FunctionTolerance * Math.Abs(x) + minimumTolerance; |
|
|
|
tol2 = 2.0 * tol1; |
|
|
|
xmid = 0.5 * (a + b); |
|
|
|
if (Math.Abs(x - xmid) < (tol2 - 0.5 * (b - a))) // check for convergence
|
|
|
|
{ |
|
|
|
xmin = x; fval = fx; |
|
|
|
result = 1; |
|
|
|
if (Math.Abs(x - xmid) < (tol2 - 0.5 * (b - a))) // check for convergence
|
|
|
|
break; |
|
|
|
} |
|
|
|
if (Math.Abs(deltax) <= tol1) |
|
|
|
{ |
|
|
|
if (x >= xmid) deltax = a - x; // do a golden section step
|
|
|
|
if (x >= xmid) deltax = a - x; // do a golden section step
|
|
|
|
else deltax = b - x; |
|
|
|
rat = cg * deltax; |
|
|
|
} |
|
|
|
@ -215,7 +128,6 @@ namespace MathNet.Numerics.Optimization |
|
|
|
u = x + rat; |
|
|
|
} |
|
|
|
fu = function(u); |
|
|
|
FunctionCalls++; |
|
|
|
if (fu > fx) // if it's bigger than current
|
|
|
|
{ |
|
|
|
if (u < x) a = u; |
|
|
|
@ -236,70 +148,127 @@ namespace MathNet.Numerics.Optimization |
|
|
|
v = w; w = x; x = u; |
|
|
|
fv = fw; fw = fx; fx = fu; |
|
|
|
} |
|
|
|
Iterations++; |
|
|
|
iterations++; |
|
|
|
} |
|
|
|
this.minPoint = x; |
|
|
|
this.minFunction = fx; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Minimizes f(p u) where p is a model parameter scalar and u is a direction vector.
|
|
|
|
/// </summary>
|
|
|
|
public class MultiDimensionalBrent |
|
|
|
{ |
|
|
|
private Func<double[], double> function; |
|
|
|
private int functionCalls; |
|
|
|
double[] point; |
|
|
|
BrentMinimizer lineSearch; |
|
|
|
|
|
|
|
public MultiDimensionalBrent(Func<double[], double> function) |
|
|
|
{ |
|
|
|
this.function = function; |
|
|
|
lineSearch = new BrentMinimizer(this.PointAlongLine); |
|
|
|
functionCalls = 0; |
|
|
|
} |
|
|
|
|
|
|
|
public double Tolerance |
|
|
|
{ |
|
|
|
get { return lineSearch.Tolerance; } |
|
|
|
set { lineSearch.Tolerance = value; } |
|
|
|
this.Result = new BrentResult() { NumberOfIterations = iterations, MinimumPoint = x, MinimumFunctionValue = fx }; |
|
|
|
return x; |
|
|
|
} |
|
|
|
|
|
|
|
public int FunctionCalls |
|
|
|
/// <summary>
|
|
|
|
/// Find the minimum of the supplied function along a specified line, using the Brent method.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="function"></param>
|
|
|
|
/// <param name="direction">Direction of line.</param>
|
|
|
|
/// <param name="startingPoint">Starting point of line.</param>
|
|
|
|
/// <returns></returns>
|
|
|
|
public double Minimize(Func<double[], double> function, double[] direction, double[] startingPoint, out double[] minimumPoint) |
|
|
|
{ |
|
|
|
get { return lineSearch.FunctionCalls; } |
|
|
|
} |
|
|
|
|
|
|
|
public double[] StartingPoint { get; set; } |
|
|
|
public double[] Direction { get; set; } |
|
|
|
|
|
|
|
|
|
|
|
public void SetDimension(int N) |
|
|
|
{ |
|
|
|
point = new double[N]; |
|
|
|
double[] point = new double[direction.Length]; |
|
|
|
Func<double, double> functionAlongLine = (p) => |
|
|
|
{ |
|
|
|
for (int i = 0; i < point.Length; ++i) |
|
|
|
point[i] = startingPoint[i] + direction[i] * p; |
|
|
|
return function(point); |
|
|
|
}; |
|
|
|
double result = Minimize(functionAlongLine); |
|
|
|
minimumPoint = point; |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
public int Search(out double[] minPoint, out double minFunction) |
|
|
|
/// <summary>
|
|
|
|
/// Updates the bracket.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="function"></param>
|
|
|
|
/// <param name="bracketInitial"></param>
|
|
|
|
/// <param name="newBracket"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
private static bool UpdateBracketInterval(Func<double, double> function, Bracket bracketInitial, out Bracket newBracket) |
|
|
|
{ |
|
|
|
double path; |
|
|
|
int result = lineSearch.Search(out path, out minFunction); |
|
|
|
for (int i = 0; i < StartingPoint.Length; ++i) |
|
|
|
int iterations = 0; |
|
|
|
double pointA = bracketInitial.PointA; |
|
|
|
double pointB = bracketInitial.PointB; |
|
|
|
int maxIterations; |
|
|
|
maxIterations = 1000; |
|
|
|
double functionA = function(pointA); |
|
|
|
double functionB = function(pointB); |
|
|
|
double temp; |
|
|
|
if (functionA < functionB) // Swap points over
|
|
|
|
{ |
|
|
|
point[i] = StartingPoint[i] + path * Direction[i]; |
|
|
|
temp = functionA; functionA = functionB; functionB = temp; |
|
|
|
temp = pointA; pointA = pointB; pointB = temp; |
|
|
|
} |
|
|
|
minPoint = point; |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
public double PointAlongLine(double path) |
|
|
|
{ |
|
|
|
for (int i = 0; i < StartingPoint.Length; ++i) |
|
|
|
double pointC = pointB + goldenRatio * (pointB - pointA); |
|
|
|
double functionC = function(pointC); |
|
|
|
iterations = 0; |
|
|
|
double temp1, temp2, value, denom; |
|
|
|
while (functionC < functionB) |
|
|
|
{ |
|
|
|
point[i] = StartingPoint[i] + path * Direction[i]; |
|
|
|
double pointW, functionW, wlim; |
|
|
|
temp1 = (pointB - pointA) * (functionB - functionC); |
|
|
|
temp2 = (pointB - pointC) * (functionB - functionA); |
|
|
|
value = temp2 - temp1; |
|
|
|
if (Math.Abs(value) < verySmallNumber) denom = 2.0 * verySmallNumber; |
|
|
|
else denom = 2.0 * value; |
|
|
|
pointW = pointB - ((pointB - pointC) * temp2 - (pointB - pointA) * temp1) / denom; |
|
|
|
wlim = pointB + growLimit * (pointC - pointB); |
|
|
|
if (iterations > maxIterations) |
|
|
|
{ |
|
|
|
newBracket = bracketInitial; |
|
|
|
return false; |
|
|
|
} |
|
|
|
iterations++; |
|
|
|
if ((pointW - pointC) * (pointB - pointW) > 0.0) |
|
|
|
{ |
|
|
|
functionW = function(pointW); |
|
|
|
if (functionW < functionC) |
|
|
|
{ |
|
|
|
pointA = pointB; pointB = pointW; |
|
|
|
functionA = functionB; functionB = functionW; |
|
|
|
break; |
|
|
|
} |
|
|
|
else if (functionW > functionB) |
|
|
|
{ |
|
|
|
pointC = pointW; functionC = functionW; |
|
|
|
break; |
|
|
|
} |
|
|
|
pointW = pointC + goldenRatio * (pointC - pointB); |
|
|
|
functionW = function(pointW); |
|
|
|
} |
|
|
|
else if ((pointW - wlim) * (wlim - pointC) >= 0.0) |
|
|
|
{ |
|
|
|
pointW = wlim; |
|
|
|
functionW = function(pointW); |
|
|
|
} |
|
|
|
else if ((pointW - wlim) * (pointC - pointW) > 0.0) |
|
|
|
{ |
|
|
|
functionW = function(pointW); |
|
|
|
if (functionW < functionC) |
|
|
|
{ |
|
|
|
pointB = pointC; pointC = pointW; |
|
|
|
pointW = pointC + goldenRatio * (pointC - pointB); |
|
|
|
functionB = functionC; functionC = functionW; |
|
|
|
functionW = function(pointW); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
pointW = pointC + goldenRatio * (pointC - pointB); |
|
|
|
functionW = function(pointW); |
|
|
|
} |
|
|
|
pointA = pointB; pointB = pointC; pointC = pointW; |
|
|
|
functionA = functionB; functionB = functionC; functionC = functionW; |
|
|
|
} |
|
|
|
return function(point); |
|
|
|
newBracket = new Bracket() |
|
|
|
{ |
|
|
|
PointA = pointA, |
|
|
|
PointB = pointB, |
|
|
|
PointC = pointC, |
|
|
|
FunctionA = functionA, |
|
|
|
FunctionB = functionB, |
|
|
|
FunctionC = functionC |
|
|
|
}; |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|