MADNESS 0.10.1
funcimpl.h
Go to the documentation of this file.
1/*
2 This file is part of MADNESS.
3
4 Copyright (C) 2007,2010 Oak Ridge National Laboratory
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 For more information please contact:
21
22 Robert J. Harrison
23 Oak Ridge National Laboratory
24 One Bethel Valley Road
25 P.O. Box 2008, MS-6367
26
27 email: harrisonrj@ornl.gov
28 tel: 865-241-3937
29 fax: 865-572-0680
30*/
31
32#ifndef MADNESS_MRA_FUNCIMPL_H__INCLUDED
33#define MADNESS_MRA_FUNCIMPL_H__INCLUDED
34
35/// \file funcimpl.h
36/// \brief Provides FunctionCommonData, FunctionImpl and FunctionFactory
37
39#include <madness/world/print.h>
40#include <madness/misc/misc.h>
43
45#include <madness/mra/indexit.h>
46#include <madness/mra/key.h>
50
51#include <madness/mra/leafop.h>
52
53#include <array>
54#include <iostream>
55#include <type_traits>
56
57namespace madness {
58 template <typename T, std::size_t NDIM>
59 class DerivativeBase;
60
61 template<typename T, std::size_t NDIM>
62 class FunctionImpl;
63
64 template<typename T, std::size_t NDIM>
65 class FunctionNode;
66
67 template<typename T, std::size_t NDIM>
68 class Function;
69
70 template<typename T, std::size_t NDIM>
71 class FunctionFactory;
72
73 template<typename T, std::size_t NDIM, std::size_t MDIM>
75
76 template<int D>
78
79}
80
81namespace madness {
82
83
84 /// A simple process map
85 template<typename keyT>
86 class SimplePmap : public WorldDCPmapInterface<keyT> {
87 private:
88 const int nproc;
90
91 public:
92 SimplePmap(World& world) : nproc(world.nproc()), me(world.rank())
93 { }
94
95 ProcessID owner(const keyT& key) const {
96 if (key.level() == 0)
97 return 0;
98 else
99 return key.hash() % nproc;
100 }
101 };
102
103 /// A pmap that locates children on odd levels with their even level parents
104 template <typename keyT>
105 class LevelPmap : public WorldDCPmapInterface<keyT> {
106 private:
107 const int nproc;
108 public:
109 LevelPmap() : nproc(0) {};
110
111 LevelPmap(World& world) : nproc(world.nproc()) {}
112
113 /// Find the owner of a given key
114 ProcessID owner(const keyT& key) const {
115 Level n = key.level();
116 if (n == 0) return 0;
117 hashT hash;
118 if (n <= 3 || (n&0x1)) hash = key.hash();
119 else hash = key.parent().hash();
120 return hash%nproc;
121 }
122 };
123
124
125 /// Sentinel for norm_tree/dnorm_tree meaning "not computed". Overflows the screening
126 /// criterion, so nodes carrying it are descended rather than screened.
127 static constexpr double NORM_TREE_UNCOMPUTED = 1e300;
128
129 /// Safety margin on the tolerance of the screened multiplication: the criterion estimates
130 /// the neglected cross terms rather than bounding them. Tightening is cheap and buys
131 /// accuracy, hence the extra factor. Applied once, in mulXXvec.
132 static constexpr double MUL_SCREENING_SAFETY = 0.1;
133
134 /// FunctionNode holds the coefficients, etc., at each node of the 2^NDIM-tree
135 template<typename T, std::size_t NDIM>
137 public:
140 private:
141 // Should compile OK with these volatile but there should
142 // be no need to set as volatile since the container internally
143 // stores the entire entry as volatile
144
145 coeffT _coeffs; ///< The coefficients, if any
146 double _norm_tree; ///< After norm_tree will contain norm of sum coefficients summed up tree
147 double _dnorm_tree=NORM_TREE_UNCOMPUTED; ///< norm of the difference coefficients summed up the tree
148 bool _has_children; ///< True if there are children
149 coeffT buffer; ///< The coefficients, if any
150 double dnorm=-1.0; ///< norm of the d coefficients, also defined if there are no d coefficients
151 double snorm=-1.0; ///< norm of the s coefficients
152
153 public:
154 typedef WorldContainer<Key<NDIM> , FunctionNode<T, NDIM> > dcT; ///< Type of container holding the nodes
155 /// Default constructor makes node without coeff or children
159
160 /// Constructor from given coefficients with optional children
161
162 /// Note that only a shallow copy of the coeff are taken so
163 /// you should pass in a deep copy if you want the node to
164 /// take ownership.
165 explicit
169
170 explicit
174
175 explicit
176 FunctionNode(const coeffT& coeff, double norm_tree, double dnorm_tree, double snorm, double dnorm, bool has_children) :
178 }
179
182 _has_children(other._has_children), dnorm(other.dnorm), snorm(other.snorm) {
183 }
184
187 if (this != &other) {
188 coeff() = copy(other.coeff());
189 _norm_tree = other._norm_tree;
190 _dnorm_tree = other._dnorm_tree;
192 dnorm=other.dnorm;
193 snorm=other.snorm;
194 }
195 return *this;
196 }
197
198 /// Copy with possible type conversion of coefficients, copying all other state
199
200 /// Choose to not overload copy and type conversion operators
201 /// so there are no automatic type conversions.
202 template<typename Q>
207
208 /// Returns true if there are coefficients in this node
209 bool
210 has_coeff() const {
211 return _coeffs.has_data();
212 }
213
214
215 /// Returns true if this node has children
216 bool
217 has_children() const {
218 return _has_children;
219 }
220
221 /// Returns true if this does not have children
222 bool
223 is_leaf() const {
224 return !_has_children;
225 }
226
227 /// Returns true if this node is invalid (no coeffs and no children)
228 bool
229 is_invalid() const {
230 return !(has_coeff() || has_children());
231 }
232
233 /// Returns a non-const reference to the tensor containing the coeffs
234
235 /// Returns an empty tensor if there are no coefficients.
236 coeffT&
238 MADNESS_ASSERT(_coeffs.ndim() == -1 || (_coeffs.dim(0) <= 2
239 * MAXK && _coeffs.dim(0) >= 0));
240 return const_cast<coeffT&>(_coeffs);
241 }
242
243 /// Returns a const reference to the tensor containing the coeffs
244
245 /// Returns an empty tensor if there are no coefficeints.
246 const coeffT&
247 coeff() const {
248 return const_cast<const coeffT&>(_coeffs);
249 }
250
251 /// Returns the number of coefficients in this node
252 size_t size() const {
253 return _coeffs.size();
254 }
255
256 public:
257
258 /// reduces the rank of the coefficients (if applicable)
259 void reduceRank(const double& eps) {
260 _coeffs.reduce_rank(eps);
261 }
262
263 /// Sets \c has_children attribute to value of \c flag.
264 void set_has_children(bool flag) {
265 _has_children = flag;
266 }
267
268 /// Sets \c has_children attribute to true recurring up to ensure connected
270 //madness::print(" set_chi_recu: ", key, *this);
271 //PROFILE_MEMBER_FUNC(FunctionNode); // Too fine grain for routine profiling
272 if (!(has_children() || has_coeff() || key.level()==0)) {
273 // If node already knows it has children or it has
274 // coefficients then it must already be connected to
275 // its parent. If not, the node was probably just
276 // created for this operation and must be connected to
277 // its parent.
278 Key<NDIM> parent = key.parent();
279 // Task on next line used to be TaskAttributes::hipri()) ... but deferring execution of this
280 // makes sense since it is not urgent and lazy connection will likely mean that less forwarding
281 // will happen since the upper level task will have already made the connection.
282 const_cast<dcT&>(c).task(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
283 //const_cast<dcT&>(c).send(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
284 //madness::print(" set_chi_recu: forwarding",key,parent);
285 }
286 _has_children = true;
287 }
288
289 /// Sets \c has_children attribute to value of \c !flag
290 void set_is_leaf(bool flag) {
291 _has_children = !flag;
292 }
293
294 /// Takes a \em shallow copy of the coeff --- same as \c this->coeff()=coeff
295 void set_coeff(const coeffT& coeffs) {
296 coeff() = coeffs;
297 if ((_coeffs.has_data()) and ((_coeffs.dim(0) < 0) || (_coeffs.dim(0)>2*MAXK))) {
298 print("set_coeff: may have a problem");
299 print("set_coeff: coeff.dim[0] =", coeffs.dim(0), ", 2* MAXK =", 2*MAXK);
300 }
301 MADNESS_ASSERT(coeffs.dim(0)<=2*MAXK && coeffs.dim(0)>=0);
302 }
303
304 /// Clears the coefficients (has_coeff() will subsequently return false)
305 void clear_coeff() {
306 coeff()=coeffT();
307 }
308
309 /// Scale the coefficients of this node
310 template <typename Q>
311 void scale(Q a) {
312 _coeffs.scale(a);
313 }
314
315 /// Sets the value of norm_tree
318 }
319
320 /// Sets the value of dnorm_tree
321 void set_dnorm_tree(double dnorm_tree) {
322 _dnorm_tree = dnorm_tree;
323 }
324
325 /// Gets the value of norm_tree
326 double get_norm_tree() const {
327 return _norm_tree;
328 }
329
330 /// Gets the value of dnorm_tree
331 double get_dnorm_tree() const {
332 return _dnorm_tree;
333 }
334
335 /// return the precomputed norm of the (virtual) d coefficients
336 double get_dnorm() const {
337 return dnorm;
338 }
339
340 /// set the precomputed norm of the (virtual) s coefficients
341 void set_snorm(const double sn) {
342 snorm=sn;
343 }
344
345 /// set the precomputed norm of the (virtual) d coefficients
346 void set_dnorm(const double dn) {
347 dnorm=dn;
348 }
349
350 /// get the precomputed norm of the (virtual) s coefficients
351 double get_snorm() const {
352 return snorm;
353 }
354
355 /// Multiply the stored norms by a, for coefficients scaled by some q with |q| = a
356
357 /// The tree state survives scaling, and mul_sparse screens on these norms
358 /// without recomputing them. Norms that were never computed stay marked as such.
359 /// A negative norm_tree is a marker left by broaden and refine_op, not a norm.
360 void scale_norms(const double a) {
363 if (snorm >= 0.0) snorm *= a;
364 if (dnorm >= 0.0) dnorm *= a;
365 }
366
368 snorm = 0.0;
369 dnorm = 0.0;
370 if (coeff().size() == 0) { ;
371 } else if (coeff().dim(0) == cdata.vk[0]) {
372 snorm = coeff().normf();
373
374 } else if (coeff().is_full_tensor()) {
375 Tensor<T> c = copy(coeff().get_tensor());
376 snorm = c(cdata.s0).normf();
377 c(cdata.s0) = 0.0;
378 dnorm = c.normf();
379
380 } else if (coeff().is_svd_tensor()) {
381 coeffT c= coeff()(cdata.s0);
382 snorm = c.normf();
383 double norm = coeff().normf();
384 dnorm = sqrt(norm * norm - snorm * snorm);
385
386 } else {
387 MADNESS_EXCEPTION("cannot use compute_dnorm", 1);
388 }
389 }
390
391
392 /// General bi-linear operation --- this = this*alpha + other*beta
393
394 /// This/other may not have coefficients. Has_children will be
395 /// true in the result if either this/other have children.
396 template <typename Q, typename R>
397 void gaxpy_inplace(const T& alpha, const FunctionNode<Q,NDIM>& other, const R& beta) {
398 //PROFILE_MEMBER_FUNC(FuncNode); // Too fine grain for routine profiling
399 if (other.has_children())
400 _has_children = true;
401 if (has_coeff()) {
402 if (other.has_coeff()) {
403 coeff().gaxpy(alpha,other.coeff(),beta);
404 }
405 else {
406 coeff().scale(alpha);
407 }
408 }
409 else if (other.has_coeff()) {
410 coeff() = other.coeff()*beta; //? Is this the correct type conversion?
411 }
412 }
413
414 /// Accumulate inplace and if necessary connect node to parent
415 void accumulate2(const tensorT& t, const typename FunctionNode<T,NDIM>::dcT& c,
416 const Key<NDIM>& key) {
417 // double cpu0=cpu_time();
418 if (has_coeff()) {
419 MADNESS_ASSERT(coeff().is_full_tensor());
420 // if (coeff().type==TT_FULL) {
421 coeff() += coeffT(t,-1.0,TT_FULL);
422 // } else {
423 // tensorT cc=coeff().full_tensor_copy();;
424 // cc += t;
425 // coeff()=coeffT(cc,args);
426 // }
427 }
428 else {
429 // No coeff and no children means the node is newly
430 // created for this operation and therefore we must
431 // tell its parent that it exists.
432 coeff() = coeffT(t,-1.0,TT_FULL);
433 // coeff() = copy(t);
434 // coeff() = coeffT(t,args);
435 if ((!_has_children) && key.level()> 0) {
436 Key<NDIM> parent = key.parent();
437 if (c.is_local(parent))
438 const_cast<dcT&>(c).send(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
439 else
440 const_cast<dcT&>(c).task(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
441 }
442 }
443 //double cpu1=cpu_time();
444 }
445
446
447 /// Accumulate inplace and if necessary connect node to parent
448 void accumulate(const coeffT& t, const typename FunctionNode<T,NDIM>::dcT& c,
449 const Key<NDIM>& key, const TensorArgs& args, const bool do_svd_add) {
450 if (has_coeff()) {
451 if (do_svd_add) coeff().add_SVD(t,args.thresh);
452 else coeff().gaxpy(1.0,t,1.0);
453
454 if (buffer.rank()<coeff().rank()) {
455 if (buffer.has_data()) {
456 if (do_svd_add) buffer.add_SVD(coeff(),args.thresh);
457 else buffer.gaxpy(1.0,coeff(),1.0);
458 } else {
459 buffer=copy(coeff());
460 }
461 coeff()=coeffT();
462 }
463
464 } else {
465 // No coeff and no children means the node is newly
466 // created for this operation and therefore we must
467 // tell its parent that it exists.
468 coeff() = copy(t);
469 if ((!_has_children) && key.level()> 0) {
470 Key<NDIM> parent = key.parent();
471 if (c.is_local(parent))
472 const_cast<dcT&>(c).send(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
473 else
474 const_cast<dcT&>(c).task(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
475 }
476 }
477 }
478
479 void consolidate_buffer(const TensorArgs& args, const bool do_svd_add) {
480 if ((coeff().has_data()) and (buffer.has_data())) {
481 if (do_svd_add) coeff().add_SVD(buffer,args.thresh);
482 else coeff().gaxpy(1.0,buffer,1.0);
483 } else if (buffer.has_data()) {
484 coeff()=buffer;
485 }
486 buffer=coeffT();
487 }
488
489 T trace_conj(const FunctionNode<T,NDIM>& rhs) const {
490 return this->_coeffs.trace_conj((rhs._coeffs));
491 }
492
493 template <typename Archive>
494 void serialize(Archive& ar) {
495 // changing this list changes the on-disk format: bump FUNCTION_ARCHIVE_MAGIC
497 }
498
499 /// like operator<<(ostream&, const FunctionNode<T,NDIM>&) but
500 /// produces a sequence JSON-formatted key-value pairs
501 /// @warning enclose the output in curly braces to make
502 /// a valid JSON object
503 void print_json(std::ostream& s) const {
504 s << "\"has_coeff\":" << this->has_coeff()
505 << ",\"has_children\":" << this->has_children() << ",\"norm\":";
506 double norm = this->has_coeff() ? this->coeff().normf() : 0.0;
507 if (norm < 1e-12)
508 norm = 0.0;
509 double nt = this->get_norm_tree();
510 if (nt == NORM_TREE_UNCOMPUTED)
511 nt = 0.0;
512 s << norm << ",\"norm_tree\":" << nt << ",\"snorm\":"
513 << this->get_snorm() << ",\"dnorm\":" << this->get_dnorm()
514 << ",\"rank\":" << this->coeff().rank();
515 if (this->coeff().is_assigned())
516 s << ",\"dim\":" << this->coeff().dim(0);
517 }
518
519 };
520
521 template <typename T, std::size_t NDIM>
522 std::ostream& operator<<(std::ostream& s, const FunctionNode<T,NDIM>& node) {
523 s << "(has_coeff=" << node.has_coeff() << ", has_children=" << node.has_children() << ", norm=";
524 double norm = node.has_coeff() ? node.coeff().normf() : 0.0;
525 if (norm < 1e-12)
526 norm = 0.0;
527 double nt = node.get_norm_tree();
528 double dnt = node.get_dnorm_tree();
529 if (nt == NORM_TREE_UNCOMPUTED) nt = 0.0;
530 if (dnt == NORM_TREE_UNCOMPUTED) dnt = 0.0;
531 s << norm << ", norm_tree = " << nt << ", dnorm_tree = " << dnt << ", s/dnorm =" << node.get_snorm() << " " << node.get_dnorm() << "), rank="<< node.coeff().rank()<<")";
532 if (node.coeff().is_assigned()) s << " dim " << node.coeff().dim(0) << " ";
533 return s;
534 }
535
536
537 /// returns true if the result of a hartree_product is a leaf node (compute norm & error)
538 template<typename T, size_t NDIM>
540
543 long k;
544 bool do_error_leaf_op() const {return false;}
545
546 hartree_leaf_op() = default;
547 hartree_leaf_op(const implT* f, const long& k) : f(f), k(k) {}
548
549 /// no pre-determination
550 bool operator()(const Key<NDIM>& key) const {return false;}
551
552 /// no post-determination
553 bool operator()(const Key<NDIM>& key, const GenTensor<T>& coeff) const {
554 MADNESS_EXCEPTION("no post-determination in hartree_leaf_op",1);
555 return true;
556 }
557
558 /// post-determination: true if f is a leaf and the result is well-represented
559
560 /// @param[in] key the hi-dimensional key (breaks into keys for f and g)
561 /// @param[in] fcoeff coefficients of f of its appropriate key in NS form
562 /// @param[in] gcoeff coefficients of g of its appropriate key in NS form
563 bool operator()(const Key<NDIM>& key, const Tensor<T>& fcoeff, const Tensor<T>& gcoeff) const {
564
565 if (key.level()<2) return false;
566 Slice s = Slice(0,k-1);
567 std::vector<Slice> s0(NDIM/2,s);
568
569 const double tol=f->get_thresh();
570 const double thresh=f->truncate_tol(tol, key)*0.3; // custom factor to "ensure" accuracy
571 // include the wavelets in the norm, makes it much more accurate
572 const double fnorm=fcoeff.normf();
573 const double gnorm=gcoeff.normf();
574
575 // if the final norm is small, perform the hartree product and return
576 const double norm=fnorm*gnorm; // computing the outer product
577 if (norm < thresh) return true;
578
579 // norm of the scaling function coefficients
580 const double sfnorm=fcoeff(s0).normf();
581 const double sgnorm=gcoeff(s0).normf();
582
583 // get the error of both functions and of the pair function;
584 // need the abs for numerics: sfnorm might be equal fnorm.
585 const double ferror=sqrt(std::abs(fnorm*fnorm-sfnorm*sfnorm));
586 const double gerror=sqrt(std::abs(gnorm*gnorm-sgnorm*sgnorm));
587
588 // if the expected error is small, perform the hartree product and return
589 const double error=fnorm*gerror + ferror*gnorm + ferror*gerror;
590 // const double error=sqrt(fnorm*fnorm*gnorm*gnorm - sfnorm*sfnorm*sgnorm*sgnorm);
591
592 if (error < thresh) return true;
593 return false;
594 }
595 template <typename Archive> void serialize (Archive& ar) {
596 ar & f & k;
597 }
598 };
599
600
601 /// returns true if the sum of hartree products is a leaf node (compute combined norm & error)
602
603 /// Generalizes hartree_leaf_op for a sum of matched pairs: Σ_i f_i(1) * g_i(2)
604 /// Uses the triangle inequality bound over all pairs to determine leafness.
605 template<typename T, size_t NDIM>
607
610 long k;
611 bool do_error_leaf_op() const {return false;}
612
614 hartree_leaf_op_vec(const implT* f, const long& k) : f(f), k(k) {}
615
616 /// no pre-determination
617 bool operator()(const Key<NDIM>& key) const {return false;}
618
619 /// post-determination for vector of pairs: true if well-represented
620
621 /// @param[in] key the hi-dimensional key
622 /// @param[in] fcoeffs vector of coefficients of f_i in NS form
623 /// @param[in] gcoeffs vector of coefficients of g_i in NS form
624 bool operator()(const Key<NDIM>& key, const std::vector<Tensor<T>>& fcoeffs,
625 const std::vector<Tensor<T>>& gcoeffs) const {
626
627 MADNESS_ASSERT(fcoeffs.size()==gcoeffs.size());
628 if (key.level()<2) return false;
629 Slice s = Slice(0,k-1);
630 std::vector<Slice> s0(NDIM/2,s);
631
632 const double tol=f->get_thresh();
633 const double thresh=f->truncate_tol(tol, key)*0.3;
634
635 // triangle inequality bound: Σ_i ||f_i|| * ||g_i||
636 double total_norm=0.0;
637 double total_error=0.0;
638 for (size_t i=0; i<fcoeffs.size(); ++i) {
639 const double fnorm=fcoeffs[i].normf();
640 const double gnorm=gcoeffs[i].normf();
641
642 total_norm += fnorm*gnorm;
643
644 const double sfnorm=fcoeffs[i](s0).normf();
645 const double sgnorm=gcoeffs[i](s0).normf();
646 const double ferror=sqrt(std::abs(fnorm*fnorm-sfnorm*sfnorm));
647 const double gerror=sqrt(std::abs(gnorm*gnorm-sgnorm*sgnorm));
648 total_error += fnorm*gerror + ferror*gnorm + ferror*gerror;
649 }
650
651 if (total_norm < thresh) return true;
652 if (total_error < thresh) return true;
653 return false;
654 }
655
656 template <typename Archive> void serialize (Archive& ar) {
657 ar & f & k;
658 }
659 };
660
661 /// returns true if the result of the convolution operator op with some provided
662 /// coefficients will be small
663 template<typename T, size_t NDIM, typename opT>
664 struct op_leaf_op {
666
667 const opT* op; ///< the convolution operator
668 const implT* f; ///< the source or result function, needed for truncate_tol
669 bool do_error_leaf_op() const {return true;}
670
671 op_leaf_op() = default;
672 op_leaf_op(const opT* op, const implT* f) : op(op), f(f) {}
673
674 /// pre-determination: we can't know if this will be a leaf node before we got the final coeffs
675 bool operator()(const Key<NDIM>& key) const {return true;}
676
677 /// post-determination: return true if operator and coefficient norms are small
678 bool operator()(const Key<NDIM>& key, const GenTensor<T>& coeff) const {
679 if (key.level()<2) return false;
680 const double cnorm=coeff.normf();
681 return this->operator()(key,cnorm);
682 }
683
684 /// post-determination: return true if operator and coefficient norms are small
685 bool operator()(const Key<NDIM>& key, const double& cnorm) const {
686 if (key.level()<2) return false;
687
688 typedef Key<opT::opdim> opkeyT;
689 const opkeyT source=op->get_source_key(key);
690
691 const double thresh=f->truncate_tol(f->get_thresh(),key);
692 const double opnorm = op->norm_bound(key.level(), source); // the operator's largest block at this level
693 const double norm=opnorm*cnorm;
694 return norm<thresh;
695
696 }
697
698 template <typename Archive> void serialize (Archive& ar) {
699 ar & op & f;
700 }
701
702 };
703
704
705 /// returns true if the result of a hartree_product is a leaf node
706 /// criteria are error, norm and its effect on a convolution operator
707 template<typename T, size_t NDIM, size_t LDIM, typename opT>
709
712
714 const implL* g; // for use of its cdata only
715 const opT* op;
716 bool do_error_leaf_op() const {return false;}
717
719 hartree_convolute_leaf_op(const implT* f, const implL* g, const opT* op)
720 : f(f), g(g), op(op) {}
721
722 /// no pre-determination
723 bool operator()(const Key<NDIM>& key) const {return true;}
724
725 /// no post-determination
726 bool operator()(const Key<NDIM>& key, const GenTensor<T>& coeff) const {
727 MADNESS_EXCEPTION("no post-determination in hartree_convolute_leaf_op",1);
728 return true;
729 }
730
731 /// post-determination: true if f is a leaf and the result is well-represented
732
733 /// @param[in] key the hi-dimensional key (breaks into keys for f and g)
734 /// @param[in] fcoeff coefficients of f of its appropriate key in NS form
735 /// @param[in] gcoeff coefficients of g of its appropriate key in NS form
736 bool operator()(const Key<NDIM>& key, const Tensor<T>& fcoeff, const Tensor<T>& gcoeff) const {
737 // bool operator()(const Key<NDIM>& key, const GenTensor<T>& coeff) const {
738
739 if (key.level()<2) return false;
740
741 const double tol=f->get_thresh();
742 const double thresh=f->truncate_tol(tol, key);
743 // include the wavelets in the norm, makes it much more accurate
744 const double fnorm=fcoeff.normf();
745 const double gnorm=gcoeff.normf();
746
747 // norm of the scaling function coefficients
748 const double sfnorm=fcoeff(g->get_cdata().s0).normf();
749 const double sgnorm=gcoeff(g->get_cdata().s0).normf();
750
751 // if the final norm is small, perform the hartree product and return
752 const double norm=fnorm*gnorm; // computing the outer product
753 if (norm < thresh) return true;
754
755 // get the error of both functions and of the pair function
756 const double ferror=sqrt(fnorm*fnorm-sfnorm*sfnorm);
757 const double gerror=sqrt(gnorm*gnorm-sgnorm*sgnorm);
758
759 // if the expected error is small, perform the hartree product and return
760 const double error=fnorm*gerror + ferror*gnorm + ferror*gerror;
761 if (error < thresh) return true;
762
763 // now check if the norm of this and the norm of the operator are significant
764 const double opnorm = op->norm_bound(key.level(), key); // the operator's largest block at this level
765 const double final_norm=opnorm*sfnorm*sgnorm;
766 if (final_norm < thresh) return true;
767
768 return false;
769 }
770 template <typename Archive> void serialize (Archive& ar) {
771 ar & f & op;
772 }
773 };
774
775 template<typename T, size_t NDIM>
776 struct noop {
777 void operator()(const Key<NDIM>& key, const GenTensor<T>& coeff, const bool& is_leaf) const {}
778 bool operator()(const Key<NDIM>& key, const GenTensor<T>& fcoeff, const GenTensor<T>& gcoeff) const {
779 MADNESS_EXCEPTION("in noop::operator()",1);
780 return true;
781 }
782 template <typename Archive> void serialize (Archive& ar) {}
783
784 };
785
786 /// insert/replaces the coefficients into the function
787 template<typename T, std::size_t NDIM>
788 struct insert_op {
793
797 insert_op(const insert_op& other) : impl(other.impl) {}
798 void operator()(const keyT& key, const coeffT& coeff, const bool& is_leaf) const {
799 MADNESS_ASSERT(impl->get_coeffs().is_local(key));
800 impl->get_coeffs().replace(key,nodeT(coeff,not is_leaf));
801 }
802 template <typename Archive> void serialize (Archive& ar) {
803 ar & impl;
804 }
805
806 };
807
808 /// inserts/accumulates coefficients into impl's tree
809
810 /// NOTE: will use buffer and will need consolidation after operation ended !! NOTE !!
811 template<typename T, std::size_t NDIM>
815
817 accumulate_op() = default;
819 accumulate_op(const accumulate_op& other) = default;
820 void operator()(const Key<NDIM>& key, const coeffT& coeff, const bool& is_leaf) const {
821 if (coeff.has_data())
822 impl->get_coeffs().task(key, &nodeT::accumulate, coeff, impl->get_coeffs(), key, impl->get_tensor_args(), false);
823 }
824 template <typename Archive> void serialize (Archive& ar) {
825 ar & impl;
826 }
827
828 };
829
830
831template<size_t NDIM>
832 struct true_op {
833
834 template<typename T>
835 bool operator()(const Key<NDIM>& key, const T& t) const {return true;}
836
837 template<typename T, typename R>
838 bool operator()(const Key<NDIM>& key, const T& t, const R& r) const {return true;}
839 template <typename Archive> void serialize (Archive& ar) {}
840
841 };
842
843 /// shallow-copy, pared-down version of FunctionNode, for special purpose only
844 template<typename T, std::size_t NDIM>
845 struct ShallowNode {
849 double dnorm=-1.0;
852 : _coeffs(node.coeff()), _has_children(node.has_children()),
853 dnorm(node.get_dnorm()) {}
855 : _coeffs(node.coeff()), _has_children(node._has_children),
856 dnorm(node.dnorm) {}
857
858 const coeffT& coeff() const {return _coeffs;}
859 coeffT& coeff() {return _coeffs;}
860 bool has_children() const {return _has_children;}
861 bool is_leaf() const {return not _has_children;}
862 template <typename Archive>
863 void serialize(Archive& ar) {
864 ar & coeff() & _has_children & dnorm;
865 }
866 };
867
868
869 /// a class to track where relevant (parent) coeffs are
870
871 /// E.g. if a 6D function is composed of two 3D functions their coefficients must be tracked.
872 /// We might need coeffs from a box that does not exist, and to avoid searching for
873 /// parents we track which are their required respective boxes.
874 /// - CoeffTracker will refer either to a requested key, if it exists, or to its
875 /// outermost parent.
876 /// - Children must be made in sequential order to be able to track correctly.
877 ///
878 /// Usage: 1. make the child of a given CoeffTracker.
879 /// If the parent CoeffTracker refers to a leaf node (flag is_leaf)
880 /// the child will refer to the same node. Otherwise it will refer
881 /// to the child node.
882 /// 2. retrieve its coefficients (possible communication/ returns a Future).
883 /// Member variable key always refers to an existing node,
884 /// so we can fetch it. Once we have the node we can determine
885 /// if it has children which allows us to make a child (see 1. )
886 template<typename T, size_t NDIM>
888
892 typedef std::pair<Key<NDIM>,ShallowNode<T,NDIM> > datumT;
894
895 /// the funcimpl that has the coeffs
896 const implT* impl;
897 /// the current key, which must exists in impl
899 /// flag if key is a leaf node
901 /// the coefficients belonging to key
903 /// norm of d coefficients corresponding to key
904 double dnorm_=-1.0;
905
906 public:
907
908 /// default ctor
909 CoeffTracker() : impl(), key_(0), is_leaf_(unknown), coeff_() {} // Initialize key to avoid warnings of possible unititialied use
910
911 /// the initial ctor making the root key
913 if (impl) key_=impl->get_cdata().key0;
914 }
915
916 /// ctor with a pair<keyT,nodeT>
917 explicit CoeffTracker(const CoeffTracker& other, const datumT& datum)
918 : impl(other.impl), key_(other.key_), coeff_(datum.second.coeff()),
919 dnorm_(datum.second.dnorm) {
920 if (datum.second.is_leaf()) is_leaf_=yes;
921 else is_leaf_=no;
922 }
923
924 /// copy ctor
925 CoeffTracker(const CoeffTracker& other) : impl(other.impl), key_(other.key_),
926 is_leaf_(other.is_leaf_), coeff_(other.coeff_), dnorm_(other.dnorm_) {};
927
928 CoeffTracker& operator=(const CoeffTracker& other) = default;
929
930 /// const reference to impl
931 const implT* get_impl() const {return impl;}
932
933 /// const reference to the coeffs
934 const coeffT& coeff() const {return coeff_;}
935
936 /// const reference to the key
937 const keyT& key() const {return key_;}
938
939 /// return the coefficients belonging to the passed-in key
940
941 /// if key equals tracked key just return the coeffs, otherwise
942 /// make the child coefficients.
943 /// @param[in] key return coeffs corresponding to this key
944 /// @return coefficients belonging to key
945 coeffT coeff(const keyT& key) const {
947 if (impl->is_compressed() or impl->is_nonstandard() or
948 impl->is_nonstandard_with_leaves())
949 return impl->parent_to_child_NS(key,key_,coeff_);
950 return impl->parent_to_child(coeff_,key_,key);
951 }
952
953 /// return the s and dnorm belonging to the passed-in key
954 double dnorm(const keyT& key) const {
955 if (key==key_) return dnorm_;
956 MADNESS_ASSERT(key.is_child_of(key_));
957 return 0.0;
958 }
959
960 /// const reference to is_leaf flag
961 const LeafStatus& is_leaf() const {return is_leaf_;}
962
963 /// make a child of this, ignoring the coeffs
964 CoeffTracker make_child(const keyT& child) const {
965
966 // fast return
967 if ((not impl) or impl->is_on_demand()) return CoeffTracker(*this);
968
969 // can't make a child without knowing if this is a leaf -- activate first
971
972 CoeffTracker result;
973 if (impl) {
974 result.impl=impl;
975 if (is_leaf_==yes) result.key_=key_;
976 if (is_leaf_==no) {
977 result.key_=child;
978 // check if child is direct descendent of this, but root node is special case
979 if (child.level()>0) MADNESS_ASSERT(result.key().level()==key().level()+1);
980 }
981 result.is_leaf_=unknown;
982 }
983 return result;
984 }
985
986 /// find the coefficients
987
988 /// this involves communication to a remote node
989 /// @return a Future<CoeffTracker> with the coefficients that key refers to
991
992 // fast return
993 if (not impl) return Future<CoeffTracker>(CoeffTracker());
994 if (impl->is_on_demand()) return Future<CoeffTracker>(CoeffTracker(impl));
995
996 // this will return a <keyT,nodeT> from a remote node
997 ProcessID p=impl->get_coeffs().owner(key());
999
1000 // construct a new CoeffTracker locally
1001 return impl->world.taskq.add(*const_cast<CoeffTracker*> (this),
1002 &CoeffTracker::forward_ctor,*this,datum1);
1003 }
1004
1005 private:
1006 /// taskq-compatible forwarding to the ctor
1007 CoeffTracker forward_ctor(const CoeffTracker& other, const datumT& datum) const {
1008 return CoeffTracker(other,datum);
1009 }
1010
1011 public:
1012 /// serialization
1013 template <typename Archive> void serialize(const Archive& ar) {
1014 int il=int(is_leaf_);
1015 ar & impl & key_ & il & coeff_ & dnorm_;
1016 is_leaf_=LeafStatus(il);
1017 }
1018 };
1019
1020 template<typename T, std::size_t NDIM>
1021 std::ostream&
1022 operator<<(std::ostream& s, const CoeffTracker<T,NDIM>& ct) {
1023 s << ct.key() << ct.is_leaf() << " " << ct.get_impl();
1024 return s;
1025 }
1026
1027 /// FunctionImpl holds all Function state to facilitate shallow copy semantics
1028
1029 /// Since Function assignment and copy constructors are shallow it
1030 /// greatly simplifies maintaining consistent state to have all
1031 /// (permanent) state encapsulated in a single class. The state
1032 /// is shared between instances using a shared_ptr<FunctionImpl>.
1033 ///
1034 /// The FunctionImpl inherits all of the functionality of WorldContainer
1035 /// (to store the coefficients) and WorldObject<WorldContainer> (used
1036 /// for RMI and for its unqiue id).
1037 ///
1038 /// The class methods are public to avoid painful multiple friend template
1039 /// declarations for Function and FunctionImpl ... but this trust should not be
1040 /// abused ... NOTHING except FunctionImpl methods should mess with FunctionImplData.
1041 /// The LB stuff might have to be an exception.
1042 template <typename T, std::size_t NDIM>
1043 class FunctionImpl : public WorldObject< FunctionImpl<T,NDIM> > {
1044 private:
1045 typedef WorldObject< FunctionImpl<T,NDIM> > woT; ///< Base class world object type
1046 public:
1047 typedef T typeT;
1048 typedef FunctionImpl<T,NDIM> implT; ///< Type of this class (implementation)
1049 typedef std::shared_ptr< FunctionImpl<T,NDIM> > pimplT; ///< pointer to this class
1050 typedef Tensor<T> tensorT; ///< Type of tensor for anything but to hold coeffs
1051 typedef Vector<Translation,NDIM> tranT; ///< Type of array holding translation
1052 typedef Key<NDIM> keyT; ///< Type of key
1053 typedef FunctionNode<T,NDIM> nodeT; ///< Type of node
1054 typedef GenTensor<T> coeffT; ///< Type of tensor used to hold coeffs
1055 typedef WorldContainer<keyT,nodeT> dcT; ///< Type of container holding the coefficients
1056 typedef std::pair<const keyT,nodeT> datumT; ///< Type of entry in container
1057 typedef Vector<double,NDIM> coordT; ///< Type of vector holding coordinates
1058
1059 //template <typename Q, int D> friend class Function;
1060 template <typename Q, std::size_t D> friend class FunctionImpl;
1061
1063
1064 /// getter
1067 const std::vector<Vector<double,NDIM> >& get_special_points()const{return special_points;}
1068
1069 private:
1070 int k; ///< Wavelet order
1071 double thresh; ///< Screening threshold
1072 int initial_level; ///< Initial level for refinement
1073 int special_level; ///< Minimium level for refinement on special points
1074 std::vector<Vector<double,NDIM> > special_points; ///< special points for further refinement (needed for composite functions or multiplication)
1075 const Tensor<double> cell; ///< the size of the root cell in each dimension, unchangeable
1076 int max_refine_level; ///< Do not refine below this level
1077 int truncate_mode; ///< 0=default=(|d|<thresh), 1=(|d|<thresh/2^n), 2=(|d|<thresh/4^n);
1078 bool autorefine; ///< If true, autorefine where appropriate
1079 bool truncate_on_project; ///< If true projection inserts at level n-1 not n
1080 TensorArgs targs; ///< type of tensor to be used in the FunctionNodes
1081
1083
1084 std::shared_ptr< FunctionFunctorInterface<T,NDIM> > functor;
1086
1087 dcT coeffs; ///< The coefficients
1088
1089 /// Neighbor coefficients pushed here by whoever owns them; null until something stages.
1090
1091 /// Values mirror what `sock_it_to_me` returns for the same key: coefficients for a same-level
1092 /// leaf, empty for an interior node, absent when the neighbor is coarser and the consumer
1093 /// must walk up. Which nodes get pushed is the operator's business, not the table's --- see
1094 /// `DerivativeBase::stage_halo`.
1095 ///
1096 /// Allocated on the first push, so functions that never stage one do not carry it: a
1097 /// `ConcurrentHashMap` default-constructs 1021 bins, ~32 kB per function.
1098 mutable std::atomic<ConcurrentHashMap<keyT,coeffT>*> neighbor_halo_{nullptr};
1099
1100 // Disable the default copy constructor
1102
1103 public:
1104 /// Is a neighbor halo staged on this function?
1105 bool halo_enabled() const {
1106 return neighbor_halo_.load(std::memory_order_acquire) != nullptr;
1107 }
1108
1109 /// Discard the neighbor halo, freeing the staged coefficients.
1110
1111 /// Requires a quiescent window: it frees a table that `halo_probe` may be reading.
1112 void halo_clear() const {
1113 delete neighbor_halo_.exchange(nullptr, std::memory_order_acq_rel);
1114 }
1115
1116 /// How many neighbor nodes are staged on this rank; zero if no halo.
1117 std::size_t halo_size() const {
1118 const auto* h = neighbor_halo_.load(std::memory_order_acquire);
1119 return h ? h->size() : 0;
1120 }
1121
1122 /// Insert pushed neighbor nodes into the halo; runs as a task, concurrently with other pushes.
1123
1124 /// Allocates the table on the first push, so staging needs no collective set-up.
1125 void receive_halo(const std::vector<std::pair<keyT,coeffT> >& buf) const {
1126 auto* h = neighbor_halo_.load(std::memory_order_acquire);
1127 if (!h) {
1128 auto* fresh = new ConcurrentHashMap<keyT,coeffT>();
1129 if (neighbor_halo_.compare_exchange_strong(h, fresh, std::memory_order_acq_rel,
1130 std::memory_order_acquire))
1131 h = fresh;
1132 else
1133 delete fresh; // lost the race; the failed CAS put the winner's table in h
1134 }
1135 for (const auto& kv : buf) {
1137 (void) h->insert(acc, kv.first);
1138 acc->second = kv.second;
1139 }
1140 }
1141
1142 /// Look up a staged neighbor; on a hit copy its coefficients, which are empty for an interior node.
1143 bool halo_probe(const keyT& key, coeffT& out) const {
1144 const auto* h = neighbor_halo_.load(std::memory_order_acquire);
1145 if (!h) return false;
1147 if (h->find(acc, key)) { out = acc->second; return true; }
1148 return false;
1149 }
1150
1159
1160 /// Initialize function impl from data in factory
1162 : WorldObject<implT>(factory._world)
1163 , world(factory._world)
1164 , k(factory._k)
1165 , thresh(factory._thresh)
1166 , initial_level(factory._initial_level)
1167 , special_level(factory._special_level)
1168 , special_points(factory._special_points)
1170 , max_refine_level(factory._max_refine_level)
1171 , truncate_mode(factory._truncate_mode)
1172 , autorefine(factory._autorefine)
1173 , truncate_on_project(factory._truncate_on_project)
1174 , targs(factory._thresh,FunctionDefaults<NDIM>::get_tensor_type())
1175 , cdata(FunctionCommonData<T,NDIM>::get(k))
1176 , functor(factory.get_functor())
1177 , tree_state(factory._tree_state)
1178 , coeffs(world,factory._pmap,false)
1179 //, bc(factory._bc)
1180 {
1181 // PROFILE_MEMBER_FUNC(FunctionImpl); // No need to profile this
1182 // !!! Ensure that all local state is correctly formed
1183 // before invoking process_pending for the coeffs and
1184 // for this. Otherwise, there is a race condition.
1185 MADNESS_ASSERT(k>0 && k<=MAXK);
1186
1187 bool empty = (factory._empty or is_on_demand());
1188 bool do_refine = factory._refine;
1189
1190 if (do_refine)
1191 initial_level = std::max(0,initial_level - 1);
1192
1193 if (empty) { // Do not set any coefficients at all
1194 // additional functors are only evaluated on-demand
1195 } else if (functor) { // Project function and optionally refine
1197 // set the union of the special points of functor and the ones explicitly given to FunctionFactory
1198 std::vector<coordT> functor_special_points=functor->special_points();
1199 if (!functor_special_points.empty()) special_points.insert(special_points.end(), functor_special_points.begin(), functor_special_points.end());
1200 // near special points refine as deeply as requested by the factory AND the functor
1201 special_level = std::max(special_level, functor->special_level());
1202
1203 typename dcT::const_iterator end = coeffs.end();
1204 for (typename dcT::const_iterator it=coeffs.begin(); it!=end; ++it) {
1205 if (it->second.is_leaf())
1206 woT::task(coeffs.owner(it->first), &implT::project_refine_op, it->first, do_refine,
1208 }
1209 }
1210 else { // Set as if a zero function
1211 initial_level = 1;
1213 }
1214
1215 coeffs.process_pending();
1216 this->process_pending();
1217 if (factory._fence && (functor || !empty)) world.gop.fence();
1218 }
1219
1220 /// Copy constructor
1221
1222 /// Allocates a \em new function in preparation for a deep copy
1223 ///
1224 /// By default takes pmap from other but can also specify a different pmap.
1225 /// Does \em not copy the coefficients ... creates an empty container.
1226 template <typename Q>
1228 const std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > >& pmap,
1229 bool dozero) : FunctionImpl(other.world, other, pmap, dozero) {
1230 }
1231
1232 /// Copy constructor
1233
1234 /// Allocates a \em new function in preparation for a deep copy
1235 ///
1236 /// By default takes pmap from other but can also specify a different pmap.
1237 /// Does \em not copy the coefficients ... creates an empty container.
1238 ///
1239 /// uses a different world for the new function
1240 template <typename Q>
1242 const FunctionImpl<Q,NDIM>& other,
1243 const std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > >& pmap,
1244 bool dozero)
1246 , world(world)
1247 , k(other.k)
1248 , thresh(other.thresh)
1252 , cell(other.cell)
1255 , autorefine(other.autorefine)
1257 , targs(other.targs)
1258 , cdata(FunctionCommonData<T,NDIM>::get(k))
1259 , functor()
1260 , tree_state(other.tree_state)
1261 , coeffs(world, pmap ? pmap : other.coeffs.get_pmap())
1262 {
1263 if (dozero) {
1264 initial_level = 1;
1266 //world.gop.fence(); <<<<<<<<<<<<<<<<<<<<<< needs a fence argument
1267 }
1268 coeffs.process_pending();
1269 this->process_pending();
1270 }
1271
1272 virtual ~FunctionImpl() { halo_clear(); }
1273
1274 const std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > >& get_pmap() const;
1275
1276 void replicate(bool fence=true) {
1277 coeffs.replicate(fence);
1278 }
1279
1280 void replicate_on_hosts(bool fence=true) {
1281 coeffs.replicate_on_hosts(fence);
1282 }
1283
1284 // remove all coeffs that are not local according to pmap
1285 void undo_replicate(bool fence=true) {
1286 std::list<keyT> keys;
1287 for (const auto& [key, node] : coeffs) if (not coeffs.is_local(key)) keys.push_back(key);
1288 for (const auto& key : keys) coeffs.erase(key);
1289 if (fence) world.gop.fence();
1290 }
1291
1292 void distribute(std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > > newmap) const {
1293 auto currentmap=coeffs.get_pmap();
1294 currentmap->redistribute(world,newmap);
1295 }
1296
1297 /// Copy coeffs from other into self
1298
1299 /// this and other might live in different worlds
1300 template <typename Q>
1301 void copy_coeffs(const FunctionImpl<Q,NDIM>& other, bool fence) {
1302 if (world.id()==other.world.id())
1303 copy_coeffs_same_world(other,false);
1304 else
1306 if (fence) world.gop.fence();
1307 }
1308
1309 /// Copy coefficients from other funcimpl with possibly different world and on a different node
1310 template<typename Q>
1312
1313 // copy coeffs from (a subset of) other's world
1314
1315 // single-owner pmap: fetch from that rank only -- the all-ranks poll costs
1316 // N-1 empty round-trips queued behind the owner's compute
1317 const ProcessID single_owner = other.get_pmap()->single_owner();
1318 if (single_owner >= 0) {
1319 copy_remote_coeffs_from_pid<Q>(single_owner, other);
1320
1321 // if other's data is distributed, we need to fetch from all ranks
1322 } else if (other.get_coeffs().is_distributed()) {
1323 for (ProcessID pid=0; pid<other.world.size(); ++pid) {
1325 }
1326
1327 // if other's data is replicated, all coeffs are on the rank that owns key0
1328 } else if (other.get_coeffs().is_replicated() or other.get_coeffs().is_host_replicated()) {
1329 auto key0=other.cdata.key0;
1330 copy_remote_coeffs_from_pid<Q>(other.get_pmap()->owner(key0), other);
1331 }
1332 }
1333
1334 /// Copy coefficients from other funcimpl with possibly different world and on a different node
1335 /// to this
1336 template <typename Q>
1338 typedef FunctionImpl<Q,NDIM> implQ; ///< Type of this class (implementation)
1339 if (pid == other.world.rank()) {
1340 // the shard is local: copy nodes directly, no serialize round-trip
1341 copy_coeffs_same_world(other, false);
1342 } else {
1343 auto v=other.task(pid, &implQ::serialize_remote_coeffs);
1344 world.taskq.add(*this, &implT::insert_serialized_coeffs,v);
1345 }
1346 }
1347
1348 /// invoked by copy_remote_coeffs_from_pid to serialize *local* coeffs
1349 std::vector<unsigned char> serialize_remote_coeffs() {
1350 std::vector<unsigned char> v;
1352 ar & get_coeffs();
1353 return v;
1354 }
1355
1356 /// insert coeffs from vector archive into this
1357 void insert_serialized_coeffs(std::vector<unsigned char>& v) {
1359 ar & get_coeffs();
1360 }
1361
1362 /// Copy coeffs from other into self
1363 template <typename Q>
1364 void copy_coeffs_same_world(const FunctionImpl<Q,NDIM>& other, bool fence) {
1365 for (const auto& [key, node] : other.coeffs) { // iterate over all entries in other
1366 coeffs.replace(key,node. template convert<T>());
1367 }
1368 if (fence)
1369 world.gop.fence();
1370 }
1371
1372 /// perform inplace gaxpy: this = alpha*this + beta*other
1373 /// @param[in] alpha prefactor for this
1374 /// @param[in] beta prefactor for other
1375 /// @param[in] g the other function, reconstructed
1376 /// @return *this = alpha*this + beta*other, in either reconstructed or redundant_after_merge state
1377 template<typename Q, typename R>
1378 void gaxpy_inplace_reconstructed(const T& alpha, const FunctionImpl<Q,NDIM>& g, const R& beta, const bool fence) {
1379 // merge g's tree into this' tree
1380 gaxpy_inplace(alpha,g,beta,fence);
1382 // this->merge_trees(beta,g,alpha,fence);
1383 // tree is now redundant_after_merge
1384 // sum down the sum coeffs into the leafs if possible to keep the state most clean
1385 if (fence) sum_down(fence);
1386 }
1387
1388// /// merge the trees of this and other, while multiplying them with the alpha or beta, resp
1389//
1390// /// first step in an inplace gaxpy operation for reconstructed functions; assuming the same
1391// /// distribution for this and other
1392//
1393// /// on output, *this = alpha* *this + beta * other
1394// /// @param[in] alpha prefactor for this
1395// /// @param[in] beta prefactor for other
1396// /// @param[in] other the other function, reconstructed
1397// template<typename Q, typename R>
1398// void merge_trees(const T alpha, const FunctionImpl<Q,NDIM>& other, const R beta, const bool fence=true) {
1399// MADNESS_ASSERT(get_pmap() == other.get_pmap());
1400// this->set_tree_state(redundant_after_merge);
1401// other.flo_unary_op_node_inplace(do_merge_trees<Q,R>(alpha,beta,*this),fence);
1402// }
1403
1404 /// merge the trees of this and other, while multiplying them with the alpha or beta, resp
1405
1406 /// result and rhs do not have to have the same distribution or live in the same world
1407 /// result+=alpha* this
1408 /// @param[in] alpha prefactor for this
1409 template<typename Q, typename R>
1410 void accumulate_trees(FunctionImpl<Q,NDIM>& result, const R alpha, const bool fence=true) const {
1411 flo_unary_op_node_inplace(do_accumulate_trees<Q,R>(result,alpha),fence);
1412 }
1413
1414 /// perform: this= alpha*f + beta*g, invoked by result
1415
1416 /// f and g are reconstructed, so we can save on the compress operation,
1417 /// walk down the joint tree, and add leaf coefficients; effectively refines
1418 /// to common finest level.
1419
1420 /// nothing returned, but leaves this's tree reconstructed and as sum of f and g
1421 /// @param[in] alpha prefactor for f
1422 /// @param[in] f first addend
1423 /// @param[in] beta prefactor for g
1424 /// @param[in] g second addend
1425 void gaxpy_oop_reconstructed(const double alpha, const implT& f,
1426 const double beta, const implT& g, const bool fence);
1427
1428 /// functor for the gaxpy_inplace method
1429 template <typename Q, typename R>
1432 FunctionImpl<T,NDIM>* f; ///< prefactor for current function impl
1433 T alpha; ///< the current function impl
1434 R beta; ///< prefactor for other function impl
1435 do_gaxpy_inplace() = default;
1437 bool operator()(typename rangeT::iterator& it) const {
1438 const keyT& key = it->first;
1439 const FunctionNode<Q,NDIM>& other_node = it->second;
1440 // Use send to get write accessor and automated construction if missing
1441 f->coeffs.send(key, &nodeT:: template gaxpy_inplace<Q,R>, alpha, other_node, beta);
1442 return true;
1443 }
1444 template <typename Archive>
1445 void serialize(Archive& ar) {
1446 ar & f & alpha & beta;
1447 }
1448 };
1449
1450 /// Inplace general bilinear operation
1451
1452 /// this's world can differ from other's world
1453 /// this = alpha * this + beta * other
1454 /// @param[in] alpha prefactor for the current function impl
1455 /// @param[in] other the other function impl
1456 /// @param[in] beta prefactor for other
1457 template <typename Q, typename R>
1458 void gaxpy_inplace(const T& alpha,const FunctionImpl<Q,NDIM>& other, const R& beta, bool fence) {
1459// MADNESS_ASSERT(get_pmap() == other.get_pmap());
1460 if (alpha != T(1.0)) scale_inplace(alpha,false);
1462 typedef do_gaxpy_inplace<Q,R> opT;
1463 other.world.taskq. template for_each<rangeT,opT>(rangeT(other.coeffs.begin(), other.coeffs.end()), opT(this, T(1.0), beta));
1464 if (fence)
1465 other.world.gop.fence();
1466 }
1467
1468 // loads a function impl from persistence
1469 // @param[in] ar the archive where the function impl is stored
1470 template <typename Archive>
1471 void load(Archive& ar) {
1472 // WE RELY ON K BEING STORED FIRST
1473 int kk = 0;
1474 ar & kk;
1475
1476 MADNESS_ASSERT(kk==k);
1477
1478 // note that functor should not be (re)stored
1480 & autorefine & truncate_on_project & tree_state;//nonstandard & compressed ; //& bc;
1481
1482 ar & coeffs;
1483 world.gop.fence();
1484 }
1485
1486 // saves a function impl to persistence
1487 // @param[in] ar the archive where the function impl is to be stored
1488 template <typename Archive>
1489 void store(Archive& ar) {
1490 // WE RELY ON K BEING STORED FIRST
1491
1492 // note that functor should not be (re)stored
1494 & autorefine & truncate_on_project & tree_state;//nonstandard & compressed ; //& bc;
1495
1496 ar & coeffs;
1497 world.gop.fence();
1498 }
1499
1500 /// Returns true if the function is compressed.
1501 bool is_compressed() const;
1502
1503 /// Returns true if the function is compressed.
1504 bool is_reconstructed() const;
1505
1506 /// Returns true if the function is redundant.
1507 bool is_redundant() const;
1508
1509 /// Returns true if the function is redundant_after_merge.
1511
1512 bool is_nonstandard() const;
1513
1515
1516 bool is_on_demand() const;
1517
1518 /// Returns true if only the leaves of this tree carry its coefficients
1519
1520 /// redundant and nonstandard_with_leaves keep s coefficients on the
1521 /// internal nodes as well, so a sum over all nodes counts the function
1522 /// more than once; the leaves alone are exactly the reconstructed tree,
1523 /// which is why change_tree_state(reconstructed) is nothing but
1524 /// remove_internal_coefficients() for these two states.
1526
1527 /// Returns true if summing over the local nodes yields the function
1528
1529 /// The precondition of norm2sq_local() and trace_local(): the tree holds
1530 /// its coefficients exactly once. reconstructed and compressed do so
1531 /// outright, the two states above once the internal nodes are skipped.
1532 /// redundant_after_merge is a sum that has not been collapsed yet, and
1533 /// nonstandard / nonstandard_after_apply have no leaf coefficients to
1534 /// single out, so neither qualifies.
1536
1537 bool has_leaves() const;
1538
1539 void set_tree_state(const TreeState& state) {
1540 tree_state=state;
1541 }
1542
1544
1545 void set_functor(const std::shared_ptr<FunctionFunctorInterface<T,NDIM> > functor1);
1546
1547 std::shared_ptr<FunctionFunctorInterface<T,NDIM> > get_functor();
1548
1549 std::shared_ptr<FunctionFunctorInterface<T,NDIM> > get_functor() const;
1550
1552
1553
1555
1558
1559 double get_thresh() const;
1560
1561 /// return the simulation cell
1562 const Tensor<double>& get_cell() const { return cell; }
1563
1564 void set_thresh(double value);
1565
1566 bool get_autorefine() const;
1567
1569
1570 int get_k() const;
1571
1572 const dcT& get_coeffs() const;
1573
1575
1577
1578 void accumulate_timer(const double time) const; // !!!!!!!!!!!! REDUNDANT !!!!!!!!!!!!!!!
1579
1580 void print_timer() const;
1581
1583
1584 /// Adds a constant to the function. Local operation, optional fence
1585
1586 /// In scaling function basis must add value to first polyn in
1587 /// each box with appropriate scaling for level. In wavelet basis
1588 /// need only add at level zero.
1589 /// @param[in] t the scalar to be added
1590 void add_scalar_inplace(T t, bool fence);
1591
1592 /// Initialize nodes to zero function at initial_level of refinement.
1593
1594 /// Works for either basis. No communication.
1596
1597 /// Truncate according to the threshold with optional global fence
1598
1599 /// If thresh<=0 the default value of this->thresh is used
1600 /// @param[in] tol the truncation tolerance
1601 void truncate(double tol, bool fence);
1602
1603 /// Returns true if after truncation this node has coefficients
1604
1605 /// Assumed to be invoked on process owning key. Possible non-blocking
1606 /// communication.
1607 /// @param[in] key the key of the current function node
1608 Future<bool> truncate_spawn(const keyT& key, double tol);
1609
1610 /// Actually do the truncate operation
1611 /// @param[in] key the key to the current function node being evaluated for truncation
1612 /// @param[in] tol the tolerance for thresholding
1613 /// @param[in] v vector of Future<bool>'s that specify whether the current nodes children have coeffs
1614 bool truncate_op(const keyT& key, double tol, const std::vector< Future<bool> >& v);
1615
1616 /// Evaluate function at quadrature points in the specified box
1617
1618 /// @param[in] key the key indicating where the quadrature points are located
1619 /// @param[in] f the interface to the elementary function
1620 /// @param[in] qx quadrature points on a level=0 box
1621 /// @param[out] fval values
1622 void fcube(const keyT& key, const FunctionFunctorInterface<T,NDIM>& f, const Tensor<double>& qx, tensorT& fval) const;
1623
1624 /// Evaluate function at quadrature points in the specified box
1625
1626 /// @param[in] key the key indicating where the quadrature points are located
1627 /// @param[in] f the interface to the elementary function
1628 /// @param[in] qx quadrature points on a level=0 box
1629 /// @param[out] fval values
1630 void fcube(const keyT& key, T (*f)(const coordT&), const Tensor<double>& qx, tensorT& fval) const;
1631
1632 /// Returns cdata.key0
1633 const keyT& key0() const;
1634
1635 /// Prints the coeffs tree of the current function impl
1636 /// @param[in] maxlevel the maximum level of the tree for printing
1637 /// @param[out] os the ostream to where the output is sent
1638 void print_tree(std::ostream& os = std::cout, Level maxlevel = 10000) const;
1639
1640 /// Functor for the do_print_tree method
1641 void do_print_tree(const keyT& key, std::ostream& os, Level maxlevel) const;
1642
1643 /// Prints the coeffs tree of the current function impl (using GraphViz)
1644 /// @param[in] maxlevel the maximum level of the tree for printing
1645 /// @param[out] os the ostream to where the output is sent
1646 void print_tree_graphviz(std::ostream& os = std::cout, Level maxlevel = 10000) const;
1647
1648 /// Functor for the do_print_tree method (using GraphViz)
1649 void do_print_tree_graphviz(const keyT& key, std::ostream& os, Level maxlevel) const;
1650
1651 /// Same as print_tree() but in JSON format
1652 /// @param[out] os the ostream to where the output is sent
1653 /// @param[in] maxlevel the maximum level of the tree for printing
1654 void print_tree_json(std::ostream& os = std::cout, Level maxlevel = 10000) const;
1655
1656 /// Functor for the do_print_tree_json method
1657 void do_print_tree_json(const keyT& key, std::multimap<Level, std::tuple<tranT, std::string>>& data, Level maxlevel) const;
1658
1659 /// convert a number [0,limit] to a hue color code [blue,red],
1660 /// or, if log is set, a number [1.e-10,limit]
1662 double limit;
1663 bool log;
1664 static double lower() {return 1.e-10;};
1666 do_convert_to_color(const double limit, const bool log) : limit(limit), log(log) {}
1667 double operator()(double val) const {
1668 double color=0.0;
1669
1670 if (log) {
1671 double val2=log10(val) - log10(lower()); // will yield >0.0
1672 double upper=log10(limit) -log10(lower());
1673 val2=0.7-(0.7/upper)*val2;
1674 color= std::max(0.0,val2);
1675 color= std::min(0.7,color);
1676 } else {
1677 double hue=0.7-(0.7/limit)*(val);
1678 color= std::max(0.0,hue);
1679 }
1680 return color;
1681 }
1682 };
1683
1684
1685 /// Print a plane ("xy", "xz", or "yz") containing the point x to file
1686
1687 /// works for all dimensions; we walk through the tree, and if a leaf node
1688 /// inside the sub-cell touches the plane we print it in pstricks format
1689 void print_plane(const std::string filename, const int xaxis, const int yaxis, const coordT& el2);
1690
1691 /// collect the data for a plot of the MRA structure locally on each node
1692
1693 /// @param[in] xaxis the x-axis in the plot (can be any axis of the MRA box)
1694 /// @param[in] yaxis the y-axis in the plot (can be any axis of the MRA box)
1695 /// @param[in] el2 needs a description
1696 /// \todo Provide a description for el2
1697 Tensor<double> print_plane_local(const int xaxis, const int yaxis, const coordT& el2);
1698
1699 /// Functor for the print_plane method
1700 /// @param[in] filename the filename for the output
1701 /// @param[in] plotinfo plotting parameters
1702 /// @param[in] xaxis the x-axis in the plot (can be any axis of the MRA box)
1703 /// @param[in] yaxis the y-axis in the plot (can be any axis of the MRA box)
1704 void do_print_plane(const std::string filename, std::vector<Tensor<double> > plotinfo,
1705 const int xaxis, const int yaxis, const coordT el2);
1706
1707 /// print the grid (the roots of the quadrature of each leaf box)
1708 /// of this function in user xyz coordinates
1709 /// @param[in] filename the filename for the output
1710 void print_grid(const std::string filename) const;
1711
1712 /// return the keys of the local leaf boxes
1713 std::vector<keyT> local_leaf_keys() const;
1714
1715 /// print the grid in xyz format
1716
1717 /// the quadrature points and the key information will be written to file,
1718 /// @param[in] filename where the quadrature points will be written to
1719 /// @param[in] keys all leaf keys
1720 void do_print_grid(const std::string filename, const std::vector<keyT>& keys) const;
1721
1722 /// read data from a grid
1723
1724 /// @param[in] keyfile file with keys and grid points for each key
1725 /// @param[in] gridfile file with grid points, w/o key, but with same ordering
1726 /// @param[in] vnuc_functor subtract the values of this functor if regularization is needed
1727 template<size_t FDIM>
1728 typename std::enable_if<NDIM==FDIM>::type
1729 read_grid(const std::string keyfile, const std::string gridfile,
1730 std::shared_ptr< FunctionFunctorInterface<double,NDIM> > vnuc_functor) {
1731
1732 std::ifstream kfile(keyfile.c_str());
1733 std::ifstream gfile(gridfile.c_str());
1734 std::string line;
1735
1736 long ndata,ndata1;
1737 if (not (std::getline(kfile,line))) MADNESS_EXCEPTION("failed reading 1st line of key data",0);
1738 if (not (std::istringstream(line) >> ndata)) MADNESS_EXCEPTION("failed reading k",0);
1739 if (not (std::getline(gfile,line))) MADNESS_EXCEPTION("failed reading 1st line of grid data",0);
1740 if (not (std::istringstream(line) >> ndata1)) MADNESS_EXCEPTION("failed reading k",0);
1741 MADNESS_CHECK(ndata==ndata1);
1742 if (not (std::getline(kfile,line))) MADNESS_EXCEPTION("failed reading 2nd line of key data",0);
1743 if (not (std::getline(gfile,line))) MADNESS_EXCEPTION("failed reading 2nd line of grid data",0);
1744
1745 // the quadrature points in simulation coordinates of the root node
1746 const Tensor<double> qx=cdata.quad_x;
1747 const size_t npt = qx.dim(0);
1748
1749 // the number of coordinates (grid point tuples) per box ({x1},{x2},{x3},..,{xNDIM})
1750 long npoints=power<NDIM>(npt);
1751 // the number of boxes
1752 long nboxes=ndata/npoints;
1753 MADNESS_ASSERT(nboxes*npoints==ndata);
1754 print("reading ",nboxes,"boxes from file",gridfile,keyfile);
1755
1756 // these will be the data
1757 Tensor<T> values(cdata.vk,false);
1758
1759 int ii=0;
1760 std::string gline,kline;
1761 // while (1) {
1762 while (std::getline(kfile,kline)) {
1763
1764 double x,y,z,x1,y1,z1,val;
1765
1766 // get the key
1767 long nn;
1768 Translation l1,l2,l3;
1769 // line looks like: # key: n l1 l2 l3
1770 kline.erase(0,7);
1771 std::stringstream(kline) >> nn >> l1 >> l2 >> l3;
1772 // kfile >> s >> nn >> l1 >> l2 >> l3;
1773 const Vector<Translation,3> ll{ l1,l2,l3 };
1774 Key<3> key(nn,ll);
1775
1776 // this is borrowed from fcube
1777 const Vector<Translation,3>& l = key.translation();
1778 const Level n = key.level();
1779 const double h = std::pow(0.5,double(n));
1780 coordT c; // will hold the point in user coordinates
1783
1784
1785 if (NDIM == 3) {
1786 for (size_t i=0; i<npt; ++i) {
1787 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
1788 for (size_t j=0; j<npt; ++j) {
1789 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
1790 for (size_t k=0; k<npt; ++k) {
1791 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
1792 // fprintf(pFile,"%18.12f %18.12f %18.12f\n",c[0],c[1],c[2]);
1793 auto& success1 = std::getline(gfile,gline); MADNESS_CHECK(success1);
1794 auto& success2 = std::getline(kfile,kline); MADNESS_CHECK(success2);
1795 std::istringstream(gline) >> x >> y >> z >> val;
1796 std::istringstream(kline) >> x1 >> y1 >> z1;
1797 MADNESS_CHECK(std::fabs(x-c[0])<1.e-4);
1798 MADNESS_CHECK(std::fabs(x1-c[0])<1.e-4);
1799 MADNESS_CHECK(std::fabs(y-c[1])<1.e-4);
1800 MADNESS_CHECK(std::fabs(y1-c[1])<1.e-4);
1801 MADNESS_CHECK(std::fabs(z-c[2])<1.e-4);
1802 MADNESS_CHECK(std::fabs(z1-c[2])<1.e-4);
1803
1804 // regularize if a functor is given
1805 if (vnuc_functor) val-=(*vnuc_functor)(c);
1806 values(i,j,k)=val;
1807 }
1808 }
1809 }
1810 } else {
1811 MADNESS_EXCEPTION("only NDIM=3 in print_grid",0);
1812 }
1813
1814 // insert the new leaf node
1815 const bool has_children=false;
1816 coeffT coeff=coeffT(this->values2coeffs(key,values),targs);
1817 nodeT node(coeff,has_children);
1818 coeffs.replace(key,node);
1820 ii++;
1821 }
1822
1823 kfile.close();
1824 gfile.close();
1825 MADNESS_CHECK(ii==nboxes);
1826
1827 }
1828
1829
1830 /// read data from a grid
1831
1832 /// @param[in] gridfile file with keys and grid points and values for each key
1833 /// @param[in] vnuc_functor subtract the values of this functor if regularization is needed
1834 template<size_t FDIM>
1835 typename std::enable_if<NDIM==FDIM>::type
1836 read_grid2(const std::string gridfile,
1837 std::shared_ptr< FunctionFunctorInterface<double,NDIM> > vnuc_functor) {
1838
1839 std::ifstream gfile(gridfile.c_str());
1840 std::string line;
1841
1842 long ndata;
1843 if (not (std::getline(gfile,line))) MADNESS_EXCEPTION("failed reading 1st line of grid data",0);
1844 if (not (std::istringstream(line) >> ndata)) MADNESS_EXCEPTION("failed reading k",0);
1845 if (not (std::getline(gfile,line))) MADNESS_EXCEPTION("failed reading 2nd line of grid data",0);
1846
1847 // the quadrature points in simulation coordinates of the root node
1848 const Tensor<double> qx=cdata.quad_x;
1849 const size_t npt = qx.dim(0);
1850
1851 // the number of coordinates (grid point tuples) per box ({x1},{x2},{x3},..,{xNDIM})
1852 long npoints=power<NDIM>(npt);
1853 // the number of boxes
1854 long nboxes=ndata/npoints;
1855 MADNESS_CHECK(nboxes*npoints==ndata);
1856 print("reading ",nboxes,"boxes from file",gridfile);
1857
1858 // these will be the data
1859 Tensor<T> values(cdata.vk,false);
1860
1861 int ii=0;
1862 std::string gline;
1863 // while (1) {
1864 while (std::getline(gfile,gline)) {
1865
1866 double x1,y1,z1,val;
1867
1868 // get the key
1869 long nn;
1870 Translation l1,l2,l3;
1871 // line looks like: # key: n l1 l2 l3
1872 gline.erase(0,7);
1873 std::stringstream(gline) >> nn >> l1 >> l2 >> l3;
1874 const Vector<Translation,3> ll{ l1,l2,l3 };
1875 Key<3> key(nn,ll);
1876
1877 // this is borrowed from fcube
1878 const Vector<Translation,3>& l = key.translation();
1879 const Level n = key.level();
1880 const double h = std::pow(0.5,double(n));
1881 coordT c; // will hold the point in user coordinates
1884
1885
1886 if (NDIM == 3) {
1887 for (int i=0; i<npt; ++i) {
1888 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
1889 for (int j=0; j<npt; ++j) {
1890 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
1891 for (int k=0; k<npt; ++k) {
1892 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
1893
1894 auto& success = std::getline(gfile,gline);
1895 MADNESS_CHECK(success);
1896 std::istringstream(gline) >> x1 >> y1 >> z1 >> val;
1897 MADNESS_CHECK(std::fabs(x1-c[0])<1.e-4);
1898 MADNESS_CHECK(std::fabs(y1-c[1])<1.e-4);
1899 MADNESS_CHECK(std::fabs(z1-c[2])<1.e-4);
1900
1901 // regularize if a functor is given
1902 if (vnuc_functor) val-=(*vnuc_functor)(c);
1903 values(i,j,k)=val;
1904 }
1905 }
1906 }
1907 } else {
1908 MADNESS_EXCEPTION("only NDIM=3 in print_grid",0);
1909 }
1910
1911 // insert the new leaf node
1912 const bool has_children=false;
1913 coeffT coeff=coeffT(this->values2coeffs(key,values),targs);
1914 nodeT node(coeff,has_children);
1915 coeffs.replace(key,node);
1916 const_cast<dcT&>(coeffs).send(key.parent(),
1918 coeffs, key.parent());
1919 ii++;
1920 }
1921
1922 gfile.close();
1923 MADNESS_CHECK(ii==nboxes);
1924
1925 }
1926
1927
1928 /// Compute by projection the scaling function coeffs in specified box
1929 /// @param[in] key the key to the current function node (box)
1930 tensorT project(const keyT& key) const;
1931
1932 /// Returns the truncation threshold according to truncate_method
1933
1934 /// here is our handwaving argument:
1935 /// this threshold will give each FunctionNode an error of less than tol. The
1936 /// total error can then be as high as sqrt(#nodes) * tol. Therefore in order
1937 /// to account for higher dimensions: divide tol by about the root of number
1938 /// of siblings (2^NDIM) that have a large error when we refine along a deep
1939 /// branch of the tree.
1940 double truncate_tol(double tol, const keyT& key) const;
1941
1942 int get_truncate_mode() const { return truncate_mode; };
1943 void set_truncate_mode(int mode) { truncate_mode = mode; };
1944
1945 /// Returns patch referring to coeffs of child in parent box
1946 /// @param[in] child the key to the child function node (box)
1947 std::vector<Slice> child_patch(const keyT& child) const;
1948
1949 /// Projection with optional refinement w/ special points
1950 /// @param[in] key the key to the current function node (box)
1951 /// @param[in] do_refine should we continue refinement?
1952 /// @param[in] specialpts vector of special points in the function where we need
1953 /// to refine at a much finer level
1954 void project_refine_op(const keyT& key, bool do_refine,
1955 const std::vector<Vector<double,NDIM> >& specialpts);
1956
1957 /// Compute the Legendre scaling functions for multiplication
1958
1959 /// Evaluate parent polyn at quadrature points of a child. The prefactor of
1960 /// 2^n/2 is included. The tensor must be preallocated as phi(k,npt).
1961 /// Refer to the implementation notes for more info.
1962 /// @todo Robert please verify this comment. I don't understand this method.
1963 /// @param[in] np level of the parent function node (box)
1964 /// @param[in] nc level of the child function node (box)
1965 /// @param[in] lp translation of the parent function node (box)
1966 /// @param[in] lc translation of the child function node (box)
1967 /// @param[out] phi tensor of the legendre scaling functions
1969
1970 /// Directly project parent coeffs to child coeffs
1971
1972 /// Currently used by diff, but other uses can be anticipated
1973
1974 /// @todo is this documentation correct?
1975 /// @param[in] child the key whose coeffs we are requesting
1976 /// @param[in] parent the (leaf) key of our function
1977 /// @param[in] s the (leaf) coeffs belonging to parent
1978 /// @return coeffs
1979 const coeffT parent_to_child(const coeffT& s, const keyT& parent, const keyT& child) const;
1980
1981 /// Directly project parent NS coeffs to child NS coeffs
1982
1983 /// return the NS coefficients if parent and child are the same,
1984 /// or construct sum coeffs from the parents and "add" zero wavelet coeffs
1985 /// @param[in] child the key whose coeffs we are requesting
1986 /// @param[in] parent the (leaf) key of our function
1987 /// @param[in] coeff the (leaf) coeffs belonging to parent
1988 /// @return coeffs in NS form
1989 coeffT parent_to_child_NS(const keyT& child, const keyT& parent,
1990 const coeffT& coeff) const;
1991
1992 /// Return the values when given the coeffs in scaling function basis
1993 /// @param[in] key the key of the function node (box)
1994 /// @param[in] coeff the tensor of scaling function coefficients for function node (box)
1995 /// @return function values for function node (box)
1996 template <typename Q>
1997 GenTensor<Q> coeffs2values(const keyT& key, const GenTensor<Q>& coeff) const {
1998 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1999 double scale = pow(2.0,0.5*NDIM*key.level())/sqrt(FunctionDefaults<NDIM>::get_cell_volume());
2000 return transform(coeff,cdata.quad_phit).scale(scale);
2001 }
2002
2003 /// convert S or NS coeffs to values on a 2k grid of the children
2004
2005 /// equivalent to unfiltering the NS coeffs and then converting all child S-coeffs
2006 /// to values in their respective boxes. If only S coeffs are provided d coeffs are
2007 /// assumed to be zero. Reverse operation to values2NScoeffs().
2008 /// @param[in] key the key of the current S or NS coeffs, level n
2009 /// @param[in] coeff coeffs in S or NS form; if S then d coeffs are assumed zero
2010 /// @param[in] s_only sanity check to avoid unintended discard of d coeffs
2011 /// @return function values on the quadrature points of the children of child (!)
2012 template <typename Q>
2014 const bool s_only) const {
2015 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
2016
2017 // sanity checks
2018 MADNESS_ASSERT((coeff.dim(0)==this->get_k()) == s_only);
2019 MADNESS_ASSERT((coeff.dim(0)==this->get_k()) or (coeff.dim(0)==2*this->get_k()));
2020
2021 // this is a block-diagonal matrix with the quadrature points on the diagonal
2022 Tensor<double> quad_phit_2k(2*cdata.k,2*cdata.npt);
2023 quad_phit_2k(cdata.s[0],cdata.s[0])=cdata.quad_phit;
2024 quad_phit_2k(cdata.s[1],cdata.s[1])=cdata.quad_phit;
2025
2026 // the transformation matrix unfilters (cdata.hg) and transforms to values in one step
2027 const Tensor<double> transf = (s_only)
2028 ? inner(cdata.hg(Slice(0,k-1),_),quad_phit_2k) // S coeffs
2029 : inner(cdata.hg,quad_phit_2k); // NS coeffs
2030
2031 // increment the level since the coeffs2values part happens on level n+1
2032 const double scale = pow(2.0,0.5*NDIM*(key.level()+1))/
2034
2035 return transform(coeff,transf).scale(scale);
2036 }
2037
2038 /// Compute the function values for multiplication
2039
2040 /// Given S or NS coefficients from a parent cell, compute the value of
2041 /// the functions at the quadrature points of a child
2042 /// currently restricted to special cases
2043 /// @param[in] child key of the box in which we compute values
2044 /// @param[in] parent key of the parent box holding the coeffs
2045 /// @param[in] coeff coeffs of the parent box
2046 /// @param[in] s_only sanity check to avoid unintended discard of d coeffs
2047 /// @return function values on the quadrature points of the children of child (!)
2048 template <typename Q>
2049 GenTensor<Q> NS_fcube_for_mul(const keyT& child, const keyT& parent,
2050 const GenTensor<Q>& coeff, const bool s_only) const {
2051 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
2052
2053 // sanity checks
2054 MADNESS_ASSERT((coeff.dim(0)==this->get_k()) == s_only);
2055 MADNESS_ASSERT((coeff.dim(0)==this->get_k()) or (coeff.dim(0)==2*this->get_k()));
2056
2057 // fast return if possible
2058 // if (child.level()==parent.level()) return NScoeffs2values(child,coeff,s_only);
2059
2060 if (s_only) {
2061
2062 Tensor<double> quad_phi[NDIM];
2063 // tmp tensor
2064 Tensor<double> phi1(cdata.k,cdata.npt);
2065
2066 for (std::size_t d=0; d<NDIM; ++d) {
2067
2068 // input is S coeffs (dimension k), output is values on 2*npt grid points
2069 quad_phi[d]=Tensor<double>(cdata.k,2*cdata.npt);
2070
2071 // for both children of "child" evaluate the Legendre polynomials
2072 // first the left child on level n+1 and translations 2l
2073 phi_for_mul(parent.level(),parent.translation()[d],
2074 child.level()+1, 2*child.translation()[d], phi1);
2075 quad_phi[d](_,Slice(0,k-1))=phi1;
2076
2077 // next the right child on level n+1 and translations 2l+1
2078 phi_for_mul(parent.level(),parent.translation()[d],
2079 child.level()+1, 2*child.translation()[d]+1, phi1);
2080 quad_phi[d](_,Slice(k,2*k-1))=phi1;
2081 }
2082
2083 const double scale = 1.0/sqrt(FunctionDefaults<NDIM>::get_cell_volume());
2084 return general_transform(coeff,quad_phi).scale(scale);
2085 }
2086 MADNESS_EXCEPTION("you should not be here in NS_fcube_for_mul",1);
2087 return GenTensor<Q>();
2088 }
2089
2090 /// convert function values of the a child generation directly to NS coeffs
2091
2092 /// equivalent to converting the function values to 2^NDIM S coeffs and then
2093 /// filtering them to NS coeffs. Reverse operation to NScoeffs2values().
2094 /// @param[in] key key of the parent of the generation
2095 /// @param[in] values tensor holding function values of the 2^NDIM children of key
2096 /// @return NS coeffs belonging to key
2097 template <typename Q>
2098 GenTensor<Q> values2NScoeffs(const keyT& key, const GenTensor<Q>& values) const {
2099 //PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
2100
2101 // sanity checks
2102 MADNESS_ASSERT(values.dim(0)==2*this->get_k());
2103
2104 // this is a block-diagonal matrix with the quadrature points on the diagonal
2105 Tensor<double> quad_phit_2k(2*cdata.npt,2*cdata.k);
2106 quad_phit_2k(cdata.s[0],cdata.s[0])=cdata.quad_phiw;
2107 quad_phit_2k(cdata.s[1],cdata.s[1])=cdata.quad_phiw;
2108
2109 // the transformation matrix unfilters (cdata.hg) and transforms to values in one step
2110 const Tensor<double> transf=inner(quad_phit_2k,cdata.hgT);
2111
2112 // increment the level since the values2coeffs part happens on level n+1
2113 const double scale = pow(0.5,0.5*NDIM*(key.level()+1))
2115
2116 return transform(values,transf).scale(scale);
2117 }
2118
2119 /// Return the scaling function coeffs when given the function values at the quadrature points
2120 /// @param[in] key the key of the function node (box)
2121 /// @return function values for function node (box)
2122 template <typename Q>
2123 Tensor<Q> coeffs2values(const keyT& key, const Tensor<Q>& coeff) const {
2124 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
2125 double scale = pow(2.0,0.5*NDIM*key.level())/sqrt(FunctionDefaults<NDIM>::get_cell_volume());
2126 return transform(coeff,cdata.quad_phit).scale(scale);
2127 }
2128
2129 template <typename Q>
2130 GenTensor<Q> values2coeffs(const keyT& key, const GenTensor<Q>& values) const {
2131 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
2132 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
2133 return transform(values,cdata.quad_phiw).scale(scale);
2134 }
2135
2136 template <typename Q>
2137 Tensor<Q> values2coeffs(const keyT& key, const Tensor<Q>& values) const {
2138 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
2139 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
2140 return transform(values,cdata.quad_phiw).scale(scale);
2141 }
2142
2143 /// Compute the function values for multiplication
2144
2145 /// Given coefficients from a parent cell, compute the value of
2146 /// the functions at the quadrature points of a child
2147 /// @param[in] child the key for the child function node (box)
2148 /// @param[in] parent the key for the parent function node (box)
2149 /// @param[in] coeff the coefficients of scaling function basis of the parent box
2150 template <typename Q>
2151 Tensor<Q> fcube_for_mul(const keyT& child, const keyT& parent, const Tensor<Q>& coeff) const {
2152 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
2153 if (child.level() == parent.level()) {
2154 return coeffs2values(parent, coeff);
2155 }
2156 else if (child.level() < parent.level()) {
2157 MADNESS_EXCEPTION("FunctionImpl: fcube_for_mul: child-parent relationship bad?",0);
2158 }
2159 else {
2160 Tensor<double> phi[NDIM];
2161 for (std::size_t d=0; d<NDIM; ++d) {
2162 phi[d] = Tensor<double>(cdata.k,cdata.npt);
2163 phi_for_mul(parent.level(),parent.translation()[d],
2164 child.level(), child.translation()[d], phi[d]);
2165 }
2166 return general_transform(coeff,phi).scale(1.0/sqrt(FunctionDefaults<NDIM>::get_cell_volume()));;
2167 }
2168 }
2169
2170
2171 /// Compute the function values for multiplication
2172
2173 /// Given coefficients from a parent cell, compute the value of
2174 /// the functions at the quadrature points of a child
2175 /// @param[in] child the key for the child function node (box)
2176 /// @param[in] parent the key for the parent function node (box)
2177 /// @param[in] coeff the coefficients of scaling function basis of the parent box
2178 template <typename Q>
2179 GenTensor<Q> fcube_for_mul(const keyT& child, const keyT& parent, const GenTensor<Q>& coeff) const {
2180 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
2181 if (child.level() == parent.level()) {
2182 return coeffs2values(parent, coeff);
2183 }
2184 else if (child.level() < parent.level()) {
2185 MADNESS_EXCEPTION("FunctionImpl: fcube_for_mul: child-parent relationship bad?",0);
2186 }
2187 else {
2188 Tensor<double> phi[NDIM];
2189 for (size_t d=0; d<NDIM; d++) {
2190 phi[d] = Tensor<double>(cdata.k,cdata.npt);
2191 phi_for_mul(parent.level(),parent.translation()[d],
2192 child.level(), child.translation()[d], phi[d]);
2193 }
2195 }
2196 }
2197
2198
2199 /// Functor for the mul method
2200 template <typename L, typename R>
2201 void do_mul(const keyT& key, const Tensor<L>& left, const std::pair< keyT, Tensor<R> >& arg) {
2202 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
2203 const keyT& rkey = arg.first;
2204 const Tensor<R>& rcoeff = arg.second;
2205 //madness::print("do_mul: r", rkey, rcoeff.size());
2206 Tensor<R> rcube = fcube_for_mul(key, rkey, rcoeff);
2207 //madness::print("do_mul: l", key, left.size());
2208 Tensor<L> lcube = fcube_for_mul(key, key, left);
2209
2210 Tensor<T> tcube(cdata.vk,false);
2211 TERNARY_OPTIMIZED_ITERATOR(T, tcube, L, lcube, R, rcube, *_p0 = *_p1 * *_p2;);
2212 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
2213 tcube = transform(tcube,cdata.quad_phiw).scale(scale);
2214 coeffs.replace(key, nodeT(coeffT(tcube,targs),false));
2215 }
2216
2217
2218 /// multiply the values of two coefficient tensors using a custom number of grid points
2219
2220 /// note both coefficient tensors have to refer to the same key!
2221 /// @param[in] c1 a tensor holding coefficients
2222 /// @param[in] c2 another tensor holding coeffs
2223 /// @param[in] npt number of grid points (optional, default is cdata.npt)
2224 /// @return coefficient tensor holding the product of the values of c1 and c2
2225 template<typename R>
2227 const int npt, const keyT& key) const {
2228 typedef TENSOR_RESULT_TYPE(T,R) resultT;
2229
2231
2232 // construct a tensor with the npt coeffs
2233 Tensor<T> c11(cdata2.vk), c22(cdata2.vk);
2234 c11(this->cdata.s0)=c1;
2235 c22(this->cdata.s0)=c2;
2236
2237 // it's sufficient to scale once
2238 double scale = pow(2.0,0.5*NDIM*key.level())/sqrt(FunctionDefaults<NDIM>::get_cell_volume());
2239 Tensor<T> c1value=transform(c11,cdata2.quad_phit).scale(scale);
2240 Tensor<R> c2value=transform(c22,cdata2.quad_phit);
2241 Tensor<resultT> resultvalue(cdata2.vk,false);
2242 TERNARY_OPTIMIZED_ITERATOR(resultT, resultvalue, T, c1value, R, c2value, *_p0 = *_p1 * *_p2;);
2243
2244 Tensor<resultT> result=transform(resultvalue,cdata2.quad_phiw);
2245
2246 // return a copy of the slice to have the tensor contiguous
2247 return copy(result(this->cdata.s0));
2248 }
2249
2250
2251 /// Functor for the binary_op method
2252 template <typename L, typename R, typename opT>
2253 void do_binary_op(const keyT& key, const Tensor<L>& left,
2254 const std::pair< keyT, Tensor<R> >& arg,
2255 const opT& op) {
2256 //PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
2257 const keyT& rkey = arg.first;
2258 const Tensor<R>& rcoeff = arg.second;
2259 Tensor<R> rcube = fcube_for_mul(key, rkey, rcoeff);
2260 Tensor<L> lcube = fcube_for_mul(key, key, left);
2261
2262 Tensor<T> tcube(cdata.vk,false);
2263 op(key, tcube, lcube, rcube);
2264 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
2265 tcube = transform(tcube,cdata.quad_phiw).scale(scale);
2266 coeffs.replace(key, nodeT(coeffT(tcube,targs),false));
2267 }
2268
2269 /// Invoked by result to perform result += alpha*left+beta*right in wavelet basis
2270
2271 /// Does not assume that any of result, left, right have the same distribution.
2272 /// For most purposes result will start as an empty so actually are implementing
2273 /// out of place gaxpy. If all functions have the same distribution there is
2274 /// no communication except for the optional fence.
2275 template <typename L, typename R>
2276 void gaxpy(T alpha, const FunctionImpl<L,NDIM>& left,
2277 T beta, const FunctionImpl<R,NDIM>& right, bool fence) {
2278 // Loop over local nodes in both functions. Add in left and subtract right.
2279 // Not that efficient in terms of memory bandwidth but ensures we do
2280 // not miss any nodes.
2281 typename FunctionImpl<L,NDIM>::dcT::const_iterator left_end = left.coeffs.end();
2283 it!=left_end;
2284 ++it) {
2285 const keyT& key = it->first;
2286 const typename FunctionImpl<L,NDIM>::nodeT& other_node = it->second;
2287 coeffs.send(key, &nodeT:: template gaxpy_inplace<T,L>, 1.0, other_node, alpha);
2288 }
2289 typename FunctionImpl<R,NDIM>::dcT::const_iterator right_end = right.coeffs.end();
2291 it!=right_end;
2292 ++it) {
2293 const keyT& key = it->first;
2294 const typename FunctionImpl<L,NDIM>::nodeT& other_node = it->second;
2295 coeffs.send(key, &nodeT:: template gaxpy_inplace<T,R>, 1.0, other_node, beta);
2296 }
2297 if (fence)
2298 world.gop.fence();
2299 }
2300
2301 /// Unary operation applied inplace to the coefficients WITHOUT refinement, optional fence
2302 /// @param[in] op the unary operator for the coefficients
2303 template <typename opT>
2304 void unary_op_coeff_inplace(const opT& op, bool fence) {
2305 typename dcT::iterator end = coeffs.end();
2306 for (typename dcT::iterator it=coeffs.begin(); it!=end; ++it) {
2307 const keyT& parent = it->first;
2308 nodeT& node = it->second;
2309 if (node.has_coeff()) {
2310 // op(parent, node.coeff());
2311 TensorArgs full(-1.0,TT_FULL);
2312 change_tensor_type(node.coeff(),full);
2313 op(parent, node.coeff().full_tensor());
2315 // op(parent,node);
2316 }
2317 }
2318 if (fence)
2319 world.gop.fence();
2320 }
2321
2322 /// Unary operation applied inplace to the coefficients WITHOUT refinement, optional fence
2323 /// @param[in] op the unary operator for the coefficients
2324 template <typename opT>
2325 void unary_op_node_inplace(const opT& op, bool fence) {
2326 typename dcT::iterator end = coeffs.end();
2327 for (typename dcT::iterator it=coeffs.begin(); it!=end; ++it) {
2328 const keyT& parent = it->first;
2329 nodeT& node = it->second;
2330 op(parent, node);
2331 }
2332 if (fence)
2333 world.gop.fence();
2334 }
2335
2336 /// Integrate over one particle of a two particle function and get a one particle function
2337 /// bsp \int g(1,2) \delta(2-1) d2 = f(1)
2338 /// The overall dimension of g should be even
2339
2340 /// The operator
2341 template<std::size_t LDIM>
2342 void dirac_convolution_op(const keyT &key, const nodeT &node, FunctionImpl<T,LDIM>* f) const {
2343 // fast return if the node has children (not a leaf node)
2344 if(node.has_children()) return;
2345
2346 const implT* g=this;
2347
2348 // break the 6D key into two 3D keys (may also work for every even dimension)
2349 Key<LDIM> key1, key2;
2350 key.break_apart(key1,key2);
2351
2352 // get the coefficients of the 6D function g
2353 const coeffT& g_coeff = node.coeff();
2354
2355 // get the values of the 6D function g
2356 coeffT g_values = g->coeffs2values(key,g_coeff);
2357
2358 // Determine rank and k
2359 const long rank=g_values.rank();
2360 const long maxk=f->get_k();
2361 MADNESS_ASSERT(maxk==g_coeff.dim(0));
2362
2363 // get tensors for particle 1 and 2 (U and V in SVD)
2364 tensorT vec1=copy(g_values.get_svdtensor().ref_vector(0).reshape(rank,maxk,maxk,maxk));
2365 tensorT vec2=g_values.get_svdtensor().ref_vector(1).reshape(rank,maxk,maxk,maxk);
2366 tensorT result(maxk,maxk,maxk); // should give zero tensor
2367 // Multiply the values of each U and V vector
2368 for (long i=0; i<rank; ++i) {
2369 tensorT c1=vec1(Slice(i,i),_,_,_); // shallow copy (!)
2370 tensorT c2=vec2(Slice(i,i),_,_,_);
2371 c1.emul(c2); // this changes vec1 because of shallow copy, but not the g function because of the deep copy made above
2372 double singular_value_i = g_values.get_svdtensor().weights(i);
2373 result += (singular_value_i*c1);
2374 }
2375
2376 // accumulate coefficients (since only diagonal boxes are used the coefficients get just replaced, but accumulate is needed to create the right tree structure
2377 tensorT f_coeff = f->values2coeffs(key1,result);
2378 f->coeffs.task(key1, &FunctionNode<T,LDIM>::accumulate2, f_coeff, f->coeffs, key1, TaskAttributes::hipri());
2379// coeffs.task(dest, &nodeT::accumulate2, result, coeffs, dest, TaskAttributes::hipri());
2380
2381
2382 return;
2383 }
2384
2385
2386 template<std::size_t LDIM>
2388 typename dcT::const_iterator end = this->coeffs.end();
2389 for (typename dcT::const_iterator it=this->coeffs.begin(); it!=end; ++it) {
2390 // looping through all the leaf(!) coefficients in the NDIM function ("this")
2391 const keyT& key = it->first;
2392 const FunctionNode<T,NDIM>& node = it->second;
2393 if (node.is_leaf()) {
2394 // only process the diagonal boxes
2395 Key<LDIM> key1, key2;
2396 key.break_apart(key1,key2);
2397 if(key1 == key2){
2398 ProcessID p = coeffs.owner(key);
2399 woT::task(p, &implT:: template dirac_convolution_op<LDIM>, key, node, f);
2400 }
2401 }
2402 }
2403 world.gop.fence(); // fence is necessary if trickle down is used afterwards
2404 // trickle down and undo redundand shouldnt change anything if only the diagonal elements are considered above -> check this
2405 f->trickle_down(true); // fence must be true otherwise undo_redundant will have trouble
2406// f->undo_redundant(true);
2407 f->verify_tree();
2408 //if (fence) world.gop.fence(); // unnecessary, fence is activated in undo_redundant
2409
2410 }
2411
2412
2413 /// Unary operation applied inplace to the coefficients WITHOUT refinement, optional fence
2414 /// @param[in] op the unary operator for the coefficients
2415 template <typename opT>
2416 void flo_unary_op_node_inplace(const opT& op, bool fence) {
2418// typedef do_unary_op_value_inplace<opT> xopT;
2419 world.taskq.for_each<rangeT,opT>(rangeT(coeffs.begin(), coeffs.end()), op);
2420 if (fence) world.gop.fence();
2421 }
2422
2423 /// Unary operation applied inplace to the coefficients WITHOUT refinement, optional fence
2424 /// @param[in] op the unary operator for the coefficients
2425 template <typename opT>
2426 void flo_unary_op_node_inplace(const opT& op, bool fence) const {
2428// typedef do_unary_op_value_inplace<opT> xopT;
2429 world.taskq.for_each<rangeT,opT>(rangeT(coeffs.begin(), coeffs.end()), op);
2430 if (fence)
2431 world.gop.fence();
2432 }
2433
2434 /// truncate tree at a certain level
2435 /// @param[in] max_level truncate tree below this level
2436 void erase(const Level& max_level);
2437
2438 /// Returns some asymmetry measure ... no comms
2439 double check_symmetry_local() const;
2440
2441 /// given an NS tree resulting from a convolution, truncate leafs if appropriate
2444 const implT* f; // for calling its member functions
2445 double thresh;
2446
2448 do_truncate_NS_leafs(const implT* f, const double thresh) : f(f), thresh(thresh) {}
2449
2450 bool operator()(typename rangeT::iterator& it) const {
2451
2452 const keyT& key = it->first;
2453 nodeT& node = it->second;
2454
2455 if (node.is_leaf() and node.coeff().has_data()) {
2456 coeffT d = copy(node.coeff());
2457 d(f->cdata.s0)=0.0;
2458 const double error=d.normf();
2459 const double tol=f->truncate_tol(thresh,key);
2460 if (error<tol) node.coeff()=copy(node.coeff()(f->cdata.s0));
2461 }
2462 return true;
2463 }
2464 template <typename Archive> void serialize(const Archive& ar) {}
2465
2466 };
2467
2468 /// remove all coefficients of internal nodes
2471
2472 /// constructor need impl for cdata
2474
2475 bool operator()(typename rangeT::iterator& it) const {
2476
2477 nodeT& node = it->second;
2478 if (node.has_children()) node.clear_coeff();
2479 return true;
2480 }
2481 template <typename Archive> void serialize(const Archive& ar) {}
2482
2483 };
2484
2485 /// remove all coefficients of leaf nodes
2488
2489 /// constructor need impl for cdata
2491
2492 bool operator()(typename rangeT::iterator& it) const {
2493 nodeT& node = it->second;
2494 if (not node.has_children()) node.clear_coeff();
2495 return true;
2496 }
2497 template <typename Archive> void serialize(const Archive& ar) {}
2498
2499 };
2500
2501
2502 /// keep only the sum coefficients in each node
2506
2507 /// constructor need impl for cdata
2509
2510 bool operator()(typename rangeT::iterator& it) const {
2511
2512 nodeT& node = it->second;
2513 coeffT s=copy(node.coeff()(impl->cdata.s0));
2514 node.coeff()=s;
2515 return true;
2516 }
2517 template <typename Archive> void serialize(const Archive& ar) {}
2518
2519 };
2520
2521
2522 /// reduce the rank of the nodes, optional fence
2525
2526 // threshold for rank reduction / SVD truncation
2528
2529 // constructor takes target precision
2530 do_reduce_rank() = default;
2532 do_reduce_rank(const double& thresh) {
2533 args.thresh=thresh;
2534 }
2535
2536 //
2537 bool operator()(typename rangeT::iterator& it) const {
2538
2539 nodeT& node = it->second;
2540 node.reduceRank(args.thresh);
2541 return true;
2542 }
2543 template <typename Archive> void serialize(const Archive& ar) {}
2544 };
2545
2546
2547
2548 /// check symmetry wrt particle exchange
2551 const implT* f;
2554
2555 /// return the norm of the difference of this node and its "mirror" node
2556 double operator()(typename rangeT::iterator& it) const {
2557
2558 // Temporary fix to GCC whining about out of range access for NDIM!=6
2559 if constexpr(NDIM==6) {
2560 const keyT& key = it->first;
2561 const nodeT& fnode = it->second;
2562
2563 // skip internal nodes
2564 if (fnode.has_children()) return 0.0;
2565
2566 if (f->world.size()>1) return 0.0;
2567
2568 // exchange particles
2569 std::vector<long> map(NDIM);
2570 map[0]=3; map[1]=4; map[2]=5;
2571 map[3]=0; map[4]=1; map[5]=2;
2572
2573 // make mapped key
2575 for (std::size_t i=0; i<NDIM; ++i) l[map[i]] = key.translation()[i];
2576 const keyT mapkey(key.level(),l);
2577
2578 double norm=0.0;
2579
2580
2581 // hope it's local
2582 if (f->get_coeffs().probe(mapkey)) {
2583 MADNESS_ASSERT(f->get_coeffs().probe(mapkey));
2584 const nodeT& mapnode=f->get_coeffs().find(mapkey).get()->second;
2585
2586// bool have_c1=fnode.coeff().has_data() and fnode.coeff().config().has_data();
2587// bool have_c2=mapnode.coeff().has_data() and mapnode.coeff().config().has_data();
2588 bool have_c1=fnode.coeff().has_data();
2589 bool have_c2=mapnode.coeff().has_data();
2590
2591 if (have_c1 and have_c2) {
2592 tensorT c1=fnode.coeff().full_tensor_copy();
2593 tensorT c2=mapnode.coeff().full_tensor_copy();
2594 c2 = copy(c2.mapdim(map));
2595 norm=(c1-c2).normf();
2596 } else if (have_c1) {
2597 tensorT c1=fnode.coeff().full_tensor_copy();
2598 norm=c1.normf();
2599 } else if (have_c2) {
2600 tensorT c2=mapnode.coeff().full_tensor_copy();
2601 norm=c2.normf();
2602 } else {
2603 norm=0.0;
2604 }
2605 } else {
2606 norm=fnode.coeff().normf();
2607 }
2608 return norm*norm;
2609 }
2610 else {
2611 MADNESS_EXCEPTION("ONLY FOR DIM 6!", 1);
2612 }
2613 }
2614
2615 double operator()(double a, double b) const {
2616 return (a+b);
2617 }
2618
2619 template <typename Archive> void serialize(const Archive& ar) {
2620 MADNESS_EXCEPTION("no serialization of do_check_symmetry yet",1);
2621 }
2622
2623
2624 };
2625
2626 /// merge the coefficent boxes of this into result's tree
2627
2628 /// result+= alpha*this
2629 /// this and result don't have to have the same distribution or live in the same world
2630 /// no comm, and the tree should be in an consistent state by virtue
2631 template<typename Q, typename R>
2635 T alpha=T(1.0);
2639
2640 /// return the norm of the difference of this node and its "mirror" node
2641 bool operator()(typename rangeT::iterator& it) const {
2642
2643 const keyT& key = it->first;
2644 const nodeT& node = it->second;
2645 if (node.has_coeff()) result->get_coeffs().task(key, &nodeT::accumulate,
2646 alpha*node.coeff(), result->get_coeffs(), key, result->targs, true);
2647 return true;
2648 }
2649
2650 template <typename Archive> void serialize(const Archive& ar) {
2651 MADNESS_EXCEPTION("no serialization of do_accumulate_trees",1);
2652 }
2653 };
2654
2655
2656// /// merge the coefficient boxes of this into other's tree
2657//
2658// /// no comm, and the tree should be in an consistent state by virtue
2659// /// of FunctionNode::gaxpy_inplace
2660// template<typename Q, typename R>
2661// struct do_merge_trees {
2662// typedef Range<typename dcT::const_iterator> rangeT;
2663// FunctionImpl<Q,NDIM>* other;
2664// T alpha;
2665// R beta;
2666// do_merge_trees() : other(0) {}
2667// do_merge_trees(const T alpha, const R beta, FunctionImpl<Q,NDIM>& other)
2668// : other(&other), alpha(alpha), beta(beta) {}
2669//
2670// /// return the norm of the difference of this node and its "mirror" node
2671// bool operator()(typename rangeT::iterator& it) const {
2672//
2673// const keyT& key = it->first;
2674// const nodeT& fnode = it->second;
2675//
2676// // if other's node exists: add this' coeffs to it
2677// // otherwise insert this' node into other's tree
2678// typename dcT::accessor acc;
2679// if (other->get_coeffs().find(acc,key)) {
2680// nodeT& gnode=acc->second;
2681// gnode.gaxpy_inplace(beta,fnode,alpha);
2682// } else {
2683// nodeT gnode=fnode;
2684// gnode.scale(alpha);
2685// other->get_coeffs().replace(key,gnode);
2686// }
2687// return true;
2688// }
2689//
2690// template <typename Archive> void serialize(const Archive& ar) {
2691// MADNESS_EXCEPTION("no serialization of do_merge_trees",1);
2692// }
2693// };
2694
2695
2696 /// map this on f
2697 struct do_mapdim {
2699
2700 std::vector<long> map;
2702
2703 do_mapdim() : f(0) {};
2704 do_mapdim(const std::vector<long> map, implT& f) : map(map), f(&f) {}
2705
2706 bool operator()(typename rangeT::iterator& it) const {
2707
2708 const keyT& key = it->first;
2709 const nodeT& node = it->second;
2710
2712 for (std::size_t i=0; i<NDIM; ++i) l[map[i]] = key.translation()[i];
2713 tensorT c = node.coeff().reconstruct_tensor();
2714 if (c.size()) c = copy(c.mapdim(map));
2715 coeffT cc(c,f->get_tensor_args());
2716 f->get_coeffs().replace(keyT(key.level(),l), nodeT(cc,node.has_children()));
2717
2718 return true;
2719 }
2720 template <typename Archive> void serialize(const Archive& ar) {
2721 MADNESS_EXCEPTION("no serialization of do_mapdim",1);
2722 }
2723
2724 };
2725
2726 /// mirror dimensions of this, write result on f
2727 struct do_mirror {
2729
2730 std::vector<long> mirror;
2732
2733 do_mirror() : f(0) {};
2734 do_mirror(const std::vector<long> mirror, implT& f) : mirror(mirror), f(&f) {}
2735
2736 bool operator()(typename rangeT::iterator& it) const {
2737
2738 const keyT& key = it->first;
2739 const nodeT& node = it->second;
2740
2741 // mirror translation index: l_new + l_old = l_max
2743 Translation lmax = (Translation(1)<<key.level()) - 1;
2744 for (std::size_t i=0; i<NDIM; ++i) {
2745 if (mirror[i]==-1) l[i]= lmax - key.translation()[i];
2746 }
2747
2748 // mirror coefficients: multiply all odd-k slices with -1
2749 tensorT c = node.coeff().full_tensor_copy();
2750 if (c.size()) {
2751 std::vector<Slice> s(___);
2752
2753 // loop over dimensions and over k
2754 for (size_t i=0; i<NDIM; ++i) {
2755 std::size_t kmax=c.dim(i);
2756 if (mirror[i]==-1) {
2757 for (size_t k=1; k<kmax; k+=2) {
2758 s[i]=Slice(k,k,1);
2759 c(s)*=(-1.0);
2760 }
2761 s[i]=_;
2762 }
2763 }
2764 }
2765 coeffT cc(c,f->get_tensor_args());
2766 f->get_coeffs().replace(keyT(key.level(),l), nodeT(cc,node.has_children()));
2767
2768 return true;
2769 }
2770 template <typename Archive> void serialize(const Archive& ar) {
2771 MADNESS_EXCEPTION("no serialization of do_mirror",1);
2772 }
2773
2774 };
2775
2776 /// mirror dimensions of this, write result on f
2779
2780 std::vector<long> map,mirror;
2782
2784 do_map_and_mirror(const std::vector<long> map, const std::vector<long> mirror, implT& f)
2785 : map(map), mirror(mirror), f(&f) {}
2786
2787 bool operator()(typename rangeT::iterator& it) const {
2788
2789 const keyT& key = it->first;
2790 const nodeT& node = it->second;
2791
2792 tensorT c = node.coeff().full_tensor_copy();
2794
2795 // do the mapping first (if present)
2796 if (map.size()>0) {
2798 for (std::size_t i=0; i<NDIM; ++i) l1[map[i]] = l[i];
2799 std::swap(l,l1);
2800 if (c.size()) c = copy(c.mapdim(map));
2801 }
2802
2803 if (mirror.size()>0) {
2804 // mirror translation index: l_new + l_old = l_max
2806 Translation lmax = (Translation(1)<<key.level()) - 1;
2807 for (std::size_t i=0; i<NDIM; ++i) {
2808 if (mirror[i]==-1) l1[i]= lmax - l[i];
2809 }
2810 std::swap(l,l1);
2811
2812 // mirror coefficients: multiply all odd-k slices with -1
2813 if (c.size()) {
2814 std::vector<Slice> s(___);
2815
2816 // loop over dimensions and over k
2817 for (size_t i=0; i<NDIM; ++i) {
2818 std::size_t kmax=c.dim(i);
2819 if (mirror[i]==-1) {
2820 for (size_t k=1; k<kmax; k+=2) {
2821 s[i]=Slice(k,k,1);
2822 c(s)*=(-1.0);
2823 }
2824 s[i]=_;
2825 }
2826 }
2827 }
2828 }
2829
2830 coeffT cc(c,f->get_tensor_args());
2831 f->get_coeffs().replace(keyT(key.level(),l), nodeT(cc,node.has_children()));
2832 return true;
2833 }
2834 template <typename Archive> void serialize(const Archive& ar) {
2835 MADNESS_EXCEPTION("no serialization of do_mirror",1);
2836 }
2837
2838 };
2839
2840
2841
2842 /// "put" this on g
2843 struct do_average {
2845
2847
2848 do_average() : g(0) {}
2850
2851 /// iterator it points to this
2852 bool operator()(typename rangeT::iterator& it) const {
2853
2854 const keyT& key = it->first;
2855 const nodeT& fnode = it->second;
2856
2857 // fast return if rhs has no coeff here
2858 if (fnode.has_coeff()) {
2859
2860 // check if there is a node already existing
2861 typename dcT::accessor acc;
2862 if (g->get_coeffs().find(acc,key)) {
2863 nodeT& gnode=acc->second;
2864 if (gnode.has_coeff()) gnode.coeff()+=fnode.coeff();
2865 } else {
2866 g->get_coeffs().replace(key,fnode);
2867 }
2868 }
2869
2870 return true;
2871 }
2872 template <typename Archive> void serialize(const Archive& ar) {}
2873 };
2874
2875 /// change representation of nodes' coeffs to low rank, optional fence
2878
2879 // threshold for rank reduction / SVD truncation
2882
2883 // constructor takes target precision
2885 // do_change_tensor_type(const TensorArgs& targs) : targs(targs) {}
2887
2888 //
2889 bool operator()(typename rangeT::iterator& it) const {
2890
2891 double cpu0=cpu_time();
2892 nodeT& node = it->second;
2894 double cpu1=cpu_time();
2895 f->timer_change_tensor_type.accumulate(cpu1-cpu0);
2896
2897 return true;
2898
2899 }
2900 template <typename Archive> void serialize(const Archive& ar) {}
2901 };
2902
2905
2906 // threshold for rank reduction / SVD truncation
2908 // if you are positive, make low-rank tensor addition fast by assuming all tensors are SVD
2909 // i.e. left and right vectors are orthogonal
2910 bool do_svd_add=false;
2911
2912 // constructor takes target precision
2916 bool operator()(typename rangeT::iterator& it) const {
2917 it->second.consolidate_buffer(targs, do_svd_add);
2918 return true;
2919 }
2920 template <typename Archive> void serialize(const Archive& ar) {}
2921 };
2922
2923
2924
2925 template <typename opT>
2929 opT op;
2931 bool operator()(typename rangeT::iterator& it) const {
2932 const keyT& key = it->first;
2933 nodeT& node = it->second;
2934 if (node.has_coeff()) {
2935 const TensorArgs full_args(-1.0,TT_FULL);
2936 change_tensor_type(node.coeff(),full_args);
2937 tensorT& t= node.coeff().full_tensor();
2938 //double before = t.normf();
2939 tensorT values = impl->fcube_for_mul(key, key, t);
2940 op(key, values);
2941 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
2942 t = transform(values,impl->cdata.quad_phiw).scale(scale);
2943 node.coeff()=coeffT(t,impl->get_tensor_args());
2944 //double after = t.normf();
2945 //madness::print("XOP:", key, before, after);
2946 }
2947 return true;
2948 }
2949 template <typename Archive> void serialize(const Archive& ar) {}
2950 };
2951
2952 template <typename Q, typename R>
2953 /// @todo I don't know what this does other than a trasform
2954 void vtransform_doit(const std::shared_ptr< FunctionImpl<R,NDIM> >& right,
2955 const Tensor<Q>& c,
2956 const std::vector< std::shared_ptr< FunctionImpl<T,NDIM> > >& vleft,
2957 double tol) {
2958 // To reduce crunch on vectors being transformed each task
2959 // does them in a random order
2960 std::vector<unsigned int> ind(vleft.size());
2961 for (unsigned int i=0; i<vleft.size(); ++i) {
2962 ind[i] = i;
2963 }
2964 for (unsigned int i=0; i<vleft.size(); ++i) {
2965 unsigned int j = RandomValue<int>()%vleft.size();
2966 std::swap(ind[i],ind[j]);
2967 }
2968
2969 for (const auto& [key, rnode] : right->coeffs) {
2970 if (rnode.has_coeff()) {
2971 const GenTensor<R>& r = rnode.coeff();
2972 double norm = r.normf();
2973 double keytol = truncate_tol(tol,key);
2974
2975 for (unsigned int j=0; j<vleft.size(); ++j) {
2976 unsigned int i = ind[j]; // Random permutation
2977 if (std::abs(norm*c(i)) > keytol) {
2978 implT* left = vleft[i].get();
2979 typename dcT::accessor acc;
2980 bool new_node = left->coeffs.insert(acc,key);
2981 if (new_node) {
2982 /* Notify parent nodes that a new child exists. */
2983 Key<NDIM> parent = key.parent();
2984 if (left->coeffs.is_local(parent))
2985 left->coeffs.send(parent, &nodeT::set_has_children_recursive, left->coeffs, parent);
2986 else
2987 left->coeffs.task(parent, &nodeT::set_has_children_recursive, left->coeffs, parent);
2988 }
2989 nodeT& node = acc->second;
2990 node.gaxpy_inplace(1.0, rnode, c(i));
2991 }
2992 }
2993 }
2994 }
2995 }
2996
2997 /// Refine multiple functions down to the same finest level
2998
2999 /// @param v the vector of functions we are refining.
3000 /// @param key the current node.
3001 /// @param c the vector of coefficients passed from above.
3003 const std::vector<tensorT>& c,
3004 const keyT key);
3005
3006 /// Inplace operate on many functions (impl's) with an operator within a certain box
3007 /// @param[in] key the key of the current function node (box)
3008 /// @param[in] op the operator
3009 /// @param[in] v the vector of function impl's on which to be operated
3010 template <typename opT>
3011 void multiop_values_doit(const keyT& key, const opT& op, const std::vector<implT*>& v) {
3012 std::vector<tensorT> c(v.size());
3013 for (unsigned int i=0; i<v.size(); i++) {
3014 if (v[i]) {
3015 coeffT cc = coeffs2values(key, v[i]->coeffs.find(key).get()->second.coeff());
3016 c[i]=cc.full_tensor();
3017 }
3018 }
3019 tensorT r = op(key, c);
3020 coeffs.replace(key, nodeT(coeffT(values2coeffs(key, r),targs),false));
3021 }
3022
3023 /// Inplace operate on many functions (impl's) with an operator within a certain box
3024 /// Assumes all functions have been refined down to the same level
3025 /// @param[in] op the operator
3026 /// @param[in] v the vector of function impl's on which to be operated
3027 template <typename opT>
3028 void multiop_values(const opT& op, const std::vector<implT*>& v) {
3029 // rough check on refinement level (ignore non-initialized functions
3030 for (std::size_t i=1; i<v.size(); ++i) {
3031 if (v[i] and v[i-1]) {
3032 MADNESS_ASSERT(v[i]->coeffs.size()==v[i-1]->coeffs.size());
3033 }
3034 }
3035 typename dcT::iterator end = v[0]->coeffs.end();
3036 for (typename dcT::iterator it=v[0]->coeffs.begin(); it!=end; ++it) {
3037 const keyT& key = it->first;
3038 if (it->second.has_coeff())
3039 world.taskq.add(*this, &implT:: template multiop_values_doit<opT>, key, op, v);
3040 else
3041 coeffs.replace(key, nodeT(coeffT(),true));
3042 }
3043 world.gop.fence();
3044 }
3045
3046 /// Inplace operate on many functions (impl's) with an operator within a certain box
3047
3048 /// @param[in] key the key of the current function node (box)
3049 /// @param[in] op the operator
3050 /// @param[in] vin the vector of function impl's on which to be operated
3051 /// @param[out] vout the resulting vector of function impl's
3052 template <typename opT>
3053 void multi_to_multi_op_values_doit(const keyT& key, const opT& op,
3054 const std::vector<implT*>& vin, std::vector<implT*>& vout) {
3055 std::vector<tensorT> c(vin.size());
3056 for (unsigned int i=0; i<vin.size(); i++) {
3057 if (vin[i]) {
3058 coeffT cc = coeffs2values(key, vin[i]->coeffs.find(key).get()->second.coeff());
3059 c[i]=cc.full_tensor();
3060 }
3061 }
3062 std::vector<tensorT> r = op(key, c);
3063 MADNESS_ASSERT(r.size()==vout.size());
3064 for (std::size_t i=0; i<vout.size(); ++i) {
3065 vout[i]->coeffs.replace(key, nodeT(coeffT(values2coeffs(key, r[i]),targs),false));
3066 }
3067 }
3068
3069 /// Inplace operate on many functions (impl's) with an operator within a certain box
3070
3071 /// Assumes all functions have been refined down to the same level
3072 /// @param[in] op the operator
3073 /// @param[in] vin the vector of function impl's on which to be operated
3074 /// @param[out] vout the resulting vector of function impl's
3075 template <typename opT>
3076 void multi_to_multi_op_values(const opT& op, const std::vector<implT*>& vin,
3077 std::vector<implT*>& vout, const bool fence=true) {
3078 // rough check on refinement level (ignore non-initialized functions
3079 for (std::size_t i=1; i<vin.size(); ++i) {
3080 if (vin[i] and vin[i-1]) {
3081 MADNESS_ASSERT(vin[i]->coeffs.size()==vin[i-1]->coeffs.size());
3082 }
3083 }
3084 typename dcT::iterator end = vin[0]->coeffs.end();
3085 for (typename dcT::iterator it=vin[0]->coeffs.begin(); it!=end; ++it) {
3086 const keyT& key = it->first;
3087 if (it->second.has_coeff())
3088 world.taskq.add(*this, &implT:: template multi_to_multi_op_values_doit<opT>,
3089 key, op, vin, vout);
3090 else {
3091 // fill result functions with empty box in this key
3092 for (implT* it2 : vout) {
3093 it2->coeffs.replace(key, nodeT(coeffT(),true));
3094 }
3095 }
3096 }
3097 if (fence) world.gop.fence();
3098 }
3099
3100 /// Transforms a vector of functions left[i] = sum[j] right[j]*c[j,i] using sparsity
3101 /// @param[in] vright vector of functions (impl's) on which to be transformed
3102 /// @param[in] c the tensor (matrix) transformer
3103 /// @param[in] vleft vector of of the *newly* transformed functions (impl's)
3104 template <typename Q, typename R>
3105 void vtransform(const std::vector< std::shared_ptr< FunctionImpl<R,NDIM> > >& vright,
3106 const Tensor<Q>& c,
3107 const std::vector< std::shared_ptr< FunctionImpl<T,NDIM> > >& vleft,
3108 double tol,
3109 bool fence) {
3110 for (unsigned int j=0; j<vright.size(); ++j) {
3111 world.taskq.add(*this, &implT:: template vtransform_doit<Q,R>, vright[j], copy(c(j,_)), vleft, tol);
3112 }
3113 if (fence)
3114 world.gop.fence();
3115 }
3116
3117 /// Unary operation applied inplace to the values with optional refinement and fence
3118 /// @param[in] op the unary operator for the values
3119 template <typename opT>
3120 void unary_op_value_inplace(const opT& op, bool fence) {
3122 typedef do_unary_op_value_inplace<opT> xopT;
3123 world.taskq.for_each<rangeT,xopT>(rangeT(coeffs.begin(), coeffs.end()), xopT(this,op));
3124 if (fence)
3125 world.gop.fence();
3126 }
3127
3128 // Multiplication assuming same distribution and recursive descent
3129 /// Both left and right functions are in the scaling function basis
3130 /// @param[in] key the key to the current function node (box)
3131 /// @param[in] left the function impl associated with the left function
3132 /// @param[in] lcin the scaling function coefficients associated with the
3133 /// current box in the left function
3134 /// @param[in] vrightin the vector of function impl's associated with
3135 /// the vector of right functions
3136 /// @param[in] vrcin the vector scaling function coefficients associated with the
3137 /// current box in the right functions
3138 /// @param[out] vresultin the vector of resulting functions (impl's)
3139 template <typename L, typename R>
3140 void mulXXveca(const keyT& key,
3141 const FunctionImpl<L,NDIM>* left, const Tensor<L>& lcin,
3142 const std::vector<const FunctionImpl<R,NDIM>*> vrightin,
3143 const std::vector< Tensor<R> >& vrcin,
3144 const std::vector<FunctionImpl<T,NDIM>*> vresultin,
3145 double tol) {
3146 typedef typename FunctionImpl<L,NDIM>::dcT::const_iterator literT;
3147 typedef typename FunctionImpl<R,NDIM>::dcT::const_iterator riterT;
3148
3149 double lnorm = 1e99;
3150 double ldnorm = 1e99;
3151 bool l_is_leaf = false;
3152 Tensor<L> lc = lcin;
3153 literT lit = left->coeffs.find(key).get();
3154
3155 if (lc.size() == 0) {
3156 MADNESS_CHECK(lit != left->coeffs.end());
3157 // redundant form puts coefficients and computed norms on every
3158 // node; without them the screen below reads garbage
3159 MADNESS_CHECK(lit->second.has_coeff());
3160 lnorm = lit->second.get_norm_tree();
3161 ldnorm = lit->second.get_dnorm_tree();
3163 l_is_leaf = !lit->second.has_children();
3164 }
3165 else {
3166 lnorm = lc.normf();
3167 ldnorm = 0.0; // node created to match trees; leaves carry no detail
3168 l_is_leaf = true;
3169 }
3170
3171 // Loop thru RHS functions seeing if anything can be multiplied
3172 std::vector<FunctionImpl<T,NDIM>*> vresult;
3173 std::vector<const FunctionImpl<R,NDIM>*> vright;
3174 std::vector< Tensor<R> > vrc;
3175 vresult.reserve(vrightin.size());
3176 vright.reserve(vrightin.size());
3177 vrc.reserve(vrightin.size());
3178
3179 // fetched at most once and shared by every right function; do_mul only reads it
3180 Tensor<L> lc_shared;
3181 bool lc_shared_set = false;
3182 auto left_coeffs = [&]() -> const Tensor<L>& {
3183 if (!lc_shared_set) {
3184 lc_shared = lc.size() ? lc : lit->second.coeff().full_tensor_copy();
3185 lc_shared_set = true;
3186 }
3187 return lc_shared;
3188 };
3189
3190 for (unsigned int i=0; i<vrightin.size(); ++i) {
3191 FunctionImpl<T,NDIM>* result = vresultin[i];
3192 const FunctionImpl<R,NDIM>* right = vrightin[i];
3193 Tensor<R> rc = vrcin[i];
3194 double rnorm, rdnorm;
3195 riterT rit = right->coeffs.find(key).get();
3196 if (rc.size() == 0) {
3197 MADNESS_CHECK(rit != right->coeffs.end());
3198 MADNESS_CHECK(rit->second.has_coeff());
3199 rnorm = rit->second.get_norm_tree();
3200 rdnorm = rit->second.get_dnorm_tree();
3202 }
3203 else {
3204 rnorm = rc.normf();
3205 rdnorm = 0.0;
3206 }
3207
3208 // the neglected cross terms are below threshold: multiply here (requires redundant form)
3209 if (rnorm*ldnorm + lnorm*rdnorm + ldnorm*rdnorm <= truncate_tol(tol, key)) {
3210 // lc/rc must keep their size for the recursion logic, so pass separate tensors
3211 Tensor<R> rc_data = (rc.size() == 0) ? rit->second.coeff().full_tensor_copy() : rc;
3212 result->task(world.rank(), &implT:: template do_mul<L,R>, key, left_coeffs(), std::make_pair(key,rc_data));
3213 }
3214 else { // Interior node
3215 result->coeffs.replace(key, nodeT(coeffT(),true));
3216 vresult.push_back(result);
3217 vright.push_back(right);
3218 vrc.push_back(rc);
3219 }
3220 }
3221
3222 if (vresult.size()) {
3223 Tensor<L> lss;
3224 if (lc.size() || l_is_leaf) {
3225 Tensor<L> ld(cdata.v2k);
3226 ld(cdata.s0) = left_coeffs()(___);
3227 lss = left->unfilter(ld);
3228 }
3229
3230 // invariant across the child loop below, so look it up once per right function
3231 std::vector<char> r_unfiltered(vresult.size(), 0);
3232 std::vector< Tensor<R> > vrss(vresult.size());
3233 for (unsigned int i=0; i<vresult.size(); ++i) {
3234 riterT rit = vright[i]->coeffs.find(key).get();
3235 // coefficients handed down from the parent stand in for a node
3236 // that need not exist here; without them the node must exist
3237 MADNESS_CHECK(vrc[i].size() || rit != vright[i]->coeffs.end());
3238 if (vrc[i].size() || !rit->second.has_children()) {
3239 MADNESS_CHECK(vrc[i].size() || rit->second.has_coeff());
3240 Tensor<R> rd(cdata.v2k);
3241 rd(cdata.s0) = (vrc[i].size() ? vrc[i] : rit->second.coeff().full_tensor_copy())(___);
3242 vrss[i] = vright[i]->unfilter(rd);
3243 r_unfiltered[i] = 1;
3244 }
3245 }
3246
3247 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
3248 const keyT& child = kit.key();
3249 Tensor<L> ll;
3250
3251 std::vector<Slice> cp = child_patch(child);
3252
3253 if (lc.size() || l_is_leaf)
3254 ll = copy(lss(cp));
3255
3256 std::vector< Tensor<R> > vv(vresult.size());
3257 for (unsigned int i=0; i<vresult.size(); ++i) {
3258 if (r_unfiltered[i])
3259 vv[i] = copy(vrss[i](cp));
3260 }
3261
3262 woT::task(coeffs.owner(child), &implT:: template mulXXveca<L,R>, child, left, ll, vright, vv, vresult, tol);
3263 }
3264 }
3265 }
3266
3267 /// Multiplication using recursive descent and assuming same distribution
3268 /// Both left and right functions are in the scaling function basis
3269 /// @param[in] key the key to the current function node (box)
3270 /// @param[in] left the function impl associated with the left function
3271 /// @param[in] lcin the scaling function coefficients associated with the
3272 /// current box in the left function
3273 /// @param[in] right the function impl associated with the right function
3274 /// @param[in] rcin the scaling function coefficients associated with the
3275 /// current box in the right function
3276 template <typename L, typename R>
3277 void mulXXa(const keyT& key,
3278 const FunctionImpl<L,NDIM>* left, const Tensor<L>& lcin,
3279 const FunctionImpl<R,NDIM>* right,const Tensor<R>& rcin,
3280 double tol) {
3281 typedef typename FunctionImpl<L,NDIM>::dcT::const_iterator literT;
3282 typedef typename FunctionImpl<R,NDIM>::dcT::const_iterator riterT;
3283
3284 double lnorm=1e99, rnorm=1e99;
3285
3286 Tensor<L> lc = lcin;
3287 if (lc.size() == 0) {
3288 literT it = left->coeffs.find(key).get();
3289 MADNESS_ASSERT(it != left->coeffs.end());
3290 lnorm = it->second.get_norm_tree();
3291 if (it->second.has_coeff())
3292 lc = it->second.coeff().reconstruct_tensor();
3293 }
3294
3295 Tensor<R> rc = rcin;
3296 if (rc.size() == 0) {
3297 riterT it = right->coeffs.find(key).get();
3298 MADNESS_ASSERT(it != right->coeffs.end());
3299 rnorm = it->second.get_norm_tree();
3300 if (it->second.has_coeff())
3301 rc = it->second.coeff().reconstruct_tensor();
3302 }
3303
3304 // both nodes are leaf nodes: multiply and return
3305 if (rc.size() && lc.size()) { // Yipee!
3306 do_mul<L,R>(key, lc, std::make_pair(key,rc));
3307 return;
3308 }
3309
3310 if (tol) {
3311 if (lc.size())
3312 lnorm = lc.normf(); // Otherwise got from norm tree above
3313 if (rc.size())
3314 rnorm = rc.normf();
3315 if (lnorm*rnorm < truncate_tol(tol, key)) {
3316 coeffs.replace(key, nodeT(coeffT(cdata.vk,targs),false)); // Zero leaf node
3317 return;
3318 }
3319 }
3320
3321 // Recur down
3322 coeffs.replace(key, nodeT(coeffT(),true)); // Interior node
3323
3324 Tensor<L> lss;
3325 if (lc.size()) {
3326 Tensor<L> ld(cdata.v2k);
3327 ld(cdata.s0) = lc(___);
3328 lss = left->unfilter(ld);
3329 }
3330
3331 Tensor<R> rss;
3332 if (rc.size()) {
3333 Tensor<R> rd(cdata.v2k);
3334 rd(cdata.s0) = rc(___);
3335 rss = right->unfilter(rd);
3336 }
3337
3338 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
3339 const keyT& child = kit.key();
3340 Tensor<L> ll;
3341 Tensor<R> rr;
3342 if (lc.size())
3343 ll = copy(lss(child_patch(child)));
3344 if (rc.size())
3345 rr = copy(rss(child_patch(child)));
3346
3347 woT::task(coeffs.owner(child), &implT:: template mulXXa<L,R>, child, left, ll, right, rr, tol);
3348 }
3349 }
3350
3351
3352 // Binary operation on values using recursive descent and assuming same distribution
3353 /// Both left and right functions are in the scaling function basis
3354 /// @param[in] key the key to the current function node (box)
3355 /// @param[in] left the function impl associated with the left function
3356 /// @param[in] lcin the scaling function coefficients associated with the
3357 /// current box in the left function
3358 /// @param[in] right the function impl associated with the right function
3359 /// @param[in] rcin the scaling function coefficients associated with the
3360 /// current box in the right function
3361 /// @param[in] op the binary operator
3362 template <typename L, typename R, typename opT>
3363 void binaryXXa(const keyT& key,
3364 const FunctionImpl<L,NDIM>* left, const Tensor<L>& lcin,
3365 const FunctionImpl<R,NDIM>* right,const Tensor<R>& rcin,
3366 const opT& op) {
3367 typedef typename FunctionImpl<L,NDIM>::dcT::const_iterator literT;
3368 typedef typename FunctionImpl<R,NDIM>::dcT::const_iterator riterT;
3369
3370 Tensor<L> lc = lcin;
3371 if (lc.size() == 0) {
3372 literT it = left->coeffs.find(key).get();
3373 MADNESS_ASSERT(it != left->coeffs.end());
3374 if (it->second.has_coeff())
3375 lc = it->second.coeff().reconstruct_tensor();
3376 }
3377
3378 Tensor<R> rc = rcin;
3379 if (rc.size() == 0) {
3380 riterT it = right->coeffs.find(key).get();
3381 MADNESS_ASSERT(it != right->coeffs.end());
3382 if (it->second.has_coeff())
3383 rc = it->second.coeff().reconstruct_tensor();
3384 }
3385
3386 if (rc.size() && lc.size()) { // Yipee!
3387 do_binary_op<L,R>(key, lc, std::make_pair(key,rc), op);
3388 return;
3389 }
3390
3391 // Recur down
3392 coeffs.replace(key, nodeT(coeffT(),true)); // Interior node
3393
3394 Tensor<L> lss;
3395 if (lc.size()) {
3396 Tensor<L> ld(cdata.v2k);
3397 ld(cdata.s0) = lc(___);
3398 lss = left->unfilter(ld);
3399 }
3400
3401 Tensor<R> rss;
3402 if (rc.size()) {
3403 Tensor<R> rd(cdata.v2k);
3404 rd(cdata.s0) = rc(___);
3405 rss = right->unfilter(rd);
3406 }
3407
3408 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
3409 const keyT& child = kit.key();
3410 Tensor<L> ll;
3411 Tensor<R> rr;
3412 if (lc.size())
3413 ll = copy(lss(child_patch(child)));
3414 if (rc.size())
3415 rr = copy(rss(child_patch(child)));
3416
3417 woT::task(coeffs.owner(child), &implT:: template binaryXXa<L,R,opT>, child, left, ll, right, rr, op);
3418 }
3419 }
3420
3421 template <typename Q, typename opT>
3423 typedef typename opT::resultT resultT;
3425 opT op;
3426
3431
3432 Tensor<resultT> operator()(const Key<NDIM>& key, const Tensor<Q>& t) const {
3433 Tensor<Q> invalues = impl_func->coeffs2values(key, t);
3434
3435 Tensor<resultT> outvalues = op(key, invalues);
3436
3437 return impl_func->values2coeffs(key, outvalues);
3438 }
3439
3440 template <typename Archive>
3441 void serialize(Archive& ar) {
3442 ar & impl_func & op;
3443 }
3444 };
3445
3446 /// Out of place unary operation on function impl
3447 /// The skeleton algorithm should resemble something like
3448 ///
3449 /// *this = op(*func)
3450 ///
3451 /// @param[in] key the key of the current function node (box)
3452 /// @param[in] func the function impl on which to be operated
3453 /// @param[in] op the unary operator
3454 template <typename Q, typename opT>
3455 void unaryXXa(const keyT& key,
3456 const FunctionImpl<Q,NDIM>* func, const opT& op) {
3457
3458 // const Tensor<Q>& fc = func->coeffs.find(key).get()->second.full_tensor_copy();
3459 const Tensor<Q> fc = func->coeffs.find(key).get()->second.coeff().reconstruct_tensor();
3460
3461 if (fc.size() == 0) {
3462 // Recur down
3463 coeffs.replace(key, nodeT(coeffT(),true)); // Interior node
3464 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
3465 const keyT& child = kit.key();
3466 woT::task(coeffs.owner(child), &implT:: template unaryXXa<Q,opT>, child, func, op);
3467 }
3468 }
3469 else {
3470 tensorT t=op(key,fc);
3471 coeffs.replace(key, nodeT(coeffT(t,targs),false)); // Leaf node
3472 }
3473 }
3474
3475 /// Multiplies two functions (impl's) together. Delegates to the mulXXa() method
3476 /// @param[in] left pointer to the left function impl
3477 /// @param[in] right pointer to the right function impl
3478 /// @param[in] tol numerical tolerance
3479 template <typename L, typename R>
3480 void mulXX(const FunctionImpl<L,NDIM>* left, const FunctionImpl<R,NDIM>* right, double tol, bool fence) {
3481 if (world.rank() == coeffs.owner(cdata.key0))
3482 mulXXa(cdata.key0, left, Tensor<L>(), right, Tensor<R>(), tol);
3483 if (fence)
3484 world.gop.fence();
3485
3486 //verify_tree();
3487 }
3488
3489 /// Performs binary operation on two functions (impl's). Delegates to the binaryXXa() method
3490 /// @param[in] left pointer to the left function impl
3491 /// @param[in] right pointer to the right function impl
3492 /// @param[in] op the binary operator
3493 template <typename L, typename R, typename opT>
3495 const opT& op, bool fence) {
3496 if (world.rank() == coeffs.owner(cdata.key0))
3497 binaryXXa(cdata.key0, left, Tensor<L>(), right, Tensor<R>(), op);
3498 if (fence)
3499 world.gop.fence();
3500
3501 //verify_tree();
3502 }
3503
3504 /// Performs unary operation on function impl. Delegates to the unaryXXa() method
3505 /// @param[in] func function impl of the operand
3506 /// @param[in] op the unary operator
3507 template <typename Q, typename opT>
3508 void unaryXX(const FunctionImpl<Q,NDIM>* func, const opT& op, bool fence) {
3509 if (world.rank() == coeffs.owner(cdata.key0))
3510 unaryXXa(cdata.key0, func, op);
3511 if (fence)
3512 world.gop.fence();
3513
3514 //verify_tree();
3515 }
3516
3517 /// Performs unary operation on function impl. Delegates to the unaryXXa() method
3518 /// @param[in] func function impl of the operand
3519 /// @param[in] op the unary operator
3520 template <typename Q, typename opT>
3521 void unaryXXvalues(const FunctionImpl<Q,NDIM>* func, const opT& op, bool fence) {
3522 if (world.rank() == coeffs.owner(cdata.key0))
3523 unaryXXa(cdata.key0, func, coeff_value_adaptor<Q,opT>(func,op));
3524 if (fence)
3525 world.gop.fence();
3526
3527 //verify_tree();
3528 }
3529
3530 /// Multiplies a function (impl) with a vector of functions (impl's). Delegates to the
3531 /// mulXXveca() method.
3532 /// @param[in] left pointer to the left function impl
3533 /// @param[in] vright vector of pointers to the right function impl's
3534 /// @param[in] tol numerical tolerance
3535 /// @param[out] vresult vector of pointers to the resulting function impl's
3536 template <typename L, typename R>
3538 const std::vector<const FunctionImpl<R,NDIM>*>& vright,
3539 const std::vector<FunctionImpl<T,NDIM>*>& vresult,
3540 double tol,
3541 bool fence) {
3542 std::vector< Tensor<R> > vr(vright.size());
3543 if (world.rank() == coeffs.owner(cdata.key0))
3544 mulXXveca(cdata.key0, left, Tensor<L>(), vright, vr, vresult,
3546 if (fence)
3547 world.gop.fence();
3548 }
3549
3551
3552 mutable long box_leaf[1000];
3553 mutable long box_interior[1000];
3554
3555 // horrifically non-scalable
3556 void put_in_box(ProcessID from, long nl, long ni) const;
3557
3558 /// Prints summary of data distribution
3559 void print_info() const;
3560
3561 /// Verify tree is properly constructed ... global synchronization involved
3562
3563 /// If an inconsistency is detected, prints a message describing the error and
3564 /// then throws a madness exception.
3565 ///
3566 /// This is a reasonably quick and scalable operation that is
3567 /// useful for debugging and paranoia.
3568 void verify_tree() const;
3569
3570 /// check that parents and children are consistent
3571
3572 /// will not check proper size of coefficients
3573 /// global communication
3575
3576 /// check that the tree state and the coeffs are consistent
3577
3578 /// will not check existence of children and/or parents
3579 /// no communication
3581
3582 /// Walk up the tree returning pair(key,node) for first node with coefficients
3583
3584 /// Three possibilities.
3585 ///
3586 /// 1) The coeffs are present and returned with the key of the containing node.
3587 ///
3588 /// 2) The coeffs are further up the tree ... the request is forwarded up.
3589 ///
3590 /// 3) The coeffs are futher down the tree ... an empty tensor is returned.
3591 ///
3592 /// !! This routine is crying out for an optimization to
3593 /// manage the number of messages being sent ... presently
3594 /// each parent is fetched 2^(n*d) times where n is the no. of
3595 /// levels between the level of evaluation and the parent.
3596 /// Alternatively, reimplement multiply as a downward tree
3597 /// walk and just pass the parent down. Slightly less
3598 /// parallelism but much less communication.
3599 /// @todo Robert .... help!
3600 void sock_it_to_me(const keyT& key,
3601 const RemoteReference< FutureImpl< std::pair<keyT,coeffT> > >& ref) const;
3602 /// As above, except
3603 /// 3) The coeffs are constructed from the avg of nodes further down the tree
3604 /// @todo Robert .... help!
3605 void sock_it_to_me_too(const keyT& key,
3606 const RemoteReference< FutureImpl< std::pair<keyT,coeffT> > >& ref) const;
3607
3608 /// @todo help!
3610 const keyT& key,
3611 const coordT& plotlo, const coordT& plothi, const std::vector<long>& npt,
3612 bool eval_refine) const;
3613
3614
3615 /// Evaluate a cube/slice of points ... plotlo and plothi are already in simulation coordinates
3616 /// No communications
3617 /// @param[in] plotlo the coordinate of the starting point
3618 /// @param[in] plothi the coordinate of the ending point
3619 /// @param[in] npt the number of points in each dimension
3621 const coordT& plothi,
3622 const std::vector<long>& npt,
3623 const bool eval_refine = false) const;
3624
3625
3626 /// Evaluate function only if point is local returning (true,value); otherwise return (false,0.0)
3627
3628 /// maxlevel is the maximum depth to search down to --- the max local depth can be
3629 /// computed with max_local_depth();
3630 std::pair<bool,T> eval_local_only(const Vector<double,NDIM>& xin, Level maxlevel) ;
3631
3632 /// Allocation-free core of the batched eval_local_only: writes one
3633 /// (local?,value) pair per point, in input order, into results[0..npt).
3634 /// Consecutive points in the same leaf box share that box's descent and
3635 /// coefficient fetch (last-box memoization); each point is evaluated by
3636 /// the same eval_cube on the same tensor as the single-point path, so
3637 /// results are bit-for-bit identical. No communications.
3638 void eval_local_only(const Vector<double,NDIM>* xin, std::size_t npt,
3639 Level maxlevel, std::pair<bool,T>* results);
3640
3641 /// Batched eval_local_only returning a fresh vector (see the pointer
3642 /// core above for semantics).
3643 /// maxlevel is the maximum depth to search down to --- the max local depth can
3644 /// be computed with max_local_depth();
3645 std::vector<std::pair<bool,T>>
3646 eval_local_only(const std::vector<Vector<double,NDIM>>& xin, Level maxlevel) ;
3647
3648
3649 /// Evaluate the function at a point in \em simulation coordinates
3650
3651 /// Only the invoking process will get the result via the
3652 /// remote reference to a future. Active messages may be sent
3653 /// to other nodes.
3654 void eval(const Vector<double,NDIM>& xin,
3655 const keyT& keyin,
3656 const typename Future<T>::remote_refT& ref);
3657
3658 /// Get the depth of the tree at a point in \em simulation coordinates
3659
3660 /// Only the invoking process will get the result via the
3661 /// remote reference to a future. Active messages may be sent
3662 /// to other nodes.
3663 ///
3664 /// This function is a minimally-modified version of eval()
3666 const keyT& keyin,
3667 const typename Future<Level>::remote_refT& ref);
3668
3669 /// Get the rank of leaf box of the tree at a point in \em simulation coordinates
3670
3671 /// Only the invoking process will get the result via the
3672 /// remote reference to a future. Active messages may be sent
3673 /// to other nodes.
3674 ///
3675 /// This function is a minimally-modified version of eval()
3677 const keyT& keyin,
3678 const typename Future<long>::remote_refT& ref);
3679
3680
3681 /// Computes norm of low/high-order polyn. coeffs for autorefinement test
3682
3683 /// t is a k^d tensor. In order to screen the autorefinement
3684 /// during multiplication compute the norms of
3685 /// ... lo ... the block of t for all polynomials of order < k/2
3686 /// ... hi ... the block of t for all polynomials of order >= k/2
3687 ///
3688 /// k=5 0,1,2,3,4 --> 0,1,2 ... 3,4
3689 /// k=6 0,1,2,3,4,5 --> 0,1,2 ... 3,4,5
3690 ///
3691 /// k=number of wavelets, so k=5 means max order is 4, so max exactly
3692 /// representable squarable polynomial is of order 2.
3693 void static tnorm(const tensorT& t, double* lo, double* hi);
3694
3695 void static tnorm(const GenTensor<T>& t, double* lo, double* hi);
3696
3697 void static tnorm(const SVDTensor<T>& t, double* lo, double* hi, const int particle);
3698
3699 // This invoked if node has not been autorefined
3700 void do_square_inplace(const keyT& key);
3701
3702 // This invoked if node has been autorefined
3703 void do_square_inplace2(const keyT& parent, const keyT& child, const tensorT& parent_coeff);
3704
3705 /// Always returns false (for when autorefine is not wanted)
3706 bool noautorefine(const keyT& key, const tensorT& t) const;
3707
3708 /// Returns true if this block of coeffs needs autorefining
3709 bool autorefine_square_test(const keyT& key, const nodeT& t) const;
3710
3711 /// Pointwise squaring of function with optional global fence
3712
3713 /// If not autorefining, local computation only if not fencing.
3714 /// If autorefining, may result in asynchronous communication.
3715 void square_inplace(bool fence);
3716 void abs_inplace(bool fence);
3717 void abs_square_inplace(bool fence);
3718
3719 /// is this the same as trickle_down() ?
3720 void sum_down_spawn(const keyT& key, const coeffT& s);
3721
3722 /// After 1d push operator must sum coeffs down the tree to restore correct scaling function coefficients
3723 void sum_down(bool fence);
3724
3725 /// perform this multiplication: h(1,2) = f(1,2) * g(1)
3726 template<size_t LDIM>
3728
3729 static bool randomize() {return false;}
3733
3734 implT* h; ///< the result function h(1,2) = f(1,2) * g(1)
3737 int particle; ///< if g is g(1) or g(2)
3738
3739 multiply_op() : h(), f(), g(), particle(1) {}
3740
3741 multiply_op(implT* h1, const ctT& f1, const ctL& g1, const int particle1)
3742 : h(h1), f(f1), g(g1), particle(particle1) {};
3743
3744 /// return true if this will be a leaf node
3745
3746 /// use generalization of tnorm for a GenTensor
3747 bool screen(const coeffT& fcoeff, const coeffT& gcoeff, const keyT& key) const {
3749 MADNESS_ASSERT(fcoeff.is_svd_tensor());
3750 MADNESS_ASSERT(g.get_impl());
3752
3753 double glo=0.0, ghi=0.0, flo=0.0, fhi=0.0;
3754 g.get_impl()->tnorm(gcoeff.get_tensor(), &glo, &ghi);
3755 g.get_impl()->tnorm(fcoeff.get_svdtensor(),&flo,&fhi,particle);
3756
3757 double total_hi=glo*fhi + ghi*flo + fhi*ghi;
3758 return (total_hi<h->truncate_tol(h->get_thresh(),key));
3759
3760 }
3761
3762 /// apply this on a FunctionNode of f and g of Key key
3763
3764 /// @param[in] key key for FunctionNode in f and g, (g: broken into particles)
3765 /// @return <this node is a leaf, coefficients of this node>
3766 std::pair<bool,coeffT> operator()(const Key<NDIM>& key) const {
3767
3768 // bool is_leaf=(not fdatum.second.has_children());
3769 // if (not is_leaf) return std::pair<bool,coeffT> (is_leaf,coeffT());
3770
3771 // break key into particles (these are the child keys, with f/gdatum come the parent keys)
3772 Key<LDIM> key1,key2;
3773 key.break_apart(key1,key2);
3774 const Key<LDIM> gkey= (particle==1) ? key1 : key2;
3775
3776 // get coefficients of the actual FunctionNode
3777 coeffT coeff1=f.get_impl()->parent_to_child(f.coeff(),f.key(),key);
3778 coeff1.normalize();
3779 const coeffT coeff2=g.get_impl()->parent_to_child(g.coeff(),g.key(),gkey);
3780
3781 // multiplication is done in TT_2D
3782 coeffT coeff1_2D=coeff1.convert(TensorArgs(h->get_thresh(),TT_2D));
3783 coeff1_2D.normalize();
3784
3785 bool is_leaf=screen(coeff1_2D,coeff2,key);
3786 if (key.level()<2) is_leaf=false;
3787
3788 coeffT hcoeff;
3789 if (is_leaf) {
3790
3791 // convert coefficients to values
3792 coeffT hvalues=f.get_impl()->coeffs2values(key,coeff1_2D);
3793 coeffT gvalues=g.get_impl()->coeffs2values(gkey,coeff2);
3794
3795 // perform multiplication
3796 coeffT result_val=h->multiply(hvalues,gvalues,particle-1);
3797
3798 hcoeff=h->values2coeffs(key,result_val);
3799
3800 // conversion on coeffs, not on values, because it implies truncation!
3801 if (not hcoeff.is_of_tensortype(h->get_tensor_type()))
3802 hcoeff=hcoeff.convert(h->get_tensor_args());
3803 }
3804
3805 return std::pair<bool,coeffT> (is_leaf,hcoeff);
3806 }
3807
3808 this_type make_child(const keyT& child) const {
3809
3810 // break key into particles
3811 Key<LDIM> key1, key2;
3812 child.break_apart(key1,key2);
3813 const Key<LDIM> gkey= (particle==1) ? key1 : key2;
3814
3815 return this_type(h,f.make_child(child),g.make_child(gkey),particle);
3816 }
3817
3819 Future<ctT> f1=f.activate();
3820 Future<ctL> g1=g.activate();
3821 return h->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
3823 }
3824
3825 this_type forward_ctor(implT* h1, const ctT& f1, const ctL& g1, const int particle) {
3826 return this_type(h1,f1,g1,particle);
3827 }
3828
3829 template <typename Archive> void serialize(const Archive& ar) {
3830 ar & h & f & g & particle;
3831 }
3832 };
3833
3834
3835 /// add two functions f and g: result=alpha * f + beta * g
3836 struct add_op {
3837
3840
3841 bool randomize() const {return false;}
3842
3843 /// tracking coeffs of first and second addend
3845 /// prefactor for f, g
3846 double alpha, beta;
3847
3848 add_op() = default;
3849 add_op(const ctT& f, const ctT& g, const double alpha, const double beta)
3850 : f(f), g(g), alpha(alpha), beta(beta){}
3851
3852 /// if we are at the bottom of the trees, return the sum of the coeffs
3853 std::pair<bool,coeffT> operator()(const keyT& key) const {
3854
3855 bool is_leaf=(f.is_leaf() and g.is_leaf());
3856 if (not is_leaf) return std::pair<bool,coeffT> (is_leaf,coeffT());
3857
3858 coeffT fcoeff=f.get_impl()->parent_to_child(f.coeff(),f.key(),key);
3859 coeffT gcoeff=g.get_impl()->parent_to_child(g.coeff(),g.key(),key);
3860 coeffT hcoeff=copy(fcoeff);
3861 hcoeff.gaxpy(alpha,gcoeff,beta);
3862 hcoeff.reduce_rank(f.get_impl()->get_tensor_args().thresh);
3863 return std::pair<bool,coeffT> (is_leaf,hcoeff);
3864 }
3865
3866 this_type make_child(const keyT& child) const {
3867 return this_type(f.make_child(child),g.make_child(child),alpha,beta);
3868 }
3869
3870 /// retrieve the coefficients (parent coeffs might be remote)
3872 Future<ctT> f1=f.activate();
3873 Future<ctT> g1=g.activate();
3874 return f.get_impl()->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
3876 }
3877
3878 /// taskq-compatible ctor
3879 this_type forward_ctor(const ctT& f1, const ctT& g1, const double alpha, const double beta) {
3880 return this_type(f1,g1,alpha,beta);
3881 }
3882
3883 template <typename Archive> void serialize(const Archive& ar) {
3884 ar & f & g & alpha & beta;
3885 }
3886
3887 };
3888
3889 /// multiply f (a pair function of NDIM) with an orbital g (LDIM=NDIM/2)
3890
3891 /// as in (with h(1,2)=*this) : h(1,2) = g(1) * f(1,2)
3892 /// use tnorm as a measure to determine if f (=*this) must be refined
3893 /// @param[in] f the NDIM function f=f(1,2)
3894 /// @param[in] g the LDIM function g(1) (or g(2))
3895 /// @param[in] particle 1 or 2, as in g(1) or g(2)
3896 template<size_t LDIM>
3897 void multiply(const implT* f, const FunctionImpl<T,LDIM>* g, const int particle) {
3898
3901
3902 typedef multiply_op<LDIM> coeff_opT;
3903 coeff_opT coeff_op(this,ff,gg,particle);
3904
3905 typedef insert_op<T,NDIM> apply_opT;
3906 apply_opT apply_op(this);
3907
3908 keyT key0=f->cdata.key0;
3909 if (world.rank() == coeffs.owner(key0)) {
3910 ProcessID p= coeffs.owner(key0);
3911 woT::task(p, &implT:: template forward_traverse<coeff_opT,apply_opT>, coeff_op, apply_op, key0);
3912 }
3913
3915 }
3916
3917 /// Hartree product of two LDIM functions to yield a NDIM = 2*LDIM function
3918 template<size_t LDIM, typename leaf_opT>
3919 struct hartree_op {
3920 bool randomize() const {return false;}
3921
3924
3925 implT* result; ///< where to construct the pair function
3926 ctL p1, p2; ///< tracking coeffs of the two lo-dim functions
3927 leaf_opT leaf_op; ///< determine if a given node will be a leaf node
3928
3929 // ctor
3931 hartree_op(implT* result, const ctL& p11, const ctL& p22, const leaf_opT& leaf_op)
3932 : result(result), p1(p11), p2(p22), leaf_op(leaf_op) {
3933 MADNESS_ASSERT(LDIM+LDIM==NDIM);
3934 }
3935
3936 std::pair<bool,coeffT> operator()(const Key<NDIM>& key) const {
3937
3938 // break key into particles (these are the child keys, with datum1/2 come the parent keys)
3939 Key<LDIM> key1,key2;
3940 key.break_apart(key1,key2);
3941
3942 // this returns the appropriate NS coeffs for key1 and key2 resp.
3943 const coeffT fcoeff=p1.coeff(key1);
3944 const coeffT gcoeff=p2.coeff(key2);
3945 bool is_leaf=leaf_op(key,fcoeff.full_tensor(),gcoeff.full_tensor());
3946 if (not is_leaf) return std::pair<bool,coeffT> (is_leaf,coeffT());
3947
3948 // extract the sum coeffs from the NS coeffs
3949 const coeffT s1=fcoeff(p1.get_impl()->cdata.s0);
3950 const coeffT s2=gcoeff(p2.get_impl()->cdata.s0);
3951
3952 // new coeffs are simply the hartree/kronecker/outer product --
3953 coeffT coeff=outer(s1,s2,result->get_tensor_args());
3954 // no post-determination
3955 // is_leaf=leaf_op(key,coeff);
3956 return std::pair<bool,coeffT>(is_leaf,coeff);
3957 }
3958
3959 this_type make_child(const keyT& child) const {
3960
3961 // break key into particles
3962 Key<LDIM> key1, key2;
3963 child.break_apart(key1,key2);
3964
3965 return this_type(result,p1.make_child(key1),p2.make_child(key2),leaf_op);
3966 }
3967
3969 Future<ctL> p11=p1.activate();
3970 Future<ctL> p22=p2.activate();
3971 return result->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
3973 }
3974
3975 this_type forward_ctor(implT* result1, const ctL& p11, const ctL& p22, const leaf_opT& leaf_op) {
3976 return this_type(result1,p11,p22,leaf_op);
3977 }
3978
3979 template <typename Archive> void serialize(const Archive& ar) {
3980 ar & result & p1 & p2 & leaf_op;
3981 }
3982 };
3983
3984 /// traverse a non-existing tree
3985
3986 /// part II: activate coeff_op, i.e. retrieve all the necessary remote boxes (communication)
3987 /// @param[in] coeff_op operator making the coefficients that needs activation
3988 /// @param[in] apply_op just passing thru
3989 /// @param[in] key the key we are working on
3990 template<typename coeff_opT, typename apply_opT>
3991 void forward_traverse(const coeff_opT& coeff_op, const apply_opT& apply_op, const keyT& key) const {
3992 MADNESS_ASSERT(coeffs.is_local(key));
3993 Future<coeff_opT> active_coeff=coeff_op.activate();
3994 woT::task(world.rank(), &implT:: template traverse_tree<coeff_opT,apply_opT>, active_coeff, apply_op, key);
3995 }
3996
3997
3998 /// traverse a non-existing tree
3999
4000 /// part I: make the coefficients, process them and continue the recursion if necessary
4001 /// @param[in] coeff_op operator making the coefficients and determining them being leaves
4002 /// @param[in] apply_op operator processing the coefficients
4003 /// @param[in] key the key we are currently working on
4004 template<typename coeff_opT, typename apply_opT>
4005 void traverse_tree(const coeff_opT& coeff_op, const apply_opT& apply_op, const keyT& key) const {
4006 MADNESS_ASSERT(coeffs.is_local(key));
4007
4008 typedef typename std::pair<bool,coeffT> argT;
4009 const argT arg=coeff_op(key);
4010 apply_op.operator()(key,arg.second,arg.first);
4011
4012 const bool has_children=(not arg.first);
4013 if (has_children) {
4014 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
4015 const keyT& child=kit.key();
4016 coeff_opT child_op=coeff_op.make_child(child);
4017 // spawn activation where child is local
4018 ProcessID p=coeffs.owner(child);
4019
4020 void (implT::*ft)(const coeff_opT&, const apply_opT&, const keyT&) const = &implT::forward_traverse<coeff_opT,apply_opT>;
4021
4022 woT::task(p, ft, child_op, apply_op, child);
4023 }
4024 }
4025 }
4026
4027
4028 /// given two functions of LDIM, perform the Hartree/Kronecker/outer product
4029
4030 /// |Phi(1,2)> = |phi(1)> x |phi(2)>
4031 /// @param[in] p1 FunctionImpl of particle 1
4032 /// @param[in] p2 FunctionImpl of particle 2
4033 /// @param[in] leaf_op operator determining of a given box will be a leaf
4034 template<std::size_t LDIM, typename leaf_opT>
4035 void hartree_product(const std::vector<std::shared_ptr<FunctionImpl<T,LDIM>>> p1,
4036 const std::vector<std::shared_ptr<FunctionImpl<T,LDIM>>> p2,
4037 const leaf_opT& leaf_op, bool fence) {
4038 MADNESS_CHECK_THROW(p1.size()==p2.size(),"hartree_product: p1 and p2 must have the same size");
4039 for (auto& p : p1) MADNESS_CHECK(p->is_nonstandard() or p->is_nonstandard_with_leaves());
4040 for (auto& p : p2) MADNESS_CHECK(p->is_nonstandard() or p->is_nonstandard_with_leaves());
4041
4042 const keyT key0=cdata.key0;
4043
4044 for (std::size_t i=0; i<p1.size(); ++i) {
4045 if (world.rank() == this->get_coeffs().owner(key0)) {
4046
4047 // prepare the CoeffTracker
4048 CoeffTracker<T,LDIM> iap1(p1[i].get());
4049 CoeffTracker<T,LDIM> iap2(p2[i].get());
4050
4051 // the operator making the coefficients
4052 typedef hartree_op<LDIM,leaf_opT> coeff_opT;
4053 coeff_opT coeff_op(this,iap1,iap2,leaf_op);
4054
4055 // this operator simply inserts the coeffs into this' tree
4056// typedef insert_op<T,NDIM> apply_opT;
4057 typedef accumulate_op<T,NDIM> apply_opT;
4058 apply_opT apply_op(this);
4059
4060 woT::task(world.rank(), &implT:: template forward_traverse<coeff_opT,apply_opT>,
4061 coeff_op, apply_op, cdata.key0);
4062
4063 }
4064 }
4065
4067 if (fence) world.gop.fence();
4068 }
4069
4070
4071 template <typename opT, typename R>
4072 void
4074 const opT* op = pop.ptr;
4075 const Level n = key.level();
4076 const double cnorm = c.normf();
4077 const double tol = truncate_tol(thresh, key)*0.1; // ??? why this value????
4078
4080 const Translation lold = lnew[axis];
4081 const Translation maxs = Translation(1)<<n;
4082
4083 int nsmall = 0; // Counts neglected blocks to terminate s loop
4084 for (Translation s=0; s<maxs; ++s) {
4085 int maxdir = s ? 1 : -1;
4086 for (int direction=-1; direction<=maxdir; direction+=2) {
4087 lnew[axis] = lold + direction*s;
4088 if (lnew[axis] >= 0 && lnew[axis] < maxs) { // NON-ZERO BOUNDARY CONDITIONS IGNORED HERE !!!!!!!!!!!!!!!!!!!!
4089 const Tensor<typename opT::opT>& r = op->rnlij(n, s*direction, true);
4090 double Rnorm = r.normf();
4091
4092 if (Rnorm == 0.0) {
4093 return; // Hard zero means finished!
4094 }
4095
4096 if (s <= 1 || r.normf()*cnorm > tol) { // Always do kernel and neighbor
4097 nsmall = 0;
4098 tensorT result = transform_dir(c,r,axis);
4099
4100 if (result.normf() > tol*0.3) {
4101 Key<NDIM> dest(n,lnew);
4102 coeffs.task(dest, &nodeT::accumulate2, result, coeffs, dest, TaskAttributes::hipri());
4103 }
4104 }
4105 else {
4106 ++nsmall;
4107 }
4108 }
4109 else {
4110 ++nsmall;
4111 }
4112 }
4113 if (nsmall >= 4) {
4114 // If have two negligble blocks in
4115 // succession in each direction interpret
4116 // this as the operator being zero beyond
4117 break;
4118 }
4119 }
4120 }
4121
4122 template <typename opT, typename R>
4123 void
4124 apply_1d_realspace_push(const opT& op, const FunctionImpl<R,NDIM>* f, int axis, bool fence) {
4125 MADNESS_ASSERT(!f->is_compressed());
4126
4127 typedef typename FunctionImpl<R,NDIM>::dcT::const_iterator fiterT;
4128 typedef FunctionNode<R,NDIM> fnodeT;
4129 fiterT end = f->coeffs.end();
4130 ProcessID me = world.rank();
4131 for (fiterT it=f->coeffs.begin(); it!=end; ++it) {
4132 const fnodeT& node = it->second;
4133 if (node.has_coeff()) {
4134 const keyT& key = it->first;
4135 const Tensor<R>& c = node.coeff().full_tensor_copy();
4138 }
4139 }
4140 if (fence) world.gop.fence();
4141 }
4142
4144 const implT* f,
4145 const keyT& key,
4146 const std::pair<keyT,coeffT>& left,
4147 const std::pair<keyT,coeffT>& center,
4148 const std::pair<keyT,coeffT>& right);
4149
4151 const implT* f,
4152 const keyT& key,
4153 const std::pair<keyT,coeffT>& left,
4154 const std::pair<keyT,coeffT>& center,
4155 const std::pair<keyT,coeffT>& right);
4156
4157 // Called by result function to differentiate f
4158 void diff(const DerivativeBase<T,NDIM>* D, const implT* f, bool fence);
4159
4160 /// Returns key of general neighbor enforcing BC
4161
4162 /// Out of volume keys are mapped to enforce the BC as follows.
4163 /// * Periodic BC map back into the volume and return the correct key
4164 /// * non-periodic BC - returns invalid() to indicate out of volume
4165 keyT neighbor(const keyT& key, const keyT& disp, const array_of_bools<NDIM>& is_periodic) const;
4166
4167 /// Returns key of general neighbor that resides in-volume
4168
4169 /// Out of volume keys are mapped to invalid()
4170 keyT neighbor_in_volume(const keyT& key, const keyT& disp) const;
4171
4172 /// find_me. Called by diff_bdry to get coefficients of boundary function
4174
4175 /// return the a std::pair<key, node>, which MUST exist
4176 std::pair<Key<NDIM>,ShallowNode<T,NDIM> > find_datum(keyT key) const;
4177
4178 /// multiply the ket with a one-electron potential rr(1,2)= f(1,2)*g(1)
4179
4180 /// @param[in] val_ket function values of f(1,2)
4181 /// @param[in] val_pot function values of g(1)
4182 /// @param[in] particle if 0 then g(1), if 1 then g(2)
4183 /// @return the resulting function values
4184 coeffT multiply(const coeffT& val_ket, const coeffT& val_pot, int particle) const;
4185
4186
4187 /// given several coefficient tensors, assemble a result tensor
4188
4189 /// the result looks like: (v(1,2) + v(1) + v(2)) |ket(1,2)>
4190 /// or (v(1,2) + v(1) + v(2)) |p(1) p(2)>
4191 /// i.e. coefficients for the ket and coefficients for the two particles are
4192 /// mutually exclusive. All potential terms are optional, just pass in empty coeffs.
4193 /// @param[in] key the key of the FunctionNode to which these coeffs belong
4194 /// @param[in] coeff_ket coefficients of the ket
4195 /// @param[in] vpotential1 function values of the potential for particle 1
4196 /// @param[in] vpotential2 function values of the potential for particle 2
4197 /// @param[in] veri function values for the 2-particle potential
4198 coeffT assemble_coefficients(const keyT& key, const coeffT& coeff_ket,
4199 const coeffT& vpotential1, const coeffT& vpotential2,
4200 const tensorT& veri) const;
4201
4202
4203
4204 template<std::size_t LDIM>
4208 double error=0.0;
4209 double lo=0.0, hi=0.0, lo1=0.0, hi1=0.0, lo2=0.0, hi2=0.0;
4210
4212 pointwise_multiplier(const Key<NDIM> key, const coeffT& clhs) : coeff_lhs(clhs) {
4213 const auto& fcf=FunctionCommonFunctionality<T,NDIM>(coeff_lhs.dim(0));
4214 val_lhs=fcf.coeffs2values(key,coeff_lhs);
4215 error=0.0;
4217 if (coeff_lhs.is_svd_tensor()) {
4218 FunctionImpl<T,LDIM>::tnorm(coeff_lhs.get_svdtensor(),&lo1,&hi1,1);
4219 FunctionImpl<T,LDIM>::tnorm(coeff_lhs.get_svdtensor(),&lo2,&hi2,2);
4220 }
4221 }
4222
4223 /// multiply values of rhs and lhs, result on rhs, rhs and lhs are of the same dimensions
4224 tensorT operator()(const Key<NDIM> key, const tensorT& coeff_rhs) {
4225
4226 MADNESS_ASSERT(coeff_rhs.dim(0)==coeff_lhs.dim(0));
4228
4229 // the tnorm estimate is not tight enough to be efficient, better use oversampling
4230 bool use_tnorm=false;
4231 if (use_tnorm) {
4232 double rlo, rhi;
4233 implT::tnorm(coeff_rhs,&rlo,&rhi);
4234 error = hi*rlo + rhi*lo + rhi*hi;
4235 tensorT val_rhs=fcf.coeffs2values(key, coeff_rhs);
4236 val_rhs.emul(val_lhs.full_tensor());
4237 return fcf.values2coeffs(key,val_rhs);
4238 } else { // use quadrature of order k+1
4239
4240 auto& cdata=FunctionCommonData<T,NDIM>::get(coeff_rhs.dim(0)); // npt=k+1
4241 auto& cdata_npt=FunctionCommonData<T,NDIM>::get(coeff_rhs.dim(0)+oversampling); // npt=k+1
4242 FunctionCommonFunctionality<T,NDIM> fcf_hi_npt(cdata_npt);
4243
4244 // coeffs2values for rhs: k -> npt=k+1
4245 tensorT coeff1(cdata_npt.vk);
4246 coeff1(cdata.s0)=coeff_rhs; // s0 is smaller than vk!
4247 tensorT val_rhs_k1=fcf_hi_npt.coeffs2values(key,coeff1);
4248
4249 // coeffs2values for lhs: k -> npt=k+1
4250 tensorT coeff_lhs_k1(cdata_npt.vk);
4251 coeff_lhs_k1(cdata.s0)=std::as_const(coeff_lhs).full_tensor();
4252 tensorT val_lhs_k1=fcf_hi_npt.coeffs2values(key,coeff_lhs_k1);
4253
4254 // multiply
4255 val_lhs_k1.emul(val_rhs_k1);
4256
4257 // values2coeffs: npt = k+1-> k
4258 tensorT result1=fcf_hi_npt.values2coeffs(key,val_lhs_k1);
4259
4260 // extract coeffs up to k
4261 tensorT result=copy(result1(cdata.s0));
4262 result1(cdata.s0)=0.0;
4263 error=result1.normf();
4264 return result;
4265 }
4266 }
4267
4268 /// multiply values of rhs and lhs, result on rhs, rhs and lhs are of differnet dimensions
4269 coeffT operator()(const Key<NDIM> key, const tensorT& coeff_rhs, const int particle) {
4270 Key<LDIM> key1, key2;
4271 key.break_apart(key1,key2);
4272 const long k=coeff_rhs.dim(0);
4274 auto& cdata_lowdim=FunctionCommonData<T,LDIM>::get(k);
4275 FunctionCommonFunctionality<T,LDIM> fcf_lo(cdata_lowdim);
4279
4280
4281 // make hi-dim values from lo-dim coeff_rhs on npt grid points
4282 tensorT ones=tensorT(fcf_lo_npt.cdata.vk);
4283 ones=1.0;
4284
4285 tensorT coeff_rhs_npt1(fcf_lo_npt.cdata.vk);
4286 coeff_rhs_npt1(fcf_lo.cdata.s0)=coeff_rhs;
4287 tensorT val_rhs_npt1=fcf_lo_npt.coeffs2values(key1,coeff_rhs_npt1);
4288
4289 TensorArgs targs(-1.0,TT_2D);
4290 coeffT val_rhs;
4291 if (particle==1) val_rhs=outer(val_rhs_npt1,ones,targs);
4292 if (particle==2) val_rhs=outer(ones,val_rhs_npt1,targs);
4293
4294 // make values from hi-dim coeff_lhs on npt grid points
4295 coeffT coeff_lhs_k1(fcf_hi_npt.cdata.vk,coeff_lhs.tensor_type());
4296 coeff_lhs_k1(fcf_hi.cdata.s0)+=coeff_lhs;
4297 coeffT val_lhs_npt=fcf_hi_npt.coeffs2values(key,coeff_lhs_k1);
4298
4299 // multiply
4300 val_lhs_npt.emul(val_rhs);
4301
4302 // values2coeffs: npt = k+1-> k
4303 coeffT result1=fcf_hi_npt.values2coeffs(key,val_lhs_npt);
4304
4305 // extract coeffs up to k
4306 coeffT result=copy(result1(cdata.s0));
4307 result1(cdata.s0)=0.0;
4308 error=result1.normf();
4309 return result;
4310 }
4311
4312 template <typename Archive> void serialize(const Archive& ar) {
4313 ar & error & lo & lo1 & lo2 & hi & hi1& hi2 & val_lhs & coeff_lhs;
4314 }
4315
4316
4317 };
4318
4319 /// given a ket and the 1- and 2-electron potentials, construct the function V phi
4320
4321 /// small memory footstep version of Vphi_op: use the NS form to have information
4322 /// about parent and children to determine if a box is a leaf. This will require
4323 /// compression of the constituent functions, which will lead to more memory usage
4324 /// there, but will avoid oversampling of the result function.
4325 template<typename opT, size_t LDIM>
4326 struct Vphi_op_NS {
4327
4328 bool randomize() const {return true;}
4329
4333
4334 implT* result; ///< where to construct Vphi, no need to track parents
4335 opT leaf_op; ///< deciding if a given FunctionNode will be a leaf node
4336 ctT iaket; ///< the ket of a pair function (exclusive with p1, p2)
4337 ctL iap1, iap2; ///< the particles 1 and 2 (exclusive with ket)
4338 ctL iav1, iav2; ///< potentials for particles 1 and 2
4339 const implT* eri; ///< 2-particle potential, must be on-demand
4340
4341 bool have_ket() const {return iaket.get_impl();}
4342 bool have_v1() const {return iav1.get_impl();}
4343 bool have_v2() const {return iav2.get_impl();}
4344 bool have_eri() const {return eri;}
4345
4346 void accumulate_into_result(const Key<NDIM>& key, const coeffT& coeff) const {
4347 result->get_coeffs().task(key, &nodeT::accumulate, coeff, result->get_coeffs(), key, result->targs, false);
4348 }
4349
4350 // ctor
4352 Vphi_op_NS(implT* result, const opT& leaf_op, const ctT& iaket,
4353 const ctL& iap1, const ctL& iap2, const ctL& iav1, const ctL& iav2,
4354 const implT* eri)
4356 , iav1(iav1), iav2(iav2), eri(eri) {
4357
4358 // 2-particle potential must be on-demand
4359 if (eri) MADNESS_ASSERT(eri->is_on_demand());
4360 }
4361
4362 /// make and insert the coefficients into result's tree
4363 std::pair<bool,coeffT> operator()(const Key<NDIM>& key) const {
4364
4365 MADNESS_ASSERT(result->get_coeffs().is_local(key));
4366 if(leaf_op.do_pre_screening()){
4367 // this means that we only construct the boxes which are leaf boxes from the other function in the leaf_op
4368 if(leaf_op.pre_screening(key)){
4369 // construct sum_coefficients, insert them and leave
4370 auto [sum_coeff, error]=make_sum_coeffs(key);
4371 accumulate_into_result(key,sum_coeff);
4372 return std::pair<bool,coeffT> (true,coeffT());
4373 }else{
4374 return continue_recursion(std::vector<bool>(1<<NDIM,false),tensorT(),key);
4375 }
4376 }
4377
4378 // this means that the function has to be completely constructed and not mirrored by another function
4379
4380 // if the initial level is not reached then this must not be a leaf box
4381 size_t il = result->get_initial_level();
4383 if(key.level()<int(il)){
4384 return continue_recursion(std::vector<bool>(1<<NDIM,false),tensorT(),key);
4385 }
4386 // if further refinement is needed (because we are at a special box, special point)
4387 // and the special_level is not reached then this must not be a leaf box
4388 if(key.level()<result->get_special_level() and leaf_op.special_refinement_needed(key)){
4389 return continue_recursion(std::vector<bool>(1<<NDIM,false),tensorT(),key);
4390 }
4391
4392 auto [sum_coeff,error]=make_sum_coeffs(key);
4393
4394 // coeffs are leaf (for whatever reason), insert into tree and stop recursion
4395 if(leaf_op.post_screening(key,sum_coeff)){
4396 accumulate_into_result(key,sum_coeff);
4397 return std::pair<bool,coeffT> (true,coeffT());
4398 }
4399
4400 // coeffs are accurate, insert into tree and stop recursion
4401 if(error<result->truncate_tol(result->get_thresh(),key)){
4402 accumulate_into_result(key,sum_coeff);
4403 return std::pair<bool,coeffT> (true,coeffT());
4404 }
4405
4406 // coeffs are inaccurate, continue recursion
4407 std::vector<bool> child_is_leaf(1<<NDIM,false);
4408 return continue_recursion(child_is_leaf,tensorT(),key);
4409 }
4410
4411
4412 /// loop over all children and either insert their sum coeffs or continue the recursion
4413
4414 /// @param[in] child_is_leaf for each child: is it a leaf?
4415 /// @param[in] coeffs coefficient tensor with 2^N sum coeffs (=unfiltered NS coeffs)
4416 /// @param[in] key the key for the NS coeffs (=parent key of the children)
4417 /// @return to avoid recursion outside this return: std::pair<is_leaf,coeff> = true,coeffT()
4418 std::pair<bool,coeffT> continue_recursion(const std::vector<bool> child_is_leaf,
4419 const tensorT& coeffs, const keyT& key) const {
4420 std::size_t i=0;
4421 for (KeyChildIterator<NDIM> kit(key); kit; ++kit, ++i) {
4422 keyT child=kit.key();
4423 bool is_leaf=child_is_leaf[i];
4424
4425 if (is_leaf) {
4426 // insert the sum coeffs
4428 iop(child,coeffT(copy(coeffs(result->child_patch(child))),result->get_tensor_args()),is_leaf);
4429 } else {
4430 this_type child_op=this->make_child(child);
4431 noop<T,NDIM> no;
4432 // spawn activation where child is local
4433 ProcessID p=result->get_coeffs().owner(child);
4434
4435 void (implT::*ft)(const Vphi_op_NS<opT,LDIM>&, const noop<T,NDIM>&, const keyT&) const = &implT:: template forward_traverse< Vphi_op_NS<opT,LDIM>, noop<T,NDIM> >;
4436 result->task(p, ft, child_op, no, child);
4437 }
4438 }
4439 // return e sum coeffs; also return always is_leaf=true:
4440 // the recursion is continued within this struct, not outside in traverse_tree!
4441 return std::pair<bool,coeffT> (true,coeffT());
4442 }
4443
4444 tensorT eri_coeffs(const keyT& key) const {
4447 if (eri->get_functor()->provides_coeff()) {
4448 return eri->get_functor()->coeff(key).full_tensor();
4449 } else {
4450 tensorT val_eri(eri->cdata.vk);
4451 eri->fcube(key,*(eri->get_functor()),eri->cdata.quad_x,val_eri);
4452 return eri->values2coeffs(key,val_eri);
4453 }
4454 }
4455
4456 /// the error is computed from the d coefficients of the constituent functions
4457
4458 /// the result is h_n = P_n(f g), computed as h_n \approx Pn(f_n g_n)
4459 /// its error is therefore
4460 /// h_n = (f g)_n = ((Pn(f) + Qn(f)) (Pn(g) + Qn(g))
4461 /// = Pn(fn gn) + Qn(fn gn) + Pn(f) Qn(g) + Qn(f) Pn(g) + Qn(f) Pn(g)
4462 /// the first term is what we compute, the second term is estimated by tnorm (in another function),
4463 /// the third to last terms are estimated in this function by e.g.: Qn(f)Pn(g) < ||Qn(f)|| ||Pn(g)||
4465 const tensorT& ceri) const {
4466 double error = 0.0;
4467 Key<LDIM> key1, key2;
4468 key.break_apart(key1,key2);
4469
4470 PROFILE_BLOCK(compute_error);
4471 double dnorm_ket, snorm_ket;
4472 if (have_ket()) {
4473 snorm_ket=iaket.coeff(key).normf();
4474 dnorm_ket=iaket.dnorm(key);
4475 } else {
4476 double s1=iap1.coeff(key1).normf();
4477 double s2=iap2.coeff(key2).normf();
4478 double d1=iap1.dnorm(key1);
4479 double d2=iap2.dnorm(key2);
4480 snorm_ket=s1*s2;
4481 dnorm_ket=s1*d2 + s2*d1 + d1*d2;
4482 }
4483
4484 if (have_v1()) {
4485 double snorm=iav1.coeff(key1).normf();
4486 double dnorm=iav1.dnorm(key1);
4487 error+=snorm*dnorm_ket + dnorm*snorm_ket + dnorm*dnorm_ket;
4488 }
4489 if (have_v2()) {
4490 double snorm=iav2.coeff(key2).normf();
4491 double dnorm=iav2.dnorm(key2);
4492 error+=snorm*dnorm_ket + dnorm*snorm_ket + dnorm*dnorm_ket;
4493 }
4494 if (have_eri()) {
4495 tensorT s_coeffs=ceri(result->cdata.s0);
4496 double snorm=s_coeffs.normf();
4497 tensorT d=copy(ceri);
4498 d(result->cdata.s0)=0.0;
4499 double dnorm=d.normf();
4500 error+=snorm*dnorm_ket + dnorm*snorm_ket + dnorm*dnorm_ket;
4501 }
4502
4503 bool no_potential=not ((have_v1() or have_v2() or have_eri()));
4504 if (no_potential) {
4505 error=dnorm_ket;
4506 }
4507 return error;
4508 }
4509
4510 /// make the sum coeffs for key
4511 std::pair<coeffT,double> make_sum_coeffs(const keyT& key) const {
4513 // break key into particles
4514 Key<LDIM> key1, key2;
4515 key.break_apart(key1,key2);
4516
4517 // bool printme=(int(key.translation()[0])==int(std::pow(key.level(),2)/2)) and
4518 // (int(key.translation()[1])==int(std::pow(key.level(),2)/2)) and
4519 // (int(key.translation()[2])==int(std::pow(key.level(),2)/2));
4520
4521// printme=false;
4522
4523 // get/make all coefficients
4524 const coeffT coeff_ket = (iaket.get_impl()) ? iaket.coeff(key)
4525 : outer(iap1.coeff(key1),iap2.coeff(key2),result->get_tensor_args());
4526 const coeffT cpot1 = (have_v1()) ? iav1.coeff(key1) : coeffT();
4527 const coeffT cpot2 = (have_v2()) ? iav2.coeff(key2) : coeffT();
4528 const tensorT ceri = (have_eri()) ? eri_coeffs(key) : tensorT();
4529
4530 // compute first part of the total error
4531 double refine_error=compute_error_from_inaccurate_refinement(key,ceri);
4532 double error=refine_error;
4533
4534 // prepare the multiplication
4535 pointwise_multiplier<LDIM> pm(key,coeff_ket);
4536
4537 // perform the multiplication, compute tnorm part of the total error
4538 coeffT cresult(result->cdata.vk,result->get_tensor_args());
4539 if (have_v1()) {
4540 cresult+=pm(key,cpot1.get_tensor(),1);
4541 error+=pm.error;
4542 }
4543 if (have_v2()) {
4544 cresult+=pm(key,cpot2.get_tensor(),2);
4545 error+=pm.error;
4546 }
4547
4548 if (have_eri()) {
4549 tensorT result1=cresult.full_tensor_copy();
4550 result1+=pm(key,copy(ceri(result->cdata.s0)));
4551 cresult=coeffT(result1,result->get_tensor_args());
4552 error+=pm.error;
4553 } else {
4554 cresult.reduce_rank(result->get_tensor_args().thresh);
4555 }
4556 if ((not have_v1()) and (not have_v2()) and (not have_eri())) {
4557 cresult=coeff_ket;
4558 }
4559
4560 return std::make_pair(cresult,error);
4561 }
4562
4563 this_type make_child(const keyT& child) const {
4564
4565 // break key into particles
4566 Key<LDIM> key1, key2;
4567 child.break_apart(key1,key2);
4568
4569 return this_type(result,leaf_op,iaket.make_child(child),
4570 iap1.make_child(key1),iap2.make_child(key2),
4571 iav1.make_child(key1),iav2.make_child(key2),eri);
4572 }
4573
4575 Future<ctT> iaket1=iaket.activate();
4576 Future<ctL> iap11=iap1.activate();
4577 Future<ctL> iap21=iap2.activate();
4578 Future<ctL> iav11=iav1.activate();
4579 Future<ctL> iav21=iav2.activate();
4580 return result->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
4582 iaket1,iap11,iap21,iav11,iav21,eri);
4583 }
4584
4585 this_type forward_ctor(implT* result1, const opT& leaf_op, const ctT& iaket1,
4586 const ctL& iap11, const ctL& iap21, const ctL& iav11, const ctL& iav21,
4587 const implT* eri1) {
4588 return this_type(result1,leaf_op,iaket1,iap11,iap21,iav11,iav21,eri1);
4589 }
4590
4591 /// serialize this (needed for use in recursive_op)
4592 template <typename Archive> void serialize(const Archive& ar) {
4593 ar & iaket & eri & result & leaf_op & iap1 & iap2 & iav1 & iav2;
4594 }
4595 };
4596
4597 /// adaptive multiplication of u(1,2) by optional orbitals i(1) and/or j(2),
4598 /// with optional contraction against an exact ERI g(1,2)
4599
4600 /// Modes (controlled by which pointers are non-null):
4601 /// - function mode (eri==null): builds result = u * [i(1)] * [j(2)] into *result
4602 /// - inner mode (eri!=null): computes <eri | u * [i(1)] * [j(2)]>,
4603 /// accumulates scalar at result->cdata.key0
4604 ///
4605 /// Error control: d-norm cross terms (inaccurate refinement, from CoeffTracker::dnorm)
4606 /// plus tnorm error from each pointwise_multiplier application.
4607 /// Replaces multiply_op<LDIM> (adds d-norm error) and do_inner_local_on_demand
4608 /// (adds inaccurate-refinement error and adaptive NS-form traversal).
4609 template<typename opT, size_t LDIM>
4611
4612 bool randomize() const {return true;}
4616
4617 implT* result; ///< output function (function mode) or accumulation handle (inner mode)
4619 ctT iau; ///< u(1,2), NS form, required
4620 ctL iai; ///< i(1), NS form, optional (null impl → skip particle-1 multiply)
4621 ctL iaj; ///< j(2), NS form, optional (null impl → skip particle-2 multiply)
4622 const implT* eri; ///< exact ERI g(1,2), on-demand, optional (null → function mode)
4623 double target_precision; ///< leaf threshold used for the post-screening truncation
4624 int oversampling; ///< pointwise_multiplier oversampling (k+oversampling grid)
4625
4626 bool have_i() const {return iai.get_impl();}
4627 bool have_j() const {return iaj.get_impl();}
4628 bool have_eri() const {return eri;}
4629
4630 void accumulate_into_result(const keyT& key, const coeffT& coeff) const {
4631 result->get_coeffs().task(key, &nodeT::accumulate, coeff,
4632 result->get_coeffs(), key, result->targs, false);
4633 }
4634
4636
4638 const ctL& iai, const ctL& iaj, const implT* eri,
4639 double target_precision, int oversampling=1)
4642 if (eri) MADNESS_ASSERT(eri->is_on_demand());
4643 }
4644
4645 /// error from inaccurate refinement: cross terms of (snorm,dnorm) for u*[i]*[j]
4647 const Key<LDIM>& key1,
4648 const Key<LDIM>& key2) const {
4649 const double sn_u = iau.coeff(key).normf();
4650 const double dn_u = iau.dnorm(key);
4651 const double sn_i = have_i() ? iai.coeff(key1).normf() : 1.0;
4652 const double dn_i = have_i() ? iai.dnorm(key1) : 0.0;
4653 const double sn_j = have_j() ? iaj.coeff(key2).normf() : 1.0;
4654 const double dn_j = have_j() ? iaj.dnorm(key2) : 0.0;
4655
4656 // all cross-terms with at least one d-norm factor
4657 return sn_u*sn_i*dn_j + sn_u*dn_i*sn_j + dn_u*sn_i*sn_j
4658 + sn_u*dn_i*dn_j + dn_u*sn_i*dn_j + dn_u*dn_i*sn_j
4659 + dn_u*dn_i*dn_j;
4660 }
4661
4662 /// get ERI coefficients at key (same logic as Vphi_op_NS::eri_coeffs)
4663 tensorT eri_coeffs(const keyT& key) const {
4665 if (eri->get_functor()->provides_coeff()) {
4666 return eri->get_functor()->coeff(key).full_tensor();
4667 } else {
4668 tensorT val_eri(eri->cdata.vk);
4669 eri->fcube(key, *(eri->get_functor()), eri->cdata.quad_x, val_eri);
4670 return eri->values2coeffs(key, val_eri);
4671 }
4672 }
4673
4674 /// multiplicative chain: current = u → u·i(1) → u·i(1)·j(2)
4675 ///
4676 /// Inputs are expected in *redundant* tree state (cheap to construct from reconstructed,
4677 /// avoids the expensive compress/nonstandard round-trip). In that state
4678 /// CoeffTracker::coeff(key) calls parent_to_child and returns scaling-function
4679 /// coefficients of shape [k]^NDIM, exactly what pointwise_multiplier consumes.
4680 ///
4681 /// Past a leaf the CoeffTracker pins key_ to the leaf, so coeff(key) interpolates
4682 /// the leaf's [k]^NDIM s-coeffs to the requested level — equivalent to embedding
4683 /// them in a [2k]^NDIM NS tensor (s-part filled, d-part zero) and unfiltering.
4684 /// d-norms past the leaf are zero, which falls out of CoeffTracker::dnorm.
4685 std::pair<coeffT,double> make_sum_coeffs(const keyT& key) const {
4686 Key<LDIM> key1, key2;
4687 key.break_apart(key1, key2);
4688
4689 coeffT current = iau.coeff(key);
4690 double error = compute_error_from_inaccurate_refinement(key, key1, key2);
4691
4692 if (have_i()) {
4693 const coeffT coeff_i = iai.coeff(key1);
4694 pointwise_multiplier<LDIM> pm(key, current);
4696 current = pm(key, coeff_i.full_tensor(), 1);
4697 error += pm.error;
4698 }
4699 if (have_j()) {
4700 const coeffT coeff_j = iaj.coeff(key2);
4701 pointwise_multiplier<LDIM> pm(key, current);
4703 current = pm(key, coeff_j.full_tensor(), 2);
4704 error += pm.error;
4705 }
4706 return {current, error};
4707 }
4708
4709 /// leaf action: insert into result (function mode) or contract with ERI and
4710 /// accumulate scalar as the first element of a vk-shaped tensor at key0 (inner mode)
4711 void leaf_action(const keyT& key, const coeffT& coeff) const {
4712 if (have_eri()) {
4713 const tensorT ceri = eri_coeffs(key);
4714 const T contribution = ceri.trace(coeff.full_tensor_copy());
4715 tensorT scalar(result->cdata.vk);
4716 scalar.flat()(0L) = contribution;
4717 accumulate_into_result(result->cdata.key0,
4718 coeffT(scalar, TensorArgs(TT_FULL, -1.0)));
4719 } else {
4720 accumulate_into_result(key, coeff);
4721 }
4722 }
4723
4724 std::pair<bool,coeffT> operator()(const keyT& key) const {
4725 MADNESS_ASSERT(result->get_coeffs().is_local(key));
4726
4727 if (leaf_op.do_pre_screening()) {
4728 if (leaf_op.pre_screening(key)) {
4729 auto [coeff, error] = make_sum_coeffs(key);
4730 leaf_action(key, coeff);
4731 return {true, coeffT()};
4732 } else {
4733 return continue_recursion(std::vector<bool>(1<<NDIM, false), tensorT(), key);
4734 }
4735 }
4736
4737 const size_t il = result->get_initial_level();
4738 if (key.level() < int(FunctionDefaults<NDIM>::get_refine() ? il+1 : il))
4739 return continue_recursion(std::vector<bool>(1<<NDIM, false), tensorT(), key);
4740
4741 if (key.level() < result->get_special_level() &&
4742 leaf_op.special_refinement_needed(key))
4743 return continue_recursion(std::vector<bool>(1<<NDIM, false), tensorT(), key);
4744
4745 auto [coeff, error] = make_sum_coeffs(key);
4746
4747 if (leaf_op.post_screening(key, coeff) ||
4749 leaf_action(key, coeff);
4750 return {true, coeffT()};
4751 }
4752
4753 return continue_recursion(std::vector<bool>(1<<NDIM, false), tensorT(), key);
4754 }
4755
4756 std::pair<bool,coeffT> continue_recursion(const std::vector<bool> child_is_leaf,
4757 const tensorT& coeffs,
4758 const keyT& key) const {
4759 std::size_t i = 0;
4760 for (KeyChildIterator<NDIM> kit(key); kit; ++kit, ++i) {
4761 const keyT child = kit.key();
4762 if (child_is_leaf[i]) {
4764 iop(child, coeffT(copy(coeffs(result->child_patch(child))),
4765 result->get_tensor_args()), true);
4766 } else {
4767 this_type child_op = this->make_child(child);
4768 noop<T,NDIM> no;
4769 const ProcessID p = result->get_coeffs().owner(child);
4770 void (implT::*ft)(const this_type&, const noop<T,NDIM>&, const keyT&) const
4771 = &implT::template forward_traverse<this_type, noop<T,NDIM>>;
4772 result->task(p, ft, child_op, no, child);
4773 }
4774 }
4775 return {true, coeffT()};
4776 }
4777
4778 this_type make_child(const keyT& child) const {
4779 Key<LDIM> key1, key2;
4780 child.break_apart(key1, key2);
4781 return this_type(result, leaf_op,
4782 iau.make_child(child),
4783 iai.make_child(key1),
4784 iaj.make_child(key2),
4786 }
4787
4789 Future<ctT> iau1 = iau.activate();
4790 Future<ctL> iai1 = iai.activate();
4791 Future<ctL> iaj1 = iaj.activate();
4792 return result->world.taskq.add(
4793 detail::wrap_mem_fn(*const_cast<this_type*>(this),
4795 result, leaf_op, iau1, iai1, iaj1, eri,
4797 }
4798
4799 this_type forward_ctor(implT* result1, const opT& leaf_op1,
4800 const ctT& iau1, const ctL& iai1, const ctL& iaj1,
4801 const implT* eri1, double target_precision1,
4802 int oversampling1) {
4803 return this_type(result1, leaf_op1, iau1, iai1, iaj1, eri1,
4804 target_precision1, oversampling1);
4805 }
4806
4807 template<typename Archive> void serialize(const Archive& ar) {
4809 }
4810 };
4811
4812 /// assemble the function V*phi using V and phi given from the functor
4813
4814 /// this function must have been constructed using the CompositeFunctorInterface.
4815 /// The interface provides one- and two-electron potentials, and the ket, which are
4816 /// assembled to give V*phi.
4817 /// @param[in] leaf_op operator to decide if a given node is a leaf node
4818 /// @param[in] fence global fence
4819 template<typename opT>
4820 void make_Vphi(const opT& leaf_op, const bool fence=true) {
4821
4822 constexpr size_t LDIM=NDIM/2;
4823 MADNESS_CHECK_THROW(NDIM==LDIM*2,"make_Vphi only works for even dimensions");
4824
4825
4826 // keep the functor available, but remove it from the result
4827 // result will return false upon is_on_demand(), which is necessary for the
4828 // CoeffTracker to track the parent coeffs correctly for error_leaf_op
4829 std::shared_ptr< FunctionFunctorInterface<T,NDIM> > func2(this->get_functor());
4830 this->unset_functor();
4831
4833 dynamic_cast<CompositeFunctorInterface<T,NDIM,LDIM>* >(&(*func2));
4835
4836 // make sure everything is in place if no fence is requested
4837 if (fence) func->make_redundant(true); // no-op if already redundant
4838 MADNESS_CHECK_THROW(func->check_redundant(),"make_Vphi requires redundant functions");
4839
4840 // loop over all functions in the functor (either ket or particles)
4841 for (auto& ket : func->impl_ket_vector) {
4842 FunctionImpl<T,NDIM>* eri=func->impl_eri.get();
4843 FunctionImpl<T,LDIM>* v1=func->impl_m1.get();
4844 FunctionImpl<T,LDIM>* v2=func->impl_m2.get();
4845 FunctionImpl<T,LDIM>* p1=nullptr;
4846 FunctionImpl<T,LDIM>* p2=nullptr;
4847 make_Vphi_only(leaf_op,ket.get(),v1,v2,p1,p2,eri,false);
4848 }
4849
4850 for (std::size_t i=0; i<func->impl_p1_vector.size(); ++i) {
4851 FunctionImpl<T,NDIM>* ket=nullptr;
4852 FunctionImpl<T,NDIM>* eri=func->impl_eri.get();
4853 FunctionImpl<T,LDIM>* v1=func->impl_m1.get();
4854 FunctionImpl<T,LDIM>* v2=func->impl_m2.get();
4855 FunctionImpl<T,LDIM>* p1=func->impl_p1_vector[i].get();
4856 FunctionImpl<T,LDIM>* p2=func->impl_p2_vector[i].get();
4857 make_Vphi_only(leaf_op,ket,v1,v2,p1,p2,eri,false);
4858 }
4859
4860 // some post-processing:
4861 // - FunctionNode::accumulate() uses buffer -> add the buffer contents to the actual coefficients
4862 // - the operation constructs sum coefficients on all scales -> sum down to get a well-defined tree-state
4863 if (fence) {
4864 world.gop.fence();
4865 flo_unary_op_node_inplace(do_consolidate_buffer(get_tensor_args(), false),true);
4866 sum_down(true);
4868 }
4869
4870
4871 }
4872
4873 /// assemble the function V*phi using V and phi given from the functor
4874
4875 /// this function must have been constructed using the CompositeFunctorInterface.
4876 /// The interface provides one- and two-electron potentials, and the ket, which are
4877 /// assembled to give V*phi.
4878 /// @param[in] leaf_op operator to decide if a given node is a leaf node
4879 /// @param[in] fence global fence
4880 template<typename opT, std::size_t LDIM>
4885 const bool fence=true) {
4886
4887 // prepare the CoeffTracker
4888 CoeffTracker<T,NDIM> iaket(ket);
4889 CoeffTracker<T,LDIM> iap1(p1);
4890 CoeffTracker<T,LDIM> iap2(p2);
4891 CoeffTracker<T,LDIM> iav1(v1);
4892 CoeffTracker<T,LDIM> iav2(v2);
4893
4894 // the operator making the coefficients
4895 typedef Vphi_op_NS<opT,LDIM> coeff_opT;
4896 coeff_opT coeff_op(this,leaf_op,iaket,iap1,iap2,iav1,iav2,eri);
4897
4898 // this operator simply inserts the coeffs into this' tree
4899 typedef noop<T,NDIM> apply_opT;
4900 apply_opT apply_op;
4901
4902 if (world.rank() == coeffs.owner(cdata.key0)) {
4903 woT::task(world.rank(), &implT:: template forward_traverse<coeff_opT,apply_opT>,
4904 coeff_op, apply_op, cdata.key0);
4905 }
4906
4908 if (fence) world.gop.fence();
4909
4910 }
4911
4912 /// build this = u * [i(1)] * [j(2)] adaptively using Vphi_ij_u_op_NS
4913
4914 /// All of u, i_func, j_func must be in redundant form.
4915 /// i_func and j_func must be replicated to all ranks (make_redundant).
4916 /// On return (after fence) this is in redundant_after_merge state;
4917 /// caller must call flo_unary_op_node_inplace(do_consolidate_buffer(...))
4918 /// then sum_down(true) and set_tree_state(reconstructed) to finalise.
4919 /// @param[in] leaf_op operator deciding leaf status (e.g. Leaf_op_other mirroring u)
4920 /// @param[in] u pair function u(1,2), NS form
4921 /// @param[in] i_func orbital i(1), NS form, replicated; may be null
4922 /// @param[in] j_func orbital j(2), NS form, replicated; may be null
4923 /// @param[in] fence global fence after completion
4924 template<typename opT, size_t LDIM>
4925 void make_Vphi_ij_u(const opT& leaf_op,
4926 const FunctionImpl<T,NDIM>* u,
4927 const FunctionImpl<T,LDIM>* i_func = nullptr,
4928 const FunctionImpl<T,LDIM>* j_func = nullptr,
4929 bool fence = true,
4930 double target_precision = 0.0,
4931 int oversampling = 1) {
4932 MADNESS_CHECK_THROW(NDIM == LDIM*2, "make_Vphi_ij_u requires NDIM==2*LDIM");
4933
4935 CoeffTracker<T,LDIM> iai(i_func);
4936 CoeffTracker<T,LDIM> iaj(j_func);
4937 if (target_precision == 0.0) target_precision = u->get_thresh();
4938
4939 typedef Vphi_ij_u_op_NS<opT,LDIM> coeff_opT;
4940 coeff_opT coeff_op(this, leaf_op, iau, iai, iaj, nullptr,
4941 target_precision, oversampling);
4942
4943 typedef noop<T,NDIM> apply_opT;
4944 apply_opT apply_op;
4945
4946 if (world.rank() == coeffs.owner(cdata.key0)) {
4947 woT::task(world.rank(),
4949 coeff_op, apply_op, cdata.key0);
4950 }
4951
4953 if (fence) world.gop.fence();
4954 }
4955
4956 /// compute <eri(1,2) | u(1,2) * [i(1)] * [j(2)]> adaptively using Vphi_ij_u_op_NS
4957
4958 /// Must be called collectively on all MPI ranks.
4959 /// All of u, i_func, j_func must be in NS form; i_func, j_func must be replicated.
4960 /// eri must be on-demand (provides_coeff() or evaluatable via fcube).
4961 /// @param[in] leaf_op operator deciding leaf status
4962 /// @param[in] u pair function u(1,2), NS form
4963 /// @param[in] i_func orbital i(1), NS form, replicated; may be null
4964 /// @param[in] j_func orbital j(2), NS form, replicated; may be null
4965 /// @param[in] eri exact on-demand 2-particle operator g(1,2)
4966 /// @return scalar inner product <eri | u * [i] * [j]>
4967 template<typename opT, size_t LDIM>
4969 const FunctionImpl<T,NDIM>* u,
4970 const FunctionImpl<T,LDIM>* i_func,
4971 const FunctionImpl<T,LDIM>* j_func,
4972 const FunctionImpl<T,NDIM>* eri,
4973 double target_precision=0.0,
4974 int oversampling=1) const {
4975 MADNESS_CHECK_THROW(NDIM == LDIM*2, "inner_ij_u_eri requires NDIM==2*LDIM");
4976 MADNESS_ASSERT(eri && eri->is_on_demand());
4977
4978 // All ranks create the accumulator collectively so its WorldObject ID is
4979 // consistent across ranks (MADNESS SPMD programming model requirement).
4980 implT acc(*u, u->get_pmap(), false); // empty copy of u: same k, thresh, pmap
4981 if (target_precision==0.0) target_precision=acc.get_thresh()*0.01;
4982
4983
4985 CoeffTracker<T,LDIM> iai(i_func);
4986 CoeffTracker<T,LDIM> iaj(j_func);
4987
4988 typedef Vphi_ij_u_op_NS<opT,LDIM> coeff_opT;
4989 coeff_opT coeff_op(&acc, leaf_op, iau, iai, iaj, eri, target_precision, oversampling);
4990
4991 typedef noop<T,NDIM> apply_opT;
4992 apply_opT apply_op;
4993
4994 if (world.rank() == acc.get_coeffs().owner(cdata.key0)) {
4995 woT::task(world.rank(),
4997 coeff_op, apply_op, cdata.key0);
4998 }
4999
5000 world.gop.fence();
5001
5002 // consolidate accumulated buffer at key0 into actual coefficients
5003 acc.flo_unary_op_node_inplace(do_consolidate_buffer(acc.get_tensor_args(), false), true);
5004
5005 // read scalar from first element of the vk-shaped tensor at key0 (inner mode convention)
5006 T inner_val = T(0);
5007 if (world.rank() == acc.get_coeffs().owner(cdata.key0)) {
5008 typename dcT::const_accessor a;
5009 if (acc.get_coeffs().find(a, cdata.key0))
5010 inner_val = a->second.coeff().full_tensor_copy().flat()(0L);
5011 }
5012 world.gop.sum(inner_val);
5013 return inner_val;
5014 }
5015
5016 /// Permute the dimensions of f according to map, result on this
5017 void mapdim(const implT& f, const std::vector<long>& map, bool fence);
5018
5019 /// mirror the dimensions of f according to map, result on this
5020 void mirror(const implT& f, const std::vector<long>& mirror, bool fence);
5021
5022 /// map and mirror the translation index and the coefficients, result on this
5023
5024 /// first map the dimensions, the mirror!
5025 /// this = mirror(map(f))
5026 void map_and_mirror(const implT& f, const std::vector<long>& map,
5027 const std::vector<long>& mirror, bool fence);
5028
5029 /// take the average of two functions, similar to: this=0.5*(this+rhs)
5030
5031 /// works in either basis and also in nonstandard form
5032 void average(const implT& rhs);
5033
5034 /// change the tensor type of the coefficients in the FunctionNode
5035
5036 /// @param[in] targs target tensor arguments (threshold and full/low rank)
5037 void change_tensor_type1(const TensorArgs& targs, bool fence);
5038
5039 /// reduce the rank of the coefficients tensors
5040
5041 /// @param[in] targs target tensor arguments (threshold and full/low rank)
5042 void reduce_rank(const double thresh, bool fence);
5043
5044
5045 /// remove all nodes with level higher than n
5046 void chop_at_level(const int n, const bool fence=true);
5047
5048 /// compute norm of s and d coefficients for all nodes
5049 void compute_snorm_and_dnorm(bool fence=true);
5050
5051 /// compute the norm of the wavelet coefficients
5054
5058
5059 bool operator()(typename rangeT::iterator& it) const {
5060 auto& node=it->second;
5061 node.recompute_snorm_and_dnorm(cdata);
5062 return true;
5063 }
5064 };
5065
5066
5067 T eval_cube(Level n, coordT& x, const tensorT& c) const;
5068
5069 /// Transform sum coefficients at level n to sums+differences at level n-1
5070
5071 /// Given scaling function coefficients s[n][l][i] and s[n][l+1][i]
5072 /// return the scaling function and wavelet coefficients at the
5073 /// coarser level. I.e., decompose Vn using Vn = Vn-1 + Wn-1.
5074 /// \code
5075 /// s_i = sum(j) h0_ij*s0_j + h1_ij*s1_j
5076 /// d_i = sum(j) g0_ij*s0_j + g1_ij*s1_j
5077 // \endcode
5078 /// Returns a new tensor and has no side effects. Works for any
5079 /// number of dimensions.
5080 ///
5081 /// No communication involved.
5082 tensorT filter(const tensorT& s) const;
5083
5084 coeffT filter(const coeffT& s) const;
5085
5086 /// Transform sums+differences at level n to sum coefficients at level n+1
5087
5088 /// Given scaling function and wavelet coefficients (s and d)
5089 /// returns the scaling function coefficients at the next finer
5090 /// level. I.e., reconstruct Vn using Vn = Vn-1 + Wn-1.
5091 /// \code
5092 /// s0 = sum(j) h0_ji*s_j + g0_ji*d_j
5093 /// s1 = sum(j) h1_ji*s_j + g1_ji*d_j
5094 /// \endcode
5095 /// Returns a new tensor and has no side effects
5096 ///
5097 /// If (sonly) ... then ss is only the scaling function coeff (and
5098 /// assume the d are zero). Works for any number of dimensions.
5099 ///
5100 /// No communication involved.
5101 tensorT unfilter(const tensorT& s) const;
5102
5103 coeffT unfilter(const coeffT& s) const;
5104
5105 /// downsample the sum coefficients of level n+1 to sum coeffs on level n
5106
5107 /// specialization of the filter method, will yield only the sum coefficients
5108 /// @param[in] key key of level n
5109 /// @param[in] v vector of sum coefficients of level n+1
5110 /// @return sum coefficients on level n in full tensor format
5111 tensorT downsample(const keyT& key, const std::vector< Future<coeffT > >& v) const;
5112
5113 /// upsample the sum coefficients of level 1 to sum coeffs on level n+1
5114
5115 /// specialization of the unfilter method, will transform only the sum coefficients
5116 /// @param[in] key key of level n+1
5117 /// @param[in] coeff sum coefficients of level n (does NOT belong to key!!)
5118 /// @return sum coefficients on level n+1
5119 coeffT upsample(const keyT& key, const coeffT& coeff) const;
5120
5121 /// Projects old function into new basis (only in reconstructed form)
5122 void project(const implT& old, bool fence);
5123
5125 bool operator()(const implT* f, const keyT& key, const nodeT& t) const {
5126 return true;
5127 }
5128 template <typename Archive> void serialize(Archive& ar) {}
5129 };
5130
5131 template <typename opT>
5132 void refine_op(const opT& op, const keyT& key) {
5133 // Must allow for someone already having autorefined the coeffs
5134 // and we get a write accessor just in case they are already executing
5135 typename dcT::accessor acc;
5136 const auto found = coeffs.find(acc,key);
5137 MADNESS_CHECK(found);
5138 nodeT& node = acc->second;
5139 if (node.has_coeff() && key.level() < max_refine_level && op(this, key, node)) {
5140 coeffT d(cdata.v2k,targs);
5141 d(cdata.s0) += copy(node.coeff());
5142 d = unfilter(d);
5143 node.clear_coeff();
5144 node.set_has_children(true);
5145 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
5146 const keyT& child = kit.key();
5147 coeffT ss = copy(d(child_patch(child)));
5148 ss.reduce_rank(targs.thresh);
5149 // coeffs.replace(child,nodeT(ss,-1.0,false).node_to_low_rank());
5150 coeffs.replace(child,nodeT(ss,-1.0,false));
5151 // Note value -1.0 for norm tree to indicate result of refinement
5152 }
5153 }
5154 }
5155
5156 template <typename opT>
5157 void refine_spawn(const opT& op, const keyT& key) {
5158 nodeT& node = coeffs.find(key).get()->second;
5159 if (node.has_children()) {
5160 for (KeyChildIterator<NDIM> kit(key); kit; ++kit)
5161 woT::task(coeffs.owner(kit.key()), &implT:: template refine_spawn<opT>, op, kit.key(), TaskAttributes::hipri());
5162 }
5163 else {
5164 woT::task(coeffs.owner(key), &implT:: template refine_op<opT>, op, key);
5165 }
5166 }
5167
5168 // Refine in real space according to local user-defined criterion
5169 template <typename opT>
5170 void refine(const opT& op, bool fence) {
5171 if (world.rank() == coeffs.owner(cdata.key0))
5172 woT::task(coeffs.owner(cdata.key0), &implT:: template refine_spawn<opT>, op, cdata.key0, TaskAttributes::hipri());
5173 if (fence)
5174 world.gop.fence();
5175 }
5176
5177 bool exists_and_has_children(const keyT& key) const;
5178
5179 bool exists_and_is_leaf(const keyT& key) const;
5180
5181
5182 void broaden_op(const keyT& key, const std::vector< Future <bool> >& v);
5183
5184 // For each local node sets norm_tree, snorm and dnorm to 0.0, and marks
5185 // dnorm_tree as uncomputed.
5187
5188 // Broaden tree
5189 void broaden(const array_of_bools<NDIM>& is_periodic, bool fence);
5190
5191 /// sum all the contributions from all scales after applying an operator in mod-NS form
5192 void trickle_down(bool fence);
5193
5194 /// sum all the contributions from all scales after applying an operator in mod-NS form
5195
5196 /// cf reconstruct_op
5197 void trickle_down_op(const keyT& key, const coeffT& s);
5198
5199 /// reconstruct this tree -- respects fence
5200 void reconstruct(bool fence);
5201
5202 void change_tree_state(const TreeState finalstate, bool fence=true);
5203
5204 // Invoked on node where key is local
5205 // void reconstruct_op(const keyT& key, const tensorT& s);
5206 void reconstruct_op(const keyT& key, const coeffT& s, const bool accumulate_NS=true);
5207
5208 /// compress the wave function
5209
5210 /// after application there will be sum coefficients at the root level,
5211 /// and difference coefficients at all other levels; furthermore:
5212 /// @param[in] nonstandard keep sum coeffs at all other levels, except leaves
5213 /// @param[in] keepleaves keep sum coeffs (but no diff coeffs) at leaves
5214 /// @param[in] redundant keep only sum coeffs at all levels, discard difference coeffs
5215// void compress(bool nonstandard, bool keepleaves, bool redundant, bool fence);
5216 void compress(const TreeState newstate, bool fence);
5217
5218 /// s coefficients plus the (snorm_tree, dnorm_tree) pair propagated up by compress
5219 typedef std::pair<coeffT, std::pair<double,double> > compressT;
5220
5221 /// Invoked on node where key is local
5222 Future<compressT> compress_spawn(const keyT& key, bool nonstandard, bool keepleaves,
5223 bool redundant1);
5224
5225 private:
5226 /// convert this to redundant, i.e. have sum coefficients on all levels
5227 void make_redundant(const bool fence);
5228 public:
5229
5230 /// convert this from redundant to standard reconstructed form
5231 void undo_redundant(const bool fence);
5232
5233 void remove_internal_coefficients(const bool fence);
5234 void remove_leaf_coefficients(const bool fence);
5235
5236
5237 /// compute for each FunctionNode the norm of the function inside that node
5238 void norm_tree(bool fence);
5239
5240 double norm_tree_op(const keyT& key, const std::vector< Future<double> >& v);
5241
5243
5244 /// truncate using a tree in reconstructed form
5245
5246 /// must be invoked where key is local
5247 Future<coeffT> truncate_reconstructed_spawn(const keyT& key, const double tol);
5248
5249 /// given the sum coefficients of all children, truncate or not
5250
5251 /// @return new sum coefficients (empty if internal, not empty, if new leaf); might delete its children
5252 coeffT truncate_reconstructed_op(const keyT& key, const std::vector< Future<coeffT > >& v, const double tol);
5253
5254 /// calculate the wavelet coefficients using the sum coefficients of all child nodes
5255
5256 /// also propagates norm_tree and dnorm_tree for all nodes
5257 /// @param[in] key this's key
5258 /// @param[in] v sum coefficients and propagated norms of the child nodes
5259 /// @param[in] nonstandard keep the sum coefficients with the wavelet coefficients
5260 /// @return the sum coefficients and propagated norms
5261 compressT compress_op(const keyT& key, const std::vector< Future<compressT> >& v, bool nonstandard);
5262
5263
5264 /// similar to compress_op, but insert only the sum coefficients in the tree
5265
5266 /// also propagates norm_tree and dnorm_tree for all nodes
5267 /// @param[in] key this's key
5268 /// @param[in] v sum coefficients and propagated norms of the child nodes
5269 /// @return the sum coefficients and propagated norms
5270 compressT make_redundant_op(const keyT& key,const std::vector< Future<compressT> >& v);
5271
5272 /// Changes non-standard compressed form to standard compressed form
5273 void standard(bool fence);
5274
5275 /// Changes non-standard compressed form to standard compressed form
5278
5279 // threshold for rank reduction / SVD truncation
5281
5282 // constructor takes target precision
5283 do_standard() = default;
5285
5286 //
5287 bool operator()(typename rangeT::iterator& it) const {
5288
5289 const keyT& key = it->first;
5290 nodeT& node = it->second;
5291 if (key.level()> 0 && node.has_coeff()) {
5292 if (node.has_children()) {
5293 // Zero out scaling coeffs
5294 MADNESS_ASSERT(node.coeff().dim(0)==2*impl->get_k());
5295 node.coeff()(impl->cdata.s0)=0.0;
5296 node.reduceRank(impl->targs.thresh);
5297 } else {
5298 // Deleting both scaling and wavelet coeffs
5299 node.clear_coeff();
5300 }
5301 }
5302 return true;
5303 }
5304 template <typename Archive> void serialize(const Archive& ar) {
5305 MADNESS_EXCEPTION("no serialization of do_standard",1);
5306 }
5307 };
5308
5309
5310 /// laziness
5311 template<size_t OPDIM>
5312 struct do_op_args {
5315 double tol, fac, cnorm;
5316
5317 do_op_args() = default;
5318 do_op_args(const Key<OPDIM>& key, const Key<OPDIM>& d, const keyT& dest, double tol, double fac, double cnorm)
5319 : key(key), d(d), dest(dest), tol(tol), fac(fac), cnorm(cnorm) {}
5320 template <class Archive>
5321 void serialize(Archive& ar) {
5322 ar & archive::wrap_opaque(this,1);
5323 }
5324 };
5325
5326 /// for fine-grain parallelism: call the apply method of an operator in a separate task
5327
5328 /// @param[in] op the operator working on our function
5329 /// @param[in] c full rank tensor holding the NS coefficients
5330 /// @param[in] args laziness holding norm of the coefficients, displacement, destination, ..
5331 template <typename opT, typename R, size_t OPDIM>
5332 void do_apply_kernel(const opT* op, const Tensor<R>& c, const do_op_args<OPDIM>& args) {
5333
5334 tensorT result = op->apply(args.key, args.d, c, args.tol/args.fac/args.cnorm);
5335
5336 // Screen here to reduce communication cost of negligible data
5337 // and also to ensure we don't needlessly widen the tree when
5338 // applying the operator
5339 if (result.normf()> 0.3*args.tol/args.fac) {
5340 coeffs.task(args.dest, &nodeT::accumulate2, result, coeffs, args.dest, TaskAttributes::hipri());
5341 //woT::task(world.rank(),&implT::accumulate_timer,time,TaskAttributes::hipri());
5342 // UGLY BUT ADDED THE OPTIMIZATION BACK IN HERE EXPLICITLY/
5343 if (args.dest == world.rank()) {
5344 coeffs.send(args.dest, &nodeT::accumulate, result, coeffs, args.dest, true);
5345 }
5346 else {
5347 coeffs.task(args.dest, &nodeT::accumulate, result, coeffs, args.dest, true, TaskAttributes::hipri());
5348 }
5349 }
5350 }
5351
5352 /// same as do_apply_kernel, but use full rank tensors as input and low rank tensors as output
5353
5354 /// @param[in] op the operator working on our function
5355 /// @param[in] c full rank tensor holding the NS coefficients
5356 /// @param[in] args laziness holding norm of the coefficients, displacement, destination, ..
5357 /// @param[in] apply_targs TensorArgs with tightened threshold for accumulation
5358 /// @return nothing, but accumulate the result tensor into the destination node
5359 template <typename opT, typename R, size_t OPDIM>
5360 double do_apply_kernel2(const opT* op, const Tensor<R>& c, const do_op_args<OPDIM>& args,
5361 const TensorArgs& apply_targs) {
5362
5363 tensorT result_full = op->apply(args.key, args.d, c, args.tol/args.fac/args.cnorm);
5364 const double norm=result_full.normf();
5365
5366 // Screen here to reduce communication cost of negligible data
5367 // and also to ensure we don't needlessly widen the tree when
5368 // applying the operator
5369 // OPTIMIZATION NEEDED HERE ... CHANGING THIS TO TASK NOT SEND REMOVED
5370 // BUILTIN OPTIMIZATION TO SHORTCIRCUIT MSG IF DATA IS LOCAL
5371 if (norm > 0.3*args.tol/args.fac) {
5372
5373 small++;
5374 //double cpu0=cpu_time();
5375 coeffT result=coeffT(result_full,apply_targs);
5376 MADNESS_ASSERT(result.is_full_tensor() or result.is_svd_tensor());
5377 //double cpu1=cpu_time();
5378 //timer_lr_result.accumulate(cpu1-cpu0);
5379
5380 coeffs.task(args.dest, &nodeT::accumulate, result, coeffs, args.dest, apply_targs, true,
5382
5383 //woT::task(world.rank(),&implT::accumulate_timer,time,TaskAttributes::hipri());
5384 }
5385 return norm;
5386 }
5387
5388
5389
5390 /// same as do_apply_kernel2, but use low rank tensors as input and low rank tensors as output
5391
5392 /// @param[in] op the operator working on our function
5393 /// @param[in] coeff full rank tensor holding the NS coefficients
5394 /// @param[in] args laziness holding norm of the coefficients, displacement, destination, ..
5395 /// @param[in] apply_targs TensorArgs with tightened threshold for accumulation
5396 /// @return nothing, but accumulate the result tensor into the destination node
5397 template <typename opT, typename R, size_t OPDIM>
5398 double do_apply_kernel3(const opT* op, const GenTensor<R>& coeff, const do_op_args<OPDIM>& args,
5399 const TensorArgs& apply_targs) {
5400
5401 coeffT result;
5402 if (2*OPDIM==NDIM) result= op->apply2_lowdim(args.key, args.d, coeff,
5403 args.tol/args.fac/args.cnorm, args.tol/args.fac);
5404 if (OPDIM==NDIM) result = op->apply2(args.key, args.d, coeff,
5405 args.tol/args.fac/args.cnorm, args.tol/args.fac);
5406
5407 const double result_norm=result.svd_normf();
5408
5409 if (result_norm> 0.3*args.tol/args.fac) {
5410 small++;
5411
5412 double cpu0=cpu_time();
5413 if (not result.is_of_tensortype(targs.tt)) result=result.convert(targs);
5414 double cpu1=cpu_time();
5415 timer_lr_result.accumulate(cpu1-cpu0);
5416
5417 // accumulate also expects result in SVD form
5418 coeffs.task(args.dest, &nodeT::accumulate, result, coeffs, args.dest, apply_targs, true,
5420// woT::task(world.rank(),&implT::accumulate_timer,time,TaskAttributes::hipri());
5421
5422 }
5423 return result_norm;
5424
5425 }
5426
5427 // volume of n-dimensional sphere of radius R
5428 double vol_nsphere(int n, double R) {
5429 return std::pow(madness::constants::pi,n*0.5)*std::pow(R,n)/std::tgamma(1+0.5*n);
5430 }
5431
5432
5433 /// apply an operator on the coeffs c (at node key)
5434
5435 /// the result is accumulated inplace to this's tree at various FunctionNodes
5436 /// @param[in] op the operator to act on the source function
5437 /// @param[in] key key of the source FunctionNode of f which is processed
5438 /// @param[in] c coeffs of the FunctionNode of f which is processed
5439 template <typename opT, typename R>
5440 void do_apply(const opT* op, const keyT& key, const Tensor<R>& c) {
5442
5443 // working assumption here WAS that the operator is
5444 // isotropic and monotonically decreasing with distance
5445 // ... however, now we are using derivative Gaussian
5446 // expansions (and also non-cubic boxes) isotropic is
5447 // violated. While not strictly monotonically decreasing,
5448 // the derivative gaussian is still such that once it
5449 // becomes negligible we are in the asymptotic region.
5450
5451 typedef typename opT::keyT opkeyT;
5452 constexpr auto opdim = opT::opdim;
5453 const opkeyT source = op->get_source_key(key);
5454
5455 // Tuning here is based on observation that with
5456 // sufficiently high-order wavelet relative to the
5457 // precision, that only nearest neighbor boxes contribute,
5458 // whereas for low-order wavelets more neighbors will
5459 // contribute. Sufficiently high is picked as
5460 // k>=2-log10(eps) which is our empirical rule for
5461 // efficiency/accuracy and code instrumentation has
5462 // previously indicated that (in 3D) just unit
5463 // displacements are invoked. The error decays as R^-(k+1),
5464 // and the number of boxes increases as R^d.
5465 //
5466 // Fac is the expected number of contributions to a given
5467 // box, so the error permitted per contribution will be
5468 // tol/fac
5469
5470 // radius of shell (nearest neighbor is diameter of 3 boxes, so radius=1.5)
5471 double radius = 1.5 + 0.33 * std::max(0.0, 2 - std::log10(thresh) -
5472 k); // 0.33 was 0.5
5473 //double radius = 2.5;
5474 double fac = vol_nsphere(NDIM, radius);
5475 // previously fac=10.0 selected empirically constrained by qmprop
5476
5477 double cnorm = c.normf();
5478
5479 // BC handling:
5480 // - if operator is lattice-summed then treat this as nonperiodic (i.e. tell neighbor() to stay in simulation cell)
5481 // - if operator is NOT lattice-summed then obey BC (i.e. tell neighbor() to go outside the simulation cell along periodic dimensions)
5482 // - BUT user can force operator to treat its arguments as non-periodic (`op.set_domain_periodicity({true,true,true})`) so ... which dimensions of this function are treated as periodic by op?
5483 const array_of_bools<NDIM> func_is_treated_by_op_as_periodic =
5484 (op->particle() == 1)
5486 op->func_domain_is_periodic())
5488 op->func_domain_is_periodic());
5489
5490 // the metric the kernel decays with, which also orders op->get_disp() (see SeparatedConvolution::displacement_real_distsq)
5491 const auto default_real_distance_squared = [&](const auto &displacement)
5492 -> double {
5493 return op->displacement_real_distsq(displacement);
5494 };
5495 const auto default_lattice_distance_squared = [&](const auto &displacement)
5496 -> std::uint64_t {
5497 return op->displacement_distsq(displacement);
5498 };
5499 const auto default_skip_predicate = [&](const auto &displacement)
5500 -> bool {
5501 return false;
5502 };
5503 const auto for_each = [&](auto &displacements,
5504 const auto &real_distance_squared,
5505 const auto &lattice_distance_squared,
5506 const auto &skip_predicate) -> std::optional<double> {
5507
5508 // used to screen estimated and actual contributions
5509 //const double tol = truncate_tol(thresh, key);
5510 //const double tol = 0.1*truncate_tol(thresh, key);
5511 const double tol = truncate_tol(thresh, key);
5512
5513 // assume isotropic decaying kernel, screen in shell-wise fashion by
5514 // monitoring the decay of magnitude of contribution norms with the
5515 // distance ... as soon as we find a shell of displacements at least
5516 // one of each in simulation domain (see neighbor()) and
5517 // all in-domain shells produce negligible contributions, stop.
5518 // a displacement is negligible if ||op|| * ||c|| > tol / fac
5519 // where fac takes into account
5520 int nvalid = 1; // Counts #valid at each distance
5521 int nused = 1; // Counts #used at each distance
5522 std::optional<double> real_last_distsq;
5523 std::optional<std::uint64_t> lattice_last_distsq;
5524
5525 // displacements to a face of the kernel range boundary are typically same magnitude (modulo variation),
5526 // but faces can be at quite different distances (anisotropic cells, lattice summation along some axes only,
5527 // mixed-parity ranges). Estimate the norm of the contributions from each face using its probing
5528 // displacement, skip the faces whose contributions are negligible, and skip everything if all are.
5529 if constexpr (std::is_same_v<std::decay_t<decltype(displacements)>,BoxSurfaceDisplacementRange<opdim>>) {
5530 bool any_face_survives = false;
5531 const auto &probing_displacements = displacements.probing_displacements();
5532 for (std::size_t d = 0; d != opdim; ++d) {
5533 if (!probing_displacements[d]) continue; // no face normal to an unlimited dimension
5534 const double opnorm =
5535 op->norm(key.level(), *probing_displacements[d], source);
5536 if (cnorm * opnorm <= tol / fac)
5537 displacements.skip_face(d);
5538 else
5539 any_face_survives = true;
5540 }
5541 if (!any_face_survives) {
5542 return {};
5543 }
5544 }
5545
5546 for (const auto& displacement: displacements) {
5547 if (skip_predicate(displacement)) continue;
5548
5549 keyT d;
5550 Key<NDIM - opdim> nullkey(key.level());
5551 MADNESS_ASSERT(op->particle() == 1 || op->particle() == 2);
5552 if (op->particle() == 1)
5553 d = displacement.merge_with(nullkey);
5554 else
5555 d = nullkey.merge_with(displacement);
5556
5557 // Screen out shells. We assume shells are grouped into shells so that the operator decays with shell index.
5558 // Shells are indexed by least distance from box to the central box.
5559 // Cells touching so much as a corner of the central box are further grouped by their lattice distance.
5560 // N.B. lattice-summed decaying kernel is periodic (i.e. does decay w.r.t. r), so loop over shells of displacements sorted by distances modulated by periodicity (Key::distsq_bc);
5561 // the rest-of-crystal kernel decays away from the nearest image instead (Key::distsq_images); the operator supplies the metric
5562 const auto real_distsq = real_distance_squared(displacement);
5563 const std::uint64_t lattice_distsq = real_distsq ? 0 : lattice_distance_squared(displacement);
5564 if (!real_last_distsq.has_value() ||
5565 !same_displacement_shell(real_distsq, *real_last_distsq) || (*real_last_distsq == 0 && lattice_distsq != *lattice_last_distsq)) { // Moved to next shell of neighbors
5566 if (nvalid > 0 && nused == 0 && (real_distsq > 0 || lattice_distsq > 1)) {
5567 // Have at least done the input box and all first
5568 // nearest neighbors, and none of the last set
5569 // of neighbors made significant contributions. Thus,
5570 // assuming monotonic decrease, we are done.
5571 break;
5572 }
5573 nused = 0;
5574 nvalid = 0;
5575 real_last_distsq = real_distsq;
5576 // After real_last_distsq > 0, we stop caring about keeping lattice_last_distsq up-to-date.
5577 lattice_last_distsq = real_distsq ? std::optional<std::uint64_t>{} : lattice_distsq;
5578 }
5579
5580 keyT dest = neighbor(key, d, func_is_treated_by_op_as_periodic);
5581 if (dest.is_valid()) {
5582 nvalid++;
5583 const double opnorm = op->norm(key.level(), displacement, source);
5584
5585 if (cnorm * opnorm > tol / fac) {
5586 tensorT result =
5587 op->apply(source, displacement, c, tol / fac / cnorm);
5588 if (result.normf() > 0.3 * tol / fac) {
5589 if (coeffs.is_local(dest))
5590 coeffs.send(dest, &nodeT::accumulate2, result, coeffs,
5591 dest);
5592 else
5593 coeffs.task(dest, &nodeT::accumulate2, result, coeffs,
5594 dest);
5595 nused++;
5596 }
5597 }
5598 }
5599 }
5600
5601 return real_last_distsq;
5602 };
5603
5604 // process "standard" displacements, screening assumes monotonic decay of the kernel
5605 // list of displacements sorted in order of increasing distance
5606 // N.B. if op is lattice-summed use periodic displacements, else use
5607 // non-periodic even if op treats any modes of this as periodic
5608 const auto max_distsq_reached = for_each(op->get_disp(key.level()), default_real_distance_squared, default_lattice_distance_squared, default_skip_predicate);
5609
5610 // for range-restricted kernels displacements to the boundary of the kernel range also need to be included
5611 // N.B. hard range restriction will result in slow decay of operator matrix elements for the displacements
5612 // to the range boundary, should use soft restriction or sacrifice precision
5613 if (op->range_restricted() && key.level() >= 1) {
5614
5615 std::array<std::optional<std::int64_t>, opdim> box_radius;
5616 std::array<std::optional<std::int64_t>, opdim> surface_thickness;
5617 auto &range = op->get_range();
5618 for (int d = 0; d != opdim; ++d) {
5619 if (range[d]) {
5620 box_radius[d] = range[d].N();
5621 surface_thickness[d] = range[d].finite_soft() ? 1 : 0;
5622 }
5623 }
5624
5625 // skip surface displacements that take us outside of the domain and/or were included in regular displacements
5626 // N.B. for lattice-summed axes the "filter" also maps the displacement back into the simulation cell.
5627 // The filter also carries the real-space reach of the standard displacements it discards, outside of which
5628 // the range places its probing displacements. If no standard displacements were processed there is nothing
5629 // to filter and nothing is known about the reach; the probes then screen nothing.
5630 using SurfaceRange = BoxSurfaceDisplacementRange<opdim>;
5631 std::optional<typename SurfaceRange::Validator> validator;
5632 if (max_distsq_reached) {
5633 // N.B. must use the same widths as default_real_distance_squared, i.e. the first opdim axes.
5634 // The validator measures the reach with Key::real_distsq_bc, which is the operator's metric here:
5635 // the only operator with another metric (OperatorInfo::images_only) rejects a finite range
5637 std::array<double, opdim> widths;
5638 for (std::size_t d = 0; d != opdim; ++d) widths[d] = cell_width(d);
5639 validator.emplace(/* is_infinite_domain= */ op->func_domain_is_periodic(), /* is_lattice_summed= */ op->lattice_summed(),
5640 StandardDisplacementsReach<opdim>{*max_distsq_reached, widths});
5641 }
5642
5643 // this range iterates over the entire surface layer(s), and provides a probing displacement per face that can be used to screen out the face
5644 auto opkey = op->particle() == 1 ? key.template extract_front<opdim>() : key.template extract_back<opdim>();
5645 SurfaceRange range_boundary_face_displacements(opkey, box_radius,
5646 surface_thickness,
5647 op->lattice_summed(),
5648 validator);
5649 for_each(
5650 range_boundary_face_displacements,
5651 // surface displacements are not screened, all are included
5652 [](const auto &displacement) -> double { return 0; },
5653 [](const auto &displacement) -> std::uint64_t { return 0; },
5654 default_skip_predicate);
5655 }
5656 }
5657
5658
5659 /// apply an operator on f to return this
5660 template <typename opT, typename R>
5661 void apply(opT& op, const FunctionImpl<R,NDIM>& f, bool fence) {
5663 MADNESS_ASSERT(!op.modified());
5664 for (const auto& [key, node]: f.coeffs) {
5665 if (node.has_coeff()) {
5666 if (node.coeff().dim(0) != k /* i.e. not a leaf */ || op.doleaves) {
5667 ProcessID p = FunctionDefaults<NDIM>::get_apply_randomize() ? world.random_proc() : coeffs.owner(key);
5668// woT::task(p, &implT:: template do_apply<opT,R>, &op, key, node.coeff()); //.full_tensor_copy() ????? why copy ????
5669 woT::task(p, &implT:: template do_apply<opT,R>, &op, key, node.coeff().reconstruct_tensor());
5670 }
5671 }
5672 }
5673 if (fence)
5674 world.gop.fence();
5675
5677// this->compressed=true;
5678// this->nonstandard=true;
5679// this->redundant=false;
5680
5681 }
5682
5683
5684
5685 /// apply an operator on the coeffs c (at node key)
5686
5687 /// invoked by result; the result is accumulated inplace to this's tree at various FunctionNodes
5688 /// @param[in] op the operator to act on the source function
5689 /// @param[in] key key of the source FunctionNode of f which is processed (see "source")
5690 /// @param[in] coeff coeffs of FunctionNode being processed
5691 /// @param[in] do_kernel true: do the 0-disp only; false: do everything but the kernel
5692 /// @return max norm, and will modify or include new nodes in this' tree
5693 template <typename opT, typename R>
5694 double do_apply_directed_screening(const opT* op, const keyT& key, const coeffT& coeff,
5695 const bool& do_kernel) {
5697 // this path screens by decay away from the source: the home displacement is "the kernel", displacements
5698 // farther out than a negligible one are skipped, and the sweep stops beyond shell 12 of Key::distsq_bc.
5699 // The rest-of-crystal kernel vanishes near the source, so it is applied through do_apply only
5700 MADNESS_CHECK_THROW(!op->images_only(),
5701 "apply: the rest-of-crystal operator (OperatorInfo::images_only) is supported only where FunctionImpl::do_apply is used (operator and function of the same dimension, NDIM <= 3)");
5702 // insert timer here
5703 typedef typename opT::keyT opkeyT;
5704
5705 // screening: contains all displacement keys that had small result norms
5706 std::list<opkeyT> blacklist;
5707
5708 constexpr auto opdim=opT::opdim;
5709 Key<NDIM-opdim> nullkey(key.level());
5710
5711 // source is that part of key that corresponds to those dimensions being processed
5712 const opkeyT source=op->get_source_key(key);
5713
5714 const double tol = truncate_tol(thresh, key);
5715
5716 // fac is the root of the number of contributing neighbors (1st shell)
5717 double fac=std::pow(3,NDIM*0.5);
5718 double cnorm = coeff.normf();
5719
5720 // for accumulation: keep slightly tighter TensorArgs
5721 TensorArgs apply_targs(targs);
5722 apply_targs.thresh=tol/fac*0.03;
5723
5724 double maxnorm=0.0;
5725
5726 // for the kernel it may be more efficient to do the convolution in full rank
5727 tensorT coeff_full;
5728 // for partial application (exchange operator) it's more efficient to
5729 // do SVD tensors instead of tensortrains, because addition in apply
5730 // can be done in full form for the specific particle
5731 coeffT coeff_SVD=coeff.convert(TensorArgs(-1.0,TT_2D));
5732#ifdef ENABLE_GENTENSOR
5733 coeff_SVD.get_svdtensor().orthonormalize(tol*GenTensor<T>::fac_reduce());
5734#endif
5735
5736 // list of displacements sorted in order of increasing distance
5737 // N.B. if op is lattice-summed gives periodic displacements, else uses
5738 // non-periodic even if op treats any modes of this as periodic
5739 const std::vector<opkeyT>& disp = Displacements<opdim>().get_disp(key.level(), op->lattice_summed());
5740
5741 for (const auto& d: disp) {
5742 const int shell=d.distsq_bc(op->lattice_summed());
5743 if (do_kernel and (shell>0)) break;
5744 if ((not do_kernel) and (shell==0)) continue;
5745
5746 keyT disp1;
5747 if (op->particle()==1) disp1=d.merge_with(nullkey);
5748 else if (op->particle()==2) disp1=nullkey.merge_with(d);
5749 else {
5750 MADNESS_EXCEPTION("confused particle in operator??",1);
5751 }
5752
5753 keyT dest = neighbor_in_volume(key, disp1);
5754
5755 if (not dest.is_valid()) continue;
5756
5757 // directed screening
5758 // working assumption here is that the operator is isotropic and
5759 // monotonically decreasing with distance
5760 bool screened=false;
5761 typename std::list<opkeyT>::const_iterator it2;
5762 for (it2=blacklist.begin(); it2!=blacklist.end(); it2++) {
5763 if (d.is_farther_out_than(*it2)) {
5764 screened=true;
5765 break;
5766 }
5767 }
5768 if (not screened) {
5769
5770 double opnorm = op->norm(key.level(), d, source);
5771 double norm=0.0;
5772
5773 if (cnorm*opnorm> tol/fac) {
5774
5775 double cost_ratio=op->estimate_costs(source, d, coeff_SVD, tol/fac/cnorm, tol/fac);
5776 // cost_ratio=1.5; // force low rank
5777 // cost_ratio=0.5; // force full rank
5778
5779 if (cost_ratio>0.0) {
5780
5781 do_op_args<opdim> args(source, d, dest, tol, fac, cnorm);
5782 norm=0.0;
5783 if (cost_ratio<1.0) {
5784 if (not coeff_full.has_data()) coeff_full=coeff.full_tensor_copy();
5785 norm=do_apply_kernel2(op, coeff_full,args,apply_targs);
5786 } else {
5787 if (2*opdim==NDIM) { // apply operator on one particle only
5788 norm=do_apply_kernel3(op,coeff_SVD,args,apply_targs);
5789 } else {
5790 norm=do_apply_kernel3(op,coeff,args,apply_targs);
5791 }
5792 }
5793 maxnorm=std::max(norm,maxnorm);
5794 }
5795
5796 } else if (shell >= 12) {
5797 break; // Assumes monotonic decay beyond nearest neighbor
5798 }
5799 if (norm<0.3*tol/fac) blacklist.push_back(d);
5800 }
5801 }
5802 return maxnorm;
5803 }
5804
5805
5806 /// similar to apply, but for low rank coeffs
5807 template <typename opT, typename R>
5808 void apply_source_driven(opT& op, const FunctionImpl<R,NDIM>& f, bool fence) {
5810
5811 MADNESS_ASSERT(not op.modified());
5812 // looping through all the coefficients of the source f
5813 typename dcT::const_iterator end = f.get_coeffs().end();
5814 for (typename dcT::const_iterator it=f.get_coeffs().begin(); it!=end; ++it) {
5815
5816 const keyT& key = it->first;
5817 const coeffT& coeff = it->second.coeff();
5818
5819 if (coeff.has_data() and (coeff.rank()!=0)) {
5820 ProcessID p = FunctionDefaults<NDIM>::get_apply_randomize() ? world.random_proc() : coeffs.owner(key);
5821 woT::task(p, &implT:: template do_apply_directed_screening<opT,R>, &op, key, coeff, true);
5822 woT::task(p, &implT:: template do_apply_directed_screening<opT,R>, &op, key, coeff, false);
5823 }
5824 }
5825 if (fence) world.gop.fence();
5827 }
5828
5829 /// after apply we need to do some cleanup;
5830
5831 /// forces fence
5833
5834 /// after summing up we need to do some cleanup;
5835
5836 /// forces fence
5838
5839 /// traverse a non-existing tree, make its coeffs and apply an operator
5840
5841 /// invoked by result
5842 /// here we use the fact that the hi-dim NS coefficients on all scales are exactly
5843 /// the outer product of the underlying low-dim functions (also in NS form),
5844 /// so we don't need to construct the full hi-dim tree and then turn it into NS form.
5845 /// @param[in] apply_op the operator acting on the NS tree
5846 /// @param[in] fimpl the funcimpl of the function of particle 1
5847 /// @param[in] gimpl the funcimpl of the function of particle 2
5848 template<typename opT, std::size_t LDIM>
5849 void recursive_apply(opT& apply_op, const FunctionImpl<T,LDIM>* fimpl,
5850 const FunctionImpl<T,LDIM>* gimpl, const bool fence) {
5851
5852 //print("IN RECUR2");
5853 const keyT& key0=cdata.key0;
5854
5855 if (world.rank() == coeffs.owner(key0)) {
5856
5857 CoeffTracker<T,LDIM> ff(fimpl);
5858 CoeffTracker<T,LDIM> gg(gimpl);
5859
5860 typedef recursive_apply_op<opT,LDIM> coeff_opT;
5861 coeff_opT coeff_op(this,ff,gg,&apply_op);
5862
5863 typedef noop<T,NDIM> apply_opT;
5864 apply_opT apply_op;
5865
5866 ProcessID p= coeffs.owner(key0);
5867 woT::task(p, &implT:: template forward_traverse<coeff_opT,apply_opT>, coeff_op, apply_op, key0);
5868
5869 }
5870 if (fence) world.gop.fence();
5872 }
5873
5874 /// recursive part of recursive_apply
5875 template<typename opT, std::size_t LDIM>
5877 bool randomize() const {return true;}
5878
5880
5885
5886 // ctor
5890 const opT* apply_op) : result(result), iaf(iaf), iag(iag), apply_op(apply_op)
5891 {
5892 MADNESS_ASSERT(LDIM+LDIM==NDIM);
5893 }
5895 iag(other.iag), apply_op(other.apply_op) {}
5896
5897
5898 /// make the NS-coefficients and send off the application of the operator
5899
5900 /// @return a Future<bool,coeffT>(is_leaf,coeffT())
5901 std::pair<bool,coeffT> operator()(const Key<NDIM>& key) const {
5902
5903 // World& world=result->world;
5904 // break key into particles (these are the child keys, with datum1/2 come the parent keys)
5905 Key<LDIM> key1,key2;
5906 key.break_apart(key1,key2);
5907
5908 // the lo-dim functions should be in full tensor form
5909 const tensorT fcoeff=iaf.coeff(key1).full_tensor();
5910 const tensorT gcoeff=iag.coeff(key2).full_tensor();
5911
5912 // would this be a leaf node? If so, then its sum coeffs have already been
5913 // processed by the parent node's wavelet coeffs. Therefore we won't
5914 // process it any more.
5916 bool is_leaf=leaf_op(key,fcoeff,gcoeff);
5917
5918 if (not is_leaf) {
5919 // new coeffs are simply the hartree/kronecker/outer product --
5920 const std::vector<Slice>& s0=iaf.get_impl()->cdata.s0;
5921 const coeffT coeff = (apply_op->modified())
5922 ? outer(copy(fcoeff(s0)),copy(gcoeff(s0)),result->targs)
5923 : outer(fcoeff,gcoeff,result->targs);
5924
5925 // now send off the application
5926 tensorT coeff_full;
5927 ProcessID p=result->world.rank();
5928 double norm0=result->do_apply_directed_screening<opT,T>(apply_op, key, coeff, true);
5929
5930 result->task(p,&implT:: template do_apply_directed_screening<opT,T>,
5931 apply_op,key,coeff,false);
5932
5933 return finalize(norm0,key,coeff);
5934
5935 } else {
5936 return std::pair<bool,coeffT> (is_leaf,coeffT());
5937 }
5938 }
5939
5940 /// sole purpose is to wait for the kernel norm, wrap it and send it back to caller
5941 std::pair<bool,coeffT> finalize(const double kernel_norm, const keyT& key,
5942 const coeffT& coeff) const {
5943 const double thresh=result->get_thresh()*0.1;
5944 bool is_leaf=(kernel_norm<result->truncate_tol(thresh,key));
5945 if (key.level()<2) is_leaf=false;
5946 return std::pair<bool,coeffT> (is_leaf,coeff);
5947 }
5948
5949
5950 this_type make_child(const keyT& child) const {
5951
5952 // break key into particles
5953 Key<LDIM> key1, key2;
5954 child.break_apart(key1,key2);
5955
5956 return this_type(result,iaf.make_child(key1),iag.make_child(key2),apply_op);
5957 }
5958
5960 Future<CoeffTracker<T,LDIM> > f1=iaf.activate();
5961 Future<CoeffTracker<T,LDIM> > g1=iag.activate();
5962 return result->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
5964 }
5965
5967 const opT* apply_op1) {
5968 return this_type(r,f1,g1,apply_op1);
5969 }
5970
5971 template <typename Archive> void serialize(const Archive& ar) {
5972 ar & result & iaf & iag & apply_op;
5973 }
5974 };
5975
5976 /// traverse a non-existing tree for a sum of hartree products and apply an operator
5977
5978 /// invoked by result
5979 /// Fuses the loop over matched pairs into a single tree traversal:
5980 /// result = op( Σ_i f_i(1) * g_i(2) )
5981 /// The hi-dim NS coefficients are the sum of outer products of the
5982 /// underlying low-dim functions (in NS form), formed on the fly.
5983 /// @param[in] apply_op the operator acting on the NS tree
5984 /// @param[in] fimpls vector of funcimpls of particle 1
5985 /// @param[in] gimpls vector of funcimpls of particle 2
5986 template<typename opT, std::size_t LDIM>
5987 void recursive_apply(opT& apply_op,
5988 const std::vector<const FunctionImpl<T,LDIM>*>& fimpls,
5989 const std::vector<const FunctionImpl<T,LDIM>*>& gimpls,
5990 const bool fence) {
5991
5992 MADNESS_ASSERT(fimpls.size()==gimpls.size());
5993 MADNESS_ASSERT(fimpls.size()>0);
5994 const keyT& key0=cdata.key0;
5995
5996 if (world.rank() == coeffs.owner(key0)) {
5997
5998 std::vector<CoeffTracker<T,LDIM>> ffs, ggs;
5999 for (size_t i=0; i<fimpls.size(); ++i) {
6000 ffs.push_back(CoeffTracker<T,LDIM>(fimpls[i]));
6001 ggs.push_back(CoeffTracker<T,LDIM>(gimpls[i]));
6002 }
6003
6004 typedef recursive_apply_op_vec<opT,LDIM> coeff_opT;
6005 coeff_opT coeff_op(this,ffs,ggs,&apply_op);
6006
6007 typedef noop<T,NDIM> apply_opT;
6008 apply_opT noop_op;
6009
6010 ProcessID p= coeffs.owner(key0);
6011 woT::task(p, &implT:: template forward_traverse<coeff_opT,apply_opT>, coeff_op, noop_op, key0);
6012 }
6013 if (fence) world.gop.fence();
6015 }
6016
6017 /// recursive part of recursive_apply for a vector of matched pairs
6018
6019 /// Handles Σ_i f_i(1) * g_i(2) in a single tree traversal,
6020 /// accumulating outer products with immediate truncation.
6021 template<typename opT, std::size_t LDIM>
6023 bool randomize() const {return true;}
6024
6027
6029 std::vector<ctL> iafs;
6030 std::vector<ctL> iags;
6032
6033 // ctor
6036 const std::vector<ctL>& iafs, const std::vector<ctL>& iags,
6037 const opT* apply_op)
6039 {
6040 MADNESS_ASSERT(LDIM+LDIM==NDIM);
6041 MADNESS_ASSERT(iafs.size()==iags.size());
6042 }
6044 : result(other.result), iafs(other.iafs), iags(other.iags), apply_op(other.apply_op) {}
6045
6046
6047 /// make the summed NS-coefficients and send off the application of the operator
6048
6049 /// Forms Σ_i outer(f_i, g_i) with immediate truncation after each addition
6050 /// @return a pair<bool,coeffT>(is_leaf,coeffT())
6051 std::pair<bool,coeffT> operator()(const Key<NDIM>& key) const {
6052
6053 // break key into particles
6054 Key<LDIM> key1,key2;
6055 key.break_apart(key1,key2);
6056
6057 const size_t npairs=iafs.size();
6058
6059 // collect all lo-dim coefficients in full tensor form
6060 std::vector<tensorT> fcoeffs(npairs), gcoeffs(npairs);
6061 for (size_t i=0; i<npairs; ++i) {
6062 fcoeffs[i]=iafs[i].coeff(key1).full_tensor();
6063 gcoeffs[i]=iags[i].coeff(key2).full_tensor();
6064 }
6065
6066 // determine if this is a leaf node using combined norm bound
6068 bool is_leaf=leaf_op(key,fcoeffs,gcoeffs);
6069
6070 if (not is_leaf) {
6071 const std::vector<Slice>& s0=iafs[0].get_impl()->cdata.s0;
6072 const double reduce_thresh=result->truncate_tol(result->get_thresh(), key);
6073
6074 // accumulate: coeff = Σ_i outer(f_i, g_i) with immediate truncation
6075 std::list<coeffT> one_term_coeffs;
6076 for (size_t i=0; i<npairs; ++i)
6077 one_term_coeffs.push_back(outer(fcoeffs[i],gcoeffs[i],result->targs));
6078 coeffT coeff=reduce(one_term_coeffs,FunctionDefaults<NDIM>::get_thresh()*0.1,false);
6079
6080 // send off the operator application once on the summed coefficient
6081 ProcessID p=result->world.rank();
6082 double norm0=result->do_apply_directed_screening<opT,T>(apply_op, key, coeff, true);
6083
6084 result->task(p,&implT:: template do_apply_directed_screening<opT,T>,
6085 apply_op,key,coeff,false);
6086
6087 return finalize(norm0,key,coeff);
6088
6089 } else {
6090 return std::pair<bool,coeffT> (is_leaf,coeffT());
6091 }
6092 }
6093
6094 /// sole purpose is to wait for the kernel norm, wrap it and send it back to caller
6095 std::pair<bool,coeffT> finalize(const double kernel_norm, const keyT& key,
6096 const coeffT& coeff) const {
6097 const double thresh=result->get_thresh()*0.1;
6098 bool is_leaf=(kernel_norm<result->truncate_tol(thresh,key));
6099 if (key.level()<2) is_leaf=false;
6100 return std::pair<bool,coeffT> (is_leaf,coeff);
6101 }
6102
6103
6104 this_type make_child(const keyT& child) const {
6105
6106 // break key into particles
6107 Key<LDIM> key1, key2;
6108 child.break_apart(key1,key2);
6109
6110 std::vector<ctL> child_fs(iafs.size()), child_gs(iags.size());
6111 for (size_t i=0; i<iafs.size(); ++i) {
6112 child_fs[i]=iafs[i].make_child(key1);
6113 child_gs[i]=iags[i].make_child(key2);
6114 }
6115 return this_type(result,child_fs,child_gs,apply_op);
6116 }
6117
6118 /// activate all CoeffTrackers using fan-in: all 2N futures are registered
6119 /// as dependencies and the forward_ctor task fires only when all are resolved
6121 const size_t n=iafs.size();
6122 std::vector<Future<ctL>> f_futures(n), g_futures(n);
6123 for (size_t i=0; i<n; ++i) {
6124 f_futures[i]=iafs[i].activate();
6125 g_futures[i]=iags[i].activate();
6126 }
6127 return result->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *>(this),
6128 &this_type::forward_ctor),result,f_futures,g_futures,apply_op);
6129 }
6130
6132 const std::vector<Future<ctL>>& f_futures,
6133 const std::vector<Future<ctL>>& g_futures,
6134 const opT* apply_op1) {
6135 std::vector<ctL> new_fs(f_futures.size()), new_gs(g_futures.size());
6136 for (size_t i=0; i<f_futures.size(); ++i) {
6137 new_fs[i]=const_cast<std::vector<Future<ctL>>&>(f_futures)[i].get();
6138 }
6139 for (size_t i=0; i<g_futures.size(); ++i) {
6140 new_gs[i]=const_cast<std::vector<Future<ctL>>&>(g_futures)[i].get();
6141 }
6142 return this_type(r,new_fs,new_gs,apply_op1);
6143 }
6144
6145 template <typename Archive> void serialize(const Archive& ar) {
6146 ar & result & iafs & iags & apply_op;
6147 }
6148 };
6149
6150 /// traverse an existing tree and apply an operator
6151
6152 /// invoked by result
6153 /// @param[in] apply_op the operator acting on the NS tree
6154 /// @param[in] fimpl the funcimpl of the source function
6155 /// @param[in] rimpl a dummy function for recursive_op to insert data
6156 template<typename opT>
6157 void recursive_apply(opT& apply_op, const implT* fimpl, implT* rimpl, const bool fence) {
6158
6159 print("IN RECUR1");
6160
6161 const keyT& key0=cdata.key0;
6162
6163 if (world.rank() == coeffs.owner(key0)) {
6164
6165 typedef recursive_apply_op2<opT> coeff_opT;
6166 coeff_opT coeff_op(this,fimpl,&apply_op);
6167
6168 typedef noop<T,NDIM> apply_opT;
6169 apply_opT apply_op;
6170
6171 woT::task(world.rank(), &implT:: template forward_traverse<coeff_opT,apply_opT>,
6172 coeff_op, apply_op, cdata.key0);
6173
6174 }
6175 if (fence) world.gop.fence();
6177 }
6178
6179 /// recursive part of recursive_apply
6180 template<typename opT>
6182 bool randomize() const {return true;}
6183
6186 typedef std::pair<bool,coeffT> argT;
6187
6188 mutable implT* result;
6189 ctT iaf; /// need this for randomization
6190 const opT* apply_op;
6191
6192 // ctor
6196
6198 iaf(other.iaf), apply_op(other.apply_op) {}
6199
6200
6201 /// send off the application of the operator
6202
6203 /// the first (core) neighbor (ie. the box itself) is processed
6204 /// immediately, all other ones are shoved into the taskq
6205 /// @return a pair<bool,coeffT>(is_leaf,coeffT())
6206 argT operator()(const Key<NDIM>& key) const {
6207
6208 const coeffT& coeff=iaf.coeff();
6209
6210 if (coeff.has_data()) {
6211
6212 // now send off the application for all neighbor boxes
6213 ProcessID p=result->world.rank();
6214 result->task(p,&implT:: template do_apply_directed_screening<opT,T>,
6215 apply_op, key, coeff, false);
6216
6217 // process the core box
6218 double norm0=result->do_apply_directed_screening<opT,T>(apply_op,key,coeff,true);
6219
6220 if (iaf.is_leaf()) return argT(true,coeff);
6221 return finalize(norm0,key,coeff,result);
6222
6223 } else {
6224 const bool is_leaf=true;
6225 return argT(is_leaf,coeffT());
6226 }
6227 }
6228
6229 /// sole purpose is to wait for the kernel norm, wrap it and send it back to caller
6230 argT finalize(const double kernel_norm, const keyT& key,
6231 const coeffT& coeff, const implT* r) const {
6232 const double thresh=r->get_thresh()*0.1;
6233 bool is_leaf=(kernel_norm<r->truncate_tol(thresh,key));
6234 if (key.level()<2) is_leaf=false;
6235 return argT(is_leaf,coeff);
6236 }
6237
6238
6239 this_type make_child(const keyT& child) const {
6240 return this_type(result,iaf.make_child(child),apply_op);
6241 }
6242
6243 /// retrieve the coefficients (parent coeffs might be remote)
6245 Future<ctT> f1=iaf.activate();
6246
6247// Future<ctL> g1=g.activate();
6248// return h->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
6249// &this_type::forward_ctor),h,f1,g1,particle);
6250
6251 return result->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
6253 }
6254
6255 /// taskq-compatible ctor
6256 this_type forward_ctor(implT* result1, const ctT& iaf1, const opT* apply_op1) {
6257 return this_type(result1,iaf1,apply_op1);
6258 }
6259
6260 template <typename Archive> void serialize(const Archive& ar) {
6261 ar & result & iaf & apply_op;
6262 }
6263 };
6264
6265 /// Returns the square of the error norm in the box labeled by key
6266
6267 /// Assumed to be invoked locally but it would be easy to eliminate
6268 /// this assumption
6269 template <typename opT>
6270 double err_box(const keyT& key, const nodeT& node, const opT& func,
6271 int npt, const Tensor<double>& qx, const Tensor<double>& quad_phit,
6272 const Tensor<double>& quad_phiw) const {
6273
6274 std::vector<long> vq(NDIM);
6275 for (std::size_t i=0; i<NDIM; ++i)
6276 vq[i] = npt;
6277 tensorT fval(vq,false), work(vq,false), result(vq,false);
6278
6279 // Compute the "exact" function in this volume at npt points
6280 // where npt is usually this->npt+1.
6281 fcube(key, func, qx, fval);
6282
6283 // Transform into the scaling function basis of order npt
6284 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
6285 fval = fast_transform(fval,quad_phiw,result,work).scale(scale);
6286
6287 // Subtract to get the error ... the original coeffs are in the order k
6288 // basis but we just computed the coeffs in the order npt(=k+1) basis
6289 // so we can either use slices or an iterator macro.
6290 const tensorT coeff = node.coeff().full_tensor();
6291 ITERATOR(coeff,fval(IND)-=coeff(IND););
6292 // flo note: we do want to keep a full tensor here!
6293
6294 // Compute the norm of what remains
6295 double err = fval.normf();
6296 return err*err;
6297 }
6298
6299 template <typename opT>
6301 const implT* impl;
6302 const opT* func;
6303 int npt;
6307 public:
6308 do_err_box() = default;
6309
6313
6316
6317 double operator()(typename dcT::const_iterator& it) const {
6318 const keyT& key = it->first;
6319 const nodeT& node = it->second;
6320 if (node.has_coeff())
6321 return impl->err_box(key, node, *func, npt, qx, quad_phit, quad_phiw);
6322 else
6323 return 0.0;
6324 }
6325
6326 double operator()(double a, double b) const {
6327 return a+b;
6328 }
6329
6330 template <typename Archive>
6331 void serialize(const Archive& ar) {
6332 MADNESS_EXCEPTION("not yet", 1);
6333 }
6334 };
6335
6336 /// Returns the sum of squares of errors from local info ... no comms
6337 template <typename opT>
6338 double errsq_local(const opT& func) const {
6340 // Make quadrature rule of higher order
6341 const int npt = cdata.npt + 1;
6342 Tensor<double> qx, qw, quad_phi, quad_phiw, quad_phit;
6343 FunctionCommonData<T,NDIM>::_init_quadrature(k+1, npt, qx, qw, quad_phi, quad_phiw, quad_phit);
6344
6346 rangeT range(coeffs.begin(), coeffs.end());
6347 return world.taskq.reduce< double,rangeT,do_err_box<opT> >(range,
6348 do_err_box<opT>(this, &func, npt, qx, quad_phit, quad_phiw));
6349 }
6350
6351 /// Returns \c int(f(x),x) in local volume
6352 T trace_local() const;
6353
6355 /// skip the internal nodes, cf. has_coefficients_on_leaves_only()
6356 bool leaves_only = false;
6357
6358 do_norm2sq_local() = default;
6360
6361 double operator()(typename dcT::const_iterator& it) const {
6362 const nodeT& node = it->second;
6363 if (leaves_only and node.has_children()) return 0.0;
6364 if (node.has_coeff()) {
6365 double norm = node.coeff().normf();
6366 return norm*norm;
6367 }
6368 else {
6369 return 0.0;
6370 }
6371 }
6372
6373 double operator()(double a, double b) const {
6374 return (a+b);
6375 }
6376
6377 template <typename Archive> void serialize(const Archive& ar) {
6378 MADNESS_EXCEPTION("NOT IMPLEMENTED", 1);
6379 }
6380 };
6381
6382
6383 /// Returns the square of the local norm ... no comms
6384
6385 /// Requires has_summable_coefficients(); throws otherwise, because on a
6386 /// tree that carries its coefficients on more than one level the sum is
6387 /// not the norm. Internal nodes are skipped where they are the
6388 /// duplicates, so no state change is needed to ask for the norm.
6389 double norm2sq_local() const;
6390
6391 /// compute the inner product of this range with other
6392 template<typename R>
6396 typedef TENSOR_RESULT_TYPE(T,R) resultT;
6397
6400 resultT operator()(typename dcT::const_iterator& it) const {
6401
6402 TENSOR_RESULT_TYPE(T,R) sum=0.0;
6403 const keyT& key=it->first;
6404 const nodeT& fnode = it->second;
6405 if (fnode.has_coeff()) {
6406 if (other->coeffs.probe(it->first)) {
6407 const FunctionNode<R,NDIM>& gnode = other->coeffs.find(key).get()->second;
6408 if (gnode.has_coeff()) {
6409 if (gnode.coeff().dim(0) != fnode.coeff().dim(0)) {
6410 madness::print("INNER", it->first, gnode.coeff().dim(0),fnode.coeff().dim(0));
6411 MADNESS_EXCEPTION("functions have different k or compress/reconstruct error", 0);
6412 }
6413 if (leaves_only) {
6414 if (gnode.is_leaf() or fnode.is_leaf()) {
6415 sum += fnode.coeff().trace_conj(gnode.coeff());
6416 }
6417 } else {
6418 sum += fnode.coeff().trace_conj(gnode.coeff());
6419 }
6420 }
6421 }
6422 }
6423 return sum;
6424 }
6425
6426 resultT operator()(resultT a, resultT b) const {
6427 return (a+b);
6428 }
6429
6430 template <typename Archive> void serialize(const Archive& ar) {
6431 MADNESS_EXCEPTION("NOT IMPLEMENTED", 1);
6432 }
6433 };
6434
6435 /// Returns the inner product ASSUMING same distribution
6436
6437 /// handles compressed and redundant form
6438 template <typename R>
6442 typedef TENSOR_RESULT_TYPE(T,R) resultT;
6443
6444 // make sure the states of the trees are consistent
6447 return world.taskq.reduce<resultT,rangeT,do_inner_local<R> >
6448 (rangeT(coeffs.begin(),coeffs.end()),do_inner_local<R>(&g, leaves_only));
6449 }
6450
6451
6452 /// compute the inner product of this range with other
6453 template<typename R>
6457 bool leaves_only=true;
6458 typedef TENSOR_RESULT_TYPE(T,R) resultT;
6459
6463 resultT operator()(typename dcT::const_iterator& it) const {
6464
6465 constexpr std::size_t LDIM=std::max(NDIM/2,std::size_t(1));
6466
6467 const keyT& key=it->first;
6468 const nodeT& fnode = it->second;
6469 if (not fnode.has_coeff()) return resultT(0.0); // probably internal nodes
6470
6471 // assuming all boxes (esp the low-dim ones) are local, i.e. the functions are replicated
6472 auto find_valid_parent = [](auto& key, auto& impl, auto&& find_valid_parent) {
6473 MADNESS_CHECK(impl->get_coeffs().owner(key)==impl->world.rank()); // make sure everything is local!
6474 if (impl->get_coeffs().probe(key)) return key;
6475 auto parentkey=key.parent();
6476 return find_valid_parent(parentkey, impl, find_valid_parent);
6477 };
6478
6479 // returns coefficients, empty if no functor present
6480 auto get_coeff = [&find_valid_parent](const auto& key, const auto& v_impl) {
6481 if ((v_impl.size()>0) and v_impl.front().get()) {
6482 auto impl=v_impl.front();
6483
6484// bool have_impl=impl.get();
6485// if (have_impl) {
6486 auto parentkey = find_valid_parent(key, impl, find_valid_parent);
6487 MADNESS_CHECK(impl->get_coeffs().probe(parentkey));
6488 typename decltype(impl->coeffs)::accessor acc;
6489 impl->get_coeffs().find(acc,parentkey);
6490 auto parentcoeff=acc->second.coeff();
6491 auto coeff=impl->parent_to_child(parentcoeff, parentkey, key);
6492 return coeff;
6493 } else {
6494 // get type of vector elements
6495 typedef typename std::decay_t<decltype(v_impl)>::value_type::element_type::typeT S;
6496// typedef typename std::decay_t<decltype(v_impl)>::value_type S;
6497 return GenTensor<S>();
6498// return GenTensor<typename std::decay_t<decltype(*impl)>::typeT>();
6499 }
6500 };
6501
6502 auto make_vector = [](auto& arg) {
6503 return std::vector<std::decay_t<decltype(arg)>>(1,arg);
6504 };
6505
6506
6507 Key<LDIM> key1,key2;
6508 key.break_apart(key1,key2);
6509
6510 auto func=dynamic_cast<CompositeFunctorInterface<R,NDIM,LDIM>* >(ket->functor.get());
6512
6513 MADNESS_CHECK_THROW(func->impl_ket_vector.size()==0 or func->impl_ket_vector.size()==1,
6514 "only one ket function supported in inner_on_demand");
6515 MADNESS_CHECK_THROW(func->impl_p1_vector.size()==0 or func->impl_p1_vector.size()==1,
6516 "only one p1 function supported in inner_on_demand");
6517 MADNESS_CHECK_THROW(func->impl_p2_vector.size()==0 or func->impl_p2_vector.size()==1,
6518 "only one p2 function supported in inner_on_demand");
6519 auto coeff_bra=fnode.coeff();
6520 auto coeff_ket=get_coeff(key,func->impl_ket_vector);
6521 auto coeff_v1=get_coeff(key1,make_vector(func->impl_m1));
6522 auto coeff_v2=get_coeff(key2,make_vector(func->impl_m2));
6523 auto coeff_p1=get_coeff(key1,func->impl_p1_vector);
6524 auto coeff_p2=get_coeff(key2,func->impl_p2_vector);
6525
6526 // construct |ket(1,2)> or |p(1)p(2)> or |p(1)p(2) ket(1,2)>
6527 double error=0.0;
6528 if (coeff_ket.has_data() and coeff_p1.has_data()) {
6529 pointwise_multiplier<LDIM> pm(key,coeff_ket);
6530 coeff_ket=pm(key,outer(coeff_p1,coeff_p2,TensorArgs(TT_FULL,-1.0)).full_tensor());
6531 error+=pm.error;
6532 } else if (coeff_ket.has_data() or coeff_p1.has_data()) {
6533 coeff_ket = (coeff_ket.has_data()) ? coeff_ket : outer(coeff_p1,coeff_p2);
6534 } else { // not ket and no p1p2
6535 MADNESS_EXCEPTION("confused ket/p1p2 in do_inner_local_on_demand",1);
6536 }
6537
6538 // construct (v(1) + v(2)) |ket(1,2)>
6539 coeffT v1v2ket;
6540 if (coeff_v1.has_data()) {
6541 pointwise_multiplier<LDIM> pm(key,coeff_ket);
6542 v1v2ket = pm(key,coeff_v1.full_tensor(), 1);
6543 error+=pm.error;
6544 v1v2ket+= pm(key,coeff_v2.full_tensor(), 2);
6545 error+=pm.error;
6546 } else {
6547 v1v2ket = coeff_ket;
6548 }
6549
6550 resultT result;
6551 if (func->impl_eri) { // project bra*ket onto eri, avoid multiplication with eri
6552 MADNESS_CHECK(func->impl_eri->get_functor()->provides_coeff());
6553 coeffT coeff_eri=func->impl_eri->get_functor()->coeff(key).full_tensor();
6554 pointwise_multiplier<LDIM> pm(key,v1v2ket);
6555 tensorT braket=pm(key,coeff_bra.full_tensor_copy().conj());
6556 error+=pm.error;
6557 if (error>1.e-3) print("error in key",key,error);
6558 result=coeff_eri.full_tensor().trace(braket);
6559
6560 } else { // no eri, project ket onto bra
6561 result=coeff_bra.full_tensor_copy().trace_conj(v1v2ket.full_tensor_copy());
6562 }
6563 return result;
6564 }
6565
6566 resultT operator()(resultT a, resultT b) const {
6567 return (a+b);
6568 }
6569
6570 template <typename Archive> void serialize(const Archive& ar) {
6571 MADNESS_EXCEPTION("NOT IMPLEMENTED", 1);
6572 }
6573 };
6574
6575 /// Returns the inner product of this with function g constructed on-the-fly
6576
6577 /// the leaf boxes of this' MRA tree defines the inner product
6578 template <typename R>
6579 TENSOR_RESULT_TYPE(T,R) inner_local_on_demand(const FunctionImpl<R,NDIM>& gimpl) const {
6582
6584 rangeT range(coeffs.begin(), coeffs.end());
6585 return world.taskq.reduce<T, rangeT, do_inner_local_on_demand<T>>(range,
6586 do_inner_local_on_demand<R>(this, &gimpl));
6587 }
6588
6589 /// compute the inner product of this range with other
6590 template<typename R>
6594 typedef TENSOR_RESULT_TYPE(T,R) resultT;
6595
6598 resultT operator()(typename dcT::const_iterator& it) const {
6599
6600 TENSOR_RESULT_TYPE(T,R) sum=0.0;
6601 const keyT& key=it->first;
6602 const nodeT& fnode = it->second;
6603 if (fnode.has_coeff()) {
6604 if (other->coeffs.probe(it->first)) {
6605 const FunctionNode<R,NDIM>& gnode = other->coeffs.find(key).get()->second;
6606 if (gnode.has_coeff()) {
6607 if (gnode.coeff().dim(0) != fnode.coeff().dim(0)) {
6608 madness::print("DOT", it->first, gnode.coeff().dim(0),fnode.coeff().dim(0));
6609 MADNESS_EXCEPTION("functions have different k or compress/reconstruct error", 0);
6610 }
6611 if (leaves_only) {
6612 if (gnode.is_leaf() or fnode.is_leaf()) {
6613 sum += fnode.coeff().full_tensor().trace(gnode.coeff().full_tensor());
6614 }
6615 } else {
6616 sum += fnode.coeff().full_tensor().trace(gnode.coeff().full_tensor());
6617 }
6618 }
6619 }
6620 }
6621 return sum;
6622 }
6623
6624 resultT operator()(resultT a, resultT b) const {
6625 return (a+b);
6626 }
6627
6628 template <typename Archive> void serialize(const Archive& ar) {
6629 MADNESS_EXCEPTION("NOT IMPLEMENTED", 1);
6630 }
6631 };
6632
6633 /// Returns the dot product ASSUMING same distribution
6634
6635 /// handles compressed and redundant form
6636 template <typename R>
6640 typedef TENSOR_RESULT_TYPE(T,R) resultT;
6641
6642 // make sure the states of the trees are consistent
6644 bool leaves_only=(this->is_redundant());
6645 return world.taskq.reduce<resultT,rangeT,do_dot_local<R> >
6646 (rangeT(coeffs.begin(),coeffs.end()),do_dot_local<R>(&g, leaves_only));
6647 }
6648
6649 /// Type of the entry in the map returned by make_key_vec_map
6650 typedef std::vector< std::pair<int,const coeffT*> > mapvecT;
6651
6652 /// Type of the map returned by make_key_vec_map
6654
6655 /// Adds keys to union of local keys with specified index
6656 void add_keys_to_map(mapT* map, int index) const {
6657 typename dcT::const_iterator end = coeffs.end();
6658 for (typename dcT::const_iterator it=coeffs.begin(); it!=end; ++it) {
6659 typename mapT::accessor acc;
6660 const keyT& key = it->first;
6661 const FunctionNode<T,NDIM>& node = it->second;
6662 if (node.has_coeff()) {
6663 [[maybe_unused]] auto inserted = map->insert(acc,key);
6664 acc->second.push_back(std::make_pair(index,&(node.coeff())));
6665 }
6666 }
6667 }
6668
6669 /// Returns map of union of local keys to vector of indexes of functions containing that key
6670
6671 /// Local concurrency and synchronization only; no communication
6672 static
6673 mapT
6674 make_key_vec_map(const std::vector<const FunctionImpl<T,NDIM>*>& v) {
6675 mapT map(100000);
6676 // This loop must be parallelized
6677 for (unsigned int i=0; i<v.size(); i++) {
6678 //v[i]->add_keys_to_map(&map,i);
6679 v[i]->world.taskq.add(*(v[i]), &FunctionImpl<T,NDIM>::add_keys_to_map, &map, int(i));
6680 }
6681 if (v.size()) v[0]->world.taskq.fence();
6682 return map;
6683 }
6684
6685#if 0
6686// Original
6687 template <typename R>
6688 static void do_inner_localX(const typename mapT::iterator lstart,
6689 const typename mapT::iterator lend,
6690 typename FunctionImpl<R,NDIM>::mapT* rmap_ptr,
6691 const bool sym,
6692 Tensor< TENSOR_RESULT_TYPE(T,R) >* result_ptr,
6693 Mutex* mutex) {
6694 Tensor< TENSOR_RESULT_TYPE(T,R) >& result = *result_ptr;
6695 Tensor< TENSOR_RESULT_TYPE(T,R) > r(result.dim(0),result.dim(1));
6696 for (typename mapT::iterator lit=lstart; lit!=lend; ++lit) {
6697 const keyT& key = lit->first;
6698 typename FunctionImpl<R,NDIM>::mapT::iterator rit=rmap_ptr->find(key);
6699 if (rit != rmap_ptr->end()) {
6700 const mapvecT& leftv = lit->second;
6701 const typename FunctionImpl<R,NDIM>::mapvecT& rightv =rit->second;
6702 const int nleft = leftv.size();
6703 const int nright= rightv.size();
6704
6705 for (int iv=0; iv<nleft; iv++) {
6706 const int i = leftv[iv].first;
6707 const GenTensor<T>* iptr = leftv[iv].second;
6708
6709 for (int jv=0; jv<nright; jv++) {
6710 const int j = rightv[jv].first;
6711 const GenTensor<R>* jptr = rightv[jv].second;
6712
6713 if (!sym || (sym && i<=j))
6714 r(i,j) += iptr->trace_conj(*jptr);
6715 }
6716 }
6717 }
6718 }
6719 mutex->lock();
6720 result += r;
6721 mutex->unlock();
6722 }
6723#else
6724 template <typename R>
6725 static void do_inner_localX(const typename mapT::iterator lstart,
6726 const typename mapT::iterator lend,
6727 typename FunctionImpl<R,NDIM>::mapT* rmap_ptr,
6728 const bool sym,
6729 Tensor< TENSOR_RESULT_TYPE(T,R) >* result_ptr,
6730 Mutex* mutex) {
6731 Tensor< TENSOR_RESULT_TYPE(T,R) >& result = *result_ptr;
6732 //Tensor< TENSOR_RESULT_TYPE(T,R) > r(result.dim(0),result.dim(1));
6733 for (typename mapT::iterator lit=lstart; lit!=lend; ++lit) {
6734 const keyT& key = lit->first;
6735 typename FunctionImpl<R,NDIM>::mapT::iterator rit=rmap_ptr->find(key);
6736 if (rit != rmap_ptr->end()) {
6737 const mapvecT& leftv = lit->second;
6738 const typename FunctionImpl<R,NDIM>::mapvecT& rightv =rit->second;
6739 const size_t nleft = leftv.size();
6740 const size_t nright= rightv.size();
6741
6742 unsigned int size = leftv[0].second->size();
6743 Tensor<T> Left(nleft, size);
6744 Tensor<R> Right(nright, size);
6745 Tensor< TENSOR_RESULT_TYPE(T,R)> r(nleft, nright);
6746 for(unsigned int iv = 0; iv < nleft; ++iv) Left(iv,_) = (*(leftv[iv].second)).full_tensor();
6747 for(unsigned int jv = 0; jv < nright; ++jv) Right(jv,_) = (*(rightv[jv].second)).full_tensor();
6748 // call mxmT from mxm.h in tensor
6749 if(TensorTypeData<T>::iscomplex) Left = Left.conj(); // Should handle complex case and leave real case alone
6750 mxmT(nleft, nright, size, r.ptr(), Left.ptr(), Right.ptr());
6751 mutex->lock();
6752 for(unsigned int iv = 0; iv < nleft; ++iv) {
6753 const int i = leftv[iv].first;
6754 for(unsigned int jv = 0; jv < nright; ++jv) {
6755 const int j = rightv[jv].first;
6756 if (!sym || (sym && i<=j)) result(i,j) += r(iv,jv);
6757 }
6758 }
6759 mutex->unlock();
6760 }
6761 }
6762 }
6763#endif
6764
6765#if 0
6766// Original
6767 template <typename R, typename = std::enable_if_t<std::is_floating_point_v<R>>>
6768 static void do_dot_localX(const typename mapT::iterator lstart,
6769 const typename mapT::iterator lend,
6770 typename FunctionImpl<R, NDIM>::mapT* rmap_ptr,
6771 const bool sym,
6772 Tensor<TENSOR_RESULT_TYPE(T, R)>* result_ptr,
6773 Mutex* mutex) {
6774 if (TensorTypeData<T>::iscomplex) MADNESS_EXCEPTION("no complex trace in LowRankTensor, sorry", 1);
6775 Tensor<TENSOR_RESULT_TYPE(T, R)>& result = *result_ptr;
6776 Tensor<TENSOR_RESULT_TYPE(T, R)> r(result.dim(0), result.dim(1));
6777 for (typename mapT::iterator lit = lstart; lit != lend; ++lit) {
6778 const keyT& key = lit->first;
6779 typename FunctionImpl<R, NDIM>::mapT::iterator rit = rmap_ptr->find(key);
6780 if (rit != rmap_ptr->end()) {
6781 const mapvecT& leftv = lit->second;
6782 const typename FunctionImpl<R, NDIM>::mapvecT& rightv = rit->second;
6783 const int nleft = leftv.size();
6784 const int nright = rightv.size();
6785
6786 for (int iv = 0; iv < nleft; iv++) {
6787 const int i = leftv[iv].first;
6788 const GenTensor<T>* iptr = leftv[iv].second;
6789
6790 for (int jv = 0; jv < nright; jv++) {
6791 const int j = rightv[jv].first;
6792 const GenTensor<R>* jptr = rightv[jv].second;
6793
6794 if (!sym || (sym && i <= j))
6795 r(i, j) += iptr->trace_conj(*jptr);
6796 }
6797 }
6798 }
6799 }
6800 mutex->lock();
6801 result += r;
6802 mutex->unlock();
6803 }
6804#else
6805 template <typename R>
6806 static void do_dot_localX(const typename mapT::iterator lstart,
6807 const typename mapT::iterator lend,
6808 typename FunctionImpl<R, NDIM>::mapT* rmap_ptr,
6809 const bool sym,
6810 Tensor<TENSOR_RESULT_TYPE(T, R)>* result_ptr,
6811 Mutex* mutex) {
6812 Tensor<TENSOR_RESULT_TYPE(T, R)>& result = *result_ptr;
6813 // Tensor<TENSOR_RESULT_TYPE(T, R)> r(result.dim(0), result.dim(1));
6814 for (typename mapT::iterator lit = lstart; lit != lend; ++lit) {
6815 const keyT& key = lit->first;
6816 typename FunctionImpl<R, NDIM>::mapT::iterator rit = rmap_ptr->find(key);
6817 if (rit != rmap_ptr->end()) {
6818 const mapvecT& leftv = lit->second;
6819 const typename FunctionImpl<R, NDIM>::mapvecT& rightv = rit->second;
6820 const size_t nleft = leftv.size();
6821 const size_t nright= rightv.size();
6822
6823 unsigned int size = leftv[0].second->size();
6824 Tensor<T> Left(nleft, size);
6825 Tensor<R> Right(nright, size);
6826 Tensor< TENSOR_RESULT_TYPE(T, R)> r(nleft, nright);
6827 for(unsigned int iv = 0; iv < nleft; ++iv) Left(iv, _) = (*(leftv[iv].second)).full_tensor();
6828 for(unsigned int jv = 0; jv < nright; ++jv) Right(jv, _) = (*(rightv[jv].second)).full_tensor();
6829 // call mxmT from mxm.h in tensor
6830 mxmT(nleft, nright, size, r.ptr(), Left.ptr(), Right.ptr());
6831 mutex->lock();
6832 for(unsigned int iv = 0; iv < nleft; ++iv) {
6833 const int i = leftv[iv].first;
6834 for(unsigned int jv = 0; jv < nright; ++jv) {
6835 const int j = rightv[jv].first;
6836 if (!sym || (sym && i <= j)) result(i, j) += r(iv, jv);
6837 }
6838 }
6839 mutex->unlock();
6840 }
6841 }
6842 }
6843#endif
6844
6845 template <typename Real>
6846 static std::enable_if_t<std::is_floating_point_v<Real>, Real> conj(const Real x) {
6847 return x;
6848 }
6849
6850 template <typename Real>
6851 static std::complex<Real> conj(const std::complex<Real>& x) {
6852 return std::conj(x);
6853 }
6854
6855 template <typename R>
6856 static Tensor< TENSOR_RESULT_TYPE(T,R) >
6857 inner_local(const std::vector<const FunctionImpl<T,NDIM>*>& left,
6858 const std::vector<const FunctionImpl<R,NDIM>*>& right,
6859 bool sym) {
6860
6861 // This is basically a sparse matrix^T * matrix product
6862 // Rij = sum(k) Aki * Bkj
6863 // where i and j index functions and k index the wavelet coeffs
6864 // eventually the goal is this structure (don't have jtile yet)
6865 //
6866 // do in parallel tiles of k (tensors of coeffs)
6867 // do tiles of j
6868 // do i
6869 // do j in jtile
6870 // do k in ktile
6871 // Rij += Aki*Bkj
6872
6873 mapT lmap = make_key_vec_map(left);
6874 typename FunctionImpl<R,NDIM>::mapT rmap;
6875 auto* rmap_ptr = (typename FunctionImpl<R,NDIM>::mapT*)(&lmap);
6876 if ((std::vector<const FunctionImpl<R,NDIM>*>*)(&left) != &right) {
6878 rmap_ptr = &rmap;
6879 }
6880
6881 size_t chunk = (lmap.size()-1)/(3*4*5)+1;
6882
6883 Tensor< TENSOR_RESULT_TYPE(T,R) > r(left.size(), right.size());
6884 Mutex mutex;
6885
6886 typename mapT::iterator lstart=lmap.begin();
6887 while (lstart != lmap.end()) {
6888 typename mapT::iterator lend = lstart;
6889 advance(lend,chunk);
6890 left[0]->world.taskq.add(&FunctionImpl<T,NDIM>::do_inner_localX<R>, lstart, lend, rmap_ptr, sym, &r, &mutex);
6891 lstart = lend;
6892 }
6893 left[0]->world.taskq.fence();
6894
6895 if (sym) {
6896 for (long i=0; i<r.dim(0); i++) {
6897 for (long j=0; j<i; j++) {
6898 TENSOR_RESULT_TYPE(T,R) sum = r(i,j)+conj(r(j,i));
6899 r(i,j) = sum;
6900 r(j,i) = conj(sum);
6901 }
6902 }
6903 }
6904 return r;
6905 }
6906
6907 template <typename R>
6908 static Tensor<TENSOR_RESULT_TYPE(T, R)>
6909 dot_local(const std::vector<const FunctionImpl<T, NDIM>*>& left,
6910 const std::vector<const FunctionImpl<R, NDIM>*>& right,
6911 bool sym) {
6912
6913 // This is basically a sparse matrix * matrix product
6914 // Rij = sum(k) Aik * Bkj
6915 // where i and j index functions and k index the wavelet coeffs
6916 // eventually the goal is this structure (don't have jtile yet)
6917 //
6918 // do in parallel tiles of k (tensors of coeffs)
6919 // do tiles of j
6920 // do i
6921 // do j in jtile
6922 // do k in ktile
6923 // Rij += Aik*Bkj
6924
6925 mapT lmap = make_key_vec_map(left);
6926 typename FunctionImpl<R, NDIM>::mapT rmap;
6927 auto* rmap_ptr = (typename FunctionImpl<R, NDIM>::mapT*)(&lmap);
6928 if ((std::vector<const FunctionImpl<R, NDIM>*>*)(&left) != &right) {
6930 rmap_ptr = &rmap;
6931 }
6932
6933 size_t chunk = (lmap.size() - 1) / (3 * 4 * 5) + 1;
6934
6935 Tensor<TENSOR_RESULT_TYPE(T, R)> r(left.size(), right.size());
6936 Mutex mutex;
6937
6938 typename mapT::iterator lstart=lmap.begin();
6939 while (lstart != lmap.end()) {
6940 typename mapT::iterator lend = lstart;
6941 advance(lend, chunk);
6942 left[0]->world.taskq.add(&FunctionImpl<T, NDIM>::do_dot_localX<R>, lstart, lend, rmap_ptr, sym, &r, &mutex);
6943 lstart = lend;
6944 }
6945 left[0]->world.taskq.fence();
6946
6947 // sym is for hermiticity
6948 if (sym) {
6949 for (long i = 0; i < r.dim(0); i++) {
6950 for (long j = 0; j < i; j++) {
6951 TENSOR_RESULT_TYPE(T, R) sum = r(i, j) + conj(r(j, i));
6952 r(i, j) = sum;
6953 r(j, i) = conj(sum);
6954 }
6955 }
6956 }
6957 return r;
6958 }
6959
6960 template <typename R>
6962 {
6963 static_assert(!std::is_same<R, int>::value &&
6964 std::is_same<R, int>::value,
6965 "Compilation failed because you wanted to know the type; see below:");
6966 }
6967
6968 /// invoked by result
6969
6970 /// contract 2 functions f(x,z) = \int g(x,y) * h(y,z) dy
6971 /// @tparam CDIM: the dimension of the contraction variable (y)
6972 /// @tparam NDIM: the dimension of the result (x,z)
6973 /// @tparam LDIM: the dimension of g(x,y)
6974 /// @tparam KDIM: the dimension of h(y,z)
6975 template<typename Q, std::size_t LDIM, typename R, std::size_t KDIM,
6976 std::size_t CDIM = (KDIM + LDIM - NDIM) / 2>
6978 const std::array<int, CDIM> v1, const std::array<int, CDIM> v2) {
6979
6980 typedef std::multimap<Key<NDIM>, std::list<Key<CDIM>>> contractionmapT;
6981 //double wall_get_lists=0.0;
6982 //double wall_recur=0.0;
6983 //double wall_contract=0.0;
6986
6987 // auto print_map = [](const auto& map) {
6988 // for (const auto& kv : map) print(kv.first,"--",kv.second);
6989 // };
6990 // logical constness, not bitwise constness
6991 FunctionImpl<Q,LDIM>& g_nc=const_cast<FunctionImpl<Q,LDIM>&>(g);
6992 FunctionImpl<R,KDIM>& h_nc=const_cast<FunctionImpl<R,KDIM>&>(h);
6993
6994 std::list<contractionmapT> all_contraction_maps;
6995 for (std::size_t n=0; n<nmax; ++n) {
6996
6997 // list of nodes with d coefficients (and their parents)
6998 //double wall0 = wall_time();
6999 auto [g_ijlist, g_jlist] = g.get_contraction_node_lists(n, v1);
7000 auto [h_ijlist, h_jlist] = h.get_contraction_node_lists(n, v2);
7001 if ((g_ijlist.size() == 0) and (h_ijlist.size() == 0)) break;
7002 //double wall1 = wall_time();
7003 //wall_get_lists += (wall1 - wall0);
7004 //wall0 = wall1;
7005// print("g_jlist");
7006// for (const auto& kv : g_jlist) print(kv.first,kv.second);
7007// print("h_jlist");
7008// for (const auto& kv : h_jlist) print(kv.first,kv.second);
7009
7010 // next lines will insert s nodes into g and h -> possible race condition!
7011 bool this_first = true; // are the remaining indices of g before those of g: f(x,z) = g(x,y) h(y,z)
7012 // CDIM, NDIM, KDIM
7013 contractionmapT contraction_map = g_nc.recur_down_for_contraction_map(
7014 g_nc.key0(), g_nc.get_coeffs().find(g_nc.key0()).get()->second, v1, v2,
7015 h_ijlist, h_jlist, this_first, thresh);
7016
7017 this_first = false;
7018 // CDIM, NDIM, LDIM
7019 auto hnode0=h_nc.get_coeffs().find(h_nc.key0()).get()->second;
7020 contractionmapT contraction_map1 = h_nc.recur_down_for_contraction_map(
7021 h_nc.key0(), hnode0, v2, v1,
7022 g_ijlist, g_jlist, this_first, thresh);
7023
7024 // will contain duplicate entries
7025 contraction_map.merge(contraction_map1);
7026 // turn multimap into a map of list
7027 auto it = contraction_map.begin();
7028 while (it != contraction_map.end()) {
7029 auto it_end = contraction_map.upper_bound(it->first);
7030 auto it2 = it;
7031 it2++;
7032 while (it2 != it_end) {
7033 it->second.splice(it->second.end(), it2->second);
7034 it2 = contraction_map.erase(it2);
7035 }
7036 it = it_end;
7037 }
7038// print("thresh ",thresh);
7039// print("contraction list size",contraction_map.size());
7040
7041 // remove all double entries
7042 for (auto& elem: contraction_map) {
7043 elem.second.sort();
7044 elem.second.unique();
7045 }
7046 //wall1 = wall_time();
7047 //wall_recur += (wall1 - wall0);
7048// if (n==2) {
7049// print("contraction map for n=", n);
7050// print_map(contraction_map);
7051// }
7052 all_contraction_maps.push_back(contraction_map);
7053
7054 long mapsize=contraction_map.size();
7055 if (mapsize==0) break;
7056 }
7057
7058
7059 // finally do the contraction
7060 for (const auto& contraction_map : all_contraction_maps) {
7061 for (const auto& key_list : contraction_map) {
7062 const Key<NDIM>& key=key_list.first;
7063 const std::list<Key<CDIM>>& list=key_list.second;
7064 woT::task(coeffs.owner(key), &implT:: template partial_inner_contract<Q,LDIM,R,KDIM>,
7065 &g,&h,v1,v2,key,list);
7066 }
7067 }
7068 }
7069
7070 /// for contraction two functions f(x,z) = \int g(x,y) h(y,z) dy
7071
7072 /// find all nodes with d coefficients and return a list of complete keys and of
7073 /// keys holding only the y dimension, also the maximum norm of all d for the j dimension
7074 /// @param[in] n the scale
7075 /// @param[in] v array holding the indices of the integration variable
7076 /// @return ijlist: list of all nodes with d coeffs; jlist: j-part of ij list only, with max d norm
7077 template<std::size_t CDIM>
7078 std::tuple<std::set<Key<NDIM>>, std::map<Key<CDIM>,double>>
7079 get_contraction_node_lists(const std::size_t n, const std::array<int, CDIM>& v) const {
7080
7081 const auto& cdata=get_cdata();
7082 auto has_d_coeffs = [&cdata](const coeffT& coeff) {
7083 if (coeff.has_no_data()) return false;
7084 return (coeff.dim(0)==2*cdata.k);
7085 };
7086
7087 // keys to be contracted in g
7088 std::set<Key<NDIM>> ij_list; // full key
7089 std::map<Key<CDIM>,double> j_list; // only that dimension that will be contracted
7090
7091 for (auto it=get_coeffs().begin(); it!=get_coeffs().end(); ++it) {
7092 const Key<NDIM>& key=it->first;
7093 const FunctionNode<T,NDIM>& node=it->second;
7094 if ((key.level()==int(n)) and (has_d_coeffs(node.coeff()))) {
7095 ij_list.insert(key);
7097 for (std::size_t i=0; i<CDIM; ++i) j_trans[i]=key.translation()[v[i]];
7098 Key<CDIM> jkey(n,j_trans);
7099 const double max_d_norm=j_list[jkey];
7100 j_list.insert_or_assign(jkey,std::max(max_d_norm,node.get_dnorm()));
7101
7102 // insert all parent keys with default dnorm 1.0
7103 Key<CDIM> parent_jkey=jkey.parent();
7104 while (j_list.count(parent_jkey)==0) {
7105 j_list.insert({parent_jkey,1.0});
7106 parent_jkey=parent_jkey.parent();
7107 }
7108 }
7109 }
7110 return std::make_tuple(ij_list,j_list);
7111 }
7112
7113 /// make a map of all nodes that will contribute to a partial inner product
7114
7115 /// given the list of d coefficient-holding nodes of the other function:
7116 /// recur down h if snorm * dnorm > tol and key n−jx ∈ other−ij-list. Make s
7117 /// coefficients if necessary. Make list of nodes n − ijk as map(n-ik, list(j)).
7118 ///
7119 /// !! WILL ADD NEW S NODES TO THIS TREE THAT MUST BE REMOVED TO AVOID INCONSISTENT TREE STRUCTURE !!
7120 ///
7121 /// @param[in] key for recursion
7122 /// @param[in] node corresponds to key
7123 /// @param[in] v_this this' dimension that are contracted
7124 /// @param[in] v_other other's dimension that are contracted
7125 /// @param[in] ij_other_list list of nodes of the other function that will be contracted (and their parents)
7126 /// @param[in] j_other_list list of column nodes of the other function that will be contracted (and their parents)
7127 /// @param[in] max_d_norm max d coeff norm of the nodes in j_list
7128 /// @param[in] this_first are the remaining coeffs of this functions first or last in the result function
7129 /// @param[in] thresh threshold for including nodes in the contraction: snorm*dnorm > thresh
7130 /// @tparam CDIM dimension to be contracted
7131 /// @tparam ODIM dimensions of the other function
7132 /// @tparam FDIM dimensions of the final function
7133 template<std::size_t CDIM, std::size_t ODIM, std::size_t FDIM=NDIM+ODIM-2*CDIM>
7134 std::multimap<Key<FDIM>, std::list<Key<CDIM>>> recur_down_for_contraction_map(
7135 const keyT& key, const nodeT& node,
7136 const std::array<int,CDIM>& v_this,
7137 const std::array<int,CDIM>& v_other,
7138 const std::set<Key<ODIM>>& ij_other_list,
7139 const std::map<Key<CDIM>,double>& j_other_list,
7140 bool this_first, const double thresh) {
7141
7142 std::multimap<Key<FDIM>, std::list<Key<CDIM>>> contraction_map;
7143
7144 // fast return if the other function has no d coeffs
7145 if (j_other_list.empty()) return contraction_map;
7146
7147 // continue recursion if this node may be contracted with the j column
7148 // extract relevant node translations from this node
7149 const auto j_this_key=key.extract_key(v_this);
7150
7151// print("\nkey, j_this_key", key, j_this_key);
7152 const double max_d_norm=j_other_list.find(j_this_key)->second;
7153 const bool sd_norm_product_large = node.get_snorm() * max_d_norm > truncate_tol(thresh,key);
7154// print("sd_product_norm",node.get_snorm() * max_d_norm, thresh);
7155
7156 // end recursion if we have reached the final scale n
7157 // with which nodes from other will this node be contracted?
7158 bool final_scale=key.level()==ij_other_list.begin()->level();
7159 if (final_scale and sd_norm_product_large) {
7160 for (auto& other_key : ij_other_list) {
7161 const auto j_other_key=other_key.extract_key(v_other);
7162 if (j_this_key != j_other_key) continue;
7163 auto i_key=key.extract_complement_key(v_this);
7164 auto k_key=other_key.extract_complement_key(v_other);
7165// print("key, ij_other_key",key,other_key);
7166// print("i, k, j key",i_key, k_key, j_this_key);
7167 Key<FDIM> ik_key=(this_first) ? i_key.merge_with(k_key) : k_key.merge_with(i_key);
7168// print("ik_key",ik_key);
7169// MADNESS_CHECK(contraction_map.count(ik_key)==0);
7170 contraction_map.insert(std::make_pair(ik_key,std::list<Key<CDIM>>{j_this_key}));
7171 }
7172 return contraction_map;
7173 }
7174
7175 bool continue_recursion = (j_other_list.count(j_this_key)==1);
7176 if (not continue_recursion) return contraction_map;
7177
7178
7179 // continue recursion if norms are large
7180 continue_recursion = (node.has_children() or sd_norm_product_large);
7181
7182 if (continue_recursion) {
7183 // in case we need to compute children's coefficients: unfilter only once
7184 bool compute_child_s_coeffs=true;
7185 coeffT d = node.coeff();
7186// print("continuing recursion from key",key);
7187
7188 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
7189 keyT child=kit.key();
7190 typename dcT::accessor acc;
7191
7192 // make child's s coeffs if it doesn't exist or if is has no s coeffs
7193 bool childnode_exists=get_coeffs().find(acc,child);
7194 // snorm > 0 stands in for "this node holds s coefficients":
7195 // every writer zeroes snorm when it clears them, cf.
7196 // FunctionNode::recompute_snorm_and_dnorm() and the
7197 // keepleaves=false path of compress_spawn().
7198 bool need_s_coeffs= childnode_exists ? (acc->second.get_snorm()<=0.0) : true;
7199
7200 coeffT child_s_coeffs;
7201 if (need_s_coeffs) {
7202 if (compute_child_s_coeffs) {
7203 if (d.dim(0)==cdata.vk[0]) { // s coeffs only in this node
7204 coeffT d1(cdata.v2k,get_tensor_args());
7205 d1(cdata.s0)+=d;
7206 d=d1;
7207 }
7208 d = unfilter(d);
7209 compute_child_s_coeffs=false;
7210 }
7211 child_s_coeffs=copy(d(child_patch(child)));
7212 child_s_coeffs.reduce_rank(thresh);
7213 }
7214
7215 if (not childnode_exists) {
7216 get_coeffs().replace(child,nodeT(child_s_coeffs,false));
7217 get_coeffs().find(acc,child);
7218 } else if (childnode_exists and need_s_coeffs) {
7219 acc->second.coeff()=child_s_coeffs;
7220 }
7221 bool exists= get_coeffs().find(acc,child);
7222 MADNESS_CHECK(exists);
7223 nodeT& childnode = acc->second;
7224 if (need_s_coeffs) childnode.recompute_snorm_and_dnorm(get_cdata());
7225// print("recurring down to",child);
7226 contraction_map.merge(recur_down_for_contraction_map(child,childnode, v_this, v_other,
7227 ij_other_list, j_other_list, this_first, thresh));
7228// print("contraction_map.size()",contraction_map.size());
7229 }
7230
7231 }
7232
7233 return contraction_map;
7234 }
7235
7236
7237 /// tensor contraction part of partial_inner
7238
7239 /// @param[in] g rhs of the inner product
7240 /// @param[in] h lhs of the inner product
7241 /// @param[in] v1 dimensions of g to be contracted
7242 /// @param[in] v2 dimensions of h to be contracted
7243 /// @param[in] key key of result's (this) FunctionNode
7244 /// @param[in] j_key_list list of contraction index-j keys contributing to this' node
7245 template<typename Q, std::size_t LDIM, typename R, std::size_t KDIM,
7246 std::size_t CDIM = (KDIM + LDIM - NDIM) / 2>
7248 const std::array<int, CDIM> v1, const std::array<int, CDIM> v2,
7249 const Key<NDIM>& key, const std::list<Key<CDIM>>& j_key_list) {
7250
7251 Key<LDIM - CDIM> i_key;
7252 Key<KDIM - CDIM> k_key;
7253 key.break_apart(i_key, k_key);
7254
7255 coeffT result_coeff(get_cdata().v2k, get_tensor_type());
7256 for (const auto& j_key: j_key_list) {
7257
7258 auto v_complement = [](const auto& v, const auto& vc) {
7259 constexpr std::size_t VDIM = std::tuple_size<std::decay_t<decltype(v)>>::value;
7260 constexpr std::size_t VCDIM = std::tuple_size<std::decay_t<decltype(vc)>>::value;
7261 std::array<int, VCDIM> result;
7262 for (std::size_t i = 0; i < VCDIM; i++) result[i] = (v.back() + i + 1) % (VDIM + VCDIM);
7263 return result;
7264 };
7265 auto make_ij_key = [&v_complement](const auto i_key, const auto j_key, const auto& v) {
7266 constexpr std::size_t IDIM = std::decay_t<decltype(i_key)>::static_size;
7267 constexpr std::size_t JDIM = std::decay_t<decltype(j_key)>::static_size;
7268 static_assert(JDIM == std::tuple_size<std::decay_t<decltype(v)>>::value);
7269
7271 for (std::size_t i = 0; i < v.size(); ++i) l[v[i]] = j_key.translation()[i];
7272 std::array<int, IDIM> vc1;
7273 auto vc = v_complement(v, vc1);
7274 for (std::size_t i = 0; i < vc.size(); ++i) l[vc[i]] = i_key.translation()[i];
7275
7276 return Key<IDIM + JDIM>(i_key.level(), l);
7277 };
7278
7279 Key<LDIM> ij_key = make_ij_key(i_key, j_key, v1);
7280 Key<KDIM> jk_key = make_ij_key(k_key, j_key, v2);
7281
7282 MADNESS_CHECK(g->get_coeffs().probe(ij_key));
7283 MADNESS_CHECK(h->get_coeffs().probe(jk_key));
7284 const coeffT& gcoeff = g->get_coeffs().find(ij_key).get()->second.coeff();
7285 const coeffT& hcoeff = h->get_coeffs().find(jk_key).get()->second.coeff();
7286 coeffT gcoeff1, hcoeff1;
7287 if (gcoeff.dim(0) == g->get_cdata().k) {
7288 gcoeff1 = coeffT(g->get_cdata().v2k, g->get_tensor_args());
7289 gcoeff1(g->get_cdata().s0) += gcoeff;
7290 } else {
7291 gcoeff1 = gcoeff;
7292 }
7293 if (hcoeff.dim(0) == g->get_cdata().k) {
7294 hcoeff1 = coeffT(h->get_cdata().v2k, h->get_tensor_args());
7295 hcoeff1(h->get_cdata().s0) += hcoeff;
7296 } else {
7297 hcoeff1 = hcoeff;
7298 }
7299
7300 // offset: 0 for full tensor, 1 for svd representation with rand being the first dimension (r,d1,d2,d3) -> (r,d1*d2*d3)
7301 auto fuse = [](Tensor<T> tensor, const std::array<int, CDIM>& v, int offset) {
7302 for (std::size_t i = 0; i < CDIM - 1; ++i) {
7303 MADNESS_CHECK((v[i] + 1) == v[i + 1]); // make sure v is contiguous and ascending
7304 tensor = tensor.fusedim(v[0]+offset);
7305 }
7306 return tensor;
7307 };
7308
7309 // use case: partial_projection of 2-electron functions in svd representation f(1) = \int g(2) h(1,2) d2
7310 // c_i = \sum_j a_j b_ij = \sum_jr a_j b_rj b'_rj
7311 // = \sum_jr ( a_j b_rj) b'_rj )
7312 auto contract2 = [](const auto& svdcoeff, const auto& tensor, const int particle) {
7313#ifdef ENABLE_GENTENSOR
7314 const int spectator_particle=(particle+1)%2;
7315 Tensor<Q> gtensor = svdcoeff.get_svdtensor().make_vector_with_weights(particle);
7316 gtensor=gtensor.reshape(svdcoeff.rank(),gtensor.size()/svdcoeff.rank());
7317 MADNESS_CHECK(gtensor.ndim()==2);
7318 Tensor<Q> gtensor_other = svdcoeff.get_svdtensor().ref_vector(spectator_particle);
7319 Tensor<T> tmp1=inner(gtensor,tensor.flat(),1,0); // tmp1(r) = sum_j a'_(r,j) b(j)
7320 MADNESS_CHECK(tmp1.ndim()==1);
7321 Tensor<T> tmp2=inner(gtensor_other,tmp1,0,0); // tmp2(i) = sum_r a_(r,i) tmp1(r)
7322 return tmp2;
7323#else
7324 MADNESS_EXCEPTION("no partial_inner using svd without GenTensor",1);
7325 return Tensor<T>();
7326#endif
7327 };
7328
7329 if (gcoeff.is_full_tensor() and hcoeff.is_full_tensor() and result_coeff.is_full_tensor()) {
7330 // merge multiple contraction dimensions into one
7331 int offset = 0;
7332 Tensor<Q> gtensor = fuse(gcoeff1.full_tensor(), v1, offset);
7333 Tensor<R> htensor = fuse(hcoeff1.full_tensor(), v2, offset);
7334 result_coeff.full_tensor() += inner(gtensor, htensor, v1[0], v2[0]);
7335 if (key.level() > 0) {
7336 gtensor = copy(gcoeff1.full_tensor()(g->get_cdata().s0));
7337 htensor = copy(hcoeff1.full_tensor()(h->get_cdata().s0));
7338 gtensor = fuse(gtensor, v1, offset);
7339 htensor = fuse(htensor, v2, offset);
7340 result_coeff.full_tensor()(get_cdata().s0) -= inner(gtensor, htensor, v1[0], v2[0]);
7341 }
7342 }
7343
7344
7345 // use case: 2-electron functions in svd representation f(1,3) = \int g(1,2) h(2,3) d2
7346 // c_ik = \sum_j a_ij b_jk = \sum_jrr' a_ri a'_rj b_r'j b_r'k
7347 // = \sum_jrr' ( a_ri (a'_rj b_r'j) ) b_r'k
7348 // = \sum_jrr' c_r'i b_r'k
7349 else if (gcoeff.is_svd_tensor() and hcoeff.is_svd_tensor() and result_coeff.is_svd_tensor()) {
7350 MADNESS_CHECK(v1[0]==0 or v1[CDIM-1]==LDIM-1);
7351 MADNESS_CHECK(v2[0]==0 or v2[CDIM-1]==KDIM-1);
7352 int gparticle= v1[0]==0 ? 0 : 1; // which particle to integrate over
7353 int hparticle= v2[0]==0 ? 0 : 1; // which particle to integrate over
7354 // merge multiple contraction dimensions into one
7355 Tensor<Q> gtensor = gcoeff1.get_svdtensor().flat_vector_with_weights(gparticle);
7356 Tensor<Q> gtensor_other = gcoeff1.get_svdtensor().flat_vector((gparticle+1)%2);
7357 Tensor<R> htensor = hcoeff1.get_svdtensor().flat_vector_with_weights(hparticle);
7358 Tensor<R> htensor_other = hcoeff1.get_svdtensor().flat_vector((hparticle+1)%2);
7359 Tensor<T> tmp1=inner(gtensor,htensor,1,1); // tmp1(r,r') = sum_j b(r,j) a(r',j)
7360 Tensor<T> tmp2=inner(tmp1,gtensor_other,0,0); // tmp2(r',i) = sum_r tmp1(r,r') a(r,i)
7362 MADNESS_CHECK(tmp2.dim(0)==htensor_other.dim(0));
7363 w=1.0;
7364 coeffT result_tmp(get_cdata().v2k, get_tensor_type());
7365 result_tmp.get_svdtensor().set_vectors_and_weights(w,tmp2,htensor_other);
7366 if (key.level() > 0) {
7367 GenTensor<Q> gcoeff2 = copy(gcoeff1(g->get_cdata().s0));
7368 GenTensor<R> hcoeff2 = copy(hcoeff1(h->get_cdata().s0));
7369 Tensor<Q> gtensor = gcoeff2.get_svdtensor().flat_vector_with_weights(gparticle);
7370 Tensor<Q> gtensor_other = gcoeff2.get_svdtensor().flat_vector((gparticle+1)%2);
7371 Tensor<R> htensor = hcoeff2.get_svdtensor().flat_vector_with_weights(hparticle);
7372 Tensor<R> htensor_other = hcoeff2.get_svdtensor().flat_vector((hparticle+1)%2);
7373 Tensor<T> tmp1=inner(gtensor,htensor,1,1); // tmp1(r,r') = sum_j b(r,j) a(r',j)
7374 Tensor<T> tmp2=inner(tmp1,gtensor_other,0,0); // tmp2(r',i) = sum_r tmp1(r,r') a(r,i)
7376 MADNESS_CHECK(tmp2.dim(0)==htensor_other.dim(0));
7377 w=1.0;
7378 coeffT result_coeff1(get_cdata().vk, get_tensor_type());
7379 result_coeff1.get_svdtensor().set_vectors_and_weights(w,tmp2,htensor_other);
7380 result_tmp(get_cdata().s0)-=result_coeff1;
7381 }
7382 result_coeff+=result_tmp;
7383 }
7384
7385 // use case: partial_projection of 2-electron functions in svd representation f(1) = \int g(2) h(1,2) d2
7386 // c_i = \sum_j a_j b_ij = \sum_jr a_j b_rj b'_rj
7387 // = \sum_jr ( a_j b_rj) b'_rj )
7388 else if (gcoeff.is_full_tensor() and hcoeff.is_svd_tensor() and result_coeff.is_full_tensor()) {
7389 MADNESS_CHECK(v1[0]==0 and v1[CDIM-1]==LDIM-1);
7390 MADNESS_CHECK(v2[0]==0 or v2[CDIM-1]==KDIM-1);
7391 MADNESS_CHECK(LDIM==CDIM);
7392 int hparticle= v2[0]==0 ? 0 : 1; // which particle to integrate over
7393
7394 Tensor<T> r=contract2(hcoeff1,gcoeff1.full_tensor(),hparticle);
7395 if (key.level()>0) r(get_cdata().s0)-=contract2(copy(hcoeff1(h->get_cdata().s0)),copy(gcoeff.full_tensor()(g->get_cdata().s0)),hparticle);
7396 result_coeff.full_tensor()+=r;
7397 }
7398 // use case: partial_projection of 2-electron functions in svd representation f(1) = \int g(1,2) h(2) d2
7399 // c_i = \sum_j a_ij b_j = \sum_jr a_ri a'_rj b_j
7400 // = \sum_jr ( a_ri (a'_rj b_j) )
7401 else if (gcoeff.is_svd_tensor() and hcoeff.is_full_tensor() and result_coeff.is_full_tensor()) {
7402 MADNESS_CHECK(v1[0]==0 or v1[CDIM-1]==LDIM-1);
7403 MADNESS_CHECK(v2[0]==0 and v2[CDIM-1]==KDIM-1);
7404 MADNESS_CHECK(KDIM==CDIM);
7405 int gparticle= v1[0]==0 ? 0 : 1; // which particle to integrate over
7406
7407 Tensor<T> r=contract2(gcoeff1,hcoeff1.full_tensor(),gparticle);
7408 if (key.level()>0) r(get_cdata().s0)-=contract2(copy(gcoeff1(g->get_cdata().s0)),copy(hcoeff.full_tensor()(h->get_cdata().s0)),gparticle);
7409 result_coeff.full_tensor()+=r;
7410
7411 } else {
7412 MADNESS_EXCEPTION("unknown case in partial_inner_contract",1);
7413 }
7414 }
7415
7416 MADNESS_CHECK(result_coeff.is_assigned());
7417 result_coeff.reduce_rank(get_thresh());
7418
7419 if (coeffs.is_local(key))
7420 coeffs.send(key, &nodeT::accumulate, result_coeff, coeffs, key, get_tensor_args(), false);
7421 else
7422 coeffs.task(key, &nodeT::accumulate, result_coeff, coeffs, key, get_tensor_args(), false, TaskAttributes::hipri());
7423 }
7424
7425 /// Return the inner product with an external function on a specified function node.
7426
7427 /// @param[in] key Key of the function node to compute the inner product on. (the domain of integration)
7428 /// @param[in] c Tensor of coefficients for the function at the function node given by key
7429 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
7430 /// @return Returns the inner product over the domain of a single function node, no guarantee of accuracy.
7431 T inner_ext_node(keyT key, tensorT c, const std::shared_ptr< FunctionFunctorInterface<T,NDIM> > f) const {
7432 tensorT fvals = tensorT(this->cdata.vk);
7433 // Compute the value of the external function at the quadrature points.
7434 fcube(key, *(f), cdata.quad_x, fvals);
7435 // Convert quadrature point values to scaling coefficients.
7436 tensorT fc = tensorT(values2coeffs(key, fvals));
7437 // Return the inner product of the two functions' scaling coefficients.
7438 return c.trace_conj(fc);
7439 }
7440
7441 /// Call inner_ext_node recursively until convergence.
7442 /// @param[in] key Key of the function node on which to compute inner product (the domain of integration)
7443 /// @param[in] c coeffs for the function at the node given by key
7444 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
7445 /// @param[in] leaf_refine boolean switch to turn on/off refinement past leaf nodes
7446 /// @param[in] old_inner the inner product on the parent function node
7447 /// @return Returns the inner product over the domain of a single function, checks for convergence.
7448 T inner_ext_recursive(keyT key, tensorT c, const std::shared_ptr< FunctionFunctorInterface<T,NDIM> > f, const bool leaf_refine, T old_inner=T(0)) const {
7449 int i = 0;
7450 tensorT c_child, inner_child;
7451 T new_inner, result = 0.0;
7452
7453 c_child = tensorT(cdata.v2k); // tensor of child coeffs
7454 inner_child = Tensor<double>(pow(2, NDIM)); // child inner products
7455
7456 // If old_inner is default value, assume this is the first call
7457 // and compute inner product on this node.
7458 if (old_inner == T(0)) {
7459 old_inner = inner_ext_node(key, c, f);
7460 }
7461
7462 if (coeffs.find(key).get()->second.has_children()) {
7463 // Since the key has children and we know the func is redundant,
7464 // Iterate over all children of this compute node, computing
7465 // the inner product on each child node. new_inner will store
7466 // the sum of these, yielding a more accurate inner product.
7467 for (KeyChildIterator<NDIM> it(key); it; ++it, ++i) {
7468 const keyT& child = it.key();
7469 tensorT cc = coeffs.find(child).get()->second.coeff().full_tensor_copy();
7470 inner_child(i) = inner_ext_node(child, cc, f);
7471 }
7472 new_inner = inner_child.sum();
7473 } else if (leaf_refine) {
7474 // We need the scaling coefficients of the numerical function
7475 // at each of the children nodes. We can't use project because
7476 // there is no guarantee that the numerical function will have
7477 // a functor. Instead, since we know we are at or below the
7478 // leaf nodes, the wavelet coefficients are zero (to within the
7479 // truncate tolerance). Thus, we can use unfilter() to
7480 // get the scaling coefficients at the next level.
7481 tensorT d = tensorT(cdata.v2k);
7482 d = T(0);
7483 d(cdata.s0) = copy(c);
7484 c_child = unfilter(d);
7485
7486 // Iterate over all children of this compute node, computing
7487 // the inner product on each child node. new_inner will store
7488 // the sum of these, yielding a more accurate inner product.
7489 for (KeyChildIterator<NDIM> it(key); it; ++it, ++i) {
7490 const keyT& child = it.key();
7491 tensorT cc = tensorT(c_child(child_patch(child)));
7492 inner_child(i) = inner_ext_node(child, cc, f);
7493 }
7494 new_inner = inner_child.sum();
7495 } else {
7496 // If we get to here, we are at the leaf nodes and the user has
7497 // specified that they do not want refinement past leaf nodes.
7498 new_inner = old_inner;
7499 }
7500
7501 // Check for convergence. If converged...yay, we're done. If not,
7502 // call inner_ext_node_recursive on each child node and accumulate
7503 // the inner product in result.
7504 // if (std::abs(new_inner - old_inner) <= truncate_tol(thresh, key)) {
7505 if (std::abs(new_inner - old_inner) <= thresh) {
7506 result = new_inner;
7507 } else {
7508 i = 0;
7509 for (KeyChildIterator<NDIM> it(key); it; ++it, ++i) {
7510 const keyT& child = it.key();
7511 tensorT cc = tensorT(c_child(child_patch(child)));
7512 result += inner_ext_recursive(child, cc, f, leaf_refine, inner_child(i));
7513 }
7514 }
7515
7516 return result;
7517 }
7518
7520 const std::shared_ptr< FunctionFunctorInterface<T, NDIM> > fref;
7521 const implT * impl;
7522 const bool leaf_refine;
7523 const bool do_leaves; ///< start with leaf nodes instead of initial_level
7524
7526 const implT * impl, const bool leaf_refine, const bool do_leaves)
7528
7529 T operator()(typename dcT::const_iterator& it) const {
7530 if (do_leaves and it->second.is_leaf()) {
7531 tensorT cc = it->second.coeff().full_tensor();
7532 return impl->inner_adaptive_recursive(it->first, cc, fref, leaf_refine, T(0));
7533 } else if ((not do_leaves) and (it->first.level() == impl->initial_level)) {
7534 tensorT cc = it->second.coeff().full_tensor();
7535 return impl->inner_ext_recursive(it->first, cc, fref, leaf_refine, T(0));
7536 } else {
7537 return 0.0;
7538 }
7539 }
7540
7541 T operator()(T a, T b) const {
7542 return (a + b);
7543 }
7544
7545 template <typename Archive> void serialize(const Archive& ar) {
7546 MADNESS_EXCEPTION("NOT IMPLEMENTED", 1);
7547 }
7548 };
7549
7550 /// Return the local part of inner product with external function ... no communication.
7551 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
7552 /// @param[in] leaf_refine boolean switch to turn on/off refinement past leaf nodes
7553 /// @return Returns local part of the inner product, i.e. over the domain of all function nodes on this compute node.
7554 T inner_ext_local(const std::shared_ptr< FunctionFunctorInterface<T,NDIM> > f, const bool leaf_refine) const {
7556
7557 return world.taskq.reduce<T, rangeT, do_inner_ext_local_ffi>(rangeT(coeffs.begin(),coeffs.end()),
7558 do_inner_ext_local_ffi(f, this, leaf_refine, false));
7559 }
7560
7561 /// Return the local part of inner product with external function ... no communication.
7562 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
7563 /// @param[in] leaf_refine boolean switch to turn on/off refinement past leaf nodes
7564 /// @return Returns local part of the inner product, i.e. over the domain of all function nodes on this compute node.
7565 T inner_adaptive_local(const std::shared_ptr< FunctionFunctorInterface<T,NDIM> > f, const bool leaf_refine) const {
7567
7568 return world.taskq.reduce<T, rangeT, do_inner_ext_local_ffi>(rangeT(coeffs.begin(),coeffs.end()),
7569 do_inner_ext_local_ffi(f, this, leaf_refine, true));
7570 }
7571
7572 /// Call inner_ext_node recursively until convergence.
7573 /// @param[in] key Key of the function node on which to compute inner product (the domain of integration)
7574 /// @param[in] c coeffs for the function at the node given by key
7575 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
7576 /// @param[in] leaf_refine boolean switch to turn on/off refinement past leaf nodes
7577 /// @param[in] old_inner the inner product on the parent function node
7578 /// @return Returns the inner product over the domain of a single function, checks for convergence.
7580 const std::shared_ptr< FunctionFunctorInterface<T,NDIM> > f,
7581 const bool leaf_refine, T old_inner=T(0)) const {
7582
7583 // the inner product in the current node
7584 old_inner = inner_ext_node(key, c, f);
7585 T result=0.0;
7586
7587 // the inner product in the child nodes
7588
7589 // compute the sum coefficients of the MRA function
7590 tensorT d = tensorT(cdata.v2k);
7591 d = T(0);
7592 d(cdata.s0) = copy(c);
7593 tensorT c_child = unfilter(d);
7594
7595 // compute the inner product in the child nodes
7596 T new_inner=0.0; // child inner products
7597 for (KeyChildIterator<NDIM> it(key); it; ++it) {
7598 const keyT& child = it.key();
7599 tensorT cc = tensorT(c_child(child_patch(child)));
7600 new_inner+= inner_ext_node(child, cc, f);
7601 }
7602
7603 // continue recursion if needed
7604 const double tol=truncate_tol(thresh,key);
7605 if (leaf_refine and (std::abs(new_inner - old_inner) > tol)) {
7606 for (KeyChildIterator<NDIM> it(key); it; ++it) {
7607 const keyT& child = it.key();
7608 tensorT cc = tensorT(c_child(child_patch(child)));
7609 result += inner_adaptive_recursive(child, cc, f, leaf_refine, T(0));
7610 }
7611 } else {
7612 result = new_inner;
7613 }
7614 return result;
7615
7616 }
7617
7618
7619 /// Return the gaxpy product with an external function on a specified
7620 /// function node.
7621 /// @param[in] key Key of the function node on which to compute gaxpy
7622 /// @param[in] lc Tensor of coefficients for the function at the
7623 /// function node given by key
7624 /// @param[in] f Pointer to function of type T that takes coordT
7625 /// arguments. This is the externally provided function and
7626 /// the right argument of gaxpy.
7627 /// @param[in] alpha prefactor of c Tensor for gaxpy
7628 /// @param[in] beta prefactor of fcoeffs for gaxpy
7629 /// @return Returns coefficient tensor of the gaxpy product at specified
7630 /// key, no guarantee of accuracy.
7631 template <typename L>
7632 tensorT gaxpy_ext_node(keyT key, Tensor<L> lc, T (*f)(const coordT&), T alpha, T beta) const {
7633 // Compute the value of external function at the quadrature points.
7634 tensorT fvals = madness::fcube(key, f, cdata.quad_x);
7635 // Convert quadrature point values to scaling coefficients.
7636 tensorT fcoeffs = values2coeffs(key, fvals);
7637 // Return the inner product of the two functions' scaling coeffs.
7638 tensorT c2 = copy(lc);
7639 c2.gaxpy(alpha, fcoeffs, beta);
7640 return c2;
7641 }
7642
7643 /// Return out of place gaxpy using recursive descent.
7644 /// @param[in] key Key of the function node on which to compute gaxpy
7645 /// @param[in] left FunctionImpl, left argument of gaxpy
7646 /// @param[in] lcin coefficients of left at this node
7647 /// @param[in] c coefficients of gaxpy product at this node
7648 /// @param[in] f pointer to function of type T that takes coordT
7649 /// arguments. This is the externally provided function and
7650 /// the right argument of gaxpy.
7651 /// @param[in] alpha prefactor of left argument for gaxpy
7652 /// @param[in] beta prefactor of right argument for gaxpy
7653 /// @param[in] tol convergence tolerance...when the norm of the gaxpy's
7654 /// difference coefficients is less than tol, we are done.
7655 template <typename L>
7656 void gaxpy_ext_recursive(const keyT& key, const FunctionImpl<L,NDIM>* left,
7657 Tensor<L> lcin, tensorT c, T (*f)(const coordT&),
7658 T alpha, T beta, double tol, bool below_leaf) {
7659 typedef typename FunctionImpl<L,NDIM>::dcT::const_iterator literT;
7660
7661 // If we haven't yet reached the leaf level, check whether the
7662 // current key is a leaf node of left. If so, set below_leaf to true
7663 // and continue. If not, make this a parent, recur down, return.
7664 if (not below_leaf) {
7665 bool left_leaf = left->coeffs.find(key).get()->second.is_leaf();
7666 if (left_leaf) {
7667 below_leaf = true;
7668 } else {
7669 this->coeffs.replace(key, nodeT(coeffT(), true));
7670 for (KeyChildIterator<NDIM> it(key); it; ++it) {
7671 const keyT& child = it.key();
7672 woT::task(left->coeffs.owner(child), &implT:: template gaxpy_ext_recursive<L>,
7673 child, left, Tensor<L>(), tensorT(), f, alpha, beta, tol, below_leaf);
7674 }
7675 return;
7676 }
7677 }
7678
7679 // Compute left's coefficients if not provided
7680 Tensor<L> lc = lcin;
7681 if (lc.size() == 0) {
7682 literT it = left->coeffs.find(key).get();
7683 MADNESS_ASSERT(it != left->coeffs.end());
7684 if (it->second.has_coeff())
7685 lc = it->second.coeff().reconstruct_tensor();
7686 }
7687
7688 // Compute this node's coefficients if not provided in function call
7689 if (c.size() == 0) {
7690 c = gaxpy_ext_node(key, lc, f, alpha, beta);
7691 }
7692
7693 // We need the scaling coefficients of the numerical function at
7694 // each of the children nodes. We can't use project because there
7695 // is no guarantee that the numerical function will have a functor.
7696 // Instead, since we know we are at or below the leaf nodes, the
7697 // wavelet coefficients are zero (to within the truncate tolerance).
7698 // Thus, we can use unfilter() to get the scaling coefficients at
7699 // the next level.
7700 Tensor<L> lc_child = Tensor<L>(cdata.v2k); // left's child coeffs
7701 Tensor<L> ld = Tensor<L>(cdata.v2k);
7702 ld = L(0);
7703 ld(cdata.s0) = copy(lc);
7704 lc_child = unfilter(ld);
7705
7706 // Iterate over children of this node,
7707 // storing the gaxpy coeffs in c_child
7708 tensorT c_child = tensorT(cdata.v2k); // tensor of child coeffs
7709 for (KeyChildIterator<NDIM> it(key); it; ++it) {
7710 const keyT& child = it.key();
7711 tensorT lcoeff = tensorT(lc_child(child_patch(child)));
7712 c_child(child_patch(child)) = gaxpy_ext_node(child, lcoeff, f, alpha, beta);
7713 }
7714
7715 // Compute the difference coefficients to test for convergence.
7716 tensorT d = tensorT(cdata.v2k);
7717 d = filter(c_child);
7718 // Filter returns both s and d coefficients, so set scaling
7719 // coefficient part of d to 0 so that we take only the
7720 // norm of the difference coefficients.
7721 d(cdata.s0) = T(0);
7722 double dnorm = d.normf();
7723
7724 // Small d.normf means we've reached a good level of resolution
7725 // Store the coefficients and return.
7726 if (dnorm <= truncate_tol(tol,key)) {
7727 this->coeffs.replace(key, nodeT(coeffT(c,targs), false));
7728 } else {
7729 // Otherwise, make this a parent node and recur down
7730 this->coeffs.replace(key, nodeT(coeffT(), true)); // Interior node
7731
7732 for (KeyChildIterator<NDIM> it(key); it; ++it) {
7733 const keyT& child = it.key();
7734 tensorT child_coeff = tensorT(c_child(child_patch(child)));
7735 tensorT left_coeff = tensorT(lc_child(child_patch(child)));
7736 woT::task(left->coeffs.owner(child), &implT:: template gaxpy_ext_recursive<L>,
7737 child, left, left_coeff, child_coeff, f, alpha, beta, tol, below_leaf);
7738 }
7739 }
7740 }
7741
7742 template <typename L>
7743 void gaxpy_ext(const FunctionImpl<L,NDIM>* left, T (*f)(const coordT&), T alpha, T beta, double tol, bool fence) {
7744 if (world.rank() == coeffs.owner(cdata.key0))
7745 gaxpy_ext_recursive<L> (cdata.key0, left, Tensor<L>(), tensorT(), f, alpha, beta, tol, false);
7746 if (fence)
7747 world.gop.fence();
7748 }
7749
7750 /// project the low-dim function g on the hi-dim function f: result(x) = <this(x,y) | g(y)>
7751
7752 /// invoked by the hi-dim function, a function of NDIM+LDIM
7753
7754 /// Upon return, result matches this, with contributions on all scales
7755 /// @param[in] result lo-dim function of NDIM-LDIM \todo Should this be param[out]?
7756 /// @param[in] gimpl lo-dim function of LDIM
7757 /// @param[in] dim over which dimensions to be integrated: 0..LDIM or LDIM..LDIM+NDIM-1
7758 template<size_t LDIM>
7760 const int dim, const bool fence) {
7761
7762 const keyT& key0=cdata.key0;
7763
7764 if (world.rank() == coeffs.owner(key0)) {
7765
7766 // coeff_op will accumulate the result
7767 typedef project_out_op<LDIM> coeff_opT;
7768 coeff_opT coeff_op(this,result,CoeffTracker<T,LDIM>(gimpl),dim);
7769
7770 // don't do anything on this -- coeff_op will accumulate into result
7771 typedef noop<T,NDIM> apply_opT;
7772 apply_opT apply_op;
7773
7774 woT::task(world.rank(), &implT:: template forward_traverse<coeff_opT,apply_opT>,
7775 coeff_op, apply_op, cdata.key0);
7776
7777 }
7778 if (fence) world.gop.fence();
7779
7780 }
7781
7782
7783 /// project the low-dim function g on the hi-dim function f: result(x) = <f(x,y) | g(y)>
7784 template<size_t LDIM>
7786 bool randomize() const {return false;}
7787
7790 typedef FunctionImpl<T,NDIM-LDIM> implL1;
7791 typedef std::pair<bool,coeffT> argT;
7792
7793 const implT* fimpl; ///< the hi dim function f
7794 mutable implL1* result; ///< the low dim result function
7795 ctL iag; ///< the low dim function g
7796 int dim; ///< 0: project 0..LDIM-1, 1: project LDIM..NDIM-1
7797
7798 // ctor
7799 project_out_op() = default;
7800 project_out_op(const implT* fimpl, implL1* result, const ctL& iag, const int dim)
7801 : fimpl(fimpl), result(result), iag(iag), dim(dim) {}
7803 : fimpl(other.fimpl), result(other.result), iag(other.iag), dim(other.dim) {}
7804
7805
7806 /// do the actual contraction
7808
7809 Key<LDIM> key1,key2,dest;
7810 key.break_apart(key1,key2);
7811
7812 // make the right coefficients
7813 coeffT gcoeff;
7814 if (dim==0) {
7815 gcoeff=iag.get_impl()->parent_to_child(iag.coeff(),iag.key(),key1);
7816 dest=key2;
7817 }
7818 if (dim==1) {
7819 gcoeff=iag.get_impl()->parent_to_child(iag.coeff(),iag.key(),key2);
7820 dest=key1;
7821 }
7822
7823 MADNESS_ASSERT(fimpl->get_coeffs().probe(key)); // must be local!
7824 const nodeT& fnode=fimpl->get_coeffs().find(key).get()->second;
7825 const coeffT& fcoeff=fnode.coeff();
7826
7827 // fast return if possible
7828 if (fcoeff.has_no_data() or gcoeff.has_no_data())
7829 return Future<argT> (argT(fnode.is_leaf(),coeffT()));;
7830
7831 MADNESS_CHECK(gcoeff.is_full_tensor());
7832 tensorT final(result->cdata.vk);
7833 const int k=fcoeff.dim(0);
7834 const int k_ldim=std::pow(k,LDIM);
7835 std::vector<long> shape(LDIM, k);
7836
7837 if (fcoeff.is_full_tensor()) {
7838 // result_i = \sum_j g_j f_ji
7839 const tensorT gtensor = gcoeff.full_tensor().reshape(k_ldim);
7840 const tensorT ftensor = fcoeff.full_tensor().reshape(k_ldim,k_ldim);
7841 final=inner(gtensor,ftensor,0,dim).reshape(shape);
7842
7843 } else if (fcoeff.is_svd_tensor()) {
7844 if (fcoeff.rank()>0) {
7845
7846 // result_i = \sum_jr g_j a_rj w_r b_ri
7847 const int otherdim = (dim + 1) % 2;
7848 const tensorT gtensor = gcoeff.full_tensor().flat();
7849 const tensorT atensor = fcoeff.get_svdtensor().flat_vector(dim); // a_rj
7850 const tensorT btensor = fcoeff.get_svdtensor().flat_vector(otherdim);
7851 const tensorT gatensor = inner(gtensor, atensor, 0, 1); // ga_r
7852 tensorT weights = copy(fcoeff.get_svdtensor().weights_);
7853 weights.emul(gatensor); // ga_r * w_r
7854 // sum over all ranks of b, include new weights:
7855 // result_i = \sum_r ga_r * w_r * b_ri
7856 for (int r = 0; r < fcoeff.rank(); ++r) final += weights(r) * btensor(r, _);
7857 final = final.reshape(shape);
7858 }
7859
7860 } else {
7861 MADNESS_EXCEPTION("unsupported tensor type in project_out_op",1);
7862 }
7863
7864 // accumulate the result
7865 result->coeffs.task(dest, &FunctionNode<T,LDIM>::accumulate2, final, result->coeffs, dest, TaskAttributes::hipri());
7866
7867 return Future<argT> (argT(fnode.is_leaf(),coeffT()));
7868 }
7869
7870 this_type make_child(const keyT& child) const {
7871 Key<LDIM> key1,key2;
7872 child.break_apart(key1,key2);
7873 const Key<LDIM> gkey = (dim==0) ? key1 : key2;
7874
7875 return this_type(fimpl,result,iag.make_child(gkey),dim);
7876 }
7877
7878 /// retrieve the coefficients (parent coeffs might be remote)
7880 Future<ctL> g1=iag.activate();
7881 return result->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
7883 }
7884
7885 /// taskq-compatible ctor
7886 this_type forward_ctor(const implT* fimpl1, implL1* result1, const ctL& iag1, const int dim1) {
7887 return this_type(fimpl1,result1,iag1,dim1);
7888 }
7889
7890 template <typename Archive> void serialize(const Archive& ar) {
7891 ar & result & iag & fimpl & dim;
7892 }
7893
7894 };
7895
7896
7897 /// project the low-dim function g on the hi-dim function f: this(x) = <f(x,y) | g(y)>
7898
7899 /// invoked by result, a function of NDIM
7900
7901 /// @param[in] f hi-dim function of LDIM+NDIM
7902 /// @param[in] g lo-dim function of LDIM
7903 /// @param[in] dim over which dimensions to be integrated: 0..LDIM or LDIM..LDIM+NDIM-1
7904 template<size_t LDIM>
7905 void project_out2(const FunctionImpl<T,LDIM+NDIM>* f, const FunctionImpl<T,LDIM>* g, const int dim) {
7906
7907 typedef std::pair< keyT,coeffT > pairT;
7908 typedef typename FunctionImpl<T,NDIM+LDIM>::dcT::const_iterator fiterator;
7909
7910 // loop over all nodes of hi-dim f, compute the inner products with all
7911 // appropriate nodes of g, and accumulate in result
7912 fiterator end = f->get_coeffs().end();
7913 for (fiterator it=f->get_coeffs().begin(); it!=end; ++it) {
7914 const Key<LDIM+NDIM> key=it->first;
7915 const FunctionNode<T,LDIM+NDIM> fnode=it->second;
7916 const coeffT& fcoeff=fnode.coeff();
7917
7918 if (fnode.is_leaf() and fcoeff.has_data()) {
7919
7920 // break key into particle: over key1 will be summed, over key2 will be
7921 // accumulated, or vice versa, depending on dim
7922 if (dim==0) {
7923 Key<NDIM> key1;
7924 Key<LDIM> key2;
7925 key.break_apart(key1,key2);
7926
7927 Future<pairT> result;
7928 // sock_it_to_me(key1, result.remote_ref(world));
7929 g->task(coeffs.owner(key1), &implT::sock_it_to_me, key1, result.remote_ref(world), TaskAttributes::hipri());
7930 woT::task(world.rank(),&implT:: template do_project_out<LDIM>,fcoeff,result,key1,key2,dim);
7931
7932 } else if (dim==1) {
7933 Key<LDIM> key1;
7934 Key<NDIM> key2;
7935 key.break_apart(key1,key2);
7936
7937 Future<pairT> result;
7938 // sock_it_to_me(key2, result.remote_ref(world));
7939 g->task(coeffs.owner(key2), &implT::sock_it_to_me, key2, result.remote_ref(world), TaskAttributes::hipri());
7940 woT::task(world.rank(),&implT:: template do_project_out<LDIM>,fcoeff,result,key2,key1,dim);
7941
7942 } else {
7943 MADNESS_EXCEPTION("confused dim in project_out",1);
7944 }
7945 }
7946 }
7948// this->compressed=false;
7949// this->nonstandard=false;
7950// this->redundant=true;
7951 }
7952
7953
7954 /// compute the inner product of two nodes of only some dimensions and accumulate on result
7955
7956 /// invoked by result
7957 /// @param[in] fcoeff coefficients of high dimension LDIM+NDIM
7958 /// @param[in] gpair key and coeffs of low dimension LDIM (possibly a parent node)
7959 /// @param[in] gkey key of actual low dim node (possibly the same as gpair.first, iff gnode exists)
7960 /// @param[in] dest destination node for the result
7961 /// @param[in] dim which dimensions should be contracted: 0..LDIM-1 or LDIM..NDIM+LDIM-1
7962 template<size_t LDIM>
7963 void do_project_out(const coeffT& fcoeff, const std::pair<keyT,coeffT> gpair, const keyT& gkey,
7964 const Key<NDIM>& dest, const int dim) const {
7965
7966 const coeffT gcoeff=parent_to_child(gpair.second,gpair.first,gkey);
7967
7968 // fast return if possible
7969 if (fcoeff.has_no_data() or gcoeff.has_no_data()) return;
7970
7971 // let's specialize for the time being on SVD tensors for f and full tensors of half dim for g
7973 MADNESS_ASSERT(fcoeff.tensor_type()==TT_2D);
7974 const tensorT gtensor=gcoeff.full_tensor();
7975 tensorT result(cdata.vk);
7976
7977 const int otherdim=(dim+1)%2;
7978 const int k=fcoeff.dim(0);
7979 std::vector<Slice> s(fcoeff.config().dim_per_vector()+1,_);
7980
7981 // do the actual contraction
7982 for (int r=0; r<fcoeff.rank(); ++r) {
7983 s[0]=Slice(r,r);
7984 const tensorT contracted_tensor=fcoeff.config().ref_vector(dim)(s).reshape(k,k,k);
7985 const tensorT other_tensor=fcoeff.config().ref_vector(otherdim)(s).reshape(k,k,k);
7986 const double ovlp= gtensor.trace_conj(contracted_tensor);
7987 const double fac=ovlp * fcoeff.config().weights(r);
7988 result+=fac*other_tensor;
7989 }
7990
7991 // accumulate the result
7992 coeffs.task(dest, &nodeT::accumulate2, result, coeffs, dest, TaskAttributes::hipri());
7993 }
7994
7995
7996
7997
7998 /// Returns the maximum local depth of the tree ... no communications.
7999 std::size_t max_local_depth() const;
8000
8001
8002 /// Returns the maximum depth of the tree ... collective ... global sum/broadcast
8003 std::size_t max_depth() const;
8004
8005 /// Returns the max number of nodes on a processor
8006 std::size_t max_nodes() const;
8007
8008 /// Returns the min number of nodes on a processor
8009 std::size_t min_nodes() const;
8010
8011 /// Returns the size of the tree structure of the function ... collective global sum
8012 std::size_t tree_size() const;
8013
8014 /// Returns the number of coefficients in the function for each rank
8015 std::size_t size_local() const;
8016
8017 /// Returns the number of coefficients in the function ... collective global sum
8018 std::size_t size() const;
8019
8020 /// Returns the number of coefficients in the function for this MPI rank
8021 std::size_t nCoeff_local() const;
8022
8023 /// Returns the number of coefficients in the function ... collective global sum
8024 std::size_t nCoeff() const;
8025
8026 /// Returns the number of coefficients in the function ... collective global sum
8027 std::size_t real_size() const;
8028
8029 /// print tree size and size
8030 void print_size(const std::string name) const;
8031
8032 /// print the number of configurations per node
8033 void print_stats() const;
8034
8035 /// In-place scale by a constant
8036 void scale_inplace(const T q, bool fence);
8037
8038 /// Out-of-place scale by a constant
8039 template <typename Q, typename F>
8040 void scale_oop(const Q q, const FunctionImpl<F,NDIM>& f, bool fence) {
8041 typedef typename FunctionImpl<F,NDIM>::nodeT fnodeT;
8042 typedef typename FunctionImpl<F,NDIM>::dcT fdcT;
8043 typename fdcT::const_iterator end = f.coeffs.end();
8044 for (typename fdcT::const_iterator it=f.coeffs.begin(); it!=end; ++it) {
8045 const keyT& key = it->first;
8046 const fnodeT& node = it->second;
8047
8048 coeffT c = node.has_coeff() ? coeffT(node.coeff()*q) : coeffT();
8049
8050 // the result inherits f's tree state, so it has to inherit the norms that
8051 // state promises too
8052 nodeT scaled(c, node.get_norm_tree(), node.get_dnorm_tree(),
8053 node.get_snorm(), node.get_dnorm(), node.has_children());
8054 scaled.scale_norms(std::abs(q));
8055 coeffs.replace(key,scaled);
8056 }
8057 if (fence)
8058 world.gop.fence();
8059 }
8060
8061 /// Hash a pointer to \c FunctionImpl
8062
8063 /// \param[in] impl pointer to a FunctionImpl
8064 /// \return The hash.
8065 inline friend hashT hash_value(const FunctionImpl<T,NDIM>* pimpl) {
8066 hashT seed = hash_value(pimpl->id().get_world_id());
8067 detail::combine_hash(seed, hash_value(pimpl->id().get_obj_id()));
8068 return seed;
8069 }
8070
8071 /// Hash a shared_ptr to \c FunctionImpl
8072
8073 /// \param[in] impl pointer to a FunctionImpl
8074 /// \return The hash.
8075 inline friend hashT hash_value(const std::shared_ptr<FunctionImpl<T,NDIM>> impl) {
8076 return hash_value(impl.get());
8077 }
8078 };
8079
8080 namespace archive {
8081 template <class Archive, class T, std::size_t NDIM>
8082 struct ArchiveLoadImpl<Archive,const FunctionImpl<T,NDIM>*> {
8083 static void load(const Archive& ar, const FunctionImpl<T,NDIM>*& ptr) {
8084 bool exists=false;
8085 ar & exists;
8086 if (exists) {
8087 uniqueidT id;
8088 ar & id;
8089 World* world = World::world_from_id(id.get_world_id());
8090 MADNESS_ASSERT(world);
8091 auto ptr_opt = world->ptr_from_id< WorldObject< FunctionImpl<T,NDIM> > >(id);
8092 if (!ptr_opt)
8093 MADNESS_EXCEPTION("FunctionImpl: remote operation attempting to use a locally uninitialized object",0);
8094 ptr = static_cast< const FunctionImpl<T,NDIM>*>(*ptr_opt);
8095 if (!ptr)
8096 MADNESS_EXCEPTION("FunctionImpl: remote operation attempting to use an unregistered object",0);
8097 } else {
8098 ptr=nullptr;
8099 }
8100 }
8101 };
8102
8103 template <class Archive, class T, std::size_t NDIM>
8104 struct ArchiveStoreImpl<Archive,const FunctionImpl<T,NDIM>*> {
8105 static void store(const Archive& ar, const FunctionImpl<T,NDIM>*const& ptr) {
8106 bool exists=(ptr) ? true : false;
8107 ar & exists;
8108 if (exists) ar & ptr->id();
8109 }
8110 };
8111
8112 template <class Archive, class T, std::size_t NDIM>
8113 struct ArchiveLoadImpl<Archive, FunctionImpl<T,NDIM>*> {
8114 static void load(const Archive& ar, FunctionImpl<T,NDIM>*& ptr) {
8115 bool exists=false;
8116 ar & exists;
8117 if (exists) {
8118 uniqueidT id;
8119 ar & id;
8120 World* world = World::world_from_id(id.get_world_id());
8121 MADNESS_ASSERT(world);
8122 auto ptr_opt = world->ptr_from_id< WorldObject< FunctionImpl<T,NDIM> > >(id);
8123 if (!ptr_opt)
8124 MADNESS_EXCEPTION("FunctionImpl: remote operation attempting to use a locally uninitialized object",0);
8125 ptr = static_cast< FunctionImpl<T,NDIM>*>(*ptr_opt);
8126 if (!ptr) {
8127 auto ids=world->get_object_ids();
8128 print(world->get_world_ids());
8129 MADNESS_EXCEPTION("FunctionImpl: remote operation attempting to use an unregistered object",0);
8130 }
8131 } else {
8132 ptr=nullptr;
8133 }
8134 }
8135 };
8136
8137 template <class Archive, class T, std::size_t NDIM>
8138 struct ArchiveStoreImpl<Archive, FunctionImpl<T,NDIM>*> {
8139 static void store(const Archive& ar, FunctionImpl<T,NDIM>*const& ptr) {
8140 bool exists=(ptr) ? true : false;
8141 ar & exists;
8142 if (exists) ar & ptr->id();
8143 // ar & ptr->id();
8144 }
8145 };
8146
8147 template <class Archive, class T, std::size_t NDIM>
8148 struct ArchiveLoadImpl<Archive, std::shared_ptr<const FunctionImpl<T,NDIM> > > {
8149 static void load(const Archive& ar, std::shared_ptr<const FunctionImpl<T,NDIM> >& ptr) {
8150 const FunctionImpl<T,NDIM>* f = nullptr;
8152 ptr.reset(f, [] (const FunctionImpl<T,NDIM> *p_) -> void {});
8153 }
8154 };
8155
8156 template <class Archive, class T, std::size_t NDIM>
8157 struct ArchiveStoreImpl<Archive, std::shared_ptr<const FunctionImpl<T,NDIM> > > {
8158 static void store(const Archive& ar, const std::shared_ptr<const FunctionImpl<T,NDIM> >& ptr) {
8160 }
8161 };
8162
8163 template <class Archive, class T, std::size_t NDIM>
8164 struct ArchiveLoadImpl<Archive, std::shared_ptr<FunctionImpl<T,NDIM> > > {
8165 static void load(const Archive& ar, std::shared_ptr<FunctionImpl<T,NDIM> >& ptr) {
8166 FunctionImpl<T,NDIM>* f = nullptr;
8168 ptr.reset(f, [] (FunctionImpl<T,NDIM> *p_) -> void {});
8169 }
8170 };
8171
8172 template <class Archive, class T, std::size_t NDIM>
8173 struct ArchiveStoreImpl<Archive, std::shared_ptr<FunctionImpl<T,NDIM> > > {
8174 static void store(const Archive& ar, const std::shared_ptr<FunctionImpl<T,NDIM> >& ptr) {
8176 }
8177 };
8178 }
8179
8180}
8181
8182#endif // MADNESS_MRA_FUNCIMPL_H__INCLUDED
double w(double t, double eps)
Definition DKops.h:22
double q(double t)
Definition DKops.h:18
This header should include pretty much everything needed for the parallel runtime.
An integer with atomic set, get, read+increment, read+decrement, and decrement+test operations.
Definition atomicint.h:126
long dim(int i) const
Returns the size of dimension i.
Definition basetensor.h:147
long ndim() const
Returns the number of dimensions in the tensor.
Definition basetensor.h:144
long size() const
Returns the number of elements in the tensor.
Definition basetensor.h:138
Definition displacements.h:646
a class to track where relevant (parent) coeffs are
Definition funcimpl.h:887
const keyT & key() const
const reference to the key
Definition funcimpl.h:937
CoeffTracker(const CoeffTracker &other, const datumT &datum)
ctor with a pair<keyT,nodeT>
Definition funcimpl.h:917
const LeafStatus & is_leaf() const
const reference to is_leaf flag
Definition funcimpl.h:961
const implT * impl
Definition funcimpl.h:896
CoeffTracker & operator=(const CoeffTracker &other)=default
LeafStatus
Definition funcimpl.h:893
@ yes
Definition funcimpl.h:893
@ no
Definition funcimpl.h:893
@ unknown
Definition funcimpl.h:893
CoeffTracker(const CoeffTracker &other)
copy ctor
Definition funcimpl.h:925
double dnorm(const keyT &key) const
Definition funcimpl.h:954
coeffT coeff_
Definition funcimpl.h:902
const implT * get_impl() const
const reference to impl
Definition funcimpl.h:931
const coeffT & coeff() const
Definition funcimpl.h:934
keyT key_
Definition funcimpl.h:898
double dnorm_
Definition funcimpl.h:904
CoeffTracker(const implT *impl)
the initial ctor making the root key
Definition funcimpl.h:912
void serialize(const Archive &ar)
serialization
Definition funcimpl.h:1013
Future< CoeffTracker > activate() const
find the coefficients
Definition funcimpl.h:990
CoeffTracker()
default ctor
Definition funcimpl.h:909
GenTensor< T > coeffT
Definition funcimpl.h:891
CoeffTracker make_child(const keyT &child) const
make a child of this, ignoring the coeffs
Definition funcimpl.h:964
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:889
std::pair< Key< NDIM >, ShallowNode< T, NDIM > > datumT
Definition funcimpl.h:892
CoeffTracker forward_ctor(const CoeffTracker &other, const datumT &datum) const
taskq-compatible forwarding to the ctor
Definition funcimpl.h:1007
LeafStatus is_leaf_
Definition funcimpl.h:900
coeffT coeff(const keyT &key) const
return the coefficients belonging to the passed-in key
Definition funcimpl.h:945
Key< NDIM > keyT
Definition funcimpl.h:890
CompositeFunctorInterface implements a wrapper of holding several functions and functors.
Definition function_interface.h:172
Definition worldhashmap.h:396
Hash_private::HashAccessor< const hashT, entryT::READLOCK > const_accessor
Definition worldhashmap.h:405
size_t size() const
Definition worldhashmap.h:560
Hash_private::HashAccessor< hashT, entryT::WRITELOCK > accessor
Definition worldhashmap.h:404
iterator begin()
Definition worldhashmap.h:571
iterator end()
Definition worldhashmap.h:583
Hash_private::HashIterator< hashT > iterator
Definition worldhashmap.h:402
iterator find(const keyT &key)
Definition worldhashmap.h:524
Tri-diagonal operator traversing tree primarily for derivative operator.
Definition derivative.h:73
Holds displacements for applying operators to avoid replicating for all operators.
Definition displacements.h:80
const std::vector< Key< NDIM > > & get_disp(Level n, const array_of_bools< NDIM > &kernel_lattice_sum_axes)
Definition displacements.h:352
FunctionCommonData holds all Function data common for given k.
Definition function_common_data.h:52
Tensor< double > quad_phit
transpose of quad_phi
Definition function_common_data.h:102
Tensor< double > quad_phiw
quad_phiw(i,j) = at x[i] value of w[i]*phi[j]
Definition function_common_data.h:103
std::vector< long > vk
(k,...) used to initialize Tensors
Definition function_common_data.h:93
std::vector< Slice > s0
s[0] in each dimension to get scaling coeff
Definition function_common_data.h:91
static const FunctionCommonData< T, NDIM > & get(int k)
Definition function_common_data.h:111
static void _init_quadrature(int k, int npt, Tensor< double > &quad_x, Tensor< double > &quad_w, Tensor< double > &quad_phi, Tensor< double > &quad_phiw, Tensor< double > &quad_phit)
Initialize the quadrature information.
Definition mraimpl.h:93
collect common functionality does not need to be member function of funcimpl
Definition function_common_data.h:135
const FunctionCommonData< T, NDIM > & cdata
Definition function_common_data.h:138
GenTensor< T > coeffs2values(const Key< NDIM > &key, const GenTensor< T > &coeff) const
Definition function_common_data.h:142
Tensor< T > values2coeffs(const Key< NDIM > &key, const Tensor< T > &values) const
Definition function_common_data.h:155
FunctionDefaults holds default paramaters as static class members.
Definition funcdefaults.h:101
static const double & get_thresh()
Returns the default threshold.
Definition funcdefaults.h:183
static double get_cell_volume()
Returns the volume of the user cell.
Definition funcdefaults.h:410
static int get_max_refine_level()
Gets the default maximum adaptive refinement level.
Definition funcdefaults.h:220
static bool get_refine()
Gets the default adaptive refinement flag.
Definition funcdefaults.h:248
static const Tensor< double > & get_cell_width()
Returns the width of each user cell dimension.
Definition funcdefaults.h:390
static bool get_apply_randomize()
Gets the random load balancing for integral operators flag.
Definition funcdefaults.h:294
static const Tensor< double > & get_cell()
Gets the user cell for the simulation.
Definition funcdefaults.h:352
FunctionFactory implements the named-parameter idiom for Function.
Definition function_factory.h:86
bool _refine
Definition function_factory.h:99
bool _empty
Definition function_factory.h:100
bool _fence
Definition function_factory.h:103
Abstract base class interface required for functors used as input to Functions.
Definition function_interface.h:68
double operator()(double a, double b) const
Definition funcimpl.h:6326
const opT * func
Definition funcimpl.h:6302
Tensor< double > qx
Definition funcimpl.h:6304
double operator()(typename dcT::const_iterator &it) const
Definition funcimpl.h:6317
void serialize(const Archive &ar)
Definition funcimpl.h:6331
do_err_box(const implT *impl, const opT *func, int npt, const Tensor< double > &qx, const Tensor< double > &quad_phit, const Tensor< double > &quad_phiw)
Definition funcimpl.h:6310
int npt
Definition funcimpl.h:6303
Tensor< double > quad_phiw
Definition funcimpl.h:6306
const implT * impl
Definition funcimpl.h:6301
Tensor< double > quad_phit
Definition funcimpl.h:6305
do_err_box(const do_err_box &e)
Definition funcimpl.h:6314
FunctionImpl holds all Function state to facilitate shallow copy semantics.
Definition funcimpl.h:1043
std::tuple< std::set< Key< NDIM > >, std::map< Key< CDIM >, double > > get_contraction_node_lists(const std::size_t n, const std::array< int, CDIM > &v) const
for contraction two functions f(x,z) = \int g(x,y) h(y,z) dy
Definition funcimpl.h:7079
void copy_coeffs(const FunctionImpl< Q, NDIM > &other, bool fence)
Copy coeffs from other into self.
Definition funcimpl.h:1301
bool is_nonstandard() const
Definition mraimpl.h:275
void insert_serialized_coeffs(std::vector< unsigned char > &v)
insert coeffs from vector archive into this
Definition funcimpl.h:1357
void make_Vphi_ij_u(const opT &leaf_op, const FunctionImpl< T, NDIM > *u, const FunctionImpl< T, LDIM > *i_func=nullptr, const FunctionImpl< T, LDIM > *j_func=nullptr, bool fence=true, double target_precision=0.0, int oversampling=1)
build this = u * [i(1)] * [j(2)] adaptively using Vphi_ij_u_op_NS
Definition funcimpl.h:4925
T eval_cube(Level n, coordT &x, const tensorT &c) const
Definition mraimpl.h:2171
void partial_inner_contract(const FunctionImpl< Q, LDIM > *g, const FunctionImpl< R, KDIM > *h, const std::array< int, CDIM > v1, const std::array< int, CDIM > v2, const Key< NDIM > &key, const std::list< Key< CDIM > > &j_key_list)
Definition funcimpl.h:7247
AtomicInt large
Definition funcimpl.h:1158
Timer timer_target_driven
Definition funcimpl.h:1156
void binaryXX(const FunctionImpl< L, NDIM > *left, const FunctionImpl< R, NDIM > *right, const opT &op, bool fence)
Definition funcimpl.h:3494
void do_apply(const opT *op, const keyT &key, const Tensor< R > &c)
apply an operator on the coeffs c (at node key)
Definition funcimpl.h:5440
void do_print_tree_graphviz(const keyT &key, std::ostream &os, Level maxlevel) const
Functor for the do_print_tree method (using GraphViz).
Definition mraimpl.h:2925
void add_keys_to_map(mapT *map, int index) const
Adds keys to union of local keys with specified index.
Definition funcimpl.h:6656
void change_tensor_type1(const TensorArgs &targs, bool fence)
change the tensor type of the coefficients in the FunctionNode
Definition mraimpl.h:1206
void gaxpy_ext_recursive(const keyT &key, const FunctionImpl< L, NDIM > *left, Tensor< L > lcin, tensorT c, T(*f)(const coordT &), T alpha, T beta, double tol, bool below_leaf)
Definition funcimpl.h:7656
int initial_level
Definition funcimpl.h:1072
int max_refine_level
Definition funcimpl.h:1076
double do_apply_kernel3(const opT *op, const GenTensor< R > &coeff, const do_op_args< OPDIM > &args, const TensorArgs &apply_targs)
same as do_apply_kernel2, but use low rank tensors as input and low rank tensors as output
Definition funcimpl.h:5398
void hartree_product(const std::vector< std::shared_ptr< FunctionImpl< T, LDIM > > > p1, const std::vector< std::shared_ptr< FunctionImpl< T, LDIM > > > p2, const leaf_opT &leaf_op, bool fence)
given two functions of LDIM, perform the Hartree/Kronecker/outer product
Definition funcimpl.h:4035
void traverse_tree(const coeff_opT &coeff_op, const apply_opT &apply_op, const keyT &key) const
Definition funcimpl.h:4005
void do_square_inplace(const keyT &key)
int special_level
Definition funcimpl.h:1073
void do_apply_kernel(const opT *op, const Tensor< R > &c, const do_op_args< OPDIM > &args)
for fine-grain parallelism: call the apply method of an operator in a separate task
Definition funcimpl.h:5332
compressT compress_op(const keyT &key, const std::vector< Future< compressT > > &v, bool nonstandard)
calculate the wavelet coefficients using the sum coefficients of all child nodes
Definition mraimpl.h:1783
double errsq_local(const opT &func) const
Returns the sum of squares of errors from local info ... no comms.
Definition funcimpl.h:6338
WorldContainer< keyT, nodeT > dcT
Definition funcimpl.h:1055
void evaldepthpt(const Vector< double, NDIM > &xin, const keyT &keyin, const typename Future< Level >::remote_refT &ref)
Get the depth of the tree at a point in simulation coordinates.
Definition mraimpl.h:3196
void scale_inplace(const T q, bool fence)
In-place scale by a constant.
Definition mraimpl.h:3368
void gaxpy_oop_reconstructed(const double alpha, const implT &f, const double beta, const implT &g, const bool fence)
perform: this= alpha*f + beta*g, invoked by result
Definition mraimpl.h:225
void unary_op_coeff_inplace(const opT &op, bool fence)
Definition funcimpl.h:2304
World & world
Definition funcimpl.h:1062
void apply_1d_realspace_push_op(const archive::archive_ptr< const opT > &pop, int axis, const keyT &key, const Tensor< R > &c)
Definition funcimpl.h:4073
bool is_redundant() const
Returns true if the function is redundant.
Definition mraimpl.h:264
FunctionNode< T, NDIM > nodeT
Definition funcimpl.h:1053
std::size_t nCoeff_local() const
Returns the number of coefficients in the function for this MPI rank.
Definition mraimpl.h:2056
void print_size(const std::string name) const
print tree size and size
Definition mraimpl.h:2075
FunctionImpl(const FunctionImpl< T, NDIM > &p)
void print_info() const
Prints summary of data distribution.
Definition mraimpl.h:934
void abs_inplace(bool fence)
Definition mraimpl.h:3380
void binaryXXa(const keyT &key, const FunctionImpl< L, NDIM > *left, const Tensor< L > &lcin, const FunctionImpl< R, NDIM > *right, const Tensor< R > &rcin, const opT &op)
Definition funcimpl.h:3363
void print_timer() const
Definition mraimpl.h:369
void evalR(const Vector< double, NDIM > &xin, const keyT &keyin, const typename Future< long >::remote_refT &ref)
Get the rank of leaf box of the tree at a point in simulation coordinates.
Definition mraimpl.h:3238
const FunctionCommonData< T, NDIM > & cdata
Definition funcimpl.h:1082
void do_print_grid(const std::string filename, const std::vector< keyT > &keys) const
print the grid in xyz format
Definition mraimpl.h:596
void mulXXa(const keyT &key, const FunctionImpl< L, NDIM > *left, const Tensor< L > &lcin, const FunctionImpl< R, NDIM > *right, const Tensor< R > &rcin, double tol)
Definition funcimpl.h:3277
T inner_ij_u_eri(const opT &leaf_op, const FunctionImpl< T, NDIM > *u, const FunctionImpl< T, LDIM > *i_func, const FunctionImpl< T, LDIM > *j_func, const FunctionImpl< T, NDIM > *eri, double target_precision=0.0, int oversampling=1) const
compute <eri(1,2) | u(1,2) * [i(1)] * [j(2)]> adaptively using Vphi_ij_u_op_NS
Definition funcimpl.h:4968
bool has_coefficients_on_leaves_only() const
Returns true if only the leaves of this tree carry its coefficients.
Definition mraimpl.h:290
int get_truncate_mode() const
Definition funcimpl.h:1942
const std::vector< Vector< double, NDIM > > & get_special_points() const
Definition funcimpl.h:1067
Future< compressT > compress_spawn(const keyT &key, bool nonstandard, bool keepleaves, bool redundant1)
Invoked on node where key is local.
Definition mraimpl.h:3527
std::size_t nCoeff() const
Returns the number of coefficients in the function ... collective global sum.
Definition mraimpl.h:2066
coeffT unfilter(const coeffT &s) const
Definition mraimpl.h:1296
double vol_nsphere(int n, double R)
Definition funcimpl.h:5428
keyT neighbor_in_volume(const keyT &key, const keyT &disp) const
Returns key of general neighbor that resides in-volume.
Definition mraimpl.h:3499
void compress(const TreeState newstate, bool fence)
compress the wave function
Definition mraimpl.h:1616
void do_dirac_convolution(FunctionImpl< T, LDIM > *f, bool fence) const
Definition funcimpl.h:2387
return world taskq reduce(rangeT(coeffs.begin(), coeffs.end()), do_inner_local< R >(&g, leaves_only))
Future< bool > truncate_spawn(const keyT &key, double tol)
Returns true if after truncation this node has coefficients.
Definition mraimpl.h:2770
void print_type_in_compilation_error(R &&)
Definition funcimpl.h:6961
Future< double > norm_tree_spawn(const keyT &key)
Definition mraimpl.h:1686
std::vector< keyT > local_leaf_keys() const
return the keys of the local leaf boxes
Definition mraimpl.h:570
MADNESS_ASSERT(this->is_redundant()==g.is_redundant())
void do_print_tree(const keyT &key, std::ostream &os, Level maxlevel) const
Functor for the do_print_tree method.
Definition mraimpl.h:2843
void vtransform(const std::vector< std::shared_ptr< FunctionImpl< R, NDIM > > > &vright, const Tensor< Q > &c, const std::vector< std::shared_ptr< FunctionImpl< T, NDIM > > > &vleft, double tol, bool fence)
Definition funcimpl.h:3105
void unset_functor()
Definition mraimpl.h:324
void refine_spawn(const opT &op, const keyT &key)
Definition funcimpl.h:5157
void apply_1d_realspace_push(const opT &op, const FunctionImpl< R, NDIM > *f, int axis, bool fence)
Definition funcimpl.h:4124
void set_truncate_mode(int mode)
Definition funcimpl.h:1943
void do_print_plane(const std::string filename, std::vector< Tensor< double > > plotinfo, const int xaxis, const int yaxis, const coordT el2)
print the MRA structure
Definition mraimpl.h:511
std::pair< Key< NDIM >, ShallowNode< T, NDIM > > find_datum(keyT key) const
return the a std::pair<key, node>, which MUST exist
Definition mraimpl.h:1072
dcT & get_coeffs()
Definition mraimpl.h:358
void set_functor(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > functor1)
Definition mraimpl.h:305
std::enable_if< NDIM==FDIM >::type read_grid2(const std::string gridfile, std::shared_ptr< FunctionFunctorInterface< double, NDIM > > vnuc_functor)
read data from a grid
Definition funcimpl.h:1836
bool verify_tree_state_local() const
check that the tree state and the coeffs are consistent
Definition mraimpl.h:171
const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > & get_pmap() const
Tensor< Q > fcube_for_mul(const keyT &child, const keyT &parent, const Tensor< Q > &coeff) const
Compute the function values for multiplication.
Definition funcimpl.h:2151
Timer timer_filter
Definition funcimpl.h:1154
void sock_it_to_me(const keyT &key, const RemoteReference< FutureImpl< std::pair< keyT, coeffT > > > &ref) const
Walk up the tree returning pair(key,node) for first node with coefficients.
Definition mraimpl.h:2983
void recursive_apply(opT &apply_op, const implT *fimpl, implT *rimpl, const bool fence)
traverse an existing tree and apply an operator
Definition funcimpl.h:6157
double get_thresh() const
Definition mraimpl.h:340
void trickle_down(bool fence)
sum all the contributions from all scales after applying an operator in mod-NS form
Definition mraimpl.h:1463
bool autorefine
Definition funcimpl.h:1078
bool halo_enabled() const
Is a neighbor halo staged on this function?
Definition funcimpl.h:1105
void set_autorefine(bool value)
Definition mraimpl.h:349
tensorT filter(const tensorT &s) const
Transform sum coefficients at level n to sums+differences at level n-1.
Definition mraimpl.h:1259
void chop_at_level(const int n, const bool fence=true)
remove all nodes with level higher than n
Definition mraimpl.h:1222
void unaryXXvalues(const FunctionImpl< Q, NDIM > *func, const opT &op, bool fence)
Definition funcimpl.h:3521
void partial_inner(const FunctionImpl< Q, LDIM > &g, const FunctionImpl< R, KDIM > &h, const std::array< int, CDIM > v1, const std::array< int, CDIM > v2)
invoked by result
Definition funcimpl.h:6977
TreeState tree_state
Definition funcimpl.h:1085
void print_tree_json(std::ostream &os=std::cout, Level maxlevel=10000) const
Definition mraimpl.h:2863
coeffT parent_to_child_NS(const keyT &child, const keyT &parent, const coeffT &coeff) const
Directly project parent NS coeffs to child NS coeffs.
Definition mraimpl.h:808
void copy_coeffs_different_world(const FunctionImpl< Q, NDIM > &other)
Copy coefficients from other funcimpl with possibly different world and on a different node.
Definition funcimpl.h:1311
void mapdim(const implT &f, const std::vector< long > &map, bool fence)
Permute the dimensions of f according to map, result on this.
Definition mraimpl.h:1164
bool is_compressed() const
Returns true if the function is compressed.
Definition mraimpl.h:252
void receive_halo(const std::vector< std::pair< keyT, coeffT > > &buf) const
Insert pushed neighbor nodes into the halo; runs as a task, concurrently with other pushes.
Definition funcimpl.h:1125
Vector< double, NDIM > coordT
Definition funcimpl.h:1057
void apply(opT &op, const FunctionImpl< R, NDIM > &f, bool fence)
apply an operator on f to return this
Definition funcimpl.h:5661
Tensor< T > tensorT
Definition funcimpl.h:1050
void mirror(const implT &f, const std::vector< long > &mirror, bool fence)
mirror the dimensions of f according to map, result on this
Definition mraimpl.h:1173
T inner_adaptive_recursive(keyT key, const tensorT &c, const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > f, const bool leaf_refine, T old_inner=T(0)) const
Definition funcimpl.h:7579
void store(Archive &ar)
Definition funcimpl.h:1489
void do_binary_op(const keyT &key, const Tensor< L > &left, const std::pair< keyT, Tensor< R > > &arg, const opT &op)
Functor for the binary_op method.
Definition funcimpl.h:2253
void gaxpy_ext(const FunctionImpl< L, NDIM > *left, T(*f)(const coordT &), T alpha, T beta, double tol, bool fence)
Definition funcimpl.h:7743
void accumulate_trees(FunctionImpl< Q, NDIM > &result, const R alpha, const bool fence=true) const
merge the trees of this and other, while multiplying them with the alpha or beta, resp
Definition funcimpl.h:1410
void print_stats() const
print the number of configurations per node
Definition mraimpl.h:2115
void eval_local_only(const Vector< double, NDIM > *xin, std::size_t npt, Level maxlevel, std::pair< bool, T > *results)
Definition mraimpl.h:3106
void broaden(const array_of_bools< NDIM > &is_periodic, bool fence)
Definition mraimpl.h:1412
coeffT truncate_reconstructed_op(const keyT &key, const std::vector< Future< coeffT > > &v, const double tol)
given the sum coefficients of all children, truncate or not
Definition mraimpl.h:1733
static void tnorm(const GenTensor< T > &t, double *lo, double *hi)
Definition mraimpl.h:3292
void refine_op(const opT &op, const keyT &key)
Definition funcimpl.h:5132
static Tensor< TENSOR_RESULT_TYPE(T, R) > inner_local(const std::vector< const FunctionImpl< T, NDIM > * > &left, const std::vector< const FunctionImpl< R, NDIM > * > &right, bool sym)
Definition funcimpl.h:6857
void fcube(const keyT &key, const FunctionFunctorInterface< T, NDIM > &f, const Tensor< double > &qx, tensorT &fval) const
Evaluate function at quadrature points in the specified box.
Definition mraimpl.h:2605
Timer timer_change_tensor_type
Definition funcimpl.h:1152
void forward_do_diff1(const DerivativeBase< T, NDIM > *D, const implT *f, const keyT &key, const std::pair< keyT, coeffT > &left, const std::pair< keyT, coeffT > &center, const std::pair< keyT, coeffT > &right)
Definition mraimpl.h:1025
std::vector< Slice > child_patch(const keyT &child) const
Returns patch referring to coeffs of child in parent box.
Definition mraimpl.h:797
void print_tree_graphviz(std::ostream &os=std::cout, Level maxlevel=10000) const
Definition mraimpl.h:2916
void project(const implT &old, bool fence)
Projects old function into new basis (only in reconstructed form).
Definition mraimpl.h:1356
void set_tree_state(const TreeState &state)
Definition funcimpl.h:1539
std::size_t min_nodes() const
Returns the min number of nodes on a processor.
Definition mraimpl.h:2007
void copy_coeffs_same_world(const FunctionImpl< Q, NDIM > &other, bool fence)
Copy coeffs from other into self.
Definition funcimpl.h:1364
std::shared_ptr< FunctionFunctorInterface< T, NDIM > > functor
Definition funcimpl.h:1084
Timer timer_compress_svd
Definition funcimpl.h:1155
Tensor< TENSOR_RESULT_TYPE(T, R)> mul(const Tensor< T > &c1, const Tensor< R > &c2, const int npt, const keyT &key) const
multiply the values of two coefficient tensors using a custom number of grid points
Definition funcimpl.h:2226
void make_redundant(const bool fence)
convert this to redundant, i.e. have sum coefficients on all levels
Definition mraimpl.h:1644
void load(Archive &ar)
Definition funcimpl.h:1471
std::size_t max_nodes() const
Returns the max number of nodes on a processor.
Definition mraimpl.h:1998
T inner_ext_local(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > f, const bool leaf_refine) const
Definition funcimpl.h:7554
coeffT upsample(const keyT &key, const coeffT &coeff) const
upsample the sum coefficients of level 1 to sum coeffs on level n+1
Definition mraimpl.h:1338
TensorArgs targs
Definition funcimpl.h:1080
void flo_unary_op_node_inplace(const opT &op, bool fence)
Definition funcimpl.h:2416
std::size_t size_local() const
Returns the number of coefficients in the function for each rank.
Definition mraimpl.h:2025
GenTensor< Q > values2coeffs(const keyT &key, const GenTensor< Q > &values) const
Definition funcimpl.h:2130
void plot_cube_kernel(archive::archive_ptr< Tensor< T > > ptr, const keyT &key, const coordT &plotlo, const coordT &plothi, const std::vector< long > &npt, bool eval_refine) const
Definition mraimpl.h:3602
T trace_local() const
Returns int(f(x),x) in local volume.
Definition mraimpl.h:3422
void print_grid(const std::string filename) const
Definition mraimpl.h:554
void replicate_on_hosts(bool fence=true)
Definition funcimpl.h:1280
bool get_autorefine() const
Definition mraimpl.h:346
int k
Definition funcimpl.h:1070
void vtransform_doit(const std::shared_ptr< FunctionImpl< R, NDIM > > &right, const Tensor< Q > &c, const std::vector< std::shared_ptr< FunctionImpl< T, NDIM > > > &vleft, double tol)
Definition funcimpl.h:2954
MADNESS_CHECK(this->is_reconstructed())
void phi_for_mul(Level np, Translation lp, Level nc, Translation lc, Tensor< double > &phi) const
Compute the Legendre scaling functions for multiplication.
Definition mraimpl.h:3390
Future< std::pair< keyT, coeffT > > find_me(const keyT &key) const
find_me. Called by diff_bdry to get coefficients of boundary function
Definition mraimpl.h:3514
TensorType get_tensor_type() const
void do_project_out(const coeffT &fcoeff, const std::pair< keyT, coeffT > gpair, const keyT &gkey, const Key< NDIM > &dest, const int dim) const
Definition funcimpl.h:7963
void remove_leaf_coefficients(const bool fence)
Definition mraimpl.h:1638
void insert_zero_down_to_initial_level(const keyT &key)
Initialize nodes to zero function at initial_level of refinement.
Definition mraimpl.h:2739
void do_diff1(const DerivativeBase< T, NDIM > *D, const implT *f, const keyT &key, const std::pair< keyT, coeffT > &left, const std::pair< keyT, coeffT > &center, const std::pair< keyT, coeffT > &right)
Definition mraimpl.h:1036
typedef TENSOR_RESULT_TYPE(T, R) resultT
void unary_op_node_inplace(const opT &op, bool fence)
Definition funcimpl.h:2325
T inner_adaptive_local(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > f, const bool leaf_refine) const
Definition funcimpl.h:7565
void do_print_tree_json(const keyT &key, std::multimap< Level, std::tuple< tranT, std::string > > &data, Level maxlevel) const
Functor for the do_print_tree_json method.
Definition mraimpl.h:2894
std::multimap< Key< FDIM >, std::list< Key< CDIM > > > recur_down_for_contraction_map(const keyT &key, const nodeT &node, const std::array< int, CDIM > &v_this, const std::array< int, CDIM > &v_other, const std::set< Key< ODIM > > &ij_other_list, const std::map< Key< CDIM >, double > &j_other_list, bool this_first, const double thresh)
make a map of all nodes that will contribute to a partial inner product
Definition funcimpl.h:7134
std::shared_ptr< FunctionImpl< T, NDIM > > pimplT
Definition funcimpl.h:1049
TENSOR_RESULT_TYPE(T, R) dot_local(const FunctionImpl< R
Returns the dot product ASSUMING same distribution.
void finalize_sum()
after summing up we need to do some cleanup;
Definition mraimpl.h:1953
std::enable_if< NDIM==FDIM >::type read_grid(const std::string keyfile, const std::string gridfile, std::shared_ptr< FunctionFunctorInterface< double, NDIM > > vnuc_functor)
read data from a grid
Definition funcimpl.h:1729
dcT coeffs
Definition funcimpl.h:1087
bool exists_and_is_leaf(const keyT &key) const
Definition mraimpl.h:1382
static std::complex< Real > conj(const std::complex< Real > &x)
Definition funcimpl.h:6851
void make_Vphi(const opT &leaf_op, const bool fence=true)
assemble the function V*phi using V and phi given from the functor
Definition funcimpl.h:4820
void unaryXX(const FunctionImpl< Q, NDIM > *func, const opT &op, bool fence)
Definition funcimpl.h:3508
std::vector< std::pair< int, const coeffT * > > mapvecT
Definition funcimpl.h:6650
void project_out(FunctionImpl< T, NDIM-LDIM > *result, const FunctionImpl< T, LDIM > *gimpl, const int dim, const bool fence)
project the low-dim function g on the hi-dim function f: result(x) = <this(x,y) | g(y)>
Definition funcimpl.h:7759
void verify_tree() const
Verify tree is properly constructed ... global synchronization involved.
Definition mraimpl.h:113
void do_square_inplace2(const keyT &parent, const keyT &child, const tensorT &parent_coeff)
void gaxpy_inplace_reconstructed(const T &alpha, const FunctionImpl< Q, NDIM > &g, const R &beta, const bool fence)
Definition funcimpl.h:1378
std::vector< std::pair< bool, T > > eval_local_only(const std::vector< Vector< double, NDIM > > &xin, Level maxlevel)
Definition mraimpl.h:3189
void undo_replicate(bool fence=true)
Definition funcimpl.h:1285
void set_tensor_args(const TensorArgs &t)
Definition mraimpl.h:337
GenTensor< Q > fcube_for_mul(const keyT &child, const keyT &parent, const GenTensor< Q > &coeff) const
Compute the function values for multiplication.
Definition funcimpl.h:2179
Range< typename dcT::const_iterator > rangeT
Definition funcimpl.h:6441
std::size_t real_size() const
Returns the number of coefficients in the function ... collective global sum.
Definition mraimpl.h:2043
bool exists_and_has_children(const keyT &key) const
Definition mraimpl.h:1377
void sum_down_spawn(const keyT &key, const coeffT &s)
is this the same as trickle_down() ?
Definition mraimpl.h:977
void multi_to_multi_op_values(const opT &op, const std::vector< implT * > &vin, std::vector< implT * > &vout, const bool fence=true)
Inplace operate on many functions (impl's) with an operator within a certain box.
Definition funcimpl.h:3076
long box_interior[1000]
Definition funcimpl.h:3553
std::atomic< ConcurrentHashMap< keyT, coeffT > * > neighbor_halo_
Definition funcimpl.h:1098
keyT neighbor(const keyT &key, const keyT &disp, const array_of_bools< NDIM > &is_periodic) const
Returns key of general neighbor enforcing BC.
Definition mraimpl.h:3484
GenTensor< Q > NS_fcube_for_mul(const keyT &child, const keyT &parent, const GenTensor< Q > &coeff, const bool s_only) const
Compute the function values for multiplication.
Definition funcimpl.h:2049
rangeT range(coeffs.begin(), coeffs.end())
void norm_tree(bool fence)
compute for each FunctionNode the norm of the function inside that node
Definition mraimpl.h:1663
void gaxpy_inplace(const T &alpha, const FunctionImpl< Q, NDIM > &other, const R &beta, bool fence)
Inplace general bilinear operation.
Definition funcimpl.h:1458
friend class FunctionImpl
Definition funcimpl.h:1060
const Tensor< double > cell
Definition funcimpl.h:1075
bool has_leaves() const
Definition mraimpl.h:300
bool verify_parents_and_children() const
check that parents and children are consistent
Definition mraimpl.h:121
void apply_source_driven(opT &op, const FunctionImpl< R, NDIM > &f, bool fence)
similar to apply, but for low rank coeffs
Definition funcimpl.h:5808
std::size_t halo_size() const
How many neighbor nodes are staged on this rank; zero if no halo.
Definition funcimpl.h:1117
void distribute(std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > newmap) const
Definition funcimpl.h:1292
int get_special_level() const
Definition funcimpl.h:1066
void reconstruct_op(const keyT &key, const coeffT &s, const bool accumulate_NS=true)
Definition mraimpl.h:2238
tensorT gaxpy_ext_node(keyT key, Tensor< L > lc, T(*f)(const coordT &), T alpha, T beta) const
Definition funcimpl.h:7632
const coeffT parent_to_child(const coeffT &s, const keyT &parent, const keyT &child) const
Directly project parent coeffs to child coeffs.
Definition mraimpl.h:3405
WorldObject< FunctionImpl< T, NDIM > > woT
Definition funcimpl.h:1045
void undo_redundant(const bool fence)
convert this from redundant to standard reconstructed form
Definition mraimpl.h:1654
GenTensor< T > coeffT
Definition funcimpl.h:1054
double finalize_apply()
after apply we need to do some cleanup;
Definition mraimpl.h:1910
std::shared_ptr< FunctionFunctorInterface< T, NDIM > > get_functor() const
Definition mraimpl.h:318
bool leaves_only
Definition funcimpl.h:6446
friend hashT hash_value(const FunctionImpl< T, NDIM > *pimpl)
Hash a pointer to FunctionImpl.
Definition funcimpl.h:8065
const dcT & get_coeffs() const
Definition mraimpl.h:355
FunctionImpl(World &world, const FunctionImpl< Q, NDIM > &other, const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > &pmap, bool dozero)
Copy constructor.
Definition funcimpl.h:1241
compressT make_redundant_op(const keyT &key, const std::vector< Future< compressT > > &v)
similar to compress_op, but insert only the sum coefficients in the tree
Definition mraimpl.h:1855
T inner_ext_node(keyT key, tensorT c, const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > f) const
Return the inner product with an external function on a specified function node.
Definition funcimpl.h:7431
double norm2sq_local() const
Returns the square of the local norm ... no comms.
Definition mraimpl.h:1962
const FunctionCommonData< T, NDIM > & get_cdata() const
Definition mraimpl.h:361
void sum_down(bool fence)
After 1d push operator must sum coeffs down the tree to restore correct scaling function coefficients...
Definition mraimpl.h:1017
T inner_ext_recursive(keyT key, tensorT c, const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > f, const bool leaf_refine, T old_inner=T(0)) const
Definition funcimpl.h:7448
bool noautorefine(const keyT &key, const tensorT &t) const
Always returns false (for when autorefine is not wanted).
Definition mraimpl.h:960
double truncate_tol(double tol, const keyT &key) const
Returns the truncation threshold according to truncate_method.
Definition mraimpl.h:722
void recursive_apply(opT &apply_op, const std::vector< const FunctionImpl< T, LDIM > * > &fimpls, const std::vector< const FunctionImpl< T, LDIM > * > &gimpls, const bool fence)
traverse a non-existing tree for a sum of hartree products and apply an operator
Definition funcimpl.h:5987
void flo_unary_op_node_inplace(const opT &op, bool fence) const
Definition funcimpl.h:2426
bool autorefine_square_test(const keyT &key, const nodeT &t) const
Returns true if this block of coeffs needs autorefining.
Definition mraimpl.h:966
void erase(const Level &max_level)
truncate tree at a certain level
Definition mraimpl.h:840
void mulXX(const FunctionImpl< L, NDIM > *left, const FunctionImpl< R, NDIM > *right, double tol, bool fence)
Definition funcimpl.h:3480
std::pair< coeffT, std::pair< double, double > > compressT
Definition funcimpl.h:5219
void reconstruct(bool fence)
reconstruct this tree – respects fence
Definition mraimpl.h:1584
void multiply(const implT *f, const FunctionImpl< T, LDIM > *g, const int particle)
multiply f (a pair function of NDIM) with an orbital g (LDIM=NDIM/2)
Definition funcimpl.h:3897
coeffT assemble_coefficients(const keyT &key, const coeffT &coeff_ket, const coeffT &vpotential1, const coeffT &vpotential2, const tensorT &veri) const
given several coefficient tensors, assemble a result tensor
Definition mraimpl.h:1120
static void tnorm(const tensorT &t, double *lo, double *hi)
Computes norm of low/high-order polyn. coeffs for autorefinement test.
Definition mraimpl.h:3281
std::pair< bool, T > eval_local_only(const Vector< double, NDIM > &xin, Level maxlevel)
Evaluate function only if point is local returning (true,value); otherwise return (false,...
Definition mraimpl.h:3076
bool halo_probe(const keyT &key, coeffT &out) const
Look up a staged neighbor; on a hit copy its coefficients, which are empty for an interior node.
Definition funcimpl.h:1143
static void tnorm(const SVDTensor< T > &t, double *lo, double *hi, const int particle)
Definition mraimpl.h:3302
std::size_t max_depth() const
Returns the maximum depth of the tree ... collective ... global sum/broadcast.
Definition mraimpl.h:1990
std::size_t size() const
Returns the number of coefficients in the function ... collective global sum.
Definition mraimpl.h:2035
void reduce_rank(const double thresh, bool fence)
reduce the rank of the coefficients tensors
Definition mraimpl.h:1214
TreeState get_tree_state() const
Definition funcimpl.h:1543
const Tensor< double > & get_cell() const
Definition funcimpl.h:1562
void halo_clear() const
Discard the neighbor halo, freeing the staged coefficients.
Definition funcimpl.h:1112
std::shared_ptr< FunctionFunctorInterface< T, NDIM > > get_functor()
double do_apply_directed_screening(const opT *op, const keyT &key, const coeffT &coeff, const bool &do_kernel)
apply an operator on the coeffs c (at node key)
Definition funcimpl.h:5694
tensorT unfilter(const tensorT &s) const
Transform sums+differences at level n to sum coefficients at level n+1.
Definition mraimpl.h:1288
int get_initial_level() const
getter
Definition funcimpl.h:1065
Tensor< T > eval_plot_cube(const coordT &plotlo, const coordT &plothi, const std::vector< long > &npt, const bool eval_refine=false) const
Definition mraimpl.h:3694
virtual ~FunctionImpl()
Definition funcimpl.h:1272
Vector< Translation, NDIM > tranT
Definition funcimpl.h:1051
void change_tree_state(const TreeState finalstate, bool fence=true)
change the tree state of this function, might or might not respect fence!
Definition mraimpl.h:1516
Future< coeffT > truncate_reconstructed_spawn(const keyT &key, const double tol)
truncate using a tree in reconstructed form
Definition mraimpl.h:1709
GenTensor< Q > coeffs2values(const keyT &key, const GenTensor< Q > &coeff) const
Definition funcimpl.h:1997
FunctionImpl(const FunctionFactory< T, NDIM > &factory)
Initialize function impl from data in factory.
Definition funcimpl.h:1161
void map_and_mirror(const implT &f, const std::vector< long > &map, const std::vector< long > &mirror, bool fence)
map and mirror the translation index and the coefficients, result on this
Definition mraimpl.h:1183
Timer timer_lr_result
Definition funcimpl.h:1153
void gaxpy(T alpha, const FunctionImpl< L, NDIM > &left, T beta, const FunctionImpl< R, NDIM > &right, bool fence)
Invoked by result to perform result += alpha*left+beta*right in wavelet basis.
Definition funcimpl.h:2276
void truncate(double tol, bool fence)
Truncate according to the threshold with optional global fence.
Definition mraimpl.h:390
void do_mul(const keyT &key, const Tensor< L > &left, const std::pair< keyT, Tensor< R > > &arg)
Functor for the mul method.
Definition funcimpl.h:2201
void copy_remote_coeffs_from_pid(const ProcessID pid, const FunctionImpl< Q, NDIM > &other)
Definition funcimpl.h:1337
void project_out2(const FunctionImpl< T, LDIM+NDIM > *f, const FunctionImpl< T, LDIM > *g, const int dim)
project the low-dim function g on the hi-dim function f: this(x) = <f(x,y) | g(y)>
Definition funcimpl.h:7905
double do_apply_kernel2(const opT *op, const Tensor< R > &c, const do_op_args< OPDIM > &args, const TensorArgs &apply_targs)
same as do_apply_kernel, but use full rank tensors as input and low rank tensors as output
Definition funcimpl.h:5360
static Tensor< TENSOR_RESULT_TYPE(T, R)> dot_local(const std::vector< const FunctionImpl< T, NDIM > * > &left, const std::vector< const FunctionImpl< R, NDIM > * > &right, bool sym)
Definition funcimpl.h:6909
void fcube(const keyT &key, T(*f)(const coordT &), const Tensor< double > &qx, tensorT &fval) const
Evaluate function at quadrature points in the specified box.
Definition mraimpl.h:2599
Tensor< Q > coeffs2values(const keyT &key, const Tensor< Q > &coeff) const
Definition funcimpl.h:2123
Tensor< Q > values2coeffs(const keyT &key, const Tensor< Q > &values) const
Definition funcimpl.h:2137
void multi_to_multi_op_values_doit(const keyT &key, const opT &op, const std::vector< implT * > &vin, std::vector< implT * > &vout)
Inplace operate on many functions (impl's) with an operator within a certain box.
Definition funcimpl.h:3053
bool is_reconstructed() const
Returns true if the function is compressed.
Definition mraimpl.h:258
void replicate(bool fence=true)
Definition funcimpl.h:1276
double norm_tree_op(const keyT &key, const std::vector< Future< double > > &v)
Definition mraimpl.h:1671
void reset_timer()
Definition mraimpl.h:378
void refine_to_common_level(const std::vector< FunctionImpl< T, NDIM > * > &v, const std::vector< tensorT > &c, const keyT key)
Refine multiple functions down to the same finest level.
Definition mraimpl.h:870
int get_k() const
Definition mraimpl.h:352
void dirac_convolution_op(const keyT &key, const nodeT &node, FunctionImpl< T, LDIM > *f) const
The operator.
Definition funcimpl.h:2342
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:1048
void eval(const Vector< double, NDIM > &xin, const keyT &keyin, const typename Future< T >::remote_refT &ref)
Evaluate the function at a point in simulation coordinates.
Definition mraimpl.h:3032
bool truncate_op(const keyT &key, double tol, const std::vector< Future< bool > > &v)
Definition mraimpl.h:2806
void zero_norm_tree()
Definition mraimpl.h:1400
std::size_t max_local_depth() const
Returns the maximum local depth of the tree ... no communications.
Definition mraimpl.h:1976
tensorT project(const keyT &key) const
Definition mraimpl.h:2951
double thresh
Definition funcimpl.h:1071
double check_symmetry_local() const
Returns some asymmetry measure ... no comms.
Definition mraimpl.h:856
Future< double > get_norm_tree_recursive(const keyT &key) const
Definition mraimpl.h:2972
bool is_redundant_after_merge() const
Returns true if the function is redundant_after_merge.
Definition mraimpl.h:270
void mulXXvec(const FunctionImpl< L, NDIM > *left, const std::vector< const FunctionImpl< R, NDIM > * > &vright, const std::vector< FunctionImpl< T, NDIM > * > &vresult, double tol, bool fence)
Definition funcimpl.h:3537
Key< NDIM > keyT
Definition funcimpl.h:1052
friend hashT hash_value(const std::shared_ptr< FunctionImpl< T, NDIM > > impl)
Hash a shared_ptr to FunctionImpl.
Definition funcimpl.h:8075
coeffT multiply(const coeffT &val_ket, const coeffT &val_pot, int particle) const
multiply the ket with a one-electron potential rr(1,2)= f(1,2)*g(1)
Definition mraimpl.h:1085
std::vector< Vector< double, NDIM > > special_points
Definition funcimpl.h:1074
bool truncate_on_project
Definition funcimpl.h:1079
AtomicInt small
Definition funcimpl.h:1157
static void do_dot_localX(const typename mapT::iterator lstart, const typename mapT::iterator lend, typename FunctionImpl< R, NDIM >::mapT *rmap_ptr, const bool sym, Tensor< TENSOR_RESULT_TYPE(T, R)> *result_ptr, Mutex *mutex)
Definition funcimpl.h:6806
bool is_on_demand() const
Definition mraimpl.h:285
double err_box(const keyT &key, const nodeT &node, const opT &func, int npt, const Tensor< double > &qx, const Tensor< double > &quad_phit, const Tensor< double > &quad_phiw) const
Returns the square of the error norm in the box labeled by key.
Definition funcimpl.h:6270
void accumulate_timer(const double time) const
Definition mraimpl.h:364
void trickle_down_op(const keyT &key, const coeffT &s)
sum all the contributions from all scales after applying an operator in mod-NS form
Definition mraimpl.h:1474
static void do_inner_localX(const typename mapT::iterator lstart, const typename mapT::iterator lend, typename FunctionImpl< R, NDIM >::mapT *rmap_ptr, const bool sym, Tensor< TENSOR_RESULT_TYPE(T, R) > *result_ptr, Mutex *mutex)
Definition funcimpl.h:6725
void mulXXveca(const keyT &key, const FunctionImpl< L, NDIM > *left, const Tensor< L > &lcin, const std::vector< const FunctionImpl< R, NDIM > * > vrightin, const std::vector< Tensor< R > > &vrcin, const std::vector< FunctionImpl< T, NDIM > * > vresultin, double tol)
Definition funcimpl.h:3140
void set_thresh(double value)
Definition mraimpl.h:343
Tensor< double > print_plane_local(const int xaxis, const int yaxis, const coordT &el2)
collect the data for a plot of the MRA structure locally on each node
Definition mraimpl.h:435
void sock_it_to_me_too(const keyT &key, const RemoteReference< FutureImpl< std::pair< keyT, coeffT > > > &ref) const
Definition mraimpl.h:3010
void broaden_op(const keyT &key, const std::vector< Future< bool > > &v)
Definition mraimpl.h:1388
void print_plane(const std::string filename, const int xaxis, const int yaxis, const coordT &el2)
Print a plane ("xy", "xz", or "yz") containing the point x to file.
Definition mraimpl.h:415
void print_tree(std::ostream &os=std::cout, Level maxlevel=10000) const
Definition mraimpl.h:2834
bool has_summable_coefficients() const
Returns true if summing over the local nodes yields the function.
Definition mraimpl.h:295
void project_refine_op(const keyT &key, bool do_refine, const std::vector< Vector< double, NDIM > > &specialpts)
Definition mraimpl.h:2617
void scale_oop(const Q q, const FunctionImpl< F, NDIM > &f, bool fence)
Out-of-place scale by a constant.
Definition funcimpl.h:8040
T typeT
Definition funcimpl.h:1047
std::size_t tree_size() const
Returns the size of the tree structure of the function ... collective global sum.
Definition mraimpl.h:2016
ConcurrentHashMap< keyT, mapvecT > mapT
Definition funcimpl.h:6653
void add_scalar_inplace(T t, bool fence)
Adds a constant to the function. Local operation, optional fence.
Definition mraimpl.h:2698
void forward_traverse(const coeff_opT &coeff_op, const apply_opT &apply_op, const keyT &key) const
Definition funcimpl.h:3991
coeffT filter(const coeffT &s) const
Definition mraimpl.h:1267
tensorT downsample(const keyT &key, const std::vector< Future< coeffT > > &v) const
downsample the sum coefficients of level n+1 to sum coeffs on level n
Definition mraimpl.h:1308
void abs_square_inplace(bool fence)
Definition mraimpl.h:3385
FunctionImpl(const FunctionImpl< Q, NDIM > &other, const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > &pmap, bool dozero)
Copy constructor.
Definition funcimpl.h:1227
void refine(const opT &op, bool fence)
Definition funcimpl.h:5170
static mapT make_key_vec_map(const std::vector< const FunctionImpl< T, NDIM > * > &v)
Returns map of union of local keys to vector of indexes of functions containing that key.
Definition funcimpl.h:6674
void put_in_box(ProcessID from, long nl, long ni) const
Definition mraimpl.h:925
void unary_op_value_inplace(const opT &op, bool fence)
Definition funcimpl.h:3120
std::pair< const keyT, nodeT > datumT
Definition funcimpl.h:1056
Timer timer_accumulate
Definition funcimpl.h:1151
TensorArgs get_tensor_args() const
Definition mraimpl.h:334
void unaryXXa(const keyT &key, const FunctionImpl< Q, NDIM > *func, const opT &op)
Definition funcimpl.h:3455
void make_Vphi_only(const opT &leaf_op, FunctionImpl< T, NDIM > *ket, FunctionImpl< T, LDIM > *v1, FunctionImpl< T, LDIM > *v2, FunctionImpl< T, LDIM > *p1, FunctionImpl< T, LDIM > *p2, FunctionImpl< T, NDIM > *eri, const bool fence=true)
assemble the function V*phi using V and phi given from the functor
Definition funcimpl.h:4881
void average(const implT &rhs)
take the average of two functions, similar to: this=0.5*(this+rhs)
Definition mraimpl.h:1195
void recursive_apply(opT &apply_op, const FunctionImpl< T, LDIM > *fimpl, const FunctionImpl< T, LDIM > *gimpl, const bool fence)
traverse a non-existing tree, make its coeffs and apply an operator
Definition funcimpl.h:5849
void diff(const DerivativeBase< T, NDIM > *D, const implT *f, bool fence)
Definition mraimpl.h:1048
void square_inplace(bool fence)
Pointwise squaring of function with optional global fence.
Definition mraimpl.h:3374
void remove_internal_coefficients(const bool fence)
Definition mraimpl.h:1633
void compute_snorm_and_dnorm(bool fence=true)
compute norm of s and d coefficients for all nodes
Definition mraimpl.h:1238
std::vector< unsigned char > serialize_remote_coeffs()
invoked by copy_remote_coeffs_from_pid to serialize local coeffs
Definition funcimpl.h:1349
long box_leaf[1000]
Definition funcimpl.h:3552
void standard(bool fence)
Changes non-standard compressed form to standard compressed form.
Definition mraimpl.h:1897
void multiop_values_doit(const keyT &key, const opT &op, const std::vector< implT * > &v)
Definition funcimpl.h:3011
bool is_nonstandard_with_leaves() const
Definition mraimpl.h:280
GenTensor< Q > values2NScoeffs(const keyT &key, const GenTensor< Q > &values) const
convert function values of the a child generation directly to NS coeffs
Definition funcimpl.h:2098
int truncate_mode
Definition funcimpl.h:1077
void multiop_values(const opT &op, const std::vector< implT * > &v)
Definition funcimpl.h:3028
GenTensor< Q > NScoeffs2values(const keyT &key, const GenTensor< Q > &coeff, const bool s_only) const
convert S or NS coeffs to values on a 2k grid of the children
Definition funcimpl.h:2013
static std::enable_if_t< std::is_floating_point_v< Real >, Real > conj(const Real x)
Definition funcimpl.h:6846
FunctionNode holds the coefficients, etc., at each node of the 2^NDIM-tree.
Definition funcimpl.h:136
FunctionNode< Q, NDIM > convert() const
Copy with possible type conversion of coefficients, copying all other state.
Definition funcimpl.h:204
GenTensor< T > coeffT
Definition funcimpl.h:138
bool has_coeff() const
Returns true if there are coefficients in this node.
Definition funcimpl.h:210
void recompute_snorm_and_dnorm(const FunctionCommonData< T, NDIM > &cdata)
Definition funcimpl.h:367
FunctionNode(const coeffT &coeff, bool has_children=false)
Constructor from given coefficients with optional children.
Definition funcimpl.h:166
FunctionNode()
Default constructor makes node without coeff or children.
Definition funcimpl.h:156
void serialize(Archive &ar)
Definition funcimpl.h:494
double _dnorm_tree
norm of the difference coefficients summed up the tree
Definition funcimpl.h:147
double get_dnorm() const
return the precomputed norm of the (virtual) d coefficients
Definition funcimpl.h:336
size_t size() const
Returns the number of coefficients in this node.
Definition funcimpl.h:252
void set_has_children_recursive(const typename FunctionNode< T, NDIM >::dcT &c, const Key< NDIM > &key)
Sets has_children attribute to true recurring up to ensure connected.
Definition funcimpl.h:269
FunctionNode< T, NDIM > & operator=(const FunctionNode< T, NDIM > &other)
Definition funcimpl.h:186
void accumulate(const coeffT &t, const typename FunctionNode< T, NDIM >::dcT &c, const Key< NDIM > &key, const TensorArgs &args, const bool do_svd_add)
Accumulate inplace and if necessary connect node to parent.
Definition funcimpl.h:448
FunctionNode(const coeffT &coeff, double norm_tree, double dnorm_tree, double snorm, double dnorm, bool has_children)
Definition funcimpl.h:176
double snorm
norm of the s coefficients
Definition funcimpl.h:151
void clear_coeff()
Clears the coefficients (has_coeff() will subsequently return false).
Definition funcimpl.h:305
Tensor< T > tensorT
Definition funcimpl.h:139
coeffT buffer
The coefficients, if any.
Definition funcimpl.h:149
T trace_conj(const FunctionNode< T, NDIM > &rhs) const
Definition funcimpl.h:489
void scale(Q a)
Scale the coefficients of this node.
Definition funcimpl.h:311
bool is_leaf() const
Returns true if this does not have children.
Definition funcimpl.h:223
void consolidate_buffer(const TensorArgs &args, const bool do_svd_add)
Definition funcimpl.h:479
void set_has_children(bool flag)
Sets has_children attribute to value of flag.
Definition funcimpl.h:264
void scale_norms(const double a)
Multiply the stored norms by a, for coefficients scaled by some q with |q| = a.
Definition funcimpl.h:360
double get_norm_tree() const
Gets the value of norm_tree.
Definition funcimpl.h:326
bool _has_children
True if there are children.
Definition funcimpl.h:148
void set_snorm(const double sn)
set the precomputed norm of the (virtual) s coefficients
Definition funcimpl.h:341
coeffT _coeffs
The coefficients, if any.
Definition funcimpl.h:145
void accumulate2(const tensorT &t, const typename FunctionNode< T, NDIM >::dcT &c, const Key< NDIM > &key)
Accumulate inplace and if necessary connect node to parent.
Definition funcimpl.h:415
void reduceRank(const double &eps)
reduces the rank of the coefficients (if applicable)
Definition funcimpl.h:259
WorldContainer< Key< NDIM >, FunctionNode< T, NDIM > > dcT
Definition funcimpl.h:154
void gaxpy_inplace(const T &alpha, const FunctionNode< Q, NDIM > &other, const R &beta)
General bi-linear operation — this = this*alpha + other*beta.
Definition funcimpl.h:397
double get_dnorm_tree() const
Gets the value of dnorm_tree.
Definition funcimpl.h:331
double _norm_tree
After norm_tree will contain norm of sum coefficients summed up tree.
Definition funcimpl.h:146
void set_is_leaf(bool flag)
Sets has_children attribute to value of !flag.
Definition funcimpl.h:290
void print_json(std::ostream &s) const
Definition funcimpl.h:503
double get_snorm() const
get the precomputed norm of the (virtual) s coefficients
Definition funcimpl.h:351
void set_dnorm_tree(double dnorm_tree)
Sets the value of dnorm_tree.
Definition funcimpl.h:321
const coeffT & coeff() const
Returns a const reference to the tensor containing the coeffs.
Definition funcimpl.h:247
FunctionNode(const coeffT &coeff, double norm_tree, bool has_children)
Definition funcimpl.h:171
bool has_children() const
Returns true if this node has children.
Definition funcimpl.h:217
void set_coeff(const coeffT &coeffs)
Takes a shallow copy of the coeff — same as this->coeff()=coeff.
Definition funcimpl.h:295
void set_dnorm(const double dn)
set the precomputed norm of the (virtual) d coefficients
Definition funcimpl.h:346
double dnorm
norm of the d coefficients, also defined if there are no d coefficients
Definition funcimpl.h:150
bool is_invalid() const
Returns true if this node is invalid (no coeffs and no children).
Definition funcimpl.h:229
FunctionNode(const FunctionNode< T, NDIM > &other)
Definition funcimpl.h:180
coeffT & coeff()
Returns a non-const reference to the tensor containing the coeffs.
Definition funcimpl.h:237
void set_norm_tree(double norm_tree)
Sets the value of norm_tree.
Definition funcimpl.h:316
A multiresolution adaptive numerical function.
Definition mra.h:141
Implements the functionality of futures.
Definition future.h:75
A future is a possibly yet unevaluated value.
Definition future.h:370
remote_refT remote_ref(World &world) const
Returns a structure used to pass references to another process.
Definition future.h:672
RemoteReference< FutureImpl< T > > remote_refT
Definition future.h:395
Definition lowranktensor.h:59
bool is_of_tensortype(const TensorType &tt) const
Definition gentensor.h:225
GenTensor convert(const TensorArgs &) const
Definition gentensor.h:198
long dim(const int i) const
return the number of entries in dimension i
Definition lowranktensor.h:391
Tensor< T > full_tensor_copy() const
Definition gentensor.h:206
constexpr bool is_full_tensor() const
Definition gentensor.h:224
void add_SVD(const GenTensor< T > &rhs, const double &)
Definition gentensor.h:235
const Tensor< T > & get_tensor() const
Definition gentensor.h:203
void reduce_rank(const double &)
Definition gentensor.h:217
bool has_no_data() const
Definition gentensor.h:211
void normalize()
Definition gentensor.h:218
GenTensor< T > & emul(const GenTensor< T > &other)
Inplace multiply by corresponding elements of argument Tensor.
Definition lowranktensor.h:639
float_scalar_type normf() const
Definition lowranktensor.h:406
double svd_normf() const
Definition gentensor.h:213
SRConf< T > config() const
Definition gentensor.h:237
long rank() const
Definition gentensor.h:212
const Tensor< T > & full_tensor() const
Definition gentensor.h:200
SVDTensor< T > & get_svdtensor()
Definition gentensor.h:228
TensorType tensor_type() const
Definition gentensor.h:221
static double fac_reduce()
return the additional safety for rank reduction
Definition gentensor.h:241
bool has_data() const
Definition gentensor.h:210
Tensor< T > reconstruct_tensor() const
Definition gentensor.h:199
GenTensor & gaxpy(const T alpha, const GenTensor &other, const T beta)
Definition lowranktensor.h:588
bool is_assigned() const
Definition gentensor.h:209
IsSupported< TensorTypeData< Q >, GenTensor< T > & >::type scale(Q fac)
Inplace multiplication by scalar of supported type (legacy name).
Definition lowranktensor.h:428
constexpr bool is_svd_tensor() const
Definition gentensor.h:222
Iterates in lexical order thru all children of a key.
Definition key.h:624
Key is the index for a node of the 2^NDIM-tree.
Definition key.h:71
Key< NDIM+LDIM > merge_with(const Key< LDIM > &rhs) const
merge with other key (ie concatenate), use level of rhs, not of this
Definition key.h:563
Level level() const
Definition key.h:170
bool is_valid() const
Checks if a key is valid.
Definition key.h:125
hashT hash() const
Definition key.h:159
Key< NDIM-VDIM > extract_complement_key(const std::array< int, VDIM > &v) const
extract a new key with the Translations complementary to the ones indicated in the v array
Definition key.h:549
Key< VDIM > extract_key(const std::array< int, VDIM > &v) const
extract a new key with the Translations indicated in the v array
Definition key.h:541
Key parent(int generation=1) const
Returns the key of the parent.
Definition key.h:366
const Vector< Translation, NDIM > & translation() const
Definition key.h:175
void break_apart(Key< LDIM > &key1, Key< KDIM > &key2) const
break key into two low-dimensional keys
Definition key.h:500
LevelPmap(World &world)
Definition funcimpl.h:111
const int nproc
Definition funcimpl.h:107
LevelPmap()
Definition funcimpl.h:109
ProcessID owner(const keyT &key) const
Find the owner of a given key.
Definition funcimpl.h:114
Definition funcimpl.h:77
Mutex using pthread mutex operations.
Definition worldmutex.h:150
void unlock() const
Free a mutex owned by this thread.
Definition worldmutex.h:184
void lock() const
Acquire the mutex waiting if necessary.
Definition worldmutex.h:174
Range, vaguely a la Intel TBB, to encapsulate a random-access, STL-like start and end iterator with c...
Definition range.h:64
iteratorT iterator
Alias for the iterator type.
Definition range.h:71
Simple structure used to manage references/pointers to remote instances.
Definition worldref.h:394
Definition SVDTensor.h:42
SimplePmap(World &world)
Definition funcimpl.h:92
const int nproc
Definition funcimpl.h:88
const ProcessID me
Definition funcimpl.h:89
ProcessID owner(const keyT &key) const
Maps key to processor.
Definition funcimpl.h:95
A slice defines a sub-range or patch of a dimension.
Definition slice.h:103
static TaskAttributes hipri()
Definition thread.h:457
Traits class to specify support of numeric types.
Definition type_data.h:56
A tensor is a multidimensional array.
Definition tensor.h:313
float_scalar_type normf() const
Returns the Frobenius norm of the tensor.
Definition tensor.h:1731
Tensor< T > & gaxpy(T alpha, const Tensor< T > &other, T beta)
Inplace generalized saxpy ... this = this*alpha + other*beta.
Definition tensor.h:1810
T sum() const
Returns the sum of all elements of the tensor.
Definition tensor.h:1667
Tensor< T > reshape(int ndimnew, const long *d)
Returns new view/tensor reshaping size/number of dimensions to conforming tensor.
Definition tensor.h:1389
T * ptr()
Returns a pointer to the internal data.
Definition tensor.h:1845
Tensor< T > mapdim(const std::vector< long > &map)
Returns new view/tensor permuting the dimensions.
Definition tensor.h:1629
IsSupported< TensorTypeData< Q >, Tensor< T > & >::type scale(Q x)
Inplace multiplication by scalar of supported type (legacy name).
Definition tensor.h:691
Tensor< T > & emul(const Tensor< T > &t)
Inplace multiply by corresponding elements of argument Tensor.
Definition tensor.h:1804
bool has_data() const
Definition tensor.h:1907
Tensor< T > fusedim(long i)
Returns new view/tensor fusing contiguous dimensions i and i+1.
Definition tensor.h:1592
Tensor< T > flat()
Returns new view/tensor rehshaping to flat (1-d) tensor.
Definition tensor.h:1560
Tensor< T > & conj()
Inplace complex conjugate.
Definition tensor.h:721
Definition function_common_data.h:169
A simple, fixed dimension vector.
Definition vector.h:64
Makes a distributed container with specified attributes.
Definition worlddc.h:1302
bool find(accessor &acc, const keyT &key)
Write access to LOCAL value by key. Returns true if found, false otherwise (always false for remote).
Definition worlddc.h:1469
iterator begin()
Returns an iterator to the beginning of the local data (no communication).
Definition worlddc.h:1552
bool is_replicated() const
Definition worlddc.h:1402
ProcessID owner(const keyT &key) const
Returns processor that logically owns key (no communication).
Definition worlddc.h:1516
implT::const_iterator const_iterator
Definition worlddc.h:1310
void replace(const pairT &datum)
Inserts/replaces key+value pair (non-blocking communication if key not local).
Definition worlddc.h:1456
iterator end()
Returns an iterator past the end of the local data (no communication).
Definition worlddc.h:1566
bool insert(accessor &acc, const keyT &key)
Write access to LOCAL value by key. Returns true if inserted, false if already exists (throws if remo...
Definition worlddc.h:1483
bool is_distributed() const
Definition worlddc.h:1398
implT::iterator iterator
Definition worlddc.h:1309
Future< REMFUTURE(MEMFUN_RETURNT(memfunT))> task(const keyT &key, memfunT memfun, const TaskAttributes &attr=TaskAttributes())
Adds task "resultT memfun()" in process owning item (non-blocking comm if remote).
Definition worlddc.h:1908
bool is_local(const keyT &key) const
Returns true if the key maps to the local processor (no communication).
Definition worlddc.h:1523
bool is_host_replicated() const
Definition worlddc.h:1406
Future< MEMFUN_RETURNT(memfunT)> send(const keyT &key, memfunT memfun)
Sends message "resultT memfun()" to item (non-blocking comm if remote).
Definition worlddc.h:1665
implT::const_accessor const_accessor
Definition worlddc.h:1312
implT::accessor accessor
Definition worlddc.h:1311
Interface to be provided by any process map.
Definition worlddc.h:128
void fence(bool debug=false)
Synchronizes all processes in communicator AND globally ensures no pending AM or tasks.
Definition worldgop.cc:177
Implements most parts of a globally addressable object (via unique ID).
Definition world_object.h:491
WorldObject(const WorldObject &other)
Definition world_object.h:500
void process_pending()
Definition world_object.h:787
ProcessID me
Definition world_object.h:514
detail::task_result_type< memfnT >::futureT send(ProcessID dest, memfnT memfn) const
Definition world_object.h:858
detail::task_result_type< memfnT >::futureT task(ProcessID dest, memfnT memfn, const TaskAttributes &attr=TaskAttributes()) const
Definition world_object.h:1132
Future< resultT > reduce(const rangeT &range, const opT &op)
Reduce op(item) for all items in range using op(sum,op(item)).
Definition world_task_queue.h:527
A parallel world class.
Definition world.h:134
static World * world_from_id(std::uint64_t id)
Convert a World ID to a World pointer.
Definition world.h:516
WorldTaskQueue & taskq
Task queue.
Definition world.h:215
std::vector< uniqueidT > get_object_ids() const
Returns a vector of all unique IDs in this World.
Definition world.h:492
ProcessID rank() const
Returns the process rank in this World (same as MPI_Comm_rank()).
Definition world.h:344
static std::vector< unsigned long > get_world_ids()
return a vector containing all world ids
Definition world.h:500
ProcessID size() const
Returns the number of processes in this World (same as MPI_Comm_size()).
Definition world.h:354
unsigned long id() const
Definition world.h:324
WorldGopInterface & gop
Global operations.
Definition world.h:216
std::optional< T * > ptr_from_id(uniqueidT id) const
Look up a local pointer from a world-wide unique ID.
Definition world.h:440
Wraps an archive around an STL vector for input.
Definition vector_archive.h:101
Wraps an archive around an STL vector for output.
Definition vector_archive.h:55
Wrapper for an opaque pointer for serialization purposes.
Definition archive.h:851
syntactic sugar for std::array<bool, N>
Definition array_of_bools.h:19
array_of_bools or_front(const array_of_bools< C > &a) const
Definition array_of_bools.h:79
array_of_bools or_back(const array_of_bools< C > &a) const
Definition array_of_bools.h:121
Class for unique global IDs.
Definition uniqueid.h:53
unsigned long get_obj_id() const
Access the object ID.
Definition uniqueid.h:97
unsigned long get_world_id() const
Access the World ID.
Definition uniqueid.h:90
static const double R
Definition csqrt.cc:46
double(* f1)(const coord_3d &)
Definition derivatives.cc:55
char * p(char *buf, const char *name, int k, int initial_level, double thresh, int order)
Definition derivatives.cc:72
static double lo
Definition dirac-hatom.cc:23
@ upper
Definition dirac-hatom.cc:15
Provides FunctionDefaults and utilities for coordinate transformation.
archive_array< unsigned char > wrap_opaque(const T *, unsigned int)
Factory function to wrap a pointer to contiguous data as an opaque (uchar) archive_array.
Definition archive.h:926
Tensor< typename Tensor< T >::scalar_type > arg(const Tensor< T > &t)
Return a new tensor holding the argument of each element of t (complex types only).
Definition tensor.h:2761
Tensor< TENSOR_RESULT_TYPE(T, Q) > & fast_transform(const Tensor< T > &t, const Tensor< Q > &c, Tensor< TENSOR_RESULT_TYPE(T, Q) > &result, Tensor< TENSOR_RESULT_TYPE(T, Q) > &workspace)
Restricted but heavily optimized form of transform().
Definition tensor.h:2464
const double beta
Definition gygi_soltion.cc:62
static const double v
Definition hatom_sf_dirac.cc:20
static double u(double r, double c)
Definition he.cc:20
Provides IndexIterator.
Tensor< double > op(const Tensor< double > &x)
Definition kain.cc:508
Multidimension Key for MRA tree and associated iterators.
static double pow(const double *a, const double *b)
Definition lda.h:74
#define MADNESS_CHECK(condition)
Check a condition — even in a release build the condition is always evaluated so it can have side eff...
Definition madness_exception.h:182
#define MADNESS_EXCEPTION(msg, value)
Macro for throwing a MADNESS exception.
Definition madness_exception.h:119
#define MADNESS_ASSERT(condition)
Assert a condition that should be free of side-effects since in release builds this might be a no-op.
Definition madness_exception.h:134
#define MADNESS_CHECK_THROW(condition, msg)
Check a condition — even in a release build the condition is always evaluated so it can have side eff...
Definition madness_exception.h:207
Header to declare stuff which has not yet found a home.
constexpr double pi
Mathematical constant .
Definition constants.h:48
MemFuncWrapper< objT *, memfnT, typename result_of< memfnT >::type > wrap_mem_fn(objT &obj, memfnT memfn)
Create a member function wrapper (MemFuncWrapper) from an object and a member function pointer.
Definition mem_func_wrapper.h:251
void combine_hash(hashT &seed, hashT hash)
Internal use only.
Definition worldhash.h:249
Namespace for all elements and tools of MADNESS.
Definition DFConvergence.h:9
std::ostream & operator<<(std::ostream &os, const particle< PDIM > &p)
Definition lowrankfunction.h:452
static const char * filename
Definition legendre.cc:96
static const std::vector< Slice > ___
Entire dimension.
Definition slice.h:124
double FunctionDefaults< NDIM >::thresh
Definition mraimpl.h:3879
int power(int base=2)
Definition power.h:43
static double cpu_time()
Returns the cpu time in seconds relative to an arbitrary origin.
Definition timers.h:128
GenTensor< TENSOR_RESULT_TYPE(R, Q)> general_transform(const GenTensor< R > &t, const Tensor< Q > c[])
Definition gentensor.h:274
std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > FunctionDefaults< NDIM >::pmap
Definition mraimpl.h:3898
void finalize()
Call this once at the very end of your main program instead of MPI_Finalize().
Definition world.cc:246
Function< Q, NDIM > convert(const Function< T, NDIM > &f, bool fence=true)
Type conversion implies a deep copy. No communication except for optional fence.
Definition mra.h:2286
void norm_tree(World &world, const std::vector< Function< T, NDIM > > &v, bool fence=true)
Makes the norm tree for all functions in a vector.
Definition vmra.h:1394
std::vector< Function< TENSOR_RESULT_TYPE(T, R), NDIM > > transform(World &world, const std::vector< Function< T, NDIM > > &v, const Tensor< R > &c, bool fence=true)
Transforms a vector of functions according to new[i] = sum[j] old[j]*c[j,i].
Definition vmra.h:807
TreeState
Definition funcdefaults.h:60
@ nonstandard_after_apply
s and d coeffs, state after operator application
Definition funcdefaults.h:65
@ redundant_after_merge
s coeffs everywhere, must be summed up to yield the result
Definition funcdefaults.h:67
@ reconstructed
s coeffs at the leaves only
Definition funcdefaults.h:61
@ nonstandard
s and d coeffs in internal nodes
Definition funcdefaults.h:63
@ redundant
s coeffs everywhere
Definition funcdefaults.h:66
static Tensor< double > weights[max_npt+1]
Definition legendre.cc:99
int64_t Translation
Definition key.h:59
Key< NDIM > displacement(const Key< NDIM > &source, const Key< NDIM > &target)
given a source and a target, return the displacement in translation
Definition key.h:609
Function< T, NDIM > sum(World &world, const std::vector< Function< T, NDIM > > &f, bool fence=true)
Returns new function — q = sum_i f[i].
Definition vmra.h:1774
static const Slice _(0,-1, 1)
std::shared_ptr< FunctionFunctorInterface< double, 3 > > func(new opT(g))
ConcurrentHashMap< hashT, std::shared_ptr< GaussianConvolution1D< double > > > GaussianConvolution1DCache< double >::map
Definition convolution1d.h:1073
int Level
Definition key.h:60
std::enable_if< std::is_base_of< ProjectorBase, projT >::value, OuterProjector< projT, projQ > >::type outer(const projT &p0, const projQ &p1)
Definition projector.h:476
static constexpr double NORM_TREE_UNCOMPUTED
Definition funcimpl.h:127
int RandomValue< int >()
Random int.
Definition ran.cc:250
Tensor< double > FunctionDefaults< NDIM >::cell_width
Definition mraimpl.h:3893
bool has_data(const PropertyResults &p)
Definition Results.h:421
static constexpr double MUL_SCREENING_SAFETY
Definition funcimpl.h:132
static double pop(std::vector< double > &v)
Definition SCF.cc:118
Tensor< T > fcube(const Key< NDIM > &, T(*f)(const Vector< double, NDIM > &), const Tensor< double > &)
Definition mraimpl.h:2292
TensorType
low rank representations of tensors (see gentensor.h)
Definition gentensor.h:120
@ TT_2D
Definition gentensor.h:120
@ TT_FULL
Definition gentensor.h:120
NDIM & f
Definition mra.h:2817
void error(const char *msg)
Definition world.cc:147
NDIM const Function< R, NDIM > & g
Definition mra.h:2817
std::size_t hashT
The hash value type.
Definition worldhash.h:146
void print(const T &t, Ts &&... ts)
Print items to std::cout (items separated by spaces) and terminate with a new line.
Definition print.h:232
void change_tensor_type(GenTensor< T > &, const TensorArgs &targs)
change representation to targ.tt
Definition gentensor.h:284
const Future< void > Future< void >::value
Definition future.cc:44
static const int kmax
Definition twoscale.cc:52
GenTensor< TENSOR_RESULT_TYPE(R, Q)> transform_dir(const GenTensor< R > &t, const Tensor< Q > &c, const int axis)
Definition lowranktensor.h:1108
Function< T, CCPairFunction< T, NDIM >::LDIM > inner(const CCPairFunction< T, NDIM > &c, const Function< T, CCPairFunction< T, NDIM >::LDIM > &f, const std::tuple< int, int, int > v1, const std::tuple< int, int, int > v2)
Definition ccpairfunction.h:1007
void scale(World &world, std::vector< Function< T, NDIM > > &v, const std::vector< Q > &factors, bool fence=true)
Scales inplace a vector of functions by distinct values.
Definition vmra.h:923
const FunctionCommonData< T, NDIM > * FunctionCommonData< T, NDIM >::data[MAXK]
Definition mraimpl.h:3875
std::string name(const FuncType &type, const int ex=-1)
Definition ccpairfunction.h:28
void mxmT(long dimi, long dimj, long dimk, T *MADNESS_RESTRICT c, const T *a, const T *b)
Matrix += Matrix * matrix transpose ... MKL interface version.
Definition mxm.h:191
bool same_displacement_shell(double a, double b)
Definition displacements.h:62
Function< T, NDIM > copy(const Function< T, NDIM > &f, const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > &pmap, bool fence=true)
Create a new copy of the function with different distribution and optional fence.
Definition mra.h:2239
static const int MAXK
The maximum wavelet order presently supported.
Definition funcdefaults.h:55
Definition mraimpl.h:53
static long abs(long a)
Definition tensor.h:214
const double cc
Definition navstokes_cosines.cc:107
static const double b
Definition nonlinschro.cc:119
static const double d
Definition nonlinschro.cc:121
static const double a
Definition nonlinschro.cc:118
Defines simple templates for printing to std::cout "a la Python".
double Q(double a)
Definition relops.cc:20
static const double c
Definition relops.cc:10
static const double L
Definition rk.cc:46
Definition test_ar.cc:204
Key parent() const
Definition test_tree.cc:68
hashT hash() const
Definition test_dc.cc:54
Future< this_type > activate() const
Definition funcimpl.h:4788
int oversampling
pointwise_multiplier oversampling (k+oversampling grid)
Definition funcimpl.h:4624
double target_precision
leaf threshold used for the post-screening truncation
Definition funcimpl.h:4623
this_type forward_ctor(implT *result1, const opT &leaf_op1, const ctT &iau1, const ctL &iai1, const ctL &iaj1, const implT *eri1, double target_precision1, int oversampling1)
Definition funcimpl.h:4799
CoeffTracker< T, LDIM > ctL
Definition funcimpl.h:4615
Vphi_ij_u_op_NS()
Definition funcimpl.h:4635
void serialize(const Archive &ar)
Definition funcimpl.h:4807
opT leaf_op
Definition funcimpl.h:4618
Vphi_ij_u_op_NS(implT *result, const opT &leaf_op, const ctT &iau, const ctL &iai, const ctL &iaj, const implT *eri, double target_precision, int oversampling=1)
Definition funcimpl.h:4637
const implT * eri
exact ERI g(1,2), on-demand, optional (null → function mode)
Definition funcimpl.h:4622
bool have_j() const
Definition funcimpl.h:4627
bool have_i() const
Definition funcimpl.h:4626
ctL iai
i(1), NS form, optional (null impl → skip particle-1 multiply)
Definition funcimpl.h:4620
ctT iau
u(1,2), NS form, required
Definition funcimpl.h:4619
void leaf_action(const keyT &key, const coeffT &coeff) const
Definition funcimpl.h:4711
double compute_error_from_inaccurate_refinement(const keyT &key, const Key< LDIM > &key1, const Key< LDIM > &key2) const
error from inaccurate refinement: cross terms of (snorm,dnorm) for u*[i]*[j]
Definition funcimpl.h:4646
void accumulate_into_result(const keyT &key, const coeffT &coeff) const
Definition funcimpl.h:4630
this_type make_child(const keyT &child) const
Definition funcimpl.h:4778
ctL iaj
j(2), NS form, optional (null impl → skip particle-2 multiply)
Definition funcimpl.h:4621
std::pair< coeffT, double > make_sum_coeffs(const keyT &key) const
Definition funcimpl.h:4685
implT * result
output function (function mode) or accumulation handle (inner mode)
Definition funcimpl.h:4617
CoeffTracker< T, NDIM > ctT
Definition funcimpl.h:4614
std::pair< bool, coeffT > operator()(const keyT &key) const
Definition funcimpl.h:4724
bool have_eri() const
Definition funcimpl.h:4628
std::pair< bool, coeffT > continue_recursion(const std::vector< bool > child_is_leaf, const tensorT &coeffs, const keyT &key) const
Definition funcimpl.h:4756
tensorT eri_coeffs(const keyT &key) const
get ERI coefficients at key (same logic as Vphi_op_NS::eri_coeffs)
Definition funcimpl.h:4663
bool randomize() const
Definition funcimpl.h:4612
Vphi_ij_u_op_NS< opT, LDIM > this_type
Definition funcimpl.h:4613
implT * result
where to construct Vphi, no need to track parents
Definition funcimpl.h:4334
bool have_v2() const
Definition funcimpl.h:4343
ctL iav1
Definition funcimpl.h:4338
Vphi_op_NS(implT *result, const opT &leaf_op, const ctT &iaket, const ctL &iap1, const ctL &iap2, const ctL &iav1, const ctL &iav2, const implT *eri)
Definition funcimpl.h:4352
ctL iap1
Definition funcimpl.h:4337
bool have_v1() const
Definition funcimpl.h:4342
std::pair< bool, coeffT > continue_recursion(const std::vector< bool > child_is_leaf, const tensorT &coeffs, const keyT &key) const
loop over all children and either insert their sum coeffs or continue the recursion
Definition funcimpl.h:4418
opT leaf_op
deciding if a given FunctionNode will be a leaf node
Definition funcimpl.h:4335
std::pair< coeffT, double > make_sum_coeffs(const keyT &key) const
make the sum coeffs for key
Definition funcimpl.h:4511
CoeffTracker< T, NDIM > ctT
Definition funcimpl.h:4331
ctL iap2
the particles 1 and 2 (exclusive with ket)
Definition funcimpl.h:4337
bool have_ket() const
Definition funcimpl.h:4341
const implT * eri
2-particle potential, must be on-demand
Definition funcimpl.h:4339
CoeffTracker< T, LDIM > ctL
Definition funcimpl.h:4332
std::pair< bool, coeffT > operator()(const Key< NDIM > &key) const
make and insert the coefficients into result's tree
Definition funcimpl.h:4363
void serialize(const Archive &ar)
serialize this (needed for use in recursive_op)
Definition funcimpl.h:4592
Vphi_op_NS< opT, LDIM > this_type
Definition funcimpl.h:4330
ctT iaket
the ket of a pair function (exclusive with p1, p2)
Definition funcimpl.h:4336
double compute_error_from_inaccurate_refinement(const keyT &key, const tensorT &ceri) const
the error is computed from the d coefficients of the constituent functions
Definition funcimpl.h:4464
void accumulate_into_result(const Key< NDIM > &key, const coeffT &coeff) const
Definition funcimpl.h:4346
this_type make_child(const keyT &child) const
Definition funcimpl.h:4563
tensorT eri_coeffs(const keyT &key) const
Definition funcimpl.h:4444
ctL iav2
potentials for particles 1 and 2
Definition funcimpl.h:4338
bool have_eri() const
Definition funcimpl.h:4344
this_type forward_ctor(implT *result1, const opT &leaf_op, const ctT &iaket1, const ctL &iap11, const ctL &iap21, const ctL &iav11, const ctL &iav21, const implT *eri1)
Definition funcimpl.h:4585
Vphi_op_NS()
Definition funcimpl.h:4351
Future< this_type > activate() const
Definition funcimpl.h:4574
bool randomize() const
Definition funcimpl.h:4328
bool randomize() const
Definition funcimpl.h:3841
Future< this_type > activate() const
retrieve the coefficients (parent coeffs might be remote)
Definition funcimpl.h:3871
add_op(const ctT &f, const ctT &g, const double alpha, const double beta)
Definition funcimpl.h:3849
ctT f
tracking coeffs of first and second addend
Definition funcimpl.h:3844
double alpha
prefactor for f, g
Definition funcimpl.h:3846
add_op this_type
Definition funcimpl.h:3839
CoeffTracker< T, NDIM > ctT
Definition funcimpl.h:3838
void serialize(const Archive &ar)
Definition funcimpl.h:3883
ctT g
Definition funcimpl.h:3844
std::pair< bool, coeffT > operator()(const keyT &key) const
if we are at the bottom of the trees, return the sum of the coeffs
Definition funcimpl.h:3853
double beta
Definition funcimpl.h:3846
this_type make_child(const keyT &child) const
Definition funcimpl.h:3866
this_type forward_ctor(const ctT &f1, const ctT &g1, const double alpha, const double beta)
taskq-compatible ctor
Definition funcimpl.h:3879
opT op
Definition funcimpl.h:3425
opT::resultT resultT
Definition funcimpl.h:3423
Tensor< resultT > operator()(const Key< NDIM > &key, const Tensor< Q > &t) const
Definition funcimpl.h:3432
coeff_value_adaptor(const FunctionImpl< Q, NDIM > *impl_func, const opT &op)
Definition funcimpl.h:3428
const FunctionImpl< Q, NDIM > * impl_func
Definition funcimpl.h:3424
void serialize(Archive &ar)
Definition funcimpl.h:3441
Range< typename dcT::const_iterator > rangeT
Definition funcimpl.h:2633
void serialize(const Archive &ar)
Definition funcimpl.h:2650
FunctionImpl< Q, NDIM > * result
Definition funcimpl.h:2634
do_accumulate_trees(FunctionImpl< Q, NDIM > &result, const T alpha)
Definition funcimpl.h:2637
T alpha
Definition funcimpl.h:2635
bool operator()(typename rangeT::iterator &it) const
return the norm of the difference of this node and its "mirror" node
Definition funcimpl.h:2641
Range< typename dcT::const_iterator > rangeT
Definition funcimpl.h:2844
void serialize(const Archive &ar)
Definition funcimpl.h:2872
implT * g
Definition funcimpl.h:2846
do_average()
Definition funcimpl.h:2848
bool operator()(typename rangeT::iterator &it) const
iterator it points to this
Definition funcimpl.h:2852
do_average(implT &g)
Definition funcimpl.h:2849
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2877
void serialize(const Archive &ar)
Definition funcimpl.h:2900
TensorArgs targs
Definition funcimpl.h:2880
do_change_tensor_type(const TensorArgs &targs, implT &g)
Definition funcimpl.h:2886
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2889
implT * f
Definition funcimpl.h:2881
Range< typename dcT::const_iterator > rangeT
Definition funcimpl.h:2550
double operator()(typename rangeT::iterator &it) const
return the norm of the difference of this node and its "mirror" node
Definition funcimpl.h:2556
do_check_symmetry_local()
Definition funcimpl.h:2552
void serialize(const Archive &ar)
Definition funcimpl.h:2619
double operator()(double a, double b) const
Definition funcimpl.h:2615
do_check_symmetry_local(const implT &f)
Definition funcimpl.h:2553
const implT * f
Definition funcimpl.h:2551
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:5053
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:5059
do_compute_snorm_and_dnorm(const FunctionCommonData< T, NDIM > &cdata)
Definition funcimpl.h:5056
const FunctionCommonData< T, NDIM > & cdata
Definition funcimpl.h:5055
TensorArgs targs
Definition funcimpl.h:2907
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2916
do_consolidate_buffer(const TensorArgs &targs, bool do_svd_add)
Definition funcimpl.h:2914
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2904
bool do_svd_add
Definition funcimpl.h:2910
void serialize(const Archive &ar)
Definition funcimpl.h:2920
double operator()(double val) const
Definition funcimpl.h:1667
double limit
Definition funcimpl.h:1662
do_convert_to_color(const double limit, const bool log)
Definition funcimpl.h:1666
bool log
Definition funcimpl.h:1663
static double lower()
Definition funcimpl.h:1664
do_dot_local(const FunctionImpl< R, NDIM > *other, const bool leaves_only)
Definition funcimpl.h:6596
bool leaves_only
Definition funcimpl.h:6593
typedef TENSOR_RESULT_TYPE(T, R) resultT
resultT operator()(resultT a, resultT b) const
Definition funcimpl.h:6624
const FunctionImpl< R, NDIM > * other
Definition funcimpl.h:6592
void serialize(const Archive &ar)
Definition funcimpl.h:6628
resultT operator()(typename dcT::const_iterator &it) const
Definition funcimpl.h:6598
FunctionImpl< T, NDIM > * f
prefactor for current function impl
Definition funcimpl.h:1432
do_gaxpy_inplace(FunctionImpl< T, NDIM > *f, T alpha, R beta)
Definition funcimpl.h:1436
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:1437
R beta
prefactor for other function impl
Definition funcimpl.h:1434
void serialize(Archive &ar)
Definition funcimpl.h:1445
Range< typename FunctionImpl< Q, NDIM >::dcT::const_iterator > rangeT
Definition funcimpl.h:1431
T alpha
the current function impl
Definition funcimpl.h:1433
const bool do_leaves
start with leaf nodes instead of initial_level
Definition funcimpl.h:7523
T operator()(T a, T b) const
Definition funcimpl.h:7541
do_inner_ext_local_ffi(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > f, const implT *impl, const bool leaf_refine, const bool do_leaves)
Definition funcimpl.h:7525
void serialize(const Archive &ar)
Definition funcimpl.h:7545
const bool leaf_refine
Definition funcimpl.h:7522
const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > fref
Definition funcimpl.h:7520
T operator()(typename dcT::const_iterator &it) const
Definition funcimpl.h:7529
const implT * impl
Definition funcimpl.h:7521
const FunctionImpl< T, NDIM > * bra
Definition funcimpl.h:6455
void serialize(const Archive &ar)
Definition funcimpl.h:6570
const FunctionImpl< R, NDIM > * ket
Definition funcimpl.h:6456
bool leaves_only
Definition funcimpl.h:6457
do_inner_local_on_demand(const FunctionImpl< T, NDIM > *bra, const FunctionImpl< R, NDIM > *ket, const bool leaves_only=true)
Definition funcimpl.h:6460
resultT operator()(resultT a, resultT b) const
Definition funcimpl.h:6566
resultT operator()(typename dcT::const_iterator &it) const
Definition funcimpl.h:6463
resultT operator()(resultT a, resultT b) const
Definition funcimpl.h:6426
bool leaves_only
Definition funcimpl.h:6395
void serialize(const Archive &ar)
Definition funcimpl.h:6430
do_inner_local(const FunctionImpl< R, NDIM > *other, const bool leaves_only)
Definition funcimpl.h:6398
const FunctionImpl< R, NDIM > * other
Definition funcimpl.h:6394
resultT operator()(typename dcT::const_iterator &it) const
Definition funcimpl.h:6400
typedef TENSOR_RESULT_TYPE(T, R) resultT
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2504
do_keep_sum_coeffs(implT *impl)
constructor need impl for cdata
Definition funcimpl.h:2508
implT * impl
Definition funcimpl.h:2505
void serialize(const Archive &ar)
Definition funcimpl.h:2517
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2510
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2787
implT * f
Definition funcimpl.h:2781
std::vector< long > mirror
Definition funcimpl.h:2780
void serialize(const Archive &ar)
Definition funcimpl.h:2834
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2778
std::vector< long > map
Definition funcimpl.h:2780
do_map_and_mirror(const std::vector< long > map, const std::vector< long > mirror, implT &f)
Definition funcimpl.h:2784
do_mapdim(const std::vector< long > map, implT &f)
Definition funcimpl.h:2704
void serialize(const Archive &ar)
Definition funcimpl.h:2720
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2698
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2706
std::vector< long > map
Definition funcimpl.h:2700
do_mapdim()
Definition funcimpl.h:2703
implT * f
Definition funcimpl.h:2701
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2736
implT * f
Definition funcimpl.h:2731
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2728
do_mirror()
Definition funcimpl.h:2733
do_mirror(const std::vector< long > mirror, implT &f)
Definition funcimpl.h:2734
void serialize(const Archive &ar)
Definition funcimpl.h:2770
std::vector< long > mirror
Definition funcimpl.h:2730
double operator()(typename dcT::const_iterator &it) const
Definition funcimpl.h:6361
bool leaves_only
skip the internal nodes, cf. has_coefficients_on_leaves_only()
Definition funcimpl.h:6356
do_norm2sq_local(bool leaves_only)
Definition funcimpl.h:6359
void serialize(const Archive &ar)
Definition funcimpl.h:6377
double operator()(double a, double b) const
Definition funcimpl.h:6373
void serialize(Archive &ar)
Definition funcimpl.h:5321
Key< OPDIM > d
Definition funcimpl.h:5313
Key< OPDIM > key
Definition funcimpl.h:5313
keyT dest
Definition funcimpl.h:5314
double fac
Definition funcimpl.h:5315
do_op_args(const Key< OPDIM > &key, const Key< OPDIM > &d, const keyT &dest, double tol, double fac, double cnorm)
Definition funcimpl.h:5318
double cnorm
Definition funcimpl.h:5315
double tol
Definition funcimpl.h:5315
do_reduce_rank(const TensorArgs &targs)
Definition funcimpl.h:2531
TensorArgs args
Definition funcimpl.h:2527
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2537
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2524
do_reduce_rank(const double &thresh)
Definition funcimpl.h:2532
void serialize(const Archive &ar)
Definition funcimpl.h:2543
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:5287
do_standard(implT *impl)
Definition funcimpl.h:5284
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:5277
void serialize(const Archive &ar)
Definition funcimpl.h:5304
implT * impl
Definition funcimpl.h:5280
void serialize(const Archive &ar)
Definition funcimpl.h:2464
const implT * f
Definition funcimpl.h:2444
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2450
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2443
double thresh
Definition funcimpl.h:2445
do_truncate_NS_leafs(const implT *f)
Definition funcimpl.h:2447
do_truncate_NS_leafs(const implT *f, const double thresh)
Definition funcimpl.h:2448
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2927
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2931
implT * impl
Definition funcimpl.h:2928
void serialize(const Archive &ar)
Definition funcimpl.h:2949
do_unary_op_value_inplace(implT *impl, const opT &op)
Definition funcimpl.h:2930
this_type forward_ctor(implT *result1, const ctL &p11, const ctL &p22, const leaf_opT &leaf_op)
Definition funcimpl.h:3975
bool randomize() const
Definition funcimpl.h:3920
void serialize(const Archive &ar)
Definition funcimpl.h:3979
hartree_op(implT *result, const ctL &p11, const ctL &p22, const leaf_opT &leaf_op)
Definition funcimpl.h:3931
CoeffTracker< T, LDIM > ctL
Definition funcimpl.h:3923
ctL p2
tracking coeffs of the two lo-dim functions
Definition funcimpl.h:3926
leaf_opT leaf_op
determine if a given node will be a leaf node
Definition funcimpl.h:3927
hartree_op()
Definition funcimpl.h:3930
implT * result
where to construct the pair function
Definition funcimpl.h:3925
hartree_op< LDIM, leaf_opT > this_type
Definition funcimpl.h:3922
std::pair< bool, coeffT > operator()(const Key< NDIM > &key) const
Definition funcimpl.h:3936
ctL p1
Definition funcimpl.h:3926
this_type make_child(const keyT &child) const
Definition funcimpl.h:3959
Future< this_type > activate() const
Definition funcimpl.h:3968
multiply_op()
Definition funcimpl.h:3739
ctL g
Definition funcimpl.h:3736
Future< this_type > activate() const
Definition funcimpl.h:3818
CoeffTracker< T, LDIM > ctL
Definition funcimpl.h:3731
implT * h
the result function h(1,2) = f(1,2) * g(1)
Definition funcimpl.h:3734
CoeffTracker< T, NDIM > ctT
Definition funcimpl.h:3730
std::pair< bool, coeffT > operator()(const Key< NDIM > &key) const
apply this on a FunctionNode of f and g of Key key
Definition funcimpl.h:3766
this_type forward_ctor(implT *h1, const ctT &f1, const ctL &g1, const int particle)
Definition funcimpl.h:3825
static bool randomize()
Definition funcimpl.h:3729
int particle
if g is g(1) or g(2)
Definition funcimpl.h:3737
ctT f
Definition funcimpl.h:3735
multiply_op< LDIM > this_type
Definition funcimpl.h:3732
multiply_op(implT *h1, const ctT &f1, const ctL &g1, const int particle1)
Definition funcimpl.h:3741
bool screen(const coeffT &fcoeff, const coeffT &gcoeff, const keyT &key) const
return true if this will be a leaf node
Definition funcimpl.h:3747
this_type make_child(const keyT &child) const
Definition funcimpl.h:3808
void serialize(const Archive &ar)
Definition funcimpl.h:3829
coeffT val_lhs
Definition funcimpl.h:4206
double lo
Definition funcimpl.h:4209
double lo1
Definition funcimpl.h:4209
long oversampling
Definition funcimpl.h:4207
double error
Definition funcimpl.h:4208
tensorT operator()(const Key< NDIM > key, const tensorT &coeff_rhs)
multiply values of rhs and lhs, result on rhs, rhs and lhs are of the same dimensions
Definition funcimpl.h:4224
coeffT coeff_lhs
Definition funcimpl.h:4206
void serialize(const Archive &ar)
Definition funcimpl.h:4312
double lo2
Definition funcimpl.h:4209
double hi1
Definition funcimpl.h:4209
pointwise_multiplier(const Key< NDIM > key, const coeffT &clhs)
Definition funcimpl.h:4212
coeffT operator()(const Key< NDIM > key, const tensorT &coeff_rhs, const int particle)
multiply values of rhs and lhs, result on rhs, rhs and lhs are of differnet dimensions
Definition funcimpl.h:4269
double hi2
Definition funcimpl.h:4209
double hi
Definition funcimpl.h:4209
project_out_op(const implT *fimpl, implL1 *result, const ctL &iag, const int dim)
Definition funcimpl.h:7800
ctL iag
the low dim function g
Definition funcimpl.h:7795
FunctionImpl< T, NDIM-LDIM > implL1
Definition funcimpl.h:7790
Future< this_type > activate() const
retrieve the coefficients (parent coeffs might be remote)
Definition funcimpl.h:7879
std::pair< bool, coeffT > argT
Definition funcimpl.h:7791
const implT * fimpl
the hi dim function f
Definition funcimpl.h:7793
this_type forward_ctor(const implT *fimpl1, implL1 *result1, const ctL &iag1, const int dim1)
taskq-compatible ctor
Definition funcimpl.h:7886
this_type make_child(const keyT &child) const
Definition funcimpl.h:7870
project_out_op< LDIM > this_type
Definition funcimpl.h:7788
implL1 * result
the low dim result function
Definition funcimpl.h:7794
Future< argT > operator()(const Key< NDIM > &key) const
do the actual contraction
Definition funcimpl.h:7807
void serialize(const Archive &ar)
Definition funcimpl.h:7890
project_out_op(const project_out_op &other)
Definition funcimpl.h:7802
int dim
0: project 0..LDIM-1, 1: project LDIM..NDIM-1
Definition funcimpl.h:7796
bool randomize() const
Definition funcimpl.h:7786
CoeffTracker< T, LDIM > ctL
Definition funcimpl.h:7789
ctT iaf
Definition funcimpl.h:6189
recursive_apply_op2< opT > this_type
Definition funcimpl.h:6184
Future< this_type > activate() const
retrieve the coefficients (parent coeffs might be remote)
Definition funcimpl.h:6244
const opT * apply_op
need this for randomization
Definition funcimpl.h:6190
bool randomize() const
Definition funcimpl.h:6182
recursive_apply_op2(const recursive_apply_op2 &other)
Definition funcimpl.h:6197
void serialize(const Archive &ar)
Definition funcimpl.h:6260
argT finalize(const double kernel_norm, const keyT &key, const coeffT &coeff, const implT *r) const
sole purpose is to wait for the kernel norm, wrap it and send it back to caller
Definition funcimpl.h:6230
this_type make_child(const keyT &child) const
Definition funcimpl.h:6239
recursive_apply_op2(implT *result, const ctT &iaf, const opT *apply_op)
Definition funcimpl.h:6194
std::pair< bool, coeffT > argT
Definition funcimpl.h:6186
implT * result
Definition funcimpl.h:6188
CoeffTracker< T, NDIM > ctT
Definition funcimpl.h:6185
argT operator()(const Key< NDIM > &key) const
send off the application of the operator
Definition funcimpl.h:6206
this_type forward_ctor(implT *result1, const ctT &iaf1, const opT *apply_op1)
taskq-compatible ctor
Definition funcimpl.h:6256
this_type forward_ctor(implT *r, const std::vector< Future< ctL > > &f_futures, const std::vector< Future< ctL > > &g_futures, const opT *apply_op1)
Definition funcimpl.h:6131
std::pair< bool, coeffT > operator()(const Key< NDIM > &key) const
make the summed NS-coefficients and send off the application of the operator
Definition funcimpl.h:6051
recursive_apply_op_vec(implT *result, const std::vector< ctL > &iafs, const std::vector< ctL > &iags, const opT *apply_op)
Definition funcimpl.h:6035
implT * result
Definition funcimpl.h:6028
bool randomize() const
Definition funcimpl.h:6023
recursive_apply_op_vec< opT, LDIM > this_type
Definition funcimpl.h:6025
Future< this_type > activate() const
Definition funcimpl.h:6120
void serialize(const Archive &ar)
Definition funcimpl.h:6145
std::pair< bool, coeffT > finalize(const double kernel_norm, const keyT &key, const coeffT &coeff) const
sole purpose is to wait for the kernel norm, wrap it and send it back to caller
Definition funcimpl.h:6095
recursive_apply_op_vec(const recursive_apply_op_vec &other)
Definition funcimpl.h:6043
std::vector< ctL > iags
Definition funcimpl.h:6030
opT * apply_op
Definition funcimpl.h:6031
std::vector< ctL > iafs
Definition funcimpl.h:6029
this_type make_child(const keyT &child) const
Definition funcimpl.h:6104
CoeffTracker< T, LDIM > ctL
Definition funcimpl.h:6026
std::pair< bool, coeffT > operator()(const Key< NDIM > &key) const
make the NS-coefficients and send off the application of the operator
Definition funcimpl.h:5901
this_type forward_ctor(implT *r, const CoeffTracker< T, LDIM > &f1, const CoeffTracker< T, LDIM > &g1, const opT *apply_op1)
Definition funcimpl.h:5966
opT * apply_op
Definition funcimpl.h:5884
recursive_apply_op(const recursive_apply_op &other)
Definition funcimpl.h:5894
recursive_apply_op< opT, LDIM > this_type
Definition funcimpl.h:5879
Future< this_type > activate() const
Definition funcimpl.h:5959
bool randomize() const
Definition funcimpl.h:5877
implT * result
Definition funcimpl.h:5881
CoeffTracker< T, LDIM > iaf
Definition funcimpl.h:5882
void serialize(const Archive &ar)
Definition funcimpl.h:5971
std::pair< bool, coeffT > finalize(const double kernel_norm, const keyT &key, const coeffT &coeff) const
sole purpose is to wait for the kernel norm, wrap it and send it back to caller
Definition funcimpl.h:5941
recursive_apply_op(implT *result, const CoeffTracker< T, LDIM > &iaf, const CoeffTracker< T, LDIM > &iag, const opT *apply_op)
Definition funcimpl.h:5888
this_type make_child(const keyT &child) const
Definition funcimpl.h:5950
CoeffTracker< T, LDIM > iag
Definition funcimpl.h:5883
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2470
remove_internal_coeffs()=default
constructor need impl for cdata
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2475
void serialize(const Archive &ar)
Definition funcimpl.h:2481
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2492
remove_leaf_coeffs()=default
constructor need impl for cdata
void serialize(const Archive &ar)
Definition funcimpl.h:2497
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2487
Definition funcimpl.h:5124
void serialize(Archive &ar)
Definition funcimpl.h:5128
bool operator()(const implT *f, const keyT &key, const nodeT &t) const
Definition funcimpl.h:5125
shallow-copy, pared-down version of FunctionNode, for special purpose only
Definition funcimpl.h:845
coeffT & coeff()
Definition funcimpl.h:859
GenTensor< T > coeffT
Definition funcimpl.h:846
bool is_leaf() const
Definition funcimpl.h:861
void serialize(Archive &ar)
Definition funcimpl.h:863
ShallowNode(const ShallowNode< T, NDIM > &node)
Definition funcimpl.h:854
ShallowNode(const FunctionNode< T, NDIM > &node)
Definition funcimpl.h:851
bool has_children() const
Definition funcimpl.h:860
ShallowNode()
Definition funcimpl.h:850
bool _has_children
Definition funcimpl.h:848
double dnorm
Definition funcimpl.h:849
const coeffT & coeff() const
Definition funcimpl.h:858
coeffT _coeffs
Definition funcimpl.h:847
Definition displacements.h:483
TensorArgs holds the arguments for creating a LowRankTensor.
Definition gentensor.h:134
double thresh
Definition gentensor.h:135
const uniqueidT & id() const
Returns the globally unique object ID.
Definition world_object.h:424
inserts/accumulates coefficients into impl's tree
Definition funcimpl.h:812
FunctionImpl< T, NDIM > * impl
Definition funcimpl.h:816
FunctionNode< T, NDIM > nodeT
Definition funcimpl.h:814
accumulate_op(const accumulate_op &other)=default
void operator()(const Key< NDIM > &key, const coeffT &coeff, const bool &is_leaf) const
Definition funcimpl.h:820
void serialize(Archive &ar)
Definition funcimpl.h:824
GenTensor< T > coeffT
Definition funcimpl.h:813
accumulate_op(FunctionImpl< T, NDIM > *f)
Definition funcimpl.h:818
static void load(const Archive &ar, FunctionImpl< T, NDIM > *&ptr)
Definition funcimpl.h:8114
static void load(const Archive &ar, const FunctionImpl< T, NDIM > *&ptr)
Definition funcimpl.h:8083
static void load(const Archive &ar, std::shared_ptr< FunctionImpl< T, NDIM > > &ptr)
Definition funcimpl.h:8165
static void load(const Archive &ar, std::shared_ptr< const FunctionImpl< T, NDIM > > &ptr)
Definition funcimpl.h:8149
Default load of an object via serialize(ar, t).
Definition archive.h:667
static void load(const A &ar, const U &t)
Load an object.
Definition archive.h:679
static void store(const Archive &ar, FunctionImpl< T, NDIM > *const &ptr)
Definition funcimpl.h:8139
static void store(const Archive &ar, const FunctionImpl< T, NDIM > *const &ptr)
Definition funcimpl.h:8105
static void store(const Archive &ar, const std::shared_ptr< FunctionImpl< T, NDIM > > &ptr)
Definition funcimpl.h:8174
static void store(const Archive &ar, const std::shared_ptr< const FunctionImpl< T, NDIM > > &ptr)
Definition funcimpl.h:8158
Default store of an object via serialize(ar, t).
Definition archive.h:612
static std::enable_if_t< is_output_archive_v< A > &&!std::is_function< U >::value &&(has_member_serialize_v< U, A >||has_nonmember_serialize_v< U, A >||has_freestanding_serialize_v< U, A >||has_freestanding_default_serialize_v< U, A >), void > store(const A &ar, const U &t)
Definition archive.h:622
void serialize(Archive &ar)
Definition funcimpl.h:770
const opT * op
Definition funcimpl.h:715
hartree_convolute_leaf_op(const implT *f, const implL *g, const opT *op)
Definition funcimpl.h:719
bool operator()(const Key< NDIM > &key) const
no pre-determination
Definition funcimpl.h:723
bool operator()(const Key< NDIM > &key, const Tensor< T > &fcoeff, const Tensor< T > &gcoeff) const
post-determination: true if f is a leaf and the result is well-represented
Definition funcimpl.h:736
const implL * g
Definition funcimpl.h:714
const FunctionImpl< T, NDIM > * f
Definition funcimpl.h:713
FunctionImpl< T, LDIM > implL
Definition funcimpl.h:711
bool do_error_leaf_op() const
Definition funcimpl.h:716
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:710
bool operator()(const Key< NDIM > &key, const GenTensor< T > &coeff) const
no post-determination
Definition funcimpl.h:726
returns true if the sum of hartree products is a leaf node (compute combined norm & error)
Definition funcimpl.h:606
bool operator()(const Key< NDIM > &key) const
no pre-determination
Definition funcimpl.h:617
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:608
bool operator()(const Key< NDIM > &key, const std::vector< Tensor< T > > &fcoeffs, const std::vector< Tensor< T > > &gcoeffs) const
post-determination for vector of pairs: true if well-represented
Definition funcimpl.h:624
long k
Definition funcimpl.h:610
void serialize(Archive &ar)
Definition funcimpl.h:656
hartree_leaf_op_vec(const implT *f, const long &k)
Definition funcimpl.h:614
const FunctionImpl< T, NDIM > * f
Definition funcimpl.h:609
bool do_error_leaf_op() const
Definition funcimpl.h:611
returns true if the result of a hartree_product is a leaf node (compute norm & error)
Definition funcimpl.h:539
bool do_error_leaf_op() const
Definition funcimpl.h:544
const FunctionImpl< T, NDIM > * f
Definition funcimpl.h:542
hartree_leaf_op(const implT *f, const long &k)
Definition funcimpl.h:547
long k
Definition funcimpl.h:543
void serialize(Archive &ar)
Definition funcimpl.h:595
bool operator()(const Key< NDIM > &key, const GenTensor< T > &coeff) const
no post-determination
Definition funcimpl.h:553
bool operator()(const Key< NDIM > &key, const Tensor< T > &fcoeff, const Tensor< T > &gcoeff) const
post-determination: true if f is a leaf and the result is well-represented
Definition funcimpl.h:563
bool operator()(const Key< NDIM > &key) const
no pre-determination
Definition funcimpl.h:550
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:541
insert/replaces the coefficients into the function
Definition funcimpl.h:788
insert_op()
Definition funcimpl.h:795
implT * impl
Definition funcimpl.h:794
void operator()(const keyT &key, const coeffT &coeff, const bool &is_leaf) const
Definition funcimpl.h:798
FunctionNode< T, NDIM > nodeT
Definition funcimpl.h:792
Key< NDIM > keyT
Definition funcimpl.h:790
insert_op(const insert_op &other)
Definition funcimpl.h:797
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:789
GenTensor< T > coeffT
Definition funcimpl.h:791
insert_op(implT *f)
Definition funcimpl.h:796
void serialize(Archive &ar)
Definition funcimpl.h:802
Definition mra.h:109
Definition funcimpl.h:776
bool operator()(const Key< NDIM > &key, const GenTensor< T > &fcoeff, const GenTensor< T > &gcoeff) const
Definition funcimpl.h:778
void serialize(Archive &ar)
Definition funcimpl.h:782
void operator()(const Key< NDIM > &key, const GenTensor< T > &coeff, const bool &is_leaf) const
Definition funcimpl.h:777
bool operator()(const Key< NDIM > &key, const double &cnorm) const
post-determination: return true if operator and coefficient norms are small
Definition funcimpl.h:685
void serialize(Archive &ar)
Definition funcimpl.h:698
const implT * f
the source or result function, needed for truncate_tol
Definition funcimpl.h:668
op_leaf_op(const opT *op, const implT *f)
Definition funcimpl.h:672
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:665
const opT * op
the convolution operator
Definition funcimpl.h:667
bool do_error_leaf_op() const
Definition funcimpl.h:669
bool operator()(const Key< NDIM > &key) const
pre-determination: we can't know if this will be a leaf node before we got the final coeffs
Definition funcimpl.h:675
bool operator()(const Key< NDIM > &key, const GenTensor< T > &coeff) const
post-determination: return true if operator and coefficient norms are small
Definition funcimpl.h:678
Definition lowrankfunction.h:387
Definition funcimpl.h:832
void serialize(Archive &ar)
Definition funcimpl.h:839
bool operator()(const Key< NDIM > &key, const T &t, const R &r) const
Definition funcimpl.h:838
bool operator()(const Key< NDIM > &key, const T &t) const
Definition funcimpl.h:835
int np
Definition tdse1d.cc:165
static const double s0
Definition tdse4.cc:83
Defines and implements most of Tensor.
#define ITERATOR(t, exp)
Definition tensor_macros.h:249
#define IND
Definition tensor_macros.h:204
#define TERNARY_OPTIMIZED_ITERATOR(X, x, Y, y, Z, z, exp)
Definition tensor_macros.h:719
double norm(const T i1)
Definition test_cloud.cc:85
int task(int i)
Definition test_runtime.cpp:4
void e()
Definition test_sig.cc:75
static double g1(const Vector< double, D > &r)
Definition test_state_archive_hdf5.cpp:34
static const double alpha
Definition testcosine.cc:10
const double offset
Definition testfuns.cc:143
constexpr std::size_t NDIM
Definition testgconv.cc:54
double h(const coord_1d &r)
Definition testgconv.cc:175
std::size_t axis
Definition testpdiff.cc:59
double source(const coordT &r)
Definition testperiodic.cc:48
#define TENSOR_RESULT_TYPE(L, R)
This macro simplifies access to TensorResultType.
Definition type_data.h:205
#define PROFILE_MEMBER_FUNC(classname)
Definition worldprofile.h:210
#define PROFILE_BLOCK(name)
Definition worldprofile.h:208
int ProcessID
Used to clearly identify process number/rank.
Definition worldtypes.h:43
Key< D > keyT
Definition writecoeff2.cc:11