MADNESS 0.10.1
mra.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_MRA_H__INCLUDED
33#define MADNESS_MRA_MRA_H__INCLUDED
34
35/*!
36 \file mra/mra.h
37 \brief Main include file for MADNESS and defines \c Function interface
38
39 \addtogroup mra
40
41*/
42
43
45#include <madness/misc/misc.h>
47
48#define FUNCTION_INSTANTIATE_1
49#define FUNCTION_INSTANTIATE_2
50#define FUNCTION_INSTANTIATE_3
51#if !defined(HAVE_IBMBGP) || !defined(HAVE_IBMBGQ)
52#define FUNCTION_INSTANTIATE_4
53#define FUNCTION_INSTANTIATE_5
54#define FUNCTION_INSTANTIATE_6
55#endif
56
57static const bool VERIFY_TREE = false; //true
58
59
60namespace madness {
61 /// @brief initialize the internal state of the MADmra library
62 ///
63 /// Reads in (and broadcasts across \p world) the twoscale and autocorrelation coefficients,
64 /// Gauss-Legendre quadrature roots/weights, function defaults and operator displacement lists.
65 /// \warning By default this generates operator displacement lists (see Displacements) for up to 6-d free
66 /// and 3-d periodic boundary conditions. For optimal support for mixed boundary conditions
67 /// (periodic along some axes only) assign the desired boundary conditions
68 /// as default (e.g. `FunctionDefaults<3>::set_bc(BoundaryConditions<3>({BC_FREE, BC_FREE, BC_FREE, BC_FREE, BC_PERIODIC, BC_PERIODIC})`)
69 /// prior to calling this. This will make operator application with such boundary conditions
70 /// as efficient as possible, but will not allow the use of operators with
71 /// other boundary conditions that include periodic axes until Displacements::reset_periodic_axes is invoked.
72 /// By default efficiency is sacrificed for generality.
73 /// \param world broadcast data across this World
74 /// \param argc command-line parameter count
75 /// \param argv command-line parameters array
76 /// \param doprint if true, will log status to std::cout on rank 0 [default=false]
77 /// \param make_stdcout_nice_to_reals if true, will configure std::cout to print reals prettily, according to the MADNESS convention [default=true]
78 void startup(World& world, int argc, char** argv, bool doprint=false, bool make_stdcout_nice_to_reals = true);
79 std::string get_mra_data_dir();
80}
81
82#include <madness/mra/key.h>
85#include <madness/mra/indexit.h>
90#include <madness/mra/lbdeux.h>
92
93// some forward declarations
94namespace madness {
95
96 template<typename T, std::size_t NDIM>
97 class FunctionImpl;
98
99 template<typename T, std::size_t NDIM>
100 class Function;
101
102 template<typename T, std::size_t NDIM>
103 class FunctionNode;
104
105 template<typename T, std::size_t NDIM>
106 class FunctionFactory;
107
108 template<typename T, std::size_t NDIM>
109 class FunctionFunctorInterface;
110
111 template<typename T, std::size_t NDIM>
112 struct leaf_op;
113
114 template<typename T, std::size_t NDIM>
116
117 template<typename T, std::size_t NDIM>
118 struct hartree_leaf_op;
119
120 template<typename T, std::size_t NDIM, std::size_t LDIM, typename opT>
122
123 template<typename T, std::size_t NDIM, typename opT>
124 struct op_leaf_op;
125
126 template<typename T, std::size_t NDIM>
128
129}
130
131
132namespace madness {
133
134 /// \ingroup mra
135 /// \addtogroup function
136
137 /// Header magic for Function::store/load; bump whenever FunctionNode::serialize changes.
138 /// 7776769 original (Mellow Mushroom Pizza tel.# in Knoxville, +1 for cell in header)
139 /// 7776770 FunctionNode gained _dnorm_tree
140 static constexpr long FUNCTION_ARCHIVE_MAGIC = 7776770;
141
142 /// A multiresolution adaptive numerical function
143 template <typename T, std::size_t NDIM>
145 // We make all of the content of Function and FunctionImpl
146 // public with the intent of avoiding the cumbersome forward
147 // and friend declarations. However, this open access should
148 // not be abused.
149
150 private:
151 std::shared_ptr< FunctionImpl<T,NDIM> > impl;
152
153 public:
154 bool impl_initialized()const{
155 if(impl==NULL) return false;
156 else return true;
157 }
161 typedef Vector<double,NDIM> coordT; ///< Type of vector holding coordinates
162 typedef T typeT;
163 static constexpr std::size_t dimT=NDIM;
164
165
166 /// Asserts that the function is initialized
167 inline void verify() const {
169 }
170
171 /// Returns true if the function is initialized
172 bool is_initialized() const {
173 return impl.get();
174 }
175
176 /// Default constructor makes uninitialized function. No communication.
177
178 /// An uninitialized function can only be assigned to. Any other operation will throw.
179 Function() : impl() {}
180
181
182 /// Constructor from FunctionFactory provides named parameter idiom. Possible non-blocking communication.
183 Function(const factoryT& factory)
184 : impl(new FunctionImpl<T,NDIM>(factory)) {
186 }
187
188
189 /// Copy constructor is \em shallow. No communication, works in either basis.
191 : impl(f.impl) {
192 }
193
194
195 /// Assignment is \em shallow. No communication, works in either basis.
198 if (this != &f) impl = f.impl;
199 return *this;
200 }
201
202 /// Destruction of any underlying implementation is deferred to next global fence.
204
205 /// implements swap algorithm
206 template <typename R, std::size_t MDIM>
208
209
210 /// Evaluates the function at a point in user coordinates. Possible non-blocking comm.
211
212 /// Only the invoking process will receive the result via the future
213 /// though other processes may be involved in the evaluation.
214 ///
215 /// Throws if function is not initialized.
216 Future<T> eval(const coordT& xuser) const {
218 const double eps=1e-15;
219 verify();
221 coordT xsim;
222 user_to_sim(xuser,xsim);
223 // If on the boundary, move the point just inside the
224 // volume so that the evaluation logic does not fail
225 for (std::size_t d=0; d<NDIM; ++d) {
226 if (xsim[d] < -eps) {
227 MADNESS_EXCEPTION("eval: coordinate lower-bound error in dimension", d);
228 }
229 else if (xsim[d] < eps) {
230 xsim[d] = eps;
231 }
232
233 if (xsim[d] > 1.0+eps) {
234 MADNESS_EXCEPTION("eval: coordinate upper-bound error in dimension", d);
235 }
236 else if (xsim[d] > 1.0-eps) {
237 xsim[d] = 1.0-eps;
238 }
239 }
240
241 Future<T> result;
242 impl->eval(xsim, impl->key0(), result.remote_ref(impl->world));
243 return result;
244 }
245
246 /// Evaluate function only if point is local returning (true,value); otherwise return (false,0.0)
247
248 /// maxlevel is the maximum depth to search down to --- the max local depth can be
249 /// computed with max_local_depth();
250 std::pair<bool,T> eval_local_only(const Vector<double,NDIM>& xuser, Level maxlevel) const {
251 const double eps=1e-15;
252 verify();
254 coordT xsim;
255 user_to_sim(xuser,xsim);
256 // If on the boundary, move the point just inside the
257 // volume so that the evaluation logic does not fail
258 for (std::size_t d=0; d<NDIM; ++d) {
259 if (xsim[d] < -eps) {
260 MADNESS_EXCEPTION("eval: coordinate lower-bound error in dimension", d);
261 }
262 else if (xsim[d] < eps) {
263 xsim[d] = eps;
264 }
265
266 if (xsim[d] > 1.0+eps) {
267 MADNESS_EXCEPTION("eval: coordinate upper-bound error in dimension", d);
268 }
269 else if (xsim[d] > 1.0-eps) {
270 xsim[d] = 1.0-eps;
271 }
272 }
273 return impl->eval_local_only(xsim,maxlevel);
274 }
275
276 /// Batched eval_local_only writing into a caller-provided buffer.
277
278 /// Resizes results to xuser.size() (reusing its capacity) and stores one
279 /// (local?,value) pair per input point, in input order: (true,value) if
280 /// the point is owned locally, otherwise (false,0.0).
281 /// Consecutive points that fall in the same leaf box share that box's
282 /// descent and coefficient fetch (last-box memoization), so spatially
283 /// coherent point streams (quadrature grids) amortise the per-point
284 /// tree descent. Results are bit-for-bit identical to calling the
285 /// single-point eval_local_only on each point. No communications, and
286 /// no per-call heap allocation once results has capacity.
287 ///
288 /// maxlevel is the maximum depth to search down to --- the max local depth can be
289 /// computed with max_local_depth();
290 void eval_local_only(const std::vector<coordT>& xuser, Level maxlevel,
291 std::vector<std::pair<bool,T>>& results) const {
292 const double eps=1e-15;
293 verify();
295 thread_local std::vector<coordT> xsim;
296 xsim.resize(xuser.size());
297 for (std::size_t ip=0; ip<xuser.size(); ++ip) {
298 coordT xs;
299 user_to_sim(xuser[ip],xs);
300 // If on the boundary, move the point just inside the volume so the
301 // evaluation logic does not fail (matches the single-point path).
302 for (std::size_t d=0; d<NDIM; ++d) {
303 if (xs[d] < -eps) {
304 MADNESS_EXCEPTION("eval: coordinate lower-bound error in dimension", d);
305 }
306 else if (xs[d] < eps) {
307 xs[d] = eps;
308 }
309
310 if (xs[d] > 1.0+eps) {
311 MADNESS_EXCEPTION("eval: coordinate upper-bound error in dimension", d);
312 }
313 else if (xs[d] > 1.0-eps) {
314 xs[d] = 1.0-eps;
315 }
316 }
317 xsim[ip] = xs;
318 }
319 results.resize(xuser.size());
320 impl->eval_local_only(xsim.data(), xsim.size(), maxlevel, results.data());
321 }
322
323 /// Batched eval_local_only returning a fresh vector (see the
324 /// output-parameter overload above for semantics).
325 std::vector<std::pair<bool,T>> eval_local_only(const std::vector<coordT>& xuser, Level maxlevel) const {
326 std::vector<std::pair<bool,T>> results;
327 eval_local_only(xuser, maxlevel, results);
328 return results;
329 }
330
331 /// Only the invoking process will receive the result via the future
332 /// though other processes may be involved in the evaluation.
333 ///
334 /// Throws if function is not initialized.
335 ///
336 /// This function is a minimally-modified version of eval()
337 Future<Level> evaldepthpt(const coordT& xuser) const {
339 const double eps=1e-15;
340 verify();
342 coordT xsim;
343 user_to_sim(xuser,xsim);
344 // If on the boundary, move the point just inside the
345 // volume so that the evaluation logic does not fail
346 for (std::size_t d=0; d<NDIM; ++d) {
347 if (xsim[d] < -eps) {
348 MADNESS_EXCEPTION("eval: coordinate lower-bound error in dimension", d);
349 }
350 else if (xsim[d] < eps) {
351 xsim[d] = eps;
352 }
353
354 if (xsim[d] > 1.0+eps) {
355 MADNESS_EXCEPTION("eval: coordinate upper-bound error in dimension", d);
356 }
357 else if (xsim[d] > 1.0-eps) {
358 xsim[d] = 1.0-eps;
359 }
360 }
361
362 Future<Level> result;
363 impl->evaldepthpt(xsim, impl->key0(), result.remote_ref(impl->world));
364 return result;
365 }
366
367
368 /// Evaluates the function rank at a point in user coordinates. Possible non-blocking comm.
369
370 /// Only the invoking process will receive the result via the future
371 /// though other processes may be involved in the evaluation.
372 ///
373 /// Throws if function is not initialized.
374 Future<long> evalR(const coordT& xuser) const {
376 const double eps=1e-15;
377 verify();
379 coordT xsim;
380 user_to_sim(xuser,xsim);
381 // If on the boundary, move the point just inside the
382 // volume so that the evaluation logic does not fail
383 for (std::size_t d=0; d<NDIM; ++d) {
384 if (xsim[d] < -eps) {
385 MADNESS_EXCEPTION("eval: coordinate lower-bound error in dimension", d);
386 }
387 else if (xsim[d] < eps) {
388 xsim[d] = eps;
389 }
390
391 if (xsim[d] > 1.0+eps) {
392 MADNESS_EXCEPTION("eval: coordinate upper-bound error in dimension", d);
393 }
394 else if (xsim[d] > 1.0-eps) {
395 xsim[d] = 1.0-eps;
396 }
397 }
398
399 Future<long> result;
400 impl->evalR(xsim, impl->key0(), result.remote_ref(impl->world));
401 return result;
402 }
403
404 /// Evaluates a cube/slice of points (probably for plotting) ... collective but no fence necessary
405
406 /// All processes receive the entire result (which is a rather severe limit
407 /// on the size of the cube that is possible).
408
409 /// Set eval_refine=true to return the refinment levels of
410 /// the given function.
411
412 /// @param[in] cell A Tensor describe the cube where the function to be evaluated in
413 /// @param[in] npt How many points to evaluate in each dimension
414 /// @param[in] eval_refine Wether to return the refinment levels of the given function
416 const std::vector<long>& npt,
417 bool eval_refine = false) const {
418 MADNESS_ASSERT(static_cast<std::size_t>(cell.dim(0))>=NDIM && cell.dim(1)==2 && npt.size()>=NDIM);
420 const double eps=1e-14;
421 verify();
422 reconstruct();
423 coordT simlo, simhi;
424 for (std::size_t d=0; d<NDIM; ++d) {
425 simlo[d] = cell(d,0);
426 simhi[d] = cell(d,1);
427 }
428 user_to_sim(simlo, simlo);
429 user_to_sim(simhi, simhi);
430
431 // Move the bounding box infintesimally inside dyadic
432 // points so that the evaluation logic does not fail
433 for (std::size_t d=0; d<NDIM; ++d) {
434 MADNESS_ASSERT(simhi[d] >= simlo[d]);
435 MADNESS_ASSERT(simlo[d] >= 0.0);
436 MADNESS_ASSERT(simhi[d] <= 1.0);
437
438 double delta = eps*(simhi[d]-simlo[d]);
439 simlo[d] += delta;
440 simhi[d] -= 2*delta; // deliberate asymmetry
441 }
442 return impl->eval_plot_cube(simlo, simhi, npt, eval_refine);
443 }
444
445
446 /// Evaluates the function at a point in user coordinates. Collective operation.
447
448 /// Throws if function is not initialized.
449 ///
450 /// This function calls eval, blocks until the result is
451 /// available and then broadcasts the result to everyone.
452 /// Therefore, if you are evaluating many points in parallel
453 /// it is \em vastly less efficient than calling eval
454 /// directly, saving the futures, and then forcing all of the
455 /// results.
456 T operator()(const coordT& xuser) const {
458 verify();
460 T result;
461 if (impl->world.rank() == 0) result = eval(xuser).get();
462 impl->world.gop.broadcast(result);
463 //impl->world.gop.fence();
464 return result;
465 }
466
467 /// Evaluates the function at a point in user coordinates. Collective operation.
468
469 /// See "operator()(const coordT& xuser)" for more info
470 T operator()(double x, double y=0, double z=0, double xx=0, double yy=0, double zz=0) const {
471 coordT r;
472 r[0] = x;
473 if (NDIM>=2) r[1] = y;
474 if (NDIM>=3) r[2] = z;
475 if (NDIM>=4) r[3] = xx;
476 if (NDIM>=5) r[4] = yy;
477 if (NDIM>=6) r[5] = zz;
478 return (*this)(r);
479 }
480
481 /// Throws if function is not initialized.
482 ///
483 /// This function mimics operator() by going through the
484 /// tree looking for the depth of the tree at the point.
485 /// It blocks until the result is
486 /// available and then broadcasts the result to everyone.
487 /// Therefore, if you are evaluating many points in parallel
488 /// it is \em vastly less efficient than calling evaldepthpt
489 /// directly, saving the futures, and then forcing all of the
490 /// results.
491 Level depthpt(const coordT& xuser) const {
493 verify();
495 Level result;
496 if (impl->world.rank() == 0) result = evaldepthpt(xuser).get();
497 impl->world.gop.broadcast(result);
498 //impl->world.gop.fence();
499 return result;
500 }
501
502 /// Returns an estimate of the difference ||this-func||^2 from local data
503
504 /// No communication is performed. If the function is not
505 /// reconstructed, it throws an exception. To get the global
506 /// value either do a global sum of the local values or call
507 /// errsq
508 /// @param[in] func Templated interface to the a user specified function
509 template <typename funcT>
510 double errsq_local(const funcT& func) const {
512 verify();
513 if (!is_reconstructed()) MADNESS_EXCEPTION("Function:errsq_local:not reconstructed",0);
514 return impl->errsq_local(func);
515 }
516
517
518 /// Returns an estimate of the difference ||this-func|| ... global sum performed
519
520 /// If the function is compressed, it is reconstructed first. For efficient use
521 /// especially with many functions, reconstruct them all first, and use errsq_local
522 /// instead so you can perform a global sum on all at the same time.
523 /// @param[in] func Templated interface to the a user specified function
524 template <typename funcT>
525 double err(const funcT& func) const {
527 verify();
531 double local = impl->errsq_local(func);
532 impl->world.gop.sum(local);
533 impl->world.gop.fence();
534 return sqrt(local);
535 }
536
537 /// Verifies the tree data structure ... global sync implied
538 void verify_tree() const {
540 if (impl) impl->verify_tree();
541 }
542
543
544 /// Returns true if compressed, false otherwise. No communication.
545
546 /// If the function is not initialized, returns false.
547 bool is_compressed() const {
549 if (impl)
550 return impl->is_compressed();
551 else
552 return false;
553 }
554
555 /// Returns true if reconstructed, false otherwise. No communication.
556
557 /// If the function is not initialized, returns false.
558 bool is_reconstructed() const {
560 if (impl)
561 return impl->is_reconstructed();
562 else
563 return false;
564 }
565
566 /// Returns true if nonstandard-compressed, false otherwise. No communication.
567
568 /// If the function is not initialized, returns false.
569 bool is_nonstandard() const {
571 return impl ? impl->is_nonstandard() : false;
572 }
573
574 /// Returns true if redundant, false otherwise. No communication.
575
576 /// If the function is not initialized, returns false.
577 bool is_redundant() const {
579 return impl ? impl->is_redundant() : false;
580 }
581
582 /// Returns true if redundant_after_merge, false otherwise. No communication.
583
584 /// If the function is not initialized, returns false.
587 return impl ? impl->is_redundant_after_merge() : false;
588 }
589
590 /// Returns the number of nodes in the function tree ... collective global sum
591 std::size_t tree_size() const {
593 if (!impl) return 0;
594 return impl->tree_size();
595 }
596
597 /// print some info about this
598 void print_size(const std::string name) const {
599 if (!impl) {
600 print("function",name,"not assigned yet");
601 } else {
602 impl->print_size(name);
603 }
604 }
605
606 /// Returns the maximum depth of the function tree ... collective global sum
607 std::size_t max_depth() const {
609 if (!impl) return 0;
610 return impl->max_depth();
611 }
612
613
614 /// Returns the maximum local depth of the function tree ... no communications
615
616 /// This is the value to pass as \c maxlevel to eval_local_only: it bounds the
617 /// descent to the deepest leaf actually held on this rank. Passing a larger
618 /// bound (e.g. Level::max()) only makes a missing/remote point descend through
619 /// empty levels doing owner() checks that never match -- pure overhead.
620 std::size_t max_local_depth() const {
622 if (!impl) return 0;
623 return impl->max_local_depth();
624 }
625
626
627 /// Returns the max number of nodes on a processor
628 std::size_t max_nodes() const {
630 if (!impl) return 0;
631 return impl->max_nodes();
632 }
633
634 /// Returns the min number of nodes on a processor
635 std::size_t min_nodes() const {
637 if (!impl) return 0;
638 return impl->min_nodes();
639 }
640
641
642 /// Returns the number of coefficients in the function ... collective global sum
643 std::size_t size() const {
645 if (!impl) return 0;
646 return impl->size();
647 }
648
649 /// Return the number of coefficients in the function on this processor
650 std::size_t size_local() const {
652 if (!impl) return 0;
653 return impl->size_local();
654 }
655
656
657 /// Returns value of autorefine flag. No communication.
658 bool autorefine() const {
660 if (!impl) return true;
661 return impl->get_autorefine();
662 }
663
664
665 /// Sets the value of the autorefine flag. Optional global fence.
666
667 /// A fence is required to ensure consistent global state.
668 void set_autorefine(bool value, bool fence = true) {
670 verify();
671 impl->set_autorefine(value);
672 if (fence) impl->world.gop.fence();
673 }
674
675
676 /// Returns value of truncation threshold. No communication.
677 double thresh() const {
679 if (!impl) return 0.0;
680 return impl->get_thresh();
681 }
682
683
684 /// Sets the value of the truncation threshold. Optional global fence.
685
686 /// A fence is required to ensure consistent global state.
687 void set_thresh(double value, bool fence = true) {
689 verify();
690 impl->set_thresh(value);
691 if (fence) impl->world.gop.fence();
692 }
693
694
695 /// Returns the number of multiwavelets (k). No communication.
696 int k() const {
698 verify();
699 return impl->get_k();
700 }
701
702
703 /// Truncate the function with optional fence. Compresses with fence if not compressed.
704
705 /// If the truncation threshold is less than or equal to zero the default value
706 /// specified when the function was created is used.
707 /// If the function is not initialized, it just returns.
708 ///
709 /// Returns this for chaining.
710 /// @param[in] tol Tolerance for truncating the coefficients. Default 0.0 means use the implementation's member value \c thresh instead.
711 /// @param[in] fence Do fence
712 Function<T,NDIM>& truncate(double tol = 0.0, bool fence = true) {
714 if (!impl) return *this;
715 verify();
716// if (!is_compressed()) compress();
717 impl->truncate(tol,fence);
719 return *this;
720 }
721
722
723 /// Returns a shared-pointer to the implementation
724 const std::shared_ptr< FunctionImpl<T,NDIM> >& get_impl() const {
726 verify();
727 return impl;
728 }
729
730 /// Replace current FunctionImpl with provided new one
731 void set_impl(const std::shared_ptr< FunctionImpl<T,NDIM> >& impl) {
733 this->impl = impl;
734 }
735
736
737 /// Replace the current functor with the provided new one
738
739 /// presumably the new functor will be a CompositeFunctor, which will
740 /// change the behavior of the function: multiply the functor with the function
741 void set_functor(const std::shared_ptr<FunctionFunctorInterface<T, NDIM> > functor) {
742 this->impl->set_functor(functor);
743 print("set functor in mra.h");
744 }
745
746 bool is_on_demand() const {return this->impl->is_on_demand();}
747
748 /// Replace current FunctionImpl with a new one using the same parameters & map as f
749
750 /// If zero is true the function is initialized to zero, otherwise it is empty
751 template <typename R>
752 void set_impl(const Function<R,NDIM>& f, bool zero = true) {
753 impl = std::shared_ptr<implT>(new implT(*f.get_impl(), f.get_pmap(), zero));
754 if (zero) world().gop.fence();
755 }
756
757 /// Returns the world
758 World& world() const {
760 verify();
761 return impl->world;
762 }
763
764
765 /// Returns a shared pointer to the process map
766 const std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > >& get_pmap() const {
768 verify();
769 return impl->get_pmap();
770 }
771
772 /// Replicates this function according to its distribution type.
773 ///
774 /// RankReplicated gives every rank every coefficient node. NodeReplicated
775 /// gives one copy per host, and the lowest rank on that host owns it. The
776 /// two methods below give the ownership rules and the limits of each policy.
777 /// NodeReplicated requires fence=true.
778 void replicate(const DistributionType type, bool fence=true) const {
779 verify();
781 else if (type==DistributionType::NodeReplicated) impl->replicate_on_hosts(fence);
782 else MADNESS_EXCEPTION("Function::replicate: unknown DistributionType",type);
783 }
784
785 /// Replicates this function on every rank.
786 ///
787 /// Every rank owns every coefficient node, so rank-local operations such as
788 /// eval_local_only work on every rank. Global reductions count the data more
789 /// than once. Queued tasks must not change the coefficients until the call
790 /// returns.
791 void replicate(bool fence=true) const {
792 verify();
793 impl->replicate(fence);
794 }
795
796 /// Replicates this function once per host.
797 ///
798 /// The lowest rank on each host owns all coefficient nodes. Other ranks route
799 /// ordinary container access, such as find, to that rank. eval_local_only does
800 /// no communication, so it returns false on those ranks. Global reductions
801 /// count one copy per host. If every rank needs local coefficient access,
802 /// replicate() is the correct policy. fence must be true.
803 void replicate_on_hosts(bool fence=true) const {
804 verify();
805 impl->replicate_on_hosts(fence);
806 }
807
808
809 /// distribute this function according to newmap
810 void distribute(std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > > newmap) const {
811 verify();
812 impl->distribute(newmap);
813 }
814
815
816 /// Returns the square of the norm of the local function ... no communication
817
818 /// Works in any state that holds its coefficients once, cf.
819 /// FunctionImpl::has_summable_coefficients()
820 double norm2sq_local() const {
822 verify();
823 MADNESS_CHECK_THROW(impl->has_summable_coefficients(),
824 "norm2sq_local needs a tree that holds its coefficients once");
825 return impl->norm2sq_local();
826 }
827
828
829 /// Returns the 2-norm of the function ... global sum ... works in either basis
830
831 /// Works in any state whose coefficients norm2sq_local() can sum, which
832 /// includes the redundant and nonstandard-with-leaves trees left behind
833 /// by mul_sparse() and friends; the remaining states are reconstructed
834 /// first. See comments for err() w.r.t. applying to many functions.
835 ///
836 /// Throws if the function is on-demand: it carries no coefficients, so
837 /// its norm is not defined until it is materialized.
838 ///
839 /// N.B. that reconstruction is a mutation -- it discards the interior
840 /// coefficients -- so this (logically const) method fences before it
841 /// changes state: any task still reading those coefficients, e.g. a
842 /// mul_sparse() invoked with fence=false, must be done with them before
843 /// they are removed.
844 ///
845 /// The branch is taken on the tree state, which is replicated, so all
846 /// ranks take the same branch and the global ops stay collective.
847 double norm2() const {
849 verify();
851 if (!impl->has_summable_coefficients()) {
853 "norm2 is not defined for an on-demand function; materialize it first");
854 impl->world.gop.fence();
855 reconstruct();
856 }
857 double local = impl->norm2sq_local();
858
859 impl->world.gop.sum(local);
860 impl->world.gop.fence();
861 return sqrt(local);
862 }
863
864
865 /// Initializes information about the function norm at all length scales
866 void norm_tree(bool fence = true) const {
868 verify();
871 const_cast<Function<T,NDIM>*>(this)->impl->norm_tree(fence);
872 }
873
874
875 /// Compresses the function, transforming into wavelet basis. Possible non-blocking comm.
876
877 /// By default fence=true meaning that this operation completes before returning,
878 /// otherwise if fence=false it returns without fencing and the user must invoke
879 /// world.gop.fence() to assure global completion before using the function
880 /// for other purposes.
881 ///
882 /// Noop if already compressed or if not initialized.
883 ///
884 /// Since reconstruction/compression do not discard information we define them
885 /// as const ... "logical constness" not "bitwise constness".
886 const Function<T,NDIM>& compress(bool fence = true) const {
888 }
889
890
891 /// Compresses the function retaining scaling function coeffs. Possible non-blocking comm.
892
893 /// By default fence=true meaning that this operation completes before returning,
894 /// otherwise if fence=false it returns without fencing and the user must invoke
895 /// world.gop.fence() to assure global completion before using the function
896 /// for other purposes.
897 ///
898 /// Noop if already compressed or if not initialized.
899 void make_nonstandard(bool keepleaves, bool fence=true) const {
901 if (keepleaves) newstate=nonstandard_with_leaves;
902 change_tree_state(newstate,fence);
903 }
904
905 /// Converts the function standard compressed form. Possible non-blocking comm.
906
907 /// By default fence=true meaning that this operation completes before returning,
908 /// otherwise if fence=false it returns without fencing and the user must invoke
909 /// world.gop.fence() to assure global completion before using the function
910 /// for other purposes.
911 ///
912 /// Must be already compressed.
913 void standard(bool fence = true) {
915 }
916
917 /// Converts the function to redundant form, i.e. sum coefficients on all levels
918
919 /// By default fence=true meaning that this operation completes before returning,
920 /// otherwise if fence=false it returns without fencing and the user must invoke
921 /// world.gop.fence() to assure global completion before using the function
922 /// for other purposes.
923 ///
924 /// Since the transformation does not discard information we define this
925 /// as const ... "logical constness" not "bitwise constness".
926 ///
927 /// Note redundant form stores sum coefficients at every level, so it is larger than
928 /// reconstructed form; a caller that keeps the function alive may want to convert back.
929 void make_redundant(bool fence = true) const {
931 }
932
933 /// Reconstructs the function, transforming into scaling function basis. Possible non-blocking comm.
934
935 /// By default fence=true meaning that this operation completes before returning,
936 /// otherwise if fence=false it returns without fencing and the user must invoke
937 /// world.gop.fence() to assure global completion before using the function
938 /// for other purposes.
939 ///
940 /// Noop if already reconstructed or if not initialized.
941 ///
942 /// Since reconstruction/compression do not discard information we define them
943 /// as const ... "logical constness" not "bitwise constness".
944 const Function<T,NDIM>& reconstruct(bool fence = true) const {
946 }
947
948 /// changes tree state to given state
949
950 /// Since reconstruction/compression do not discard information we define them
951 /// as const ... "logical constness" not "bitwise constness".
952 /// @param[in] finalstate The final state of the tree
953 /// @param[in] fence Fence after the operation (might not be respected!!!)
954 const Function<T,NDIM>& change_tree_state(const TreeState finalstate, bool fence = true) const {
956 if (not impl) return *this;
957 TreeState current_state = impl->get_tree_state();
958 if (finalstate == current_state) return *this;
959 MADNESS_CHECK_THROW(current_state != TreeState::unknown, "unknown tree state");
960
961 impl->change_tree_state(finalstate, fence);
962 if (fence && VERIFY_TREE) verify_tree();
963 return *this;
964 }
965
966 /// Sums scaling coeffs down tree restoring state with coeffs only at leaves. Optional fence. Possible non-blocking comm.
967 void sum_down(bool fence = true) const {
969 verify();
970 MADNESS_CHECK_THROW(impl->get_tree_state()==redundant_after_merge, "sum_down requires a redundant_after_merge state");
971 const_cast<Function<T,NDIM>*>(this)->impl->sum_down(fence);
972 const_cast<Function<T,NDIM>*>(this)->impl->set_tree_state(reconstructed);
973
974 if (fence && VERIFY_TREE) verify_tree(); // Must be after in case nonstandard
975 }
976
977
978 /// Inplace autorefines the function. Optional fence. Possible non-blocking comm.
979 template <typename opT>
980 void refine_general(const opT& op, bool fence = true) const {
982 verify();
984 impl->refine(op, fence);
985 }
986
987
989 bool operator()(implT* impl, const Key<NDIM>& key, const nodeT& t) const {
990 return impl->autorefine_square_test(key, t);
991 }
992
993 template <typename Archive> void serialize (Archive& ar) {}
994 };
995
996 /// Inplace autorefines the function using same test as for squaring.
997
998 /// return this for chaining
999 const Function<T,NDIM>& refine(bool fence = true) const {
1001 return *this;
1002 }
1003
1004 /// Inplace broadens support in scaling function basis
1005
1006 /// N.B. with fence=false the per-node norm reset is skipped: norm_tree
1007 /// keeps the -1.0 broadened marker and dnorm_tree its previous value,
1008 /// so broadening cannot be repeated until the norms are recomputed.
1010 bool fence = true) const {
1011 verify();
1012 reconstruct();
1013 impl->broaden(bc.is_periodic(), fence);
1014 }
1015
1016
1017 /// Clears the function as if constructed uninitialized. Optional fence.
1018
1019 /// Any underlying data will not be freed until the next global fence.
1020 void clear(bool fence = true) {
1022 if (impl) {
1023 World& world = impl->world;
1024 impl.reset();
1025 if (fence) world.gop.fence();
1026 }
1027 }
1028
1029 /// Process 0 prints a summary of all nodes in the tree (collective)
1030 void print_tree(std::ostream& os = std::cout) const {
1032 if (impl) impl->print_tree(os);
1033 }
1034
1035 /// same as print_tree() but produces JSON-formatted string
1036 /// @warning enclose the result in braces to make it a valid JSON object
1037 void print_tree_json(std::ostream& os = std::cout) const {
1039 if (impl) impl->print_tree_json(os);
1040 }
1041
1042 /// Process 0 prints a graphviz-formatted output of all nodes in the tree (collective)
1043 void print_tree_graphviz(std::ostream& os = std::cout) const {
1045 os << "digraph G {" << std::endl;
1046 if (impl) impl->print_tree_graphviz(os);
1047 os << "}" << std::endl;
1048 }
1049
1050 /// Print a summary of the load balancing info
1051
1052 /// This is serial and VERY expensive
1053 void print_info() const {
1055 if (impl) impl->print_info();
1056 }
1057
1059 T (*f)(T);
1060 SimpleUnaryOpWrapper(T (*f)(T)) : f(f) {}
1061 void operator()(const Key<NDIM>& key, Tensor<T>& t) const {
1062 UNARY_OPTIMIZED_ITERATOR(T, t, *_p0 = f(*_p0));
1063 }
1064 template <typename Archive> void serialize(Archive& ar) {}
1065 };
1066
1067 /// Inplace unary operation on function values
1068 void unaryop(T (*f)(T)) {
1069 // Must fence here due to temporary object on stack
1070 // stopping us returning before complete
1072 }
1073
1074
1075 /// Inplace unary operation on function values
1076 template <typename opT>
1077 void unaryop(const opT& op, bool fence=true) {
1079 verify();
1080 reconstruct();
1081 impl->unary_op_value_inplace(op, fence);
1082 }
1083
1084
1085 /// Unary operation applied inplace to the coefficients
1086 template <typename opT>
1087 void unaryop_coeff(const opT& op,
1088 bool fence = true) {
1090 verify();
1091 impl->unary_op_coeff_inplace(op, fence);
1092 }
1093
1094
1095 /// Unary operation applied inplace to the nodes
1096 template <typename opT>
1097 void unaryop_node(const opT& op,
1098 bool fence = true) {
1100 verify();
1101 impl->unary_op_node_inplace(op, fence);
1102 }
1103
1104
1105
1106
1107 static void doconj(const Key<NDIM>, Tensor<T>& t) {
1109 t.conj();
1110 }
1111
1112 /// Inplace complex conjugate. No communication except for optional fence.
1113
1114 /// Returns this for chaining. Works in either basis.
1118 return *this;
1119 }
1120
1121
1122 /// Inplace, scale the function by a constant. No communication except for optional fence.
1123
1124 /// Works in either basis. Returns reference to this for chaining.
1125 template <typename Q>
1126 Function<T,NDIM>& scale(const Q q, bool fence=true) {
1128 verify();
1129 if (VERIFY_TREE) verify_tree();
1130 impl->scale_inplace(q,fence);
1131 return *this;
1132 }
1133
1134
1135 /// Inplace add scalar. No communication except for optional fence.
1138 verify();
1139 if (VERIFY_TREE) verify_tree();
1140 impl->add_scalar_inplace(t,fence);
1141 return *this;
1142 }
1143
1144
1145 /// Inplace, general bi-linear operation in wavelet basis. No communication except for optional fence.
1146
1147 /// If the functions are not in the wavelet basis an exception is thrown since this routine
1148 /// is intended to be fast and unexpected compression is assumed to be a performance bug.
1149 ///
1150 /// Returns this for chaining, can be in states compressed of redundant_after_merge.
1151 ///
1152 /// this and other may have different distributions and may even live in different worlds
1153 ///
1154 /// this <-- this*alpha + other*beta
1155 template <typename Q, typename R>
1157 const Function<Q,NDIM>& other, const R& beta, bool fence=true) {
1159 verify();
1160 other.verify();
1161
1162 // operation is done either in compressed or reconstructed state
1163 TreeState operating_state=this->get_impl()->get_tensor_type()==TT_FULL ? compressed : reconstructed;
1164
1165 TreeState thisstate=impl->get_tree_state();
1166 TreeState otherstate=other.get_impl()->get_tree_state();
1167
1169 MADNESS_CHECK_THROW(thisstate==compressed, "gaxpy: this must be compressed");
1170 MADNESS_CHECK_THROW(otherstate==compressed, "gaxpy: other must be compressed");
1171 impl->gaxpy_inplace(alpha, *other.get_impl(), beta, fence);
1172
1173 } else if (operating_state==reconstructed) {
1174 // this works both in reconstructed and redundant_after_merge states
1176 "gaxpy: this must be reconstructed or redundant_after_merge");
1177 MADNESS_CHECK_THROW(otherstate==reconstructed or otherstate==redundant_after_merge,
1178 "gaxpy: other must be reconstructed or redundant_after_merge");
1179
1180 impl->gaxpy_inplace_reconstructed(alpha,*other.get_impl(),beta,fence);
1181 } else {
1182 MADNESS_EXCEPTION("unknown tree state",1);
1183 }
1184 return *this;
1185 }
1186
1187
1188 /// Inplace addition of functions in the wavelet basis
1189
1190 /// Using operator notation forces a global fence after every operation.
1191 /// Functions don't need to be compressed, it's the caller's responsibility
1192 /// to choose an appropriate state with performance, usually compressed for 3d,
1193 /// reconstructed for 6d)
1194 template <typename Q>
1197
1198 // do this in reconstructed or compressed form
1200 this->change_tree_state(operating_state);
1202
1203 MADNESS_ASSERT(impl->get_tree_state() == other.get_impl()->get_tree_state());
1204 if (VERIFY_TREE) verify_tree();
1205 if (VERIFY_TREE) other.verify_tree();
1206 return gaxpy(T(1.0), other, Q(1.0), true);
1207 }
1208
1209
1210 /// Inplace subtraction of functions in the wavelet basis
1211
1212 /// Using operator notation forces a global fence after every operation
1213 template <typename Q>
1216 if (NDIM<=3) {
1217 compress();
1218 other.compress();
1219 } else {
1220 reconstruct();
1221 other.reconstruct();
1222 }
1223 MADNESS_ASSERT(impl->get_tree_state() == other.get_impl()->get_tree_state());
1224 if (VERIFY_TREE) verify_tree();
1225 if (VERIFY_TREE) other.verify_tree();
1226 return gaxpy(T(1.0), other, Q(-1.0), true);
1227 }
1228
1229
1230 /// Inplace scaling by a constant
1231
1232 /// Using operator notation forces a global fence after every operation
1233 template <typename Q>
1235 operator*=(const Q q) {
1237 scale(q,true);
1238 return *this;
1239 }
1240
1241
1242 /// Inplace squaring of function ... global comm only if not reconstructed
1243
1244 /// Returns *this for chaining.
1247 if (!is_reconstructed()) reconstruct();
1248 if (VERIFY_TREE) verify_tree();
1249 impl->square_inplace(fence);
1250 return *this;
1251 }
1252
1253 /// Returns *this for chaining.
1256 if (!is_reconstructed()) reconstruct();
1257 if (VERIFY_TREE) verify_tree();
1258 impl->abs_inplace(fence);
1259 return *this;
1260 }
1261
1262 /// Returns *this for chaining.
1265 if (!is_reconstructed()) reconstruct();
1266 if (VERIFY_TREE) verify_tree();
1267 impl->abs_square_inplace(fence);
1268 return *this;
1269 }
1270
1271 /// Returns local contribution to \c int(f(x),x) ... no communication
1272
1273 /// In the wavelet basis this is just the coefficient of the first scaling
1274 /// function which is a constant. In the scaling function basis we
1275 /// must add up contributions from each box.
1276 T trace_local() const {
1278 if (!impl) return 0.0;
1279 MADNESS_CHECK_THROW(impl->has_summable_coefficients(),
1280 "trace_local needs a tree that holds its coefficients once");
1281 if (VERIFY_TREE) verify_tree();
1282 return impl->trace_local();
1283 }
1284
1285
1286 /// Returns global value of \c int(f(x),x) ... global comm required
1287
1288 /// Works in any state whose coefficients trace_local() can sum; the
1289 /// remaining states are reconstructed first. For efficient use
1290 /// especially with many functions, reconstruct them all first and use
1291 /// trace_local instead, so you can perform a global sum on all at the
1292 /// same time.
1293 ///
1294 /// Throws if the function is on-demand, and fences before reconstructing;
1295 /// see norm2() for both, including why the branch stays collective.
1296 T trace() const {
1298 if (!impl) return 0.0;
1299 if (!impl->has_summable_coefficients()) {
1301 "trace is not defined for an on-demand function; materialize it first");
1302 impl->world.gop.fence();
1303 reconstruct();
1304 }
1305 if (VERIFY_TREE) verify_tree();
1306 T sum = impl->trace_local();
1307 impl->world.gop.sum(sum);
1308 impl->world.gop.fence();
1309 return sum;
1310 }
1311
1312
1313 /// Returns local part of inner product ... throws if both not compressed
1314 template <typename R>
1315 TENSOR_RESULT_TYPE(T,R) inner_local(const Function<R,NDIM>& g) const {
1322 return impl->inner_local(*(g.get_impl()));
1323 }
1324
1325 /// Returns local part of dot product ... throws if both not compressed
1326 template <typename R>
1327 TENSOR_RESULT_TYPE(T,R) dot_local(const Function<R,NDIM>& g) const {
1330 MADNESS_ASSERT(g.is_compressed());
1332 if (VERIFY_TREE) g.verify_tree();
1333 return impl->dot_local(*(g.get_impl()));
1334 }
1335
1336
1337 /// With this being an on-demand function, fill the MRA tree according to different criteria
1338
1339 /// @param[in] g the function after which the MRA structure is modeled (any basis works)
1340 template<typename R>
1342 MADNESS_ASSERT(g.is_initialized());
1344
1345 // clear what we have
1346 impl->get_coeffs().clear();
1347
1348 //leaf_op<T,NDIM> gnode_is_leaf(g.get_impl().get());
1349 Leaf_op_other<T,NDIM> gnode_is_leaf(g.get_impl().get());
1350 impl->make_Vphi(gnode_is_leaf,fence);
1351 return *this;
1352
1353 }
1354
1355 /// With this being an on-demand function, fill the MRA tree according to different criteria
1356
1357 /// @param[in] op the convolution operator for screening
1358 template<typename opT>
1359 Function<T,NDIM>& fill_tree(const opT& op, bool fence=true) {
1361 // clear what we have
1362 impl->get_coeffs().clear();
1365 impl ->make_Vphi(leaf_op,fence);
1366 return *this;
1367 }
1368
1369 /// With this being an on-demand function, fill the MRA tree according to different criteria
1372 // clear what we have
1373 impl->get_coeffs().clear();
1375 impl->make_Vphi(leaf_op,fence);
1376 return *this;
1377 }
1378
1379 /// Special refinement on 6D boxes where the electrons come close (meet)
1380 /// @param[in] op the convolution operator for screening
1381 template<typename opT>
1382 Function<T,NDIM>& fill_cuspy_tree(const opT& op,const bool fence=true){
1384 // clear what we have
1385 impl->get_coeffs().clear();
1387
1389 impl ->make_Vphi(leaf_op,fence);
1390
1391 return *this;
1392 }
1393
1394 /// Special refinement on 6D boxes where the electrons come close (meet)
1397 // clear what we have
1398 impl->get_coeffs().clear();
1400
1402 impl ->make_Vphi(leaf_op,fence);
1403
1404 return *this;
1405 }
1406
1407 /// Special refinement on 6D boxes for the nuclear potentials (regularized with cusp, non-regularized with singularity)
1408 /// @param[in] op the convolution operator for screening
1409 template<typename opT>
1410 Function<T,NDIM>& fill_nuclear_cuspy_tree(const opT& op,const size_t particle,const bool fence=true){
1412 // clear what we have
1413 impl->get_coeffs().clear();
1415
1417 impl ->make_Vphi(leaf_op,fence);
1418
1419 return *this;
1420 }
1421
1422 /// Special refinement on 6D boxes for the nuclear potentials (regularized with cusp, non-regularized with singularity)
1425 // clear what we have
1426 impl->get_coeffs().clear();
1428
1430 impl ->make_Vphi(leaf_op,fence);
1431
1432 return *this;
1433 }
1434
1435 /// perform the hartree product of f*g, invoked by result
1436 template<size_t LDIM, size_t KDIM, typename opT>
1437 void do_hartree_product(const std::vector<std::shared_ptr<FunctionImpl<T,LDIM>>> left,
1438 const std::vector<std::shared_ptr<FunctionImpl<T,KDIM>>> right,
1439 const opT* op) {
1440
1441 // get the right leaf operator
1443 impl->hartree_product(left,right,leaf_op,true);
1444 impl->finalize_sum();
1445// this->truncate();
1446
1447 }
1448
1449 /// perform the hartree product of f*g, invoked by result
1450 template<size_t LDIM, size_t KDIM>
1451 void do_hartree_product(const std::vector<std::shared_ptr<FunctionImpl<T,LDIM>>> left,
1452 const std::vector<std::shared_ptr<FunctionImpl<T,KDIM>>> right) {
1453
1454// hartree_leaf_op<T,KDIM+LDIM> leaf_op(impl.get(),cdata.s0);
1456 impl->hartree_product(left,right,leaf_op,true);
1457 impl->finalize_sum();
1458// this->truncate();
1459
1460 }
1461
1462 /// Returns the inner product
1463
1464 /// Not efficient for computing multiple inner products
1465 /// @param[in] g Function, optionally on-demand
1466 template <typename R>
1469
1470 // fast return if possible
1471 if (not this->is_initialized()) return 0.0;
1472 if (not g.is_initialized()) return 0.0;
1473
1474 // if this and g are the same, use norm2()
1475 if constexpr (std::is_same_v<T,R>) {
1476 if (this->get_impl() == g.get_impl()) {
1477 // let norm2() handle tree state
1478 double norm = this->norm2();
1479 return norm * norm;
1480 }
1481 }
1482
1483 // do it case-by-case
1484 if constexpr (std::is_same_v<R,T>) {
1485 if (this->is_on_demand())
1486 return g.inner_on_demand(*this);
1487 if (g.is_on_demand())
1488 return this->inner_on_demand(g);
1489 }
1490
1492 if (VERIFY_TREE) g.verify_tree();
1493
1494 // compute in compressed form if compression is fast, otherwise in redundant form
1496
1498 g.change_tree_state(operating_state,false);
1499 impl->world.gop.fence();
1500
1501 TENSOR_RESULT_TYPE(T,R) local = impl->inner_local(*g.get_impl());
1502 impl->world.gop.sum(local);
1503 impl->world.gop.fence();
1504
1505 // restore state -- no need for this
1506 // change_tree_state(state,false);
1507 // g.change_tree_state(gstate,false);
1508 // impl->world.gop.fence();
1509
1510 return local;
1511 }
1512
1513 /// Return the local part of inner product with external function ... no communication.
1514 /// If you are going to be doing a bunch of inner_ext calls, set
1515 /// keep_redundant to true and then manually undo_redundant when you
1516 /// are finished.
1517 /// @param[in] f Pointer to function of type T that take coordT arguments. This is the externally provided function
1518 /// @param[in] leaf_refine boolean switch to turn on/off refinement past leaf nodes
1519 /// @param[in] keep_redundant boolean switch to turn on/off undo_redundant
1520 /// @return Returns local part of the inner product, i.e. over the domain of all function nodes on this compute node.
1521 T inner_ext_local(const std::shared_ptr< FunctionFunctorInterface<T,NDIM> > f, const bool leaf_refine=true, const bool keep_redundant=false) const {
1524 T local = impl->inner_ext_local(f, leaf_refine);
1525 if (not keep_redundant) change_tree_state(reconstructed);
1526 return local;
1527 }
1528
1529 /// Return the inner product with external function ... requires communication.
1530 /// If you are going to be doing a bunch of inner_ext calls, set
1531 /// keep_redundant to true and then manually undo_redundant when you
1532 /// are finished.
1533 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
1534 /// @param[in] leaf_refine boolean switch to turn on/off refinement past leaf nodes
1535 /// @param[in] keep_redundant boolean switch to turn on/off undo_redundant
1536 /// @return Returns the inner product
1537 T inner_ext(const std::shared_ptr< FunctionFunctorInterface<T,NDIM> > f, const bool leaf_refine=true, const bool keep_redundant=false) const {
1540 T local = impl->inner_ext_local(f, leaf_refine);
1541 impl->world.gop.sum(local);
1542 impl->world.gop.fence();
1543 if (not keep_redundant) change_tree_state(reconstructed);
1544 return local;
1545 }
1546
1547 /// Return the inner product with external function ... requires communication.
1548 /// If you are going to be doing a bunch of inner_ext calls, set
1549 /// keep_redundant to true and then manually undo_redundant when you
1550 /// are finished.
1551 /// @param[in] f Reference to FunctionFunctorInterface. This is the externally provided function
1552 /// @param[in] leaf_refine boolean switch to turn on/off refinement past leaf nodes
1553 /// @return Returns the inner product
1555 const bool leaf_refine=true) const {
1557 reconstruct();
1558 T local = impl->inner_adaptive_local(f, leaf_refine);
1559 impl->world.gop.sum(local);
1560 impl->world.gop.fence();
1561 return local;
1562 }
1563
1564 /// Return the local part of gaxpy with external function, this*alpha + f*beta ... no communication.
1565 /// @param[in] alpha prefactor for this Function
1566 /// @param[in] f Pointer to function of type T that take coordT arguments. This is the externally provided function
1567 /// @param[in] beta prefactor for f
1568 template <typename L>
1569 void gaxpy_ext(const Function<L,NDIM>& left, T (*f)(const coordT&), T alpha, T beta, double tol, bool fence=true) const {
1571 if (!left.is_reconstructed()) left.reconstruct();
1572 impl->gaxpy_ext(left.get_impl().get(), f, alpha, beta, tol, fence);
1573 }
1574
1575 /// Returns the inner product for one on-demand function
1576
1577 /// It does work, but it might not give you the precision you expect.
1578 /// The assumption is that the function g returns proper sum
1579 /// coefficients on the MRA tree of this. This might not be the case if
1580 /// g is constructed with an implicit multiplication, e.g.
1581 /// result = <this|g>, with g = 1/r12 | gg>
1582 /// @param[in] g on-demand function
1583 template<typename R>
1584 TENSOR_RESULT_TYPE(T, R) inner_on_demand(const Function<R, NDIM>& g) const {
1585 MADNESS_ASSERT(g.is_on_demand() and (not this->is_on_demand()));
1586
1587 constexpr std::size_t LDIM=std::max(NDIM/2,std::size_t(1));
1588 auto func=dynamic_cast<CompositeFunctorInterface<T,NDIM,LDIM>* >(g.get_impl()->get_functor().get());
1590 func->make_redundant(true);
1591 func->replicate_low_dim_functions(true);
1592 this->reconstruct(); // if this == &g we don't need g to be redundant
1593
1595
1596 TENSOR_RESULT_TYPE(T, R) local = impl->inner_local_on_demand(*g.get_impl());
1597 impl->world.gop.sum(local);
1598 impl->world.gop.fence();
1599
1600 return local;
1601 }
1602
1603 /// project this on the low-dim function g: h(x) = <f(x,y) | g(y)>
1604
1605 /// @param[in] g low-dim function
1606 /// @param[in] dim over which dimensions to be integrated: 0..LDIM-1 or LDIM..NDIM-1
1607 /// @return new function of dimension NDIM-LDIM
1608 template <typename R, size_t LDIM>
1610 if (NDIM<=LDIM) MADNESS_EXCEPTION("confused dimensions in project_out?",1);
1611 MADNESS_CHECK_THROW(dim==0 or dim==1,"dim must be 0 or 1 in project_out");
1612 verify();
1613 typedef TENSOR_RESULT_TYPE(T,R) resultT;
1614 static const size_t KDIM=NDIM-LDIM;
1615
1617 .k(g.k()).thresh(g.thresh());
1618 Function<resultT,KDIM> result=factory; // no empty() here!
1619
1621 g.change_tree_state(redundant,false);
1622 world().gop.fence();
1623 this->get_impl()->project_out(result.get_impl().get(),g.get_impl().get(),dim,true);
1624// result.get_impl()->project_out2(this->get_impl().get(),gimpl,dim);
1625 result.world().gop.fence();
1626 g.change_tree_state(reconstructed,false);
1627 result.get_impl()->trickle_down(false);
1628 result.get_impl()->set_tree_state(reconstructed);
1629 result.world().gop.fence();
1630 return result;
1631 }
1632
1633 Function<T,NDIM/2> dirac_convolution(const bool fence=true) const {
1634 constexpr std::size_t LDIM=NDIM/2;
1635 MADNESS_CHECK_THROW(NDIM==2*LDIM,"NDIM must be even");
1636// // this will be the result function
1638 Function<T,LDIM> f = factory;
1639 if(!is_reconstructed()) this->reconstruct();
1640 this->get_impl()->do_dirac_convolution(f.get_impl().get(),fence);
1641 return f;
1642 }
1643
1644 /// Replaces this function with one loaded from an archive using the default processor map
1645
1646 /// Archive can be sequential or parallel.
1647 ///
1648 /// The & operator for serializing will only work with parallel archives.
1649 template <typename Archive>
1650 void load(World& world, Archive& ar) {
1652 // Type checking since we are probably circumventing the archive's own type checking
1653 long magic = 0l, id = 0l, ndim = 0l, k = 0l;
1654 Tensor<double> cell;
1655 ar & magic & id & ndim & k & cell;
1656 // CHECK not ASSERT: ASSERT is compiled out when ASSERTION_TYPE=disable
1658 "Function archive was written by an incompatible MADNESS version; regenerate it.");
1660 MADNESS_CHECK(ndim == NDIM);
1661
1662 // if simulation cell is set it must match the cell from function on file.
1663 // if simulation cell is not set set it to the one found on file
1664 // -- for the latter the only use case seems a python script for plotting
1666 if ((cell-FunctionDefaults<NDIM>::get_cell()).normf()>1.e-14) {
1667 std::ostringstream oss;
1668 oss << "simulation cells inconsistent: stored cell differs from FunctionDefaults cell.\n"
1669 << "Call FunctionDefaults<" << NDIM << ">::clear_cell() before reloading "
1670 << "(this will render all existing functions useless!)";
1671 MADNESS_EXCEPTION(oss.str().c_str(), 1);
1672 }
1673 } else { // no cell set in the defaults: use the one from file
1675 }
1676
1677 impl.reset(new implT(FunctionFactory<T,NDIM>(world).k(k).empty()));
1678 impl->load(ar);
1679 }
1680
1681
1682 /// Stores the function to an archive
1683
1684 /// Archive can be sequential or parallel.
1685 ///
1686 /// The & operator for serializing will only work with parallel archives.
1687 template <typename Archive>
1688 void store(Archive& ar) const {
1690 verify();
1691 // For type checking, etc.
1692 ar & long(FUNCTION_ARCHIVE_MAGIC) & long(TensorTypeData<T>::id) & long(NDIM) & long(k()) & impl->get_cell();
1693
1694 impl->store(ar);
1695 }
1696
1697 /// change the tensor type of the coefficients in the FunctionNode
1698
1699 /// @param[in] targs target tensor arguments (threshold and full/low rank)
1700 void change_tensor_type(const TensorArgs& targs, bool fence=true) {
1701 if (not impl) return;
1702 impl->change_tensor_type1(targs,fence);
1703 }
1704
1705
1706 /// This is replaced with left*right ... private
1707 template <typename Q, typename opT>
1709 const opT& op, bool fence) {
1711 func.verify();
1712 MADNESS_ASSERT(func.is_reconstructed());
1713 if (VERIFY_TREE) func.verify_tree();
1714 impl.reset(new implT(*func.get_impl(), func.get_pmap(), false));
1715 impl->unaryXX(func.get_impl().get(), op, fence);
1716 return *this;
1717 }
1718
1719 /// Returns vector of FunctionImpl pointers corresponding to vector of functions
1720 template <typename Q, std::size_t D>
1721 static std::vector< std::shared_ptr< FunctionImpl<Q,D> > > vimpl(const std::vector< Function<Q,D> >& v) {
1723 std::vector< std::shared_ptr< FunctionImpl<Q,D> > > r(v.size());
1724 for (unsigned int i=0; i<v.size(); ++i) r[i] = v[i].get_impl();
1725 return r;
1726 }
1727
1728 /// This is replaced with op(vector of functions) ... private
1729 template <typename opT>
1730 Function<T,NDIM>& multiop_values(const opT& op, const std::vector< Function<T,NDIM> >& vf) {
1731 std::vector<implT*> v(vf.size(),NULL);
1732 for (unsigned int i=0; i<v.size(); ++i) {
1733 if (vf[i].is_initialized()) v[i] = vf[i].get_impl().get();
1734 }
1735 impl->multiop_values(op, v);
1736 world().gop.fence();
1737 if (VERIFY_TREE) verify_tree();
1738
1739 return *this;
1740 }
1741
1742 /// apply op on the input vector yielding an output vector of functions
1743
1744 /// (*this) is just a dummy Function to be able to call internal methods in FuncImpl
1745 /// @param[in] op the operator working on vin
1746 /// @param[in] vin vector of input Functions
1747 /// @param[out] vout vector of output Functions vout = op(vin)
1748 template <typename opT>
1750 const std::vector< Function<T,NDIM> >& vin,
1751 std::vector< Function<T,NDIM> >& vout,
1752 const bool fence=true) {
1753 std::vector<implT*> vimplin(vin.size(),NULL);
1754 for (unsigned int i=0; i<vin.size(); ++i) {
1755 if (vin[i].is_initialized()) vimplin[i] = vin[i].get_impl().get();
1756 }
1757 std::vector<implT*> vimplout(vout.size(),NULL);
1758 for (unsigned int i=0; i<vout.size(); ++i) {
1759 if (vout[i].is_initialized()) vimplout[i] = vout[i].get_impl().get();
1760 }
1761
1762 impl->multi_to_multi_op_values(op, vimplin, vimplout, fence);
1763 if (VERIFY_TREE) verify_tree();
1764
1765 }
1766
1767
1768 /// Multiplication of function * vector of functions using recursive algorithm of mulxx
1769 template <typename L, typename R>
1770 void vmulXX(const Function<L,NDIM>& left,
1771 const std::vector< Function<R,NDIM> >& right,
1772 std::vector< Function<T,NDIM> >& result,
1773 double tol,
1774 bool fence) {
1776
1777 std::vector<FunctionImpl<T,NDIM>*> vresult(right.size());
1778 std::vector<const FunctionImpl<R,NDIM>*> vright(right.size());
1779 for (unsigned int i=0; i<right.size(); ++i) {
1780 result[i].set_impl(left,false);
1781 // set_impl copies left's state, which is redundant here, but the kernel builds
1782 // a reconstructed tree (interior nodes carry no coefficients)
1783 result[i].get_impl()->set_tree_state(reconstructed);
1784 vresult[i] = result[i].impl.get();
1785 vright[i] = right[i].get_impl().get();
1786 }
1787
1788 left.world().gop.fence(); // Is this still essential? Yes.
1789 vresult[0]->mulXXvec(left.get_impl().get(), vright, vresult, tol, fence);
1790 }
1791
1792 /// Same as \c operator* but with optional fence and no automatic reconstruction
1793
1794 /// f or g are on-demand functions
1795 template<typename L, typename R>
1796 void mul_on_demand(const Function<L,NDIM>& f, const Function<R,NDIM>& g, bool fence=true) {
1797 const FunctionImpl<L,NDIM>* fimpl=f.get_impl().get();
1798 const FunctionImpl<R,NDIM>* gimpl=g.get_impl().get();
1799 if (fimpl->is_on_demand() and gimpl->is_on_demand()) {
1800 MADNESS_EXCEPTION("can't multiply two on-demand functions",1);
1801 }
1802
1803 if (fimpl->is_on_demand()) {
1804 leaf_op<R,NDIM> leaf_op1(gimpl);
1805 impl->multiply(leaf_op1,gimpl,fimpl,fence);
1806 } else {
1807 leaf_op<L,NDIM> leaf_op1(fimpl);
1808 impl->multiply(leaf_op1,fimpl,gimpl,fence);
1809 }
1810 }
1811
1812 /// sparse transformation of a vector of functions ... private
1813 template <typename R, typename Q>
1814 void vtransform(const std::vector< Function<R,NDIM> >& v,
1815 const Tensor<Q>& c,
1816 std::vector< Function<T,NDIM> >& vresult,
1817 double tol,
1818 bool fence=true) {
1820 vresult[0].impl->vtransform(vimpl(v), c, vimpl(vresult), tol, fence);
1821 }
1822
1823 /// This is replaced with alpha*left + beta*right ... private
1824 template <typename L, typename R>
1826 T beta, const Function<R,NDIM>& right, bool fence) {
1828 left.verify();
1829 right.verify();
1830 MADNESS_ASSERT(left.is_compressed() && right.is_compressed());
1831 if (VERIFY_TREE) left.verify_tree();
1832 if (VERIFY_TREE) right.verify_tree();
1833 impl.reset(new implT(*left.get_impl(), left.get_pmap(), false));
1834 impl->gaxpy(alpha,*left.get_impl(),beta,*right.get_impl(),fence);
1835 return *this;
1836 }
1837
1838 /// This is replaced with mapdim(f) ... private
1839 Function<T,NDIM>& mapdim(const Function<T,NDIM>& f, const std::vector<long>& map, bool fence) {
1841 f.verify();
1842 if (VERIFY_TREE) f.verify_tree();
1843 for (std::size_t i=0; i<NDIM; ++i) MADNESS_ASSERT(map[i]>=0 && static_cast<std::size_t>(map[i])<NDIM);
1844 impl.reset(new implT(*f.impl, f.get_pmap(), false));
1845 impl->mapdim(*f.impl,map,fence);
1846 return *this;
1847 }
1848
1849 /// This is replaced with mirror(f) ... private
1850
1851 /// similar to mapdim, but maps from x to -x, y to -y, and so on
1852 /// Example: mirror a 3d function on the xy plane: mirror={1,1,-1}
1853 /// @param[in] mirror array of -1 and 1, corresponding to mirror or not
1854 Function<T,NDIM>& mirror(const Function<T,NDIM>& f, const std::vector<long>& mirrormap, bool fence) {
1856 f.verify();
1857 if (VERIFY_TREE) f.verify_tree();
1858 for (std::size_t i=0; i<NDIM; ++i) MADNESS_ASSERT((mirrormap[i]==1) or (mirrormap[i]==-1));
1859 impl.reset(new implT(*f.impl, f.get_pmap(), false));
1860 impl->mirror(*f.impl,mirrormap,fence);
1861 return *this;
1862 }
1863
1864 /// This is replaced with mirror(map(f)) ... private
1865
1866 /// first map then mirror!
1867 /// mirror is similar to mapdim, but maps from x to -x, y to -y, and so on
1868 /// Example: mirror a 3d function on the xy plane: mirror={1,1,-1}
1869 /// Example: c4 rotation of a 3d function around the z axis:
1870 /// x->y, y->-x, z->z: map(1,0,2); mirror(-1,1,1)
1871 /// @param[in] map array holding dimensions
1872 /// @param[in] mirror array of -1 and 1, corresponding to mirror or not
1874 const std::vector<long>& map, const std::vector<long>& mirror,
1875 bool fence) {
1877 f.verify();
1878 if (VERIFY_TREE) f.verify_tree();
1879 for (std::size_t i=0; i<mirror.size(); ++i) MADNESS_ASSERT((mirror[i]==1) or (mirror[i]==-1));
1880 for (std::size_t i=0; i<map.size(); ++i) MADNESS_ASSERT(map[i]>=0 && static_cast<std::size_t>(map[i])<NDIM);
1881
1882 impl.reset(new implT(*f.impl, f.get_pmap(), false));
1883 impl->map_and_mirror(*f.impl,map,mirror,fence);
1884 return *this;
1885 }
1886
1887
1888 /// check symmetry of a function by computing the 2nd derivative
1889 double check_symmetry() const {
1890
1892 if (VERIFY_TREE) verify_tree();
1893 double local = impl->check_symmetry_local();
1894 impl->world.gop.sum(local);
1895 impl->world.gop.fence();
1896 double asy=sqrt(local);
1897 if (this->world().rank()==0) print("asymmetry wrt particle",asy);
1899 return asy;
1900 }
1901
1902 /// reduce the rank of the coefficient tensors
1903 Function<T,NDIM>& reduce_rank(const double thresh=0.0, const bool fence=true) {
1904 verify();
1905 double thresh1= (thresh==0.0) ? impl->get_tensor_args().thresh : thresh;
1906 impl->reduce_rank(thresh1,fence);
1907 return *this;
1908 }
1909
1910 /// remove all nodes with level higher than n
1911 Function<T,NDIM>& chop_at_level(const int n, const bool fence=true) {
1912 verify();
1914 impl->chop_at_level(n,true);
1916 return *this;
1917 }
1918 };
1919
1920// template <typename T, typename opT, std::size_t NDIM>
1921 template <typename T, typename opT, std::size_t NDIM>
1922 Function<T,NDIM> multiop_values(const opT& op, const std::vector< Function<T,NDIM> >& vf) {
1924 r.set_impl(vf[0], false);
1925 r.multiop_values(op, vf);
1926 return r;
1927 }
1928
1929 /// Returns new function equal to alpha*f(x) with optional fence
1930 template <typename Q, typename T, std::size_t NDIM>
1932 mul(const Q alpha, const Function<T,NDIM>& f, bool fence=true) {
1934 f.verify();
1935 if (VERIFY_TREE) f.verify_tree();
1936 Function<TENSOR_RESULT_TYPE(Q,T),NDIM> result;
1937 result.set_impl(f, false);
1938 result.get_impl()->scale_oop(alpha,*f.get_impl(),fence);
1939 return result;
1940 }
1941
1942
1943 /// Returns new function equal to f(x)*alpha with optional fence
1944 template <typename Q, typename T, std::size_t NDIM>
1946 mul(const Function<T,NDIM>& f, const Q alpha, bool fence=true) {
1948 return mul(alpha,f,fence);
1949 }
1950
1951
1952 /// Returns new function equal to f(x)*alpha
1953
1954 /// Using operator notation forces a global fence after each operation
1955 template <typename Q, typename T, std::size_t NDIM>
1958 return mul(alpha, f, true);
1959 }
1960
1961 /// Returns new function equal to alpha*f(x)
1962
1963 /// Using operator notation forces a global fence after each operation
1964 template <typename Q, typename T, std::size_t NDIM>
1967 return mul(alpha, f, true);
1968 }
1969
1970 /// Sparse multiplication; the scalar interface redirects to the vector one in vmra.h
1971
1972 /// @param[in] tol target absolute accuracy of the product; see the vector mul_sparse in
1973 /// vmra.h for the semantics, including the internal safety margin and tol=0
1974 /// @param[in] do_make_redundant if false, both inputs must already be redundant
1975 template <typename L, typename R,std::size_t NDIM>
1977 mul_sparse(const Function<L,NDIM>& left, const Function<R,NDIM>& right, double tol,
1978 bool fence=true, bool do_make_redundant=true) {
1980 left.verify();
1981 right.verify();
1982 std::vector< Function<R,NDIM> > vright(1,right);
1983 return mul_sparse(left.get_impl()->world, left, vright, tol, fence, do_make_redundant)[0];
1984 }
1985
1986 /// Same as \c operator* but with optional fence; see mul_sparse to screen
1987 template <typename L, typename R,std::size_t NDIM>
1989 mul(const Function<L,NDIM>& left, const Function<R,NDIM>& right, bool fence=true,
1990 bool do_make_redundant=true) {
1991 return mul_sparse(left,right,/*tol=*/0.0,fence,do_make_redundant);
1992 }
1993
1994 /// Generate new function = op(left,right) where op acts on the function values
1995 template <typename L, typename R, typename opT, std::size_t NDIM>
1997 binary_op(const Function<L,NDIM>& left, const Function<R,NDIM>& right, const opT& op, bool fence=true) {
1999 if (!left.is_reconstructed()) left.reconstruct();
2000 if (!right.is_reconstructed()) right.reconstruct();
2001
2003 result.set_impl(left, false);
2004 result.get_impl()->binaryXX(left.get_impl().get(), right.get_impl().get(), op, fence);
2005 return result;
2006 }
2007
2008 /// Out of place application of unary operation to function values with optional fence
2009 template <typename Q, typename opT, std::size_t NDIM>
2010 Function<typename opT::resultT, NDIM>
2011 unary_op(const Function<Q,NDIM>& func, const opT& op, bool fence=true) {
2012 if (!func.is_reconstructed()) func.reconstruct();
2015 result.set_impl(func, false);
2016 result.get_impl()->unaryXXvalues(func.get_impl().get(), op, fence);
2017 return result;
2018 }
2019
2020
2021 /// Out of place application of unary operation to scaling function coefficients with optional fence
2022 template <typename Q, typename opT, std::size_t NDIM>
2023 Function<typename opT::resultT, NDIM>
2024 unary_op_coeffs(const Function<Q,NDIM>& func, const opT& op, bool fence=true) {
2025 if (!func.is_reconstructed()) func.reconstruct();
2027 return result.unary_op_coeffs(func,op,fence);
2028 }
2029
2030 /// Use the vmra/mul(...) interface instead
2031
2032 /// This so that we don't have to have friend functions in a different header.
2033 ///
2034 /// left and right must be in redundant state, with tree norms available.
2035 template <typename L, typename R, std::size_t D>
2036 std::vector< Function<TENSOR_RESULT_TYPE(L,R),D> >
2037 vmulXX(const Function<L,D>& left, const std::vector< Function<R,D> >& vright, double tol, bool fence=true) {
2038 if (vright.size() == 0) return std::vector< Function<TENSOR_RESULT_TYPE(L,R),D> >();
2039 std::vector< Function<TENSOR_RESULT_TYPE(L,R),D> > vresult(vright.size());
2040 vresult[0].vmulXX(left, vright, vresult, tol, fence);
2041 return vresult;
2042 }
2043
2044 /// Multiplies two functions with the new result being of type TensorResultType<L,R>
2045
2046 /// Using operator notation forces a global fence after each operation but also
2047 /// enables us to automatically reconstruct the input functions as required.
2048 template <typename L, typename R, std::size_t NDIM>
2050 operator*(const Function<L,NDIM>& left, const Function<R,NDIM>& right) {
2051 if (!left.is_reconstructed()) left.reconstruct();
2052 if (!right.is_reconstructed()) right.reconstruct();
2053 MADNESS_ASSERT(not (left.is_on_demand() or right.is_on_demand()));
2054 return mul(left,right,true);
2055 }
2056
2057 /// Performs a Hartree/outer product on the two given low-dimensional function vectors
2058
2059 /// @return result(x,y) = \sum_i f_i(x) g_i(y)
2060 template<typename T, std::size_t KDIM, std::size_t LDIM>
2061 Function<T,KDIM+LDIM>
2062 hartree_product(const std::vector<Function<T,KDIM>>& left, const std::vector<Function<T,LDIM>>& right) {
2063
2064 MADNESS_CHECK_THROW(left.size()==right.size(), "hartree_product: left and right must have same size");
2065 if (left.size()==0) return Function<T,KDIM+LDIM>();
2066
2068
2070 .k(left.front().k()).thresh(thresh);
2071 Function<T,KDIM+LDIM> result=factory.empty();
2072
2073 // some prep work
2076 std::vector<std::shared_ptr<FunctionImpl<T,KDIM>>> vleft=get_impl(left);
2077 std::vector<std::shared_ptr<FunctionImpl<T,LDIM>>> vright=get_impl(right);
2078
2079 result.do_hartree_product(vleft,vright);
2080
2081 return result;
2082
2083 }
2084
2085 /// Performs a Hartree product on the two given low-dimensional functions
2086 template<typename T, std::size_t KDIM, std::size_t LDIM>
2087 Function<T,KDIM+LDIM>
2089 typedef std::vector<Function<T,KDIM>> vector;
2090 return hartree_product(vector({left2}),vector({right2}));
2091 }
2092
2093 /// Performs a Hartree product on the two given low-dimensional functions
2094 template<typename T, std::size_t KDIM, std::size_t LDIM, typename opT>
2095 Function<T,KDIM+LDIM>
2097 const opT& op) {
2098
2099 // we need both sum and difference coeffs for error estimation
2100 Function<T,KDIM>& left = const_cast< Function<T,KDIM>& >(left2);
2101 Function<T,LDIM>& right = const_cast< Function<T,LDIM>& >(right2);
2102
2104
2106 .k(left.k()).thresh(thresh);
2107 Function<T,KDIM+LDIM> result=factory.empty();
2108
2109 if (result.world().rank()==0) {
2110 print("incomplete FunctionFactory in Function::hartree_product");
2111 print("thresh: ", thresh);
2112 }
2113 bool same=(left2.get_impl()==right2.get_impl());
2114
2115 // some prep work
2116 left.make_nonstandard(true, true);
2117 right.make_nonstandard(true, true);
2118
2119 std::vector<std::shared_ptr<FunctionImpl<T,KDIM>>> vleft;
2120 std::vector<std::shared_ptr<FunctionImpl<T,LDIM>>> vright;
2121 vleft.push_back(left.get_impl());
2122 vright.push_back(right.get_impl());
2123 result.do_hartree_product(vleft,right,&op);
2124
2125 left.standard(false);
2126 if (not same) right.standard(false);
2127 left2.world().gop.fence();
2128
2129 return result;
2130 }
2131
2132 /// adds beta*right only left: alpha*left + beta*right optional fence and no automatic compression
2133
2134 /// left and right might live in different worlds, the accumulation is non-blocking
2135 template <typename L, typename R,std::size_t NDIM>
2136 void
2138 TENSOR_RESULT_TYPE(L,R) beta, const Function<R,NDIM>& right, bool fence=true) {
2141 left.gaxpy(alpha, right, beta, fence);
2142 }
2143
2144 /// Returns new function alpha*left + beta*right optional fence and no automatic compression
2145 template <typename L, typename R,std::size_t NDIM>
2148 TENSOR_RESULT_TYPE(L,R) beta, const Function<R,NDIM>& right, bool fence=true) {
2151 return result.gaxpy_oop(alpha, left, beta, right, fence);
2152 }
2153
2154 /// Same as \c operator+ but with optional fence and no automatic compression
2155 template <typename L, typename R,std::size_t NDIM>
2157 add(const Function<L,NDIM>& left, const Function<R,NDIM>& right, bool fence=true) {
2158 return gaxpy_oop(TENSOR_RESULT_TYPE(L,R)(1.0), left,
2159 TENSOR_RESULT_TYPE(L,R)(1.0), right, fence);
2160 }
2161
2162
2163 /// Returns new function alpha*left + beta*right optional fence, having both addends reconstructed
2164 template<typename T, std::size_t NDIM>
2166 const double beta, const Function<T,NDIM>& right, const bool fence=true) {
2167 Function<T,NDIM> result;
2168 result.set_impl(right,false);
2169
2172 result.get_impl()->gaxpy_oop_reconstructed(alpha,*left.get_impl(),beta,*right.get_impl(),fence);
2173 return result;
2174
2175 }
2176
2177 /// Adds two functions with the new result being of type TensorResultType<L,R>
2178
2179 /// Using operator notation forces a global fence after each operation
2180 template <typename L, typename R, std::size_t NDIM>
2182 operator+(const Function<L,NDIM>& left, const Function<R,NDIM>& right) {
2183 if (VERIFY_TREE) left.verify_tree();
2184 if (VERIFY_TREE) right.verify_tree();
2185
2186 TreeState operating_state=left.get_impl()->get_tensor_type()==TT_FULL ? compressed : reconstructed;
2187 // no compression for high-dimensional functions
2189 left.reconstruct();
2190 right.reconstruct();
2191 return gaxpy_oop_reconstructed(1.0,left,1.0,right,true);
2192 } else {
2193 if (!left.is_compressed()) left.compress();
2194 if (!right.is_compressed()) right.compress();
2195 return add(left,right,true);
2196 }
2197 }
2198
2199 /// Same as \c operator- but with optional fence and no automatic compression
2200 template <typename L, typename R,std::size_t NDIM>
2202 sub(const Function<L,NDIM>& left, const Function<R,NDIM>& right, bool fence=true) {
2203 return gaxpy_oop(TENSOR_RESULT_TYPE(L,R)(1.0), left,
2204 TENSOR_RESULT_TYPE(L,R)(-1.0), right, fence);
2205 }
2206
2207
2208 /// Subtracts two functions with the new result being of type TensorResultType<L,R>
2209
2210 /// Using operator notation forces a global fence after each operation
2211 template <typename L, typename R, std::size_t NDIM>
2213 operator-(const Function<L,NDIM>& left, const Function<R,NDIM>& right) {
2215 // no compression for high-dimensional functions
2216 if (NDIM==6) {
2217 left.reconstruct();
2218 right.reconstruct();
2219 return gaxpy_oop_reconstructed(1.0,left,-1.0,right,true);
2220 } else {
2221 if (!left.is_compressed()) left.compress();
2222 if (!right.is_compressed()) right.compress();
2223 return sub(left,right,true);
2224 }
2225 }
2226
2227 /// Create a new copy of the function with different distribution and optional fence
2228
2229 /// Works in either basis. Different distributions imply
2230 /// asynchronous communication and the optional fence is
2231 /// collective.
2232 template <typename T, std::size_t NDIM>
2234 const std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > >& pmap,
2235 bool fence = true) {
2237 f.verify();
2238 Function<T,NDIM> result;
2240 result.set_impl(std::shared_ptr<implT>(new implT(*f.get_impl(), pmap, false)));
2241 result.get_impl()->copy_coeffs(*f.get_impl(), fence);
2242 if (VERIFY_TREE) result.verify_tree();
2243 return result;
2244 }
2245
2246 /// Create a new copy of the function with the same distribution and optional fence
2247 template <typename T, std::size_t NDIM>
2250 return copy(f, f.get_pmap(), fence);
2251 }
2252
2253 /// Create a new copy of function f living in world (might differ from f.world)
2254
2255 /// uses the default processor map of world
2256 template <typename T, std::size_t NDIM>
2261
2262 // create a new function with pmap distribution, same parameters as f, but no coeffs
2263 Function<T,NDIM> result;
2264 result.set_impl(std::make_shared<implT>(world,*f.get_impl(), pmap, false));
2265 // copy f's coefficients to result
2266 result.get_impl()->copy_coeffs(*f.get_impl(), fence);
2267 return result;
2268 }
2269
2270 /// Type conversion implies a deep copy. No communication except for optional fence.
2271
2272 /// Works in either basis but any loss of precision may result in different errors
2273 /// in applied in a different basis.
2274 ///
2275 /// The new function is formed with the options from the default constructor.
2276 ///
2277 /// There is no automatic type conversion since this is generally a rather dangerous
2278 /// thing and because there would be no way to make the fence optional.
2279 template <typename T, typename Q, std::size_t NDIM>
2282 f.verify();
2283 Function<Q,NDIM> result;
2284 result.set_impl(f, false);
2285 result.get_impl()->copy_coeffs(*f.get_impl(), fence);
2286 return result;
2287 }
2288
2289
2290 /// Return the complex conjugate of the input function with the same distribution and optional fence
2291
2292 /// !!! The fence is actually not optional in the current implementation !!!
2293 template <typename T, std::size_t NDIM>
2296 Function<T,NDIM> result = copy(f,true);
2297 return result.conj(fence);
2298 }
2299
2300 /// Apply operator on a hartree product of two low-dimensional functions
2301
2302 /// Supposed to be something like result= G( f(1)*f(2))
2303 /// the hartree product is never constructed explicitly, but its coeffs are
2304 /// constructed on the fly and processed immediately.
2305 /// @param[in] op the operator
2306 /// @param[in] f1 function of particle 1
2307 /// @param[in] f2 function of particle 2
2308 /// @param[in] fence if we shall fence
2309 /// @return a function of dimension NDIM=LDIM+LDIM
2310 template <typename opT, typename T, std::size_t LDIM>
2311 Function<TENSOR_RESULT_TYPE(typename opT::opT,T), LDIM+LDIM>
2312 apply(const opT& op, const std::vector<Function<T,LDIM>>& f1, const std::vector<Function<T,LDIM>>& f2, bool fence=true) {
2313
2314 World& world=f1.front().world();
2315
2316 typedef TENSOR_RESULT_TYPE(T,typename opT::opT) resultT;
2317 typedef std::vector<Function<T,LDIM>> vecfuncL;
2318
2319 vecfuncL& ff1 = const_cast< vecfuncL& >(f1);
2320 vecfuncL& ff2 = const_cast< vecfuncL& >(f2);
2321
2322 bool same=(ff1[0].get_impl()==ff2[0].get_impl());
2323
2324 reconstruct(world,f1,false);
2325 reconstruct(world,f2,false);
2326 world.gop.fence();
2327 // keep the leaves! They are assumed to be there later
2328 // even for modified op we need NS form for the hartree_leaf_op
2329 for (auto& f : f1) f.make_nonstandard(true,false);
2330 for (auto& f : f2) f.make_nonstandard(true,false);
2331 world.gop.fence();
2332
2333
2336 Function<resultT,LDIM+LDIM> result=factory.empty().fence();
2337
2338 result.get_impl()->reset_timer();
2339 op.reset_timer();
2340
2341 // will fence here
2342 for (size_t i=0; i<f1.size(); ++i)
2343 result.get_impl()->recursive_apply(op, f1[i].get_impl().get(),f2[i].get_impl().get(),false);
2344 world.gop.fence();
2345
2346 if (op.print_timings) {
2347 result.get_impl()->print_timer();
2348 op.print_timer();
2349 }
2350
2351 result.get_impl()->finalize_apply(); // need fence before reconstruct
2352
2353 if (op.modified()) {
2354 result.get_impl()->trickle_down(true);
2355 } else {
2356 result.get_impl()->reconstruct(true);
2357 }
2358 standard(world,ff1,false);
2359 if (not same) standard(world,ff2,false);
2360
2361 return result;
2362 }
2363
2364
2365 /// Apply operator ONLY in non-standard form - required other steps missing !!
2366 template <typename opT, typename R, std::size_t NDIM>
2367 Function<TENSOR_RESULT_TYPE(typename opT::opT,R), NDIM>
2368 apply_only(const opT& op, const Function<R,NDIM>& f, bool fence=true) {
2369 Function<TENSOR_RESULT_TYPE(typename opT::opT,R), NDIM> result;
2370
2371 constexpr std::size_t OPDIM=opT::opdim;
2372 constexpr bool low_dim=(OPDIM*2==NDIM); // apply on some dimensions only
2373
2374 // specialized version for 3D
2375 if (NDIM <= 3 and (not low_dim)) {
2376 result.set_impl(f, false);
2377 result.get_impl()->apply(op, *f.get_impl(), fence);
2378
2379 } else { // general version for higher dimension
2380 //bool print_timings=false;
2381 Function<TENSOR_RESULT_TYPE(typename opT::opT,R), NDIM> r1;
2382
2383 result.set_impl(f, false);
2384 r1.set_impl(f, false);
2385
2386 result.get_impl()->reset_timer();
2387 op.reset_timer();
2388
2389 result.get_impl()->apply_source_driven(op, *f.get_impl(), fence);
2390
2391 // recursive_apply is about 20% faster than apply_source_driven
2392 //result.get_impl()->recursive_apply(op, f.get_impl().get(),
2393 // r1.get_impl().get(),true); // will fence here
2394
2395 }
2396
2397 return result;
2398 }
2399
2400 /// Apply operator in non-standard form
2401
2402 /// Returns a new function with the same distribution
2403 ///
2404 /// !!! For the moment does NOT respect fence option ... always fences
2405 /// if the operator acts on one particle only the result will be sorted as
2406 /// g.particle=1: g(f) = \int g(x,x') f(x',y) dx' = result(x,y)
2407 /// g.particle=2: g(f) = \int g(y,y') f(x,y') dy' = result(x,y)
2408 /// for the second case it will notably *not* be as it is implemented in the partial inner product!
2409 /// g.particle=2 g(f) = result(x,y)
2410 /// inner(g(y,y'),f(x,y'),1,1) = result(y,x)
2411 /// also note the confusion with the counting of the particles/integration variables
2412 template <typename opT, typename R, std::size_t NDIM>
2413 Function<TENSOR_RESULT_TYPE(typename opT::opT,R), NDIM>
2414 apply(const opT& op, const Function<R,NDIM>& f, bool fence=true) {
2415
2416 typedef TENSOR_RESULT_TYPE(typename opT::opT,R) resultT;
2417 Function<R,NDIM>& ff = const_cast< Function<R,NDIM>& >(f);
2419
2420 MADNESS_ASSERT(not f.is_on_demand());
2421 bool print_timings=op.print_timings;
2422
2423 if (VERIFY_TREE) ff.verify_tree();
2424 ff.reconstruct();
2425 if (print_timings) ff.print_size("ff in apply after reconstruct");
2426
2427 if (op.modified()) {
2428
2430// ff.get_impl()->make_redundant(true);
2431 result = apply_only(op, ff, fence);
2432 ff.get_impl()->undo_redundant(false);
2433 result.get_impl()->trickle_down(true);
2434
2435 } else {
2436
2437 // saves the standard() step, which is very expensive in 6D
2438// Function<R,NDIM> fff=copy(ff);
2439 Function<R,NDIM> fff=(ff);
2440 fff.make_nonstandard(op.doleaves, true);
2441 if (print_timings) fff.print_size("ff in apply after make_nonstandard");
2442 if ((print_timings) and (f.world().rank()==0)) {
2443 fff.get_impl()->timer_filter.print("filter");
2444 fff.get_impl()->timer_compress_svd.print("compress_svd");
2445 }
2446 result = apply_only(op, fff, fence);
2447 result.get_impl()->set_tree_state(nonstandard_after_apply);
2448 ff.world().gop.fence();
2449 if (print_timings) result.print_size("result after apply_only");
2450
2451 // svd-tensors need some post-processing
2452 if (result.get_impl()->get_tensor_type()==TT_2D) {
2453 double elapsed=result.get_impl()->finalize_apply();
2454 if (print_timings) printf("time in finalize_apply %8.2f\n",elapsed);
2455 }
2456 if (print_timings) {
2457 result.get_impl()->print_timer();
2458 op.print_timer();
2459 }
2460
2461 result.get_impl()->reconstruct(true);
2462
2463// fff.clear();
2464 if (op.destructive()) {
2465 ff.world().gop.fence();
2466 ff.clear();
2467 } else {
2468 // ff.standard();
2469 ff.reconstruct();
2470 }
2471
2472 }
2473 if (print_timings) result.print_size("result after reconstruction");
2474 return result;
2475 }
2476
2477
2478 template <typename opT, typename R, std::size_t NDIM>
2479 Function<TENSOR_RESULT_TYPE(typename opT::opT,R), NDIM>
2480 apply_1d_realspace_push(const opT& op, const Function<R,NDIM>& f, int axis, bool fence=true) {
2482 Function<R,NDIM>& ff = const_cast< Function<R,NDIM>& >(f);
2483 if (VERIFY_TREE) ff.verify_tree();
2484 ff.reconstruct();
2485
2486 Function<TENSOR_RESULT_TYPE(typename opT::opT,R), NDIM> result;
2487
2488 result.set_impl(ff, false);
2489 result.get_impl()->apply_1d_realspace_push(op, ff.get_impl().get(), axis, fence);
2490 result.get_impl()->set_tree_state(redundant_after_merge);
2491 return result;
2492 }
2493
2494
2495 /// Generate a new function by reordering dimensions ... optional fence
2496
2497 /// You provide an array of dimension NDIM that maps old to new dimensions
2498 /// according to
2499 /// \code
2500 /// newdim = mapdim[olddim]
2501 /// \endcode
2502 /// Works in either scaling function or wavelet basis.
2503 ///
2504 /// Would be easy to modify this to also change the procmap here
2505 /// if desired but presently it uses the same procmap as f.
2506 template <typename T, std::size_t NDIM>
2507 Function<T,NDIM>
2508 mapdim(const Function<T,NDIM>& f, const std::vector<long>& map, bool fence=true) {
2510 Function<T,NDIM> result;
2511 return result.mapdim(f,map,fence);
2512 }
2513
2514 /// Generate a new function by mirroring within the dimensions .. optional fence
2515
2516 /// similar to mapdim
2517 /// @param[in] mirror array with -1 and 1, corresponding to mirror this dimension or not
2518 template <typename T, std::size_t NDIM>
2519 Function<T,NDIM>
2520 mirror(const Function<T,NDIM>& f, const std::vector<long>& mirrormap, bool fence=true) {
2522 Function<T,NDIM> result;
2523 return result.mirror(f,mirrormap,fence);
2524 }
2525
2526 /// This is replaced with mirror(map(f)), optional fence
2527
2528 /// first map then mirror!
2529 /// mirror is similar to mapdim, but maps from x to -x, y to -y, and so on
2530 /// Example: mirror a 3d function on the xy plane: mirror={1,1,-1}
2531 /// Example: c4 rotation of a 3d function around the z axis:
2532 /// x->y, y->-x, z->z: map(1,0,2); mirror(-1,1,1)
2533 /// @param[in] map array holding dimensions
2534 /// @param[in] mirror array of -1 and 1, corresponding to mirror or not
2535 template <typename T, std::size_t NDIM>
2536 Function<T,NDIM>
2537 map_and_mirror(const Function<T,NDIM>& f, const std::vector<long>& map,
2538 const std::vector<long>& mirror, bool fence=true) {
2540 Function<T,NDIM> result;
2541 return result.map_and_mirror(f,map,mirror,fence);
2542 }
2543
2544
2545 /// swap particles 1 and 2
2546
2547 /// param[in] f a function of 2 particles f(1,2)
2548 /// return the input function with particles swapped g(1,2) = f(2,1)
2549 template <typename T, std::size_t NDIM>
2550 typename std::enable_if_t<NDIM%2==0, Function<T,NDIM>>
2552 // this could be done more efficiently for SVD, but it works decently
2553 std::vector<long> map(NDIM);
2554 constexpr std::size_t LDIM=NDIM/2;
2555 static_assert(LDIM*2==NDIM);
2556 for (std::size_t d=0; d<LDIM; ++d) {
2557 map[d]=d+LDIM;
2558 map[d+LDIM]=d;
2559 }
2560// map[0]=3;
2561// map[1]=4;
2562// map[2]=5; // 2 -> 1
2563// map[3]=0;
2564// map[4]=1;
2565// map[5]=2; // 1 -> 2
2566 return mapdim(f,map);
2567 }
2568
2569 /// symmetrize a function
2570
2571 /// @param[in] symmetry possibilities are:
2572 /// (anti-) symmetric particle permutation ("sy_particle", "antisy_particle")
2573 /// symmetric mirror plane ("xy", "xz", "yz")
2574 /// @return a new function symmetrized according to the input parameter
2575 template <typename T, std::size_t NDIM>
2576 Function<T,NDIM>
2577 symmetrize(const Function<T,NDIM>& f, const std::string symmetry, bool fence=true) {
2578 Function<T,NDIM> result;
2579
2580 MADNESS_ASSERT(NDIM==6); // works only for pair functions
2581 std::vector<long> map(NDIM);
2582
2583 // symmetric particle permutation
2584 if (symmetry=="sy_particle") {
2585 map[0]=3; map[1]=4; map[2]=5;
2586 map[3]=0; map[4]=1; map[5]=2;
2587 } else if (symmetry=="cx") {
2588 map[0]=0; map[1]=2; map[2]=1;
2589 map[3]=3; map[4]=5; map[5]=4;
2590
2591 } else if (symmetry=="cy") {
2592 map[0]=2; map[1]=1; map[2]=0;
2593 map[3]=5; map[4]=4; map[5]=3;
2594
2595 } else if (symmetry=="cz") {
2596 map[0]=1; map[1]=0; map[2]=2;
2597 map[3]=4; map[4]=3; map[5]=5;
2598
2599 } else {
2600 if (f.world().rank()==0) {
2601 print("unknown parameter in symmetrize:",symmetry);
2602 }
2603 MADNESS_EXCEPTION("unknown parameter in symmetrize",1);
2604 }
2605
2606 result.mapdim(f,map,true); // need to fence here
2607 result.get_impl()->average(*f.get_impl());
2608
2609 return result;
2610 }
2611
2612
2613
2614 /// multiply a high-dimensional function with a low-dimensional function
2615
2616 /// @param[in] f NDIM function of 2 particles: f=f(1,2)
2617 /// @param[in] g LDIM function of 1 particle: g=g(1) or g=g(2)
2618 /// @param[in] particle if g=g(1) or g=g(2)
2619 /// @return h(1,2) = f(1,2) * g(p)
2620 template<typename T, std::size_t NDIM, std::size_t LDIM>
2621 Function<T,NDIM> multiply(const Function<T,NDIM> f, const Function<T,LDIM> g, const int particle, const bool fence=true) {
2622
2623 static_assert(LDIM+LDIM==NDIM);
2625
2626 Function<T,NDIM> result;
2627 result.set_impl(f, false);
2628
2629// Function<T,NDIM>& ff = const_cast< Function<T,NDIM>& >(f);
2630// Function<T,LDIM>& gg = const_cast< Function<T,LDIM>& >(g);
2631
2632 f.change_tree_state(redundant,false);
2633 g.change_tree_state(redundant,false);
2634 // neither call is fenced, and either may be a no-op if the function already is
2635 // redundant -- fence explicitly before the trees are traversed
2636 result.world().gop.fence();
2637 FunctionImpl<T,NDIM>* fimpl=f.get_impl().get();
2638 FunctionImpl<T,LDIM>* gimpl=g.get_impl().get();
2639
2640 result.get_impl()->multiply(fimpl,gimpl,particle);
2641 result.world().gop.fence();
2642
2643 f.change_tree_state(reconstructed,false);
2644 g.change_tree_state(reconstructed);
2645 return result;
2646 }
2647
2648
2649 template <typename T, std::size_t NDIM>
2650 Function<T,NDIM>
2654 bool fence=true)
2655 {
2658 other.reconstruct();
2659 result.get_impl()->project(*other.get_impl(),fence);
2660 return result;
2661 }
2662
2663
2664 /// Computes the scalar/inner product between two functions
2665
2666 /// In Maple this would be \c int(conjugate(f(x))*g(x),x=-infinity..infinity)
2667 template <typename T, typename R, std::size_t NDIM>
2670 return f.inner(g);
2671 }
2672
2673
2674 /// Computes the partial scalar/inner product between two functions, returns a low-dim function
2675
2676 /// syntax similar to the inner product in tensor.h
2677 /// e.g result=inner<3>(f,g),{0},{1}) : r(x,y) = int f(x1,x) g(y,x1) dx1
2678 /// @param[in] task 0: everything, 1; prepare only (fence), 2: work only (no fence), 3: finalize only (fence)
2679 template<std::size_t NDIM, typename T, std::size_t LDIM, typename R, std::size_t KDIM,
2680 std::size_t CDIM = (KDIM + LDIM - NDIM) / 2>
2681 std::vector<Function<TENSOR_RESULT_TYPE(T, R), NDIM>>
2682 innerXX(const Function<T, LDIM>& f, const std::vector<Function<R, KDIM>>& vg, const std::array<int, CDIM> v1,
2683 const std::array<int, CDIM> v2, int task=0) {
2684 bool prepare = ((task==0) or (task==1));
2685 bool work = ((task==0) or (task==2));
2686 bool finish = ((task==0) or (task==3));
2687
2688 static_assert((KDIM + LDIM - NDIM) % 2 == 0, "faulty dimensions in inner (partial version)");
2689 static_assert(KDIM + LDIM - 2 * CDIM == NDIM, "faulty dimensions in inner (partial version)");
2690
2691 // contraction indices must be contiguous and either in the beginning or at the end
2692 for (size_t i=0; i<CDIM-1; ++i) MADNESS_CHECK((v1[i]+1)==v1[i+1]);
2693 MADNESS_CHECK((v1[0]==0) or (v1[CDIM-1]==LDIM-1));
2694
2695 for (size_t i=0; i<CDIM-1; ++i) MADNESS_CHECK((v2[i]+1)==v2[i+1]);
2696 MADNESS_CHECK((v2[0]==0) or (v2[CDIM-1]==KDIM-1));
2697
2698 MADNESS_CHECK(f.is_initialized());
2700 MADNESS_CHECK(f.world().id() == vg[0].world().id());
2701 // this needs to be run in a single world, so that all coefficients are local.
2702 // Use macrotasks if run on multiple processes.
2703 World& world=f.world();
2704 MADNESS_CHECK(world.size() == 1);
2705
2706 if (prepare) {
2707 f.change_tree_state(nonstandard);
2709 world.gop.fence();
2710 f.get_impl()->compute_snorm_and_dnorm(false);
2711 for (auto& g : vg) g.get_impl()->compute_snorm_and_dnorm(false);
2712 world.gop.fence();
2713 }
2714
2715 typedef TENSOR_RESULT_TYPE(T, R) resultT;
2716 std::vector<Function<resultT,NDIM>> result(vg.size());
2717 if (work) {
2719 for (size_t i=0; i<vg.size(); ++i) {
2721 .k(f.k()).thresh(f.thresh()).empty().nofence();
2722 result[i].get_impl()->partial_inner(*f.get_impl(),*(vg[i]).get_impl(),v1,v2);
2723 result[i].get_impl()->set_tree_state(nonstandard_after_apply);
2724 }
2725 world.gop.set_forbid_fence(false);
2726 }
2727
2728 if (finish) {
2729
2730 world.gop.fence();
2731// result.get_impl()->reconstruct(true);
2732
2734// result.reconstruct();
2735 // restore initial state of g and h
2736 auto erase_list = [] (const auto& funcimpl) {
2737 typedef typename std::decay_t<decltype(funcimpl)>::keyT keyTT;
2738 std::list<keyTT> to_be_erased;
2739 for (auto it=funcimpl.get_coeffs().begin(); it!=funcimpl.get_coeffs().end(); ++it) {
2740 const auto& key=it->first;
2741 const auto& node=it->second;
2742 if (not node.has_children()) to_be_erased.push_back(key);
2743 }
2744 return to_be_erased;
2745 };
2746
2747 FunctionImpl<T,LDIM>& f_nc=const_cast<FunctionImpl<T,LDIM>&>(*f.get_impl());
2748 for (auto& key : erase_list(f_nc)) f_nc.get_coeffs().erase(key);
2749 for (auto& g : vg) {
2750 FunctionImpl<R,KDIM>& g_nc=const_cast<FunctionImpl<R,KDIM>&>(*g.get_impl());
2751 for (auto& key : erase_list(g_nc)) g_nc.get_coeffs().erase(key);
2752 }
2753 world.gop.fence();
2755 f_nc.reconstruct(false);
2756 world.gop.fence();
2757
2758 }
2759
2760 return result;
2761 }
2762
2763
2764 /// Computes the partial scalar/inner product between two functions, returns a low-dim function
2765
2766 /// syntax similar to the inner product in tensor.h
2767 /// e.g result=inner<3>(f,g),{0},{1}) : r(x,y) = int f(x1,x) g(y,x1) dx1
2768 /// @param[in] task 0: everything, 1; prepare only (fence), 2: work only (no fence), 3: finalize only (fence)
2769 template<std::size_t NDIM, typename T, std::size_t LDIM, typename R, std::size_t KDIM,
2770 std::size_t CDIM = (KDIM + LDIM - NDIM) / 2>
2772 innerXX(const Function<T, LDIM>& f, const Function<R, KDIM>& g, const std::array<int, CDIM> v1,
2773 const std::array<int, CDIM> v2, int task=0) {
2774 return innerXX<NDIM,T,LDIM,R,KDIM>(f,std::vector<Function<R,KDIM>>({g}),v1,v2,task)[0];
2775 }
2776
2777 /// Computes the partial scalar/inner product between two functions, returns a low-dim function
2778
2779 /// syntax similar to the inner product in tensor.h
2780 /// e.g result=inner<3>(f,g),{0},{1}) : r(x,y) = int f(x1,x) g(y,x1) dx1
2781 template <typename T, std::size_t LDIM, typename R, std::size_t KDIM>
2783 inner(const Function<T,LDIM>& f, const Function<R,KDIM>& g, const std::tuple<int> v1, const std::tuple<int> v2) {
2784 return innerXX<KDIM+LDIM-2>(f,g,
2785 std::array<int,1>({std::get<0>(v1)}),
2786 std::array<int,1>({std::get<0>(v2)}));
2787 }
2788
2789 /// Computes the partial scalar/inner product between two functions, returns a low-dim function
2790
2791 /// syntax similar to the inner product in tensor.h
2792 /// e.g result=inner<3>(f,g),{0,1},{1,2}) : r(y) = int f(x1,x2) g(y,x1,x2) dx1 dx2
2793 template <typename T, std::size_t LDIM, typename R, std::size_t KDIM>
2795 inner(const Function<T,LDIM>& f, const Function<R,KDIM>& g, const std::tuple<int,int> v1, const std::tuple<int,int> v2) {
2796 return innerXX<KDIM+LDIM-4>(f,g,
2797 std::array<int,2>({std::get<0>(v1),std::get<1>(v1)}),
2798 std::array<int,2>({std::get<0>(v2),std::get<1>(v2)}));
2799 }
2800
2801 /// Computes the partial scalar/inner product between two functions, returns a low-dim function
2802
2803 /// syntax similar to the inner product in tensor.h
2804 /// e.g result=inner<3>(f,g),{1},{2}) : r(x,y,z) = int f(x,x1) g(y,z,x1) dx1
2805 template <typename T, std::size_t LDIM, typename R, std::size_t KDIM>
2807 inner(const Function<T,LDIM>& f, const Function<R,KDIM>& g, const std::tuple<int,int,int> v1, const std::tuple<int,int,int> v2) {
2808 return innerXX<KDIM+LDIM-6>(f,g,
2809 std::array<int,3>({std::get<0>(v1),std::get<1>(v1),std::get<2>(v1)}),
2810 std::array<int,3>({std::get<0>(v2),std::get<1>(v2),std::get<2>(v2)}));
2811 }
2812
2813
2814
2815 /// Computes the scalar/inner product between an MRA function and an external functor
2816
2817 /// Currently this defaults to inner_adaptive, which might be more expensive
2818 /// than inner_ext since it loops over all leaf nodes. If you feel inner_ext
2819 /// is more efficient you need to call it directly
2820 /// @param[in] f MRA function
2821 /// @param[in] g functor
2822 /// @result inner(f,g)
2823 template <typename T, typename opT, std::size_t NDIM>
2824 TENSOR_RESULT_TYPE(T,typename opT::value_type) inner(const Function<T,NDIM>& f, const opT& g) {
2826 std::shared_ptr< FunctionFunctorInterface<double,3> > func(new opT(g));
2827 return f.inner_adaptive(func);
2828 }
2829
2830 /// Computes the scalar/inner product between an MRA function and an external functor
2831
2832 /// Currently this defaults to inner_adaptive, which might be more expensive
2833 /// than inner_ext since it loops over all leaf nodes. If you feel inner_ext
2834 /// is more efficient you need to call it directly
2835 /// @param[in] g functor
2836 /// @param[in] f MRA function
2837 /// @result inner(f,g)
2838 template <typename T, typename opT, std::size_t NDIM>
2839 TENSOR_RESULT_TYPE(T,typename opT::value_type) inner(const opT& g, const Function<T,NDIM>& f) {
2840 return inner(f,g);
2841 }
2842
2843 template <typename T, typename R, std::size_t NDIM>
2844 typename IsSupported<TensorTypeData<R>, Function<TENSOR_RESULT_TYPE(T,R),NDIM> >::type
2846 return (f*R(1.0)).add_scalar(r);
2847 }
2848
2849 template <typename T, typename R, std::size_t NDIM>
2850 typename IsSupported<TensorTypeData<R>, Function<TENSOR_RESULT_TYPE(T,R),NDIM> >::type
2852 return (f*R(1.0)).add_scalar(r);
2853 }
2854
2855 template <typename T, typename R, std::size_t NDIM>
2856 typename IsSupported<TensorTypeData<R>, Function<TENSOR_RESULT_TYPE(T,R),NDIM> >::type
2858 return (f*R(1.0)).add_scalar(-r);
2859 }
2860
2861 template <typename T, typename R, std::size_t NDIM>
2862 typename IsSupported<TensorTypeData<R>, Function<TENSOR_RESULT_TYPE(T,R),NDIM> >::type
2864 return (f*R(-1.0)).add_scalar(r);
2865 }
2866
2867 namespace detail {
2868 template <std::size_t NDIM>
2869 struct realop {
2870 typedef double resultT;
2872 return real(t);
2873 }
2874
2875 template <typename Archive> void serialize (Archive& ar) {}
2876 };
2877
2878 template <std::size_t NDIM>
2879 struct imagop {
2880 typedef double resultT;
2882 return imag(t);
2883 }
2884
2885 template <typename Archive> void serialize (Archive& ar) {}
2886 };
2887
2888 template <std::size_t NDIM>
2889 struct abssqop {
2890 typedef double resultT;
2892 Tensor<double> r = abs(t);
2893 return r.emul(r);
2894 }
2895
2896 template <typename Archive> void serialize (Archive& ar) {}
2897 };
2898
2899 template <std::size_t NDIM>
2900 struct absop {
2901 typedef double resultT;
2903 Tensor<double> r = abs(t);
2904 return r;
2905 }
2906
2907 template <typename Archive> void serialize (Archive& ar) {}
2908 };
2909
2910 }
2911
2912 /// Returns a new function that is the real part of the input
2913 template <std::size_t NDIM>
2917
2918 /// Returns a new function that is the real part of the input
2919 template <std::size_t NDIM>
2921 return copy(z);
2922 }
2923
2924 /// Returns a new function that is the imaginary part of the input
2925 template <std::size_t NDIM>
2929
2930
2931 /// Create a new function that is the square of f - global comm only if not reconstructed
2932 template <typename T, std::size_t NDIM>
2935 Function<T,NDIM> result = copy(f,true); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2936 return result.square(true); //fence); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2937 }
2938
2939 /// Create a new function that is the abs of f - global comm only if not reconstructed
2940 template <typename T, std::size_t NDIM>
2941 Function<T,NDIM> abs(const Function<T,NDIM>& f, bool fence=true) {
2943 Function<T,NDIM> result = copy(f,true); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2944 return result.abs(true); //fence); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2945 }
2946
2947 /// Create a new function that is the abs_square of f - global comm only if not reconstructed
2948 template <typename T, std::size_t NDIM>
2949 typename std::enable_if<!TensorTypeData<T>::iscomplex, Function<T,NDIM> >::type
2950 abs_square(const Function<T,NDIM>& f, bool fence=true) {
2952 Function<T,NDIM> result = copy(f,true); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2953 return result.abs_square(true); //fence); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2954 }
2955
2956 /// Create a new function that is the abs_square of f - global comm only if not reconstructed
2957 template <typename T, std::size_t NDIM>
2958 typename std::enable_if<TensorTypeData<T>::iscomplex, Function<typename Tensor<T>::scalar_type,NDIM> >::type
2959 abs_square(const Function<T,NDIM>& f, bool fence=true) {
2961 }
2962
2963 /// Returns a new function that is the square of the absolute value of the input
2964 template <std::size_t NDIM>
2968
2969 /// Returns a new function that is the absolute value of the input
2970 template <std::size_t NDIM>
2974
2975 /// get tree state of a function
2976
2977 /// there is a corresponding function in vmra.h
2978 /// @param[in] f function
2979 /// @return TreeState::unknown if the function is not initialized
2980 template <typename T, std::size_t NDIM>
2982 if (f.is_initialized()) return f.get_impl()->get_tree_state();
2983 return TreeState::unknown;
2984 }
2985
2986 /// change tree state of a function
2987
2988 /// there is a corresponding function in vmra.h
2989 /// return this for chaining
2990 /// @param[in] f function
2991 /// @param[in] finalstate the new state
2992 /// @return this in the requested state
2993 template <typename T, std::size_t NDIM>
2995 const TreeState finalstate, bool fence=true) {
2996 return f.change_tree_state(finalstate,fence);
2997 }
2998
2999 template <typename R, std::size_t MDIM>
3001 f1.impl.swap(f2.impl);
3002 }
3003
3004}
3005
3006#include <madness/mra/funcplot.h>
3007
3008namespace madness {
3009 namespace archive {
3010 template <class archiveT, class T, std::size_t NDIM>
3012 static inline void load(const ParallelInputArchive<archiveT>& ar, Function<T,NDIM>& f) {
3013 f.load(*ar.get_world(), ar);
3014 }
3015 };
3016
3017 template <class archiveT, class T, std::size_t NDIM>
3019 static inline void store(const ParallelOutputArchive<archiveT>& ar, const Function<T,NDIM>& f) {
3020 f.store(ar);
3021 }
3022 };
3023 }
3024
3025 template <class T, std::size_t NDIM>
3026 void save(const Function<T,NDIM>& f, const std::string name) {
3028 ar2 & f;
3029 }
3030
3031 template <class T, std::size_t NDIM>
3032 void load(Function<T,NDIM>& f, const std::string name) {
3034 ar2 & f;
3035 }
3036
3037}
3038
3039namespace madness {
3040 // type traits to check if a template parameter is a Function
3041 template<typename>
3042 struct is_madness_function : std::false_type {};
3043
3044 template<typename T, std::size_t NDIM>
3045 struct is_madness_function<madness::Function<T, NDIM>> : std::true_type {};
3046
3047}
3048
3049
3050/* @} */
3051
3052#include <madness/mra/derivative.h>
3053#include <madness/mra/operator.h>
3055#include <madness/mra/vmra.h>
3056// #include <madness/mra/mraimpl.h> !!!!!!!!!!!!! NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO !!!!!!!!!!!!!!!!!!
3057
3058#endif // MADNESS_MRA_MRA_H__INCLUDED
double q(double t)
Definition DKops.h:18
This header should include pretty much everything needed for the parallel runtime.
long dim(int i) const
Returns the size of dimension i.
Definition basetensor.h:147
This class is used to specify boundary conditions for all operators.
Definition bc.h:72
CompositeFunctorInterface implements a wrapper of holding several functions and functors.
Definition function_interface.h:172
FunctionDefaults holds default paramaters as static class members.
Definition funcdefaults.h:101
static const double & get_thresh()
Returns the default threshold.
Definition funcdefaults.h:183
static std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > & get_pmap()
Returns the default process map that was last initialized via set_default_pmap()
Definition funcdefaults.h:415
static void set_cell(const Tensor< double > &value)
Sets the user cell for the simulation.
Definition funcdefaults.h:374
FunctionFactory implements the named-parameter idiom for Function.
Definition function_factory.h:86
FunctionFactory & nofence()
Definition function_factory.h:282
virtual FunctionFactory & thresh(double thresh)
Definition function_factory.h:198
FunctionFactory & fence(bool fence=true)
Definition function_factory.h:276
virtual FunctionFactory & k(int k)
Definition function_factory.h:193
FunctionFactory & empty()
Definition function_factory.h:246
Abstract base class interface required for functors used as input to Functions.
Definition function_interface.h:68
FunctionImpl holds all Function state to facilitate shallow copy semantics.
Definition funcimpl.h:970
const dcT & get_coeffs() const
Definition mraimpl.h:355
void reconstruct(bool fence)
reconstruct this tree – respects fence
Definition mraimpl.h:1509
bool is_on_demand() const
Definition mraimpl.h:285
FunctionNode holds the coefficients, etc., at each node of the 2^NDIM-tree.
Definition funcimpl.h:136
A multiresolution adaptive numerical function.
Definition mra.h:144
void print_tree_json(std::ostream &os=std::cout) const
Definition mra.h:1037
Tensor< T > eval_cube(const Tensor< double > &cell, const std::vector< long > &npt, bool eval_refine=false) const
Evaluates a cube/slice of points (probably for plotting) ... collective but no fence necessary.
Definition mra.h:415
TENSOR_RESULT_TYPE(T, R) inner(const Function< R
Returns the inner product.
Function< T, NDIM > & map_and_mirror(const Function< T, NDIM > &f, const std::vector< long > &map, const std::vector< long > &mirror, bool fence)
This is replaced with mirror(map(f)) ... private.
Definition mra.h:1873
void gaxpy_ext(const Function< L, NDIM > &left, T(*f)(const coordT &), T alpha, T beta, double tol, bool fence=true) const
Definition mra.h:1569
T inner_adaptive(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > f, const bool leaf_refine=true) const
Definition mra.h:1554
void unaryop_coeff(const opT &op, bool fence=true)
Unary operation applied inplace to the coefficients.
Definition mra.h:1087
bool is_compressed() const
Returns true if compressed, false otherwise. No communication.
Definition mra.h:547
Function< T, NDIM/2 > dirac_convolution(const bool fence=true) const
Definition mra.h:1633
return impl inner_local * g())
void set_impl(const Function< R, NDIM > &f, bool zero=true)
Replace current FunctionImpl with a new one using the same parameters & map as f.
Definition mra.h:752
bool autorefine() const
Returns value of autorefine flag. No communication.
Definition mra.h:658
TreeState operating_state
Definition mra.h:1495
Function< T, NDIM > & add_scalar(T t, bool fence=true)
Inplace add scalar. No communication except for optional fence.
Definition mra.h:1136
void print_size(const std::string name) const
print some info about this
Definition mra.h:598
Function< T, NDIM > & fill_tree(const Function< R, NDIM > &g, bool fence=true)
With this being an on-demand function, fill the MRA tree according to different criteria.
Definition mra.h:1341
void broaden(const BoundaryConditions< NDIM > &bc=FunctionDefaults< NDIM >::get_bc(), bool fence=true) const
Inplace broadens support in scaling function basis.
Definition mra.h:1009
void print_info() const
Print a summary of the load balancing info.
Definition mra.h:1053
Function< T, NDIM > & scale(const Q q, bool fence=true)
Inplace, scale the function by a constant. No communication except for optional fence.
Definition mra.h:1126
void norm_tree(bool fence=true) const
Initializes information about the function norm at all length scales.
Definition mra.h:866
void load(World &world, Archive &ar)
Replaces this function with one loaded from an archive using the default processor map.
Definition mra.h:1650
Function< T, NDIM > & abs_square(bool fence=true)
Returns *this for chaining.
Definition mra.h:1263
void replicate(const DistributionType type, bool fence=true) const
Definition mra.h:778
double norm2sq_local() const
Returns the square of the norm of the local function ... no communication.
Definition mra.h:820
IsSupported< TensorTypeData< Q >, Function< T, NDIM > >::type & operator*=(const Q q)
Inplace scaling by a constant.
Definition mra.h:1235
void sum_down(bool fence=true) const
Sums scaling coeffs down tree restoring state with coeffs only at leaves. Optional fence....
Definition mra.h:967
Level depthpt(const coordT &xuser) const
Definition mra.h:491
bool is_redundant() const
Returns true if redundant, false otherwise. No communication.
Definition mra.h:577
Function< T, NDIM > & operator+=(const Function< Q, NDIM > &other)
Inplace addition of functions in the wavelet basis.
Definition mra.h:1195
Function< T, NDIM > & operator=(const Function< T, NDIM > &f)
Assignment is shallow. No communication, works in either basis.
Definition mra.h:196
void set_autorefine(bool value, bool fence=true)
Sets the value of the autorefine flag. Optional global fence.
Definition mra.h:668
World & world() const
Returns the world.
Definition mra.h:758
T trace() const
Returns global value of int(f(x),x) ... global comm required.
Definition mra.h:1296
T typeT
Definition mra.h:162
friend void swap(Function< R, MDIM > &f1, Function< R, MDIM > &f2)
implements swap algorithm
Definition mra.h:3000
Function< T, NDIM > & fill_tree(const opT &op, bool fence=true)
With this being an on-demand function, fill the MRA tree according to different criteria.
Definition mra.h:1359
const Function< T, NDIM > & change_tree_state(const TreeState finalstate, bool fence=true) const
changes tree state to given state
Definition mra.h:954
Function< typename opT::resultT, NDIM > & unary_op_coeffs(const Function< Q, NDIM > &func, const opT &op, bool fence)
This is replaced with left*right ... private.
Definition mra.h:1708
void print_tree_graphviz(std::ostream &os=std::cout) const
Process 0 prints a graphviz-formatted output of all nodes in the tree (collective)
Definition mra.h:1043
double norm2() const
Returns the 2-norm of the function ... global sum ... works in either basis.
Definition mra.h:847
Function< T, NDIM > & fill_cuspy_tree(const opT &op, const bool fence=true)
Definition mra.h:1382
void change_tensor_type(const TensorArgs &targs, bool fence=true)
change the tensor type of the coefficients in the FunctionNode
Definition mra.h:1700
const Function< T, NDIM > & refine(bool fence=true) const
Inplace autorefines the function using same test as for squaring.
Definition mra.h:999
TENSOR_RESULT_TYPE(T, R) inner_on_demand(const Function< R
Returns the inner product for one on-demand function.
T operator()(const coordT &xuser) const
Evaluates the function at a point in user coordinates. Collective operation.
Definition mra.h:456
MADNESS_ASSERT(g.is_compressed())
Function< T, NDIM > & square(bool fence=true)
Inplace squaring of function ... global comm only if not reconstructed.
Definition mra.h:1245
Function< T, NDIM > & fill_tree(bool fence=true)
With this being an on-demand function, fill the MRA tree according to different criteria.
Definition mra.h:1370
void verify_tree() const
Verifies the tree data structure ... global sync implied.
Definition mra.h:538
Future< Level > evaldepthpt(const coordT &xuser) const
Definition mra.h:337
void do_hartree_product(const std::vector< std::shared_ptr< FunctionImpl< T, LDIM > > > left, const std::vector< std::shared_ptr< FunctionImpl< T, KDIM > > > right)
perform the hartree product of f*g, invoked by result
Definition mra.h:1451
Function< T, NDIM > & mapdim(const Function< T, NDIM > &f, const std::vector< long > &map, bool fence)
This is replaced with mapdim(f) ... private.
Definition mra.h:1839
impl world gop fence()
int k() const
Returns the number of multiwavelets (k). No communication.
Definition mra.h:696
const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > & get_pmap() const
Returns a shared pointer to the process map.
Definition mra.h:766
double thresh() const
Returns value of truncation threshold. No communication.
Definition mra.h:677
Function< T, NDIM > & gaxpy_oop(T alpha, const Function< L, NDIM > &left, T beta, const Function< R, NDIM > &right, bool fence)
This is replaced with alpha*left + beta*right ... private.
Definition mra.h:1825
void set_thresh(double value, bool fence=true)
Sets the value of the truncation threshold. Optional global fence.
Definition mra.h:687
Function< T, NDIM > & multiop_values(const opT &op, const std::vector< Function< T, NDIM > > &vf)
This is replaced with op(vector of functions) ... private.
Definition mra.h:1730
void distribute(std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > newmap) const
distribute this function according to newmap
Definition mra.h:810
void unaryop(const opT &op, bool fence=true)
Inplace unary operation on function values.
Definition mra.h:1077
const std::shared_ptr< FunctionImpl< T, NDIM > > & get_impl() const
Returns a shared-pointer to the implementation.
Definition mra.h:724
void standard(bool fence=true)
Converts the function standard compressed form. Possible non-blocking comm.
Definition mra.h:913
void unaryop_node(const opT &op, bool fence=true)
Unary operation applied inplace to the nodes.
Definition mra.h:1097
Function< T, NDIM > & truncate(double tol=0.0, bool fence=true)
Truncate the function with optional fence. Compresses with fence if not compressed.
Definition mra.h:712
Function< T, NDIM > & fill_cuspy_tree(const bool fence=true)
Special refinement on 6D boxes where the electrons come close (meet)
Definition mra.h:1395
std::size_t size() const
Returns the number of coefficients in the function ... collective global sum.
Definition mra.h:643
bool is_on_demand() const
Definition mra.h:746
Function< T, NDIM > & operator-=(const Function< Q, NDIM > &other)
Inplace subtraction of functions in the wavelet basis.
Definition mra.h:1214
bool compressed
Definition mra.h:1317
const Function< T, NDIM > & reconstruct(bool fence=true) const
Reconstructs the function, transforming into scaling function basis. Possible non-blocking comm.
Definition mra.h:944
Function< T, NDIM > & abs(bool fence=true)
Returns *this for chaining.
Definition mra.h:1254
Function< T, NDIM > & reduce_rank(const double thresh=0.0, const bool fence=true)
reduce the rank of the coefficient tensors
Definition mra.h:1903
Function< T, NDIM > & mirror(const Function< T, NDIM > &f, const std::vector< long > &mirrormap, bool fence)
This is replaced with mirror(f) ... private.
Definition mra.h:1854
std::size_t max_nodes() const
Returns the max number of nodes on a processor.
Definition mra.h:628
void do_hartree_product(const std::vector< std::shared_ptr< FunctionImpl< T, LDIM > > > left, const std::vector< std::shared_ptr< FunctionImpl< T, KDIM > > > right, const opT *op)
perform the hartree product of f*g, invoked by result
Definition mra.h:1437
std::shared_ptr< FunctionImpl< T, NDIM > > impl
Definition mra.h:151
void replicate_on_hosts(bool fence=true) const
Definition mra.h:803
void replicate(bool fence=true) const
Definition mra.h:791
T trace_local() const
Returns local contribution to int(f(x),x) ... no communication.
Definition mra.h:1276
void mul_on_demand(const Function< L, NDIM > &f, const Function< R, NDIM > &g, bool fence=true)
Same as operator* but with optional fence and no automatic reconstruction.
Definition mra.h:1796
Function< T, NDIM > & gaxpy(const T &alpha, const Function< Q, NDIM > &other, const R &beta, bool fence=true)
Inplace, general bi-linear operation in wavelet basis. No communication except for optional fence.
Definition mra.h:1156
Vector< double, NDIM > coordT
Type of vector holding coordinates.
Definition mra.h:161
void store(Archive &ar) const
Stores the function to an archive.
Definition mra.h:1688
std::size_t max_local_depth() const
Returns the maximum local depth of the function tree ... no communications.
Definition mra.h:620
void vtransform(const std::vector< Function< R, NDIM > > &v, const Tensor< Q > &c, std::vector< Function< T, NDIM > > &vresult, double tol, bool fence=true)
sparse transformation of a vector of functions ... private
Definition mra.h:1814
TENSOR_RESULT_TYPE(T, R) local
std::size_t max_depth() const
Returns the maximum depth of the function tree ... collective global sum.
Definition mra.h:607
Function()
Default constructor makes uninitialized function. No communication.
Definition mra.h:179
std::vector< std::pair< bool, T > > eval_local_only(const std::vector< coordT > &xuser, Level maxlevel) const
Definition mra.h:325
std::size_t tree_size() const
Returns the number of nodes in the function tree ... collective global sum.
Definition mra.h:591
Function< TENSOR_RESULT_TYPE(T, R), NDIM-LDIM > project_out(const Function< R, LDIM > &g, const int dim) const
project this on the low-dim function g: h(x) = <f(x,y) | g(y)>
Definition mra.h:1609
static std::vector< std::shared_ptr< FunctionImpl< Q, D > > > vimpl(const std::vector< Function< Q, D > > &v)
Returns vector of FunctionImpl pointers corresponding to vector of functions.
Definition mra.h:1721
FunctionImpl< T, NDIM > implT
Definition mra.h:158
void clear(bool fence=true)
Clears the function as if constructed uninitialized. Optional fence.
Definition mra.h:1020
void refine_general(const opT &op, bool fence=true) const
Inplace autorefines the function. Optional fence. Possible non-blocking comm.
Definition mra.h:980
static void doconj(const Key< NDIM >, Tensor< T > &t)
Definition mra.h:1107
std::pair< bool, T > eval_local_only(const Vector< double, NDIM > &xuser, Level maxlevel) const
Evaluate function only if point is local returning (true,value); otherwise return (false,...
Definition mra.h:250
void set_functor(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > functor)
Replace the current functor with the provided new one.
Definition mra.h:741
bool impl_initialized() const
Definition mra.h:154
Function< T, NDIM > & fill_nuclear_cuspy_tree(const opT &op, const size_t particle, const bool fence=true)
Definition mra.h:1410
bool is_redundant_after_merge() const
Returns true if redundant_after_merge, false otherwise. No communication.
Definition mra.h:585
return local
Definition mra.h:1510
void eval_local_only(const std::vector< coordT > &xuser, Level maxlevel, std::vector< std::pair< bool, T > > &results) const
Batched eval_local_only writing into a caller-provided buffer.
Definition mra.h:290
auto func
Definition mra.h:1588
~Function()
Destruction of any underlying implementation is deferred to next global fence.
Definition mra.h:203
Function(const Function< T, NDIM > &f)
Copy constructor is shallow. No communication, works in either basis.
Definition mra.h:190
void set_impl(const std::shared_ptr< FunctionImpl< T, NDIM > > &impl)
Replace current FunctionImpl with provided new one.
Definition mra.h:731
T operator()(double x, double y=0, double z=0, double xx=0, double yy=0, double zz=0) const
Evaluates the function at a point in user coordinates. Collective operation.
Definition mra.h:470
T inner_ext_local(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > f, const bool leaf_refine=true, const bool keep_redundant=false) const
Definition mra.h:1521
std::size_t min_nodes() const
Returns the min number of nodes on a processor.
Definition mra.h:635
void make_redundant(bool fence=true) const
Converts the function to redundant form, i.e. sum coefficients on all levels.
Definition mra.h:929
constexpr std::size_t LDIM
Definition mra.h:1587
static constexpr std::size_t dimT
Definition mra.h:163
change_tree_state(operating_state, false)
bool is_nonstandard() const
Returns true if nonstandard-compressed, false otherwise. No communication.
Definition mra.h:569
void verify() const
Asserts that the function is initialized.
Definition mra.h:167
double err(const funcT &func) const
Returns an estimate of the difference ||this-func|| ... global sum performed.
Definition mra.h:525
T inner_ext(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > f, const bool leaf_refine=true, const bool keep_redundant=false) const
Definition mra.h:1537
Function< T, NDIM > & fill_nuclear_cuspy_tree(const size_t particle, const bool fence=true)
Special refinement on 6D boxes for the nuclear potentials (regularized with cusp, non-regularized wit...
Definition mra.h:1423
double check_symmetry() const
check symmetry of a function by computing the 2nd derivative
Definition mra.h:1889
void multi_to_multi_op_values(const opT &op, const std::vector< Function< T, NDIM > > &vin, std::vector< Function< T, NDIM > > &vout, const bool fence=true)
apply op on the input vector yielding an output vector of functions
Definition mra.h:1749
FunctionFactory< T, NDIM > factoryT
Definition mra.h:160
std::size_t size_local() const
Return the number of coefficients in the function on this processor.
Definition mra.h:650
const Function< T, NDIM > & compress(bool fence=true) const
Compresses the function, transforming into wavelet basis. Possible non-blocking comm.
Definition mra.h:886
bool is_initialized() const
Returns true if the function is initialized.
Definition mra.h:172
bool is_reconstructed() const
Returns true if reconstructed, false otherwise. No communication.
Definition mra.h:558
Function< T, NDIM > & chop_at_level(const int n, const bool fence=true)
remove all nodes with level higher than n
Definition mra.h:1911
MADNESS_ASSERT(is_compressed())
void vmulXX(const Function< L, NDIM > &left, const std::vector< Function< R, NDIM > > &right, std::vector< Function< T, NDIM > > &result, double tol, bool fence)
Multiplication of function * vector of functions using recursive algorithm of mulxx.
Definition mra.h:1770
double errsq_local(const funcT &func) const
Returns an estimate of the difference ||this-func||^2 from local data.
Definition mra.h:510
void make_nonstandard(bool keepleaves, bool fence=true) const
Compresses the function retaining scaling function coeffs. Possible non-blocking comm.
Definition mra.h:899
if(VERIFY_TREE) verify_tree()
Future< T > eval(const coordT &xuser) const
Evaluates the function at a point in user coordinates. Possible non-blocking comm.
Definition mra.h:216
void print_tree(std::ostream &os=std::cout) const
Process 0 prints a summary of all nodes in the tree (collective)
Definition mra.h:1030
FunctionNode< T, NDIM > nodeT
Definition mra.h:159
bool redundant
Definition mra.h:1318
Future< long > evalR(const coordT &xuser) const
Evaluates the function rank at a point in user coordinates. Possible non-blocking comm.
Definition mra.h:374
Function(const factoryT &factory)
Constructor from FunctionFactory provides named parameter idiom. Possible non-blocking communication.
Definition mra.h:183
NDIM &g const
Definition mra.h:1315
void unaryop(T(*f)(T))
Inplace unary operation on function values.
Definition mra.h:1068
Function< T, NDIM > conj(bool fence=true)
Inplace complex conjugate. No communication except for optional fence.
Definition mra.h:1115
A future is a possibly yet unevaluated value.
Definition future.h:370
remote_refT remote_ref(World &world) const
Returns a structure used to pass references to another process.
Definition future.h:672
Key is the index for a node of the 2^NDIM-tree.
Definition key.h:70
Definition leafop.h:391
Definition leafop.h:261
Traits class to specify support of numeric types.
Definition type_data.h:56
A tensor is a multidimensional array.
Definition tensor.h:318
Tensor< T > & emul(const Tensor< T > &t)
Inplace multiply by corresponding elements of argument Tensor.
Definition tensor.h:1800
Tensor< T > & conj()
Inplace complex conjugate.
Definition tensor.h:717
A simple, fixed dimension vector.
Definition vector.h:64
void erase(const keyT &key)
Erases entry from container (non-blocking comm if remote)
Definition worlddc.h:1552
Interface to be provided by any process map.
Definition worlddc.h:125
void fence(bool debug=false)
Synchronizes all processes in communicator AND globally ensures no pending AM or tasks.
Definition worldgop.cc:177
bool set_forbid_fence(bool value)
Set forbid_fence flag to new value and return old value.
Definition worldgop.h:677
A parallel world class.
Definition world.h:134
ProcessID rank() const
Returns the process rank in this World (same as MPI_Comm_rank()).
Definition world.h:344
ProcessID size() const
Returns the number of processes in this World (same as MPI_Comm_size()).
Definition world.h:354
WorldGopInterface & gop
Global operations.
Definition world.h:216
World * get_world() const
Returns a pointer to the world.
Definition parallel_archive.h:130
An archive for storing local or parallel data, wrapping a BinaryFstreamInputArchive.
Definition parallel_archive.h:366
An archive for storing local or parallel data wrapping a BinaryFstreamOutputArchive.
Definition parallel_archive.h:321
Objects that implement their own parallel archive interface should derive from this class.
Definition parallel_archive.h:58
static const double R
Definition csqrt.cc:46
Declaration and initialization of tree traversal functions and generic derivative.
double(* f1)(const coord_3d &)
Definition derivatives.cc:55
double(* f2)(const coord_3d &)
Definition derivatives.cc:56
const double delta
Definition dielectric_external_field.cc:119
Provides FunctionDefaults and utilities for coordinate transformation.
Provides FunctionCommonData, FunctionImpl and FunctionFactory.
Defines/implements plotting interface for functions.
Provides typedefs to hide use of templates and to increase interoperability.
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.
Implements (2nd generation) static load/data balancing for functions.
#define MADNESS_CHECK(condition)
Check a condition — even in a release build the condition is always evaluated so it can have side eff...
Definition madness_exception.h:182
#define MADNESS_EXCEPTION(msg, value)
Macro for throwing a MADNESS exception.
Definition madness_exception.h:119
#define MADNESS_ASSERT(condition)
Assert a condition that should be free of side-effects since in release builds this might be a no-op.
Definition madness_exception.h:134
#define MADNESS_CHECK_THROW(condition, msg)
Check a condition — even in a release build the condition is always evaluated so it can have side eff...
Definition madness_exception.h:207
Header to declare stuff which has not yet found a home.
static const bool VERIFY_TREE
Definition mra.h:57
Definition potentialmanager.cc:41
Namespace for all elements and tools of MADNESS.
Definition DFConvergence.h:9
double abs(double x)
Definition complexfun.h:48
Function< double, NDIM > abssq(const Function< double_complex, NDIM > &z, bool fence=true)
Returns a new function that is the square of the absolute value of the input.
Definition mra.h:2965
DistributionType
some introspection of how data is distributed
Definition worlddc.h:84
@ NodeReplicated
even if there are several ranks per node
Definition worlddc.h:87
@ RankReplicated
replicate the container over all world ranks
Definition worlddc.h:86
Function< TENSOR_RESULT_TYPE(typename opT::opT, R), NDIM > apply_1d_realspace_push(const opT &op, const Function< R, NDIM > &f, int axis, bool fence=true)
Definition mra.h:2480
Function< TENSOR_RESULT_TYPE(L, R), NDIM > sub(const Function< L, NDIM > &left, const Function< R, NDIM > &right, bool fence=true)
Same as operator- but with optional fence and no automatic compression.
Definition mra.h:2202
Function< TENSOR_RESULT_TYPE(L, R), NDIM > binary_op(const Function< L, NDIM > &left, const Function< R, NDIM > &right, const opT &op, bool fence=true)
Generate new function = op(left,right) where op acts on the function values.
Definition mra.h:1997
Function< Q, NDIM > convert(const Function< T, NDIM > &f, bool fence=true)
Type conversion implies a deep copy. No communication except for optional fence.
Definition mra.h:2280
Function< TENSOR_RESULT_TYPE(Q, T), NDIM > mul(const Q alpha, const Function< T, NDIM > &f, bool fence=true)
Returns new function equal to alpha*f(x) with optional fence.
Definition mra.h:1932
std::enable_if_t< NDIM%2==0, Function< T, NDIM > > swap_particles(const Function< T, NDIM > &f)
swap particles 1 and 2
Definition mra.h:2551
TreeState
Definition funcdefaults.h:60
@ nonstandard_after_apply
s and d coeffs, state after operator application
Definition funcdefaults.h:65
@ redundant_after_merge
s coeffs everywhere, must be summed up to yield the result
Definition funcdefaults.h:67
@ reconstructed
s coeffs at the leaves only
Definition funcdefaults.h:61
@ nonstandard
s and d coeffs in internal nodes
Definition funcdefaults.h:63
@ unknown
Definition funcdefaults.h:69
@ nonstandard_with_leaves
like nonstandard, with s coeffs at the leaves
Definition funcdefaults.h:64
static void user_to_sim(const Vector< double, NDIM > &xuser, Vector< double, NDIM > &xsim)
Convert user coords (cell[][]) to simulation coords ([0,1]^ndim)
Definition funcdefaults.h:469
std::vector< Function< TENSOR_RESULT_TYPE(T, R), NDIM > > innerXX(const Function< T, LDIM > &f, const std::vector< Function< R, KDIM > > &vg, const std::array< int, CDIM > v1, const std::array< int, CDIM > v2, int task=0)
Computes the partial scalar/inner product between two functions, returns a low-dim function.
Definition mra.h:2682
static constexpr long FUNCTION_ARCHIVE_MAGIC
Definition mra.h:140
std::vector< CCPairFunction< T, NDIM > > operator*(const double fac, const std::vector< CCPairFunction< T, NDIM > > &arg)
Definition ccpairfunction.h:1089
int Level
Definition key.h:59
TreeState get_tree_state(const Function< T, NDIM > &f)
get tree state of a function
Definition mra.h:2981
std::vector< CCPairFunction< T, NDIM > > operator-(const std::vector< CCPairFunction< T, NDIM > > c1, const std::vector< CCPairFunction< T, NDIM > > &c2)
Definition ccpairfunction.h:1060
std::string get_mra_data_dir()
Definition startup.cc:209
Function< T, NDIM > gaxpy_oop_reconstructed(const double alpha, const Function< T, NDIM > &left, const double beta, const Function< T, NDIM > &right, const bool fence=true)
Returns new function alpha*left + beta*right optional fence, having both addends reconstructed.
Definition mra.h:2165
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:227
@ TT_2D
Definition gentensor.h:120
@ TT_FULL
Definition gentensor.h:120
NDIM & f
Definition mra.h:2668
Function< TENSOR_RESULT_TYPE(L, R), NDIM > add(const Function< L, NDIM > &left, const Function< R, NDIM > &right, bool fence=true)
Same as operator+ but with optional fence and no automatic compression.
Definition mra.h:2157
Function< T, NDIM > symmetrize(const Function< T, NDIM > &f, const std::string symmetry, bool fence=true)
symmetrize a function
Definition mra.h:2577
Function< TENSOR_RESULT_TYPE(typename opT::opT, R), NDIM > apply_only(const opT &op, const Function< R, NDIM > &f, bool fence=true)
Apply operator ONLY in non-standard form - required other steps missing !!
Definition mra.h:2368
double imag(double x)
Definition complexfun.h:56
Function< typename opT::resultT, NDIM > unary_op(const Function< Q, NDIM > &func, const opT &op, bool fence=true)
Out of place application of unary operation to function values with optional fence.
Definition mra.h:2011
void startup(World &world, int argc, char **argv, bool doprint=false, bool make_stdcout_nice_to_reals=true)
initialize the internal state of the MADmra library
Definition startup.cc:64
std::string type(const PairType &n)
Definition PNOParameters.h:18
static bool print_timings
Definition SCF.cc:108
CCPairFunction< T, NDIM > apply(const SeparatedConvolution< T, NDIM/2 > &op, const CCPairFunction< T, NDIM > &arg)
apply the operator to the argument
Definition ccpairfunction.h:896
std::vector< CCPairFunction< T, NDIM > > operator+(const std::vector< CCPairFunction< T, NDIM > > c1, const std::vector< CCPairFunction< T, NDIM > > &c2)
Definition ccpairfunction.h:1052
Function< TENSOR_RESULT_TYPE(L, R), NDIM > mul_sparse(const Function< L, NDIM > &left, const Function< R, NDIM > &right, double tol, bool fence=true, bool do_make_redundant=true)
Sparse multiplication; the scalar interface redirects to the vector one in vmra.h.
Definition mra.h:1977
Function< T, CCPairFunction< T, NDIM >::LDIM > inner(const CCPairFunction< T, NDIM > &c, const Function< T, CCPairFunction< T, NDIM >::LDIM > &f, const std::tuple< int, int, int > v1, const std::tuple< int, int, int > v2)
Definition ccpairfunction.h:993
Function< T, NDIM > multiply(const Function< T, NDIM > f, const Function< T, LDIM > g, const int particle, const bool fence=true)
multiply a high-dimensional function with a low-dimensional function
Definition mra.h:2621
void load(Function< T, NDIM > &f, const std::string name)
Definition mra.h:3032
Function< T, NDIM > project(const Function< T, NDIM > &other, int k=FunctionDefaults< NDIM >::get_k(), double thresh=FunctionDefaults< NDIM >::get_thresh(), bool fence=true)
Definition mra.h:2651
double real(double x)
Definition complexfun.h:52
@ same
same atoms at the same places
std::string name(const FuncType &type, const int ex=-1)
Definition ccpairfunction.h:28
void save(const Function< T, NDIM > &f, const std::string name)
Definition mra.h:3026
Function< T, KDIM+LDIM > hartree_product(const std::vector< Function< T, KDIM > > &left, const std::vector< Function< T, LDIM > > &right)
Performs a Hartree/outer product on the two given low-dimensional function vectors.
Definition mra.h:2062
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:2233
static const double d
Definition nonlinschro.cc:121
Implements most functionality of separated operators.
Implements ParallelInputArchive and ParallelOutputArchive for parallel serialization of data.
double Q(double a)
Definition relops.cc:20
static const double c
Definition relops.cc:10
static const double L
Definition rk.cc:46
Definition test_ar.cc:204
Definition test_dc.cc:47
Definition leafop.h:133
void serialize(Archive &ar)
Definition mra.h:1064
T(* f)(T)
Definition mra.h:1059
void operator()(const Key< NDIM > &key, Tensor< T > &t) const
Definition mra.h:1061
SimpleUnaryOpWrapper(T(*f)(T))
Definition mra.h:1060
void serialize(Archive &ar)
Definition mra.h:993
bool operator()(implT *impl, const Key< NDIM > &key, const nodeT &t) const
Definition mra.h:989
Definition type_data.h:146
Definition leafop.h:185
Definition leafop.h:62
TensorArgs holds the arguments for creating a LowRankTensor.
Definition gentensor.h:134
static void load(const ParallelInputArchive< archiveT > &ar, Function< T, NDIM > &f)
Definition mra.h:3012
Default load of an object via serialize(ar, t).
Definition archive.h:667
static void store(const ParallelOutputArchive< archiveT > &ar, const Function< T, NDIM > &f)
Definition mra.h:3019
Default store of an object via serialize(ar, t).
Definition archive.h:612
Definition mra.h:2900
Tensor< double > operator()(const Key< NDIM > &key, const Tensor< double_complex > &t) const
Definition mra.h:2902
double resultT
Definition mra.h:2901
void serialize(Archive &ar)
Definition mra.h:2907
Definition mra.h:2889
void serialize(Archive &ar)
Definition mra.h:2896
Tensor< double > operator()(const Key< NDIM > &key, const Tensor< double_complex > &t) const
Definition mra.h:2891
double resultT
Definition mra.h:2890
Definition mra.h:2879
Tensor< double > operator()(const Key< NDIM > &key, const Tensor< double_complex > &t) const
Definition mra.h:2881
void serialize(Archive &ar)
Definition mra.h:2885
double resultT
Definition mra.h:2880
Definition mra.h:2869
Tensor< double > operator()(const Key< NDIM > &key, const Tensor< double_complex > &t) const
Definition mra.h:2871
double resultT
Definition mra.h:2870
void serialize(Archive &ar)
Definition mra.h:2875
Definition mra.h:127
Definition funcimpl.h:633
returns true if the result of a hartree_product is a leaf node (compute norm & error)
Definition funcimpl.h:523
Definition mra.h:3042
Definition mra.h:112
Definition mra.h:115
Definition funcimpl.h:587
Definition lowrankfunction.h:336
double real(double a)
Definition tdse4.cc:78
Defines and implements most of Tensor.
#define UNARY_OPTIMIZED_ITERATOR(X, x, exp)
Definition tensor_macros.h:658
AtomicInt sum
Definition test_atomicint.cc:46
double norm(const T i1)
Definition test_cloud.cc:85
int task(int i)
Definition test_runtime.cpp:4
void e()
Definition test_sig.cc:75
static const double alpha
Definition testcosine.cc:10
constexpr std::size_t NDIM
Definition testgconv.cc:54
std::size_t axis
Definition testpdiff.cc:59
#define TENSOR_RESULT_TYPE(L, R)
This macro simplifies access to TensorResultType.
Definition type_data.h:205
Defines operations on vectors of Functions.
Implements WorldContainer.
#define PROFILE_FUNC
Definition worldprofile.h:209
#define PROFILE_MEMBER_FUNC(classname)
Definition worldprofile.h:210