3
0

SolverBFGS.cpp 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Optimization/SolverBFGS.h>
  9. #include <Optimization/LineSearch.h>
  10. #include <Optimization/Utilities.h>
  11. #include <Optimization/Constants.h>
  12. namespace NumericalMethods::Optimization
  13. {
  14. SolverResult MinimizeBFGS(const Function& f, const AZStd::vector<double>& xInitial)
  15. {
  16. // using the notation from Nocedal and Wright
  17. // H - an approximation to the inverse of the Hessian (matrix of second derivatives)
  18. // s - the difference between the function value this iteration and the previous iteration
  19. // y - the difference between the function gradient this iteration and the previous iteration
  20. SolverResult result;
  21. const AZ::u32 dimension = static_cast<AZ::u32>(xInitial.size());
  22. VectorVariable searchDirection(dimension);
  23. MatrixVariable H(dimension, dimension);
  24. MatrixVariable I(dimension, dimension);
  25. for (AZ::u32 i = 0; i < dimension; i++)
  26. {
  27. H.Element(i, i) = 1.0;
  28. I.Element(i, i) = 1.0;
  29. }
  30. VectorVariable x = VectorVariable::CreateFromVector(xInitial);
  31. double f_x = FunctionValue(f, x);
  32. for (; result.m_iterations < solverIterations; ++result.m_iterations)
  33. {
  34. // stop if the gradient is small enough
  35. VectorVariable gradient = Gradient(f, x);
  36. if (gradient.Norm() < gradientTolerance)
  37. {
  38. result.m_outcome = SolverOutcome::Success;
  39. result.m_xValues = x.GetValues();
  40. return result;
  41. }
  42. // find a search direction based on the Hessian and gradient and then search for an appropriate step size in
  43. // that direction
  44. searchDirection = -(H * gradient);
  45. LineSearchResult lineSearchResult = LineSearchWolfe(f, x, f_x, searchDirection);
  46. if (IsFailure(lineSearchResult))
  47. {
  48. result.m_outcome = SolverOutcome::Incomplete;
  49. result.m_xValues = x.GetValues();
  50. return result;
  51. }
  52. VectorVariable s = lineSearchResult.m_stepSize * searchDirection;
  53. x += s;
  54. f_x = lineSearchResult.m_functionValue;
  55. VectorVariable y = Gradient(f, x) - gradient;
  56. // on the first iteration, use a heuristic to scale the Hessian
  57. if (result.m_iterations == 0)
  58. {
  59. double scale = y.Dot(s) / y.Dot(y);
  60. for (AZ::u32 i = 0; i < dimension; i++)
  61. {
  62. H.Element(i, i) = scale;
  63. }
  64. }
  65. // update the approximate inverse Hessian using the BFGS formula (see Nocedal and Wright)
  66. double rho = 1.0 / y.Dot(s);
  67. H = (I - rho * OuterProduct(s, y)) * H * (I - rho * OuterProduct(y, s)) + rho * OuterProduct(s, s);
  68. }
  69. result.m_outcome = SolverOutcome::MaxIterations;
  70. result.m_xValues = x.GetValues();
  71. return result;
  72. }
  73. } // namespace NumericalMethods::Optimization