1 // See LICENSE file in top directory for details.
2 //
3 // Copyright (c) 2020 QMCPACK developers.
4 //
5 // File developed by: Bryan Clark, bclark@Princeton.edu, Princeton University
6 //                    Ken Esler, kpesler@gmail.com, University of Illinois at Urbana-Champaign
7 //                    Miguel Morales, moralessilva2@llnl.gov, Lawrence Livermore National Laboratory
8 //                    Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
9 //                    Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
10 //                    Raymond Clay III, j.k.rofling@gmail.com, 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 //                    Jeongnim Kim, jeongnim.kim@intel.com, Intel Corp.
14 //
15 // File created by: Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
16 //////////////////////////////////////////////////////////////////////////////////////
17 
18 #include "DiracDeterminant.h"
19 #include <stdexcept>
20 #include "CPU/BLAS.hpp"
21 #include "CPU/SIMD/simd.hpp"
22 #include "Numerics/DeterminantOperators.h"
23 #include "Numerics/MatrixOperators.h"
24 
25 namespace qmcplusplus
26 {
27 /** constructor
28  *@param spos the single-particle orbital set
29  *@param first index of the first particle
30  */
31 template<typename DU_TYPE>
DiracDeterminant(std::shared_ptr<SPOSet> && spos,int first)32 DiracDeterminant<DU_TYPE>::DiracDeterminant(std::shared_ptr<SPOSet>&& spos, int first)
33     : DiracDeterminantBase("DiracDeterminant", std::move(spos), first), ndelay(1), invRow_id(-1)
34 {}
35 
36 /** set the index of the first particle in the determinant and reset the size of the determinant
37  *@param first index of first particle
38  *@param nel number of particles in the determinant
39  */
40 template<typename DU_TYPE>
set(int first,int nel,int delay)41 void DiracDeterminant<DU_TYPE>::set(int first, int nel, int delay)
42 {
43   FirstIndex = first;
44   ndelay     = delay;
45 
46   resize(nel, nel);
47 
48   if (Optimizable)
49     Phi->buildOptVariables(nel);
50 
51   if (Phi->getOrbitalSetSize() < nel)
52   {
53     std::ostringstream err_msg;
54     err_msg << "The SPOSet " << Phi->getName() << " only has " << Phi->getOrbitalSetSize() << " orbitals "
55             << "but this determinant needs at least " << nel << std::endl;
56     throw std::runtime_error(err_msg.str());
57   }
58 }
59 
60 template<typename DU_TYPE>
invertPsiM(const ValueMatrix_t & logdetT,ValueMatrix_t & invMat)61 void DiracDeterminant<DU_TYPE>::invertPsiM(const ValueMatrix_t& logdetT, ValueMatrix_t& invMat)
62 {
63   ScopedTimer local_timer(InverseTimer);
64   updateEng.invert_transpose(logdetT, invMat, LogValue);
65 }
66 
67 
68 ///reset the size: with the number of particles and number of orbtials
69 template<typename DU_TYPE>
resize(int nel,int morb)70 void DiracDeterminant<DU_TYPE>::resize(int nel, int morb)
71 {
72   if (Bytes_in_WFBuffer > 0)
73     throw std::runtime_error("DiracDeterimnant just went out of sync with buffer");
74   int norb = morb;
75   if (norb <= 0)
76     norb = nel; // for morb == -1 (default)
77   updateEng.resize(norb, ndelay);
78   psiM.resize(nel, norb);
79   dpsiM.resize(nel, norb);
80   d2psiM.resize(nel, norb);
81   psiV.resize(norb);
82   invRow.resize(norb);
83   psiM_temp.resize(nel, norb);
84   LastIndex   = FirstIndex + nel;
85   NumPtcls    = nel;
86   NumOrbitals = norb;
87 
88   dpsiV.resize(NumOrbitals);
89   dspin_psiV.resize(NumOrbitals);
90   d2psiV.resize(NumOrbitals);
91   FirstAddressOfdV = &(dpsiM(0, 0)[0]); //(*dpsiM.begin())[0]);
92   LastAddressOfdV  = FirstAddressOfdV + NumPtcls * NumOrbitals * DIM;
93 }
94 
95 template<typename DU_TYPE>
evalGrad(ParticleSet & P,int iat)96 typename DiracDeterminant<DU_TYPE>::GradType DiracDeterminant<DU_TYPE>::evalGrad(ParticleSet& P, int iat)
97 {
98   ScopedTimer local_timer(RatioTimer);
99   const int WorkingIndex = iat - FirstIndex;
100   assert(WorkingIndex >= 0);
101   invRow_id = WorkingIndex;
102   updateEng.getInvRow(psiM, WorkingIndex, invRow);
103   GradType g = simd::dot(invRow.data(), dpsiM[WorkingIndex], invRow.size());
104   assert(checkG(g));
105   return g;
106 }
107 
108 template<typename DU_TYPE>
evalGradWithSpin(ParticleSet & P,int iat,ComplexType & spingrad)109 typename DiracDeterminant<DU_TYPE>::GradType DiracDeterminant<DU_TYPE>::evalGradWithSpin(ParticleSet& P,
110                                                                                          int iat,
111                                                                                          ComplexType& spingrad)
112 {
113   Phi->evaluate_spin(P, iat, psiV, dspin_psiV);
114   ScopedTimer local_timer(RatioTimer);
115   const int WorkingIndex = iat - FirstIndex;
116   assert(WorkingIndex >= 0);
117   invRow_id = WorkingIndex;
118   updateEng.getInvRow(psiM, WorkingIndex, invRow);
119   GradType g         = simd::dot(invRow.data(), dpsiM[WorkingIndex], invRow.size());
120   ComplexType spin_g = simd::dot(invRow.data(), dspin_psiV.data(), invRow.size());
121   spingrad += spin_g;
122 
123   return g;
124 }
125 
126 template<typename DU_TYPE>
ratioGrad(ParticleSet & P,int iat,GradType & grad_iat)127 typename DiracDeterminant<DU_TYPE>::PsiValueType DiracDeterminant<DU_TYPE>::ratioGrad(ParticleSet& P,
128                                                                                       int iat,
129                                                                                       GradType& grad_iat)
130 {
131   {
132     ScopedTimer local_timer(SPOVGLTimer);
133     Phi->evaluateVGL(P, iat, psiV, dpsiV, d2psiV);
134   }
135   return ratioGrad_compute(iat, grad_iat);
136 }
137 
138 template<typename DU_TYPE>
ratioGrad_compute(int iat,GradType & grad_iat)139 typename DiracDeterminant<DU_TYPE>::PsiValueType DiracDeterminant<DU_TYPE>::ratioGrad_compute(int iat,
140                                                                                               GradType& grad_iat)
141 {
142   ScopedTimer local_timer(RatioTimer);
143 
144   UpdateMode             = ORB_PBYP_PARTIAL;
145   const int WorkingIndex = iat - FirstIndex;
146   assert(WorkingIndex >= 0);
147   // This is an satefy mechanism.
148   // check invRow_id against WorkingIndex to see if getInvRow() has been called already
149   // when evalGrad has not been called already or the particle id is not consistent,
150   // invRow is recomputed.
151   if (invRow_id != WorkingIndex)
152   {
153     invRow_id = WorkingIndex;
154     updateEng.getInvRow(psiM, WorkingIndex, invRow);
155   }
156   curRatio = simd::dot(invRow.data(), psiV.data(), invRow.size());
157   grad_iat += static_cast<ValueType>(static_cast<PsiValueType>(1.0) / curRatio) *
158       simd::dot(invRow.data(), dpsiV.data(), invRow.size());
159   return curRatio;
160 }
161 
162 template<typename DU_TYPE>
ratioGradWithSpin(ParticleSet & P,int iat,GradType & grad_iat,ComplexType & spingrad_iat)163 typename DiracDeterminant<DU_TYPE>::PsiValueType DiracDeterminant<DU_TYPE>::ratioGradWithSpin(ParticleSet& P,
164                                                                                               int iat,
165                                                                                               GradType& grad_iat,
166                                                                                               ComplexType& spingrad_iat)
167 {
168   {
169     ScopedTimer local_timer(SPOVGLTimer);
170     Phi->evaluateVGL_spin(P, iat, psiV, dpsiV, d2psiV, dspin_psiV);
171   }
172 
173   {
174     ScopedTimer local_timer(RatioTimer);
175     UpdateMode             = ORB_PBYP_PARTIAL;
176     const int WorkingIndex = iat - FirstIndex;
177     assert(WorkingIndex >= 0);
178     // This is an optimization.
179     // check invRow_id against WorkingIndex to see if getInvRow() has been called already
180     // Some code paths call evalGrad before calling ratioGrad.
181     if (invRow_id != WorkingIndex)
182     {
183       invRow_id = WorkingIndex;
184       updateEng.getInvRow(psiM, WorkingIndex, invRow);
185     }
186     curRatio = simd::dot(invRow.data(), psiV.data(), invRow.size());
187     grad_iat += static_cast<ValueType>(static_cast<PsiValueType>(1.0) / curRatio) *
188         simd::dot(invRow.data(), dpsiV.data(), invRow.size());
189 
190     spingrad_iat += static_cast<ValueType>(static_cast<PsiValueType>(1.0) / curRatio) *
191         simd::dot(invRow.data(), dspin_psiV.data(), invRow.size());
192   }
193 
194   return curRatio;
195 }
196 
197 template<typename DU_TYPE>
mw_ratioGrad(const RefVectorWithLeader<WaveFunctionComponent> & wfc_list,const RefVectorWithLeader<ParticleSet> & p_list,int iat,std::vector<PsiValueType> & ratios,std::vector<GradType> & grad_new) const198 void DiracDeterminant<DU_TYPE>::mw_ratioGrad(const RefVectorWithLeader<WaveFunctionComponent>& wfc_list,
199                                              const RefVectorWithLeader<ParticleSet>& p_list,
200                                              int iat,
201                                              std::vector<PsiValueType>& ratios,
202                                              std::vector<GradType>& grad_new) const
203 {
204   {
205     ScopedTimer local_timer(SPOVGLTimer);
206     RefVectorWithLeader<SPOSet> phi_list(*Phi);
207     phi_list.reserve(wfc_list.size());
208     RefVector<ValueVector_t> psi_v_list;
209     psi_v_list.reserve(wfc_list.size());
210     RefVector<GradVector_t> dpsi_v_list;
211     dpsi_v_list.reserve(wfc_list.size());
212     RefVector<ValueVector_t> d2psi_v_list;
213     d2psi_v_list.reserve(wfc_list.size());
214 
215     for (WaveFunctionComponent& wfc : wfc_list)
216     {
217       auto& det = static_cast<DiracDeterminant<DU_TYPE>&>(wfc);
218       phi_list.push_back(*det.Phi);
219       psi_v_list.push_back(det.psiV);
220       dpsi_v_list.push_back(det.dpsiV);
221       d2psi_v_list.push_back(det.d2psiV);
222     }
223 
224     Phi->mw_evaluateVGL(phi_list, p_list, iat, psi_v_list, dpsi_v_list, d2psi_v_list);
225   }
226 
227   for (int iw = 0; iw < wfc_list.size(); iw++)
228     ratios[iw] = wfc_list.getCastedElement<DiracDeterminant<DU_TYPE>>(iw).ratioGrad_compute(iat, grad_new[iw]);
229 }
230 
231 
232 /** move was accepted, update the real container
233 */
234 template<typename DU_TYPE>
acceptMove(ParticleSet & P,int iat,bool safe_to_delay)235 void DiracDeterminant<DU_TYPE>::acceptMove(ParticleSet& P, int iat, bool safe_to_delay)
236 {
237   ScopedTimer local_timer(UpdateTimer);
238   const int WorkingIndex = iat - FirstIndex;
239   assert(WorkingIndex >= 0);
240   LogValue += convertValueToLog(curRatio);
241   updateEng.acceptRow(psiM, WorkingIndex, psiV, curRatio);
242   if (!safe_to_delay)
243     updateEng.updateInvMat(psiM);
244   // invRow becomes invalid after accepting a move
245   invRow_id = -1;
246   if (UpdateMode == ORB_PBYP_PARTIAL)
247   {
248     simd::copy(dpsiM[WorkingIndex], dpsiV.data(), NumOrbitals);
249     simd::copy(d2psiM[WorkingIndex], d2psiV.data(), NumOrbitals);
250   }
251   curRatio = 1.0;
252 }
253 
254 /** move was rejected. copy the real container to the temporary to move on
255 */
256 template<typename DU_TYPE>
restore(int iat)257 void DiracDeterminant<DU_TYPE>::restore(int iat)
258 {
259   curRatio = 1.0;
260 }
261 
262 template<typename DU_TYPE>
completeUpdates()263 void DiracDeterminant<DU_TYPE>::completeUpdates()
264 {
265   ScopedTimer local_timer(UpdateTimer);
266   // invRow becomes invalid after updating the inverse matrix
267   invRow_id = -1;
268   updateEng.updateInvMat(psiM);
269 }
270 
271 template<typename DU_TYPE>
updateAfterSweep(const ParticleSet & P,ParticleSet::ParticleGradient_t & G,ParticleSet::ParticleLaplacian_t & L)272 void DiracDeterminant<DU_TYPE>::updateAfterSweep(const ParticleSet& P,
273                                                  ParticleSet::ParticleGradient_t& G,
274                                                  ParticleSet::ParticleLaplacian_t& L)
275 {
276   if (UpdateMode == ORB_PBYP_RATIO)
277   { //need to compute dpsiM and d2psiM. Do not touch psiM!
278     ScopedTimer local_timer(SPOVGLTimer);
279     Phi->evaluate_notranspose(P, FirstIndex, LastIndex, psiM_temp, dpsiM, d2psiM);
280   }
281 
282   if (NumPtcls == 1)
283   {
284     ValueType y = psiM(0, 0);
285     GradType rv = y * dpsiM(0, 0);
286     G[FirstIndex] += rv;
287     L[FirstIndex] += y * d2psiM(0, 0) - dot(rv, rv);
288   }
289   else
290   {
291     for (size_t i = 0, iat = FirstIndex; i < NumPtcls; ++i, ++iat)
292     {
293       mValueType dot_temp = simd::dot(psiM[i], d2psiM[i], NumOrbitals);
294       mGradType rv        = simd::dot(psiM[i], dpsiM[i], NumOrbitals);
295       G[iat] += rv;
296       L[iat] += dot_temp - dot(rv, rv);
297     }
298   }
299 }
300 
301 template<typename DU_TYPE>
registerData(ParticleSet & P,WFBufferType & buf)302 void DiracDeterminant<DU_TYPE>::registerData(ParticleSet& P, WFBufferType& buf)
303 {
304   if (Bytes_in_WFBuffer == 0)
305   {
306     //add the data: inverse, gradient and laplacian
307     Bytes_in_WFBuffer = buf.current();
308     buf.add(psiM.first_address(), psiM.last_address());
309     buf.add(FirstAddressOfdV, LastAddressOfdV);
310     buf.add(d2psiM.first_address(), d2psiM.last_address());
311     Bytes_in_WFBuffer = buf.current() - Bytes_in_WFBuffer;
312     // free local space
313     psiM.free();
314     dpsiM.free();
315     d2psiM.free();
316   }
317   else
318   {
319     buf.forward(Bytes_in_WFBuffer);
320 #ifndef NDEBUG
321     // this causes too much output in the legacy code.
322     // \todo turn this back on after legacy is dropped,
323     // I don't think it should print at all in the new design
324     // std::cerr << ("You really should know whether you have registered this objects data previously!, consider this an error in the unified code");
325 #endif
326   }
327   buf.add(LogValue);
328 }
329 
330 template<typename DU_TYPE>
evaluateGL(const ParticleSet & P,ParticleSet::ParticleGradient_t & G,ParticleSet::ParticleLaplacian_t & L,bool fromscratch)331 typename DiracDeterminant<DU_TYPE>::LogValueType DiracDeterminant<DU_TYPE>::evaluateGL(
332     const ParticleSet& P,
333     ParticleSet::ParticleGradient_t& G,
334     ParticleSet::ParticleLaplacian_t& L,
335     bool fromscratch)
336 {
337   if (fromscratch)
338     evaluateLog(P, G, L);
339   else
340     updateAfterSweep(P, G, L);
341   return LogValue;
342 }
343 
344 template<typename DU_TYPE>
updateBuffer(ParticleSet & P,WFBufferType & buf,bool fromscratch)345 typename DiracDeterminant<DU_TYPE>::LogValueType DiracDeterminant<DU_TYPE>::updateBuffer(ParticleSet& P,
346                                                                                          WFBufferType& buf,
347                                                                                          bool fromscratch)
348 {
349   evaluateGL(P, P.G, P.L, fromscratch);
350   {
351     ScopedTimer local_timer(BufferTimer);
352     buf.forward(Bytes_in_WFBuffer);
353     buf.put(LogValue);
354   }
355   return LogValue;
356 }
357 
358 template<typename DU_TYPE>
copyFromBuffer(ParticleSet & P,WFBufferType & buf)359 void DiracDeterminant<DU_TYPE>::copyFromBuffer(ParticleSet& P, WFBufferType& buf)
360 {
361   ScopedTimer local_timer(BufferTimer);
362   psiM.attachReference(buf.lendReference<ValueType>(psiM.size()));
363   dpsiM.attachReference(buf.lendReference<GradType>(dpsiM.size()));
364   d2psiM.attachReference(buf.lendReference<ValueType>(d2psiM.size()));
365   buf.get(LogValue);
366   // start with invRow labelled invalid
367   invRow_id = -1;
368   updateEng.initializeInv(psiM);
369 }
370 
371 /** return the ratio only for the  iat-th partcle move
372  * @param P current configuration
373  * @param iat the particle thas is being moved
374  */
375 template<typename DU_TYPE>
ratio(ParticleSet & P,int iat)376 typename DiracDeterminant<DU_TYPE>::PsiValueType DiracDeterminant<DU_TYPE>::ratio(ParticleSet& P, int iat)
377 {
378   UpdateMode             = ORB_PBYP_RATIO;
379   const int WorkingIndex = iat - FirstIndex;
380   assert(WorkingIndex >= 0);
381   {
382     ScopedTimer local_timer(SPOVTimer);
383     Phi->evaluateValue(P, iat, psiV);
384   }
385   {
386     ScopedTimer local_timer(RatioTimer);
387     // This is an optimization.
388     // check invRow_id against WorkingIndex to see if getInvRow() has been called
389     // This is intended to save redundant compuation in TM1 and TM3
390     if (invRow_id != WorkingIndex)
391     {
392       invRow_id = WorkingIndex;
393       updateEng.getInvRow(psiM, WorkingIndex, invRow);
394     }
395     curRatio = simd::dot(invRow.data(), psiV.data(), invRow.size());
396   }
397   return curRatio;
398 }
399 
400 template<typename DU_TYPE>
evaluateRatios(const VirtualParticleSet & VP,std::vector<ValueType> & ratios)401 void DiracDeterminant<DU_TYPE>::evaluateRatios(const VirtualParticleSet& VP, std::vector<ValueType>& ratios)
402 {
403   {
404     ScopedTimer local_timer(RatioTimer);
405     const int WorkingIndex = VP.refPtcl - FirstIndex;
406     assert(WorkingIndex >= 0);
407     std::copy_n(psiM[WorkingIndex], invRow.size(), invRow.data());
408   }
409   {
410     ScopedTimer local_timer(SPOVTimer);
411     Phi->evaluateDetRatios(VP, psiV, invRow, ratios);
412   }
413 }
414 
415 template<typename DU_TYPE>
mw_evaluateRatios(const RefVectorWithLeader<WaveFunctionComponent> & wfc_list,const RefVector<const VirtualParticleSet> & vp_list,std::vector<std::vector<ValueType>> & ratios) const416 void DiracDeterminant<DU_TYPE>::mw_evaluateRatios(const RefVectorWithLeader<WaveFunctionComponent>& wfc_list,
417                                                   const RefVector<const VirtualParticleSet>& vp_list,
418                                                   std::vector<std::vector<ValueType>>& ratios) const
419 {
420   const size_t nw = wfc_list.size();
421 
422   RefVectorWithLeader<SPOSet> phi_list(*Phi);
423   RefVector<ValueVector_t> psiV_list;
424   std::vector<const ValueType*> invRow_ptr_list;
425   phi_list.reserve(nw);
426   psiV_list.reserve(nw);
427   invRow_ptr_list.reserve(nw);
428 
429   {
430     ScopedTimer local_timer(RatioTimer);
431     for (size_t iw = 0; iw < nw; iw++)
432     {
433       auto& det = wfc_list.getCastedElement<DiracDeterminant<DU_TYPE>>(iw);
434       const VirtualParticleSet& vp(vp_list[iw]);
435       const int WorkingIndex = vp.refPtcl - FirstIndex;
436       assert(WorkingIndex >= 0);
437       // If DiracDeterminant is in a valid state this copy_n is not necessary.
438       // That is at minimum a call to evaluateLog and ...
439       // std::copy_n(det.psiM[WorkingIndex], det.invRow.s.ize(), det.invRow.data());
440       // build lists
441       phi_list.push_back(*det.Phi);
442       psiV_list.push_back(det.psiV);
443       invRow_ptr_list.push_back(det.psiM[WorkingIndex]);
444     }
445   }
446 
447   {
448     ScopedTimer local_timer(SPOVTimer);
449     // Phi->isOMPoffload() requires device invRow pointers for mw_evaluateDetRatios.
450     // evaluateDetRatios only requires host invRow pointers.
451     if (Phi->isOMPoffload())
452       for (int iw = 0; iw < phi_list.size(); iw++)
453       {
454         Vector<ValueType> invRow(const_cast<ValueType*>(invRow_ptr_list[iw]), psiV_list[iw].get().size());
455         phi_list[iw].evaluateDetRatios(vp_list[iw], psiV_list[iw], invRow, ratios[iw]);
456       }
457     else
458       Phi->mw_evaluateDetRatios(phi_list, vp_list, psiV_list, invRow_ptr_list, ratios);
459   }
460 }
461 
462 template<typename DU_TYPE>
evaluateRatiosAlltoOne(ParticleSet & P,std::vector<ValueType> & ratios)463 void DiracDeterminant<DU_TYPE>::evaluateRatiosAlltoOne(ParticleSet& P, std::vector<ValueType>& ratios)
464 {
465   ScopedTimer local_timer(SPOVTimer);
466   Phi->evaluateValue(P, -1, psiV);
467   MatrixOperators::product(psiM, psiV.data(), &ratios[FirstIndex]);
468 }
469 
470 
471 template<typename DU_TYPE>
resizeScratchObjectsForIonDerivs()472 void DiracDeterminant<DU_TYPE>::resizeScratchObjectsForIonDerivs()
473 {
474   grad_source_psiM.resize(NumPtcls, NumOrbitals);
475   grad_lapl_source_psiM.resize(NumPtcls, NumOrbitals);
476   grad_grad_source_psiM.resize(NumPtcls, NumOrbitals);
477   phi_alpha_Minv.resize(NumPtcls, NumOrbitals);
478   grad_phi_Minv.resize(NumPtcls, NumOrbitals);
479   lapl_phi_Minv.resize(NumPtcls, NumOrbitals);
480   grad_phi_alpha_Minv.resize(NumPtcls, NumOrbitals);
481 }
482 
483 template<typename DU_TYPE>
evalGradSource(ParticleSet & P,ParticleSet & source,int iat)484 typename DiracDeterminant<DU_TYPE>::GradType DiracDeterminant<DU_TYPE>::evalGradSource(ParticleSet& P,
485                                                                                        ParticleSet& source,
486                                                                                        int iat)
487 {
488   GradType g(0.0);
489   if (Phi->hasIonDerivs())
490   {
491     resizeScratchObjectsForIonDerivs();
492     Phi->evaluateGradSource(P, FirstIndex, LastIndex, source, iat, grad_source_psiM);
493     g = simd::dot(psiM.data(), grad_source_psiM.data(), psiM.size());
494   }
495 
496   return g;
497 }
498 
499 template<typename DU_TYPE>
evaluateHessian(ParticleSet & P,HessVector_t & grad_grad_psi)500 void DiracDeterminant<DU_TYPE>::evaluateHessian(ParticleSet& P, HessVector_t& grad_grad_psi)
501 {
502   // Hessian is not often used, so only resize/allocate if used
503   grad_grad_source_psiM.resize(psiM.rows(), psiM.cols());
504   //IM A HACK.  Assumes evaluateLog has already been executed.
505   Phi->evaluate_notranspose(P, FirstIndex, LastIndex, psiM_temp, dpsiM, grad_grad_source_psiM);
506   invertPsiM(psiM_temp, psiM);
507 
508   phi_alpha_Minv      = 0.0;
509   grad_phi_Minv       = 0.0;
510   lapl_phi_Minv       = 0.0;
511   grad_phi_alpha_Minv = 0.0;
512   //grad_grad_psi.resize(NumPtcls);
513 
514   for (int i = 0, iat = FirstIndex; i < NumPtcls; i++, iat++)
515   {
516     GradType rv = simd::dot(psiM[i], dpsiM[i], NumOrbitals);
517     //  HessType hess_tmp=simd::dot(psiM[i],grad_grad_source_psiM[i],NumOrbitals);
518     HessType hess_tmp;
519     hess_tmp           = 0.0;
520     hess_tmp           = simd::dot(psiM[i], grad_grad_source_psiM[i], NumOrbitals);
521     grad_grad_psi[iat] = hess_tmp - outerProduct(rv, rv);
522   }
523 }
524 
525 template<typename DU_TYPE>
evalGradSource(ParticleSet & P,ParticleSet & source,int iat,TinyVector<ParticleSet::ParticleGradient_t,OHMMS_DIM> & grad_grad,TinyVector<ParticleSet::ParticleLaplacian_t,OHMMS_DIM> & lapl_grad)526 typename DiracDeterminant<DU_TYPE>::GradType DiracDeterminant<DU_TYPE>::evalGradSource(
527     ParticleSet& P,
528     ParticleSet& source,
529     int iat,
530     TinyVector<ParticleSet::ParticleGradient_t, OHMMS_DIM>& grad_grad,
531     TinyVector<ParticleSet::ParticleLaplacian_t, OHMMS_DIM>& lapl_grad)
532 {
533   GradType gradPsi(0.0);
534   if (Phi->hasIonDerivs())
535   {
536     resizeScratchObjectsForIonDerivs();
537     Phi->evaluateGradSource(P, FirstIndex, LastIndex, source, iat, grad_source_psiM, grad_grad_source_psiM,
538                             grad_lapl_source_psiM);
539     // HACK HACK HACK
540     // Phi->evaluateVGL(P, FirstIndex, LastIndex, psiM, dpsiM, d2psiM);
541     // psiM_temp = psiM;
542     // LogValue=InvertWithLog(psiM.data(),NumPtcls,NumOrbitals,
543     // 			   WorkSpace.data(),Pivot.data(),PhaseValue);
544     // for (int i=0; i<NumPtcls; i++)
545     //   for (int j=0; j<NumPtcls; j++) {
546     // 	double val = 0.0;
547     // 	for (int k=0; k<NumPtcls; k++)
548     // 	  val += psiM(i,k) * psiM_temp(k,j);
549     // 	val -= (i == j) ? 1.0 : 0.0;
550     // 	if (std::abs(val) > 1.0e-12)
551     // 	  std::cerr << "Error in inverse.\n";
552     //   }
553     // for (int i=0; i<NumPtcls; i++) {
554     //   P.G[FirstIndex+i] = GradType();
555     //   for (int j=0; j<NumOrbitals; j++)
556     // 	P.G[FirstIndex+i] += psiM(i,j)*dpsiM(i,j);
557     // }
558     // Compute matrices
559     phi_alpha_Minv      = 0.0;
560     grad_phi_Minv       = 0.0;
561     lapl_phi_Minv       = 0.0;
562     grad_phi_alpha_Minv = 0.0;
563     for (int i = 0; i < NumPtcls; i++)
564       for (int j = 0; j < NumOrbitals; j++)
565       {
566         lapl_phi_Minv(i, j) = 0.0;
567         for (int k = 0; k < NumOrbitals; k++)
568           lapl_phi_Minv(i, j) += d2psiM(i, k) * psiM(j, k);
569       }
570     for (int dim = 0; dim < OHMMS_DIM; dim++)
571     {
572       for (int i = 0; i < NumPtcls; i++)
573         for (int j = 0; j < NumOrbitals; j++)
574         {
575           for (int k = 0; k < NumOrbitals; k++)
576           {
577             phi_alpha_Minv(i, j)[dim] += grad_source_psiM(i, k)[dim] * psiM(j, k);
578             grad_phi_Minv(i, j)[dim] += dpsiM(i, k)[dim] * psiM(j, k);
579             for (int dim_el = 0; dim_el < OHMMS_DIM; dim_el++)
580               grad_phi_alpha_Minv(i, j)(dim, dim_el) += grad_grad_source_psiM(i, k)(dim, dim_el) * psiM(j, k);
581           }
582         }
583     }
584     for (int i = 0, iel = FirstIndex; i < NumPtcls; i++, iel++)
585     {
586       HessType dval(0.0);
587       GradType d2val(0.0);
588       for (int dim = 0; dim < OHMMS_DIM; dim++)
589         for (int dim_el = 0; dim_el < OHMMS_DIM; dim_el++)
590           dval(dim, dim_el) = grad_phi_alpha_Minv(i, i)(dim, dim_el);
591       for (int j = 0; j < NumOrbitals; j++)
592       {
593         gradPsi += grad_source_psiM(i, j) * psiM(i, j);
594         for (int dim = 0; dim < OHMMS_DIM; dim++)
595           for (int k = 0; k < OHMMS_DIM; k++)
596             dval(dim, k) -= phi_alpha_Minv(j, i)[dim] * grad_phi_Minv(i, j)[k];
597       }
598       for (int dim = 0; dim < OHMMS_DIM; dim++)
599       {
600         for (int k = 0; k < OHMMS_DIM; k++)
601           grad_grad[dim][iel][k] += dval(dim, k);
602         for (int j = 0; j < NumOrbitals; j++)
603         {
604           // First term, eq 9
605           lapl_grad[dim][iel] += grad_lapl_source_psiM(i, j)[dim] * psiM(i, j);
606           // Second term, eq 9
607           if (j == i)
608             for (int dim_el = 0; dim_el < OHMMS_DIM; dim_el++)
609               lapl_grad[dim][iel] -=
610                   (RealType)2.0 * grad_phi_alpha_Minv(j, i)(dim, dim_el) * grad_phi_Minv(i, j)[dim_el];
611           // Third term, eq 9
612           // First term, eq 10
613           lapl_grad[dim][iel] -= phi_alpha_Minv(j, i)[dim] * lapl_phi_Minv(i, j);
614           // Second term, eq 11
615           for (int dim_el = 0; dim_el < OHMMS_DIM; dim_el++)
616             lapl_grad[dim][iel] +=
617                 (RealType)2.0 * phi_alpha_Minv(j, i)[dim] * grad_phi_Minv(i, i)[dim_el] * grad_phi_Minv(i, j)[dim_el];
618         }
619       }
620     }
621   }
622   return gradPsi;
623 }
624 
625 
626 /** Calculate the log value of the Dirac determinant for particles
627  *@param P input configuration containing N particles
628  *@param G a vector containing N gradients
629  *@param L a vector containing N laplacians
630  *@return the value of the determinant
631  *
632  *\f$ (first,first+nel). \f$  Add the gradient and laplacian
633  *contribution of the determinant to G(radient) and L(aplacian)
634  *for local energy calculations.
635  */
636 template<typename DU_TYPE>
evaluateLog(const ParticleSet & P,ParticleSet::ParticleGradient_t & G,ParticleSet::ParticleLaplacian_t & L)637 typename DiracDeterminant<DU_TYPE>::LogValueType DiracDeterminant<DU_TYPE>::evaluateLog(
638     const ParticleSet& P,
639     ParticleSet::ParticleGradient_t& G,
640     ParticleSet::ParticleLaplacian_t& L)
641 {
642   recompute(P);
643 
644   if (NumPtcls == 1)
645   {
646     ValueType y = psiM(0, 0);
647     GradType rv = y * dpsiM(0, 0);
648     G[FirstIndex] += rv;
649     L[FirstIndex] += y * d2psiM(0, 0) - dot(rv, rv);
650   }
651   else
652   {
653     for (int i = 0, iat = FirstIndex; i < NumPtcls; i++, iat++)
654     {
655       mGradType rv   = simd::dot(psiM[i], dpsiM[i], NumOrbitals);
656       mValueType lap = simd::dot(psiM[i], d2psiM[i], NumOrbitals);
657       G[iat] += rv;
658       L[iat] += lap - dot(rv, rv);
659     }
660   }
661   return LogValue;
662 }
663 
664 template<typename DU_TYPE>
recompute(const ParticleSet & P)665 void DiracDeterminant<DU_TYPE>::recompute(const ParticleSet& P)
666 {
667   {
668     ScopedTimer local_timer(SPOVGLTimer);
669     Phi->evaluate_notranspose(P, FirstIndex, LastIndex, psiM_temp, dpsiM, d2psiM);
670   }
671   if (NumPtcls == 1)
672   {
673     ValueType det = psiM_temp(0, 0);
674     psiM(0, 0)    = RealType(1) / det;
675     LogValue      = convertValueToLog(det);
676   }
677   else
678     invertPsiM(psiM_temp, psiM);
679 
680   // invRow becomes invalid after updating the inverse matrix
681   invRow_id = -1;
682 }
683 
684 template<typename DU_TYPE>
evaluateDerivatives(ParticleSet & P,const opt_variables_type & active,std::vector<ValueType> & dlogpsi,std::vector<ValueType> & dhpsioverpsi)685 void DiracDeterminant<DU_TYPE>::evaluateDerivatives(ParticleSet& P,
686                                                     const opt_variables_type& active,
687                                                     std::vector<ValueType>& dlogpsi,
688                                                     std::vector<ValueType>& dhpsioverpsi)
689 {
690   Phi->evaluateDerivatives(P, active, dlogpsi, dhpsioverpsi, FirstIndex, LastIndex);
691 }
692 
693 template<typename DU_TYPE>
makeCopy(std::shared_ptr<SPOSet> && spo) const694 DiracDeterminant<DU_TYPE>* DiracDeterminant<DU_TYPE>::makeCopy(std::shared_ptr<SPOSet>&& spo) const
695 {
696   DiracDeterminant<DU_TYPE>* dclone = new DiracDeterminant<DU_TYPE>(std::move(spo));
697   dclone->set(FirstIndex, LastIndex - FirstIndex, ndelay);
698   return dclone;
699 }
700 
701 template<typename DU_TYPE>
createResource(ResourceCollection & collection) const702 void DiracDeterminant<DU_TYPE>::createResource(ResourceCollection& collection) const
703 {
704   Phi->createResource(collection);
705 }
706 
707 template<typename DU_TYPE>
acquireResource(ResourceCollection & collection)708 void DiracDeterminant<DU_TYPE>::acquireResource(ResourceCollection& collection)
709 {
710   Phi->acquireResource(collection);
711 }
712 
713 template<typename DU_TYPE>
releaseResource(ResourceCollection & collection)714 void DiracDeterminant<DU_TYPE>::releaseResource(ResourceCollection& collection)
715 {
716   Phi->releaseResource(collection);
717 }
718 
719 template class DiracDeterminant<>;
720 #if defined(ENABLE_CUDA)
721 template class DiracDeterminant<DelayedUpdateCUDA<QMCTraits::ValueType, QMCTraits::QTFull::ValueType>>;
722 #endif
723 
724 } // namespace qmcplusplus
725