예제 #1
0
        /// <summary>
        /// Test a scaling operation of the high point, and replace it if it is an improvement
        /// </summary>
        /// <param name="scaleFactor"></param>
        /// <param name="errorProfile"></param>
        /// <param name="vertices"></param>
        /// <param name="errorValues"></param>
        /// <param name="objectiveFunction"></param>
        /// <returns></returns>
        static double TryToScaleSimplex(double scaleFactor, ref ErrorProfile errorProfile, Vector <double>[] vertices,
                                        double[] errorValues, IObjectiveFunction objectiveFunction)
        {
            // find the centroid through which we will reflect
            Vector <double> centroid = ComputeCentroid(vertices, errorProfile);

            // define the vector from the centroid to the high point
            Vector <double> centroidToHighPoint = vertices[errorProfile.HighestIndex].Subtract(centroid);

            // scale and position the vector to determine the new trial point
            Vector <double> newPoint = centroidToHighPoint.Multiply(scaleFactor).Add(centroid);

            // evaluate the new point
            objectiveFunction.EvaluateAt(newPoint);
            double newErrorValue = objectiveFunction.Value;

            // if it's better, replace the old high point
            if (newErrorValue < errorValues[errorProfile.HighestIndex])
            {
                vertices[errorProfile.HighestIndex]    = newPoint;
                errorValues[errorProfile.HighestIndex] = newErrorValue;
            }

            return(newErrorValue);
        }
예제 #2
0
 /// <summary>
 /// Evaluate the objective function at each vertex to create a corresponding
 /// list of error values for each vertex
 /// </summary>
 /// <param name="vertices"></param>
 /// <param name="objectiveFunction"></param>
 /// <returns></returns>
 static double[] InitializeErrorValues(Vector <double>[] vertices, IObjectiveFunction objectiveFunction)
 {
     double[] errorValues = new double[vertices.Length];
     for (int i = 0; i < vertices.Length; i++)
     {
         objectiveFunction.EvaluateAt(vertices[i]);
         errorValues[i] = objectiveFunction.Value;
     }
     return(errorValues);
 }
예제 #3
0
        /// <summary>
        /// Contract the simplex uniformly around the lowest point
        /// </summary>
        /// <param name="errorProfile"></param>
        /// <param name="vertices"></param>
        /// <param name="errorValues"></param>
        /// <param name="objectiveFunction"></param>
        static void ShrinkSimplex(ErrorProfile errorProfile, Vector <double>[] vertices, double[] errorValues,
                                  IObjectiveFunction objectiveFunction)
        {
            Vector <double> lowestVertex = vertices[errorProfile.LowestIndex];

            for (int i = 0; i < vertices.Length; i++)
            {
                if (i != errorProfile.LowestIndex)
                {
                    vertices[i] = (vertices[i].Add(lowestVertex)).Multiply(0.5);
                    objectiveFunction.EvaluateAt(vertices[i]);
                    errorValues[i] = objectiveFunction.Value;
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Find the minimum of the objective function given lower and upper bounds
        /// </summary>
        /// <param name="objective">The objective function, must support a gradient</param>
        /// <param name="initialGuess">The initial guess</param>
        /// <returns>The MinimizationResult which contains the minimum and the ExitCondition</returns>
        public MinimizationResult FindMinimum(IObjectiveFunction objective, Vector <double> initialGuess)
        {
            if (!objective.IsGradientSupported)
            {
                throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for BFGS minimization.");
            }

            objective.EvaluateAt(initialGuess);
            ValidateGradientAndObjective(objective);

            // Check that we're not already done
            ExitCondition currentExitCondition = ExitCriteriaSatisfied(objective, null, 0);

            if (currentExitCondition != ExitCondition.None)
            {
                return(new MinimizationResult(objective, 0, currentExitCondition));
            }

            // Set up line search algorithm
            var lineSearcher = new WeakWolfeLineSearch(1e-4, 0.9, Math.Max(ParameterTolerance, 1e-10), 1000);

            // First step
            var inversePseudoHessian = CreateMatrix.DenseIdentity <double>(initialGuess.Count);
            var lineSearchDirection  = -objective.Gradient;
            var stepSize             = 100 * GradientTolerance / (lineSearchDirection * lineSearchDirection);

            var previousPoint = objective;

            LineSearchResult lineSearchResult;

            try
            {
                lineSearchResult = lineSearcher.FindConformingStep(objective, lineSearchDirection, stepSize);
            }
            catch (OptimizationException e)
            {
                throw new InnerOptimizationException("Line search failed.", e);
            }
            catch (ArgumentException e)
            {
                throw new InnerOptimizationException("Line search failed.", e);
            }

            var candidate = lineSearchResult.FunctionInfoAtMinimum;

            ValidateGradientAndObjective(candidate);

            var gradient = candidate.Gradient;
            var step     = candidate.Point - initialGuess;

            // Subsequent steps
            Matrix <double> I = CreateMatrix.DiagonalIdentity <double>(initialGuess.Count);
            int             iterations;
            int             totalLineSearchSteps = lineSearchResult.Iterations;
            int             iterationsWithNontrivialLineSearch = lineSearchResult.Iterations > 0 ? 0 : 1;

            iterations = DoBfgsUpdate(ref currentExitCondition, lineSearcher, ref inversePseudoHessian, ref lineSearchDirection, ref previousPoint, ref lineSearchResult, ref candidate, ref step, ref totalLineSearchSteps, ref iterationsWithNontrivialLineSearch);

            if (iterations == MaximumIterations && currentExitCondition == ExitCondition.None)
            {
                throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", MaximumIterations));
            }

            return(new MinimizationWithLineSearchResult(candidate, iterations, ExitCondition.AbsoluteGradient, totalLineSearchSteps, iterationsWithNontrivialLineSearch));
        }
예제 #5
0
        public MinimizationResult FindMinimum(IObjectiveFunction objective, Vector <double> initialGuess)
        {
            if (!objective.IsGradientSupported)
            {
                throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for Newton minimization.");
            }

            if (!objective.IsHessianSupported)
            {
                throw new IncompatibleObjectiveException("Hessian not supported in objective function, but required for Newton minimization.");
            }

            // Check that we're not already done
            objective.EvaluateAt(initialGuess);
            ValidateGradient(objective);
            if (ExitCriteriaSatisfied(objective.Gradient))
            {
                return(new MinimizationResult(objective, 0, ExitCondition.AbsoluteGradient));
            }

            // Set up line search algorithm
            var lineSearcher = new WeakWolfeLineSearch(1e-4, 0.9, 1e-4, maxIterations: 1000);

            // Subsequent steps
            int  iterations           = 0;
            int  totalLineSearchSteps = 0;
            int  iterationsWithNontrivialLineSearch = 0;
            bool tmpLineSearch = false;

            while (!ExitCriteriaSatisfied(objective.Gradient) && iterations < MaximumIterations)
            {
                ValidateHessian(objective);

                var searchDirection = objective.Hessian.LU().Solve(-objective.Gradient);
                if (searchDirection * objective.Gradient >= 0)
                {
                    searchDirection = -objective.Gradient;
                    tmpLineSearch   = true;
                }

                if (UseLineSearch || tmpLineSearch)
                {
                    LineSearchResult result;
                    try
                    {
                        result = lineSearcher.FindConformingStep(objective, searchDirection, 1.0);
                    }
                    catch (Exception e)
                    {
                        throw new InnerOptimizationException("Line search failed.", e);
                    }

                    iterationsWithNontrivialLineSearch += result.Iterations > 0 ? 1 : 0;
                    totalLineSearchSteps += result.Iterations;
                    objective             = result.FunctionInfoAtMinimum;
                }
                else
                {
                    objective.EvaluateAt(objective.Point + searchDirection);
                }

                ValidateGradient(objective);

                tmpLineSearch = false;
                iterations   += 1;
            }

            if (iterations == MaximumIterations)
            {
                throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", MaximumIterations));
            }

            return(new MinimizationWithLineSearchResult(objective, iterations, ExitCondition.AbsoluteGradient, totalLineSearchSteps, iterationsWithNontrivialLineSearch));
        }
예제 #6
0
        /// <summary>
        /// Find the minimum of the objective function given lower and upper bounds
        /// </summary>
        /// <param name="objective">The objective function, must support a gradient</param>
        /// <param name="lowerBound">The lower bound</param>
        /// <param name="upperBound">The upper bound</param>
        /// <param name="initialGuess">The initial guess</param>
        /// <returns>The MinimizationResult which contains the minimum and the ExitCondition</returns>
        public MinimizationResult FindMinimum(IObjectiveFunction objective, Vector <double> lowerBound, Vector <double> upperBound, Vector <double> initialGuess)
        {
            _lowerBound = lowerBound;
            _upperBound = upperBound;
            if (!objective.IsGradientSupported)
            {
                throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for BFGS minimization.");
            }

            // Check that dimensions match
            if (lowerBound.Count != upperBound.Count || lowerBound.Count != initialGuess.Count)
            {
                throw new ArgumentException("Dimensions of bounds and/or initial guess do not match.");
            }

            // Check that initial guess is feasible
            for (int ii = 0; ii < initialGuess.Count; ++ii)
            {
                if (initialGuess[ii] < lowerBound[ii] || initialGuess[ii] > upperBound[ii])
                {
                    throw new ArgumentException("Initial guess is not in the feasible region");
                }
            }

            objective.EvaluateAt(initialGuess);
            ValidateGradientAndObjective(objective);

            // Check that we're not already done
            ExitCondition currentExitCondition = ExitCriteriaSatisfied(objective, null, 0);

            if (currentExitCondition != ExitCondition.None)
            {
                return(new MinimizationResult(objective, 0, currentExitCondition));
            }

            // Set up line search algorithm
            var lineSearcher = new StrongWolfeLineSearch(1e-4, 0.9, Math.Max(ParameterTolerance, 1e-5), maxIterations: 1000);

            // Declare state variables
            Vector <double> reducedSolution1, reducedGradient, reducedInitialPoint, reducedCauchyPoint, solution1;
            Matrix <double> reducedHessian;
            List <int>      reducedMap;

            // First step
            var pseudoHessian = CreateMatrix.DiagonalIdentity <double>(initialGuess.Count);

            // Determine active set
            var gradientProjectionResult = QuadraticGradientProjectionSearch.Search(objective.Point, objective.Gradient, pseudoHessian, lowerBound, upperBound);
            var cauchyPoint = gradientProjectionResult.CauchyPoint;
            var fixedCount  = gradientProjectionResult.FixedCount;
            var isFixed     = gradientProjectionResult.IsFixed;
            var freeCount   = lowerBound.Count - fixedCount;

            if (freeCount > 0)
            {
                reducedGradient     = new DenseVector(freeCount);
                reducedHessian      = new DenseMatrix(freeCount, freeCount);
                reducedMap          = new List <int>(freeCount);
                reducedInitialPoint = new DenseVector(freeCount);
                reducedCauchyPoint  = new DenseVector(freeCount);

                CreateReducedData(objective.Point, cauchyPoint, isFixed, lowerBound, upperBound, objective.Gradient, pseudoHessian, reducedInitialPoint, reducedCauchyPoint, reducedGradient, reducedHessian, reducedMap);

                // Determine search direction and maximum step size
                reducedSolution1 = reducedInitialPoint + reducedHessian.Cholesky().Solve(-reducedGradient);

                solution1 = ReducedToFull(reducedMap, reducedSolution1, cauchyPoint);
            }
            else
            {
                solution1 = cauchyPoint;
            }

            var directionFromCauchy    = solution1 - cauchyPoint;
            var maxStepFromCauchyPoint = FindMaxStep(cauchyPoint, directionFromCauchy, lowerBound, upperBound);

            var solution2 = cauchyPoint + Math.Min(maxStepFromCauchyPoint, 1.0) * directionFromCauchy;

            var lineSearchDirection = solution2 - objective.Point;
            var maxLineSearchStep   = FindMaxStep(objective.Point, lineSearchDirection, lowerBound, upperBound);
            var estStepSize         = -objective.Gradient * lineSearchDirection / (lineSearchDirection * pseudoHessian * lineSearchDirection);

            var startingStepSize = Math.Min(Math.Max(estStepSize, 1.0), maxLineSearchStep);

            // Line search
            LineSearchResult lineSearchResult;

            try
            {
                lineSearchResult = lineSearcher.FindConformingStep(objective, lineSearchDirection, startingStepSize, upperBound: maxLineSearchStep);
            }
            catch (Exception e)
            {
                throw new InnerOptimizationException("Line search failed.", e);
            }

            var previousPoint  = objective.Fork();
            var candidatePoint = lineSearchResult.FunctionInfoAtMinimum;
            var gradient       = candidatePoint.Gradient;
            var step           = candidatePoint.Point - initialGuess;

            // Subsequent steps
            int totalLineSearchSteps = lineSearchResult.Iterations;
            int iterationsWithNontrivialLineSearch = lineSearchResult.Iterations > 0 ? 0 : 1;

            int iterations = DoBfgsUpdate(ref currentExitCondition, lineSearcher, ref pseudoHessian, ref lineSearchDirection, ref previousPoint, ref lineSearchResult, ref candidatePoint, ref step, ref totalLineSearchSteps, ref iterationsWithNontrivialLineSearch);

            if (iterations == MaximumIterations && currentExitCondition == ExitCondition.None)
            {
                throw new MaximumIterationsException(string.Format("Maximum iterations ({0}) reached.", MaximumIterations));
            }

            return(new MinimizationWithLineSearchResult(candidatePoint, iterations, currentExitCondition, totalLineSearchSteps, iterationsWithNontrivialLineSearch));
        }
        public static MinimizationResult Minimum(IObjectiveFunction objective, Vector <double> initialGuess, double gradientTolerance = 1e-8, int maxIterations = 1000)
        {
            if (!objective.IsGradientSupported)
            {
                throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for ConjugateGradient minimization.");
            }

            objective.EvaluateAt(initialGuess);
            var gradient = objective.Gradient;

            ValidateGradient(objective);

            // Check that we're not already done
            if (gradient.Norm(2.0) < gradientTolerance)
            {
                return(new MinimizationResult(objective, 0, ExitCondition.AbsoluteGradient));
            }

            // Set up line search algorithm
            var lineSearcher = new WeakWolfeLineSearch(1e-4, 0.1, 1e-4, 1000);

            // First step
            var    steepestDirection = -gradient;
            var    searchDirection   = steepestDirection;
            double initialStepSize   = 100 * gradientTolerance / (gradient * gradient);

            LineSearchResult result;

            try
            {
                result = lineSearcher.FindConformingStep(objective, searchDirection, initialStepSize);
            }
            catch (Exception e)
            {
                throw new InnerOptimizationException("Line search failed.", e);
            }

            objective = result.FunctionInfoAtMinimum;
            ValidateGradient(objective);

            double stepSize = result.FinalStep;

            // Subsequent steps
            int iterations           = 1;
            int totalLineSearchSteps = result.Iterations;
            int iterationsWithNontrivialLineSearch = result.Iterations > 0 ? 0 : 1;
            int steepestDescentResets = 0;

            while (objective.Gradient.Norm(2.0) >= gradientTolerance && iterations < maxIterations)
            {
                var previousSteepestDirection = steepestDirection;
                steepestDirection = -objective.Gradient;
                var searchDirectionAdjuster = Math.Max(0, steepestDirection * (steepestDirection - previousSteepestDirection) / (previousSteepestDirection * previousSteepestDirection));
                searchDirection = steepestDirection + searchDirectionAdjuster * searchDirection;
                if (searchDirection * objective.Gradient >= 0)
                {
                    searchDirection        = steepestDirection;
                    steepestDescentResets += 1;
                }

                try
                {
                    result = lineSearcher.FindConformingStep(objective, searchDirection, stepSize);
                }
                catch (Exception e)
                {
                    throw new InnerOptimizationException("Line search failed.", e);
                }

                iterationsWithNontrivialLineSearch += result.Iterations == 0 ? 1 : 0;
                totalLineSearchSteps += result.Iterations;
                stepSize              = result.FinalStep;
                objective             = result.FunctionInfoAtMinimum;
                iterations           += 1;
            }

            //if (iterations == maxIterations)
            //{
            //    throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", maxIterations));
            //}

            return(new MinimizationWithLineSearchResult(objective, iterations, ExitCondition.AbsoluteGradient, totalLineSearchSteps, iterationsWithNontrivialLineSearch));
        }
예제 #8
0
        /// <summary></summary>
        /// <param name="startingPoint">The objective function being optimized, evaluated at the starting point of the search</param>
        /// <param name="searchDirection">Search direction</param>
        /// <param name="initialStep">Initial size of the step in the search direction</param>
        /// <param name="upperBound">The upper bound</param>
        public LineSearchResult FindConformingStep(IObjectiveFunctionEvaluation startingPoint, Vector <double> searchDirection, double initialStep, double upperBound)
        {
            ValidateInputArguments(startingPoint, searchDirection, initialStep, upperBound);

            double lowerBound = 0.0;
            double step       = initialStep;

            double          initialValue    = startingPoint.Value;
            Vector <double> initialGradient = startingPoint.Gradient;

            double initialDd = searchDirection * initialGradient;

            IObjectiveFunction objective = startingPoint.CreateNew();
            int           ii;
            ExitCondition reasonForExit = ExitCondition.None;

            for (ii = 0; ii < MaximumIterations; ++ii)
            {
                objective.EvaluateAt(startingPoint.Point + searchDirection * step);
                ValidateGradient(objective);
                ValidateValue(objective);

                double stepDd = searchDirection * objective.Gradient;

                if (objective.Value > initialValue + C1 * step * initialDd)
                {
                    upperBound = step;
                    step       = 0.5 * (lowerBound + upperBound);
                }
                else if (WolfeCondition(stepDd, initialDd))
                {
                    lowerBound = step;
                    step       = double.IsPositiveInfinity(upperBound) ? 2 * lowerBound : 0.5 * (lowerBound + upperBound);
                }
                else
                {
                    reasonForExit = WolfeExitCondition;
                    break;
                }

                if (!double.IsInfinity(upperBound))
                {
                    double maxRelChange = 0.0;
                    for (int jj = 0; jj < objective.Point.Count; ++jj)
                    {
                        double tmp = Math.Abs(searchDirection[jj] * (upperBound - lowerBound)) / Math.Max(Math.Abs(objective.Point[jj]), 1.0);
                        maxRelChange = Math.Max(maxRelChange, tmp);
                    }
                    if (maxRelChange < ParameterTolerance)
                    {
                        reasonForExit = ExitCondition.LackOfProgress;
                        break;
                    }
                }
            }

            if (ii == MaximumIterations && Double.IsPositiveInfinity(upperBound))
            {
                throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached. Function appears to be unbounded in search direction.", MaximumIterations));
            }

            if (ii == MaximumIterations)
            {
                throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", MaximumIterations));
            }

            return(new LineSearchResult(objective, ii, step, reasonForExit));
        }
예제 #9
0
        /// <summary>
        /// Finds the minimum of the objective function with an initial perturbation
        /// </summary>
        /// <param name="objectiveFunction">The objective function, no gradient or hessian needed</param>
        /// <param name="initialGuess">The initial guess</param>
        /// <param name="initalPertubation">The initial perturbation</param>
        /// <returns>The minimum point</returns>
        public static MinimizationResult Minimum(IObjectiveFunction objectiveFunction, Vector <double> initialGuess, Vector <double> initalPertubation, double convergenceTolerance = 1e-8, int maximumIterations = 1000)
        {
            // confirm that we are in a position to commence
            if (objectiveFunction == null)
            {
                throw new ArgumentNullException(nameof(objectiveFunction), "ObjectiveFunction must be set to a valid ObjectiveFunctionDelegate");
            }

            if (initialGuess == null)
            {
                throw new ArgumentNullException(nameof(initialGuess), "initialGuess must be initialized");
            }

            if (initalPertubation == null)
            {
                throw new ArgumentNullException(nameof(initalPertubation), "initalPertubation must be initialized, if unknown use overloaded version of FindMinimum()");
            }

            SimplexConstant[] simplexConstants = SimplexConstant.CreateSimplexConstantsFromVectors(initialGuess, initalPertubation);

            // create the initial simplex
            int numDimensions = simplexConstants.Length;
            int numVertices   = numDimensions + 1;

            Vector <double>[] vertices = InitializeVertices(simplexConstants);

            int           evaluationCount = 0;
            ExitCondition exitCondition;
            ErrorProfile  errorProfile;

            double[] errorValues          = InitializeErrorValues(vertices, objectiveFunction);
            int      numTimesHasConverged = 0;

            // iterate until we converge, or complete our permitted number of iterations
            while (true)
            {
                errorProfile = EvaluateSimplex(errorValues);

                // see if the range in point heights is small enough to exit
                // to handle the case when the function is symmetrical and extra iteration is performed
                if (HasConverged(convergenceTolerance, errorProfile, errorValues))
                {
                    numTimesHasConverged++;
                }
                else
                {
                    numTimesHasConverged = 0;
                }
                if (numTimesHasConverged == 2)
                {
                    exitCondition = ExitCondition.Converged;
                    break;
                }

                // attempt a reflection of the simplex
                double reflectionPointValue = TryToScaleSimplex(-1.0, ref errorProfile, vertices, errorValues, objectiveFunction);
                ++evaluationCount;
                if (reflectionPointValue <= errorValues[errorProfile.LowestIndex])
                {
                    // it's better than the best point, so attempt an expansion of the simplex
                    TryToScaleSimplex(2.0, ref errorProfile, vertices, errorValues, objectiveFunction);
                    ++evaluationCount;
                }
                else if (reflectionPointValue >= errorValues[errorProfile.NextHighestIndex])
                {
                    // it would be worse than the second best point, so attempt a contraction to look
                    // for an intermediate point
                    double currentWorst          = errorValues[errorProfile.HighestIndex];
                    double contractionPointValue = TryToScaleSimplex(0.5, ref errorProfile, vertices, errorValues, objectiveFunction);
                    ++evaluationCount;
                    if (contractionPointValue >= currentWorst)
                    {
                        // that would be even worse, so let's try to contract uniformly towards the low point;
                        // don't bother to update the error profile, we'll do it at the start of the
                        // next iteration
                        ShrinkSimplex(errorProfile, vertices, errorValues, objectiveFunction);
                        evaluationCount += numVertices; // that required one function evaluation for each vertex; keep track
                    }
                }
                // check to see if we have exceeded our alloted number of evaluations
                if (evaluationCount >= maximumIterations)
                {
                    throw new MaximumIterationsException(FormattableString.Invariant($"Maximum iterations ({maximumIterations}) reached."));
                }
            }
            objectiveFunction.EvaluateAt(vertices[errorProfile.LowestIndex]);
            var regressionResult = new MinimizationResult(objectiveFunction, evaluationCount, exitCondition);

            return(regressionResult);
        }
예제 #10
0
        /// <summary>
        /// Find the minimum of the objective function given lower and upper bounds
        /// </summary>
        /// <param name="objective">The objective function, must support a gradient</param>
        /// <param name="initialGuess">The initial guess</param>
        /// <returns>The MinimizationResult which contains the minimum and the ExitCondition</returns>
        public MinimizationResult FindMinimum(IObjectiveFunction objective, Vector <double> initialGuess)
        {
            if (!objective.IsGradientSupported)
            {
                throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for L-BFGS minimization.");
            }

            objective.EvaluateAt(initialGuess);
            ValidateGradientAndObjective(objective);

            // Check that we're not already done
            ExitCondition currentExitCondition = ExitCriteriaSatisfied(objective, null, 0);

            if (currentExitCondition != ExitCondition.None)
            {
                return(new MinimizationResult(objective, 0, currentExitCondition));
            }

            // Set up line search algorithm
            var lineSearcher = new WeakWolfeLineSearch(1e-4, 0.9, Math.Max(ParameterTolerance, 1e-10), 1000);

            // First step

            var lineSearchDirection = -objective.Gradient;
            var stepSize            = (100 * GradientTolerance) / (lineSearchDirection * lineSearchDirection);

            var previousPoint = objective;

            LineSearchResult lineSearchResult;

            try
            {
                lineSearchResult = lineSearcher.FindConformingStep(objective, lineSearchDirection, stepSize);
            }
            catch (OptimizationException e)
            {
                throw new InnerOptimizationException("Line search failed.", e);
            }
            catch (ArgumentException e)
            {
                throw new InnerOptimizationException("Line search failed.", e);
            }

            var candidate = lineSearchResult.FunctionInfoAtMinimum;

            ValidateGradientAndObjective(candidate);

            var gradient  = candidate.Gradient;
            var step      = candidate.Point - initialGuess;
            var yk        = candidate.Gradient - previousPoint.Gradient;
            var ykhistory = new List <Vector <double> >()
            {
                yk
            };
            var skhistory = new List <Vector <double> >()
            {
                step
            };
            var rhokhistory = new List <double>()
            {
                1.0 / yk.DotProduct(step)
            };

            // Subsequent steps
            int iterations           = 1;
            int totalLineSearchSteps = lineSearchResult.Iterations;
            int iterationsWithNontrivialLineSearch = lineSearchResult.Iterations > 0 ? 0 : 1;

            previousPoint = candidate;
            while ((iterations++ < MaximumIterations) && (previousPoint.Gradient.Norm(2) >= GradientTolerance))
            {
                lineSearchDirection = -ApplyLbfgsUpdate(previousPoint, ykhistory, skhistory, rhokhistory);
                var directionalDerivative = previousPoint.Gradient.DotProduct(lineSearchDirection);
                if (directionalDerivative > 0)
                {
                    throw new InnerOptimizationException("Direction is not a descent direction.");
                }
                try
                {
                    lineSearchResult = lineSearcher.FindConformingStep(previousPoint, lineSearchDirection, 1.0);
                }
                catch (OptimizationException e)
                {
                    throw new InnerOptimizationException("Line search failed.", e);
                }
                catch (ArgumentException e)
                {
                    throw new InnerOptimizationException("Line search failed.", e);
                }
                iterationsWithNontrivialLineSearch += lineSearchResult.Iterations > 0 ? 1 : 0;
                totalLineSearchSteps += lineSearchResult.Iterations;

                candidate            = lineSearchResult.FunctionInfoAtMinimum;
                currentExitCondition = ExitCriteriaSatisfied(candidate, previousPoint, iterations);
                if (currentExitCondition != ExitCondition.None)
                {
                    break;
                }
                step = candidate.Point - previousPoint.Point;
                yk   = candidate.Gradient - previousPoint.Gradient;
                ykhistory.Add(yk);
                skhistory.Add(step);
                rhokhistory.Add(1.0 / yk.DotProduct(step));
                previousPoint = candidate;
                if (ykhistory.Count > Memory)
                {
                    ykhistory.RemoveAt(0);
                    skhistory.RemoveAt(0);
                    rhokhistory.RemoveAt(0);
                }
            }

            if ((iterations == MaximumIterations) && (currentExitCondition == ExitCondition.None))
            {
                throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", MaximumIterations));
            }

            return(new MinimizationWithLineSearchResult(candidate, iterations, ExitCondition.AbsoluteGradient, totalLineSearchSteps, iterationsWithNontrivialLineSearch));
        }