Browse Source

Optimization: lambda objective functions (both greedy and lazy)

pull/489/head
Christoph Ruegg 11 years ago
committed by Erik Ovegard
parent
commit
b91d40dcaf
  1. 6
      src/Numerics/Numerics.csproj
  2. 65
      src/Numerics/Optimization/ObjectiveFunction.cs
  3. 57
      src/Numerics/Optimization/ObjectiveFunctions/GradientHessianObjectiveFunction.cs
  4. 59
      src/Numerics/Optimization/ObjectiveFunctions/GradientObjectiveFunction.cs
  5. 59
      src/Numerics/Optimization/ObjectiveFunctions/HessianObjectiveFunction.cs
  6. 112
      src/Numerics/Optimization/ObjectiveFunctions/LazyObjectiveFunction.cs
  7. 59
      src/Numerics/Optimization/ObjectiveFunctions/ValueObjectiveFunction.cs
  8. 6
      src/UnitTests/OptimizationTests/TestNewtonMinimizer.cs

6
src/Numerics/Numerics.csproj

@ -118,6 +118,12 @@
<Compile Include="Optimization\BaseEvaluation.cs" /> <Compile Include="Optimization\BaseEvaluation.cs" />
<Compile Include="Optimization\BaseObjectiveFunction.cs" /> <Compile Include="Optimization\BaseObjectiveFunction.cs" />
<Compile Include="Optimization\Exceptions.cs" /> <Compile Include="Optimization\Exceptions.cs" />
<Compile Include="Optimization\ObjectiveFunctions\LazyObjectiveFunction.cs" />
<Compile Include="Optimization\ObjectiveFunction.cs" />
<Compile Include="Optimization\ObjectiveFunctions\ValueObjectiveFunction.cs" />
<Compile Include="Optimization\ObjectiveFunctions\HessianObjectiveFunction.cs" />
<Compile Include="Optimization\ObjectiveFunctions\GradientObjectiveFunction.cs" />
<Compile Include="Optimization\ObjectiveFunctions\GradientHessianObjectiveFunction.cs" />
<Compile Include="Optimization\IObjectiveFunction.cs" /> <Compile Include="Optimization\IObjectiveFunction.cs" />
<Compile Include="Optimization\Implementation\LineSearchOutput.cs" /> <Compile Include="Optimization\Implementation\LineSearchOutput.cs" />
<Compile Include="Optimization\Implementation\CheckedObjectiveFunction.cs" /> <Compile Include="Optimization\Implementation\CheckedObjectiveFunction.cs" />

65
src/Numerics/Optimization/ObjectiveFunction.cs

@ -0,0 +1,65 @@
using System;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.Optimization.ObjectiveFunctions;
namespace MathNet.Numerics.Optimization
{
public static class ObjectiveFunction
{
/// <summary>
/// Objective function where neither Gradient nor Hessian is available.
/// </summary>
public static IObjectiveFunction Value(Func<Vector<double>, double> function)
{
return new ValueObjectiveFunction(function);
}
/// <summary>
/// Objective function where the Gradient is available. Greedy evaluation.
/// </summary>
public static IObjectiveFunction Gradient(Func<Vector<double>, Tuple<double, Vector<double>>> function)
{
return new GradientObjectiveFunction(function);
}
/// <summary>
/// Objective function where the Gradient is available. Lazy evaluation.
/// </summary>
public static IObjectiveFunction Gradient(Func<Vector<double>, double> function, Func<Vector<double>, Vector<double>> gradient)
{
return new LazyObjectiveFunction(function, gradient: gradient);
}
/// <summary>
/// Objective function where the Hessian is available. Greedy evaluation.
/// </summary>
public static IObjectiveFunction Hessian(Func<Vector<double>, Tuple<double, Matrix<double>>> function)
{
return new HessianObjectiveFunction(function);
}
/// <summary>
/// Objective function where the Hessian is available. Lazy evaluation.
/// </summary>
public static IObjectiveFunction Hessian(Func<Vector<double>, double> function, Func<Vector<double>, Matrix<double>> hessian)
{
return new LazyObjectiveFunction(function, hessian: hessian);
}
/// <summary>
/// Objective function where both Gradient and Hessian are available. Greedy evaluation.
/// </summary>
public static IObjectiveFunction GradientHessian(Func<Vector<double>, Tuple<double, Vector<double>, Matrix<double>>> function)
{
return new GradientHessianObjectiveFunction(function);
}
/// <summary>
/// Objective function where both Gradient and Hessian are available. Lazy evaluation.
/// </summary>
public static IObjectiveFunction GradientHessian(Func<Vector<double>, double> function, Func<Vector<double>, Vector<double>> gradient, Func<Vector<double>, Matrix<double>> hessian)
{
return new LazyObjectiveFunction(function, gradient: gradient, hessian: hessian);
}
}
}

57
src/Numerics/Optimization/ObjectiveFunctions/GradientHessianObjectiveFunction.cs

@ -0,0 +1,57 @@
using System;
using MathNet.Numerics.LinearAlgebra;
namespace MathNet.Numerics.Optimization.ObjectiveFunctions
{
internal class GradientHessianObjectiveFunction : IObjectiveFunction
{
readonly Func<Vector<double>, Tuple<double, Vector<double>, Matrix<double>>> _function;
public GradientHessianObjectiveFunction(Func<Vector<double>, Tuple<double, Vector<double>, Matrix<double>>> function)
{
_function = function;
}
public IObjectiveFunction CreateNew()
{
return new GradientHessianObjectiveFunction(_function);
}
public IObjectiveFunction Fork()
{
// no need to deep-clone values since they are replaced on evaluation
return new GradientHessianObjectiveFunction(_function)
{
Point = Point,
Value = Value,
Gradient = Gradient,
Hessian = Hessian
};
}
public bool IsGradientSupported
{
get { return true; }
}
public bool IsHessianSupported
{
get { return true; }
}
public void EvaluateAt(Vector<double> point)
{
Point = point;
var result = _function(point);
Value = result.Item1;
Gradient = result.Item2;
Hessian = result.Item3;
}
public Vector<double> Point { get; private set; }
public double Value { get; private set; }
public Vector<double> Gradient { get; private set; }
public Matrix<double> Hessian { get; private set; }
}
}

59
src/Numerics/Optimization/ObjectiveFunctions/GradientObjectiveFunction.cs

@ -0,0 +1,59 @@
using System;
using MathNet.Numerics.LinearAlgebra;
namespace MathNet.Numerics.Optimization.ObjectiveFunctions
{
internal class GradientObjectiveFunction : IObjectiveFunction
{
readonly Func<Vector<double>, Tuple<double, Vector<double>>> _function;
public GradientObjectiveFunction(Func<Vector<double>, Tuple<double, Vector<double>>> function)
{
_function = function;
}
public IObjectiveFunction CreateNew()
{
return new GradientObjectiveFunction(_function);
}
public IObjectiveFunction Fork()
{
// no need to deep-clone values since they are replaced on evaluation
return new GradientObjectiveFunction(_function)
{
Point = Point,
Value = Value,
Gradient = Gradient
};
}
public bool IsGradientSupported
{
get { return true; }
}
public bool IsHessianSupported
{
get { return false; }
}
public void EvaluateAt(Vector<double> point)
{
Point = point;
var result = _function(point);
Value = result.Item1;
Gradient = result.Item2;
}
public Vector<double> Point { get; private set; }
public double Value { get; private set; }
public Vector<double> Gradient { get; private set; }
public Matrix<double> Hessian
{
get { throw new NotSupportedException(); }
}
}
}

59
src/Numerics/Optimization/ObjectiveFunctions/HessianObjectiveFunction.cs

@ -0,0 +1,59 @@
using System;
using MathNet.Numerics.LinearAlgebra;
namespace MathNet.Numerics.Optimization.ObjectiveFunctions
{
internal class HessianObjectiveFunction : IObjectiveFunction
{
readonly Func<Vector<double>, Tuple<double, Matrix<double>>> _function;
public HessianObjectiveFunction(Func<Vector<double>, Tuple<double, Matrix<double>>> function)
{
_function = function;
}
public IObjectiveFunction CreateNew()
{
return new HessianObjectiveFunction(_function);
}
public IObjectiveFunction Fork()
{
// no need to deep-clone values since they are replaced on evaluation
return new HessianObjectiveFunction(_function)
{
Point = Point,
Value = Value,
Hessian = Hessian
};
}
public bool IsGradientSupported
{
get { return false; }
}
public bool IsHessianSupported
{
get { return true; }
}
public void EvaluateAt(Vector<double> point)
{
Point = point;
var result = _function(point);
Value = result.Item1;
Hessian = result.Item2;
}
public Vector<double> Point { get; private set; }
public double Value { get; private set; }
public Matrix<double> Hessian { get; private set; }
public Vector<double> Gradient
{
get { throw new NotSupportedException(); }
}
}
}

112
src/Numerics/Optimization/ObjectiveFunctions/LazyObjectiveFunction.cs

@ -0,0 +1,112 @@
using System;
using MathNet.Numerics.LinearAlgebra;
namespace MathNet.Numerics.Optimization.ObjectiveFunctions
{
internal class LazyObjectiveFunction : IObjectiveFunction
{
readonly Func<Vector<double>, double> _function;
readonly Func<Vector<double>, Vector<double>> _gradient;
readonly Func<Vector<double>, Matrix<double>> _hessian;
Vector<double> _point;
bool _hasFunctionValue;
double _functionValue;
bool _hasGradientValue;
Vector<double> _gradientValue;
bool _hasHessianValue;
Matrix<double> _hessianValue;
public LazyObjectiveFunction(Func<Vector<double>, double> function, Func<Vector<double>, Vector<double>> gradient = null, Func<Vector<double>, Matrix<double>> hessian = null)
{
_function = function;
_gradient = gradient;
_hessian = hessian;
IsGradientSupported = gradient != null;
IsHessianSupported = hessian != null;
}
public IObjectiveFunction CreateNew()
{
return new LazyObjectiveFunction(_function, _gradient, _hessian);
}
public IObjectiveFunction Fork()
{
// no need to deep-clone values since they are replaced on evaluation
return new LazyObjectiveFunction(_function, _gradient, _hessian)
{
_point = _point,
_hasFunctionValue = _hasFunctionValue,
_functionValue = _functionValue,
_hasGradientValue = _hasGradientValue,
_gradientValue = _gradientValue,
_hasHessianValue = _hasHessianValue,
_hessianValue = _hessianValue
};
}
public bool IsGradientSupported { get; private set; }
public bool IsHessianSupported { get; private set; }
public void EvaluateAt(Vector<double> point)
{
_point = point;
_hasFunctionValue = false;
_hasGradientValue = false;
_hasHessianValue = false;
// don't keep references unnecessarily
_gradientValue = null;
_hessianValue = null;
}
public Vector<double> Point
{
get { return _point; }
}
public double Value
{
get
{
if (!_hasFunctionValue)
{
_functionValue = _function(_point);
_hasFunctionValue = true;
}
return _functionValue;
}
}
public Vector<double> Gradient
{
get
{
if (!_hasGradientValue)
{
_gradientValue = _gradient(_point);
_hasGradientValue = true;
}
return _gradientValue;
}
}
public Matrix<double> Hessian
{
get
{
if (!_hasHessianValue)
{
_hessianValue = _hessian(_point);
_hasHessianValue = true;
}
return _hessianValue;
}
}
}
}

59
src/Numerics/Optimization/ObjectiveFunctions/ValueObjectiveFunction.cs

@ -0,0 +1,59 @@
using System;
using MathNet.Numerics.LinearAlgebra;
namespace MathNet.Numerics.Optimization.ObjectiveFunctions
{
internal class ValueObjectiveFunction : IObjectiveFunction
{
readonly Func<Vector<double>, double> _function;
public ValueObjectiveFunction(Func<Vector<double>, double> function)
{
_function = function;
}
public IObjectiveFunction CreateNew()
{
return new ValueObjectiveFunction(_function);
}
public IObjectiveFunction Fork()
{
// no need to deep-clone values since they are replaced on evaluation
return new ValueObjectiveFunction(_function)
{
Point = Point,
Value = Value,
};
}
public bool IsGradientSupported
{
get { return false; }
}
public bool IsHessianSupported
{
get { return false; }
}
public void EvaluateAt(Vector<double> point)
{
Point = point;
Value = _function(point);
}
public Vector<double> Point { get; private set; }
public double Value { get; private set; }
public Matrix<double> Hessian
{
get { throw new NotSupportedException(); }
}
public Vector<double> Gradient
{
get { throw new NotSupportedException(); }
}
}
}

6
src/UnitTests/OptimizationTests/TestNewtonMinimizer.cs

@ -34,12 +34,10 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[TestFixture] [TestFixture]
public class TestNewtonMinimizer public class TestNewtonMinimizer
{ {
[Test] [Test]
public void FindMinimum_Rosenbrock_Easy() public void FindMinimum_Rosenbrock_Easy()
{ {
var obj = new RosenbrockObjectiveFunction(); var obj = ObjectiveFunction.GradientHessian(RosenbrockFunction.Value, RosenbrockFunction.Gradient, RosenbrockFunction.Hessian);
var solver = new NewtonMinimizer(1e-5, 1000); var solver = new NewtonMinimizer(1e-5, 1000);
var result = solver.FindMinimum(obj, new DenseVector(new[] { 1.2, 1.2 })); var result = solver.FindMinimum(obj, new DenseVector(new[] { 1.2, 1.2 }));
@ -50,7 +48,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[Test] [Test]
public void FindMinimum_Rosenbrock_Hard() public void FindMinimum_Rosenbrock_Hard()
{ {
var obj = new RosenbrockObjectiveFunction(); var obj = ObjectiveFunction.GradientHessian(point => Tuple.Create(RosenbrockFunction.Value(point), RosenbrockFunction.Gradient(point), RosenbrockFunction.Hessian(point)));
var solver = new NewtonMinimizer(1e-5, 1000); var solver = new NewtonMinimizer(1e-5, 1000);
var result = solver.FindMinimum(obj, new DenseVector(new[] { -1.2, 1.0 })); var result = solver.FindMinimum(obj, new DenseVector(new[] { -1.2, 1.0 }));

Loading…
Cancel
Save