forked from tsai/mathnet-numerics
committed by
Christoph Ruegg
14 changed files with 495 additions and 0 deletions
@ -0,0 +1,11 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace MathNet.Numerics.Optimization |
||||
|
{ |
||||
|
class BFGS |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace MathNet.Numerics.Optimization |
||||
|
{ |
||||
|
public class BisectionRootFinder |
||||
|
{ |
||||
|
|
||||
|
public double ObjectiveTolerance { get; set; } |
||||
|
public double XTolerance { get; set; } |
||||
|
public double LowerExpansionFactor { get; set; } |
||||
|
public double UpperExapansionFactor { get; set; } |
||||
|
public int MaxExpansionSteps { get; set; } |
||||
|
|
||||
|
public BisectionRootFinder(double objective_tolerance=1e-5, double x_tolerance=1e-5, double lower_expansion_factor=-1.0, double upper_expansion_factor=-1.0, int max_expansion_steps=10) |
||||
|
{ |
||||
|
this.ObjectiveTolerance = objective_tolerance; |
||||
|
this.XTolerance = x_tolerance; |
||||
|
this.LowerExpansionFactor = lower_expansion_factor; |
||||
|
this.UpperExapansionFactor = upper_expansion_factor; |
||||
|
this.MaxExpansionSteps = max_expansion_steps; |
||||
|
} |
||||
|
|
||||
|
public double FindRoot(Func<double, double> objective_function, double lower_bound, double upper_bound) |
||||
|
{ |
||||
|
double lower_val = objective_function(lower_bound); |
||||
|
double upper_val = objective_function(upper_bound); |
||||
|
|
||||
|
if (lower_val == 0.0) |
||||
|
return lower_bound; |
||||
|
if (upper_val == 0.0) |
||||
|
return upper_bound; |
||||
|
|
||||
|
this.ValidateEvaluation(lower_val, lower_bound); |
||||
|
this.ValidateEvaluation(upper_val, upper_bound); |
||||
|
|
||||
|
if (Math.Sign(lower_val) == Math.Sign(upper_val) && this.LowerExpansionFactor <= 1.0 && this.UpperExapansionFactor <= 1.0) |
||||
|
throw new Exception("Bounds do not necessarily span a root, and StepExpansionFactor is not set to expand the interval in this case."); |
||||
|
|
||||
|
while (Math.Abs(upper_val - lower_val) > 0.5 * this.ObjectiveTolerance || Math.Abs(upper_bound - lower_bound) > 0.5 * this.XTolerance) |
||||
|
{ |
||||
|
double midpoint = 0.5 * (upper_bound + lower_bound); |
||||
|
double midval = objective_function(midpoint); |
||||
|
this.ValidateEvaluation(midval, midpoint); |
||||
|
|
||||
|
if (Math.Sign(midval) == Math.Sign(lower_val)) |
||||
|
{ |
||||
|
lower_bound = midpoint; |
||||
|
lower_val = midval; |
||||
|
} |
||||
|
else if (Math.Sign(midval) == Math.Sign(upper_val)) |
||||
|
{ |
||||
|
upper_bound = midpoint; |
||||
|
upper_val = midval; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return midpoint; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return 0.5 * (lower_bound + upper_bound); |
||||
|
} |
||||
|
|
||||
|
private void ValidateEvaluation(double output, double input) |
||||
|
{ |
||||
|
if (!IsFinite(output)) |
||||
|
throw new Exception(String.Format("Objective function returned non-finite result: f({0}) = {1}", input, output)); |
||||
|
} |
||||
|
|
||||
|
private static bool IsFinite(double x) |
||||
|
{ |
||||
|
return !(Double.IsInfinity(x) || Double.IsNaN(x)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,86 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
using MathNet.Numerics.LinearAlgebra; |
||||
|
|
||||
|
namespace MathNet.Numerics.Optimization |
||||
|
{ |
||||
|
public class ConjugateGradientMinimizer |
||||
|
{ |
||||
|
public double GradientTolerance { get; set; } |
||||
|
public int MaximumIterations { get; set; } |
||||
|
|
||||
|
public ConjugateGradientMinimizer(double gradient_tolerance, int maximum_iterations) |
||||
|
{ |
||||
|
this.GradientTolerance = gradient_tolerance; |
||||
|
this.MaximumIterations = maximum_iterations; |
||||
|
} |
||||
|
|
||||
|
public MinimizationOutput FindMinimum(IObjectiveFunction objective, Vector<double> initial_guess) |
||||
|
{ |
||||
|
if (!objective.GradientSupported) |
||||
|
throw new Exception("Gradient not supported in objective function, but required for ConjugateGradient minimization."); |
||||
|
|
||||
|
IEvaluation initial_eval = objective.Evaluate(initial_guess); |
||||
|
var gradient = initial_eval.Gradient; |
||||
|
this.ValidateGradient(gradient, initial_guess); |
||||
|
|
||||
|
// Check that we're not already done
|
||||
|
if (this.ExitCriteriaSatisfied(initial_guess, gradient)) |
||||
|
return new MinimizationOutput(initial_eval, 0); |
||||
|
|
||||
|
// Set up line search algorithm
|
||||
|
var line_searcher = new WeakWolfeLineSearch(1e-4, 0.1); |
||||
|
|
||||
|
// Declare state variables
|
||||
|
IEvaluation candidate_point; |
||||
|
Vector<double> steepest_direction, previous_steepest_direction, search_direction; |
||||
|
|
||||
|
// First step
|
||||
|
steepest_direction = -gradient; |
||||
|
search_direction = steepest_direction; |
||||
|
var result = line_searcher.FindConformingStep(objective, initial_eval, search_direction, 1.0); |
||||
|
candidate_point = result.FunctionInfoAtMinimum; |
||||
|
this.ValidateGradient(candidate_point.Gradient, candidate_point.Point); |
||||
|
|
||||
|
// Subsequent steps
|
||||
|
int iterations = 1; |
||||
|
while (!this.ExitCriteriaSatisfied(candidate_point.Point, candidate_point.Gradient) && iterations < this.MaximumIterations) |
||||
|
{ |
||||
|
previous_steepest_direction = steepest_direction; |
||||
|
steepest_direction = -candidate_point.Gradient; |
||||
|
var search_direction_adjuster = steepest_direction * (steepest_direction - previous_steepest_direction) / (previous_steepest_direction * previous_steepest_direction); |
||||
|
search_direction = steepest_direction + search_direction_adjuster * previous_steepest_direction; |
||||
|
result = line_searcher.FindConformingStep(objective, candidate_point, search_direction, 1.0); |
||||
|
|
||||
|
candidate_point = result.FunctionInfoAtMinimum; |
||||
|
|
||||
|
iterations += 1; |
||||
|
} |
||||
|
|
||||
|
return new MinimizationOutput(candidate_point, iterations); |
||||
|
} |
||||
|
|
||||
|
private bool ExitCriteriaSatisfied(Vector<double> candidate_point, Vector<double> gradient) |
||||
|
{ |
||||
|
return gradient.Norm(2.0) < this.GradientTolerance; |
||||
|
} |
||||
|
|
||||
|
private void ValidateGradient(Vector<double> gradient, Vector<double> input) |
||||
|
{ |
||||
|
foreach (var x in gradient) |
||||
|
{ |
||||
|
if (Double.IsNaN(x) || Double.IsInfinity(x)) |
||||
|
throw new Exception("Non-finite gradient returned."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ValidateObjective(double objective, Vector<double> input) |
||||
|
{ |
||||
|
if (Double.IsNaN(objective) || Double.IsInfinity(objective)) |
||||
|
throw new Exception("Non-finite objective function returned."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace MathNet.Numerics.Optimization |
||||
|
{ |
||||
|
class GoldenSectionMinimizer |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
using MathNet.Numerics.LinearAlgebra; |
||||
|
|
||||
|
namespace MathNet.Numerics.Optimization |
||||
|
{ |
||||
|
public class MinimizationOutput |
||||
|
{ |
||||
|
public Vector<double> MinimizingPoint { get { return FunctionInfoAtMinimum.Point; } } |
||||
|
public IEvaluation FunctionInfoAtMinimum { get; private set; } |
||||
|
public int Iterations { get; private set; } |
||||
|
|
||||
|
public MinimizationOutput(IEvaluation function_info, int iterations) |
||||
|
{ |
||||
|
this.FunctionInfoAtMinimum = function_info; |
||||
|
this.Iterations = iterations; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,103 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
using MathNet.Numerics.LinearAlgebra; |
||||
|
|
||||
|
namespace MathNet.Numerics.Optimization |
||||
|
{ |
||||
|
public interface IEvaluation |
||||
|
{ |
||||
|
Vector<double> Point { get; } |
||||
|
double Value { get; } |
||||
|
Vector<double> Gradient { get; } |
||||
|
Matrix<double> Hessian { get; } |
||||
|
} |
||||
|
|
||||
|
public interface IObjectiveFunction |
||||
|
{ |
||||
|
bool GradientSupported { get; } |
||||
|
bool HessianSupported { get; } |
||||
|
IEvaluation Evaluate(Vector<double> point); |
||||
|
} |
||||
|
|
||||
|
public class CachedEvaluation : IEvaluation |
||||
|
{ |
||||
|
private double? _value; |
||||
|
private Vector<double> _gradient; |
||||
|
private Matrix<double> _hessian; |
||||
|
private SimpleObjectiveFunction _objective_object; |
||||
|
private Vector<double> _point; |
||||
|
|
||||
|
public CachedEvaluation(SimpleObjectiveFunction f, Vector<double> point) |
||||
|
{ |
||||
|
_objective_object = f; |
||||
|
_point = point; |
||||
|
} |
||||
|
private double setValue() |
||||
|
{ |
||||
|
_value = _objective_object.Objective(_point); |
||||
|
return _value.Value; |
||||
|
} |
||||
|
private Vector<double> setGradient() |
||||
|
{ |
||||
|
_gradient = _objective_object.Gradient(_point); |
||||
|
return _gradient; |
||||
|
} |
||||
|
private Matrix<double> setHessian() |
||||
|
{ |
||||
|
_hessian = _objective_object.Hessian(_point); |
||||
|
return _hessian; |
||||
|
} |
||||
|
|
||||
|
public Vector<double> Point { get { return _point; } } |
||||
|
public double Value { get { return _value ?? setValue(); } } |
||||
|
public Vector<double> Gradient { get { return _gradient ?? setGradient(); } } |
||||
|
public Matrix<double> Hessian { get { return _hessian ?? setHessian(); } } |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public class SimpleObjectiveFunction : IObjectiveFunction |
||||
|
{ |
||||
|
public Func<Vector<double>,double> Objective { get; private set; } |
||||
|
public Func<Vector<double>,Vector<double>> Gradient { get; private set; } |
||||
|
public Func<Vector<double>, Matrix<double>> Hessian { get; private set; } |
||||
|
|
||||
|
public SimpleObjectiveFunction(Func<Vector<double>,double> objective) |
||||
|
{ |
||||
|
this.Objective = objective; |
||||
|
this.Gradient = null; |
||||
|
this.Hessian = null; |
||||
|
} |
||||
|
|
||||
|
public SimpleObjectiveFunction(Func<Vector<double>,double> objective, Func<Vector<double>,Vector<double>> gradient) |
||||
|
{ |
||||
|
this.Objective = objective; |
||||
|
this.Gradient = gradient; |
||||
|
this.Hessian = null; |
||||
|
} |
||||
|
|
||||
|
public SimpleObjectiveFunction(Func<Vector<double>,double> objective, Func<Vector<double>,Vector<double>> gradient, Func<Vector<double>, Matrix<double>> hessian) |
||||
|
{ |
||||
|
this.Objective = objective; |
||||
|
this.Gradient = gradient; |
||||
|
this.Hessian = hessian; |
||||
|
} |
||||
|
|
||||
|
public bool GradientSupported |
||||
|
{ |
||||
|
get { return this.Gradient != null; } |
||||
|
} |
||||
|
|
||||
|
public bool HessianSupported |
||||
|
{ |
||||
|
get { return this.Hessian != null; } |
||||
|
} |
||||
|
|
||||
|
public IEvaluation Evaluate(Vector<double> point) |
||||
|
{ |
||||
|
return new CachedEvaluation(this, point); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace MathNet.Numerics.Optimization |
||||
|
{ |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace MathNet.Numerics.Optimization |
||||
|
{ |
||||
|
class StrongWolfeLineSearch |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
using MathNet.Numerics.LinearAlgebra; |
||||
|
|
||||
|
namespace MathNet.Numerics.Optimization |
||||
|
{ |
||||
|
public class WeakWolfeLineSearch |
||||
|
{ |
||||
|
public double C1 { get; set; } |
||||
|
public double C2 { get; set; } |
||||
|
|
||||
|
public WeakWolfeLineSearch(double c1, double c2) |
||||
|
{ |
||||
|
this.C1 = c1; |
||||
|
this.C2 = c2; |
||||
|
} |
||||
|
|
||||
|
// Implemented following http://www.math.washington.edu/~burke/crs/408/lectures/L9-weak-Wolfe.pdf
|
||||
|
public MinimizationOutput FindConformingStep(IObjectiveFunction objective, IEvaluation starting_point, Vector<double> search_direction, double initial_step) |
||||
|
{ |
||||
|
double lower_bound = 0.0; |
||||
|
double upper_bound = Double.PositiveInfinity; |
||||
|
double step = initial_step; |
||||
|
|
||||
|
double initial_value = starting_point.Value; |
||||
|
Vector<double> initial_gradient = starting_point.Gradient; |
||||
|
|
||||
|
double initial_dd = search_direction*initial_gradient; |
||||
|
|
||||
|
int ii; |
||||
|
IEvaluation candidate_eval = null; |
||||
|
for (ii = 0; ii < 10; ++ii) |
||||
|
{ |
||||
|
candidate_eval = objective.Evaluate(starting_point.Point + search_direction * step); |
||||
|
|
||||
|
double step_dd = search_direction * candidate_eval.Gradient; |
||||
|
|
||||
|
if (candidate_eval.Value > initial_value + this.C1 * step * initial_dd) |
||||
|
{ |
||||
|
upper_bound = step; |
||||
|
step = 0.5 * (lower_bound + upper_bound); |
||||
|
} |
||||
|
else if (step_dd < this.C2*initial_dd) |
||||
|
{ |
||||
|
lower_bound = step; |
||||
|
step = Double.IsPositiveInfinity(upper_bound) ? 2 * lower_bound : 0.5 * (lower_bound + upper_bound); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (ii == 10) |
||||
|
throw new Exception("Line search failed with max iterations. Function is likely unbounded in search direction."); |
||||
|
else |
||||
|
return new MinimizationOutput(candidate_eval, ii); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
using MathNet.Numerics.LinearAlgebra; |
||||
|
|
||||
|
namespace MathNet.Numerics.UnitTests.OptimizationTests |
||||
|
{ |
||||
|
public static class RosenbrockFunction |
||||
|
{ |
||||
|
public static double Value(Vector<double> input) |
||||
|
{ |
||||
|
return Math.Pow((1 - input[0]), 2) + 100 * Math.Pow((input[1] - input[0] * input[0]), 2); |
||||
|
} |
||||
|
|
||||
|
public static Vector<double> Gradient(Vector<double> input) |
||||
|
{ |
||||
|
Vector<double> output = new MathNet.Numerics.LinearAlgebra.Double.DenseVector(2); |
||||
|
output[0] = -2 * (1 - input[0]) + 2 * 100 * (input[1] - input[0] * input[0]) * (-2 * input[0]); |
||||
|
output[1] = 2 * 100 * (input[1] - input[0] * input[0]); |
||||
|
return output; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using NUnit.Framework; |
||||
|
|
||||
|
using MathNet.Numerics.Optimization; |
||||
|
|
||||
|
namespace MathNet.Numerics.UnitTests.OptimizationTests |
||||
|
{ |
||||
|
[TestFixture] |
||||
|
class TestBisectionRootFinder |
||||
|
{ |
||||
|
[Test] |
||||
|
public void FindRoot_Works() |
||||
|
{ |
||||
|
var algorithm = new BisectionRootFinder(0.001, 0.001); |
||||
|
var f1 = new Func<double, double>((x) => (x - 3) * (x - 4)); |
||||
|
var r1 = algorithm.FindRoot(f1, 2.1, 3.9); |
||||
|
Assert.That(Math.Abs(f1(r1)), Is.LessThan(0.001)); |
||||
|
Assert.That(Math.Abs(r1 - 3.0), Is.LessThan(0.001)); |
||||
|
|
||||
|
var f2 = new Func<double, double>((x) => (x - 3) * (x - 4)); |
||||
|
var r2 = algorithm.FindRoot(f1, 2.1, 3.4); |
||||
|
Assert.That(Math.Abs(f2(r2)), Is.LessThan(0.001)); |
||||
|
Assert.That(Math.Abs(r2 - 3.0), Is.LessThan(0.001)); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
using NUnit.Framework; |
||||
|
|
||||
|
using MathNet.Numerics.Optimization; |
||||
|
|
||||
|
namespace MathNet.Numerics.UnitTests.OptimizationTests |
||||
|
{ |
||||
|
[TestFixture] |
||||
|
public class TestConjugateGradientMinimizer |
||||
|
{ |
||||
|
|
||||
|
[Test] |
||||
|
public void FindMinimum_Rosenbrock_Easy() |
||||
|
{ |
||||
|
var obj = new SimpleObjectiveFunction(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
||||
|
var solver = new ConjugateGradientMinimizer(1e-5, 100); |
||||
|
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[]{1.2,1.2})); |
||||
|
Assert.That(result.MinimizingPoint[0], Is.EqualTo(1.0)); |
||||
|
Assert.That(result.MinimizingPoint[1], Is.EqualTo(1.0)); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void FindMinimum_Rosenbrock_Hard() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue