G+Smo  24.08.0
Geometry + Simulation Modules
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gsHLBFGS.h
1 #include <functional>
2 #include <vector>
3 #include <cassert>
4 
5 #pragma once
6 
9 
10 #include "HLBFGS/HLBFGS.h"
11 /*
12 To do:
13 - Use Eigen
14 - change T, int
15 */
16 
17 // https://xueyuhanlang.github.io/software/HLBFGS/
18 
19 #include<iostream>
20 #include <iomanip>// Header file needed to use setw
21 
22 namespace gismo
23 {
24 
25 struct gsHLBFGSObjective
26 {
27  typedef real_t T;
28  typedef gsEigen::Matrix<T, gsEigen::Dynamic, 1> Vector;
29  // typedef Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> Matrix;
30 
31  gsHLBFGSObjective(gsOptProblem<T>* objective)
32  :
33  obj(objective)
34  { }
35 
36  gsHLBFGSObjective()
37  :
38  obj(nullptr)
39  { }
40 
41  void operator() (int N, real_t* x, real_t* prev_x, real_t* f, real_t* g) const
42  {
43  gsAsConstVector<real_t> u(x,N);
44  *f = obj->evalObj(u);
45 
46  gsAsVector<real_t> Gvec(g,N);
47  obj->gradObj_into(u,Gvec);
48  }
49 
50  gsOptProblem<T> * obj;
51 };
52 
53 template<typename T>
54 class gsHLBFGS : public gsOptimizer<T>
55 {
56 public:
57  using Base = gsOptimizer<T>;
58 
59 public:
60  // gsHLBFGS(gsOptProblem<T> * problem)
61  // :
62  // Base(problem)
63  // {
64  // this->defaultOptions();
65  // }
66 
67  gsHLBFGS(gsOptProblem<T> * problem)
68  :
69  Base(problem)
70  {
71  this->defaultOptions();
72  }
73 
74 
75 protected:
76  void defaultOptions()
77  {
78  Base::defaultOptions();
79  m_options.addReal("MinGradientLength","Minimal gradient length",1e-9);
80  m_options.addReal("MinStepLength","Minimal step length",1e-9);
81  m_options.addInt("LBFGSUpdates","Number of LBFGS updates (typically 3-20, put to 0 for gradient descent)",20);
82  }
83 
84  void getOptions()
85  {
86  Base::getOptions();
87  m_minGradientLength = m_options.getReal("MinGradientLength");
88  m_minStepLength = m_options.getReal("MinStepLength");
89  m_M = m_options.getInt("LBFGSUpdates");
90 
91  // m_hlbfgs_info[3]:The lbfgs strategy. 0: standard, 1: M1QN3 strategy (recommended)
92  // Gilbert, J. C., & LemarĂ©chal, C. (1989). Some numerical experiments with variable-storage
93  // quasi-Newton algorithms. Mathematical programming, 45(1), 407-435.
94  m_hlbfgs_info[3] = 1;
95 
96  m_hlbfgs_info[4] = static_cast<index_t>(m_maxIterations);
97  m_hlbfgs_info[5] = static_cast<index_t>(m_verbose);
98 
99  m_hlbfgs_pars[5] = m_minGradientLength;
100  m_hlbfgs_pars[6] = m_minStepLength;
101  }
102 
103 protected:
104 
105  static void static_func_grad(int N, T* x, T* prev_x, T* f, T* g)
106  {
107  (*local_func_grad)(N, x, prev_x, f, g);
108  }
109 
110  static void static_newiter_callback(int iter, int call_iter, T *x, T* f, T *g, T* gnorm)
111  {
112  (*local_newiter_callback)(iter, call_iter, x, f, g, gnorm);
113  }
114 
115 public:
116  // void run(const gsMatrix<T> & initialGuess)
117  void run(std::vector<T> & sol)
118  {
119  // m_curDesign = initialGuess;
120 
121  INIT_HLBFGS(m_hlbfgs_pars, m_hlbfgs_info);
122  this->getOptions();
123  // std::function<void(index_t N, T* x, T* prev_x, T* f, T* g)>
124 
125  const std::function<void(int N, real_t* x, real_t* prev_x, real_t* f, real_t* g)> wrapfunc =
126  [&](int N, real_t* x, real_t*, real_t* f, real_t* g) {
127  std::vector<real_t> array_x(N), array_g(N);
128 
129  gsAsConstVector<real_t> u(x,N);
130  *f = m_op->evalObj(u);
131 
132  gsAsVector<real_t> Gvec(g,N);
133  m_op->gradObj_into(u,Gvec);
134  };
135 
136  local_func_grad = &wrapfunc;
137 
138  const std::function<void(int iter, int call_iter, T *x, T* f, T *g, T* gnorm)> wrapcallback =
139  [this](int iter, int call_iter, T *x, T* f, T *g, T* gnorm)
140  {
141  // TODO: give it a flag to decide if we need to print or not.
142  if (m_verbose==2)
143  gsInfo<<"# iter "<< iter << ": #func eval. " << call_iter << ", f = " << *f <<", ||g|| = " << *gnorm << std::endl;
144  };
145 
146  local_newiter_callback = &wrapcallback;
147 
148  // WHAT ABOUT CONSTRAINTS????
149  HLBFGS(
150  sol.size(),
151  m_M, // hardcoded??? -->>> change to an option of the class
152  sol.data(),
153  static_func_grad,
154 // obj,
155  nullptr,
156  HLBFGS_UPDATE_Hessian,
157  static_newiter_callback,
158  m_hlbfgs_pars,
159  m_hlbfgs_info
160  );
161  }
162 
163  void solve(const gsMatrix<T> & initialGuess)
164  {
165  GISMO_ASSERT(initialGuess.cols()==1,"The initial guess should have vector format");
166  std::vector<T> sol(initialGuess.rows());
167  gsMatrix<T>::Map(sol.data(), initialGuess.rows(),1) = initialGuess;
168  this->run(sol);
169 
170  m_curDesign = gsMatrix<T>::Map(sol.data(), sol.size(),1);
171  m_numIterations = m_hlbfgs_info[2];
172  m_finalObjective = m_op->evalObj(gsAsConstVector<T>(m_curDesign.data(),m_curDesign.rows()));
173 
174  if (m_verbose==1)
175  gsInfo<<"HLBFGS finished in "<<m_numIterations<<" iterations, with final objective "<<m_finalObjective<<"\n";
176 
177  }
178 
180  // own analytic gradient. Author: Ye Ji (jiyess@outlook.com)
181  void gradientCheck(const gsVector<T> &u) {
182  // Get the analytic gradient
183  std::vector<T> sol(u.size());
184  gsAsVector<T> analyticGrad(sol);
185  std::copy(u.begin(), u.end(), sol.begin());
186  m_op->gradObj_into(sol, analyticGrad);
187 
188  // Finite difference calculation of gradient using central differences
189  gsVector<T> numericalGrad(u.size());
190  T h = sqrt(std::numeric_limits<T>::epsilon()) * u.cwiseAbs().maxCoeff();
191  T forwardValue, backwardValue;
192 
193  std::vector<T> solForNumericalGrad(u.size());
194  std::copy(u.begin(), u.end(), solForNumericalGrad.begin());
195 
196  // Iterate through each dimension
197  for (int k = 0; k < u.size(); ++k) {
198  // Compute function value at forward step
199  solForNumericalGrad[k] += h;
200  forwardValue = m_op->evalObj(solForNumericalGrad);
201 
202  // Compute function value at backward step
203  solForNumericalGrad[k] -= 2.0 * h;
204  backwardValue = m_op->evalObj(solForNumericalGrad);
205 
206  // Compute the numerical gradient using central difference formula
207  numericalGrad(k) = (forwardValue - backwardValue) / (2.0 * h);
208 
209  // Reset the k-th component to its original value
210  solForNumericalGrad[k] += h;
211  }
212 
213  // Compare the analytic gradient and the numerical gradient
214  gsInfo << "Analytical gradient: Finite difference gradient: \n";
215 
216  int numElementsToPrint = std::min(analyticGrad.size(), 30);
217  for (int i = 0; i < numElementsToPrint; ++i) {
218  gsInfo << std::setw(5) << i << std::setw(20) << analyticGrad(i)
219  << std::setw(20) << numericalGrad(i) << "\n";
220  }
221 
222  if (u.size() > 30) {
223  gsInfo << "(Displaying the first 30 components only)\n";
224  }
225 
226  T relativeError =
227  (analyticGrad - numericalGrad).norm() / analyticGrad.norm();
228  gsInfo << "The relative error between the analytic gradient and the "
229  "numerical gradient is: " << relativeError << "\n\n";
230  }
231 
232 // Members taken from Base
233 protected:
234  using Base::m_op;
235  using Base::m_numIterations;
236  using Base::m_finalObjective;
237  using Base::m_curDesign;
238  using Base::m_options;
239  using Base::m_verbose;
240  using Base::m_maxIterations;
241 
242  using Base::defaultOptions;
243  using Base::getOptions;
244 
245 // Options
246 protected:
247  T m_minGradientLength;
248  T m_minStepLength;
249 
250 // HLBFGS options
251 protected:
252  index_t m_hlbfgs_info[20] = {0};
253  T m_hlbfgs_pars[20] = {0};
254  index_t m_M;
255 
256  static const std::function<void(int N, T* x, T* prev_x, T* f, T* g)> * local_func_grad;
257  static const std::function<void(int iter, int call_iter, T *x, T* f, T *g, T* gnorm)> * local_newiter_callback;
258 };
259 
260 template<typename T>
261 const std::function<void(int N, T* x, T* prev_x, T* f, T* g)> * gsHLBFGS<T>::local_func_grad = nullptr;
262 template<typename T>
263 const std::function<void(int iter, int call_iter, T *x, T* f, T *g, T* gnorm)> * gsHLBFGS<T>::local_newiter_callback = nullptr;
264 
265 
266 // using gsHLBFGS = gdc::GradientDescent<T, Objective, StepSize, Callback, FiniteDifferences>;
267 
268 } //namespace gismo
269 
gsOptionList m_options
Options.
Definition: gsOptimizer.h:108
#define index_t
Definition: gsConfig.h:32
const index_t & getInt(const std::string &label) const
Reads value for option label from options.
Definition: gsOptionList.cpp:37
This file is part of the G+Smo library.
#define GISMO_ASSERT(cond, message)
Definition: gsDebug.h:89
void addInt(const std::string &label, const std::string &desc, const index_t &value)
Adds a option named label, with description desc and value value.
Definition: gsOptionList.cpp:201
#define gsInfo
Definition: gsDebug.h:43
void addReal(const std::string &label, const std::string &desc, const Real &value)
Adds a option named label, with description desc and value value.
Definition: gsOptionList.cpp:211
This is the main header file that collects wrappers of Eigen for linear algebra.
gsMatrix< T > m_curDesign
Current design variables (and starting point )
Definition: gsOptimizer.h:105
Real getReal(const std::string &label) const
Reads value for option label from options.
Definition: gsOptionList.cpp:44
void copy(T begin, T end, U *result)
Small wrapper for std::copy mimicking std::copy for a raw pointer destination, copies n positions sta...
Definition: gsMemory.h:391