1 //////////////////////////////////////////////////////////////////////////////////////
2 // This file is distributed under the University of Illinois/NCSA Open Source License.
3 // See LICENSE file in top directory for details.
4 //
5 // Copyright (c) 2020 QMCPACK developers.
6 //
7 // File developed by: Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
8 //                    Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
9 //                    Jaron T. Krogel, krogeljt@ornl.gov, Oak Ridge National Laboratory
10 //                    Miguel Morales, moralessilva2@llnl.gov, Lawrence Livermore National Laboratory
11 //                    Ye Luo, yeluo@anl.gov, Argonne National Laboratory
12 //                    Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
13 //                    Mark Dewing, mdewing@anl.gov, Argonne National Laboratory
14 //
15 // File created by: Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
16 //////////////////////////////////////////////////////////////////////////////////////
17 
18 
19 #include "QMCFixedSampleLinearOptimizeBatched.h"
20 #include "Particle/HDFWalkerIO.h"
21 #include "OhmmsData/AttributeSet.h"
22 #include "Message/CommOperators.h"
23 #include "QMCDrivers/WFOpt/QMCCostFunctionBase.h"
24 #include "QMCDrivers/WFOpt/QMCCostFunctionBatched.h"
25 #include "QMCDrivers/VMC/VMCBatched.h"
26 #include "QMCDrivers/WFOpt/QMCCostFunction.h"
27 #include "QMCHamiltonians/HamiltonianPool.h"
28 #include "Concurrency/Info.hpp"
29 #include "CPU/Blasf.h"
30 #include "Numerics/MatrixOperators.h"
31 #include <cassert>
32 #ifdef HAVE_LMY_ENGINE
33 #include "formic/utils/matrix.h"
34 #include "formic/utils/random.h"
35 #include "formic/utils/lmyengine/var_dependencies.h"
36 #endif
37 #include <iostream>
38 #include <fstream>
39 #include <stdexcept>
40 
41 
42 namespace qmcplusplus
43 {
44 using MatrixOperators::product;
45 
46 
QMCFixedSampleLinearOptimizeBatched(const ProjectData & project_data,MCWalkerConfiguration & w,QMCDriverInput && qmcdriver_input,VMCDriverInput && vmcdriver_input,MCPopulation && population,SampleStack & samples,Communicate * comm)47 QMCFixedSampleLinearOptimizeBatched::QMCFixedSampleLinearOptimizeBatched(const ProjectData& project_data,
48                                                                          MCWalkerConfiguration& w,
49                                                                          QMCDriverInput&& qmcdriver_input,
50                                                                          VMCDriverInput&& vmcdriver_input,
51                                                                          MCPopulation&& population,
52                                                                          SampleStack& samples,
53                                                                          Communicate* comm)
54     : QMCLinearOptimizeBatched(project_data,
55                                w,
56                                std::move(qmcdriver_input),
57                                std::move(vmcdriver_input),
58                                std::move(population),
59                                samples,
60                                comm,
61                                "QMCFixedSampleLinearOptimizeBatched"),
62 #ifdef HAVE_LMY_ENGINE
63       vdeps(1, std::vector<double>()),
64 #endif
65       Max_iterations(1),
66       nstabilizers(3),
67       stabilizerScale(2.0),
68       bigChange(50),
69       exp0(-16),
70       stepsize(0.25),
71       GEVtype("mixed"),
72       StabilizerMethod("best"),
73       GEVSplit("no"),
74       bestShift_i(-1.0),
75       bestShift_s(-1.0),
76       shift_i_input(0.01),
77       shift_s_input(1.00),
78       accept_history(3),
79       shift_s_base(4.0),
80       num_shifts(3),
81       max_relative_cost_change(10.0),
82       max_param_change(0.3),
83       cost_increase_tol(0.0),
84       target_shift_i(-1.0),
85       targetExcitedStr("no"),
86       targetExcited(false),
87       block_lmStr("no"),
88       block_lm(false),
89       nblocks(1),
90       nolds(1),
91       nkept(1),
92       nsamp_comp(0),
93       omega_shift(0.0),
94       block_first(true),
95       block_second(false),
96       block_third(false),
97       crowd_size_(1),
98       opt_num_crowds_(0),
99       MinMethod("OneShiftOnly"),
100       previous_optimizer_type_(OptimizerType::NONE),
101       current_optimizer_type_(OptimizerType::NONE),
102       do_output_matrices_(false),
103       output_matrices_initialized_(false),
104       freeze_parameters_(false)
105 
106 {
107   //set the optimization flag
108   qmc_driver_mode_.set(QMC_OPTIMIZE, 1);
109   //read to use vmc output (just in case)
110   m_param.add(Max_iterations, "max_its");
111   m_param.add(nstabilizers, "nstabilizers");
112   m_param.add(stabilizerScale, "stabilizerscale");
113   m_param.add(bigChange, "bigchange");
114   m_param.add(MinMethod, "MinMethod");
115   m_param.add(exp0, "exp0");
116   m_param.add(targetExcitedStr, "targetExcited");
117   m_param.add(block_lmStr, "block_lm");
118   m_param.add(nblocks, "nblocks");
119   m_param.add(nolds, "nolds");
120   m_param.add(nkept, "nkept");
121   m_param.add(nsamp_comp, "nsamp_comp");
122   m_param.add(omega_shift, "omega");
123   m_param.add(max_relative_cost_change, "max_relative_cost_change");
124   m_param.add(max_param_change, "max_param_change");
125   m_param.add(shift_i_input, "shift_i");
126   m_param.add(shift_s_input, "shift_s");
127   m_param.add(num_shifts, "num_shifts");
128   m_param.add(cost_increase_tol, "cost_increase_tol");
129   m_param.add(target_shift_i, "target_shift_i");
130   m_param.add(crowd_size_, "opt_crowd_size");
131   m_param.add(opt_num_crowds_, "opt_num_crowds");
132 
133 
134 #ifdef HAVE_LMY_ENGINE
135   //app_log() << "construct QMCFixedSampleLinearOptimizeBatched" << endl;
136   std::vector<double> shift_scales(3, 1.0);
137   EngineObj = new cqmc::engine::LMYEngine<ValueType>(&vdeps,
138                                                      false, // exact sampling
139                                                      true,  // ground state?
140                                                      false, // variance correct,
141                                                      true,
142                                                      true,  // print matrices,
143                                                      true,  // build matrices
144                                                      false, // spam
145                                                      false, // use var deps?
146                                                      true,  // chase lowest
147                                                      false, // chase closest
148                                                      false, // eom
149                                                      false,
150                                                      false,  // eom related
151                                                      false,  // eom related
152                                                      false,  // use block?
153                                                      120000, // number of samples
154                                                      0,      // number of parameters
155                                                      60,     // max krylov iter
156                                                      0,      // max spam inner iter
157                                                      1,      // spam appro degree
158                                                      0,      // eom related
159                                                      0,      // eom related
160                                                      0,      // eom related
161                                                      0.0,    // omega
162                                                      0.0,    // var weight
163                                                      1.0e-6, // convergence threshold
164                                                      0.99,   // minimum S singular val
165                                                      0.0, 0.0,
166                                                      10.0, // max change allowed
167                                                      1.00, // identity shift
168                                                      1.00, // overlap shift
169                                                      0.3,  // max parameter change
170                                                      shift_scales, app_log());
171 #endif
172 
173 
174   //   stale parameters
175   //   m_param.add(eigCG,"eigcg");
176   //   m_param.add(TotalCGSteps,"cgsteps");
177   //   m_param.add(w_beta,"beta");
178   //   quadstep=-1.0;
179   //   m_param.add(quadstep,"quadstep");
180   //   m_param.add(stepsize,"stepsize");
181   //   m_param.add(exp1,"exp1");
182   //   m_param.add(GEVtype,"GEVMethod");
183   //   m_param.add(GEVSplit,"GEVSplit");
184   //   m_param.add(StabilizerMethod,"StabilizerMethod");
185   //   m_param.add(LambdaMax,"LambdaMax");
186   //Set parameters for line minimization:
187 }
188 
189 /** Clean up the vector */
~QMCFixedSampleLinearOptimizeBatched()190 QMCFixedSampleLinearOptimizeBatched::~QMCFixedSampleLinearOptimizeBatched()
191 {
192 #ifdef HAVE_LMY_ENGINE
193   delete EngineObj;
194 #endif
195 }
196 
Func(RealType dl)197 QMCFixedSampleLinearOptimizeBatched::RealType QMCFixedSampleLinearOptimizeBatched::Func(RealType dl)
198 {
199   for (int i = 0; i < optparm.size(); i++)
200     optTarget->Params(i) = optparm[i] + dl * optdir[i];
201   QMCLinearOptimizeBatched::RealType c = optTarget->Cost(false);
202   //only allow this to go false if it was true. If false, stay false
203   //    if (validFuncVal)
204   validFuncVal = optTarget->IsValid;
205   return c;
206 }
207 
run()208 bool QMCFixedSampleLinearOptimizeBatched::run()
209 {
210   if (do_output_matrices_ && !output_matrices_initialized_)
211   {
212     numParams = optTarget->getNumParams();
213     int N     = numParams + 1;
214     output_overlap_.init_file(get_root_name(), "ovl", N);
215     output_hamiltonian_.init_file(get_root_name(), "ham", N);
216     output_matrices_initialized_ = true;
217   }
218 
219 #ifdef HAVE_LMY_ENGINE
220   if (doHybrid)
221   {
222     app_log() << "Doing hybrid run" << std::endl;
223     return hybrid_run();
224   }
225 
226   // if requested, perform the update via the adaptive three-shift or single-shift method
227   if (current_optimizer_type_ == OptimizerType::ADAPTIVE)
228     return adaptive_three_shift_run();
229 
230   if (current_optimizer_type_ == OptimizerType::DESCENT)
231     return descent_run();
232 
233 #endif
234 
235   if (current_optimizer_type_ == OptimizerType::ONESHIFTONLY)
236     return one_shift_run();
237 
238   start();
239   bool Valid(true);
240   int Total_iterations(0);
241   //size of matrix
242   numParams = optTarget->getNumParams();
243   N         = numParams + 1;
244   //   where we are and where we are pointing
245   std::vector<RealType> currentParameterDirections(N, 0);
246   std::vector<RealType> currentParameters(numParams, 0);
247   std::vector<RealType> bestParameters(numParams, 0);
248   for (int i = 0; i < numParams; i++)
249     bestParameters[i] = currentParameters[i] = std::real(optTarget->Params(i));
250   //   proposed direction and new parameters
251   optdir.resize(numParams, 0);
252   optparm.resize(numParams, 0);
253 
254   while (Total_iterations < Max_iterations)
255   {
256     Total_iterations += 1;
257     app_log() << "Iteration: " << Total_iterations << "/" << Max_iterations << std::endl;
258     if (!ValidCostFunction(Valid))
259       continue;
260     //this is the small amount added to the diagonal to stabilize the eigenvalue equation. 10^stabilityBase
261     RealType stabilityBase(exp0);
262     //     reset params if necessary
263     for (int i = 0; i < numParams; i++)
264       optTarget->Params(i) = currentParameters[i];
265     cost_function_timer_.start();
266     RealType lastCost(optTarget->Cost(true));
267     cost_function_timer_.stop();
268     //     if cost function is currently invalid continue
269     Valid = optTarget->IsValid;
270     if (!ValidCostFunction(Valid))
271       continue;
272     RealType newCost(lastCost);
273     RealType startCost(lastCost);
274     Matrix<RealType> Left(N, N);
275     Matrix<RealType> Right(N, N);
276     Matrix<RealType> S(N, N);
277     //     stick in wrong matrix to reduce the number of matrices we need by 1.( Left is actually stored in Right, & vice-versa)
278     optTarget->fillOverlapHamiltonianMatrices(Right, Left);
279     S.copy(Left);
280     bool apply_inverse(true);
281     if (apply_inverse)
282     {
283       Matrix<RealType> RightT(Left);
284       invert_matrix(RightT, false);
285       Left = 0;
286       product(RightT, Right, Left);
287       //       Now the left matrix is the Hamiltonian with the inverse of the overlap applied ot it.
288     }
289     //Find largest off-diagonal element compared to diagonal element.
290     //This gives us an idea how well conditioned it is, used to stabilize.
291     RealType od_largest(0);
292     for (int i = 0; i < N; i++)
293       for (int j = 0; j < N; j++)
294         od_largest = std::max(std::max(od_largest, std::abs(Left(i, j)) - std::abs(Left(i, i))),
295                               std::abs(Left(i, j)) - std::abs(Left(j, j)));
296     app_log() << "od_largest " << od_largest << std::endl;
297     //if(od_largest>0)
298     //  od_largest = std::log(od_largest);
299     //else
300     //  od_largest = -1e16;
301     //if (od_largest<stabilityBase)
302     //  stabilityBase=od_largest;
303     //else
304     //  stabilizerScale = std::max( 0.2*(od_largest-stabilityBase)/nstabilizers, stabilizerScale);
305     app_log() << "  stabilityBase " << stabilityBase << std::endl;
306     app_log() << "  stabilizerScale " << stabilizerScale << std::endl;
307     int failedTries(0);
308     bool acceptedOneMove(false);
309     for (int stability = 0; stability < nstabilizers; stability++)
310     {
311       bool goodStep(true);
312       //       store the Hamiltonian matrix in Right
313       for (int i = 0; i < N; i++)
314         for (int j = 0; j < N; j++)
315           Right(i, j) = Left(j, i);
316       RealType XS(stabilityBase + stabilizerScale * (failedTries + stability));
317       for (int i = 1; i < N; i++)
318         Right(i, i) += std::exp(XS);
319       app_log() << "  Using XS:" << XS << " " << failedTries << " " << stability << std::endl;
320       RealType lowestEV(0);
321       eigenvalue_timer_.start();
322       lowestEV = getLowestEigenvector(Right, currentParameterDirections);
323       Lambda   = getNonLinearRescale(currentParameterDirections, S);
324       eigenvalue_timer_.stop();
325       //       biggest gradient in the parameter direction vector
326       RealType bigVec(0);
327       for (int i = 0; i < numParams; i++)
328         bigVec = std::max(bigVec, std::abs(currentParameterDirections[i + 1]));
329       //       this can be overwritten during the line minimization
330       RealType evaluated_cost(startCost);
331       if (MinMethod == "rescale")
332       {
333         if (std::abs(Lambda * bigVec) > bigChange)
334         {
335           goodStep = false;
336           app_log() << "  Failed Step. Magnitude of largest parameter change: " << std::abs(Lambda * bigVec)
337                     << std::endl;
338           if (stability == 0)
339           {
340             failedTries++;
341             stability--;
342           }
343           else
344             stability = nstabilizers;
345         }
346         for (int i = 0; i < numParams; i++)
347           optTarget->Params(i) = currentParameters[i] + Lambda * currentParameterDirections[i + 1];
348         optTarget->IsValid = true;
349       }
350       else
351       {
352         for (int i = 0; i < numParams; i++)
353           optparm[i] = currentParameters[i];
354         for (int i = 0; i < numParams; i++)
355           optdir[i] = currentParameterDirections[i + 1];
356         TOL              = param_tol / bigVec;
357         AbsFuncTol       = true;
358         largeQuarticStep = bigChange / bigVec;
359         LambdaMax        = 0.5 * Lambda;
360         line_min_timer_.start();
361         if (MinMethod == "quartic")
362         {
363           int npts(7);
364           quadstep         = stepsize * Lambda;
365           largeQuarticStep = bigChange / bigVec;
366           Valid            = lineoptimization3(npts, evaluated_cost);
367         }
368         else
369           Valid = lineoptimization2();
370         line_min_timer_.stop();
371         RealType biggestParameterChange = bigVec * std::abs(Lambda);
372         if (biggestParameterChange > bigChange)
373         {
374           goodStep = false;
375           failedTries++;
376           app_log() << "  Failed Step. Largest LM parameter change:" << biggestParameterChange << std::endl;
377           if (stability == 0)
378             stability--;
379           else
380             stability = nstabilizers;
381         }
382         else
383         {
384           for (int i = 0; i < numParams; i++)
385             optTarget->Params(i) = optparm[i] + Lambda * optdir[i];
386           app_log() << "  Good Step. Largest LM parameter change:" << biggestParameterChange << std::endl;
387         }
388       }
389 
390       if (goodStep)
391       {
392         // 	this may have been evaluated already
393         // 	newCost=evaluated_cost;
394         //get cost at new minimum
395         newCost = optTarget->Cost(false);
396         app_log() << " OldCost: " << lastCost << " NewCost: " << newCost << " Delta Cost:" << (newCost - lastCost)
397                   << std::endl;
398         optTarget->printEstimates();
399         //                 quit if newcost is greater than lastcost. E(Xs) looks quadratic (between steepest descent and parabolic)
400         // mmorales
401         Valid = optTarget->IsValid;
402         //if (MinMethod!="rescale" && !ValidCostFunction(Valid))
403         if (!ValidCostFunction(Valid))
404         {
405           goodStep = false;
406           app_log() << "  Good Step, but cost function invalid" << std::endl;
407           failedTries++;
408           if (stability > 0)
409             stability = nstabilizers;
410           else
411             stability--;
412         }
413         if (newCost < lastCost && goodStep)
414         {
415           //Move was acceptable
416           for (int i = 0; i < numParams; i++)
417             bestParameters[i] = std::real(optTarget->Params(i));
418           lastCost        = newCost;
419           acceptedOneMove = true;
420           if (std::abs(newCost - lastCost) < 1e-4)
421           {
422             failedTries++;
423             stability = nstabilizers;
424             continue;
425           }
426         }
427         else if (stability > 0)
428         {
429           failedTries++;
430           stability = nstabilizers;
431           continue;
432         }
433       }
434       app_log().flush();
435       if (failedTries > 20)
436         break;
437       //APP_ABORT("QMCFixedSampleLinearOptimizeBatched::run TOO MANY FAILURES");
438     }
439 
440     if (acceptedOneMove)
441     {
442       app_log() << "Setting new Parameters" << std::endl;
443       for (int i = 0; i < numParams; i++)
444         optTarget->Params(i) = bestParameters[i];
445     }
446     else
447     {
448       app_log() << "Reverting to old Parameters" << std::endl;
449       for (int i = 0; i < numParams; i++)
450         optTarget->Params(i) = currentParameters[i];
451     }
452     app_log().flush();
453   }
454 
455   finish();
456   return (optTarget->getReportCounter() > 0);
457 }
458 
459 /** Parses the xml input file for parameter definitions for the wavefunction
460 * optimization.
461 * @param q current xmlNode
462 * @return true if successful
463 */
process(xmlNodePtr q)464 void QMCFixedSampleLinearOptimizeBatched::process(xmlNodePtr q)
465 {
466   std::string useGPU("yes");
467   std::string vmcMove("pbyp");
468   std::string ReportToH5("no");
469   std::string OutputMatrices("no");
470   std::string FreezeParameters("no");
471   OhmmsAttributeSet oAttrib;
472   oAttrib.add(useGPU, "gpu");
473   oAttrib.add(vmcMove, "move");
474   oAttrib.add(ReportToH5, "hdf5");
475 
476   m_param.add(OutputMatrices, "output_matrices");
477   m_param.add(FreezeParameters, "freeze_parameters");
478 
479   oAttrib.put(q);
480   m_param.put(q);
481 
482   do_output_matrices_ = (OutputMatrices != "no");
483   freeze_parameters_  = (FreezeParameters != "no");
484 
485   // Use freeze_parameters with output_matrices to generate multiple lines in the output with
486   // the same parameters so statistics can be computed in post-processing.
487 
488   if (freeze_parameters_)
489   {
490     app_log() << std::endl;
491     app_warning() << "  The option 'freeze_parameters' is enabled.  Variational parameters will not be updated.  This "
492                      "run will not perform variational parameter optimization!"
493                   << std::endl;
494     app_log() << std::endl;
495   }
496 
497 
498   doHybrid = false;
499 
500 
501   if (MinMethod == "hybrid")
502   {
503     doHybrid = true;
504     if (!hybridEngineObj)
505       hybridEngineObj = std::make_unique<HybridEngine>(myComm, q);
506 
507     processOptXML(hybridEngineObj->getSelectedXML(), vmcMove, ReportToH5 == "yes", useGPU == "yes");
508   }
509   else
510     processOptXML(q, vmcMove, ReportToH5 == "yes", useGPU == "yes");
511 
512   // This code is also called when setting up vmcEngine.  Would be nice to not duplicate the call.
513   QMCDriverNew::AdjustedWalkerCounts awc =
514       adjustGlobalWalkerCount(myComm->size(), myComm->rank(), qmcdriver_input_.get_total_walkers(),
515                               qmcdriver_input_.get_walkers_per_rank(), 1.0, qmcdriver_input_.get_num_crowds());
516   QMCDriverNew::startup(q, awc);
517 }
518 
processOptXML(xmlNodePtr opt_xml,const std::string & vmcMove,bool reportH5,bool useGPU)519 bool QMCFixedSampleLinearOptimizeBatched::processOptXML(xmlNodePtr opt_xml,
520                                                         const std::string& vmcMove,
521                                                         bool reportH5,
522                                                         bool useGPU)
523 {
524   m_param.put(opt_xml);
525   tolower(targetExcitedStr);
526   targetExcited = (targetExcitedStr == "yes");
527 
528   tolower(block_lmStr);
529   block_lm = (block_lmStr == "yes");
530 
531   auto iter = OptimizerNames.find(MinMethod);
532   if (iter == OptimizerNames.end())
533     throw std::runtime_error("Unknown MinMethod!\n");
534   previous_optimizer_type_ = current_optimizer_type_;
535   current_optimizer_type_  = OptimizerNames.at(MinMethod);
536 
537   if (current_optimizer_type_ == OptimizerType::DESCENT && !descentEngineObj)
538     descentEngineObj = std::make_unique<DescentEngine>(myComm, opt_xml);
539 
540   // sanity check
541   if (targetExcited && current_optimizer_type_ != OptimizerType::ADAPTIVE)
542     APP_ABORT("targetExcited = \"yes\" requires that MinMethod = \"adaptive");
543 
544 #ifdef ENABLE_OPENMP
545   if (current_optimizer_type_ == OptimizerType::ADAPTIVE && (omp_get_max_threads() > 1))
546   {
547     // throw std::runtime_error("OpenMP threading not enabled with AdaptiveThreeShift optimizer. Use MPI for parallelism instead, and set OMP_NUM_THREADS to 1.");
548     app_log() << "test version of OpenMP threading with AdaptiveThreeShift optimizer" << std::endl;
549   }
550 #endif
551 
552   // check parameter change sanity
553   if (max_param_change <= 0.0)
554     throw std::runtime_error("max_param_change must be positive in QMCFixedSampleLinearOptimizeBatched::put");
555 
556   // check cost change sanity
557   if (max_relative_cost_change <= 0.0)
558     throw std::runtime_error("max_relative_cost_change must be positive in QMCFixedSampleLinearOptimizeBatched::put");
559 
560   // check shift sanity
561   if (shift_i_input <= 0.0)
562     throw std::runtime_error("shift_i must be positive in QMCFixedSampleLinearOptimizeBatched::put");
563   if (shift_s_input <= 0.0)
564     throw std::runtime_error("shift_s must be positive in QMCFixedSampleLinearOptimizeBatched::put");
565 
566   // check cost increase tolerance sanity
567   if (cost_increase_tol < 0.0)
568     throw std::runtime_error("cost_increase_tol must be non-negative in QMCFixedSampleLinearOptimizeBatched::put");
569 
570   // if this is the first time this function has been called, set the initial shifts
571   if (bestShift_i < 0.0 && (current_optimizer_type_ == OptimizerType::ADAPTIVE || doHybrid))
572     bestShift_i = shift_i_input;
573   if (current_optimizer_type_ == OptimizerType::ONESHIFTONLY)
574     bestShift_i = shift_i_input;
575   if (bestShift_s < 0.0)
576     bestShift_s = shift_s_input;
577 
578   xmlNodePtr qsave = opt_xml;
579   xmlNodePtr cur   = qsave->children;
580   int pid          = OHMMS::Controller->rank();
581   while (cur != NULL)
582   {
583     std::string cname((const char*)(cur->name));
584     if (cname == "mcwalkerset")
585     {
586       mcwalkerNodePtr.push_back(cur);
587     }
588     cur = cur->next;
589   }
590 
591   // Destroy old object to stop timer to correctly order timer with object lifetime scope
592   vmcEngine.reset(nullptr);
593   // create VMC engine
594   // if (vmcEngine == 0)
595   // {
596   QMCDriverInput qmcdriver_input_copy = qmcdriver_input_;
597   VMCDriverInput vmcdriver_input_copy = vmcdriver_input_;
598   vmcEngine =
599       std::make_unique<VMCBatched>(project_data_, std::move(qmcdriver_input_copy), std::move(vmcdriver_input_copy),
600                                    MCPopulation(myComm->size(), myComm->rank(), population_.getWalkerConfigsRef(),
601                                                 population_.get_golden_electrons(), &population_.get_golden_twf(),
602                                                 &population_.get_golden_hamiltonian()),
603                                    samples_, myComm);
604 
605   vmcEngine->setUpdateMode(vmcMove[0] == 'p');
606 
607 
608   bool AppendRun = false;
609   vmcEngine->setStatus(get_root_name(), h5_file_root_, AppendRun);
610   vmcEngine->process(qsave);
611 
612   vmcEngine->enable_sample_collection();
613 
614   // Code to check and set crowds take from QMCDriverNew::adjustGlobalWalkerCount
615   checkNumCrowdsLTNumThreads(opt_num_crowds_);
616   if (opt_num_crowds_ == 0)
617     opt_num_crowds_ = Concurrency::maxCapacity<>();
618 
619   app_log() << " Number of crowds for optimizer: " << opt_num_crowds_ << std::endl;
620 
621   bool success = true;
622   //allways reset optTarget
623   optTarget =
624       std::make_unique<QMCCostFunctionBatched>(W, population_.get_golden_twf(), population_.get_golden_hamiltonian(),
625                                                samples_, opt_num_crowds_, crowd_size_, myComm);
626   optTarget->setStream(&app_log());
627   if (reportH5)
628     optTarget->reportH5 = true;
629   success = optTarget->put(qsave);
630 
631   return success;
632 }
633 
634 ///////////////////////////////////////////////////////////////////////////////////////////////////
635 /// \brief  returns a vector of three shift values centered around the provided shift.
636 ///
637 /// \param[in]      central_shift  the central shift
638 ///
639 ///////////////////////////////////////////////////////////////////////////////////////////////////
prepare_shifts(const double central_shift) const640 std::vector<double> QMCFixedSampleLinearOptimizeBatched::prepare_shifts(const double central_shift) const
641 {
642   std::vector<double> retval(num_shifts);
643 
644   // check to see whether the number of shifts is odd
645   if (num_shifts % 2 == 0)
646     throw std::runtime_error("number of shifts must be odd in QMCFixedSampleLinearOptimizeBatched::prepare_shifts");
647 
648   // decide the central shift index
649   int central_index = num_shifts / 2;
650 
651   for (int i = 0; i < num_shifts; i++)
652   {
653     if (i < central_index)
654       retval.at(i) = central_shift / (4.0 * (central_index - i));
655     else if (i > central_index)
656       retval.at(i) = central_shift * (4.0 * (i - central_index));
657     else if (i == central_index)
658       retval.at(i) = central_shift;
659     //retval.at(i) = central_shift
660     //retval.at(0) = central_shift * 4.0;
661     //retval.at(1) = central_shift;
662     //retval.at(2) = central_shift / 4.0;
663   }
664   return retval;
665 }
666 
667 ///////////////////////////////////////////////////////////////////////////////////////////////////
668 /// \brief  prints a header for the summary of each shift's result
669 ///
670 ///////////////////////////////////////////////////////////////////////////////////////////////////
print_cost_summary_header()671 void QMCFixedSampleLinearOptimizeBatched::print_cost_summary_header()
672 {
673   app_log() << "   " << std::right << std::setw(12) << "shift_i";
674   app_log() << "   " << std::right << std::setw(12) << "shift_s";
675   app_log() << "   " << std::right << std::setw(20) << "max param change";
676   app_log() << "   " << std::right << std::setw(20) << "cost function value";
677   app_log() << std::endl;
678   app_log() << "   " << std::right << std::setw(12) << "------------";
679   app_log() << "   " << std::right << std::setw(12) << "------------";
680   app_log() << "   " << std::right << std::setw(20) << "--------------------";
681   app_log() << "   " << std::right << std::setw(20) << "--------------------";
682   app_log() << std::endl;
683 }
684 
685 ///////////////////////////////////////////////////////////////////////////////////////////////////
686 /// \brief  prints a summary of the computed cost for the given shift
687 ///
688 /// \param[in]      si             the identity shift
689 /// \param[in]      ss             the overlap shift
690 /// \param[in]      mc             the maximum parameter change
691 /// \param[in]      cv             the cost function value
692 /// \param[in]      ind            the shift index: -1 (for initial state), 0, 1, or 2
693 /// \param[in]      bi             index of the best shift
694 /// \param[in]      gu             flag telling whether it was a good update
695 ///
696 ///////////////////////////////////////////////////////////////////////////////////////////////////
print_cost_summary(const double si,const double ss,const RealType mc,const RealType cv,const int ind,const int bi,const bool gu)697 void QMCFixedSampleLinearOptimizeBatched::print_cost_summary(const double si,
698                                                              const double ss,
699                                                              const RealType mc,
700                                                              const RealType cv,
701                                                              const int ind,
702                                                              const int bi,
703                                                              const bool gu)
704 {
705   if (ind >= 0)
706   {
707     if (gu)
708     {
709       app_log() << "   " << std::scientific << std::right << std::setw(12) << std::setprecision(4) << si;
710       app_log() << "   " << std::scientific << std::right << std::setw(12) << std::setprecision(4) << ss;
711       app_log() << "   " << std::scientific << std::right << std::setw(20) << std::setprecision(4) << mc;
712       app_log() << "   " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << cv;
713       //app_log() << "   " << std::right << std::setw(12) << ( ind == 0 ? "big shift" : ( ind == 1 ? "medium shift" : "small shift" ) );
714     }
715     else
716     {
717       app_log() << "   " << std::right << std::setw(12) << "N/A";
718       app_log() << "   " << std::right << std::setw(12) << "N/A";
719       app_log() << "   " << std::right << std::setw(20) << "N/A";
720       app_log() << "   " << std::right << std::setw(20) << "N/A";
721       app_log() << "   " << std::right << std::setw(12) << "bad update";
722     }
723   }
724   else
725   {
726     app_log() << "   " << std::right << std::setw(12) << "N/A";
727     app_log() << "   " << std::right << std::setw(12) << "N/A";
728     app_log() << "   " << std::right << std::setw(20) << "N/A";
729     app_log() << "   " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << cv;
730     app_log() << "   " << std::right << std::setw(12) << "initial";
731   }
732   if (ind == bi)
733     app_log() << "  <--";
734   app_log() << std::endl;
735 }
736 
737 ///////////////////////////////////////////////////////////////////////////////////////////////////
738 /// \brief  Returns whether the proposed new cost is the best compared to the others.
739 ///
740 /// \param[in]      ii             index of the proposed best cost
741 /// \param[in]      cv             vector of new costs
742 /// \param[in]      sh             vector of identity shifts (shift_i values)
743 /// \param[in]      ic             the initial cost
744 ///
745 ///////////////////////////////////////////////////////////////////////////////////////////////////
is_best_cost(const int ii,const std::vector<RealType> & cv,const std::vector<double> & sh,const RealType ic) const746 bool QMCFixedSampleLinearOptimizeBatched::is_best_cost(const int ii,
747                                                        const std::vector<RealType>& cv,
748                                                        const std::vector<double>& sh,
749                                                        const RealType ic) const
750 {
751   //app_log() << "determining best cost with cost_increase_tol = " << cost_increase_tol << " and target_shift_i = " << target_shift_i << std::endl;
752 
753   // initialize return value
754   bool retval = true;
755 
756   //app_log() << "retval = " << retval << std::endl;
757 
758   // compare to other costs
759   for (int i = 0; i < cv.size(); i++)
760   {
761     // don't compare to yourself
762     if (i == ii)
763       continue;
764 
765     // we only worry about the other value if it is within the maximum relative change threshold and not too high
766     const bool other_is_valid = ((ic == 0.0 ? 0.0 : std::abs((cv.at(i) - ic) / ic)) < max_relative_cost_change &&
767                                  cv.at(i) < ic + cost_increase_tol);
768     if (other_is_valid)
769     {
770       // if we are using a target shift and the cost is not too much higher, then prefer this cost if its shift is closer to the target shift
771       if (target_shift_i > 0.0)
772       {
773         const bool closer_to_target   = (std::abs(sh.at(ii) - target_shift_i) < std::abs(sh.at(i) - target_shift_i));
774         const bool cost_is_similar    = (std::abs(cv.at(ii) - cv.at(i)) < cost_increase_tol);
775         const bool cost_is_much_lower = (!cost_is_similar && cv.at(ii) < cv.at(i) - cost_increase_tol);
776         if (cost_is_much_lower || (closer_to_target && cost_is_similar))
777           retval = (retval && true);
778         else
779           retval = false;
780 
781         // if we are not using a target shift, then prefer this cost if it is lower
782       }
783       else
784       {
785         retval = (retval && cv.at(ii) <= cv.at(i));
786       }
787     }
788 
789     //app_log() << "cv.at(ii)   = " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << cv.at(ii) << " <= "
790     //          << "cv.at(i)    = " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << cv.at(i)  << " ?" << std::endl;
791     //app_log() << "retval = " << retval << std::endl;
792   }
793 
794   // new cost can only be the best cost if it is less than (or not too much higher than) the initial cost
795   retval = (retval && cv.at(ii) < ic + cost_increase_tol);
796   //app_log() << "cv.at(ii)   = " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << cv.at(ii) << " <= "
797   //          << "ic          = " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << ic        << " ?" << std::endl;
798   //app_log() << "retval = " << retval << std::endl;
799 
800   // new cost is only best if it's relative change from the initial cost is not too large ( or if the initial cost is exactly zero )
801   retval = (retval && (ic == 0.0 ? 0.0 : std::abs((cv.at(ii) - ic) / ic)) < max_relative_cost_change);
802   //app_log() << "std::abs( ( cv.at(ii) - ic ) / ic ) = " << std::fixed << std::right << std::setw(20) << std::setprecision(12)
803   //          << std::abs( ( cv.at(ii) - ic ) / ic ) << " <= " << this->max_relative_cost_change << " ? " << std::endl;
804   //app_log() << "retval = " << retval << std::endl;
805   //app_log() << std::endl;
806 
807   // return whether the proposed cost is actually the best
808   return retval;
809 }
810 
811 ///////////////////////////////////////////////////////////////////////////////////////////////////
812 /// \brief  For each set of shifts, solves the linear method eigenproblem by building and
813 ///         diagonalizing the matrices.
814 ///
815 /// \param[in]      shfits_i              vector of identity shifts
816 /// \param[in]      shfits_s              vector of overlap shifts
817 /// \param[out]     parameterDirections   on exit, the update directions for the different shifts
818 ///
819 ///////////////////////////////////////////////////////////////////////////////////////////////////
solveShiftsWithoutLMYEngine(const std::vector<double> & shifts_i,const std::vector<double> & shifts_s,std::vector<std::vector<RealType>> & parameterDirections)820 void QMCFixedSampleLinearOptimizeBatched::solveShiftsWithoutLMYEngine(
821     const std::vector<double>& shifts_i,
822     const std::vector<double>& shifts_s,
823     std::vector<std::vector<RealType>>& parameterDirections)
824 {
825   // get number of shifts to solve
826   const int nshifts = shifts_i.size();
827 
828   // get number of optimizable parameters
829   numParams = optTarget->getNumParams();
830 
831   // get dimension of the linear method matrices
832   N = numParams + 1;
833 
834   // prepare vectors to hold the parameter updates
835   parameterDirections.resize(nshifts);
836   for (int i = 0; i < parameterDirections.size(); i++)
837     parameterDirections.at(i).assign(N, 0.0);
838 
839   // allocate the matrices we will need
840   Matrix<RealType> ovlMat(N, N);
841   ovlMat = 0.0;
842   Matrix<RealType> hamMat(N, N);
843   hamMat = 0.0;
844   Matrix<RealType> invMat(N, N);
845   invMat = 0.0;
846   Matrix<RealType> sftMat(N, N);
847   sftMat = 0.0;
848   Matrix<RealType> prdMat(N, N);
849   prdMat = 0.0;
850 
851   // build the overlap and hamiltonian matrices
852   optTarget->fillOverlapHamiltonianMatrices(hamMat, ovlMat);
853 
854   //// print the hamiltonian matrix
855   //app_log() << std::endl;
856   //app_log() << "printing H matrix:" << std::endl;
857   //for (int i = 0; i < hamMat.rows(); i++) {
858   //  for (int j = 0; j < hamMat.cols(); j++)
859   //    app_log() << " " << std::scientific << std::right << std::setw(14) << std::setprecision(5) << hamMat(i,j);
860   //  app_log() << std::endl;
861   //}
862   //app_log() << std::endl;
863 
864   //// print the overlap matrix
865   //app_log() << std::endl;
866   //app_log() << "printing S matrix:" << std::endl;
867   //for (int i = 0; i < ovlMat.rows(); i++) {
868   //  for (int j = 0; j < ovlMat.cols(); j++)
869   //    app_log() << " " << std::scientific << std::right << std::setw(14) << std::setprecision(5) << ovlMat(i,j);
870   //  app_log() << std::endl;
871   //}
872   //app_log() << std::endl;
873 
874   // compute the inverse of the overlap matrix
875   invMat.copy(ovlMat);
876   invert_matrix(invMat, false);
877 
878   // compute the update for each shift
879   for (int shift_index = 0; shift_index < nshifts; shift_index++)
880   {
881     // prepare to shift the hamiltonain matrix
882     sftMat.copy(hamMat);
883 
884     // apply the identity shift
885     for (int i = 1; i < N; i++)
886       sftMat(i, i) += shifts_i.at(shift_index);
887 
888     // apply the overlap shift
889     for (int i = 1; i < N; i++)
890       for (int j = 1; j < N; j++)
891         sftMat(i, j) += shifts_s.at(shift_index) * ovlMat(i, j);
892 
893     // multiply the shifted hamiltonian matrix by the inverse of the overlap matrix
894     qmcplusplus::MatrixOperators::product(invMat, sftMat, prdMat);
895 
896     // transpose the result (why?)
897     for (int i = 0; i < N; i++)
898       for (int j = i + 1; j < N; j++)
899         std::swap(prdMat(i, j), prdMat(j, i));
900 
901     // compute the lowest eigenvalue of the product matrix and the corresponding eigenvector
902     const RealType lowestEV = getLowestEigenvector(prdMat, parameterDirections.at(shift_index));
903 
904     // compute the scaling constant to apply to the update
905     Lambda = getNonLinearRescale(parameterDirections.at(shift_index), ovlMat);
906 
907     // scale the update by the scaling constant
908     for (int i = 0; i < numParams; i++)
909       parameterDirections.at(shift_index).at(i + 1) *= Lambda;
910   }
911 }
912 
913 ///////////////////////////////////////////////////////////////////////////////////////////////////
914 /// \brief  Performs one iteration of the linear method using an adaptive scheme that tries three
915 ///         different shift magnitudes and picks the best one.
916 ///         The scheme is adaptive in that it saves the best shift to use as a starting point
917 ///         in the next iteration.
918 ///         Note that the best shift is chosen based on a different sample than that used to
919 ///         construct the linear method matrices in order to avoid over-optimizing on a particular
920 ///         sample.
921 ///
922 /// \return  ???
923 ///
924 ///////////////////////////////////////////////////////////////////////////////////////////////////
925 #ifdef HAVE_LMY_ENGINE
adaptive_three_shift_run()926 bool QMCFixedSampleLinearOptimizeBatched::adaptive_three_shift_run()
927 {
928   // remember what the cost function grads flag was
929   const bool saved_grads_flag = optTarget->getneedGrads();
930 
931   // remember the initial number of samples
932   const int init_num_samp = optTarget->getNumSamples();
933 
934   // the index of central shift
935   const int central_index = num_shifts / 2;
936 
937   // get number of optimizable parameters
938   numParams = optTarget->getNumParams();
939 
940   // prepare the shifts that we will try
941   const std::vector<double> shifts_i = prepare_shifts(bestShift_i);
942   const std::vector<double> shifts_s = prepare_shifts(bestShift_s);
943   std::vector<double> shift_scales(shifts_i.size(), 1.0);
944   for (int i = 0; i < shift_scales.size(); i++)
945     shift_scales.at(i) = shifts_i.at(i) / shift_i_input;
946 
947   // ensure the cost function is set to compute derivative vectors
948   optTarget->setneedGrads(true);
949 
950   // prepare previous updates
951   int count = 0;
952   while (block_lm && previous_update.size() < nolds)
953   {
954     previous_update.push_back(formic::ColVec<double>(numParams));
955     for (int i = 0; i < numParams; i++)
956       previous_update.at(count).at(i) = 2.0 * (formic::random_number<double>() - 0.5);
957     count++;
958   }
959 
960   if (!EngineObj->full_init())
961   {
962     // prepare a variable dependency object with no dependencies
963     formic::VarDeps real_vdeps(numParams, std::vector<double>());
964     vdeps = real_vdeps;
965     EngineObj->get_param(&vdeps,
966                          false, // exact sampling
967                          !targetExcited,
968                          false, // variable deps use?
969                          false, // eom
970                          false, // ssquare
971                          block_lm, 12000, numParams, omega_shift, max_relative_cost_change, shifts_i.at(central_index),
972                          shifts_s.at(central_index), max_param_change, shift_scales);
973   }
974 
975   // update shift
976   EngineObj->shift_update(shift_scales);
977 
978   // turn on wavefunction update mode
979   EngineObj->turn_on_update();
980 
981   // initialize the engine if we do not use block lm or it's the first part of block lm
982   EngineObj->initialize(nblocks, 0, nkept, previous_update, false);
983 
984   // reset the engine
985   EngineObj->reset();
986 
987   // generate samples and compute weights, local energies, and derivative vectors
988   engine_start(EngineObj, *descentEngineObj, MinMethod);
989 
990   // get dimension of the linear method matrices
991   N = numParams + 1;
992 
993   // have the cost function prepare derivative vectors
994   EngineObj->energy_target_compute();
995   const RealType starting_cost = EngineObj->target_value();
996   const RealType init_energy   = EngineObj->energy_mean();
997 
998   // print out the initial energy
999   app_log() << std::endl
1000             << "*************************************************************************************************"
1001             << std::endl
1002             << "Solving the linear method equations on the initial sample with initial energy" << std::setw(20)
1003             << std::setprecision(12) << init_energy << std::endl
1004             << "*************************************************************************************************"
1005             << std::endl
1006             << std::endl;
1007   //const Return_t starting_cost = this->optTarget->LMYEngineCost(true);
1008 
1009   // prepare wavefunction update which does nothing if we do not use block lm
1010   EngineObj->wfn_update_prep();
1011 
1012   if (block_lm)
1013   {
1014     optTarget->setneedGrads(true);
1015 
1016     int numOptParams = optTarget->getNumParams();
1017 
1018     // reset the engine object
1019     EngineObj->reset();
1020 
1021     // finish last sample
1022     finish();
1023 
1024     // take sample
1025     engine_start(EngineObj, *descentEngineObj, MinMethod);
1026   }
1027 
1028   // say what we are doing
1029   app_log() << std::endl
1030             << "*********************************************************" << std::endl
1031             << "Solving the linear method equations on the initial sample" << std::endl
1032             << "*********************************************************" << std::endl
1033             << std::endl;
1034 
1035   // for each set of shifts, solve the linear method equations for the parameter update direction
1036   std::vector<std::vector<ValueType>> parameterDirections;
1037 #ifdef HAVE_LMY_ENGINE
1038   // call the engine to perform update
1039   EngineObj->wfn_update_compute();
1040 #else
1041   solveShiftsWithoutLMYEngine(shifts_i, shifts_s, parameterDirections);
1042 #endif
1043 
1044   // size update direction vector correctly
1045   parameterDirections.resize(shifts_i.size());
1046   for (int i = 0; i < shifts_i.size(); i++)
1047   {
1048     parameterDirections.at(i).assign(N, 0.0);
1049     if (true)
1050     {
1051       for (int j = 0; j < N; j++)
1052         parameterDirections.at(i).at(j) = EngineObj->wfn_update().at(i * N + j);
1053     }
1054     else
1055       parameterDirections.at(i).at(0) = 1.0;
1056   }
1057 
1058   // now that we are done with them, prevent further computation of derivative vectors
1059   optTarget->setneedGrads(false);
1060 
1061   // prepare vectors to hold the initial and current parameters
1062   std::vector<ValueType> currParams(numParams, 0.0);
1063 
1064   // initialize the initial and current parameter vectors
1065   for (int i = 0; i < numParams; i++)
1066     currParams.at(i) = optTarget->Params(i);
1067 
1068   // create a vector telling which updates are within our constraints
1069   std::vector<bool> good_update(parameterDirections.size(), true);
1070 
1071   // compute the largest parameter change for each shift, and zero out updates that have too-large changes
1072   std::vector<RealType> max_change(parameterDirections.size(), 0.0);
1073   for (int k = 0; k < parameterDirections.size(); k++)
1074   {
1075     for (int i = 0; i < numParams; i++)
1076       max_change.at(k) =
1077           std::max(max_change.at(k), std::abs(parameterDirections.at(k).at(i + 1) / parameterDirections.at(k).at(0)));
1078     good_update.at(k) = (good_update.at(k) && max_change.at(k) <= max_param_change);
1079   }
1080 
1081   // prepare to use the middle shift's update as the guiding function for a new sample
1082   for (int i = 0; i < numParams; i++)
1083     optTarget->Params(i) = currParams.at(i) + parameterDirections.at(central_index).at(i + 1);
1084 
1085   // say what we are doing
1086   app_log() << std::endl
1087             << "************************************************************" << std::endl
1088             << "Updating the guiding function with the middle shift's update" << std::endl
1089             << "************************************************************" << std::endl
1090             << std::endl;
1091 
1092   // generate the new sample on which we will compare the different shifts
1093 
1094   finish();
1095   app_log() << std::endl
1096             << "*************************************************************" << std::endl
1097             << "Generating a new sample based on the updated guiding function" << std::endl
1098             << "*************************************************************" << std::endl
1099             << std::endl;
1100 
1101   std::string old_name = vmcEngine->getCommunicator()->getName();
1102   vmcEngine->getCommunicator()->setName(old_name + ".middleShift");
1103   start();
1104   vmcEngine->getCommunicator()->setName(old_name);
1105 
1106   // say what we are doing
1107   app_log() << std::endl
1108             << "******************************************************************" << std::endl
1109             << "Comparing different shifts' cost function values on updated sample" << std::endl
1110             << "******************************************************************" << std::endl
1111             << std::endl;
1112 
1113   // update the current parameters to those of the new guiding function
1114   for (int i = 0; i < numParams; i++)
1115     currParams.at(i) = optTarget->Params(i);
1116 
1117   // compute cost function for the initial parameters (by subtracting the middle shift's update back off)
1118   for (int i = 0; i < numParams; i++)
1119     optTarget->Params(i) = currParams.at(i) - parameterDirections.at(central_index).at(i + 1);
1120   optTarget->IsValid      = true;
1121   const RealType initCost = optTarget->LMYEngineCost(false, EngineObj);
1122 
1123   // compute the update directions for the smaller and larger shifts relative to that of the middle shift
1124   for (int i = 0; i < numParams; i++)
1125   {
1126     for (int j = 0; j < parameterDirections.size(); j++)
1127     {
1128       if (j != central_index)
1129         parameterDirections.at(j).at(i + 1) -= parameterDirections.at(central_index).at(i + 1);
1130     }
1131   }
1132 
1133   // prepare a vector to hold the cost function value for each different shift
1134   std::vector<RealType> costValues(num_shifts, 0.0);
1135 
1136   // compute the cost function value for each shift and make sure the change is within our constraints
1137   for (int k = 0; k < parameterDirections.size(); k++)
1138   {
1139     for (int i = 0; i < numParams; i++)
1140       optTarget->Params(i) = currParams.at(i) + (k == central_index ? 0.0 : parameterDirections.at(k).at(i + 1));
1141     optTarget->IsValid = true;
1142     costValues.at(k)   = optTarget->LMYEngineCost(false, EngineObj);
1143     good_update.at(k) =
1144         (good_update.at(k) && std::abs((initCost - costValues.at(k)) / initCost) < max_relative_cost_change);
1145     if (!good_update.at(k))
1146       costValues.at(k) = std::abs(1.5 * initCost) + 1.0;
1147   }
1148 
1149   // find the best shift and the corresponding update direction
1150   const std::vector<ValueType>* bestDirection = 0;
1151   int best_shift                              = -1;
1152   for (int k = 0; k < costValues.size() && std::abs((initCost - initCost) / initCost) < max_relative_cost_change; k++)
1153     if (is_best_cost(k, costValues, shifts_i, initCost) && good_update.at(k))
1154     {
1155       best_shift    = k;
1156       bestDirection = &parameterDirections.at(k);
1157     }
1158 
1159   // print the results for each shift
1160   app_log() << std::endl;
1161   print_cost_summary_header();
1162   print_cost_summary(0.0, 0.0, 0.0, initCost, -1, best_shift, true);
1163   for (int k = 0; k < good_update.size(); k++)
1164     print_cost_summary(shifts_i.at(k), shifts_s.at(k), max_change.at(k), costValues.at(k), k, best_shift,
1165                        good_update.at(k));
1166 
1167   // if any of the shifts produced a good update, apply the best such update and remember those shifts for next time
1168   if (bestDirection)
1169   {
1170     bestShift_i = shifts_i.at(best_shift);
1171     bestShift_s = shifts_s.at(best_shift);
1172     for (int i = 0; i < numParams; i++)
1173       optTarget->Params(i) = currParams.at(i) + (best_shift == central_index ? 0.0 : bestDirection->at(i + 1));
1174     app_log() << std::endl
1175               << "*****************************************************************************" << std::endl
1176               << "Applying the update for shift_i = " << std::scientific << std::right << std::setw(12)
1177               << std::setprecision(4) << bestShift_i << "     and shift_s = " << std::scientific << std::right
1178               << std::setw(12) << std::setprecision(4) << bestShift_s << std::endl
1179               << "*****************************************************************************" << std::endl
1180               << std::endl;
1181 
1182     // otherwise revert to the old parameters and set the next shift to be larger
1183   }
1184   else
1185   {
1186     bestShift_i *= 10.0;
1187     bestShift_s *= 10.0;
1188     for (int i = 0; i < numParams; i++)
1189       optTarget->Params(i) = currParams.at(i) - parameterDirections.at(central_index).at(i + 1);
1190     app_log() << std::endl
1191               << "***********************************************************" << std::endl
1192               << "Reverting to old parameters and increasing shift magnitudes" << std::endl
1193               << "***********************************************************" << std::endl
1194               << std::endl;
1195   }
1196 
1197   // save the update for future linear method iterations
1198   if (block_lm && bestDirection)
1199   {
1200     // save the difference between the updated and old variables
1201     formic::ColVec<RealType> update_dirs(numParams, 0.0);
1202     for (int i = 0; i < numParams; i++)
1203       // take the real part since blocked LM currently does not support complex parameter optimization
1204       update_dirs.at(i) = std::real(bestDirection->at(i + 1) + parameterDirections.at(central_index).at(i + 1));
1205     previous_update.insert(previous_update.begin(), update_dirs);
1206 
1207     // eliminate the oldest saved update if necessary
1208     while (previous_update.size() > nolds)
1209       previous_update.pop_back();
1210   }
1211 
1212   // return the cost function grads flag to what it was
1213   optTarget->setneedGrads(saved_grads_flag);
1214 
1215   // perform some finishing touches for this linear method iteration
1216   finish();
1217 
1218   // set the number samples to be initial one
1219   optTarget->setNumSamples(init_num_samp);
1220 
1221   //app_log() << "block first second third end " << block_first << block_second << block_third << endl;
1222   // return whether the cost function's report counter is positive
1223   return (optTarget->getReportCounter() > 0);
1224 }
1225 #endif
1226 
one_shift_run()1227 bool QMCFixedSampleLinearOptimizeBatched::one_shift_run()
1228 {
1229   // ensure the cost function is set to compute derivative vectors
1230   optTarget->setneedGrads(true);
1231 
1232   // generate samples and compute weights, local energies, and derivative vectors
1233   start();
1234 
1235   // get number of optimizable parameters
1236   numParams = optTarget->getNumParams();
1237 
1238   // get dimension of the linear method matrices
1239   N = numParams + 1;
1240 
1241   // prepare vectors to hold the initial and current parameters
1242   std::vector<RealType> currentParameters(numParams, 0.0);
1243 
1244   // initialize the initial and current parameter vectors
1245   for (int i = 0; i < numParams; i++)
1246     currentParameters.at(i) = std::real(optTarget->Params(i));
1247 
1248   // prepare vectors to hold the parameter update directions for each shift
1249   std::vector<RealType> parameterDirections;
1250   parameterDirections.assign(N, 0.0);
1251 
1252   // compute the initial cost
1253 #ifdef QMC_CUDA
1254   // Ye : can't call computedCost directly, internal data was not correct for ham,ovl matrices.
1255   // more investiation is needed.
1256   const RealType initCost = optTarget->Cost(true);
1257 #else
1258   const RealType initCost = optTarget->computedCost();
1259 #endif
1260 
1261   // say what we are doing
1262   app_log() << std::endl
1263             << "*****************************************" << std::endl
1264             << "Building overlap and Hamiltonian matrices" << std::endl
1265             << "*****************************************" << std::endl;
1266 
1267   // allocate the matrices we will need
1268   Matrix<RealType> ovlMat(N, N);
1269   ovlMat = 0.0;
1270   Matrix<RealType> hamMat(N, N);
1271   hamMat = 0.0;
1272   Matrix<RealType> invMat(N, N);
1273   invMat = 0.0;
1274   Matrix<RealType> prdMat(N, N);
1275   prdMat = 0.0;
1276 
1277   // build the overlap and hamiltonian matrices
1278   optTarget->fillOverlapHamiltonianMatrices(hamMat, ovlMat);
1279   invMat.copy(ovlMat);
1280 
1281   if (do_output_matrices_)
1282   {
1283     output_overlap_.output(ovlMat);
1284     output_hamiltonian_.output(hamMat);
1285   }
1286 
1287   // apply the identity shift
1288   for (int i = 1; i < N; i++)
1289   {
1290     hamMat(i, i) += bestShift_i;
1291     if (invMat(i, i) == 0)
1292       invMat(i, i) = bestShift_i * bestShift_s;
1293   }
1294 
1295   // compute the inverse of the overlap matrix
1296   invert_matrix(invMat, false);
1297 
1298   // apply the overlap shift
1299   for (int i = 1; i < N; i++)
1300     for (int j = 1; j < N; j++)
1301       hamMat(i, j) += bestShift_s * ovlMat(i, j);
1302 
1303   // multiply the shifted hamiltonian matrix by the inverse of the overlap matrix
1304   qmcplusplus::MatrixOperators::product(invMat, hamMat, prdMat);
1305 
1306   // transpose the result (why?)
1307   for (int i = 0; i < N; i++)
1308     for (int j = i + 1; j < N; j++)
1309       std::swap(prdMat(i, j), prdMat(j, i));
1310 
1311   // compute the lowest eigenvalue of the product matrix and the corresponding eigenvector
1312   const RealType lowestEV = getLowestEigenvector(prdMat, parameterDirections);
1313 
1314   // compute the scaling constant to apply to the update
1315   Lambda = getNonLinearRescale(parameterDirections, ovlMat);
1316 
1317   // scale the update by the scaling constant
1318   for (int i = 0; i < numParams; i++)
1319     parameterDirections.at(i + 1) *= Lambda;
1320 
1321   // now that we are done building the matrices, prevent further computation of derivative vectors
1322   optTarget->setneedGrads(false);
1323 
1324   // prepare to use the middle shift's update as the guiding function for a new sample
1325   if (!freeze_parameters_)
1326   {
1327     for (int i = 0; i < numParams; i++)
1328       optTarget->Params(i) = currentParameters.at(i) + parameterDirections.at(i + 1);
1329   }
1330 
1331   RealType largestChange(0);
1332   int max_element = 0;
1333   for (int i = 0; i < numParams; i++)
1334     if (std::abs(parameterDirections.at(i + 1)) > largestChange)
1335     {
1336       largestChange = std::abs(parameterDirections.at(i + 1));
1337       max_element   = i;
1338     }
1339   app_log() << std::endl
1340             << "Among totally " << numParams << " optimized parameters, "
1341             << "largest LM parameter change : " << largestChange << " at parameter " << max_element << std::endl;
1342 
1343   // compute the new cost
1344   optTarget->IsValid     = true;
1345   const RealType newCost = optTarget->Cost(false);
1346 
1347   app_log() << std::endl
1348             << "******************************************************************************" << std::endl
1349             << "Init Cost = " << std::scientific << std::right << std::setw(12) << std::setprecision(4) << initCost
1350             << "    New Cost = " << std::scientific << std::right << std::setw(12) << std::setprecision(4) << newCost
1351             << "  Delta Cost = " << std::scientific << std::right << std::setw(12) << std::setprecision(4)
1352             << newCost - initCost << std::endl
1353             << "******************************************************************************" << std::endl;
1354 
1355   if (!optTarget->IsValid || std::isnan(newCost))
1356   {
1357     app_log() << std::endl << "The new set of parameters is not valid. Revert to the old set!" << std::endl;
1358     for (int i = 0; i < numParams; i++)
1359       optTarget->Params(i) = currentParameters.at(i);
1360     bestShift_s = bestShift_s * shift_s_base;
1361     if (accept_history[0] == true && accept_history[1] == false) // rejected the one before last and accepted the last
1362     {
1363       shift_s_base = std::sqrt(shift_s_base);
1364       app_log() << "Update shift_s_base to " << shift_s_base << std::endl;
1365     }
1366     accept_history <<= 1;
1367   }
1368   else
1369   {
1370     if (bestShift_s > 1.0e-2)
1371       bestShift_s = bestShift_s / shift_s_base;
1372     // say what we are doing
1373     app_log() << std::endl << "The new set of parameters is valid. Updating the trial wave function!" << std::endl;
1374     accept_history <<= 1;
1375     accept_history.set(0, true);
1376   }
1377 
1378   app_log() << std::endl
1379             << "*****************************************************************************" << std::endl
1380             << "Applying the update for shift_i = " << std::scientific << std::right << std::setw(12)
1381             << std::setprecision(4) << bestShift_i << "     and shift_s = " << std::scientific << std::right
1382             << std::setw(12) << std::setprecision(4) << bestShift_s << std::endl
1383             << "*****************************************************************************" << std::endl;
1384 
1385   // perform some finishing touches for this linear method iteration
1386   finish();
1387 
1388   // return whether the cost function's report counter is positive
1389   return (optTarget->getReportCounter() > 0);
1390 }
1391 
1392 #ifdef HAVE_LMY_ENGINE
1393 //Function for optimizing using gradient descent
descent_run()1394 bool QMCFixedSampleLinearOptimizeBatched::descent_run()
1395 {
1396   start();
1397 
1398   //Compute Lagrangian derivatives needed for parameter updates with engine_checkConfigurations, which is called inside engine_start
1399   engine_start(EngineObj, *descentEngineObj, MinMethod);
1400 
1401   int descent_num = descentEngineObj->getDescentNum();
1402 
1403   if (descent_num == 0)
1404     descentEngineObj->setupUpdate(optTarget->getOptVariables());
1405 
1406   //Store the derivatives and then compute parameter updates
1407   descentEngineObj->storeDerivRecord();
1408 
1409   descentEngineObj->updateParameters();
1410 
1411   std::vector<ValueType> results = descentEngineObj->retrieveNewParams();
1412 
1413 
1414   for (int i = 0; i < results.size(); i++)
1415   {
1416     optTarget->Params(i) = results[i];
1417   }
1418 
1419   //If descent is being run as part of a hybrid optimization, need to check if a vector of
1420   //parameter differences should be stored.
1421   if (doHybrid)
1422   {
1423     int store_num = descentEngineObj->retrieveStoreFrequency();
1424     bool store    = hybridEngineObj->queryStore(store_num, OptimizerType::DESCENT);
1425     if (store)
1426     {
1427       descentEngineObj->storeVectors(results);
1428     }
1429   }
1430 
1431   finish();
1432   return (optTarget->getReportCounter() > 0);
1433 }
1434 #endif
1435 
1436 
1437 //Function for controlling the alternation between sections of descent optimization and BLM optimization.
1438 #ifdef HAVE_LMY_ENGINE
hybrid_run()1439 bool QMCFixedSampleLinearOptimizeBatched::hybrid_run()
1440 {
1441   app_log() << "This is methodName: " << MinMethod << std::endl;
1442 
1443   //Either the adaptive BLM or descent optimization is run
1444 
1445   if (current_optimizer_type_ == OptimizerType::ADAPTIVE)
1446   {
1447     //If the optimization just switched to using the BLM, need to transfer a set
1448     //of vectors to the BLM engine.
1449     if (previous_optimizer_type_ == OptimizerType::DESCENT)
1450     {
1451       std::vector<std::vector<ValueType>> hybridBLM_Input = descentEngineObj->retrieveHybridBLM_Input();
1452 #if !defined(QMC_COMPLEX)
1453       //FIXME once complex is fixed in BLM engine
1454       EngineObj->setHybridBLM_Input(hybridBLM_Input);
1455 #endif
1456     }
1457     adaptive_three_shift_run();
1458   }
1459 
1460   if (current_optimizer_type_ == OptimizerType::DESCENT)
1461     descent_run();
1462 
1463   app_log() << "Finished a hybrid step" << std::endl;
1464   return (optTarget->getReportCounter() > 0);
1465 }
1466 #endif
1467 
1468 } // namespace qmcplusplus
1469