diff --git a/src/Numerics/Optimization/BfgsMinimizer.cs b/src/Numerics/Optimization/BfgsMinimizer.cs index 45ed0209..9b954b79 100644 --- a/src/Numerics/Optimization/BfgsMinimizer.cs +++ b/src/Numerics/Optimization/BfgsMinimizer.cs @@ -146,19 +146,19 @@ namespace MathNet.Numerics.Optimization return ExitCondition.None; } - private void ValidateGradient(Vector gradient, Vector 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 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); } } } diff --git a/src/Numerics/Optimization/ConjugateGradientMinimizer.cs b/src/Numerics/Optimization/ConjugateGradientMinimizer.cs index bd123256..3bcc509f 100644 --- a/src/Numerics/Optimization/ConjugateGradientMinimizer.cs +++ b/src/Numerics/Optimization/ConjugateGradientMinimizer.cs @@ -114,19 +114,19 @@ namespace MathNet.Numerics.Optimization return gradient.Norm(2.0) < this.GradientTolerance; } - private void ValidateGradient(Vector gradient, Vector 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 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); } } } diff --git a/src/Numerics/Optimization/Exceptions.cs b/src/Numerics/Optimization/Exceptions.cs index c9c94f5b..aab4f690 100644 --- a/src/Numerics/Optimization/Exceptions.cs +++ b/src/Numerics/Optimization/Exceptions.cs @@ -24,24 +24,32 @@ namespace MathNet.Numerics.Optimization public class EvaluationException : OptimizationException { - public Vector Point { get; private set; } - public EvaluationException(string message, Vector 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 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 diff --git a/src/Numerics/Optimization/GoldenSectionMinimizer.cs b/src/Numerics/Optimization/GoldenSectionMinimizer.cs index 9f9cd81b..22455350 100644 --- a/src/Numerics/Optimization/GoldenSectionMinimizer.cs +++ b/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; } diff --git a/src/Numerics/Optimization/NewtonMinimizer.cs b/src/Numerics/Optimization/NewtonMinimizer.cs index 3e10affa..c6b329ad 100644 --- a/src/Numerics/Optimization/NewtonMinimizer.cs +++ b/src/Numerics/Optimization/NewtonMinimizer.cs @@ -98,29 +98,29 @@ namespace MathNet.Numerics.Optimization return gradient.Norm(2.0) < this.GradientTolerance; } - private void ValidateGradient(Vector gradient, Vector 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 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 hessian, Vector 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); } } } diff --git a/src/Numerics/Optimization/ObjectiveChecker.cs b/src/Numerics/Optimization/ObjectiveChecker.cs index f80c5881..53e9a011 100644 --- a/src/Numerics/Optimization/ObjectiveChecker.cs +++ b/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> ValueChecker { get; private set; } - public Action, Vector> GradientChecker { get; private set; } - public Action, Vector> HessianChecker { get; private set; } + public Action ValueChecker { get; private set; } + public Action GradientChecker { get; private set; } + public Action HessianChecker { get; private set; } - public ObjectiveChecker(IObjectiveFunction objective, Action> value_checker, Action, Vector> gradient_checker, Action, Vector> hessian_checker) + public ObjectiveChecker(IObjectiveFunction objective, Action value_checker, Action gradient_checker, Action 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); } } } diff --git a/src/Numerics/Optimization/ObjectiveChecker1D.cs b/src/Numerics/Optimization/ObjectiveChecker1D.cs index 78484ec6..49c489cd 100644 --- a/src/Numerics/Optimization/ObjectiveChecker1D.cs +++ b/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 ValueChecker { get; private set; } - public Action DerivativeChecker { get; private set; } - public Action SecondDerivativeChecker { get; private set; } + public Action ValueChecker { get; private set; } + public Action DerivativeChecker { get; private set; } + public Action SecondDerivativeChecker { get; private set; } - public ObjectiveChecker1D(IObjectiveFunction1D objective, Action value_checker, Action gradient_checker, Action hessian_checker) + public ObjectiveChecker1D(IObjectiveFunction1D objective, Action value_checker, Action gradient_checker, Action 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); } } } diff --git a/src/Numerics/Optimization/ObjectiveFunction.cs b/src/Numerics/Optimization/ObjectiveFunction.cs index 342c69a3..6c91c694 100644 --- a/src/Numerics/Optimization/ObjectiveFunction.cs +++ b/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 Point { get; } + Vector Point { get; } + EvaluationStatus Status { get; } double Value { get; } Vector Gradient { get; } Matrix Hessian { get; } @@ -24,18 +29,30 @@ namespace MathNet.Numerics.Optimization public abstract class BaseEvaluation : IEvaluation { + protected EvaluationStatus _status; protected double? _value; protected Vector _gradient; protected Matrix _hessian; protected Vector _point; public Vector 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 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 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 Gradient + { + get { return DenseVector.Create(1,this.InnerEval.Derivative) ; } + } + + public Matrix Hessian + { + get { return DenseMatrix.Create(1,1,this.InnerEval.SecondDerivative); } + } + } + + public class CachedEvaluation : BaseEvaluation { - private double? _value; - private Vector _gradient; - private Matrix _hessian; private SimpleObjectiveFunction _objective_object; - private Vector _point; public CachedEvaluation(SimpleObjectiveFunction f, Vector point) { _objective_object = f; _point = point; } - private double setValue() + + protected override void setValue() { _value = _objective_object.Objective(_point); - return _value.Value; } - private Vector setGradient() + + protected override void setGradient() { _gradient = _objective_object.Gradient(_point); - return _gradient; } - private Matrix setHessian() + + protected override void setHessian() { _hessian = _objective_object.Hessian(_point); - return _hessian; } - - public Vector Point { get { return _point; } } - public double Value { get { return _value ?? setValue(); } } - public Vector Gradient { get { return _gradient ?? setGradient(); } } - public Matrix Hessian { get { return _hessian ?? setHessian(); } } - } public class SimpleObjectiveFunction : IObjectiveFunction diff --git a/src/Numerics/Optimization/ObjectiveFunction1D.cs b/src/Numerics/Optimization/ObjectiveFunction1D.cs index 8611eda4..339bf1b9 100644 --- a/src/Numerics/Optimization/ObjectiveFunction1D.cs +++ b/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 diff --git a/src/Numerics/Optimization/WeakWolfeLineSearch.cs b/src/Numerics/Optimization/WeakWolfeLineSearch.cs index 40133403..f7481154 100644 --- a/src/Numerics/Optimization/WeakWolfeLineSearch.cs +++ b/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 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 gradient, Vector 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); } }