From 330a577f19c58ce9313a08ff7bedbd86b5488686 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Fri, 15 May 2015 13:17:54 +0200 Subject: [PATCH] Optimization: (Lazy)ObjectiveFunction base classes --- src/Numerics/Numerics.csproj | 4 +- .../Optimization/BaseObjectiveFunction.cs | 99 --------------- .../InplaceObjectiveFunction.cs | 62 --------- .../LazyObjectiveFunctionBase.cs | 119 ++++++++++++++++++ .../ObjectiveFunctionBase.cs | 42 +++++++ .../OptimizationTests/TestNewtonMinimizer.cs | 47 ++++--- 6 files changed, 186 insertions(+), 187 deletions(-) delete mode 100644 src/Numerics/Optimization/BaseObjectiveFunction.cs delete mode 100644 src/Numerics/Optimization/ObjectiveFunctions/InplaceObjectiveFunction.cs create mode 100644 src/Numerics/Optimization/ObjectiveFunctions/LazyObjectiveFunctionBase.cs create mode 100644 src/Numerics/Optimization/ObjectiveFunctions/ObjectiveFunctionBase.cs diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 2370e819..32dc2b7c 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -102,9 +102,9 @@ - + - + diff --git a/src/Numerics/Optimization/BaseObjectiveFunction.cs b/src/Numerics/Optimization/BaseObjectiveFunction.cs deleted file mode 100644 index ed7f1331..00000000 --- a/src/Numerics/Optimization/BaseObjectiveFunction.cs +++ /dev/null @@ -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 PointRaw { get; set; } - protected double ValueRaw { get; set; } - protected Vector GradientRaw { get; set; } - protected Matrix 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 Point - { - get { return PointRaw; } - } - - public void EvaluateAt(Vector point) - { - PointRaw = point; - Status = EvaluationStatus.None; - } - - public double Value - { - get - { - if (!Status.HasFlag(EvaluationStatus.Value)) - { - SetValue(); - Status |= EvaluationStatus.Value; - } - return ValueRaw; - } - } - public Vector Gradient - { - get - { - if (!Status.HasFlag(EvaluationStatus.Gradient)) - { - SetGradient(); - Status |= EvaluationStatus.Gradient; - } - return GradientRaw; - } - } - public Matrix 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(); - } -} diff --git a/src/Numerics/Optimization/ObjectiveFunctions/InplaceObjectiveFunction.cs b/src/Numerics/Optimization/ObjectiveFunctions/InplaceObjectiveFunction.cs deleted file mode 100644 index d2554dfb..00000000 --- a/src/Numerics/Optimization/ObjectiveFunctions/InplaceObjectiveFunction.cs +++ /dev/null @@ -1,62 +0,0 @@ -using MathNet.Numerics.LinearAlgebra; - -namespace MathNet.Numerics.Optimization.ObjectiveFunctions -{ - public abstract class InplaceObjectiveFunction : IObjectiveFunction - { - Vector _point; - double _functionValue; - Vector _gradientValue; - Matrix _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 point) - { - _point = point; - EvaluateAt(_point, ref _functionValue, ref _gradientValue, ref _hessianValue); - } - - protected abstract void EvaluateAt(Vector point, ref double value, ref Vector gradient, ref Matrix hessian); - - public Vector Point - { - get { return _point; } - } - - public double Value - { - get { return _functionValue; } - } - - public Vector Gradient - { - get { return _gradientValue; } - } - - public Matrix Hessian - { - get { return _hessianValue; } - } - } -} diff --git a/src/Numerics/Optimization/ObjectiveFunctions/LazyObjectiveFunctionBase.cs b/src/Numerics/Optimization/ObjectiveFunctions/LazyObjectiveFunctionBase.cs new file mode 100644 index 00000000..d8357772 --- /dev/null +++ b/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 _point; + + bool _hasFunctionValue; + double _functionValue; + + bool _hasGradientValue; + Vector _gradientValue; + + bool _hasHessianValue; + Matrix _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 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 Point + { + get { return _point; } + } + + public double Value + { + get + { + if (!_hasFunctionValue) + { + EvaluateValue(); + } + return _functionValue; + } + protected set + { + _functionValue = value; + _hasFunctionValue = true; + } + } + + public Vector Gradient + { + get + { + if (!_hasGradientValue) + { + EvaluateGradient(); + } + return _gradientValue; + } + protected set + { + _gradientValue = value; + _hasGradientValue = true; + } + } + + public Matrix Hessian + { + get + { + if (!_hasHessianValue) + { + EvaluateHessian(); + } + return _hessianValue; + } + protected set + { + _hessianValue = value; + _hasHessianValue = true; + } + } + } +} diff --git a/src/Numerics/Optimization/ObjectiveFunctions/ObjectiveFunctionBase.cs b/src/Numerics/Optimization/ObjectiveFunctions/ObjectiveFunctionBase.cs new file mode 100644 index 00000000..f212bcc0 --- /dev/null +++ b/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 point) + { + Point = point; + Evaluate(); + } + + protected abstract void Evaluate(); + + public Vector Point { get; private set; } + public double Value { get; protected set; } + public Vector Gradient { get; protected set; } + public Matrix Hessian { get; protected set; } + } +} diff --git a/src/UnitTests/OptimizationTests/TestNewtonMinimizer.cs b/src/UnitTests/OptimizationTests/TestNewtonMinimizer.cs index 85912a65..772c07ad 100644 --- a/src/UnitTests/OptimizationTests/TestNewtonMinimizer.cs +++ b/src/UnitTests/OptimizationTests/TestNewtonMinimizer.cs @@ -1,5 +1,4 @@ using System; -using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.LinearAlgebra.Double; using MathNet.Numerics.Optimization; using MathNet.Numerics.Optimization.ObjectiveFunctions; @@ -7,47 +6,47 @@ using NUnit.Framework; 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() { - return new InplaceRosenbrockObjectiveFunction(); + return new RosenbrockObjectiveFunction(); } - protected override void EvaluateAt(Vector point, ref double value, ref Vector gradient, ref Matrix hessian) + protected override void Evaluate() { - // here we could directly overwrite the existing matrices instead. - // note: values must then be initialized manually here first, if null. - value = RosenbrockFunction.Value(point); - gradient = RosenbrockFunction.Gradient(point); - hessian = RosenbrockFunction.Hessian(point); + // here we could directly overwrite the existing matrix cells instead. + // note: values must then be initialized manually first, if null. + Value = RosenbrockFunction.Value(Point); + Gradient = RosenbrockFunction.Gradient(Point); + Hessian = RosenbrockFunction.Hessian(Point); } } @@ -79,7 +78,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests [Test] public void FindMinimum_Rosenbrock_Overton() { - var obj = new RosenbrockObjectiveFunction(); + var obj = new LazyRosenbrockObjectiveFunction(); var solver = new NewtonMinimizer(1e-5, 1000); var result = solver.FindMinimum(obj, new DenseVector(new[] { -0.9, -0.5 })); @@ -90,7 +89,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests [Test] public void FindMinimum_Linesearch_Rosenbrock_Easy() { - var obj = new InplaceRosenbrockObjectiveFunction(); + var obj = new RosenbrockObjectiveFunction(); var solver = new NewtonMinimizer(1e-5, 1000, true); var result = solver.FindMinimum(obj, new DenseVector(new[] { 1.2, 1.2 })); @@ -101,7 +100,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests [Test] public void FindMinimum_Linesearch_Rosenbrock_Hard() { - var obj = new RosenbrockObjectiveFunction(); + var obj = new LazyRosenbrockObjectiveFunction(); var solver = new NewtonMinimizer(1e-5, 1000, true); var result = solver.FindMinimum(obj, new DenseVector(new[] { -1.2, 1.0 })); @@ -112,7 +111,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests [Test] public void FindMinimum_Linesearch_Rosenbrock_Overton() { - var obj = new RosenbrockObjectiveFunction(); + var obj = new LazyRosenbrockObjectiveFunction(); var solver = new NewtonMinimizer(1e-5, 1000, true); var result = solver.FindMinimum(obj, new DenseVector(new[] { -0.9, -0.5 }));