Browse Source

Optimization: Try design with unifed objective function/evaluation concepts

pull/489/head
Scott Stephens 12 years ago
committed by Erik Ovegard
parent
commit
01055c4bcb
  1. 2
      src/Numerics/Numerics.csproj
  2. 40
      src/Numerics/Optimization/BaseEvaluation.cs
  3. 39
      src/Numerics/Optimization/BaseObjectiveFunction.cs
  4. 11
      src/Numerics/Optimization/IEvaluation.cs
  5. 18
      src/Numerics/Optimization/IObjectiveFunction.cs
  6. 2
      src/Numerics/Optimization/IUnconstrainedMinimizer.cs
  7. 7
      src/Numerics/Optimization/Implementation/NullEvaluation.cs
  8. 89
      src/Numerics/Optimization/Implementation/ObjectiveChecker.cs
  9. 10
      src/Numerics/Optimization/Implementation/WeakWolfeLineSearch.cs
  10. 12
      src/Numerics/Optimization/NewtonMinimizer.cs
  11. 23
      src/UnitTests/OptimizationTests/TestNewtonMinimizer.cs

2
src/Numerics/Numerics.csproj

@ -122,8 +122,6 @@
<Compile Include="Optimization\Implementation\NullEvaluation.cs" />
<Compile Include="Optimization\Implementation\ObjectiveChecker.cs" />
<Compile Include="Optimization\Implementation\WeakWolfeLineSearch.cs" />
<Compile Include="Optimization\IObjectiveFunction.cs" />
<Compile Include="Optimization\BaseObjectiveFunction.cs" />
<Compile Include="Optimization\IUnconstrainedMinimizer.cs" />
<Compile Include="Optimization\MinimizationOutput.cs" />
<Compile Include="Optimization\MinimizationWithLineSearchOutput.cs" />

40
src/Numerics/Optimization/BaseEvaluation.cs

@ -9,15 +9,34 @@ namespace MathNet.Numerics.Optimization
{
public abstract class BaseEvaluation : IEvaluation
{
public EvaluationStatus Status { get; set; }
public Vector<double> Point { get; set; }
public double ValueRaw { get; set; }
public Vector<double> GradientRaw { get; set; }
public Matrix<double> HessianRaw { get; set; }
protected BaseEvaluation()
public EvaluationStatus Status { get; protected set; }
protected Vector<double> PointRaw { get; set; }
protected double ValueRaw { get; set; }
protected Vector<double> GradientRaw { get; set; }
protected Matrix<double> HessianRaw { get; set; }
public bool GradientSupported { get; private set; }
public bool HessianSupported { get; private set; }
protected BaseEvaluation(bool gradient_supported, bool hessian_supported)
{
Status = EvaluationStatus.None;
this.GradientSupported = gradient_supported;
this.HessianSupported = hessian_supported;
}
public Vector<double> Point
{
get
{
return this.PointRaw;
}
set
{
this.PointRaw = value;
this.Status = EvaluationStatus.None;
}
}
public double Value
@ -57,14 +76,9 @@ namespace MathNet.Numerics.Optimization
}
}
public void Reset(Vector<double> new_point)
{
this.Point = new_point;
this.Status = EvaluationStatus.None;
}
protected abstract void setValue();
protected abstract void setGradient();
protected abstract void setHessian();
public abstract IEvaluation CreateNew();
}
}

39
src/Numerics/Optimization/BaseObjectiveFunction.cs

@ -1,39 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MathNet.Numerics.Optimization
{
public class BaseObjectiveFunction<T> : IObjectiveFunction where T : IEvaluation
{
public BaseObjectiveFunction(bool gradient_supported, bool hessian_supported)
{
_gradient_supported = gradient_supported;
_hessian_supported = hessian_supported;
}
private bool _gradient_supported;
private bool _hessian_supported;
public bool GradientSupported
{
get { return _gradient_supported; }
}
public bool HessianSupported
{
get { return _hessian_supported; }
}
public void Evaluate(LinearAlgebra.Vector<double> point, IEvaluation output)
{
output.Reset(point);
}
public virtual IEvaluation CreateEvaluationObject()
{
return default(T);
}
}
}

11
src/Numerics/Optimization/IEvaluation.cs

@ -12,17 +12,14 @@ namespace MathNet.Numerics.Optimization
public interface IEvaluation
{
Vector<double> Point { get; set; }
EvaluationStatus Status { get; set; }
IEvaluation CreateNew();
// Used by algorithm
bool GradientSupported { get; }
bool HessianSupported { get; }
EvaluationStatus Status { get; }
double Value { get; }
Vector<double> Gradient { get; }
Matrix<double> Hessian { get; }
// Used by ObjectiveFunction
void Reset(Vector<double> new_point);
double ValueRaw { get; set; }
Vector<double> GradientRaw { get; set; }
Matrix<double> HessianRaw { get; set; }
}
}

18
src/Numerics/Optimization/IObjectiveFunction.cs

@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MathNet.Numerics.LinearAlgebra;
namespace MathNet.Numerics.Optimization
{
public interface IObjectiveFunction
{
bool GradientSupported { get; }
bool HessianSupported { get; }
IEvaluation CreateEvaluationObject();
void Evaluate(Vector<double> point, IEvaluation output);
}
}

2
src/Numerics/Optimization/IUnconstrainedMinimizer.cs

@ -8,7 +8,7 @@ namespace MathNet.Numerics.Optimization
{
public interface IUnconstrainedMinimizer
{
MinimizationOutput FindMinimum(IObjectiveFunction objective, Vector<double> initial_guess);
MinimizationOutput FindMinimum(IEvaluation objective, Vector<double> initial_guess);
}
}

7
src/Numerics/Optimization/Implementation/NullEvaluation.cs

@ -10,7 +10,7 @@ namespace MathNet.Numerics.Optimization.Implementation
public class NullEvaluation : BaseEvaluation
{
public NullEvaluation(Vector<double> point)
: base()
: base(false, false)
{
this.Point = point;
}
@ -28,5 +28,10 @@ namespace MathNet.Numerics.Optimization.Implementation
{
throw new NotImplementedException();
}
public override IEvaluation CreateNew()
{
throw new NotImplementedException();
}
}
}

89
src/Numerics/Optimization/Implementation/ObjectiveChecker.cs

@ -8,16 +8,21 @@ namespace MathNet.Numerics.Optimization.Implementation
{
public class CheckedEvaluation : IEvaluation
{
private ObjectiveChecker Checker;
public IEvaluation InnerEvaluation { get; private set; }
private bool ValueChecked;
private bool GradientChecked;
private bool HessianChecked;
public Action<IEvaluation> ValueChecker { get; private set; }
public Action<IEvaluation> GradientChecker { get; private set; }
public Action<IEvaluation> HessianChecker { get; private set; }
public CheckedEvaluation(ObjectiveChecker checker, IEvaluation evaluation)
public CheckedEvaluation(IEvaluation objective, Action<IEvaluation> value_checker, Action<IEvaluation> gradient_checker, Action<IEvaluation> hessian_checker)
{
this.Checker = checker;
this.InnerEvaluation = evaluation;
this.InnerEvaluation = objective;
this.ValueChecker = value_checker;
this.GradientChecker = gradient_checker;
this.HessianChecker = hessian_checker;
}
public Vector<double> Point
@ -32,34 +37,6 @@ namespace MathNet.Numerics.Optimization.Implementation
{
return this.InnerEvaluation.Status;
}
set
{
this.InnerEvaluation.Status = value;
}
}
public double ValueRaw
{
get
{
return this.InnerEvaluation.Value;
}
set
{
this.InnerEvaluation.ValueRaw = value;
}
}
public Vector<double> GradientRaw
{
get { return this.InnerEvaluation.GradientRaw; }
set { this.InnerEvaluation.GradientRaw = value; }
}
public Matrix<double> HessianRaw
{
get { return this.InnerEvaluation.HessianRaw; }
set { this.InnerEvaluation.HessianRaw = value; }
}
public double Value
@ -78,7 +55,7 @@ namespace MathNet.Numerics.Optimization.Implementation
{
throw new EvaluationException("Objective function evaluation failed.", this.InnerEvaluation, e);
}
this.Checker.ValueChecker(this.InnerEvaluation);
this.ValueChecker(this.InnerEvaluation);
this.ValueChecked = true;
}
return this.InnerEvaluation.Value;
@ -101,7 +78,7 @@ namespace MathNet.Numerics.Optimization.Implementation
{
throw new EvaluationException("Objective gradient evaluation failed.", this.InnerEvaluation, e);
}
this.Checker.GradientChecker(this.InnerEvaluation);
this.GradientChecker(this.InnerEvaluation);
this.GradientChecked = true;
}
return this.InnerEvaluation.Gradient;
@ -124,60 +101,26 @@ namespace MathNet.Numerics.Optimization.Implementation
{
throw new EvaluationException("Objective hessian evaluation failed.", this.InnerEvaluation, e);
}
this.Checker.HessianChecker(InnerEvaluation);
this.HessianChecker(InnerEvaluation);
this.HessianChecked = true;
}
return this.InnerEvaluation.Hessian;
}
}
public void Reset(Vector<double> new_point)
public IEvaluation CreateNew()
{
this.InnerEvaluation.Reset(new_point);
}
}
public class ObjectiveChecker : IObjectiveFunction
{
public IObjectiveFunction InnerObjective { get; private set; }
public Action<IEvaluation> ValueChecker { get; private set; }
public Action<IEvaluation> GradientChecker { get; private set; }
public Action<IEvaluation> HessianChecker { get; private set; }
public ObjectiveChecker(IObjectiveFunction objective, Action<IEvaluation> value_checker, Action<IEvaluation> gradient_checker, Action<IEvaluation> hessian_checker)
{
this.InnerObjective = objective;
this.ValueChecker = value_checker;
this.GradientChecker = gradient_checker;
this.HessianChecker = hessian_checker;
return new CheckedEvaluation(this.InnerEvaluation, this.ValueChecker, this.GradientChecker, this.HessianChecker);
}
public bool GradientSupported
{
get { return this.InnerObjective.GradientSupported; }
get { return this.InnerEvaluation.GradientSupported; }
}
public bool HessianSupported
{
get { return this.InnerObjective.HessianSupported; }
}
public void Evaluate(Vector<double> point, IEvaluation output)
{
try
{
this.InnerObjective.Evaluate(point, output);
}
catch (Exception e)
{
throw new EvaluationException("Objective evaluation failed.", new NullEvaluation(point), e);
}
}
public IEvaluation CreateEvaluationObject()
{
return this.InnerObjective.CreateEvaluationObject();
get { return this.InnerEvaluation.HessianSupported; }
}
}
}

10
src/Numerics/Optimization/Implementation/WeakWolfeLineSearch.cs

@ -23,11 +23,11 @@ namespace MathNet.Numerics.Optimization.Implementation
}
// Implemented following http://www.math.washington.edu/~burke/crs/408/lectures/L9-weak-Wolfe.pdf
public LineSearchOutput FindConformingStep(IObjectiveFunction objective, IEvaluation starting_point, Vector<double> search_direction, double initial_step)
public LineSearchOutput FindConformingStep(IEvaluation objective, IEvaluation starting_point, Vector<double> search_direction, double initial_step)
{
if (!(objective is ObjectiveChecker))
objective = new ObjectiveChecker(objective, this.ValidateValue, this.ValidateGradient, null);
if (!(objective is CheckedEvaluation))
objective = new CheckedEvaluation(objective, this.ValidateValue, this.ValidateGradient, null);
double lower_bound = 0.0;
double upper_bound = Double.PositiveInfinity;
@ -39,11 +39,11 @@ namespace MathNet.Numerics.Optimization.Implementation
double initial_dd = search_direction * initial_gradient;
int ii;
IEvaluation candidate_eval = objective.CreateEvaluationObject();
IEvaluation candidate_eval = objective.CreateNew();
MinimizationOutput.ExitCondition reason_for_exit = MinimizationOutput.ExitCondition.None;
for (ii = 0; ii < this.MaximumIterations; ++ii)
{
objective.Evaluate(starting_point.Point + search_direction * step, candidate_eval);
candidate_eval.Point = starting_point.Point + search_direction * step;
double step_dd = search_direction * candidate_eval.Gradient;

12
src/Numerics/Optimization/NewtonMinimizer.cs

@ -21,7 +21,7 @@ namespace MathNet.Numerics.Optimization
this.UseLineSearch = use_line_search;
}
public MinimizationOutput FindMinimum(IObjectiveFunction objective, Vector<double> initial_guess)
public MinimizationOutput FindMinimum(IEvaluation objective, Vector<double> initial_guess)
{
if (!objective.GradientSupported)
throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for Newton minimization.");
@ -29,11 +29,11 @@ namespace MathNet.Numerics.Optimization
if (!objective.HessianSupported)
throw new IncompatibleObjectiveException("Hessian not supported in objective function, but required for Newton minimization.");
if (!(objective is ObjectiveChecker))
objective = new ObjectiveChecker(objective, this.ValidateObjective, this.ValidateGradient, this.ValidateHessian);
if (!(objective is CheckedEvaluation))
objective = new CheckedEvaluation(objective, this.ValidateObjective, this.ValidateGradient, this.ValidateHessian);
IEvaluation initial_eval = objective.CreateEvaluationObject();
objective.Evaluate(initial_guess, initial_eval);
IEvaluation initial_eval = objective.CreateNew();
initial_eval.Point = initial_guess;
// Check that we're not already done
if (this.ExitCriteriaSatisfied(initial_guess, initial_eval.Gradient))
@ -81,7 +81,7 @@ namespace MathNet.Numerics.Optimization
}
else
{
objective.Evaluate(candidate_point.Point + search_direction, candidate_point);
candidate_point.Point = candidate_point.Point + search_direction;
}
tmp_line_search = false;

23
src/UnitTests/OptimizationTests/TestNewtonMinimizer.cs

@ -11,9 +11,9 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
{
public class RosenbrockEvaluation : BaseEvaluation
{
public const bool SupportsGradient = true;
public const bool SupportsHessian = true;
public RosenbrockEvaluation()
: base(true, true) { }
protected override void setValue()
{
this.ValueRaw = RosenbrockFunction.Value(this.Point);
@ -28,6 +28,11 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
{
this.HessianRaw = RosenbrockFunction.Hessian(this.Point);
}
public override IEvaluation CreateNew()
{
return new RosenbrockEvaluation();
}
}
[TestFixture]
@ -37,7 +42,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[Test]
public void FindMinimum_Rosenbrock_Easy()
{
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian);
var obj = new RosenbrockEvaluation();
var solver = new NewtonMinimizer(1e-5, 1000);
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { 1.2, 1.2 }));
@ -49,7 +54,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[Test]
public void FindMinimum_Rosenbrock_Hard()
{
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian);
var obj = new RosenbrockEvaluation();
var solver = new NewtonMinimizer(1e-5, 1000);
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { -1.2, 1.0 }));
@ -60,7 +65,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[Test]
public void FindMinimum_Rosenbrock_Overton()
{
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian);
var obj = new RosenbrockEvaluation();
var solver = new NewtonMinimizer(1e-5, 1000);
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { -0.9, -0.5 }));
@ -71,7 +76,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[Test]
public void FindMinimum_Linesearch_Rosenbrock_Easy()
{
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian);
var obj = new RosenbrockEvaluation();
var solver = new NewtonMinimizer(1e-5, 1000, true);
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { 1.2, 1.2 }));
@ -82,7 +87,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[Test]
public void FindMinimum_Linesearch_Rosenbrock_Hard()
{
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian);
var obj = new RosenbrockEvaluation();
var solver = new NewtonMinimizer(1e-5, 1000, true);
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { -1.2, 1.0 }));
@ -93,7 +98,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[Test]
public void FindMinimum_Linesearch_Rosenbrock_Overton()
{
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian);
var obj = new RosenbrockEvaluation();
var solver = new NewtonMinimizer(1e-5, 1000, true);
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { -0.9, -0.5 }));

Loading…
Cancel
Save