/* LA: linear algebra C++ interface library Copyright (C) 2026 Jiri Pittner or This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "optimize.h" namespace LA { template T testproblem::funcgrad(const NRVec &x, NRVec *grad) const { int n=x.size(); T r= n*(n-1)/2; for(int i=0; isize()!=x.size()) laerror("grad size mismatch"); grad->copyonwrite(); //I forgot this initially for(int k=0; k static T cubic_interpolate(const T a, const T fa, const T da, const T b, const T fb, const T db) { T lo= a=lo+safe && r<=hi-safe)) r=(a+b)/2; //also catches NaN return r; } template T linesearch_wolfe(optevaluator &e, NRVec &x, T &f, NRVec &g, const NRVec &p, const T alpha1, const linesearch_hyperparameters &lp) { const T c1=lp.my_c1; const T c2=lp.my_c2; const int verbose=lp.my_verbose; const int maxiter= std::min(lp.my_maxlinesearch,e.remaining()); const int n=x.size(); const NRVec x0(x); const T f0=f; const T d0= g*p; if(d0>=0) {if(verbose) std::cout <<"linesearch: not a descent direction\n"; return 0;} const T alphamax=lp.my_maxsteplength/p.norm(); T alpha=std::min(alpha1,alphamax); //data of trial points; vectors are always freshly allocated, so that assignments (which share data) are safe T alpha0=0, f_alpha0=f0, d_alpha0=d0; NRVec x_alpha0(x0), g_alpha0(g); //previous trial point T alo=0,flo=0,dlo=0,ahi=0,fhi=0,dhi=0; NRVec xlo,glo; //bracketing interval for the zoom phase, lo is the best point bool bracketed=false; int it; //bracketing phase for(it=0; it xt = x0 + p*alpha; NRVec gt(n); T ft=e(xt,>); T dt= gt*p; if(verbose>1) std::cout <<"linesearch bracket "< static void BFGS_update(NRSMat &H, const NRVec &s, const NRVec &y, const T sy) { T rho = 1/sy; NRVec Hy = H*y; T yHy = y*Hy; NRVec u = s*((rho*rho*yHy + rho)/2); u.axpy(-rho,Hy); H.rank2update((T)1,s,u); } template bool optstate::stop(const T ff, const T gn) { f=ff; gnorm=gn; if(hp.my_verbose) { std::cout < optresult optstate::finish(hyperparameters *achieved) const { if(hp.my_verbose) { std::cout < e(o,hp.my_maxfunc); optstate st("BFGS",hp,e); const int n=x.size(); NRVec g(n); T f=e(x,&g); NRSMat H(n); H=1.; bool Hinit=true; //H is the initial guess, rescale it after the first step for(;; ++st.iter) { if(st.stop(f,g.norm())) break; NRVec p = H*g; p *= (T)-1; if(p*g >= 0) //H lost positive definiteness numerically, restart { if(verbose) std::cout <<"BFGS: not a descent direction, resetting inverse Hessian\n"; H=1.; Hinit=true; p = g*(T)-1; } NRVec xold(x), gold(g); T fold=f; //on the first step (or after restart) try unit step length in x, otherwise the Newton step T alpha=linesearch_wolfe(e,x,f,g,p,Hinit ? 1/p.norm() : (T)1,hp); if(alpha==0) { if(e.exhausted()) continue; //reported by stop() if(Hinit) {if(verbose) std::cout <<"BFGS: line search failed along steepest descent, giving up\n"; break;} if(verbose) std::cout <<"BFGS: line search failed, resetting inverse Hessian\n"; H=1.; Hinit=true; continue; } NRVec s = x-xold; NRVec y = g-gold; T sy = s*y; T snorm=s.norm(); T ynorm=y.norm(); if(verbose>1) std::cout <<"BFGS step alpha= "< &e, NRVec &x, double &f, NRVec &g, const NRVec &p, const double alpha1, const linesearch_hyperparameters &lp); template class optstate; template class testproblem; template class BFGS_Optimizer; }//namespace