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 = ¶meterDirections.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