From 96a618fd3f5653e40f7d0377556c407a3c42b256 Mon Sep 17 00:00:00 2001 From: joemoorhouse Date: Tue, 12 Nov 2013 22:19:10 +0000 Subject: [PATCH] Cleaning up Powell and Brent minimizers. --- src/Numerics/Optimization/BrentMinimizer.cs | 333 ++++++++---------- .../NonLinearLeastSquaresMinimizer.cs | 5 +- src/Numerics/Optimization/PowellMinimizer.cs | 187 +++++----- .../Mkl/MklOptimizationProvider.cs | 2 +- .../FunctionMinimizationTests.cs | 14 +- .../NonLinearLeastSquaresTest.cs | 12 +- 6 files changed, 272 insertions(+), 281 deletions(-) diff --git a/src/Numerics/Optimization/BrentMinimizer.cs b/src/Numerics/Optimization/BrentMinimizer.cs index 3d029903..78d3c6f7 100644 --- a/src/Numerics/Optimization/BrentMinimizer.cs +++ b/src/Numerics/Optimization/BrentMinimizer.cs @@ -11,167 +11,80 @@ namespace MathNet.Numerics.Optimization public class BrentOptions { public int MaximumIterations = 1000; - public double FunctionTolerance = 1e-4; } + + /// + /// Result of Brent Minimization. + /// + public class BrentResult + { + public int NumberOfIterations; + public double MinimumPoint; + public double MinimumFunctionValue; + } /// /// Minimizes f(p) where p is a model parameter scalar, i.e. a line-search. + /// Inspired by the SciPy implementation. /// 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 function; - double pointA, pointB, pointC; - double functionA, functionB, functionC; - - public double Tolerance { get; set; } - - public BrentMinimizer(Func 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) + /// + /// Find the minimum of the supplied function using the Brent method. + /// + /// + /// + public double Minimize(Func 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; - } - } - - /// - /// Minimizes f(p u) where p is a model parameter scalar and u is a direction vector. - /// - public class MultiDimensionalBrent - { - private Func function; - private int functionCalls; - double[] point; - BrentMinimizer lineSearch; - - public MultiDimensionalBrent(Func 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 + /// + /// Find the minimum of the supplied function along a specified line, using the Brent method. + /// + /// + /// Direction of line. + /// Starting point of line. + /// + public double Minimize(Func 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 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) + /// + /// Updates the bracket. + /// + /// + /// + /// + /// + private static bool UpdateBracketInterval(Func 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; } + } } diff --git a/src/Numerics/Optimization/NonLinearLeastSquaresMinimizer.cs b/src/Numerics/Optimization/NonLinearLeastSquaresMinimizer.cs index bec4f902..caaf1a99 100644 --- a/src/Numerics/Optimization/NonLinearLeastSquaresMinimizer.cs +++ b/src/Numerics/Optimization/NonLinearLeastSquaresMinimizer.cs @@ -13,9 +13,7 @@ namespace MathNet.Numerics.Optimization public class NonLinearLeastSquaresOptions { public int MaximumIterations = 1000; - public int MaximumTrialStepIterations = 100; - public NonLinearLeastSquaresConvergenceType ConvergenceType; /// @@ -54,7 +52,7 @@ namespace MathNet.Numerics.Optimization /// /// For details of convergence criteria, see Options. /// - public enum NonLinearLeastSquaresConvergenceType { NoneMaxIterationExceeded, Criterion0, Criterion1, Criterion2, Criterion3, Criterion4, Error }; + public enum NonLinearLeastSquaresConvergenceType { MaxIterationsExceeded, Criterion0, Criterion1, Criterion2, Criterion3, Criterion4, Error }; /// /// Result of Non-Linear Least Squares Minimization. @@ -62,7 +60,6 @@ namespace MathNet.Numerics.Optimization public class NonLinearLeastSquaresResult { public int NumberOfIterations; - public NonLinearLeastSquaresConvergenceType ConvergenceType; } diff --git a/src/Numerics/Optimization/PowellMinimizer.cs b/src/Numerics/Optimization/PowellMinimizer.cs index 1927e44a..2d062d04 100644 --- a/src/Numerics/Optimization/PowellMinimizer.cs +++ b/src/Numerics/Optimization/PowellMinimizer.cs @@ -5,39 +5,39 @@ using System.Text; namespace MathNet.Numerics.Optimization { + /// + /// Options for Powell Minimization. + /// + public class PowellOptions + { + public int? MaximumIterations = null; + public int? MaximumFunctionCalls = null; + public double PointTolerance = 1e-4; + public double FunctionTolerance = 1e-4; + } + + public enum PowellConvergenceType { Success, MaxIterationsExceeded, MaxFunctionCallsExceeded }; + + /// + /// Result of Powell Minimization. + /// + public class PowellResult + { + public int NumberOfIterations; + public int NumberOfFunctionCalls; + public double[] MinimumPoint; + public double MinimumFunctionValue; + public PowellConvergenceType ConvergenceType; + } + /// /// Minimizes f(p) where p is a vector of model parameters using the Powell method. /// public class PowellMinimizer { - public double PointTolerance { get; set; } - public double FunctionTolerance { get; set; } - int? maxIterations, maxFunctionCalls; - double[] minimumPoint; - double functionAtMinimum; - public int FunctionCalls { get; set; } - public int Iterations { get; set; } - public int? MaxIterations { get; set; } - public int? MaxFunctionCalls { get; set; } - public double[] MinimumPoint { get { return minimumPoint; } } - public double FunctionValueAtMinimum { get { return functionAtMinimum; } } - - Func function; - MultiDimensionalBrent powellLineSearch; - - public PowellMinimizer() - { - //this.function = function; - //powellLineSearch = new MultiDimensionalBrent(function); - PointTolerance = 1e-4; - FunctionTolerance = 1e-4; - MaxIterations = null; - MaxFunctionCalls = null; - FunctionCalls = 0; - Iterations = 0; - minimumPoint = null; - functionAtMinimum = 0; - } + public PowellResult Result { get; private set; } + + public readonly PowellOptions Options = new PowellOptions(); public double[] CurveFit(double[] x, double[] y, Func f, double[] pStart) @@ -53,61 +53,71 @@ namespace MathNet.Numerics.Optimization } return sum; }; - this.function = function; - powellLineSearch = new MultiDimensionalBrent(function); - this.Minimize(pStart); - return minimumPoint; + return Minimize(function, pStart); } - public int Minimize(double[] p) + public double[] Minimize(Func function, double[] p) { - // Set line search valuer to use main valuer: - // (this valuer takes starting point, direciton and length and - // returns scalar): - // - int N = p.Length; // number of dimensions - powellLineSearch.SetDimension(N); + BrentMinimizer brentMinimizer = new BrentMinimizer(); + // used in closure: + double[] point = new double[p.Length]; + double[] startingPoint = new double[p.Length]; + double[] direction = new double[p.Length]; + double lineMiniumum = 0; + int functionCalls = 0; + Func functionAlongLine = (u) => + { + for (int i = 0; i < point.Length; ++i) + point[i] = startingPoint[i] + direction[i] * u; + lineMiniumum = function(point); + functionCalls++; + return lineMiniumum; + }; + + int n = p.Length; // number of dimensions double fval; - FunctionCalls = 0; - Iterations = 0; - if (maxIterations == null) maxIterations = N * 1000; - if (maxFunctionCalls == null) maxFunctionCalls = N * 1000; - - // An array of N directions: - double[][] direc = new double[N][]; - for (int i = 0; i < N; ++i) + + int iterations = 0; + int maxIterations = (Options.MaximumIterations == null) ? n * 1000 : (int)Options.MaximumIterations; + int maxFunctionCalls = (Options.MaximumFunctionCalls == null) ? n * 1000 : (int)Options.MaximumFunctionCalls; + + // An array of n directions: + double[][] direc = new double[n][]; + for (int i = 0; i < n; ++i) { - direc[i] = new double[N]; + direc[i] = new double[n]; direc[i][i] = 1.0; } double[] x = p; double[] x1 = (double[])x.Clone(); - powellLineSearch.Tolerance = PointTolerance * 100; // Set tolerance + + brentMinimizer.Options.FunctionTolerance = Options.PointTolerance * 100; fval = function(x); - FunctionCalls++; - double[] x2 = new double[N]; + + double[] x2 = new double[n]; double fx; - double[] direc1 = new double[N]; + double[] direc1 = new double[n]; double[] xnew; while (true) - {; + { fx = fval; int bigind = 0; double delta = 0.0; double fx2; - for (int i = 0; i < N; ++i) + for (int i = 0; i < n; ++i) { direc1 = direc[i]; fx2 = fval; - powellLineSearch.StartingPoint = x; - powellLineSearch.Direction = direc1; - powellLineSearch.Search(out xnew, out fval); - // Do a linesearch with specified starting point and direction. - FunctionCalls += powellLineSearch.FunctionCalls; + // Do a linesearch with specified starting point and direction. + direction = direc1; + startingPoint = x; + double u = brentMinimizer.Minimize(functionAlongLine); + fval = functionAlongLine(u); + xnew = point; - for (int j = 0; j < N; ++j) x[j] = xnew[j]; + for (int j = 0; j < n; ++j) x[j] = xnew[j]; if ((fx2 - fval) > delta) { @@ -115,14 +125,14 @@ namespace MathNet.Numerics.Optimization bigind = i; } } - Iterations++; - if (2.0 * (fx - fval) <= FunctionTolerance * ((Math.Abs(fx) + Math.Abs(fval)) + 1e-20)) break; - if (FunctionCalls >= maxFunctionCalls) break; - if (Iterations >= maxIterations) break; + iterations++; + if (2.0 * (fx - fval) <= Options.FunctionTolerance * ((Math.Abs(fx) + Math.Abs(fval)) + 1e-20)) break; + if (functionCalls >= maxFunctionCalls) break; + if (iterations >= maxIterations) break; // Construct the extrapolated point - direc1 = new double[N]; - for (int i = 0; i < N; ++i) + direc1 = new double[n]; + for (int i = 0; i < n; ++i) { direc1[i] = x[i] - x1[i]; x2[i] = 2.0 * x[i] - x1[i]; @@ -130,7 +140,6 @@ namespace MathNet.Numerics.Optimization } fx2 = function(x2); - FunctionCalls++; if (fx > fx2) { double t = 2.0 * (fx + fx2 - 2.0 * fval); @@ -140,36 +149,40 @@ namespace MathNet.Numerics.Optimization t -= delta * temp * temp; if (t < 0.0) { - powellLineSearch.StartingPoint = x; - powellLineSearch.Direction = direc1; - powellLineSearch.Search(out xnew, out fval); + direction = direc1; + startingPoint = x; + double u = brentMinimizer.Minimize(functionAlongLine); + fval = functionAlongLine(u); + xnew = point; - FunctionCalls += powellLineSearch.FunctionCalls; - direc1 = new double[N]; - for (int i = 0; i < N; ++i) + direc1 = new double[n]; + for (int i = 0; i < n; ++i) { direc1[i] = xnew[i] - x[i]; x[i] = xnew[i]; } - direc[bigind] = direc[N - 1]; - direc[N - 1] = direc1; + direc[bigind] = direc[n - 1]; + direc[n - 1] = direc1; } } } - minimumPoint = (double[])x.Clone(); - functionAtMinimum = fx; - - // Find out what happened: - if (FunctionCalls >= maxFunctionCalls) - { - return 1; // Max function calls exceeded - } - if (Iterations >= maxIterations) + var convergenceType = PowellConvergenceType.Success; + if (functionCalls >= maxFunctionCalls) + convergenceType = PowellConvergenceType.MaxFunctionCallsExceeded; + else if (iterations > maxIterations) + convergenceType = PowellConvergenceType.MaxFunctionCallsExceeded; + + Result = new PowellResult() { - return 2; // Max iterations exceeded - } - return 0; // all good + MinimumPoint = (double[])x.Clone(), + MinimumFunctionValue = fx, + ConvergenceType = convergenceType, + NumberOfIterations = iterations, + NumberOfFunctionCalls = functionCalls + }; + + return Result.MinimumPoint; } } } diff --git a/src/Numerics/Providers/Optimization/Mkl/MklOptimizationProvider.cs b/src/Numerics/Providers/Optimization/Mkl/MklOptimizationProvider.cs index b5530720..d9f81a63 100644 --- a/src/Numerics/Providers/Optimization/Mkl/MklOptimizationProvider.cs +++ b/src/Numerics/Providers/Optimization/Mkl/MklOptimizationProvider.cs @@ -180,7 +180,7 @@ namespace MathNet.Numerics.Providers.Optimization.Mkl switch (rciRequest) { case -1: - convergenceType = NonLinearLeastSquaresConvergenceType.NoneMaxIterationExceeded; break; + convergenceType = NonLinearLeastSquaresConvergenceType.MaxIterationsExceeded; break; case -2: convergenceType = NonLinearLeastSquaresConvergenceType.Criterion0; break; case -3: diff --git a/src/UnitTests/OptimizationTests/FunctionMinimizationTests.cs b/src/UnitTests/OptimizationTests/FunctionMinimizationTests.cs index 52975f64..14b5cb16 100644 --- a/src/UnitTests/OptimizationTests/FunctionMinimizationTests.cs +++ b/src/UnitTests/OptimizationTests/FunctionMinimizationTests.cs @@ -48,14 +48,22 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests var minimizer = new PowellMinimizer(); - var popt = minimizer.CurveFit(xin, yin, function, new double[] { 1, 1 }); // 100, 0.75 + var watch = new System.Diagnostics.Stopwatch(); watch.Start(); + double[] popt = null; + for (int i = 0; i < 1000; ++i) + { + popt = minimizer.CurveFit(xin, yin, function, new double[] { 1, 1 }); // 100, 0.75 + } + watch.Stop(); + double elapsed = watch.ElapsedMilliseconds; double[] expected = new double[] { 2.1380940889E+02, 5.4723748542E-01 }; double residual = 0; for (int i = 0; i < yin.Length; ++i) residual += (yin[i] - function(xin[i], popt)) * (yin[i] - function(xin[i], popt)); - //Assert.AreEqual(3, Brent.FindRoot(f2, 2.1, 3.4, 0.001, 50), 0.001); - } + Assert.AreEqual(expected[0], popt[0], 1e-4); + Assert.AreEqual(expected[1], popt[1], 1e-4); + } } } diff --git a/src/UnitTests/OptimizationTests/NonLinearLeastSquaresTest.cs b/src/UnitTests/OptimizationTests/NonLinearLeastSquaresTest.cs index 98bc6276..5c345a44 100644 --- a/src/UnitTests/OptimizationTests/NonLinearLeastSquaresTest.cs +++ b/src/UnitTests/OptimizationTests/NonLinearLeastSquaresTest.cs @@ -46,20 +46,24 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests // y = b1*(1-exp[-b2*x]) + e var xin = new double[] { 1, 2, 3, 5, 7, 10 }; var yin = new double[] { 109, 149, 149, 191, 213, 224 }; - var popt = minimizer.CurveFit(xin, yin, (x, p) => p[0] * (1 - Math.Exp(-p[1] * x)), new double[] { 1, 1 }); + + // estimated derivative method: does not find best solution. + //var popt = minimizer.CurveFit(xin, yin, (x, p) => p[0] * (1 - Math.Exp(-p[1] * x)), new double[] { 1, 1 }); Func function = (x, p) => p[0] * (1 - Math.Exp(-p[1] * x)); Func jacobian = (x, p) => new double[] { 1 - Math.Exp(-p[1] * x), p[0] * x * Math.Exp(-p[1] * x) }; - popt = minimizer.CurveFit(xin, yin, function, new double[] { 1, 1 }, jacobian); // 100, 0.75 + var popt = minimizer.CurveFit(xin, yin, function, new double[] { 1, 1 }, jacobian); // 100, 0.75 double[] expected = new double[] { 2.1380940889E+02, 5.4723748542E-01 }; double residual = 0; - for (int i = 0; i < yin.Length; ++i) residual += (yin[i] - function(xin[i], popt)) * (yin[i] - function(xin[i], popt)); - //Assert.AreEqual(3, Brent.FindRoot(f2, 2.1, 3.4, 0.001, 50), 0.001); + for (int i = 0; i < yin.Length; ++i) residual += (yin[i] - function(xin[i], popt)) * (yin[i] - function(xin[i], popt)); + + Assert.AreEqual(expected[0], popt[0], 1e-6); + Assert.AreEqual(expected[1], popt[1], 1e-6); } } }