MADNESS 0.10.1
vmra.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 $Id$
32*/
33#ifndef MADNESS_MRA_VMRA_H__INCLUDED
34#define MADNESS_MRA_VMRA_H__INCLUDED
35
36/*!
37 \file vmra.h
38 \brief Defines operations on vectors of Functions
39 \ingroup mra
40
41 This file defines a number of operations on vectors of functions.
42 Assume v is a vector of NDIM-D functions of a certain type.
43
44
45 Operations on array of functions
46
47 *) copying: deep copying of vectors of functions to vector of functions
48 \code
49 vector2 = copy(world, vector1,fence);
50 \endcode
51
52 *) compress: convert multiwavelet representation to legendre representation
53 \code
54 compress(world, vector, fence);
55 \endcode
56
57 *) reconstruct: convert representation to multiwavelets
58 \code
59 reconstruct(world, vector, fence);
60 \endcode
61
62 *) make_nonstandard: convert to non-standard form
63 \code
64 make_nonstandard(world, v, fence);
65 \endcode
66
67 *) standard: convert to standard form
68 \code
69 standard(world, v, fence);
70 \endcode
71
72 *) truncate: truncating vectors of functions to desired precision
73 \code
74 truncate(world, v, tolerance, fence);
75 \endcode
76
77
78 *) zero function: create a vector of zero functions of length n
79 \code
80 v=zero(world, n);
81 \endcode
82
83 *) transform: transform a representation from one basis to another
84 \code
85 transform(world, vector, tensor, tolerance, fence )
86 \endcode
87
88 Setting thresh-hold for precision
89
90 *) set_thresh: setting a finite thresh-hold for a vector of functions
91 \code
92 void set_thresh(World& world, std::vector< Function<T,NDIM> >& v, double thresh, bool fence=true);
93 \endcode
94
95 Arithmetic Operations on arrays of functions
96
97 *) conjugation: conjugate a vector of complex functions
98
99 *) add
100 *) sub
101 *) mul
102 - mul_sparse
103 *) square
104 *) gaxpy
105 *) apply
106
107 Norms, inner-products, blas-1 like operations on vectors of functions
108
109 *) inner
110 *) matrix_inner
111 *) norm_tree
112 *) normalize
113 *) norm2
114 - norm2s
115 *) scale(world, v, alpha);
116
117
118
119
120*/
121
122#include <madness/mra/mra.h>
125#include <cstdio>
126#include <algorithm>
127
128namespace madness {
129
130
131 /// get tree state of a vector of functions
132
133 /// @return TreeState::unknown if the vector is empty or if the functions have different tree states
134 template <typename T, std::size_t NDIM>
136 if (v.size()==0) return TreeState::unknown;
137 // return unknown if any function is not initialized
138 if (std::any_of(v.begin(), v.end(), [](const Function<T,NDIM>& f) {return not f.is_initialized();})) {
139 return TreeState::unknown;
140 }
141 TreeState state=v[0].get_impl()->get_tree_state();
142 for (const auto& f : v) {
143 if (f.get_impl()->get_tree_state()!=state) state=TreeState::unknown;
144 }
145 return state;
146 }
147
148 /// Compress a vector of functions
149 template <typename T, std::size_t NDIM>
150 void compress(World& world,
151 const std::vector< Function<T,NDIM> >& v,
152 bool fence=true) {
153 PROFILE_BLOCK(Vcompress);
155 }
156
157
158 /// reconstruct a vector of functions
159
160 /// implies fence
161 /// return v for chaining
162 template <typename T, std::size_t NDIM>
163 const std::vector< Function<T,NDIM> >& reconstruct(const std::vector< Function<T,NDIM> >& v) {
165 }
166
167 /// compress a vector of functions
168
169 /// implies fence
170 /// return v for chaining
171 template <typename T, std::size_t NDIM>
172 const std::vector< Function<T,NDIM> >& compress(const std::vector< Function<T,NDIM> >& v) {
174 }
175
176 /// Reconstruct a vector of functions
177 template <typename T, std::size_t NDIM>
178 void reconstruct(World& world,
179 const std::vector< Function<T,NDIM> >& v,
180 bool fence=true) {
181 PROFILE_BLOCK(Vreconstruct);
183 }
184
185 /// change tree_state of a vector of functions to redundant
186 template <typename T, std::size_t NDIM>
188 const std::vector< Function<T,NDIM> >& v,
189 bool fence=true) {
190
191 PROFILE_BLOCK(Vcompress);
193 }
194
195 /// refine the functions according to the autorefine criteria
196 template <typename T, std::size_t NDIM>
197 void refine(World& world, const std::vector<Function<T,NDIM> >& vf,
198 bool fence=true) {
199 for (const auto& f : vf) f.refine(false);
200 if (fence) world.gop.fence();
201 }
202
203 /// refine all functions to a common (finest) level
204
205 /// if functions are not initialized (impl==NULL) they are ignored
206 template <typename T, std::size_t NDIM>
207 void refine_to_common_level(World& world, std::vector<Function<T,NDIM> >& vf,
208 bool fence=true) {
209
210 reconstruct(world,vf);
212 std::vector<FunctionImpl<T,NDIM>*> v_ptr;
213
214 // push initialized function pointers into the vector v_ptr
215 for (unsigned int i=0; i<vf.size(); ++i) {
216 if (vf[i].is_initialized()) v_ptr.push_back(vf[i].get_impl().get());
217 }
218
219 // sort and remove duplicates to not confuse the refining function
220 std::sort(v_ptr.begin(),v_ptr.end());
221 typename std::vector<FunctionImpl<T, NDIM>*>::iterator it;
222 it = std::unique(v_ptr.begin(), v_ptr.end());
223 v_ptr.resize( std::distance(v_ptr.begin(),it) );
224
225 std::vector< Tensor<T> > c(v_ptr.size());
226 v_ptr[0]->refine_to_common_level(v_ptr, c, key0);
227 if (fence) v_ptr[0]->world.gop.fence();
228 if (VERIFY_TREE)
229 for (unsigned int i=0; i<vf.size(); i++) vf[i].verify_tree();
230 }
231
232 /// Generates non-standard form of a vector of functions
233 template <typename T, std::size_t NDIM>
235 std::vector< Function<T,NDIM> >& v,
236 bool fence= true) {
237 PROFILE_BLOCK(Vnonstandard);
239 }
240
241
242 /// Generates standard form of a vector of functions
243 template <typename T, std::size_t NDIM>
244 void standard(World& world,
245 std::vector< Function<T,NDIM> >& v,
246 bool fence=true) {
247 PROFILE_BLOCK(Vstandard);
249 }
250
251
252 /// change tree state of the functions
253
254 /// might not respect fence
255 /// @return v for chaining
256 template <typename T, std::size_t NDIM>
257 const std::vector<Function<T,NDIM>>& change_tree_state(const std::vector<Function<T,NDIM>>& v,
258 const TreeState finalstate,
259 const bool fence=true) {
260 // fast return
261 if (v.size()==0) return v;
262 if (get_tree_state(v)==finalstate) return v;
263
264 // find initialized function with world
265 Function<T,NDIM> dummy;
266 for (const auto& f : v)
267 if (f.is_initialized()) {
268 dummy=f;
269 break;
270 }
271 if (not dummy.is_initialized()) return v;
272 World& world=dummy.world();
273
274
275 // if a tree state cannot directly be changed to finalstate, we need to go via intermediate
276 auto change_initial_to_intermediate =[](const std::vector<Function<T,NDIM>>& v,
277 const TreeState initialstate,
278 const TreeState intermediatestate) {
279 int must_fence=0;
280 for (auto& f : v) {
281 if (f.is_initialized() and f.get_impl()->get_tree_state()==initialstate) {
282 f.change_tree_state(intermediatestate,false);
283 must_fence=1;
284 }
285 }
286 return must_fence;
287 };
288
289 int do_fence=0;
290 if (finalstate==compressed) {
291 do_fence+=change_initial_to_intermediate(v,redundant,TreeState::reconstructed);
292 }
293 if (finalstate==nonstandard) {
294 do_fence+=change_initial_to_intermediate(v,compressed,TreeState::reconstructed);
295 do_fence+=change_initial_to_intermediate(v,redundant,TreeState::reconstructed);
296 }
297 if (finalstate==nonstandard_with_leaves) {
298 do_fence+=change_initial_to_intermediate(v,compressed,TreeState::reconstructed);
299 do_fence+=change_initial_to_intermediate(v,nonstandard,TreeState::reconstructed);
300 do_fence+=change_initial_to_intermediate(v,redundant,TreeState::reconstructed);
301 }
302 if (finalstate==redundant) {
303 do_fence+=change_initial_to_intermediate(v,compressed,TreeState::reconstructed);
304 do_fence+=change_initial_to_intermediate(v,nonstandard,TreeState::reconstructed);
305 do_fence+=change_initial_to_intermediate(v,nonstandard_with_leaves,TreeState::reconstructed);
306 }
307 if (do_fence>0) world.gop.fence();
308
309 for (unsigned int i=0; i<v.size(); ++i) v[i].change_tree_state(finalstate,fence);
310 if (fence) world.gop.fence();
311
312 return v;
313 }
314
315 /// ensure v has the requested tree state, change the tree state of v if necessary and no fence is given
316 template<typename T, std::size_t NDIM>
318 const TreeState state, bool fence) {
319 // fast return
320 if (get_tree_state(v)==state) return true;;
321
322 // if there is a fence we can simply change the tree state, might be a no-op
323 if (fence) change_tree_state(v,state,true);
324
325 // check success, throw if not
326 bool ok=get_tree_state(v)==state;
327 if (not ok) {
328 print("ensure_tree_state_respecting_fence failed");
329 throw std::runtime_error("ensure_tree_state_respecting_fence failed");
330 }
331 return ok;
332 }
333
334 /// Truncates a vector of functions
335 template <typename T, std::size_t NDIM>
336 void truncate(World& world,
337 std::vector< Function<T,NDIM> >& v,
338 double tol=0.0,
339 bool fence=true) {
340 PROFILE_BLOCK(Vtruncate);
341
342 // truncate in compressed form only for low-dimensional functions
343 // compression is very expensive if low-rank tensor approximations are used
344 if (NDIM<4) compress(world, v);
345
346 for (auto& vv: v) {
347 vv.truncate(tol, false);
348 }
349
350 if (fence) world.gop.fence();
351 }
352
353 /// Truncates a vector of functions
354
355 /// @return the truncated vector for chaining
356 template <typename T, std::size_t NDIM>
357 std::vector< Function<T,NDIM> > truncate(std::vector< Function<T,NDIM> > v,
358 double tol=0.0, bool fence=true) {
359 if (v.size()>0) truncate(v[0].world(),v,tol,fence);
360 return v;
361 }
362
363 /// reduces the tensor rank of the coefficient tensor (if applicable)
364
365 /// @return the vector for chaining
366 template <typename T, std::size_t NDIM>
367 std::vector< Function<T,NDIM> > reduce_rank(std::vector< Function<T,NDIM> > v,
368 double thresh=0.0, bool fence=true) {
369 if (v.size()==0) return v;
370 for (auto& vv : v) vv.reduce_rank(thresh,false);
371 if (fence) v[0].world().gop.fence();
372 return v;
373 }
374
375
376 /// Pre-stages the neighbor coefficients that differentiating v with each of grad will need
377
378 /// Differentiating then serves those neighbors locally instead of fetching them one at a time.
379 /// One halo per function holds every operator's pushes, so it pays when several functions are
380 /// differentiated together. `clear_halo` frees them afterwards.
381 template <typename T, std::size_t NDIM>
382 void stage_halo(World& world,
383 const std::vector< std::shared_ptr< Derivative<T,NDIM> > >& grad,
384 const std::vector< Function<T,NDIM> >& v,
385 bool fence=true)
386 {
387 for (const auto& f : v) MADNESS_CHECK(f.is_reconstructed());
388 for (const auto& D : grad)
389 for (const auto& f : v) D->stage_halo(f.get_impl().get(), false);
390 if (fence) world.gop.fence();
391 }
392
393 /// Discards the neighbor halos staged on v
394
395 /// Requires a quiescent window: it frees tables the derivative may still be reading.
396 template <typename T, std::size_t NDIM>
397 void clear_halo(const std::vector< Function<T,NDIM> >& v)
398 {
399 for (const auto& f : v) f.get_impl()->halo_clear();
400 }
401
402 /// Applies a derivative operator to a vector of functions
403 template <typename T, std::size_t NDIM>
404 std::vector< Function<T,NDIM> >
405 apply(World& world,
406 const Derivative<T,NDIM>& D,
407 const std::vector< Function<T,NDIM> >& v,
408 bool fence=true)
409 {
410 reconstruct(world, v);
411 std::vector< Function<T,NDIM> > df(v.size());
412 for (unsigned int i=0; i<v.size(); ++i) {
413 df[i] = D(v[i],false);
414 }
415 if (fence) world.gop.fence();
416 return df;
417 }
418
419 /// Generates a vector of zero functions with a given tree state
420 template <typename T, std::size_t NDIM>
421 std::vector< Function<T,NDIM> >
422 zero_functions_tree_state(World& world, int n, const TreeState state, bool fence=true) {
423 std::vector< Function<T,NDIM> > r(n);
424 for (int i=0; i<n; ++i) {
425 if (state==compressed)
426 r[i] = Function<T,NDIM>(FunctionFactory<T,NDIM>(world).fence(false).compressed(true).initial_level(1));
427 else if (state==reconstructed)
428 r[i] = Function<T,NDIM>(FunctionFactory<T,NDIM>(world).fence(false));
429 else {
430 print("zero_functions_tree_state: unknown tree state");
431 throw std::runtime_error("zero_functions_tree_state: unknown tree state");
432 }
433 }
434
435 if (n && fence) world.gop.fence();
436 return r;
437
438 }
439
440 /// Generates a vector of zero functions (reconstructed)
441 template <typename T, std::size_t NDIM>
442 std::vector< Function<T,NDIM> >
443 zero_functions(World& world, int n, bool fence=true) {
444 return zero_functions_tree_state<T,NDIM>(world,n,reconstructed,fence);
445 }
446
447 /// Generates a vector of zero functions (compressed)
448 template <typename T, std::size_t NDIM>
449 std::vector< Function<T,NDIM> >
450 zero_functions_compressed(World& world, int n, bool fence=true) {
451 return zero_functions_tree_state<T,NDIM>(world,n,compressed,fence);
452 }
453
454 /// Generates a vector of zero functions, either compressed or reconstructed, depending on tensor type
455 template <typename T, std::size_t NDIM>
456 std::vector< Function<T,NDIM> >
457 zero_functions_auto_tree_state(World& world, int n, bool fence=true) {
459 return zero_functions_tree_state<T,NDIM>(world,n,state,fence);
460 }
461
462
463
464 /// orthonormalize the vectors
465 template<typename T, std::size_t NDIM>
466 std::vector<Function<T,NDIM>> orthonormalize(const std::vector<Function<T,NDIM> >& vf_in) {
467 if (vf_in.size()==0) return std::vector<Function<T,NDIM>>();
468 World& world=vf_in.front().world();
469 auto vf=copy(world,vf_in);
470 normalize(world,vf);
471 if (vf.size()==1) return copy(world,vf_in);
472 double maxq;
473 double trantol=0.0;
474 auto Q2=[](const Tensor<T>& s) {
475 Tensor<T> Q = -0.5*s;
476 for (int i=0; i<s.dim(0); ++i) Q(i,i) += 1.5;
477 return Q;
478 };
479
480 do {
481 Tensor<T> Q = Q2(matrix_inner(world, vf, vf));
482 maxq=0.0;
483 for (int i=0; i<Q.dim(0); ++i)
484 for (int j=0; j<i; ++j)
485 maxq = std::max(maxq,std::abs(Q(i,j)));
486
487 vf = transform(world, vf, Q, trantol, true);
488 truncate(world, vf);
489
490 } while (maxq>0.01);
491 normalize(world,vf);
492 return vf;
493 }
494
495
496 /// symmetric orthonormalization (see e.g. Szabo/Ostlund)
497
498 /// @param[in] the vector to orthonormalize
499 /// @param[in] overlap matrix
500 template <typename T, std::size_t NDIM>
501 std::vector<Function<T,NDIM> > orthonormalize_symmetric(
502 const std::vector<Function<T,NDIM> >& v,
503 const Tensor<T>& ovlp,
504 double lindep = 1e-12) {
505 if(v.empty()) return v;
506
507 World& world = v.front().world();
508 const size_t n = v.size();
509
510 Tensor<T> U;
512 syev(ovlp, U, s);
513 lindep *= s(s.size() - 1); // eigenvalues are in ascending order
514
515 // transform s to s^{-1/2} in-place
516 int rank = 0, nlindep = 0;
517 for(size_t i = 0; i < n; ++i) {
518 const auto s_i = s(i);
519 s(i) = 1.0 / sqrt(s_i);
520 (s_i > lindep) ? rank++ : nlindep++;
521 }
522 MADNESS_ASSERT(size_t(nlindep + rank) == n);
523
524 // warn of linearly dependent vectors and values
525 if (nlindep > 0) {
526 if (world.rank() == 0)
527 print("WARNING: linear dependencies detected in ", nlindep,
528 " functions, rank = ", rank);
529 }
530
531 // save Ut before U gets modified with s^{-1/2}
532 const Tensor<T> Ut = conj_transpose(U);
533
534 for(size_t i = 0; i < n; ++i){
535 for(size_t j = 0; j < n; ++j){
536 U(i, j) = U(i, j) * s(j);
537 }
538 }
539
540 Tensor<T> X = inner(U, Ut, 1, 0);
541
542 return transform(world, v, X);
543 }
544
545 /// convenience routine for symmetric orthonormalization (see e.g. Szabo/Ostlund)
546 /// overlap matrix is calculated
547 /// @param[in] the vector to orthonormalize
548 template <typename T, std::size_t NDIM>
549 std::vector<Function<T,NDIM> > orthonormalize_symmetric(const std::vector<Function<T,NDIM> >& v,
550 double lindep = 1e-12){
551 if(v.empty()) return v;
552
553 Tensor<T> ovlp = matrix_inner(v.front().world(), v, v, /* sym= */ true);
554
555 return orthonormalize_symmetric(v, ovlp, lindep);
556 }
557
558 /// canonical orthonormalization (see e.g. Szabo/Ostlund)
559 /// @param[in] the vector to orthonormalize
560 /// @param[in] overlap matrix
561 /// @param[in] lindep linear dependency threshold relative to largest eigenvalue
562 template <typename T, std::size_t NDIM>
563 std::vector<Function<T,NDIM> > orthonormalize_canonical(
564 const std::vector<Function<T,NDIM> >& v,
565 const Tensor<T>& ovlp,
566 double lindep = 1e-12) {
567 if(v.empty()) return v;
568
569 World& world = v.front().world();
570 const size_t n = v.size();
571
572 Tensor<T> U;
574 syev(ovlp, U, s);
575 lindep *= s(s.size() - 1); // eigenvalues are in ascending order
576
577 // transform s to s^{-1/2} in-place
578 size_t rank = 0, nlindep = 0;
579 for(size_t i = 0; i < n; ++i) {
580 const auto s_i = s(i);
581 if (s_i > lindep) {
582 s(i) = 1.0 / sqrt(s_i);
583 rank++;
584 } else {
585 nlindep++;
586 }
587 }
588 MADNESS_ASSERT(size_t(nlindep + rank) == n);
589
590 // remove linearly dependent vectors and values
591 if (nlindep > 0) {
592 if (world.rank() == 0)
593 print("Linear dependencies detected: removed ", nlindep,
594 " functions, rank = ", rank);
595 U = U(_, Slice(nlindep, -1));
596 s = s(Slice(nlindep, -1));
597 }
598
599 // modify U in-place, U is now transformation matrix (U * s^{-1/2})
600 for(size_t i = 0; i < n; ++i){
601 for(size_t j = 0; j < rank; ++j){
602 U(i, j) = U(i, j) * s(j);
603 }
604 }
605
606 return transform(world, v, U);
607 }
608
609 /// convenience routine for canonical routine for symmetric orthonormalization (see e.g. Szabo/Ostlund)
610 /// overlap matrix is calculated
611 /// @param[in] the vector to orthonormalize
612 template <typename T, std::size_t NDIM>
613 std::vector<Function<T,NDIM> > orthonormalize_canonical(const std::vector<Function<T,NDIM> >& v,
614 double lindep = 1e-12){
615 if(v.empty()) return v;
616
617 Tensor<T> ovlp = matrix_inner(v.front().world(), v, v, /* sym= */ true);
618
619 return orthonormalize_canonical(v, ovlp, lindep);
620 }
621
622 /// cholesky orthonormalization without pivoting
623 /// @param[in] the vector to orthonormalize
624 /// @param[in] overlap matrix, destroyed on return!
625 template <typename T, std::size_t NDIM>
626 std::vector<Function<T,NDIM> > orthonormalize_cd(
627 const std::vector<Function<T,NDIM> >& v,
628 Tensor<T>& ovlp) {
629
630 if (v.empty()) return v;
631
632 cholesky(ovlp); // destroys ovlp and gives back Upper ∆ Matrix from CD
633
634 Tensor<T> L = transpose(ovlp);
635 Tensor<T> Linv = inverse(L);
636 Tensor<T> U = transpose(Linv);
637
638 World& world=v.front().world();
639 return transform(world, v, U);
640
641 }
642
643 /// convenience routine for cholesky orthonormalization without pivoting
644 /// @param[in] the vector to orthonormalize
645 /// @param[in] overlap matrix
646 template <typename T, std::size_t NDIM>
647 std::vector<Function<T,NDIM> > orthonormalize_cd(const std::vector<Function<T,NDIM> >& v){
648 if(v.empty()) return v;
649
650 World& world=v.front().world();
651 Tensor<T> ovlp = matrix_inner(world, v, v, /* sym= */ true);
652
653 return orthonormalize_cd(v,ovlp);
654 }
655
656 /// @param[in] the vector to orthonormalize
657 /// @param[in] overlap matrix, will be destroyed on return!
658 /// @param[in] tolerance for numerical rank reduction
659 /// @param[out] pivoting vector, no allocation on input needed
660 /// @param[out] rank
661 /// @return orthonormalized vector (may or may not be truncated)
662 template <typename T, std::size_t NDIM>
663 std::vector<Function<T,NDIM> > orthonormalize_rrcd(
664 const std::vector<Function<T,NDIM> >& v,
665 Tensor<T>& ovlp,
666 const double tol,
667 Tensor<integer>& piv,
668 int& rank) {
669
670 if (v.empty()) {
671 return v;
672 }
673
674 rr_cholesky(ovlp,tol,piv,rank); // destroys ovlp and gives back Upper ∆ Matrix from CCD
675
676 // rearrange and truncate the functions according to the pivoting of the rr_cholesky
677 std::vector<Function<T,NDIM> > pv(rank);
678 for(integer i=0;i<rank;++i){
679 pv[i]=v[piv[i]];
680 }
681 ovlp=ovlp(Slice(0,rank-1),Slice(0,rank-1));
682
683 Tensor<T> L = transpose(ovlp);
684 Tensor<T> Linv = inverse(L);
685 Tensor<T> U = transpose(Linv);
686
687 World& world=v.front().world();
688 return transform(world, pv, U);
689 }
690
691 /// convenience routine for orthonormalize_cholesky: orthonormalize_cholesky without information on pivoting and rank
692 /// @param[in] the vector to orthonormalize
693 /// @param[in] overlap matrix
694 /// @param[in] tolerance for numerical rank reduction
695 template <typename T, std::size_t NDIM>
696 std::vector<Function<T,NDIM> > orthonormalize_rrcd(const std::vector<Function<T,NDIM> >& v, Tensor<T> ovlp , const double tol) {
697 Tensor<integer> piv;
698 int rank;
699 return orthonormalize_rrcd(v,ovlp,tol,piv,rank);
700 }
701
702 /// convenience routine for orthonormalize_cholesky: computes the overlap matrix and then calls orthonormalize_cholesky
703 /// @param[in] the vector to orthonormalize
704 /// @param[in] tolerance for numerical rank reduction
705 template <typename T, std::size_t NDIM>
706 std::vector<Function<T,NDIM> > orthonormalize_rrcd(const std::vector<Function<T,NDIM> >& v, const double tol) {
707 if (v.empty()) {
708 return v;
709 }
710 // compute overlap
711 World& world=v.front().world();
712 Tensor<T> ovlp = matrix_inner(world, v, v, /* sym= */ true);
713 return orthonormalize_rrcd(v,ovlp,tol);
714 }
715
716 /// combine two vectors
717 template <typename T, std::size_t NDIM>
718 std::vector<Function<T,NDIM> > append(const std::vector<Function<T,NDIM> > & lhs, const std::vector<Function<T,NDIM> > & rhs){
719 std::vector<Function<T,NDIM> > v=lhs;
720 for (std::size_t i = 0; i < rhs.size(); ++i) v.push_back(rhs[i]);
721 return v;
722 }
723
724 template <typename T, std::size_t NDIM>
725 std::vector<Function<T,NDIM> > flatten(const std::vector< std::vector<Function<T,NDIM> > >& vv){
726 std::vector<Function<T,NDIM> >result;
727 for(const auto& x:vv) result=append(result,x);
728 return result;
729 }
730
731 template<typename T, std::size_t NDIM>
732 std::vector<std::shared_ptr<FunctionImpl<T,NDIM>>> get_impl(const std::vector<Function<T,NDIM>>& v) {
733 std::vector<std::shared_ptr<FunctionImpl<T,NDIM>>> result;
734 for (auto& f : v) result.push_back(f.get_impl());
735 return result;
736 }
737
738 template<typename T, std::size_t NDIM>
739 void set_impl(std::vector<Function<T,NDIM>>& v, const std::vector<std::shared_ptr<FunctionImpl<T,NDIM>>> vimpl) {
740 MADNESS_CHECK(vimpl.size()==v.size());
741 for (std::size_t i=0; i<vimpl.size(); ++i) v[i].set_impl(vimpl[i]);
742 }
743
744 template<typename T, std::size_t NDIM>
745 std::vector<Function<T,NDIM>> impl2function(const std::vector<std::shared_ptr<FunctionImpl<T,NDIM>>> vimpl) {
746 std::vector<Function<T,NDIM>> v(vimpl.size());
747 for (std::size_t i=0; i<vimpl.size(); ++i) v[i].set_impl(vimpl[i]);
748 return v;
749 }
750
751
752 /// Transforms a vector of functions according to new[i] = sum[j] old[j]*c[j,i]
753
754 /// Uses sparsity in the transformation matrix --- set small elements to
755 /// zero to take advantage of this.
756 template <typename T, typename R, std::size_t NDIM>
757 std::vector< Function<TENSOR_RESULT_TYPE(T,R),NDIM> >
759 const std::vector< Function<T,NDIM> >& v,
760 const Tensor<R>& c,
761 bool fence=true) {
762
763 PROFILE_BLOCK(Vtransformsp);
764 typedef TENSOR_RESULT_TYPE(T,R) resultT;
765 int n = v.size(); // n is the old dimension
766 int m = c.dim(1); // m is the new dimension
767 MADNESS_CHECK(n==c.dim(0));
768
769 std::vector< Function<resultT,NDIM> > vc = zero_functions_compressed<resultT,NDIM>(world, m);
770 compress(world, v);
771
772 for (int i=0; i<m; ++i) {
773 for (int j=0; j<n; ++j) {
774 if (c(j,i) != R(0.0)) vc[i].gaxpy(resultT(1.0),v[j],resultT(c(j,i)),false);
775 }
776 }
777
778 if (fence) world.gop.fence();
779 return vc;
780 }
781
782 /// Transforms a vector of functions according to new[i] = sum[j] old[j]*c[j,i]
783
784 /// all trees are in reconstructed state, final trees have to be summed down if no fence is present
785 template <typename T, typename R, std::size_t NDIM>
786 std::vector< Function<TENSOR_RESULT_TYPE(T,R),NDIM> >
788 const std::vector< Function<T,NDIM> >& v,
789 const Tensor<R>& c,
790 bool fence=true) {
791
792 PROFILE_BLOCK(Vtransformsp);
793 typedef TENSOR_RESULT_TYPE(T,R) resultT;
794 int n = v.size(); // n is the old dimension
795 int m = c.dim(1); // m is the new dimension
796 MADNESS_CHECK(n==c.dim(0));
797
798 // if we fence set the right tree state here, otherwise it has to be correct from the start.
800 for (const auto& vv : v) MADNESS_CHECK_THROW(
801 vv.get_impl()->get_tree_state()==reconstructed,"trees have to be reconstructed in transform_reconstructed");
802
803 std::vector< Function<resultT,NDIM> > result = zero_functions<resultT,NDIM>(world, m);
804
805 for (int i=0; i<m; ++i) {
806 result[i].get_impl()->set_tree_state(redundant_after_merge);
807 for (int j=0; j<n; ++j) {
808 if (c(j,i) != R(0.0)) v[j].get_impl()->accumulate_trees(*(result[i].get_impl()),resultT(c(j,i)),true);
809 }
810 }
811
812 // if we fence we can as well finish the job here. Otherwise no harm done, as the tree state is well-defined.
813 if (fence) {
814 world.gop.fence();
815 // for (auto& r : vc) r.sum_down(false);
816 for (auto& r : result) r.get_impl()->finalize_sum();
817 world.gop.fence();
818 }
819 return result;
820 }
821
822 /// this version of transform uses Function::vtransform and screens
823 /// using both elements of `c` and `v`
824 template <typename L, typename R, std::size_t NDIM>
825 std::vector< Function<TENSOR_RESULT_TYPE(L,R),NDIM> >
826 transform(World& world, const std::vector< Function<L,NDIM> >& v,
827 const Tensor<R>& c, double tol, bool fence=true) {
828 PROFILE_BLOCK(Vtransform);
829 MADNESS_ASSERT(v.size() == (unsigned int)(c.dim(0)));
830
831 std::vector< Function<TENSOR_RESULT_TYPE(L,R),NDIM> > vresult
832 = zero_functions_compressed<TENSOR_RESULT_TYPE(L,R),NDIM>(world, c.dim(1));
833
834 compress(world, v, true);
835 vresult[0].vtransform(v, c, vresult, tol, fence);
836 return vresult;
837 }
838
839 template <typename T, typename R, std::size_t NDIM>
840 std::vector< Function<TENSOR_RESULT_TYPE(T,R),NDIM> >
842 const std::vector< Function<T,NDIM> >& v,
843 const DistributedMatrix<R>& c,
844 bool fence=true) {
846
847 typedef TENSOR_RESULT_TYPE(T,R) resultT;
848 long n = v.size(); // n is the old dimension
849 long m = c.rowdim(); // m is the new dimension
850 MADNESS_ASSERT(n==c.coldim());
851
852 // new(i) = sum(j) old(j) c(j,i)
853
854 Tensor<T> tmp(n,m);
855 c.copy_to_replicated(tmp); // for debugging
856 tmp = transpose(tmp);
857
858 std::vector< Function<resultT,NDIM> > vc = zero_functions_compressed<resultT,NDIM>(world, m);
859 compress(world, v);
860
861 for (int i=0; i<m; ++i) {
862 for (int j=0; j<n; ++j) {
863 if (tmp(j,i) != R(0.0)) vc[i].gaxpy(1.0,v[j],tmp(j,i),false);
864 }
865 }
866
867 if (fence) world.gop.fence();
868 return vc;
869 }
870
871
872 /// Scales inplace a vector of functions by distinct values
873 template <typename T, typename Q, std::size_t NDIM>
874 void scale(World& world,
875 std::vector< Function<T,NDIM> >& v,
876 const std::vector<Q>& factors,
877 bool fence=true) {
878 PROFILE_BLOCK(Vscale);
879 for (unsigned int i=0; i<v.size(); ++i) v[i].scale(factors[i],false);
880 if (fence) world.gop.fence();
881 }
882
883 /// Scales inplace a vector of functions by the same
884 template <typename T, typename Q, std::size_t NDIM>
885 void scale(World& world,
886 std::vector< Function<T,NDIM> >& v,
887 const Q factor,
888 bool fence=true) {
889 PROFILE_BLOCK(Vscale);
890 for (unsigned int i=0; i<v.size(); ++i) v[i].scale(factor,false);
891 if (fence) world.gop.fence();
892 }
893
894 namespace detail {
895 /// put a vector of functions into a state whose coefficients sum to ||f||^2
896
897 /// norm2sq_local() adds up the nodes of a tree, which is the norm only in
898 /// a state that carries the coefficients once; where the duplicates are
899 /// the interior nodes it skips them, so the redundant and
900 /// nonstandard-with-leaves trees left behind by mul_sparse() and friends
901 /// need no conversion at all. The rest are reconstructed, and that is a
902 /// mutation: it discards the interior coefficients. So fence first, or
903 /// the removal tasks race a task still reading those coefficients, e.g. a
904 /// mul_sparse(..., fence=false) that has not been fenced yet.
905 /// Cf. Function::norm2(), which does the same for a single function.
906 /// The branch is taken on the tree state, which is replicated, so all
907 /// ranks take the same branch and the global ops stay collective.
908 template <typename T, std::size_t NDIM>
909 void reconstruct_for_norm(World& world, const std::vector<Function<T,NDIM>>& v) {
910 if (v.size()==0) return; // nothing to sum, and nothing to fence for
911 if (std::all_of(v.begin(), v.end(), [](const Function<T,NDIM>& f) {
912 return (not f.is_initialized()) or f.get_impl()->has_summable_coefficients();}))
913 return;
914 MADNESS_CHECK_THROW(std::none_of(v.begin(), v.end(),
915 [](const Function<T,NDIM>& f) {return f.is_initialized() and f.is_on_demand();}),
916 "norm2/norm2s are not defined for an on-demand function; materialize it first");
917 world.gop.fence();
918 reconstruct(world,v);
919 }
920 }
921
922 /// Computes the 2-norms of a vector of functions
923
924 /// Reconstructs the functions if their state does not admit a direct sum;
925 /// see detail::reconstruct_for_norm for what that costs.
926 template <typename T, std::size_t NDIM>
927 std::vector<double> norm2s(World& world,
928 const std::vector< Function<T,NDIM> >& v) {
929 PROFILE_BLOCK(Vnorm2);
930 std::vector<double> norms(v.size());
931 if (v.size()==0) return norms; // &norms[0] below is UB on an empty container
933 for (unsigned int i=0; i<v.size(); ++i) norms[i] = v[i].norm2sq_local();
934 world.gop.sum(&norms[0], norms.size());
935 for (unsigned int i=0; i<v.size(); ++i) norms[i] = sqrt(norms[i]);
936 world.gop.fence();
937 return norms;
938 }
939 /// Computes the 2-norms of a vector of functions
940
941 /// Reconstructs the functions if their state does not admit a direct sum;
942 /// see detail::reconstruct_for_norm for what that costs.
943 template <typename T, std::size_t NDIM>
944 Tensor<double> norm2s_T(World& world, const std::vector<Function<T, NDIM>>& v) {
945 PROFILE_BLOCK(Vnorm2);
946 Tensor<double> norms(v.size());
947 if (v.size()==0) return norms; // &norms[0] below is UB on an empty container
949 for (unsigned int i = 0; i < v.size(); ++i) norms[i] = v[i].norm2sq_local();
950 world.gop.sum(&norms[0], norms.size());
951 for (unsigned int i = 0; i < v.size(); ++i) norms[i] = sqrt(norms[i]);
952 world.gop.fence();
953 return norms;
954 }
955
956 /// Computes the 2-norm of a vector of functions
957
958 /// Reconstructs the functions if their state does not admit a direct sum;
959 /// see detail::reconstruct_for_norm for what that costs.
960 template <typename T, std::size_t NDIM>
961 double norm2(World& world,const std::vector< Function<T,NDIM> >& v) {
962 PROFILE_BLOCK(Vnorm2);
963 if (v.size()==0) return 0.0;
965 std::vector<double> norms(v.size());
966 for (unsigned int i=0; i<v.size(); ++i) norms[i] = v[i].norm2sq_local();
967 world.gop.sum(&norms[0], norms.size());
968 for (unsigned int i=1; i<v.size(); ++i) norms[0] += norms[i];
969 world.gop.fence();
970 return sqrt(norms[0]);
971 }
972
973 inline double conj(double x) {
974 return x;
975 }
976
977 inline double conj(float x) {
978 return x;
979 }
980
981// !!! FIXME: this task is broken because FunctionImpl::inner_local forces a
982// future on return from WorldTaskQueue::reduce, which will causes a deadlock if
983// run inside a task. This behavior must be changed before this task can be used
984// again.
985//
986// template <typename T, typename R, std::size_t NDIM>
987// struct MatrixInnerTask : public TaskInterface {
988// Tensor<TENSOR_RESULT_TYPE(T,R)> result; // Must be a copy
989// const Function<T,NDIM>& f;
990// const std::vector< Function<R,NDIM> >& g;
991// long jtop;
992//
993// MatrixInnerTask(const Tensor<TENSOR_RESULT_TYPE(T,R)>& result,
994// const Function<T,NDIM>& f,
995// const std::vector< Function<R,NDIM> >& g,
996// long jtop)
997// : result(result), f(f), g(g), jtop(jtop) {}
998//
999// void run(World& world) {
1000// for (long j=0; j<jtop; ++j) {
1001// result(j) = f.inner_local(g[j]);
1002// }
1003// }
1004//
1005// private:
1006// /// Get the task id
1007//
1008// /// \param id The id to set for this task
1009// virtual void get_id(std::pair<void*,unsigned short>& id) const {
1010// PoolTaskInterface::make_id(id, *this);
1011// }
1012// }; // struct MatrixInnerTask
1013
1014
1015
1016 template <typename T, std::size_t NDIM>
1018 const std::vector< Function<T,NDIM> >& f,
1019 const std::vector< Function<T,NDIM> >& g,
1020 bool sym=false)
1021 {
1024 const int64_t n = A.coldim();
1025 const int64_t m = A.rowdim();
1026 MADNESS_ASSERT(int64_t(f.size()) == n && int64_t(g.size()) == m);
1027
1028 // Assume we can always create an ichunk*jchunk matrix locally
1029 const int ichunk = 1000;
1030 const int jchunk = 1000; // 1000*1000*8 = 8 MBytes
1031 for (int64_t ilo=0; ilo<n; ilo+=ichunk) {
1032 int64_t ihi = std::min(ilo + ichunk, n);
1033 std::vector< Function<T,NDIM> > ivec(f.begin()+ilo, f.begin()+ihi);
1034 for (int64_t jlo=0; jlo<m; jlo+=jchunk) {
1035 int64_t jhi = std::min(jlo + jchunk, m);
1036 std::vector< Function<T,NDIM> > jvec(g.begin()+jlo, g.begin()+jhi);
1037
1038 Tensor<T> P = matrix_inner(A.get_world(), ivec, jvec);
1039 A.copy_from_replicated_patch(ilo, ihi - 1, jlo, jhi - 1, P);
1040 }
1041 }
1042 return A;
1043 }
1044
1045 /// Computes the matrix inner product of two function vectors - q(i,j) = inner(f[i],g[j])
1046
1047 /// For complex types symmetric is interpreted as Hermitian.
1048
1049 /// The current parallel loop is non-optimal but functional.
1050 template <typename T, typename R, std::size_t NDIM>
1052 const std::vector< Function<T,NDIM> >& f,
1053 const std::vector< Function<R,NDIM> >& g,
1054 bool sym=false)
1055 {
1056 world.gop.fence();
1057 auto tensor_type = [](const std::vector<Function<T,NDIM>>& v) {
1058 return v.front().get_impl()->get_tensor_type();
1059 };
1060 TreeState operating_state=tensor_type(f)==TT_FULL ? compressed : redundant;
1061 ensure_tree_state_respecting_fence(f,operating_state,true);
1062 ensure_tree_state_respecting_fence(g,operating_state,true);
1063
1064 std::vector<const FunctionImpl<T,NDIM>*> left(f.size());
1065 std::vector<const FunctionImpl<R,NDIM>*> right(g.size());
1066 for (unsigned int i=0; i<f.size(); i++) left[i] = f[i].get_impl().get();
1067 for (unsigned int i=0; i<g.size(); i++) right[i]= g[i].get_impl().get();
1068
1070
1071 world.gop.fence();
1072 world.gop.sum(r.ptr(),f.size()*g.size());
1073
1074 return r;
1075 }
1076
1077 /// Computes the matrix inner product of two function vectors - q(i,j) = inner(f[i],g[j])
1078
1079 /// For complex types symmetric is interpreted as Hermitian.
1080 ///
1081 /// The current parallel loop is non-optimal but functional.
1082 template <typename T, typename R, std::size_t NDIM>
1084 const std::vector< Function<T,NDIM> >& f,
1085 const std::vector< Function<R,NDIM> >& g,
1086 bool sym=false) {
1087 PROFILE_BLOCK(Vmatrix_inner);
1088 long n=f.size(), m=g.size();
1089 Tensor< TENSOR_RESULT_TYPE(T,R) > r(n,m);
1090 if (sym) MADNESS_ASSERT(n==m);
1091
1092 world.gop.fence();
1093 compress(world, f);
1094 if ((void*)(&f) != (void*)(&g)) compress(world, g);
1095
1096 for (long i=0; i<n; ++i) {
1097 long jtop = m;
1098 if (sym) jtop = i+1;
1099 for (long j=0; j<jtop; ++j) {
1100 r(i,j) = f[i].inner_local(g[j]);
1101 if (sym) r(j,i) = conj(r(i,j));
1102 }
1103 }
1104
1105// for (long i=n-1; i>=0; --i) {
1106// long jtop = m;
1107// if (sym) jtop = i+1;
1108// world.taskq.add(new MatrixInnerTask<T,R,NDIM>(r(i,_), f[i], g, jtop));
1109// }
1110 world.gop.fence();
1111 world.gop.sum(r.ptr(),n*m);
1112
1113// if (sym) {
1114// for (int i=0; i<n; ++i) {
1115// for (int j=0; j<i; ++j) {
1116// r(j,i) = conj(r(i,j));
1117// }
1118// }
1119// }
1120 return r;
1121 }
1122
1123 /// Computes the element-wise inner product of two function vectors - q(i) = inner(f[i],g[i])
1124
1125 /// works in reconstructed or compressed state, state is chosen based on TensorType
1126 template <typename T, typename R, std::size_t NDIM>
1128 const std::vector< Function<T,NDIM> >& f,
1129 const std::vector< Function<R,NDIM> >& g) {
1130 PROFILE_BLOCK(Vinnervv);
1131 long n=f.size(), m=g.size();
1132 MADNESS_CHECK(n==m);
1133 Tensor< TENSOR_RESULT_TYPE(T,R) > r(n);
1134 if (n==0) return r;
1135
1136 auto tensor_type = [](const std::vector<Function<T,NDIM>>& v) {
1137 return v.front().get_impl()->get_tensor_type();
1138 };
1139 TreeState operating_state=tensor_type(f)==TT_FULL ? compressed : redundant;
1140 ensure_tree_state_respecting_fence(f,operating_state,true);
1141 ensure_tree_state_respecting_fence(g,operating_state,true);
1142
1143 for (long i=0; i<n; ++i) r(i) = f[i].inner_local(g[i]);
1144
1145 world.taskq.fence();
1146 world.gop.sum(r.ptr(),n);
1147 world.gop.fence();
1148 return r;
1149 }
1150
1151
1152 /// Computes the inner product of a function with a function vector - q(i) = inner(f,g[i])
1153
1154 /// works in reconstructed or compressed state, state is chosen based on TensorType
1155 template <typename T, typename R, std::size_t NDIM>
1157 const Function<T,NDIM>& f,
1158 const std::vector< Function<R,NDIM> >& g) {
1159 PROFILE_BLOCK(Vinner);
1160 long n=g.size();
1161 Tensor< TENSOR_RESULT_TYPE(T,R) > r(n);
1162
1163 auto tensor_type = [](const std::vector<Function<T,NDIM>>& v) {
1164 return v.front().get_impl()->get_tensor_type();
1165 };
1166 TreeState operating_state=tensor_type(g)==TT_FULL ? compressed : redundant;
1167 f.change_tree_state(operating_state,false);
1168 ensure_tree_state_respecting_fence(g,operating_state,true);
1169 world.gop.fence();
1170
1171 for (long i=0; i<n; ++i) {
1172 r(i) = f.inner_local(g[i]);
1173 }
1174
1175 world.taskq.fence();
1176 world.gop.sum(r.ptr(),n);
1177 world.gop.fence();
1178 return r;
1179 }
1180
1181 /// inner function with right signature for the nonlinear solver
1182 /// this is needed for the KAIN solvers and other functions
1183 template <typename T, typename R, std::size_t NDIM>
1184 TENSOR_RESULT_TYPE(T,R) inner( const std::vector< Function<T,NDIM> >& f,
1185 const std::vector< Function<R,NDIM> >& g){
1186 MADNESS_ASSERT(f.size()==g.size());
1187 if(f.empty()) return 0.0;
1188 else return inner(f[0].world(),f,g).sum();
1189 }
1190
1191
1192 /// Multiplies a function against a vector of functions --- q[i] = a * v[i]
1193 template <typename T, typename R, std::size_t NDIM>
1194 std::vector< Function<TENSOR_RESULT_TYPE(T,R), NDIM> >
1195 mul(World& world,
1196 const Function<T,NDIM>& a,
1197 const std::vector< Function<R,NDIM> >& v,
1198 bool fence=true) {
1199 PROFILE_BLOCK(Vmul);
1200 make_redundant(world, v, false);
1201 a.make_redundant(false);
1202 world.gop.fence();
1203 return vmulXX(a, v, 0.0, fence);
1204 }
1205
1206 /// Multiplies a function against a vector of functions using sparsity of a and v[i] --- q[i] = a * v[i]
1207 ///
1208 /// Box pairs whose estimated contribution falls below the tolerance are skipped instead
1209 /// of being multiplied. Both inputs are made redundant; the screening reads their
1210 /// norm_tree and dnorm_tree.
1211 ///
1212 /// Leaves both inputs in redundant form. Function is a shallow handle, so this is visible
1213 /// to the caller: logically const, not bitwise const. Converting back is not free, so a
1214 /// caller that reuses the operands afterwards must do it itself.
1215 ///
1216 /// @param[in] tol target absolute accuracy of the product; the safety margin is applied
1217 /// internally (FunctionImpl::MUL_SCREENING_SAFETY), so pass the accuracy
1218 /// wanted, not a pre-scaled value. tol=0 multiplies exactly. The criterion
1219 /// estimates the neglected cross terms rather than bounding them: the error
1220 /// tracks tol up to a measured O(1-20) constant and decays as ~tol^0.75
1221 /// rather than ~tol (see test_mul_sparse.cc). The meaning differs from the
1222 /// earlier norm_tree-based screen, so a previously tuned value needs
1223 /// re-checking.
1224 /// @param[in] do_make_redundant if false, both inputs must already be redundant
1225 template <typename T, typename R, std::size_t NDIM>
1226 std::vector< Function<TENSOR_RESULT_TYPE(T,R), NDIM> >
1228 const Function<T,NDIM>& a,
1229 const std::vector< Function<R,NDIM> >& v,
1230 double tol,
1231 bool fence=true,
1232 bool do_make_redundant=true) {
1233 PROFILE_BLOCK(Vmulsp);
1234 if (do_make_redundant) {
1235 try {
1238 } catch (...) {
1239 print("could not respect fence in mul_sparse");
1240 a.make_redundant(false);
1241 make_redundant(world, v, false);
1242 world.gop.fence();
1243 }
1244 } else if (!v.empty()) {
1245 MADNESS_CHECK_THROW(a.get_impl()->get_tree_state() == TreeState::redundant,
1246 "mul_sparse: left input must be redundant when do_make_redundant=false");
1248 "mul_sparse: right inputs must be redundant when do_make_redundant=false");
1249 }
1250 return vmulXX(a, v, tol, fence);
1251 }
1252
1253 /// Multiplies two vectors of functions using sparsity of a[i] and b[i] --- q[i] = a[i] * b[i]
1254 ///
1255 /// Box pairs whose estimated contribution falls below the tolerance are skipped instead
1256 /// of being multiplied. Both inputs are made redundant; the screening reads their
1257 /// norm_tree and dnorm_tree.
1258 ///
1259 /// Leaves both inputs in redundant form. Function is a shallow handle, so this is visible
1260 /// to the caller: logically const, not bitwise const. Converting back is not free, so a
1261 /// caller that reuses the operands afterwards must do it itself.
1262 ///
1263 /// @param[in] tol target absolute accuracy of the product; the safety margin is applied
1264 /// internally (FunctionImpl::MUL_SCREENING_SAFETY), so pass the accuracy
1265 /// wanted, not a pre-scaled value. tol=0 multiplies exactly. The criterion
1266 /// estimates the neglected cross terms rather than bounding them: the error
1267 /// tracks tol up to a measured O(1-20) constant and decays as ~tol^0.75
1268 /// rather than ~tol (see test_mul_sparse.cc). The meaning differs from the
1269 /// earlier norm_tree-based screen, so a previously tuned value needs
1270 /// re-checking.
1271 /// @param[in] do_make_redundant if false, both inputs must already be redundant
1272 template <typename T, typename R, std::size_t NDIM>
1273 std::vector< Function<TENSOR_RESULT_TYPE(T,R), NDIM> >
1275 const std::vector< Function<T,NDIM> >& a,
1276 const std::vector< Function<R,NDIM> >& b,
1277 double tol,
1278 bool fence=true,
1279 bool do_make_redundant=true) {
1280 PROFILE_BLOCK(Vmulvv);
1281 if (do_make_redundant) {
1282 try {
1285 } catch (...) {
1286 print("could not respect fence in mul_sparse");
1287 make_redundant(world, a, false);
1288 make_redundant(world, b, false);
1289 world.gop.fence();
1290 }
1291 }
1292 std::vector< Function<TENSOR_RESULT_TYPE(T,R),NDIM> > q(a.size());
1293 for (unsigned int i=0; i<a.size(); ++i) {
1294 q[i] = mul_sparse(a[i], b[i], tol, false, false);
1295 }
1296 if (fence) world.gop.fence();
1297 return q;
1298 }
1299
1300
1301 /// Outer product of a vector of functions with a vector of functions using sparsity
1302
1303 /// \tparam T type parameter for first factor
1304 /// \tparam R type parameter for second factor
1305 /// \tparam NDIM dimension of first and second factors
1306 /// \param world the world
1307 /// \param f first vector of functions
1308 /// \param g second vector of functions
1309 /// \param tol target absolute accuracy of each product; see mul_sparse for the
1310 /// semantics, including the internal safety margin and tol=0
1311 /// \param fence force fence (will always fence if necessary)
1312 /// \param symm if true, only compute f(i) * g(j) for j<=i
1313 /// \return fg(i,j) = f(i) * g(j), as a vector of vectors
1314 template <typename T, typename R, std::size_t NDIM>
1315 std::vector<std::vector<Function<TENSOR_RESULT_TYPE(T, R), NDIM> > >
1317 const std::vector<Function<R, NDIM> > &f,
1318 const std::vector<Function<R, NDIM> > &g,
1319 double tol,
1320 bool fence = true,
1321 bool symm = false) {
1322 PROFILE_BLOCK(Vmulsp);
1323 bool same=(&f == &g);
1324 make_redundant(world, f, false);
1325 if (not same) make_redundant(world, g, false);
1326 world.gop.fence();
1327
1328 std::vector<std::vector<Function<R,NDIM> > >result(f.size());
1329 std::vector<Function<R,NDIM>> g_i;
1330 for (int64_t i=f.size()-1; i>=0; --i) {
1331 if (!symm)
1332 result[i]= vmulXX(f[i], g, tol, false);
1333 else {
1334 if (g_i.empty()) g_i = g;
1335 g_i.resize(i+1); // this shrinks g_i down to single function for i=0
1336 result[i]= vmulXX(f[i], g_i, tol, false);
1337 }
1338 }
1339 if (fence) world.gop.fence();
1340 return result;
1341 }
1342
1343 /// Makes the norm tree for all functions in a vector
1344 template <typename T, std::size_t NDIM>
1345 void norm_tree(World& world,
1346 const std::vector< Function<T,NDIM> >& v,
1347 bool fence=true)
1348 {
1349 PROFILE_BLOCK(Vnorm_tree);
1350 for (unsigned int i=0; i<v.size(); ++i) {
1351 v[i].norm_tree(false);
1352 }
1353 if (fence) world.gop.fence();
1354 }
1355
1356 /// Multiplies two vectors of functions q[i] = a[i] * b[i]; see mul_sparse to screen
1357 template <typename T, typename R, std::size_t NDIM>
1358 std::vector< Function<TENSOR_RESULT_TYPE(T,R), NDIM> >
1359 mul(World& world,
1360 const std::vector< Function<T,NDIM> >& a,
1361 const std::vector< Function<R,NDIM> >& b,
1362 bool fence=true,
1363 bool do_make_redundant=true) {
1364 PROFILE_BLOCK(Vmulvv);
1365 if (do_make_redundant) {
1366 try {
1369 } catch (...) {
1370 print("could not respect fence in mul");
1371 make_redundant(world, a, false);
1372 make_redundant(world, b, false);
1373 world.gop.fence();
1374 }
1375 }
1376 std::vector< Function<TENSOR_RESULT_TYPE(T,R),NDIM> > q(a.size());
1377 for (unsigned int i=0; i<a.size(); ++i) {
1378 q[i] = mul(a[i], b[i], false, false);
1379 }
1380 if (fence) world.gop.fence();
1381 return q;
1382 }
1383
1384
1385 /// multiply a high-dimensional function with a low-dimensional function
1386
1387 /// @param[in] f NDIM function of NDIM dimensions
1388 /// @param[in] g LDIM function of LDIM
1389 /// @param[in] v dimension indices of f to multiply
1390 /// @return h[i](0,1,2,3) = f(0,1,2,3) * g[i](1,2,3) for v={1,2,3}
1391 template<typename T, std::size_t NDIM, std::size_t LDIM>
1392 std::vector<Function<T,NDIM> > partial_mul(const Function<T,NDIM> f, const std::vector<Function<T,LDIM> > g,
1393 const int particle) {
1394
1395 World& world=f.world();
1396 std::vector<Function<T,NDIM> > result(g.size());
1397 for (auto& r : result) r.set_impl(f, false);
1398
1399 FunctionImpl<T,NDIM>* fimpl=f.get_impl().get();
1400// fimpl->make_redundant(false);
1401 fimpl->change_tree_state(redundant,false);
1402 make_redundant(world,g,false);
1403 world.gop.fence();
1404
1405 for (std::size_t i=0; i<result.size(); ++i) {
1406 FunctionImpl<T,LDIM>* gimpl=g[i].get_impl().get();
1407 result[i].get_impl()->multiply(fimpl,gimpl,particle); // stupid naming inconsistency
1408 }
1409 world.gop.fence();
1410
1411 fimpl->undo_redundant(false);
1412 for (auto& ig : g) ig.get_impl()->undo_redundant(false);
1413 world.gop.fence();
1414 return result;
1415 }
1416
1417 template<typename T, std::size_t NDIM, std::size_t LDIM>
1418 std::vector<Function<T,NDIM> > multiply(const Function<T,NDIM> f, const std::vector<Function<T,LDIM> > g,
1419 const std::tuple<int,int,int> v) {
1420 return partial_mul<T,NDIM,LDIM>(f,g,std::array<int,3>({std::get<0>(v),std::get<1>(v),std::get<2>(v)}));
1421 }
1422
1423
1424/// Computes the square of a vector of functions --- q[i] = v[i]**2
1425 template <typename T, std::size_t NDIM>
1426 std::vector< Function<T,NDIM> >
1428 const std::vector< Function<T,NDIM> >& v,
1429 bool fence=true) {
1430 return mul<T,T,NDIM>(world, v, v, fence);
1431// std::vector< Function<T,NDIM> > vsq(v.size());
1432// for (unsigned int i=0; i<v.size(); ++i) {
1433// vsq[i] = square(v[i], false);
1434// }
1435// if (fence) world.gop.fence();
1436// return vsq;
1437 }
1438
1439
1440 /// Computes the square of a vector of functions --- q[i] = abs(v[i])**2
1441 template <typename T, std::size_t NDIM>
1442 std::vector< Function<typename Tensor<T>::scalar_type,NDIM> >
1443 abssq(World& world,
1444 const std::vector< Function<T,NDIM> >& v,
1445 bool fence=true) {
1446 typedef typename Tensor<T>::scalar_type scalartype;
1447 reconstruct(world,v);
1448 std::vector<Function<scalartype,NDIM> > result(v.size());
1449 for (size_t i=0; i<v.size(); ++i) result[i]=abs_square(v[i],false);
1450 if (fence) world.gop.fence();
1451 return result;
1452 }
1453
1454
1455 /// Sets the threshold in a vector of functions
1456 template <typename T, std::size_t NDIM>
1457 void set_thresh(World& world, std::vector< Function<T,NDIM> >& v, double thresh, bool fence=true) {
1458 for (unsigned int j=0; j<v.size(); ++j) {
1459 v[j].set_thresh(thresh,false);
1460 }
1461 if (fence) world.gop.fence();
1462 }
1463
1464 /// Returns the complex conjugate of the vector of functions
1465 template <typename T, std::size_t NDIM>
1466 std::vector< Function<T,NDIM> >
1467 conj(World& world,
1468 const std::vector< Function<T,NDIM> >& v,
1469 bool fence=true) {
1470 PROFILE_BLOCK(Vconj);
1471 std::vector< Function<T,NDIM> > r = copy(world, v); // Currently don't have oop conj
1472 for (unsigned int i=0; i<v.size(); ++i) {
1473 r[i].conj(false);
1474 }
1475 if (fence) world.gop.fence();
1476 return r;
1477 }
1478
1479 /// Returns a deep copy of a vector of functions
1480 template <typename T, typename R, std::size_t NDIM>
1481 std::vector< Function<R,NDIM> > convert(World& world,
1482 const std::vector< Function<T,NDIM> >& v, bool fence=true) {
1483 PROFILE_BLOCK(Vcopy);
1484 std::vector< Function<R,NDIM> > r(v.size());
1485 for (unsigned int i=0; i<v.size(); ++i) {
1486 r[i] = convert<T,R,NDIM>(v[i], false);
1487 }
1488 if (fence) world.gop.fence();
1489 return r;
1490 }
1491
1492
1493 /// Returns a deep copy of a vector of functions
1494 template <typename T, std::size_t NDIM>
1495 std::vector< Function<T,NDIM> >
1496 copy(World& world,
1497 const std::vector< Function<T,NDIM> >& v,
1498 bool fence=true) {
1499 PROFILE_BLOCK(Vcopy);
1500 std::vector< Function<T,NDIM> > r(v.size());
1501 for (unsigned int i=0; i<v.size(); ++i) {
1502 r[i] = copy(v[i], false);
1503 }
1504 if (fence) world.gop.fence();
1505 return r;
1506 }
1507
1508
1509 /// Returns a deep copy of a vector of functions
1510 template <typename T, std::size_t NDIM>
1511 std::vector< Function<T,NDIM> >
1512 copy(const std::vector< Function<T,NDIM> >& v, bool fence=true) {
1513 PROFILE_BLOCK(Vcopy);
1514 std::vector< Function<T,NDIM> > r(v.size());
1515 if (v.size()>0) r=copy(v.front().world(),v,fence);
1516 return r;
1517 }
1518
1519 /// Returns a vector of `n` deep copies of a function
1520 template <typename T, std::size_t NDIM>
1521 std::vector< Function<T,NDIM> >
1523 const Function<T,NDIM>& v,
1524 const unsigned int n,
1525 bool fence=true) {
1526 PROFILE_BLOCK(Vcopy1);
1527 std::vector< Function<T,NDIM> > r(n);
1528 for (unsigned int i=0; i<n; ++i) {
1529 r[i] = copy(v, false);
1530 }
1531 if (fence) world.gop.fence();
1532 return r;
1533 }
1534
1535 /// Create a new copy of the function with different distribution and optional
1536 /// fence
1537
1538 /// Works in either basis. Different distributions imply
1539 /// asynchronous communication and the optional fence is
1540 /// collective.
1541 //
1542 /// Returns a deep copy of a vector of functions
1543
1544 template <typename T, std::size_t NDIM>
1545 std::vector<Function<T, NDIM>> copy(World& world,
1546 const std::vector<Function<T, NDIM>>& v,
1547 const std::shared_ptr<WorldDCPmapInterface<Key<NDIM>>>& pmap,
1548 bool fence = true) {
1549 PROFILE_BLOCK(Vcopy);
1550 std::vector<Function<T, NDIM>> r(v.size());
1551 for (unsigned int i = 0; i < v.size(); ++i) {
1552 r[i] = copy(v[i], pmap, false);
1553 }
1554 if (fence) world.gop.fence();
1555 return r;
1556 }
1557
1558 /// owner[j] = j % nranks. For redistribute_to_batches.
1559 inline std::vector<ProcessID> assign_round_robin(std::size_t nfunc, int nranks) {
1560 MADNESS_CHECK(nranks > 0);
1561 std::vector<ProcessID> owner(nfunc);
1562 for (std::size_t j = 0; j < nfunc; ++j) owner[j] = ProcessID(j % std::size_t(nranks));
1563 return owner;
1564 }
1565
1566 /// Cost-balanced assignment: descending cost, each function to the least-loaded
1567 /// rank (LPT greedy). Deterministic for a replicated cost[] (stable sort, index
1568 /// tie-break), so owner[] agrees across ranks. For redistribute_to_batches.
1569 /// @param[in] cost per-function cost proxy (>= 0), identical on every rank
1570 inline std::vector<ProcessID> assign_cost_aware(const std::vector<double>& cost, int nranks) {
1571 MADNESS_CHECK(nranks > 0);
1572 const std::size_t nfunc = cost.size();
1573 std::vector<ProcessID> owner(nfunc);
1574 std::vector<std::size_t> order(nfunc);
1575 for (std::size_t j = 0; j < nfunc; ++j) order[j] = j;
1576 // descending cost; ascending index breaks ties -> stable and reproducible
1577 std::stable_sort(order.begin(), order.end(),
1578 [&](std::size_t a, std::size_t b) { return cost[a] > cost[b]; });
1579 std::vector<double> load(std::size_t(nranks), 0.0);
1580 for (std::size_t k = 0; k < nfunc; ++k) {
1581 int best = 0; // least-loaded rank; smallest
1582 for (int r = 1; r < nranks; ++r) // rank index breaks ties
1583 if (load[std::size_t(r)] < load[std::size_t(best)]) best = r;
1584 const std::size_t j = order[k];
1585 owner[j] = ProcessID(best);
1586 load[std::size_t(best)] += std::max(1.0, cost[j]); // floor: zero-cost funcs still rotate
1587 }
1588 return owner;
1589 }
1590
1591 /// Global coefficient count per function -- one reduction, identical on every rank,
1592 /// so safe for a deterministic assignment. A proxy for convolution cost; does not
1593 /// predict result-tree refinement.
1594 template <typename T, std::size_t NDIM>
1595 std::vector<double> function_costs(World& world, const std::vector<Function<T, NDIM>>& v) {
1596 std::vector<double> cost(v.size(), 0.0);
1597 for (std::size_t j = 0; j < v.size(); ++j) cost[j] = double(v[j].size_local());
1598 if (!v.empty()) world.gop.sum(cost.data(), cost.size());
1599 return cost;
1600 }
1601
1602 /// Move each v[j] so its whole tree lives on rank owner[j], via the coalesced
1603 /// WorldContainer transport (bulk AMs, erase-after-copy: streams, no 2x transient).
1604 /// State-preserving. Each function gets its OWN single-owner pmap (Key<NDIM> is
1605 /// function-agnostic), and the pmap outlives the call -- until redistributed again,
1606 /// every operation on v[j] runs on rank owner[j] alone.
1607 ///
1608 /// @param[in,out] v functions to localize (moved in place)
1609 /// @param[in] owner destination rank per function; MUST be identical on every
1610 /// rank (checked collectively)
1611 /// @param[in] cap_bytes soft cap per message (0 => ~1 MiB, sized for the default
1612 /// MAD_BUFFER_SIZE; lower it if that buffer was shrunk)
1613 /// @param[in] rotate stagger destinations to reduce incast
1614 template <typename T, std::size_t NDIM>
1616 std::vector<Function<T, NDIM>>& v,
1617 const std::vector<ProcessID>& owner,
1618 std::size_t cap_bytes = 0,
1619 bool rotate = true) {
1620 MADNESS_CHECK(owner.size() == v.size());
1621 if (v.empty()) return;
1622
1623 // owner[] must agree across ranks -- divergence silently corrupts ownership
1624 {
1625 long h = 0;
1626 for (std::size_t j = 0; j < owner.size(); ++j) h += long(owner[j]) * long(j + 1);
1627 long hmax = h, hmin = h;
1628 world.gop.max(hmax);
1629 world.gop.min(hmin);
1630 MADNESS_CHECK(hmax == hmin);
1631 }
1632
1633 // chunk cap in #boxes; box size bounded by the functions' own k, not
1634 // FunctionDefaults (v may carry its own k)
1635 if (cap_bytes == 0) cap_bytes = 1024 * 1024; // ~1 MiB, under the default RMI buffer
1636 long kmax = 1;
1637 for (const auto& f : v) kmax = std::max(kmax, long(f.k()));
1638 std::size_t box_bytes = sizeof(T);
1639 for (std::size_t d = 0; d < NDIM; ++d) box_bytes *= std::size_t(2 * kmax);
1640 const std::size_t cap_boxes = std::max<std::size_t>(1, cap_bytes / box_bytes);
1641
1642 // fence, phase1, fence, phase2, fence: the middle fence is REQUIRED -- phase1
1643 // iterates the ConcurrentHashMap and needs a quiescent window (see worlddc.h)
1644 world.gop.fence();
1645 for (std::size_t j = 0; j < v.size(); ++j) {
1646 auto pmap = std::shared_ptr<WorldDCPmapInterface<Key<NDIM>>>(
1647 new WorldDCSingleOwnerPmap<Key<NDIM>>(owner[j]));
1648 v[j].get_impl()->get_coeffs().redistribute_coalesced_phase1(pmap);
1649 }
1650 world.gop.fence();
1651 for (std::size_t j = 0; j < v.size(); ++j)
1652 v[j].get_impl()->get_coeffs().redistribute_coalesced_phase2(cap_boxes, rotate);
1653 world.gop.fence();
1654 }
1655
1656 /// Returns new vector of functions --- q[i] = a[i] + b[i]
1657 template <typename T, typename R, std::size_t NDIM>
1658 std::vector< Function<TENSOR_RESULT_TYPE(T,R), NDIM> >
1659 add(World& world,
1660 const std::vector< Function<T,NDIM> >& a,
1661 const std::vector< Function<R,NDIM> >& b,
1662 bool fence=true) {
1663 PROFILE_BLOCK(Vadd);
1664 MADNESS_ASSERT(a.size() == b.size());
1665 compress(world, a);
1666 compress(world, b);
1667
1668 std::vector< Function<TENSOR_RESULT_TYPE(T,R),NDIM> > r(a.size());
1669 for (unsigned int i=0; i<a.size(); ++i) {
1670 r[i] = add(a[i], b[i], false);
1671 }
1672 if (fence) world.gop.fence();
1673 return r;
1674 }
1675
1676 /// Returns new vector of functions --- q[i] = a + b[i]
1677 template <typename T, typename R, std::size_t NDIM>
1678 std::vector< Function<TENSOR_RESULT_TYPE(T,R), NDIM> >
1679 add(World& world,
1680 const Function<T,NDIM> & a,
1681 const std::vector< Function<R,NDIM> >& b,
1682 bool fence=true) {
1683 PROFILE_BLOCK(Vadd1);
1684 a.compress();
1685 compress(world, b);
1686
1687 std::vector< Function<TENSOR_RESULT_TYPE(T,R),NDIM> > r(b.size());
1688 for (unsigned int i=0; i<b.size(); ++i) {
1689 r[i] = add(a, b[i], false);
1690 }
1691 if (fence) world.gop.fence();
1692 return r;
1693 }
1694 template <typename T, typename R, std::size_t NDIM>
1695 inline std::vector< Function<TENSOR_RESULT_TYPE(T,R), NDIM> >
1696 add(World& world,
1697 const std::vector< Function<R,NDIM> >& b,
1698 const Function<T,NDIM> & a,
1699 bool fence=true) {
1700 return add(world, a, b, fence);
1701 }
1702
1703 /// Returns new vector of functions --- q[i] = a[i] - b[i]
1704 template <typename T, typename R, std::size_t NDIM>
1705 std::vector< Function<TENSOR_RESULT_TYPE(T,R), NDIM> >
1706 sub(World& world,
1707 const std::vector< Function<T,NDIM> >& a,
1708 const std::vector< Function<R,NDIM> >& b,
1709 bool fence=true) {
1710 PROFILE_BLOCK(Vsub);
1711 MADNESS_ASSERT(a.size() == b.size());
1712 compress(world, a);
1713 compress(world, b);
1714
1715 std::vector< Function<TENSOR_RESULT_TYPE(T,R),NDIM> > r(a.size());
1716 for (unsigned int i=0; i<a.size(); ++i) {
1717 r[i] = sub(a[i], b[i], false);
1718 }
1719 if (fence) world.gop.fence();
1720 return r;
1721 }
1722
1723 /// Returns new function --- q = sum_i f[i]
1724 template <typename T, std::size_t NDIM>
1725 Function<T, NDIM> sum(World& world, const std::vector<Function<T,NDIM> >& f,
1726 bool fence=true) {
1727
1728 compress(world, f);
1730
1731 for (unsigned int i=0; i<f.size(); ++i) r.gaxpy(1.0,f[i],1.0,false);
1732 if (fence) world.gop.fence();
1733 return r;
1734 }
1735
1736 template <typename T, std::size_t NDIM>
1738 const std::vector<Function<T, NDIM>>& f,
1739 const std::vector<Function<T, NDIM>>& g,
1740 bool sym=false)
1741 {
1744 const int64_t n = A.coldim();
1745 const int64_t m = A.rowdim();
1746 MADNESS_ASSERT(int64_t(f.size()) == n && int64_t(g.size()) == m);
1747
1748 // Assume we can always create an ichunk*jchunk matrix locally
1749 const int ichunk = 1000;
1750 const int jchunk = 1000; // 1000*1000*8 = 8 MBytes
1751 for (int64_t ilo = 0; ilo < n; ilo += ichunk) {
1752 int64_t ihi = std::min(ilo + ichunk, n);
1753 std::vector<Function<T, NDIM>> ivec(f.begin() + ilo, f.begin() + ihi);
1754 for (int64_t jlo = 0; jlo < m; jlo += jchunk) {
1755 int64_t jhi = std::min(jlo + jchunk, m);
1756 std::vector<Function<T, NDIM>> jvec(g.begin() + jlo, g.begin() + jhi);
1757
1758 Tensor<T> P = matrix_dot(A.get_world(), ivec, jvec, sym);
1759 A.copy_from_replicated_patch(ilo, ihi - 1, jlo, jhi - 1, P);
1760 }
1761 }
1762 return A;
1763 }
1764
1765 /// Computes the matrix dot product of two function vectors - q(i,j) = dot(f[i],g[j])
1766
1767 /// For complex types symmetric is interpreted as Hermitian.
1768 ///
1769 /// The current parallel loop is non-optimal but functional.
1770 template <typename T, typename R, std::size_t NDIM>
1772 const std::vector<Function<T, NDIM>>& f,
1773 const std::vector<Function<R, NDIM>>& g,
1774 bool sym=false)
1775 {
1776 world.gop.fence();
1777 compress(world, f);
1778 // if ((void*)(&f) != (void*)(&g)) compress(world, g);
1779 compress(world, g);
1780
1781 std::vector<const FunctionImpl<T, NDIM>*> left(f.size());
1782 std::vector<const FunctionImpl<R, NDIM>*> right(g.size());
1783 for (unsigned int i = 0; i < f.size(); i++) left[i] = f[i].get_impl().get();
1784 for (unsigned int i = 0; i < g.size(); i++) right[i] = g[i].get_impl().get();
1785
1787
1788 world.gop.fence();
1789 world.gop.sum(r.ptr(), f.size() * g.size());
1790
1791 return r;
1792 }
1793
1794 /// Computes the matrix dot product of two function vectors - q(i,j) = dot(f[i],g[j])
1795
1796 /// For complex types symmetric is interpreted as Hermitian.
1797 ///
1798 /// The current parallel loop is non-optimal but functional.
1799 template <typename T, typename R, std::size_t NDIM>
1801 const std::vector< Function<T,NDIM> >& f,
1802 const std::vector< Function<R,NDIM> >& g,
1803 bool sym=false) {
1804 PROFILE_BLOCK(Vmatrix_dot);
1805 long n=f.size(), m=g.size();
1806 Tensor< TENSOR_RESULT_TYPE(T,R) > r(n,m);
1807 if (sym) MADNESS_ASSERT(n==m);
1808
1809 world.gop.fence();
1810 compress(world, f);
1811 if ((void*)(&f) != (void*)(&g)) compress(world, g);
1812
1813 for (long i=0; i<n; ++i) {
1814 long jtop = m;
1815 if (sym) jtop = i+1;
1816 for (long j=0; j<jtop; ++j) {
1817 if (sym) {
1818 r(j,i) = f[i].dot_local(g[j]);
1819 if (i != j)
1820 r(i,j) = conj(r(j,i));
1821 } else
1822 r(i,j) = f[i].dot_local(g[j]);
1823 }
1824 }
1825
1826 world.gop.fence();
1827 world.gop.sum(r.ptr(),n*m);
1828
1829 return r;
1830 }
1831
1832 /// Multiplies and sums two vectors of functions r = \sum_i a[i] * b[i]
1833 template <typename T, typename R, std::size_t NDIM>
1834 Function<TENSOR_RESULT_TYPE(T,R), NDIM>
1836 const std::vector< Function<T,NDIM> >& a,
1837 const std::vector< Function<R,NDIM> >& b,
1838 double tol,
1839 bool fence=true,
1840 bool do_make_redundant=true) {
1841 MADNESS_CHECK(a.size()==b.size());
1842 return sum(world,mul_sparse(world,a,b,tol,/*fence=*/true,do_make_redundant),fence);
1843 }
1844
1845 /// Multiplies and sums two vectors of functions r = \sum_i a[i] * b[i]; see dot_sparse for screening
1846 template <typename T, typename R, std::size_t NDIM>
1847 Function<TENSOR_RESULT_TYPE(T,R), NDIM>
1848 dot(World& world,
1849 const std::vector< Function<T,NDIM> >& a,
1850 const std::vector< Function<R,NDIM> >& b,
1851 bool fence=true,
1852 bool do_make_redundant=true) {
1853 MADNESS_CHECK(a.size()==b.size());
1854 return sum(world,mul(world,a,b,/*fence=*/true,do_make_redundant),fence);
1855 }
1856
1857 /// out-of-place gaxpy for two vectors: result[i] = alpha * a[i] + beta * b[i]
1858 template <typename T, typename Q, typename R, std::size_t NDIM>
1859 std::vector<Function<TENSOR_RESULT_TYPE(Q,TENSOR_RESULT_TYPE(T,R)),NDIM> >
1861 const std::vector< Function<T,NDIM> >& a,
1862 Q beta,
1863 const std::vector< Function<R,NDIM> >& b,
1864 bool fence=true) {
1865
1866 MADNESS_ASSERT(a.size() == b.size());
1867 typedef TENSOR_RESULT_TYPE(Q,TENSOR_RESULT_TYPE(T,R)) resultT;
1868 if (a.size()==0) return std::vector<Function<resultT,NDIM> >();
1869
1870 auto tensor_type = [](const std::vector<Function<T,NDIM>>& v) {
1871 return v.front().get_impl()->get_tensor_type();
1872 };
1873
1874 // gaxpy can be done either in reconstructed or in compressed state
1875 World& world=a[0].world();
1876 std::vector<Function<resultT,NDIM> > result(a.size());
1877
1878 TreeState operating_state=tensor_type(a)==TT_FULL ? compressed : reconstructed;
1879 try {
1880 ensure_tree_state_respecting_fence(a,operating_state,fence);
1881 ensure_tree_state_respecting_fence(b,operating_state,fence);
1882 } catch (...) {
1883 print("could not respect fence in gaxpy");
1884 change_tree_state(a,operating_state,true);
1885 change_tree_state(b,operating_state,true);
1886 }
1887
1888 if (operating_state==compressed) {
1889 for (unsigned int i=0; i<a.size(); ++i) result[i]=gaxpy_oop(alpha, a[i], beta, b[i], false);
1890 } else {
1891 for (unsigned int i=0; i<a.size(); ++i) result[i]=gaxpy_oop_reconstructed(alpha, a[i], beta, b[i], false);
1892 }
1893
1894 if (fence) world.gop.fence();
1895 return result;
1896 }
1897
1898
1899 /// out-of-place gaxpy for a vectors and a function: result[i] = alpha * a[i] + beta * b
1900 template <typename T, typename Q, typename R, std::size_t NDIM>
1901 std::vector<Function<TENSOR_RESULT_TYPE(Q,TENSOR_RESULT_TYPE(T,R)),NDIM> >
1903 const std::vector< Function<T,NDIM> >& a,
1904 Q beta,
1905 const Function<R,NDIM>& b,
1906 bool fence=true) {
1907
1908 typedef TENSOR_RESULT_TYPE(Q,TENSOR_RESULT_TYPE(T,R)) resultT;
1909 if (a.size()==0) return std::vector<Function<resultT,NDIM> >();
1910
1911 World& world=a[0].world();
1912 try {
1914 // ensure_tree_state_respecting_fence({b},compressed,fence);
1916 } catch (...) {
1917 print("could not respect fence in gaxpy_oop");
1918 compress(world,a);
1919 b.compress();
1920 }
1921 std::vector<Function<resultT,NDIM> > result(a.size());
1922 for (unsigned int i=0; i<a.size(); ++i) {
1923 result[i]=gaxpy_oop(alpha, a[i], beta, b, false);
1924 }
1925 if (fence) world.gop.fence();
1926 return result;
1927 }
1928
1929
1930 /// Generalized A*X+Y for vectors of functions ---- a[i] = alpha*a[i] + beta*b[i]
1931 template <typename T, typename Q, typename R, std::size_t NDIM>
1932 void gaxpy(Q alpha, std::vector<Function<T,NDIM>>& a, Q beta, const std::vector<Function<R,NDIM>>& b, const bool fence) {
1933 if (a.size() == 0) return;
1934 World& world=a.front().world();
1935 gaxpy(world,alpha,a,beta,b,fence);
1936 }
1937
1938 /// Generalized A*X+Y for vectors of functions ---- a[i] = alpha*a[i] + beta*b[i]
1939 template <typename T, typename Q, typename R, std::size_t NDIM>
1940 void gaxpy(World& world,
1941 Q alpha,
1942 std::vector< Function<T,NDIM> >& a,
1943 Q beta,
1944 const std::vector< Function<R,NDIM> >& b,
1945 bool fence=true) {
1946 PROFILE_BLOCK(Vgaxpy);
1947 MADNESS_ASSERT(a.size() == b.size());
1948 if (a.empty()) return;
1949
1950 auto tensor_type = [](const std::vector<Function<T,NDIM>>& v) {
1951 return v.front().get_impl()->get_tensor_type();
1952 };
1953
1954 // gaxpy can be done either in reconstructed or in compressed state
1955 bool do_in_reconstructed_state=tensor_type(a)!=TT_FULL;
1956 TreeState operating_state=do_in_reconstructed_state ? reconstructed : compressed;
1957
1958 if (operating_state==compressed) {
1959 // this is strict: both vectors have to be compressed
1960 try {
1961 ensure_tree_state_respecting_fence(a,operating_state,fence);
1962 ensure_tree_state_respecting_fence(b,operating_state,fence);
1963 } catch (...) {
1964 print("could not respect fence in gaxpy");
1965 change_tree_state(a,operating_state,true);
1966 change_tree_state(b,operating_state,true);
1967 }
1968 MADNESS_CHECK_THROW(get_tree_state(a)==get_tree_state(b),"gaxpy requires same tree state for all functions");
1969 MADNESS_CHECK_THROW(get_tree_state(a)==operating_state,"gaxpy requires reconstructed/compressed tree state for all functions");
1970 } else {
1971 // both vectors can be reconstructed or redundant_after_merge, and they don't have to be the same
1972 TreeState astate=get_tree_state(a);
1973 TreeState bstate=get_tree_state(b);
1974 if (not (astate==reconstructed or astate==redundant_after_merge)) {
1975 try {
1976 ensure_tree_state_respecting_fence(a,operating_state,fence);
1977 } catch (...) {
1978 print("could not respect fence in gaxpy for a");
1980 }
1981 }
1982 if (not (bstate==reconstructed or bstate==redundant_after_merge)) {
1983 try {
1984 ensure_tree_state_respecting_fence(b,operating_state,fence);
1985 } catch (...) {
1986 print("could not respect fence in gaxpy for b");
1988 }
1989 }
1990 }
1991
1992 // finally do the work
1993 for (unsigned int i=0; i<a.size(); ++i) {
1994 a[i].gaxpy(alpha, b[i], beta, false);
1995 }
1996 if (fence and (get_tree_state(a)==redundant_after_merge)) {
1997 for (unsigned int i=0; i<a.size(); ++i) a[i].get_impl()->finalize_sum();
1998 }
1999
2000 if (fence) world.gop.fence();
2001 }
2002
2003
2004 /// Applies a vector of operators to a vector of functions --- q[i] = apply(op[i],f[i])
2005 template <typename opT, typename R, std::size_t NDIM>
2006 std::vector< Function<TENSOR_RESULT_TYPE(typename opT::opT,R), NDIM> >
2007 apply(World& world,
2008 const std::vector< std::shared_ptr<opT> >& op,
2009 const std::vector< Function<R,NDIM> > f) {
2010
2011 PROFILE_BLOCK(Vapplyv);
2012 MADNESS_ASSERT(f.size()==op.size());
2013
2014 std::vector< Function<R,NDIM> >& ncf = *const_cast< std::vector< Function<R,NDIM> >* >(&f);
2015
2016// reconstruct(world, f);
2017 make_nonstandard(world, ncf);
2018
2019 std::vector< Function<TENSOR_RESULT_TYPE(typename opT::opT,R), NDIM> > result(f.size());
2020 for (unsigned int i=0; i<f.size(); ++i) {
2021 result[i] = apply_only(*op[i], f[i], false);
2022 result[i].get_impl()->set_tree_state(nonstandard_after_apply);
2023 }
2024
2025 world.gop.fence();
2026
2027 standard(world, ncf, false); // restores promise of logical constness
2028 reconstruct(result);
2029 world.gop.fence();
2030
2031 return result;
2032 }
2033
2034
2035 /// Applies an operator to a vector of functions --- q[i] = apply(op,f[i])
2036 template <typename T, typename R, std::size_t NDIM, std::size_t KDIM>
2037 std::vector< Function<TENSOR_RESULT_TYPE(T,R), NDIM> >
2039 const std::vector< Function<R,NDIM> > f) {
2040 return apply(op.get_world(),op,f);
2041 }
2042
2043
2044 /// Applies an operator to a vector of functions --- q[i] = apply(op,f[i])
2045 template <typename T, typename R, std::size_t NDIM, std::size_t KDIM>
2046 std::vector< Function<TENSOR_RESULT_TYPE(T,R), NDIM> >
2047 apply(World& world,
2049 const std::vector< Function<R,NDIM> > f) {
2050 PROFILE_BLOCK(Vapply);
2051
2052 std::vector< Function<R,NDIM> >& ncf = *const_cast< std::vector< Function<R,NDIM> >* >(&f);
2053 bool print_timings=(NDIM==6) and (world.rank()==0) and op.print_timings;
2054
2055 double wall0=wall_time();
2056// reconstruct(world, f);
2057 make_nonstandard(world, ncf);
2058 double wall1=wall_time();
2059 if (print_timings) printf("timer: %20.20s %8.2fs\n", "make_nonstandard", wall1-wall0);
2060
2061 std::vector< Function<TENSOR_RESULT_TYPE(T,R), NDIM> > result(f.size());
2062 for (unsigned int i=0; i<f.size(); ++i) {
2063 result[i] = apply_only(op, f[i], false);
2064 }
2065
2066 world.gop.fence();
2067
2068 // restores promise of logical constness
2069 if (op.destructive()) {
2070 for (auto& ff : ncf) ff.clear(false);
2071 world.gop.fence();
2072 } else {
2073 reconstruct(world,f);
2074 }
2075
2076 // svd-tensor requires some cleanup after apply
2077 if (result[0].get_impl()->get_tensor_type()==TT_2D) {
2078 for (auto& r : result) r.get_impl()->finalize_apply();
2079 }
2080
2081 if (print_timings) {
2082 for (auto& r : result) r.get_impl()->print_timer();
2083 op.print_timer();
2084 }
2085 reconstruct(world, result);
2086
2087 return result;
2088 }
2089
2090 /// Normalizes a vector of functions --- v[i] = v[i].scale(1.0/v[i].norm2())
2091 template <typename T, std::size_t NDIM>
2092 void normalize(World& world, std::vector< Function<T,NDIM> >& v, bool fence=true) {
2093 PROFILE_BLOCK(Vnormalize);
2094 std::vector<double> nn = norm2s(world, v);
2095 for (unsigned int i=0; i<v.size(); ++i) v[i].scale(1.0/nn[i],false);
2096 if (fence) world.gop.fence();
2097 }
2098
2099 template <typename T, std::size_t NDIM>
2100 void print_size(World &world, const std::vector<Function<T,NDIM> > &v, const std::string &msg = "vectorfunction" ){
2101 if(v.empty()){
2102 if(world.rank()==0) std::cout << "print_size: " << msg << " is empty" << std::endl;
2103 }else if(v.size()==1){
2104 v.front().print_size(msg);
2105 }else{
2106 for(auto x:v){
2107 // print("impl",x.get_impl().get());
2108 x.print_size(msg);
2109 }
2110 }
2111 }
2112
2113 /// return the size of a vector of functions for each rank
2114 template <typename T, std::size_t NDIM>
2115 double get_size_local(World& world, const std::vector< Function<T,NDIM> >& v){
2116 double size=0.0;
2117 for(auto x:v){
2118 if (x.is_initialized()) size+=x.size_local();
2119 }
2120 const double d=sizeof(T);
2121 const double fac=1024*1024*1024;
2122 return size/fac*d;
2123 }
2124
2125 /// return the size of a function for each rank
2126 template <typename T, std::size_t NDIM>
2128 return get_size_local(f.world(),std::vector<Function<T,NDIM> >(1,f));
2129 }
2130
2131
2132 // gives back the size in GB
2133 template <typename T, std::size_t NDIM>
2134 double get_size(World& world, const std::vector< Function<T,NDIM> >& v){
2135
2136 if (v.empty()) return 0.0;
2137
2138 const double d=sizeof(T);
2139 const double fac=1024*1024*1024;
2140
2141 double size=0.0;
2142 for(unsigned int i=0;i<v.size();i++){
2143 if (v[i].is_initialized()) size+=v[i].size();
2144 }
2145
2146 return size/fac*d;
2147
2148 }
2149
2150 // gives back the size in GB
2151 template <typename T, std::size_t NDIM>
2152 double get_size(const Function<T,NDIM> & f){
2153 const double d=sizeof(T);
2154 const double fac=1024*1024*1024;
2155 double size=f.size();
2156 return size/fac*d;
2157 }
2158
2159 /// apply op on the input vector yielding an output vector of functions
2160
2161 /// @param[in] op the operator working on vin
2162 /// @param[in] vin vector of input Functions; needs to be refined to common level!
2163 /// @return vector of output Functions vout = op(vin)
2164 template <typename T, typename opT, std::size_t NDIM>
2165 std::vector<Function<T,NDIM> > multi_to_multi_op_values(const opT& op,
2166 const std::vector< Function<T,NDIM> >& vin,
2167 const bool fence=true) {
2168 MADNESS_ASSERT(vin.size()>0);
2169 MADNESS_ASSERT(vin[0].is_initialized()); // might be changed
2170 World& world=vin[0].world();
2171 Function<T,NDIM> dummy;
2172 dummy.set_impl(vin[0], false);
2173 std::vector<Function<T,NDIM> > vout=zero_functions<T,NDIM>(world, op.get_result_size());
2174 for (auto& out : vout) out.set_impl(vin[0],false);
2175 dummy.multi_to_multi_op_values(op, vin, vout, fence);
2176 return vout;
2177 }
2178
2179
2180
2181
2182 // convenience operators
2183
2184 /// result[i] = a[i] + b[i]
2185 template <typename T, std::size_t NDIM>
2186 std::vector<Function<T,NDIM> > operator+(const std::vector<Function<T,NDIM> >& lhs,
2187 const std::vector<Function<T,NDIM>>& rhs) {
2188 MADNESS_CHECK(lhs.size() == rhs.size());
2189 return gaxpy_oop(1.0,lhs,1.0,rhs);
2190 }
2191
2192 /// result[i] = a[i] - b[i]
2193 template <typename T, std::size_t NDIM>
2194 std::vector<Function<T,NDIM> > operator-(const std::vector<Function<T,NDIM> >& lhs,
2195 const std::vector<Function<T,NDIM> >& rhs) {
2196 MADNESS_CHECK(lhs.size() == rhs.size());
2197 return gaxpy_oop(1.0,lhs,-1.0,rhs);
2198 }
2199
2200 /// result[i] = a[i] + b
2201 template <typename T, std::size_t NDIM>
2202 std::vector<Function<T,NDIM> > operator+(const std::vector<Function<T,NDIM> >& lhs,
2203 const Function<T,NDIM>& rhs) {
2204 // MADNESS_CHECK(lhs.size() == rhs.size()); // no!!
2205 return gaxpy_oop(1.0,lhs,1.0,rhs);
2206 }
2207
2208 /// result[i] = a[i] - b
2209 template <typename T, std::size_t NDIM>
2210 std::vector<Function<T,NDIM> > operator-(const std::vector<Function<T,NDIM> >& lhs,
2211 const Function<T,NDIM>& rhs) {
2212 // MADNESS_CHECK(lhs.size() == rhs.size()); // no
2213 return gaxpy_oop(1.0,lhs,-1.0,rhs);
2214 }
2215
2216 /// result[i] = a + b[i]
2217 template <typename T, std::size_t NDIM>
2218 std::vector<Function<T,NDIM> > operator+(const Function<T,NDIM>& lhs,
2219 const std::vector<Function<T,NDIM> >& rhs) {
2220 // MADNESS_CHECK(lhs.size() == rhs.size()); // no
2221 return gaxpy_oop(1.0,rhs,1.0,lhs);
2222 }
2223
2224 /// result[i] = a - b[i]
2225 template <typename T, std::size_t NDIM>
2226 std::vector<Function<T,NDIM> > operator-(const Function<T,NDIM>& lhs,
2227 const std::vector<Function<T,NDIM> >& rhs) {
2228// MADNESS_CHECK(lhs.size() == rhs.size()); // no
2229 return gaxpy_oop(-1.0,rhs,1.0,lhs);
2230 }
2231
2232
2233 template <typename T, typename R, std::size_t NDIM>
2234 std::vector<Function<TENSOR_RESULT_TYPE(T,R),NDIM> > operator*(const R fac,
2235 const std::vector<Function<T,NDIM> >& rhs) {
2236 if (rhs.size()>0) {
2237 std::vector<Function<T,NDIM> > tmp=copy(rhs[0].world(),rhs);
2238 scale(tmp[0].world(),tmp,TENSOR_RESULT_TYPE(T,R)(fac));
2239 return tmp;
2240 }
2241 return std::vector<Function<TENSOR_RESULT_TYPE(T,R),NDIM> >();
2242 }
2243
2244 template <typename T, typename R, std::size_t NDIM>
2245 std::vector<Function<T,NDIM> > operator*(const std::vector<Function<T,NDIM> >& rhs,
2246 const R fac) {
2247 if (rhs.size()>0) {
2248 std::vector<Function<TENSOR_RESULT_TYPE(T,R),NDIM> > tmp=copy(rhs[0].world(),rhs);
2249 scale(tmp[0].world(),tmp,TENSOR_RESULT_TYPE(T,R)(fac));
2250 return tmp;
2251 }
2252 return std::vector<Function<TENSOR_RESULT_TYPE(T,R),NDIM> >();
2253 }
2254
2255 /// multiply a vector of functions with a function: r[i] = v[i] * a
2256 template <typename T, typename R, std::size_t NDIM>
2258 const std::vector<Function<R,NDIM> >& v) {
2259 if (v.size()>0) return mul(v[0].world(),a,v,true);
2260 return std::vector<Function<TENSOR_RESULT_TYPE(T,R),NDIM> >();
2261 }
2262
2263
2264 /// multiply a vector of functions with a function: r[i] = a * v[i]
2265 template <typename T, typename R, std::size_t NDIM>
2266 std::vector<Function<TENSOR_RESULT_TYPE(T,R),NDIM> > operator*(const std::vector<Function<T,NDIM> >& v,
2267 const Function<R,NDIM>& a) {
2268 if (v.size()>0) return mul(v[0].world(),a,v,true);
2269 return std::vector<Function<TENSOR_RESULT_TYPE(T,R),NDIM> >();
2270 }
2271
2272
2273 template <typename T, std::size_t NDIM>
2274 std::vector<Function<T,NDIM> > operator+=(std::vector<Function<T,NDIM> >& lhs, const std::vector<Function<T,NDIM> >& rhs) {
2275 MADNESS_CHECK(lhs.size() == rhs.size());
2276 if (lhs.size() > 0) gaxpy(lhs.front().world(), 1.0, lhs, 1.0, rhs);
2277 return lhs;
2278 }
2279
2280 template <typename T, std::size_t NDIM>
2281 std::vector<Function<T,NDIM> > operator-=(std::vector<Function<T,NDIM> >& lhs,
2282 const std::vector<Function<T,NDIM> >& rhs) {
2283 MADNESS_CHECK(lhs.size() == rhs.size());
2284 if (lhs.size() > 0) gaxpy(lhs.front().world(), 1.0, lhs, -1.0, rhs);
2285 return lhs;
2286 }
2287
2288 /// return the real parts of the vector's function (if complex)
2289 template <typename T, std::size_t NDIM>
2290 std::vector<Function<typename Tensor<T>::scalar_type,NDIM> >
2291 real(const std::vector<Function<T,NDIM> >& v, bool fence=true) {
2292 std::vector<Function<typename Tensor<T>::scalar_type,NDIM> > result(v.size());
2293 for (std::size_t i=0; i<v.size(); ++i) result[i]=real(v[i],false);
2294 if (fence and result.size()>0) result[0].world().gop.fence();
2295 return result;
2296 }
2297
2298 /// return the imaginary parts of the vector's function (if complex)
2299 template <typename T, std::size_t NDIM>
2300 std::vector<Function<typename Tensor<T>::scalar_type,NDIM> >
2301 imag(const std::vector<Function<T,NDIM> >& v, bool fence=true) {
2302 std::vector<Function<typename Tensor<T>::scalar_type,NDIM> > result(v.size());
2303 for (std::size_t i=0; i<v.size(); ++i) result[i]=imag(v[i],false);
2304 if (fence and result.size()>0) result[0].world().gop.fence();
2305 return result;
2306 }
2307
2308 /// shorthand gradient operator
2309
2310 /// returns the differentiated function f in all NDIM directions
2311 /// @param[in] f the function on which the grad operator works on
2312 /// @param[in] refine refinement before diff'ing makes the result more accurate
2313 /// @param[in] fence fence after completion; if reconstruction is needed always fence
2314 /// @return the vector \frac{\partial}{\partial x_i} f
2315 template <typename T, std::size_t NDIM>
2316 std::vector<Function<T,NDIM> > grad(const Function<T,NDIM>& f,
2317 bool refine=false, bool fence=true) {
2318
2319 World& world=f.world();
2320 f.reconstruct();
2321 if (refine) f.refine(); // refine to make result more precise
2322
2323 std::vector< std::shared_ptr< Derivative<T,NDIM> > > grad=
2324 gradient_operator<T,NDIM>(world);
2325
2326 std::vector<Function<T,NDIM> > result(NDIM);
2327 for (size_t i=0; i<NDIM; ++i) result[i]=apply(*(grad[i]),f,false);
2328 if (fence) world.gop.fence();
2329 return result;
2330 }
2331
2332 // BLM first derivative
2333 template <typename T, std::size_t NDIM>
2334 std::vector<Function<T,NDIM> > grad_ble_one(const Function<T,NDIM>& f,
2335 bool refine=false, bool fence=true) {
2336
2337 World& world=f.world();
2338 f.reconstruct();
2339 if (refine) f.refine(); // refine to make result more precise
2340
2341 std::vector< std::shared_ptr< Derivative<T,NDIM> > > grad=
2342 gradient_operator<T,NDIM>(world);
2343
2344 // Read in new coeff for each operator
2345 for (unsigned int i=0; i<NDIM; ++i) (*grad[i]).set_ble1();
2346
2347 std::vector<Function<T,NDIM> > result(NDIM);
2348 for (unsigned int i=0; i<NDIM; ++i) result[i]=apply(*(grad[i]),f,false);
2349 if (fence) world.gop.fence();
2350 return result;
2351 }
2352
2353 // BLM second derivative
2354 template <typename T, std::size_t NDIM>
2355 std::vector<Function<T,NDIM> > grad_ble_two(const Function<T,NDIM>& f,
2356 bool refine=false, bool fence=true) {
2357
2358 World& world=f.world();
2359 f.reconstruct();
2360 if (refine) f.refine(); // refine to make result more precise
2361
2362 std::vector< std::shared_ptr< Derivative<T,NDIM> > > grad=
2363 gradient_operator<T,NDIM>(world);
2364
2365 // Read in new coeff for each operator
2366 for (unsigned int i=0; i<NDIM; ++i) (*grad[i]).set_ble2();
2367
2368 std::vector<Function<T,NDIM> > result(NDIM);
2369 for (unsigned int i=0; i<NDIM; ++i) result[i]=apply(*(grad[i]),f,false);
2370 if (fence) world.gop.fence();
2371 return result;
2372 }
2373
2374 // Bspline first derivative
2375 template <typename T, std::size_t NDIM>
2376 std::vector<Function<T,NDIM> > grad_bspline_one(const Function<T,NDIM>& f,
2377 bool refine=false, bool fence=true) {
2378
2379 World& world=f.world();
2380 f.reconstruct();
2381 if (refine) f.refine(); // refine to make result more precise
2382
2383 std::vector< std::shared_ptr< Derivative<T,NDIM> > > grad=
2384 gradient_operator<T,NDIM>(world);
2385
2386 // Read in new coeff for each operator
2387 for (unsigned int i=0; i<NDIM; ++i) (*grad[i]).set_bspline1();
2388
2389 std::vector<Function<T,NDIM> > result(NDIM);
2390 for (unsigned int i=0; i<NDIM; ++i) result[i]=apply(*(grad[i]),f,false);
2391 if (fence) world.gop.fence();
2392 return result;
2393 }
2394
2395 // Bpsline second derivative
2396 template <typename T, std::size_t NDIM>
2397 std::vector<Function<T,NDIM> > grad_bpsline_two(const Function<T,NDIM>& f,
2398 bool refine=false, bool fence=true) {
2399
2400 World& world=f.world();
2401 f.reconstruct();
2402 if (refine) f.refine(); // refine to make result more precise
2403
2404 std::vector< std::shared_ptr< Derivative<T,NDIM> > > grad=
2405 gradient_operator<T,NDIM>(world);
2406
2407 // Read in new coeff for each operator
2408 for (unsigned int i=0; i<NDIM; ++i) (*grad[i]).set_bspline2();
2409
2410 std::vector<Function<T,NDIM> > result(NDIM);
2411 for (unsigned int i=0; i<NDIM; ++i) result[i]=apply(*(grad[i]),f,false);
2412 if (fence) world.gop.fence();
2413 return result;
2414 }
2415
2416 // Bspline third derivative
2417 template <typename T, std::size_t NDIM>
2418 std::vector<Function<T,NDIM> > grad_bspline_three(const Function<T,NDIM>& f,
2419 bool refine=false, bool fence=true) {
2420
2421 World& world=f.world();
2422 f.reconstruct();
2423 if (refine) f.refine(); // refine to make result more precise
2424
2425 std::vector< std::shared_ptr< Derivative<T,NDIM> > > grad=
2426 gradient_operator<T,NDIM>(world);
2427
2428 // Read in new coeff for each operator
2429 for (unsigned int i=0; i<NDIM; ++i) (*grad[i]).set_bspline3();
2430
2431 std::vector<Function<T,NDIM> > result(NDIM);
2432 for (unsigned int i=0; i<NDIM; ++i) result[i]=apply(*(grad[i]),f,false);
2433 if (fence) world.gop.fence();
2434 return result;
2435 }
2436
2437
2438
2439 /// which first derivative the div_* variants use
2440
2441 /// abgv is the default ABGV operator; bspline and ble are the smoothing
2442 /// first derivatives, cf. Derivative::set_bspline1() and set_ble1() and the
2443 /// grad_bspline_one() / grad_ble_one() gradients.
2444 enum class DerivMethod { abgv, bspline, ble };
2445
2446 /// shorthand div operator, with a choice of first derivative
2447
2448 /// returns the dot product of nabla with a vector f
2449 /// @param[in] v the vector of functions on which the div operator works on
2450 /// @param[in] method which first derivative to use
2451 /// @param[in] do_refine refinement before diff'ing makes the result more accurate
2452 /// @param[in] fence fence after completion; currently always fences
2453 /// @return the divergence \sum_i \frac{\partial}{\partial x_i} v_i
2454 /// TODO: add this to operator fusion
2455 template <typename T, std::size_t NDIM>
2457 const DerivMethod method, bool do_refine=false, bool fence=true) {
2458
2459 MADNESS_ASSERT(v.size()>0);
2460 World& world=v[0].world();
2461 reconstruct(world,v);
2462 if (do_refine) refine(world,v); // refine to make result more precise
2463
2464 std::vector< std::shared_ptr< Derivative<T,NDIM> > > grad=
2465 gradient_operator<T,NDIM>(world);
2466
2467 // read in new coeff for each operator, as grad_bspline_one()/grad_ble_one() do
2468 if (method==DerivMethod::bspline)
2469 for (unsigned int i=0; i<NDIM; ++i) (*grad[i]).set_bspline1();
2470 else if (method==DerivMethod::ble)
2471 for (unsigned int i=0; i<NDIM; ++i) (*grad[i]).set_ble1();
2472
2473 std::vector<Function<T,NDIM> > result(NDIM);
2474 for (size_t i=0; i<NDIM; ++i) result[i]=apply(*(grad[i]),v[i],false);
2475 world.gop.fence();
2476 return sum(world,result,fence);
2477 }
2478
2479 /// div with the ABGV derivative
2480 template <typename T, std::size_t NDIM>
2482 bool do_refine=false, bool fence=true) {
2483 return div_deriv(v,DerivMethod::abgv,do_refine,fence);
2484 }
2485
2486 /// div with the b-spline smoothing first derivative
2487 template <typename T, std::size_t NDIM>
2489 bool do_refine=false, bool fence=true) {
2490 return div_deriv(v,DerivMethod::bspline,do_refine,fence);
2491 }
2492
2493 /// div with the BLE smoothing first derivative
2494 template <typename T, std::size_t NDIM>
2496 bool do_refine=false, bool fence=true) {
2497 return div_deriv(v,DerivMethod::ble,do_refine,fence);
2498 }
2499
2500 /// shorthand div operator, with the default (ABGV) derivative
2501 template <typename T, std::size_t NDIM>
2503 bool do_refine=false, bool fence=true) {
2504 return div_abgv(v,do_refine,fence);
2505 }
2506
2507 /// shorthand rot operator
2508
2509 /// returns the cross product of nabla with a vector f
2510 /// @param[in] f the vector of functions on which the rot operator works on
2511 /// @param[in] refine refinement before diff'ing makes the result more accurate
2512 /// @param[in] fence fence after completion; currently always fences
2513 /// @return the vector \frac{\partial}{\partial x_i} f
2514 /// TODO: add this to operator fusion
2515 template <typename T, std::size_t NDIM>
2516 std::vector<Function<T,NDIM> > rot(const std::vector<Function<T,NDIM> >& v,
2517 bool do_refine=false, bool fence=true) {
2518
2519 MADNESS_ASSERT(v.size()==3);
2520 World& world=v[0].world();
2521 reconstruct(world,v);
2522 if (do_refine) refine(world,v); // refine to make result more precise
2523
2524 std::vector< std::shared_ptr< Derivative<T,NDIM> > > grad=
2525 gradient_operator<T,NDIM>(world);
2526
2527 std::vector<Function<T,NDIM> > d(NDIM),dd(NDIM);
2528 d[0]=apply(*(grad[1]),v[2],false); // Dy z
2529 d[1]=apply(*(grad[2]),v[0],false); // Dz x
2530 d[2]=apply(*(grad[0]),v[1],false); // Dx y
2531 dd[0]=apply(*(grad[2]),v[1],false); // Dz y
2532 dd[1]=apply(*(grad[0]),v[2],false); // Dx z
2533 dd[2]=apply(*(grad[1]),v[0],false); // Dy x
2534 world.gop.fence();
2535
2536 compress(world,d,false);
2537 compress(world,dd,false);
2538 world.gop.fence();
2539 d[0].gaxpy(1.0,dd[0],-1.0,false);
2540 d[1].gaxpy(1.0,dd[1],-1.0,false);
2541 d[2].gaxpy(1.0,dd[2],-1.0,false);
2542
2543 world.gop.fence();
2544 reconstruct(d);
2545 return d;
2546 }
2547
2548 /// shorthand cross operator
2549
2550 /// returns the cross product of vectors f and g
2551 /// @param[in] f the vector of functions on which the rot operator works on
2552 /// @param[in] g the vector of functions on which the rot operator works on
2553 /// @param[in] fence fence after completion; currently always fences
2554 /// @return the vector \frac{\partial}{\partial x_i} f, in redundant state
2555 /// TODO: add this to operator fusion
2556 template <typename T, typename R, std::size_t NDIM>
2557 std::vector<Function<TENSOR_RESULT_TYPE(T,R),NDIM> > cross(const std::vector<Function<T,NDIM> >& f,
2558 const std::vector<Function<R,NDIM> >& g,
2559 bool do_refine=false, bool fence=true) {
2560
2561 MADNESS_ASSERT(f.size()==3);
2562 MADNESS_ASSERT(g.size()==3);
2563 World& world=f[0].world();
2566
2567 std::vector<Function<TENSOR_RESULT_TYPE(T,R),NDIM> > d(f.size()),dd(f.size());
2568
2569 d[0]=mul(f[1],g[2],false);
2570 d[1]=mul(f[2],g[0],false);
2571 d[2]=mul(f[0],g[1],false);
2572
2573 dd[0]=mul(f[2],g[1],false);
2574 dd[1]=mul(f[0],g[2],false);
2575 dd[2]=mul(f[1],g[0],false);
2576 world.gop.fence();
2577
2578 compress(world,d,false);
2579 compress(world,dd,false);
2580 world.gop.fence();
2581
2582 d[0].gaxpy(1.0,dd[0],-1.0,false);
2583 d[1].gaxpy(1.0,dd[1],-1.0,false);
2584 d[2].gaxpy(1.0,dd[2],-1.0,false);
2585 world.gop.fence();
2586
2587 make_redundant(world, d);
2588 return d;
2589 }
2590
2591 template<typename T, std::size_t NDIM>
2592 void load_balance(World& world, std::vector<Function<T,NDIM> >& vf) {
2593
2594 struct LBCost {
2595 LBCost() = default;
2596 double operator()(const Key<NDIM>& key, const FunctionNode<T,NDIM>& node) const {
2597 return node.coeff().size();
2598 }
2599 };
2600
2601 LoadBalanceDeux<6> lb(world);
2602 for (const auto& f : vf) lb.add_tree(f, LBCost());
2604
2605 }
2606
2607 /// load a vector of functions
2608 template<typename T, size_t NDIM>
2609 void load_function(World& world, std::vector<Function<T,NDIM> >& f,
2610 const std::string name) {
2611 if (world.rank()==0) print("loading vector of functions",name);
2613 std::size_t fsize=0;
2614 ar & fsize;
2615 f.resize(fsize);
2616 for (std::size_t i=0; i<fsize; ++i) ar & f[i];
2617 }
2618
2619 /// save a vector of functions
2620 template<typename T, size_t NDIM>
2621 void save_function(const std::vector<Function<T,NDIM> >& f, const std::string name) {
2622 if (f.size()>0) {
2623 World& world=f.front().world();
2624 if (world.rank()==0) print("saving vector of functions",name);
2626 std::size_t fsize=f.size();
2627 ar & fsize;
2628 for (std::size_t i=0; i<fsize; ++i) ar & f[i];
2629 }
2630 }
2631
2632
2633}
2634#endif // MADNESS_MRA_VMRA_H__INCLUDED
double q(double t)
Definition DKops.h:18
Definition test_ar.cc:118
long size() const
Returns the number of elements in the tensor.
Definition basetensor.h:138
Implements derivatives operators with variety of boundary conditions on simulation domain.
Definition derivative.h:337
Definition distributed_matrix.h:68
Manages data associated with a row/column/block distributed array.
Definition distributed_matrix.h:388
static void redistribute(World &world, const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > &newpmap)
Sets the default process map and redistributes all functions using the old map.
Definition funcdefaults.h:458
static TensorType get_tensor_type()
Returns the default tensor type.
Definition funcdefaults.h:329
FunctionFactory implements the named-parameter idiom for Function.
Definition function_factory.h:86
FunctionFactory & compressed(bool value=true)
Definition function_factory.h:168
FunctionImpl holds all Function state to facilitate shallow copy semantics.
Definition funcimpl.h:970
static Tensor< TENSOR_RESULT_TYPE(T, R) > inner_local(const std::vector< const FunctionImpl< T, NDIM > * > &left, const std::vector< const FunctionImpl< R, NDIM > * > &right, bool sym)
Definition funcimpl.h:6278
void undo_redundant(const bool fence)
convert this from redundant to standard reconstructed form
Definition mraimpl.h:1579
void multiply(const implT *f, const FunctionImpl< T, LDIM > *g, const int particle)
multiply f (a pair function of NDIM) with an orbital g (LDIM=NDIM/2)
Definition funcimpl.h:3819
void change_tree_state(const TreeState finalstate, bool fence=true)
change the tree state of this function, might or might not respect fence!
Definition mraimpl.h:1441
static Tensor< TENSOR_RESULT_TYPE(T, R)> dot_local(const std::vector< const FunctionImpl< T, NDIM > * > &left, const std::vector< const FunctionImpl< R, NDIM > * > &right, bool sym)
Definition funcimpl.h:6330
FunctionNode holds the coefficients, etc., at each node of the 2^NDIM-tree.
Definition funcimpl.h:136
coeffT & coeff()
Returns a non-const reference to the tensor containing the coeffs.
Definition funcimpl.h:237
A multiresolution adaptive numerical function.
Definition mra.h:144
World & world() const
Returns the world.
Definition mra.h:758
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
void set_impl(const std::shared_ptr< FunctionImpl< T, NDIM > > &impl)
Replace current FunctionImpl with provided new one.
Definition mra.h:731
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
bool is_initialized() const
Returns true if the function is initialized.
Definition mra.h:172
long size() const
Definition lowranktensor.h:488
Key is the index for a node of the 2^NDIM-tree.
Definition key.h:70
Definition lbdeux.h:233
std::shared_ptr< WorldDCPmapInterface< keyT > > load_balance(double fac=1.0, bool printstuff=false)
Actually does the partitioning of the tree.
Definition lbdeux.h:390
void add_tree(const Function< T, NDIM > &f, const costT &costfn, bool fence=false)
Accumulates cost from a function.
Definition lbdeux.h:294
Definition operator.h:156
A slice defines a sub-range or patch of a dimension.
Definition slice.h:103
A tensor is a multidimensional array.
Definition tensor.h:318
TensorTypeData< T >::scalar_type scalar_type
C++ typename of the real type associated with a complex type.
Definition tensor.h:410
T * ptr()
Returns a pointer to the internal data.
Definition tensor.h:1841
A simple, fixed dimension vector.
Definition vector.h:64
Interface to be provided by any process map.
Definition worlddc.h:125
Definition worlddc.h:309
void max(T *buf, size_t nelem)
Inplace global max while still processing AM & tasks.
Definition worldgop.h:902
void fence(bool debug=false)
Synchronizes all processes in communicator AND globally ensures no pending AM or tasks.
Definition worldgop.cc:177
void min(T *buf, size_t nelem)
Inplace global min while still processing AM & tasks.
Definition worldgop.h:896
void sum(T *buf, size_t nelem)
Inplace global sum while still processing AM & tasks.
Definition worldgop.h:890
void fence()
Returns after all local tasks have completed.
Definition world_task_queue.h:1384
A parallel world class.
Definition world.h:134
WorldTaskQueue & taskq
Task queue.
Definition world.h:215
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
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
int integer
Definition crayio.c:25
static const double R
Definition csqrt.cc:46
Declaration and initialization of tree traversal functions and generic derivative.
Tensor< T > conj_transpose(const Tensor< T > &t)
Returns a new deep copy of the complex conjugate transpose of the input tensor.
Definition tensor.h:2044
Tensor< T > transpose(const Tensor< T > &t)
Returns a new deep copy of the transpose of the input tensor.
Definition tensor.h:2035
const double beta
Definition gygi_soltion.cc:62
static const double v
Definition hatom_sf_dirac.cc:20
Tensor< double > op(const Tensor< double > &x)
Definition kain.cc:508
#define rot(x, k)
Definition lookup3.c:84
#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_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
Main include file for MADNESS and defines Function interface.
static const bool VERIFY_TREE
Definition mra.h:57
Definition potentialmanager.cc:41
void reconstruct_for_norm(World &world, const std::vector< Function< T, NDIM > > &v)
put a vector of functions into a state whose coefficients sum to ||f||^2
Definition vmra.h:909
Namespace for all elements and tools of MADNESS.
Definition DFConvergence.h:9
void save_function(const std::vector< Function< T, NDIM > > &f, const std::string name)
save a vector of functions
Definition vmra.h:2621
bool ensure_tree_state_respecting_fence(const std::vector< Function< T, NDIM > > &v, const TreeState state, bool fence)
ensure v has the requested tree state, change the tree state of v if necessary and no fence is given
Definition vmra.h:317
void rr_cholesky(Tensor< T > &A, typename Tensor< T >::scalar_type tol, Tensor< integer > &piv, int &rank)
Compute the rank-revealing Cholesky factorization.
Definition lapack.cc:1203
void make_redundant(World &world, const std::vector< Function< T, NDIM > > &v, bool fence=true)
change tree_state of a vector of functions to redundant
Definition vmra.h:187
std::vector< Function< T, NDIM > > orthonormalize_rrcd(const std::vector< Function< T, NDIM > > &v, Tensor< T > &ovlp, const double tol, Tensor< integer > &piv, int &rank)
Definition vmra.h:663
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
Function< TENSOR_RESULT_TYPE(L, R), NDIM > gaxpy_oop(TENSOR_RESULT_TYPE(L, R) alpha, const Function< L, NDIM > &left, TENSOR_RESULT_TYPE(L, R) beta, const Function< R, NDIM > &right, bool fence=true)
Returns new function alpha*left + beta*right optional fence and no automatic compression.
Definition mra.h:2147
Function< typename TensorTypeData< Q >::scalar_type, NDIM > abs_square(const Function< Q, NDIM > &func)
Definition complexfun.h:121
std::vector< double > function_costs(World &world, const std::vector< Function< T, NDIM > > &v)
Definition vmra.h:1595
Function< T, NDIM > square(const Function< T, NDIM > &f, bool fence=true)
Create a new function that is the square of f - global comm only if not reconstructed.
Definition mra.h:2933
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
std::vector< ProcessID > assign_round_robin(std::size_t nfunc, int nranks)
owner[j] = j % nranks. For redistribute_to_batches.
Definition vmra.h:1559
std::vector< Function< T, NDIM > > reduce_rank(std::vector< Function< T, NDIM > > v, double thresh=0.0, bool fence=true)
reduces the tensor rank of the coefficient tensor (if applicable)
Definition vmra.h:367
Tensor< double > norm2s_T(World &world, const std::vector< Function< T, NDIM > > &v)
Computes the 2-norms of a vector of functions.
Definition vmra.h:944
std::vector< double > norm2s(World &world, const std::vector< Function< T, NDIM > > &v)
Computes the 2-norms of a vector of functions.
Definition vmra.h:927
Function< TENSOR_RESULT_TYPE(T, R), NDIM > dot_sparse(World &world, const std::vector< Function< T, NDIM > > &a, const std::vector< Function< R, NDIM > > &b, double tol, bool fence=true, bool do_make_redundant=true)
Multiplies and sums two vectors of functions r = \sum_i a[i] * b[i].
Definition vmra.h:1835
Function< T, NDIM > div_abgv(const std::vector< Function< T, NDIM > > &v, bool do_refine=false, bool fence=true)
div with the ABGV derivative
Definition vmra.h:2481
std::vector< Function< T, NDIM > > grad_bspline_one(const Function< T, NDIM > &f, bool refine=false, bool fence=true)
Definition vmra.h:2376
void set_impl(std::vector< Function< T, NDIM > > &v, const std::vector< std::shared_ptr< FunctionImpl< T, NDIM > > > vimpl)
Definition vmra.h:739
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
Function< TENSOR_RESULT_TYPE(T, R), NDIM > dot(World &world, const std::vector< Function< T, NDIM > > &a, const std::vector< Function< R, NDIM > > &b, bool fence=true, bool do_make_redundant=true)
Multiplies and sums two vectors of functions r = \sum_i a[i] * b[i]; see dot_sparse for screening.
Definition vmra.h:1848
std::vector< Function< T, NDIM > > orthonormalize_symmetric(const std::vector< Function< T, NDIM > > &v, const Tensor< T > &ovlp, double lindep=1e-12)
symmetric orthonormalization (see e.g. Szabo/Ostlund)
Definition vmra.h:501
std::vector< std::shared_ptr< FunctionImpl< T, NDIM > > > get_impl(const std::vector< Function< T, NDIM > > &v)
Definition vmra.h:732
Function< T, NDIM > div(const std::vector< Function< T, NDIM > > &v, bool do_refine=false, bool fence=true)
shorthand div operator, with the default (ABGV) derivative
Definition vmra.h:2502
std::vector< Function< T, NDIM > > orthonormalize_cd(const std::vector< Function< T, NDIM > > &v, Tensor< T > &ovlp)
Definition vmra.h:626
std::vector< Function< T, NDIM > > copy_n(World &world, const Function< T, NDIM > &v, const unsigned int n, bool fence=true)
Returns a vector of n deep copies of a function.
Definition vmra.h:1522
void norm_tree(World &world, const std::vector< Function< T, NDIM > > &v, bool fence=true)
Makes the norm tree for all functions in a vector.
Definition vmra.h:1345
tensorT Q2(const tensorT &s)
Given overlap matrix, return rotation with 2nd order error to orthonormalize the vectors.
Definition SCF.cc:139
std::vector< Function< TENSOR_RESULT_TYPE(T, R), NDIM > > transform(World &world, const std::vector< Function< T, NDIM > > &v, const Tensor< R > &c, bool fence=true)
Transforms a vector of functions according to new[i] = sum[j] old[j]*c[j,i].
Definition vmra.h:758
std::vector< Function< TENSOR_RESULT_TYPE(T, R), NDIM > > cross(const std::vector< Function< T, NDIM > > &f, const std::vector< Function< R, NDIM > > &g, bool do_refine=false, bool fence=true)
shorthand cross operator
Definition vmra.h:2557
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
@ compressed
d coeffs in internal nodes, s and d coeffs at the root, empty leaves may be present
Definition funcdefaults.h:62
@ redundant
s coeffs everywhere
Definition funcdefaults.h:66
@ nonstandard_with_leaves
like nonstandard, with s coeffs at the leaves
Definition funcdefaults.h:64
Function< T, NDIM > conj(const Function< T, NDIM > &f, bool fence=true)
Return the complex conjugate of the input function with the same distribution and optional fence.
Definition mra.h:2294
void cholesky(Tensor< T > &A)
Compute the Cholesky factorization.
Definition lapack.cc:1174
std::vector< std::vector< Function< TENSOR_RESULT_TYPE(T, R), NDIM > > > matrix_mul_sparse(World &world, const std::vector< Function< R, NDIM > > &f, const std::vector< Function< R, NDIM > > &g, double tol, bool fence=true, bool symm=false)
Outer product of a vector of functions with a vector of functions using sparsity.
Definition vmra.h:1316
Function< T, NDIM > div_bspline(const std::vector< Function< T, NDIM > > &v, bool do_refine=false, bool fence=true)
div with the b-spline smoothing first derivative
Definition vmra.h:2488
void standard(World &world, std::vector< Function< T, NDIM > > &v, bool fence=true)
Generates standard form of a vector of functions.
Definition vmra.h:244
void truncate(World &world, std::vector< Function< T, NDIM > > &v, double tol=0.0, bool fence=true)
Truncates a vector of functions.
Definition vmra.h:336
void compress(World &world, const std::vector< Function< T, NDIM > > &v, bool fence=true)
Compress a vector of functions.
Definition vmra.h:150
const std::vector< Function< T, NDIM > > & reconstruct(const std::vector< Function< T, NDIM > > &v)
reconstruct a vector of functions
Definition vmra.h:163
std::vector< Function< T, NDIM > > impl2function(const std::vector< std::shared_ptr< FunctionImpl< T, NDIM > > > vimpl)
Definition vmra.h:745
std::vector< Function< T, NDIM > > grad_bpsline_two(const Function< T, NDIM > &f, bool refine=false, bool fence=true)
Definition vmra.h:2397
void set_thresh(World &world, std::vector< Function< T, NDIM > > &v, double thresh, bool fence=true)
Sets the threshold in a vector of functions.
Definition vmra.h:1457
double norm2(World &world, const std::vector< Function< T, NDIM > > &v)
Computes the 2-norm of a vector of functions.
Definition vmra.h:961
Function< T, NDIM > div_ble(const std::vector< Function< T, NDIM > > &v, bool do_refine=false, bool fence=true)
div with the BLE smoothing first derivative
Definition vmra.h:2495
std::vector< ProcessID > assign_cost_aware(const std::vector< double > &cost, int nranks)
Definition vmra.h:1570
std::vector< Function< T, NDIM > > flatten(const std::vector< std::vector< Function< T, NDIM > > > &vv)
Definition vmra.h:725
std::vector< CCPairFunction< T, NDIM > > operator*(const double fac, const std::vector< CCPairFunction< T, NDIM > > &arg)
Definition ccpairfunction.h:1089
static void verify_tree(World &world, const std::vector< Function< T, NDIM > > &v)
Definition SCF.cc:76
std::vector< Function< T, NDIM > > multi_to_multi_op_values(const opT &op, const std::vector< Function< T, NDIM > > &vin, const bool fence=true)
apply op on the input vector yielding an output vector of functions
Definition vmra.h:2165
static const Slice _(0,-1, 1)
Tensor< T > inverse(const Tensor< T > &a_in)
invert general square matrix A
Definition lapack.cc:832
void load_balance(const real_function_6d &f, const bool leaf)
do some load-balancing
Definition madness/chem/mp2.cc:70
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::vector< Function< T, NDIM > > partial_mul(const Function< T, NDIM > f, const std::vector< Function< T, LDIM > > g, const int particle)
multiply a high-dimensional function with a low-dimensional function
Definition vmra.h:1392
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
std::vector< Function< TENSOR_RESULT_TYPE(T, R), NDIM > > transform_reconstructed(World &world, const std::vector< Function< T, NDIM > > &v, const Tensor< R > &c, bool fence=true)
Transforms a vector of functions according to new[i] = sum[j] old[j]*c[j,i].
Definition vmra.h:787
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
std::vector< Function< T, NDIM > > append(const std::vector< Function< T, NDIM > > &lhs, const std::vector< Function< T, NDIM > > &rhs)
combine two vectors
Definition vmra.h:718
void redistribute_to_batches(World &world, std::vector< Function< T, NDIM > > &v, const std::vector< ProcessID > &owner, std::size_t cap_bytes=0, bool rotate=true)
Definition vmra.h:1615
@ TT_2D
Definition gentensor.h:120
@ TT_FULL
Definition gentensor.h:120
void refine(World &world, const std::vector< Function< T, NDIM > > &vf, bool fence=true)
refine the functions according to the autorefine criteria
Definition vmra.h:197
Function< T, NDIM > div_deriv(const std::vector< Function< T, NDIM > > &v, const DerivMethod method, bool do_refine=false, bool fence=true)
shorthand div operator, with a choice of first derivative
Definition vmra.h:2456
void print_size(World &world, const std::vector< Function< T, NDIM > > &v, const std::string &msg="vectorfunction")
Definition vmra.h:2100
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
const Function< T, NDIM > & change_tree_state(const Function< T, NDIM > &f, const TreeState finalstate, bool fence=true)
change tree state of a function
Definition mra.h:2994
std::vector< CCPairFunction< T, NDIM > > & operator-=(std::vector< CCPairFunction< T, NDIM > > &rhs, const std::vector< CCPairFunction< T, NDIM > > &lhs)
Definition ccpairfunction.h:1082
std::vector< Function< T, NDIM > > orthonormalize(const std::vector< Function< T, NDIM > > &vf_in)
orthonormalize the vectors
Definition vmra.h:466
NDIM const Function< R, NDIM > & g
Definition mra.h:2668
double wall_time()
Returns the wall time in seconds relative to an arbitrary origin.
Definition timers.cc:48
std::vector< Function< T, NDIM > > grad_ble_one(const Function< T, NDIM > &f, bool refine=false, bool fence=true)
Definition vmra.h:2334
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
std::vector< Function< T, NDIM > > grad_bspline_three(const Function< T, NDIM > &f, bool refine=false, bool fence=true)
Definition vmra.h:2418
DerivMethod
which first derivative the div_* variants use
Definition vmra.h:2444
static const int kmax
Definition twoscale.cc:52
std::vector< Function< T, NDIM > > zero_functions_compressed(World &world, int n, bool fence=true)
Generates a vector of zero functions (compressed)
Definition vmra.h:450
double imag(double x)
Definition complexfun.h:56
void load_function(World &world, std::vector< Function< T, NDIM > > &f, const std::string name)
load a vector of functions
Definition vmra.h:2609
std::vector< Function< TENSOR_RESULT_TYPE(L, R), D > > vmulXX(const Function< L, D > &left, const std::vector< Function< R, D > > &vright, double tol, bool fence=true)
Use the vmra/mul(...) interface instead.
Definition mra.h:2037
void refine_to_common_level(World &world, std::vector< Function< T, NDIM > > &vf, bool fence=true)
refine all functions to a common (finest) level
Definition vmra.h:207
std::vector< Function< T, NDIM > > grad_ble_two(const Function< T, NDIM > &f, bool refine=false, bool fence=true)
Definition vmra.h:2355
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
void normalize(World &world, std::vector< Function< T, NDIM > > &v, bool fence=true)
Normalizes a vector of functions — v[i] = v[i].scale(1.0/v[i].norm2())
Definition vmra.h:2092
void stage_halo(World &world, const std::vector< std::shared_ptr< Derivative< T, NDIM > > > &grad, const std::vector< Function< T, NDIM > > &v, bool fence=true)
Pre-stages the neighbor coefficients that differentiating v with each of grad will need.
Definition vmra.h:382
std::vector< Function< T, NDIM > > zero_functions(World &world, int n, bool fence=true)
Generates a vector of zero functions (reconstructed)
Definition vmra.h:443
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
std::vector< Function< T, NDIM > > grad(const Function< T, NDIM > &f, bool refine=false, bool fence=true)
shorthand gradient operator
Definition vmra.h:2316
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 scale(World &world, std::vector< Function< T, NDIM > > &v, const std::vector< Q > &factors, bool fence=true)
Scales inplace a vector of functions by distinct values.
Definition vmra.h:874
std::vector< Function< T, NDIM > > zero_functions_auto_tree_state(World &world, int n, bool fence=true)
Generates a vector of zero functions, either compressed or reconstructed, depending on tensor type.
Definition vmra.h:457
DistributedMatrix< T > matrix_dot(const DistributedMatrixDistribution &d, const std::vector< Function< T, NDIM > > &f, const std::vector< Function< T, NDIM > > &g, bool sym=false)
Definition vmra.h:1737
void load(Function< T, NDIM > &f, const std::string name)
Definition mra.h:3032
std::vector< Function< T, NDIM > > zero_functions_tree_state(World &world, int n, const TreeState state, bool fence=true)
Generates a vector of zero functions with a given tree state.
Definition vmra.h:422
double real(double x)
Definition complexfun.h:52
std::vector< CCPairFunction< T, NDIM > > & operator+=(std::vector< CCPairFunction< T, NDIM > > &lhs, const CCPairFunction< T, NDIM > &rhs)
Definition ccpairfunction.h:1068
@ same
same atoms at the same places
std::vector< Function< T, NDIM > > orthonormalize_canonical(const std::vector< Function< T, NDIM > > &v, const Tensor< T > &ovlp, double lindep=1e-12)
Definition vmra.h:563
Tensor< TENSOR_RESULT_TYPE(T, R) > matrix_dot_old(World &world, const std::vector< Function< T, NDIM > > &f, const std::vector< Function< R, NDIM > > &g, bool sym=false)
Computes the matrix dot product of two function vectors - q(i,j) = dot(f[i],g[j])
Definition vmra.h:1800
std::string name(const FuncType &type, const int ex=-1)
Definition ccpairfunction.h:28
void clear_halo(const std::vector< Function< T, NDIM > > &v)
Discards the neighbor halos staged on v.
Definition vmra.h:397
void matrix_inner(DistributedMatrix< T > &A, const std::vector< Function< T, NDIM > > &f, const std::vector< Function< T, NDIM > > &g, bool sym=false)
Definition distpm.cc:46
double get_size(World &world, const std::vector< Function< T, NDIM > > &v)
Definition vmra.h:2134
double get_size_local(World &world, const std::vector< Function< T, NDIM > > &v)
return the size of a vector of functions for each rank
Definition vmra.h:2115
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
void syev(const Tensor< T > &A, Tensor< T > &V, Tensor< typename Tensor< T >::scalar_type > &e)
Real-symmetric or complex-Hermitian eigenproblem.
Definition lapack.cc:969
Tensor< TENSOR_RESULT_TYPE(T, R) > matrix_inner_old(World &world, const std::vector< Function< T, NDIM > > &f, const std::vector< Function< R, NDIM > > &g, bool sym=false)
Computes the matrix inner product of two function vectors - q(i,j) = inner(f[i],g[j])
Definition vmra.h:1083
void make_nonstandard(World &world, std::vector< Function< T, NDIM > > &v, bool fence=true)
Generates non-standard form of a vector of functions.
Definition vmra.h:234
void gaxpy(const double a, ScalarResult< T > &left, const double b, const T &right, const bool fence=true)
the result type of a macrotask must implement gaxpy
Definition macrotaskq.h:244
int distance(const madness::Hash_private::HashIterator< hashT > &it, const madness::Hash_private::HashIterator< hashT > &jt)
Definition worldhashmap.h:616
static long abs(long a)
Definition tensor.h:219
static const double b
Definition nonlinschro.cc:119
static const double d
Definition nonlinschro.cc:121
static const double a
Definition nonlinschro.cc:118
static const size_t nfunc
Definition pcr.cc:63
double Q(double a)
Definition relops.cc:20
static const double c
Definition relops.cc:10
static const double m
Definition relops.cc:9
static const double L
Definition rk.cc:46
static const double thresh
Definition rk.cc:45
static const long k
Definition rk.cc:44
Definition test_ar.cc:204
Definition mp2.h:63
double operator()(const Key< 6 > &key, const FunctionNode< double, 6 > &node) const
Definition mp2.h:70
Definition lowrankfunction.h:336
Definition dirac-hatom.cc:112
std::string ok(const bool b)
Definition test6.cc:43
AtomicInt sum
Definition test_atomicint.cc:46
int P
Definition test_binsorter.cc:9
void e()
Definition test_sig.cc:75
static const double alpha
Definition testcosine.cc:10
constexpr std::size_t NDIM
Definition testgconv.cc:54
double h(const coord_1d &r)
Definition testgconv.cc:175
#define TENSOR_RESULT_TYPE(L, R)
This macro simplifies access to TensorResultType.
Definition type_data.h:205
#define PROFILE_FUNC
Definition worldprofile.h:209
#define PROFILE_BLOCK(name)
Definition worldprofile.h:208
int ProcessID
Used to clearly identify process number/rank.
Definition worldtypes.h:43