diff --git a/optimize.cc b/optimize.cc index cdd3cfc..4717153 100644 --- a/optimize.cc +++ b/optimize.cc @@ -18,6 +18,8 @@ #include "optimize.h" #include "la_random.h" +#include "diis.h" +#include "nonclass.h" namespace LA { @@ -47,6 +49,151 @@ return r; } +//central difference (f(x+h e_i) - f(x-h e_i))/(2h); xs is a working copy of x, restored on return +template +static T central_difference(const OptProblem *p, NRVec &xs, const NRVec &x, const int i, const T h) +{ +xs[i]=x[i]+h; +T d=p->funcgrad(xs,NULL); +xs[i]=x[i]-h; +d-=p->funcgrad(xs,NULL); +xs[i]=x[i]; +return d/(2*h); +} + + +template +ParameterShift::ParameterShift(const OptProblem *pp, const T hh) : OptProblemAdapter(pp), trig(optcapability >(pp)), h(hh) +{ +if(!trig) laerror("ParameterShift needs a problem providing OptTrigonometric"); +} + +//derivative of a trigonometric polynomial of degree R with frequencies 1..R (in units of w): +//f'(0) = sum_{mu=1}^{2R} (-1)^{mu-1} f(s_mu)/(4R sin^2(s_mu/2)), s_mu = (2mu-1)pi/(2R) (Wierichs et al., Quantum 6, 677 (2022)) +template +T ParameterShift::funcgrad(const NRVec &x, NRVec *grad) const +{ +T f=this->p->funcgrad(x,NULL); +if(!grad) return f; +grad->copyonwrite(); +NRVec xs(x); +xs.copyonwrite(); //working copy with shifted elements +for(int i=0; idegree(i); + if(R==0) {(*grad)[i]=central_difference(this->p,xs,x,i,h); continue;} + const T w=2*M_PI/trig->period(i); + T d=0; + for(int mu=1; mu<=2*R; ++mu) + { + const T s=(2*mu-1)*M_PI/(2*R); + const T sn=std::sin(s/2); + xs[i]=x[i]+s/w; + d+= (mu%2 ? 1 : -1)*this->p->funcgrad(xs,NULL)/(4*R*sn*sn); + } + xs[i]=x[i]; + (*grad)[i]=w*d; + } +return f; +} + + +template +T FiniteDifference::funcgrad(const NRVec &x, NRVec *grad) const +{ +T f=this->p->funcgrad(x,NULL); +if(!grad) return f; +grad->copyonwrite(); +NRVec xs(x); +xs.copyonwrite(); //working copy with shifted elements +for(int i=0; ip,xs,x,i,h); +return f; +} + + +//Bloch vector components z = cos a cos b, x = cos a sin b of the qubit RY(b) RX(a)|0> and their derivatives +template +class blochqubit { +public: +T z, x, za, zb, xa, xb; +blochqubit(const T a, const T b) + { + T ca=std::cos(a), sa=std::sin(a), cb=std::cos(b), sb=std::sin(b); + z=ca*cb; x=ca*sb; za= -sa*cb; zb= -ca*sb; xa= -sa*sb; xb=ca*cb; + } +}; + +template +T testproblem_vqe::funcgrad(const NRVec &x, NRVec *grad) const +{ +const int nq=x.size()/2; +if(2*nq!=x.size()) laerror("testproblem_vqe needs an even number of parameters"); +T r=0; +for(int i=0; i q(x[2*i],x[2*i+1]); + r+= hfield(i)*q.z + kfield(i)*q.x; + if(isize()!=x.size()) laerror("grad size mismatch"); + grad->copyonwrite(); + for(int i=0; i q(x[2*i],x[2*i+1]); + T eff=hfield(i); //coefficient of z_i including the couplings + if(i>0) eff+= J(i-1)*std::cos(x[2*i-2])*std::cos(x[2*i-1]); + if(i +void testproblem_vqe::hessian(const NRVec &x, NRSMat &h) const +{ +const int nq=x.size()/2; +h.copyonwrite(); +h=(T)0; +for(int i=0; i q(x[2*i],x[2*i+1]); + T eff=hfield(i); + if(i>0) eff+= J(i-1)*std::cos(x[2*i-2])*std::cos(x[2*i-1]); + if(i q1(x[2*i+2],x[2*i+3]); + h(2*i,2*i+2)= J(i)*q.za*q1.za; + h(2*i,2*i+3)= J(i)*q.za*q1.zb; + h(2*i+1,2*i+2)= J(i)*q.zb*q1.za; + h(2*i+1,2*i+3)= J(i)*q.zb*q1.zb; + } + } +} + +template +void testproblem_vqe::metric(const NRVec &x, NRSMat &g) const +{ +const int nq=x.size()/2; +g.copyonwrite(); +g=(T)0; +for(int i=0; i static T cubic_interpolate(const T a, const T fa, const T da, const T b, const T fb, const T db) @@ -428,6 +575,93 @@ return linesearch_minimize(names[hp.my_update],dir,o,x,hp,achieved); } +template +optresult GDIIS_Optimizer::minimize(const OptProblem *o, NRVec &x, const hyperparameters *par, hyperparameters *achieved) +{ +const char *name="GDIIS"; +GDIIS_hyperparameters hp; +if(par) hp.setfrom(*par); +if(hp.my_saddle_order) laerror("GDIIS cannot search for saddle points"); +if(hp.my_diis_dim<1) laerror("GDIIS needs diis_dim >= 1"); +const int verbose=hp.my_verbose; +optevaluator e(o,hp.my_maxfunc); +optstate st(name,hp,e); +const int n=x.size(); +QN_direction dir(hp,n); +DIIS,NRVec > diis(hp.my_diis_dim); +bool fresh=true; //first step after a (re)start, the quasi-Newton step from the initial Hessian guess has no scale +NRVec g(n); +T f=e(x,&g); + +for(;; ++st.iter) + { + if(st.stop(f,g.norm())) break; + + NRVec p = dir.direction(g); //quasi-Newton step, the error vector of DIIS + if(p*g >= 0) + { + if(verbose) std::cout < xold(x), gold(g); + T fold=f; + NRVec s; + bool accepted=false; + if(!fresh) //DIIS extrapolation, otherwise a line search is done directly + { + s = x+p; + NRVec err(p); + diis.extrapolate(s,err,verbose>1,hp.my_diis_eps); + s -= x; + if(s*g >= 0 || s.norm() > hp.my_maxsteplength) + { + if(verbose) std::cout < hp.my_maxsteplength) s *= hp.my_maxsteplength/snorm; + } + NRVec xnew = x+s; + NRVec gnew(n); + T fnew=e(xnew,&gnew); + if(fnew y = g-gold; + if(verbose>1) std::cout < class LBFGS_direction : public linesearch_direction { @@ -742,11 +976,42 @@ return st.finish(achieved); +//natural gradient (G + reg I)^-1 g from the gradient of an inner estimator and the metric G of the problem +template +class natural_gradient_estimator : public gradient_estimator { +public: +gradient_estimator &inner; +const OptMetric *m; +T reg; +natural_gradient_estimator(gradient_estimator &in, const OptMetric *mm, const T r) : inner(in), m(mm), reg(r) {}; +T operator()(optevaluator &e, const NRVec &x, NRVec &g, const int k) + { + T f=inner(e,x,g,k); + NRSMat G(x.size()); + m->metric(x,G); + if(reg!=0) G+=reg; + linear_solve(G,g); + return f; + } +int cost() const {return inner.cost();} +bool exact() const {return inner.exact();} +T calibrate(optevaluator &e, const NRVec &x, const firstorder_hyperparameters &hp) {return inner.calibrate(e,x,hp);} +}; + + template optresult firstorder_minimize(const char *name, firstorder_step &stepper, const OptProblem *o, NRVec &x, const firstorder_hyperparameters &hp, hyperparameters *achieved, gradient_estimator *est) { gradient_estimator exact; -gradient_estimator &grad = est ? *est : exact; +gradient_estimator *plain = est ? est : &exact; +const OptMetric *metric=NULL; +if(hp.my_natural) + { + metric=optcapability >(o); + if(!metric) laerror("natural gradient needs a problem providing the metric (OptMetric)"); + } +natural_gradient_estimator natural(*plain,metric,hp.my_metric_reg); +gradient_estimator &grad = hp.my_natural ? natural : *plain; optevaluator e(o,hp.my_maxfunc); optstate st(name,hp,e); const int n=x.size(); @@ -802,6 +1067,142 @@ NRVec step(const NRVec &g, const int k) }; +template +optresult QNG_Optimizer::minimize(const OptProblem *o, NRVec &x, const hyperparameters *par, hyperparameters *achieved) +{ +QNG_hyperparameters hp; +if(par) hp.setfrom(*par); +hp.my_natural=true; //also if the caller passed e.g. SGD_hyperparameters +if(hp.my_saddle_order) laerror("QNG cannot search for saddle points"); +SGD_stepper stepper(hp); +return firstorder_minimize("QNG",stepper,o,x,hp,achieved); +} + + +//trigonometric polynomial a_0 + sum_{k=1}^R (a_k cos(k w t) + b_k sin(k w t)) of the single variable t = x[0] +template +class trigpolynomial : public OptProblem { +public: +NRVec a, b; +T w; +trigpolynomial(const int R, const T ww) : a(R+1), b(R+1), w(ww) {}; +T funcgrad(const NRVec &x, NRVec *grad) const + { + if(grad) laerror("trigpolynomial provides only the function"); + T f=a[0]; + for(int k=1; k +static bool trig_minimize(optevaluator &e, NRVec &x, T &f, const int i, const int R, const T period, const bool reevaluate, const int verbose) +{ +const int N=2*R+1; +if(e.remaining() &)x)[i]; //const access, x may be shared with the evaluator +NRVec fs(N); //samples +fs[0]=f; +NRVec xs(x); +for(int j=1; j p(R,w); +p.a[0]=fs.sum()/N; +for(int k=1; k<=R; ++k) + { + p.a[k]=p.b[k]=0; + for(int j=0; j tt(1); + int jbest=0; + T fbest=0; + for(int j=0; j pe(&p); + tt[0]=jbest*period/M; + NRVec u((T)1,1); + linemin_brent(pe,tt,fbest,u,period/M,(T)1e-10,(T)1e-12*period,100); + t=tt[0]; + fmin=fbest; + } +t-= period*std::floor(t/period+0.5); //shortest step, t in (-period/2,period/2] +x.copyonwrite(); +x[i]=xi+t; +f= reevaluate ? e(x) : fmin; +if(verbose>1) std::cout <<"trigonometric minimization of parameter "< hp; +if(par) hp.setfrom(*par); +if(hp.my_saddle_order) laerror("Rotosolve cannot search for saddle points"); +const int verbose=hp.my_verbose; +const T abstol=0.01*hp.my_x_thr; //as in Powell's method +const int n=x.size(); +const OptTrigonometric *trig=optcapability >(o); +if(!trig && verbose) std::cout < e(o,hp.my_maxfunc); +optstate st(name,hp,e); +T f=e(x); +NRVec tstep(hp.my_initstep,n); //trial steps of the line minimizations + +for(;; ++st.iter) + { + if(st.stop(f)) break; + NRVec x0(x); + T f0=f; + bool budget=true; + for(int i=0; idegree(i) : 0; + if(R>0) budget=trig_minimize(e,x,f,i,R,trig->period(i),hp.my_reevaluate,verbose); + else + { + NRVec u((T)0,n); + u[i]=1; + T t=linemin_brent(e,x,f,u,tstep[i],hp.my_linmin_tol,abstol,hp.my_maxlinemin,verbose); + if(t!=0) tstep[i]=std::abs(t); + budget=!e.exhausted(); + } + } + NRVec dx = x-x0; + st.step(f0-f,dx.norm()); + if(!budget) //incomplete sweep, no convergence test + { + if(verbose) std::cout < optresult SGD_Optimizer::minimize(const OptProblem *o, NRVec &x, const hyperparameters *par, hyperparameters *achieved) { @@ -949,12 +1350,18 @@ return firstorder_minimize("SPSA",stepper,o,x,hp,achieved,&est); template double linesearch_wolfe(optevaluator &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 testproblem_vqe; +template class ParameterShift; +template class FiniteDifference; template class BFGS_Optimizer; template class LBFGS_Optimizer; +template class GDIIS_Optimizer; template class CG_Optimizer; template class NelderMead_Optimizer; template class Powell_Optimizer; +template class Rotosolve_Optimizer; template class SGD_Optimizer; +template class QNG_Optimizer; template class Adam_Optimizer; template class SPSA_Optimizer; template double linemin_brent(optevaluator &e, NRVec &x, double &f, const NRVec &u, const double t1, const double tol, const double abstol, const int maxiter, const int verbose); diff --git a/optimize.h b/optimize.h index cfd6d3f..15892bf 100644 --- a/optimize.h +++ b/optimize.h @@ -24,6 +24,7 @@ #include #include +#include #include "vec.h" #include "mat.h" #include "smat.h" @@ -41,6 +42,79 @@ virtual T funcgrad(const NRVec &x, NRVec *grad) const =0; //evaluate funct virtual ~OptProblem() {}; }; +//optional capabilities of a problem, to be multiply inherited together with OptProblem; methods find them by optcapability() +//matrices are allocated by the caller, implementations writing their elements have to call copyonwrite() first + +//Hessian of f +template +class OptHessian { +public: +virtual void hessian(const NRVec &x, NRSMat &h) const =0; //Hessian at x +virtual NRVec hessian_times(const NRVec &x, const NRVec &v) const {NRSMat h(x.size()); hessian(x,h); return h*v;} //Hessian-vector product, can be overridden for large problems +virtual ~OptHessian() {}; +}; + +//metric tensor of the parameter space: Fubini-Study metric for the quantum natural gradient (Stokes et al., Quantum 4, 269 (2020)), +//Fisher information for the natural gradient in machine learning (Amari, Neural Comput. 10, 251 (1998)) +template +class OptMetric { +public: +virtual void metric(const NRVec &x, NRSMat &g) const =0; //metric at x +virtual ~OptMetric() {}; +}; + +//f as a function of x_i alone is a trigonometric polynomial a_0 + sum_{k=1}^{R_i} (a_k cos(k w_i x_i) + b_k sin(k w_i x_i)), w_i = 2 pi/period_i; +//for parameterized quantum circuits with gates exp(-i x_i P/2), P a Pauli string, R_i = 1 and period_i = 2 pi +template +class OptTrigonometric { +public: +virtual int degree(const int i) const =0; //R_i, 0 = f is not trigonometric in x_i +virtual T period(const int i) const {return 2*M_PI;} +virtual ~OptTrigonometric() {}; +}; + + +//problem wrapping another one (e.g. providing gradients); the capabilities of the wrapped problem remain accessible by optcapability() +template +class OptProblemAdapter : public OptProblem { +public: +const OptProblem *p; //wrapped problem +OptProblemAdapter(const OptProblem *pp) : p(pp) {}; +}; + +//capability C (OptHessian, OptMetric, ...) of the problem, searched also through adapters; NULL if not available +template +const C *optcapability(const OptProblem *o) +{ +const C *c=dynamic_cast(o); +if(c) return c; +const OptProblemAdapter *a=dynamic_cast *>(o); +return a ? optcapability(a->p) : NULL; +} + + +//gradient by the general parameter-shift rule (Wierichs et al., Quantum 6, 677 (2022); degree 1: Mitarai et al., Phys. Rev. A 98, 032309 (2018), +//Schuld et al., Phys. Rev. A 99, 032331 (2019)), exact from 2R_i evaluations per parameter; parameters of degree 0 by central differences with step h +//the wrapped problem has to provide OptTrigonometric +template +class ParameterShift : public OptProblemAdapter { +public: +const OptTrigonometric *trig; +T h; +ParameterShift(const OptProblem *pp, const T hh=1e-6); +virtual T funcgrad(const NRVec &x, NRVec *grad) const; +}; + +//gradient by central differences with step h, 2 evaluations per parameter +template +class FiniteDifference : public OptProblemAdapter { +public: +T h; +FiniteDifference(const OptProblem *pp, const T hh=1e-6) : OptProblemAdapter(pp), h(hh) {}; +virtual T funcgrad(const NRVec &x, NRVec *grad) const; +}; + + //test case template class testproblem : public OptProblem { @@ -49,6 +123,22 @@ virtual T funcgrad(const NRVec &x, NRVec *grad) const; virtual ~testproblem() {}; }; +//test case with all capabilities: Ising chain with longitudinal and transverse fields in the product state of qubits RY(b_i) RX(a_i)|0>, +//x = (a_0,b_0,a_1,b_1,...), E = sum_i (h_i z_i + k_i x_i) + sum_i J_i z_i z_{i+1}, Bloch vector components z_i = cos a_i cos b_i, x_i = cos a_i sin b_i; +//trigonometric of degree 1 with period 2 pi in all parameters, Fubini-Study metric 1/4 diag(1, cos^2 a_i) for each qubit +template +class testproblem_vqe : public OptProblem, public OptHessian, public OptMetric, public OptTrigonometric { +public: +static T hfield(const int i) {return 0.7+0.1*(i%3);} +static T kfield(const int i) {return 0.4;} +static T J(const int i) {return 1;} +virtual T funcgrad(const NRVec &x, NRVec *grad) const; +virtual void hessian(const NRVec &x, NRSMat &h) const; +virtual void metric(const NRVec &x, NRSMat &g) const; +virtual int degree(const int i) const {return 1;} +virtual ~testproblem_vqe() {}; +}; + //base classes for optimization methods @@ -206,7 +296,9 @@ T my_lr_decay; //learning rate in iteration k is lr/(1+lr_decay*k)^lr_power T my_lr_power; T my_weight_decay; //decoupled weight decay x -= lr*weight_decay*x (Loshchilov & Hutter, ICLR 2019) bool my_return_best; //return the point with the lowest function value found instead of the last one; only for (nearly) deterministic f, the lowest of noisy values is biased by the noise -firstorder_hyperparameters() : my_lr(1e-3), my_lr_decay(0), my_lr_power(1), my_weight_decay(0), my_return_best(false) {this->my_maxstep=1000;}; +bool my_natural; //natural gradient (G + metric_reg I)^-1 g with the metric G of the problem (OptMetric) (Amari, Neural Comput. 10, 251 (1998); Stokes et al., Quantum 4, 269 (2020)); |g| refers then to it +T my_metric_reg; //regularization added to the diagonal of the metric +firstorder_hyperparameters() : my_lr(1e-3), my_lr_decay(0), my_lr_power(1), my_weight_decay(0), my_return_best(false), my_natural(false), my_metric_reg(1e-3) {this->my_maxstep=1000;}; virtual void setfrom(const hyperparameters &p) {const firstorder_hyperparameters *q=dynamic_cast(&p); if(q) *this= *q; else gradient_hyperparameters::setfrom(p);} }; @@ -284,6 +376,27 @@ virtual ~LBFGS_Optimizer() {}; }; +//geometry DIIS (Csaszar & Pulay, J. Mol. Struct. 114, 31 (1984)) with a quasi-Newton Hessian (Farkas & Schlegel, Phys. Chem. Chem. Phys. 4, 11 (2002)) +//error vectors are the quasi-Newton steps e_i = -H g_i, the new point is sum_i c_i (x_i+e_i) minimizing |sum_i c_i e_i| with sum_i c_i = 1 +//one function evaluation per iteration; if the DIIS step goes uphill, is longer than maxsteplength or does not decrease f, +//the DIIS history is restarted and a line search along the quasi-Newton step is done +template +class GDIIS_hyperparameters : public BFGS_hyperparameters { +public: +int my_diis_dim; //no. of stored points +T my_diis_eps; //regularization added to the diagonal of the DIIS matrix +GDIIS_hyperparameters() : my_diis_dim(5), my_diis_eps(0) {}; +virtual void setfrom(const hyperparameters &p) {const GDIIS_hyperparameters *q=dynamic_cast(&p); if(q) *this= *q; else BFGS_hyperparameters::setfrom(p);} +}; + +template +class GDIIS_Optimizer : public Optimizer { +public: +virtual optresult minimize(const OptProblem *o, NRVec &x, const hyperparameters *par=NULL, hyperparameters *achieved=NULL); +virtual ~GDIIS_Optimizer() {}; +}; + + //nonlinear conjugate gradient methods (Nocedal & Wright chap. 5.2; Hager & Zhang, SIAM J. Optim. 16, 170 (2005)) //differing by the formula for beta in p_{k+1} = -g_{k+1} + beta p_k; PR and HS are used in the nonnegative variants PR+ and HS+ enum CG_beta {CG_FR, CG_PR, CG_HS, CG_DY, CG_HZ}; @@ -349,6 +462,26 @@ virtual ~Powell_Optimizer() {}; }; +//Rotosolve: sweeps of exact minimizations along single parameters in which f is trigonometric (OptTrigonometric) +//(Ostaszewski et al., Quantum 5, 391 (2021); Nakanishi et al., Phys. Rev. Research 2, 043158 (2020); degree > 1: Wierichs et al., Quantum 6, 677 (2022)); +//f along x_i is reconstructed from 2R_i+1 equidistant samples over its period (2R_i new evaluations) and minimized without further evaluations; +//parameters of degree 0, or all if the problem is not trigonometric, are minimized by line minimization as in Powell's method (whose parameters are used) +template +class Rotosolve_hyperparameters : public Powell_hyperparameters { +public: +bool my_reevaluate; //evaluate f at the new point instead of taking the value of the reconstruction (for noisy f or doubtful degrees) +Rotosolve_hyperparameters() : my_reevaluate(false) {}; +virtual void setfrom(const hyperparameters &p) {const Rotosolve_hyperparameters *q=dynamic_cast(&p); if(q) *this= *q; else Powell_hyperparameters::setfrom(p);} +}; + +template +class Rotosolve_Optimizer : public Optimizer { +public: +virtual optresult minimize(const OptProblem *o, NRVec &x, const hyperparameters *par=NULL, hyperparameters *achieved=NULL); +virtual ~Rotosolve_Optimizer() {}; +}; + + //stochastic gradient descent with momentum v = momentum*v + g, step d = v or d = g + momentum*v (Nesterov) (Sutskever et al., ICML 2013) template class SGD_hyperparameters : public firstorder_hyperparameters { @@ -367,6 +500,24 @@ virtual ~SGD_Optimizer() {}; }; +//quantum natural gradient x -= lr (G + metric_reg I)^-1 g (Stokes et al., Quantum 4, 269 (2020)): SGD without momentum with the natural gradient; +//the problem has to provide the metric (OptMetric), the gradient can come e.g. from ParameterShift; momentum, Adam or SPSA can be combined with the natural gradient by setting my_natural +//natural steps are longer than gradient steps with the same lr (metric eigenvalues ~1/4 or smaller): lr up to ~0.1, or a larger metric_reg +template +class QNG_hyperparameters : public SGD_hyperparameters { +public: +QNG_hyperparameters() {this->my_momentum=0; this->my_natural=true; this->my_lr=0.05;}; +virtual void setfrom(const hyperparameters &p) {const QNG_hyperparameters *q=dynamic_cast(&p); if(q) *this= *q; else SGD_hyperparameters::setfrom(p);} +}; + +template +class QNG_Optimizer : public Optimizer { +public: +virtual optresult minimize(const OptProblem *o, NRVec &x, const hyperparameters *par=NULL, hyperparameters *achieved=NULL); +virtual ~QNG_Optimizer() {}; +}; + + //Adam (Kingma & Ba, ICLR 2015), with weight_decay>0 AdamW, optionally AMSGrad (Reddi et al., ICLR 2018); beta1=0 gives RMSprop template class Adam_hyperparameters : public firstorder_hyperparameters { diff --git a/t.cc b/t.cc index e5585f0..fe62ec2 100644 --- a/t.cc +++ b/t.cc @@ -4830,7 +4830,7 @@ cout <<"adjoint error = "<<(a*adj).norm(abs(det))< problem; //Powell_hyperparameters param; //tested //SGD_hyperparameters param; //tested //Adam_hyperparameters param; //tested -SPSA_hyperparameters param; +//SPSA_hyperparameters param; //tested +GDIIS_hyperparameters param; param.my_maxstep=100; param.my_f_thr=1e-6; param.my_x_thr=1e-3; param.my_g_thr=1e-3; //gradient methods -param.my_lr=1e-2; //first-order methods +//param.my_lr=1e-2; //first-order methods param.my_maxstep=2000; //first-order methods //param.my_update=QN_BFGS; //tested //param.my_update=QN_DFP; //tested @@ -4868,7 +4869,8 @@ cout << "initial " < o; //tested //SGD_Optimizer o; //tested //Adam_Optimizer o; //tested -SPSA_Optimizer o; +//SPSA_Optimizer o; //tested +GDIIS_Optimizer o; gradient_hyperparameters achieved; //gradient methods //hyperparameters achieved; //derivative-free methods optresult res=o.minimize(&problem,x,¶m,&achieved); @@ -4879,4 +4881,38 @@ double r=problem.funcgrad(x,NULL); cout <>n; +NRVec x(n); +x.randomize(1.); +testproblem_vqe problem; //needs an even dimension +ParameterShift psproblem(&problem); //gradient by the parameter-shift rule from function values only +//QNG_hyperparameters param; //tested +Rotosolve_hyperparameters param; +param.my_maxstep=2000; +param.my_f_thr=1e-6; +param.my_x_thr=1e-3; +//param.my_g_thr=1e-3; //gradient methods +cout << "initial " < o; //tested +Rotosolve_Optimizer o; +//gradient_hyperparameters achieved; //gradient methods +hyperparameters achieved; //derivative-free methods +optresult res=o.minimize(&problem,x,¶m,&achieved); +//optresult res=o.minimize(&psproblem,x,¶m,&achieved); //gradient methods with parameter-shift gradients +cout << "optimized " <