diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj
index cadd0bf1..163c728e 100644
--- a/src/Numerics/Numerics.csproj
+++ b/src/Numerics/Numerics.csproj
@@ -198,10 +198,12 @@
+
+
diff --git a/src/Numerics/Optimization/ConjugateGradientMinimizer.cs b/src/Numerics/Optimization/ConjugateGradientMinimizer.cs
index c16e2173..4fe75e83 100644
--- a/src/Numerics/Optimization/ConjugateGradientMinimizer.cs
+++ b/src/Numerics/Optimization/ConjugateGradientMinimizer.cs
@@ -21,11 +21,12 @@ namespace MathNet.Numerics.Optimization
public MinimizationOutput FindMinimum(IObjectiveFunction objective, Vector initial_guess)
{
if (!objective.GradientSupported)
- throw new Exception("Gradient not supported in objective function, but required for ConjugateGradient minimization.");
+ throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for ConjugateGradient minimization.");
+
+ objective = new ObjectiveChecker(objective, this.ValidateObjective, this.ValidateGradient, null);
IEvaluation initial_eval = objective.Evaluate(initial_guess);
- var gradient = initial_eval.Gradient;
- this.ValidateGradient(gradient, initial_guess);
+ var gradient = initial_eval.Gradient;
// Check that we're not already done
if (this.ExitCriteriaSatisfied(initial_guess, gradient))
@@ -42,11 +43,20 @@ namespace MathNet.Numerics.Optimization
steepest_direction = -gradient;
search_direction = steepest_direction;
double initial_step_size = 100 * this.GradientTolerance / (gradient * gradient);
- var result = line_searcher.FindConformingStep(objective, initial_eval, search_direction, initial_step_size);
- candidate_point = result.FunctionInfoAtMinimum;
- this.ValidateGradient(candidate_point.Gradient, candidate_point.Point);
+ LineSearchOutput result;
+ try
+ {
+ result = line_searcher.FindConformingStep(objective, initial_eval, search_direction, initial_step_size);
+ }
+ catch (Exception e)
+ {
+ throw new InnerOptimizationException("Line search failed.", e);
+ }
+
+ candidate_point = result.FunctionInfoAtMinimum;
double step_size = (candidate_point.Point - initial_guess).Norm(2.0);
+
// Subsequent steps
int iterations = 1;
int total_line_search_steps = result.Iterations;
@@ -73,8 +83,15 @@ namespace MathNet.Numerics.Optimization
search_direction = steepest_direction;
steepest_descent_resets += 1;
}
-
- result = line_searcher.FindConformingStep(objective, candidate_point, search_direction, step_size);
+
+ try
+ {
+ result = line_searcher.FindConformingStep(objective, candidate_point, search_direction, step_size);
+ }
+ catch (Exception e)
+ {
+ throw new InnerOptimizationException("Line search failed.", e);
+ }
no_line_search_iterations += result.Iterations == 0 ? 1 : 0;
total_line_search_steps += result.Iterations;
@@ -85,6 +102,9 @@ namespace MathNet.Numerics.Optimization
iterations += 1;
}
+ if (iterations == this.MaximumIterations)
+ throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", this.MaximumIterations));
+
return new MinimizationWithLineSearchOutput(candidate_point, iterations, total_line_search_steps, no_line_search_iterations);
}
@@ -98,14 +118,14 @@ namespace MathNet.Numerics.Optimization
foreach (var x in gradient)
{
if (Double.IsNaN(x) || Double.IsInfinity(x))
- throw new Exception("Non-finite gradient returned.");
+ throw new EvaluationException("Non-finite gradient returned.");
}
}
private void ValidateObjective(double objective, Vector input)
{
if (Double.IsNaN(objective) || Double.IsInfinity(objective))
- throw new Exception("Non-finite objective function returned.");
+ throw new EvaluationException("Non-finite objective function returned.");
}
}
}
diff --git a/src/Numerics/Optimization/Exceptions.cs b/src/Numerics/Optimization/Exceptions.cs
new file mode 100644
index 00000000..fcb0f758
--- /dev/null
+++ b/src/Numerics/Optimization/Exceptions.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MathNet.Numerics.Optimization
+{
+ public class OptimizationException : Exception
+ {
+ public OptimizationException(string message)
+ : base(message) {}
+
+ public OptimizationException(string message, Exception inner_exception)
+ : base(message, inner_exception) { }
+ }
+
+ public class MaximumIterationsException : OptimizationException
+ {
+ public MaximumIterationsException(string message)
+ : base(message) {}
+ }
+
+ public class EvaluationException : OptimizationException
+ {
+ public EvaluationException(string message)
+ : base(message) {}
+
+ public EvaluationException(string message, Exception inner_exception)
+ : base(message, inner_exception) { }
+ }
+
+ public class InnerOptimizationException : OptimizationException
+ {
+ public InnerOptimizationException(string message)
+ : base(message) {}
+
+ public InnerOptimizationException(string message, Exception inner_exception)
+ : base(message, inner_exception) { }
+ }
+
+ public class IncompatibleObjectiveException : OptimizationException
+ {
+ public IncompatibleObjectiveException(string message)
+ : base(message) {}
+ }
+
+
+}
diff --git a/src/Numerics/Optimization/ObjectiveChecker.cs b/src/Numerics/Optimization/ObjectiveChecker.cs
new file mode 100644
index 00000000..04c14495
--- /dev/null
+++ b/src/Numerics/Optimization/ObjectiveChecker.cs
@@ -0,0 +1,132 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using MathNet.Numerics.LinearAlgebra;
+
+namespace MathNet.Numerics.Optimization
+{
+ public class CheckedEvaluation : IEvaluation
+ {
+ private ObjectiveChecker Checker;
+ private IEvaluation InnerEvaluation;
+ private bool ValueChecked;
+ private bool GradientChecked;
+ private bool HessianChecked;
+
+ public CheckedEvaluation(ObjectiveChecker checker, IEvaluation evaluation)
+ {
+ this.Checker = checker;
+ this.InnerEvaluation = evaluation;
+ }
+
+ public Vector Point
+ {
+ get { return this.InnerEvaluation.Point; }
+ }
+
+ public double Value
+ {
+ get
+ {
+
+ if (!this.ValueChecked)
+ {
+ double tmp;
+ try
+ {
+ tmp = this.InnerEvaluation.Value;
+ }
+ catch (Exception e)
+ {
+ throw new EvaluationException("Objective function evaluation failed.", e);
+ }
+ this.Checker.ValueChecker(tmp,this.InnerEvaluation.Point);
+ }
+ return this.InnerEvaluation.Value;
+ }
+ }
+
+ public Vector Gradient
+ {
+ get
+ {
+
+ if (!this.GradientChecked)
+ {
+ Vector tmp;
+ try
+ {
+ tmp = this.InnerEvaluation.Gradient;
+ }
+ catch (Exception e)
+ {
+ throw new EvaluationException("Objective gradient evaluation failed.", e);
+ }
+ this.Checker.GradientChecker(tmp, this.InnerEvaluation.Point);
+ }
+ return this.InnerEvaluation.Gradient;
+ }
+ }
+
+ public Matrix Hessian
+ {
+ get
+ {
+
+ if (!this.HessianChecked)
+ {
+ Matrix tmp;
+ try
+ {
+ tmp = this.InnerEvaluation.Hessian;
+ }
+ catch (Exception e)
+ {
+ throw new EvaluationException("Objective hessian evaluation failed.", e);
+ }
+ this.Checker.HessianChecker(tmp, this.InnerEvaluation.Point);
+ }
+ return this.InnerEvaluation.Hessian;
+ }
+ }
+ }
+
+ 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 ObjectiveChecker(IObjectiveFunction objective, Action> value_checker, Action, Vector> gradient_checker, Action, Vector> hessian_checker)
+ {
+ this.InnerObjective = objective;
+ this.ValueChecker = value_checker;
+ this.GradientChecker = gradient_checker;
+ this.HessianChecker = hessian_checker;
+ }
+
+ public bool GradientSupported
+ {
+ get { return this.InnerObjective.GradientSupported; }
+ }
+
+ public bool HessianSupported
+ {
+ get { return this.InnerObjective.HessianSupported; }
+ }
+
+ public IEvaluation Evaluate(Vector point)
+ {
+ try
+ {
+ return new CheckedEvaluation(this, this.InnerObjective.Evaluate(point));
+ }
+ catch (Exception e)
+ {
+ throw new EvaluationException("Objective evaluation failed.", e);
+ }
+ }
+ }
+}
diff --git a/src/Numerics/Optimization/WeakWolfeLineSearch.cs b/src/Numerics/Optimization/WeakWolfeLineSearch.cs
index 7286a79c..0690a100 100644
--- a/src/Numerics/Optimization/WeakWolfeLineSearch.cs
+++ b/src/Numerics/Optimization/WeakWolfeLineSearch.cs
@@ -57,7 +57,7 @@ namespace MathNet.Numerics.Optimization
}
if (ii == this.MaximumIterations)
- throw new Exception("Line search failed with max iterations. Function is likely unbounded in search direction.");
+ throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached. Function may be unbounded in search direction.",this.MaximumIterations));
else
return new LineSearchOutput(candidate_eval, ii, step);
}