Browse Source

Optimization: (Lazy)ObjectiveFunction base classes

pull/391/head
Christoph Ruegg 11 years ago
parent
commit
330a577f19
  1. 4
      src/Numerics/Numerics.csproj
  2. 99
      src/Numerics/Optimization/BaseObjectiveFunction.cs
  3. 62
      src/Numerics/Optimization/ObjectiveFunctions/InplaceObjectiveFunction.cs
  4. 119
      src/Numerics/Optimization/ObjectiveFunctions/LazyObjectiveFunctionBase.cs
  5. 42
      src/Numerics/Optimization/ObjectiveFunctions/ObjectiveFunctionBase.cs
  6. 47
      src/UnitTests/OptimizationTests/TestNewtonMinimizer.cs

4
src/Numerics/Numerics.csproj

@ -102,9 +102,9 @@
<Compile Include="LinearAlgebra\Solvers\DelegateStopCriterion.cs" /> <Compile Include="LinearAlgebra\Solvers\DelegateStopCriterion.cs" />
<Compile Include="LinearAlgebra\CreateVector.cs" /> <Compile Include="LinearAlgebra\CreateVector.cs" />
<Compile Include="LinearRegression\Options.cs" /> <Compile Include="LinearRegression\Options.cs" />
<Compile Include="Optimization\BaseObjectiveFunction.cs" /> <Compile Include="Optimization\ObjectiveFunctions\LazyObjectiveFunctionBase.cs" />
<Compile Include="Optimization\Exceptions.cs" /> <Compile Include="Optimization\Exceptions.cs" />
<Compile Include="Optimization\ObjectiveFunctions\InplaceObjectiveFunction.cs" /> <Compile Include="Optimization\ObjectiveFunctions\ObjectiveFunctionBase.cs" />
<Compile Include="Optimization\ObjectiveFunctions\LazyObjectiveFunction.cs" /> <Compile Include="Optimization\ObjectiveFunctions\LazyObjectiveFunction.cs" />
<Compile Include="Optimization\ObjectiveFunction.cs" /> <Compile Include="Optimization\ObjectiveFunction.cs" />
<Compile Include="Optimization\ObjectiveFunctions\ValueObjectiveFunction.cs" /> <Compile Include="Optimization\ObjectiveFunctions\ValueObjectiveFunction.cs" />

99
src/Numerics/Optimization/BaseObjectiveFunction.cs

@ -1,99 +0,0 @@
using System;
using MathNet.Numerics.LinearAlgebra;
namespace MathNet.Numerics.Optimization
{
public abstract class BaseObjectiveFunction : IObjectiveFunction
{
[Flags]
enum EvaluationStatus
{
None = 0,
Value = 1,
Gradient = 2,
Hessian = 4
}
EvaluationStatus Status { get; 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 IsGradientSupported { get; private set; }
public bool IsHessianSupported { get; private set; }
protected BaseObjectiveFunction(bool gradientSupported, bool hessianSupported)
{
Status = EvaluationStatus.None;
IsGradientSupported = gradientSupported;
IsHessianSupported = hessianSupported;
}
public Vector<double> Point
{
get { return PointRaw; }
}
public void EvaluateAt(Vector<double> point)
{
PointRaw = point;
Status = EvaluationStatus.None;
}
public double Value
{
get
{
if (!Status.HasFlag(EvaluationStatus.Value))
{
SetValue();
Status |= EvaluationStatus.Value;
}
return ValueRaw;
}
}
public Vector<double> Gradient
{
get
{
if (!Status.HasFlag(EvaluationStatus.Gradient))
{
SetGradient();
Status |= EvaluationStatus.Gradient;
}
return GradientRaw;
}
}
public Matrix<double> Hessian
{
get
{
if (!Status.HasFlag(EvaluationStatus.Hessian))
{
SetHessian();
Status |= EvaluationStatus.Hessian;
}
return HessianRaw;
}
}
public abstract IObjectiveFunction CreateNew();
public virtual IObjectiveFunction Fork()
{
BaseObjectiveFunction fork = (BaseObjectiveFunction)CreateNew();
fork.PointRaw = PointRaw;
fork.ValueRaw = ValueRaw;
fork.GradientRaw = GradientRaw;
fork.HessianRaw = HessianRaw;
fork.Status = Status;
return fork;
}
protected abstract void SetValue();
protected abstract void SetGradient();
protected abstract void SetHessian();
}
}

62
src/Numerics/Optimization/ObjectiveFunctions/InplaceObjectiveFunction.cs

@ -1,62 +0,0 @@
using MathNet.Numerics.LinearAlgebra;
namespace MathNet.Numerics.Optimization.ObjectiveFunctions
{
public abstract class InplaceObjectiveFunction : IObjectiveFunction
{
Vector<double> _point;
double _functionValue;
Vector<double> _gradientValue;
Matrix<double> _hessianValue;
protected InplaceObjectiveFunction(bool isGradientSupported, bool isHessianSupported)
{
IsGradientSupported = isGradientSupported;
IsHessianSupported = isHessianSupported;
}
public abstract IObjectiveFunction CreateNew();
public virtual IObjectiveFunction Fork()
{
// no need to deep-clone values since they are replaced on evaluation
InplaceObjectiveFunction objective = (InplaceObjectiveFunction)CreateNew();
objective._point = _point == null ? null : _point.Clone();
objective._functionValue = _functionValue;
objective._gradientValue = _gradientValue == null ? null : _gradientValue.Clone();
objective._hessianValue = _hessianValue == null ? null : _hessianValue.Clone();
return objective;
}
public bool IsGradientSupported { get; private set; }
public bool IsHessianSupported { get; private set; }
public void EvaluateAt(Vector<double> point)
{
_point = point;
EvaluateAt(_point, ref _functionValue, ref _gradientValue, ref _hessianValue);
}
protected abstract void EvaluateAt(Vector<double> point, ref double value, ref Vector<double> gradient, ref Matrix<double> hessian);
public Vector<double> Point
{
get { return _point; }
}
public double Value
{
get { return _functionValue; }
}
public Vector<double> Gradient
{
get { return _gradientValue; }
}
public Matrix<double> Hessian
{
get { return _hessianValue; }
}
}
}

119
src/Numerics/Optimization/ObjectiveFunctions/LazyObjectiveFunctionBase.cs

@ -0,0 +1,119 @@
using MathNet.Numerics.LinearAlgebra;
namespace MathNet.Numerics.Optimization.ObjectiveFunctions
{
public abstract class LazyObjectiveFunctionBase : IObjectiveFunction
{
Vector<double> _point;
bool _hasFunctionValue;
double _functionValue;
bool _hasGradientValue;
Vector<double> _gradientValue;
bool _hasHessianValue;
Matrix<double> _hessianValue;
protected LazyObjectiveFunctionBase(bool gradientSupported, bool hessianSupported)
{
IsGradientSupported = gradientSupported;
IsHessianSupported = hessianSupported;
}
public abstract IObjectiveFunction CreateNew();
public virtual IObjectiveFunction Fork()
{
// we need to deep-clone values since they may be updated inplace on evaluation
LazyObjectiveFunctionBase fork = (LazyObjectiveFunctionBase)CreateNew();
fork._point = _point == null ? null : _point.Clone();
fork._hasFunctionValue = _hasFunctionValue;
fork._functionValue = _functionValue;
fork._hasGradientValue = _hasGradientValue;
fork._gradientValue = _gradientValue == null ? null : _gradientValue.Clone(); ;
fork._hasHessianValue = _hasHessianValue;
fork._hessianValue = _hessianValue == null ? null : _hessianValue.Clone();
return fork;
}
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;
}
protected abstract void EvaluateValue();
protected virtual void EvaluateGradient()
{
Gradient = null;
}
protected virtual void EvaluateHessian()
{
Hessian = null;
}
public Vector<double> Point
{
get { return _point; }
}
public double Value
{
get
{
if (!_hasFunctionValue)
{
EvaluateValue();
}
return _functionValue;
}
protected set
{
_functionValue = value;
_hasFunctionValue = true;
}
}
public Vector<double> Gradient
{
get
{
if (!_hasGradientValue)
{
EvaluateGradient();
}
return _gradientValue;
}
protected set
{
_gradientValue = value;
_hasGradientValue = true;
}
}
public Matrix<double> Hessian
{
get
{
if (!_hasHessianValue)
{
EvaluateHessian();
}
return _hessianValue;
}
protected set
{
_hessianValue = value;
_hasHessianValue = true;
}
}
}
}

42
src/Numerics/Optimization/ObjectiveFunctions/ObjectiveFunctionBase.cs

@ -0,0 +1,42 @@
using MathNet.Numerics.LinearAlgebra;
namespace MathNet.Numerics.Optimization.ObjectiveFunctions
{
public abstract class ObjectiveFunctionBase : IObjectiveFunction
{
protected ObjectiveFunctionBase(bool isGradientSupported, bool isHessianSupported)
{
IsGradientSupported = isGradientSupported;
IsHessianSupported = isHessianSupported;
}
public abstract IObjectiveFunction CreateNew();
public virtual IObjectiveFunction Fork()
{
// we need to deep-clone values since they may be updated inplace on evaluation
ObjectiveFunctionBase objective = (ObjectiveFunctionBase)CreateNew();
objective.Point = Point == null ? null : Point.Clone();
objective.Value = Value;
objective.Gradient = Gradient == null ? null : Gradient.Clone();
objective.Hessian = Hessian == null ? null : Hessian.Clone();
return objective;
}
public bool IsGradientSupported { get; private set; }
public bool IsHessianSupported { get; private set; }
public void EvaluateAt(Vector<double> point)
{
Point = point;
Evaluate();
}
protected abstract void Evaluate();
public Vector<double> Point { get; private set; }
public double Value { get; protected set; }
public Vector<double> Gradient { get; protected set; }
public Matrix<double> Hessian { get; protected set; }
}
}

47
src/UnitTests/OptimizationTests/TestNewtonMinimizer.cs

@ -1,5 +1,4 @@
using System; using System;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double; using MathNet.Numerics.LinearAlgebra.Double;
using MathNet.Numerics.Optimization; using MathNet.Numerics.Optimization;
using MathNet.Numerics.Optimization.ObjectiveFunctions; using MathNet.Numerics.Optimization.ObjectiveFunctions;
@ -7,47 +6,47 @@ using NUnit.Framework;
namespace MathNet.Numerics.UnitTests.OptimizationTests namespace MathNet.Numerics.UnitTests.OptimizationTests
{ {
public class RosenbrockObjectiveFunction : BaseObjectiveFunction public class LazyRosenbrockObjectiveFunction : LazyObjectiveFunctionBase
{ {
public RosenbrockObjectiveFunction() : base(true, true) { } public LazyRosenbrockObjectiveFunction() : base(true, true) { }
protected override void SetValue() public override IObjectiveFunction CreateNew()
{ {
ValueRaw = RosenbrockFunction.Value(Point); return new LazyRosenbrockObjectiveFunction();
} }
protected override void SetGradient() protected override void EvaluateValue()
{ {
GradientRaw = RosenbrockFunction.Gradient(Point); Value = RosenbrockFunction.Value(Point);
} }
protected override void SetHessian() protected override void EvaluateGradient()
{ {
HessianRaw = RosenbrockFunction.Hessian(Point); Gradient = RosenbrockFunction.Gradient(Point);
} }
public override IObjectiveFunction CreateNew() protected override void EvaluateHessian()
{ {
return new RosenbrockObjectiveFunction(); Hessian = RosenbrockFunction.Hessian(Point);
} }
} }
public class InplaceRosenbrockObjectiveFunction : InplaceObjectiveFunction public class RosenbrockObjectiveFunction : ObjectiveFunctionBase
{ {
public InplaceRosenbrockObjectiveFunction() : base(true, true) { } public RosenbrockObjectiveFunction() : base(true, true) { }
public override IObjectiveFunction CreateNew() public override IObjectiveFunction CreateNew()
{ {
return new InplaceRosenbrockObjectiveFunction(); return new RosenbrockObjectiveFunction();
} }
protected override void EvaluateAt(Vector<double> point, ref double value, ref Vector<double> gradient, ref Matrix<double> hessian) protected override void Evaluate()
{ {
// here we could directly overwrite the existing matrices instead. // here we could directly overwrite the existing matrix cells instead.
// note: values must then be initialized manually here first, if null. // note: values must then be initialized manually first, if null.
value = RosenbrockFunction.Value(point); Value = RosenbrockFunction.Value(Point);
gradient = RosenbrockFunction.Gradient(point); Gradient = RosenbrockFunction.Gradient(Point);
hessian = RosenbrockFunction.Hessian(point); Hessian = RosenbrockFunction.Hessian(Point);
} }
} }
@ -79,7 +78,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[Test] [Test]
public void FindMinimum_Rosenbrock_Overton() public void FindMinimum_Rosenbrock_Overton()
{ {
var obj = new RosenbrockObjectiveFunction(); var obj = new LazyRosenbrockObjectiveFunction();
var solver = new NewtonMinimizer(1e-5, 1000); var solver = new NewtonMinimizer(1e-5, 1000);
var result = solver.FindMinimum(obj, new DenseVector(new[] { -0.9, -0.5 })); var result = solver.FindMinimum(obj, new DenseVector(new[] { -0.9, -0.5 }));
@ -90,7 +89,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[Test] [Test]
public void FindMinimum_Linesearch_Rosenbrock_Easy() public void FindMinimum_Linesearch_Rosenbrock_Easy()
{ {
var obj = new InplaceRosenbrockObjectiveFunction(); var obj = new RosenbrockObjectiveFunction();
var solver = new NewtonMinimizer(1e-5, 1000, true); var solver = new NewtonMinimizer(1e-5, 1000, true);
var result = solver.FindMinimum(obj, new DenseVector(new[] { 1.2, 1.2 })); var result = solver.FindMinimum(obj, new DenseVector(new[] { 1.2, 1.2 }));
@ -101,7 +100,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[Test] [Test]
public void FindMinimum_Linesearch_Rosenbrock_Hard() public void FindMinimum_Linesearch_Rosenbrock_Hard()
{ {
var obj = new RosenbrockObjectiveFunction(); var obj = new LazyRosenbrockObjectiveFunction();
var solver = new NewtonMinimizer(1e-5, 1000, true); var solver = new NewtonMinimizer(1e-5, 1000, true);
var result = solver.FindMinimum(obj, new DenseVector(new[] { -1.2, 1.0 })); var result = solver.FindMinimum(obj, new DenseVector(new[] { -1.2, 1.0 }));
@ -112,7 +111,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
[Test] [Test]
public void FindMinimum_Linesearch_Rosenbrock_Overton() public void FindMinimum_Linesearch_Rosenbrock_Overton()
{ {
var obj = new RosenbrockObjectiveFunction(); var obj = new LazyRosenbrockObjectiveFunction();
var solver = new NewtonMinimizer(1e-5, 1000, true); var solver = new NewtonMinimizer(1e-5, 1000, true);
var result = solver.FindMinimum(obj, new DenseVector(new[] { -0.9, -0.5 })); var result = solver.FindMinimum(obj, new DenseVector(new[] { -0.9, -0.5 }));

Loading…
Cancel
Save