CheMPS2
TensorS1.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 <stdlib.h>
21 #include <math.h>
22 
23 #include "TensorS1.h"
24 #include "Lapack.h"
25 #include "Wigner.h"
26 
27 CheMPS2::TensorS1::TensorS1(const int boundary_index, const int Idiff, const bool moving_right, const SyBookkeeper * denBK) :
28 TensorOperator(boundary_index,
29  2, // two_j
30  2, // n_elec
31  Idiff,
32  moving_right,
33  true, // prime_last
34  false, // jw_phase (two 2nd quantized operators)
35  denBK,
36  denBK){ }
37 
39 
40 void CheMPS2::TensorS1::makenew(TensorL * denL, TensorT * denT, double * workmem){
41 
42  if (moving_right){ makenewRight(denL, denT, workmem); }
43  else{ makenewLeft( denL, denT, workmem); }
44 
45 }
46 
47 void CheMPS2::TensorS1::makenewRight(TensorL * denL, TensorT * denT, double * workmem){
48 
49  clear();
50 
51  for (int ikappa=0; ikappa<nKappa; ikappa++){
52  const int IDR = Irreps::directProd(n_irrep,sector_irrep_up[ikappa]);
53  int dimUR = bk_up->gCurrentDim(index, sector_nelec_up[ikappa], sector_spin_up[ikappa], sector_irrep_up[ikappa]);
54  int dimDR = bk_up->gCurrentDim(index, sector_nelec_up[ikappa]+2, sector_spin_down[ikappa], IDR );
55 
56  for (int geval=0; geval<4; geval++){
57  int NLU,TwoSLU,ILU,TwoSLD,ILD; //NLD = NLU+1
58  switch(geval){
59  case 0:
60  NLU = sector_nelec_up[ikappa];
61  TwoSLU = sector_spin_up[ikappa];
62  ILU = sector_irrep_up[ikappa];
63  TwoSLD = sector_spin_down[ikappa]-1;
64  ILD = Irreps::directProd( ILU, denL->get_irrep() );
65  break;
66  case 1:
67  NLU = sector_nelec_up[ikappa];
68  TwoSLU = sector_spin_up[ikappa];
69  ILU = sector_irrep_up[ikappa];
70  TwoSLD = sector_spin_down[ikappa]+1;
71  ILD = Irreps::directProd( ILU, denL->get_irrep() );
72  break;
73  case 2:
74  NLU = sector_nelec_up[ikappa]-1;
75  TwoSLU = sector_spin_up[ikappa]-1;
76  ILU = Irreps::directProd( sector_irrep_up[ikappa] , bk_up->gIrrep(index-1) );
77  TwoSLD = sector_spin_down[ikappa];
78  ILD = IDR;
79  break;
80  case 3:
81  NLU = sector_nelec_up[ikappa]-1;
82  TwoSLU = sector_spin_up[ikappa]+1;
83  ILU = Irreps::directProd( sector_irrep_up[ikappa] , bk_up->gIrrep(index-1) );
84  TwoSLD = sector_spin_down[ikappa];
85  ILD = IDR;
86  break;
87  }
88  int dimLU = bk_up->gCurrentDim(index-1, NLU, TwoSLU, ILU);
89  int dimLD = bk_up->gCurrentDim(index-1, NLU+1, TwoSLD, ILD);
90  if ((dimLU>0) && (dimLD>0) && (abs(TwoSLU-TwoSLD)<2)){
91 
92  double * BlockTup = denT->gStorage(NLU, TwoSLU, ILU, sector_nelec_up[ikappa], sector_spin_up[ikappa], sector_irrep_up[ikappa]);
93  double * BlockTdown = denT->gStorage(NLU+1, TwoSLD, ILD, sector_nelec_up[ikappa]+2, sector_spin_down[ikappa], IDR );
94  double * BlockL = denL->gStorage(NLU, TwoSLU, ILU, NLU+1, TwoSLD, ILD );
95 
96  //factor * Tup^T * L -> mem
97  char trans = 'T';
98  char notrans = 'N';
99  double alpha = 1.0;
100  if (geval<=1){
101  int fase = ((((sector_spin_up[ikappa] + sector_spin_down[ikappa] + 2)/2)%2)!=0)?-1:1;
102  alpha = fase * sqrt(3.0*(TwoSLD+1)) * Wigner::wigner6j(1,1,2,sector_spin_up[ikappa],sector_spin_down[ikappa],TwoSLD);
103  } else {
104  int fase = ((((TwoSLU + sector_spin_down[ikappa] + 1)/2)%2)!=0)?-1:1;
105  alpha = fase * sqrt(3.0*(sector_spin_up[ikappa]+1)) * Wigner::wigner6j(1,1,2,sector_spin_up[ikappa],sector_spin_down[ikappa],TwoSLU);
106  }
107  double beta = 0.0; //set
108  dgemm_(&trans,&notrans,&dimUR,&dimLD,&dimLU,&alpha,BlockTup,&dimLU,BlockL,&dimLU,&beta,workmem,&dimUR);
109 
110  //mem * Tdown -> storage
111  alpha = 1.0;
112  beta = 1.0; // add
113  dgemm_(&notrans,&notrans,&dimUR,&dimDR,&dimLD,&alpha,workmem,&dimUR,BlockTdown,&dimLD,&beta,storage+kappa2index[ikappa],&dimUR);
114 
115  }
116  }
117  }
118 
119 }
120 
121 void CheMPS2::TensorS1::makenewLeft(TensorL * denL, TensorT * denT, double * workmem){
122 
123  clear();
124 
125  for (int ikappa=0; ikappa<nKappa; ikappa++){
126  const int IDL = Irreps::directProd(n_irrep,sector_irrep_up[ikappa]);
127  int dimUL = bk_up->gCurrentDim(index, sector_nelec_up[ikappa], sector_spin_up[ikappa], sector_irrep_up[ikappa]);
128  int dimDL = bk_up->gCurrentDim(index, sector_nelec_up[ikappa]+2, sector_spin_down[ikappa], IDL );
129 
130  for (int geval=0; geval<4; geval++){
131  int NRU,TwoSRU,IRU,TwoSRD,IRD; //NRD = NRU+1
132  switch(geval){
133  case 0:
134  NRU = sector_nelec_up[ikappa]+1;
135  TwoSRU = sector_spin_up[ikappa]-1;
136  IRU = Irreps::directProd( sector_irrep_up[ikappa] , bk_up->gIrrep(index) );
137  TwoSRD = sector_spin_down[ikappa];
138  IRD = IDL;
139  break;
140  case 1:
141  NRU = sector_nelec_up[ikappa]+1;
142  TwoSRU = sector_spin_up[ikappa]+1;
143  IRU = Irreps::directProd( sector_irrep_up[ikappa] , bk_up->gIrrep(index) );
144  TwoSRD = sector_spin_down[ikappa];
145  IRD = IDL;
146  break;
147  case 2:
148  NRU = sector_nelec_up[ikappa]+2;
149  TwoSRU = sector_spin_up[ikappa];
150  IRU = sector_irrep_up[ikappa];
151  TwoSRD = sector_spin_down[ikappa]-1;
152  IRD = Irreps::directProd( sector_irrep_up[ikappa] , denL->get_irrep() );
153  break;
154  case 3:
155  NRU = sector_nelec_up[ikappa]+2;
156  TwoSRU = sector_spin_up[ikappa];
157  IRU = sector_irrep_up[ikappa];
158  TwoSRD = sector_spin_down[ikappa]+1;
159  IRD = Irreps::directProd( sector_irrep_up[ikappa] , denL->get_irrep() );
160  break;
161  }
162  int dimRU = bk_up->gCurrentDim(index+1, NRU, TwoSRU, IRU);
163  int dimRD = bk_up->gCurrentDim(index+1, NRU+1, TwoSRD, IRD);
164  if ((dimRU>0) && (dimRD>0) && (abs(TwoSRD-TwoSRU)<2)){
165 
166  double * BlockTup = denT->gStorage(sector_nelec_up[ikappa], sector_spin_up[ikappa], sector_irrep_up[ikappa], NRU, TwoSRU, IRU);
167  double * BlockTdown = denT->gStorage(sector_nelec_up[ikappa]+2, sector_spin_down[ikappa], IDL, NRU+1, TwoSRD, IRD);
168  double * BlockL = denL->gStorage(NRU, TwoSRU, IRU, NRU+1, TwoSRD, IRD);
169 
170  //factor * Tup * L -> mem
171  char notrans = 'N';
172  double alpha = 1.0;
173  if (geval<=1){
174  int fase = ((((sector_spin_up[ikappa] + sector_spin_down[ikappa] + 2)/2)%2)!=0)?-1:1;
175  alpha = fase * sqrt(3.0 * (TwoSRU+1)) * Wigner::wigner6j( 1, 1, 2, sector_spin_up[ikappa], sector_spin_down[ikappa], TwoSRU );
176  } else {
177  int fase = ((((sector_spin_up[ikappa] + TwoSRD + 1)/2)%2)!=0)?-1:1;
178  alpha = fase * sqrt(3.0 / (sector_spin_down[ikappa] + 1.0)) * (TwoSRD + 1)
179  * Wigner::wigner6j( 1, 1, 2, sector_spin_up[ikappa], sector_spin_down[ikappa], TwoSRD );
180  }
181  double beta = 0.0; //set
182  dgemm_(&notrans,&notrans,&dimUL,&dimRD,&dimRU,&alpha,BlockTup,&dimUL,BlockL,&dimRU,&beta,workmem,&dimUL);
183 
184  //mem * Tdown -> storage
185  char trans = 'T';
186  alpha = 1.0;
187  beta = 1.0; // add
188  dgemm_(&notrans,&trans,&dimUL,&dimDL,&dimRD,&alpha,workmem,&dimUL,BlockTdown,&dimDL,&beta,storage+kappa2index[ikappa],&dimUL);
189 
190  }
191  }
192  }
193 
194 }
195 
static double wigner6j(const int two_ja, const int two_jb, const int two_jc, const int two_jd, const int two_je, const int two_jf)
Wigner-6j symbol (gsl api)
Definition: Wigner.cpp:294
int index
Index of the Tensor object. For TensorT: a site index; for other tensors: a boundary index...
Definition: Tensor.h:75
int * sector_nelec_up
The up particle number sector.
int * sector_spin_down
The down spin symmetry sector (pointer points to sectorTwoS1 if two_j == 0)
int gCurrentDim(const int boundary, const int N, const int TwoS, const int irrep) const
Get the current virtual dimensions.
virtual ~TensorS1()
Destructor.
Definition: TensorS1.cpp:38
TensorS1(const int boundary_index, const int Idiff, const bool moving_right, const SyBookkeeper *denBK)
Constructor.
Definition: TensorS1.cpp:27
void makenew(TensorL *denL, TensorT *denT, double *workmem)
Definition: TensorS1.cpp:40
double * gStorage()
Get the pointer to the storage.
bool moving_right
Whether or not moving right.
int * sector_spin_up
The up spin symmetry sector.
double * gStorage()
Get the pointer to the storage.
Definition: TensorT.cpp:134
int n_irrep
The (real-valued abelian) point group irrep difference between the symmetry sectors of the lower and ...
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&#39;s convent...
Definition: Irreps.h:123
int * sector_irrep_up
The up spin symmetry sector.
const SyBookkeeper * bk_up
The bookkeeper of the upper MPS.
int * kappa2index
kappa2index[kappa] indicates the start of tensor block kappa in storage. kappa2index[nKappa] gives th...
Definition: Tensor.h:84
int get_irrep() const
Get the (real-valued abelian) point group irrep difference between the symmetry sectors of the lower ...
void clear()
Set all storage variables to 0.0.
int nKappa
Number of Tensor blocks.
Definition: Tensor.h:81
int gIrrep(const int orbital) const
Get an orbital irrep.
double * storage
The actual variables. Tensor block kappa begins at storage+kappa2index[kappa] and ends at storage+kap...
Definition: Tensor.h:78