MADNESS 0.10.1
composite_product_op.h
Go to the documentation of this file.
1/*
2 This file is part of MADNESS.
3
4 Unified adaptive NS-form product/inner operator.
5
6 Generalises three pre-existing operators in funcimpl.h:
7 * multiply_op<LDIM> : h(1,2) = f(1,2) * g(p)
8 * Vphi_op_NS<opT,LDIM> : result = sum_t product_t (sum of single-multiplications)
9 * Vphi_ij_u_op_NS<opT,LDIM> : u * i(1) * j(2) and <eri | u*i*j>
10
11 A composite_term carries a ket (or a hartree pair p1*p2), a chain of LDIM factors per
12 particle, and an optional NDIM "extra" factor (typically an on-demand operator such as
13 an ERI). The result is sum over terms of the products. An inner-product mode contracts
14 each term against an NDIM "bra" function and accumulates a scalar at key0.
15
16 Error control follows Vphi_ij_u_op_NS::compute_error_from_inaccurate_refinement:
17 (s,d)-norm cross-terms over {ket, factors_1, factors_2, extra} summed across the
18 multiplication chain, plus tnorm/oversampling error from each pointwise_multiplier.
19*/
20#ifndef MADNESS_MRA_COMPOSITE_PRODUCT_OP_H__
21#define MADNESS_MRA_COMPOSITE_PRODUCT_OP_H__
22
24
25namespace madness {
26
27/// One term of a composite product: ket(1,2) * factor1(1) * factor2(2) * extra(1,2)
28///
29/// The ket is either an explicit NDIM Function (`ket`) or a low-rank sum of hartree
30/// products ket(1,2) = Σ_k p1[k](1) ⊗ p2[k](2). Provide `ket` *or* (p1, p2); not both.
31template <typename T, std::size_t NDIM>
33 static constexpr std::size_t LDIM = NDIM / 2;
34 static_assert(NDIM == 2 * LDIM, "composite_term requires NDIM = 2*LDIM");
35
38
39 /// NDIM ket factor. If uninitialized, the term ket is Σ_k p1[k] ⊗ p2[k].
41 /// parallel vectors forming the ket as a sum of hartree products (p1.size() == p2.size())
42 std::vector<FL> p1, p2;
43 /// single LDIM factor per particle (uninitialized = skip)
45 /// optional single NDIM extra factor (e.g. an on-demand ERI). Multiplied into the
46 /// chain in function mode; ignored in inner mode (use the global `bra` instead).
48 /// scalar prefactor applied to the entire product. Used by the DSL to fold scaled
49 /// subtrees (`α * f`) into a single term without an explicit scalar Function literal.
50 double coeff = 1.0;
51};
52
53/// Adaptive sum-of-products operator over a vector of composite_terms.
54template <typename T, std::size_t NDIM, typename opT, std::size_t LDIM>
56 static_assert(NDIM == 2 * LDIM, "composite_product_op_NS requires NDIM = 2*LDIM");
57
58 enum class Mode { function, inner };
59
63 using keyT = Key<NDIM>;
64 using keyL = Key<LDIM>;
65 using nodeT = typename implT::nodeT;
66 using coeffT = typename implT::coeffT;
70
71 /// internal serialisable view of a term (CoeffTracker-based)
72 struct TermImpl {
73 ctT iaket; ///< NDIM ket (optional, exclusive with iap*)
74 std::vector<ctL> iap1, iap2; ///< sum-of-hartree-products ket if iaket is null
75 ctL ifactor1, ifactor2; ///< single per-particle LDIM factor (each optional)
76 const implT* extra = nullptr; ///< NDIM extra factor (function mode only)
77 double coeff = 1.0; ///< scalar prefactor (DSL)
78
79 bool have_ket() const { return iaket.get_impl(); }
80 bool have_factor1() const { return ifactor1.get_impl(); }
81 bool have_factor2() const { return ifactor2.get_impl(); }
82 bool have_extra() const { return extra; }
83
84 template <typename Archive> void serialize(Archive& ar) {
85 ar & iaket & iap1 & iap2 & ifactor1 & ifactor2 & extra & coeff;
86 }
87 };
88
89 bool randomize() const { return true; }
90
91 implT* result; ///< output tree (function mode) or scalar sink (inner)
93 std::vector<TermImpl> terms;
95 const implT* bra = nullptr; ///< inner-mode on-demand bra (e.g., ERI)
96 ctT cbra; ///< inner-mode explicit-NDIM bra (tracked).
97 /// At most one of {bra, cbra} is active.
98 double target_precision = 0.0;
99 int oversampling = 1;
100
101 bool have_explicit_bra() const { return cbra.get_impl(); }
102
104
106 const std::vector<TermImpl>& terms,
107 Mode mode,
108 const implT* bra,
109 double target_precision,
110 int oversampling,
111 const ctT& cbra = ctT{})
113 mode(mode), bra(bra), cbra(cbra),
115 if (mode == Mode::inner) {
116 MADNESS_CHECK_THROW(bool(bra) ^ bool(cbra.get_impl()),
117 "inner mode requires exactly one bra: either on-demand (bra) "
118 "or explicit-NDIM (cbra)");
120 "inner mode: on-demand bra pointer must point to an on-demand FunctionImpl");
121 }
122 }
123
124 // ---------------------------------------------------------------------
125 // error model: snorm/dnorm cross-terms over the factor chain of one term
126 // ---------------------------------------------------------------------
127
128 /// retrieve scaling- and detail-norm at the given key from a CoeffTracker
129 template <typename CT, typename KEY>
130 static std::pair<double, double> sd_norm(const CT& ct, const KEY& key) {
131 if (!ct.get_impl()) return {1.0, 0.0};
132 return {ct.coeff(key).normf(), ct.dnorm(key)};
133 }
134
135 /// scaling- and detail-norm for an on-demand NDIM extra factor. Returns
136 /// (snorm, dnorm) ~ (||P_n eri||, ||Q_n eri||), with dnorm computed from the
137 /// NS s0 / non-s0 split of the eri coeff tensor.
138 static std::pair<double, double> sd_norm_extra(const implT* extra, const keyT& key) {
139 if (!extra) return {1.0, 0.0};
140 tensorT c = eri_coeffs(extra, key);
141 const double sn = c(extra->get_cdata().s0).normf();
142 tensorT d = copy(c);
143 d(extra->get_cdata().s0) = 0.0;
144 return {sn, d.normf()};
145 }
146
147 /// Σ over all non-trivial cross-products of (s_i + d_i) − ∏ s_i.
148 /// Each factor contributes (s,d); error = total − product_of_s.
149 static double cross_term_error(const std::vector<std::pair<double, double>>& sd) {
150 double total = 1.0, sprod = 1.0;
151 for (const auto& [s, d] : sd) { total *= (s + d); sprod *= s; }
152 return total - sprod;
153 }
154
155 /// (s,d) of a sum-of-hartree-products ket: snorm = Σ_k s1_k s2_k,
156 /// dnorm = Σ_k (s1_k d2_k + d1_k s2_k + d1_k d2_k). Upper-bound on the true
157 /// (s,d) of the rank-r sum.
158 std::pair<double, double> sd_norm_pair_sum(const std::vector<ctL>& iap1,
159 const std::vector<ctL>& iap2,
160 const keyL& k1, const keyL& k2) const {
161 MADNESS_ASSERT(iap1.size() == iap2.size());
162 double s = 0.0, d = 0.0;
163 for (std::size_t k = 0; k < iap1.size(); ++k) {
164 const auto [s1, d1] = sd_norm(iap1[k], k1);
165 const auto [s2, d2] = sd_norm(iap2[k], k2);
166 s += s1 * s2;
167 d += s1 * d2 + d1 * s2 + d1 * d2;
168 }
169 return {s, d};
170 }
171
172 /// full inaccurate-refinement error for one term, scaled by the term's coeff.
173 double term_refinement_error(const TermImpl& t, const keyT& key,
174 const keyL& k1, const keyL& k2) const {
175 std::vector<std::pair<double, double>> sd;
176 if (t.have_ket()) sd.push_back(sd_norm(t.iaket, key));
177 else sd.push_back(sd_norm_pair_sum(t.iap1, t.iap2, k1, k2));
178 if (t.have_factor1()) sd.push_back(sd_norm(t.ifactor1, k1));
179 if (t.have_factor2()) sd.push_back(sd_norm(t.ifactor2, k2));
180 if (t.have_extra()) sd.push_back(sd_norm_extra(t.extra, key));
181 return std::abs(t.coeff) * cross_term_error(sd);
182 }
183
184 // ---------------------------------------------------------------------
185 // multiplication chain for one term: current = ket → ket·f1 → … → ket·…·extra
186 // ---------------------------------------------------------------------
187
188 /// fetch coeffs of an on-demand NDIM function at `key`
189 static tensorT eri_coeffs(const implT* eri, const keyT& key) {
190 MADNESS_ASSERT(eri);
191 if (eri->get_functor()->provides_coeff()) {
192 return eri->get_functor()->coeff(key).full_tensor();
193 } else {
194 tensorT val(eri->get_cdata().vk);
195 eri->fcube(key, *(eri->get_functor()), eri->get_cdata().quad_x, val);
196 return eri->values2coeffs(key, val);
197 }
198 }
199
200 /// build the NDIM ket coefficients at `key` for a term: either the explicit NDIM ket
201 /// or the sum Σ_k outer(p1[k](k1), p2[k](k2)).
202 coeffT make_term_ket(const TermImpl& t, const keyT& key,
203 const keyL& k1, const keyL& k2) const {
204 if (t.have_ket()) return t.iaket.coeff(key);
205 MADNESS_ASSERT(!t.iap1.empty() && t.iap1.size() == t.iap2.size());
206 coeffT acc = outer(t.iap1[0].coeff(k1), t.iap2[0].coeff(k2),
207 result->get_tensor_args());
208 for (std::size_t k = 1; k < t.iap1.size(); ++k) {
209 acc += outer(t.iap1[k].coeff(k1), t.iap2[k].coeff(k2),
210 result->get_tensor_args());
211 }
212 acc.reduce_rank(result->get_tensor_args().thresh);
213 return acc;
214 }
215
216 /// multiply one term: ket * factor1(1) * factor2(2) * extra(1,2)
217 /// Returns (coeffs at key, accumulated tnorm/oversampling error).
218 std::pair<coeffT, double> make_term_coeffs(const TermImpl& t, const keyT& key) const {
219 keyL k1, k2; key.break_apart(k1, k2);
220 coeffT current = make_term_ket(t, key, k1, k2);
221 double err = 0.0;
222
223 if (t.have_factor1()) {
224 typename implT::template pointwise_multiplier<LDIM> pm(key, current);
225 pm.oversampling = oversampling;
226 current = pm(key, t.ifactor1.coeff(k1).full_tensor(), 1);
227 err += pm.error;
228 }
229 if (t.have_factor2()) {
230 typename implT::template pointwise_multiplier<LDIM> pm(key, current);
231 pm.oversampling = oversampling;
232 current = pm(key, t.ifactor2.coeff(k2).full_tensor(), 2);
233 err += pm.error;
234 }
235 if (t.have_extra()) {
236 tensorT ceri = eri_coeffs(t.extra, key);
237 typename implT::template pointwise_multiplier<LDIM> pm(key, current);
238 pm.oversampling = oversampling;
239 tensorT v = pm(key, ceri);
240 current = coeffT(v, result->get_tensor_args());
241 err += pm.error;
242 }
243 // DSL scalar prefactor: scale coeffs and error
244 if (t.coeff != 1.0) {
245 current.scale(t.coeff);
246 err *= std::abs(t.coeff);
247 }
248 return {current, err};
249 }
250
251 /// Σ over terms. Function mode: returns the sum of NDIM coeffs.
252 /// Inner mode: returns a degenerate coeff (the scalar lives elsewhere);
253 /// here we return the sum of contractions packed as the
254 /// (0,…) element of a vk-shaped tensor.
255 std::pair<coeffT, double> make_sum_coeffs(const keyT& key) const {
256 keyL k1, k2; key.break_apart(k1, k2);
257
258 double error = 0.0;
259 if (mode == Mode::function) {
260 coeffT cresult(result->get_cdata().vk, result->get_tensor_args());
261 for (const auto& t : terms) {
262 error += term_refinement_error(t, key, k1, k2);
263 auto [c, e] = make_term_coeffs(t, key);
264 cresult += c;
265 error += e;
266 }
267 if (terms.size() > 1) cresult.reduce_rank(result->get_tensor_args().thresh);
268 return {cresult, error};
269 } else { // inner mode
270 // Fetch the bra coefficients at this key. Two paths:
271 // - on-demand bra: evaluate the functor / use provided coeffs
272 // - explicit-NDIM bra: read from the CoeffTracker (parent-to-child)
273 const tensorT cbra_t = have_explicit_bra()
274 ? cbra.coeff(key).full_tensor_copy()
275 : eri_coeffs(bra, key);
276 T sum = T(0);
277 for (const auto& t : terms) {
278 error += term_refinement_error(t, key, k1, k2);
279 auto [c, e] = make_term_coeffs(t, key);
280 error += e;
281 sum += cbra_t.trace(c.full_tensor_copy());
282 }
283 // wrap scalar in a vk tensor at key0 convention
284 tensorT scalar(result->get_cdata().vk);
285 scalar.flat()(0L) = sum;
286 return {coeffT(scalar, TensorArgs(TT_FULL, -1.0)), error};
287 }
288 }
289
290 // ---------------------------------------------------------------------
291 // leaf action: insert into result tree (function), or accumulate scalar at key0 (inner)
292 // ---------------------------------------------------------------------
293
294 void accumulate_into_result(const keyT& key, const coeffT& coeff) const {
295 result->get_coeffs().task(key, &nodeT::accumulate, coeff,
296 result->get_coeffs(), key, result->get_tensor_args(), false);
297 }
298
299 void leaf_action(const keyT& key, const coeffT& coeff) const {
300 if (mode == Mode::function) {
301 accumulate_into_result(key, coeff);
302 } else {
303 accumulate_into_result(result->get_cdata().key0, coeff);
304 }
305 }
306
307 // ---------------------------------------------------------------------
308 // tree traversal — mirrors Vphi_ij_u_op_NS::operator()
309 // ---------------------------------------------------------------------
310
311 std::pair<bool, coeffT> operator()(const keyT& key) const {
312 MADNESS_ASSERT(result->get_coeffs().is_local(key));
313
314 if (leaf_op.do_pre_screening()) {
315 if (leaf_op.pre_screening(key)) {
316 auto [c, err] = make_sum_coeffs(key);
317 leaf_action(key, c);
318 return {true, coeffT()};
319 }
320 return continue_recursion(std::vector<bool>(1 << NDIM, false), tensorT(), key);
321 }
322
323 const size_t il = result->get_initial_level();
324 const int min_lvl = FunctionDefaults<NDIM>::get_refine() ? int(il) + 1 : int(il);
325 if (key.level() < min_lvl)
326 return continue_recursion(std::vector<bool>(1 << NDIM, false), tensorT(), key);
327
328 if (key.level() < result->get_special_level() &&
329 leaf_op.special_refinement_needed(key))
330 return continue_recursion(std::vector<bool>(1 << NDIM, false), tensorT(), key);
331
332 auto [c, err] = make_sum_coeffs(key);
333
334 const double tol = result->truncate_tol(target_precision, key);
335 if (leaf_op.post_screening(key, c) || err < tol) {
336 leaf_action(key, c);
337 return {true, coeffT()};
338 }
339 return continue_recursion(std::vector<bool>(1 << NDIM, false), tensorT(), key);
340 }
341
342 std::pair<bool, coeffT> continue_recursion(const std::vector<bool> child_is_leaf,
343 const tensorT& coeffs,
344 const keyT& key) const {
345 std::size_t i = 0;
346 for (KeyChildIterator<NDIM> kit(key); kit; ++kit, ++i) {
347 const keyT child = kit.key();
348 if (child_is_leaf[i]) {
350 iop(child, coeffT(copy(coeffs(result->child_patch(child))),
351 result->get_tensor_args()), true);
352 } else {
353 this_type child_op = this->make_child(child);
354 noop<T, NDIM> no;
355 const ProcessID p = result->get_coeffs().owner(child);
356 void (implT::*ft)(const this_type&, const noop<T, NDIM>&, const keyT&) const
357 = &implT::template forward_traverse<this_type, noop<T, NDIM>>;
358 result->task(p, ft, child_op, no, child);
359 }
360 }
361 return {true, coeffT()};
362 }
363
364 this_type make_child(const keyT& child) const {
365 keyL k1, k2; child.break_apart(k1, k2);
366 std::vector<TermImpl> child_terms;
367 child_terms.reserve(terms.size());
368 for (const auto& t : terms) {
369 TermImpl ct;
370 ct.iaket = t.have_ket() ? t.iaket.make_child(child) : ctT();
371 if (!t.have_ket()) {
372 ct.iap1.reserve(t.iap1.size());
373 ct.iap2.reserve(t.iap2.size());
374 for (const auto& p : t.iap1) ct.iap1.push_back(p.make_child(k1));
375 for (const auto& p : t.iap2) ct.iap2.push_back(p.make_child(k2));
376 }
377 ct.ifactor1 = t.have_factor1() ? t.ifactor1.make_child(k1) : ctL();
378 ct.ifactor2 = t.have_factor2() ? t.ifactor2.make_child(k2) : ctL();
379 ct.extra = t.extra;
380 ct.coeff = t.coeff;
381 child_terms.push_back(std::move(ct));
382 }
383 ctT child_cbra = have_explicit_bra() ? cbra.make_child(child) : ctT{};
384 return this_type(result, leaf_op, child_terms, mode, bra,
385 target_precision, oversampling, child_cbra);
386 }
387
389 // Collect all CoeffTracker activations into two flat future vectors without
390 // calling .get(). The forward_ctor task fires only once every future in
391 // these vectors is resolved, so .get() inside it is non-blocking.
392 // The old approach called .get() here, which entered the MADNESS work-
393 // stealing loop, picked up more forward_traverse tasks, and caused
394 // unbounded stack growth (stack overflow -> segfault).
395 //
396 // TaskFn supports at most 9 args, so we pack into two flat vectors:
397 // t_futs: [0..N-1] = ket futures (placeholder ctT{} if !have_ket), [N] = cbra
398 // l_futs: [0..p_total-1] = flat p1/p2 per term (p1[0..n-1], p2[0..n-1]),
399 // [p_total..p_total+N-1] = f1 futures,
400 // [p_total+N..p_total+2N-1] = f2 futures
401 const size_t N = terms.size();
402 std::vector<Future<ctT>> t_futs(N + 1);
403 std::vector<Future<ctL>> l_futs;
404 for (size_t i = 0; i < N; ++i) {
405 const auto& t = terms[i];
406 if (t.have_ket()) {
407 t_futs[i] = t.iaket.activate();
408 } else {
409 t_futs[i] = Future<ctT>(ctT{});
410 for (const auto& p : t.iap1) l_futs.push_back(p.activate());
411 for (const auto& p : t.iap2) l_futs.push_back(p.activate());
412 }
413 }
414 for (size_t i = 0; i < N; ++i) {
415 const auto& t = terms[i];
416 l_futs.push_back(t.have_factor1() ? t.ifactor1.activate() : Future<ctL>(ctL{}));
417 }
418 for (size_t i = 0; i < N; ++i) {
419 const auto& t = terms[i];
420 l_futs.push_back(t.have_factor2() ? t.ifactor2.activate() : Future<ctL>(ctL{}));
421 }
422 t_futs[N] = have_explicit_bra() ? cbra.activate() : Future<ctT>(ctT{});
423 return result->world.taskq.add(
426 t_futs, l_futs);
427 }
428
429 this_type forward_ctor(implT* result1, const opT& leaf_op1,
430 const std::vector<TermImpl>& orig_terms, Mode mode1,
431 const implT* bra1, double target_precision1, int oversampling1,
432 const std::vector<Future<ctT>>& t_futs,
433 const std::vector<Future<ctL>>& l_futs) {
434 // All futures are resolved when this task fires — .get() is non-blocking.
435 auto& tf = const_cast<std::vector<Future<ctT>>&>(t_futs);
436 auto& lf = const_cast<std::vector<Future<ctL>>&>(l_futs);
437 const size_t N = orig_terms.size();
438 // p_total: offset where f1/f2 futures begin in l_futs
439 size_t p_total = 0;
440 for (size_t i = 0; i < N; ++i)
441 if (!orig_terms[i].have_ket()) p_total += 2 * orig_terms[i].iap1.size();
442 std::vector<TermImpl> activated(N);
443 size_t p_idx = 0;
444 for (size_t i = 0; i < N; ++i) {
445 const auto& t = orig_terms[i];
446 activated[i].extra = t.extra;
447 activated[i].coeff = t.coeff;
448 if (t.have_ket()) {
449 activated[i].iaket = tf[i].get();
450 } else {
451 const size_t np = t.iap1.size();
452 for (size_t k = 0; k < np; ++k)
453 activated[i].iap1.push_back(lf[p_idx + k].get());
454 for (size_t k = 0; k < np; ++k)
455 activated[i].iap2.push_back(lf[p_idx + np + k].get());
456 p_idx += 2 * np;
457 }
458 if (t.have_factor1()) activated[i].ifactor1 = lf[p_total + i].get();
459 if (t.have_factor2()) activated[i].ifactor2 = lf[p_total + N + i].get();
460 }
461 return this_type(result1, leaf_op1, activated, mode1, bra1,
462 target_precision1, oversampling1, tf[N].get());
463 }
464
465 template <typename Archive> void serialize(Archive& ar) {
466 ar & result & leaf_op & terms & mode & bra & cbra
468 }
469};
470
471// =====================================================================================
472// ORCHESTRATORS — FunctionImpl-level free helpers used by mra.h wrappers.
473// =====================================================================================
474
475namespace detail {
476
477/// Bring every input of a composite_term into redundant state.
478template <typename T, std::size_t NDIM>
480 if (term.ket.is_initialized()) term.ket.change_tree_state(redundant, false);
481 for (auto& p : term.p1) p.change_tree_state(redundant, false);
482 for (auto& p : term.p2) p.change_tree_state(redundant, false);
485 // extra is on-demand → no state change
486}
487
488/// Restore every input of a composite_term to reconstructed state.
489template <typename T, std::size_t NDIM>
491 if (term.ket.is_initialized()) term.ket.change_tree_state(reconstructed, false);
492 for (auto& p : term.p1) p.change_tree_state(reconstructed, false);
493 for (auto& p : term.p2) p.change_tree_state(reconstructed, false);
496}
497
498/// Translate a composite_term<T,NDIM> into the op's CoeffTracker-based TermImpl.
499template <typename T, std::size_t NDIM, typename OP>
500typename OP::TermImpl make_term_impl(const composite_term<T, NDIM>& term) {
501 constexpr std::size_t LDIM = NDIM / 2;
502 typename OP::TermImpl x;
503 if (term.ket.is_initialized()) {
504 x.iaket = CoeffTracker<T, NDIM>(term.ket.get_impl().get());
505 } else {
506 MADNESS_ASSERT(!term.p1.empty() && term.p1.size() == term.p2.size());
507 x.iap1.reserve(term.p1.size());
508 x.iap2.reserve(term.p2.size());
509 for (const auto& p : term.p1) x.iap1.emplace_back(p.get_impl().get());
510 for (const auto& p : term.p2) x.iap2.emplace_back(p.get_impl().get());
511 }
512 if (term.factor1.is_initialized())
513 x.ifactor1 = CoeffTracker<T, LDIM>(term.factor1.get_impl().get());
514 if (term.factor2.is_initialized())
515 x.ifactor2 = CoeffTracker<T, LDIM>(term.factor2.get_impl().get());
516 if (term.extra.is_initialized()) x.extra = term.extra.get_impl().get();
517 x.coeff = term.coeff;
518 return x;
519}
520
521} // namespace detail
522
523/// build result = Σ_t product_t adaptively
524///
525/// All input functions are brought into redundant state (cheap from reconstructed).
526/// The result is left in reconstructed state on exit.
527template <typename T, std::size_t NDIM, typename leaf_opT>
529 const leaf_opT& leaf_op,
530 std::vector<composite_term<T, NDIM>>& terms,
531 double target_precision,
532 int oversampling,
533 bool fence) {
534 constexpr std::size_t LDIM = NDIM / 2;
536
537 World& world = result->world;
538 for (auto& t : terms) detail::make_term_inputs_redundant(t);
539 world.gop.fence();
540
541 if (target_precision == 0.0) target_precision = result->get_thresh();
542
543 std::vector<typename OP::TermImpl> impl_terms;
544 impl_terms.reserve(terms.size());
545 for (const auto& t : terms) {
546 impl_terms.push_back(detail::make_term_impl<T, NDIM, OP>(t));
547 }
548
549 OP coeff_op(result, leaf_op, impl_terms, OP::Mode::function,
550 /*bra*/ nullptr, target_precision, oversampling);
551 noop<T, NDIM> apply_op;
552
553 if (world.rank() == result->get_coeffs().owner(result->get_cdata().key0)) {
554 result->task(world.rank(),
555 &FunctionImpl<T, NDIM>::template forward_traverse<OP, noop<T, NDIM>>,
556 coeff_op, apply_op, result->get_cdata().key0);
557 }
558
560 if (fence) {
561 world.gop.fence();
564 true);
565 result->sum_down(true);
567
568 // Restore input tree state so callers can keep using the same Function handles
569 // (e.g., to compare against the old multiply / make_Vphi paths that expect
570 // reconstructed inputs).
571 for (auto& t : terms) detail::make_term_inputs_reconstructed(t);
572 world.gop.fence();
573 }
574}
575
576/// compute <bra | Σ_t product_t> adaptively.
577///
578/// `bra` may be on-demand (e.g. an ERI functor) — the operator evaluates its coeffs
579/// per box via fcube — or a regular tree-resident NDIM function, in which case the
580/// bra is brought into `redundant` state and a CoeffTracker walks it. Returns the
581/// accumulated scalar.
582template <typename T, std::size_t NDIM, typename leaf_opT>
584 const FunctionImpl<T, NDIM>* like,
585 const leaf_opT& leaf_op,
586 std::vector<composite_term<T, NDIM>>& terms,
587 Function<T, NDIM>& bra_function,
588 double target_precision,
589 int oversampling) {
590 constexpr std::size_t LDIM = NDIM / 2;
592
593 MADNESS_CHECK_THROW(bra_function.is_initialized(),
594 "composite_inner: bra Function must be initialised");
595
596 // Per-term `extra` factors *are* allowed in inner mode: the trace becomes the triple
597 // integral ⟨bra | ket·factor1·factor2·extra⟩. `make_term_coeffs` already multiplies
598 // the extra into the term coefficients before the contraction.
599 for (auto& t : terms) {
601 }
602
603 // Decide bra dispatch path
604 const FunctionImpl<T,NDIM>* bra_impl = bra_function.get_impl().get();
605 const bool on_demand_bra = bra_impl->is_on_demand();
606 typename OP::ctT cbra_tracker;
607 if (!on_demand_bra) {
608 bra_function.change_tree_state(redundant, false);
609 cbra_tracker = typename OP::ctT(bra_impl);
610 }
611 world.gop.fence();
612
613 // Build a same-shape accumulator tree (empty initially); the scalar accumulates at key0.
614 FunctionImpl<T, NDIM> acc(*like, like->get_pmap(), false);
615 if (target_precision == 0.0) target_precision = acc.get_thresh() * 0.01;
616
617 std::vector<typename OP::TermImpl> impl_terms;
618 impl_terms.reserve(terms.size());
619 for (const auto& t : terms) {
620 impl_terms.push_back(detail::make_term_impl<T, NDIM, OP>(t));
621 }
622
623 OP coeff_op(&acc, leaf_op, impl_terms, OP::Mode::inner,
624 /*bra ptr*/ on_demand_bra ? bra_impl : nullptr,
625 target_precision, oversampling,
626 cbra_tracker);
627 noop<T, NDIM> apply_op;
628
629 if (world.rank() == acc.get_coeffs().owner(acc.get_cdata().key0)) {
630 acc.task(world.rank(),
631 &FunctionImpl<T, NDIM>::template forward_traverse<OP, noop<T, NDIM>>,
632 coeff_op, apply_op, acc.get_cdata().key0);
633 }
634 world.gop.fence();
635
636 // consolidate the buffer at key0 and read the scalar
639
640 T scalar = T(0);
641 if (world.rank() == acc.get_coeffs().owner(acc.get_cdata().key0)) {
643 if (acc.get_coeffs().find(a, acc.get_cdata().key0))
644 scalar = a->second.coeff().full_tensor_copy().flat()(0L);
645 }
646 world.gop.sum(scalar);
647
648 // Restore input tree state for the caller.
649 for (auto& t : terms) detail::make_term_inputs_reconstructed(t);
650 if (!on_demand_bra) bra_function.change_tree_state(reconstructed, false);
651 world.gop.fence();
652
653 return scalar;
654}
655
656} // namespace madness
657
658#endif // MADNESS_MRA_COMPOSITE_PRODUCT_OP_H__
a class to track where relevant (parent) coeffs are
Definition funcimpl.h:887
const implT * get_impl() const
const reference to impl
Definition funcimpl.h:931
const coeffT & coeff() const
const reference to the coeffs
Definition funcimpl.h:934
CoeffTracker make_child(const keyT &child) const
make a child of this, ignoring the coeffs
Definition funcimpl.h:964
static bool get_refine()
Gets the default adaptive refinement flag.
Definition funcdefaults.h:248
FunctionImpl holds all Function state to facilitate shallow copy semantics.
Definition funcimpl.h:1043
World & world
Definition funcimpl.h:1062
FunctionNode< T, NDIM > nodeT
Type of node.
Definition funcimpl.h:1053
const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > & get_pmap() const
Definition mraimpl.h:209
double get_thresh() const
Definition mraimpl.h:340
void fcube(const keyT &key, const FunctionFunctorInterface< T, NDIM > &f, const Tensor< double > &qx, tensorT &fval) const
Evaluate function at quadrature points in the specified box.
Definition mraimpl.h:2605
void set_tree_state(const TreeState &state)
Definition funcimpl.h:1539
void flo_unary_op_node_inplace(const opT &op, bool fence)
Definition funcimpl.h:2416
GenTensor< Q > values2coeffs(const keyT &key, const GenTensor< Q > &values) const
Definition funcimpl.h:2130
GenTensor< T > coeffT
Type of tensor used to hold coeffs.
Definition funcimpl.h:1054
const dcT & get_coeffs() const
Definition mraimpl.h:355
const FunctionCommonData< T, NDIM > & get_cdata() const
Definition mraimpl.h:361
void sum_down(bool fence)
After 1d push operator must sum coeffs down the tree to restore correct scaling function coefficients...
Definition mraimpl.h:1017
std::shared_ptr< FunctionFunctorInterface< T, NDIM > > get_functor()
Definition mraimpl.h:312
bool is_on_demand() const
Definition mraimpl.h:285
TensorArgs get_tensor_args() const
Definition mraimpl.h:334
A multiresolution adaptive numerical function.
Definition mra.h:141
const Function< T, NDIM > & change_tree_state(const TreeState finalstate, bool fence=true) const
changes tree state to given state
Definition mra.h:960
const std::shared_ptr< FunctionImpl< T, NDIM > > & get_impl() const
Returns a shared-pointer to the implementation.
Definition mra.h:730
bool is_initialized() const
Returns true if the function is initialized.
Definition mra.h:169
A future is a possibly yet unevaluated value.
Definition future.h:370
const Tensor< T > & full_tensor() const
Definition gentensor.h:200
Iterates in lexical order thru all children of a key.
Definition key.h:624
Key is the index for a node of the 2^NDIM-tree.
Definition key.h:71
Level level() const
Definition key.h:170
void break_apart(Key< LDIM > &key1, Key< KDIM > &key2) const
break key into two low-dimensional keys
Definition key.h:500
A tensor is a multidimensional array.
Definition tensor.h:313
Tensor< T > flat()
Returns new view/tensor rehshaping to flat (1-d) tensor.
Definition tensor.h:1560
bool find(accessor &acc, const keyT &key)
Write access to LOCAL value by key. Returns true if found, false otherwise (always false for remote).
Definition worlddc.h:1469
ProcessID owner(const keyT &key) const
Returns processor that logically owns key (no communication).
Definition worlddc.h:1516
implT::const_accessor const_accessor
Definition worlddc.h:1312
void fence(bool debug=false)
Synchronizes all processes in communicator AND globally ensures no pending AM or tasks.
Definition worldgop.cc:177
void sum(T *buf, size_t nelem)
Inplace global sum while still processing AM & tasks.
Definition worldgop.h:890
detail::task_result_type< memfnT >::futureT task(ProcessID dest, memfnT memfn, const TaskAttributes &attr=TaskAttributes()) const
Sends task to derived class method returnT (this->*memfn)().
Definition world_object.h:1132
A parallel world class.
Definition world.h:134
ProcessID rank() const
Returns the process rank in this World (same as MPI_Comm_rank()).
Definition world.h:344
WorldGopInterface & gop
Global operations.
Definition world.h:216
char * p(char *buf, const char *name, int k, int initial_level, double thresh, int order)
Definition derivatives.cc:72
Provides FunctionCommonData, FunctionImpl and FunctionFactory.
static double function(const coord_3d &r)
Normalized gaussian.
Definition functionio.cc:100
static const double v
Definition hatom_sf_dirac.cc:20
#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
Definition potentialmanager.cc:41
MemFuncWrapper< objT *, memfnT, typename result_of< memfnT >::type > wrap_mem_fn(objT &obj, memfnT memfn)
Create a member function wrapper (MemFuncWrapper) from an object and a member function pointer.
Definition mem_func_wrapper.h:251
void make_term_inputs_reconstructed(composite_term< T, NDIM > &term)
Restore every input of a composite_term to reconstructed state.
Definition composite_product_op.h:490
void make_term_inputs_redundant(composite_term< T, NDIM > &term)
Bring every input of a composite_term into redundant state.
Definition composite_product_op.h:479
OP::TermImpl make_term_impl(const composite_term< T, NDIM > &term)
Translate a composite_term<T,NDIM> into the op's CoeffTracker-based TermImpl.
Definition composite_product_op.h:500
Namespace for all elements and tools of MADNESS.
Definition DFConvergence.h:9
int FunctionDefaults< NDIM >::k
Definition mraimpl.h:3878
@ 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
@ redundant
s coeffs everywhere
Definition funcdefaults.h:66
T composite_inner_apply(World &world, const FunctionImpl< T, NDIM > *like, const leaf_opT &leaf_op, std::vector< composite_term< T, NDIM > > &terms, Function< T, NDIM > &bra_function, double target_precision, int oversampling)
Definition composite_product_op.h:583
Function< T, NDIM > sum(World &world, const std::vector< Function< T, NDIM > > &f, bool fence=true)
Returns new function — q = sum_i f[i].
Definition vmra.h:1774
std::enable_if< std::is_base_of< ProjectorBase, projT >::value, OuterProjector< projT, projQ > >::type outer(const projT &p0, const projQ &p1)
Definition projector.h:476
@ TT_FULL
Definition gentensor.h:120
void error(const char *msg)
Definition world.cc:147
void composite_product_apply(FunctionImpl< T, NDIM > *result, const leaf_opT &leaf_op, std::vector< composite_term< T, NDIM > > &terms, double target_precision, int oversampling, bool fence)
Definition composite_product_op.h:528
Function< T, CCPairFunction< T, NDIM >::LDIM > inner(const CCPairFunction< T, NDIM > &c, const Function< T, CCPairFunction< T, NDIM >::LDIM > &f, const std::tuple< int, int, int > v1, const std::tuple< int, int, int > v2)
Definition ccpairfunction.h:1007
Function< T, NDIM > copy(const Function< T, NDIM > &f, const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > &pmap, bool fence=true)
Create a new copy of the function with different distribution and optional fence.
Definition mra.h:2239
static long abs(long a)
Definition tensor.h:214
static const double d
Definition nonlinschro.cc:121
static const double a
Definition nonlinschro.cc:118
static const double c
Definition relops.cc:10
static const double L
Definition rk.cc:46
TensorArgs holds the arguments for creating a LowRankTensor.
Definition gentensor.h:134
internal serialisable view of a term (CoeffTracker-based)
Definition composite_product_op.h:72
std::vector< ctL > iap2
sum-of-hartree-products ket if iaket is null
Definition composite_product_op.h:74
ctT iaket
NDIM ket (optional, exclusive with iap*).
Definition composite_product_op.h:73
ctL ifactor1
Definition composite_product_op.h:75
bool have_extra() const
Definition composite_product_op.h:82
double coeff
scalar prefactor (DSL)
Definition composite_product_op.h:77
std::vector< ctL > iap1
Definition composite_product_op.h:74
const implT * extra
NDIM extra factor (function mode only).
Definition composite_product_op.h:76
bool have_ket() const
Definition composite_product_op.h:79
bool have_factor1() const
Definition composite_product_op.h:80
void serialize(Archive &ar)
Definition composite_product_op.h:84
ctL ifactor2
single per-particle LDIM factor (each optional)
Definition composite_product_op.h:75
bool have_factor2() const
Definition composite_product_op.h:81
Adaptive sum-of-products operator over a vector of composite_terms.
Definition composite_product_op.h:55
int oversampling
Definition composite_product_op.h:99
void serialize(Archive &ar)
Definition composite_product_op.h:465
Mode mode
Definition composite_product_op.h:94
this_type make_child(const keyT &child) const
Definition composite_product_op.h:364
composite_product_op_NS(implT *result, const opT &leaf_op, const std::vector< TermImpl > &terms, Mode mode, const implT *bra, double target_precision, int oversampling, const ctT &cbra=ctT{})
Definition composite_product_op.h:105
typename implT::nodeT nodeT
Definition composite_product_op.h:65
opT leaf_op
Definition composite_product_op.h:92
coeffT make_term_ket(const TermImpl &t, const keyT &key, const keyL &k1, const keyL &k2) const
Definition composite_product_op.h:202
CoeffTracker< T, LDIM > ctL
Definition composite_product_op.h:69
ctT cbra
Definition composite_product_op.h:96
composite_product_op_NS()
Definition composite_product_op.h:103
static tensorT eri_coeffs(const implT *eri, const keyT &key)
fetch coeffs of an on-demand NDIM function at key
Definition composite_product_op.h:189
static std::pair< double, double > sd_norm_extra(const implT *extra, const keyT &key)
Definition composite_product_op.h:138
std::pair< coeffT, double > make_term_coeffs(const TermImpl &t, const keyT &key) const
Definition composite_product_op.h:218
CoeffTracker< T, NDIM > ctT
Definition composite_product_op.h:68
FunctionImpl< T, LDIM > implL
Definition composite_product_op.h:62
bool have_explicit_bra() const
Definition composite_product_op.h:101
std::pair< bool, coeffT > operator()(const keyT &key) const
Definition composite_product_op.h:311
Mode
Definition composite_product_op.h:58
@ function
Definition composite_product_op.h:58
@ inner
Definition composite_product_op.h:58
typename implT::coeffT coeffT
Definition composite_product_op.h:66
Tensor< T > tensorT
Definition composite_product_op.h:67
std::pair< double, double > sd_norm_pair_sum(const std::vector< ctL > &iap1, const std::vector< ctL > &iap2, const keyL &k1, const keyL &k2) const
Definition composite_product_op.h:158
composite_product_op_NS< T, NDIM, opT, LDIM > this_type
Definition composite_product_op.h:60
double target_precision
Definition composite_product_op.h:98
this_type forward_ctor(implT *result1, const opT &leaf_op1, const std::vector< TermImpl > &orig_terms, Mode mode1, const implT *bra1, double target_precision1, int oversampling1, const std::vector< Future< ctT > > &t_futs, const std::vector< Future< ctL > > &l_futs)
Definition composite_product_op.h:429
const implT * bra
inner-mode on-demand bra (e.g., ERI)
Definition composite_product_op.h:95
implT * result
output tree (function mode) or scalar sink (inner)
Definition composite_product_op.h:91
static double cross_term_error(const std::vector< std::pair< double, double > > &sd)
Definition composite_product_op.h:149
Key< NDIM > keyT
Definition composite_product_op.h:63
std::pair< coeffT, double > make_sum_coeffs(const keyT &key) const
Definition composite_product_op.h:255
void leaf_action(const keyT &key, const coeffT &coeff) const
Definition composite_product_op.h:299
Future< this_type > activate() const
Definition composite_product_op.h:388
std::vector< TermImpl > terms
Definition composite_product_op.h:93
bool randomize() const
Definition composite_product_op.h:89
Key< LDIM > keyL
Definition composite_product_op.h:64
static std::pair< double, double > sd_norm(const CT &ct, const KEY &key)
retrieve scaling- and detail-norm at the given key from a CoeffTracker
Definition composite_product_op.h:130
FunctionImpl< T, NDIM > implT
Definition composite_product_op.h:61
void accumulate_into_result(const keyT &key, const coeffT &coeff) const
Definition composite_product_op.h:294
double term_refinement_error(const TermImpl &t, const keyT &key, const keyL &k1, const keyL &k2) const
full inaccurate-refinement error for one term, scaled by the term's coeff.
Definition composite_product_op.h:173
std::pair< bool, coeffT > continue_recursion(const std::vector< bool > child_is_leaf, const tensorT &coeffs, const keyT &key) const
Definition composite_product_op.h:342
Definition composite_product_op.h:32
Function< T, LDIM > FL
Definition composite_product_op.h:37
std::vector< FL > p1
parallel vectors forming the ket as a sum of hartree products (p1.size() == p2.size())
Definition composite_product_op.h:42
double coeff
Definition composite_product_op.h:50
Function< T, NDIM > FN
Definition composite_product_op.h:36
FL factor1
single LDIM factor per particle (uninitialized = skip)
Definition composite_product_op.h:44
FN ket
NDIM ket factor. If uninitialized, the term ket is Σ_k p1[k] ⊗ p2[k].
Definition composite_product_op.h:40
static constexpr std::size_t LDIM
Definition composite_product_op.h:33
FL factor2
Definition composite_product_op.h:44
std::vector< FL > p2
Definition composite_product_op.h:42
FN extra
Definition composite_product_op.h:47
insert/replaces the coefficients into the function
Definition funcimpl.h:788
Definition mra.h:109
Definition funcimpl.h:776
int np
Definition tdse1d.cc:165
void e()
Definition test_sig.cc:75
#define N
Definition testconv.cc:37
constexpr std::size_t NDIM
Definition testgconv.cc:54
double k2
Definition testperiodic.cc:68
double k1
Definition testperiodic.cc:67
int ProcessID
Used to clearly identify process number/rank.
Definition worldtypes.h:43