committed by
Erik Ovegard
17 changed files with 938 additions and 0 deletions
@ -0,0 +1,70 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public abstract class BaseEvaluation : IEvaluation |
|||
{ |
|||
public EvaluationStatus Status { get; set; } |
|||
public Vector<double> Point { get; set; } |
|||
public double ValueRaw { get; set; } |
|||
public Vector<double> GradientRaw { get; set; } |
|||
public Matrix<double> HessianRaw { get; set; } |
|||
|
|||
protected BaseEvaluation() |
|||
{ |
|||
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 void Reset(Vector<double> new_point) |
|||
{ |
|||
this.Point = new_point; |
|||
this.Status = EvaluationStatus.None; |
|||
} |
|||
|
|||
protected abstract void setValue(); |
|||
protected abstract void setGradient(); |
|||
protected abstract void setHessian(); |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public class BaseObjectiveFunction<T> : IObjectiveFunction where T : IEvaluation |
|||
{ |
|||
public BaseObjectiveFunction(bool gradient_supported, bool hessian_supported) |
|||
{ |
|||
_gradient_supported = gradient_supported; |
|||
_hessian_supported = hessian_supported; |
|||
} |
|||
|
|||
private bool _gradient_supported; |
|||
private bool _hessian_supported; |
|||
|
|||
public bool GradientSupported |
|||
{ |
|||
get { return _gradient_supported; } |
|||
} |
|||
|
|||
public bool HessianSupported |
|||
{ |
|||
get { return _hessian_supported; } |
|||
} |
|||
|
|||
public void Evaluate(LinearAlgebra.Vector<double> point, IEvaluation output) |
|||
{ |
|||
output.Reset(point); |
|||
} |
|||
|
|||
public virtual IEvaluation CreateEvaluationObject() |
|||
{ |
|||
return default(T); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
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 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.Evaluation = eval; |
|||
} |
|||
|
|||
//public EvaluationException(string message, IEvaluation1D eval)
|
|||
// : base(message)
|
|||
//{
|
|||
// this.Evaluation = new OneDEvaluationExpander(eval);
|
|||
//}
|
|||
|
|||
//public EvaluationException(string message, IEvaluation1D eval, Exception inner_exception)
|
|||
// : base(message, inner_exception)
|
|||
//{
|
|||
// this.Evaluation = new OneDEvaluationExpander(eval);
|
|||
//}
|
|||
|
|||
} |
|||
|
|||
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) { } |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
[Flags] |
|||
public enum EvaluationStatus { None = 0, Value = 1, Gradient = 2, Hessian = 4 } |
|||
|
|||
public interface IEvaluation |
|||
{ |
|||
Vector<double> Point { get; set; } |
|||
EvaluationStatus Status { get; set; } |
|||
|
|||
// Used by algorithm
|
|||
double Value { get; } |
|||
Vector<double> Gradient { get; } |
|||
Matrix<double> Hessian { get; } |
|||
|
|||
// Used by ObjectiveFunction
|
|||
void Reset(Vector<double> new_point); |
|||
double ValueRaw { get; set; } |
|||
Vector<double> GradientRaw { get; set; } |
|||
Matrix<double> HessianRaw { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public interface IObjectiveFunction |
|||
{ |
|||
bool GradientSupported { get; } |
|||
bool HessianSupported { get; } |
|||
|
|||
IEvaluation CreateEvaluationObject(); |
|||
void Evaluate(Vector<double> point, IEvaluation output); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public interface IUnconstrainedMinimizer |
|||
{ |
|||
MinimizationOutput FindMinimum(IObjectiveFunction objective, Vector<double> initial_guess); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
namespace MathNet.Numerics.Optimization.Implementation |
|||
{ |
|||
public class LineSearchOutput : MinimizationOutput |
|||
{ |
|||
public double FinalStep { get; private set; } |
|||
|
|||
public LineSearchOutput(IEvaluation function_info, int iterations, double final_step, ExitCondition reason_for_exit) |
|||
: base(function_info, iterations, reason_for_exit) |
|||
{ |
|||
this.FinalStep = final_step; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization.Implementation |
|||
{ |
|||
public class NullEvaluation : BaseEvaluation |
|||
{ |
|||
public NullEvaluation(Vector<double> point) |
|||
: base() |
|||
{ |
|||
this.Point = point; |
|||
} |
|||
protected override void setValue() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
protected override void setGradient() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
protected override void setHessian() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,183 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization.Implementation |
|||
{ |
|||
public class CheckedEvaluation : IEvaluation |
|||
{ |
|||
private ObjectiveChecker Checker; |
|||
public IEvaluation InnerEvaluation { get; private set; } |
|||
private bool ValueChecked; |
|||
private bool GradientChecked; |
|||
private bool HessianChecked; |
|||
|
|||
public CheckedEvaluation(ObjectiveChecker checker, IEvaluation evaluation) |
|||
{ |
|||
this.Checker = checker; |
|||
this.InnerEvaluation = evaluation; |
|||
} |
|||
|
|||
public Vector<double> Point |
|||
{ |
|||
get { return this.InnerEvaluation.Point; } |
|||
set { this.InnerEvaluation.Point = value; } |
|||
} |
|||
|
|||
public EvaluationStatus Status |
|||
{ |
|||
get |
|||
{ |
|||
return this.InnerEvaluation.Status; |
|||
} |
|||
set |
|||
{ |
|||
this.InnerEvaluation.Status = value; |
|||
} |
|||
} |
|||
|
|||
public double ValueRaw |
|||
{ |
|||
get |
|||
{ |
|||
return this.InnerEvaluation.Value; |
|||
} |
|||
set |
|||
{ |
|||
this.InnerEvaluation.ValueRaw = value; |
|||
} |
|||
} |
|||
|
|||
public Vector<double> GradientRaw |
|||
{ |
|||
get { return this.InnerEvaluation.GradientRaw; } |
|||
set { this.InnerEvaluation.GradientRaw = value; } |
|||
} |
|||
|
|||
public Matrix<double> HessianRaw |
|||
{ |
|||
get { return this.InnerEvaluation.HessianRaw; } |
|||
set { this.InnerEvaluation.HessianRaw = value; } |
|||
} |
|||
|
|||
public double Value |
|||
{ |
|||
get |
|||
{ |
|||
|
|||
if (!this.ValueChecked) |
|||
{ |
|||
double tmp; |
|||
try |
|||
{ |
|||
tmp = this.InnerEvaluation.Value; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new EvaluationException("Objective function evaluation failed.", this.InnerEvaluation, e); |
|||
} |
|||
this.Checker.ValueChecker(this.InnerEvaluation); |
|||
this.ValueChecked = true; |
|||
} |
|||
return this.InnerEvaluation.Value; |
|||
} |
|||
} |
|||
|
|||
public Vector<double> Gradient |
|||
{ |
|||
get |
|||
{ |
|||
|
|||
if (!this.GradientChecked) |
|||
{ |
|||
Vector<double> tmp; |
|||
try |
|||
{ |
|||
tmp = this.InnerEvaluation.Gradient; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new EvaluationException("Objective gradient evaluation failed.", this.InnerEvaluation, e); |
|||
} |
|||
this.Checker.GradientChecker(this.InnerEvaluation); |
|||
this.GradientChecked = true; |
|||
} |
|||
return this.InnerEvaluation.Gradient; |
|||
} |
|||
} |
|||
|
|||
public Matrix<double> Hessian |
|||
{ |
|||
get |
|||
{ |
|||
|
|||
if (!this.HessianChecked) |
|||
{ |
|||
Matrix<double> tmp; |
|||
try |
|||
{ |
|||
tmp = this.InnerEvaluation.Hessian; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new EvaluationException("Objective hessian evaluation failed.", this.InnerEvaluation, e); |
|||
} |
|||
this.Checker.HessianChecker(InnerEvaluation); |
|||
this.HessianChecked = true; |
|||
} |
|||
return this.InnerEvaluation.Hessian; |
|||
} |
|||
} |
|||
|
|||
public void Reset(Vector<double> new_point) |
|||
{ |
|||
this.InnerEvaluation.Reset(new_point); |
|||
} |
|||
} |
|||
|
|||
public class ObjectiveChecker : IObjectiveFunction |
|||
{ |
|||
public IObjectiveFunction InnerObjective { 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<IEvaluation> value_checker, Action<IEvaluation> gradient_checker, Action<IEvaluation> 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 void Evaluate(Vector<double> point, IEvaluation output) |
|||
{ |
|||
try |
|||
{ |
|||
this.InnerObjective.Evaluate(point, output); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new EvaluationException("Objective evaluation failed.", new NullEvaluation(point), e); |
|||
} |
|||
} |
|||
|
|||
|
|||
public IEvaluation CreateEvaluationObject() |
|||
{ |
|||
return this.InnerObjective.CreateEvaluationObject(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,119 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization.Implementation |
|||
{ |
|||
public class WeakWolfeLineSearch |
|||
{ |
|||
public double C1 { get; set; } |
|||
public double C2 { get; set; } |
|||
public double ParameterTolerance { get; set; } |
|||
public int MaximumIterations { get; set; } |
|||
|
|||
public WeakWolfeLineSearch(double c1, double c2, double parameter_tolerance, int max_iterations = 10) |
|||
{ |
|||
this.C1 = c1; |
|||
this.C2 = c2; |
|||
this.ParameterTolerance = parameter_tolerance; |
|||
this.MaximumIterations = max_iterations; |
|||
} |
|||
|
|||
// Implemented following http://www.math.washington.edu/~burke/crs/408/lectures/L9-weak-Wolfe.pdf
|
|||
public LineSearchOutput FindConformingStep(IObjectiveFunction objective, IEvaluation starting_point, Vector<double> search_direction, double initial_step) |
|||
{ |
|||
|
|||
if (!(objective is ObjectiveChecker)) |
|||
objective = new ObjectiveChecker(objective, this.ValidateValue, this.ValidateGradient, null); |
|||
|
|||
double lower_bound = 0.0; |
|||
double upper_bound = Double.PositiveInfinity; |
|||
double step = initial_step; |
|||
|
|||
double initial_value = starting_point.Value; |
|||
Vector<double> initial_gradient = starting_point.Gradient; |
|||
|
|||
double initial_dd = search_direction * initial_gradient; |
|||
|
|||
int ii; |
|||
IEvaluation candidate_eval = objective.CreateEvaluationObject(); |
|||
MinimizationOutput.ExitCondition reason_for_exit = MinimizationOutput.ExitCondition.None; |
|||
for (ii = 0; ii < this.MaximumIterations; ++ii) |
|||
{ |
|||
objective.Evaluate(starting_point.Point + search_direction * step, candidate_eval); |
|||
|
|||
double step_dd = search_direction * candidate_eval.Gradient; |
|||
|
|||
if (candidate_eval.Value > initial_value + this.C1 * step * initial_dd) |
|||
{ |
|||
upper_bound = step; |
|||
step = 0.5 * (lower_bound + upper_bound); |
|||
} |
|||
else if (step_dd < this.C2 * initial_dd) |
|||
{ |
|||
lower_bound = step; |
|||
step = Double.IsPositiveInfinity(upper_bound) ? 2 * lower_bound : 0.5 * (lower_bound + upper_bound); |
|||
} |
|||
else |
|||
{ |
|||
reason_for_exit = MinimizationOutput.ExitCondition.WeakWolfeCriteria; |
|||
break; |
|||
} |
|||
|
|||
if (!Double.IsInfinity(upper_bound)) |
|||
{ |
|||
double max_rel_change = 0.0; |
|||
for (int jj = 0; jj < candidate_eval.Point.Count; ++jj) |
|||
{ |
|||
double tmp = Math.Abs(search_direction[jj] * (upper_bound - lower_bound)) / Math.Max(Math.Abs(candidate_eval.Point[jj]), 1.0); |
|||
max_rel_change = Math.Max(max_rel_change, tmp); |
|||
} |
|||
if (max_rel_change < this.ParameterTolerance) |
|||
{ |
|||
reason_for_exit = MinimizationOutput.ExitCondition.LackOfProgress; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (ii == this.MaximumIterations && Double.IsPositiveInfinity(upper_bound)) |
|||
throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached. Function appears to be unbounded in search direction.", this.MaximumIterations)); |
|||
else if (ii == this.MaximumIterations) |
|||
throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", this.MaximumIterations)); |
|||
else |
|||
return new LineSearchOutput(candidate_eval, ii, step, reason_for_exit); |
|||
} |
|||
|
|||
private bool Conforms(IEvaluation starting_point, Vector<double> search_direction, double step, IEvaluation ending_point) |
|||
{ |
|||
|
|||
bool sufficient_decrease = ending_point.Value <= starting_point.Value + this.C1 * step * (starting_point.Gradient * search_direction); |
|||
bool not_too_steep = ending_point.Gradient * search_direction >= this.C2 * starting_point.Gradient * search_direction; |
|||
|
|||
return step > 0 && sufficient_decrease && not_too_steep; |
|||
} |
|||
|
|||
private void ValidateValue(IEvaluation eval) |
|||
{ |
|||
if (!this.IsFinite(eval.Value)) |
|||
throw new EvaluationException(String.Format("Non-finite value returned by objective function: {0}", eval.Value), eval); |
|||
} |
|||
|
|||
private void ValidateGradient(IEvaluation eval) |
|||
{ |
|||
foreach (double x in eval.Gradient) |
|||
if (!this.IsFinite(x)) |
|||
{ |
|||
throw new EvaluationException(String.Format("Non-finite value returned by gradient: {0}", x), eval); |
|||
} |
|||
} |
|||
|
|||
private bool IsFinite(double x) |
|||
{ |
|||
return !(Double.IsNaN(x) || Double.IsInfinity(x)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public class MinimizationOutput |
|||
{ |
|||
public enum ExitCondition { None, RelativeGradient, LackOfProgress, AbsoluteGradient, WeakWolfeCriteria, BoundTolerance, StrongWolfeCriteria, LackOfFunctionImprovement } |
|||
|
|||
public Vector<double> MinimizingPoint { get { return FunctionInfoAtMinimum.Point; } } |
|||
public IEvaluation FunctionInfoAtMinimum { get; private set; } |
|||
public int Iterations { get; private set; } |
|||
public ExitCondition ReasonForExit { get; private set; } |
|||
|
|||
public MinimizationOutput(IEvaluation function_info, int iterations, ExitCondition reason_for_exit) |
|||
{ |
|||
this.FunctionInfoAtMinimum = function_info; |
|||
this.Iterations = iterations; |
|||
this.ReasonForExit = reason_for_exit; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public class MinimizationWithLineSearchOutput : MinimizationOutput |
|||
{ |
|||
public int TotalLineSearchIterations { get; private set; } |
|||
public int IterationsWithNonTrivialLineSearch { get; private set; } |
|||
|
|||
public MinimizationWithLineSearchOutput(IEvaluation function_info, int iterations, ExitCondition reason_for_exit, int total_line_search_iterations, int iterations_with_non_trivial_line_search) |
|||
: base(function_info, iterations, reason_for_exit) |
|||
{ |
|||
this.TotalLineSearchIterations = total_line_search_iterations; |
|||
this.IterationsWithNonTrivialLineSearch = iterations_with_non_trivial_line_search; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,130 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using LU = MathNet.Numerics.LinearAlgebra.Factorization.LU<double>; |
|||
using MathNet.Numerics.Optimization.Implementation; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public class NewtonMinimizer |
|||
{ |
|||
public double GradientTolerance { get; set; } |
|||
public int MaximumIterations { get; set; } |
|||
public bool UseLineSearch { get; set; } |
|||
|
|||
public NewtonMinimizer(double gradient_tolerance, int maximum_iterations, bool use_line_search = false) |
|||
{ |
|||
this.GradientTolerance = gradient_tolerance; |
|||
this.MaximumIterations = maximum_iterations; |
|||
this.UseLineSearch = use_line_search; |
|||
} |
|||
|
|||
public MinimizationOutput FindMinimum(IObjectiveFunction objective, Vector<double> initial_guess) |
|||
{ |
|||
if (!objective.GradientSupported) |
|||
throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for Newton minimization."); |
|||
|
|||
if (!objective.HessianSupported) |
|||
throw new IncompatibleObjectiveException("Hessian not supported in objective function, but required for Newton minimization."); |
|||
|
|||
if (!(objective is ObjectiveChecker)) |
|||
objective = new ObjectiveChecker(objective, this.ValidateObjective, this.ValidateGradient, this.ValidateHessian); |
|||
|
|||
IEvaluation initial_eval = objective.CreateEvaluationObject(); |
|||
objective.Evaluate(initial_guess, initial_eval); |
|||
|
|||
// Check that we're not already done
|
|||
if (this.ExitCriteriaSatisfied(initial_guess, initial_eval.Gradient)) |
|||
return new MinimizationOutput(initial_eval, 0, MinimizationOutput.ExitCondition.AbsoluteGradient); |
|||
|
|||
// Set up line search algorithm
|
|||
var line_searcher = new WeakWolfeLineSearch(1e-4, 0.9, 1e-4, max_iterations: 1000); |
|||
|
|||
// Declare state variables
|
|||
IEvaluation candidate_point = initial_eval; |
|||
Vector<double> search_direction; |
|||
LineSearchOutput result; |
|||
|
|||
// Subsequent steps
|
|||
int iterations = 0; |
|||
int total_line_search_steps = 0; |
|||
int iterations_with_nontrivial_line_search = 0; |
|||
int steepest_descent_resets = 0; |
|||
bool tmp_line_search = false; |
|||
while (!this.ExitCriteriaSatisfied(candidate_point.Point, candidate_point.Gradient) && iterations < this.MaximumIterations) |
|||
{ |
|||
|
|||
search_direction = candidate_point.Hessian.LU().Solve(-candidate_point.Gradient); |
|||
|
|||
if (search_direction * candidate_point.Gradient >= 0) |
|||
{ |
|||
search_direction = -candidate_point.Gradient; |
|||
steepest_descent_resets += 1; |
|||
tmp_line_search = true; |
|||
} |
|||
|
|||
if (this.UseLineSearch || tmp_line_search) |
|||
{ |
|||
try |
|||
{ |
|||
result = line_searcher.FindConformingStep(objective, candidate_point, search_direction, 1.0); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new InnerOptimizationException("Line search failed.", e); |
|||
} |
|||
iterations_with_nontrivial_line_search += result.Iterations > 0 ? 1 : 0; |
|||
total_line_search_steps += result.Iterations; |
|||
candidate_point = result.FunctionInfoAtMinimum; |
|||
} |
|||
else |
|||
{ |
|||
objective.Evaluate(candidate_point.Point + search_direction, candidate_point); |
|||
} |
|||
|
|||
tmp_line_search = false; |
|||
|
|||
iterations += 1; |
|||
} |
|||
|
|||
if (iterations == this.MaximumIterations) |
|||
throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", this.MaximumIterations)); |
|||
|
|||
return new MinimizationWithLineSearchOutput(candidate_point, iterations, MinimizationOutput.ExitCondition.AbsoluteGradient, total_line_search_steps, iterations_with_nontrivial_line_search); |
|||
} |
|||
|
|||
private bool ExitCriteriaSatisfied(Vector<double> candidate_point, Vector<double> gradient) |
|||
{ |
|||
return gradient.Norm(2.0) < this.GradientTolerance; |
|||
} |
|||
|
|||
private void ValidateGradient(IEvaluation eval) |
|||
{ |
|||
foreach (var x in eval.Gradient) |
|||
{ |
|||
if (Double.IsNaN(x) || Double.IsInfinity(x)) |
|||
throw new EvaluationException("Non-finite gradient returned.", eval); |
|||
} |
|||
} |
|||
|
|||
private void ValidateObjective(IEvaluation eval) |
|||
{ |
|||
if (Double.IsNaN(eval.Value) || Double.IsInfinity(eval.Value)) |
|||
throw new EvaluationException("Non-finite objective function returned.", eval); |
|||
} |
|||
|
|||
private void ValidateHessian(IEvaluation eval) |
|||
{ |
|||
for (int ii = 0; ii < eval.Hessian.RowCount; ++ii) |
|||
{ |
|||
for (int jj = 0; jj < eval.Hessian.ColumnCount; ++jj) |
|||
{ |
|||
if (Double.IsNaN(eval.Hessian[ii, jj]) || Double.IsInfinity(eval.Hessian[ii, jj])) |
|||
throw new EvaluationException("Non-finite Hessian returned.", eval); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
public static class RosenbrockFunction |
|||
{ |
|||
public static double Value(Vector<double> input) |
|||
{ |
|||
return Math.Pow((1 - input[0]), 2) + 100 * Math.Pow((input[1] - input[0] * input[0]), 2); |
|||
} |
|||
|
|||
public static Vector<double> Gradient(Vector<double> input) |
|||
{ |
|||
Vector<double> output = new MathNet.Numerics.LinearAlgebra.Double.DenseVector(2); |
|||
output[0] = -2 * (1 - input[0]) + 200 * (input[1] - input[0] * input[0]) * (-2 * input[0]); |
|||
output[1] = 2 * 100 * (input[1] - input[0] * input[0]); |
|||
return output; |
|||
} |
|||
|
|||
public static Matrix<double> Hessian(Vector<double> input) |
|||
{ |
|||
|
|||
Matrix<double> output = new MathNet.Numerics.LinearAlgebra.Double.DenseMatrix(2, 2); |
|||
output[0, 0] = 2 - 400 * input[1] + 1200 * input[0] * input[0]; |
|||
output[1, 1] = 200; |
|||
output[0, 1] = -400 * input[0]; |
|||
output[1, 0] = output[0, 1]; |
|||
return output; |
|||
} |
|||
} |
|||
|
|||
public static class BigRosenbrockFunction |
|||
{ |
|||
public static double Value(Vector<double> input) |
|||
{ |
|||
return 1000.0 + 100.0 * RosenbrockFunction.Value(input / 100.0); |
|||
} |
|||
|
|||
public static Vector<double> Gradient(Vector<double> input) |
|||
{ |
|||
return 100.0 * RosenbrockFunction.Gradient(input / 100.0); |
|||
} |
|||
|
|||
public static Matrix<double> Hessian(Vector<double> input) |
|||
{ |
|||
return 100.0 * RosenbrockFunction.Hessian(input / 100.0); |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,104 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
using NUnit.Framework; |
|||
using MathNet.Numerics.Optimization; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
public class RosenbrockEvaluation : BaseEvaluation |
|||
{ |
|||
public const bool SupportsGradient = true; |
|||
public const bool SupportsHessian = true; |
|||
|
|||
protected override void setValue() |
|||
{ |
|||
this.ValueRaw = RosenbrockFunction.Value(this.Point); |
|||
} |
|||
|
|||
protected override void setGradient() |
|||
{ |
|||
this.GradientRaw = RosenbrockFunction.Gradient(this.Point); |
|||
} |
|||
|
|||
protected override void setHessian() |
|||
{ |
|||
this.HessianRaw = RosenbrockFunction.Hessian(this.Point); |
|||
} |
|||
} |
|||
|
|||
[TestFixture] |
|||
public class TestNewtonMinimizer |
|||
{ |
|||
|
|||
[Test] |
|||
public void FindMinimum_Rosenbrock_Easy() |
|||
{ |
|||
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian); |
|||
|
|||
var solver = new NewtonMinimizer(1e-5, 1000); |
|||
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { 1.2, 1.2 })); |
|||
|
|||
Assert.That(Math.Abs(result.MinimizingPoint[0] - 1.0), Is.LessThan(1e-3)); |
|||
Assert.That(Math.Abs(result.MinimizingPoint[1] - 1.0), Is.LessThan(1e-3)); |
|||
} |
|||
|
|||
[Test] |
|||
public void FindMinimum_Rosenbrock_Hard() |
|||
{ |
|||
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian); |
|||
var solver = new NewtonMinimizer(1e-5, 1000); |
|||
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { -1.2, 1.0 })); |
|||
|
|||
Assert.That(Math.Abs(result.MinimizingPoint[0] - 1.0), Is.LessThan(1e-3)); |
|||
Assert.That(Math.Abs(result.MinimizingPoint[1] - 1.0), Is.LessThan(1e-3)); |
|||
} |
|||
|
|||
[Test] |
|||
public void FindMinimum_Rosenbrock_Overton() |
|||
{ |
|||
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian); |
|||
var solver = new NewtonMinimizer(1e-5, 1000); |
|||
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { -0.9, -0.5 })); |
|||
|
|||
Assert.That(Math.Abs(result.MinimizingPoint[0] - 1.0), Is.LessThan(1e-3)); |
|||
Assert.That(Math.Abs(result.MinimizingPoint[1] - 1.0), Is.LessThan(1e-3)); |
|||
} |
|||
|
|||
[Test] |
|||
public void FindMinimum_Linesearch_Rosenbrock_Easy() |
|||
{ |
|||
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian); |
|||
var solver = new NewtonMinimizer(1e-5, 1000, true); |
|||
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { 1.2, 1.2 })); |
|||
|
|||
Assert.That(Math.Abs(result.MinimizingPoint[0] - 1.0), Is.LessThan(1e-3)); |
|||
Assert.That(Math.Abs(result.MinimizingPoint[1] - 1.0), Is.LessThan(1e-3)); |
|||
} |
|||
|
|||
[Test] |
|||
public void FindMinimum_Linesearch_Rosenbrock_Hard() |
|||
{ |
|||
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian); |
|||
var solver = new NewtonMinimizer(1e-5, 1000, true); |
|||
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { -1.2, 1.0 })); |
|||
|
|||
Assert.That(Math.Abs(result.MinimizingPoint[0] - 1.0), Is.LessThan(1e-3)); |
|||
Assert.That(Math.Abs(result.MinimizingPoint[1] - 1.0), Is.LessThan(1e-3)); |
|||
} |
|||
|
|||
[Test] |
|||
public void FindMinimum_Linesearch_Rosenbrock_Overton() |
|||
{ |
|||
var obj = new BaseObjectiveFunction<RosenbrockEvaluation>(RosenbrockEvaluation.SupportsGradient, RosenbrockEvaluation.SupportsHessian); |
|||
var solver = new NewtonMinimizer(1e-5, 1000, true); |
|||
var result = solver.FindMinimum(obj, new MathNet.Numerics.LinearAlgebra.Double.DenseVector(new double[] { -0.9, -0.5 })); |
|||
|
|||
Assert.That(Math.Abs(result.MinimizingPoint[0] - 1.0), Is.LessThan(1e-3)); |
|||
Assert.That(Math.Abs(result.MinimizingPoint[1] - 1.0), Is.LessThan(1e-3)); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue