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