CheMPS2
DMRG.cpp
1 /*
2  CheMPS2: a spin-adapted implementation of DMRG for ab initio quantum chemistry
3  Copyright (C) 2013-2016 Sebastian Wouters
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License along
16  with this program; if not, write to the Free Software Foundation, Inc.,
17  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #include <math.h>
21 #include <stdlib.h>
22 #include <iostream>
23 #include <string.h>
24 #include <sstream>
25 #include <sys/stat.h>
26 #include <sys/time.h>
27 #include <assert.h>
28 #include <unistd.h>
29 
30 #include "DMRG.h"
31 #include "MPIchemps2.h"
32 
33 using std::cout;
34 using std::endl;
35 
36 CheMPS2::DMRG::DMRG( Problem * ProbIn, ConvergenceScheme * OptSchemeIn, const bool makechkpt, const string tmpfolder ){
37 
38  #ifdef CHEMPS2_MPI_COMPILATION
39  if ( MPIchemps2::mpi_rank() == MPI_CHEMPS2_MASTER ){ PrintLicense(); }
40  #else
41  PrintLicense();
42  #endif
43 
44  assert( ProbIn->checkConsistency() );
45  Prob = ProbIn;
46  L = Prob->gL();
47  Prob->construct_mxelem();
48  OptScheme = OptSchemeIn;
49  thePID = getpid(); //PID is unique for each MPI process
50  nStates = 1;
51 
52  Ltensors = new TensorL ** [ L - 1 ];
53  F0tensors = new TensorF0 *** [ L - 1 ];
54  F1tensors = new TensorF1 *** [ L - 1 ];
55  S0tensors = new TensorS0 *** [ L - 1 ];
56  S1tensors = new TensorS1 *** [ L - 1 ];
57  Atensors = new TensorOperator *** [ L - 1 ];
58  Btensors = new TensorOperator *** [ L - 1 ];
59  Ctensors = new TensorOperator *** [ L - 1 ];
60  Dtensors = new TensorOperator *** [ L - 1 ];
61  Qtensors = new TensorQ ** [ L - 1 ];
62  Xtensors = new TensorX * [ L - 1 ];
63  isAllocated = new int[ L - 1 ]; // 0 not allocated; 1 moving right; 2 moving left
64 
65  tensor_3rdm_a_J0_doublet = NULL;
66  tensor_3rdm_a_J1_doublet = NULL;
67  tensor_3rdm_a_J1_quartet = NULL;
68  tensor_3rdm_b_J0_doublet = NULL;
69  tensor_3rdm_b_J1_doublet = NULL;
70  tensor_3rdm_b_J1_quartet = NULL;
71  tensor_3rdm_c_J0_doublet = NULL;
72  tensor_3rdm_c_J1_doublet = NULL;
73  tensor_3rdm_c_J1_quartet = NULL;
74  tensor_3rdm_d_J0_doublet = NULL;
75  tensor_3rdm_d_J1_doublet = NULL;
76  tensor_3rdm_d_J1_quartet = NULL;
77 
78  Gtensors = NULL;
79  Ytensors = NULL;
80  Ztensors = NULL;
81  Ktensors = NULL;
82  Mtensors = NULL;
83 
84  for ( int cnt = 0; cnt < L - 1; cnt++ ){ isAllocated[ cnt ] = 0; }
85  for ( int timecnt = 0; timecnt < CHEMPS2_TIME_VECLENGTH; timecnt++ ){ timings[ timecnt ] = 0.0; }
86  num_double_write_disk = 0;
87  num_double_read_disk = 0;
88 
89  the2DM = NULL;
90  the3DM = NULL;
91  theCorr = NULL;
92  Exc_activated = false;
93  makecheckpoints = makechkpt;
94  tempfolder = tmpfolder;
95 
96  setupBookkeeperAndMPS();
97  PreSolve();
98 
99 }
100 
101 void CheMPS2::DMRG::setupBookkeeperAndMPS(){
102 
103  denBK = new SyBookkeeper( Prob, OptScheme->get_D( 0 ) );
104  assert( denBK->IsPossible() );
105 
106  std::stringstream sstream;
107  sstream << CheMPS2::DMRG_MPS_storage_prefix << nStates-1 << ".h5";
108  MPSstoragename.assign( sstream.str() );
109  struct stat stFileInfo;
110  int intStat = stat( MPSstoragename.c_str(), &stFileInfo );
111  loadedMPS = (( makecheckpoints ) && ( intStat==0 )) ? true : false;
112  #ifdef CHEMPS2_MPI_COMPILATION
113  assert( MPIchemps2::all_booleans_equal( loadedMPS ) );
114  #endif
115 
116  if ( loadedMPS ){ loadDIM( MPSstoragename, denBK ); }
117 
118  MPS = new TensorT * [ L ];
119  for ( int cnt = 0; cnt < L; cnt++ ){ MPS[ cnt ] = new TensorT( cnt, denBK ); }
120 
121  if ( loadedMPS ){
122  bool isConverged;
123  loadMPS( MPSstoragename, MPS, &isConverged );
124  #ifdef CHEMPS2_MPI_COMPILATION
125  if ( MPIchemps2::mpi_rank() == MPI_CHEMPS2_MASTER )
126  #endif
127  { cout << "Loaded MPS " << MPSstoragename << " converged y/n? : " << isConverged << endl; }
128  } else {
129  #ifdef CHEMPS2_MPI_COMPILATION
130  const bool am_i_master = ( MPIchemps2::mpi_rank() == MPI_CHEMPS2_MASTER );
131  #else
132  const bool am_i_master = true;
133  #endif
134  for ( int site = 0; site < L; site++ ){
135  if ( am_i_master ){ MPS[ site ]->random(); }
136  left_normalize( MPS[ site ], NULL );
137  }
138  }
139 
140 }
141 
143 
144  if ( the2DM != NULL ){ delete the2DM; }
145  if ( the3DM != NULL ){ delete the3DM; }
146  if ( theCorr != NULL ){ delete theCorr; }
147 
148  deleteAllBoundaryOperators();
149 
150  delete [] Ltensors;
151  delete [] F0tensors;
152  delete [] F1tensors;
153  delete [] S0tensors;
154  delete [] S1tensors;
155  delete [] Atensors;
156  delete [] Btensors;
157  delete [] Ctensors;
158  delete [] Dtensors;
159  delete [] Qtensors;
160  delete [] Xtensors;
161  delete [] isAllocated;
162 
163  for ( int site = 0; site < L; site++ ){ delete MPS[ site ]; }
164  delete [] MPS;
165 
166  if ( Exc_activated ){
167  delete [] Exc_Eshifts;
168  for ( int state = 0; state < nStates - 1; state++ ){
169  #ifdef CHEMPS2_MPI_COMPILATION
170  if ( MPIchemps2::owner_specific_excitation( L, state ) == MPIchemps2::mpi_rank() )
171  #endif
172  {
173  for ( int orb = 0; orb < L; orb++ ){ delete Exc_MPSs[ state ][ orb ]; }
174  delete [] Exc_MPSs[ state ];
175  delete Exc_BKs[ state ];
176  delete [] Exc_Overlaps[ state ]; // The rest is allocated and deleted at DMRGoperators.cpp
177  }
178  }
179  delete [] Exc_MPSs;
180  delete [] Exc_BKs;
181  delete [] Exc_Overlaps;
182  }
183 
184  delete denBK;
185 
186 }
187 
189 
190  deleteAllBoundaryOperators();
191 
192  for ( int cnt = 0; cnt < L - 2; cnt++ ){ updateMovingRightSafeFirstTime( cnt ); }
193 
194  TotalMinEnergy = 1e8;
195  MaxDiscWeightLastSweep = 0.0;
196 
197 }
198 
200 
201  bool change = ( TotalMinEnergy < 1e8 ) ? true : false; // 1 sweep from right to left: fixed virtual dimensions
202 
203  double Energy = 0.0;
204 
205  #ifdef CHEMPS2_MPI_COMPILATION
206  const bool am_i_master = ( MPIchemps2::mpi_rank() == MPI_CHEMPS2_MASTER );
207  #else
208  const bool am_i_master = true;
209  #endif
210 
211  for ( int instruction = 0; instruction < OptScheme->get_number(); instruction++ ){
212 
213  int nIterations = 0;
214  double EnergyPrevious = Energy + 10 * OptScheme->get_energy_conv( instruction ); // Guarantees that there's always at least 1 left-right sweep
215 
216  while (( fabs( Energy - EnergyPrevious ) > OptScheme->get_energy_conv( instruction ) ) && ( nIterations < OptScheme->get_max_sweeps( instruction ) )){
217 
218  for ( int timecnt = 0; timecnt < CHEMPS2_TIME_VECLENGTH; timecnt++ ){ timings[ timecnt ] = 0.0; }
219  num_double_write_disk = 0;
220  num_double_read_disk = 0;
221  struct timeval start, end;
222  EnergyPrevious = Energy;
223  gettimeofday( &start, NULL );
224  Energy = sweepleft( change, instruction, am_i_master ); // Only relevant call in this block of code
225  gettimeofday( &end, NULL );
226  double elapsed = ( end.tv_sec - start.tv_sec ) + 1e-6 * ( end.tv_usec - start.tv_usec );
227  if ( am_i_master ){
228  cout << "******************************************************************" << endl;
229  cout << "*** Information on left sweep " << nIterations << " of instruction " << instruction << ":" << endl;
230  cout << "*** Elapsed wall time = " << elapsed << " seconds" << endl;
231  cout << "*** |--> S.join = " << timings[ CHEMPS2_TIME_S_JOIN ] << " seconds" << endl;
232  cout << "*** |--> S.solve = " << timings[ CHEMPS2_TIME_S_SOLVE ] << " seconds" << endl;
233  cout << "*** |--> S.split = " << timings[ CHEMPS2_TIME_S_SPLIT ] << " seconds" << endl;
234  print_tensor_update_performance();
235  cout << "*** Minimum energy = " << LastMinEnergy << endl;
236  cout << "*** Maximum discarded weight = " << MaxDiscWeightLastSweep << endl;
237  }
238  if ( Exc_activated ){ calc_overlaps( false ); }
239  if ( am_i_master ){
240  cout << "******************************************************************" << endl;
241  }
242  change = true; //rest of sweeps: variable virtual dimensions
243  for ( int timecnt = 0; timecnt < CHEMPS2_TIME_VECLENGTH; timecnt++ ){ timings[ timecnt ] = 0.0; }
244  num_double_write_disk = 0;
245  num_double_read_disk = 0;
246  gettimeofday( &start, NULL );
247  Energy = sweepright( change, instruction, am_i_master ); // Only relevant call in this block of code
248  gettimeofday( &end, NULL );
249  elapsed = ( end.tv_sec - start.tv_sec ) + 1e-6 * ( end.tv_usec - start.tv_usec );
250  if ( am_i_master ){
251  cout << "******************************************************************" << endl;
252  cout << "*** Information on right sweep " << nIterations << " of instruction " << instruction << ":" << endl;
253  cout << "*** Elapsed wall time = " << elapsed << " seconds" << endl;
254  cout << "*** |--> S.join = " << timings[ CHEMPS2_TIME_S_JOIN ] << " seconds" << endl;
255  cout << "*** |--> S.solve = " << timings[ CHEMPS2_TIME_S_SOLVE ] << " seconds" << endl;
256  cout << "*** |--> S.split = " << timings[ CHEMPS2_TIME_S_SPLIT ] << " seconds" << endl;
257  print_tensor_update_performance();
258  cout << "*** Minimum energy = " << LastMinEnergy << endl;
259  cout << "*** Maximum discarded weight = " << MaxDiscWeightLastSweep << endl;
260  cout << "*** Energy difference with respect to previous leftright sweep = " << fabs(Energy-EnergyPrevious) << endl;
261  }
262  if ( Exc_activated ){ calc_overlaps( true ); }
263  if ( am_i_master ){
264  cout << "******************************************************************" << endl;
265  if ( makecheckpoints ){ saveMPS( MPSstoragename, MPS, denBK, false ); } // Only the master proc makes MPS checkpoints !!
266  }
267 
268  nIterations++;
269 
270  }
271 
272  if ( am_i_master ){
273  cout << "*** Information on completed instruction " << instruction << ":" << endl;
274  cout << "*** The reduced virtual dimension DSU(2) = " << OptScheme->get_D(instruction) << endl;
275  cout << "*** Minimum energy encountered during all instructions = " << TotalMinEnergy << endl;
276  cout << "*** Minimum energy encountered during the last sweep = " << LastMinEnergy << endl;
277  cout << "*** Maximum discarded weight during the last sweep = " << MaxDiscWeightLastSweep << endl;
278  cout << "******************************************************************" << endl;
279  }
280 
281  }
282 
283  return TotalMinEnergy;
284 
285 }
286 
287 double CheMPS2::DMRG::sweepleft( const bool change, const int instruction, const bool am_i_master ){
288 
289  double Energy = 0.0;
290  const double noise_level = fabs( OptScheme->get_noise_prefactor( instruction ) ) * MaxDiscWeightLastSweep;
291  const double dvdson_rtol = OptScheme->get_dvdson_rtol( instruction );
292  const int vir_dimension = OptScheme->get_D( instruction );
293  MaxDiscWeightLastSweep = 0.0;
294  LastMinEnergy = 1e8;
295 
296  for ( int index = L - 2; index > 0; index-- ){
297 
298  Energy = solve_site( index, dvdson_rtol, noise_level, vir_dimension, am_i_master, false, change );
299  if ( Energy < TotalMinEnergy ){ TotalMinEnergy = Energy; }
300  if ( Energy < LastMinEnergy ){ LastMinEnergy = Energy; }
301  if ( am_i_master ){
302  cout << "Energy at sites (" << index << ", " << index + 1 << ") is " << Energy << endl;
303  }
304 
305  // Prepare for next step
306  struct timeval start, end;
307  gettimeofday( &start, NULL );
308  updateMovingLeftSafe( index );
309  gettimeofday( &end, NULL );
310  timings[ CHEMPS2_TIME_TENS_TOTAL ] += ( end.tv_sec - start.tv_sec ) + 1e-6 * ( end.tv_usec - start.tv_usec );
311 
312  }
313 
314  return Energy;
315 
316 }
317 
318 double CheMPS2::DMRG::sweepright( const bool change, const int instruction, const bool am_i_master ){
319 
320  double Energy = 0.0;
321  const double noise_level = fabs( OptScheme->get_noise_prefactor( instruction ) ) * MaxDiscWeightLastSweep;
322  const double dvdson_rtol = OptScheme->get_dvdson_rtol( instruction );
323  const int vir_dimension = OptScheme->get_D( instruction );
324  MaxDiscWeightLastSweep = 0.0;
325  LastMinEnergy = 1e8;
326 
327  for ( int index = 0; index < L - 2; index++ ){
328 
329  Energy = solve_site( index, dvdson_rtol, noise_level, vir_dimension, am_i_master, true, change );
330  if ( Energy < TotalMinEnergy ){ TotalMinEnergy = Energy; }
331  if ( Energy < LastMinEnergy ){ LastMinEnergy = Energy; }
332  if ( am_i_master ){
333  cout << "Energy at sites (" << index << ", " << index + 1 << ") is " << Energy << endl;
334  }
335 
336  // Prepare for next step
337  struct timeval start, end;
338  gettimeofday( &start, NULL );
339  updateMovingRightSafe( index );
340  gettimeofday( &end, NULL );
341  timings[ CHEMPS2_TIME_TENS_TOTAL ] += ( end.tv_sec - start.tv_sec ) + 1e-6 * ( end.tv_usec - start.tv_usec );
342 
343  }
344 
345  return Energy;
346 
347 }
348 
349 double CheMPS2::DMRG::solve_site( const int index, const double dvdson_rtol, const double noise_level, const int virtual_dimension, const bool am_i_master, const bool moving_right, const bool change ){
350 
351  struct timeval start, end;
352 
353  // Construct two-site object S. Each MPI process joins the MPS tensors. Before a matrix-vector multiplication the vector is broadcasted anyway.
354  gettimeofday( &start, NULL );
355  Sobject * denS = new Sobject( index, denBK );
356  denS->Join( MPS[ index ], MPS[ index + 1 ] );
357  gettimeofday( &end, NULL );
358  timings[ CHEMPS2_TIME_S_JOIN ] += ( end.tv_sec - start.tv_sec ) + 1e-6 * ( end.tv_usec - start.tv_usec );
359 
360  // Feed everything to the solver. Each MPI process returns the correct energy. Only MPI_CHEMPS2_MASTER has the correct denS solution.
361  gettimeofday( &start, NULL );
362  Heff Solver( denBK, Prob, dvdson_rtol );
363  double ** VeffTilde = NULL;
364  if ( Exc_activated ){ VeffTilde = prepare_excitations( denS ); }
365  double Energy = Solver.SolveDAVIDSON( denS, Ltensors, Atensors, Btensors, Ctensors, Dtensors, S0tensors, S1tensors, F0tensors, F1tensors, Qtensors, Xtensors, nStates - 1, VeffTilde );
366  Energy += Prob->gEconst();
367  if ( Exc_activated ){ cleanup_excitations( VeffTilde ); }
368  gettimeofday( &end, NULL );
369  timings[ CHEMPS2_TIME_S_SOLVE ] += ( end.tv_sec - start.tv_sec ) + 1e-6 * ( end.tv_usec - start.tv_usec );
370 
371  // Decompose the S-object. MPI_CHEMPS2_MASTER decomposes denS. Each MPI process returns the correct discWeight. Each MPI process has the new MPS tensors set.
372  gettimeofday( &start, NULL );
373  if (( noise_level > 0.0 ) && ( am_i_master )){ denS->addNoise( noise_level ); }
374  const double discWeight = denS->Split( MPS[ index ], MPS[ index + 1 ], virtual_dimension, moving_right, change );
375  delete denS;
376  if ( discWeight > MaxDiscWeightLastSweep ){ MaxDiscWeightLastSweep = discWeight; }
377  gettimeofday( &end, NULL );
378  timings[ CHEMPS2_TIME_S_SPLIT ] += ( end.tv_sec - start.tv_sec ) + 1e-6 * ( end.tv_usec - start.tv_usec );
379 
380  return Energy;
381 
382 }
383 
384 void CheMPS2::DMRG::activateExcitations( const int maxExcIn ){
385 
386  Exc_activated = true;
387  maxExc = maxExcIn;
388  Exc_Eshifts = new double[ maxExc ];
389  Exc_MPSs = new TensorT ** [ maxExc ];
390  Exc_BKs = new SyBookkeeper * [ maxExc ];
391  Exc_Overlaps = new TensorO ** [ maxExc ];
392 
393 }
394 
395 void CheMPS2::DMRG::newExcitation( const double EshiftIn ){
396 
397  assert( Exc_activated );
398  assert( nStates - 1 < maxExc );
399 
400  if ( the2DM != NULL ){ delete the2DM; the2DM = NULL; }
401  if ( the3DM != NULL ){ delete the3DM; the3DM = NULL; }
402  if ( theCorr != NULL ){ delete theCorr; theCorr = NULL; }
403  deleteAllBoundaryOperators();
404 
405  Exc_Eshifts[ nStates - 1 ] = EshiftIn;
406  #ifdef CHEMPS2_MPI_COMPILATION
407  if ( MPIchemps2::owner_specific_excitation( L, nStates - 1 ) == MPIchemps2::mpi_rank() ){
408  #endif
409  Exc_MPSs[ nStates - 1 ] = MPS;
410  Exc_BKs[ nStates - 1 ] = denBK;
411  Exc_Overlaps[ nStates - 1 ] = new TensorO*[ L - 1 ];
412  #ifdef CHEMPS2_MPI_COMPILATION
413  } else {
414  for ( int site = 0; site < L; site++ ){ delete MPS[ site ]; }
415  delete [] MPS;
416  delete denBK;
417  }
418  #endif
419 
420  nStates++;
421 
422  setupBookkeeperAndMPS();
423  PreSolve();
424 
425 }
426 
double get_energy_conv(const int instruction) const
Get the energy convergence threshold for a particular instruction.
void random()
Fill storage with random numbers 0 < val < 1.
Definition: TensorT.cpp:167
double get_noise_prefactor(const int instruction) const
Get the noise prefactor for a particular instruction.
DMRG(Problem *Probin, ConvergenceScheme *OptSchemeIn, const bool makechkpt=CheMPS2::DMRG_storeMpsOnDisk, const string tmpfolder=CheMPS2::defaultTMPpath)
Constructor.
Definition: DMRG.cpp:36
double Split(TensorT *Tleft, TensorT *Tright, const int virtualdimensionD, const bool movingright, const bool change)
SVD an S-object into 2 TensorT&#39;s.
Definition: Sobject.cpp:260
static int mpi_rank()
Get the rank of this MPI process.
Definition: MPIchemps2.h:131
bool checkConsistency() const
Check whether the given parameters L, N, and TwoS are not inconsistent and whether 0<=Irrep<nIrreps...
Definition: Problem.cpp:385
double Solve()
Solver.
Definition: DMRG.cpp:199
int get_D(const int instruction) const
Get the number of renormalized states for a particular instruction.
int get_number() const
Get the number of instructions.
void PreSolve()
Reconstruct the renormalized operators when you overwrite the matrix elements with Prob->setMxElement...
Definition: DMRG.cpp:188
void newExcitation(const double EshiftIn)
Push back current calculation and set everything up to calculate a (new) excitation.
Definition: DMRG.cpp:395
bool IsPossible() const
Get whether the desired symmetry sector is possible.
void Join(TensorT *Tleft, TensorT *Tright)
Join two sites to form a composite S-object.
Definition: Sobject.cpp:212
static void PrintLicense()
Print the license.
double get_dvdson_rtol(const int instruction) const
Get the Davidson residual tolerance for a particular instruction.
void activateExcitations(const int maxExcIn)
Activate the necessary storage and machinery to handle excitations.
Definition: DMRG.cpp:384
virtual ~DMRG()
Destructor.
Definition: DMRG.cpp:142
double SolveDAVIDSON(Sobject *denS, TensorL ***Ltensors, TensorOperator ****Atensors, TensorOperator ****Btensors, TensorOperator ****Ctensors, TensorOperator ****Dtensors, TensorS0 ****S0tensors, TensorS1 ****S1tensors, TensorF0 ****F0tensors, TensorF1 ****F1tensors, TensorQ ***Qtensors, TensorX **Xtensors, int nLower=0, double **VeffTilde=NULL) const
Davidson Solver.
Definition: Heff.cpp:317
double gEconst() const
Get the constant part of the Hamiltonian.
Definition: Problem.h:76
void addNoise(const double NoiseLevel)
Add noise to the current S-object.
Definition: Sobject.cpp:650