v2DM-DOCI  1.0
OrbitalTransform.cpp
Go to the documentation of this file.
1 // CIFlow is a very flexible configuration interaction program
2 // Copyright (C) 2014 Mario Van Raemdonck <mario.vanraemdonck@UGent.be>
3 //
4 // This file is part of CIFlow.
5 //
6 // CIFlow is private software; developed by Mario Van Raemdonck
7 // a member of the Ghent Quantum Chemistry Group (Ghent University).
8 // See also : http://www.quantum.ugent.be
9 //
10 // At this moment CIFlow is not yet distributed.
11 // However this might change in the future in the hope that
12 // it will be useful to someone.
13 //
14 // For now you have to ask the main author for permission.
15 //
16 //--
17 #include <cstdlib>
18 #include <cmath>
19 #include <algorithm>
20 #include <iostream>
21 #include <cassert>
22 #include <cstring>
23 
24 #include "OrbitalTransform.h"
25 #include "Hamiltonian.h"
26 #include "OptIndex.h"
27 #include "UnitaryMatrix.h"
28 #include "Lapack.h"
29 
30 using std::min;
31 using std::max;
33 using namespace simanneal;
34 
36  index(HamIn)
37 {
39  int L = HamIn.getL();
40  SymmInfo.setGroup(HamIn.getNGroup());
41  _hamorig.reset(new CheMPS2::Hamiltonian(HamIn));
42 
43  _unitary.reset(new UnitaryMatrix(index));
44 
45  //Create the memory for the orbital transformations.
46  unsigned long long maxlinsize = 0;
47  for (int irrep=0; irrep< index.getNirreps(); irrep++)
48  {
49  unsigned int linsize_irrep = index.getNORB(irrep);
50  if (linsize_irrep > maxlinsize)
51  maxlinsize = linsize_irrep;
52  }
53 
54  //Determine the blocksize for the 2-body transformation
55  auto& maxBlockSize = maxlinsize;
56  //Allocate 2-body rotation memory: One array is approx (maxBlockSize/273.0)^4 * 42 GiB --> [maxBlockSize=100 --> 750 MB]
57  auto maxBSpower4 = maxBlockSize * maxBlockSize * maxBlockSize * maxBlockSize; //Note that 273**4 overfloats the 32 bit integer!!!
58  auto sizeWorkmem1 = max( max( maxBSpower4 , 3*maxlinsize*maxlinsize ) , 1uLL * L * L ); //For (2-body tfo , updateUnitary, calcNOON)
59  auto sizeWorkmem2 = max( max( maxBSpower4 , 2*maxlinsize*maxlinsize ) , L*(L + 1uLL) ); //For (2-body tfo, updateUnitary and rotate_to_active_space, rotate2DMand1DM)
60  mem1.reset(new double[sizeWorkmem1]);
61  mem2.reset(new double[sizeWorkmem2]);
62 }
63 
65 {
66  assert(&HamCI != _hamorig.get());
68  fillConstAndTmat(HamCI); //fill one body terms and constant part.
69 
70  //Two-body terms --> use eightfold permutation symmetry in the irreps :-)
71  for (int irrep1 = 0; irrep1<numberOfIrreps; irrep1++)
72  for (int irrep2 = irrep1; irrep2<numberOfIrreps; irrep2++)
73  {
74  const int productSymm = SymmInfo.directProd(irrep1,irrep2);
75  for (int irrep3 = irrep1; irrep3<numberOfIrreps; irrep3++)
76  {
77  const int irrep4 = SymmInfo.directProd(productSymm,irrep3);
78  // Generated all possible combinations of allowed irreps
79  if (irrep4>=irrep2)
80  {
81  int linsize1 = index.getNORB(irrep1);
82  int linsize2 =index.getNORB(irrep2);
83  int linsize3 =index.getNORB(irrep3);
84  int linsize4 =index.getNORB(irrep4);
85 
86  if ((linsize1>0) && (linsize2>0) && (linsize3>0) && (linsize4>0))
87  {
88  for (int cnt1=0; cnt1<linsize1; cnt1++)
89  for (int cnt2=0; cnt2<linsize2; cnt2++)
90  for (int cnt3=0; cnt3<linsize3; cnt3++)
91  for (int cnt4=0; cnt4<linsize4; cnt4++)
92  mem1[cnt1 + linsize1 * ( cnt2 + linsize2 * (cnt3 + linsize3 * cnt4) ) ]
93  = _hamorig->getVmat(index.getNstart(irrep1) + cnt1,index.getNstart(irrep2) + cnt2, index.getNstart(irrep3) + cnt3, index.getNstart(irrep4) + cnt4 );
94 
95  char trans = 'T';
96  char notra = 'N';
97  double alpha = 1.0;
98  double beta = 0.0; //SET !!!
99 
100  int rightdim = linsize2 * linsize3 * linsize4; //(ijkl) -> (ajkl)
101  double * Umx = _unitary->getBlock(irrep1);
102  dgemm_(&notra, &notra, &linsize1, &rightdim, &linsize1, &alpha, Umx, &linsize1, mem1.get(), &linsize1, &beta, mem2.get(), &linsize1);
103 
104  int leftdim = linsize1 * linsize2 * linsize3; //(ajkl) -> (ajkd)
105  Umx = _unitary->getBlock(irrep4);
106  dgemm_(&notra, &trans, &leftdim, &linsize4, &linsize4, &alpha, mem2.get(), &leftdim, Umx, &linsize4, &beta, mem1.get(), &leftdim);
107 
108  int jump1 = linsize1 * linsize2 * linsize3; //(ajkd) -> (ajcd)
109  int jump2 = linsize1 * linsize2 * linsize3;
110  leftdim = linsize1 * linsize2;
111  Umx = _unitary->getBlock(irrep3);
112  for (int bla=0; bla<linsize4; bla++)
113  dgemm_(&notra, &trans, &leftdim, &linsize3, &linsize3, &alpha, mem1.get()+jump1*bla, &leftdim, Umx, &linsize3, &beta, mem2.get()+jump2*bla, &leftdim);
114 
115  jump2 = linsize1 * linsize2;
116  jump1 = linsize1 * linsize2;
117  rightdim = linsize3 * linsize4;
118  Umx = _unitary->getBlock(irrep2);
119  for (int bla=0; bla<rightdim; bla++)
120  dgemm_(&notra, &trans, &linsize1, &linsize2, &linsize2, &alpha, mem2.get()+jump2*bla, &linsize1, Umx, &linsize2, &beta, mem1.get()+jump1*bla, &linsize1);
121 
122  for (int cnt1=0; cnt1<linsize1; cnt1++)
123  for (int cnt2=0; cnt2<linsize2; cnt2++)
124  for (int cnt3=0; cnt3<linsize3; cnt3++)
125  for (int cnt4=0; cnt4<linsize4; cnt4++)
126  HamCI.setVmat(index.getNstart(irrep1) + cnt1,index.getNstart(irrep2) + cnt2, index.getNstart(irrep3) + cnt3,index.getNstart(irrep4) + cnt4, mem1[cnt1 + linsize1 * ( cnt2 + linsize2 * (cnt3 + linsize3 * cnt4) ) ] );
127 
128  } //end if the problem has orbitals from all 4 selected irreps
129  } // end if irrep 4 >= irrep2
130  }// end run irrep3
131  } // end run irrep2
132 }
133 
139 {
140  if(!QmatrixWork.size() || !OneBodyMatrixElements.size())
141  {
142  QmatrixWork.resize(numberOfIrreps);
144 
145  for (int irrep=0; irrep<numberOfIrreps; irrep++)
146  {
147  const int size = index.getNORB(irrep) * index.getNORB(irrep);
148  QmatrixWork[irrep].reset(new double[size]);
149  OneBodyMatrixElements[irrep].reset(new double[size]);
150  }
151  }
152 
153  //#pragma omp parallel for schedule(dynamic)
154  for (int irrep=0; irrep<numberOfIrreps; irrep++)
155  {
156  const int linsize = index.getNORB(irrep);
157  for (int row=0; row<linsize; row++)
158  {
159  const int HamIndex1 = index.getNstart(irrep) + row;
160  //set the diagonal elements.
161  OneBodyMatrixElements[irrep][row*(1+linsize)] = _hamorig->getTmat(HamIndex1,HamIndex1);
162  for (int col=row+1; col<linsize; col++)
163  {
164  const int HamIndex2 = index.getNstart(irrep) + col;
165  // remember blas and lapack are in fortran and there it is convenient to run first over columnsand then over rows.
166  OneBodyMatrixElements[irrep][row + linsize * col] = _hamorig->getTmat(HamIndex1,HamIndex2);
167  OneBodyMatrixElements[irrep][col + linsize * row] = OneBodyMatrixElements[irrep][row + linsize * col];
168  }
169  }
170  }
172 }
173 
180 void OrbitalTransform::rotate_old_to_new(std::unique_ptr<double []> * matrix)
181 {
182  //#pragma omp parallel for schedule(dynamic)
183  for (int irrep=0; irrep<numberOfIrreps; irrep++)
184  {
185  int linsize = get_norb(irrep);
186  if(linsize > 1)
187  {
188  double * Umx = _unitary->getBlock(irrep);
189  double alpha = 1.0;
190  double beta = 0.0;
191  char trans = 'T';
192  char notrans = 'N';
193  // Qmatrixwork = Umx * OneBodyMatrixElements
194  dgemm_(&notrans,&notrans,&linsize,&linsize,&linsize,&alpha,Umx,&linsize,matrix[irrep].get(),&linsize,&beta,QmatrixWork[irrep].get(),&linsize);
195  // OneBodyMatrixElements = Qmatrixwork * Umx^T
196  dgemm_(&notrans,&trans, &linsize,&linsize,&linsize,&alpha,QmatrixWork[irrep].get(),&linsize,Umx,&linsize,&beta,matrix[irrep].get(),&linsize);
197  }
198  }
199 }
200 
201 double OrbitalTransform::TmatRotated(const int index1, const int index2) const
202 {
203  if(!OneBodyMatrixElements.size())
204  {
205  std::cerr << "First build the OneBodyMatrixElements!" << std::endl;
206  exit(EXIT_FAILURE);
207  }
208 
209  const int irrep1 = _hamorig->getOrbitalIrrep(index1);
210  const int irrep2 = _hamorig->getOrbitalIrrep(index2);
211 
212  if (irrep1 != irrep2)
213  //From now on: both irreps are the same.
214  return 0.0;
215 
216  int shift = index.getNstart(irrep1);
217 
218  return OneBodyMatrixElements[irrep1][index1-shift + index.getNORB(irrep1) * (index2-shift)];
219 }
220 
222 {
223  //Constant part of the energy
224  double value = _hamorig->getEconst();
225  Ham.setEconst(value);
226 
227  //One-body terms: diagonal in the irreps
228  for (int irrep=0; irrep<numberOfIrreps; irrep++)
229  {
230  const int linsize = index.getNORB(irrep);
231  for (int cnt1=0; cnt1<linsize; cnt1++)
232  for (int cnt2=cnt1; cnt2<linsize; cnt2++)
233  {
234  int shift = index.getNstart(irrep);
235  Ham.setTmat( cnt1 + shift , cnt2 + shift , TmatRotated(cnt1 + shift , cnt2 + shift) );
236  }
237  }
238 }
239 
241 {
242  assert(0);
243  return 0;
244 // return _hamorig->get_difference(&hamin);
245 }
246 
248 {
249  _unitary->CheckDeviationFromUnitary(mem2.get());
250 }
251 
253 {
254  _unitary.reset(new UnitaryMatrix(unit));
255 }
256 
257 double OrbitalTransform::get_norb(int irrep) const
258 {
259  return index.getNORB(irrep);
260 }
261 
262 void OrbitalTransform::update_unitary(double * change)
263 {
264  _unitary->updateUnitary(mem1.get(),mem2.get(),change,1);//First 1, sets multiply.
265 }
266 
274 {
275  _unitary->updateUnitary(mem1.get(),mem2.get(),X,replace);
276 // _unitary->CheckDeviationFromUnitary(mem1.get());
277 }
278 
288 void OrbitalTransform::DoJacobiRotation(CheMPS2::Hamiltonian &ham_rot, int k, int l, double theta)
289 {
290  assert(ham_rot.getOrbitalIrrep(k) == ham_rot.getOrbitalIrrep(l));
291 
292  const int irrep = ham_rot.getOrbitalIrrep(k);
293  const int linsize = index.getNORB(irrep);
294  const int shift = index.getNstart(irrep);
295  // the relative index in the irrep
296  const int k2 = k - shift;
297  const int l2 = l - shift;
298 
299  const double cos = std::cos(theta);
300  const double sin = std::sin(theta);
301 
302  // the one particle integrals
303 
304  // first copy element that we are gonna overwrite
305  const double tmpkk = ham_rot.getTmat(k,k);
306  const double tmpll = ham_rot.getTmat(l,l);
307  const double tmpkl = ham_rot.getTmat(k,l);
308 
309  double tmp;
310  tmp = cos*cos*tmpkk+sin*sin*tmpll-2*cos*sin*tmpkl;
311  ham_rot.setTmat(k,k,tmp);
312 
313  tmp = cos*cos*tmpll+sin*sin*tmpkk+2*cos*sin*tmpkl;
314  ham_rot.setTmat(l,l,tmp);
315 
316  tmp = tmpkl*(cos*cos-sin*sin)+cos*sin*(tmpkk-tmpll);
317  ham_rot.setTmat(k,l,tmp);
318 
319  for(int a=shift;a<linsize+shift;a++)
320  {
321  if(a == k || a == l)
322  continue;
323 
324  const double tmpk = ham_rot.getTmat(k,a);
325  const double tmpl = ham_rot.getTmat(l,a);
326 
327  tmp = cos * tmpk - sin * tmpl;
328  ham_rot.setTmat(a,k,tmp);
329 
330  tmp = cos * tmpl + sin * tmpk;
331  ham_rot.setTmat(a,l,tmp);
332  }
333 
334 
335  // the two particle elements
336  // this is code from Sebastian (CheMPS2) adapted to
337  // Jacobi rotations in one irrep
338  // Two-body terms --> use eightfold permutation symmetry in the irreps :-)
339  for (int irrep1 = 0; irrep1<numberOfIrreps; irrep1++)
340  for (int irrep2 = irrep1; irrep2<numberOfIrreps; irrep2++)
341  {
342  const int productSymm = SymmInfo.directProd(irrep1, irrep2);
343  for (int irrep3 = irrep1; irrep3<numberOfIrreps; irrep3++)
344  {
345  const int irrep4 = SymmInfo.directProd(productSymm, irrep3);
346  // Generated all possible combinations of allowed irreps
347  if(irrep4>=irrep2 && (irrep4==irrep || irrep3==irrep || irrep2==irrep || irrep1==irrep))
348  {
349  const int linsize1 = index.getNORB(irrep1);
350  const int linsize2 = index.getNORB(irrep2);
351  const int linsize3 = index.getNORB(irrep3);
352  const int linsize4 = index.getNORB(irrep4);
353 
354  if ((linsize1>0) && (linsize2>0) && (linsize3>0) && (linsize4>0))
355  {
356  for (int cnt1=0; cnt1<linsize1; cnt1++)
357  for (int cnt2=0; cnt2<linsize2; cnt2++)
358  for (int cnt3=0; cnt3<linsize3; cnt3++)
359  for (int cnt4=0; cnt4<linsize4; cnt4++)
360  mem1[cnt1 + linsize1 * ( cnt2 + linsize2 * (cnt3 + linsize3 * cnt4) ) ]
361  = ham_rot.getVmat(index.getNstart(irrep1) + cnt1,index.getNstart(irrep2) + cnt2, index.getNstart(irrep3) + cnt3, index.getNstart(irrep4) + cnt4 );
362 
363  int rightdim = linsize2 * linsize3 * linsize4;
364  // (ijkl) -> (ajkl)
365  if(irrep1 == irrep)
366  for(int d=0;d<rightdim;d++)
367  {
368  const double tmpk = mem1[d*linsize1+k2];
369  const double tmpl = mem1[d*linsize1+l2];
370 
371  mem1[d*linsize1+k2] = cos*tmpk-sin*tmpl;
372  mem1[d*linsize1+l2] = cos*tmpl+sin*tmpk;
373  }
374 
375  int leftdim = linsize1 * linsize2 * linsize3;
376  // (ajkl) -> (ajkd)
377  if(irrep4 == irrep)
378  for(int d=0;d<leftdim;d++)
379  {
380  const double tmpk = mem1[k2*leftdim+d];
381  const double tmpl = mem1[l2*leftdim+d];
382 
383  mem1[k2*leftdim+d] = cos*tmpk-sin*tmpl;
384  mem1[l2*leftdim+d] = cos*tmpl+sin*tmpk;
385  }
386 
387  int jump = linsize1 * linsize2 * linsize3;
388  leftdim = linsize1 * linsize2;
389  // (ajkd) -> (ajcd)
390  if(irrep3 == irrep)
391  for (int strideidx=0;strideidx<linsize4;strideidx++)
392  for(int d=0;d<leftdim;d++)
393  {
394  const double tmpk = mem1[k2*leftdim+d+jump*strideidx];
395  const double tmpl = mem1[l2*leftdim+d+jump*strideidx];
396 
397  mem1[k2*leftdim+d+jump*strideidx] = cos*tmpk-sin*tmpl;
398  mem1[l2*leftdim+d+jump*strideidx] = cos*tmpl+sin*tmpk;
399  }
400 
401 
402  jump = linsize1 * linsize2;
403  rightdim = linsize3 * linsize4;
404  // (ajcd) -> (abcd)
405  if(irrep2 == irrep)
406  for (int strideidx=0;strideidx<rightdim;strideidx++)
407  for(int d=0;d<linsize1;d++)
408  {
409  const double tmpk = mem1[k2*linsize1+d+jump*strideidx];
410  const double tmpl = mem1[l2*linsize1+d+jump*strideidx];
411 
412  mem1[k2*linsize1+d+jump*strideidx] = cos*tmpk-sin*tmpl;
413  mem1[l2*linsize1+d+jump*strideidx] = cos*tmpl+sin*tmpk;
414  }
415 
416  for (int cnt1=0; cnt1<linsize1; cnt1++)
417  for (int cnt2=0; cnt2<linsize2; cnt2++)
418  for (int cnt3=0; cnt3<linsize3; cnt3++)
419  for (int cnt4=0; cnt4<linsize4; cnt4++)
420  ham_rot.setVmat(index.getNstart(irrep1) + cnt1,index.getNstart(irrep2) + cnt2, index.getNstart(irrep3) + cnt3,index.getNstart(irrep4) + cnt4, mem1[cnt1 + linsize1 * ( cnt2 + linsize2 * (cnt3 + linsize3 * cnt4) ) ] );
421 
422  } //end if the problem has orbitals from all 4 selected irreps
423  } // end if irrep 4 >= irrep2
424  }// end run irrep3
425  } // end run irrep2
426 }
427 
428 /* vim: set ts=4 sw=4 expandtab :*/
std::vector< std::unique_ptr< double[]> > OneBodyMatrixElements
void fillConstAndTmat(CheMPS2::Hamiltonian &Ham) const
bool setGroup(const int nGroup)
Set the group.
Definition: Irreps.cpp:50
void update_unitary(double *step)
void setTmat(const int index1, const int index2, const double val)
Set a Tmat element.
void fillHamCI(CheMPS2::Hamiltonian &HamCI)
int getOrbitalIrrep(const int nOrb) const
Get an orbital irrep number.
double getVmat(const int index1, const int index2, const int index3, const int index4) const
Get a Vmat element.
std::unique_ptr< simanneal::UnitaryMatrix > _unitary
The rotation to perfrom on _hamorig to get the current hamiltonian.
void setVmat(const int index1, const int index2, const int index3, const int index4, const double val)
Set a Vmat element.
void setEconst(const double val)
Set the constant energy.
std::vector< std::unique_ptr< double[]> > QmatrixWork
Some memory to do the one body work, allocated when needed.
void dgemm_(char *transA, char *transB, int *m, int *n, int *k, double *alpha, double *A, int *lda, double *B, int *ldb, double *beta, double *C, int *ldc)
double getTmat(const int index1, const int index2) const
Get a Tmat element.
int getNORB(const int irrep) const
Definition: OptIndex.cpp:73
int getNstart(const int irrep) const
Definition: OptIndex.cpp:75
OrbitalTransform(const CheMPS2::Hamiltonian &ham)
void rotate_old_to_new(std::unique_ptr< double[]> *matrix)
std::unique_ptr< double[]> mem2
void set_unitary(UnitaryMatrix &unit)
std::unique_ptr< double[]> mem1
static int directProd(const int Irrep1, const int Irrep2)
Get the direct product of the irreps with numbers Irrep1 and Irrep2: a bitwise XOR for Psi4's convent...
Definition: Irreps.h:117
double get_norb(int irrep) const
int getNirreps() const
Definition: OptIndex.cpp:71
int getNGroup() const
Get the group number.
int getL() const
Get the number of orbitals.
double get_difference_orig(CheMPS2::Hamiltonian &hamin) const
double TmatRotated(const int index1, const int index2) const
std::unique_ptr< CheMPS2::Hamiltonian > _hamorig
the orginal hamiltonian
void DoJacobiRotation(CheMPS2::Hamiltonian &, int k, int l, double theta)