5 changed files with 301 additions and 0 deletions
@ -0,0 +1,108 @@ |
|||||
|
// <copyright file="BfgsSolver.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://numerics.mathdotnet.com
|
||||
|
// http://github.com/mathnet/mathnet-numerics
|
||||
|
// http://mathnetnumerics.codeplex.com
|
||||
|
//
|
||||
|
// Copyright (c) 2009-2013 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using MathNet.Numerics.LinearAlgebra; |
||||
|
using MathNet.Numerics.LinearAlgebra.Double; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace MathNet.Numerics.RootFinding |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Broyden-Fletcher-Goldfarb-Shanno solver for finding function minima
|
||||
|
/// See http://en.wikipedia.org/wiki/Broyden%E2%80%93Fletcher%E2%80%93Goldfarb%E2%80%93Shanno_algorithm
|
||||
|
/// Inspired by implementation: https://github.com/PatWie/CppNumericalSolvers/blob/master/src/BfgsSolver.cpp
|
||||
|
/// </summary>
|
||||
|
public static class BfgsSolver |
||||
|
{ |
||||
|
private const double gradientTolerance = 1e-5; |
||||
|
private const int maxIterations = 100000; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Finds a minimum of a function by the BFGS quasi-Newton method
|
||||
|
/// This uses the function and it's gradient (partial derivatives in each direction) and approximates the Hessian
|
||||
|
/// </summary>
|
||||
|
/// <param name="initialGuess">An initial guess</param>
|
||||
|
/// <param name="functionValue">Evaluates the function at a point</param>
|
||||
|
/// <param name="functionGradient">Evaluates the gradient of the function at a point</param>
|
||||
|
/// <returns>The minimum found</returns>
|
||||
|
public static Vector<double> Solve(Vector initialGuess, Func<Vector<double>, double> functionValue, Func<Vector<double>, Vector<double>> functionGradient) |
||||
|
{ |
||||
|
int dim = initialGuess.Count; |
||||
|
int iter = 0; |
||||
|
// H represents the approximation of the inverse hessian matrix
|
||||
|
// it is updated via the Sherman–Morrison formula (http://en.wikipedia.org/wiki/Sherman%E2%80%93Morrison_formula)
|
||||
|
Matrix<double> H = DenseMatrix.CreateIdentity(dim); |
||||
|
|
||||
|
Vector<double> x = initialGuess; |
||||
|
Vector<double> x_old = x; |
||||
|
Vector<double> grad; |
||||
|
|
||||
|
do |
||||
|
{ |
||||
|
// search along the direction of the gradient
|
||||
|
grad = functionGradient(x); |
||||
|
Vector<double> p = -1 * H * grad; |
||||
|
double rate = WolfeRule.LineSearch(x, p, functionValue, functionGradient); |
||||
|
x = x + rate * p; |
||||
|
Vector<double> grad_old = grad; |
||||
|
|
||||
|
// update the gradient
|
||||
|
grad = functionGradient(x); |
||||
|
|
||||
|
Vector<double> s = x - x_old; |
||||
|
Vector<double> y = grad - grad_old; |
||||
|
|
||||
|
double rho = 1.0 / (y * s); |
||||
|
if (iter == 0) |
||||
|
{ |
||||
|
// set up an initial hessian
|
||||
|
H = (y * s) / (y * y) * DenseMatrix.CreateIdentity(dim); |
||||
|
} |
||||
|
|
||||
|
var sM = s.ToColumnMatrix(); |
||||
|
var yM = y.ToColumnMatrix(); |
||||
|
|
||||
|
// Update the estimate of the hessian
|
||||
|
H = H |
||||
|
- rho * (sM * (yM.TransposeThisAndMultiply(H)) + (H * yM).TransposeAndMultiply(sM)) |
||||
|
+ rho * rho * (y.DotProduct(H * y) + 1.0 / rho) * (sM.TransposeAndMultiply(sM)); |
||||
|
x_old = x; |
||||
|
iter++; |
||||
|
} |
||||
|
while ((grad.InfinityNorm() > gradientTolerance) && (iter < maxIterations)); |
||||
|
|
||||
|
return x; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,113 @@ |
|||||
|
// <copyright file="WolfeRule.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://numerics.mathdotnet.com
|
||||
|
// http://github.com/mathnet/mathnet-numerics
|
||||
|
// http://mathnetnumerics.codeplex.com
|
||||
|
//
|
||||
|
// Copyright (c) 2009-2013 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using MathNet.Numerics.LinearAlgebra; |
||||
|
|
||||
|
namespace MathNet.Numerics.RootFinding |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Performs an inexact line search. This is used as a part of quasi-Newton optimization methods to figure
|
||||
|
/// out how far to move along a certain gradient.
|
||||
|
/// See http://en.wikipedia.org/wiki/Wolfe_conditions
|
||||
|
/// Inspired by implementation: https://github.com/PatWie/CppNumericalSolvers/blob/master/src/linesearch/WolfeRule.h
|
||||
|
/// </summary>
|
||||
|
internal static class WolfeRule |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Searches along a line to satisfy the Wolfe conditions (inexact search for minimum)
|
||||
|
/// </summary>
|
||||
|
/// <param name="x0">Starting point of search</param>
|
||||
|
/// <param name="z">Search direction</param>
|
||||
|
/// <param name="functionValue">Evaluates the function being minimized</param>
|
||||
|
/// <param name="functionGradient">Evaluates the gradient of the function</param>
|
||||
|
/// <param name="alphaInit">Initial value for the coefficient of z (distance to travel in z direction)</param>
|
||||
|
/// <returns></returns>
|
||||
|
public static double LineSearch( |
||||
|
Vector<double> x0, |
||||
|
Vector<double> z, |
||||
|
Func<Vector<double>, double> functionValue, |
||||
|
Func<Vector<double>, Vector<double>> functionGradient, |
||||
|
float alphaInit = 1) |
||||
|
{ |
||||
|
Vector<double> x = x0; |
||||
|
|
||||
|
// evaluate phi(0)
|
||||
|
double phi0 = functionValue(x0); |
||||
|
|
||||
|
// evaluate phi'(0)
|
||||
|
Vector<double> grad = functionGradient(x); |
||||
|
double phi0_dash = z * grad; |
||||
|
|
||||
|
double alpha = alphaInit; |
||||
|
|
||||
|
bool decrease_direction = true; |
||||
|
|
||||
|
// 200 guesses max
|
||||
|
for (int iter = 0; iter < 200; ++iter) { |
||||
|
|
||||
|
// new guess for phi(alpha)
|
||||
|
Vector<double> x_candidate = x + alpha * z; |
||||
|
double phi = functionValue(x_candidate); |
||||
|
|
||||
|
// decrease condition invalid --> shrink interval
|
||||
|
if (phi > phi0 + 0.0001 * alpha * phi0_dash) |
||||
|
{ |
||||
|
alpha *= 0.5; |
||||
|
decrease_direction = false; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// valid decrease --> test strong wolfe condition
|
||||
|
Vector<double> grad2 = functionGradient(x_candidate); |
||||
|
double phi_dash = z * grad2; |
||||
|
|
||||
|
// curvature condition invalid ?
|
||||
|
if ((phi_dash < 0.9 * phi0_dash) || !decrease_direction) { |
||||
|
// increase interval
|
||||
|
alpha *= 4.0; |
||||
|
} |
||||
|
else { |
||||
|
// both condition are valid --> we are happy
|
||||
|
x = x_candidate; |
||||
|
grad = grad2; |
||||
|
phi0 = phi; |
||||
|
return alpha; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
return alpha; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
// <copyright file="BfgsTest.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://numerics.mathdotnet.com
|
||||
|
// http://github.com/mathnet/mathnet-numerics
|
||||
|
// http://mathnetnumerics.codeplex.com
|
||||
|
//
|
||||
|
// Copyright (c) 2009-2013 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using MathNet.Numerics.LinearAlgebra; |
||||
|
using NUnit.Framework; |
||||
|
using MathNet.Numerics.LinearAlgebra.Double; |
||||
|
using MathNet.Numerics.RootFinding; |
||||
|
|
||||
|
namespace MathNet.Numerics.UnitTests.RootFindingTests |
||||
|
{ |
||||
|
[TestFixture, Category("RootFinding")] |
||||
|
internal class BfgsTest |
||||
|
{ |
||||
|
private const double precision = 1e-4; |
||||
|
|
||||
|
[Test] |
||||
|
public void MinimizeRosenbrock() |
||||
|
{ |
||||
|
CheckRosenbrock(15.0, 8.0, expectedMin: 0.0); |
||||
|
CheckRosenbrock(-1.2, 1.0, expectedMin: 0.0); |
||||
|
CheckRosenbrock(-1.2, 100.0, expectedMin: 0.0); |
||||
|
} |
||||
|
|
||||
|
private static void CheckRosenbrock(double a, double b, double expectedMin) |
||||
|
{ |
||||
|
var x = BfgsSolver.Solve(new DenseVector(new[] { a, b }), Rosenbrock, RosenbrockGradient); |
||||
|
Precision.AlmostEqual(expectedMin, Rosenbrock(x), precision); |
||||
|
} |
||||
|
|
||||
|
private static double Rosenbrock(Vector<double> x) |
||||
|
{ |
||||
|
double t1 = (1 - x[0]); |
||||
|
double t2 = (x[1] - x[0] * x[0]); |
||||
|
return t1 * t1 + 100 * t2 * t2; |
||||
|
} |
||||
|
|
||||
|
private static Vector<double> RosenbrockGradient(Vector<double> x) |
||||
|
{ |
||||
|
var grad = new DenseVector(2); |
||||
|
grad[0] = -2 * (1 - x[0]) + 200 * (x[1] - x[0] * x[0]) * (-2 * x[0]); |
||||
|
grad[1] = 200 * (x[1] - x[0] * x[0]); |
||||
|
return grad; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue