QMCPACK
QMCFixedSampleLinearOptimize.cpp
Go to the documentation of this file.
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) 2016 Jeongnim Kim and 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 //
14 // File created by: Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
15 //////////////////////////////////////////////////////////////////////////////////////
16 
17 
19 #include "Particle/HDFWalkerIO.h"
20 #include "OhmmsData/AttributeSet.h"
21 #include "Message/CommOperators.h"
22 #include "RandomNumberControl.h"
25 #include "QMCDrivers/VMC/VMC.h"
28 #include "CPU/Blasf.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 /*#include "Message/Communicate.h"*/
42 
43 namespace qmcplusplus
44 {
46 
47 
50  TrialWaveFunction& psi,
51  QMCHamiltonian& h,
53  : QMCDriver(project_data, w, psi, h, comm, "QMCFixedSampleLinearOptimize"),
54 #ifdef HAVE_LMY_ENGINE
55  vdeps(1, std::vector<double>()),
56 #endif
57  nstabilizers(3),
58  stabilizerScale(2.0),
59  bigChange(50),
60  exp0(-16),
61  stepsize(0.25),
62  StabilizerMethod("best"),
63  bestShift_i(-1.0),
64  bestShift_s(-1.0),
65  shift_i_input(0.01),
66  shift_s_input(1.00),
67  accept_history(3),
68  shift_s_base(4.0),
69  num_shifts(3),
70  max_relative_cost_change(10.0),
71  max_param_change(0.3),
72  cost_increase_tol(0.0),
73  target_shift_i(-1.0),
74  targetExcitedStr("no"),
75  targetExcited(false),
76  block_lmStr("no"),
77  block_lm(false),
78  nblocks(1),
79  nolds(1),
80  nkept(1),
81  nsamp_comp(0),
82  omega_shift(0.0),
83  block_first(true),
84  block_second(false),
85  block_third(false),
86  MinMethod("OneShiftOnly"),
87  previous_optimizer_type_(OptimizerType::NONE),
88  current_optimizer_type_(OptimizerType::NONE),
89  do_output_matrices_(false),
90  output_matrices_initialized_(false),
91  freeze_parameters_(false),
92  Max_iterations(1),
93  wfNode(NULL),
94  param_tol(1e-4),
95  generate_samples_timer_(createGlobalTimer("QMCLinearOptimize::generateSamples", timer_level_medium)),
96  initialize_timer_(createGlobalTimer("QMCLinearOptimize::Initialize", timer_level_medium)),
97  eigenvalue_timer_(createGlobalTimer("QMCLinearOptimize::EigenvalueSolve", timer_level_medium)),
98  involvmat_timer_(createGlobalTimer("QMCLinearOptimize::invertOverlapMat", timer_level_medium)),
99  line_min_timer_(createGlobalTimer("QMCLinearOptimize::Line_Minimization", timer_level_medium)),
100  cost_function_timer_(createGlobalTimer("QMCLinearOptimize::CostFunction", timer_level_medium))
101 {
102  IsQMCDriver = false;
103  //set the optimization flag
105  //read to use vmc output (just in case)
106  RootName = "pot";
107  m_param.add(Max_iterations, "max_its");
108  m_param.add(nstabilizers, "nstabilizers");
109  m_param.add(stabilizerScale, "stabilizerscale");
110  m_param.add(bigChange, "bigchange");
111  m_param.add(MinMethod, "MinMethod");
112  m_param.add(exp0, "exp0");
113  m_param.add(targetExcitedStr, "targetExcited");
114  m_param.add(block_lmStr, "block_lm");
115  m_param.add(nblocks, "nblocks");
116  m_param.add(nolds, "nolds");
117  m_param.add(nkept, "nkept");
118  m_param.add(nsamp_comp, "nsamp_comp");
119  m_param.add(omega_shift, "omega");
120  m_param.add(max_relative_cost_change, "max_relative_cost_change");
121  m_param.add(max_param_change, "max_param_change");
122  m_param.add(shift_i_input, "shift_i");
123  m_param.add(shift_s_input, "shift_s");
124  m_param.add(num_shifts, "num_shifts");
125  m_param.add(cost_increase_tol, "cost_increase_tol");
126  m_param.add(target_shift_i, "target_shift_i");
127  m_param.add(param_tol, "alloweddifference");
128 
129 #ifdef HAVE_LMY_ENGINE
130  //app_log() << "construct QMCFixedSampleLinearOptimize" << endl;
131  std::vector<double> shift_scales(3, 1.0);
132  EngineObj = new cqmc::engine::LMYEngine<ValueType>(&vdeps,
133  false, // exact sampling
134  true, // ground state?
135  false, // variance correct,
136  true,
137  true, // print matrices,
138  true, // build matrices
139  false, // spam
140  false, // use var deps?
141  true, // chase lowest
142  false, // chase closest
143  false, // eom
144  false,
145  false, // eom related
146  false, // eom related
147  false, // use block?
148  120000, // number of samples
149  0, // number of parameters
150  60, // max krylov iter
151  0, // max spam inner iter
152  1, // spam appro degree
153  0, // eom related
154  0, // eom related
155  0, // eom related
156  0.0, // omega
157  0.0, // var weight
158  1.0e-6, // convergence threshold
159  0.99, // minimum S singular val
160  0.0, 0.0,
161  10.0, // max change allowed
162  1.00, // identity shift
163  1.00, // overlap shift
164  0.3, // max parameter change
165  shift_scales, app_log());
166 #endif
167 
168 
169  // stale parameters
170  // m_param.add(eigCG,"eigcg");
171  // m_param.add(TotalCGSteps,"cgsteps");
172  // m_param.add(w_beta,"beta");
173  // quadstep=-1.0;
174  // m_param.add(quadstep,"quadstep");
175  // m_param.add(stepsize,"stepsize");
176  // m_param.add(exp1,"exp1");
177  // m_param.add(StabilizerMethod,"StabilizerMethod");
178  // m_param.add(LambdaMax,"LambdaMax");
179  //Set parameters for line minimization:
180 }
181 
182 /** Clean up the vector */
184 {
185 #ifdef HAVE_LMY_ENGINE
186  delete EngineObj;
187 #endif
188 }
189 
191 {
192  for (int i = 0; i < optparam.size(); i++)
193  optTarget->Params(i) = optparam[i] + dl * optdir[i];
194  RealType c = optTarget->Cost(false);
195  //only allow this to go false if it was true. If false, stay false
196  // if (validFuncVal)
197  validFuncVal = optTarget->IsValid;
198  return c;
199 }
200 
202 {
203  // generate samples and compute weights, local energies, and derivative vectors
204  start();
205 
207 
208  finish();
209 
210  return true;
211 }
212 
214 {
216  {
217  size_t numParams = optTarget->getNumParams();
218  size_t N = numParams + 1;
222  }
223 
224  if (doGradientTest)
225  {
226  app_log() << "Doing gradient test run" << std::endl;
227  return test_run();
228  }
229 
230 #ifdef HAVE_LMY_ENGINE
231  if (doHybrid)
232  {
233 #if !defined(QMC_COMPLEX)
234  app_log() << "Doing hybrid run" << std::endl;
235  return hybrid_run();
236 #else
237  myComm->barrier_and_abort(" Error: Hybrid method does not work with QMC_COMPLEX=1. \n");
238 #endif
239  }
240 
242 #if !defined(QMC_COMPLEX)
243  return descent_run();
244 #else
245  myComm->barrier_and_abort(" Error: Descent method does not work with QMC_COMPLEX=1. \n");
246 #endif
247 
248 
249  // if requested, perform the update via the adaptive three-shift or single-shift method
251  return adaptive_three_shift_run();
252 
253 
254 #endif
255 
257  return one_shift_run();
258 
259  start();
260  bool Valid(true);
261  int Total_iterations(0);
262  //size of matrix
263  size_t numParams = optTarget->getNumParams();
264  size_t N = numParams + 1;
265  // where we are and where we are pointing
266  std::vector<RealType> currentParameterDirections(N, 0);
267  std::vector<RealType> currentParameters(numParams, 0);
268  std::vector<RealType> bestParameters(numParams, 0);
269  for (int i = 0; i < numParams; i++)
270  bestParameters[i] = currentParameters[i] = std::real(optTarget->Params(i));
271  // proposed direction and new parameters
272  optdir.resize(numParams, 0);
273  optparam.resize(numParams, 0);
274 
275  while (Total_iterations < Max_iterations)
276  {
277  Total_iterations += 1;
278  app_log() << "Iteration: " << Total_iterations << "/" << Max_iterations << std::endl;
279  if (!ValidCostFunction(Valid))
280  continue;
281  //this is the small amount added to the diagonal to stabilize the eigenvalue equation. 10^stabilityBase
282  RealType stabilityBase(exp0);
283  // reset params if necessary
284  for (int i = 0; i < numParams; i++)
285  optTarget->Params(i) = currentParameters[i];
287  RealType lastCost(optTarget->Cost(true));
289  // if cost function is currently invalid continue
290  Valid = optTarget->IsValid;
291  if (!ValidCostFunction(Valid))
292  continue;
293  RealType newCost(lastCost);
294  RealType startCost(lastCost);
295  Matrix<RealType> Left(N, N);
296  Matrix<RealType> Right(N, N);
297  Matrix<RealType> S(N, N);
298  // stick in wrong matrix to reduce the number of matrices we need by 1.( Left is actually stored in Right, & vice-versa)
299  optTarget->fillOverlapHamiltonianMatrices(Right, Left);
300  S.copy(Left);
301  bool apply_inverse(true);
302  if (apply_inverse)
303  {
304  Matrix<RealType> RightT(Left);
305  {
307  invert_matrix(RightT, false);
308  }
309  Left = 0;
310  product(RightT, Right, Left);
311  // Now the left matrix is the Hamiltonian with the inverse of the overlap applied ot it.
312  }
313  //Find largest off-diagonal element compared to diagonal element.
314  //This gives us an idea how well conditioned it is, used to stabilize.
315  RealType od_largest(0);
316  for (int i = 0; i < N; i++)
317  for (int j = 0; j < N; j++)
318  od_largest = std::max(std::max(od_largest, std::abs(Left(i, j)) - std::abs(Left(i, i))),
319  std::abs(Left(i, j)) - std::abs(Left(j, j)));
320  app_log() << "od_largest " << od_largest << std::endl;
321  //if(od_largest>0)
322  // od_largest = std::log(od_largest);
323  //else
324  // od_largest = -1e16;
325  //if (od_largest<stabilityBase)
326  // stabilityBase=od_largest;
327  //else
328  // stabilizerScale = std::max( 0.2*(od_largest-stabilityBase)/nstabilizers, stabilizerScale);
329  app_log() << " stabilityBase " << stabilityBase << std::endl;
330  app_log() << " stabilizerScale " << stabilizerScale << std::endl;
331  int failedTries(0);
332  bool acceptedOneMove(false);
333  for (int stability = 0; stability < nstabilizers; stability++)
334  {
335  bool goodStep(true);
336  // store the Hamiltonian matrix in Right
337  for (int i = 0; i < N; i++)
338  for (int j = 0; j < N; j++)
339  Right(i, j) = Left(j, i);
340  RealType XS(stabilityBase + stabilizerScale * (failedTries + stability));
341  for (int i = 1; i < N; i++)
342  Right(i, i) += std::exp(XS);
343  app_log() << " Using XS:" << XS << " " << failedTries << " " << stability << std::endl;
344 
345  {
347  getLowestEigenvector(Right, currentParameterDirections);
348  Lambda = getNonLinearRescale(currentParameterDirections, S, *optTarget);
349  }
350  // biggest gradient in the parameter direction vector
351  RealType bigVec(0);
352  for (int i = 0; i < numParams; i++)
353  bigVec = std::max(bigVec, std::abs(currentParameterDirections[i + 1]));
354  // this can be overwritten during the line minimization
355  RealType evaluated_cost(startCost);
356  if (MinMethod == "rescale")
357  {
358  if (std::abs(Lambda * bigVec) > bigChange)
359  {
360  goodStep = false;
361  app_log() << " Failed Step. Magnitude of largest parameter change: " << std::abs(Lambda * bigVec)
362  << std::endl;
363  if (stability == 0)
364  {
365  failedTries++;
366  stability--;
367  }
368  else
369  stability = nstabilizers;
370  }
371  for (int i = 0; i < numParams; i++)
372  optTarget->Params(i) = currentParameters[i] + Lambda * currentParameterDirections[i + 1];
373  optTarget->IsValid = true;
374  }
375  else
376  {
377  for (int i = 0; i < numParams; i++)
378  optparam[i] = currentParameters[i];
379  for (int i = 0; i < numParams; i++)
380  optdir[i] = currentParameterDirections[i + 1];
381  TOL = param_tol / bigVec;
382  AbsFuncTol = true;
383  largeQuarticStep = bigChange / bigVec;
384  LambdaMax = 0.5 * Lambda;
386  if (MinMethod == "quartic")
387  {
388  int npts(7);
389  quadstep = stepsize * Lambda;
390  largeQuarticStep = bigChange / bigVec;
391  Valid = lineoptimization3(npts, evaluated_cost);
392  }
393  else
394  Valid = lineoptimization2();
396  RealType biggestParameterChange = bigVec * std::abs(Lambda);
397  if (biggestParameterChange > bigChange)
398  {
399  goodStep = false;
400  failedTries++;
401  app_log() << " Failed Step. Largest LM parameter change:" << biggestParameterChange << std::endl;
402  if (stability == 0)
403  stability--;
404  else
405  stability = nstabilizers;
406  }
407  else
408  {
409  for (int i = 0; i < numParams; i++)
410  optTarget->Params(i) = optparam[i] + Lambda * optdir[i];
411  app_log() << " Good Step. Largest LM parameter change:" << biggestParameterChange << std::endl;
412  }
413  }
414 
415  if (goodStep)
416  {
417  // this may have been evaluated already
418  // newCost=evaluated_cost;
419  //get cost at new minimum
420  newCost = optTarget->Cost(false);
421  app_log() << " OldCost: " << lastCost << " NewCost: " << newCost << " Delta Cost:" << (newCost - lastCost)
422  << std::endl;
423  optTarget->printEstimates();
424  // quit if newcost is greater than lastcost. E(Xs) looks quadratic (between steepest descent and parabolic)
425  // mmorales
426  Valid = optTarget->IsValid;
427  //if (MinMethod!="rescale" && !ValidCostFunction(Valid))
428  if (!ValidCostFunction(Valid))
429  {
430  goodStep = false;
431  app_log() << " Good Step, but cost function invalid" << std::endl;
432  failedTries++;
433  if (stability > 0)
434  stability = nstabilizers;
435  else
436  stability--;
437  }
438  if (newCost < lastCost && goodStep)
439  {
440  //Move was acceptable
441  for (int i = 0; i < numParams; i++)
442  bestParameters[i] = std::real(optTarget->Params(i));
443  lastCost = newCost;
444  acceptedOneMove = true;
445  if (std::abs(newCost - lastCost) < 1e-4)
446  {
447  failedTries++;
448  stability = nstabilizers;
449  continue;
450  }
451  }
452  else if (stability > 0)
453  {
454  failedTries++;
455  stability = nstabilizers;
456  continue;
457  }
458  }
459  app_log().flush();
460  if (failedTries > 20)
461  break;
462  //APP_ABORT("QMCFixedSampleLinearOptimize::run TOO MANY FAILURES");
463  }
464 
465  if (acceptedOneMove)
466  {
467  app_log() << "Setting new Parameters" << std::endl;
468  for (int i = 0; i < numParams; i++)
469  optTarget->Params(i) = bestParameters[i];
470  }
471  else
472  {
473  app_log() << "Reverting to old Parameters" << std::endl;
474  for (int i = 0; i < numParams; i++)
475  optTarget->Params(i) = currentParameters[i];
476  }
477  app_log().flush();
478  }
479 
480  finish();
481  return (optTarget->getReportCounter() > 0);
482 }
483 
484 /** Parses the xml input file for parameter definitions for the wavefunction
485 * optimization.
486 * @param q current xmlNode
487 * @return true if successful
488 */
490 {
491  std::string vmcMove("pbyp");
492  std::string ReportToH5("no");
493  std::string OutputMatrices("no");
494  std::string FreezeParameters("no");
495  OhmmsAttributeSet oAttrib;
496  oAttrib.add(vmcMove, "move");
497  oAttrib.add(ReportToH5, "hdf5");
498 
499  m_param.add(OutputMatrices, "output_matrices_csv");
500  m_param.add(FreezeParameters, "freeze_parameters");
501 
502  oAttrib.put(q);
503  m_param.put(q);
504 
505  do_output_matrices_ = (OutputMatrices != "no");
506  freeze_parameters_ = (FreezeParameters != "no");
507 
508  // Use freeze_parameters with output_matrices to generate multiple lines in the output with
509  // the same parameters so statistics can be computed in post-processing.
510 
511  if (freeze_parameters_)
512  {
513  app_log() << std::endl;
514  app_warning() << " The option 'freeze_parameters' is enabled. Variational parameters will not be updated. This "
515  "run will not perform variational parameter optimization!"
516  << std::endl;
517  app_log() << std::endl;
518  }
519 
520  doGradientTest = false;
521  processChildren(q, [&](const std::string& cname, const xmlNodePtr element) {
522  if (cname == "optimize")
523  {
524  const std::string att(getXMLAttributeValue(element, "method"));
525  if (!att.empty() && att == "gradient_test")
526  {
527  GradientTestInput test_grad_input;
528  test_grad_input.readXML(element);
529  if (!testEngineObj)
530  testEngineObj = std::make_unique<GradientTest>(std::move(test_grad_input));
531  doGradientTest = true;
532  MinMethod = "gradient_test";
533  }
534  else
535  {
536  std::stringstream error_msg;
537  app_log() << "Unknown or missing 'method' attribute in optimize tag: " << att << "\n";
538  throw UniformCommunicateError(error_msg.str());
539  }
540  }
541  });
542 
543 
544  doHybrid = false;
545 
546  if (MinMethod == "hybrid")
547  {
548  doHybrid = true;
549  if (!hybridEngineObj)
550  hybridEngineObj = std::make_unique<HybridEngine>(myComm, q);
551 
552  hybridEngineObj->incrementStepCounter();
553 
554  return processOptXML(hybridEngineObj->getSelectedXML(), vmcMove, ReportToH5 == "yes");
555  }
556  else
557  return processOptXML(q, vmcMove, ReportToH5 == "yes");
558 }
559 
560 bool QMCFixedSampleLinearOptimize::processOptXML(xmlNodePtr opt_xml, const std::string& vmcMove, bool reportH5)
561 {
562  m_param.put(opt_xml);
564  targetExcited = (targetExcitedStr == "yes");
565 
567  block_lm = (block_lmStr == "yes");
568 
569  auto iter = OptimizerNames.find(MinMethod);
570  if (iter == OptimizerNames.end())
571  throw std::runtime_error("Unknown MinMethod!\n");
574 
576  {
577  if (!descentEngineObj)
578  {
579  descentEngineObj = std::make_unique<DescentEngine>(myComm, opt_xml);
580  }
581 
582  else
583  {
584  descentEngineObj->processXML(opt_xml);
585  }
586  }
587 
588 
589  // sanity check
592  myComm->barrier_and_abort(R"(targetExcited = "yes" requires that MinMethod = "adaptive or descent)");
593 
594 #ifdef _OPENMP
596  {
597  // throw std::runtime_error("OpenMP threading not enabled with AdaptiveThreeShift optimizer. Use MPI for parallelism instead, and set OMP_NUM_THREADS to 1.");
598  app_log() << "test version of OpenMP threading with AdaptiveThreeShift optimizer" << std::endl;
599  }
600 #endif
601 
602  // check parameter change sanity
603  if (max_param_change <= 0.0)
604  throw std::runtime_error("max_param_change must be positive in QMCFixedSampleLinearOptimize::put");
605 
606  // check cost change sanity
607  if (max_relative_cost_change <= 0.0)
608  throw std::runtime_error("max_relative_cost_change must be positive in QMCFixedSampleLinearOptimize::put");
609 
610  // check shift sanity
611  if (shift_i_input <= 0.0)
612  throw std::runtime_error("shift_i must be positive in QMCFixedSampleLinearOptimize::put");
613  if (shift_s_input <= 0.0)
614  throw std::runtime_error("shift_s must be positive in QMCFixedSampleLinearOptimize::put");
615 
616  // check cost increase tolerance sanity
617  if (cost_increase_tol < 0.0)
618  throw std::runtime_error("cost_increase_tol must be non-negative in QMCFixedSampleLinearOptimize::put");
619 
620  // if this is the first time this function has been called, set the initial shifts
625  if (bestShift_s < 0.0)
627 
628  xmlNodePtr qsave = opt_xml;
629  xmlNodePtr cur = qsave->children;
630  int pid = OHMMS::Controller->rank();
631  while (cur != NULL)
632  {
633  std::string cname((const char*)(cur->name));
634  if (cname == "mcwalkerset")
635  {
636  mcwalkerNodePtr.push_back(cur);
637  }
638  cur = cur->next;
639  }
640  // no walkers exist, add 10
641  if (W.getActiveWalkers() == 0)
644 
645  // Destroy old object to stop timer to correctly order timer with object lifetime scope
646  vmcEngine.reset(nullptr);
647  vmcEngine = std::make_unique<VMC>(project_data_, W, Psi, H, RandomNumberControl::Children, myComm, false);
648  vmcEngine->setUpdateMode(vmcMove[0] == 'p');
649 
650 
651  vmcEngine->setStatus(RootName, h5FileRoot, AppendRun);
652  vmcEngine->process(qsave);
653 
654  bool success = true;
655  //allways reset optTarget
656  optTarget = std::make_unique<QMCCostFunction>(W, Psi, H, myComm);
657  optTarget->setStream(&app_log());
658  if (reportH5)
659  optTarget->reportH5 = true;
660  success = optTarget->put(qsave);
661 
662  return success;
663 }
664 
665 ///////////////////////////////////////////////////////////////////////////////////////////////////
666 /// \brief returns a vector of three shift values centered around the provided shift.
667 ///
668 /// \param[in] central_shift the central shift
669 ///
670 ///////////////////////////////////////////////////////////////////////////////////////////////////
671 std::vector<double> QMCFixedSampleLinearOptimize::prepare_shifts(const double central_shift) const
672 {
673  std::vector<double> retval(num_shifts);
674 
675  // check to see whether the number of shifts is odd
676  if (num_shifts % 2 == 0)
677  throw std::runtime_error("number of shifts must be odd in QMCFixedSampleLinearOptimize::prepare_shifts");
678 
679  // decide the central shift index
680  int central_index = num_shifts / 2;
681 
682  for (int i = 0; i < num_shifts; i++)
683  {
684  if (i < central_index)
685  retval.at(i) = central_shift / (4.0 * (central_index - i));
686  else if (i > central_index)
687  retval.at(i) = central_shift * (4.0 * (i - central_index));
688  else if (i == central_index)
689  retval.at(i) = central_shift;
690  //retval.at(i) = central_shift
691  //retval.at(0) = central_shift * 4.0;
692  //retval.at(1) = central_shift;
693  //retval.at(2) = central_shift / 4.0;
694  }
695  return retval;
696 }
697 
698 ///////////////////////////////////////////////////////////////////////////////////////////////////
699 /// \brief prints a header for the summary of each shift's result
700 ///
701 ///////////////////////////////////////////////////////////////////////////////////////////////////
703 {
704  app_log() << " " << std::right << std::setw(12) << "shift_i";
705  app_log() << " " << std::right << std::setw(12) << "shift_s";
706  app_log() << " " << std::right << std::setw(20) << "max param change";
707  app_log() << " " << std::right << std::setw(20) << "cost function value";
708  app_log() << std::endl;
709  app_log() << " " << std::right << std::setw(12) << "------------";
710  app_log() << " " << std::right << std::setw(12) << "------------";
711  app_log() << " " << std::right << std::setw(20) << "--------------------";
712  app_log() << " " << std::right << std::setw(20) << "--------------------";
713  app_log() << std::endl;
714 }
715 
716 ///////////////////////////////////////////////////////////////////////////////////////////////////
717 /// \brief prints a summary of the computed cost for the given shift
718 ///
719 /// \param[in] si the identity shift
720 /// \param[in] ss the overlap shift
721 /// \param[in] mc the maximum parameter change
722 /// \param[in] cv the cost function value
723 /// \param[in] ind the shift index: -1 (for initial state), 0, 1, or 2
724 /// \param[in] bi index of the best shift
725 /// \param[in] gu flag telling whether it was a good update
726 ///
727 ///////////////////////////////////////////////////////////////////////////////////////////////////
729  const double ss,
730  const RealType mc,
731  const RealType cv,
732  const int ind,
733  const int bi,
734  const bool gu)
735 {
736  if (ind >= 0)
737  {
738  if (gu)
739  {
740  app_log() << " " << std::scientific << std::right << std::setw(12) << std::setprecision(4) << si;
741  app_log() << " " << std::scientific << std::right << std::setw(12) << std::setprecision(4) << ss;
742  app_log() << " " << std::scientific << std::right << std::setw(20) << std::setprecision(4) << mc;
743  app_log() << " " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << cv;
744  //app_log() << " " << std::right << std::setw(12) << ( ind == 0 ? "big shift" : ( ind == 1 ? "medium shift" : "small shift" ) );
745  }
746  else
747  {
748  app_log() << " " << std::right << std::setw(12) << "N/A";
749  app_log() << " " << std::right << std::setw(12) << "N/A";
750  app_log() << " " << std::right << std::setw(20) << "N/A";
751  app_log() << " " << std::right << std::setw(20) << "N/A";
752  app_log() << " " << std::right << std::setw(12) << "bad update";
753  }
754  }
755  else
756  {
757  app_log() << " " << std::right << std::setw(12) << "N/A";
758  app_log() << " " << std::right << std::setw(12) << "N/A";
759  app_log() << " " << std::right << std::setw(20) << "N/A";
760  app_log() << " " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << cv;
761  app_log() << " " << std::right << std::setw(12) << "initial";
762  }
763  if (ind == bi)
764  app_log() << " <--";
765  app_log() << std::endl;
766 }
767 
768 ///////////////////////////////////////////////////////////////////////////////////////////////////
769 /// \brief Returns whether the proposed new cost is the best compared to the others.
770 ///
771 /// \param[in] ii index of the proposed best cost
772 /// \param[in] cv vector of new costs
773 /// \param[in] sh vector of identity shifts (shift_i values)
774 /// \param[in] ic the initial cost
775 ///
776 ///////////////////////////////////////////////////////////////////////////////////////////////////
778  const std::vector<RealType>& cv,
779  const std::vector<double>& sh,
780  const RealType ic) const
781 {
782  //app_log() << "determining best cost with cost_increase_tol = " << cost_increase_tol << " and target_shift_i = " << target_shift_i << std::endl;
783 
784  // initialize return value
785  bool retval = true;
786 
787  //app_log() << "retval = " << retval << std::endl;
788 
789  // compare to other costs
790  for (int i = 0; i < cv.size(); i++)
791  {
792  // don't compare to yourself
793  if (i == ii)
794  continue;
795 
796  // we only worry about the other value if it is within the maximum relative change threshold and not too high
797  const bool other_is_valid = ((ic == 0.0 ? 0.0 : std::abs((cv.at(i) - ic) / ic)) < max_relative_cost_change &&
798  cv.at(i) < ic + cost_increase_tol);
799  if (other_is_valid)
800  {
801  // 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
802  if (target_shift_i > 0.0)
803  {
804  const bool closer_to_target = (std::abs(sh.at(ii) - target_shift_i) < std::abs(sh.at(i) - target_shift_i));
805  const bool cost_is_similar = (std::abs(cv.at(ii) - cv.at(i)) < cost_increase_tol);
806  const bool cost_is_much_lower = (!cost_is_similar && cv.at(ii) < cv.at(i) - cost_increase_tol);
807  if (cost_is_much_lower || (closer_to_target && cost_is_similar))
808  retval = (retval && true);
809  else
810  retval = false;
811 
812  // if we are not using a target shift, then prefer this cost if it is lower
813  }
814  else
815  {
816  retval = (retval && cv.at(ii) <= cv.at(i));
817  }
818  }
819 
820  //app_log() << "cv.at(ii) = " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << cv.at(ii) << " <= "
821  // << "cv.at(i) = " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << cv.at(i) << " ?" << std::endl;
822  //app_log() << "retval = " << retval << std::endl;
823  }
824 
825  // new cost can only be the best cost if it is less than (or not too much higher than) the initial cost
826  retval = (retval && cv.at(ii) < ic + cost_increase_tol);
827  //app_log() << "cv.at(ii) = " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << cv.at(ii) << " <= "
828  // << "ic = " << std::fixed << std::right << std::setw(20) << std::setprecision(12) << ic << " ?" << std::endl;
829  //app_log() << "retval = " << retval << std::endl;
830 
831  // 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 )
832  retval = (retval && (ic == 0.0 ? 0.0 : std::abs((cv.at(ii) - ic) / ic)) < max_relative_cost_change);
833  //app_log() << "std::abs( ( cv.at(ii) - ic ) / ic ) = " << std::fixed << std::right << std::setw(20) << std::setprecision(12)
834  // << std::abs( ( cv.at(ii) - ic ) / ic ) << " <= " << this->max_relative_cost_change << " ? " << std::endl;
835  //app_log() << "retval = " << retval << std::endl;
836  //app_log() << std::endl;
837 
838  // return whether the proposed cost is actually the best
839  return retval;
840 }
841 
842 ///////////////////////////////////////////////////////////////////////////////////////////////////
843 /// \brief For each set of shifts, solves the linear method eigenproblem by building and
844 /// diagonalizing the matrices.
845 ///
846 /// \param[in] shfits_i vector of identity shifts
847 /// \param[in] shfits_s vector of overlap shifts
848 /// \param[out] parameterDirections on exit, the update directions for the different shifts
849 ///
850 ///////////////////////////////////////////////////////////////////////////////////////////////////
851 void QMCFixedSampleLinearOptimize::solveShiftsWithoutLMYEngine(const std::vector<double>& shifts_i,
852  const std::vector<double>& shifts_s,
853  std::vector<std::vector<RealType>>& parameterDirections)
854 {
855  // get number of shifts to solve
856  const int nshifts = shifts_i.size();
857 
858  // get number of optimizable parameters
859  size_t numParams = optTarget->getNumParams();
860 
861  // get dimension of the linear method matrices
862  size_t N = numParams + 1;
863 
864  // prepare vectors to hold the parameter updates
865  parameterDirections.resize(nshifts);
866  for (int i = 0; i < parameterDirections.size(); i++)
867  parameterDirections.at(i).assign(N, 0.0);
868 
869  // allocate the matrices we will need
870  Matrix<RealType> ovlMat(N, N);
871  ovlMat = 0.0;
872  Matrix<RealType> hamMat(N, N);
873  hamMat = 0.0;
874  Matrix<RealType> invMat(N, N);
875  invMat = 0.0;
876  Matrix<RealType> sftMat(N, N);
877  sftMat = 0.0;
878  Matrix<RealType> prdMat(N, N);
879  prdMat = 0.0;
880 
881  // build the overlap and hamiltonian matrices
882  optTarget->fillOverlapHamiltonianMatrices(hamMat, ovlMat);
883 
884  // Output Hamiltonian and Overlap matrices
886  {
887  output_overlap_.output(ovlMat);
888  output_hamiltonian_.output(hamMat);
889  }
890 
891  // compute the inverse of the overlap matrix
892  invMat.copy(ovlMat);
893  invert_matrix(invMat, false);
894 
895  // compute the update for each shift
896  for (int shift_index = 0; shift_index < nshifts; shift_index++)
897  {
898  // prepare to shift the hamiltonain matrix
899  sftMat.copy(hamMat);
900 
901  // apply the identity shift
902  for (int i = 1; i < N; i++)
903  sftMat(i, i) += shifts_i.at(shift_index);
904 
905  // apply the overlap shift
906  for (int i = 1; i < N; i++)
907  for (int j = 1; j < N; j++)
908  sftMat(i, j) += shifts_s.at(shift_index) * ovlMat(i, j);
909 
910  // multiply the shifted hamiltonian matrix by the inverse of the overlap matrix
911  qmcplusplus::MatrixOperators::product(invMat, sftMat, prdMat);
912 
913  // transpose the result (why?)
914  for (int i = 0; i < N; i++)
915  for (int j = i + 1; j < N; j++)
916  std::swap(prdMat(i, j), prdMat(j, i));
917 
918  // compute the lowest eigenvalue of the product matrix and the corresponding eigenvector
919  getLowestEigenvector(prdMat, parameterDirections.at(shift_index));
920 
921  // compute the scaling constant to apply to the update
922  Lambda = getNonLinearRescale(parameterDirections.at(shift_index), ovlMat, *optTarget);
923 
924  // scale the update by the scaling constant
925  for (int i = 0; i < numParams; i++)
926  parameterDirections.at(shift_index).at(i + 1) *= Lambda;
927  }
928 }
929 
930 ///////////////////////////////////////////////////////////////////////////////////////////////////
931 /// \brief Performs one iteration of the linear method using an adaptive scheme that tries three
932 /// different shift magnitudes and picks the best one.
933 /// The scheme is adaptive in that it saves the best shift to use as a starting point
934 /// in the next iteration.
935 /// Note that the best shift is chosen based on a different sample than that used to
936 /// construct the linear method matrices in order to avoid over-optimizing on a particular
937 /// sample.
938 ///
939 /// \return ???
940 ///
941 ///////////////////////////////////////////////////////////////////////////////////////////////////
942 #ifdef HAVE_LMY_ENGINE
944 {
945  // remember what the cost function grads flag was
946  const bool saved_grads_flag = optTarget->getneedGrads();
947 
948  // remember the initial number of samples
949  const int init_num_samp = optTarget->getNumSamples();
950 
951  // the index of central shift
952  const int central_index = num_shifts / 2;
953 
954  // get number of optimizable parameters
955  size_t numParams = optTarget->getNumParams();
956 
957  // prepare the shifts that we will try
958  const std::vector<double> shifts_i = prepare_shifts(bestShift_i);
959  const std::vector<double> shifts_s = prepare_shifts(bestShift_s);
960  std::vector<double> shift_scales(shifts_i.size(), 1.0);
961  for (int i = 0; i < shift_scales.size(); i++)
962  shift_scales.at(i) = shifts_i.at(i) / shift_i_input;
963 
964  // ensure the cost function is set to compute derivative vectors
965  optTarget->setneedGrads(true);
966 
967  // prepare previous updates
968  int count = 0;
969  while (block_lm && previous_update.size() < nolds)
970  {
971  previous_update.push_back(formic::ColVec<double>(numParams));
972  for (int i = 0; i < numParams; i++)
973  previous_update.at(count).at(i) = 2.0 * (formic::random_number<double>() - 0.5);
974  count++;
975  }
976 
977  if (!EngineObj->full_init())
978  {
979  // prepare a variable dependency object with no dependencies
980  formic::VarDeps real_vdeps(numParams, std::vector<double>());
981  vdeps = real_vdeps;
982  EngineObj->get_param(&vdeps,
983  false, // exact sampling
984  !targetExcited,
985  false, // variable deps use?
986  false, // eom
987  false, // ssquare
988  block_lm, 12000, numParams, omega_shift, max_relative_cost_change, shifts_i.at(central_index),
989  shifts_s.at(central_index), max_param_change, shift_scales);
990  }
991 
992  // update shift
993  EngineObj->shift_update(shift_scales);
994 
995  // turn on wavefunction update mode
996  EngineObj->turn_on_update();
997 
998  // initialize the engine if we do not use block lm or it's the first part of block lm
999  EngineObj->initialize(nblocks, 0, nkept, previous_update, false);
1000 
1001  // reset the engine
1002  EngineObj->reset();
1003 
1004  // generate samples and compute weights, local energies, and derivative vectors
1005  engine_start(EngineObj, *descentEngineObj, MinMethod);
1006 
1007  // get dimension of the linear method matrices
1008  size_t N = numParams + 1;
1009 
1010  // have the cost function prepare derivative vectors
1011  EngineObj->energy_target_compute();
1012  const RealType starting_cost = EngineObj->target_value();
1013  const RealType init_energy = EngineObj->energy_mean();
1014 
1015  // print out the initial energy
1016  app_log() << std::endl
1017  << "*************************************************************************************************"
1018  << std::endl
1019  << "Solving the linear method equations on the initial sample with initial energy" << std::setw(20)
1020  << std::setprecision(12) << init_energy << std::endl
1021  << "*************************************************************************************************"
1022  << std::endl
1023  << std::endl;
1024  //const Return_t starting_cost = this->optTarget->LMYEngineCost(true);
1025 
1026  // prepare wavefunction update which does nothing if we do not use block lm
1027  EngineObj->wfn_update_prep();
1028 
1029  if (block_lm)
1030  {
1031  optTarget->setneedGrads(true);
1032 
1033  int numOptParams = optTarget->getNumParams();
1034 
1035  // reset the engine object
1036  EngineObj->reset();
1037 
1038  // finish last sample
1039  finish();
1040 
1041  // take sample
1042  engine_start(EngineObj, *descentEngineObj, MinMethod);
1043  }
1044 
1045  // say what we are doing
1046  app_log() << std::endl
1047  << "*********************************************************" << std::endl
1048  << "Solving the linear method equations on the initial sample" << std::endl
1049  << "*********************************************************" << std::endl
1050  << std::endl;
1051 
1052  // for each set of shifts, solve the linear method equations for the parameter update direction
1053  std::vector<std::vector<RealType>> parameterDirections;
1054 #ifdef HAVE_LMY_ENGINE
1055  // call the engine to perform update
1056  EngineObj->wfn_update_compute();
1057 #else
1058  solveShiftsWithoutLMYEngine(shifts_i, shifts_s, parameterDirections);
1059 #endif
1060 
1061  // size update direction vector correctly
1062  parameterDirections.resize(shifts_i.size());
1063  for (int i = 0; i < shifts_i.size(); i++)
1064  {
1065  parameterDirections.at(i).assign(N, 0.0);
1066  if (true)
1067  {
1068  for (int j = 0; j < N; j++)
1069  parameterDirections.at(i).at(j) = std::real(EngineObj->wfn_update().at(i * N + j));
1070  }
1071  else
1072  parameterDirections.at(i).at(0) = 1.0;
1073  }
1074 
1075  // now that we are done with them, prevent further computation of derivative vectors
1076  optTarget->setneedGrads(false);
1077 
1078  // prepare vectors to hold the initial and current parameters
1079  std::vector<RealType> currParams(numParams, 0.0);
1080 
1081  // initialize the initial and current parameter vectors
1082  for (int i = 0; i < numParams; i++)
1083  currParams.at(i) = optTarget->Params(i);
1084 
1085  // create a vector telling which updates are within our constraints
1086  std::vector<bool> good_update(parameterDirections.size(), true);
1087 
1088  // compute the largest parameter change for each shift, and zero out updates that have too-large changes
1089  std::vector<RealType> max_change(parameterDirections.size(), 0.0);
1090  for (int k = 0; k < parameterDirections.size(); k++)
1091  {
1092  for (int i = 0; i < numParams; i++)
1093  max_change.at(k) =
1094  std::max(max_change.at(k), std::abs(parameterDirections.at(k).at(i + 1) / parameterDirections.at(k).at(0)));
1095  good_update.at(k) = (good_update.at(k) && max_change.at(k) <= max_param_change);
1096  }
1097 
1098  // prepare to use the middle shift's update as the guiding function for a new sample
1099  for (int i = 0; i < numParams; i++)
1100  optTarget->Params(i) = currParams.at(i) + parameterDirections.at(central_index).at(i + 1);
1101 
1102  // say what we are doing
1103  app_log() << std::endl
1104  << "************************************************************" << std::endl
1105  << "Updating the guiding function with the middle shift's update" << std::endl
1106  << "************************************************************" << std::endl
1107  << std::endl;
1108 
1109  // generate the new sample on which we will compare the different shifts
1110 
1111  finish();
1112  app_log() << std::endl
1113  << "*************************************************************" << std::endl
1114  << "Generating a new sample based on the updated guiding function" << std::endl
1115  << "*************************************************************" << std::endl
1116  << std::endl;
1117 
1118  std::string old_name = vmcEngine->getCommunicator()->getName();
1119  vmcEngine->getCommunicator()->setName(old_name + ".middleShift");
1120  start();
1121  vmcEngine->getCommunicator()->setName(old_name);
1122 
1123  // say what we are doing
1124  app_log() << std::endl
1125  << "******************************************************************" << std::endl
1126  << "Comparing different shifts' cost function values on updated sample" << std::endl
1127  << "******************************************************************" << std::endl
1128  << std::endl;
1129 
1130  // update the current parameters to those of the new guiding function
1131  for (int i = 0; i < numParams; i++)
1132  currParams.at(i) = optTarget->Params(i);
1133 
1134  // compute cost function for the initial parameters (by subtracting the middle shift's update back off)
1135  for (int i = 0; i < numParams; i++)
1136  optTarget->Params(i) = currParams.at(i) - parameterDirections.at(central_index).at(i + 1);
1137  optTarget->IsValid = true;
1138  const RealType initCost = optTarget->LMYEngineCost(false, EngineObj);
1139 
1140  // compute the update directions for the smaller and larger shifts relative to that of the middle shift
1141  for (int i = 0; i < numParams; i++)
1142  {
1143  for (int j = 0; j < parameterDirections.size(); j++)
1144  {
1145  if (j != central_index)
1146  parameterDirections.at(j).at(i + 1) -= parameterDirections.at(central_index).at(i + 1);
1147  }
1148  }
1149 
1150  // prepare a vector to hold the cost function value for each different shift
1151  std::vector<RealType> costValues(num_shifts, 0.0);
1152 
1153  // compute the cost function value for each shift and make sure the change is within our constraints
1154  for (int k = 0; k < parameterDirections.size(); k++)
1155  {
1156  for (int i = 0; i < numParams; i++)
1157  optTarget->Params(i) = currParams.at(i) + (k == central_index ? 0.0 : parameterDirections.at(k).at(i + 1));
1158  optTarget->IsValid = true;
1159  costValues.at(k) = optTarget->LMYEngineCost(false, EngineObj);
1160  good_update.at(k) =
1161  (good_update.at(k) && std::abs((initCost - costValues.at(k)) / initCost) < max_relative_cost_change);
1162  if (!good_update.at(k))
1163  costValues.at(k) = std::abs(1.5 * initCost) + 1.0;
1164  }
1165 
1166  // find the best shift and the corresponding update direction
1167  const std::vector<RealType>* bestDirection = 0;
1168  int best_shift = -1;
1169  for (int k = 0; k < costValues.size() && std::abs((initCost - initCost) / initCost) < max_relative_cost_change; k++)
1170  if (is_best_cost(k, costValues, shifts_i, initCost) && good_update.at(k))
1171  {
1172  best_shift = k;
1173  bestDirection = &parameterDirections.at(k);
1174  }
1175 
1176  // print the results for each shift
1177  app_log() << std::endl;
1179  print_cost_summary(0.0, 0.0, 0.0, initCost, -1, best_shift, true);
1180  for (int k = 0; k < good_update.size(); k++)
1181  print_cost_summary(shifts_i.at(k), shifts_s.at(k), max_change.at(k), costValues.at(k), k, best_shift,
1182  good_update.at(k));
1183 
1184  // if any of the shifts produced a good update, apply the best such update and remember those shifts for next time
1185  if (bestDirection)
1186  {
1187  bestShift_i = shifts_i.at(best_shift);
1188  bestShift_s = shifts_s.at(best_shift);
1189  for (int i = 0; i < numParams; i++)
1190  optTarget->Params(i) = currParams.at(i) + (best_shift == central_index ? 0.0 : bestDirection->at(i + 1));
1191  app_log() << std::endl
1192  << "*****************************************************************************" << std::endl
1193  << "Applying the update for shift_i = " << std::scientific << std::right << std::setw(12)
1194  << std::setprecision(4) << bestShift_i << " and shift_s = " << std::scientific << std::right
1195  << std::setw(12) << std::setprecision(4) << bestShift_s << std::endl
1196  << "*****************************************************************************" << std::endl
1197  << std::endl;
1198 
1199  // otherwise revert to the old parameters and set the next shift to be larger
1200  }
1201  else
1202  {
1203  bestShift_i *= 10.0;
1204  bestShift_s *= 10.0;
1205  for (int i = 0; i < numParams; i++)
1206  optTarget->Params(i) = currParams.at(i) - parameterDirections.at(central_index).at(i + 1);
1207  app_log() << std::endl
1208  << "***********************************************************" << std::endl
1209  << "Reverting to old parameters and increasing shift magnitudes" << std::endl
1210  << "***********************************************************" << std::endl
1211  << std::endl;
1212  }
1213 
1214  // save the update for future linear method iterations
1215  if (block_lm && bestDirection)
1216  {
1217  // save the difference between the updated and old variables
1218  formic::ColVec<RealType> update_dirs(numParams, 0.0);
1219  for (int i = 0; i < numParams; i++)
1220  // take the real part since blocked LM currently does not support complex parameter optimization
1221  update_dirs.at(i) = std::real(bestDirection->at(i + 1) + parameterDirections.at(central_index).at(i + 1));
1222  previous_update.insert(previous_update.begin(), update_dirs);
1223 
1224  // eliminate the oldest saved update if necessary
1225  while (previous_update.size() > nolds)
1226  previous_update.pop_back();
1227  }
1228 
1229  // return the cost function grads flag to what it was
1230  optTarget->setneedGrads(saved_grads_flag);
1231 
1232  // perform some finishing touches for this linear method iteration
1233  finish();
1234 
1235  // set the number samples to be initial one
1236  optTarget->setNumSamples(init_num_samp);
1237  nTargetSamples = init_num_samp;
1238 
1239  //app_log() << "block first second third end " << block_first << block_second << block_third << endl;
1240  // return whether the cost function's report counter is positive
1241  return (optTarget->getReportCounter() > 0);
1242 }
1243 #endif
1244 
1246 {
1247  // ensure the cost function is set to compute derivative vectors
1248  optTarget->setneedGrads(true);
1249 
1250  // generate samples and compute weights, local energies, and derivative vectors
1251  start();
1252 
1253  // get number of optimizable parameters
1254  size_t numParams = optTarget->getNumParams();
1255 
1256  // get dimension of the linear method matrices
1257  size_t N = numParams + 1;
1258 
1259  // prepare vectors to hold the initial and current parameters
1260  std::vector<RealType> currentParameters(numParams, 0.0);
1261 
1262  // initialize the initial and current parameter vectors
1263  for (int i = 0; i < numParams; i++)
1264  currentParameters.at(i) = std::real(optTarget->Params(i));
1265 
1266  // prepare vectors to hold the parameter update directions for each shift
1267  std::vector<RealType> parameterDirections;
1268  parameterDirections.assign(N, 0.0);
1269 
1270  // compute the initial cost
1271  const RealType initCost = optTarget->computedCost();
1272 
1273  // say what we are doing
1274  app_log() << std::endl
1275  << "*****************************************" << std::endl
1276  << "Building overlap and Hamiltonian matrices" << std::endl
1277  << "*****************************************" << std::endl;
1278 
1279  // allocate the matrices we will need
1280  Matrix<RealType> ovlMat(N, N);
1281  ovlMat = 0.0;
1282  Matrix<RealType> hamMat(N, N);
1283  hamMat = 0.0;
1284  Matrix<RealType> invMat(N, N);
1285  invMat = 0.0;
1286  Matrix<RealType> prdMat(N, N);
1287  prdMat = 0.0;
1288 
1289  // build the overlap and hamiltonian matrices
1290  optTarget->fillOverlapHamiltonianMatrices(hamMat, ovlMat);
1291  invMat.copy(ovlMat);
1292 
1293  if (do_output_matrices_)
1294  {
1295  output_overlap_.output(ovlMat);
1296  output_hamiltonian_.output(hamMat);
1297  }
1298 
1299  // apply the identity shift
1300  for (int i = 1; i < N; i++)
1301  {
1302  hamMat(i, i) += bestShift_i;
1303  if (invMat(i, i) == 0)
1304  invMat(i, i) = bestShift_i * bestShift_s;
1305  }
1306 
1307  // compute the inverse of the overlap matrix
1308  {
1310  invert_matrix(invMat, false);
1311  }
1312 
1313  // apply the overlap shift
1314  for (int i = 1; i < N; i++)
1315  for (int j = 1; j < N; j++)
1316  hamMat(i, j) += bestShift_s * ovlMat(i, j);
1317 
1318  // multiply the shifted hamiltonian matrix by the inverse of the overlap matrix
1319  qmcplusplus::MatrixOperators::product(invMat, hamMat, prdMat);
1320 
1321  // transpose the result (why?)
1322  for (int i = 0; i < N; i++)
1323  for (int j = i + 1; j < N; j++)
1324  std::swap(prdMat(i, j), prdMat(j, i));
1325 
1326  // compute the lowest eigenvalue of the product matrix and the corresponding eigenvector
1327  {
1329  getLowestEigenvector(prdMat, parameterDirections);
1330  }
1331 
1332  // compute the scaling constant to apply to the update
1333  Lambda = getNonLinearRescale(parameterDirections, ovlMat, *optTarget);
1334 
1335  // scale the update by the scaling constant
1336  for (int i = 0; i < numParams; i++)
1337  parameterDirections.at(i + 1) *= Lambda;
1338 
1339  // now that we are done building the matrices, prevent further computation of derivative vectors
1340  optTarget->setneedGrads(false);
1341 
1342  // prepare to use the middle shift's update as the guiding function for a new sample
1343  if (!freeze_parameters_)
1344  {
1345  for (int i = 0; i < numParams; i++)
1346  optTarget->Params(i) = currentParameters.at(i) + parameterDirections.at(i + 1);
1347  }
1348 
1349  RealType largestChange(0);
1350  int max_element = 0;
1351  for (int i = 0; i < numParams; i++)
1352  if (std::abs(parameterDirections.at(i + 1)) > largestChange)
1353  {
1354  largestChange = std::abs(parameterDirections.at(i + 1));
1355  max_element = i;
1356  }
1357  app_log() << std::endl
1358  << "Among totally " << numParams << " optimized parameters, "
1359  << "largest LM parameter change : " << largestChange << " at parameter " << max_element << std::endl;
1360 
1361  // compute the new cost
1362  optTarget->IsValid = true;
1363  const RealType newCost = optTarget->Cost(false);
1364 
1365  app_log() << std::endl
1366  << "******************************************************************************" << std::endl
1367  << "Init Cost = " << std::scientific << std::right << std::setw(12) << std::setprecision(4) << initCost
1368  << " New Cost = " << std::scientific << std::right << std::setw(12) << std::setprecision(4) << newCost
1369  << " Delta Cost = " << std::scientific << std::right << std::setw(12) << std::setprecision(4)
1370  << newCost - initCost << std::endl
1371  << "******************************************************************************" << std::endl;
1372 
1373  if (!optTarget->IsValid || qmcplusplus::isnan(newCost))
1374  {
1375  app_log() << std::endl << "The new set of parameters is not valid. Revert to the old set!" << std::endl;
1376  for (int i = 0; i < numParams; i++)
1377  optTarget->Params(i) = currentParameters.at(i);
1379  if (accept_history[0] == true && accept_history[1] == false) // rejected the one before last and accepted the last
1380  {
1382  app_log() << "Update shift_s_base to " << shift_s_base << std::endl;
1383  }
1384  accept_history <<= 1;
1385  }
1386  else
1387  {
1388  if (bestShift_s > 1.0e-2)
1390  // say what we are doing
1391  app_log() << std::endl << "The new set of parameters is valid. Updating the trial wave function!" << std::endl;
1392  accept_history <<= 1;
1393  accept_history.set(0, true);
1394  }
1395 
1396  app_log() << std::endl
1397  << "*****************************************************************************" << std::endl
1398  << "Applying the update for shift_i = " << std::scientific << std::right << std::setw(12)
1399  << std::setprecision(4) << bestShift_i << " and shift_s = " << std::scientific << std::right
1400  << std::setw(12) << std::setprecision(4) << bestShift_s << std::endl
1401  << "*****************************************************************************" << std::endl;
1402 
1403  // perform some finishing touches for this linear method iteration
1404  finish();
1405 
1406  // return whether the cost function's report counter is positive
1407  return (optTarget->getReportCounter() > 0);
1408 }
1409 
1410 #ifdef HAVE_LMY_ENGINE
1411 //Function for optimizing using gradient descent
1413 {
1414  const bool saved_grads_flag = optTarget->getneedGrads();
1415 
1416  //Make sure needGrads is true before engine_checkConfigurations is called
1417  optTarget->setneedGrads(true);
1418 
1419  //Compute Lagrangian derivatives needed for parameter updates with engine_checkConfigurations, which is called inside engine_start
1420  engine_start(EngineObj, *descentEngineObj, MinMethod);
1421 
1422 
1423  int descent_num = descentEngineObj->getDescentNum();
1424 
1425  if (descent_num == 0)
1426  descentEngineObj->setupUpdate(optTarget->getOptVariables());
1427 
1428  //Store the derivatives and then compute parameter updates
1429  descentEngineObj->storeDerivRecord();
1430 
1431  descentEngineObj->updateParameters();
1432 
1433 
1434  std::vector<ValueType> results = descentEngineObj->retrieveNewParams();
1435 
1436 
1437  for (int i = 0; i < results.size(); i++)
1438  {
1439  optTarget->Params(i) = std::real(results[i]);
1440  }
1441 
1442  //If descent is being run as part of a hybrid optimization, need to check if a vector of
1443  //parameter differences should be stored.
1444  if (doHybrid)
1445  {
1446  int store_num = descentEngineObj->retrieveStoreFrequency();
1447  bool store = hybridEngineObj->queryStore(store_num, OptimizerType::DESCENT);
1448  if (store)
1449  {
1450  descentEngineObj->storeVectors(results);
1451  }
1452  }
1453 
1454 
1455  finish();
1456  return (optTarget->getReportCounter() > 0);
1457 }
1458 #endif
1459 
1460 
1461 //Function for controlling the alternation between sections of descent optimization and Blocked LM optimization.
1462 #ifdef HAVE_LMY_ENGINE
1463 bool QMCFixedSampleLinearOptimize::hybrid_run()
1464 {
1465  app_log() << "This is methodName: " << MinMethod << std::endl;
1466 
1467  //Either the adaptive BLM or descent optimization is run
1468 
1470  {
1471  //If the optimization just switched to using the BLM, need to transfer a set
1472  //of vectors to the BLM engine.
1474  {
1475  descentEngineObj->resetStorageCount();
1476  std::vector<std::vector<ValueType>> hybridBLM_Input = descentEngineObj->retrieveHybridBLM_Input();
1477 #if !defined(QMC_COMPLEX)
1478  //FIXME once complex is fixed in BLM engine
1479  EngineObj->setHybridBLM_Input(hybridBLM_Input);
1480 #endif
1481  }
1483 
1484  app_log() << "Update descent engine parameter values after Blocked LM step" << std::endl;
1485  for (int i = 0; i < optTarget->getNumParams(); i++)
1486  {
1487  RealType val = optTarget->Params(i);
1488  descentEngineObj->setParamVal(i, val);
1489  }
1490  }
1491 
1493  descent_run();
1494 
1495  app_log() << "Finished a hybrid step" << std::endl;
1496  return (optTarget->getReportCounter() > 0);
1497 }
1498 #endif
1499 
1501 {
1502  {
1503  //generate samples
1505  generateSamples();
1506  //store active number of walkers
1508  }
1509 
1510  app_log() << "<opt stage=\"setup\">" << std::endl;
1511  app_log() << " <log>" << std::endl;
1512  //reset the rootname
1513  optTarget->setRootName(RootName);
1514  optTarget->setWaveFunctionNode(wfNode);
1515  app_log() << " Reading configurations from h5FileRoot " << h5FileRoot << std::endl;
1516  {
1517  //get configuration from the previous run
1519  Timer t2;
1520  optTarget->getConfigurations(h5FileRoot);
1521  optTarget->setRng(vmcEngine->getRngRefs());
1522 
1523  // Compute wfn parameter derivatives
1524  NullEngineHandle handle;
1525  optTarget->checkConfigurations(handle);
1526 
1527  // check recomputed variance against VMC
1528  auto sigma2_vmc = vmcEngine->getBranchEngine()->vParam[SimpleFixedNodeBranch::SBVP::SIGMA2];
1529  auto sigma2_check = optTarget->getVariance();
1530  if (optTarget->getNumSamples() > 1 && (sigma2_check > 2.0 * sigma2_vmc || sigma2_check < 0.5 * sigma2_vmc))
1531  throw std::runtime_error(
1532  "Safeguard failure: checkConfigurations variance out of [0.5, 2.0] * reference! Please report this bug.\n");
1533  app_log() << " Execution time = " << std::setprecision(4) << t2.elapsed() << std::endl;
1534  }
1535  app_log() << " </log>" << std::endl;
1536  app_log() << "</opt>" << std::endl;
1537  app_log() << R"(<opt stage="main" walkers=")" << optTarget->getNumSamples() << "\">" << std::endl;
1538  app_log() << " <log>" << std::endl;
1539  t1.restart();
1540 }
1541 
1542 #ifdef HAVE_LMY_ENGINE
1543 void QMCFixedSampleLinearOptimize::engine_start(cqmc::engine::LMYEngine<ValueType>* EngineObj,
1544  DescentEngine& descentEngineObj,
1545  std::string MinMethod)
1546 {
1547  app_log() << "entering engine_start function" << std::endl;
1548 
1549  {
1550  //generate samples
1552  generateSamples();
1553  //store active number of walkers
1555  }
1556 
1557  app_log() << "<opt stage=\"setup\">" << std::endl;
1558  app_log() << " <log>" << std::endl;
1559 
1560  // reset the root name
1561  optTarget->setRootName(RootName);
1562  optTarget->setWaveFunctionNode(wfNode);
1563  app_log() << " Reading configurations from h5FileRoot " << h5FileRoot << std::endl;
1564  {
1565  // get configuration from the previous run
1567  Timer t2;
1568  optTarget->getConfigurations(h5FileRoot);
1569  optTarget->setRng(vmcEngine->getRngRefs());
1570  optTarget->engine_checkConfigurations(EngineObj, descentEngineObj,
1571  MinMethod); // computes derivative ratios and pass into engine
1572  app_log() << " Execution time = " << std::setprecision(4) << t2.elapsed() << std::endl;
1573  }
1574  app_log() << " </log>" << std::endl;
1575  app_log() << "</opt>" << std::endl;
1576  app_log() << R"(<opt stage="main" walkers=")" << optTarget->getNumSamples() << "\">" << std::endl;
1577  app_log() << " <log>" << std::endl;
1578  t1.restart();
1579 }
1580 #endif
1581 
1583 {
1584  MyCounter++;
1585  app_log() << " Execution time = " << std::setprecision(4) << t1.elapsed() << std::endl;
1586  app_log() << " </log>" << std::endl;
1587 
1588  if (optTarget->reportH5)
1589  optTarget->reportParametersH5();
1590  optTarget->reportParameters();
1591 
1592 
1593  int nw_removed = W.getActiveWalkers() - NumOfVMCWalkers;
1594  app_log() << " Restore the number of walkers to " << NumOfVMCWalkers << ", removing " << nw_removed << " walkers."
1595  << std::endl;
1596  if (nw_removed > 0)
1597  W.destroyWalkers(nw_removed);
1598  else
1599  W.createWalkers(-nw_removed);
1600  app_log() << "</opt>" << std::endl;
1601  app_log() << "</optimization-report>" << std::endl;
1602 }
1603 
1605 {
1606  app_log() << "<optimization-report>" << std::endl;
1607  vmcEngine->qmc_driver_mode.set(QMC_WARMUP, 1);
1608  // vmcEngine->run();
1609  // vmcEngine->setValue("blocks",nBlocks);
1610  // app_log() << " Execution time = " << std::setprecision(4) << t1.elapsed() << std::endl;
1611  // app_log() << "</vmc>" << std::endl;
1612  //}
1613  // if (W.getActiveWalkers()>NumOfVMCWalkers)
1614  // {
1615  // W.destroyWalkers(W.getActiveWalkers()-NumOfVMCWalkers);
1616  // app_log() << " QMCFixedSampleLinearOptimize::generateSamples removed walkers." << std::endl;
1617  // app_log() << " Number of Walkers per node " << W.getActiveWalkers() << std::endl;
1618  // }
1619  vmcEngine->qmc_driver_mode.set(QMC_OPTIMIZE, 1);
1620  vmcEngine->qmc_driver_mode.set(QMC_WARMUP, 0);
1621  //vmcEngine->setValue("recordWalkers",1);//set record
1622  vmcEngine->setValue("current", 0); //reset CurrentStep
1623  app_log() << R"(<vmc stage="main" blocks=")" << nBlocks << "\">" << std::endl;
1624  t1.restart();
1625  // W.reset();
1626  branchEngine->flush(0);
1627  branchEngine->reset();
1628  vmcEngine->run();
1629  app_log() << " Execution time = " << std::setprecision(4) << t1.elapsed() << std::endl;
1630  app_log() << "</vmc>" << std::endl;
1631  h5FileRoot = RootName;
1632 }
1633 
1634 } // namespace qmcplusplus
bool targetExcited
whether we are targeting an excited state
std::unique_ptr< QMCDriver > vmcEngine
vmc engine
MatrixA::value_type invert_matrix(MatrixA &M, bool getdet=true)
invert a matrix
std::string h5FileRoot
the root of h5File
Definition: QMCDriver.h:315
bool run() override
Run the Optimization algorithm.
double elapsed() const
Definition: Timer.h:30
void finish()
common operation to finish optimization, used by the derived classes
std::bitset< 2 > accept_history
accept history, remember the last 2 iterations, value 00, 01, 10, 11
void restart()
Definition: Timer.h:29
RealType max_param_change
max amount a parameter may change relative to current wave function weight
A set of walkers that are to be advanced by Metropolis Monte Carlo.
std::ostream & app_warning()
Definition: OutputManager.h:69
QMCTraits::RealType real
helper functions for EinsplineSetBuilder
Definition: Configuration.h:43
bool block_lm
whether we are doing block algorithm
int rank() const
return the rank
Definition: Communicate.h:116
const std::map< std::string, OptimizerType > OptimizerNames
QTBase::RealType RealType
Definition: Configuration.h:58
size_t getActiveWalkers() const
return the number of active walkers
std::unique_ptr< QMCCostFunctionBase > optTarget
target cost function to optimize
std::string RootName
root of all the output files
Definition: QMCDriver.h:317
MakeReturn< UnaryNode< FnFabs, typename CreateLeaf< Vector< T1, C1 > >::Leaf_t > >::Expression_t abs(const Vector< T1, C1 > &l)
std::ostream & app_log()
Definition: OutputManager.h:65
class ProjectData
Definition: ProjectData.h:36
bool put(xmlNodePtr cur)
assign attributes to the set
Definition: AttributeSet.h:55
RealType max_relative_cost_change
the maximum relative change in the cost function for the adaptive three-shift scheme ...
Collection of Local Energy Operators.
int nsamp_comp
number of samples to do in correlated sampling part
ScopeGuard< NewTimer > ScopedTimer
Definition: NewTimer.h:257
std::string block_lmStr
whether we are doing block algorithm
RealType bestShift_i
the previous best identity shift
abstract base class for QMC engines
Definition: QMCDriver.h:71
bool AppendRun
flag to append or restart the run
Definition: QMCDriver.h:224
Communicate * Controller
Global Communicator for a process.
Definition: Communicate.cpp:35
void copy(const This_t &rhs)
Definition: OhmmsMatrix.h:143
bool put(std::istream &is) override
read from std::istream
Definition: ParameterSet.h:42
iterator destroyWalkers(iterator first, iterator last)
destroy Walkers from itstart to itend
ParameterSet m_param
store any parameter that has to be read from a file
Definition: QMCDriver.h:320
RealType shift_i_input
current shift_i, shift_s input values
Wrapping information on parallelism.
Definition: Communicate.h:68
const std::string & get_root_name() const override
Definition: QMCDriver.h:378
omp_int_t omp_get_max_threads()
Definition: OpenMP.h:26
void product(const Matrix< T > &A, const Matrix< T > &B, Matrix< T > &C)
static function to perform C=AB for real matrices
std::bitset< QMC_MODE_MAX > qmc_driver_mode
bits to classify QMCDriver
Definition: QMCDriver.h:93
std::vector< double > prepare_shifts(const double central_shift) const
Real getLowestEigenvector(Matrix< Real > &A, std::vector< Real > &ev) const
Communicate * myComm
pointer to Communicate
Definition: MPIObjectBase.h:62
class to handle a set of attributes of an xmlNode
Definition: AttributeSet.h:24
std::unique_ptr< BranchEngineType > branchEngine
branch engine
Definition: QMCDriver.h:218
This a subclass for runtime errors that will occur on all ranks.
Tests for variational parameter derivatives.
std::string lowerCase(const std::string_view s)
++17
NewTimer & createGlobalTimer(const std::string &myname, timer_levels mylevel)
QMCHamiltonian & H
Hamiltonian.
Definition: QMCDriver.h:329
MakeReturn< UnaryNode< FnExp, typename CreateLeaf< Vector< T1, C1 > >::Leaf_t > >::Expression_t exp(const Vector< T1, C1 > &l)
MCWalkerConfiguration & W
walker ensemble
Definition: QMCDriver.h:323
static UPtrVector< RandomBase< FullPrecRealType > > Children
std::string getXMLAttributeValue(const xmlNodePtr cur, const std::string_view name)
get the value string for attribute name if name is unfound in cur you get an empty string back this i...
void start()
common operation to start optimization, used by the derived classes
void solveShiftsWithoutLMYEngine(const std::vector< double > &shifts_i, const std::vector< double > &shiffts_s, std::vector< std::vector< RealType >> &parameterDirections)
bool IsQMCDriver
true, if it is a real QMC engine
Definition: QMCDriver.h:228
void output(Matrix< RealType > &mat)
Print matrix to text-formatted scalar.dat file.
void add(PDT &aparam, const std::string &aname_in, std::vector< PDT > candidate_values={}, TagStatus status=TagStatus::OPTIONAL)
add a new parameter corresponding to an xmlNode <parameter>
MakeReturn< UnaryNode< FnSqrt, typename CreateLeaf< Vector< T1, C1 > >::Leaf_t > >::Expression_t sqrt(const Vector< T1, C1 > &l)
QMCTraits::RealType RealType
void readXML(xmlNodePtr xml_input)
RealType omega_shift
the shift to use when targeting an excited state
void processChildren(const xmlNodePtr cur, const F &functor)
process through all the children of an XML element F is a lambda or functor void F/[](const std::stri...
Definition: libxmldefs.h:175
bool processOptXML(xmlNodePtr cur, const std::string &vmcMove, bool reportH5)
process xml node value (parameters for both VMC and OPT) for the actual optimization ...
Class to represent a many-body trial wave function.
Definition: Grid.h:31
IndexType nTargetSamples
the number of saved samples
Definition: QMCDriver.h:289
RealType cost_increase_tol
the tolerance to cost function increases when choosing the best shift in the adaptive shift method ...
std::string targetExcitedStr
whether we are targeting an excited state
int nblocks
number of blocks used in block algorithm
TrialWaveFunction & Psi
trial function
Definition: QMCDriver.h:326
std::vector< xmlNodePtr > mcwalkerNodePtr
a list of mcwalkerset element
Definition: QMCDriver.h:344
RealType target_shift_i
the shift_i value that the adaptive shift method should aim for
void print_cost_summary(const double si, const double ss, const RealType mc, const RealType cv, const int ind, const int bi, const bool gu)
const ProjectData & project_data_
top-level project data information
Definition: QMCDriver.h:216
IndexType nBlocks
maximum number of blocks
Definition: QMCDriver.h:266
bool put(xmlNodePtr cur) override
preprocess xml node
Definition of QMCDriver which performs VMC and optimization.
void createWalkers(int numWalkers)
create numWalkers Walkers
void addWalkers(int nwalkers)
Add walkers to the end of the ensemble of walkers.
Definition: QMCDriver.cpp:338
void barrier_and_abort(const std::string &msg) const
int Max_iterations
Number of iterations maximum before generating new configurations.
void init_file(const std::string &root_name, const std::string &name, int N)
Open a text-formatted scalar.dat file and print the header line.
void add(PDT &aparam, const std::string &aname, std::vector< PDT > candidate_values={}, TagStatus status=TagStatus::OPTIONAL)
add a new attribute
Definition: AttributeSet.h:42
int MyCounter
the number of times this QMCDriver is executed
Definition: QMCDriver.h:235
RealType bestShift_s
the previous best overlap shift
QMCFixedSampleLinearOptimize(const ProjectData &project_data, MCWalkerConfiguration &w, TrialWaveFunction &psi, QMCHamiltonian &h, Communicate *)
Constructor.
Real getNonLinearRescale(std::vector< Real > &dP, Matrix< Real > &S, const QMCCostFunctionBase &optTarget) const
bool is_best_cost(const int ii, const std::vector< RealType > &cv, const std::vector< double > &sh, const RealType ic) const
bool isnan(float a)
return true if the value is NaN.
Definition: math.cpp:18