MPQC  3.0.0-alpha
contract_tbint_tensor.h
1 //
2 // contract_tbint_tensor.h
3 //
4 // Copyright (C) 2005 Edward Valeev
5 //
6 // Author: Edward Valeev <evaleev@vt.edu>
7 // Maintainer: EV
8 //
9 // This file is part of the SC Toolkit.
10 //
11 // The SC Toolkit is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU Library General Public License as published by
13 // the Free Software Foundation; either version 2, or (at your option)
14 // any later version.
15 //
16 // The SC Toolkit is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU Library General Public License for more details.
20 //
21 // You should have received a copy of the GNU Library General Public License
22 // along with the SC Toolkit; see the file COPYING.LIB. If not, write to
23 // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 //
25 // The U.S. Government is granted a limited license as per AL 91-7.
26 //
27 
28 #ifndef _chemistry_qc_mbptr12_contracttbinttensor_h
29 #define _chemistry_qc_mbptr12_contracttbinttensor_h
30 
31 #include <unistd.h>
32 #include <cmath>
33 #include <util/misc/regtime.h>
34 #include <util/misc/consumableresources.h>
35 #include <math/mmisc/pairiter.h>
36 #include <chemistry/qc/lcao/utils.h>
37 #include <chemistry/qc/lcao/utils.impl.h>
38 #include <chemistry/qc/mbptr12/r12int_eval.h>
39 #include <util/misc/print.h>
40 
41 namespace sc {
42 
43  template<typename DataProcess_Bra, typename DataProcess_Ket,
44  typename DataProcess_BraKet, bool CorrFactorInBra, bool CorrFactorInKet,
45  bool CorrFactorInInt>
46  void R12IntEval::contract_tbint_tensor(
47  RefSCMatrix& T,
48  TwoBodyOper::type tbint_type_bra,
49  TwoBodyOper::type tbint_type_ket,
50  const Ref<OrbitalSpace>& space1_bra,
51  const Ref<OrbitalSpace>& space2_bra,
52  const Ref<OrbitalSpace>& space1_intb,
53  const Ref<OrbitalSpace>& space2_intb,
54  const Ref<OrbitalSpace>& space1_ket,
55  const Ref<OrbitalSpace>& space2_ket,
56  const Ref<OrbitalSpace>& space1_intk,
57  const Ref<OrbitalSpace>& space2_intk,
58  const Ref<mbptr12::TwoParticleContraction>& tpcontract,
59  bool antisymmetrize,
60  const std::vector<std::string>& tformkeys_bra,
61  const std::vector<std::string>& tformkeys_ket) {
62  // are external spaces of particles 1 and 2 equivalent?
63  const bool part1_strong_equiv_part2 = (space1_bra == space2_bra
64  && space1_ket == space2_ket);
65  // can external spaces of particles 1 and 2 be equivalent?
66  const bool part1_weak_equiv_part2 = (space1_bra->rank()
67  == space2_bra->rank() && space1_ket->rank() == space2_ket->rank());
68  // Check correct semantics of this call : if antisymmetrize then particles must be equivalent
69  bool correct_semantics = (antisymmetrize && (part1_weak_equiv_part2
70  || part1_strong_equiv_part2)) || !antisymmetrize;
71  // also
72  correct_semantics
73  = (correct_semantics && (space1_intb->rank() == space1_intk->rank())
74  && (space2_intb->rank() == space2_intk->rank()));
75  // also dimensions of tpcontract must match those of space1_int and space2_int
76  correct_semantics = (correct_semantics && (tpcontract->nrow()
77  == space1_intb->rank()) && (tpcontract->ncol() == space2_intb->rank()));
78  if (!correct_semantics)
79  throw ProgrammingError(
80  "R12IntEval::contract_tbint_tensor_() -- incorrect call semantics",
81  __FILE__, __LINE__);
82 
83  //
84  // How is permutational symmetry implemented?
85  //
86  // 1) if need to antisymmetrize && internal spaces for p1 and p2 are same, then
87  // can antisymmetrize each integral explicitly and compute antisymmetric tensor
88  // 2) inf need to antisymmetrize but internal spaces for p1 and p2 do not match,
89  // then compute as AlphaBeta and antisymmetrize at the end. I have to allocate temporary
90  // result.
91  //
92 
93  // are internal spaces of particles 1 and 2 equivalent?
94  const bool part1_intequiv_part2 = (space1_intb == space2_intb
95  && space1_intk == space2_intk);
96 #if 0
97  // antisymmetrization for weakly equivalent particles and nonmatching internal spaces
98  // is probably incorrect semantics
99  if (!part1_intequiv_part2 && ! part1_strong_equiv_part2 && antisymmetrize)
100  throw ProgrammingError("R12IntEval::contract_tbint_tensor_() -- dubious call semantics",
101  __FILE__,__LINE__);
102 #endif
103  // Will antisymmetrize each integral? If no, then result will be computed
104  // as AlphaBeta and antisymmetrized at the end
105  const bool alphabeta = !(antisymmetrize && part1_strong_equiv_part2
106  && part1_intequiv_part2);
107 
108  //
109  // NOTE! Even if computing in AlphaBeta, internal sums can be over AlphaAlpha!!!
110  // Logic should not become much more complicated. Only need time to implement.
111  //
112 
113  const bool CorrFactorInBraInt = CorrFactorInBra && CorrFactorInInt;
114  const bool CorrFactorInKetInt = CorrFactorInKet && CorrFactorInInt;
115 
116  const unsigned int nbrasets = (CorrFactorInBra ? corrfactor()->nfunctions()
117  : 1);
118  const unsigned int nketsets = (CorrFactorInKet ? corrfactor()->nfunctions()
119  : 1);
120  const unsigned int nintsets = (CorrFactorInInt ? corrfactor()->nfunctions()
121  : 1);
122  const unsigned int nbraintsets = (CorrFactorInBraInt ? UINT_MAX : nbrasets
123  * nintsets);
124  const unsigned int nketintsets = (CorrFactorInKetInt ? UINT_MAX : nketsets
125  * nintsets);
126 
127  //
128  // create transforms, if needed
129  //
130  typedef std::vector<Ref<TwoBodyMOIntsTransform> > tformvec;
131 
132  // bra transforms
133  const size_t num_tforms_bra = tformkeys_bra.size();
134  tformvec transforms_bra(num_tforms_bra);
135  for (unsigned int t = 0; t < num_tforms_bra; ++t) {
136  transforms_bra[t] = moints_runtime4()->get(tformkeys_bra[t]);
137  }
138 
139  // ket transforms
140  const size_t num_tforms_ket = tformkeys_ket.size();
141  tformvec transforms_ket(num_tforms_ket);
142  for (unsigned int t = 0; t < num_tforms_ket; ++t) {
143  transforms_ket[t] = moints_runtime4()->get(tformkeys_ket[t]);
144  }
145 
146  //
147  // Generate contract label
148  //
149  Timer tim_gen_tensor_contract("Generic tensor contract");
150  std::string label;
151  {
152  std::ostringstream oss_bra;
153  oss_bra << "<" << space1_bra->id() << " " << space2_bra->id()
154  << (antisymmetrize ? "||" : "|") << space1_intb->id() << " "
155  << space2_intb->id() << ">";
156  const std::string label_bra = oss_bra.str();
157  std::ostringstream oss_ket;
158  oss_ket << "<" << space1_ket->id() << " " << space2_ket->id()
159  << (antisymmetrize ? "||" : "|") << space1_intk->id() << " "
160  << space2_intk->id() << ">";
161  const std::string label_ket = oss_ket.str();
162  std::ostringstream oss;
163  oss << "<" << space1_bra->id() << " " << space2_bra->id()
164  << (antisymmetrize ? "||" : "|") << space1_ket->id() << " "
165  << space2_ket->id() << "> = " << label_bra << " . " << label_ket
166  << "^T";
167  label = oss.str();
168  }
169  ExEnv::out0() << std::endl << indent << "Entered generic contraction (" << label
170  << ")" << std::endl;
171  ExEnv::out0() << incindent;
172 
173  //
174  // Construct maps
175  //
176  // WARNING: Assuming all transforms are over same spaces!!!
177  //
178  Ref<OrbitalSpace> tspace1_bra = transforms_bra[0]->space1();
179  Ref<OrbitalSpace> tspace2_bra = transforms_bra[0]->space3();
180  Ref<OrbitalSpace> tspace1_intb = transforms_bra[0]->space2();
181  Ref<OrbitalSpace> tspace2_intb = transforms_bra[0]->space4();
182  Ref<OrbitalSpace> tspace1_ket = transforms_ket[0]->space1();
183  Ref<OrbitalSpace> tspace2_ket = transforms_ket[0]->space3();
184  Ref<OrbitalSpace> tspace1_intk = transforms_ket[0]->space2();
185  Ref<OrbitalSpace> tspace2_intk = transforms_ket[0]->space4();
186  // maps spaceX to spaceX of the transform
187  std::vector<unsigned int> map1_bra, map2_bra, map1_ket, map2_ket,
188  map1_intb, map2_intb, map1_intk, map2_intk;
189  // maps space2_intb to space1_intb of transform
190  std::vector<unsigned int> map12_intb;
191  // maps space1_intb to space2_intb of transform
192  std::vector<unsigned int> map21_intb;
193  // maps space2_intk to space1_intk of transform
194  std::vector<unsigned int> map12_intk;
195  // maps space1_intk to space2_intk of transform
196  std::vector<unsigned int> map21_intk;
197 
198  { // bra maps
199  map1_bra = *tspace1_bra << *space1_bra;
200  map2_bra = *tspace2_bra << *space2_bra;
201  map1_intb = *tspace1_intb << *space1_intb;
202  map2_intb = *tspace2_intb << *space2_intb;
203  // Will antisymmetrize the integrals? Then need ijkl AND ijlk
204  if (!alphabeta) {
205  if (tspace1_intb == tspace2_intb) {
206  map12_intb = map1_intb;
207  map21_intb = map2_intb;
208  } else {
209  map12_intb = *tspace1_intb << *space2_intb;
210  map21_intb = *tspace2_intb << *space1_intb;
211  }
212  }
213  }
214  { // ket maps
215  map1_ket = *tspace1_ket << *space1_ket;
216  map2_ket = *tspace2_ket << *space2_ket;
217  map1_intk = *tspace1_intk << *space1_intk;
218  map2_intk = *tspace2_intk << *space2_intk;
219  // Will antisymmetrize the integrals? Then need ijkl AND ijlk
220  if (!alphabeta) {
221  if (tspace1_intk == tspace2_intk) {
222  map12_intk = map1_intk;
223  map21_intk = map2_intk;
224  } else {
225  map12_intk = *tspace1_intk << *space2_intk;
226  map21_intk = *tspace2_intk << *space1_intk;
227  }
228  }
229  }
230 
231  const unsigned int bratform_block_ncols = tspace2_intb->rank();
232  const unsigned int kettform_block_ncols = tspace2_intk->rank();
233  const RefDiagSCMatrix evals1_bra = space1_bra->evals();
234  const RefDiagSCMatrix evals2_bra = space2_bra->evals();
235  const RefDiagSCMatrix evals1_ket = space1_ket->evals();
236  const RefDiagSCMatrix evals2_ket = space2_ket->evals();
237  const RefDiagSCMatrix evals1_intb = space1_intb->evals();
238  const RefDiagSCMatrix evals2_intb = space2_intb->evals();
239  const RefDiagSCMatrix evals1_intk = space1_intk->evals();
240  const RefDiagSCMatrix evals2_intk = space2_intk->evals();
241 
242  // Using spinorbital iterators means I don't take into account perm symmetry
243  // More efficient algorithm will require generic code
244  const SpinCase2 S = (alphabeta ? AlphaBeta : AlphaAlpha);
245  SpinMOPairIter
246  iterbra(space1_bra->rank(), (alphabeta ? space2_bra : space1_bra)->rank(), S);
247  SpinMOPairIter
248  iterket(space1_ket->rank(), (alphabeta ? space2_ket : space1_ket)->rank(), S);
249  SpinMOPairIter iterint(space1_intb->rank(),
250  (alphabeta ? space2_intb : space1_intb)->rank(), S);
251  // size of one block of <space1_bra space2_bra|
252  const unsigned int nbra = iterbra.nij();
253  // size of one block of <space1_ket space2_ket|
254  const unsigned int nket = iterket.nij();
255  // size of one block of |space1_int space2_int>
256  const unsigned int nint = iterint.nij();
257 
258  RefSCMatrix Tcontr;
259  // Allocate storage for the result, if need to antisymmetrize at the end; else accumulate directly to T
260  if (antisymmetrize && alphabeta) {
261  Tcontr = T.kit()->matrix(new SCDimension(nbra * nbrasets),
262  new SCDimension(nket * nketsets));
263  Tcontr.assign(0.0);
264  } else
265  Tcontr = T;
266 
267  // size of integral blocks to contract
268  const unsigned int blksize_int = space1_intb->rank() * space2_intb->rank();
269  double* T_ij = new double[blksize_int];
270  double* T_kl = new double[blksize_int];
271 
272  //
273  // Contraction loops
274  //
275 
276  // outermost loop over contraction blocks to minimize number of activate()/deactivate() calls
277  for (unsigned int fint = 0; fint < nintsets; ++fint) {
278 
279  unsigned int fbraoffset = 0;
280  for (unsigned int fbra = 0; fbra < nbrasets; ++fbra, fbraoffset += nbra) {
281  const unsigned int fbraint = fbra * nintsets + fint;
282  Ref<TwoBodyMOIntsTransform> tformb = transforms_bra[fbraint];
283  const Ref<TwoBodyIntDescr>& intdescrb = tformb->intdescr();
284  const unsigned int intsetidx_bra = intdescrb->intset(tbint_type_bra);
285 
286  tformb->compute();
287  Ref<DistArray4> accumb = tformb->ints_distarray4();
288  accumb->activate();
289 
290  unsigned int fketoffset = 0;
291  for (unsigned int fket = 0; fket < nketsets; ++fket, fketoffset += nket) {
292  const unsigned int fketint = fket * nintsets + fint;
293  Ref<TwoBodyMOIntsTransform> tformk = transforms_ket[fketint];
294  const Ref<TwoBodyIntDescr>& intdescrk = tformk->intdescr();
295  const unsigned int intsetidx_ket = intdescrk->intset(tbint_type_ket);
296 
297  tformk->compute();
298  Ref<DistArray4> accumk = tformk->ints_distarray4();
299  accumk->activate();
300 
301  if (debug_ >= DefaultPrintThresholds::diagnostics) {
302  ExEnv::out0() << indent << "Using transforms " << tformb->name()
303  << " and " << tformk->name() << std::endl;
304  }
305 
306  // split work over tasks which have access to integrals
307  // WARNING: assuming same accessibility for both bra and ket transforms
308  std::vector<int> proc_with_ints;
309  const int nproc_with_ints = accumb->tasks_with_access(proc_with_ints);
310  const int me = r12world()->world()->msg()->me();
311  const bool nket_ge_nevals = (nket >= nproc_with_ints);
312 
313  if (accumb->has_access(me)) {
314 
315  unsigned int ijkl = 0;
316  for (iterbra.start(); iterbra; iterbra.next()) {
317  const int ij = iterbra.ij();
318 
319  bool fetch_ij_block = false;
320  if (nket_ge_nevals) {
321  fetch_ij_block = true;
322  } else {
323  const int first_ij_task = ijkl % nproc_with_ints;
324  const int last_ij_task = (ijkl + nket - 1) % nproc_with_ints;
325  // figure out if for this ij there is ijkl to be handled by me
326  if (!(first_ij_task > proc_with_ints[me] && proc_with_ints[me]
327  > last_ij_task))
328  fetch_ij_block = true;
329  }
330  if (!fetch_ij_block)
331  continue;
332 
333  const unsigned int i = iterbra.i();
334  const unsigned int j = iterbra.j();
335  const unsigned int ii = map1_bra[i];
336  const unsigned int jj = map2_bra[j];
337 
338  Timer tim_intsretrieve("MO ints retrieve");
339  const double *ij_buf = accumb->retrieve_pair_block(ii, jj,
340  intsetidx_bra);
341  tim_intsretrieve.exit();
342  if (debug_ >= DefaultPrintThresholds::allO2N2)
343  ExEnv::outn() << indent << "task " << me
344  << ": obtained ij blocks" << std::endl;
345 
346  for (iterket.start(); iterket; iterket.next(), ijkl++) {
347  const int kl = iterket.ij();
348 
349  const int ijkl_proc = ijkl % nproc_with_ints;
350  if (ijkl_proc != proc_with_ints[me])
351  continue;
352 
353  const unsigned int k = iterket.i();
354  const unsigned int l = iterket.j();
355  const unsigned int kk = map1_ket[k];
356  const unsigned int ll = map2_ket[l];
357 
358  if (debug_ >= DefaultPrintThresholds::allO2N2)
359  ExEnv::outn() << indent << "task " << me
360  << ": working on (i,j | k,l) = (" << i << "," << j
361  << " | " << k << "," << l << ")" << std::endl;
362  tim_intsretrieve.enter("MO ints retrieve");
363  const double *kl_buf =
364  accumk->retrieve_pair_block(kk, ll, intsetidx_ket);
365  tim_intsretrieve.exit();
366  if (debug_ >= DefaultPrintThresholds::allO2N2)
367  ExEnv::outn() << indent << "task " << me
368  << ": obtained kl blocks" << std::endl;
369 
370  // zero out intblocks
371  memset(T_ij, 0, blksize_int * sizeof(double));
372  memset(T_kl, 0, blksize_int * sizeof(double));
373 
374  if (debug_ >= DefaultPrintThresholds::allO2N2) {
375  ExEnv::out0() << indent << "i = " << i << " j = " << j
376  << " k = " << k << " l = " << l << incindent << std::endl;
377  }
378 
379  for (iterint.start(); iterint; iterint.next()) {
380  const unsigned int m = iterint.i();
381  const unsigned int n = iterint.j();
382  const int mn = iterint.ij();
383  int MN_bra, MN_ket;
384  {
385  const unsigned int mm = map1_intb[m];
386  const unsigned int nn = map2_intb[n];
387  MN_bra = mm * bratform_block_ncols + nn;
388  }
389  {
390  const unsigned int mm = map1_intk[m];
391  const unsigned int nn = map2_intk[n];
392  MN_ket = mm * kettform_block_ncols + nn;
393  }
394 
395  const double I_ijmn = ij_buf[MN_bra];
396  const double I_klmn = kl_buf[MN_ket];
397 
398  if (debug_ >= DefaultPrintThresholds::mostO6) {
399  ExEnv::out0() << indent << " m = " << m << " n = " << n
400  << std::endl;
401  }
402 
403  if (alphabeta) {
404  if (debug_ >= DefaultPrintThresholds::mostO6) {
405  ExEnv::out0() << indent << " <ij|mn> = " << I_ijmn
406  << std::endl << indent << " <kl|mn> = " << I_klmn << std::endl;
407  }
408  const double T_ijmn = DataProcess_Bra::I2T(I_ijmn, i, j, m,
409  n, evals1_bra,
410  evals1_intb,
411  evals2_bra,
412  evals2_intb);
413  const double T_klmn = DataProcess_Ket::I2T(I_klmn, k, l, m,
414  n, evals1_ket,
415  evals1_intk,
416  evals2_ket,
417  evals2_intk);
418  if (debug_ >= DefaultPrintThresholds::mostO6) {
419  ExEnv::out0() << indent << " <ij|T|mn> = " << T_ijmn
420  << std::endl << indent << " <kl|T|mn> = " << T_klmn
421  << std::endl;
422  }
423 
424  T_ij[mn] = T_ijmn;
425  T_kl[mn] = T_klmn;
426  } else {
427 
428  int NM_bra, NM_ket;
429  {
430  const unsigned int mm = map21_intb[m];
431  const unsigned int nn = map12_intb[n];
432  NM_bra = nn * bratform_block_ncols + mm;
433  }
434  {
435  const unsigned int mm = map21_intk[m];
436  const unsigned int nn = map12_intk[n];
437  NM_ket = nn * kettform_block_ncols + mm;
438  }
439 
440  const double I_ijnm = ij_buf[NM_bra];
441  const double I_klnm = kl_buf[NM_ket];
442 
443  if (debug_ >= DefaultPrintThresholds::mostO6) {
444  ExEnv::out0() << " <ij|mn> = " << I_ijmn << std::endl
445  << " <ij|nm> = " << I_ijnm << std::endl << " <kl|mn> = "
446  << I_klmn << std::endl << " <kl|nm> = " << I_klnm << std::endl;
447  }
448 
449  const double T_ijmn = DataProcess_Bra::I2T(I_ijmn - I_ijnm,
450  i, j, m, n,
451  evals1_bra,
452  evals1_intb,
453  evals2_bra,
454  evals2_intb);
455  const double T_klmn = DataProcess_Ket::I2T(I_klmn - I_klnm,
456  k, l, m, n,
457  evals1_ket,
458  evals1_intk,
459  evals2_ket,
460  evals2_intk);
461  if (debug_ >= DefaultPrintThresholds::mostO6) {
462  ExEnv::out0() << indent << " <ij|T|mn> = " << T_ijmn
463  << std::endl << indent << " <kl|T|mn> = " << T_klmn
464  << std::endl;
465  }
466  T_ij[mn] = T_ijmn;
467  T_kl[mn] = T_klmn;
468  }
469 
470  } // int loop
471 
472  // contract matrices
473  double T_ijkl = tpcontract->contract(T_ij, T_kl);
474  if (debug_ >= DefaultPrintThresholds::allO2N2) {
475  ExEnv::out0() << decindent << indent << " <ij|kl> = "
476  << T_ijkl << std::endl;
477  }
478  T_ijkl = DataProcess_BraKet::I2T(T_ijkl, i, j, k, l,
479  evals1_bra, evals1_ket,
480  evals2_bra, evals2_ket);
481  if (debug_ >= DefaultPrintThresholds::allO2N2) {
482  ExEnv::out0() << indent << " <ij|T|kl>(ref) = "
483  << Tcontr.get_element(ij + fbraoffset, kl + fketoffset)
484  << std::endl;
485  ExEnv::out0() << indent << " +<ij|T|kl> = " << T_ijkl << std::endl;
486  }
487  Tcontr.accumulate_element(ij + fbraoffset, kl + fketoffset,
488  T_ijkl);
489  if (debug_ >= DefaultPrintThresholds::allO2N2) {
490  ExEnv::out0() << indent << " <ij|T|kl>(new) = "
491  << Tcontr.get_element(ij + fbraoffset, kl + fketoffset)
492  << std::endl;
493  }
494 
495  accumk->release_pair_block(kk, ll, intsetidx_ket);
496 
497  } // ket loop
498 
499  if (fetch_ij_block)
500  accumb->release_pair_block(ii, jj, intsetidx_bra);
501 
502  } // bra loop
503  } // loop over tasks with access
504 
505  //ExEnv::out0() << indent << "Accumb = " << accumb.pointer() << std::endl;
506  //ExEnv::out0() << indent << "Accumk = " << accumk.pointer() << std::endl;
507  //ExEnv::out0() << indent << "Accumb == Accumk : " << (accumb==accumk) << std::endl;
508  if (accumb != accumk && accumk->data_persistent()) accumk->deactivate();
509 
510  } // ket blocks
511 
512  if (accumb->data_persistent()) accumb->deactivate();
513 
514  } // bra blocks
515  } // int blocks
516 
517  if (antisymmetrize && alphabeta) {
518  // antisymmetrization implies equivalent particles -- hence symmetrize before antisymmetrize
519  symmetrize<false> (Tcontr, Tcontr, space1_bra, space1_ket);
520  sc::antisymmetrize(T, Tcontr, space1_bra, space1_ket, true);
521  Tcontr = 0;
522  }
523 
524  delete[] T_ij;
525  delete[] T_kl;
526 
527  ExEnv::out0() << decindent;
528  ExEnv::out0() << indent << "Exited generic contraction (" << label << ")"
529  << std::endl;
530  tim_gen_tensor_contract.exit();
531  }
532 
533  template<bool CorrFactorInBra, bool CorrFactorInKet>
534  void R12IntEval::contract_tbint_tensor(
535  RefSCMatrix& T,
536  TwoBodyOper::type tbint_type_bra,
537  TwoBodyOper::type tbint_type_ket,
538  double scale,
539  const Ref<OrbitalSpace>& space1_bra,
540  const Ref<OrbitalSpace>& space2_bra,
541  const Ref<OrbitalSpace>& space1_intb,
542  const Ref<OrbitalSpace>& space2_intb,
543  const Ref<OrbitalSpace>& space1_ket,
544  const Ref<OrbitalSpace>& space2_ket,
545  const Ref<OrbitalSpace>& space1_intk,
546  const Ref<OrbitalSpace>& space2_intk,
547  bool antisymmetrize,
548  const std::vector<std::string>& tformkeys_bra,
549  const std::vector<std::string>& tformkeys_ket) {
550 
551  MPQC_ASSERT(tformkeys_bra.size() == 1);
552  MPQC_ASSERT(tformkeys_ket.size() == 1);
553 
554  std::vector< Ref<DistArray4> > results;
555  contract_tbint_tensor<CorrFactorInBra, CorrFactorInKet>(
556  results,
557  tbint_type_bra,
558  tbint_type_ket,
559  scale,
560  space1_bra,
561  space2_bra,
562  space1_intb,
563  space2_intb,
564  space1_ket,
565  space2_ket,
566  space1_intk,
567  space2_intk,
569  tformkeys_bra,
570  tformkeys_ket);
571 
572  RefSCMatrix Tclone = T.clone();
573  Tclone << results[0];
574  T.accumulate(Tclone);
575  }
576 
577  template<bool CorrFactorInBra, bool CorrFactorInKet>
578  void
579  R12IntEval::contract_tbint_tensor(std::vector< Ref<DistArray4> >& results,
580  TwoBodyOper::type tbint_type_bra,
581  TwoBodyOper::type tbint_type_ket,
582  double scale,
583  const Ref<OrbitalSpace>& space1_bra,
584  const Ref<OrbitalSpace>& space2_bra,
585  const Ref<OrbitalSpace>& space1_intb,
586  const Ref<OrbitalSpace>& space2_intb,
587  const Ref<OrbitalSpace>& space1_ket,
588  const Ref<OrbitalSpace>& space2_ket,
589  const Ref<OrbitalSpace>& space1_intk,
590  const Ref<OrbitalSpace>& space2_intk,
591  bool antisymmetrize,
592  const std::vector<std::string>& tformkeys_bra,
593  const std::vector<std::string>& tformkeys_ket) {
594 
595  // are external spaces of particles 1 and 2 equivalent?
596  const bool part1_strong_equiv_part2 = (space1_bra == space2_bra
597  && space1_ket == space2_ket);
598  // can external spaces of particles 1 and 2 be equivalent?
599  const bool part1_weak_equiv_part2 = (space1_bra->rank()
600  == space2_bra->rank() && space1_ket->rank() == space2_ket->rank());
601  // Check correct semantics of this call : if antisymmetrize then particles must be equivalent
602  bool correct_semantics = (antisymmetrize && (part1_weak_equiv_part2
603  || part1_strong_equiv_part2)) || !antisymmetrize;
604  // also
605  correct_semantics
606  = (correct_semantics && (space1_intb->rank() == space1_intk->rank())
607  && (space2_intb->rank() == space2_intk->rank()));
608  if (!correct_semantics)
609  throw ProgrammingError(
610  "R12IntEval::contract_tbint_tensor_() -- incorrect call semantics",
611  __FILE__, __LINE__);
612 
613  //
614  // How is permutational symmetry implemented?
615  //
616  // 1) if need to antisymmetrize && internal spaces for p1 and p2 are same, then
617  // can antisymmetrize each integral explicitly and compute antisymmetric tensor:
618  // \f$
619  // T_{ij}^{kl} = \sum_{p<q} (A_{ij}^{pq} - A_{ij}^{qp}) (B^{kl}_{pq} - B^{kl}_{qp})
620  // \f$
621  // 2) if need to antisymmetrize but internal spaces for p1 and p2 do not match,
622  // then compute as AlphaBeta and antisymmetrize at the end. Temporary storage
623  // will be allocated.
624  //
625 
626  // are internal spaces of particles 1 and 2 equivalent?
627  const bool part1_intequiv_part2 = (space1_intb == space2_intb
628  && space1_intk == space2_intk);
629  // If antisymmetrize == true and particles 1 and 2 are equivalent, then the target is computed as:
630  // T_{ij}^{kl} = \sum_{p<q} (A_{ij}^{pq} - A_{ij}^{qp}) (B^{kl}_{pq} - B^{kl}_{qp})
631  // i.e. each block of A and B is antisymmetrized first (for each i<j, k<l).
632  // In all other cases, even when antisymmetric target T tensor is requested,
633  // compute alpha-beta T and then antisymmetrize the result, i.e.
634  // V_{ij}^{kl} = \sum_{pq} A_{ij}^{pq} B^{kl}_{pq}
635  // for all i,j,k,l, then
636  // T_{ij}^{kl} = V_{ij}{kl} - V_{ij}^{lk}
637  // for i<j, k<l
638  // this flag determines whether to contract as alpha-beta. If not, each source integral will be antisymmetrized
639  // first
640  const bool alphabeta = !(antisymmetrize && part1_strong_equiv_part2
641  && part1_intequiv_part2);
642 
643  //
644  // NOTE! Even if computing in AlphaBeta, internal sums can be over AlphaAlpha!!!
645  // Logic should not become much more complicated. Only need time to implement.
646  //
647 
648  const unsigned int nbrasets = (CorrFactorInBra ? corrfactor()->nfunctions()
649  : 1);
650  const unsigned int nketsets = (CorrFactorInKet ? corrfactor()->nfunctions()
651  : 1);
652 
653  //
654  // get transforms
655  //
656  typedef std::vector<Ref<TwoBodyMOIntsTransform> > tformvec;
657 
658  // bra transforms
659  const size_t num_tforms_bra = tformkeys_bra.size();
660  tformvec transforms_bra(num_tforms_bra);
661  for (unsigned int t = 0; t < num_tforms_bra; ++t) {
662  transforms_bra[t] = moints_runtime4()->get(tformkeys_bra[t]);
663  }
664 
665  // ket transforms
666  const size_t num_tforms_ket = tformkeys_ket.size();
667  tformvec transforms_ket(num_tforms_ket);
668  for (unsigned int t = 0; t < num_tforms_ket; ++t) {
669  transforms_ket[t] = moints_runtime4()->get(tformkeys_ket[t]);
670  }
671 
672  // create result DistArray4 objects, if needed
673  bool accumulate = false; // need to accumulate result to a given DistArray4?
674  std::vector< Ref<DistArray4> > results_clone;
675  if (results.empty()) {
676  std::vector<std::string> tformkeys_result;
677  { // create result objects by cloning transforms_bra[0]
678  transforms_bra[0]->compute();
679  for (unsigned int tb = 0; tb < num_tforms_bra; ++tb) {
680  const std::string key_bra = tformkeys_bra[tb];
681  const ParsedTwoBodyFourCenterIntKey pkey_bra(key_bra);
682  const std::string parb = pkey_bra.params();
683  std::string params_bra;
684  if (!parb.empty()) { // remove [ and ]
685  const std::string::size_type lbpos = parb.find_first_of('[');
686  const std::string::size_type rbpos = parb.find_last_of(']');
687  MPQC_ASSERT(lbpos < rbpos);
688  params_bra = parb.substr(lbpos + 1, (rbpos - lbpos - 1));
689  }
690 
691  for (unsigned int tk = 0; tk < num_tforms_ket; ++tk) {
692  const std::string key_ket = tformkeys_ket[tk];
693  const ParsedTwoBodyFourCenterIntKey pkey_ket(key_ket);
694  const std::string park = pkey_ket.params();
695  std::string params_ket;
696  if (!park.empty()) { // remove [ and ]
697  const std::string::size_type lbpos = park.find_first_of('[');
698  const std::string::size_type rbpos = park.find_last_of(']');
699  MPQC_ASSERT(lbpos < rbpos);
700  params_ket = park.substr(lbpos + 1, (rbpos - lbpos - 1));
701  }
702 
703  std::string params_result;
704  if (CorrFactorInBra && CorrFactorInKet)
705  params_result = '[' + params_bra + ',' + params_ket + ']';
706  else {
707  if (CorrFactorInBra && !CorrFactorInKet)
708  params_result = '[' + params_bra + ']';
709  if (!CorrFactorInBra && CorrFactorInKet)
710  params_result = '[' + params_ket + ']';
711  }
712 
713  const std::string descr_key = pkey_bra.oper() + "@" + pkey_ket.oper() + params_result;
714 
715  const std::string key_result =
716  ParsedTwoBodyFourCenterIntKey::key(space1_bra->id(),
717  space2_bra->id(),
718  space1_ket->id(),
719  space2_ket->id(), descr_key,
720  TwoBodyIntLayout::b1b2_k1k2);
721 
722  tformkeys_result.push_back(key_result);
723 
724  DistArray4Dimensions dims(1, space1_bra->rank(),
725  space2_bra->rank(), space1_ket->rank(),
726  space2_ket->rank());
727  Ref<DistArray4> result = transforms_bra[0]->ints_distarray4()->clone(dims);
728  results.push_back(result);
729  }
730  }
731  }
732  } else { // results were given -- must clone them, and accumulate after contraction (can' currently do accumulation in place)
733  accumulate = true;
734  // make sure they have expected shape
735  const size_t nresults = results.size();
736  MPQC_ASSERT(nresults == num_tforms_bra*num_tforms_ket);
737  for(int r=0; r<nresults; ++r) {
738  MPQC_ASSERT(results[r]->num_te_types() == 1);
739  MPQC_ASSERT(results[r]->ni() == space1_bra->rank());
740  MPQC_ASSERT(results[r]->nj() == space2_bra->rank());
741  MPQC_ASSERT(results[r]->nx() == space1_ket->rank());
742  MPQC_ASSERT(results[r]->ny() == space2_ket->rank());
743  MPQC_ASSERT(results[r]->storage() == DistArray4Storage_XY);
744  }
745 
746  for(int r=0; r<nresults; ++r) {
747  results_clone.push_back( results[r]->clone() );
748  }
749  }
750  std::vector< Ref<DistArray4> >& results_ref = accumulate ? results_clone : results;
751 
752  //
753  // Generate contract label
754  //
755  Timer tim_gen_tensor_contract("Generic tensor contract");
756  std::string label;
757  {
758  std::ostringstream oss_bra;
759  oss_bra << "<" << space1_bra->id() << " " << space2_bra->id()
760  << (antisymmetrize ? "||" : "|") << space1_intb->id() << " "
761  << space2_intb->id() << ">";
762  const std::string label_bra = oss_bra.str();
763  std::ostringstream oss_ket;
764  oss_ket << "<" << space1_ket->id() << " " << space2_ket->id()
765  << (antisymmetrize ? "||" : "|") << space1_intk->id() << " "
766  << space2_intk->id() << ">";
767  const std::string label_ket = oss_ket.str();
768  std::ostringstream oss;
769  oss << "<" << space1_bra->id() << " " << space2_bra->id()
770  << (antisymmetrize ? "||" : "|") << space1_ket->id() << " "
771  << space2_ket->id() << "> = " << label_bra << " . " << label_ket
772  << "^T";
773  label = oss.str();
774  }
775  ExEnv::out0() << std::endl << indent << "Entered generic contraction (" << label
776  << ")" << std::endl;
777  ExEnv::out0() << incindent;
778 
779  //
780  // make sure that these are the needed transforms
781  //
782  {
783  Ref<OrbitalSpace> tspace1_bra = transforms_bra[0]->space1();
784  Ref<OrbitalSpace> tspace2_bra = transforms_bra[0]->space3();
785  Ref<OrbitalSpace> tspace1_intb = transforms_bra[0]->space2();
786  Ref<OrbitalSpace> tspace2_intb = transforms_bra[0]->space4();
787  Ref<OrbitalSpace> tspace1_ket = transforms_ket[0]->space1();
788  Ref<OrbitalSpace> tspace2_ket = transforms_ket[0]->space3();
789  Ref<OrbitalSpace> tspace1_intk = transforms_ket[0]->space2();
790  Ref<OrbitalSpace> tspace2_intk = transforms_ket[0]->space4();
791  MPQC_ASSERT(tspace1_bra == space1_bra);
792  MPQC_ASSERT(tspace2_bra == space2_bra);
793  MPQC_ASSERT(tspace1_ket == space1_ket);
794  MPQC_ASSERT(tspace2_ket == space2_ket);
795  MPQC_ASSERT(tspace1_intb == space1_intb);
796  MPQC_ASSERT(tspace2_intb == space2_intb);
797  MPQC_ASSERT(tspace1_intk == space1_intk);
798  MPQC_ASSERT(tspace2_intk == space2_intk);
799  }
800 
801  //
802  // Contraction loops
803  //
804 
805  unsigned int fbraoffset = 0;
806  unsigned int fbraket = 0;
807  for (unsigned int fbra = 0; fbra < nbrasets; ++fbra) {
808  Ref<TwoBodyMOIntsTransform> tformb = transforms_bra[fbra];
809  const Ref<TwoBodyIntDescr>& intdescrb = tformb->intdescr();
810  const unsigned int intsetidx_bra = intdescrb->intset(tbint_type_bra);
811 
812  tformb->compute();
813  Ref<DistArray4> accumb = tformb->ints_distarray4();
814 
815  unsigned int fketoffset = 0;
816  for (unsigned int fket = 0; fket < nketsets; ++fket, ++fbraket) {
817  Ref<TwoBodyMOIntsTransform> tformk = transforms_ket[fket];
818  const Ref<TwoBodyIntDescr>& intdescrk = tformk->intdescr();
819  const unsigned int intsetidx_ket = intdescrk->intset(tbint_type_ket);
820 
821  tformk->compute();
822  Ref<DistArray4> accumk = tformk->ints_distarray4();
823 
824 
825  if (debug_ >= DefaultPrintThresholds::diagnostics) {
826  ExEnv::out0() << indent << "Using transforms " << tformb->name()
827  << " and " << tformk->name() << std::endl;
828  }
829 
830  sc::contract34(results_ref[fbraket],
831  scale,
832  accumb,
833  intsetidx_bra,
834  accumk,
835  intsetidx_ket,
836  debug_);
837 
838  if (antisymmetrize) {
839  sc::antisymmetrize(results_ref[fbraket]);
840  }
841 
842  if (accumulate)
843  sc::axpy(results_ref[fbraket], 1.0, results[fbraket]);
844 
845  //ExEnv::out0() << indent << "Accumb = " << accumb.pointer() << std::endl;
846  //ExEnv::out0() << indent << "Accumk = " << accumk.pointer() << std::endl;
847  //ExEnv::out0() << indent << "Accumb == Accumk : " << (accumb==accumk) << std::endl;
848  } // ket blocks
849 
850  } // bra blocks
851 
852  ExEnv::out0() << decindent;
853  ExEnv::out0() << indent << "Exited generic contraction (" << label << ")"
854  << std::endl;
855  tim_gen_tensor_contract.exit();
856 
857  }
858 
859 }
860 
861 #endif
862 
sc::DefaultPrintThresholds::diagnostics
static const unsigned int diagnostics
Additional diagnostics, e.g., transform progress, etc.
Definition: print.h:39
sc::R12IntEval::r12world
const Ref< R12WavefunctionWorld > & r12world() const
the R12World in which this object lives
Definition: r12int_eval.h:642
sc::TwoBodyOper::type
type
types of known two-body operators
Definition: operator.h:318
sc::ExEnv::out0
static std::ostream & out0()
Return an ostream that writes from node 0.
sc::DefaultPrintThresholds::mostO6
static const unsigned int mostO6
Print most o^6 quantities.
Definition: print.h:75
sc::DefaultPrintThresholds::allO2N2
static const unsigned int allO2N2
Print all o^2N^2 quantities.
Definition: print.h:65
sc::ExEnv::outn
static std::ostream & outn()
Return an ostream that writes from all nodes.
Definition: exenv.h:81
sc::antisymmetrize
void antisymmetrize(RefSCMatrix &Aanti, const RefSCMatrix &A, const Ref< OrbitalSpace > &bra, const Ref< OrbitalSpace > &ket, bool accumulate=false)
Antisymmetrizes 4-index quantity <ij|A|kl> -> <ij|A|kl> - <ij|A|lk> and saves to Aanti.
sc::contract34
void contract34(Ref< DistArray4 > &braket, double scale, const Ref< DistArray4 > &bra, unsigned int intsetidx_bra, const Ref< DistArray4 > &ket, unsigned int intsetidx_ket, int debug=0)
contracts ijxy ("bra") with klxy ("ket") to produce ijkl ("braket")
sc
Contains all MPQC code up to version 3.
Definition: mpqcin.h:14
sc::axpy
void axpy(const Ref< DistArray4 > &X, double a, const Ref< DistArray4 > &Y, double scale=1.0)
axpy followed by scaling: Y += a*X; Y *= scale.

Generated at Sun Jan 26 2020 23:23:58 for MPQC 3.0.0-alpha using the documentation package Doxygen 1.8.16.