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
38#include <iostream>
39#include <type_traits>
41#include <madness/world/print.h>
42#include <madness/misc/misc.h>
45
47#include <madness/mra/indexit.h>
48#include <madness/mra/key.h>
51
52#include "leafop.h"
53
54namespace madness {
55 template <typename T, std::size_t NDIM>
56 class DerivativeBase;
57
58 template<typename T, std::size_t NDIM>
59 class FunctionImpl;
60
61 template<typename T, std::size_t NDIM>
62 class FunctionNode;
63
64 template<typename T, std::size_t NDIM>
65 class Function;
66
67 template<typename T, std::size_t NDIM>
68 class FunctionFactory;
69
70 template<typename T, std::size_t NDIM, std::size_t MDIM>
71 class CompositeFunctorInterface;
72
73 template<int D>
75
76}
77
78namespace madness {
79
80
81 /// A simple process map
82 template<typename keyT>
83 class SimplePmap : public WorldDCPmapInterface<keyT> {
84 private:
85 const int nproc;
87
88 public:
89 SimplePmap(World& world) : nproc(world.nproc()), me(world.rank())
90 { }
91
92 ProcessID owner(const keyT& key) const {
93 if (key.level() == 0)
94 return 0;
95 else
96 return key.hash() % nproc;
97 }
98 };
99
100 /// A pmap that locates children on odd levels with their even level parents
101 template <typename keyT>
102 class LevelPmap : public WorldDCPmapInterface<keyT> {
103 private:
104 const int nproc;
105 public:
106 LevelPmap() : nproc(0) {};
107
108 LevelPmap(World& world) : nproc(world.nproc()) {}
109
110 /// Find the owner of a given key
111 ProcessID owner(const keyT& key) const {
112 Level n = key.level();
113 if (n == 0) return 0;
114 hashT hash;
115 if (n <= 3 || (n&0x1)) hash = key.hash();
116 else hash = key.parent().hash();
117 return hash%nproc;
118 }
119 };
120
121
122 /// FunctionNode holds the coefficients, etc., at each node of the 2^NDIM-tree
123 template<typename T, std::size_t NDIM>
125 public:
128 private:
129 // Should compile OK with these volatile but there should
130 // be no need to set as volatile since the container internally
131 // stores the entire entry as volatile
132
133 coeffT _coeffs; ///< The coefficients, if any
134 double _norm_tree; ///< After norm_tree will contain norm of coefficients summed up tree
135 bool _has_children; ///< True if there are children
136 coeffT buffer; ///< The coefficients, if any
137 double dnorm=-1.0; ///< norm of the d coefficients, also defined if there are no d coefficients
138 double snorm=-1.0; ///< norm of the s coefficients
139
140 public:
141 typedef WorldContainer<Key<NDIM> , FunctionNode<T, NDIM> > dcT; ///< Type of container holding the nodes
142 /// Default constructor makes node without coeff or children
144 _coeffs(), _norm_tree(1e300), _has_children(false) {
145 }
146
147 /// Constructor from given coefficients with optional children
148
149 /// Note that only a shallow copy of the coeff are taken so
150 /// you should pass in a deep copy if you want the node to
151 /// take ownership.
152 explicit
156
157 explicit
161
162 explicit
166
169 dnorm(other.dnorm), snorm(other.snorm) {
170 }
171
174 if (this != &other) {
175 coeff() = copy(other.coeff());
176 _norm_tree = other._norm_tree;
178 dnorm=other.dnorm;
179 snorm=other.snorm;
181 }
182 return *this;
183 }
184
185 /// Copy with possible type conversion of coefficients, copying all other state
186
187 /// Choose to not overload copy and type conversion operators
188 /// so there are no automatic type conversions.
189 template<typename Q>
191 convert() const {
192 return FunctionNode<Q, NDIM> (madness::convert<Q,T>(coeff()), _norm_tree, snorm, dnorm, _has_children);
193 }
194
195 /// Returns true if there are coefficients in this node
196 bool
197 has_coeff() const {
198 return _coeffs.has_data();
199 }
200
201
202 /// Returns true if this node has children
203 bool
204 has_children() const {
205 return _has_children;
206 }
207
208 /// Returns true if this does not have children
209 bool
210 is_leaf() const {
211 return !_has_children;
212 }
213
214 /// Returns true if this node is invalid (no coeffs and no children)
215 bool
216 is_invalid() const {
217 return !(has_coeff() || has_children());
218 }
219
220 /// Returns a non-const reference to the tensor containing the coeffs
221
222 /// Returns an empty tensor if there are no coefficients.
223 coeffT&
225 MADNESS_ASSERT(_coeffs.ndim() == -1 || (_coeffs.dim(0) <= 2
226 * MAXK && _coeffs.dim(0) >= 0));
227 return const_cast<coeffT&>(_coeffs);
228 }
229
230 /// Returns a const reference to the tensor containing the coeffs
231
232 /// Returns an empty tensor if there are no coefficeints.
233 const coeffT&
234 coeff() const {
235 return const_cast<const coeffT&>(_coeffs);
236 }
237
238 /// Returns the number of coefficients in this node
239 size_t size() const {
240 return _coeffs.size();
241 }
242
243 public:
244
245 /// reduces the rank of the coefficients (if applicable)
246 void reduceRank(const double& eps) {
247 _coeffs.reduce_rank(eps);
248 }
249
250 /// Sets \c has_children attribute to value of \c flag.
251 void set_has_children(bool flag) {
252 _has_children = flag;
253 }
254
255 /// Sets \c has_children attribute to true recurring up to ensure connected
257 //madness::print(" set_chi_recu: ", key, *this);
258 //PROFILE_MEMBER_FUNC(FunctionNode); // Too fine grain for routine profiling
259 if (!(has_children() || has_coeff() || key.level()==0)) {
260 // If node already knows it has children or it has
261 // coefficients then it must already be connected to
262 // its parent. If not, the node was probably just
263 // created for this operation and must be connected to
264 // its parent.
265 Key<NDIM> parent = key.parent();
266 // Task on next line used to be TaskAttributes::hipri()) ... but deferring execution of this
267 // makes sense since it is not urgent and lazy connection will likely mean that less forwarding
268 // will happen since the upper level task will have already made the connection.
269 const_cast<dcT&>(c).task(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
270 //const_cast<dcT&>(c).send(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
271 //madness::print(" set_chi_recu: forwarding",key,parent);
272 }
273 _has_children = true;
274 }
275
276 /// Sets \c has_children attribute to value of \c !flag
277 void set_is_leaf(bool flag) {
278 _has_children = !flag;
279 }
280
281 /// Takes a \em shallow copy of the coeff --- same as \c this->coeff()=coeff
282 void set_coeff(const coeffT& coeffs) {
283 coeff() = coeffs;
284 if ((_coeffs.has_data()) and ((_coeffs.dim(0) < 0) || (_coeffs.dim(0)>2*MAXK))) {
285 print("set_coeff: may have a problem");
286 print("set_coeff: coeff.dim[0] =", coeffs.dim(0), ", 2* MAXK =", 2*MAXK);
287 }
288 MADNESS_ASSERT(coeffs.dim(0)<=2*MAXK && coeffs.dim(0)>=0);
289 }
290
291 /// Clears the coefficients (has_coeff() will subsequently return false)
292 void clear_coeff() {
293 coeff()=coeffT();
294 }
295
296 /// Scale the coefficients of this node
297 template <typename Q>
298 void scale(Q a) {
299 _coeffs.scale(a);
300 }
301
302 /// Sets the value of norm_tree
305 }
306
307 /// Gets the value of norm_tree
308 double get_norm_tree() const {
309 return _norm_tree;
310 }
311
312 /// return the precomputed norm of the (virtual) d coefficients
313 double get_dnorm() const {
314 return dnorm;
315 }
316
317 /// set the precomputed norm of the (virtual) s coefficients
318 void set_snorm(const double sn) {
319 snorm=sn;
320 }
321
322 /// set the precomputed norm of the (virtual) d coefficients
323 void set_dnorm(const double dn) {
324 dnorm=dn;
325 }
326
327 /// get the precomputed norm of the (virtual) s coefficients
328 double get_snorm() const {
329 return snorm;
330 }
331
333 snorm = 0.0;
334 dnorm = 0.0;
335 if (coeff().size() == 0) { ;
336 } else if (coeff().dim(0) == cdata.vk[0]) {
337 snorm = coeff().normf();
338
339 } else if (coeff().is_full_tensor()) {
340 Tensor<T> c = copy(coeff().get_tensor());
341 snorm = c(cdata.s0).normf();
342 c(cdata.s0) = 0.0;
343 dnorm = c.normf();
344
345 } else if (coeff().is_svd_tensor()) {
346 coeffT c= coeff()(cdata.s0);
347 snorm = c.normf();
348 double norm = coeff().normf();
349 dnorm = sqrt(norm * norm - snorm * snorm);
350
351 } else {
352 MADNESS_EXCEPTION("cannot use compute_dnorm", 1);
353 }
354 }
355
356
357 /// General bi-linear operation --- this = this*alpha + other*beta
358
359 /// This/other may not have coefficients. Has_children will be
360 /// true in the result if either this/other have children.
361 template <typename Q, typename R>
362 void gaxpy_inplace(const T& alpha, const FunctionNode<Q,NDIM>& other, const R& beta) {
363 //PROFILE_MEMBER_FUNC(FuncNode); // Too fine grain for routine profiling
364 if (other.has_children())
365 _has_children = true;
366 if (has_coeff()) {
367 if (other.has_coeff()) {
368 coeff().gaxpy(alpha,other.coeff(),beta);
369 }
370 else {
371 coeff().scale(alpha);
372 }
373 }
374 else if (other.has_coeff()) {
375 coeff() = other.coeff()*beta; //? Is this the correct type conversion?
376 }
377 }
378
379 /// Accumulate inplace and if necessary connect node to parent
380 void accumulate2(const tensorT& t, const typename FunctionNode<T,NDIM>::dcT& c,
381 const Key<NDIM>& key) {
382 // double cpu0=cpu_time();
383 if (has_coeff()) {
384 MADNESS_ASSERT(coeff().is_full_tensor());
385 // if (coeff().type==TT_FULL) {
386 coeff() += coeffT(t,-1.0,TT_FULL);
387 // } else {
388 // tensorT cc=coeff().full_tensor_copy();;
389 // cc += t;
390 // coeff()=coeffT(cc,args);
391 // }
392 }
393 else {
394 // No coeff and no children means the node is newly
395 // created for this operation and therefore we must
396 // tell its parent that it exists.
397 coeff() = coeffT(t,-1.0,TT_FULL);
398 // coeff() = copy(t);
399 // coeff() = coeffT(t,args);
400 if ((!_has_children) && key.level()> 0) {
401 Key<NDIM> parent = key.parent();
402 if (c.is_local(parent))
403 const_cast<dcT&>(c).send(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
404 else
405 const_cast<dcT&>(c).task(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
406 }
407 }
408 //double cpu1=cpu_time();
409 }
410
411
412 /// Accumulate inplace and if necessary connect node to parent
413 void accumulate(const coeffT& t, const typename FunctionNode<T,NDIM>::dcT& c,
414 const Key<NDIM>& key, const TensorArgs& args) {
415 if (has_coeff()) {
416 coeff().add_SVD(t,args.thresh);
417 if (buffer.rank()<coeff().rank()) {
418 if (buffer.has_data()) {
419 buffer.add_SVD(coeff(),args.thresh);
420 } else {
421 buffer=copy(coeff());
422 }
423 coeff()=coeffT();
424 }
425
426 } else {
427 // No coeff and no children means the node is newly
428 // created for this operation and therefore we must
429 // tell its parent that it exists.
430 coeff() = copy(t);
431 if ((!_has_children) && key.level()> 0) {
432 Key<NDIM> parent = key.parent();
433 if (c.is_local(parent))
434 const_cast<dcT&>(c).send(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
435 else
436 const_cast<dcT&>(c).task(parent, &FunctionNode<T,NDIM>::set_has_children_recursive, c, parent);
437 }
438 }
439 }
440
441 void consolidate_buffer(const TensorArgs& args) {
442 if ((coeff().has_data()) and (buffer.has_data())) {
443 coeff().add_SVD(buffer,args.thresh);
444 } else if (buffer.has_data()) {
445 coeff()=buffer;
446 }
447 buffer=coeffT();
448 }
449
451 return this->_coeffs.trace_conj((rhs._coeffs));
452 }
453
454 template <typename Archive>
455 void serialize(Archive& ar) {
457 }
458
459 /// like operator<<(ostream&, const FunctionNode<T,NDIM>&) but
460 /// produces a sequence JSON-formatted key-value pairs
461 /// @warning enclose the output in curly braces to make
462 /// a valid JSON object
463 void print_json(std::ostream& s) const {
464 s << "\"has_coeff\":" << this->has_coeff()
465 << ",\"has_children\":" << this->has_children() << ",\"norm\":";
466 double norm = this->has_coeff() ? this->coeff().normf() : 0.0;
467 if (norm < 1e-12)
468 norm = 0.0;
469 double nt = this->get_norm_tree();
470 if (nt == 1e300)
471 nt = 0.0;
472 s << norm << ",\"norm_tree\":" << nt << ",\"snorm\":"
473 << this->get_snorm() << ",\"dnorm\":" << this->get_dnorm()
474 << ",\"rank\":" << this->coeff().rank();
475 if (this->coeff().is_assigned())
476 s << ",\"dim\":" << this->coeff().dim(0);
477 }
478
479 };
480
481 template <typename T, std::size_t NDIM>
482 std::ostream& operator<<(std::ostream& s, const FunctionNode<T,NDIM>& node) {
483 s << "(has_coeff=" << node.has_coeff() << ", has_children=" << node.has_children() << ", norm=";
484 double norm = node.has_coeff() ? node.coeff().normf() : 0.0;
485 if (norm < 1e-12)
486 norm = 0.0;
487 double nt = node.get_norm_tree();
488 if (nt == 1e300) nt = 0.0;
489 s << norm << ", norm_tree, s/dnorm =" << nt << ", " << node.get_snorm() << " " << node.get_dnorm() << "), rank="<< node.coeff().rank()<<")";
490 if (node.coeff().is_assigned()) s << " dim " << node.coeff().dim(0) << " ";
491 return s;
492 }
493
494
495 /// returns true if the result of a hartree_product is a leaf node (compute norm & error)
496 template<typename T, size_t NDIM>
498
501 long k;
502 bool do_error_leaf_op() const {return false;}
503
504 hartree_leaf_op() = default;
505 hartree_leaf_op(const implT* f, const long& k) : f(f), k(k) {}
506
507 /// no pre-determination
508 bool operator()(const Key<NDIM>& key) const {return false;}
509
510 /// no post-determination
511 bool operator()(const Key<NDIM>& key, const GenTensor<T>& coeff) const {
512 MADNESS_EXCEPTION("no post-determination in hartree_leaf_op",1);
513 return true;
514 }
515
516 /// post-determination: true if f is a leaf and the result is well-represented
517
518 /// @param[in] key the hi-dimensional key (breaks into keys for f and g)
519 /// @param[in] fcoeff coefficients of f of its appropriate key in NS form
520 /// @param[in] gcoeff coefficients of g of its appropriate key in NS form
521 bool operator()(const Key<NDIM>& key, const Tensor<T>& fcoeff, const Tensor<T>& gcoeff) const {
522
523 if (key.level()<2) return false;
524 Slice s = Slice(0,k-1);
525 std::vector<Slice> s0(NDIM/2,s);
526
527 const double tol=f->get_thresh();
528 const double thresh=f->truncate_tol(tol, key)*0.3; // custom factor to "ensure" accuracy
529 // include the wavelets in the norm, makes it much more accurate
530 const double fnorm=fcoeff.normf();
531 const double gnorm=gcoeff.normf();
532
533 // if the final norm is small, perform the hartree product and return
534 const double norm=fnorm*gnorm; // computing the outer product
535 if (norm < thresh) return true;
536
537 // norm of the scaling function coefficients
538 const double sfnorm=fcoeff(s0).normf();
539 const double sgnorm=gcoeff(s0).normf();
540
541 // get the error of both functions and of the pair function;
542 // need the abs for numerics: sfnorm might be equal fnorm.
543 const double ferror=sqrt(std::abs(fnorm*fnorm-sfnorm*sfnorm));
544 const double gerror=sqrt(std::abs(gnorm*gnorm-sgnorm*sgnorm));
545
546 // if the expected error is small, perform the hartree product and return
547 const double error=fnorm*gerror + ferror*gnorm + ferror*gerror;
548 // const double error=sqrt(fnorm*fnorm*gnorm*gnorm - sfnorm*sfnorm*sgnorm*sgnorm);
549
550 if (error < thresh) return true;
551 return false;
552 }
553 template <typename Archive> void serialize (Archive& ar) {
554 ar & f & k;
555 }
556 };
557
558 /// returns true if the result of the convolution operator op with some provided
559 /// coefficients will be small
560 template<typename T, size_t NDIM, typename opT>
561 struct op_leaf_op {
563
564 const opT* op; ///< the convolution operator
565 const implT* f; ///< the source or result function, needed for truncate_tol
566 bool do_error_leaf_op() const {return true;}
567
568 op_leaf_op() = default;
569 op_leaf_op(const opT* op, const implT* f) : op(op), f(f) {}
570
571 /// pre-determination: we can't know if this will be a leaf node before we got the final coeffs
572 bool operator()(const Key<NDIM>& key) const {return true;}
573
574 /// post-determination: return true if operator and coefficient norms are small
575 bool operator()(const Key<NDIM>& key, const GenTensor<T>& coeff) const {
576 if (key.level()<2) return false;
577 const double cnorm=coeff.normf();
578 return this->operator()(key,cnorm);
579 }
580
581 /// post-determination: return true if operator and coefficient norms are small
582 bool operator()(const Key<NDIM>& key, const double& cnorm) const {
583 if (key.level()<2) return false;
584
585 typedef Key<opT::opdim> opkeyT;
586 const opkeyT source=op->get_source_key(key);
587
588 const double thresh=f->truncate_tol(f->get_thresh(),key);
589 const std::vector<opkeyT>& disp = op->get_disp(key.level());
590 const opkeyT& d = *disp.begin(); // use the zero-displacement for screening
591 const double opnorm = op->norm(key.level(), d, source);
592 const double norm=opnorm*cnorm;
593 return norm<thresh;
594
595 }
596
597 template <typename Archive> void serialize (Archive& ar) {
598 ar & op & f;
599 }
600
601 };
602
603
604 /// returns true if the result of a hartree_product is a leaf node
605 /// criteria are error, norm and its effect on a convolution operator
606 template<typename T, size_t NDIM, size_t LDIM, typename opT>
608
611
613 const implL* g; // for use of its cdata only
614 const opT* op;
615 bool do_error_leaf_op() const {return false;}
616
618 hartree_convolute_leaf_op(const implT* f, const implL* g, const opT* op)
619 : f(f), g(g), op(op) {}
620
621 /// no pre-determination
622 bool operator()(const Key<NDIM>& key) const {return true;}
623
624 /// no post-determination
625 bool operator()(const Key<NDIM>& key, const GenTensor<T>& coeff) const {
626 MADNESS_EXCEPTION("no post-determination in hartree_convolute_leaf_op",1);
627 return true;
628 }
629
630 /// post-determination: true if f is a leaf and the result is well-represented
631
632 /// @param[in] key the hi-dimensional key (breaks into keys for f and g)
633 /// @param[in] fcoeff coefficients of f of its appropriate key in NS form
634 /// @param[in] gcoeff coefficients of g of its appropriate key in NS form
635 bool operator()(const Key<NDIM>& key, const Tensor<T>& fcoeff, const Tensor<T>& gcoeff) const {
636 // bool operator()(const Key<NDIM>& key, const GenTensor<T>& coeff) const {
637
638 if (key.level()<2) return false;
639
640 const double tol=f->get_thresh();
641 const double thresh=f->truncate_tol(tol, key);
642 // include the wavelets in the norm, makes it much more accurate
643 const double fnorm=fcoeff.normf();
644 const double gnorm=gcoeff.normf();
645
646 // norm of the scaling function coefficients
647 const double sfnorm=fcoeff(g->get_cdata().s0).normf();
648 const double sgnorm=gcoeff(g->get_cdata().s0).normf();
649
650 // if the final norm is small, perform the hartree product and return
651 const double norm=fnorm*gnorm; // computing the outer product
652 if (norm < thresh) return true;
653
654 // get the error of both functions and of the pair function
655 const double ferror=sqrt(fnorm*fnorm-sfnorm*sfnorm);
656 const double gerror=sqrt(gnorm*gnorm-sgnorm*sgnorm);
657
658 // if the expected error is small, perform the hartree product and return
659 const double error=fnorm*gerror + ferror*gnorm + ferror*gerror;
660 if (error < thresh) return true;
661
662 // now check if the norm of this and the norm of the operator are significant
663 const std::vector<Key<NDIM> >& disp = op->get_disp(key.level());
664 const Key<NDIM>& d = *disp.begin(); // use the zero-displacement for screening
665 const double opnorm = op->norm(key.level(), d, key);
666 const double final_norm=opnorm*sfnorm*sgnorm;
667 if (final_norm < thresh) return true;
668
669 return false;
670 }
671 template <typename Archive> void serialize (Archive& ar) {
672 ar & f & op;
673 }
674 };
675
676 template<typename T, size_t NDIM>
677 struct noop {
678 void operator()(const Key<NDIM>& key, const GenTensor<T>& coeff, const bool& is_leaf) const {}
679 bool operator()(const Key<NDIM>& key, const GenTensor<T>& fcoeff, const GenTensor<T>& gcoeff) const {
680 MADNESS_EXCEPTION("in noop::operator()",1);
681 return true;
682 }
683 template <typename Archive> void serialize (Archive& ar) {}
684
685 };
686
687 /// insert/replaces the coefficients into the function
688 template<typename T, std::size_t NDIM>
689 struct insert_op {
694
698 insert_op(const insert_op& other) : impl(other.impl) {}
699 void operator()(const keyT& key, const coeffT& coeff, const bool& is_leaf) const {
701 impl->get_coeffs().replace(key,nodeT(coeff,not is_leaf));
702 }
703 template <typename Archive> void serialize (Archive& ar) {
704 ar & impl;
705 }
706
707 };
708
709 /// inserts/accumulates coefficients into impl's tree
710
711 /// NOTE: will use buffer and will need consolidation after operation ended !! NOTE !!
712 template<typename T, std::size_t NDIM>
716
718 accumulate_op() = default;
720 accumulate_op(const accumulate_op& other) = default;
721 void operator()(const Key<NDIM>& key, const coeffT& coeff, const bool& is_leaf) const {
722 if (coeff.has_data())
723 impl->get_coeffs().task(key, &nodeT::accumulate, coeff, impl->get_coeffs(), key, impl->get_tensor_args());
724 }
725 template <typename Archive> void serialize (Archive& ar) {
726 ar & impl;
727 }
728
729 };
730
731
732template<size_t NDIM>
733 struct true_op {
734
735 template<typename T>
736 bool operator()(const Key<NDIM>& key, const T& t) const {return true;}
737
738 template<typename T, typename R>
739 bool operator()(const Key<NDIM>& key, const T& t, const R& r) const {return true;}
740 template <typename Archive> void serialize (Archive& ar) {}
741
742 };
743
744 /// shallow-copy, pared-down version of FunctionNode, for special purpose only
745 template<typename T, std::size_t NDIM>
746 struct ShallowNode {
750 double dnorm=-1.0;
753 : _coeffs(node.coeff()), _has_children(node.has_children()),
754 dnorm(node.get_dnorm()) {}
756 : _coeffs(node.coeff()), _has_children(node._has_children),
757 dnorm(node.dnorm) {}
758
759 const coeffT& coeff() const {return _coeffs;}
760 coeffT& coeff() {return _coeffs;}
761 bool has_children() const {return _has_children;}
762 bool is_leaf() const {return not _has_children;}
763 template <typename Archive>
764 void serialize(Archive& ar) {
765 ar & coeff() & _has_children & dnorm;
766 }
767 };
768
769
770 /// a class to track where relevant (parent) coeffs are
771
772 /// E.g. if a 6D function is composed of two 3D functions their coefficients must be tracked.
773 /// We might need coeffs from a box that does not exist, and to avoid searching for
774 /// parents we track which are their required respective boxes.
775 /// - CoeffTracker will refer either to a requested key, if it exists, or to its
776 /// outermost parent.
777 /// - Children must be made in sequential order to be able to track correctly.
778 ///
779 /// Usage: 1. make the child of a given CoeffTracker.
780 /// If the parent CoeffTracker refers to a leaf node (flag is_leaf)
781 /// the child will refer to the same node. Otherwise it will refer
782 /// to the child node.
783 /// 2. retrieve its coefficients (possible communication/ returns a Future).
784 /// Member variable key always refers to an existing node,
785 /// so we can fetch it. Once we have the node we can determine
786 /// if it has children which allows us to make a child (see 1. )
787 template<typename T, size_t NDIM>
789
793 typedef std::pair<Key<NDIM>,ShallowNode<T,NDIM> > datumT;
795
796 /// the funcimpl that has the coeffs
797 const implT* impl;
798 /// the current key, which must exists in impl
800 /// flag if key is a leaf node
802 /// the coefficients belonging to key
804 /// norm of d coefficients corresponding to key
805 double dnorm_=-1.0;
806
807 public:
808
809 /// default ctor
811
812 /// the initial ctor making the root key
814 if (impl) key_=impl->get_cdata().key0;
815 }
816
817 /// ctor with a pair<keyT,nodeT>
818 explicit CoeffTracker(const CoeffTracker& other, const datumT& datum)
819 : impl(other.impl), key_(other.key_), coeff_(datum.second.coeff()),
820 dnorm_(datum.second.dnorm) {
821 if (datum.second.is_leaf()) is_leaf_=yes;
822 else is_leaf_=no;
823 }
824
825 /// copy ctor
826 CoeffTracker(const CoeffTracker& other) : impl(other.impl), key_(other.key_),
827 is_leaf_(other.is_leaf_), coeff_(other.coeff_), dnorm_(other.dnorm_) {};
828
829 /// const reference to impl
830 const implT* get_impl() const {return impl;}
831
832 /// const reference to the coeffs
833 const coeffT& coeff() const {return coeff_;}
834
835 /// const reference to the key
836 const keyT& key() const {return key_;}
837
838 /// return the coefficients belonging to the passed-in key
839
840 /// if key equals tracked key just return the coeffs, otherwise
841 /// make the child coefficients.
842 /// @param[in] key return coeffs corresponding to this key
843 /// @return coefficients belonging to key
851
852 /// return the s and dnorm belonging to the passed-in key
853 double dnorm(const keyT& key) const {
854 if (key==key_) return dnorm_;
855 MADNESS_ASSERT(key.is_child_of(key_));
856 return 0.0;
857 }
858
859 /// const reference to is_leaf flag
860 const LeafStatus& is_leaf() const {return is_leaf_;}
861
862 /// make a child of this, ignoring the coeffs
863 CoeffTracker make_child(const keyT& child) const {
864
865 // fast return
866 if ((not impl) or impl->is_on_demand()) return CoeffTracker(*this);
867
868 // can't make a child without knowing if this is a leaf -- activate first
870
871 CoeffTracker result;
872 if (impl) {
873 result.impl=impl;
874 if (is_leaf_==yes) result.key_=key_;
875 if (is_leaf_==no) {
876 result.key_=child;
877 // check if child is direct descendent of this, but root node is special case
878 if (child.level()>0) MADNESS_ASSERT(result.key().level()==key().level()+1);
879 }
880 result.is_leaf_=unknown;
881 }
882 return result;
883 }
884
885 /// find the coefficients
886
887 /// this involves communication to a remote node
888 /// @return a Future<CoeffTracker> with the coefficients that key refers to
890
891 // fast return
892 if (not impl) return Future<CoeffTracker>(CoeffTracker());
894
895 // this will return a <keyT,nodeT> from a remote node
898
899 // construct a new CoeffTracker locally
900 return impl->world.taskq.add(*const_cast<CoeffTracker*> (this),
901 &CoeffTracker::forward_ctor,*this,datum1);
902 }
903
904 private:
905 /// taskq-compatible forwarding to the ctor
906 CoeffTracker forward_ctor(const CoeffTracker& other, const datumT& datum) const {
907 return CoeffTracker(other,datum);
908 }
909
910 public:
911 /// serialization
912 template <typename Archive> void serialize(const Archive& ar) {
913 int il=int(is_leaf_);
914 ar & impl & key_ & il & coeff_ & dnorm_;
916 }
917 };
918
919 template<typename T, std::size_t NDIM>
920 std::ostream&
921 operator<<(std::ostream& s, const CoeffTracker<T,NDIM>& ct) {
922 s << ct.key() << ct.is_leaf() << " " << ct.get_impl();
923 return s;
924 }
925
926 /// FunctionImpl holds all Function state to facilitate shallow copy semantics
927
928 /// Since Function assignment and copy constructors are shallow it
929 /// greatly simplifies maintaining consistent state to have all
930 /// (permanent) state encapsulated in a single class. The state
931 /// is shared between instances using a shared_ptr<FunctionImpl>.
932 ///
933 /// The FunctionImpl inherits all of the functionality of WorldContainer
934 /// (to store the coefficients) and WorldObject<WorldContainer> (used
935 /// for RMI and for its unqiue id).
936 ///
937 /// The class methods are public to avoid painful multiple friend template
938 /// declarations for Function and FunctionImpl ... but this trust should not be
939 /// abused ... NOTHING except FunctionImpl methods should mess with FunctionImplData.
940 /// The LB stuff might have to be an exception.
941 template <typename T, std::size_t NDIM>
942 class FunctionImpl : public WorldObject< FunctionImpl<T,NDIM> > {
943 private:
944 typedef WorldObject< FunctionImpl<T,NDIM> > woT; ///< Base class world object type
945 public:
946 typedef T typeT;
947 typedef FunctionImpl<T,NDIM> implT; ///< Type of this class (implementation)
948 typedef std::shared_ptr< FunctionImpl<T,NDIM> > pimplT; ///< pointer to this class
949 typedef Tensor<T> tensorT; ///< Type of tensor for anything but to hold coeffs
950 typedef Vector<Translation,NDIM> tranT; ///< Type of array holding translation
951 typedef Key<NDIM> keyT; ///< Type of key
952 typedef FunctionNode<T,NDIM> nodeT; ///< Type of node
953 typedef GenTensor<T> coeffT; ///< Type of tensor used to hold coeffs
954 typedef WorldContainer<keyT,nodeT> dcT; ///< Type of container holding the coefficients
955 typedef std::pair<const keyT,nodeT> datumT; ///< Type of entry in container
956 typedef Vector<double,NDIM> coordT; ///< Type of vector holding coordinates
957
958 //template <typename Q, int D> friend class Function;
959 template <typename Q, std::size_t D> friend class FunctionImpl;
960
962
963 /// getter
966 const std::vector<Vector<double,NDIM> >& get_special_points()const{return special_points;}
967
968 private:
969 int k; ///< Wavelet order
970 double thresh; ///< Screening threshold
971 int initial_level; ///< Initial level for refinement
972 int special_level; ///< Minimium level for refinement on special points
973 std::vector<Vector<double,NDIM> > special_points; ///< special points for further refinement (needed for composite functions or multiplication)
974 int max_refine_level; ///< Do not refine below this level
975 int truncate_mode; ///< 0=default=(|d|<thresh), 1=(|d|<thresh/2^n), 1=(|d|<thresh/4^n);
976 bool autorefine; ///< If true, autorefine where appropriate
977 bool truncate_on_project; ///< If true projection inserts at level n-1 not n
978 TensorArgs targs; ///< type of tensor to be used in the FunctionNodes
979
981
982 std::shared_ptr< FunctionFunctorInterface<T,NDIM> > functor;
984
985 dcT coeffs; ///< The coefficients
986
987 // Disable the default copy constructor
989
990 public:
999
1000 /// Initialize function impl from data in factory
1002 : WorldObject<implT>(factory._world)
1003 , world(factory._world)
1004 , k(factory._k)
1005 , thresh(factory._thresh)
1006 , initial_level(factory._initial_level)
1007 , special_level(factory._special_level)
1008 , special_points(factory._special_points)
1009 , max_refine_level(factory._max_refine_level)
1010 , truncate_mode(factory._truncate_mode)
1011 , autorefine(factory._autorefine)
1012 , truncate_on_project(factory._truncate_on_project)
1013// , nonstandard(false)
1014 , targs(factory._thresh,FunctionDefaults<NDIM>::get_tensor_type())
1015 , cdata(FunctionCommonData<T,NDIM>::get(k))
1016 , functor(factory.get_functor())
1017// , on_demand(factory._is_on_demand)
1018// , compressed(factory._compressed)
1019// , redundant(false)
1020 , tree_state(factory._tree_state)
1021 , coeffs(world,factory._pmap,false)
1022 //, bc(factory._bc)
1023 {
1024 // PROFILE_MEMBER_FUNC(FunctionImpl); // No need to profile this
1025 // !!! Ensure that all local state is correctly formed
1026 // before invoking process_pending for the coeffs and
1027 // for this. Otherwise, there is a race condition.
1028 MADNESS_ASSERT(k>0 && k<=MAXK);
1029
1030 bool empty = (factory._empty or is_on_demand());
1031 bool do_refine = factory._refine;
1032
1033 if (do_refine)
1034 initial_level = std::max(0,initial_level - 1);
1035
1036 if (empty) { // Do not set any coefficients at all
1037 // additional functors are only evaluated on-demand
1038 } else if (functor) { // Project function and optionally refine
1040 // set the union of the special points of functor and the ones explicitly given to FunctionFactory
1041 std::vector<coordT> functor_special_points=functor->special_points();
1042 if (!functor_special_points.empty()) special_points.insert(special_points.end(), functor_special_points.begin(), functor_special_points.end());
1043
1044 typename dcT::const_iterator end = coeffs.end();
1045 for (typename dcT::const_iterator it=coeffs.begin(); it!=end; ++it) {
1046 if (it->second.is_leaf())
1047 woT::task(coeffs.owner(it->first), &implT::project_refine_op, it->first, do_refine,
1049 }
1050 }
1051 else { // Set as if a zero function
1052 initial_level = 1;
1054 }
1055
1057 this->process_pending();
1058 if (factory._fence && (functor || !empty)) world.gop.fence();
1059 }
1060
1061 /// Copy constructor
1062
1063 /// Allocates a \em new function in preparation for a deep copy
1064 ///
1065 /// By default takes pmap from other but can also specify a different pmap.
1066 /// Does \em not copy the coefficients ... creates an empty container.
1067 template <typename Q>
1069 const std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > >& pmap,
1070 bool dozero)
1071 : WorldObject<implT>(other.world)
1072 , world(other.world)
1073 , k(other.k)
1074 , thresh(other.thresh)
1080 , autorefine(other.autorefine)
1082 , targs(other.targs)
1083 , cdata(FunctionCommonData<T,NDIM>::get(k))
1084 , functor()
1085 , tree_state(other.tree_state)
1086 , coeffs(world, pmap ? pmap : other.coeffs.get_pmap())
1087 {
1088 if (dozero) {
1089 initial_level = 1;
1091 //world.gop.fence(); <<<<<<<<<<<<<<<<<<<<<< needs a fence argument
1092 }
1094 this->process_pending();
1095 }
1096
1097 virtual ~FunctionImpl() { }
1098
1099 const std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > >& get_pmap() const;
1100
1101 void replicate(bool fence=true) {
1102 coeffs.replicate(fence);
1103 }
1104
1105 void distribute(std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > > newmap) const {
1106 auto currentmap=coeffs.get_pmap();
1107 currentmap->redistribute(world,newmap);
1108 }
1109
1110
1111 /// Copy coeffs from other into self
1112 template <typename Q>
1113 void copy_coeffs(const FunctionImpl<Q,NDIM>& other, bool fence) {
1116 it!=end; ++it) {
1117 const keyT& key = it->first;
1118 const typename FunctionImpl<Q,NDIM>::nodeT& node = it->second;
1119 coeffs.replace(key,node. template convert<T>());
1120 }
1121 if (fence)
1122 world.gop.fence();
1123 }
1124
1125 /// perform inplace gaxpy: this = alpha*this + beta*other
1126 /// @param[in] alpha prefactor for this
1127 /// @param[in] beta prefactor for other
1128 /// @param[in] g the other function, reconstructed
1129 template<typename Q, typename R>
1130 void gaxpy_inplace_reconstructed(const T& alpha, const FunctionImpl<Q,NDIM>& g, const R& beta, const bool fence) {
1131 // merge g's tree into this' tree
1132 this->merge_trees(beta,g,alpha,true);
1133
1134 // sum down the sum coeffs into the leafs
1135 if (world.rank() == coeffs.owner(cdata.key0)) sum_down_spawn(cdata.key0, coeffT());
1136 if (fence) world.gop.fence();
1137 }
1138
1139 /// merge the trees of this and other, while multiplying them with the alpha or beta, resp
1140
1141 /// first step in an inplace gaxpy operation for reconstructed functions; assuming the same
1142 /// distribution for this and other
1143
1144 /// on output, *this = alpha* *this + beta * other
1145 /// @param[in] alpha prefactor for this
1146 /// @param[in] beta prefactor for other
1147 /// @param[in] other the other function, reconstructed
1148 template<typename Q, typename R>
1149 void merge_trees(const T alpha, const FunctionImpl<Q,NDIM>& other, const R beta, const bool fence=true) {
1150 MADNESS_ASSERT(get_pmap() == other.get_pmap());
1152 if (fence) world.gop.fence();
1153 }
1154
1155 /// merge the trees of this and other, while multiplying them with the alpha or beta, resp
1156
1157 /// result and rhs do not have to have the same distribution or live in the same world
1158 /// result+=alpha* this
1159 /// @param[in] alpha prefactor for this
1160 template<typename Q, typename R>
1161 void accumulate_trees(FunctionImpl<Q,NDIM>& result, const R alpha, const bool fence=true) const {
1163 }
1164
1165 /// perform: this= alpha*f + beta*g, invoked by result
1166
1167 /// f and g are reconstructed, so we can save on the compress operation,
1168 /// walk down the joint tree, and add leaf coefficients; effectively refines
1169 /// to common finest level.
1170
1171 /// nothing returned, but leaves this's tree reconstructed and as sum of f and g
1172 /// @param[in] alpha prefactor for f
1173 /// @param[in] f first addend
1174 /// @param[in] beta prefactor for g
1175 /// @param[in] g second addend
1176 void gaxpy_oop_reconstructed(const double alpha, const implT& f,
1177 const double beta, const implT& g, const bool fence);
1178
1179 /// functor for the gaxpy_inplace method
1180 template <typename Q, typename R>
1183 FunctionImpl<T,NDIM>* f; ///< prefactor for current function impl
1184 T alpha; ///< the current function impl
1185 R beta; ///< prefactor for other function impl
1186 do_gaxpy_inplace() = default;
1188 bool operator()(typename rangeT::iterator& it) const {
1189 const keyT& key = it->first;
1190 const FunctionNode<Q,NDIM>& other_node = it->second;
1191 // Use send to get write accessor and automated construction if missing
1192 f->coeffs.send(key, &nodeT:: template gaxpy_inplace<Q,R>, alpha, other_node, beta);
1193 return true;
1194 }
1195 template <typename Archive>
1196 void serialize(Archive& ar) {
1197 ar & f & alpha & beta;
1198 }
1199 };
1200
1201 /// Inplace general bilinear operation
1202 /// @param[in] alpha prefactor for the current function impl
1203 /// @param[in] other the other function impl
1204 /// @param[in] beta prefactor for other
1205 template <typename Q, typename R>
1206 void gaxpy_inplace(const T& alpha,const FunctionImpl<Q,NDIM>& other, const R& beta, bool fence) {
1207// MADNESS_ASSERT(get_pmap() == other.get_pmap());
1208 if (alpha != T(1.0)) scale_inplace(alpha,false);
1210 typedef do_gaxpy_inplace<Q,R> opT;
1211 other.world.taskq. template for_each<rangeT,opT>(rangeT(other.coeffs.begin(), other.coeffs.end()), opT(this, T(1.0), beta));
1212 if (fence)
1213 other.world.gop.fence();
1214 }
1215
1216 // loads a function impl from persistence
1217 // @param[in] ar the archive where the function impl is stored
1218 template <typename Archive>
1219 void load(Archive& ar) {
1220 // WE RELY ON K BEING STORED FIRST
1221 int kk = 0;
1222 ar & kk;
1223
1224 MADNESS_ASSERT(kk==k);
1225
1226 // note that functor should not be (re)stored
1228 & autorefine & truncate_on_project & tree_state;//nonstandard & compressed ; //& bc;
1229
1230 ar & coeffs;
1231 world.gop.fence();
1232 }
1233
1234 // saves a function impl to persistence
1235 // @param[in] ar the archive where the function impl is to be stored
1236 template <typename Archive>
1237 void store(Archive& ar) {
1238 // WE RELY ON K BEING STORED FIRST
1239
1240 // note that functor should not be (re)stored
1242 & autorefine & truncate_on_project & tree_state;//nonstandard & compressed ; //& bc;
1243
1244 ar & coeffs;
1245 world.gop.fence();
1246 }
1247
1248 /// Returns true if the function is compressed.
1249 bool is_compressed() const;
1250
1251 /// Returns true if the function is compressed.
1252 bool is_reconstructed() const;
1253
1254 /// Returns true if the function is redundant.
1255 bool is_redundant() const;
1256
1257 bool is_nonstandard() const;
1258
1259 bool is_nonstandard_with_leaves() const;
1260
1261 bool is_on_demand() const;
1262
1263 bool has_leaves() const;
1264
1265 void set_tree_state(const TreeState& state) {
1266 tree_state=state;
1267 }
1268
1270
1271 void set_functor(const std::shared_ptr<FunctionFunctorInterface<T,NDIM> > functor1);
1272
1273 std::shared_ptr<FunctionFunctorInterface<T,NDIM> > get_functor();
1274
1275 std::shared_ptr<FunctionFunctorInterface<T,NDIM> > get_functor() const;
1276
1277 void unset_functor();
1278
1279
1281
1283 void set_tensor_args(const TensorArgs& t);
1284
1285 double get_thresh() const;
1286
1287 void set_thresh(double value);
1288
1289 bool get_autorefine() const;
1290
1291 void set_autorefine(bool value);
1292
1293 int get_k() const;
1294
1295 const dcT& get_coeffs() const;
1296
1297 dcT& get_coeffs();
1298
1300
1301 void accumulate_timer(const double time) const; // !!!!!!!!!!!! REDUNDANT !!!!!!!!!!!!!!!
1302
1303 void print_timer() const;
1304
1305 void reset_timer();
1306
1307 /// Adds a constant to the function. Local operation, optional fence
1308
1309 /// In scaling function basis must add value to first polyn in
1310 /// each box with appropriate scaling for level. In wavelet basis
1311 /// need only add at level zero.
1312 /// @param[in] t the scalar to be added
1313 void add_scalar_inplace(T t, bool fence);
1314
1315 /// Initialize nodes to zero function at initial_level of refinement.
1316
1317 /// Works for either basis. No communication.
1318 void insert_zero_down_to_initial_level(const keyT& key);
1319
1320 /// Truncate according to the threshold with optional global fence
1321
1322 /// If thresh<=0 the default value of this->thresh is used
1323 /// @param[in] tol the truncation tolerance
1324 void truncate(double tol, bool fence);
1325
1326 /// Returns true if after truncation this node has coefficients
1327
1328 /// Assumed to be invoked on process owning key. Possible non-blocking
1329 /// communication.
1330 /// @param[in] key the key of the current function node
1331 Future<bool> truncate_spawn(const keyT& key, double tol);
1332
1333 /// Actually do the truncate operation
1334 /// @param[in] key the key to the current function node being evaluated for truncation
1335 /// @param[in] tol the tolerance for thresholding
1336 /// @param[in] v vector of Future<bool>'s that specify whether the current nodes children have coeffs
1337 bool truncate_op(const keyT& key, double tol, const std::vector< Future<bool> >& v);
1338
1339 /// Evaluate function at quadrature points in the specified box
1340
1341 /// @param[in] key the key indicating where the quadrature points are located
1342 /// @param[in] f the interface to the elementary function
1343 /// @param[in] qx quadrature points on a level=0 box
1344 /// @param[out] fval values
1345 void fcube(const keyT& key, const FunctionFunctorInterface<T,NDIM>& f, const Tensor<double>& qx, tensorT& fval) const;
1346
1347 /// Evaluate function at quadrature points in the specified box
1348
1349 /// @param[in] key the key indicating where the quadrature points are located
1350 /// @param[in] f the interface to the elementary function
1351 /// @param[in] qx quadrature points on a level=0 box
1352 /// @param[out] fval values
1353 void fcube(const keyT& key, T (*f)(const coordT&), const Tensor<double>& qx, tensorT& fval) const;
1354
1355 /// Returns cdata.key0
1356 const keyT& key0() const;
1357
1358 /// Prints the coeffs tree of the current function impl
1359 /// @param[in] maxlevel the maximum level of the tree for printing
1360 /// @param[out] os the ostream to where the output is sent
1361 void print_tree(std::ostream& os = std::cout, Level maxlevel = 10000) const;
1362
1363 /// Functor for the do_print_tree method
1364 void do_print_tree(const keyT& key, std::ostream& os, Level maxlevel) const;
1365
1366 /// Prints the coeffs tree of the current function impl (using GraphViz)
1367 /// @param[in] maxlevel the maximum level of the tree for printing
1368 /// @param[out] os the ostream to where the output is sent
1369 void print_tree_graphviz(std::ostream& os = std::cout, Level maxlevel = 10000) const;
1370
1371 /// Functor for the do_print_tree method (using GraphViz)
1372 void do_print_tree_graphviz(const keyT& key, std::ostream& os, Level maxlevel) const;
1373
1374 /// Same as print_tree() but in JSON format
1375 /// @param[out] os the ostream to where the output is sent
1376 /// @param[in] maxlevel the maximum level of the tree for printing
1377 void print_tree_json(std::ostream& os = std::cout, Level maxlevel = 10000) const;
1378
1379 /// Functor for the do_print_tree_json method
1380 void do_print_tree_json(const keyT& key, std::multimap<Level, std::tuple<tranT, std::string>>& data, Level maxlevel) const;
1381
1382 /// convert a number [0,limit] to a hue color code [blue,red],
1383 /// or, if log is set, a number [1.e-10,limit]
1385 double limit;
1386 bool log;
1387 static double lower() {return 1.e-10;};
1389 do_convert_to_color(const double limit, const bool log) : limit(limit), log(log) {}
1390 double operator()(double val) const {
1391 double color=0.0;
1392
1393 if (log) {
1394 double val2=log10(val) - log10(lower()); // will yield >0.0
1395 double upper=log10(limit) -log10(lower());
1396 val2=0.7-(0.7/upper)*val2;
1397 color= std::max(0.0,val2);
1398 color= std::min(0.7,color);
1399 } else {
1400 double hue=0.7-(0.7/limit)*(val);
1401 color= std::max(0.0,hue);
1402 }
1403 return color;
1404 }
1405 };
1406
1407
1408 /// Print a plane ("xy", "xz", or "yz") containing the point x to file
1409
1410 /// works for all dimensions; we walk through the tree, and if a leaf node
1411 /// inside the sub-cell touches the plane we print it in pstricks format
1412 void print_plane(const std::string filename, const int xaxis, const int yaxis, const coordT& el2);
1413
1414 /// collect the data for a plot of the MRA structure locally on each node
1415
1416 /// @param[in] xaxis the x-axis in the plot (can be any axis of the MRA box)
1417 /// @param[in] yaxis the y-axis in the plot (can be any axis of the MRA box)
1418 /// @param[in] el2 needs a description
1419 /// \todo Provide a description for el2
1420 Tensor<double> print_plane_local(const int xaxis, const int yaxis, const coordT& el2);
1421
1422 /// Functor for the print_plane method
1423 /// @param[in] filename the filename for the output
1424 /// @param[in] plotinfo plotting parameters
1425 /// @param[in] xaxis the x-axis in the plot (can be any axis of the MRA box)
1426 /// @param[in] yaxis the y-axis in the plot (can be any axis of the MRA box)
1427 void do_print_plane(const std::string filename, std::vector<Tensor<double> > plotinfo,
1428 const int xaxis, const int yaxis, const coordT el2);
1429
1430 /// print the grid (the roots of the quadrature of each leaf box)
1431 /// of this function in user xyz coordinates
1432 /// @param[in] filename the filename for the output
1433 void print_grid(const std::string filename) const;
1434
1435 /// return the keys of the local leaf boxes
1436 std::vector<keyT> local_leaf_keys() const;
1437
1438 /// print the grid in xyz format
1439
1440 /// the quadrature points and the key information will be written to file,
1441 /// @param[in] filename where the quadrature points will be written to
1442 /// @param[in] keys all leaf keys
1443 void do_print_grid(const std::string filename, const std::vector<keyT>& keys) const;
1444
1445 /// read data from a grid
1446
1447 /// @param[in] keyfile file with keys and grid points for each key
1448 /// @param[in] gridfile file with grid points, w/o key, but with same ordering
1449 /// @param[in] vnuc_functor subtract the values of this functor if regularization is needed
1450 template<size_t FDIM>
1451 typename std::enable_if<NDIM==FDIM>::type
1452 read_grid(const std::string keyfile, const std::string gridfile,
1453 std::shared_ptr< FunctionFunctorInterface<double,NDIM> > vnuc_functor) {
1454
1455 std::ifstream kfile(keyfile.c_str());
1456 std::ifstream gfile(gridfile.c_str());
1457 std::string line;
1458
1459 long ndata,ndata1;
1460 if (not (std::getline(kfile,line))) MADNESS_EXCEPTION("failed reading 1st line of key data",0);
1461 if (not (std::istringstream(line) >> ndata)) MADNESS_EXCEPTION("failed reading k",0);
1462 if (not (std::getline(gfile,line))) MADNESS_EXCEPTION("failed reading 1st line of grid data",0);
1463 if (not (std::istringstream(line) >> ndata1)) MADNESS_EXCEPTION("failed reading k",0);
1464 MADNESS_CHECK(ndata==ndata1);
1465 if (not (std::getline(kfile,line))) MADNESS_EXCEPTION("failed reading 2nd line of key data",0);
1466 if (not (std::getline(gfile,line))) MADNESS_EXCEPTION("failed reading 2nd line of grid data",0);
1467
1468 // the quadrature points in simulation coordinates of the root node
1469 const Tensor<double> qx=cdata.quad_x;
1470 const size_t npt = qx.dim(0);
1471
1472 // the number of coordinates (grid point tuples) per box ({x1},{x2},{x3},..,{xNDIM})
1473 long npoints=power<NDIM>(npt);
1474 // the number of boxes
1475 long nboxes=ndata/npoints;
1476 MADNESS_ASSERT(nboxes*npoints==ndata);
1477 print("reading ",nboxes,"boxes from file",gridfile,keyfile);
1478
1479 // these will be the data
1480 Tensor<T> values(cdata.vk,false);
1481
1482 int ii=0;
1483 std::string gline,kline;
1484 // while (1) {
1485 while (std::getline(kfile,kline)) {
1486
1487 double x,y,z,x1,y1,z1,val;
1488
1489 // get the key
1490 long nn;
1491 Translation l1,l2,l3;
1492 // line looks like: # key: n l1 l2 l3
1493 kline.erase(0,7);
1494 std::stringstream(kline) >> nn >> l1 >> l2 >> l3;
1495 // kfile >> s >> nn >> l1 >> l2 >> l3;
1496 const Vector<Translation,3> ll{ l1,l2,l3 };
1497 Key<3> key(nn,ll);
1498
1499 // this is borrowed from fcube
1500 const Vector<Translation,3>& l = key.translation();
1501 const Level n = key.level();
1502 const double h = std::pow(0.5,double(n));
1503 coordT c; // will hold the point in user coordinates
1506
1507
1508 if (NDIM == 3) {
1509 for (size_t i=0; i<npt; ++i) {
1510 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
1511 for (size_t j=0; j<npt; ++j) {
1512 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
1513 for (size_t k=0; k<npt; ++k) {
1514 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
1515 // fprintf(pFile,"%18.12f %18.12f %18.12f\n",c[0],c[1],c[2]);
1516 auto& success1 = std::getline(gfile,gline); MADNESS_CHECK(success1);
1517 auto& success2 = std::getline(kfile,kline); MADNESS_CHECK(success2);
1518 std::istringstream(gline) >> x >> y >> z >> val;
1519 std::istringstream(kline) >> x1 >> y1 >> z1;
1520 MADNESS_CHECK(std::fabs(x-c[0])<1.e-4);
1521 MADNESS_CHECK(std::fabs(x1-c[0])<1.e-4);
1522 MADNESS_CHECK(std::fabs(y-c[1])<1.e-4);
1523 MADNESS_CHECK(std::fabs(y1-c[1])<1.e-4);
1524 MADNESS_CHECK(std::fabs(z-c[2])<1.e-4);
1525 MADNESS_CHECK(std::fabs(z1-c[2])<1.e-4);
1526
1527 // regularize if a functor is given
1528 if (vnuc_functor) val-=(*vnuc_functor)(c);
1529 values(i,j,k)=val;
1530 }
1531 }
1532 }
1533 } else {
1534 MADNESS_EXCEPTION("only NDIM=3 in print_grid",0);
1535 }
1536
1537 // insert the new leaf node
1538 const bool has_children=false;
1539 coeffT coeff=coeffT(this->values2coeffs(key,values),targs);
1540 nodeT node(coeff,has_children);
1541 coeffs.replace(key,node);
1543 ii++;
1544 }
1545
1546 kfile.close();
1547 gfile.close();
1548 MADNESS_CHECK(ii==nboxes);
1549
1550 }
1551
1552
1553 /// read data from a grid
1554
1555 /// @param[in] gridfile file with keys and grid points and values for each key
1556 /// @param[in] vnuc_functor subtract the values of this functor if regularization is needed
1557 template<size_t FDIM>
1558 typename std::enable_if<NDIM==FDIM>::type
1559 read_grid2(const std::string gridfile,
1560 std::shared_ptr< FunctionFunctorInterface<double,NDIM> > vnuc_functor) {
1561
1562 std::ifstream gfile(gridfile.c_str());
1563 std::string line;
1564
1565 long ndata;
1566 if (not (std::getline(gfile,line))) MADNESS_EXCEPTION("failed reading 1st line of grid data",0);
1567 if (not (std::istringstream(line) >> ndata)) MADNESS_EXCEPTION("failed reading k",0);
1568 if (not (std::getline(gfile,line))) MADNESS_EXCEPTION("failed reading 2nd line of grid data",0);
1569
1570 // the quadrature points in simulation coordinates of the root node
1571 const Tensor<double> qx=cdata.quad_x;
1572 const size_t npt = qx.dim(0);
1573
1574 // the number of coordinates (grid point tuples) per box ({x1},{x2},{x3},..,{xNDIM})
1575 long npoints=power<NDIM>(npt);
1576 // the number of boxes
1577 long nboxes=ndata/npoints;
1578 MADNESS_CHECK(nboxes*npoints==ndata);
1579 print("reading ",nboxes,"boxes from file",gridfile);
1580
1581 // these will be the data
1582 Tensor<T> values(cdata.vk,false);
1583
1584 int ii=0;
1585 std::string gline;
1586 // while (1) {
1587 while (std::getline(gfile,gline)) {
1588
1589 double x1,y1,z1,val;
1590
1591 // get the key
1592 long nn;
1593 Translation l1,l2,l3;
1594 // line looks like: # key: n l1 l2 l3
1595 gline.erase(0,7);
1596 std::stringstream(gline) >> nn >> l1 >> l2 >> l3;
1597 const Vector<Translation,3> ll{ l1,l2,l3 };
1598 Key<3> key(nn,ll);
1599
1600 // this is borrowed from fcube
1601 const Vector<Translation,3>& l = key.translation();
1602 const Level n = key.level();
1603 const double h = std::pow(0.5,double(n));
1604 coordT c; // will hold the point in user coordinates
1607
1608
1609 if (NDIM == 3) {
1610 for (int i=0; i<npt; ++i) {
1611 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
1612 for (int j=0; j<npt; ++j) {
1613 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
1614 for (int k=0; k<npt; ++k) {
1615 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
1616
1617 auto& success = std::getline(gfile,gline);
1618 MADNESS_CHECK(success);
1619 std::istringstream(gline) >> x1 >> y1 >> z1 >> val;
1620 MADNESS_CHECK(std::fabs(x1-c[0])<1.e-4);
1621 MADNESS_CHECK(std::fabs(y1-c[1])<1.e-4);
1622 MADNESS_CHECK(std::fabs(z1-c[2])<1.e-4);
1623
1624 // regularize if a functor is given
1625 if (vnuc_functor) val-=(*vnuc_functor)(c);
1626 values(i,j,k)=val;
1627 }
1628 }
1629 }
1630 } else {
1631 MADNESS_EXCEPTION("only NDIM=3 in print_grid",0);
1632 }
1633
1634 // insert the new leaf node
1635 const bool has_children=false;
1636 coeffT coeff=coeffT(this->values2coeffs(key,values),targs);
1637 nodeT node(coeff,has_children);
1638 coeffs.replace(key,node);
1639 const_cast<dcT&>(coeffs).send(key.parent(),
1641 coeffs, key.parent());
1642 ii++;
1643 }
1644
1645 gfile.close();
1646 MADNESS_CHECK(ii==nboxes);
1647
1648 }
1649
1650
1651 /// Compute by projection the scaling function coeffs in specified box
1652 /// @param[in] key the key to the current function node (box)
1653 tensorT project(const keyT& key) const;
1654
1655 /// Returns the truncation threshold according to truncate_method
1656
1657 /// here is our handwaving argument:
1658 /// this threshold will give each FunctionNode an error of less than tol. The
1659 /// total error can then be as high as sqrt(#nodes) * tol. Therefore in order
1660 /// to account for higher dimensions: divide tol by about the root of number
1661 /// of siblings (2^NDIM) that have a large error when we refine along a deep
1662 /// branch of the tree.
1663 double truncate_tol(double tol, const keyT& key) const;
1664
1665
1666 /// Returns patch referring to coeffs of child in parent box
1667 /// @param[in] child the key to the child function node (box)
1668 std::vector<Slice> child_patch(const keyT& child) const;
1669
1670 /// Projection with optional refinement w/ special points
1671 /// @param[in] key the key to the current function node (box)
1672 /// @param[in] do_refine should we continue refinement?
1673 /// @param[in] specialpts vector of special points in the function where we need
1674 /// to refine at a much finer level
1675 void project_refine_op(const keyT& key, bool do_refine,
1676 const std::vector<Vector<double,NDIM> >& specialpts);
1677
1678 /// Compute the Legendre scaling functions for multiplication
1679
1680 /// Evaluate parent polyn at quadrature points of a child. The prefactor of
1681 /// 2^n/2 is included. The tensor must be preallocated as phi(k,npt).
1682 /// Refer to the implementation notes for more info.
1683 /// @todo Robert please verify this comment. I don't understand this method.
1684 /// @param[in] np level of the parent function node (box)
1685 /// @param[in] nc level of the child function node (box)
1686 /// @param[in] lp translation of the parent function node (box)
1687 /// @param[in] lc translation of the child function node (box)
1688 /// @param[out] phi tensor of the legendre scaling functions
1689 void phi_for_mul(Level np, Translation lp, Level nc, Translation lc, Tensor<double>& phi) const;
1690
1691 /// Directly project parent coeffs to child coeffs
1692
1693 /// Currently used by diff, but other uses can be anticipated
1694
1695 /// @todo is this documentation correct?
1696 /// @param[in] child the key whose coeffs we are requesting
1697 /// @param[in] parent the (leaf) key of our function
1698 /// @param[in] s the (leaf) coeffs belonging to parent
1699 /// @return coeffs
1700 const coeffT parent_to_child(const coeffT& s, const keyT& parent, const keyT& child) const;
1701
1702 /// Directly project parent NS coeffs to child NS coeffs
1703
1704 /// return the NS coefficients if parent and child are the same,
1705 /// or construct sum coeffs from the parents and "add" zero wavelet coeffs
1706 /// @param[in] child the key whose coeffs we are requesting
1707 /// @param[in] parent the (leaf) key of our function
1708 /// @param[in] coeff the (leaf) coeffs belonging to parent
1709 /// @return coeffs in NS form
1710 coeffT parent_to_child_NS(const keyT& child, const keyT& parent,
1711 const coeffT& coeff) const;
1712
1713 /// Return the values when given the coeffs in scaling function basis
1714 /// @param[in] key the key of the function node (box)
1715 /// @param[in] coeff the tensor of scaling function coefficients for function node (box)
1716 /// @return function values for function node (box)
1717 template <typename Q>
1718 GenTensor<Q> coeffs2values(const keyT& key, const GenTensor<Q>& coeff) const {
1719 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1720 double scale = pow(2.0,0.5*NDIM*key.level())/sqrt(FunctionDefaults<NDIM>::get_cell_volume());
1721 return transform(coeff,cdata.quad_phit).scale(scale);
1722 }
1723
1724 /// convert S or NS coeffs to values on a 2k grid of the children
1725
1726 /// equivalent to unfiltering the NS coeffs and then converting all child S-coeffs
1727 /// to values in their respective boxes. If only S coeffs are provided d coeffs are
1728 /// assumed to be zero. Reverse operation to values2NScoeffs().
1729 /// @param[in] key the key of the current S or NS coeffs, level n
1730 /// @param[in] coeff coeffs in S or NS form; if S then d coeffs are assumed zero
1731 /// @param[in] s_only sanity check to avoid unintended discard of d coeffs
1732 /// @return function values on the quadrature points of the children of child (!)
1733 template <typename Q>
1735 const bool s_only) const {
1736 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1737
1738 // sanity checks
1739 MADNESS_ASSERT((coeff.dim(0)==this->get_k()) == s_only);
1740 MADNESS_ASSERT((coeff.dim(0)==this->get_k()) or (coeff.dim(0)==2*this->get_k()));
1741
1742 // this is a block-diagonal matrix with the quadrature points on the diagonal
1743 Tensor<double> quad_phit_2k(2*cdata.k,2*cdata.npt);
1744 quad_phit_2k(cdata.s[0],cdata.s[0])=cdata.quad_phit;
1745 quad_phit_2k(cdata.s[1],cdata.s[1])=cdata.quad_phit;
1746
1747 // the transformation matrix unfilters (cdata.hg) and transforms to values in one step
1748 const Tensor<double> transf = (s_only)
1749 ? inner(cdata.hg(Slice(0,k-1),_),quad_phit_2k) // S coeffs
1750 : inner(cdata.hg,quad_phit_2k); // NS coeffs
1751
1752 // increment the level since the coeffs2values part happens on level n+1
1753 const double scale = pow(2.0,0.5*NDIM*(key.level()+1))/
1755
1756 return transform(coeff,transf).scale(scale);
1757 }
1758
1759 /// Compute the function values for multiplication
1760
1761 /// Given S or NS coefficients from a parent cell, compute the value of
1762 /// the functions at the quadrature points of a child
1763 /// currently restricted to special cases
1764 /// @param[in] child key of the box in which we compute values
1765 /// @param[in] parent key of the parent box holding the coeffs
1766 /// @param[in] coeff coeffs of the parent box
1767 /// @param[in] s_only sanity check to avoid unintended discard of d coeffs
1768 /// @return function values on the quadrature points of the children of child (!)
1769 template <typename Q>
1770 GenTensor<Q> NS_fcube_for_mul(const keyT& child, const keyT& parent,
1771 const GenTensor<Q>& coeff, const bool s_only) const {
1772 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1773
1774 // sanity checks
1775 MADNESS_ASSERT((coeff.dim(0)==this->get_k()) == s_only);
1776 MADNESS_ASSERT((coeff.dim(0)==this->get_k()) or (coeff.dim(0)==2*this->get_k()));
1777
1778 // fast return if possible
1779 // if (child.level()==parent.level()) return NScoeffs2values(child,coeff,s_only);
1780
1781 if (s_only) {
1782
1783 Tensor<double> quad_phi[NDIM];
1784 // tmp tensor
1785 Tensor<double> phi1(cdata.k,cdata.npt);
1786
1787 for (std::size_t d=0; d<NDIM; ++d) {
1788
1789 // input is S coeffs (dimension k), output is values on 2*npt grid points
1790 quad_phi[d]=Tensor<double>(cdata.k,2*cdata.npt);
1791
1792 // for both children of "child" evaluate the Legendre polynomials
1793 // first the left child on level n+1 and translations 2l
1794 phi_for_mul(parent.level(),parent.translation()[d],
1795 child.level()+1, 2*child.translation()[d], phi1);
1796 quad_phi[d](_,Slice(0,k-1))=phi1;
1797
1798 // next the right child on level n+1 and translations 2l+1
1799 phi_for_mul(parent.level(),parent.translation()[d],
1800 child.level()+1, 2*child.translation()[d]+1, phi1);
1801 quad_phi[d](_,Slice(k,2*k-1))=phi1;
1802 }
1803
1804 const double scale = 1.0/sqrt(FunctionDefaults<NDIM>::get_cell_volume());
1805 return general_transform(coeff,quad_phi).scale(scale);
1806 }
1807 MADNESS_EXCEPTION("you should not be here in NS_fcube_for_mul",1);
1808 return GenTensor<Q>();
1809 }
1810
1811 /// convert function values of the a child generation directly to NS coeffs
1812
1813 /// equivalent to converting the function values to 2^NDIM S coeffs and then
1814 /// filtering them to NS coeffs. Reverse operation to NScoeffs2values().
1815 /// @param[in] key key of the parent of the generation
1816 /// @param[in] values tensor holding function values of the 2^NDIM children of key
1817 /// @return NS coeffs belonging to key
1818 template <typename Q>
1819 GenTensor<Q> values2NScoeffs(const keyT& key, const GenTensor<Q>& values) const {
1820 //PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1821
1822 // sanity checks
1823 MADNESS_ASSERT(values.dim(0)==2*this->get_k());
1824
1825 // this is a block-diagonal matrix with the quadrature points on the diagonal
1826 Tensor<double> quad_phit_2k(2*cdata.npt,2*cdata.k);
1827 quad_phit_2k(cdata.s[0],cdata.s[0])=cdata.quad_phiw;
1828 quad_phit_2k(cdata.s[1],cdata.s[1])=cdata.quad_phiw;
1829
1830 // the transformation matrix unfilters (cdata.hg) and transforms to values in one step
1831 const Tensor<double> transf=inner(quad_phit_2k,cdata.hgT);
1832
1833 // increment the level since the values2coeffs part happens on level n+1
1834 const double scale = pow(0.5,0.5*NDIM*(key.level()+1))
1836
1837 return transform(values,transf).scale(scale);
1838 }
1839
1840 /// Return the scaling function coeffs when given the function values at the quadrature points
1841 /// @param[in] key the key of the function node (box)
1842 /// @return function values for function node (box)
1843 template <typename Q>
1844 Tensor<Q> coeffs2values(const keyT& key, const Tensor<Q>& coeff) const {
1845 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1846 double scale = pow(2.0,0.5*NDIM*key.level())/sqrt(FunctionDefaults<NDIM>::get_cell_volume());
1847 return transform(coeff,cdata.quad_phit).scale(scale);
1848 }
1849
1850 template <typename Q>
1851 GenTensor<Q> values2coeffs(const keyT& key, const GenTensor<Q>& values) const {
1852 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1853 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
1854 return transform(values,cdata.quad_phiw).scale(scale);
1855 }
1856
1857 template <typename Q>
1858 Tensor<Q> values2coeffs(const keyT& key, const Tensor<Q>& values) const {
1859 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1860 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
1861 return transform(values,cdata.quad_phiw).scale(scale);
1862 }
1863
1864 /// Compute the function values for multiplication
1865
1866 /// Given coefficients from a parent cell, compute the value of
1867 /// the functions at the quadrature points of a child
1868 /// @param[in] child the key for the child function node (box)
1869 /// @param[in] parent the key for the parent function node (box)
1870 /// @param[in] coeff the coefficients of scaling function basis of the parent box
1871 template <typename Q>
1872 Tensor<Q> fcube_for_mul(const keyT& child, const keyT& parent, const Tensor<Q>& coeff) const {
1873 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1874 if (child.level() == parent.level()) {
1875 return coeffs2values(parent, coeff);
1876 }
1877 else if (child.level() < parent.level()) {
1878 MADNESS_EXCEPTION("FunctionImpl: fcube_for_mul: child-parent relationship bad?",0);
1879 }
1880 else {
1881 Tensor<double> phi[NDIM];
1882 for (std::size_t d=0; d<NDIM; ++d) {
1883 phi[d] = Tensor<double>(cdata.k,cdata.npt);
1884 phi_for_mul(parent.level(),parent.translation()[d],
1885 child.level(), child.translation()[d], phi[d]);
1886 }
1887 return general_transform(coeff,phi).scale(1.0/sqrt(FunctionDefaults<NDIM>::get_cell_volume()));;
1888 }
1889 }
1890
1891
1892 /// Compute the function values for multiplication
1893
1894 /// Given coefficients from a parent cell, compute the value of
1895 /// the functions at the quadrature points of a child
1896 /// @param[in] child the key for the child function node (box)
1897 /// @param[in] parent the key for the parent function node (box)
1898 /// @param[in] coeff the coefficients of scaling function basis of the parent box
1899 template <typename Q>
1900 GenTensor<Q> fcube_for_mul(const keyT& child, const keyT& parent, const GenTensor<Q>& coeff) const {
1901 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1902 if (child.level() == parent.level()) {
1903 return coeffs2values(parent, coeff);
1904 }
1905 else if (child.level() < parent.level()) {
1906 MADNESS_EXCEPTION("FunctionImpl: fcube_for_mul: child-parent relationship bad?",0);
1907 }
1908 else {
1909 Tensor<double> phi[NDIM];
1910 for (size_t d=0; d<NDIM; d++) {
1911 phi[d] = Tensor<double>(cdata.k,cdata.npt);
1912 phi_for_mul(parent.level(),parent.translation()[d],
1913 child.level(), child.translation()[d], phi[d]);
1914 }
1915 return general_transform(coeff,phi).scale(1.0/sqrt(FunctionDefaults<NDIM>::get_cell_volume()));
1916 }
1917 }
1918
1919
1920 /// Functor for the mul method
1921 template <typename L, typename R>
1922 void do_mul(const keyT& key, const Tensor<L>& left, const std::pair< keyT, Tensor<R> >& arg) {
1923 // PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1924 const keyT& rkey = arg.first;
1925 const Tensor<R>& rcoeff = arg.second;
1926 //madness::print("do_mul: r", rkey, rcoeff.size());
1927 Tensor<R> rcube = fcube_for_mul(key, rkey, rcoeff);
1928 //madness::print("do_mul: l", key, left.size());
1929 Tensor<L> lcube = fcube_for_mul(key, key, left);
1930
1931 Tensor<T> tcube(cdata.vk,false);
1932 TERNARY_OPTIMIZED_ITERATOR(T, tcube, L, lcube, R, rcube, *_p0 = *_p1 * *_p2;);
1933 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
1934 tcube = transform(tcube,cdata.quad_phiw).scale(scale);
1935 coeffs.replace(key, nodeT(coeffT(tcube,targs),false));
1936 }
1937
1938
1939 /// multiply the values of two coefficient tensors using a custom number of grid points
1940
1941 /// note both coefficient tensors have to refer to the same key!
1942 /// @param[in] c1 a tensor holding coefficients
1943 /// @param[in] c2 another tensor holding coeffs
1944 /// @param[in] npt number of grid points (optional, default is cdata.npt)
1945 /// @return coefficient tensor holding the product of the values of c1 and c2
1946 template<typename R>
1948 const int npt, const keyT& key) const {
1949 typedef TENSOR_RESULT_TYPE(T,R) resultT;
1950
1952
1953 // construct a tensor with the npt coeffs
1954 Tensor<T> c11(cdata2.vk), c22(cdata2.vk);
1955 c11(this->cdata.s0)=c1;
1956 c22(this->cdata.s0)=c2;
1957
1958 // it's sufficient to scale once
1959 double scale = pow(2.0,0.5*NDIM*key.level())/sqrt(FunctionDefaults<NDIM>::get_cell_volume());
1960 Tensor<T> c1value=transform(c11,cdata2.quad_phit).scale(scale);
1961 Tensor<R> c2value=transform(c22,cdata2.quad_phit);
1962 Tensor<resultT> resultvalue(cdata2.vk,false);
1963 TERNARY_OPTIMIZED_ITERATOR(resultT, resultvalue, T, c1value, R, c2value, *_p0 = *_p1 * *_p2;);
1964
1965 Tensor<resultT> result=transform(resultvalue,cdata2.quad_phiw);
1966
1967 // return a copy of the slice to have the tensor contiguous
1968 return copy(result(this->cdata.s0));
1969 }
1970
1971
1972 /// Functor for the binary_op method
1973 template <typename L, typename R, typename opT>
1974 void do_binary_op(const keyT& key, const Tensor<L>& left,
1975 const std::pair< keyT, Tensor<R> >& arg,
1976 const opT& op) {
1977 //PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
1978 const keyT& rkey = arg.first;
1979 const Tensor<R>& rcoeff = arg.second;
1980 Tensor<R> rcube = fcube_for_mul(key, rkey, rcoeff);
1981 Tensor<L> lcube = fcube_for_mul(key, key, left);
1982
1983 Tensor<T> tcube(cdata.vk,false);
1984 op(key, tcube, lcube, rcube);
1985 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
1986 tcube = transform(tcube,cdata.quad_phiw).scale(scale);
1987 coeffs.replace(key, nodeT(coeffT(tcube,targs),false));
1988 }
1989
1990 /// Invoked by result to perform result += alpha*left+beta*right in wavelet basis
1991
1992 /// Does not assume that any of result, left, right have the same distribution.
1993 /// For most purposes result will start as an empty so actually are implementing
1994 /// out of place gaxpy. If all functions have the same distribution there is
1995 /// no communication except for the optional fence.
1996 template <typename L, typename R>
1998 T beta, const FunctionImpl<R,NDIM>& right, bool fence) {
1999 // Loop over local nodes in both functions. Add in left and subtract right.
2000 // Not that efficient in terms of memory bandwidth but ensures we do
2001 // not miss any nodes.
2002 typename FunctionImpl<L,NDIM>::dcT::const_iterator left_end = left.coeffs.end();
2004 it!=left_end;
2005 ++it) {
2006 const keyT& key = it->first;
2007 const typename FunctionImpl<L,NDIM>::nodeT& other_node = it->second;
2008 coeffs.send(key, &nodeT:: template gaxpy_inplace<T,L>, 1.0, other_node, alpha);
2009 }
2010 typename FunctionImpl<R,NDIM>::dcT::const_iterator right_end = right.coeffs.end();
2012 it!=right_end;
2013 ++it) {
2014 const keyT& key = it->first;
2015 const typename FunctionImpl<L,NDIM>::nodeT& other_node = it->second;
2016 coeffs.send(key, &nodeT:: template gaxpy_inplace<T,R>, 1.0, other_node, beta);
2017 }
2018 if (fence)
2019 world.gop.fence();
2020 }
2021
2022 /// Unary operation applied inplace to the coefficients WITHOUT refinement, optional fence
2023 /// @param[in] op the unary operator for the coefficients
2024 template <typename opT>
2025 void unary_op_coeff_inplace(const opT& op, bool fence) {
2026 typename dcT::iterator end = coeffs.end();
2027 for (typename dcT::iterator it=coeffs.begin(); it!=end; ++it) {
2028 const keyT& parent = it->first;
2029 nodeT& node = it->second;
2030 if (node.has_coeff()) {
2031 // op(parent, node.coeff());
2032 TensorArgs full(-1.0,TT_FULL);
2033 change_tensor_type(node.coeff(),full);
2034 op(parent, node.coeff().full_tensor());
2036 // op(parent,node);
2037 }
2038 }
2039 if (fence)
2040 world.gop.fence();
2041 }
2042
2043 /// Unary operation applied inplace to the coefficients WITHOUT refinement, optional fence
2044 /// @param[in] op the unary operator for the coefficients
2045 template <typename opT>
2046 void unary_op_node_inplace(const opT& op, bool fence) {
2047 typename dcT::iterator end = coeffs.end();
2048 for (typename dcT::iterator it=coeffs.begin(); it!=end; ++it) {
2049 const keyT& parent = it->first;
2050 nodeT& node = it->second;
2051 op(parent, node);
2052 }
2053 if (fence)
2054 world.gop.fence();
2055 }
2056
2057 /// Integrate over one particle of a two particle function and get a one particle function
2058 /// bsp \int g(1,2) \delta(2-1) d2 = f(1)
2059 /// The overall dimension of g should be even
2060
2061 /// The operator
2062 template<std::size_t LDIM>
2063 void dirac_convolution_op(const keyT &key, const nodeT &node, FunctionImpl<T,LDIM>* f) const {
2064 // fast return if the node has children (not a leaf node)
2065 if(node.has_children()) return;
2066
2067 const implT* g=this;
2068
2069 // break the 6D key into two 3D keys (may also work for every even dimension)
2070 Key<LDIM> key1, key2;
2071 key.break_apart(key1,key2);
2072
2073 // get the coefficients of the 6D function g
2074 const coeffT& g_coeff = node.coeff();
2075
2076 // get the values of the 6D function g
2077 coeffT g_values = g->coeffs2values(key,g_coeff);
2078
2079 // Determine rank and k
2080 const long rank=g_values.rank();
2081 const long maxk=f->get_k();
2082 MADNESS_ASSERT(maxk==g_coeff.dim(0));
2083
2084 // get tensors for particle 1 and 2 (U and V in SVD)
2085 tensorT vec1=copy(g_values.get_svdtensor().ref_vector(0).reshape(rank,maxk,maxk,maxk));
2086 tensorT vec2=g_values.get_svdtensor().ref_vector(1).reshape(rank,maxk,maxk,maxk);
2087 tensorT result(maxk,maxk,maxk); // should give zero tensor
2088 // Multiply the values of each U and V vector
2089 for (long i=0; i<rank; ++i) {
2090 tensorT c1=vec1(Slice(i,i),_,_,_); // shallow copy (!)
2091 tensorT c2=vec2(Slice(i,i),_,_,_);
2092 c1.emul(c2); // this changes vec1 because of shallow copy, but not the g function because of the deep copy made above
2093 double singular_value_i = g_values.get_svdtensor().weights(i);
2094 result += (singular_value_i*c1);
2095 }
2096
2097 // accumulate coefficients (since only diagonal boxes are used the coefficients get just replaced, but accumulate is needed to create the right tree structure
2098 tensorT f_coeff = f->values2coeffs(key1,result);
2099 f->coeffs.task(key1, &FunctionNode<T,LDIM>::accumulate2, f_coeff, f->coeffs, key1, TaskAttributes::hipri());
2100// coeffs.task(dest, &nodeT::accumulate2, result, coeffs, dest, TaskAttributes::hipri());
2101
2102
2103 return;
2104 }
2105
2106
2107 template<std::size_t LDIM>
2109 typename dcT::const_iterator end = this->coeffs.end();
2110 for (typename dcT::const_iterator it=this->coeffs.begin(); it!=end; ++it) {
2111 // looping through all the leaf(!) coefficients in the NDIM function ("this")
2112 const keyT& key = it->first;
2113 const FunctionNode<T,NDIM>& node = it->second;
2114 if (node.is_leaf()) {
2115 // only process the diagonal boxes
2116 Key<LDIM> key1, key2;
2117 key.break_apart(key1,key2);
2118 if(key1 == key2){
2119 ProcessID p = coeffs.owner(key);
2120 woT::task(p, &implT:: template dirac_convolution_op<LDIM>, key, node, f);
2121 }
2122 }
2123 }
2124 world.gop.fence(); // fence is necessary if trickle down is used afterwards
2125 // trickle down and undo redundand shouldnt change anything if only the diagonal elements are considered above -> check this
2126 f->trickle_down(true); // fence must be true otherwise undo_redundant will have trouble
2127// f->undo_redundant(true);
2128 f->verify_tree();
2129 //if (fence) world.gop.fence(); // unnecessary, fence is activated in undo_redundant
2130
2131 }
2132
2133
2134 /// Unary operation applied inplace to the coefficients WITHOUT refinement, optional fence
2135 /// @param[in] op the unary operator for the coefficients
2136 template <typename opT>
2137 void flo_unary_op_node_inplace(const opT& op, bool fence) {
2139// typedef do_unary_op_value_inplace<opT> xopT;
2141 if (fence) world.gop.fence();
2142 }
2143
2144 /// Unary operation applied inplace to the coefficients WITHOUT refinement, optional fence
2145 /// @param[in] op the unary operator for the coefficients
2146 template <typename opT>
2147 void flo_unary_op_node_inplace(const opT& op, bool fence) const {
2149// typedef do_unary_op_value_inplace<opT> xopT;
2151 if (fence)
2152 world.gop.fence();
2153 }
2154
2155 /// truncate tree at a certain level
2156 /// @param[in] max_level truncate tree below this level
2157 void erase(const Level& max_level);
2158
2159 /// Returns some asymmetry measure ... no comms
2160 double check_symmetry_local() const;
2161
2162 /// given an NS tree resulting from a convolution, truncate leafs if appropriate
2165 const implT* f; // for calling its member functions
2166
2168
2169 bool operator()(typename rangeT::iterator& it) const {
2170
2171 const keyT& key = it->first;
2172 nodeT& node = it->second;
2173
2174 if (node.is_leaf() and node.coeff().has_data()) {
2175 coeffT d = copy(node.coeff());
2176 d(f->cdata.s0)=0.0;
2177 const double error=d.normf();
2178 const double tol=f->truncate_tol(f->get_thresh(),key);
2179 if (error<tol) node.coeff()=copy(node.coeff()(f->cdata.s0));
2180 }
2181 return true;
2182 }
2183 template <typename Archive> void serialize(const Archive& ar) {}
2184
2185 };
2186
2187 /// remove all coefficients of internal nodes
2190
2191 /// constructor need impl for cdata
2193
2194 bool operator()(typename rangeT::iterator& it) const {
2195
2196 nodeT& node = it->second;
2197 if (node.has_children()) node.clear_coeff();
2198 return true;
2199 }
2200 template <typename Archive> void serialize(const Archive& ar) {}
2201
2202 };
2203
2204 /// remove all coefficients of leaf nodes
2207
2208 /// constructor need impl for cdata
2210
2211 bool operator()(typename rangeT::iterator& it) const {
2212 nodeT& node = it->second;
2213 if (not node.has_children()) node.clear_coeff();
2214 return true;
2215 }
2216 template <typename Archive> void serialize(const Archive& ar) {}
2217
2218 };
2219
2220
2221 /// keep only the sum coefficients in each node
2225
2226 /// constructor need impl for cdata
2228
2229 bool operator()(typename rangeT::iterator& it) const {
2230
2231 nodeT& node = it->second;
2232 coeffT s=copy(node.coeff()(impl->cdata.s0));
2233 node.coeff()=s;
2234 return true;
2235 }
2236 template <typename Archive> void serialize(const Archive& ar) {}
2237
2238 };
2239
2240
2241 /// reduce the rank of the nodes, optional fence
2244
2245 // threshold for rank reduction / SVD truncation
2247
2248 // constructor takes target precision
2249 do_reduce_rank() = default;
2251 do_reduce_rank(const double& thresh) {
2253 }
2254
2255 //
2256 bool operator()(typename rangeT::iterator& it) const {
2257
2258 nodeT& node = it->second;
2259 node.reduceRank(args.thresh);
2260 return true;
2261 }
2262 template <typename Archive> void serialize(const Archive& ar) {}
2263 };
2264
2265
2266
2267 /// check symmetry wrt particle exchange
2270 const implT* f;
2273
2274 /// return the norm of the difference of this node and its "mirror" node
2275 double operator()(typename rangeT::iterator& it) const {
2276
2277 // Temporary fix to GCC whining about out of range access for NDIM!=6
2278 if constexpr(NDIM==6) {
2279 const keyT& key = it->first;
2280 const nodeT& fnode = it->second;
2281
2282 // skip internal nodes
2283 if (fnode.has_children()) return 0.0;
2284
2285 if (f->world.size()>1) return 0.0;
2286
2287 // exchange particles
2288 std::vector<long> map(NDIM);
2289 map[0]=3; map[1]=4; map[2]=5;
2290 map[3]=0; map[4]=1; map[5]=2;
2291
2292 // make mapped key
2294 for (std::size_t i=0; i<NDIM; ++i) l[map[i]] = key.translation()[i];
2295 const keyT mapkey(key.level(),l);
2296
2297 double norm=0.0;
2298
2299
2300 // hope it's local
2301 if (f->get_coeffs().probe(mapkey)) {
2302 MADNESS_ASSERT(f->get_coeffs().probe(mapkey));
2303 const nodeT& mapnode=f->get_coeffs().find(mapkey).get()->second;
2304
2305// bool have_c1=fnode.coeff().has_data() and fnode.coeff().config().has_data();
2306// bool have_c2=mapnode.coeff().has_data() and mapnode.coeff().config().has_data();
2307 bool have_c1=fnode.coeff().has_data();
2308 bool have_c2=mapnode.coeff().has_data();
2309
2310 if (have_c1 and have_c2) {
2311 tensorT c1=fnode.coeff().full_tensor_copy();
2312 tensorT c2=mapnode.coeff().full_tensor_copy();
2313 c2 = copy(c2.mapdim(map));
2314 norm=(c1-c2).normf();
2315 } else if (have_c1) {
2316 tensorT c1=fnode.coeff().full_tensor_copy();
2317 norm=c1.normf();
2318 } else if (have_c2) {
2319 tensorT c2=mapnode.coeff().full_tensor_copy();
2320 norm=c2.normf();
2321 } else {
2322 norm=0.0;
2323 }
2324 } else {
2325 norm=fnode.coeff().normf();
2326 }
2327 return norm*norm;
2328 }
2329 else {
2330 MADNESS_EXCEPTION("ONLY FOR DIM 6!", 1);
2331 }
2332 }
2333
2334 double operator()(double a, double b) const {
2335 return (a+b);
2336 }
2337
2338 template <typename Archive> void serialize(const Archive& ar) {
2339 MADNESS_EXCEPTION("no serialization of do_check_symmetry yet",1);
2340 }
2341
2342
2343 };
2344
2345 /// merge the coefficent boxes of this into result's tree
2346
2347 /// result+= alpha*this
2348 /// this and result don't have to have the same distribution or live in the same world
2349 /// no comm, and the tree should be in an consistent state by virtue
2350 template<typename Q, typename R>
2354 T alpha=T(1.0);
2358
2359 /// return the norm of the difference of this node and its "mirror" node
2360 bool operator()(typename rangeT::iterator& it) const {
2361
2362 const keyT& key = it->first;
2363 const nodeT& node = it->second;
2364 if (node.has_coeff()) result->get_coeffs().task(key, &nodeT::accumulate,
2365 alpha*node.coeff(), result->get_coeffs(), key, result->targs);
2366 return true;
2367 }
2368
2369 template <typename Archive> void serialize(const Archive& ar) {
2370 MADNESS_EXCEPTION("no serialization of do_accumulate_trees",1);
2371 }
2372 };
2373
2374
2375 /// merge the coefficent boxes of this into other's tree
2376
2377 /// no comm, and the tree should be in an consistent state by virtue
2378 /// of FunctionNode::gaxpy_inplace
2379 template<typename Q, typename R>
2388
2389 /// return the norm of the difference of this node and its "mirror" node
2390 bool operator()(typename rangeT::iterator& it) const {
2391
2392 const keyT& key = it->first;
2393 const nodeT& fnode = it->second;
2394
2395 // if other's node exists: add this' coeffs to it
2396 // otherwise insert this' node into other's tree
2397 typename dcT::accessor acc;
2398 if (other->get_coeffs().find(acc,key)) {
2399 nodeT& gnode=acc->second;
2400 gnode.gaxpy_inplace(beta,fnode,alpha);
2401 } else {
2402 nodeT gnode=fnode;
2403 gnode.scale(alpha);
2404 other->get_coeffs().replace(key,gnode);
2405 }
2406 return true;
2407 }
2408
2409 template <typename Archive> void serialize(const Archive& ar) {
2410 MADNESS_EXCEPTION("no serialization of do_merge_trees",1);
2411 }
2412 };
2413
2414
2415 /// map this on f
2416 struct do_mapdim {
2418
2419 std::vector<long> map;
2421
2422 do_mapdim() : f(0) {};
2423 do_mapdim(const std::vector<long> map, implT& f) : map(map), f(&f) {}
2424
2425 bool operator()(typename rangeT::iterator& it) const {
2426
2427 const keyT& key = it->first;
2428 const nodeT& node = it->second;
2429
2431 for (std::size_t i=0; i<NDIM; ++i) l[map[i]] = key.translation()[i];
2432 tensorT c = node.coeff().reconstruct_tensor();
2433 if (c.size()) c = copy(c.mapdim(map));
2435 f->get_coeffs().replace(keyT(key.level(),l), nodeT(cc,node.has_children()));
2436
2437 return true;
2438 }
2439 template <typename Archive> void serialize(const Archive& ar) {
2440 MADNESS_EXCEPTION("no serialization of do_mapdim",1);
2441 }
2442
2443 };
2444
2445 /// mirror dimensions of this, write result on f
2446 struct do_mirror {
2448
2449 std::vector<long> mirror;
2451
2452 do_mirror() : f(0) {};
2453 do_mirror(const std::vector<long> mirror, implT& f) : mirror(mirror), f(&f) {}
2454
2455 bool operator()(typename rangeT::iterator& it) const {
2456
2457 const keyT& key = it->first;
2458 const nodeT& node = it->second;
2459
2460 // mirror translation index: l_new + l_old = l_max
2462 Translation lmax = (Translation(1)<<key.level()) - 1;
2463 for (std::size_t i=0; i<NDIM; ++i) {
2464 if (mirror[i]==-1) l[i]= lmax - key.translation()[i];
2465 }
2466
2467 // mirror coefficients: multiply all odd-k slices with -1
2468 tensorT c = node.coeff().full_tensor_copy();
2469 if (c.size()) {
2470 std::vector<Slice> s(___);
2471
2472 // loop over dimensions and over k
2473 for (size_t i=0; i<NDIM; ++i) {
2474 std::size_t kmax=c.dim(i);
2475 if (mirror[i]==-1) {
2476 for (size_t k=1; k<kmax; k+=2) {
2477 s[i]=Slice(k,k,1);
2478 c(s)*=(-1.0);
2479 }
2480 s[i]=_;
2481 }
2482 }
2483 }
2485 f->get_coeffs().replace(keyT(key.level(),l), nodeT(cc,node.has_children()));
2486
2487 return true;
2488 }
2489 template <typename Archive> void serialize(const Archive& ar) {
2490 MADNESS_EXCEPTION("no serialization of do_mirror",1);
2491 }
2492
2493 };
2494
2495 /// mirror dimensions of this, write result on f
2498
2499 std::vector<long> map,mirror;
2501
2503 do_map_and_mirror(const std::vector<long> map, const std::vector<long> mirror, implT& f)
2504 : map(map), mirror(mirror), f(&f) {}
2505
2506 bool operator()(typename rangeT::iterator& it) const {
2507
2508 const keyT& key = it->first;
2509 const nodeT& node = it->second;
2510
2511 tensorT c = node.coeff().full_tensor_copy();
2513
2514 // do the mapping first (if present)
2515 if (map.size()>0) {
2517 for (std::size_t i=0; i<NDIM; ++i) l1[map[i]] = l[i];
2518 std::swap(l,l1);
2519 if (c.size()) c = copy(c.mapdim(map));
2520 }
2521
2522 if (mirror.size()>0) {
2523 // mirror translation index: l_new + l_old = l_max
2525 Translation lmax = (Translation(1)<<key.level()) - 1;
2526 for (std::size_t i=0; i<NDIM; ++i) {
2527 if (mirror[i]==-1) l1[i]= lmax - l[i];
2528 }
2529 std::swap(l,l1);
2530
2531 // mirror coefficients: multiply all odd-k slices with -1
2532 if (c.size()) {
2533 std::vector<Slice> s(___);
2534
2535 // loop over dimensions and over k
2536 for (size_t i=0; i<NDIM; ++i) {
2537 std::size_t kmax=c.dim(i);
2538 if (mirror[i]==-1) {
2539 for (size_t k=1; k<kmax; k+=2) {
2540 s[i]=Slice(k,k,1);
2541 c(s)*=(-1.0);
2542 }
2543 s[i]=_;
2544 }
2545 }
2546 }
2547 }
2548
2550 f->get_coeffs().replace(keyT(key.level(),l), nodeT(cc,node.has_children()));
2551 return true;
2552 }
2553 template <typename Archive> void serialize(const Archive& ar) {
2554 MADNESS_EXCEPTION("no serialization of do_mirror",1);
2555 }
2556
2557 };
2558
2559
2560
2561 /// "put" this on g
2562 struct do_average {
2564
2566
2567 do_average() : g(0) {}
2569
2570 /// iterator it points to this
2571 bool operator()(typename rangeT::iterator& it) const {
2572
2573 const keyT& key = it->first;
2574 const nodeT& fnode = it->second;
2575
2576 // fast return if rhs has no coeff here
2577 if (fnode.has_coeff()) {
2578
2579 // check if there is a node already existing
2580 typename dcT::accessor acc;
2581 if (g->get_coeffs().find(acc,key)) {
2582 nodeT& gnode=acc->second;
2583 if (gnode.has_coeff()) gnode.coeff()+=fnode.coeff();
2584 } else {
2585 g->get_coeffs().replace(key,fnode);
2586 }
2587 }
2588
2589 return true;
2590 }
2591 template <typename Archive> void serialize(const Archive& ar) {}
2592 };
2593
2594 /// change representation of nodes' coeffs to low rank, optional fence
2597
2598 // threshold for rank reduction / SVD truncation
2601
2602 // constructor takes target precision
2604 // do_change_tensor_type(const TensorArgs& targs) : targs(targs) {}
2606
2607 //
2608 bool operator()(typename rangeT::iterator& it) const {
2609
2610 double cpu0=cpu_time();
2611 nodeT& node = it->second;
2613 double cpu1=cpu_time();
2615
2616 return true;
2617
2618 }
2619 template <typename Archive> void serialize(const Archive& ar) {}
2620 };
2621
2624
2625 // threshold for rank reduction / SVD truncation
2627
2628 // constructor takes target precision
2631 bool operator()(typename rangeT::iterator& it) const {
2632 it->second.consolidate_buffer(targs);
2633 return true;
2634 }
2635 template <typename Archive> void serialize(const Archive& ar) {}
2636 };
2637
2638
2639
2640 template <typename opT>
2644 opT op;
2646 bool operator()(typename rangeT::iterator& it) const {
2647 const keyT& key = it->first;
2648 nodeT& node = it->second;
2649 if (node.has_coeff()) {
2650 const TensorArgs full_args(-1.0,TT_FULL);
2651 change_tensor_type(node.coeff(),full_args);
2652 tensorT& t= node.coeff().full_tensor();
2653 //double before = t.normf();
2654 tensorT values = impl->fcube_for_mul(key, key, t);
2655 op(key, values);
2656 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
2657 t = transform(values,impl->cdata.quad_phiw).scale(scale);
2658 node.coeff()=coeffT(t,impl->get_tensor_args());
2659 //double after = t.normf();
2660 //madness::print("XOP:", key, before, after);
2661 }
2662 return true;
2663 }
2664 template <typename Archive> void serialize(const Archive& ar) {}
2665 };
2666
2667 template <typename Q, typename R>
2668 /// @todo I don't know what this does other than a trasform
2669 void vtransform_doit(const std::shared_ptr< FunctionImpl<R,NDIM> >& right,
2670 const Tensor<Q>& c,
2671 const std::vector< std::shared_ptr< FunctionImpl<T,NDIM> > >& vleft,
2672 double tol) {
2673 // To reduce crunch on vectors being transformed each task
2674 // does them in a random order
2675 std::vector<unsigned int> ind(vleft.size());
2676 for (unsigned int i=0; i<vleft.size(); ++i) {
2677 ind[i] = i;
2678 }
2679 for (unsigned int i=0; i<vleft.size(); ++i) {
2680 unsigned int j = RandomValue<int>()%vleft.size();
2681 std::swap(ind[i],ind[j]);
2682 }
2683
2684 typename FunctionImpl<R,NDIM>::dcT::const_iterator end = right->coeffs.end();
2685 for (typename FunctionImpl<R,NDIM>::dcT::const_iterator it=right->coeffs.begin(); it != end; ++it) {
2686 if (it->second.has_coeff()) {
2687 const Key<NDIM>& key = it->first;
2688 const GenTensor<R>& r = it->second.coeff();
2689 double norm = r.normf();
2690 double keytol = truncate_tol(tol,key);
2691
2692 for (unsigned int j=0; j<vleft.size(); ++j) {
2693 unsigned int i = ind[j]; // Random permutation
2694 if (std::abs(norm*c(i)) > keytol) {
2695 implT* left = vleft[i].get();
2696 typename dcT::accessor acc;
2697 bool newnode = left->coeffs.insert(acc,key);
2698 if (newnode && key.level()>0) {
2699 Key<NDIM> parent = key.parent();
2700 if (left->coeffs.is_local(parent))
2701 left->coeffs.send(parent, &nodeT::set_has_children_recursive, left->coeffs, parent);
2702 else
2703 left->coeffs.task(parent, &nodeT::set_has_children_recursive, left->coeffs, parent);
2704
2705 }
2706 nodeT& node = acc->second;
2707 if (!node.has_coeff())
2708 node.set_coeff(coeffT(cdata.v2k,targs));
2709 coeffT& t = node.coeff();
2710 t.gaxpy(1.0, r, c(i));
2711 }
2712 }
2713 }
2714 }
2715 }
2716
2717 /// Refine multiple functions down to the same finest level
2718
2719 /// @param v the vector of functions we are refining.
2720 /// @param key the current node.
2721 /// @param c the vector of coefficients passed from above.
2722 void refine_to_common_level(const std::vector<FunctionImpl<T,NDIM>*>& v,
2723 const std::vector<tensorT>& c,
2724 const keyT key);
2725
2726 /// Inplace operate on many functions (impl's) with an operator within a certain box
2727 /// @param[in] key the key of the current function node (box)
2728 /// @param[in] op the operator
2729 /// @param[in] v the vector of function impl's on which to be operated
2730 template <typename opT>
2731 void multiop_values_doit(const keyT& key, const opT& op, const std::vector<implT*>& v) {
2732 std::vector<tensorT> c(v.size());
2733 for (unsigned int i=0; i<v.size(); i++) {
2734 if (v[i]) {
2735 coeffT cc = coeffs2values(key, v[i]->coeffs.find(key).get()->second.coeff());
2736 c[i]=cc.full_tensor();
2737 }
2738 }
2739 tensorT r = op(key, c);
2740 coeffs.replace(key, nodeT(coeffT(values2coeffs(key, r),targs),false));
2741 }
2742
2743 /// Inplace operate on many functions (impl's) with an operator within a certain box
2744 /// Assumes all functions have been refined down to the same level
2745 /// @param[in] op the operator
2746 /// @param[in] v the vector of function impl's on which to be operated
2747 template <typename opT>
2748 void multiop_values(const opT& op, const std::vector<implT*>& v) {
2749 // rough check on refinement level (ignore non-initialized functions
2750 for (std::size_t i=1; i<v.size(); ++i) {
2751 if (v[i] and v[i-1]) {
2752 MADNESS_ASSERT(v[i]->coeffs.size()==v[i-1]->coeffs.size());
2753 }
2754 }
2755 typename dcT::iterator end = v[0]->coeffs.end();
2756 for (typename dcT::iterator it=v[0]->coeffs.begin(); it!=end; ++it) {
2757 const keyT& key = it->first;
2758 if (it->second.has_coeff())
2759 world.taskq.add(*this, &implT:: template multiop_values_doit<opT>, key, op, v);
2760 else
2761 coeffs.replace(key, nodeT(coeffT(),true));
2762 }
2763 world.gop.fence();
2764 }
2765
2766 /// Inplace operate on many functions (impl's) with an operator within a certain box
2767
2768 /// @param[in] key the key of the current function node (box)
2769 /// @param[in] op the operator
2770 /// @param[in] vin the vector of function impl's on which to be operated
2771 /// @param[out] vout the resulting vector of function impl's
2772 template <typename opT>
2773 void multi_to_multi_op_values_doit(const keyT& key, const opT& op,
2774 const std::vector<implT*>& vin, std::vector<implT*>& vout) {
2775 std::vector<tensorT> c(vin.size());
2776 for (unsigned int i=0; i<vin.size(); i++) {
2777 if (vin[i]) {
2778 coeffT cc = coeffs2values(key, vin[i]->coeffs.find(key).get()->second.coeff());
2779 c[i]=cc.full_tensor();
2780 }
2781 }
2782 std::vector<tensorT> r = op(key, c);
2783 MADNESS_ASSERT(r.size()==vout.size());
2784 for (std::size_t i=0; i<vout.size(); ++i) {
2785 vout[i]->coeffs.replace(key, nodeT(coeffT(values2coeffs(key, r[i]),targs),false));
2786 }
2787 }
2788
2789 /// Inplace operate on many functions (impl's) with an operator within a certain box
2790
2791 /// Assumes all functions have been refined down to the same level
2792 /// @param[in] op the operator
2793 /// @param[in] vin the vector of function impl's on which to be operated
2794 /// @param[out] vout the resulting vector of function impl's
2795 template <typename opT>
2796 void multi_to_multi_op_values(const opT& op, const std::vector<implT*>& vin,
2797 std::vector<implT*>& vout, const bool fence=true) {
2798 // rough check on refinement level (ignore non-initialized functions
2799 for (std::size_t i=1; i<vin.size(); ++i) {
2800 if (vin[i] and vin[i-1]) {
2801 MADNESS_ASSERT(vin[i]->coeffs.size()==vin[i-1]->coeffs.size());
2802 }
2803 }
2804 typename dcT::iterator end = vin[0]->coeffs.end();
2805 for (typename dcT::iterator it=vin[0]->coeffs.begin(); it!=end; ++it) {
2806 const keyT& key = it->first;
2807 if (it->second.has_coeff())
2808 world.taskq.add(*this, &implT:: template multi_to_multi_op_values_doit<opT>,
2809 key, op, vin, vout);
2810 else {
2811 // fill result functions with empty box in this key
2812 for (implT* it2 : vout) {
2813 it2->coeffs.replace(key, nodeT(coeffT(),true));
2814 }
2815 }
2816 }
2817 if (fence) world.gop.fence();
2818 }
2819
2820 /// Transforms a vector of functions left[i] = sum[j] right[j]*c[j,i] using sparsity
2821 /// @param[in] vright vector of functions (impl's) on which to be transformed
2822 /// @param[in] c the tensor (matrix) transformer
2823 /// @param[in] vleft vector of of the *newly* transformed functions (impl's)
2824 template <typename Q, typename R>
2825 void vtransform(const std::vector< std::shared_ptr< FunctionImpl<R,NDIM> > >& vright,
2826 const Tensor<Q>& c,
2827 const std::vector< std::shared_ptr< FunctionImpl<T,NDIM> > >& vleft,
2828 double tol,
2829 bool fence) {
2830 for (unsigned int j=0; j<vright.size(); ++j) {
2831 world.taskq.add(*this, &implT:: template vtransform_doit<Q,R>, vright[j], copy(c(j,_)), vleft, tol);
2832 }
2833 if (fence)
2834 world.gop.fence();
2835 }
2836
2837 /// Unary operation applied inplace to the values with optional refinement and fence
2838 /// @param[in] op the unary operator for the values
2839 template <typename opT>
2840 void unary_op_value_inplace(const opT& op, bool fence) {
2842 typedef do_unary_op_value_inplace<opT> xopT;
2843 world.taskq.for_each<rangeT,xopT>(rangeT(coeffs.begin(), coeffs.end()), xopT(this,op));
2844 if (fence)
2845 world.gop.fence();
2846 }
2847
2848 // Multiplication assuming same distribution and recursive descent
2849 /// Both left and right functions are in the scaling function basis
2850 /// @param[in] key the key to the current function node (box)
2851 /// @param[in] left the function impl associated with the left function
2852 /// @param[in] lcin the scaling function coefficients associated with the
2853 /// current box in the left function
2854 /// @param[in] vrightin the vector of function impl's associated with
2855 /// the vector of right functions
2856 /// @param[in] vrcin the vector scaling function coefficients associated with the
2857 /// current box in the right functions
2858 /// @param[out] vresultin the vector of resulting functions (impl's)
2859 template <typename L, typename R>
2860 void mulXXveca(const keyT& key,
2861 const FunctionImpl<L,NDIM>* left, const Tensor<L>& lcin,
2862 const std::vector<const FunctionImpl<R,NDIM>*> vrightin,
2863 const std::vector< Tensor<R> >& vrcin,
2864 const std::vector<FunctionImpl<T,NDIM>*> vresultin,
2865 double tol) {
2866 typedef typename FunctionImpl<L,NDIM>::dcT::const_iterator literT;
2867 typedef typename FunctionImpl<R,NDIM>::dcT::const_iterator riterT;
2868
2869 double lnorm = 1e99;
2870 Tensor<L> lc = lcin;
2871 if (lc.size() == 0) {
2872 literT it = left->coeffs.find(key).get();
2873 MADNESS_ASSERT(it != left->coeffs.end());
2874 lnorm = it->second.get_norm_tree();
2875 if (it->second.has_coeff())
2876 lc = it->second.coeff().full_tensor_copy();
2877 }
2878
2879 // Loop thru RHS functions seeing if anything can be multiplied
2880 std::vector<FunctionImpl<T,NDIM>*> vresult;
2881 std::vector<const FunctionImpl<R,NDIM>*> vright;
2882 std::vector< Tensor<R> > vrc;
2883 vresult.reserve(vrightin.size());
2884 vright.reserve(vrightin.size());
2885 vrc.reserve(vrightin.size());
2886
2887 for (unsigned int i=0; i<vrightin.size(); ++i) {
2888 FunctionImpl<T,NDIM>* result = vresultin[i];
2889 const FunctionImpl<R,NDIM>* right = vrightin[i];
2890 Tensor<R> rc = vrcin[i];
2891 double rnorm;
2892 if (rc.size() == 0) {
2893 riterT it = right->coeffs.find(key).get();
2894 MADNESS_ASSERT(it != right->coeffs.end());
2895 rnorm = it->second.get_norm_tree();
2896 if (it->second.has_coeff())
2897 rc = it->second.coeff().full_tensor_copy();
2898 }
2899 else {
2900 rnorm = rc.normf();
2901 }
2902
2903 if (rc.size() && lc.size()) { // Yipee!
2904 result->task(world.rank(), &implT:: template do_mul<L,R>, key, lc, std::make_pair(key,rc));
2905 }
2906 else if (tol && lnorm*rnorm < truncate_tol(tol, key)) {
2907 result->coeffs.replace(key, nodeT(coeffT(cdata.vk,targs),false)); // Zero leaf
2908 }
2909 else { // Interior node
2910 result->coeffs.replace(key, nodeT(coeffT(),true));
2911 vresult.push_back(result);
2912 vright.push_back(right);
2913 vrc.push_back(rc);
2914 }
2915 }
2916
2917 if (vresult.size()) {
2918 Tensor<L> lss;
2919 if (lc.size()) {
2920 Tensor<L> ld(cdata.v2k);
2921 ld(cdata.s0) = lc(___);
2922 lss = left->unfilter(ld);
2923 }
2924
2925 std::vector< Tensor<R> > vrss(vresult.size());
2926 for (unsigned int i=0; i<vresult.size(); ++i) {
2927 if (vrc[i].size()) {
2928 Tensor<R> rd(cdata.v2k);
2929 rd(cdata.s0) = vrc[i](___);
2930 vrss[i] = vright[i]->unfilter(rd);
2931 }
2932 }
2933
2934 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
2935 const keyT& child = kit.key();
2936 Tensor<L> ll;
2937
2938 std::vector<Slice> cp = child_patch(child);
2939
2940 if (lc.size())
2941 ll = copy(lss(cp));
2942
2943 std::vector< Tensor<R> > vv(vresult.size());
2944 for (unsigned int i=0; i<vresult.size(); ++i) {
2945 if (vrc[i].size())
2946 vv[i] = copy(vrss[i](cp));
2947 }
2948
2949 woT::task(coeffs.owner(child), &implT:: template mulXXveca<L,R>, child, left, ll, vright, vv, vresult, tol);
2950 }
2951 }
2952 }
2953
2954 /// Multiplication using recursive descent and assuming same distribution
2955 /// Both left and right functions are in the scaling function basis
2956 /// @param[in] key the key to the current function node (box)
2957 /// @param[in] left the function impl associated with the left function
2958 /// @param[in] lcin the scaling function coefficients associated with the
2959 /// current box in the left function
2960 /// @param[in] right the function impl associated with the right function
2961 /// @param[in] rcin the scaling function coefficients associated with the
2962 /// current box in the right function
2963 template <typename L, typename R>
2964 void mulXXa(const keyT& key,
2965 const FunctionImpl<L,NDIM>* left, const Tensor<L>& lcin,
2966 const FunctionImpl<R,NDIM>* right,const Tensor<R>& rcin,
2967 double tol) {
2968 typedef typename FunctionImpl<L,NDIM>::dcT::const_iterator literT;
2969 typedef typename FunctionImpl<R,NDIM>::dcT::const_iterator riterT;
2970
2971 double lnorm=1e99, rnorm=1e99;
2972
2973 Tensor<L> lc = lcin;
2974 if (lc.size() == 0) {
2975 literT it = left->coeffs.find(key).get();
2976 MADNESS_ASSERT(it != left->coeffs.end());
2977 lnorm = it->second.get_norm_tree();
2978 if (it->second.has_coeff())
2979 lc = it->second.coeff().reconstruct_tensor();
2980 }
2981
2982 Tensor<R> rc = rcin;
2983 if (rc.size() == 0) {
2984 riterT it = right->coeffs.find(key).get();
2985 MADNESS_ASSERT(it != right->coeffs.end());
2986 rnorm = it->second.get_norm_tree();
2987 if (it->second.has_coeff())
2988 rc = it->second.coeff().reconstruct_tensor();
2989 }
2990
2991 // both nodes are leaf nodes: multiply and return
2992 if (rc.size() && lc.size()) { // Yipee!
2993 do_mul<L,R>(key, lc, std::make_pair(key,rc));
2994 return;
2995 }
2996
2997 if (tol) {
2998 if (lc.size())
2999 lnorm = lc.normf(); // Otherwise got from norm tree above
3000 if (rc.size())
3001 rnorm = rc.normf();
3002 if (lnorm*rnorm < truncate_tol(tol, key)) {
3003 coeffs.replace(key, nodeT(coeffT(cdata.vk,targs),false)); // Zero leaf node
3004 return;
3005 }
3006 }
3007
3008 // Recur down
3009 coeffs.replace(key, nodeT(coeffT(),true)); // Interior node
3010
3011 Tensor<L> lss;
3012 if (lc.size()) {
3013 Tensor<L> ld(cdata.v2k);
3014 ld(cdata.s0) = lc(___);
3015 lss = left->unfilter(ld);
3016 }
3017
3018 Tensor<R> rss;
3019 if (rc.size()) {
3020 Tensor<R> rd(cdata.v2k);
3021 rd(cdata.s0) = rc(___);
3022 rss = right->unfilter(rd);
3023 }
3024
3025 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
3026 const keyT& child = kit.key();
3027 Tensor<L> ll;
3028 Tensor<R> rr;
3029 if (lc.size())
3030 ll = copy(lss(child_patch(child)));
3031 if (rc.size())
3032 rr = copy(rss(child_patch(child)));
3033
3034 woT::task(coeffs.owner(child), &implT:: template mulXXa<L,R>, child, left, ll, right, rr, tol);
3035 }
3036 }
3037
3038
3039 // Binary operation on values using recursive descent and assuming same distribution
3040 /// Both left and right functions are in the scaling function basis
3041 /// @param[in] key the key to the current function node (box)
3042 /// @param[in] left the function impl associated with the left function
3043 /// @param[in] lcin the scaling function coefficients associated with the
3044 /// current box in the left function
3045 /// @param[in] right the function impl associated with the right function
3046 /// @param[in] rcin the scaling function coefficients associated with the
3047 /// current box in the right function
3048 /// @param[in] op the binary operator
3049 template <typename L, typename R, typename opT>
3050 void binaryXXa(const keyT& key,
3051 const FunctionImpl<L,NDIM>* left, const Tensor<L>& lcin,
3052 const FunctionImpl<R,NDIM>* right,const Tensor<R>& rcin,
3053 const opT& op) {
3054 typedef typename FunctionImpl<L,NDIM>::dcT::const_iterator literT;
3055 typedef typename FunctionImpl<R,NDIM>::dcT::const_iterator riterT;
3056
3057 Tensor<L> lc = lcin;
3058 if (lc.size() == 0) {
3059 literT it = left->coeffs.find(key).get();
3060 MADNESS_ASSERT(it != left->coeffs.end());
3061 if (it->second.has_coeff())
3062 lc = it->second.coeff().reconstruct_tensor();
3063 }
3064
3065 Tensor<R> rc = rcin;
3066 if (rc.size() == 0) {
3067 riterT it = right->coeffs.find(key).get();
3068 MADNESS_ASSERT(it != right->coeffs.end());
3069 if (it->second.has_coeff())
3070 rc = it->second.coeff().reconstruct_tensor();
3071 }
3072
3073 if (rc.size() && lc.size()) { // Yipee!
3074 do_binary_op<L,R>(key, lc, std::make_pair(key,rc), op);
3075 return;
3076 }
3077
3078 // Recur down
3079 coeffs.replace(key, nodeT(coeffT(),true)); // Interior node
3080
3081 Tensor<L> lss;
3082 if (lc.size()) {
3083 Tensor<L> ld(cdata.v2k);
3084 ld(cdata.s0) = lc(___);
3085 lss = left->unfilter(ld);
3086 }
3087
3088 Tensor<R> rss;
3089 if (rc.size()) {
3090 Tensor<R> rd(cdata.v2k);
3091 rd(cdata.s0) = rc(___);
3092 rss = right->unfilter(rd);
3093 }
3094
3095 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
3096 const keyT& child = kit.key();
3097 Tensor<L> ll;
3098 Tensor<R> rr;
3099 if (lc.size())
3100 ll = copy(lss(child_patch(child)));
3101 if (rc.size())
3102 rr = copy(rss(child_patch(child)));
3103
3104 woT::task(coeffs.owner(child), &implT:: template binaryXXa<L,R,opT>, child, left, ll, right, rr, op);
3105 }
3106 }
3107
3108 template <typename Q, typename opT>
3110 typedef typename opT::resultT resultT;
3112 opT op;
3113
3118
3119 Tensor<resultT> operator()(const Key<NDIM>& key, const Tensor<Q>& t) const {
3120 Tensor<Q> invalues = impl_func->coeffs2values(key, t);
3121
3122 Tensor<resultT> outvalues = op(key, invalues);
3123
3124 return impl_func->values2coeffs(key, outvalues);
3125 }
3126
3127 template <typename Archive>
3128 void serialize(Archive& ar) {
3129 ar & impl_func & op;
3130 }
3131 };
3132
3133 /// Out of place unary operation on function impl
3134 /// The skeleton algorithm should resemble something like
3135 ///
3136 /// *this = op(*func)
3137 ///
3138 /// @param[in] key the key of the current function node (box)
3139 /// @param[in] func the function impl on which to be operated
3140 /// @param[in] op the unary operator
3141 template <typename Q, typename opT>
3142 void unaryXXa(const keyT& key,
3143 const FunctionImpl<Q,NDIM>* func, const opT& op) {
3144
3145 // const Tensor<Q>& fc = func->coeffs.find(key).get()->second.full_tensor_copy();
3146 const Tensor<Q> fc = func->coeffs.find(key).get()->second.coeff().reconstruct_tensor();
3147
3148 if (fc.size() == 0) {
3149 // Recur down
3150 coeffs.replace(key, nodeT(coeffT(),true)); // Interior node
3151 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
3152 const keyT& child = kit.key();
3153 woT::task(coeffs.owner(child), &implT:: template unaryXXa<Q,opT>, child, func, op);
3154 }
3155 }
3156 else {
3157 tensorT t=op(key,fc);
3158 coeffs.replace(key, nodeT(coeffT(t,targs),false)); // Leaf node
3159 }
3160 }
3161
3162 /// Multiplies two functions (impl's) together. Delegates to the mulXXa() method
3163 /// @param[in] left pointer to the left function impl
3164 /// @param[in] right pointer to the right function impl
3165 /// @param[in] tol numerical tolerance
3166 template <typename L, typename R>
3167 void mulXX(const FunctionImpl<L,NDIM>* left, const FunctionImpl<R,NDIM>* right, double tol, bool fence) {
3168 if (world.rank() == coeffs.owner(cdata.key0))
3169 mulXXa(cdata.key0, left, Tensor<L>(), right, Tensor<R>(), tol);
3170 if (fence)
3171 world.gop.fence();
3172
3173 //verify_tree();
3174 }
3175
3176 /// Performs binary operation on two functions (impl's). Delegates to the binaryXXa() method
3177 /// @param[in] left pointer to the left function impl
3178 /// @param[in] right pointer to the right function impl
3179 /// @param[in] op the binary operator
3180 template <typename L, typename R, typename opT>
3182 const opT& op, bool fence) {
3183 if (world.rank() == coeffs.owner(cdata.key0))
3184 binaryXXa(cdata.key0, left, Tensor<L>(), right, Tensor<R>(), op);
3185 if (fence)
3186 world.gop.fence();
3187
3188 //verify_tree();
3189 }
3190
3191 /// Performs unary operation on function impl. Delegates to the unaryXXa() method
3192 /// @param[in] func function impl of the operand
3193 /// @param[in] op the unary operator
3194 template <typename Q, typename opT>
3195 void unaryXX(const FunctionImpl<Q,NDIM>* func, const opT& op, bool fence) {
3196 if (world.rank() == coeffs.owner(cdata.key0))
3197 unaryXXa(cdata.key0, func, op);
3198 if (fence)
3199 world.gop.fence();
3200
3201 //verify_tree();
3202 }
3203
3204 /// Performs unary operation on function impl. Delegates to the unaryXXa() method
3205 /// @param[in] func function impl of the operand
3206 /// @param[in] op the unary operator
3207 template <typename Q, typename opT>
3208 void unaryXXvalues(const FunctionImpl<Q,NDIM>* func, const opT& op, bool fence) {
3209 if (world.rank() == coeffs.owner(cdata.key0))
3211 if (fence)
3212 world.gop.fence();
3213
3214 //verify_tree();
3215 }
3216
3217 /// Multiplies a function (impl) with a vector of functions (impl's). Delegates to the
3218 /// mulXXveca() method.
3219 /// @param[in] left pointer to the left function impl
3220 /// @param[in] vright vector of pointers to the right function impl's
3221 /// @param[in] tol numerical tolerance
3222 /// @param[out] vresult vector of pointers to the resulting function impl's
3223 template <typename L, typename R>
3225 const std::vector<const FunctionImpl<R,NDIM>*>& vright,
3226 const std::vector<FunctionImpl<T,NDIM>*>& vresult,
3227 double tol,
3228 bool fence) {
3229 std::vector< Tensor<R> > vr(vright.size());
3230 if (world.rank() == coeffs.owner(cdata.key0))
3231 mulXXveca(cdata.key0, left, Tensor<L>(), vright, vr, vresult, tol);
3232 if (fence)
3233 world.gop.fence();
3234 }
3235
3237
3238 mutable long box_leaf[1000];
3239 mutable long box_interior[1000];
3240
3241 // horrifically non-scalable
3242 void put_in_box(ProcessID from, long nl, long ni) const;
3243
3244 /// Prints summary of data distribution
3245 void print_info() const;
3246
3247 /// Verify tree is properly constructed ... global synchronization involved
3248
3249 /// If an inconsistency is detected, prints a message describing the error and
3250 /// then throws a madness exception.
3251 ///
3252 /// This is a reasonably quick and scalable operation that is
3253 /// useful for debugging and paranoia.
3254 void verify_tree() const;
3255
3256 /// Walk up the tree returning pair(key,node) for first node with coefficients
3257
3258 /// Three possibilities.
3259 ///
3260 /// 1) The coeffs are present and returned with the key of the containing node.
3261 ///
3262 /// 2) The coeffs are further up the tree ... the request is forwarded up.
3263 ///
3264 /// 3) The coeffs are futher down the tree ... an empty tensor is returned.
3265 ///
3266 /// !! This routine is crying out for an optimization to
3267 /// manage the number of messages being sent ... presently
3268 /// each parent is fetched 2^(n*d) times where n is the no. of
3269 /// levels between the level of evaluation and the parent.
3270 /// Alternatively, reimplement multiply as a downward tree
3271 /// walk and just pass the parent down. Slightly less
3272 /// parallelism but much less communication.
3273 /// @todo Robert .... help!
3274 void sock_it_to_me(const keyT& key,
3275 const RemoteReference< FutureImpl< std::pair<keyT,coeffT> > >& ref) const;
3276 /// As above, except
3277 /// 3) The coeffs are constructed from the avg of nodes further down the tree
3278 /// @todo Robert .... help!
3279 void sock_it_to_me_too(const keyT& key,
3280 const RemoteReference< FutureImpl< std::pair<keyT,coeffT> > >& ref) const;
3281
3282 /// @todo help!
3284 const keyT& key,
3285 const coordT& plotlo, const coordT& plothi, const std::vector<long>& npt,
3286 bool eval_refine) const;
3287
3288
3289 /// Evaluate a cube/slice of points ... plotlo and plothi are already in simulation coordinates
3290 /// No communications
3291 /// @param[in] plotlo the coordinate of the starting point
3292 /// @param[in] plothi the coordinate of the ending point
3293 /// @param[in] npt the number of points in each dimension
3294 Tensor<T> eval_plot_cube(const coordT& plotlo,
3295 const coordT& plothi,
3296 const std::vector<long>& npt,
3297 const bool eval_refine = false) const;
3298
3299
3300 /// Evaluate function only if point is local returning (true,value); otherwise return (false,0.0)
3301
3302 /// maxlevel is the maximum depth to search down to --- the max local depth can be
3303 /// computed with max_local_depth();
3304 std::pair<bool,T> eval_local_only(const Vector<double,NDIM>& xin, Level maxlevel) ;
3305
3306
3307 /// Evaluate the function at a point in \em simulation coordinates
3308
3309 /// Only the invoking process will get the result via the
3310 /// remote reference to a future. Active messages may be sent
3311 /// to other nodes.
3312 void eval(const Vector<double,NDIM>& xin,
3313 const keyT& keyin,
3314 const typename Future<T>::remote_refT& ref);
3315
3316 /// Get the depth of the tree at a point in \em simulation coordinates
3317
3318 /// Only the invoking process will get the result via the
3319 /// remote reference to a future. Active messages may be sent
3320 /// to other nodes.
3321 ///
3322 /// This function is a minimally-modified version of eval()
3323 void evaldepthpt(const Vector<double,NDIM>& xin,
3324 const keyT& keyin,
3325 const typename Future<Level>::remote_refT& ref);
3326
3327 /// Get the rank of leaf box of the tree at a point in \em simulation coordinates
3328
3329 /// Only the invoking process will get the result via the
3330 /// remote reference to a future. Active messages may be sent
3331 /// to other nodes.
3332 ///
3333 /// This function is a minimally-modified version of eval()
3334 void evalR(const Vector<double,NDIM>& xin,
3335 const keyT& keyin,
3336 const typename Future<long>::remote_refT& ref);
3337
3338
3339 /// Computes norm of low/high-order polyn. coeffs for autorefinement test
3340
3341 /// t is a k^d tensor. In order to screen the autorefinement
3342 /// during multiplication compute the norms of
3343 /// ... lo ... the block of t for all polynomials of order < k/2
3344 /// ... hi ... the block of t for all polynomials of order >= k/2
3345 ///
3346 /// k=5 0,1,2,3,4 --> 0,1,2 ... 3,4
3347 /// k=6 0,1,2,3,4,5 --> 0,1,2 ... 3,4,5
3348 ///
3349 /// k=number of wavelets, so k=5 means max order is 4, so max exactly
3350 /// representable squarable polynomial is of order 2.
3351 void static tnorm(const tensorT& t, double* lo, double* hi);
3352
3353 void static tnorm(const GenTensor<T>& t, double* lo, double* hi);
3354
3355 void static tnorm(const SVDTensor<T>& t, double* lo, double* hi, const int particle);
3356
3357 // This invoked if node has not been autorefined
3358 void do_square_inplace(const keyT& key);
3359
3360 // This invoked if node has been autorefined
3361 void do_square_inplace2(const keyT& parent, const keyT& child, const tensorT& parent_coeff);
3362
3363 /// Always returns false (for when autorefine is not wanted)
3364 bool noautorefine(const keyT& key, const tensorT& t) const;
3365
3366 /// Returns true if this block of coeffs needs autorefining
3367 bool autorefine_square_test(const keyT& key, const nodeT& t) const;
3368
3369 /// Pointwise squaring of function with optional global fence
3370
3371 /// If not autorefining, local computation only if not fencing.
3372 /// If autorefining, may result in asynchronous communication.
3373 void square_inplace(bool fence);
3374 void abs_inplace(bool fence);
3375 void abs_square_inplace(bool fence);
3376
3377 /// is this the same as trickle_down() ?
3378 void sum_down_spawn(const keyT& key, const coeffT& s);
3379
3380 /// After 1d push operator must sum coeffs down the tree to restore correct scaling function coefficients
3381 void sum_down(bool fence);
3382
3383 /// perform this multiplication: h(1,2) = f(1,2) * g(1)
3384 template<size_t LDIM>
3386
3387 static bool randomize() {return false;}
3391
3392 implT* h; ///< the result function h(1,2) = f(1,2) * g(1)
3395 int particle; ///< if g is g(1) or g(2)
3396
3397 multiply_op() : h(), f(), g(), particle(1) {}
3398
3399 multiply_op(implT* h1, const ctT& f1, const ctL& g1, const int particle1)
3400 : h(h1), f(f1), g(g1), particle(particle1) {};
3401
3402 /// return true if this will be a leaf node
3403
3404 /// use generalization of tnorm for a GenTensor
3405 bool screen(const coeffT& fcoeff, const coeffT& gcoeff, const keyT& key) const {
3407 MADNESS_ASSERT(fcoeff.is_svd_tensor());
3410
3411 double glo=0.0, ghi=0.0, flo=0.0, fhi=0.0;
3412 g.get_impl()->tnorm(gcoeff.get_tensor(), &glo, &ghi);
3413 g.get_impl()->tnorm(fcoeff.get_svdtensor(),&flo,&fhi,particle);
3414
3415 double total_hi=glo*fhi + ghi*flo + fhi*ghi;
3416 return (total_hi<h->truncate_tol(h->get_thresh(),key));
3417
3418 }
3419
3420 /// apply this on a FunctionNode of f and g of Key key
3421
3422 /// @param[in] key key for FunctionNode in f and g, (g: broken into particles)
3423 /// @return <this node is a leaf, coefficients of this node>
3424 std::pair<bool,coeffT> operator()(const Key<NDIM>& key) const {
3425
3426 // bool is_leaf=(not fdatum.second.has_children());
3427 // if (not is_leaf) return std::pair<bool,coeffT> (is_leaf,coeffT());
3428
3429 // break key into particles (these are the child keys, with f/gdatum come the parent keys)
3430 Key<LDIM> key1,key2;
3431 key.break_apart(key1,key2);
3432 const Key<LDIM> gkey= (particle==1) ? key1 : key2;
3433
3434 // get coefficients of the actual FunctionNode
3435 coeffT coeff1=f.get_impl()->parent_to_child(f.coeff(),f.key(),key);
3436 coeff1.normalize();
3437 const coeffT coeff2=g.get_impl()->parent_to_child(g.coeff(),g.key(),gkey);
3438
3439 // multiplication is done in TT_2D
3440 coeffT coeff1_2D=coeff1.convert(TensorArgs(h->get_thresh(),TT_2D));
3441 coeff1_2D.normalize();
3442
3443 bool is_leaf=screen(coeff1_2D,coeff2,key);
3444 if (key.level()<2) is_leaf=false;
3445
3446 coeffT hcoeff;
3447 if (is_leaf) {
3448
3449 // convert coefficients to values
3450 coeffT hvalues=f.get_impl()->coeffs2values(key,coeff1_2D);
3451 coeffT gvalues=g.get_impl()->coeffs2values(gkey,coeff2);
3452
3453 // perform multiplication
3454 coeffT result_val=h->multiply(hvalues,gvalues,particle-1);
3455
3456 hcoeff=h->values2coeffs(key,result_val);
3457
3458 // conversion on coeffs, not on values, because it implies truncation!
3459 if (not hcoeff.is_of_tensortype(h->get_tensor_type()))
3460 hcoeff=hcoeff.convert(h->get_tensor_args());
3461 }
3462
3463 return std::pair<bool,coeffT> (is_leaf,hcoeff);
3464 }
3465
3466 this_type make_child(const keyT& child) const {
3467
3468 // break key into particles
3469 Key<LDIM> key1, key2;
3470 child.break_apart(key1,key2);
3471 const Key<LDIM> gkey= (particle==1) ? key1 : key2;
3472
3473 return this_type(h,f.make_child(child),g.make_child(gkey),particle);
3474 }
3475
3477 Future<ctT> f1=f.activate();
3478 Future<ctL> g1=g.activate();
3479 return h->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
3480 &this_type::forward_ctor),h,f1,g1,particle);
3481 }
3482
3483 this_type forward_ctor(implT* h1, const ctT& f1, const ctL& g1, const int particle) {
3484 return this_type(h1,f1,g1,particle);
3485 }
3486
3487 template <typename Archive> void serialize(const Archive& ar) {
3488 ar & h & f & g & particle;
3489 }
3490 };
3491
3492
3493 /// add two functions f and g: result=alpha * f + beta * g
3494 struct add_op {
3495
3498
3499 bool randomize() const {return false;}
3500
3501 /// tracking coeffs of first and second addend
3503 /// prefactor for f, g
3504 double alpha, beta;
3505
3506 add_op() = default;
3507 add_op(const ctT& f, const ctT& g, const double alpha, const double beta)
3508 : f(f), g(g), alpha(alpha), beta(beta){}
3509
3510 /// if we are at the bottom of the trees, return the sum of the coeffs
3511 std::pair<bool,coeffT> operator()(const keyT& key) const {
3512
3513 bool is_leaf=(f.is_leaf() and g.is_leaf());
3514 if (not is_leaf) return std::pair<bool,coeffT> (is_leaf,coeffT());
3515
3516 coeffT fcoeff=f.get_impl()->parent_to_child(f.coeff(),f.key(),key);
3517 coeffT gcoeff=g.get_impl()->parent_to_child(g.coeff(),g.key(),key);
3518 coeffT hcoeff=copy(fcoeff);
3519 hcoeff.gaxpy(alpha,gcoeff,beta);
3520 hcoeff.reduce_rank(f.get_impl()->get_tensor_args().thresh);
3521 return std::pair<bool,coeffT> (is_leaf,hcoeff);
3522 }
3523
3524 this_type make_child(const keyT& child) const {
3525 return this_type(f.make_child(child),g.make_child(child),alpha,beta);
3526 }
3527
3528 /// retrieve the coefficients (parent coeffs might be remote)
3530 Future<ctT> f1=f.activate();
3531 Future<ctT> g1=g.activate();
3532 return f.get_impl()->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
3534 }
3535
3536 /// taskq-compatible ctor
3537 this_type forward_ctor(const ctT& f1, const ctT& g1, const double alpha, const double beta) {
3538 return this_type(f1,g1,alpha,beta);
3539 }
3540
3541 template <typename Archive> void serialize(const Archive& ar) {
3542 ar & f & g & alpha & beta;
3543 }
3544
3545 };
3546
3547 /// multiply f (a pair function of NDIM) with an orbital g (LDIM=NDIM/2)
3548
3549 /// as in (with h(1,2)=*this) : h(1,2) = g(1) * f(1,2)
3550 /// use tnorm as a measure to determine if f (=*this) must be refined
3551 /// @param[in] f the NDIM function f=f(1,2)
3552 /// @param[in] g the LDIM function g(1) (or g(2))
3553 /// @param[in] particle 1 or 2, as in g(1) or g(2)
3554 template<size_t LDIM>
3555 void multiply(const implT* f, const FunctionImpl<T,LDIM>* g, const int particle) {
3556
3559
3560 typedef multiply_op<LDIM> coeff_opT;
3561 coeff_opT coeff_op(this,ff,gg,particle);
3562
3563 typedef insert_op<T,NDIM> apply_opT;
3564 apply_opT apply_op(this);
3565
3566 keyT key0=f->cdata.key0;
3567 if (world.rank() == coeffs.owner(key0)) {
3569 woT::task(p, &implT:: template forward_traverse<coeff_opT,apply_opT>, coeff_op, apply_op, key0);
3570 }
3571
3573 }
3574
3575 /// Hartree product of two LDIM functions to yield a NDIM = 2*LDIM function
3576 template<size_t LDIM, typename leaf_opT>
3577 struct hartree_op {
3578 bool randomize() const {return false;}
3579
3582
3583 implT* result; ///< where to construct the pair function
3584 ctL p1, p2; ///< tracking coeffs of the two lo-dim functions
3585 leaf_opT leaf_op; ///< determine if a given node will be a leaf node
3586
3587 // ctor
3589 hartree_op(implT* result, const ctL& p11, const ctL& p22, const leaf_opT& leaf_op)
3590 : result(result), p1(p11), p2(p22), leaf_op(leaf_op) {
3591 MADNESS_ASSERT(LDIM+LDIM==NDIM);
3592 }
3593
3594 std::pair<bool,coeffT> operator()(const Key<NDIM>& key) const {
3595
3596 // break key into particles (these are the child keys, with datum1/2 come the parent keys)
3597 Key<LDIM> key1,key2;
3598 key.break_apart(key1,key2);
3599
3600 // this returns the appropriate NS coeffs for key1 and key2 resp.
3601 const coeffT fcoeff=p1.coeff(key1);
3602 const coeffT gcoeff=p2.coeff(key2);
3603 bool is_leaf=leaf_op(key,fcoeff.full_tensor(),gcoeff.full_tensor());
3604 if (not is_leaf) return std::pair<bool,coeffT> (is_leaf,coeffT());
3605
3606 // extract the sum coeffs from the NS coeffs
3607 const coeffT s1=fcoeff(p1.get_impl()->cdata.s0);
3608 const coeffT s2=gcoeff(p2.get_impl()->cdata.s0);
3609
3610 // new coeffs are simply the hartree/kronecker/outer product --
3611 coeffT coeff=outer(s1,s2,result->get_tensor_args());
3612 // no post-determination
3613 // is_leaf=leaf_op(key,coeff);
3614 return std::pair<bool,coeffT>(is_leaf,coeff);
3615 }
3616
3617 this_type make_child(const keyT& child) const {
3618
3619 // break key into particles
3620 Key<LDIM> key1, key2;
3621 child.break_apart(key1,key2);
3622
3623 return this_type(result,p1.make_child(key1),p2.make_child(key2),leaf_op);
3624 }
3625
3627 Future<ctL> p11=p1.activate();
3628 Future<ctL> p22=p2.activate();
3629 return result->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
3630 &this_type::forward_ctor),result,p11,p22,leaf_op);
3631 }
3632
3633 this_type forward_ctor(implT* result1, const ctL& p11, const ctL& p22, const leaf_opT& leaf_op) {
3634 return this_type(result1,p11,p22,leaf_op);
3635 }
3636
3637 template <typename Archive> void serialize(const Archive& ar) {
3638 ar & result & p1 & p2 & leaf_op;
3639 }
3640 };
3641
3642 /// traverse a non-existing tree
3643
3644 /// part II: activate coeff_op, i.e. retrieve all the necessary remote boxes (communication)
3645 /// @param[in] coeff_op operator making the coefficients that needs activation
3646 /// @param[in] apply_op just passing thru
3647 /// @param[in] key the key we are working on
3648 template<typename coeff_opT, typename apply_opT>
3649 void forward_traverse(const coeff_opT& coeff_op, const apply_opT& apply_op, const keyT& key) const {
3651 Future<coeff_opT> active_coeff=coeff_op.activate();
3652 woT::task(world.rank(), &implT:: template traverse_tree<coeff_opT,apply_opT>, active_coeff, apply_op, key);
3653 }
3654
3655
3656 /// traverse a non-existing tree
3657
3658 /// part I: make the coefficients, process them and continue the recursion if necessary
3659 /// @param[in] coeff_op operator making the coefficients and determining them being leaves
3660 /// @param[in] apply_op operator processing the coefficients
3661 /// @param[in] key the key we are currently working on
3662 template<typename coeff_opT, typename apply_opT>
3663 void traverse_tree(const coeff_opT& coeff_op, const apply_opT& apply_op, const keyT& key) const {
3665
3666 typedef typename std::pair<bool,coeffT> argT;
3667 const argT arg=coeff_op(key);
3668 apply_op.operator()(key,arg.second,arg.first);
3669
3670 const bool has_children=(not arg.first);
3671 if (has_children) {
3672 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
3673 const keyT& child=kit.key();
3674 coeff_opT child_op=coeff_op.make_child(child);
3675 // spawn activation where child is local
3676 ProcessID p=coeffs.owner(child);
3677
3678 void (implT::*ft)(const coeff_opT&, const apply_opT&, const keyT&) const = &implT::forward_traverse<coeff_opT,apply_opT>;
3679
3680 woT::task(p, ft, child_op, apply_op, child);
3681 }
3682 }
3683 }
3684
3685
3686 /// given two functions of LDIM, perform the Hartree/Kronecker/outer product
3687
3688 /// |Phi(1,2)> = |phi(1)> x |phi(2)>
3689 /// @param[in] p1 FunctionImpl of particle 1
3690 /// @param[in] p2 FunctionImpl of particle 2
3691 /// @param[in] leaf_op operator determining of a given box will be a leaf
3692 template<std::size_t LDIM, typename leaf_opT>
3693 void hartree_product(const std::vector<std::shared_ptr<FunctionImpl<T,LDIM>>> p1,
3694 const std::vector<std::shared_ptr<FunctionImpl<T,LDIM>>> p2,
3695 const leaf_opT& leaf_op, bool fence) {
3696 MADNESS_CHECK_THROW(p1.size()==p2.size(),"hartree_product: p1 and p2 must have the same size");
3697 for (auto& p : p1) MADNESS_CHECK(p->is_nonstandard() or p->is_nonstandard_with_leaves());
3698 for (auto& p : p2) MADNESS_CHECK(p->is_nonstandard() or p->is_nonstandard_with_leaves());
3699
3700 const keyT key0=cdata.key0;
3701
3702 for (std::size_t i=0; i<p1.size(); ++i) {
3703 if (world.rank() == this->get_coeffs().owner(key0)) {
3704
3705 // prepare the CoeffTracker
3706 CoeffTracker<T,LDIM> iap1(p1[i].get());
3707 CoeffTracker<T,LDIM> iap2(p2[i].get());
3708
3709 // the operator making the coefficients
3710 typedef hartree_op<LDIM,leaf_opT> coeff_opT;
3711 coeff_opT coeff_op(this,iap1,iap2,leaf_op);
3712
3713 // this operator simply inserts the coeffs into this' tree
3714// typedef insert_op<T,NDIM> apply_opT;
3715 typedef accumulate_op<T,NDIM> apply_opT;
3716 apply_opT apply_op(this);
3717
3718 woT::task(world.rank(), &implT:: template forward_traverse<coeff_opT,apply_opT>,
3719 coeff_op, apply_op, cdata.key0);
3720
3721 }
3722 }
3723
3725 if (fence) world.gop.fence();
3726 }
3727
3728
3729 template <typename opT, typename R>
3730 void
3732 const opT* op = pop.ptr;
3733 const Level n = key.level();
3734 const double cnorm = c.normf();
3735 const double tol = truncate_tol(thresh, key)*0.1; // ??? why this value????
3736
3738 const Translation lold = lnew[axis];
3739 const Translation maxs = Translation(1)<<n;
3740
3741 int nsmall = 0; // Counts neglected blocks to terminate s loop
3742 for (Translation s=0; s<maxs; ++s) {
3743 int maxdir = s ? 1 : -1;
3744 for (int direction=-1; direction<=maxdir; direction+=2) {
3745 lnew[axis] = lold + direction*s;
3746 if (lnew[axis] >= 0 && lnew[axis] < maxs) { // NON-ZERO BOUNDARY CONDITIONS IGNORED HERE !!!!!!!!!!!!!!!!!!!!
3747 const Tensor<typename opT::opT>& r = op->rnlij(n, s*direction, true);
3748 double Rnorm = r.normf();
3749
3750 if (Rnorm == 0.0) {
3751 return; // Hard zero means finished!
3752 }
3753
3754 if (s <= 1 || r.normf()*cnorm > tol) { // Always do kernel and neighbor
3755 nsmall = 0;
3756 tensorT result = transform_dir(c,r,axis);
3757
3758 if (result.normf() > tol*0.3) {
3759 Key<NDIM> dest(n,lnew);
3760 coeffs.task(dest, &nodeT::accumulate2, result, coeffs, dest, TaskAttributes::hipri());
3761 }
3762 }
3763 else {
3764 ++nsmall;
3765 }
3766 }
3767 else {
3768 ++nsmall;
3769 }
3770 }
3771 if (nsmall >= 4) {
3772 // If have two negligble blocks in
3773 // succession in each direction interpret
3774 // this as the operator being zero beyond
3775 break;
3776 }
3777 }
3778 }
3779
3780 template <typename opT, typename R>
3781 void
3782 apply_1d_realspace_push(const opT& op, const FunctionImpl<R,NDIM>* f, int axis, bool fence) {
3783 MADNESS_ASSERT(!f->is_compressed());
3784
3785 typedef typename FunctionImpl<R,NDIM>::dcT::const_iterator fiterT;
3786 typedef FunctionNode<R,NDIM> fnodeT;
3787 fiterT end = f->coeffs.end();
3788 ProcessID me = world.rank();
3789 for (fiterT it=f->coeffs.begin(); it!=end; ++it) {
3790 const fnodeT& node = it->second;
3791 if (node.has_coeff()) {
3792 const keyT& key = it->first;
3793 const Tensor<R>& c = node.coeff().full_tensor_copy();
3794 woT::task(me, &implT:: template apply_1d_realspace_push_op<opT,R>,
3796 }
3797 }
3798 if (fence) world.gop.fence();
3799 }
3800
3802 const implT* f,
3803 const keyT& key,
3804 const std::pair<keyT,coeffT>& left,
3805 const std::pair<keyT,coeffT>& center,
3806 const std::pair<keyT,coeffT>& right);
3807
3808 void do_diff1(const DerivativeBase<T,NDIM>* D,
3809 const implT* f,
3810 const keyT& key,
3811 const std::pair<keyT,coeffT>& left,
3812 const std::pair<keyT,coeffT>& center,
3813 const std::pair<keyT,coeffT>& right);
3814
3815 // Called by result function to differentiate f
3816 void diff(const DerivativeBase<T,NDIM>* D, const implT* f, bool fence);
3817
3818 /// Returns key of general neighbor enforcing BC
3819
3820 /// Out of volume keys are mapped to enforce the BC as follows.
3821 /// * Periodic BC map back into the volume and return the correct key
3822 /// * Zero BC - returns invalid() to indicate out of volume
3823 keyT neighbor(const keyT& key, const keyT& disp, const std::vector<bool>& is_periodic) const;
3824
3825 /// find_me. Called by diff_bdry to get coefficients of boundary function
3826 Future< std::pair<keyT,coeffT> > find_me(const keyT& key) const;
3827
3828 /// return the a std::pair<key, node>, which MUST exist
3829 std::pair<Key<NDIM>,ShallowNode<T,NDIM> > find_datum(keyT key) const;
3830
3831 /// multiply the ket with a one-electron potential rr(1,2)= f(1,2)*g(1)
3832
3833 /// @param[in] val_ket function values of f(1,2)
3834 /// @param[in] val_pot function values of g(1)
3835 /// @param[in] particle if 0 then g(1), if 1 then g(2)
3836 /// @return the resulting function values
3837 coeffT multiply(const coeffT& val_ket, const coeffT& val_pot, int particle) const;
3838
3839
3840 /// given several coefficient tensors, assemble a result tensor
3841
3842 /// the result looks like: (v(1,2) + v(1) + v(2)) |ket(1,2)>
3843 /// or (v(1,2) + v(1) + v(2)) |p(1) p(2)>
3844 /// i.e. coefficients for the ket and coefficients for the two particles are
3845 /// mutually exclusive. All potential terms are optional, just pass in empty coeffs.
3846 /// @param[in] key the key of the FunctionNode to which these coeffs belong
3847 /// @param[in] coeff_ket coefficients of the ket
3848 /// @param[in] vpotential1 function values of the potential for particle 1
3849 /// @param[in] vpotential2 function values of the potential for particle 2
3850 /// @param[in] veri function values for the 2-particle potential
3851 coeffT assemble_coefficients(const keyT& key, const coeffT& coeff_ket,
3852 const coeffT& vpotential1, const coeffT& vpotential2,
3853 const tensorT& veri) const;
3854
3855
3856
3857 template<std::size_t LDIM>
3861 double error=0.0;
3862 double lo=0.0, hi=0.0, lo1=0.0, hi1=0.0, lo2=0.0, hi2=0.0;
3863
3865 pointwise_multiplier(const Key<NDIM> key, const coeffT& clhs) : coeff_lhs(clhs) {
3867 val_lhs=fcf.coeffs2values(key,coeff_lhs);
3868 error=0.0;
3870 if (coeff_lhs.is_svd_tensor()) {
3873 }
3874 }
3875
3876 /// multiply values of rhs and lhs, result on rhs, rhs and lhs are of the same dimensions
3877 tensorT operator()(const Key<NDIM> key, const tensorT& coeff_rhs) {
3878
3879 MADNESS_ASSERT(coeff_rhs.dim(0)==coeff_lhs.dim(0));
3881
3882 // the tnorm estimate is not tight enough to be efficient, better use oversampling
3883 bool use_tnorm=false;
3884 if (use_tnorm) {
3885 double rlo, rhi;
3886 implT::tnorm(coeff_rhs,&rlo,&rhi);
3887 error = hi*rlo + rhi*lo + rhi*hi;
3888 tensorT val_rhs=fcf.coeffs2values(key, coeff_rhs);
3889 val_rhs.emul(val_lhs.full_tensor_copy());
3890 return fcf.values2coeffs(key,val_rhs);
3891 } else { // use quadrature of order k+1
3892
3893 auto& cdata=FunctionCommonData<T,NDIM>::get(coeff_rhs.dim(0)); // npt=k+1
3894 auto& cdata_npt=FunctionCommonData<T,NDIM>::get(coeff_rhs.dim(0)+oversampling); // npt=k+1
3895 FunctionCommonFunctionality<T,NDIM> fcf_hi_npt(cdata_npt);
3896
3897 // coeffs2values for rhs: k -> npt=k+1
3898 tensorT coeff1(cdata_npt.vk);
3899 coeff1(cdata.s0)=coeff_rhs; // s0 is smaller than vk!
3900 tensorT val_rhs_k1=fcf_hi_npt.coeffs2values(key,coeff1);
3901
3902 // coeffs2values for lhs: k -> npt=k+1
3903 tensorT coeff_lhs_k1(cdata_npt.vk);
3904 coeff_lhs_k1(cdata.s0)=coeff_lhs.full_tensor_copy();
3905 tensorT val_lhs_k1=fcf_hi_npt.coeffs2values(key,coeff_lhs_k1);
3906
3907 // multiply
3908 val_lhs_k1.emul(val_rhs_k1);
3909
3910 // values2coeffs: npt = k+1-> k
3911 tensorT result1=fcf_hi_npt.values2coeffs(key,val_lhs_k1);
3912
3913 // extract coeffs up to k
3914 tensorT result=copy(result1(cdata.s0));
3915 result1(cdata.s0)=0.0;
3916 error=result1.normf();
3917 return result;
3918 }
3919 }
3920
3921 /// multiply values of rhs and lhs, result on rhs, rhs and lhs are of differnet dimensions
3922 coeffT operator()(const Key<NDIM> key, const tensorT& coeff_rhs, const int particle) {
3923 Key<LDIM> key1, key2;
3924 key.break_apart(key1,key2);
3925 const long k=coeff_rhs.dim(0);
3927 auto& cdata_lowdim=FunctionCommonData<T,LDIM>::get(k);
3928 FunctionCommonFunctionality<T,LDIM> fcf_lo(cdata_lowdim);
3932
3933
3934 // make hi-dim values from lo-dim coeff_rhs on npt grid points
3935 tensorT ones=tensorT(fcf_lo_npt.cdata.vk);
3936 ones=1.0;
3937
3938 tensorT coeff_rhs_npt1(fcf_lo_npt.cdata.vk);
3939 coeff_rhs_npt1(fcf_lo.cdata.s0)=coeff_rhs;
3940 tensorT val_rhs_npt1=fcf_lo_npt.coeffs2values(key1,coeff_rhs_npt1);
3941
3942 TensorArgs targs(-1.0,TT_2D);
3943 coeffT val_rhs;
3944 if (particle==1) val_rhs=outer(val_rhs_npt1,ones,targs);
3945 if (particle==2) val_rhs=outer(ones,val_rhs_npt1,targs);
3946
3947 // make values from hi-dim coeff_lhs on npt grid points
3948 coeffT coeff_lhs_k1(fcf_hi_npt.cdata.vk,coeff_lhs.tensor_type());
3949 coeff_lhs_k1(fcf_hi.cdata.s0)+=coeff_lhs;
3950 coeffT val_lhs_npt=fcf_hi_npt.coeffs2values(key,coeff_lhs_k1);
3951
3952 // multiply
3953 val_lhs_npt.emul(val_rhs);
3954
3955 // values2coeffs: npt = k+1-> k
3956 coeffT result1=fcf_hi_npt.values2coeffs(key,val_lhs_npt);
3957
3958 // extract coeffs up to k
3959 coeffT result=copy(result1(cdata.s0));
3960 result1(cdata.s0)=0.0;
3961 error=result1.normf();
3962 return result;
3963 }
3964
3965 template <typename Archive> void serialize(const Archive& ar) {
3966 ar & error & lo & lo1 & lo2 & hi & hi1& hi2 & val_lhs & coeff_lhs;
3967 }
3968
3969
3970 };
3971
3972 /// given a ket and the 1- and 2-electron potentials, construct the function V phi
3973
3974 /// small memory footstep version of Vphi_op: use the NS form to have information
3975 /// about parent and children to determine if a box is a leaf. This will require
3976 /// compression of the constituent functions, which will lead to more memory usage
3977 /// there, but will avoid oversampling of the result function.
3978 template<typename opT, size_t LDIM>
3979 struct Vphi_op_NS {
3980
3981 bool randomize() const {return true;}
3982
3986
3987 implT* result; ///< where to construct Vphi, no need to track parents
3988 opT leaf_op; ///< deciding if a given FunctionNode will be a leaf node
3989 ctT iaket; ///< the ket of a pair function (exclusive with p1, p2)
3990 ctL iap1, iap2; ///< the particles 1 and 2 (exclusive with ket)
3991 ctL iav1, iav2; ///< potentials for particles 1 and 2
3992 const implT* eri; ///< 2-particle potential, must be on-demand
3993
3994 bool have_ket() const {return iaket.get_impl();}
3995 bool have_v1() const {return iav1.get_impl();}
3996 bool have_v2() const {return iav2.get_impl();}
3997 bool have_eri() const {return eri;}
3998
3999 void accumulate_into_result(const Key<NDIM>& key, const coeffT& coeff) const {
4001 }
4002
4003 // ctor
4005 Vphi_op_NS(implT* result, const opT& leaf_op, const ctT& iaket,
4006 const ctL& iap1, const ctL& iap2, const ctL& iav1, const ctL& iav2,
4007 const implT* eri)
4009 , iav1(iav1), iav2(iav2), eri(eri) {
4010
4011 // 2-particle potential must be on-demand
4013 }
4014
4015 /// make and insert the coefficients into result's tree
4016 std::pair<bool,coeffT> operator()(const Key<NDIM>& key) const {
4017
4019 if(leaf_op.do_pre_screening()){
4020 // this means that we only construct the boxes which are leaf boxes from the other function in the leaf_op
4021 if(leaf_op.pre_screening(key)){
4022 // construct sum_coefficients, insert them and leave
4023 auto [sum_coeff, error]=make_sum_coeffs(key);
4024 accumulate_into_result(key,sum_coeff);
4025 return std::pair<bool,coeffT> (true,coeffT());
4026 }else{
4027 return continue_recursion(std::vector<bool>(1<<NDIM,false),tensorT(),key);
4028 }
4029 }
4030
4031 // this means that the function has to be completely constructed and not mirrored by another function
4032
4033 // if the initial level is not reached then this must not be a leaf box
4034 size_t il = result->get_initial_level();
4036 if(key.level()<int(il)){
4037 return continue_recursion(std::vector<bool>(1<<NDIM,false),tensorT(),key);
4038 }
4039 // if further refinement is needed (because we are at a special box, special point)
4040 // and the special_level is not reached then this must not be a leaf box
4041 if(key.level()<result->get_special_level() and leaf_op.special_refinement_needed(key)){
4042 return continue_recursion(std::vector<bool>(1<<NDIM,false),tensorT(),key);
4043 }
4044
4045 auto [sum_coeff,error]=make_sum_coeffs(key);
4046
4047 // coeffs are leaf (for whatever reason), insert into tree and stop recursion
4048 if(leaf_op.post_screening(key,sum_coeff)){
4049 accumulate_into_result(key,sum_coeff);
4050 return std::pair<bool,coeffT> (true,coeffT());
4051 }
4052
4053 // coeffs are accurate, insert into tree and stop recursion
4054 if(error<result->truncate_tol(result->get_thresh(),key)){
4055 accumulate_into_result(key,sum_coeff);
4056 return std::pair<bool,coeffT> (true,coeffT());
4057 }
4058
4059 // coeffs are inaccurate, continue recursion
4060 std::vector<bool> child_is_leaf(1<<NDIM,false);
4061 return continue_recursion(child_is_leaf,tensorT(),key);
4062 }
4063
4064
4065 /// loop over all children and either insert their sum coeffs or continue the recursion
4066
4067 /// @param[in] child_is_leaf for each child: is it a leaf?
4068 /// @param[in] coeffs coefficient tensor with 2^N sum coeffs (=unfiltered NS coeffs)
4069 /// @param[in] key the key for the NS coeffs (=parent key of the children)
4070 /// @return to avoid recursion outside this return: std::pair<is_leaf,coeff> = true,coeffT()
4071 std::pair<bool,coeffT> continue_recursion(const std::vector<bool> child_is_leaf,
4072 const tensorT& coeffs, const keyT& key) const {
4073 std::size_t i=0;
4074 for (KeyChildIterator<NDIM> kit(key); kit; ++kit, ++i) {
4075 keyT child=kit.key();
4076 bool is_leaf=child_is_leaf[i];
4077
4078 if (is_leaf) {
4079 // insert the sum coeffs
4081 iop(child,coeffT(copy(coeffs(result->child_patch(child))),result->get_tensor_args()),is_leaf);
4082 } else {
4083 this_type child_op=this->make_child(child);
4084 noop<T,NDIM> no;
4085 // spawn activation where child is local
4086 ProcessID p=result->get_coeffs().owner(child);
4087
4088 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> >;
4089 result->task(p, ft, child_op, no, child);
4090 }
4091 }
4092 // return e sum coeffs; also return always is_leaf=true:
4093 // the recursion is continued within this struct, not outside in traverse_tree!
4094 return std::pair<bool,coeffT> (true,coeffT());
4095 }
4096
4097 tensorT eri_coeffs(const keyT& key) const {
4100 if (eri->get_functor()->provides_coeff()) {
4101 return eri->get_functor()->coeff(key).full_tensor();
4102 } else {
4103 tensorT val_eri(eri->cdata.vk);
4104 eri->fcube(key,*(eri->get_functor()),eri->cdata.quad_x,val_eri);
4105 return eri->values2coeffs(key,val_eri);
4106 }
4107 }
4108
4109 /// the error is computed from the d coefficients of the constituent functions
4110
4111 /// the result is h_n = P_n(f g), computed as h_n \approx Pn(f_n g_n)
4112 /// its error is therefore
4113 /// h_n = (f g)_n = ((Pn(f) + Qn(f)) (Pn(g) + Qn(g))
4114 /// = Pn(fn gn) + Qn(fn gn) + Pn(f) Qn(g) + Qn(f) Pn(g) + Qn(f) Pn(g)
4115 /// the first term is what we compute, the second term is estimated by tnorm (in another function),
4116 /// the third to last terms are estimated in this function by e.g.: Qn(f)Pn(g) < ||Qn(f)|| ||Pn(g)||
4118 const tensorT& ceri) const {
4119 double error = 0.0;
4120 Key<LDIM> key1, key2;
4121 key.break_apart(key1,key2);
4122
4123 PROFILE_BLOCK(compute_error);
4124 double dnorm_ket, snorm_ket;
4125 if (have_ket()) {
4126 snorm_ket=iaket.coeff(key).normf();
4127 dnorm_ket=iaket.dnorm(key);
4128 } else {
4129 double s1=iap1.coeff(key1).normf();
4130 double s2=iap2.coeff(key2).normf();
4131 double d1=iap1.dnorm(key1);
4132 double d2=iap2.dnorm(key2);
4133 snorm_ket=s1*s2;
4134 dnorm_ket=s1*d2 + s2*d1 + d1*d2;
4135 }
4136
4137 if (have_v1()) {
4138 double snorm=iav1.coeff(key1).normf();
4139 double dnorm=iav1.dnorm(key1);
4140 error+=snorm*dnorm_ket + dnorm*snorm_ket + dnorm*dnorm_ket;
4141 }
4142 if (have_v2()) {
4143 double snorm=iav2.coeff(key2).normf();
4144 double dnorm=iav2.dnorm(key2);
4145 error+=snorm*dnorm_ket + dnorm*snorm_ket + dnorm*dnorm_ket;
4146 }
4147 if (have_eri()) {
4148 tensorT s_coeffs=ceri(result->cdata.s0);
4149 double snorm=s_coeffs.normf();
4150 tensorT d=copy(ceri);
4151 d(result->cdata.s0)=0.0;
4152 double dnorm=d.normf();
4153 error+=snorm*dnorm_ket + dnorm*snorm_ket + dnorm*dnorm_ket;
4154 }
4155
4156 bool no_potential=not ((have_v1() or have_v2() or have_eri()));
4157 if (no_potential) {
4158 error=dnorm_ket;
4159 }
4160 return error;
4161 }
4162
4163 /// make the sum coeffs for key
4164 std::pair<coeffT,double> make_sum_coeffs(const keyT& key) const {
4166 // break key into particles
4167 Key<LDIM> key1, key2;
4168 key.break_apart(key1,key2);
4169
4170 // bool printme=(int(key.translation()[0])==int(std::pow(key.level(),2)/2)) and
4171 // (int(key.translation()[1])==int(std::pow(key.level(),2)/2)) and
4172 // (int(key.translation()[2])==int(std::pow(key.level(),2)/2));
4173
4174// printme=false;
4175
4176 // get/make all coefficients
4177 const coeffT coeff_ket = (iaket.get_impl()) ? iaket.coeff(key)
4178 : outer(iap1.coeff(key1),iap2.coeff(key2),result->get_tensor_args());
4179 const coeffT cpot1 = (have_v1()) ? iav1.coeff(key1) : coeffT();
4180 const coeffT cpot2 = (have_v2()) ? iav2.coeff(key2) : coeffT();
4181 const tensorT ceri = (have_eri()) ? eri_coeffs(key) : tensorT();
4182
4183 // compute first part of the total error
4184 double refine_error=compute_error_from_inaccurate_refinement(key,ceri);
4185 double error=refine_error;
4186
4187 // prepare the multiplication
4188 pointwise_multiplier<LDIM> pm(key,coeff_ket);
4189
4190 // perform the multiplication, compute tnorm part of the total error
4191 coeffT cresult(result->cdata.vk,result->get_tensor_args());
4192 if (have_v1()) {
4193 cresult+=pm(key,cpot1.get_tensor(),1);
4194 error+=pm.error;
4195 }
4196 if (have_v2()) {
4197 cresult+=pm(key,cpot2.get_tensor(),2);
4198 error+=pm.error;
4199 }
4200
4201 if (have_eri()) {
4202 tensorT result1=cresult.full_tensor_copy();
4203 result1+=pm(key,copy(ceri(result->cdata.s0)));
4204 cresult=coeffT(result1,result->get_tensor_args());
4205 error+=pm.error;
4206 } else {
4208 }
4209 if ((not have_v1()) and (not have_v2()) and (not have_eri())) {
4210 cresult=coeff_ket;
4211 }
4212
4213 return std::make_pair(cresult,error);
4214 }
4215
4216 this_type make_child(const keyT& child) const {
4217
4218 // break key into particles
4219 Key<LDIM> key1, key2;
4220 child.break_apart(key1,key2);
4221
4222 return this_type(result,leaf_op,iaket.make_child(child),
4223 iap1.make_child(key1),iap2.make_child(key2),
4224 iav1.make_child(key1),iav2.make_child(key2),eri);
4225 }
4226
4228 Future<ctT> iaket1=iaket.activate();
4229 Future<ctL> iap11=iap1.activate();
4230 Future<ctL> iap21=iap2.activate();
4231 Future<ctL> iav11=iav1.activate();
4232 Future<ctL> iav21=iav2.activate();
4233 return result->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
4234 &this_type::forward_ctor),result,leaf_op,
4235 iaket1,iap11,iap21,iav11,iav21,eri);
4236 }
4237
4238 this_type forward_ctor(implT* result1, const opT& leaf_op, const ctT& iaket1,
4239 const ctL& iap11, const ctL& iap21, const ctL& iav11, const ctL& iav21,
4240 const implT* eri1) {
4241 return this_type(result1,leaf_op,iaket1,iap11,iap21,iav11,iav21,eri1);
4242 }
4243
4244 /// serialize this (needed for use in recursive_op)
4245 template <typename Archive> void serialize(const Archive& ar) {
4246 ar & iaket & eri & result & leaf_op & iap1 & iap2 & iav1 & iav2;
4247 }
4248 };
4249
4250 /// assemble the function V*phi using V and phi given from the functor
4251
4252 /// this function must have been constructed using the CompositeFunctorInterface.
4253 /// The interface provides one- and two-electron potentials, and the ket, which are
4254 /// assembled to give V*phi.
4255 /// @param[in] leaf_op operator to decide if a given node is a leaf node
4256 /// @param[in] fence global fence
4257 template<typename opT>
4258 void make_Vphi(const opT& leaf_op, const bool fence=true) {
4259
4260 constexpr size_t LDIM=NDIM/2;
4261 MADNESS_CHECK_THROW(NDIM==LDIM*2,"make_Vphi only works for even dimensions");
4262
4263
4264 // keep the functor available, but remove it from the result
4265 // result will return false upon is_on_demand(), which is necessary for the
4266 // CoeffTracker to track the parent coeffs correctly for error_leaf_op
4267 std::shared_ptr< FunctionFunctorInterface<T,NDIM> > func2(this->get_functor());
4268 this->unset_functor();
4269
4271 dynamic_cast<CompositeFunctorInterface<T,NDIM,LDIM>* >(&(*func2));
4273
4274 // make sure everything is in place if no fence is requested
4275 if (fence) func->make_redundant(true); // no-op if already redundant
4276 MADNESS_CHECK_THROW(func->check_redundant(),"make_Vphi requires redundant functions");
4277
4278 // loop over all functions in the functor (either ket or particles)
4279 for (auto& ket : func->impl_ket_vector) {
4280 FunctionImpl<T,NDIM>* eri=func->impl_eri.get();
4281 FunctionImpl<T,LDIM>* v1=func->impl_m1.get();
4282 FunctionImpl<T,LDIM>* v2=func->impl_m2.get();
4283 FunctionImpl<T,LDIM>* p1=nullptr;
4284 FunctionImpl<T,LDIM>* p2=nullptr;
4285 make_Vphi_only(leaf_op,ket.get(),v1,v2,p1,p2,eri,false);
4286 }
4287
4288 for (std::size_t i=0; i<func->impl_p1_vector.size(); ++i) {
4289 FunctionImpl<T,NDIM>* ket=nullptr;
4290 FunctionImpl<T,NDIM>* eri=func->impl_eri.get();
4291 FunctionImpl<T,LDIM>* v1=func->impl_m1.get();
4292 FunctionImpl<T,LDIM>* v2=func->impl_m2.get();
4293 FunctionImpl<T,LDIM>* p1=func->impl_p1_vector[i].get();
4294 FunctionImpl<T,LDIM>* p2=func->impl_p2_vector[i].get();
4295 make_Vphi_only(leaf_op,ket,v1,v2,p1,p2,eri,false);
4296 }
4297
4298 // some post-processing:
4299 // - FunctionNode::accumulate() uses buffer -> add the buffer contents to the actual coefficients
4300 // - the operation constructs sum coefficients on all scales -> sum down to get a well-defined tree-state
4301 if (fence) {
4302 world.gop.fence();
4304 sum_down(true);
4306 }
4307
4308
4309 }
4310
4311 /// assemble the function V*phi using V and phi given from the functor
4312
4313 /// this function must have been constructed using the CompositeFunctorInterface.
4314 /// The interface provides one- and two-electron potentials, and the ket, which are
4315 /// assembled to give V*phi.
4316 /// @param[in] leaf_op operator to decide if a given node is a leaf node
4317 /// @param[in] fence global fence
4318 template<typename opT, std::size_t LDIM>
4323 const bool fence=true) {
4324
4325 // prepare the CoeffTracker
4326 CoeffTracker<T,NDIM> iaket(ket);
4327 CoeffTracker<T,LDIM> iap1(p1);
4328 CoeffTracker<T,LDIM> iap2(p2);
4329 CoeffTracker<T,LDIM> iav1(v1);
4330 CoeffTracker<T,LDIM> iav2(v2);
4331
4332 // the operator making the coefficients
4333 typedef Vphi_op_NS<opT,LDIM> coeff_opT;
4334 coeff_opT coeff_op(this,leaf_op,iaket,iap1,iap2,iav1,iav2,eri);
4335
4336 // this operator simply inserts the coeffs into this' tree
4337 typedef noop<T,NDIM> apply_opT;
4338 apply_opT apply_op;
4339
4340 if (world.rank() == coeffs.owner(cdata.key0)) {
4341 woT::task(world.rank(), &implT:: template forward_traverse<coeff_opT,apply_opT>,
4342 coeff_op, apply_op, cdata.key0);
4343 }
4344
4346 if (fence) world.gop.fence();
4347
4348 }
4349
4350 /// Permute the dimensions of f according to map, result on this
4351 void mapdim(const implT& f, const std::vector<long>& map, bool fence);
4352
4353 /// mirror the dimensions of f according to map, result on this
4354 void mirror(const implT& f, const std::vector<long>& mirror, bool fence);
4355
4356 /// map and mirror the translation index and the coefficients, result on this
4357
4358 /// first map the dimensions, the mirror!
4359 /// this = mirror(map(f))
4360 void map_and_mirror(const implT& f, const std::vector<long>& map,
4361 const std::vector<long>& mirror, bool fence);
4362
4363 /// take the average of two functions, similar to: this=0.5*(this+rhs)
4364
4365 /// works in either basis and also in nonstandard form
4366 void average(const implT& rhs);
4367
4368 /// change the tensor type of the coefficients in the FunctionNode
4369
4370 /// @param[in] targs target tensor arguments (threshold and full/low rank)
4371 void change_tensor_type1(const TensorArgs& targs, bool fence);
4372
4373 /// reduce the rank of the coefficients tensors
4374
4375 /// @param[in] targs target tensor arguments (threshold and full/low rank)
4376 void reduce_rank(const double thresh, bool fence);
4377
4378
4379 /// remove all nodes with level higher than n
4380 void chop_at_level(const int n, const bool fence=true);
4381
4382 /// compute norm of s and d coefficients for all nodes
4383 void compute_snorm_and_dnorm(bool fence=true);
4384
4385 /// compute the norm of the wavelet coefficients
4388
4392
4393 bool operator()(typename rangeT::iterator& it) const {
4394 auto& node=it->second;
4395 node.recompute_snorm_and_dnorm(cdata);
4396 return true;
4397 }
4398 };
4399
4400
4401 T eval_cube(Level n, coordT& x, const tensorT& c) const;
4402
4403 /// Transform sum coefficients at level n to sums+differences at level n-1
4404
4405 /// Given scaling function coefficients s[n][l][i] and s[n][l+1][i]
4406 /// return the scaling function and wavelet coefficients at the
4407 /// coarser level. I.e., decompose Vn using Vn = Vn-1 + Wn-1.
4408 /// \code
4409 /// s_i = sum(j) h0_ij*s0_j + h1_ij*s1_j
4410 /// d_i = sum(j) g0_ij*s0_j + g1_ij*s1_j
4411 // \endcode
4412 /// Returns a new tensor and has no side effects. Works for any
4413 /// number of dimensions.
4414 ///
4415 /// No communication involved.
4416 tensorT filter(const tensorT& s) const;
4417
4418 coeffT filter(const coeffT& s) const;
4419
4420 /// Transform sums+differences at level n to sum coefficients at level n+1
4421
4422 /// Given scaling function and wavelet coefficients (s and d)
4423 /// returns the scaling function coefficients at the next finer
4424 /// level. I.e., reconstruct Vn using Vn = Vn-1 + Wn-1.
4425 /// \code
4426 /// s0 = sum(j) h0_ji*s_j + g0_ji*d_j
4427 /// s1 = sum(j) h1_ji*s_j + g1_ji*d_j
4428 /// \endcode
4429 /// Returns a new tensor and has no side effects
4430 ///
4431 /// If (sonly) ... then ss is only the scaling function coeff (and
4432 /// assume the d are zero). Works for any number of dimensions.
4433 ///
4434 /// No communication involved.
4435 tensorT unfilter(const tensorT& s) const;
4436
4437 coeffT unfilter(const coeffT& s) const;
4438
4439 /// downsample the sum coefficients of level n+1 to sum coeffs on level n
4440
4441 /// specialization of the filter method, will yield only the sum coefficients
4442 /// @param[in] key key of level n
4443 /// @param[in] v vector of sum coefficients of level n+1
4444 /// @return sum coefficients on level n in full tensor format
4445 tensorT downsample(const keyT& key, const std::vector< Future<coeffT > >& v) const;
4446
4447 /// upsample the sum coefficients of level 1 to sum coeffs on level n+1
4448
4449 /// specialization of the unfilter method, will transform only the sum coefficients
4450 /// @param[in] key key of level n+1
4451 /// @param[in] coeff sum coefficients of level n (does NOT belong to key!!)
4452 /// @return sum coefficients on level n+1
4453 coeffT upsample(const keyT& key, const coeffT& coeff) const;
4454
4455 /// Projects old function into new basis (only in reconstructed form)
4456 void project(const implT& old, bool fence);
4457
4459 bool operator()(const implT* f, const keyT& key, const nodeT& t) const {
4460 return true;
4461 }
4462 template <typename Archive> void serialize(Archive& ar) {}
4463 };
4464
4465 template <typename opT>
4466 void refine_op(const opT& op, const keyT& key) {
4467 // Must allow for someone already having autorefined the coeffs
4468 // and we get a write accessor just in case they are already executing
4469 typename dcT::accessor acc;
4470 const auto found = coeffs.find(acc,key);
4471 MADNESS_CHECK(found);
4472 nodeT& node = acc->second;
4473 if (node.has_coeff() && key.level() < max_refine_level && op(this, key, node)) {
4474 coeffT d(cdata.v2k,targs);
4475 d(cdata.s0) += copy(node.coeff());
4476 d = unfilter(d);
4477 node.clear_coeff();
4478 node.set_has_children(true);
4479 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
4480 const keyT& child = kit.key();
4481 coeffT ss = copy(d(child_patch(child)));
4483 // coeffs.replace(child,nodeT(ss,-1.0,false).node_to_low_rank());
4484 coeffs.replace(child,nodeT(ss,-1.0,false));
4485 // Note value -1.0 for norm tree to indicate result of refinement
4486 }
4487 }
4488 }
4489
4490 template <typename opT>
4491 void refine_spawn(const opT& op, const keyT& key) {
4492 nodeT& node = coeffs.find(key).get()->second;
4493 if (node.has_children()) {
4494 for (KeyChildIterator<NDIM> kit(key); kit; ++kit)
4495 woT::task(coeffs.owner(kit.key()), &implT:: template refine_spawn<opT>, op, kit.key(), TaskAttributes::hipri());
4496 }
4497 else {
4498 woT::task(coeffs.owner(key), &implT:: template refine_op<opT>, op, key);
4499 }
4500 }
4501
4502 // Refine in real space according to local user-defined criterion
4503 template <typename opT>
4504 void refine(const opT& op, bool fence) {
4505 if (world.rank() == coeffs.owner(cdata.key0))
4506 woT::task(coeffs.owner(cdata.key0), &implT:: template refine_spawn<opT>, op, cdata.key0, TaskAttributes::hipri());
4507 if (fence)
4508 world.gop.fence();
4509 }
4510
4511 bool exists_and_has_children(const keyT& key) const;
4512
4513 bool exists_and_is_leaf(const keyT& key) const;
4514
4515
4516 void broaden_op(const keyT& key, const std::vector< Future <bool> >& v);
4517
4518 // For each local node sets value of norm tree, snorm and dnorm to 0.0
4519 void zero_norm_tree();
4520
4521 // Broaden tree
4522 void broaden(std::vector<bool> is_periodic, bool fence);
4523
4524 /// sum all the contributions from all scales after applying an operator in mod-NS form
4525 void trickle_down(bool fence);
4526
4527 /// sum all the contributions from all scales after applying an operator in mod-NS form
4528
4529 /// cf reconstruct_op
4530 void trickle_down_op(const keyT& key, const coeffT& s);
4531
4532 /// reconstruct this tree -- respects fence
4533 void reconstruct(bool fence);
4534
4535 void change_tree_state(const TreeState finalstate, bool fence=true);
4536
4537 // Invoked on node where key is local
4538 // void reconstruct_op(const keyT& key, const tensorT& s);
4539 void reconstruct_op(const keyT& key, const coeffT& s, const bool accumulate_NS=true);
4540
4541 /// compress the wave function
4542
4543 /// after application there will be sum coefficients at the root level,
4544 /// and difference coefficients at all other levels; furthermore:
4545 /// @param[in] nonstandard keep sum coeffs at all other levels, except leaves
4546 /// @param[in] keepleaves keep sum coeffs (but no diff coeffs) at leaves
4547 /// @param[in] redundant keep only sum coeffs at all levels, discard difference coeffs
4548// void compress(bool nonstandard, bool keepleaves, bool redundant, bool fence);
4549 void compress(const TreeState newstate, bool fence);
4550
4551 /// Invoked on node where key is local
4552 Future<std::pair<coeffT,double> > compress_spawn(const keyT& key, bool nonstandard, bool keepleaves,
4553 bool redundant1);
4554
4555 private:
4556 /// convert this to redundant, i.e. have sum coefficients on all levels
4557 void make_redundant(const bool fence);
4558 public:
4559
4560 /// convert this from redundant to standard reconstructed form
4561 void undo_redundant(const bool fence);
4562
4563 void remove_internal_coefficients(const bool fence);
4564 void remove_leaf_coefficients(const bool fence);
4565
4566
4567 /// compute for each FunctionNode the norm of the function inside that node
4568 void norm_tree(bool fence);
4569
4570 double norm_tree_op(const keyT& key, const std::vector< Future<double> >& v);
4571
4573
4574 /// truncate using a tree in reconstructed form
4575
4576 /// must be invoked where key is local
4577 Future<coeffT> truncate_reconstructed_spawn(const keyT& key, const double tol);
4578
4579 /// given the sum coefficients of all children, truncate or not
4580
4581 /// @return new sum coefficients (empty if internal, not empty, if new leaf); might delete its children
4582 coeffT truncate_reconstructed_op(const keyT& key, const std::vector< Future<coeffT > >& v, const double tol);
4583
4584 /// calculate the wavelet coefficients using the sum coefficients of all child nodes
4585
4586 /// also compute the norm tree for all nodes
4587 /// @param[in] key this's key
4588 /// @param[in] v sum coefficients of the child nodes
4589 /// @param[in] nonstandard keep the sum coefficients with the wavelet coefficients
4590 /// @param[in] redundant keep only the sum coefficients, discard the wavelet coefficients
4591 /// @return the sum coefficients
4592 std::pair<coeffT,double> compress_op(const keyT& key, const std::vector< Future<std::pair<coeffT,double>> >& v, bool nonstandard);
4593
4594
4595 /// similar to compress_op, but insert only the sum coefficients in the tree
4596
4597 /// also compute the norm tree for all nodes
4598 /// @param[in] key this's key
4599 /// @param[in] v sum coefficients of the child nodes
4600 /// @return the sum coefficients
4601 std::pair<coeffT,double> make_redundant_op(const keyT& key,const std::vector< Future<std::pair<coeffT,double> > >& v);
4602
4603 /// Changes non-standard compressed form to standard compressed form
4604 void standard(bool fence);
4605
4606 /// Changes non-standard compressed form to standard compressed form
4609
4610 // threshold for rank reduction / SVD truncation
4612
4613 // constructor takes target precision
4614 do_standard() = default;
4616
4617 //
4618 bool operator()(typename rangeT::iterator& it) const {
4619
4620 const keyT& key = it->first;
4621 nodeT& node = it->second;
4622 if (key.level()> 0 && node.has_coeff()) {
4623 if (node.has_children()) {
4624 // Zero out scaling coeffs
4625 MADNESS_ASSERT(node.coeff().dim(0)==2*impl->get_k());
4626 node.coeff()(impl->cdata.s0)=0.0;
4627 node.reduceRank(impl->targs.thresh);
4628 } else {
4629 // Deleting both scaling and wavelet coeffs
4630 node.clear_coeff();
4631 }
4632 }
4633 return true;
4634 }
4635 template <typename Archive> void serialize(const Archive& ar) {
4636 MADNESS_EXCEPTION("no serialization of do_standard",1);
4637 }
4638 };
4639
4640
4641 /// laziness
4642 template<size_t OPDIM>
4643 struct do_op_args {
4646 double tol, fac, cnorm;
4647
4648 do_op_args() = default;
4649 do_op_args(const Key<OPDIM>& key, const Key<OPDIM>& d, const keyT& dest, double tol, double fac, double cnorm)
4650 : key(key), d(d), dest(dest), tol(tol), fac(fac), cnorm(cnorm) {}
4651 template <class Archive>
4652 void serialize(Archive& ar) {
4653 ar & archive::wrap_opaque(this,1);
4654 }
4655 };
4656
4657 /// for fine-grain parallelism: call the apply method of an operator in a separate task
4658
4659 /// @param[in] op the operator working on our function
4660 /// @param[in] c full rank tensor holding the NS coefficients
4661 /// @param[in] args laziness holding norm of the coefficients, displacement, destination, ..
4662 template <typename opT, typename R, size_t OPDIM>
4663 void do_apply_kernel(const opT* op, const Tensor<R>& c, const do_op_args<OPDIM>& args) {
4664
4665 tensorT result = op->apply(args.key, args.d, c, args.tol/args.fac/args.cnorm);
4666
4667 // Screen here to reduce communication cost of negligible data
4668 // and also to ensure we don't needlessly widen the tree when
4669 // applying the operator
4670 if (result.normf()> 0.3*args.tol/args.fac) {
4672 //woT::task(world.rank(),&implT::accumulate_timer,time,TaskAttributes::hipri());
4673 // UGLY BUT ADDED THE OPTIMIZATION BACK IN HERE EXPLICITLY/
4674 if (args.dest == world.rank()) {
4675 coeffs.send(args.dest, &nodeT::accumulate, result, coeffs, args.dest);
4676 }
4677 else {
4679 }
4680 }
4681 }
4682
4683 /// same as do_apply_kernel, but use full rank tensors as input and low rank tensors as output
4684
4685 /// @param[in] op the operator working on our function
4686 /// @param[in] c full rank tensor holding the NS coefficients
4687 /// @param[in] args laziness holding norm of the coefficients, displacement, destination, ..
4688 /// @param[in] apply_targs TensorArgs with tightened threshold for accumulation
4689 /// @return nothing, but accumulate the result tensor into the destination node
4690 template <typename opT, typename R, size_t OPDIM>
4691 double do_apply_kernel2(const opT* op, const Tensor<R>& c, const do_op_args<OPDIM>& args,
4692 const TensorArgs& apply_targs) {
4693
4694 tensorT result_full = op->apply(args.key, args.d, c, args.tol/args.fac/args.cnorm);
4695 const double norm=result_full.normf();
4696
4697 // Screen here to reduce communication cost of negligible data
4698 // and also to ensure we don't needlessly widen the tree when
4699 // applying the operator
4700 // OPTIMIZATION NEEDED HERE ... CHANGING THIS TO TASK NOT SEND REMOVED
4701 // BUILTIN OPTIMIZATION TO SHORTCIRCUIT MSG IF DATA IS LOCAL
4702 if (norm > 0.3*args.tol/args.fac) {
4703
4704 small++;
4705 //double cpu0=cpu_time();
4706 coeffT result=coeffT(result_full,apply_targs);
4707 MADNESS_ASSERT(result.is_full_tensor() or result.is_svd_tensor());
4708 //double cpu1=cpu_time();
4709 //timer_lr_result.accumulate(cpu1-cpu0);
4710
4711 coeffs.task(args.dest, &nodeT::accumulate, result, coeffs, args.dest, apply_targs,
4713
4714 //woT::task(world.rank(),&implT::accumulate_timer,time,TaskAttributes::hipri());
4715 }
4716 return norm;
4717 }
4718
4719
4720
4721 /// same as do_apply_kernel2, but use low rank tensors as input and low rank tensors as output
4722
4723 /// @param[in] op the operator working on our function
4724 /// @param[in] coeff full rank tensor holding the NS coefficients
4725 /// @param[in] args laziness holding norm of the coefficients, displacement, destination, ..
4726 /// @param[in] apply_targs TensorArgs with tightened threshold for accumulation
4727 /// @return nothing, but accumulate the result tensor into the destination node
4728 template <typename opT, typename R, size_t OPDIM>
4729 double do_apply_kernel3(const opT* op, const GenTensor<R>& coeff, const do_op_args<OPDIM>& args,
4730 const TensorArgs& apply_targs) {
4731
4732 coeffT result;
4733 if (2*OPDIM==NDIM) result= op->apply2_lowdim(args.key, args.d, coeff,
4734 args.tol/args.fac/args.cnorm, args.tol/args.fac);
4735 if (OPDIM==NDIM) result = op->apply2(args.key, args.d, coeff,
4736 args.tol/args.fac/args.cnorm, args.tol/args.fac);
4737
4738 const double result_norm=result.svd_normf();
4739
4740 if (result_norm> 0.3*args.tol/args.fac) {
4741 small++;
4742
4743 double cpu0=cpu_time();
4744 if (not result.is_of_tensortype(targs.tt)) result=result.convert(targs);
4745 double cpu1=cpu_time();
4746 timer_lr_result.accumulate(cpu1-cpu0);
4747
4748 // accumulate also expects result in SVD form
4749 coeffs.task(args.dest, &nodeT::accumulate, result, coeffs, args.dest, apply_targs,
4751// woT::task(world.rank(),&implT::accumulate_timer,time,TaskAttributes::hipri());
4752
4753 }
4754 return result_norm;
4755
4756 }
4757
4758 // volume of n-dimensional sphere of radius R
4759 double vol_nsphere(int n, double R) {
4760 return std::pow(madness::constants::pi,n*0.5)*std::pow(R,n)/std::tgamma(1+0.5*n);
4761 }
4762
4763
4764 /// apply an operator on the coeffs c (at node key)
4765
4766 /// the result is accumulated inplace to this's tree at various FunctionNodes
4767 /// @param[in] op the operator to act on the source function
4768 /// @param[in] key key of the source FunctionNode of f which is processed
4769 /// @param[in] c coeffs of the FunctionNode of f which is processed
4770 template <typename opT, typename R>
4771 void do_apply(const opT* op, const keyT& key, const Tensor<R>& c) {
4773
4774 // working assumption here WAS that the operator is
4775 // isotropic and montonically decreasing with distance
4776 // ... however, now we are using derivative Gaussian
4777 // expansions (and also non-cubic boxes) isotropic is
4778 // violated. While not strictly monotonically decreasing,
4779 // the derivative gaussian is still such that once it
4780 // becomes negligible we are in the asymptotic region.
4781
4782 typedef typename opT::keyT opkeyT;
4783 static const size_t opdim=opT::opdim;
4784 const opkeyT source=op->get_source_key(key);
4785
4786
4787 // Tuning here is based on observation that with
4788 // sufficiently high-order wavelet relative to the
4789 // precision, that only nearest neighbor boxes contribute,
4790 // whereas for low-order wavelets more neighbors will
4791 // contribute. Sufficiently high is picked as
4792 // k>=2-log10(eps) which is our empirical rule for
4793 // efficiency/accuracy and code instrumentation has
4794 // previously indicated that (in 3D) just unit
4795 // displacements are invoked. The error decays as R^-(k+1),
4796 // and the number of boxes increases as R^d.
4797 //
4798 // Fac is the expected number of contributions to a given
4799 // box, so the error permitted per contribution will be
4800 // tol/fac
4801
4802 // radius of shell (nearest neighbor is diameter of 3 boxes, so radius=1.5)
4803 double radius = 1.5 + 0.33*std::max(0.0,2-std::log10(thresh)-k); // 0.33 was 0.5
4804 double fac = vol_nsphere(NDIM, radius);
4805 //previously fac=10.0 selected empirically constrained by qmprop
4806
4807 double cnorm = c.normf();
4808
4809 const std::vector<opkeyT>& disp = op->get_disp(key.level()); // list of displacements sorted in orer of increasing distance
4810 const std::vector<bool> is_periodic(NDIM,false); // Periodic sum is already done when making rnlp
4811 int nvalid=1; // Counts #valid at each distance
4812 int nused=1; // Counts #used at each distance
4813 uint64_t distsq = 99999999999999;
4814 for (typename std::vector<opkeyT>::const_iterator it=disp.begin(); it != disp.end(); ++it) {
4815 keyT d;
4816 Key<NDIM-opdim> nullkey(key.level());
4817 if (op->particle()==1) d=it->merge_with(nullkey);
4818 if (op->particle()==2) d=nullkey.merge_with(*it);
4819
4820 uint64_t dsq = d.distsq();
4821 if (dsq != distsq) { // Moved to next shell of neighbors
4822 if (nvalid > 0 && nused == 0 && dsq > 1) {
4823 // Have at least done the input box and all first
4824 // nearest neighbors, and for all of the last set
4825 // of neighbors had no contribution. Thus,
4826 // assuming monotonic decrease, we are done.
4827 break;
4828 }
4829 nused = 0;
4830 nvalid = 0;
4831 distsq = dsq;
4832 }
4833
4834 keyT dest = neighbor(key, d, is_periodic);
4835 if (dest.is_valid()) {
4836 nvalid++;
4837 double opnorm = op->norm(key.level(), *it, source);
4838 double tol = truncate_tol(thresh, key);
4839
4840 if (cnorm*opnorm> tol/fac) {
4841 nused++;
4842 tensorT result = op->apply(source, *it, c, tol/fac/cnorm);
4843 if (result.normf() > 0.3*tol/fac) {
4844 if (coeffs.is_local(dest))
4845 coeffs.send(dest, &nodeT::accumulate2, result, coeffs, dest);
4846 else
4847 coeffs.task(dest, &nodeT::accumulate2, result, coeffs, dest);
4848 }
4849 }
4850 }
4851 }
4852 }
4853
4854
4855 /// apply an operator on f to return this
4856 template <typename opT, typename R>
4857 void apply(opT& op, const FunctionImpl<R,NDIM>& f, bool fence) {
4859 MADNESS_ASSERT(!op.modified());
4860 typename dcT::const_iterator end = f.coeffs.end();
4861 for (typename dcT::const_iterator it=f.coeffs.begin(); it!=end; ++it) {
4862 // looping through all the coefficients in the source
4863 const keyT& key = it->first;
4864 const FunctionNode<R,NDIM>& node = it->second;
4865 if (node.has_coeff()) {
4866 if (node.coeff().dim(0) != k || op.doleaves) {
4868// woT::task(p, &implT:: template do_apply<opT,R>, &op, key, node.coeff()); //.full_tensor_copy() ????? why copy ????
4869 woT::task(p, &implT:: template do_apply<opT,R>, &op, key, node.coeff().reconstruct_tensor());
4870 }
4871 }
4872 }
4873 if (fence)
4874 world.gop.fence();
4875
4877// this->compressed=true;
4878// this->nonstandard=true;
4879// this->redundant=false;
4880
4881 }
4882
4883
4884
4885 /// apply an operator on the coeffs c (at node key)
4886
4887 /// invoked by result; the result is accumulated inplace to this's tree at various FunctionNodes
4888 /// @param[in] op the operator to act on the source function
4889 /// @param[in] key key of the source FunctionNode of f which is processed (see "source")
4890 /// @param[in] coeff coeffs of FunctionNode being processed
4891 /// @param[in] do_kernel true: do the 0-disp only; false: do everything but the kernel
4892 /// @return max norm, and will modify or include new nodes in this' tree
4893 template <typename opT, typename R>
4894 double do_apply_directed_screening(const opT* op, const keyT& key, const coeffT& coeff,
4895 const bool& do_kernel) {
4897 // insert timer here
4898 typedef typename opT::keyT opkeyT;
4899
4900 // screening: contains all displacement keys that had small result norms
4901 std::list<opkeyT> blacklist;
4902
4903 static const size_t opdim=opT::opdim;
4904 Key<NDIM-opdim> nullkey(key.level());
4905
4906 // source is that part of key that corresponds to those dimensions being processed
4907 const opkeyT source=op->get_source_key(key);
4908
4909 const double tol = truncate_tol(thresh, key);
4910
4911 // fac is the root of the number of contributing neighbors (1st shell)
4912 double fac=std::pow(3,NDIM*0.5);
4913 double cnorm = coeff.normf();
4914
4915 // for accumulation: keep slightly tighter TensorArgs
4916 TensorArgs apply_targs(targs);
4917 apply_targs.thresh=tol/fac*0.03;
4918
4919 double maxnorm=0.0;
4920
4921 // for the kernel it may be more efficient to do the convolution in full rank
4922 tensorT coeff_full;
4923 // for partial application (exchange operator) it's more efficient to
4924 // do SVD tensors instead of tensortrains, because addition in apply
4925 // can be done in full form for the specific particle
4926 coeffT coeff_SVD=coeff.convert(TensorArgs(-1.0,TT_2D));
4927#ifdef HAVE_GENTENSOR
4928 coeff_SVD.get_svdtensor().orthonormalize(tol*GenTensor<T>::fac_reduce());
4929#endif
4930
4931 const std::vector<opkeyT>& disp = op->get_disp(key.level());
4932 const std::vector<bool> is_periodic(NDIM,false); // Periodic sum is already done when making rnlp
4933
4934 for (typename std::vector<opkeyT>::const_iterator it=disp.begin(); it != disp.end(); ++it) {
4935 const opkeyT& d = *it;
4936
4937 const int shell=d.distsq();
4938 if (do_kernel and (shell>0)) break;
4939 if ((not do_kernel) and (shell==0)) continue;
4940
4941 keyT disp1;
4942 if (op->particle()==1) disp1=it->merge_with(nullkey);
4943 else if (op->particle()==2) disp1=nullkey.merge_with(*it);
4944 else {
4945 MADNESS_EXCEPTION("confused particle in operato??",1);
4946 }
4947
4948 keyT dest = neighbor(key, disp1, is_periodic);
4949
4950 if (not dest.is_valid()) continue;
4951
4952 // directed screening
4953 // working assumption here is that the operator is isotropic and
4954 // monotonically decreasing with distance
4955 bool screened=false;
4956 typename std::list<opkeyT>::const_iterator it2;
4957 for (it2=blacklist.begin(); it2!=blacklist.end(); it2++) {
4958 if (d.is_farther_out_than(*it2)) {
4959 screened=true;
4960 break;
4961 }
4962 }
4963 if (not screened) {
4964
4965 double opnorm = op->norm(key.level(), d, source);
4966 double norm=0.0;
4967
4968 if (cnorm*opnorm> tol/fac) {
4969
4970 double cost_ratio=op->estimate_costs(source, d, coeff_SVD, tol/fac/cnorm, tol/fac);
4971 // cost_ratio=1.5; // force low rank
4972 // cost_ratio=0.5; // force full rank
4973
4974 if (cost_ratio>0.0) {
4975
4976 do_op_args<opdim> args(source, d, dest, tol, fac, cnorm);
4977 norm=0.0;
4978 if (cost_ratio<1.0) {
4979 if (not coeff_full.has_data()) coeff_full=coeff.full_tensor_copy();
4980 norm=do_apply_kernel2(op, coeff_full,args,apply_targs);
4981 } else {
4982 if (2*opdim==NDIM) { // apply operator on one particle only
4983 norm=do_apply_kernel3(op,coeff_SVD,args,apply_targs);
4984 } else {
4985 norm=do_apply_kernel3(op,coeff,args,apply_targs);
4986 }
4987 }
4988 maxnorm=std::max(norm,maxnorm);
4989 }
4990
4991 } else if (shell >= 12) {
4992 break; // Assumes monotonic decay beyond nearest neighbor
4993 }
4994 if (norm<0.3*tol/fac) blacklist.push_back(d);
4995 }
4996 }
4997 return maxnorm;
4998 }
4999
5000
5001 /// similar to apply, but for low rank coeffs
5002 template <typename opT, typename R>
5003 void apply_source_driven(opT& op, const FunctionImpl<R,NDIM>& f, bool fence) {
5005
5006 MADNESS_ASSERT(not op.modified());
5007 // looping through all the coefficients of the source f
5008 typename dcT::const_iterator end = f.get_coeffs().end();
5009 for (typename dcT::const_iterator it=f.get_coeffs().begin(); it!=end; ++it) {
5010
5011 const keyT& key = it->first;
5012 const coeffT& coeff = it->second.coeff();
5013
5014 if (coeff.has_data() and (coeff.rank()!=0)) {
5016 woT::task(p, &implT:: template do_apply_directed_screening<opT,R>, &op, key, coeff, true);
5017 woT::task(p, &implT:: template do_apply_directed_screening<opT,R>, &op, key, coeff, false);
5018 }
5019 }
5020 if (fence) world.gop.fence();
5022 }
5023
5024 /// after apply we need to do some cleanup;
5025
5026 /// forces fence
5027 double finalize_apply();
5028
5029 /// after summing up we need to do some cleanup;
5030
5031 /// forces fence
5032 void finalize_sum();
5033
5034 /// traverse a non-existing tree, make its coeffs and apply an operator
5035
5036 /// invoked by result
5037 /// here we use the fact that the hi-dim NS coefficients on all scales are exactly
5038 /// the outer product of the underlying low-dim functions (also in NS form),
5039 /// so we don't need to construct the full hi-dim tree and then turn it into NS form.
5040 /// @param[in] apply_op the operator acting on the NS tree
5041 /// @param[in] fimpl the funcimpl of the function of particle 1
5042 /// @param[in] gimpl the funcimpl of the function of particle 2
5043 template<typename opT, std::size_t LDIM>
5044 void recursive_apply(opT& apply_op, const FunctionImpl<T,LDIM>* fimpl,
5045 const FunctionImpl<T,LDIM>* gimpl, const bool fence) {
5046
5047 //print("IN RECUR2");
5048 const keyT& key0=cdata.key0;
5049
5050 if (world.rank() == coeffs.owner(key0)) {
5051
5052 CoeffTracker<T,LDIM> ff(fimpl);
5053 CoeffTracker<T,LDIM> gg(gimpl);
5054
5055 typedef recursive_apply_op<opT,LDIM> coeff_opT;
5056 coeff_opT coeff_op(this,ff,gg,&apply_op);
5057
5058 typedef noop<T,NDIM> apply_opT;
5059 apply_opT apply_op;
5060
5062 woT::task(p, &implT:: template forward_traverse<coeff_opT,apply_opT>, coeff_op, apply_op, key0);
5063
5064 }
5065 if (fence) world.gop.fence();
5067 }
5068
5069 /// recursive part of recursive_apply
5070 template<typename opT, std::size_t LDIM>
5072 bool randomize() const {return true;}
5073
5075
5080
5081 // ctor
5085 const opT* apply_op) : result(result), iaf(iaf), iag(iag), apply_op(apply_op)
5086 {
5087 MADNESS_ASSERT(LDIM+LDIM==NDIM);
5088 }
5090 iag(other.iag), apply_op(other.apply_op) {}
5091
5092
5093 /// make the NS-coefficients and send off the application of the operator
5094
5095 /// @return a Future<bool,coeffT>(is_leaf,coeffT())
5096 std::pair<bool,coeffT> operator()(const Key<NDIM>& key) const {
5097
5098 // World& world=result->world;
5099 // break key into particles (these are the child keys, with datum1/2 come the parent keys)
5100 Key<LDIM> key1,key2;
5101 key.break_apart(key1,key2);
5102
5103 // the lo-dim functions should be in full tensor form
5104 const tensorT fcoeff=iaf.coeff(key1).full_tensor();
5105 const tensorT gcoeff=iag.coeff(key2).full_tensor();
5106
5107 // would this be a leaf node? If so, then its sum coeffs have already been
5108 // processed by the parent node's wavelet coeffs. Therefore we won't
5109 // process it any more.
5111 bool is_leaf=leaf_op(key,fcoeff,gcoeff);
5112
5113 if (not is_leaf) {
5114 // new coeffs are simply the hartree/kronecker/outer product --
5115 const std::vector<Slice>& s0=iaf.get_impl()->cdata.s0;
5116 const coeffT coeff = (apply_op->modified())
5117 ? outer(copy(fcoeff(s0)),copy(gcoeff(s0)),result->targs)
5118 : outer(fcoeff,gcoeff,result->targs);
5119
5120 // now send off the application
5121 tensorT coeff_full;
5123 double norm0=result->do_apply_directed_screening<opT,T>(apply_op, key, coeff, true);
5124
5125 result->task(p,&implT:: template do_apply_directed_screening<opT,T>,
5126 apply_op,key,coeff,false);
5127
5128 return finalize(norm0,key,coeff);
5129
5130 } else {
5131 return std::pair<bool,coeffT> (is_leaf,coeffT());
5132 }
5133 }
5134
5135 /// sole purpose is to wait for the kernel norm, wrap it and send it back to caller
5136 std::pair<bool,coeffT> finalize(const double kernel_norm, const keyT& key,
5137 const coeffT& coeff) const {
5138 const double thresh=result->get_thresh()*0.1;
5139 bool is_leaf=(kernel_norm<result->truncate_tol(thresh,key));
5140 if (key.level()<2) is_leaf=false;
5141 return std::pair<bool,coeffT> (is_leaf,coeff);
5142 }
5143
5144
5145 this_type make_child(const keyT& child) const {
5146
5147 // break key into particles
5148 Key<LDIM> key1, key2;
5149 child.break_apart(key1,key2);
5150
5151 return this_type(result,iaf.make_child(key1),iag.make_child(key2),apply_op);
5152 }
5153
5157 return result->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
5158 &this_type::forward_ctor),result,f1,g1,apply_op);
5159 }
5160
5162 const opT* apply_op1) {
5163 return this_type(r,f1,g1,apply_op1);
5164 }
5165
5166 template <typename Archive> void serialize(const Archive& ar) {
5167 ar & result & iaf & iag & apply_op;
5168 }
5169 };
5170
5171 /// traverse an existing tree and apply an operator
5172
5173 /// invoked by result
5174 /// @param[in] apply_op the operator acting on the NS tree
5175 /// @param[in] fimpl the funcimpl of the source function
5176 /// @param[in] rimpl a dummy function for recursive_op to insert data
5177 template<typename opT>
5178 void recursive_apply(opT& apply_op, const implT* fimpl, implT* rimpl, const bool fence) {
5179
5180 print("IN RECUR1");
5181
5182 const keyT& key0=cdata.key0;
5183
5184 if (world.rank() == coeffs.owner(key0)) {
5185
5186 typedef recursive_apply_op2<opT> coeff_opT;
5187 coeff_opT coeff_op(this,fimpl,&apply_op);
5188
5189 typedef noop<T,NDIM> apply_opT;
5190 apply_opT apply_op;
5191
5192 woT::task(world.rank(), &implT:: template forward_traverse<coeff_opT,apply_opT>,
5193 coeff_op, apply_op, cdata.key0);
5194
5195 }
5196 if (fence) world.gop.fence();
5198 }
5199
5200 /// recursive part of recursive_apply
5201 template<typename opT>
5203 bool randomize() const {return true;}
5204
5207 typedef std::pair<bool,coeffT> argT;
5208
5209 mutable implT* result;
5210 ctT iaf; /// need this for randomization
5211 const opT* apply_op;
5212
5213 // ctor
5217
5219 iaf(other.iaf), apply_op(other.apply_op) {}
5220
5221
5222 /// send off the application of the operator
5223
5224 /// the first (core) neighbor (ie. the box itself) is processed
5225 /// immediately, all other ones are shoved into the taskq
5226 /// @return a pair<bool,coeffT>(is_leaf,coeffT())
5227 argT operator()(const Key<NDIM>& key) const {
5228
5229 const coeffT& coeff=iaf.coeff();
5230
5231 if (coeff.has_data()) {
5232
5233 // now send off the application for all neighbor boxes
5235 result->task(p,&implT:: template do_apply_directed_screening<opT,T>,
5236 apply_op, key, coeff, false);
5237
5238 // process the core box
5239 double norm0=result->do_apply_directed_screening<opT,T>(apply_op,key,coeff,true);
5240
5241 if (iaf.is_leaf()) return argT(true,coeff);
5242 return finalize(norm0,key,coeff,result);
5243
5244 } else {
5245 const bool is_leaf=true;
5246 return argT(is_leaf,coeffT());
5247 }
5248 }
5249
5250 /// sole purpose is to wait for the kernel norm, wrap it and send it back to caller
5251 argT finalize(const double kernel_norm, const keyT& key,
5252 const coeffT& coeff, const implT* r) const {
5253 const double thresh=r->get_thresh()*0.1;
5254 bool is_leaf=(kernel_norm<r->truncate_tol(thresh,key));
5255 if (key.level()<2) is_leaf=false;
5256 return argT(is_leaf,coeff);
5257 }
5258
5259
5260 this_type make_child(const keyT& child) const {
5261 return this_type(result,iaf.make_child(child),apply_op);
5262 }
5263
5264 /// retrieve the coefficients (parent coeffs might be remote)
5266 Future<ctT> f1=iaf.activate();
5267
5268// Future<ctL> g1=g.activate();
5269// return h->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
5270// &this_type::forward_ctor),h,f1,g1,particle);
5271
5272 return result->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
5273 &this_type::forward_ctor),result,f1,apply_op);
5274 }
5275
5276 /// taskq-compatible ctor
5277 this_type forward_ctor(implT* result1, const ctT& iaf1, const opT* apply_op1) {
5278 return this_type(result1,iaf1,apply_op1);
5279 }
5280
5281 template <typename Archive> void serialize(const Archive& ar) {
5282 ar & result & iaf & apply_op;
5283 }
5284 };
5285
5286 /// Returns the square of the error norm in the box labeled by key
5287
5288 /// Assumed to be invoked locally but it would be easy to eliminate
5289 /// this assumption
5290 template <typename opT>
5291 double err_box(const keyT& key, const nodeT& node, const opT& func,
5292 int npt, const Tensor<double>& qx, const Tensor<double>& quad_phit,
5293 const Tensor<double>& quad_phiw) const {
5294
5295 std::vector<long> vq(NDIM);
5296 for (std::size_t i=0; i<NDIM; ++i)
5297 vq[i] = npt;
5298 tensorT fval(vq,false), work(vq,false), result(vq,false);
5299
5300 // Compute the "exact" function in this volume at npt points
5301 // where npt is usually this->npt+1.
5302 fcube(key, func, qx, fval);
5303
5304 // Transform into the scaling function basis of order npt
5305 double scale = pow(0.5,0.5*NDIM*key.level())*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
5306 fval = fast_transform(fval,quad_phiw,result,work).scale(scale);
5307
5308 // Subtract to get the error ... the original coeffs are in the order k
5309 // basis but we just computed the coeffs in the order npt(=k+1) basis
5310 // so we can either use slices or an iterator macro.
5311 const tensorT coeff = node.coeff().full_tensor_copy();
5312 ITERATOR(coeff,fval(IND)-=coeff(IND););
5313 // flo note: we do want to keep a full tensor here!
5314
5315 // Compute the norm of what remains
5316 double err = fval.normf();
5317 return err*err;
5318 }
5319
5320 template <typename opT>
5322 const implT* impl;
5323 const opT* func;
5324 int npt;
5328 public:
5329 do_err_box() = default;
5330
5334
5337
5338 double operator()(typename dcT::const_iterator& it) const {
5339 const keyT& key = it->first;
5340 const nodeT& node = it->second;
5341 if (node.has_coeff())
5342 return impl->err_box(key, node, *func, npt, qx, quad_phit, quad_phiw);
5343 else
5344 return 0.0;
5345 }
5346
5347 double operator()(double a, double b) const {
5348 return a+b;
5349 }
5350
5351 template <typename Archive>
5352 void serialize(const Archive& ar) {
5353 MADNESS_EXCEPTION("not yet", 1);
5354 }
5355 };
5356
5357 /// Returns the sum of squares of errors from local info ... no comms
5358 template <typename opT>
5359 double errsq_local(const opT& func) const {
5361 // Make quadrature rule of higher order
5362 const int npt = cdata.npt + 1;
5363 Tensor<double> qx, qw, quad_phi, quad_phiw, quad_phit;
5364 FunctionCommonData<T,NDIM>::_init_quadrature(k+1, npt, qx, qw, quad_phi, quad_phiw, quad_phit);
5365
5368 return world.taskq.reduce< double,rangeT,do_err_box<opT> >(range,
5369 do_err_box<opT>(this, &func, npt, qx, quad_phit, quad_phiw));
5370 }
5371
5372 /// Returns \c int(f(x),x) in local volume
5373 T trace_local() const;
5374
5376 double operator()(typename dcT::const_iterator& it) const {
5377 const nodeT& node = it->second;
5378 if (node.has_coeff()) {
5379 double norm = node.coeff().normf();
5380 return norm*norm;
5381 }
5382 else {
5383 return 0.0;
5384 }
5385 }
5386
5387 double operator()(double a, double b) const {
5388 return (a+b);
5389 }
5390
5391 template <typename Archive> void serialize(const Archive& ar) {
5392 MADNESS_EXCEPTION("NOT IMPLEMENTED", 1);
5393 }
5394 };
5395
5396
5397 /// Returns the square of the local norm ... no comms
5398 double norm2sq_local() const;
5399
5400 /// compute the inner product of this range with other
5401 template<typename R>
5405 typedef TENSOR_RESULT_TYPE(T,R) resultT;
5406
5409 resultT operator()(typename dcT::const_iterator& it) const {
5410
5412 const keyT& key=it->first;
5413 const nodeT& fnode = it->second;
5414 if (fnode.has_coeff()) {
5415 if (other->coeffs.probe(it->first)) {
5416 const FunctionNode<R,NDIM>& gnode = other->coeffs.find(key).get()->second;
5417 if (gnode.has_coeff()) {
5418 if (gnode.coeff().dim(0) != fnode.coeff().dim(0)) {
5419 madness::print("INNER", it->first, gnode.coeff().dim(0),fnode.coeff().dim(0));
5420 MADNESS_EXCEPTION("functions have different k or compress/reconstruct error", 0);
5421 }
5422 if (leaves_only) {
5423 if (gnode.is_leaf() or fnode.is_leaf()) {
5424 sum += fnode.coeff().trace_conj(gnode.coeff());
5425 }
5426 } else {
5427 sum += fnode.coeff().trace_conj(gnode.coeff());
5428 }
5429 }
5430 }
5431 }
5432 return sum;
5433 }
5434
5435 resultT operator()(resultT a, resultT b) const {
5436 return (a+b);
5437 }
5438
5439 template <typename Archive> void serialize(const Archive& ar) {
5440 MADNESS_EXCEPTION("NOT IMPLEMENTED", 1);
5441 }
5442 };
5443
5444 /// Returns the inner product ASSUMING same distribution
5445
5446 /// handles compressed and redundant form
5447 template <typename R>
5451 typedef TENSOR_RESULT_TYPE(T,R) resultT;
5452
5453 // make sure the states of the trees are consistent
5456 return world.taskq.reduce<resultT,rangeT,do_inner_local<R> >
5458 }
5459
5460
5461 /// compute the inner product of this range with other
5462 template<typename R>
5466 bool leaves_only=true;
5467 typedef TENSOR_RESULT_TYPE(T,R) resultT;
5468
5472 resultT operator()(typename dcT::const_iterator& it) const {
5473
5474 constexpr std::size_t LDIM=std::max(NDIM/2,std::size_t(1));
5475
5476 const keyT& key=it->first;
5477 const nodeT& fnode = it->second;
5478 if (not fnode.has_coeff()) return resultT(0.0); // probably internal nodes
5479
5480 // assuming all boxes (esp the low-dim ones) are local, i.e. the functions are replicated
5481 auto find_valid_parent = [](auto& key, auto& impl, auto&& find_valid_parent) {
5482 MADNESS_CHECK(impl->get_coeffs().owner(key)==impl->world.rank()); // make sure everything is local!
5483 if (impl->get_coeffs().probe(key)) return key;
5484 auto parentkey=key.parent();
5485 return find_valid_parent(parentkey, impl, find_valid_parent);
5486 };
5487
5488 // returns coefficients, empty if no functor present
5489 auto get_coeff = [&find_valid_parent](const auto& key, const auto& v_impl) {
5490 if ((v_impl.size()>0) and v_impl.front().get()) {
5491 auto impl=v_impl.front();
5492
5493// bool have_impl=impl.get();
5494// if (have_impl) {
5495 auto parentkey = find_valid_parent(key, impl, find_valid_parent);
5496 MADNESS_CHECK(impl->get_coeffs().probe(parentkey));
5497 typename decltype(impl->coeffs)::accessor acc;
5498 impl->get_coeffs().find(acc,parentkey);
5499 auto parentcoeff=acc->second.coeff();
5500 auto coeff=impl->parent_to_child(parentcoeff, parentkey, key);
5501 return coeff;
5502 } else {
5503 // get type of vector elements
5504 typedef typename std::decay_t<decltype(v_impl)>::value_type::element_type::typeT S;
5505// typedef typename std::decay_t<decltype(v_impl)>::value_type S;
5506 return GenTensor<S>();
5507// return GenTensor<typename std::decay_t<decltype(*impl)>::typeT>();
5508 }
5509 };
5510
5511 auto make_vector = [](auto& arg) {
5512 return std::vector<std::decay_t<decltype(arg)>>(1,arg);
5513 };
5514
5515
5516 Key<LDIM> key1,key2;
5517 key.break_apart(key1,key2);
5518
5519 auto func=dynamic_cast<CompositeFunctorInterface<R,NDIM,LDIM>* >(ket->functor.get());
5521
5522 MADNESS_CHECK_THROW(func->impl_ket_vector.size()==0 or func->impl_ket_vector.size()==1,
5523 "only one ket function supported in inner_on_demand");
5524 MADNESS_CHECK_THROW(func->impl_p1_vector.size()==0 or func->impl_p1_vector.size()==1,
5525 "only one p1 function supported in inner_on_demand");
5526 MADNESS_CHECK_THROW(func->impl_p2_vector.size()==0 or func->impl_p2_vector.size()==1,
5527 "only one p2 function supported in inner_on_demand");
5528 auto coeff_bra=fnode.coeff();
5529 auto coeff_ket=get_coeff(key,func->impl_ket_vector);
5530 auto coeff_v1=get_coeff(key1,make_vector(func->impl_m1));
5531 auto coeff_v2=get_coeff(key2,make_vector(func->impl_m2));
5532 auto coeff_p1=get_coeff(key1,func->impl_p1_vector);
5533 auto coeff_p2=get_coeff(key2,func->impl_p2_vector);
5534
5535 // construct |ket(1,2)> or |p(1)p(2)> or |p(1)p(2) ket(1,2)>
5536 double error=0.0;
5537 if (coeff_ket.has_data() and coeff_p1.has_data()) {
5538 pointwise_multiplier<LDIM> pm(key,coeff_ket);
5539 coeff_ket=pm(key,outer(coeff_p1,coeff_p2,TensorArgs(TT_FULL,-1.0)).full_tensor());
5540 error+=pm.error;
5541 } else if (coeff_ket.has_data() or coeff_p1.has_data()) {
5542 coeff_ket = (coeff_ket.has_data()) ? coeff_ket : outer(coeff_p1,coeff_p2);
5543 } else { // not ket and no p1p2
5544 MADNESS_EXCEPTION("confused ket/p1p2 in do_inner_local_on_demand",1);
5545 }
5546
5547 // construct (v(1) + v(2)) |ket(1,2)>
5548 coeffT v1v2ket;
5549 if (coeff_v1.has_data()) {
5550 pointwise_multiplier<LDIM> pm(key,coeff_ket);
5551 v1v2ket = pm(key,coeff_v1.full_tensor(), 1);
5552 error+=pm.error;
5553 v1v2ket+= pm(key,coeff_v2.full_tensor(), 2);
5554 error+=pm.error;
5555 } else {
5556 v1v2ket = coeff_ket;
5557 }
5558
5559 resultT result;
5560 if (func->impl_eri) { // project bra*ket onto eri, avoid multiplication with eri
5561 MADNESS_CHECK(func->impl_eri->get_functor()->provides_coeff());
5562 coeffT coeff_eri=func->impl_eri->get_functor()->coeff(key).full_tensor();
5563 pointwise_multiplier<LDIM> pm(key,v1v2ket);
5564 tensorT braket=pm(key,coeff_bra.full_tensor_copy().conj());
5565 error+=pm.error;
5566 if (error>1.e-3) print("error in key",key,error);
5567 result=coeff_eri.full_tensor().trace(braket);
5568
5569 } else { // no eri, project ket onto bra
5570 result=coeff_bra.full_tensor_copy().trace_conj(v1v2ket.full_tensor_copy());
5571 }
5572 return result;
5573 }
5574
5575 resultT operator()(resultT a, resultT b) const {
5576 return (a+b);
5577 }
5578
5579 template <typename Archive> void serialize(const Archive& ar) {
5580 MADNESS_EXCEPTION("NOT IMPLEMENTED", 1);
5581 }
5582 };
5583
5584 /// Returns the inner product of this with function g constructed on-the-fly
5585
5586 /// the leaf boxes of this' MRA tree defines the inner product
5587 template <typename R>
5588 TENSOR_RESULT_TYPE(T,R) inner_local_on_demand(const FunctionImpl<R,NDIM>& gimpl) const {
5591
5595 do_inner_local_on_demand<R>(this, &gimpl));
5596 }
5597
5598 /// Type of the entry in the map returned by make_key_vec_map
5599 typedef std::vector< std::pair<int,const coeffT*> > mapvecT;
5600
5601 /// Type of the map returned by make_key_vec_map
5603
5604 /// Adds keys to union of local keys with specified index
5605 void add_keys_to_map(mapT* map, int index) const {
5606 typename dcT::const_iterator end = coeffs.end();
5607 for (typename dcT::const_iterator it=coeffs.begin(); it!=end; ++it) {
5608 typename mapT::accessor acc;
5609 const keyT& key = it->first;
5610 const FunctionNode<T,NDIM>& node = it->second;
5611 if (node.has_coeff()) {
5612 [[maybe_unused]] auto inserted = map->insert(acc,key);
5613 acc->second.push_back(std::make_pair(index,&(node.coeff())));
5614 }
5615 }
5616 }
5617
5618 /// Returns map of union of local keys to vector of indexes of functions containing that key
5619
5620 /// Local concurrency and synchronization only; no communication
5621 static
5622 mapT
5623 make_key_vec_map(const std::vector<const FunctionImpl<T,NDIM>*>& v) {
5624 mapT map(100000);
5625 // This loop must be parallelized
5626 for (unsigned int i=0; i<v.size(); i++) {
5627 //v[i]->add_keys_to_map(&map,i);
5628 v[i]->world.taskq.add(*(v[i]), &FunctionImpl<T,NDIM>::add_keys_to_map, &map, int(i));
5629 }
5630 if (v.size()) v[0]->world.taskq.fence();
5631 return map;
5632 }
5633
5634#if HAVE_GENTENSOR
5635// Original
5636 template <typename R>
5637 static void do_inner_localX(const typename mapT::iterator lstart,
5638 const typename mapT::iterator lend,
5639 typename FunctionImpl<R,NDIM>::mapT* rmap_ptr,
5640 const bool sym,
5641 Tensor< TENSOR_RESULT_TYPE(T,R) >* result_ptr,
5642 Mutex* mutex) {
5643 Tensor< TENSOR_RESULT_TYPE(T,R) >& result = *result_ptr;
5644 Tensor< TENSOR_RESULT_TYPE(T,R) > r(result.dim(0),result.dim(1));
5645 for (typename mapT::iterator lit=lstart; lit!=lend; ++lit) {
5646 const keyT& key = lit->first;
5647 typename FunctionImpl<R,NDIM>::mapT::iterator rit=rmap_ptr->find(key);
5648 if (rit != rmap_ptr->end()) {
5649 const mapvecT& leftv = lit->second;
5650 const typename FunctionImpl<R,NDIM>::mapvecT& rightv =rit->second;
5651 const int nleft = leftv.size();
5652 const int nright= rightv.size();
5653
5654 for (int iv=0; iv<nleft; iv++) {
5655 const int i = leftv[iv].first;
5656 const GenTensor<T>* iptr = leftv[iv].second;
5657
5658 for (int jv=0; jv<nright; jv++) {
5659 const int j = rightv[jv].first;
5660 const GenTensor<R>* jptr = rightv[jv].second;
5661
5662 if (!sym || (sym && i<=j))
5663 r(i,j) += iptr->trace_conj(*jptr);
5664 }
5665 }
5666 }
5667 }
5668 mutex->lock();
5669 result += r;
5670 mutex->unlock();
5671 }
5672#else
5673 template <typename R>
5674 static void do_inner_localX(const typename mapT::iterator lstart,
5675 const typename mapT::iterator lend,
5676 typename FunctionImpl<R,NDIM>::mapT* rmap_ptr,
5677 const bool sym,
5678 Tensor< TENSOR_RESULT_TYPE(T,R) >* result_ptr,
5679 Mutex* mutex) {
5680 Tensor< TENSOR_RESULT_TYPE(T,R) >& result = *result_ptr;
5681 //Tensor< TENSOR_RESULT_TYPE(T,R) > r(result.dim(0),result.dim(1));
5682 for (typename mapT::iterator lit=lstart; lit!=lend; ++lit) {
5683 const keyT& key = lit->first;
5684 typename FunctionImpl<R,NDIM>::mapT::iterator rit=rmap_ptr->find(key);
5685 if (rit != rmap_ptr->end()) {
5686 const mapvecT& leftv = lit->second;
5687 const typename FunctionImpl<R,NDIM>::mapvecT& rightv =rit->second;
5688 const size_t nleft = leftv.size();
5689 const size_t nright= rightv.size();
5690
5691 unsigned int size = leftv[0].second->size();
5692 Tensor<T> Left(nleft, size);
5693 Tensor<R> Right(nright, size);
5694 Tensor< TENSOR_RESULT_TYPE(T,R)> r(nleft, nright);
5695 for(unsigned int iv = 0; iv < nleft; ++iv) Left(iv,_) = *(leftv[iv].second);
5696 for(unsigned int jv = 0; jv < nright; ++jv) Right(jv,_) = *(rightv[jv].second);
5697 // call mxmT from mxm.h in tensor
5698 if(TensorTypeData<T>::iscomplex) Left = Left.conj(); // Should handle complex case and leave real case alone
5699 mxmT(nleft, nright, size, r.ptr(), Left.ptr(), Right.ptr());
5700 mutex->lock();
5701 for(unsigned int iv = 0; iv < nleft; ++iv) {
5702 const int i = leftv[iv].first;
5703 for(unsigned int jv = 0; jv < nright; ++jv) {
5704 const int j = rightv[jv].first;
5705 if (!sym || (sym && i<=j)) result(i,j) += r(iv,jv);
5706 }
5707 }
5708 mutex->unlock();
5709 }
5710 }
5711 }
5712#endif
5713
5714#if HAVE_GENTENSOR
5715// Original
5716 template <typename R>
5717 static void do_dot_localX(const typename mapT::iterator lstart,
5718 const typename mapT::iterator lend,
5719 typename FunctionImpl<R, NDIM>::mapT* rmap_ptr,
5720 const bool sym,
5721 Tensor<TENSOR_RESULT_TYPE(T, R)>* result_ptr,
5722 Mutex* mutex) {
5723 Tensor<TENSOR_RESULT_TYPE(T, R)>& result = *result_ptr;
5724 Tensor<TENSOR_RESULT_TYPE(T, R)> r(result.dim(0), result.dim(1));
5725 for (typename mapT::iterator lit = lstart; lit != lend; ++lit) {
5726 const keyT& key = lit->first;
5727 typename FunctionImpl<R, NDIM>::mapT::iterator rit = rmap_ptr->find(key);
5728 if (rit != rmap_ptr->end()) {
5729 const mapvecT& leftv = lit->second;
5730 const typename FunctionImpl<R, NDIM>::mapvecT& rightv = rit->second;
5731 const int nleft = leftv.size();
5732 const int nright = rightv.size();
5733
5734 for (int iv = 0; iv < nleft; iv++) {
5735 const int i = leftv[iv].first;
5736 const GenTensor<T>* iptr = leftv[iv].second;
5737
5738 for (int jv = 0; jv < nright; jv++) {
5739 const int j = rightv[jv].first;
5740 const GenTensor<R>* jptr = rightv[jv].second;
5741
5742 if (!sym || (sym && i <= j))
5743 r(i, j) += iptr->trace(*jptr);
5744 }
5745 }
5746 }
5747 }
5748 mutex->lock();
5749 result += r;
5750 mutex->unlock();
5751 }
5752#else
5753 template <typename R>
5754 static void do_dot_localX(const typename mapT::iterator lstart,
5755 const typename mapT::iterator lend,
5756 typename FunctionImpl<R, NDIM>::mapT* rmap_ptr,
5757 const bool sym,
5758 Tensor<TENSOR_RESULT_TYPE(T, R)>* result_ptr,
5759 Mutex* mutex) {
5760 Tensor<TENSOR_RESULT_TYPE(T, R)>& result = *result_ptr;
5761 // Tensor<TENSOR_RESULT_TYPE(T, R)> r(result.dim(0), result.dim(1));
5762 for (typename mapT::iterator lit = lstart; lit != lend; ++lit) {
5763 const keyT& key = lit->first;
5764 typename FunctionImpl<R, NDIM>::mapT::iterator rit = rmap_ptr->find(key);
5765 if (rit != rmap_ptr->end()) {
5766 const mapvecT& leftv = lit->second;
5767 const typename FunctionImpl<R, NDIM>::mapvecT& rightv = rit->second;
5768 const size_t nleft = leftv.size();
5769 const size_t nright= rightv.size();
5770
5771 unsigned int size = leftv[0].second->size();
5772 Tensor<T> Left(nleft, size);
5773 Tensor<R> Right(nright, size);
5774 Tensor< TENSOR_RESULT_TYPE(T, R)> r(nleft, nright);
5775 for(unsigned int iv = 0; iv < nleft; ++iv) Left(iv, _) = *(leftv[iv].second);
5776 for(unsigned int jv = 0; jv < nright; ++jv) Right(jv, _) = *(rightv[jv].second);
5777 // call mxmT from mxm.h in tensor
5778 mxmT(nleft, nright, size, r.ptr(), Left.ptr(), Right.ptr());
5779 mutex->lock();
5780 for(unsigned int iv = 0; iv < nleft; ++iv) {
5781 const int i = leftv[iv].first;
5782 for(unsigned int jv = 0; jv < nright; ++jv) {
5783 const int j = rightv[jv].first;
5784 if (!sym || (sym && i <= j)) result(i, j) += r(iv, jv);
5785 }
5786 }
5787 mutex->unlock();
5788 }
5789 }
5790 }
5791#endif
5792
5793 static double conj(float x) {
5794 return x;
5795 }
5796
5797 static std::complex<double> conj(const std::complex<double> x) {
5798 return std::conj(x);
5799 }
5800
5801 template <typename R>
5802 static Tensor< TENSOR_RESULT_TYPE(T,R) >
5803 inner_local(const std::vector<const FunctionImpl<T,NDIM>*>& left,
5804 const std::vector<const FunctionImpl<R,NDIM>*>& right,
5805 bool sym) {
5806
5807 // This is basically a sparse matrix^T * matrix product
5808 // Rij = sum(k) Aki * Bkj
5809 // where i and j index functions and k index the wavelet coeffs
5810 // eventually the goal is this structure (don't have jtile yet)
5811 //
5812 // do in parallel tiles of k (tensors of coeffs)
5813 // do tiles of j
5814 // do i
5815 // do j in jtile
5816 // do k in ktile
5817 // Rij += Aki*Bkj
5818
5819 mapT lmap = make_key_vec_map(left);
5820 typename FunctionImpl<R,NDIM>::mapT rmap;
5821 auto* rmap_ptr = (typename FunctionImpl<R,NDIM>::mapT*)(&lmap);
5822 if ((std::vector<const FunctionImpl<R,NDIM>*>*)(&left) != &right) {
5824 rmap_ptr = &rmap;
5825 }
5826
5827 size_t chunk = (lmap.size()-1)/(3*4*5)+1;
5828
5829 Tensor< TENSOR_RESULT_TYPE(T,R) > r(left.size(), right.size());
5830 Mutex mutex;
5831
5832 typename mapT::iterator lstart=lmap.begin();
5833 while (lstart != lmap.end()) {
5834 typename mapT::iterator lend = lstart;
5835 advance(lend,chunk);
5836 left[0]->world.taskq.add(&FunctionImpl<T,NDIM>::do_inner_localX<R>, lstart, lend, rmap_ptr, sym, &r, &mutex);
5837 lstart = lend;
5838 }
5839 left[0]->world.taskq.fence();
5840
5841 if (sym) {
5842 for (long i=0; i<r.dim(0); i++) {
5843 for (long j=0; j<i; j++) {
5844 TENSOR_RESULT_TYPE(T,R) sum = r(i,j)+conj(r(j,i));
5845 r(i,j) = sum;
5846 r(j,i) = conj(sum);
5847 }
5848 }
5849 }
5850 return r;
5851 }
5852
5853 template <typename R>
5854 static Tensor<TENSOR_RESULT_TYPE(T, R)>
5855 dot_local(const std::vector<const FunctionImpl<T, NDIM>*>& left,
5856 const std::vector<const FunctionImpl<R, NDIM>*>& right,
5857 bool sym) {
5858
5859 // This is basically a sparse matrix * matrix product
5860 // Rij = sum(k) Aik * Bkj
5861 // where i and j index functions and k index the wavelet coeffs
5862 // eventually the goal is this structure (don't have jtile yet)
5863 //
5864 // do in parallel tiles of k (tensors of coeffs)
5865 // do tiles of j
5866 // do i
5867 // do j in jtile
5868 // do k in ktile
5869 // Rij += Aik*Bkj
5870
5871 mapT lmap = make_key_vec_map(left);
5872 typename FunctionImpl<R, NDIM>::mapT rmap;
5873 auto* rmap_ptr = (typename FunctionImpl<R, NDIM>::mapT*)(&lmap);
5874 if ((std::vector<const FunctionImpl<R, NDIM>*>*)(&left) != &right) {
5876 rmap_ptr = &rmap;
5877 }
5878
5879 size_t chunk = (lmap.size() - 1) / (3 * 4 * 5) + 1;
5880
5881 Tensor<TENSOR_RESULT_TYPE(T, R)> r(left.size(), right.size());
5882 Mutex mutex;
5883
5884 typename mapT::iterator lstart=lmap.begin();
5885 while (lstart != lmap.end()) {
5886 typename mapT::iterator lend = lstart;
5887 advance(lend, chunk);
5888 left[0]->world.taskq.add(&FunctionImpl<T, NDIM>::do_dot_localX<R>, lstart, lend, rmap_ptr, sym, &r, &mutex);
5889 lstart = lend;
5890 }
5891 left[0]->world.taskq.fence();
5892
5893 // sym is for hermiticity
5894 if (sym) {
5895 for (long i = 0; i < r.dim(0); i++) {
5896 for (long j = 0; j < i; j++) {
5897 TENSOR_RESULT_TYPE(T, R) sum = r(i, j) + conj(r(j, i));
5898 r(i, j) = sum;
5899 r(j, i) = conj(sum);
5900 }
5901 }
5902 }
5903 return r;
5904 }
5905
5906 template <typename R>
5908 {
5909 static_assert(!std::is_same<R, int>::value &&
5910 std::is_same<R, int>::value,
5911 "Compilation failed because you wanted to know the type; see below:");
5912 }
5913
5914 /// invoked by result
5915
5916 /// contract 2 functions f(x,z) = \int g(x,y) * h(y,z) dy
5917 /// @tparam CDIM: the dimension of the contraction variable (y)
5918 /// @tparam NDIM: the dimension of the result (x,z)
5919 /// @tparam LDIM: the dimension of g(x,y)
5920 /// @tparam KDIM: the dimension of h(y,z)
5921 template<typename Q, std::size_t LDIM, typename R, std::size_t KDIM,
5922 std::size_t CDIM = (KDIM + LDIM - NDIM) / 2>
5924 const std::array<int, CDIM> v1, const std::array<int, CDIM> v2) {
5925
5926 typedef std::multimap<Key<NDIM>, std::list<Key<CDIM>>> contractionmapT;
5927 //double wall_get_lists=0.0;
5928 //double wall_recur=0.0;
5929 //double wall_contract=0.0;
5932
5933 // auto print_map = [](const auto& map) {
5934 // for (const auto& kv : map) print(kv.first,"--",kv.second);
5935 // };
5936 // logical constness, not bitwise constness
5937 FunctionImpl<Q,LDIM>& g_nc=const_cast<FunctionImpl<Q,LDIM>&>(g);
5938 FunctionImpl<R,KDIM>& h_nc=const_cast<FunctionImpl<R,KDIM>&>(h);
5939
5940 std::list<contractionmapT> all_contraction_maps;
5941 for (std::size_t n=0; n<nmax; ++n) {
5942
5943 // list of nodes with d coefficients (and their parents)
5944 //double wall0 = wall_time();
5945 auto [g_ijlist, g_jlist] = g.get_contraction_node_lists(n, v1);
5946 auto [h_ijlist, h_jlist] = h.get_contraction_node_lists(n, v2);
5947 if ((g_ijlist.size() == 0) and (h_ijlist.size() == 0)) break;
5948 //double wall1 = wall_time();
5949 //wall_get_lists += (wall1 - wall0);
5950 //wall0 = wall1;
5951// print("g_jlist");
5952// for (const auto& kv : g_jlist) print(kv.first,kv.second);
5953// print("h_jlist");
5954// for (const auto& kv : h_jlist) print(kv.first,kv.second);
5955
5956 // next lines will insert s nodes into g and h -> possible race condition!
5957 bool this_first = true; // are the remaining indices of g before those of g: f(x,z) = g(x,y) h(y,z)
5958 // CDIM, NDIM, KDIM
5959 contractionmapT contraction_map = g_nc.recur_down_for_contraction_map(
5960 g_nc.key0(), g_nc.get_coeffs().find(g_nc.key0()).get()->second, v1, v2,
5961 h_ijlist, h_jlist, this_first, thresh);
5962
5963 this_first = false;
5964 // CDIM, NDIM, LDIM
5965 auto hnode0=h_nc.get_coeffs().find(h_nc.key0()).get()->second;
5966 contractionmapT contraction_map1 = h_nc.recur_down_for_contraction_map(
5967 h_nc.key0(), hnode0, v2, v1,
5968 g_ijlist, g_jlist, this_first, thresh);
5969
5970 // will contain duplicate entries
5971 contraction_map.merge(contraction_map1);
5972 // turn multimap into a map of list
5973 auto it = contraction_map.begin();
5974 while (it != contraction_map.end()) {
5975 auto it_end = contraction_map.upper_bound(it->first);
5976 auto it2 = it;
5977 it2++;
5978 while (it2 != it_end) {
5979 it->second.splice(it->second.end(), it2->second);
5980 it2 = contraction_map.erase(it2);
5981 }
5982 it = it_end;
5983 }
5984// print("thresh ",thresh);
5985// print("contraction list size",contraction_map.size());
5986
5987 // remove all double entries
5988 for (auto& elem: contraction_map) {
5989 elem.second.sort();
5990 elem.second.unique();
5991 }
5992 //wall1 = wall_time();
5993 //wall_recur += (wall1 - wall0);
5994// if (n==2) {
5995// print("contraction map for n=", n);
5996// print_map(contraction_map);
5997// }
5998 all_contraction_maps.push_back(contraction_map);
5999
6000 long mapsize=contraction_map.size();
6001 if (mapsize==0) break;
6002 }
6003
6004
6005 // finally do the contraction
6006 for (const auto& contraction_map : all_contraction_maps) {
6007 for (const auto& key_list : contraction_map) {
6008 const Key<NDIM>& key=key_list.first;
6009 const std::list<Key<CDIM>>& list=key_list.second;
6010 woT::task(coeffs.owner(key), &implT:: template partial_inner_contract<Q,LDIM,R,KDIM>,
6011 &g,&h,v1,v2,key,list);
6012 }
6013 }
6014 }
6015
6016 /// for contraction two functions f(x,z) = \int g(x,y) h(y,z) dy
6017
6018 /// find all nodes with d coefficients and return a list of complete keys and of
6019 /// keys holding only the y dimension, also the maximum norm of all d for the j dimension
6020 /// @param[in] n the scale
6021 /// @param[in] v array holding the indices of the integration variable
6022 /// @return ijlist: list of all nodes with d coeffs; jlist: j-part of ij list only
6023 template<std::size_t CDIM>
6024 std::tuple<std::set<Key<NDIM>>, std::map<Key<CDIM>,double>>
6025 get_contraction_node_lists(const std::size_t n, const std::array<int, CDIM>& v) const {
6026
6027 const auto& cdata=get_cdata();
6028 auto has_d_coeffs = [&cdata](const coeffT& coeff) {
6029 if (coeff.has_no_data()) return false;
6030 return (coeff.dim(0)==2*cdata.k);
6031 };
6032
6033 // keys to be contracted in g
6034 std::set<Key<NDIM>> ij_list; // full key
6035 std::map<Key<CDIM>,double> j_list; // only that dimension that will be contracted
6036
6037 for (auto it=get_coeffs().begin(); it!=get_coeffs().end(); ++it) {
6038 const Key<NDIM>& key=it->first;
6039 const FunctionNode<T,NDIM>& node=it->second;
6040 if ((key.level()==n) and (has_d_coeffs(node.coeff()))) {
6041 ij_list.insert(key);
6043 for (std::size_t i=0; i<CDIM; ++i) j_trans[i]=key.translation()[v[i]];
6044 Key<CDIM> jkey(n,j_trans);
6045 const double max_d_norm=j_list[jkey];
6046 j_list.insert_or_assign(jkey,std::max(max_d_norm,node.get_dnorm()));
6047 Key<CDIM> parent_jkey=jkey.parent();
6048 while (j_list.count(parent_jkey)==0) {
6049 j_list.insert({parent_jkey,1.0});
6050 parent_jkey=parent_jkey.parent();
6051 }
6052 }
6053 }
6054 return std::make_tuple(ij_list,j_list);
6055 }
6056
6057 /// make a map of all nodes that will contribute to a partial inner product
6058
6059 /// given the list of d coefficient-holding nodes of the other function:
6060 /// recur down h if snorm * dnorm > tol and key n−jx ∈ other−ij-list. Make s
6061 /// coefficients if necessary. Make list of nodes n − ijk as map(n-ik, list(j)).
6062 ///
6063 /// !! WILL ADD NEW S NODES TO THIS TREE THAT MUST BE REMOVED TO AVOID INCONSISTENT TREE STRUCTURE !!
6064 ///
6065 /// @param[in] key for recursion
6066 /// @param[in] node corresponds to key
6067 /// @param[in] v_this this' dimension that are contracted
6068 /// @param[in] v_other other's dimension that are contracted
6069 /// @param[in] ij_other_list list of nodes of the other function that will be contracted (and their parents)
6070 /// @param[in] j_other_list list of column nodes of the other function that will be contracted (and their parents)
6071 /// @param[in] max_d_norm max d coeff norm of the nodes in j_list
6072 /// @param[in] this_first are the remaining coeffs of this functions first or last in the result function
6073 /// @param[in] thresh threshold for including nodes in the contraction: snorm*dnorm > thresh
6074 /// @tparam CDIM dimension to be contracted
6075 /// @tparam ODIM dimensions of the other function
6076 /// @tparam FDIM dimensions of the final function
6077 template<std::size_t CDIM, std::size_t ODIM, std::size_t FDIM=NDIM+ODIM-2*CDIM>
6078 std::multimap<Key<FDIM>, std::list<Key<CDIM>>> recur_down_for_contraction_map(
6079 const keyT& key, const nodeT& node,
6080 const std::array<int,CDIM>& v_this,
6081 const std::array<int,CDIM>& v_other,
6082 const std::set<Key<ODIM>>& ij_other_list,
6083 const std::map<Key<CDIM>,double>& j_other_list,
6084 bool this_first, const double thresh) {
6085
6086 std::multimap<Key<FDIM>, std::list<Key<CDIM>>> contraction_map;
6087
6088 // fast return if the other function has no d coeffs
6089 if (j_other_list.empty()) return contraction_map;
6090
6091 // continue recursion if this node may be contracted with the j column
6092 // extract relevant node translations from this node
6093 const auto j_this_key=key.extract_key(v_this);
6094
6095// print("\nkey, j_this_key", key, j_this_key);
6096 const double max_d_norm=j_other_list.find(j_this_key)->second;
6097 const bool sd_norm_product_large = node.get_snorm() * max_d_norm > truncate_tol(thresh,key);
6098// print("sd_product_norm",node.get_snorm() * max_d_norm, thresh);
6099
6100 // end recursion if we have reached the final scale n
6101 // with which nodes from other will this node be contracted?
6102 bool final_scale=key.level()==ij_other_list.begin()->level();
6103 if (final_scale and sd_norm_product_large) {
6104 for (auto& other_key : ij_other_list) {
6105 const auto j_other_key=other_key.extract_key(v_other);
6106 if (j_this_key != j_other_key) continue;
6107 auto i_key=key.extract_complement_key(v_this);
6108 auto k_key=other_key.extract_complement_key(v_other);
6109// print("key, ij_other_key",key,other_key);
6110// print("i, k, j key",i_key, k_key, j_this_key);
6111 Key<FDIM> ik_key=(this_first) ? i_key.merge_with(k_key) : k_key.merge_with(i_key);
6112// print("ik_key",ik_key);
6113// MADNESS_CHECK(contraction_map.count(ik_key)==0);
6114 contraction_map.insert(std::make_pair(ik_key,std::list<Key<CDIM>>{j_this_key}));
6115 }
6116 return contraction_map;
6117 }
6118
6119 bool continue_recursion = (j_other_list.count(j_this_key)==1);
6120 if (not continue_recursion) return contraction_map;
6121
6122
6123 // continue recursion if norms are large
6124 continue_recursion = (node.has_children() or sd_norm_product_large);
6125
6126 if (continue_recursion) {
6127 // in case we need to compute children's coefficients: unfilter only once
6128 bool compute_child_s_coeffs=true;
6129 coeffT d = node.coeff();
6130// print("continuing recursion from key",key);
6131
6132 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
6133 keyT child=kit.key();
6134 typename dcT::accessor acc;
6135
6136 // make child's s coeffs if it doesn't exist or if is has no s coeffs
6137 bool childnode_exists=get_coeffs().find(acc,child);
6138 bool need_s_coeffs= childnode_exists ? (acc->second.get_snorm()<=0.0) : true;
6139
6140 coeffT child_s_coeffs;
6141 if (need_s_coeffs and compute_child_s_coeffs) {
6142 if (d.dim(0)==cdata.vk[0]) { // s coeffs only in this node
6143 coeffT d1(cdata.v2k,get_tensor_args());
6144 d1(cdata.s0)+=d;
6145 d=d1;
6146 }
6147 d = unfilter(d);
6148 child_s_coeffs=copy(d(child_patch(child)));
6149 child_s_coeffs.reduce_rank(thresh);
6150 compute_child_s_coeffs=false;
6151 }
6152
6153 if (not childnode_exists) {
6154 get_coeffs().replace(child,nodeT(child_s_coeffs,false));
6155 get_coeffs().find(acc,child);
6156 } else if (childnode_exists and need_s_coeffs) {
6157 acc->second.coeff()=child_s_coeffs;
6158 }
6159 bool exists= get_coeffs().find(acc,child);
6160 MADNESS_CHECK(exists);
6161 nodeT& childnode = acc->second;
6162 if (need_s_coeffs) childnode.recompute_snorm_and_dnorm(get_cdata());
6163// print("recurring down to",child);
6164 contraction_map.merge(recur_down_for_contraction_map(child,childnode, v_this, v_other,
6165 ij_other_list, j_other_list, this_first, thresh));
6166// print("contraction_map.size()",contraction_map.size());
6167 }
6168
6169 }
6170
6171 return contraction_map;
6172 }
6173
6174
6175 /// tensor contraction part of partial_inner
6176
6177 /// @param[in] g rhs of the inner product
6178 /// @param[in] h lhs of the inner product
6179 /// @param[in] v1 dimensions of g to be contracted
6180 /// @param[in] v2 dimensions of h to be contracted
6181 /// @param[in] key key of result's (this) FunctionNode
6182 /// @param[in] j_key_list list of contraction index-j keys contributing to this' node
6183 template<typename Q, std::size_t LDIM, typename R, std::size_t KDIM,
6184 std::size_t CDIM = (KDIM + LDIM - NDIM) / 2>
6186 const std::array<int, CDIM> v1, const std::array<int, CDIM> v2,
6187 const Key<NDIM>& key, const std::list<Key<CDIM>>& j_key_list) {
6188
6189 Key<LDIM - CDIM> i_key;
6190 Key<KDIM - CDIM> k_key;
6191 key.break_apart(i_key, k_key);
6192
6193 coeffT result_coeff(get_cdata().v2k, get_tensor_type());
6194 for (const auto& j_key: j_key_list) {
6195
6196 auto v_complement = [](const auto& v, const auto& vc) {
6197 constexpr std::size_t VDIM = std::tuple_size<std::decay_t<decltype(v)>>::value;
6198 constexpr std::size_t VCDIM = std::tuple_size<std::decay_t<decltype(vc)>>::value;
6199 std::array<int, VCDIM> result;
6200 for (std::size_t i = 0; i < VCDIM; i++) result[i] = (v.back() + i + 1) % (VDIM + VCDIM);
6201 return result;
6202 };
6203 auto make_ij_key = [&v_complement](const auto i_key, const auto j_key, const auto& v) {
6204 constexpr std::size_t IDIM = std::decay_t<decltype(i_key)>::static_size;
6205 constexpr std::size_t JDIM = std::decay_t<decltype(j_key)>::static_size;
6206 static_assert(JDIM == std::tuple_size<std::decay_t<decltype(v)>>::value);
6207
6209 for (std::size_t i = 0; i < v.size(); ++i) l[v[i]] = j_key.translation()[i];
6210 std::array<int, IDIM> vc1;
6211 auto vc = v_complement(v, vc1);
6212 for (std::size_t i = 0; i < vc.size(); ++i) l[vc[i]] = i_key.translation()[i];
6213
6214 return Key<IDIM + JDIM>(i_key.level(), l);
6215 };
6216
6217 Key<LDIM> ij_key = make_ij_key(i_key, j_key, v1);
6218 Key<KDIM> jk_key = make_ij_key(k_key, j_key, v2);
6219
6220 MADNESS_CHECK(g->get_coeffs().probe(ij_key));
6221 MADNESS_CHECK(h->get_coeffs().probe(jk_key));
6222 const coeffT& gcoeff = g->get_coeffs().find(ij_key).get()->second.coeff();
6223 const coeffT& hcoeff = h->get_coeffs().find(jk_key).get()->second.coeff();
6224 coeffT gcoeff1, hcoeff1;
6225 if (gcoeff.dim(0) == g->get_cdata().k) {
6226 gcoeff1 = coeffT(g->get_cdata().v2k, g->get_tensor_args());
6227 gcoeff1(g->get_cdata().s0) += gcoeff;
6228 } else {
6229 gcoeff1 = gcoeff;
6230 }
6231 if (hcoeff.dim(0) == g->get_cdata().k) {
6232 hcoeff1 = coeffT(h->get_cdata().v2k, h->get_tensor_args());
6233 hcoeff1(h->get_cdata().s0) += hcoeff;
6234 } else {
6235 hcoeff1 = hcoeff;
6236 }
6237
6238 // offset: 0 for full tensor, 1 for svd representation with rand being the first dimension (r,d1,d2,d3) -> (r,d1*d2*d3)
6239 auto fuse = [](Tensor<T> tensor, const std::array<int, CDIM>& v, int offset) {
6240 for (std::size_t i = 0; i < CDIM - 1; ++i) {
6241 MADNESS_CHECK((v[i] + 1) == v[i + 1]); // make sure v is contiguous and ascending
6242 tensor = tensor.fusedim(v[0]+offset);
6243 }
6244 return tensor;
6245 };
6246
6247 // use case: partial_projection of 2-electron functions in svd representation f(1) = \int g(2) h(1,2) d2
6248 // c_i = \sum_j a_j b_ij = \sum_jr a_j b_rj b'_rj
6249 // = \sum_jr ( a_j b_rj) b'_rj )
6250 auto contract2 = [](const auto& svdcoeff, const auto& tensor, const int particle) {
6251#if HAVE_GENTENSOR
6252 const int spectator_particle=(particle+1)%2;
6253 Tensor<Q> gtensor = svdcoeff.get_svdtensor().make_vector_with_weights(particle);
6254 gtensor=gtensor.reshape(svdcoeff.rank(),gtensor.size()/svdcoeff.rank());
6255 MADNESS_CHECK(gtensor.ndim()==2);
6256 Tensor<Q> gtensor_other = svdcoeff.get_svdtensor().ref_vector(spectator_particle);
6257 Tensor<T> tmp1=inner(gtensor,tensor.flat(),1,0); // tmp1(r) = sum_j a'_(r,j) b(j)
6258 MADNESS_CHECK(tmp1.ndim()==1);
6259 Tensor<T> tmp2=inner(gtensor_other,tmp1,0,0); // tmp2(i) = sum_r a_(r,i) tmp1(r)
6260 return tmp2;
6261#else
6262 MADNESS_EXCEPTION("no partial_inner using svd without GenTensor",1);
6263 return Tensor<T>();
6264#endif
6265 };
6266
6267 if (gcoeff.is_full_tensor() and hcoeff.is_full_tensor() and result_coeff.is_full_tensor()) {
6268 // merge multiple contraction dimensions into one
6269 int offset = 0;
6270 Tensor<Q> gtensor = fuse(gcoeff1.full_tensor(), v1, offset);
6271 Tensor<R> htensor = fuse(hcoeff1.full_tensor(), v2, offset);
6272 result_coeff.full_tensor() += inner(gtensor, htensor, v1[0], v2[0]);
6273 if (key.level() > 0) {
6274 gtensor = copy(gcoeff1.full_tensor()(g->get_cdata().s0));
6275 htensor = copy(hcoeff1.full_tensor()(h->get_cdata().s0));
6276 gtensor = fuse(gtensor, v1, offset);
6277 htensor = fuse(htensor, v2, offset);
6278 result_coeff.full_tensor()(get_cdata().s0) -= inner(gtensor, htensor, v1[0], v2[0]);
6279 }
6280 }
6281
6282
6283 // use case: 2-electron functions in svd representation f(1,3) = \int g(1,2) h(2,3) d2
6284 // c_ik = \sum_j a_ij b_jk = \sum_jrr' a_ri a'_rj b_r'j b_r'k
6285 // = \sum_jrr' ( a_ri (a'_rj b_r'j) ) b_r'k
6286 // = \sum_jrr' c_r'i b_r'k
6287 else if (gcoeff.is_svd_tensor() and hcoeff.is_svd_tensor() and result_coeff.is_svd_tensor()) {
6288 MADNESS_CHECK(v1[0]==0 or v1[CDIM-1]==LDIM-1);
6289 MADNESS_CHECK(v2[0]==0 or v2[CDIM-1]==KDIM-1);
6290 int gparticle= v1[0]==0 ? 0 : 1; // which particle to integrate over
6291 int hparticle= v2[0]==0 ? 0 : 1; // which particle to integrate over
6292 // merge multiple contraction dimensions into one
6293 Tensor<Q> gtensor = gcoeff1.get_svdtensor().flat_vector_with_weights(gparticle);
6294 Tensor<Q> gtensor_other = gcoeff1.get_svdtensor().flat_vector((gparticle+1)%2);
6295 Tensor<R> htensor = hcoeff1.get_svdtensor().flat_vector_with_weights(hparticle);
6296 Tensor<R> htensor_other = hcoeff1.get_svdtensor().flat_vector((hparticle+1)%2);
6297 Tensor<T> tmp1=inner(gtensor,htensor,1,1); // tmp1(r,r') = sum_j b(r,j) a(r',j)
6298 Tensor<T> tmp2=inner(tmp1,gtensor_other,0,0); // tmp2(r',i) = sum_r tmp1(r,r') a(r,i)
6300 MADNESS_CHECK(tmp2.dim(0)==htensor_other.dim(0));
6301 w=1.0;
6302 coeffT result_tmp(get_cdata().v2k, get_tensor_type());
6303 result_tmp.get_svdtensor().set_vectors_and_weights(w,tmp2,htensor_other);
6304 if (key.level() > 0) {
6305 GenTensor<Q> gcoeff2 = copy(gcoeff1(g->get_cdata().s0));
6306 GenTensor<R> hcoeff2 = copy(hcoeff1(h->get_cdata().s0));
6307 Tensor<Q> gtensor = gcoeff2.get_svdtensor().flat_vector_with_weights(gparticle);
6308 Tensor<Q> gtensor_other = gcoeff2.get_svdtensor().flat_vector((gparticle+1)%2);
6309 Tensor<R> htensor = hcoeff2.get_svdtensor().flat_vector_with_weights(hparticle);
6310 Tensor<R> htensor_other = hcoeff2.get_svdtensor().flat_vector((hparticle+1)%2);
6311 Tensor<T> tmp1=inner(gtensor,htensor,1,1); // tmp1(r,r') = sum_j b(r,j) a(r',j)
6312 Tensor<T> tmp2=inner(tmp1,gtensor_other,0,0); // tmp2(r',i) = sum_r tmp1(r,r') a(r,i)
6314 MADNESS_CHECK(tmp2.dim(0)==htensor_other.dim(0));
6315 w=1.0;
6316 coeffT result_coeff1(get_cdata().vk, get_tensor_type());
6317 result_coeff1.get_svdtensor().set_vectors_and_weights(w,tmp2,htensor_other);
6318 result_tmp(get_cdata().s0)-=result_coeff1;
6319 }
6320 result_coeff+=result_tmp;
6321 }
6322
6323 // use case: partial_projection of 2-electron functions in svd representation f(1) = \int g(2) h(1,2) d2
6324 // c_i = \sum_j a_j b_ij = \sum_jr a_j b_rj b'_rj
6325 // = \sum_jr ( a_j b_rj) b'_rj )
6326 else if (gcoeff.is_full_tensor() and hcoeff.is_svd_tensor() and result_coeff.is_full_tensor()) {
6327 MADNESS_CHECK(v1[0]==0 and v1[CDIM-1]==LDIM-1);
6328 MADNESS_CHECK(v2[0]==0 or v2[CDIM-1]==KDIM-1);
6329 MADNESS_CHECK(LDIM==CDIM);
6330 int hparticle= v2[0]==0 ? 0 : 1; // which particle to integrate over
6331
6332 Tensor<T> r=contract2(hcoeff1,gcoeff1.full_tensor(),hparticle);
6333 if (key.level()>0) r(get_cdata().s0)-=contract2(copy(hcoeff1(h->get_cdata().s0)),copy(gcoeff.full_tensor()(g->get_cdata().s0)),hparticle);
6334 result_coeff.full_tensor()+=r;
6335 }
6336 // use case: partial_projection of 2-electron functions in svd representation f(1) = \int g(1,2) h(2) d2
6337 // c_i = \sum_j a_ij b_j = \sum_jr a_ri a'_rj b_j
6338 // = \sum_jr ( a_ri (a'_rj b_j) )
6339 else if (gcoeff.is_svd_tensor() and hcoeff.is_full_tensor() and result_coeff.is_full_tensor()) {
6340 MADNESS_CHECK(v1[0]==0 or v1[CDIM-1]==LDIM-1);
6341 MADNESS_CHECK(v2[0]==0 and v2[CDIM-1]==KDIM-1);
6342 MADNESS_CHECK(KDIM==CDIM);
6343 int gparticle= v1[0]==0 ? 0 : 1; // which particle to integrate over
6344
6345 Tensor<T> r=contract2(gcoeff1,hcoeff1.full_tensor(),gparticle);
6346 if (key.level()>0) r(get_cdata().s0)-=contract2(copy(gcoeff1(g->get_cdata().s0)),copy(hcoeff.full_tensor()(h->get_cdata().s0)),gparticle);
6347 result_coeff.full_tensor()+=r;
6348
6349 } else {
6350 MADNESS_EXCEPTION("unknown case in partial_inner_contract",1);
6351 }
6352 }
6353
6354 MADNESS_CHECK(result_coeff.is_assigned());
6355 result_coeff.reduce_rank(get_thresh());
6356
6357 if (coeffs.is_local(key))
6358 coeffs.send(key, &nodeT::accumulate, result_coeff, coeffs, key, get_tensor_args());
6359 else
6361 }
6362
6363 /// Return the inner product with an external function on a specified function node.
6364
6365 /// @param[in] key Key of the function node to compute the inner product on. (the domain of integration)
6366 /// @param[in] c Tensor of coefficients for the function at the function node given by key
6367 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
6368 /// @return Returns the inner product over the domain of a single function node, no guarantee of accuracy.
6369 T inner_ext_node(keyT key, tensorT c, const std::shared_ptr< FunctionFunctorInterface<T,NDIM> > f) const {
6370 tensorT fvals = tensorT(this->cdata.vk);
6371 // Compute the value of the external function at the quadrature points.
6372 fcube(key, *(f), cdata.quad_x, fvals);
6373 // Convert quadrature point values to scaling coefficients.
6374 tensorT fc = tensorT(values2coeffs(key, fvals));
6375 // Return the inner product of the two functions' scaling coefficients.
6376 return c.trace_conj(fc);
6377 }
6378
6379 /// Call inner_ext_node recursively until convergence.
6380 /// @param[in] key Key of the function node on which to compute inner product (the domain of integration)
6381 /// @param[in] c coeffs for the function at the node given by key
6382 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
6383 /// @param[in] leaf_refine boolean switch to turn on/off refinement past leaf nodes
6384 /// @param[in] old_inner the inner product on the parent function node
6385 /// @return Returns the inner product over the domain of a single function, checks for convergence.
6386 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 {
6387 int i = 0;
6388 tensorT c_child, inner_child;
6389 T new_inner, result = 0.0;
6390
6391 c_child = tensorT(cdata.v2k); // tensor of child coeffs
6392 inner_child = Tensor<double>(pow(2, NDIM)); // child inner products
6393
6394 // If old_inner is default value, assume this is the first call
6395 // and compute inner product on this node.
6396 if (old_inner == T(0)) {
6397 old_inner = inner_ext_node(key, c, f);
6398 }
6399
6400 if (coeffs.find(key).get()->second.has_children()) {
6401 // Since the key has children and we know the func is redundant,
6402 // Iterate over all children of this compute node, computing
6403 // the inner product on each child node. new_inner will store
6404 // the sum of these, yielding a more accurate inner product.
6405 for (KeyChildIterator<NDIM> it(key); it; ++it, ++i) {
6406 const keyT& child = it.key();
6407 tensorT cc = coeffs.find(child).get()->second.coeff().full_tensor_copy();
6408 inner_child(i) = inner_ext_node(child, cc, f);
6409 }
6410 new_inner = inner_child.sum();
6411 } else if (leaf_refine) {
6412 // We need the scaling coefficients of the numerical function
6413 // at each of the children nodes. We can't use project because
6414 // there is no guarantee that the numerical function will have
6415 // a functor. Instead, since we know we are at or below the
6416 // leaf nodes, the wavelet coefficients are zero (to within the
6417 // truncate tolerance). Thus, we can use unfilter() to
6418 // get the scaling coefficients at the next level.
6419 tensorT d = tensorT(cdata.v2k);
6420 d = T(0);
6421 d(cdata.s0) = copy(c);
6422 c_child = unfilter(d);
6423
6424 // Iterate over all children of this compute node, computing
6425 // the inner product on each child node. new_inner will store
6426 // the sum of these, yielding a more accurate inner product.
6427 for (KeyChildIterator<NDIM> it(key); it; ++it, ++i) {
6428 const keyT& child = it.key();
6429 tensorT cc = tensorT(c_child(child_patch(child)));
6430 inner_child(i) = inner_ext_node(child, cc, f);
6431 }
6432 new_inner = inner_child.sum();
6433 } else {
6434 // If we get to here, we are at the leaf nodes and the user has
6435 // specified that they do not want refinement past leaf nodes.
6436 new_inner = old_inner;
6437 }
6438
6439 // Check for convergence. If converged...yay, we're done. If not,
6440 // call inner_ext_node_recursive on each child node and accumulate
6441 // the inner product in result.
6442 // if (std::abs(new_inner - old_inner) <= truncate_tol(thresh, key)) {
6443 if (std::abs(new_inner - old_inner) <= thresh) {
6444 result = new_inner;
6445 } else {
6446 i = 0;
6447 for (KeyChildIterator<NDIM> it(key); it; ++it, ++i) {
6448 const keyT& child = it.key();
6449 tensorT cc = tensorT(c_child(child_patch(child)));
6450 result += inner_ext_recursive(child, cc, f, leaf_refine, inner_child(i));
6451 }
6452 }
6453
6454 return result;
6455 }
6456
6458 const std::shared_ptr< FunctionFunctorInterface<T, NDIM> > fref;
6459 const implT * impl;
6460 const bool leaf_refine;
6461 const bool do_leaves; ///< start with leaf nodes instead of initial_level
6462
6464 const implT * impl, const bool leaf_refine, const bool do_leaves)
6465 : fref(f), impl(impl), leaf_refine(leaf_refine), do_leaves(do_leaves) {};
6466
6467 T operator()(typename dcT::const_iterator& it) const {
6468 if (do_leaves and it->second.is_leaf()) {
6469 tensorT cc = it->second.coeff().full_tensor();
6470 return impl->inner_adaptive_recursive(it->first, cc, fref, leaf_refine, T(0));
6471 } else if ((not do_leaves) and (it->first.level() == impl->initial_level)) {
6472 tensorT cc = it->second.coeff().full_tensor();
6473 return impl->inner_ext_recursive(it->first, cc, fref, leaf_refine, T(0));
6474 } else {
6475 return 0.0;
6476 }
6477 }
6478
6479 T operator()(T a, T b) const {
6480 return (a + b);
6481 }
6482
6483 template <typename Archive> void serialize(const Archive& ar) {
6484 MADNESS_EXCEPTION("NOT IMPLEMENTED", 1);
6485 }
6486 };
6487
6488 /// Return the local part of inner product with external function ... no communication.
6489 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
6490 /// @param[in] leaf_refine boolean switch to turn on/off refinement past leaf nodes
6491 /// @return Returns local part of the inner product, i.e. over the domain of all function nodes on this compute node.
6492 T inner_ext_local(const std::shared_ptr< FunctionFunctorInterface<T,NDIM> > f, const bool leaf_refine) const {
6494
6496 do_inner_ext_local_ffi(f, this, leaf_refine, false));
6497 }
6498
6499 /// Return the local part of inner product with external function ... no communication.
6500 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
6501 /// @param[in] leaf_refine boolean switch to turn on/off refinement past leaf nodes
6502 /// @return Returns local part of the inner product, i.e. over the domain of all function nodes on this compute node.
6503 T inner_adaptive_local(const std::shared_ptr< FunctionFunctorInterface<T,NDIM> > f, const bool leaf_refine) const {
6505
6507 do_inner_ext_local_ffi(f, this, leaf_refine, true));
6508 }
6509
6510 /// Call inner_ext_node recursively until convergence.
6511 /// @param[in] key Key of the function node on which to compute inner product (the domain of integration)
6512 /// @param[in] c coeffs for the function at the node given by key
6513 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
6514 /// @param[in] leaf_refine boolean switch to turn on/off refinement past leaf nodes
6515 /// @param[in] old_inner the inner product on the parent function node
6516 /// @return Returns the inner product over the domain of a single function, checks for convergence.
6518 const std::shared_ptr< FunctionFunctorInterface<T,NDIM> > f,
6519 const bool leaf_refine, T old_inner=T(0)) const {
6520
6521 // the inner product in the current node
6522 old_inner = inner_ext_node(key, c, f);
6523 T result=0.0;
6524
6525 // the inner product in the child nodes
6526
6527 // compute the sum coefficients of the MRA function
6528 tensorT d = tensorT(cdata.v2k);
6529 d = T(0);
6530 d(cdata.s0) = copy(c);
6531 tensorT c_child = unfilter(d);
6532
6533 // compute the inner product in the child nodes
6534 T new_inner=0.0; // child inner products
6535 for (KeyChildIterator<NDIM> it(key); it; ++it) {
6536 const keyT& child = it.key();
6537 tensorT cc = tensorT(c_child(child_patch(child)));
6538 new_inner+= inner_ext_node(child, cc, f);
6539 }
6540
6541 // continue recursion if needed
6542 const double tol=truncate_tol(thresh,key);
6543 if (leaf_refine and (std::abs(new_inner - old_inner) > tol)) {
6544 for (KeyChildIterator<NDIM> it(key); it; ++it) {
6545 const keyT& child = it.key();
6546 tensorT cc = tensorT(c_child(child_patch(child)));
6547 result += inner_adaptive_recursive(child, cc, f, leaf_refine, T(0));
6548 }
6549 } else {
6550 result = new_inner;
6551 }
6552 return result;
6553
6554 }
6555
6556
6557 /// Return the gaxpy product with an external function on a specified
6558 /// function node.
6559 /// @param[in] key Key of the function node on which to compute gaxpy
6560 /// @param[in] lc Tensor of coefficients for the function at the
6561 /// function node given by key
6562 /// @param[in] f Pointer to function of type T that takes coordT
6563 /// arguments. This is the externally provided function and
6564 /// the right argument of gaxpy.
6565 /// @param[in] alpha prefactor of c Tensor for gaxpy
6566 /// @param[in] beta prefactor of fcoeffs for gaxpy
6567 /// @return Returns coefficient tensor of the gaxpy product at specified
6568 /// key, no guarantee of accuracy.
6569 template <typename L>
6570 tensorT gaxpy_ext_node(keyT key, Tensor<L> lc, T (*f)(const coordT&), T alpha, T beta) const {
6571 // Compute the value of external function at the quadrature points.
6572 tensorT fvals = madness::fcube(key, f, cdata.quad_x);
6573 // Convert quadrature point values to scaling coefficients.
6574 tensorT fcoeffs = values2coeffs(key, fvals);
6575 // Return the inner product of the two functions' scaling coeffs.
6576 tensorT c2 = copy(lc);
6577 c2.gaxpy(alpha, fcoeffs, beta);
6578 return c2;
6579 }
6580
6581 /// Return out of place gaxpy using recursive descent.
6582 /// @param[in] key Key of the function node on which to compute gaxpy
6583 /// @param[in] left FunctionImpl, left argument of gaxpy
6584 /// @param[in] lcin coefficients of left at this node
6585 /// @param[in] c coefficients of gaxpy product at this node
6586 /// @param[in] f pointer to function of type T that takes coordT
6587 /// arguments. This is the externally provided function and
6588 /// the right argument of gaxpy.
6589 /// @param[in] alpha prefactor of left argument for gaxpy
6590 /// @param[in] beta prefactor of right argument for gaxpy
6591 /// @param[in] tol convergence tolerance...when the norm of the gaxpy's
6592 /// difference coefficients is less than tol, we are done.
6593 template <typename L>
6594 void gaxpy_ext_recursive(const keyT& key, const FunctionImpl<L,NDIM>* left,
6595 Tensor<L> lcin, tensorT c, T (*f)(const coordT&),
6596 T alpha, T beta, double tol, bool below_leaf) {
6597 typedef typename FunctionImpl<L,NDIM>::dcT::const_iterator literT;
6598
6599 // If we haven't yet reached the leaf level, check whether the
6600 // current key is a leaf node of left. If so, set below_leaf to true
6601 // and continue. If not, make this a parent, recur down, return.
6602 if (not below_leaf) {
6603 bool left_leaf = left->coeffs.find(key).get()->second.is_leaf();
6604 if (left_leaf) {
6605 below_leaf = true;
6606 } else {
6607 this->coeffs.replace(key, nodeT(coeffT(), true));
6608 for (KeyChildIterator<NDIM> it(key); it; ++it) {
6609 const keyT& child = it.key();
6610 woT::task(left->coeffs.owner(child), &implT:: template gaxpy_ext_recursive<L>,
6611 child, left, Tensor<L>(), tensorT(), f, alpha, beta, tol, below_leaf);
6612 }
6613 return;
6614 }
6615 }
6616
6617 // Compute left's coefficients if not provided
6618 Tensor<L> lc = lcin;
6619 if (lc.size() == 0) {
6620 literT it = left->coeffs.find(key).get();
6621 MADNESS_ASSERT(it != left->coeffs.end());
6622 if (it->second.has_coeff())
6623 lc = it->second.coeff().reconstruct_tensor();
6624 }
6625
6626 // Compute this node's coefficients if not provided in function call
6627 if (c.size() == 0) {
6628 c = gaxpy_ext_node(key, lc, f, alpha, beta);
6629 }
6630
6631 // We need the scaling coefficients of the numerical function at
6632 // each of the children nodes. We can't use project because there
6633 // is no guarantee that the numerical function will have a functor.
6634 // Instead, since we know we are at or below the leaf nodes, the
6635 // wavelet coefficients are zero (to within the truncate tolerance).
6636 // Thus, we can use unfilter() to get the scaling coefficients at
6637 // the next level.
6638 Tensor<L> lc_child = Tensor<L>(cdata.v2k); // left's child coeffs
6639 Tensor<L> ld = Tensor<L>(cdata.v2k);
6640 ld = L(0);
6641 ld(cdata.s0) = copy(lc);
6642 lc_child = unfilter(ld);
6643
6644 // Iterate over children of this node,
6645 // storing the gaxpy coeffs in c_child
6646 tensorT c_child = tensorT(cdata.v2k); // tensor of child coeffs
6647 for (KeyChildIterator<NDIM> it(key); it; ++it) {
6648 const keyT& child = it.key();
6649 tensorT lcoeff = tensorT(lc_child(child_patch(child)));
6650 c_child(child_patch(child)) = gaxpy_ext_node(child, lcoeff, f, alpha, beta);
6651 }
6652
6653 // Compute the difference coefficients to test for convergence.
6654 tensorT d = tensorT(cdata.v2k);
6655 d = filter(c_child);
6656 // Filter returns both s and d coefficients, so set scaling
6657 // coefficient part of d to 0 so that we take only the
6658 // norm of the difference coefficients.
6659 d(cdata.s0) = T(0);
6660 double dnorm = d.normf();
6661
6662 // Small d.normf means we've reached a good level of resolution
6663 // Store the coefficients and return.
6664 if (dnorm <= truncate_tol(tol,key)) {
6665 this->coeffs.replace(key, nodeT(coeffT(c,targs), false));
6666 } else {
6667 // Otherwise, make this a parent node and recur down
6668 this->coeffs.replace(key, nodeT(coeffT(), true)); // Interior node
6669
6670 for (KeyChildIterator<NDIM> it(key); it; ++it) {
6671 const keyT& child = it.key();
6672 tensorT child_coeff = tensorT(c_child(child_patch(child)));
6673 tensorT left_coeff = tensorT(lc_child(child_patch(child)));
6674 woT::task(left->coeffs.owner(child), &implT:: template gaxpy_ext_recursive<L>,
6675 child, left, left_coeff, child_coeff, f, alpha, beta, tol, below_leaf);
6676 }
6677 }
6678 }
6679
6680 template <typename L>
6681 void gaxpy_ext(const FunctionImpl<L,NDIM>* left, T (*f)(const coordT&), T alpha, T beta, double tol, bool fence) {
6682 if (world.rank() == coeffs.owner(cdata.key0))
6683 gaxpy_ext_recursive<L> (cdata.key0, left, Tensor<L>(), tensorT(), f, alpha, beta, tol, false);
6684 if (fence)
6685 world.gop.fence();
6686 }
6687
6688 /// project the low-dim function g on the hi-dim function f: result(x) = <this(x,y) | g(y)>
6689
6690 /// invoked by the hi-dim function, a function of NDIM+LDIM
6691
6692 /// Upon return, result matches this, with contributions on all scales
6693 /// @param[in] result lo-dim function of NDIM-LDIM \todo Should this be param[out]?
6694 /// @param[in] gimpl lo-dim function of LDIM
6695 /// @param[in] dim over which dimensions to be integrated: 0..LDIM or LDIM..LDIM+NDIM-1
6696 template<size_t LDIM>
6698 const int dim, const bool fence) {
6699
6700 const keyT& key0=cdata.key0;
6701
6702 if (world.rank() == coeffs.owner(key0)) {
6703
6704 // coeff_op will accumulate the result
6705 typedef project_out_op<LDIM> coeff_opT;
6706 coeff_opT coeff_op(this,result,CoeffTracker<T,LDIM>(gimpl),dim);
6707
6708 // don't do anything on this -- coeff_op will accumulate into result
6709 typedef noop<T,NDIM> apply_opT;
6710 apply_opT apply_op;
6711
6712 woT::task(world.rank(), &implT:: template forward_traverse<coeff_opT,apply_opT>,
6713 coeff_op, apply_op, cdata.key0);
6714
6715 }
6716 if (fence) world.gop.fence();
6717
6718 }
6719
6720
6721 /// project the low-dim function g on the hi-dim function f: result(x) = <f(x,y) | g(y)>
6722 template<size_t LDIM>
6724 bool randomize() const {return false;}
6725
6728 typedef FunctionImpl<T,NDIM-LDIM> implL1;
6729 typedef std::pair<bool,coeffT> argT;
6730
6731 const implT* fimpl; ///< the hi dim function f
6732 mutable implL1* result; ///< the low dim result function
6733 ctL iag; ///< the low dim function g
6734 int dim; ///< 0: project 0..LDIM-1, 1: project LDIM..NDIM-1
6735
6736 // ctor
6737 project_out_op() = default;
6738 project_out_op(const implT* fimpl, implL1* result, const ctL& iag, const int dim)
6739 : fimpl(fimpl), result(result), iag(iag), dim(dim) {}
6741 : fimpl(other.fimpl), result(other.result), iag(other.iag), dim(other.dim) {}
6742
6743
6744 /// do the actual contraction
6746
6747 Key<LDIM> key1,key2,dest;
6748 key.break_apart(key1,key2);
6749
6750 // make the right coefficients
6751 coeffT gcoeff;
6752 if (dim==0) {
6753 gcoeff=iag.get_impl()->parent_to_child(iag.coeff(),iag.key(),key1);
6754 dest=key2;
6755 }
6756 if (dim==1) {
6757 gcoeff=iag.get_impl()->parent_to_child(iag.coeff(),iag.key(),key2);
6758 dest=key1;
6759 }
6760
6761 MADNESS_ASSERT(fimpl->get_coeffs().probe(key)); // must be local!
6762 const nodeT& fnode=fimpl->get_coeffs().find(key).get()->second;
6763 const coeffT& fcoeff=fnode.coeff();
6764
6765 // fast return if possible
6766 if (fcoeff.has_no_data() or gcoeff.has_no_data())
6767 return Future<argT> (argT(fnode.is_leaf(),coeffT()));;
6768
6769 MADNESS_CHECK(gcoeff.is_full_tensor());
6770 tensorT final(result->cdata.vk);
6771 const int k=fcoeff.dim(0);
6772 const int k_ldim=std::pow(k,LDIM);
6773 std::vector<long> shape(LDIM, k);
6774
6775 if (fcoeff.is_full_tensor()) {
6776 // result_i = \sum_j g_j f_ji
6777 const tensorT gtensor = gcoeff.full_tensor().reshape(k_ldim);
6778 const tensorT ftensor = fcoeff.full_tensor().reshape(k_ldim,k_ldim);
6779 final=inner(gtensor,ftensor,0,dim).reshape(shape);
6780
6781 } else if (fcoeff.is_svd_tensor()) {
6782 if (fcoeff.rank()>0) {
6783
6784 // result_i = \sum_jr g_j a_rj w_r b_ri
6785 const int otherdim = (dim + 1) % 2;
6786 const tensorT gtensor = gcoeff.full_tensor().flat();
6787 const tensorT atensor = fcoeff.get_svdtensor().flat_vector(dim); // a_rj
6788 const tensorT btensor = fcoeff.get_svdtensor().flat_vector(otherdim);
6789 const tensorT gatensor = inner(gtensor, atensor, 0, 1); // ga_r
6790 tensorT weights = copy(fcoeff.get_svdtensor().weights_);
6791 weights.emul(gatensor); // ga_r * w_r
6792 // sum over all ranks of b, include new weights:
6793 // result_i = \sum_r ga_r * w_r * b_ri
6794 for (int r = 0; r < fcoeff.rank(); ++r) final += weights(r) * btensor(r, _);
6795 final = final.reshape(shape);
6796 }
6797
6798 } else {
6799 MADNESS_EXCEPTION("unsupported tensor type in project_out_op",1);
6800 }
6801
6802 // accumulate the result
6803 result->coeffs.task(dest, &FunctionNode<T,LDIM>::accumulate2, final, result->coeffs, dest, TaskAttributes::hipri());
6804
6805 return Future<argT> (argT(fnode.is_leaf(),coeffT()));
6806 }
6807
6808 this_type make_child(const keyT& child) const {
6809 Key<LDIM> key1,key2;
6810 child.break_apart(key1,key2);
6811 const Key<LDIM> gkey = (dim==0) ? key1 : key2;
6812
6813 return this_type(fimpl,result,iag.make_child(gkey),dim);
6814 }
6815
6816 /// retrieve the coefficients (parent coeffs might be remote)
6819 return result->world.taskq.add(detail::wrap_mem_fn(*const_cast<this_type *> (this),
6820 &this_type::forward_ctor),fimpl,result,g1,dim);
6821 }
6822
6823 /// taskq-compatible ctor
6824 this_type forward_ctor(const implT* fimpl1, implL1* result1, const ctL& iag1, const int dim1) {
6825 return this_type(fimpl1,result1,iag1,dim1);
6826 }
6827
6828 template <typename Archive> void serialize(const Archive& ar) {
6829 ar & result & iag & fimpl & dim;
6830 }
6831
6832 };
6833
6834
6835 /// project the low-dim function g on the hi-dim function f: this(x) = <f(x,y) | g(y)>
6836
6837 /// invoked by result, a function of NDIM
6838
6839 /// @param[in] f hi-dim function of LDIM+NDIM
6840 /// @param[in] g lo-dim function of LDIM
6841 /// @param[in] dim over which dimensions to be integrated: 0..LDIM or LDIM..LDIM+NDIM-1
6842 template<size_t LDIM>
6843 void project_out2(const FunctionImpl<T,LDIM+NDIM>* f, const FunctionImpl<T,LDIM>* g, const int dim) {
6844
6845 typedef std::pair< keyT,coeffT > pairT;
6846 typedef typename FunctionImpl<T,NDIM+LDIM>::dcT::const_iterator fiterator;
6847
6848 // loop over all nodes of hi-dim f, compute the inner products with all
6849 // appropriate nodes of g, and accumulate in result
6850 fiterator end = f->get_coeffs().end();
6851 for (fiterator it=f->get_coeffs().begin(); it!=end; ++it) {
6852 const Key<LDIM+NDIM> key=it->first;
6853 const FunctionNode<T,LDIM+NDIM> fnode=it->second;
6854 const coeffT& fcoeff=fnode.coeff();
6855
6856 if (fnode.is_leaf() and fcoeff.has_data()) {
6857
6858 // break key into particle: over key1 will be summed, over key2 will be
6859 // accumulated, or vice versa, depending on dim
6860 if (dim==0) {
6861 Key<NDIM> key1;
6862 Key<LDIM> key2;
6863 key.break_apart(key1,key2);
6864
6865 Future<pairT> result;
6866 // sock_it_to_me(key1, result.remote_ref(world));
6867 g->task(coeffs.owner(key1), &implT::sock_it_to_me, key1, result.remote_ref(world), TaskAttributes::hipri());
6868 woT::task(world.rank(),&implT:: template do_project_out<LDIM>,fcoeff,result,key1,key2,dim);
6869
6870 } else if (dim==1) {
6871 Key<LDIM> key1;
6872 Key<NDIM> key2;
6873 key.break_apart(key1,key2);
6874
6875 Future<pairT> result;
6876 // sock_it_to_me(key2, result.remote_ref(world));
6877 g->task(coeffs.owner(key2), &implT::sock_it_to_me, key2, result.remote_ref(world), TaskAttributes::hipri());
6878 woT::task(world.rank(),&implT:: template do_project_out<LDIM>,fcoeff,result,key2,key1,dim);
6879
6880 } else {
6881 MADNESS_EXCEPTION("confused dim in project_out",1);
6882 }
6883 }
6884 }
6886// this->compressed=false;
6887// this->nonstandard=false;
6888// this->redundant=true;
6889 }
6890
6891
6892 /// compute the inner product of two nodes of only some dimensions and accumulate on result
6893
6894 /// invoked by result
6895 /// @param[in] fcoeff coefficients of high dimension LDIM+NDIM
6896 /// @param[in] gpair key and coeffs of low dimension LDIM (possibly a parent node)
6897 /// @param[in] gkey key of actual low dim node (possibly the same as gpair.first, iff gnode exists)
6898 /// @param[in] dest destination node for the result
6899 /// @param[in] dim which dimensions should be contracted: 0..LDIM-1 or LDIM..NDIM+LDIM-1
6900 template<size_t LDIM>
6901 void do_project_out(const coeffT& fcoeff, const std::pair<keyT,coeffT> gpair, const keyT& gkey,
6902 const Key<NDIM>& dest, const int dim) const {
6903
6904 const coeffT gcoeff=parent_to_child(gpair.second,gpair.first,gkey);
6905
6906 // fast return if possible
6907 if (fcoeff.has_no_data() or gcoeff.has_no_data()) return;
6908
6909 // let's specialize for the time being on SVD tensors for f and full tensors of half dim for g
6911 MADNESS_ASSERT(fcoeff.tensor_type()==TT_2D);
6912 const tensorT gtensor=gcoeff.full_tensor();
6913 tensorT result(cdata.vk);
6914
6915 const int otherdim=(dim+1)%2;
6916 const int k=fcoeff.dim(0);
6917 std::vector<Slice> s(fcoeff.config().dim_per_vector()+1,_);
6918
6919 // do the actual contraction
6920 for (int r=0; r<fcoeff.rank(); ++r) {
6921 s[0]=Slice(r,r);
6922 const tensorT contracted_tensor=fcoeff.config().ref_vector(dim)(s).reshape(k,k,k);
6923 const tensorT other_tensor=fcoeff.config().ref_vector(otherdim)(s).reshape(k,k,k);
6924 const double ovlp= gtensor.trace_conj(contracted_tensor);
6925 const double fac=ovlp * fcoeff.config().weights(r);
6926 result+=fac*other_tensor;
6927 }
6928
6929 // accumulate the result
6930 coeffs.task(dest, &nodeT::accumulate2, result, coeffs, dest, TaskAttributes::hipri());
6931 }
6932
6933
6934
6935
6936 /// Returns the maximum local depth of the tree ... no communications.
6937 std::size_t max_local_depth() const;
6938
6939
6940 /// Returns the maximum depth of the tree ... collective ... global sum/broadcast
6941 std::size_t max_depth() const;
6942
6943 /// Returns the max number of nodes on a processor
6944 std::size_t max_nodes() const;
6945
6946 /// Returns the min number of nodes on a processor
6947 std::size_t min_nodes() const;
6948
6949 /// Returns the size of the tree structure of the function ... collective global sum
6950 std::size_t tree_size() const;
6951
6952 /// Returns the number of coefficients in the function ... collective global sum
6953 std::size_t size() const;
6954
6955 /// Returns the number of coefficients in the function ... collective global sum
6956 std::size_t nCoeff() const;
6957
6958 /// Returns the number of coefficients in the function ... collective global sum
6959 std::size_t real_size() const;
6960
6961 /// print tree size and size
6962 void print_size(const std::string name) const;
6963
6964 /// print the number of configurations per node
6965 void print_stats() const;
6966
6967 /// In-place scale by a constant
6968 void scale_inplace(const T q, bool fence);
6969
6970 /// Out-of-place scale by a constant
6971 template <typename Q, typename F>
6972 void scale_oop(const Q q, const FunctionImpl<F,NDIM>& f, bool fence) {
6973 typedef typename FunctionImpl<F,NDIM>::nodeT fnodeT;
6974 typedef typename FunctionImpl<F,NDIM>::dcT fdcT;
6975 typename fdcT::const_iterator end = f.coeffs.end();
6976 for (typename fdcT::const_iterator it=f.coeffs.begin(); it!=end; ++it) {
6977 const keyT& key = it->first;
6978 const fnodeT& node = it->second;
6979
6980 if (node.has_coeff()) {
6981 coeffs.replace(key,nodeT(node.coeff()*q,node.has_children()));
6982 }
6983 else {
6984 coeffs.replace(key,nodeT(coeffT(),node.has_children()));
6985 }
6986 }
6987 if (fence)
6988 world.gop.fence();
6989 }
6990
6991 /// Hash a pointer to \c FunctionImpl
6992
6993 /// \param[in] impl pointer to a FunctionImpl
6994 /// \return The hash.
6995 inline friend hashT hash_value(const FunctionImpl<T,NDIM>* pimpl) {
6996 hashT seed = hash_value(pimpl->id().get_world_id());
6997 detail::combine_hash(seed, hash_value(pimpl->id().get_obj_id()));
6998 return seed;
6999 }
7000
7001 /// Hash a shared_ptr to \c FunctionImpl
7002
7003 /// \param[in] impl pointer to a FunctionImpl
7004 /// \return The hash.
7005 inline friend hashT hash_value(const std::shared_ptr<FunctionImpl<T,NDIM>> impl) {
7006 return hash_value(impl.get());
7007 }
7008 };
7009
7010 namespace archive {
7011 template <class Archive, class T, std::size_t NDIM>
7012 struct ArchiveLoadImpl<Archive,const FunctionImpl<T,NDIM>*> {
7013 static void load(const Archive& ar, const FunctionImpl<T,NDIM>*& ptr) {
7014 bool exists=false;
7015 ar & exists;
7016 if (exists) {
7017 uniqueidT id;
7018 ar & id;
7019 World* world = World::world_from_id(id.get_world_id());
7020 MADNESS_ASSERT(world);
7021 auto ptr_opt = world->ptr_from_id< WorldObject< FunctionImpl<T,NDIM> > >(id);
7022 if (!ptr_opt)
7023 MADNESS_EXCEPTION("FunctionImpl: remote operation attempting to use a locally uninitialized object",0);
7024 ptr = static_cast< const FunctionImpl<T,NDIM>*>(*ptr_opt);
7025 if (!ptr)
7026 MADNESS_EXCEPTION("FunctionImpl: remote operation attempting to use an unregistered object",0);
7027 } else {
7028 ptr=nullptr;
7029 }
7030 }
7031 };
7032
7033 template <class Archive, class T, std::size_t NDIM>
7034 struct ArchiveStoreImpl<Archive,const FunctionImpl<T,NDIM>*> {
7035 static void store(const Archive& ar, const FunctionImpl<T,NDIM>*const& ptr) {
7036 bool exists=(ptr) ? true : false;
7037 ar & exists;
7038 if (exists) ar & ptr->id();
7039 }
7040 };
7041
7042 template <class Archive, class T, std::size_t NDIM>
7043 struct ArchiveLoadImpl<Archive, FunctionImpl<T,NDIM>*> {
7044 static void load(const Archive& ar, FunctionImpl<T,NDIM>*& ptr) {
7045 bool exists=false;
7046 ar & exists;
7047 if (exists) {
7048 uniqueidT id;
7049 ar & id;
7050 World* world = World::world_from_id(id.get_world_id());
7051 MADNESS_ASSERT(world);
7052 auto ptr_opt = world->ptr_from_id< WorldObject< FunctionImpl<T,NDIM> > >(id);
7053 if (!ptr_opt)
7054 MADNESS_EXCEPTION("FunctionImpl: remote operation attempting to use a locally uninitialized object",0);
7055 ptr = static_cast< FunctionImpl<T,NDIM>*>(*ptr_opt);
7056 if (!ptr)
7057 MADNESS_EXCEPTION("FunctionImpl: remote operation attempting to use an unregistered object",0);
7058 } else {
7059 ptr=nullptr;
7060 }
7061 }
7062 };
7063
7064 template <class Archive, class T, std::size_t NDIM>
7066 static void store(const Archive& ar, FunctionImpl<T,NDIM>*const& ptr) {
7067 bool exists=(ptr) ? true : false;
7068 ar & exists;
7069 if (exists) ar & ptr->id();
7070 // ar & ptr->id();
7071 }
7072 };
7073
7074 template <class Archive, class T, std::size_t NDIM>
7075 struct ArchiveLoadImpl<Archive, std::shared_ptr<const FunctionImpl<T,NDIM> > > {
7076 static void load(const Archive& ar, std::shared_ptr<const FunctionImpl<T,NDIM> >& ptr) {
7077 const FunctionImpl<T,NDIM>* f = nullptr;
7079 ptr.reset(f, [] (const FunctionImpl<T,NDIM> *p_) -> void {});
7080 }
7081 };
7082
7083 template <class Archive, class T, std::size_t NDIM>
7084 struct ArchiveStoreImpl<Archive, std::shared_ptr<const FunctionImpl<T,NDIM> > > {
7085 static void store(const Archive& ar, const std::shared_ptr<const FunctionImpl<T,NDIM> >& ptr) {
7087 }
7088 };
7089
7090 template <class Archive, class T, std::size_t NDIM>
7091 struct ArchiveLoadImpl<Archive, std::shared_ptr<FunctionImpl<T,NDIM> > > {
7092 static void load(const Archive& ar, std::shared_ptr<FunctionImpl<T,NDIM> >& ptr) {
7093 FunctionImpl<T,NDIM>* f = nullptr;
7095 ptr.reset(f, [] (FunctionImpl<T,NDIM> *p_) -> void {});
7096 }
7097 };
7098
7099 template <class Archive, class T, std::size_t NDIM>
7100 struct ArchiveStoreImpl<Archive, std::shared_ptr<FunctionImpl<T,NDIM> > > {
7101 static void store(const Archive& ar, const std::shared_ptr<FunctionImpl<T,NDIM> >& ptr) {
7103 }
7104 };
7105 }
7106
7107}
7108
7109#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
a class to track where relevant (parent) coeffs are
Definition funcimpl.h:788
const keyT & key() const
const reference to the key
Definition funcimpl.h:836
CoeffTracker(const CoeffTracker &other, const datumT &datum)
ctor with a pair<keyT,nodeT>
Definition funcimpl.h:818
const LeafStatus & is_leaf() const
const reference to is_leaf flag
Definition funcimpl.h:860
const implT * impl
the funcimpl that has the coeffs
Definition funcimpl.h:797
LeafStatus
Definition funcimpl.h:794
@ yes
Definition funcimpl.h:794
@ no
Definition funcimpl.h:794
@ unknown
Definition funcimpl.h:794
CoeffTracker(const CoeffTracker &other)
copy ctor
Definition funcimpl.h:826
double dnorm(const keyT &key) const
return the s and dnorm belonging to the passed-in key
Definition funcimpl.h:853
coeffT coeff_
the coefficients belonging to key
Definition funcimpl.h:803
const implT * get_impl() const
const reference to impl
Definition funcimpl.h:830
const coeffT & coeff() const
const reference to the coeffs
Definition funcimpl.h:833
keyT key_
the current key, which must exists in impl
Definition funcimpl.h:799
double dnorm_
norm of d coefficients corresponding to key
Definition funcimpl.h:805
CoeffTracker(const implT *impl)
the initial ctor making the root key
Definition funcimpl.h:813
void serialize(const Archive &ar)
serialization
Definition funcimpl.h:912
Future< CoeffTracker > activate() const
find the coefficients
Definition funcimpl.h:889
CoeffTracker()
default ctor
Definition funcimpl.h:810
GenTensor< T > coeffT
Definition funcimpl.h:792
CoeffTracker make_child(const keyT &child) const
make a child of this, ignoring the coeffs
Definition funcimpl.h:863
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:790
std::pair< Key< NDIM >, ShallowNode< T, NDIM > > datumT
Definition funcimpl.h:793
CoeffTracker forward_ctor(const CoeffTracker &other, const datumT &datum) const
taskq-compatible forwarding to the ctor
Definition funcimpl.h:906
LeafStatus is_leaf_
flag if key is a leaf node
Definition funcimpl.h:801
coeffT coeff(const keyT &key) const
return the coefficients belonging to the passed-in key
Definition funcimpl.h:844
Key< NDIM > keyT
Definition funcimpl.h:791
CompositeFunctorInterface implements a wrapper of holding several functions and functors.
Definition function_interface.h:165
Definition worldhashmap.h:396
Tri-diagonal operator traversing tree primarily for derivative operator.
Definition derivative.h:73
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:88
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:204
static const double & get_thresh()
Returns the default threshold.
Definition funcdefaults.h:279
static int get_max_refine_level()
Gets the default maximum adaptive refinement level.
Definition funcdefaults.h:316
static const Tensor< double > & get_cell_width()
Returns the width of each user cell dimension.
Definition funcdefaults.h:468
static bool get_apply_randomize()
Gets the random load balancing for integral operators flag.
Definition funcdefaults.h:392
static const Tensor< double > & get_cell()
Gets the user cell for the simulation.
Definition funcdefaults.h:446
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
Definition funcimpl.h:5321
double operator()(double a, double b) const
Definition funcimpl.h:5347
const opT * func
Definition funcimpl.h:5323
Tensor< double > qx
Definition funcimpl.h:5325
double operator()(typename dcT::const_iterator &it) const
Definition funcimpl.h:5338
void serialize(const Archive &ar)
Definition funcimpl.h:5352
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:5331
int npt
Definition funcimpl.h:5324
Tensor< double > quad_phiw
Definition funcimpl.h:5327
const implT * impl
Definition funcimpl.h:5322
Tensor< double > quad_phit
Definition funcimpl.h:5326
do_err_box(const do_err_box &e)
Definition funcimpl.h:5335
FunctionImpl holds all Function state to facilitate shallow copy semantics.
Definition funcimpl.h:942
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:6025
void copy_coeffs(const FunctionImpl< Q, NDIM > &other, bool fence)
Copy coeffs from other into self.
Definition funcimpl.h:1113
bool is_nonstandard() const
Definition mraimpl.h:252
T eval_cube(Level n, coordT &x, const tensorT &c) const
Definition mraimpl.h:2055
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)
tensor contraction part of partial_inner
Definition funcimpl.h:6185
AtomicInt large
Definition funcimpl.h:998
Timer timer_target_driven
Definition funcimpl.h:996
void binaryXX(const FunctionImpl< L, NDIM > *left, const FunctionImpl< R, NDIM > *right, const opT &op, bool fence)
Definition funcimpl.h:3181
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:4771
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:2789
void add_keys_to_map(mapT *map, int index) const
Adds keys to union of local keys with specified index.
Definition funcimpl.h:5605
void change_tensor_type1(const TensorArgs &targs, bool fence)
change the tensor type of the coefficients in the FunctionNode
Definition mraimpl.h:1078
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:6594
int initial_level
Initial level for refinement.
Definition funcimpl.h:971
int max_refine_level
Do not refine below this level.
Definition funcimpl.h:974
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:4729
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:3693
void traverse_tree(const coeff_opT &coeff_op, const apply_opT &apply_op, const keyT &key) const
traverse a non-existing tree
Definition funcimpl.h:3663
void do_square_inplace(const keyT &key)
int special_level
Minimium level for refinement on special points.
Definition funcimpl.h:972
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:4663
double errsq_local(const opT &func) const
Returns the sum of squares of errors from local info ... no comms.
Definition funcimpl.h:5359
WorldContainer< keyT, nodeT > dcT
Type of container holding the coefficients.
Definition funcimpl.h:954
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:2970
void scale_inplace(const T q, bool fence)
In-place scale by a constant.
Definition mraimpl.h:3141
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:208
void unary_op_coeff_inplace(const opT &op, bool fence)
Definition funcimpl.h:2025
World & world
Definition funcimpl.h:961
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:3731
bool is_redundant() const
Returns true if the function is redundant.
Definition mraimpl.h:247
FunctionNode< T, NDIM > nodeT
Type of node.
Definition funcimpl.h:952
void print_size(const std::string name) const
print tree size and size
Definition mraimpl.h:1971
FunctionImpl(const FunctionImpl< T, NDIM > &p)
void print_info() const
Prints summary of data distribution.
Definition mraimpl.h:812
void abs_inplace(bool fence)
Definition mraimpl.h:3153
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:3050
void print_timer() const
Definition mraimpl.h:336
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:3012
const FunctionCommonData< T, NDIM > & cdata
Definition funcimpl.h:980
void do_print_grid(const std::string filename, const std::vector< keyT > &keys) const
print the grid in xyz format
Definition mraimpl.h:563
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:2964
const std::vector< Vector< double, NDIM > > & get_special_points() const
Definition funcimpl.h:966
std::size_t nCoeff() const
Returns the number of coefficients in the function ... collective global sum.
Definition mraimpl.h:1957
double vol_nsphere(int n, double R)
Definition funcimpl.h:4759
void compress(const TreeState newstate, bool fence)
compress the wave function
Definition mraimpl.h:1522
void do_dirac_convolution(FunctionImpl< T, LDIM > *f, bool fence) const
Definition funcimpl.h:2108
std::pair< coeffT, double > compress_op(const keyT &key, const std::vector< Future< std::pair< coeffT, double > > > &v, bool nonstandard)
calculate the wavelet coefficients using the sum coefficients of all child nodes
Definition mraimpl.h:1690
Future< bool > truncate_spawn(const keyT &key, double tol)
Returns true if after truncation this node has coefficients.
Definition mraimpl.h:2634
void print_type_in_compilation_error(R &&)
Definition funcimpl.h:5907
Future< double > norm_tree_spawn(const keyT &key)
Definition mraimpl.h:1592
std::vector< keyT > local_leaf_keys() const
return the keys of the local leaf boxes
Definition mraimpl.h:537
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:2707
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:2825
void unset_functor()
Definition mraimpl.h:291
void refine_spawn(const opT &op, const keyT &key)
Definition funcimpl.h:4491
void apply_1d_realspace_push(const opT &op, const FunctionImpl< R, NDIM > *f, int axis, bool fence)
Definition funcimpl.h:3782
static double conj(float x)
Definition funcimpl.h:5793
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:478
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:944
void set_functor(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > functor1)
Definition mraimpl.h:272
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:1559
void broaden(std::vector< bool > is_periodic, bool fence)
Definition mraimpl.h:1282
const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > & get_pmap() const
Definition mraimpl.h:192
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:1872
Timer timer_filter
Definition funcimpl.h:994
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:2847
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:5178
double get_thresh() const
Definition mraimpl.h:307
void trickle_down(bool fence)
sum all the contributions from all scales after applying an operator in mod-NS form
Definition mraimpl.h:1333
bool autorefine
If true, autorefine where appropriate.
Definition funcimpl.h:976
std::pair< coeffT, double > make_redundant_op(const keyT &key, const std::vector< Future< std::pair< coeffT, double > > > &v)
similar to compress_op, but insert only the sum coefficients in the tree
Definition mraimpl.h:1750
void set_autorefine(bool value)
Definition mraimpl.h:316
tensorT filter(const tensorT &s) const
Transform sum coefficients at level n to sums+differences at level n-1.
Definition mraimpl.h:1131
void chop_at_level(const int n, const bool fence=true)
remove all nodes with level higher than n
Definition mraimpl.h:1094
void unaryXXvalues(const FunctionImpl< Q, NDIM > *func, const opT &op, bool fence)
Definition funcimpl.h:3208
static std::complex< double > conj(const std::complex< double > x)
Definition funcimpl.h:5797
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:5923
TreeState tree_state
Definition funcimpl.h:983
void print_tree_json(std::ostream &os=std::cout, Level maxlevel=10000) const
Definition mraimpl.h:2727
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:686
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:1036
bool is_compressed() const
Returns true if the function is compressed.
Definition mraimpl.h:235
Vector< double, NDIM > coordT
Type of vector holding coordinates.
Definition funcimpl.h:956
void apply(opT &op, const FunctionImpl< R, NDIM > &f, bool fence)
apply an operator on f to return this
Definition funcimpl.h:4857
Tensor< T > tensorT
Type of tensor for anything but to hold coeffs.
Definition funcimpl.h:949
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:1045
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:6517
void store(Archive &ar)
Definition funcimpl.h:1237
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:1974
void gaxpy_ext(const FunctionImpl< L, NDIM > *left, T(*f)(const coordT &), T alpha, T beta, double tol, bool fence)
Definition funcimpl.h:6681
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:1161
void print_stats() const
print the number of configurations per node
Definition mraimpl.h:1999
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:1639
void refine_op(const opT &op, const keyT &key)
Definition funcimpl.h:4466
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:5803
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:2472
Timer timer_change_tensor_type
Definition funcimpl.h:992
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:902
std::vector< Slice > child_patch(const keyT &child) const
Returns patch referring to coeffs of child in parent box.
Definition mraimpl.h:675
void print_tree_graphviz(std::ostream &os=std::cout, Level maxlevel=10000) const
Definition mraimpl.h:2780
void set_tree_state(const TreeState &state)
Definition funcimpl.h:1265
std::size_t min_nodes() const
Returns the min number of nodes on a processor.
Definition mraimpl.h:1895
std::shared_ptr< FunctionFunctorInterface< T, NDIM > > functor
Definition funcimpl.h:982
Timer timer_compress_svd
Definition funcimpl.h:995
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:1947
void make_redundant(const bool fence)
convert this to redundant, i.e. have sum coefficients on all levels
Definition mraimpl.h:1550
void load(Archive &ar)
Definition funcimpl.h:1219
std::size_t max_nodes() const
Returns the max number of nodes on a processor.
Definition mraimpl.h:1886
T inner_ext_local(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > f, const bool leaf_refine) const
Definition funcimpl.h:6492
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:1210
TensorArgs targs
type of tensor to be used in the FunctionNodes
Definition funcimpl.h:978
void flo_unary_op_node_inplace(const opT &op, bool fence)
Definition funcimpl.h:2137
GenTensor< Q > values2coeffs(const keyT &key, const GenTensor< Q > &values) const
Definition funcimpl.h:1851
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:3319
T trace_local() const
Returns int(f(x),x) in local volume.
Definition mraimpl.h:3195
void print_grid(const std::string filename) const
Definition mraimpl.h:521
Future< std::pair< coeffT, double > > compress_spawn(const keyT &key, bool nonstandard, bool keepleaves, bool redundant1)
Invoked on node where key is local.
Definition mraimpl.h:3267
bool get_autorefine() const
Definition mraimpl.h:313
int k
Wavelet order.
Definition funcimpl.h:969
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:2669
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:3163
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:3254
TensorType get_tensor_type() const
Definition mraimpl.h:298
void do_project_out(const coeffT &fcoeff, const std::pair< keyT, coeffT > gpair, const keyT &gkey, const Key< NDIM > &dest, const int dim) const
compute the inner product of two nodes of only some dimensions and accumulate on result
Definition funcimpl.h:6901
void remove_leaf_coefficients(const bool fence)
Definition mraimpl.h:1544
void insert_zero_down_to_initial_level(const keyT &key)
Initialize nodes to zero function at initial_level of refinement.
Definition mraimpl.h:2603
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:913
typedef TENSOR_RESULT_TYPE(T, R) resultT
void unary_op_node_inplace(const opT &op, bool fence)
Definition funcimpl.h:2046
T inner_adaptive_local(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > f, const bool leaf_refine) const
Definition funcimpl.h:6503
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:2758
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:6078
std::shared_ptr< FunctionImpl< T, NDIM > > pimplT
pointer to this class
Definition funcimpl.h:948
void finalize_sum()
after summing up we need to do some cleanup;
Definition mraimpl.h:1843
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:1452
dcT coeffs
The coefficients.
Definition funcimpl.h:985
bool exists_and_is_leaf(const keyT &key) const
Definition mraimpl.h:1254
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:4258
void unaryXX(const FunctionImpl< Q, NDIM > *func, const opT &op, bool fence)
Definition funcimpl.h:3195
std::vector< std::pair< int, const coeffT * > > mapvecT
Type of the entry in the map returned by make_key_vec_map.
Definition funcimpl.h:5599
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:6697
void verify_tree() const
Verify tree is properly constructed ... global synchronization involved.
Definition mraimpl.h:109
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:1130
void set_tensor_args(const TensorArgs &t)
Definition mraimpl.h:304
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:1900
Range< typename dcT::const_iterator > rangeT
Definition funcimpl.h:5450
std::size_t real_size() const
Returns the number of coefficients in the function ... collective global sum.
Definition mraimpl.h:1944
bool exists_and_has_children(const keyT &key) const
Definition mraimpl.h:1249
void sum_down_spawn(const keyT &key, const coeffT &s)
is this the same as trickle_down() ?
Definition mraimpl.h:855
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:2796
long box_interior[1000]
Definition funcimpl.h:3239
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:1770
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:1569
void gaxpy_inplace(const T &alpha, const FunctionImpl< Q, NDIM > &other, const R &beta, bool fence)
Definition funcimpl.h:1206
bool has_leaves() const
Definition mraimpl.h:267
void apply_source_driven(opT &op, const FunctionImpl< R, NDIM > &f, bool fence)
similar to apply, but for low rank coeffs
Definition funcimpl.h:5003
void distribute(std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > newmap) const
Definition funcimpl.h:1105
int get_special_level() const
Definition funcimpl.h:965
void reconstruct_op(const keyT &key, const coeffT &s, const bool accumulate_NS=true)
Definition mraimpl.h:2109
tensorT gaxpy_ext_node(keyT key, Tensor< L > lc, T(*f)(const coordT &), T alpha, T beta) const
Definition funcimpl.h:6570
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:3178
WorldObject< FunctionImpl< T, NDIM > > woT
Base class world object type.
Definition funcimpl.h:944
void undo_redundant(const bool fence)
convert this from redundant to standard reconstructed form
Definition mraimpl.h:1560
GenTensor< T > coeffT
Type of tensor used to hold coeffs.
Definition funcimpl.h:953
const keyT & key0() const
Returns cdata.key0.
Definition mraimpl.h:373
double finalize_apply()
after apply we need to do some cleanup;
Definition mraimpl.h:1800
bool leaves_only
Definition funcimpl.h:5455
friend hashT hash_value(const FunctionImpl< T, NDIM > *pimpl)
Hash a pointer to FunctionImpl.
Definition funcimpl.h:6995
const dcT & get_coeffs() const
Definition mraimpl.h:322
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:6369
double norm2sq_local() const
Returns the square of the local norm ... no comms.
Definition mraimpl.h:1852
const FunctionCommonData< T, NDIM > & get_cdata() const
Definition mraimpl.h:328
void sum_down(bool fence)
After 1d push operator must sum coeffs down the tree to restore correct scaling function coefficients...
Definition mraimpl.h:894
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:6386
bool noautorefine(const keyT &key, const tensorT &t) const
Always returns false (for when autorefine is not wanted)
Definition mraimpl.h:838
double truncate_tol(double tol, const keyT &key) const
Returns the truncation threshold according to truncate_method.
Definition mraimpl.h:628
void flo_unary_op_node_inplace(const opT &op, bool fence) const
Definition funcimpl.h:2147
bool autorefine_square_test(const keyT &key, const nodeT &t) const
Returns true if this block of coeffs needs autorefining.
Definition mraimpl.h:844
void erase(const Level &max_level)
truncate tree at a certain level
Definition mraimpl.h:718
void mulXX(const FunctionImpl< L, NDIM > *left, const FunctionImpl< R, NDIM > *right, double tol, bool fence)
Definition funcimpl.h:3167
void reconstruct(bool fence)
reconstruct this tree – respects fence
Definition mraimpl.h:1490
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:3555
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:992
static void tnorm(const tensorT &t, double *lo, double *hi)
Computes norm of low/high-order polyn. coeffs for autorefinement test.
Definition mraimpl.h:3055
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:2941
std::size_t max_depth() const
Returns the maximum depth of the tree ... collective ... global sum/broadcast.
Definition mraimpl.h:1878
std::size_t size() const
Returns the number of coefficients in the function ... collective global sum.
Definition mraimpl.h:1913
void reduce_rank(const double thresh, bool fence)
reduce the rank of the coefficients tensors
Definition mraimpl.h:1086
TreeState get_tree_state() const
Definition funcimpl.h:1269
void merge_trees(const T alpha, const FunctionImpl< Q, NDIM > &other, const R beta, const bool fence=true)
merge the trees of this and other, while multiplying them with the alpha or beta, resp
Definition funcimpl.h:1149
std::shared_ptr< FunctionFunctorInterface< T, NDIM > > get_functor()
Definition mraimpl.h:279
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:4894
tensorT unfilter(const tensorT &s) const
Transform sums+differences at level n to sum coefficients at level n+1.
Definition mraimpl.h:1160
int get_initial_level() const
getter
Definition funcimpl.h:964
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:3412
virtual ~FunctionImpl()
Definition funcimpl.h:1097
Vector< Translation, NDIM > tranT
Type of array holding translation.
Definition funcimpl.h:950
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:1386
Future< coeffT > truncate_reconstructed_spawn(const keyT &key, const double tol)
truncate using a tree in reconstructed form
Definition mraimpl.h:1615
GenTensor< Q > coeffs2values(const keyT &key, const GenTensor< Q > &coeff) const
Definition funcimpl.h:1718
FunctionImpl(const FunctionFactory< T, NDIM > &factory)
Initialize function impl from data in factory.
Definition funcimpl.h:1001
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:1055
Timer timer_lr_result
Definition funcimpl.h:993
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:1997
void truncate(double tol, bool fence)
Truncate according to the threshold with optional global fence.
Definition mraimpl.h:357
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:1922
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:6843
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:4691
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:5855
Tensor< Q > coeffs2values(const keyT &key, const Tensor< Q > &coeff) const
Definition funcimpl.h:1844
Tensor< Q > values2coeffs(const keyT &key, const Tensor< Q > &values) const
Definition funcimpl.h:1858
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:2773
bool is_reconstructed() const
Returns true if the function is compressed.
Definition mraimpl.h:241
void replicate(bool fence=true)
Definition funcimpl.h:1101
double norm_tree_op(const keyT &key, const std::vector< Future< double > > &v)
Definition mraimpl.h:1577
void reset_timer()
Definition mraimpl.h:345
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:748
int get_k() const
Definition mraimpl.h:319
void dirac_convolution_op(const keyT &key, const nodeT &node, FunctionImpl< T, LDIM > *f) const
The operator.
Definition funcimpl.h:2063
FunctionImpl< T, NDIM > implT
Type of this class (implementation)
Definition funcimpl.h:947
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:2897
bool truncate_op(const keyT &key, double tol, const std::vector< Future< bool > > &v)
Definition mraimpl.h:2670
void zero_norm_tree()
Definition mraimpl.h:1271
std::size_t max_local_depth() const
Returns the maximum local depth of the tree ... no communications.
Definition mraimpl.h:1864
tensorT project(const keyT &key) const
Definition mraimpl.h:2815
double thresh
Screening threshold.
Definition funcimpl.h:970
double check_symmetry_local() const
Returns some asymmetry measure ... no comms.
Definition mraimpl.h:734
Future< double > get_norm_tree_recursive(const keyT &key) const
Definition mraimpl.h:2836
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:3224
Key< NDIM > keyT
Type of key.
Definition funcimpl.h:951
friend hashT hash_value(const std::shared_ptr< FunctionImpl< T, NDIM > > impl)
Hash a shared_ptr to FunctionImpl.
Definition funcimpl.h:7005
std::vector< Vector< double, NDIM > > special_points
special points for further refinement (needed for composite functions or multiplication)
Definition funcimpl.h:973
bool truncate_on_project
If true projection inserts at level n-1 not n.
Definition funcimpl.h:977
AtomicInt small
Definition funcimpl.h:997
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:5754
bool is_on_demand() const
Definition mraimpl.h:262
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:5291
void accumulate_timer(const double time) const
Definition mraimpl.h:331
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:1344
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:5674
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:2860
void set_thresh(double value)
Definition mraimpl.h:310
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:402
void sock_it_to_me_too(const keyT &key, const RemoteReference< FutureImpl< std::pair< keyT, coeffT > > > &ref) const
Definition mraimpl.h:2875
void broaden_op(const keyT &key, const std::vector< Future< bool > > &v)
Definition mraimpl.h:1260
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:382
void print_tree(std::ostream &os=std::cout, Level maxlevel=10000) const
Definition mraimpl.h:2698
void project_refine_op(const keyT &key, bool do_refine, const std::vector< Vector< double, NDIM > > &specialpts)
Definition mraimpl.h:2484
void scale_oop(const Q q, const FunctionImpl< F, NDIM > &f, bool fence)
Out-of-place scale by a constant.
Definition funcimpl.h:6972
T typeT
Definition funcimpl.h:946
std::size_t tree_size() const
Returns the size of the tree structure of the function ... collective global sum.
Definition mraimpl.h:1904
ConcurrentHashMap< keyT, mapvecT > mapT
Type of the map returned by make_key_vec_map.
Definition funcimpl.h:5602
void add_scalar_inplace(T t, bool fence)
Adds a constant to the function. Local operation, optional fence.
Definition mraimpl.h:2562
void forward_traverse(const coeff_opT &coeff_op, const apply_opT &apply_op, const keyT &key) const
traverse a non-existing tree
Definition funcimpl.h:3649
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:1180
void abs_square_inplace(bool fence)
Definition mraimpl.h:3158
FunctionImpl(const FunctionImpl< Q, NDIM > &other, const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > &pmap, bool dozero)
Copy constructor.
Definition funcimpl.h:1068
void refine(const opT &op, bool fence)
Definition funcimpl.h:4504
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:5623
void put_in_box(ProcessID from, long nl, long ni) const
Definition mraimpl.h:803
void unary_op_value_inplace(const opT &op, bool fence)
Definition funcimpl.h:2840
std::pair< const keyT, nodeT > datumT
Type of entry in container.
Definition funcimpl.h:955
Timer timer_accumulate
Definition funcimpl.h:991
TensorArgs get_tensor_args() const
Definition mraimpl.h:301
void unaryXXa(const keyT &key, const FunctionImpl< Q, NDIM > *func, const opT &op)
Definition funcimpl.h:3142
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:4319
void average(const implT &rhs)
take the average of two functions, similar to: this=0.5*(this+rhs)
Definition mraimpl.h:1067
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:5044
void diff(const DerivativeBase< T, NDIM > *D, const implT *f, bool fence)
Definition mraimpl.h:925
keyT neighbor(const keyT &key, const keyT &disp, const std::vector< bool > &is_periodic) const
Returns key of general neighbor enforcing BC.
Definition mraimpl.h:3237
void square_inplace(bool fence)
Pointwise squaring of function with optional global fence.
Definition mraimpl.h:3147
void remove_internal_coefficients(const bool fence)
Definition mraimpl.h:1539
void compute_snorm_and_dnorm(bool fence=true)
compute norm of s and d coefficients for all nodes
Definition mraimpl.h:1110
long box_leaf[1000]
Definition funcimpl.h:3238
void standard(bool fence)
Changes non-standard compressed form to standard compressed form.
Definition mraimpl.h:1787
void multiop_values_doit(const keyT &key, const opT &op, const std::vector< implT * > &v)
Definition funcimpl.h:2731
bool is_nonstandard_with_leaves() const
Definition mraimpl.h:257
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:1819
int truncate_mode
0=default=(|d|<thresh), 1=(|d|<thresh/2^n), 1=(|d|<thresh/4^n);
Definition funcimpl.h:975
void multiop_values(const opT &op, const std::vector< implT * > &v)
Definition funcimpl.h:2748
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:1734
FunctionNode holds the coefficients, etc., at each node of the 2^NDIM-tree.
Definition funcimpl.h:124
FunctionNode< Q, NDIM > convert() const
Copy with possible type conversion of coefficients, copying all other state.
Definition funcimpl.h:191
GenTensor< T > coeffT
Definition funcimpl.h:126
bool has_coeff() const
Returns true if there are coefficients in this node.
Definition funcimpl.h:197
void recompute_snorm_and_dnorm(const FunctionCommonData< T, NDIM > &cdata)
Definition funcimpl.h:332
FunctionNode(const coeffT &coeff, bool has_children=false)
Constructor from given coefficients with optional children.
Definition funcimpl.h:153
FunctionNode()
Default constructor makes node without coeff or children.
Definition funcimpl.h:143
void serialize(Archive &ar)
Definition funcimpl.h:455
void consolidate_buffer(const TensorArgs &args)
Definition funcimpl.h:441
double get_dnorm() const
return the precomputed norm of the (virtual) d coefficients
Definition funcimpl.h:313
size_t size() const
Returns the number of coefficients in this node.
Definition funcimpl.h:239
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:256
FunctionNode< T, NDIM > & operator=(const FunctionNode< T, NDIM > &other)
Definition funcimpl.h:173
double snorm
norm of the s coefficients
Definition funcimpl.h:138
void clear_coeff()
Clears the coefficients (has_coeff() will subsequently return false)
Definition funcimpl.h:292
Tensor< T > tensorT
Definition funcimpl.h:127
coeffT buffer
The coefficients, if any.
Definition funcimpl.h:136
T trace_conj(const FunctionNode< T, NDIM > &rhs) const
Definition funcimpl.h:450
void scale(Q a)
Scale the coefficients of this node.
Definition funcimpl.h:298
bool is_leaf() const
Returns true if this does not have children.
Definition funcimpl.h:210
void set_has_children(bool flag)
Sets has_children attribute to value of flag.
Definition funcimpl.h:251
void accumulate(const coeffT &t, const typename FunctionNode< T, NDIM >::dcT &c, const Key< NDIM > &key, const TensorArgs &args)
Accumulate inplace and if necessary connect node to parent.
Definition funcimpl.h:413
double get_norm_tree() const
Gets the value of norm_tree.
Definition funcimpl.h:308
bool _has_children
True if there are children.
Definition funcimpl.h:135
FunctionNode(const coeffT &coeff, double norm_tree, double snorm, double dnorm, bool has_children)
Definition funcimpl.h:163
void set_snorm(const double sn)
set the precomputed norm of the (virtual) s coefficients
Definition funcimpl.h:318
coeffT _coeffs
The coefficients, if any.
Definition funcimpl.h:133
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:380
void reduceRank(const double &eps)
reduces the rank of the coefficients (if applicable)
Definition funcimpl.h:246
WorldContainer< Key< NDIM >, FunctionNode< T, NDIM > > dcT
Definition funcimpl.h:141
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:362
double _norm_tree
After norm_tree will contain norm of coefficients summed up tree.
Definition funcimpl.h:134
void set_is_leaf(bool flag)
Sets has_children attribute to value of !flag.
Definition funcimpl.h:277
void print_json(std::ostream &s) const
Definition funcimpl.h:463
double get_snorm() const
get the precomputed norm of the (virtual) s coefficients
Definition funcimpl.h:328
const coeffT & coeff() const
Returns a const reference to the tensor containing the coeffs.
Definition funcimpl.h:234
FunctionNode(const coeffT &coeff, double norm_tree, bool has_children)
Definition funcimpl.h:158
bool has_children() const
Returns true if this node has children.
Definition funcimpl.h:204
void set_coeff(const coeffT &coeffs)
Takes a shallow copy of the coeff — same as this->coeff()=coeff.
Definition funcimpl.h:282
void set_dnorm(const double dn)
set the precomputed norm of the (virtual) d coefficients
Definition funcimpl.h:323
double dnorm
norm of the d coefficients, also defined if there are no d coefficients
Definition funcimpl.h:137
bool is_invalid() const
Returns true if this node is invalid (no coeffs and no children)
Definition funcimpl.h:216
FunctionNode(const FunctionNode< T, NDIM > &other)
Definition funcimpl.h:167
coeffT & coeff()
Returns a non-const reference to the tensor containing the coeffs.
Definition funcimpl.h:224
void set_norm_tree(double norm_tree)
Sets the value of norm_tree.
Definition funcimpl.h:303
Implements the functionality of futures.
Definition future.h:74
A future is a possibly yet unevaluated value.
Definition future.h:373
remote_refT remote_ref(World &world) const
Returns a structure used to pass references to another process.
Definition future.h:675
RemoteReference< FutureImpl< T > > remote_refT
Definition future.h:398
Definition lowranktensor.h:59
bool is_of_tensortype(const TensorType &tt) const
Definition gentensor.h:225
GenTensor convert(const TensorArgs &targs) const
Definition gentensor.h:198
GenTensor full_tensor() const
Definition gentensor.h:200
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
long ndim() const
Definition lowranktensor.h:386
void add_SVD(const GenTensor< T > &rhs, const double &eps)
Definition gentensor.h:235
constexpr bool is_full_tensor() const
Definition gentensor.h:224
GenTensor get_tensor() const
Definition gentensor.h:203
GenTensor reconstruct_tensor() const
Definition gentensor.h:199
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:631
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
void reduce_rank(const double &eps)
Definition gentensor.h:217
long rank() const
Definition gentensor.h:212
long size() const
Definition lowranktensor.h:482
SVDTensor< T > & get_svdtensor()
Definition gentensor.h:228
TensorType tensor_type() const
Definition gentensor.h:221
bool has_data() const
Definition gentensor.h:210
GenTensor & gaxpy(const T alpha, const GenTensor &other, const T beta)
Definition lowranktensor.h:580
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:426
constexpr bool is_svd_tensor() const
Definition gentensor.h:222
Iterates in lexical order thru all children of a key.
Definition key.h:374
Key is the index for a node of the 2^NDIM-tree.
Definition key.h:66
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:313
Level level() const
Definition key.h:159
bool is_valid() const
Checks if a key is valid.
Definition key.h:114
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:299
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:291
Key parent(int generation=1) const
Returns the key of the parent.
Definition key.h:187
const Vector< Translation, NDIM > & translation() const
Definition key.h:164
void break_apart(Key< LDIM > &key1, Key< KDIM > &key2) const
break key into two low-dimensional keys
Definition key.h:269
A pmap that locates children on odd levels with their even level parents.
Definition funcimpl.h:102
LevelPmap(World &world)
Definition funcimpl.h:108
const int nproc
Definition funcimpl.h:104
LevelPmap()
Definition funcimpl.h:106
ProcessID owner(const keyT &key) const
Find the owner of a given key.
Definition funcimpl.h:111
Definition funcimpl.h:74
Mutex using pthread mutex operations.
Definition worldmutex.h:131
void unlock() const
Free a mutex owned by this thread.
Definition worldmutex.h:165
void lock() const
Acquire the mutex waiting if necessary.
Definition worldmutex.h:155
Range, vaguely a la Intel TBB, to encapsulate a random-access, STL-like start and end iterator with c...
Definition range.h:64
Simple structure used to manage references/pointers to remote instances.
Definition worldref.h:395
Definition SVDTensor.h:42
A simple process map.
Definition funcimpl.h:83
SimplePmap(World &world)
Definition funcimpl.h:89
const int nproc
Definition funcimpl.h:85
const ProcessID me
Definition funcimpl.h:86
ProcessID owner(const keyT &key) const
Maps key to processor.
Definition funcimpl.h:92
A slice defines a sub-range or patch of a dimension.
Definition slice.h:103
static TaskAttributes hipri()
Definition thread.h:450
Traits class to specify support of numeric types.
Definition type_data.h:56
A tensor is a multidimension array.
Definition tensor.h:317
float_scalar_type normf() const
Returns the Frobenius norm of the tensor.
Definition tensor.h:1726
T sum() const
Returns the sum of all elements of the tensor.
Definition tensor.h:1662
Tensor< T > reshape(int ndimnew, const long *d)
Returns new view/tensor reshaping size/number of dimensions to conforming tensor.
Definition tensor.h:1384
T * ptr()
Returns a pointer to the internal data.
Definition tensor.h:1824
Tensor< T > mapdim(const std::vector< long > &map)
Returns new view/tensor permuting the dimensions.
Definition tensor.h:1624
IsSupported< TensorTypeData< Q >, Tensor< T > & >::type scale(Q x)
Inplace multiplication by scalar of supported type (legacy name)
Definition tensor.h:686
T trace(const Tensor< T > &t) const
Return the trace of two tensors (no complex conjugate invoked)
Definition tensor.h:1776
Tensor< T > & emul(const Tensor< T > &t)
Inplace multiply by corresponding elements of argument Tensor.
Definition tensor.h:1798
bool has_data() const
Definition tensor.h:1886
Tensor< T > fusedim(long i)
Returns new view/tensor fusing contiguous dimensions i and i+1.
Definition tensor.h:1587
Tensor< T > flat()
Returns new view/tensor rehshaping to flat (1-d) tensor.
Definition tensor.h:1555
Tensor< T > & gaxpy(T alpha, const Tensor< T > &t, T beta)
Inplace generalized saxpy ... this = this*alpha + other*beta.
Definition tensor.h:1804
Tensor< T > & conj()
Inplace complex conjugate.
Definition tensor.h:716
Definition function_common_data.h:169
void accumulate(const double time) const
accumulate timer
Definition function_common_data.h:183
A simple, fixed dimension vector.
Definition vector.h:64
Makes a distributed container with specified attributes.
Definition worlddc.h:866
void process_pending()
Process pending messages.
Definition worlddc.h:1166
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:987
bool probe(const keyT &key) const
Returns true if local data is immediately available (no communication)
Definition worlddc.h:1024
iterator begin()
Returns an iterator to the beginning of the local data (no communication)
Definition worlddc.h:1070
ProcessID owner(const keyT &key) const
Returns processor that logically owns key (no communication)
Definition worlddc.h:1034
implT::const_iterator const_iterator
Definition worlddc.h:872
void replicate(bool fence=true)
replicates this WorldContainer on all ProcessIDs
Definition worlddc.h:968
void replace(const pairT &datum)
Inserts/replaces key+value pair (non-blocking communication if key not local)
Definition worlddc.h:974
iterator end()
Returns an iterator past the end of the local data (no communication)
Definition worlddc.h:1084
const std::shared_ptr< WorldDCPmapInterface< keyT > > & get_pmap() const
Returns shared pointer to the process mapping.
Definition worlddc.h:1142
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:1001
implT::iterator iterator
Definition worlddc.h:871
std::size_t size() const
Returns the number of local entries (no communication)
Definition worlddc.h:1135
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:1426
bool is_local(const keyT &key) const
Returns true if the key maps to the local processor (no communication)
Definition worlddc.h:1041
Future< MEMFUN_RETURNT(memfunT)> send(const keyT &key, memfunT memfun)
Sends message "resultT memfun()" to item (non-blocking comm if remote)
Definition worlddc.h:1183
implT::accessor accessor
Definition worlddc.h:873
Interface to be provided by any process map.
Definition worlddc.h:82
void fence(bool debug=false)
Synchronizes all processes in communicator AND globally ensures no pending AM or tasks.
Definition worldgop.cc:161
Implements most parts of a globally addressable object (via unique ID).
Definition world_object.h:364
const uniqueidT & id() const
Returns the globally unique object ID.
Definition world_object.h:711
void process_pending()
To be called from derived constructor to process pending messages.
Definition world_object.h:656
ProcessID me
Rank of self.
Definition world_object.h:385
detail::task_result_type< memfnT >::futureT send(ProcessID dest, memfnT memfn) const
Definition world_object.h:731
detail::task_result_type< memfnT >::futureT task(ProcessID dest, memfnT memfn, const TaskAttributes &attr=TaskAttributes()) const
Sends task to derived class method returnT (this->*memfn)().
Definition world_object.h:1005
Future< bool > for_each(const rangeT &range, const opT &op)
Apply op(item) on all items in range.
Definition world_task_queue.h:572
void add(TaskInterface *t)
Add a new local task, taking ownership of the pointer.
Definition world_task_queue.h:466
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:132
static World * world_from_id(std::uint64_t id)
Convert a World ID to a World pointer.
Definition world.h:474
WorldTaskQueue & taskq
Task queue.
Definition world.h:204
ProcessID rank() const
Returns the process rank in this World (same as MPI_Comm_rank()).
Definition world.h:318
ProcessID size() const
Returns the number of processes in this World (same as MPI_Comm_size()).
Definition world.h:328
WorldGopInterface & gop
Global operations.
Definition world.h:205
std::optional< T * > ptr_from_id(uniqueidT id) const
Look up a local pointer from a world-wide unique ID.
Definition world.h:414
ProcessID random_proc()
Returns a random process number; that is, an integer in [0,world.size()).
Definition world.h:573
Wrapper for an opaque pointer for serialization purposes.
Definition archive.h:850
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.
auto T(World &world, response_space &f) -> response_space
Definition global_functions.cc:34
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:925
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:2502
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:2443
const double beta
Definition gygi_soltion.cc:62
static const double v
Definition hatom_sf_dirac.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.
const 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:248
Namespace for all elements and tools of MADNESS.
Definition DFParameters.h:10
std::ostream & operator<<(std::ostream &os, const particle< PDIM > &p)
Definition lowrankfunction.h:397
static const char * filename
Definition legendre.cc:96
static const std::vector< Slice > ___
Entire dimension.
Definition slice.h:128
static double cpu_time()
Returns the cpu time in seconds relative to an arbitrary origin.
Definition timers.h:127
GenTensor< TENSOR_RESULT_TYPE(R, Q)> general_transform(const GenTensor< R > &t, const Tensor< Q > c[])
Definition gentensor.h:274
response_space scale(response_space a, double b)
void finalize()
Call this once at the very end of your main program instead of MPI_Finalize().
Definition world.cc:232
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:1147
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:689
TreeState
Definition funcdefaults.h:58
@ nonstandard_after_apply
s and d coeffs, state after operator application
Definition funcdefaults.h:63
@ redundant_after_merge
s coeffs everywhere, must be summed up to yield the result
Definition funcdefaults.h:65
@ reconstructed
s coeffs at the leaves only
Definition funcdefaults.h:59
@ nonstandard
s and d coeffs in internal nodes
Definition funcdefaults.h:61
@ redundant
s coeffs everywhere
Definition funcdefaults.h:64
static Tensor< double > weights[max_npt+1]
Definition legendre.cc:99
int64_t Translation
Definition key.h:54
static const Slice _(0,-1, 1)
std::shared_ptr< FunctionFunctorInterface< double, 3 > > func(new opT(g))
void change_tensor_type(GenTensor< T > &t, const TensorArgs &targs)
change representation to targ.tt
Definition gentensor.h:284
int Level
Definition key.h:55
std::enable_if< std::is_base_of< ProjectorBase, projT >::value, OuterProjector< projT, projQ > >::type outer(const projT &p0, const projQ &p1)
Definition projector.h:457
int RandomValue< int >()
Random int.
Definition ran.cc:250
static double pop(std::vector< double > &v)
Definition SCF.cc:113
void print(const T &t, const Ts &... ts)
Print items to std::cout (items separated by spaces) and terminate with a new line.
Definition print.h:225
Tensor< T > fcube(const Key< NDIM > &, T(*f)(const Vector< double, NDIM > &), const Tensor< double > &)
Definition mraimpl.h:2163
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:2416
void error(const char *msg)
Definition world.cc:139
NDIM const Function< R, NDIM > & g
Definition mra.h:2416
std::size_t hashT
The hash value type.
Definition worldhash.h:145
static const int kmax
Definition twoscale.cc:52
double inner(response_space &a, response_space &b)
Definition response_functions.h:442
GenTensor< TENSOR_RESULT_TYPE(R, Q)> transform_dir(const GenTensor< R > &t, const Tensor< Q > &c, const int axis)
Definition lowranktensor.h:1099
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:225
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:2002
static const int MAXK
The maximum wavelet order presently supported.
Definition funcdefaults.h:51
Definition mraimpl.h:50
static long abs(long a)
Definition tensor.h:218
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
static const double thresh
Definition rk.cc:45
Definition test_ar.cc:204
Definition test_ccpairfunction.cc:22
given a ket and the 1- and 2-electron potentials, construct the function V phi
Definition funcimpl.h:3979
implT * result
where to construct Vphi, no need to track parents
Definition funcimpl.h:3987
bool have_v2() const
Definition funcimpl.h:3996
ctL iav1
Definition funcimpl.h:3991
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:4005
ctL iap1
Definition funcimpl.h:3990
bool have_v1() const
Definition funcimpl.h:3995
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:4071
opT leaf_op
deciding if a given FunctionNode will be a leaf node
Definition funcimpl.h:3988
std::pair< coeffT, double > make_sum_coeffs(const keyT &key) const
make the sum coeffs for key
Definition funcimpl.h:4164
CoeffTracker< T, NDIM > ctT
Definition funcimpl.h:3984
ctL iap2
the particles 1 and 2 (exclusive with ket)
Definition funcimpl.h:3990
bool have_ket() const
Definition funcimpl.h:3994
const implT * eri
2-particle potential, must be on-demand
Definition funcimpl.h:3992
CoeffTracker< T, LDIM > ctL
Definition funcimpl.h:3985
std::pair< bool, coeffT > operator()(const Key< NDIM > &key) const
make and insert the coefficients into result's tree
Definition funcimpl.h:4016
void serialize(const Archive &ar)
serialize this (needed for use in recursive_op)
Definition funcimpl.h:4245
Vphi_op_NS< opT, LDIM > this_type
Definition funcimpl.h:3983
ctT iaket
the ket of a pair function (exclusive with p1, p2)
Definition funcimpl.h:3989
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:4117
void accumulate_into_result(const Key< NDIM > &key, const coeffT &coeff) const
Definition funcimpl.h:3999
this_type make_child(const keyT &child) const
Definition funcimpl.h:4216
tensorT eri_coeffs(const keyT &key) const
Definition funcimpl.h:4097
ctL iav2
potentials for particles 1 and 2
Definition funcimpl.h:3991
bool have_eri() const
Definition funcimpl.h:3997
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:4238
Vphi_op_NS()
Definition funcimpl.h:4004
Future< this_type > activate() const
Definition funcimpl.h:4227
bool randomize() const
Definition funcimpl.h:3981
add two functions f and g: result=alpha * f + beta * g
Definition funcimpl.h:3494
bool randomize() const
Definition funcimpl.h:3499
Future< this_type > activate() const
retrieve the coefficients (parent coeffs might be remote)
Definition funcimpl.h:3529
add_op(const ctT &f, const ctT &g, const double alpha, const double beta)
Definition funcimpl.h:3507
ctT f
tracking coeffs of first and second addend
Definition funcimpl.h:3502
double alpha
prefactor for f, g
Definition funcimpl.h:3504
add_op this_type
Definition funcimpl.h:3497
CoeffTracker< T, NDIM > ctT
Definition funcimpl.h:3496
void serialize(const Archive &ar)
Definition funcimpl.h:3541
ctT g
Definition funcimpl.h:3502
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:3511
double beta
Definition funcimpl.h:3504
this_type make_child(const keyT &child) const
Definition funcimpl.h:3524
this_type forward_ctor(const ctT &f1, const ctT &g1, const double alpha, const double beta)
taskq-compatible ctor
Definition funcimpl.h:3537
opT op
Definition funcimpl.h:3112
opT::resultT resultT
Definition funcimpl.h:3110
Tensor< resultT > operator()(const Key< NDIM > &key, const Tensor< Q > &t) const
Definition funcimpl.h:3119
coeff_value_adaptor(const FunctionImpl< Q, NDIM > *impl_func, const opT &op)
Definition funcimpl.h:3115
const FunctionImpl< Q, NDIM > * impl_func
Definition funcimpl.h:3111
void serialize(Archive &ar)
Definition funcimpl.h:3128
merge the coefficent boxes of this into result's tree
Definition funcimpl.h:2351
Range< typename dcT::const_iterator > rangeT
Definition funcimpl.h:2352
void serialize(const Archive &ar)
Definition funcimpl.h:2369
FunctionImpl< Q, NDIM > * result
Definition funcimpl.h:2353
do_accumulate_trees(FunctionImpl< Q, NDIM > &result, const T alpha)
Definition funcimpl.h:2356
T alpha
Definition funcimpl.h:2354
bool operator()(typename rangeT::iterator &it) const
return the norm of the difference of this node and its "mirror" node
Definition funcimpl.h:2360
"put" this on g
Definition funcimpl.h:2562
Range< typename dcT::const_iterator > rangeT
Definition funcimpl.h:2563
void serialize(const Archive &ar)
Definition funcimpl.h:2591
implT * g
Definition funcimpl.h:2565
do_average()
Definition funcimpl.h:2567
bool operator()(typename rangeT::iterator &it) const
iterator it points to this
Definition funcimpl.h:2571
do_average(implT &g)
Definition funcimpl.h:2568
change representation of nodes' coeffs to low rank, optional fence
Definition funcimpl.h:2595
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2596
void serialize(const Archive &ar)
Definition funcimpl.h:2619
TensorArgs targs
Definition funcimpl.h:2599
do_change_tensor_type(const TensorArgs &targs, implT &g)
Definition funcimpl.h:2605
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2608
implT * f
Definition funcimpl.h:2600
check symmetry wrt particle exchange
Definition funcimpl.h:2268
Range< typename dcT::const_iterator > rangeT
Definition funcimpl.h:2269
double operator()(typename rangeT::iterator &it) const
return the norm of the difference of this node and its "mirror" node
Definition funcimpl.h:2275
do_check_symmetry_local()
Definition funcimpl.h:2271
void serialize(const Archive &ar)
Definition funcimpl.h:2338
double operator()(double a, double b) const
Definition funcimpl.h:2334
do_check_symmetry_local(const implT &f)
Definition funcimpl.h:2272
const implT * f
Definition funcimpl.h:2270
compute the norm of the wavelet coefficients
Definition funcimpl.h:4386
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:4387
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:4393
do_compute_snorm_and_dnorm(const FunctionCommonData< T, NDIM > &cdata)
Definition funcimpl.h:4390
const FunctionCommonData< T, NDIM > & cdata
Definition funcimpl.h:4389
TensorArgs targs
Definition funcimpl.h:2626
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2631
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2623
do_consolidate_buffer(const TensorArgs &targs)
Definition funcimpl.h:2630
void serialize(const Archive &ar)
Definition funcimpl.h:2635
double operator()(double val) const
Definition funcimpl.h:1390
double limit
Definition funcimpl.h:1385
do_convert_to_color(const double limit, const bool log)
Definition funcimpl.h:1389
bool log
Definition funcimpl.h:1386
static double lower()
Definition funcimpl.h:1387
functor for the gaxpy_inplace method
Definition funcimpl.h:1181
FunctionImpl< T, NDIM > * f
prefactor for current function impl
Definition funcimpl.h:1183
do_gaxpy_inplace(FunctionImpl< T, NDIM > *f, T alpha, R beta)
Definition funcimpl.h:1187
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:1188
R beta
prefactor for other function impl
Definition funcimpl.h:1185
void serialize(Archive &ar)
Definition funcimpl.h:1196
Range< typename FunctionImpl< Q, NDIM >::dcT::const_iterator > rangeT
Definition funcimpl.h:1182
T alpha
the current function impl
Definition funcimpl.h:1184
const bool do_leaves
start with leaf nodes instead of initial_level
Definition funcimpl.h:6461
T operator()(T a, T b) const
Definition funcimpl.h:6479
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:6463
void serialize(const Archive &ar)
Definition funcimpl.h:6483
const bool leaf_refine
Definition funcimpl.h:6460
const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > fref
Definition funcimpl.h:6458
T operator()(typename dcT::const_iterator &it) const
Definition funcimpl.h:6467
const implT * impl
Definition funcimpl.h:6459
compute the inner product of this range with other
Definition funcimpl.h:5463
const FunctionImpl< T, NDIM > * bra
Definition funcimpl.h:5464
void serialize(const Archive &ar)
Definition funcimpl.h:5579
const FunctionImpl< R, NDIM > * ket
Definition funcimpl.h:5465
bool leaves_only
Definition funcimpl.h:5466
do_inner_local_on_demand(const FunctionImpl< T, NDIM > *bra, const FunctionImpl< R, NDIM > *ket, const bool leaves_only=true)
Definition funcimpl.h:5469
resultT operator()(resultT a, resultT b) const
Definition funcimpl.h:5575
resultT operator()(typename dcT::const_iterator &it) const
Definition funcimpl.h:5472
compute the inner product of this range with other
Definition funcimpl.h:5402
resultT operator()(resultT a, resultT b) const
Definition funcimpl.h:5435
bool leaves_only
Definition funcimpl.h:5404
void serialize(const Archive &ar)
Definition funcimpl.h:5439
do_inner_local(const FunctionImpl< R, NDIM > *other, const bool leaves_only)
Definition funcimpl.h:5407
const FunctionImpl< R, NDIM > * other
Definition funcimpl.h:5403
resultT operator()(typename dcT::const_iterator &it) const
Definition funcimpl.h:5409
typedef TENSOR_RESULT_TYPE(T, R) resultT
keep only the sum coefficients in each node
Definition funcimpl.h:2222
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2223
do_keep_sum_coeffs(implT *impl)
constructor need impl for cdata
Definition funcimpl.h:2227
implT * impl
Definition funcimpl.h:2224
void serialize(const Archive &ar)
Definition funcimpl.h:2236
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2229
mirror dimensions of this, write result on f
Definition funcimpl.h:2496
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2506
implT * f
Definition funcimpl.h:2500
std::vector< long > mirror
Definition funcimpl.h:2499
void serialize(const Archive &ar)
Definition funcimpl.h:2553
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2497
std::vector< long > map
Definition funcimpl.h:2499
do_map_and_mirror(const std::vector< long > map, const std::vector< long > mirror, implT &f)
Definition funcimpl.h:2503
map this on f
Definition funcimpl.h:2416
do_mapdim(const std::vector< long > map, implT &f)
Definition funcimpl.h:2423
void serialize(const Archive &ar)
Definition funcimpl.h:2439
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2417
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2425
std::vector< long > map
Definition funcimpl.h:2419
do_mapdim()
Definition funcimpl.h:2422
implT * f
Definition funcimpl.h:2420
merge the coefficent boxes of this into other's tree
Definition funcimpl.h:2380
bool operator()(typename rangeT::iterator &it) const
return the norm of the difference of this node and its "mirror" node
Definition funcimpl.h:2390
Range< typename dcT::const_iterator > rangeT
Definition funcimpl.h:2381
FunctionImpl< Q, NDIM > * other
Definition funcimpl.h:2382
do_merge_trees(const T alpha, const R beta, FunctionImpl< Q, NDIM > &other)
Definition funcimpl.h:2386
T alpha
Definition funcimpl.h:2383
do_merge_trees()
Definition funcimpl.h:2385
R beta
Definition funcimpl.h:2384
void serialize(const Archive &ar)
Definition funcimpl.h:2409
mirror dimensions of this, write result on f
Definition funcimpl.h:2446
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2455
implT * f
Definition funcimpl.h:2450
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2447
do_mirror()
Definition funcimpl.h:2452
do_mirror(const std::vector< long > mirror, implT &f)
Definition funcimpl.h:2453
void serialize(const Archive &ar)
Definition funcimpl.h:2489
std::vector< long > mirror
Definition funcimpl.h:2449
Definition funcimpl.h:5375
double operator()(typename dcT::const_iterator &it) const
Definition funcimpl.h:5376
void serialize(const Archive &ar)
Definition funcimpl.h:5391
double operator()(double a, double b) const
Definition funcimpl.h:5387
laziness
Definition funcimpl.h:4643
void serialize(Archive &ar)
Definition funcimpl.h:4652
Key< OPDIM > d
Definition funcimpl.h:4644
Key< OPDIM > key
Definition funcimpl.h:4644
keyT dest
Definition funcimpl.h:4645
double fac
Definition funcimpl.h:4646
do_op_args(const Key< OPDIM > &key, const Key< OPDIM > &d, const keyT &dest, double tol, double fac, double cnorm)
Definition funcimpl.h:4649
double cnorm
Definition funcimpl.h:4646
double tol
Definition funcimpl.h:4646
reduce the rank of the nodes, optional fence
Definition funcimpl.h:2242
do_reduce_rank(const TensorArgs &targs)
Definition funcimpl.h:2250
TensorArgs args
Definition funcimpl.h:2246
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2256
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2243
do_reduce_rank(const double &thresh)
Definition funcimpl.h:2251
void serialize(const Archive &ar)
Definition funcimpl.h:2262
Changes non-standard compressed form to standard compressed form.
Definition funcimpl.h:4607
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:4618
do_standard(implT *impl)
Definition funcimpl.h:4615
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:4608
void serialize(const Archive &ar)
Definition funcimpl.h:4635
implT * impl
Definition funcimpl.h:4611
given an NS tree resulting from a convolution, truncate leafs if appropriate
Definition funcimpl.h:2163
void serialize(const Archive &ar)
Definition funcimpl.h:2183
const implT * f
Definition funcimpl.h:2165
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2169
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2164
do_truncate_NS_leafs(const implT *f)
Definition funcimpl.h:2167
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2642
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2646
implT * impl
Definition funcimpl.h:2643
void serialize(const Archive &ar)
Definition funcimpl.h:2664
do_unary_op_value_inplace(implT *impl, const opT &op)
Definition funcimpl.h:2645
Hartree product of two LDIM functions to yield a NDIM = 2*LDIM function.
Definition funcimpl.h:3577
this_type forward_ctor(implT *result1, const ctL &p11, const ctL &p22, const leaf_opT &leaf_op)
Definition funcimpl.h:3633
bool randomize() const
Definition funcimpl.h:3578
void serialize(const Archive &ar)
Definition funcimpl.h:3637
hartree_op(implT *result, const ctL &p11, const ctL &p22, const leaf_opT &leaf_op)
Definition funcimpl.h:3589
CoeffTracker< T, LDIM > ctL
Definition funcimpl.h:3581
ctL p2
tracking coeffs of the two lo-dim functions
Definition funcimpl.h:3584
leaf_opT leaf_op
determine if a given node will be a leaf node
Definition funcimpl.h:3585
hartree_op()
Definition funcimpl.h:3588
implT * result
where to construct the pair function
Definition funcimpl.h:3583
hartree_op< LDIM, leaf_opT > this_type
Definition funcimpl.h:3580
std::pair< bool, coeffT > operator()(const Key< NDIM > &key) const
Definition funcimpl.h:3594
ctL p1
Definition funcimpl.h:3584
this_type make_child(const keyT &child) const
Definition funcimpl.h:3617
Future< this_type > activate() const
Definition funcimpl.h:3626
perform this multiplication: h(1,2) = f(1,2) * g(1)
Definition funcimpl.h:3385
multiply_op()
Definition funcimpl.h:3397
ctL g
Definition funcimpl.h:3394
Future< this_type > activate() const
Definition funcimpl.h:3476
CoeffTracker< T, LDIM > ctL
Definition funcimpl.h:3389
implT * h
the result function h(1,2) = f(1,2) * g(1)
Definition funcimpl.h:3392
CoeffTracker< T, NDIM > ctT
Definition funcimpl.h:3388
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:3424
this_type forward_ctor(implT *h1, const ctT &f1, const ctL &g1, const int particle)
Definition funcimpl.h:3483
static bool randomize()
Definition funcimpl.h:3387
int particle
if g is g(1) or g(2)
Definition funcimpl.h:3395
ctT f
Definition funcimpl.h:3393
multiply_op< LDIM > this_type
Definition funcimpl.h:3390
multiply_op(implT *h1, const ctT &f1, const ctL &g1, const int particle1)
Definition funcimpl.h:3399
bool screen(const coeffT &fcoeff, const coeffT &gcoeff, const keyT &key) const
return true if this will be a leaf node
Definition funcimpl.h:3405
this_type make_child(const keyT &child) const
Definition funcimpl.h:3466
void serialize(const Archive &ar)
Definition funcimpl.h:3487
coeffT val_lhs
Definition funcimpl.h:3859
double lo
Definition funcimpl.h:3862
double lo1
Definition funcimpl.h:3862
long oversampling
Definition funcimpl.h:3860
double error
Definition funcimpl.h:3861
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:3877
coeffT coeff_lhs
Definition funcimpl.h:3859
void serialize(const Archive &ar)
Definition funcimpl.h:3965
double lo2
Definition funcimpl.h:3862
double hi1
Definition funcimpl.h:3862
pointwise_multiplier(const Key< NDIM > key, const coeffT &clhs)
Definition funcimpl.h:3865
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:3922
double hi2
Definition funcimpl.h:3862
double hi
Definition funcimpl.h:3862
project the low-dim function g on the hi-dim function f: result(x) = <f(x,y) | g(y)>
Definition funcimpl.h:6723
project_out_op(const implT *fimpl, implL1 *result, const ctL &iag, const int dim)
Definition funcimpl.h:6738
ctL iag
the low dim function g
Definition funcimpl.h:6733
FunctionImpl< T, NDIM-LDIM > implL1
Definition funcimpl.h:6728
Future< this_type > activate() const
retrieve the coefficients (parent coeffs might be remote)
Definition funcimpl.h:6817
std::pair< bool, coeffT > argT
Definition funcimpl.h:6729
const implT * fimpl
the hi dim function f
Definition funcimpl.h:6731
this_type forward_ctor(const implT *fimpl1, implL1 *result1, const ctL &iag1, const int dim1)
taskq-compatible ctor
Definition funcimpl.h:6824
this_type make_child(const keyT &child) const
Definition funcimpl.h:6808
project_out_op< LDIM > this_type
Definition funcimpl.h:6726
implL1 * result
the low dim result function
Definition funcimpl.h:6732
Future< argT > operator()(const Key< NDIM > &key) const
do the actual contraction
Definition funcimpl.h:6745
void serialize(const Archive &ar)
Definition funcimpl.h:6828
project_out_op(const project_out_op &other)
Definition funcimpl.h:6740
int dim
0: project 0..LDIM-1, 1: project LDIM..NDIM-1
Definition funcimpl.h:6734
bool randomize() const
Definition funcimpl.h:6724
CoeffTracker< T, LDIM > ctL
Definition funcimpl.h:6727
recursive part of recursive_apply
Definition funcimpl.h:5202
ctT iaf
Definition funcimpl.h:5210
recursive_apply_op2< opT > this_type
Definition funcimpl.h:5205
Future< this_type > activate() const
retrieve the coefficients (parent coeffs might be remote)
Definition funcimpl.h:5265
const opT * apply_op
need this for randomization
Definition funcimpl.h:5211
bool randomize() const
Definition funcimpl.h:5203
recursive_apply_op2(const recursive_apply_op2 &other)
Definition funcimpl.h:5218
void serialize(const Archive &ar)
Definition funcimpl.h:5281
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:5251
this_type make_child(const keyT &child) const
Definition funcimpl.h:5260
recursive_apply_op2(implT *result, const ctT &iaf, const opT *apply_op)
Definition funcimpl.h:5215
std::pair< bool, coeffT > argT
Definition funcimpl.h:5207
implT * result
Definition funcimpl.h:5209
CoeffTracker< T, NDIM > ctT
Definition funcimpl.h:5206
argT operator()(const Key< NDIM > &key) const
send off the application of the operator
Definition funcimpl.h:5227
this_type forward_ctor(implT *result1, const ctT &iaf1, const opT *apply_op1)
taskq-compatible ctor
Definition funcimpl.h:5277
recursive part of recursive_apply
Definition funcimpl.h:5071
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:5096
this_type forward_ctor(implT *r, const CoeffTracker< T, LDIM > &f1, const CoeffTracker< T, LDIM > &g1, const opT *apply_op1)
Definition funcimpl.h:5161
opT * apply_op
Definition funcimpl.h:5079
recursive_apply_op(const recursive_apply_op &other)
Definition funcimpl.h:5089
recursive_apply_op< opT, LDIM > this_type
Definition funcimpl.h:5074
Future< this_type > activate() const
Definition funcimpl.h:5154
bool randomize() const
Definition funcimpl.h:5072
implT * result
Definition funcimpl.h:5076
CoeffTracker< T, LDIM > iaf
Definition funcimpl.h:5077
void serialize(const Archive &ar)
Definition funcimpl.h:5166
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:5136
recursive_apply_op(implT *result, const CoeffTracker< T, LDIM > &iaf, const CoeffTracker< T, LDIM > &iag, const opT *apply_op)
Definition funcimpl.h:5083
this_type make_child(const keyT &child) const
Definition funcimpl.h:5145
CoeffTracker< T, LDIM > iag
Definition funcimpl.h:5078
remove all coefficients of internal nodes
Definition funcimpl.h:2188
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2189
remove_internal_coeffs()=default
constructor need impl for cdata
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2194
void serialize(const Archive &ar)
Definition funcimpl.h:2200
remove all coefficients of leaf nodes
Definition funcimpl.h:2205
bool operator()(typename rangeT::iterator &it) const
Definition funcimpl.h:2211
remove_leaf_coeffs()=default
constructor need impl for cdata
void serialize(const Archive &ar)
Definition funcimpl.h:2216
Range< typename dcT::iterator > rangeT
Definition funcimpl.h:2206
Definition funcimpl.h:4458
void serialize(Archive &ar)
Definition funcimpl.h:4462
bool operator()(const implT *f, const keyT &key, const nodeT &t) const
Definition funcimpl.h:4459
shallow-copy, pared-down version of FunctionNode, for special purpose only
Definition funcimpl.h:746
coeffT & coeff()
Definition funcimpl.h:760
GenTensor< T > coeffT
Definition funcimpl.h:747
bool is_leaf() const
Definition funcimpl.h:762
void serialize(Archive &ar)
Definition funcimpl.h:764
ShallowNode(const ShallowNode< T, NDIM > &node)
Definition funcimpl.h:755
ShallowNode(const FunctionNode< T, NDIM > &node)
Definition funcimpl.h:752
bool has_children() const
Definition funcimpl.h:761
ShallowNode()
Definition funcimpl.h:751
bool _has_children
Definition funcimpl.h:749
double dnorm
Definition funcimpl.h:750
const coeffT & coeff() const
Definition funcimpl.h:759
coeffT _coeffs
Definition funcimpl.h:748
TensorArgs holds the arguments for creating a LowRankTensor.
Definition gentensor.h:134
double thresh
Definition gentensor.h:135
TensorType tt
Definition gentensor.h:136
inserts/accumulates coefficients into impl's tree
Definition funcimpl.h:713
FunctionImpl< T, NDIM > * impl
Definition funcimpl.h:717
FunctionNode< T, NDIM > nodeT
Definition funcimpl.h:715
accumulate_op(const accumulate_op &other)=default
void operator()(const Key< NDIM > &key, const coeffT &coeff, const bool &is_leaf) const
Definition funcimpl.h:721
void serialize(Archive &ar)
Definition funcimpl.h:725
GenTensor< T > coeffT
Definition funcimpl.h:714
accumulate_op(FunctionImpl< T, NDIM > *f)
Definition funcimpl.h:719
static void load(const Archive &ar, FunctionImpl< T, NDIM > *&ptr)
Definition funcimpl.h:7044
static void load(const Archive &ar, const FunctionImpl< T, NDIM > *&ptr)
Definition funcimpl.h:7013
static void load(const Archive &ar, std::shared_ptr< FunctionImpl< T, NDIM > > &ptr)
Definition funcimpl.h:7092
static void load(const Archive &ar, std::shared_ptr< const FunctionImpl< T, NDIM > > &ptr)
Definition funcimpl.h:7076
Default load of an object via serialize(ar, t).
Definition archive.h:666
static void load(const A &ar, const U &t)
Load an object.
Definition archive.h:678
static void store(const Archive &ar, FunctionImpl< T, NDIM > *const &ptr)
Definition funcimpl.h:7066
static void store(const Archive &ar, const FunctionImpl< T, NDIM > *const &ptr)
Definition funcimpl.h:7035
static void store(const Archive &ar, const std::shared_ptr< FunctionImpl< T, NDIM > > &ptr)
Definition funcimpl.h:7101
static void store(const Archive &ar, const std::shared_ptr< const FunctionImpl< T, NDIM > > &ptr)
Definition funcimpl.h:7085
Default store of an object via serialize(ar, t).
Definition archive.h:611
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:621
Definition funcimpl.h:607
void serialize(Archive &ar)
Definition funcimpl.h:671
const opT * op
Definition funcimpl.h:614
hartree_convolute_leaf_op(const implT *f, const implL *g, const opT *op)
Definition funcimpl.h:618
bool operator()(const Key< NDIM > &key) const
no pre-determination
Definition funcimpl.h:622
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:635
const implL * g
Definition funcimpl.h:613
const FunctionImpl< T, NDIM > * f
Definition funcimpl.h:612
FunctionImpl< T, LDIM > implL
Definition funcimpl.h:610
bool do_error_leaf_op() const
Definition funcimpl.h:615
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:609
bool operator()(const Key< NDIM > &key, const GenTensor< T > &coeff) const
no post-determination
Definition funcimpl.h:625
returns true if the result of a hartree_product is a leaf node (compute norm & error)
Definition funcimpl.h:497
bool do_error_leaf_op() const
Definition funcimpl.h:502
const FunctionImpl< T, NDIM > * f
Definition funcimpl.h:500
hartree_leaf_op(const implT *f, const long &k)
Definition funcimpl.h:505
long k
Definition funcimpl.h:501
void serialize(Archive &ar)
Definition funcimpl.h:553
bool operator()(const Key< NDIM > &key, const GenTensor< T > &coeff) const
no post-determination
Definition funcimpl.h:511
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:521
bool operator()(const Key< NDIM > &key) const
no pre-determination
Definition funcimpl.h:508
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:499
insert/replaces the coefficients into the function
Definition funcimpl.h:689
insert_op()
Definition funcimpl.h:696
implT * impl
Definition funcimpl.h:695
void operator()(const keyT &key, const coeffT &coeff, const bool &is_leaf) const
Definition funcimpl.h:699
FunctionNode< T, NDIM > nodeT
Definition funcimpl.h:693
Key< NDIM > keyT
Definition funcimpl.h:691
insert_op(const insert_op &other)
Definition funcimpl.h:698
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:690
GenTensor< T > coeffT
Definition funcimpl.h:692
insert_op(implT *f)
Definition funcimpl.h:697
void serialize(Archive &ar)
Definition funcimpl.h:703
Definition mra.h:95
Definition funcimpl.h:677
bool operator()(const Key< NDIM > &key, const GenTensor< T > &fcoeff, const GenTensor< T > &gcoeff) const
Definition funcimpl.h:679
void serialize(Archive &ar)
Definition funcimpl.h:683
void operator()(const Key< NDIM > &key, const GenTensor< T > &coeff, const bool &is_leaf) const
Definition funcimpl.h:678
Definition funcimpl.h:561
bool operator()(const Key< NDIM > &key, const double &cnorm) const
post-determination: return true if operator and coefficient norms are small
Definition funcimpl.h:582
void serialize(Archive &ar)
Definition funcimpl.h:597
const implT * f
the source or result function, needed for truncate_tol
Definition funcimpl.h:565
op_leaf_op(const opT *op, const implT *f)
Definition funcimpl.h:569
FunctionImpl< T, NDIM > implT
Definition funcimpl.h:562
const opT * op
the convolution operator
Definition funcimpl.h:564
bool do_error_leaf_op() const
Definition funcimpl.h:566
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:572
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:575
Definition lowrankfunction.h:332
Definition funcimpl.h:733
void serialize(Archive &ar)
Definition funcimpl.h:740
bool operator()(const Key< NDIM > &key, const T &t, const R &r) const
Definition funcimpl.h:739
bool operator()(const Key< NDIM > &key, const T &t) const
Definition funcimpl.h:736
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
AtomicInt sum
Definition test_atomicint.cc:46
double norm(const T i1)
Definition test_cloud.cc:72
int task(int i)
Definition test_runtime.cpp:4
void e()
Definition test_sig.cc:75
static const double alpha
Definition testcosine.cc:10
const double offset
Definition testfuns.cc:143
double h(const coord_1d &r)
Definition testgconv.cc:68
static const std::size_t NDIM
Definition testpdiff.cc:42
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