MPQC 3.0.0-alpha
Loading...
Searching...
No Matches
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
41namespace 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
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();
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
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();
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
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
static const unsigned int allO2N2
Print all o^2N^2 quantities.
Definition print.h:65
static const unsigned int mostO6
Print most o^6 quantities.
Definition print.h:75
static const unsigned int diagnostics
Additional diagnostics, e.g., transform progress, etc.
Definition print.h:39
static std::ostream & outn()
Return an ostream that writes from all nodes.
Definition exenv.h:81
static std::ostream & out0()
Return an ostream that writes from node 0.
const Ref< R12WavefunctionWorld > & r12world() const
the R12World in which this object lives
Definition r12int_eval.h:642
Contains all MPQC code up to version 3.
Definition mpqcin.h:14
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.
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")
void symmetrize(const Ref< GPetiteList2 > &plist2, const Ref< Integral > &integral, const RefSymmSCMatrix &skel, const RefSymmSCMatrix &sym)
Uses plist2 to convert the "skeleton" matrix into the full matrix. Only applicable when the two basis...
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.
type
types of known two-body operators
Definition operator.h:318

Generated at Wed Sep 25 2024 02:45:29 for MPQC 3.0.0-alpha using the documentation package Doxygen 1.12.0.