Browse Source

Optimization: Rework interfaces for handling evaluation problems

* EvaluationException now contains the evaluation object

(updated by cdrnet)
optimization-2
Scott Stephens 13 years ago
committed by Christoph Ruegg
parent
commit
bfb1d0afc7
  1. 12
      src/Numerics/Optimization/BfgsMinimizer.cs
  2. 12
      src/Numerics/Optimization/ConjugateGradientMinimizer.cs
  3. 26
      src/Numerics/Optimization/Exceptions.cs
  4. 6
      src/Numerics/Optimization/GoldenSectionMinimizer.cs
  5. 22
      src/Numerics/Optimization/NewtonMinimizer.cs
  6. 23
      src/Numerics/Optimization/ObjectiveChecker.cs
  7. 24
      src/Numerics/Optimization/ObjectiveChecker1D.cs
  8. 115
      src/Numerics/Optimization/ObjectiveFunction.cs
  9. 81
      src/Numerics/Optimization/ObjectiveFunction1D.cs
  10. 12
      src/Numerics/Optimization/WeakWolfeLineSearch.cs

12
src/Numerics/Optimization/BfgsMinimizer.cs

@ -146,19 +146,19 @@ namespace MathNet.Numerics.Optimization
return ExitCondition.None;
}
private void ValidateGradient(Vector<double> gradient, Vector<double> input)
private void ValidateGradient(IEvaluation eval)
{
foreach (var x in gradient)
foreach (var x in eval.Gradient)
{
if (Double.IsNaN(x) || Double.IsInfinity(x))
throw new EvaluationException("Non-finite gradient returned.",input);
throw new EvaluationException("Non-finite gradient returned.", eval);
}
}
private void ValidateObjective(double objective, Vector<double> input)
private void ValidateObjective(IEvaluation eval)
{
if (Double.IsNaN(objective) || Double.IsInfinity(objective))
throw new EvaluationException("Non-finite objective function returned.", input);
if (Double.IsNaN(eval.Value) || Double.IsInfinity(eval.Value))
throw new EvaluationException("Non-finite objective function returned.", eval);
}
}
}

12
src/Numerics/Optimization/ConjugateGradientMinimizer.cs

@ -114,19 +114,19 @@ namespace MathNet.Numerics.Optimization
return gradient.Norm(2.0) < this.GradientTolerance;
}
private void ValidateGradient(Vector<double> gradient, Vector<double> input)
private void ValidateGradient(IEvaluation eval)
{
foreach (var x in gradient)
foreach (var x in eval.Gradient)
{
if (Double.IsNaN(x) || Double.IsInfinity(x))
throw new EvaluationException("Non-finite gradient returned.", input);
throw new EvaluationException("Non-finite gradient returned.", eval);
}
}
private void ValidateObjective(double objective, Vector<double> input)
private void ValidateObjective(IEvaluation eval)
{
if (Double.IsNaN(objective) || Double.IsInfinity(objective))
throw new EvaluationException("Non-finite objective function returned.", input);
if (Double.IsNaN(eval.Value) || Double.IsInfinity(eval.Value))
throw new EvaluationException("Non-finite objective function returned.", eval);
}
}
}

26
src/Numerics/Optimization/Exceptions.cs

@ -24,24 +24,32 @@ namespace MathNet.Numerics.Optimization
public class EvaluationException : OptimizationException
{
public Vector<double> Point { get; private set; }
public EvaluationException(string message, Vector<double> point)
: base(message)
public IEvaluation Evaluation { get; private set; }
public EvaluationException(string message, IEvaluation eval)
: base(message)
{
this.Evaluation = eval;
}
public EvaluationException(string message, IEvaluation eval, Exception inner_exception)
: base(message, inner_exception)
{
this.Point = point;
this.Evaluation = eval;
}
public EvaluationException(string message, double point)
public EvaluationException(string message, IEvaluation1D eval)
: base(message)
{
this.Point = DenseVector.Create(1, point);
this.Evaluation = new OneDEvaluationExpander(eval);
}
public EvaluationException(string message, Exception inner_exception, Vector<double> point)
: base(message, inner_exception)
public EvaluationException(string message, IEvaluation1D eval, Exception inner_exception)
: base(message, inner_exception)
{
this.Point = point;
this.Evaluation = new OneDEvaluationExpander(eval);
}
}
public class InnerOptimizationException : OptimizationException

6
src/Numerics/Optimization/GoldenSectionMinimizer.cs

@ -63,10 +63,10 @@ namespace MathNet.Numerics.Optimization
}
private void ValueChecker(double value, double point)
private void ValueChecker(IEvaluation1D eval)
{
if (Double.IsNaN(value) || Double.IsInfinity(value))
throw new EvaluationException("Objective function returned non-finite value.", point);
if (Double.IsNaN(eval.Value) || Double.IsInfinity(eval.Value))
throw new EvaluationException("Objective function returned non-finite value.", eval);
}
private static double _golden_ratio = (1.0 + Math.Sqrt(5)) / 2.0;
}

22
src/Numerics/Optimization/NewtonMinimizer.cs

@ -98,29 +98,29 @@ namespace MathNet.Numerics.Optimization
return gradient.Norm(2.0) < this.GradientTolerance;
}
private void ValidateGradient(Vector<double> gradient, Vector<double> input)
private void ValidateGradient(IEvaluation eval)
{
foreach (var x in gradient)
foreach (var x in eval.Gradient)
{
if (Double.IsNaN(x) || Double.IsInfinity(x))
throw new EvaluationException("Non-finite gradient returned.", input);
throw new EvaluationException("Non-finite gradient returned.", eval);
}
}
private void ValidateObjective(double objective, Vector<double> input)
private void ValidateObjective(IEvaluation eval)
{
if (Double.IsNaN(objective) || Double.IsInfinity(objective))
throw new EvaluationException("Non-finite objective function returned.", input);
if (Double.IsNaN(eval.Value) || Double.IsInfinity(eval.Value))
throw new EvaluationException("Non-finite objective function returned.", eval);
}
private void ValidateHessian(Matrix<double> hessian, Vector<double> input)
private void ValidateHessian(IEvaluation eval)
{
for (int ii = 0; ii < hessian.RowCount; ++ii)
for (int ii = 0; ii < eval.Hessian.RowCount; ++ii)
{
for (int jj = 0; jj < hessian.ColumnCount; ++jj)
for (int jj = 0; jj < eval.Hessian.ColumnCount; ++jj)
{
if (Double.IsNaN(hessian[ii,jj]) || Double.IsInfinity(hessian[ii,jj]))
throw new EvaluationException("Non-finite Hessian returned.", input);
if (Double.IsNaN(eval.Hessian[ii,jj]) || Double.IsInfinity(eval.Hessian[ii,jj]))
throw new EvaluationException("Non-finite Hessian returned.", eval);
}
}
}

23
src/Numerics/Optimization/ObjectiveChecker.cs

@ -24,6 +24,7 @@ namespace MathNet.Numerics.Optimization
{
get { return this.InnerEvaluation.Point; }
}
public EvaluationStatus Status { get { return this.InnerEvaluation.Status; } }
public double Value
{
@ -39,9 +40,9 @@ namespace MathNet.Numerics.Optimization
}
catch (Exception e)
{
throw new EvaluationException("Objective function evaluation failed.", e, this.Point);
throw new EvaluationException("Objective function evaluation failed.", this.InnerEvaluation, e);
}
this.Checker.ValueChecker(tmp,this.InnerEvaluation.Point);
this.Checker.ValueChecker(this.InnerEvaluation);
}
return this.InnerEvaluation.Value;
}
@ -61,9 +62,9 @@ namespace MathNet.Numerics.Optimization
}
catch (Exception e)
{
throw new EvaluationException("Objective gradient evaluation failed.", e, this.Point);
throw new EvaluationException("Objective gradient evaluation failed.", this.InnerEvaluation, e);
}
this.Checker.GradientChecker(tmp, this.InnerEvaluation.Point);
this.Checker.GradientChecker(this.InnerEvaluation);
}
return this.InnerEvaluation.Gradient;
}
@ -83,9 +84,9 @@ namespace MathNet.Numerics.Optimization
}
catch (Exception e)
{
throw new EvaluationException("Objective hessian evaluation failed.", e, this.Point);
throw new EvaluationException("Objective hessian evaluation failed.", this.InnerEvaluation, e);
}
this.Checker.HessianChecker(tmp, this.InnerEvaluation.Point);
this.Checker.HessianChecker(InnerEvaluation);
}
return this.InnerEvaluation.Hessian;
}
@ -95,11 +96,11 @@ namespace MathNet.Numerics.Optimization
public class ObjectiveChecker : IObjectiveFunction
{
public IObjectiveFunction InnerObjective { get; private set; }
public Action<double, Vector<double>> ValueChecker { get; private set; }
public Action<Vector<double>, Vector<double>> GradientChecker { get; private set; }
public Action<Matrix<double>, Vector<double>> HessianChecker { 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<double, Vector<double>> value_checker, Action<Vector<double>, Vector<double>> gradient_checker, Action<Matrix<double>, Vector<double>> hessian_checker)
public ObjectiveChecker(IObjectiveFunction objective, Action<IEvaluation> value_checker, Action<IEvaluation> gradient_checker, Action<IEvaluation> hessian_checker)
{
this.InnerObjective = objective;
this.ValueChecker = value_checker;
@ -125,7 +126,7 @@ namespace MathNet.Numerics.Optimization
}
catch (Exception e)
{
throw new EvaluationException("Objective evaluation failed.", e, point);
throw new EvaluationException("Objective evaluation failed.", new NullEvaluation(point), e);
}
}
}

24
src/Numerics/Optimization/ObjectiveChecker1D.cs

@ -25,6 +25,8 @@ namespace MathNet.Numerics.Optimization
get { return this.InnerEvaluation.Point; }
}
public EvaluationStatus Status { get { return this.InnerEvaluation.Status; } }
public double Value
{
get
@ -39,9 +41,9 @@ namespace MathNet.Numerics.Optimization
}
catch (Exception e)
{
throw new EvaluationException("Objective function evaluation failed.", e, DenseVector.Create(1, this.Point));
throw new EvaluationException("Objective function evaluation failed.", this.InnerEvaluation, e);
}
this.Checker.ValueChecker(tmp, this.InnerEvaluation.Point);
this.Checker.ValueChecker(this.InnerEvaluation);
}
return this.InnerEvaluation.Value;
}
@ -61,9 +63,9 @@ namespace MathNet.Numerics.Optimization
}
catch (Exception e)
{
throw new EvaluationException("Objective derivative evaluation failed.", e, DenseVector.Create(1, this.Point));
throw new EvaluationException("Objective derivative evaluation failed.", this.InnerEvaluation, e);
}
this.Checker.DerivativeChecker(tmp, this.InnerEvaluation.Point);
this.Checker.DerivativeChecker(this.InnerEvaluation);
}
return this.InnerEvaluation.Derivative;
}
@ -83,9 +85,9 @@ namespace MathNet.Numerics.Optimization
}
catch (Exception e)
{
throw new EvaluationException("Objective second derivative evaluation failed.", e, DenseVector.Create(1, this.Point));
throw new EvaluationException("Objective second derivative evaluation failed.", this.InnerEvaluation, e);
}
this.Checker.SecondDerivativeChecker(tmp, this.InnerEvaluation.Point);
this.Checker.SecondDerivativeChecker(this.InnerEvaluation);
}
return this.InnerEvaluation.SecondDerivative;
}
@ -95,11 +97,11 @@ namespace MathNet.Numerics.Optimization
public class ObjectiveChecker1D : IObjectiveFunction1D
{
public IObjectiveFunction1D InnerObjective { get; private set; }
public Action<double, double> ValueChecker { get; private set; }
public Action<double, double> DerivativeChecker { get; private set; }
public Action<double, double> SecondDerivativeChecker { get; private set; }
public Action<IEvaluation1D> ValueChecker { get; private set; }
public Action<IEvaluation1D> DerivativeChecker { get; private set; }
public Action<IEvaluation1D> SecondDerivativeChecker { get; private set; }
public ObjectiveChecker1D(IObjectiveFunction1D objective, Action<double, double> value_checker, Action<double, double> gradient_checker, Action<double, double> hessian_checker)
public ObjectiveChecker1D(IObjectiveFunction1D objective, Action<IEvaluation1D> value_checker, Action<IEvaluation1D> gradient_checker, Action<IEvaluation1D> hessian_checker)
{
this.InnerObjective = objective;
this.ValueChecker = value_checker;
@ -125,7 +127,7 @@ namespace MathNet.Numerics.Optimization
}
catch (Exception e)
{
throw new EvaluationException("Objective evaluation failed.", e, DenseVector.Create(1, point));
throw new EvaluationException("Objective evaluation failed.", new NullEvaluation(point), e);
}
}
}

115
src/Numerics/Optimization/ObjectiveFunction.cs

@ -4,12 +4,17 @@ using System.Linq;
using System.Text;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
namespace MathNet.Numerics.Optimization
{
[Flags]
public enum EvaluationStatus { None = 0, Value = 1, Gradient = 2, Hessian = 4 }
public interface IEvaluation
{
Vector<double> Point { get; }
Vector<double> Point { get; }
EvaluationStatus Status { get; }
double Value { get; }
Vector<double> Gradient { get; }
Matrix<double> Hessian { get; }
@ -24,18 +29,30 @@ namespace MathNet.Numerics.Optimization
public abstract class BaseEvaluation : IEvaluation
{
protected EvaluationStatus _status;
protected double? _value;
protected Vector<double> _gradient;
protected Matrix<double> _hessian;
protected Vector<double> _point;
public Vector<double> Point { get { return _point; } }
protected BaseEvaluation()
{
_status = EvaluationStatus.None;
}
public EvaluationStatus Status { get { return _status; } }
public double Value
{
get
{
if (!_value.HasValue)
{
setValue();
_status |= EvaluationStatus.Value;
}
return _value.Value;
}
}
@ -44,7 +61,10 @@ namespace MathNet.Numerics.Optimization
get
{
if (_gradient == null)
{
setGradient();
_status |= EvaluationStatus.Gradient;
}
return _gradient;
}
}
@ -53,7 +73,10 @@ namespace MathNet.Numerics.Optimization
get
{
if (_hessian == null)
{
setHessian();
_status |= EvaluationStatus.Hessian;
}
return _hessian;
}
}
@ -64,40 +87,96 @@ namespace MathNet.Numerics.Optimization
}
public class CachedEvaluation : IEvaluation
public class NullEvaluation : BaseEvaluation
{
public NullEvaluation(Vector<double> point)
{
_point = point;
_status = EvaluationStatus.None;
}
public NullEvaluation(double point)
{
_point = DenseVector.Create(1, point);
_status = EvaluationStatus.None;
}
protected override void setValue()
{
throw new NotImplementedException();
}
protected override void setGradient()
{
throw new NotImplementedException();
}
protected override void setHessian()
{
throw new NotImplementedException();
}
}
public class OneDEvaluationExpander : IEvaluation
{
public IEvaluation1D InnerEval { get; private set; }
public OneDEvaluationExpander(IEvaluation1D eval)
{
this.InnerEval = eval;
}
public Vector<double> Point
{
get { return DenseVector.Create(1,this.InnerEval.Point); }
}
public EvaluationStatus Status
{
get { return this.InnerEval.Status; }
}
public double Value
{
get { return this.InnerEval.Value; }
}
public Vector<double> Gradient
{
get { return DenseVector.Create(1,this.InnerEval.Derivative) ; }
}
public Matrix<double> Hessian
{
get { return DenseMatrix.Create(1,1,this.InnerEval.SecondDerivative); }
}
}
public class CachedEvaluation : BaseEvaluation
{
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()
protected override void setValue()
{
_value = _objective_object.Objective(_point);
return _value.Value;
}
private Vector<double> setGradient()
protected override void setGradient()
{
_gradient = _objective_object.Gradient(_point);
return _gradient;
}
private Matrix<double> setHessian()
protected override void 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

81
src/Numerics/Optimization/ObjectiveFunction1D.cs

@ -8,6 +8,7 @@ namespace MathNet.Numerics.Optimization
public interface IEvaluation1D
{
double Point { get; }
EvaluationStatus Status { get; }
double Value { get; }
double Derivative { get; }
double SecondDerivative { get; }
@ -20,40 +21,86 @@ namespace MathNet.Numerics.Optimization
IEvaluation1D Evaluate(double point);
}
public class CachedEvaluation1D : IEvaluation1D
public abstract class BaseEvaluation1D : IEvaluation1D
{
protected double _point;
protected EvaluationStatus _status;
protected double? _value;
protected double? _derivative;
protected double? _second_derivative;
public double Point { get { return _point; } }
protected BaseEvaluation1D()
{
_status = EvaluationStatus.None;
}
public EvaluationStatus Status { get { return _status; } }
public double Value
{
get
{
if (!_value.HasValue)
{
setValue();
_status |= EvaluationStatus.Value;
}
return _value.Value;
}
}
public double Derivative
{
get
{
if (_derivative == null)
{
setDerivative();
_status |= EvaluationStatus.Gradient;
}
return _derivative.Value;
}
}
public double SecondDerivative
{
get
{
if (_second_derivative == null)
{
setSecondDerivative();
_status |= EvaluationStatus.Hessian;
}
return _second_derivative.Value;
}
}
protected abstract void setValue();
protected abstract void setDerivative();
protected abstract void setSecondDerivative();
}
public class CachedEvaluation1D : BaseEvaluation1D
{
private double? _value;
private double? _derivative;
private double? _second_derivative;
private SimpleObjectiveFunction1D _objective_object;
private double _point;
public CachedEvaluation1D(SimpleObjectiveFunction1D f, double point)
{
_objective_object = f;
_point = point;
}
private double setValue()
protected override void setValue()
{
_value = _objective_object.Objective(_point);
return _value.Value;
}
private double setDerivative()
protected override void setDerivative()
{
_derivative = _objective_object.Derivative(_point);
return _derivative.Value;
}
private double setSecondDerivative()
protected override void setSecondDerivative()
{
_second_derivative = _objective_object.SecondDerivative(_point);
return _second_derivative.Value;
}
public double Point { get { return _point; } }
public double Value { get { return _value ?? setValue(); } }
public double Derivative { get { return _derivative ?? setDerivative(); } }
public double SecondDerivative { get { return _second_derivative ?? setSecondDerivative(); } }
}
public class SimpleObjectiveFunction1D : IObjectiveFunction1D

12
src/Numerics/Optimization/WeakWolfeLineSearch.cs

@ -96,18 +96,18 @@ namespace MathNet.Numerics.Optimization
return step > 0 && sufficient_decrease && not_too_steep;
}
private void ValidateValue(double value, Vector<double> input)
private void ValidateValue(IEvaluation eval)
{
if (!this.IsFinite(value))
throw new EvaluationException(String.Format("Non-finite value returned by objective function: {0}", value),input);
if (!this.IsFinite(eval.Value))
throw new EvaluationException(String.Format("Non-finite value returned by objective function: {0}", eval.Value),eval);
}
private void ValidateGradient(Vector<double> gradient, Vector<double> input)
private void ValidateGradient(IEvaluation eval)
{
foreach (double x in gradient)
foreach (double x in eval.Gradient)
if (!this.IsFinite(x))
{
throw new EvaluationException(String.Format("Non-finite value returned by gradient: {0}", x),input);
throw new EvaluationException(String.Format("Non-finite value returned by gradient: {0}", x),eval);
}
}

Loading…
Cancel
Save