• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

adc-connect / adcc / 4899696005

pending completion
4899696005

push

github

Maximilian Scheurer
Bump version: 0.15.15 → 0.15.16

1488 of 2311 branches covered (64.39%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

7144 of 9343 relevant lines covered (76.46%)

291077.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.26
/libadcc/TensorImpl/ExpressionTree.cc
1
//
2
// Copyright (C) 2020 by the adcc authors
3
//
4
// This file is part of adcc.
5
//
6
// adcc is free software: you can redistribute it and/or modify
7
// it under the terms of the GNU General Public License as published
8
// by the Free Software Foundation, either version 3 of the License, or
9
// (at your option) any later version.
10
//
11
// adcc 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 adcc. If not, see <http://www.gnu.org/licenses/>.
18
//
19

20
// This needs to be the first include ... libtensor reasons
21
#pragma GCC visibility push(default)
22
#include <libtensor/expr/eval/eval_exception.h>
23
#pragma GCC visibility pop
24
//
25
#include "../exceptions.hh"
26
#include "ExpressionTree.hh"
27

28
// Change visibility of libtensor singletons to public
29
#pragma GCC visibility push(default)
30
#include <libtensor/block_tensor/btod_add.h>
31
#include <libtensor/block_tensor/btod_set.h>
32
#include <libtensor/expr/btensor/eval_btensor_double.h>
33
#include <libtensor/expr/btensor/impl/eval_tree_builder_btensor.h>
34
#include <libtensor/expr/btensor/impl/tensor_from_node.h>
35
#include <libtensor/expr/dag/node_add.h>
36
#include <libtensor/expr/dag/node_assign.h>
37
#include <libtensor/expr/dag/node_ident.h>
38
#include <libtensor/expr/dag/node_transform.h>
39
#include <libtensor/expr/iface/node_ident_any_tensor.h>
40
#include <libtensor/expr/opt/opt_add_before_transf.h>
41
#include <libtensor/expr/opt/opt_merge_adjacent_add.h>
42
#include <libtensor/expr/opt/opt_merge_adjacent_transf.h>
43
#include <libtensor/expr/opt/opt_merge_equiv_ident.h>
44
#include <libtensor/linalg/BlasSequential.h>
45
#include <libtensor/symmetry/so_copy.h>
46
#pragma GCC visibility pop
47

48
namespace libadcc {
49
namespace lt = libtensor;
50

51
template <size_t N>
52
lt::expr::expr_rhs<N, scalar_type> ExpressionTree::attach_letters(
3,098,839✔
53
      const std::vector<std::shared_ptr<const lt::letter>>& letters) const {
54

55
  if (N != permutation.size()) {
3,098,839✔
56
    throw runtime_error("Internal error: Mismatch between permutation.size() == " +
×
57
                        std::to_string(permutation.size()) +
58
                        " and expr_rhs dimensionality " + std::to_string(N) + ".");
59
  }
60
  if (letters.size() != permutation.size()) {
3,098,839✔
61
    throw runtime_error("Internal error: Mismatch between permutation.size() == " +
×
62
                        std::to_string(permutation.size()) + " and letters size " +
63
                        std::to_string(letters.size()) + ".");
64
  }
65

66
  // The permutation is to be applied *after* the expression is evaluated,
67
  // so these letters need to pick up the inverse permutation.
68
  std::vector<const lt::letter*> let_ptrs;
3,098,839✔
69
  for (size_t i = 0; i < N; ++i) {
12,379,140✔
70
    auto it = std::find(permutation.begin(), permutation.end(), i);
9,280,301✔
71
    if (it == permutation.end()) {
9,280,301✔
72
      throw runtime_error("Internal error: Could not build inverse permutation");
×
73
    }
74
    const size_t inv_i = static_cast<size_t>(it - permutation.begin());
9,280,301✔
75

76
    let_ptrs.push_back(letters[inv_i].get());
9,280,301✔
77
  }
3,077,509✔
78

79
  // This copies both the expression as well as the letters to internal storage.
80
  return lt::expr::expr_rhs<N, scalar_type>(*tree_ptr, let_ptrs);
5,169,996✔
81
}
3,098,839✔
82

83
namespace {
84
lt::expr::expr_tree optimise_tree(const lt::expr::expr_tree& in) {
299,404✔
85
  lt::expr::expr_tree ret(in);
299,404✔
86
  lt::expr::opt_merge_equiv_ident(ret);
299,404✔
87
  lt::expr::opt_merge_adjacent_transf(ret);
299,404✔
88
  lt::expr::opt_add_before_transf(ret);
299,404✔
89
  lt::expr::opt_merge_adjacent_transf(ret);
299,404✔
90
  lt::expr::opt_merge_adjacent_add(ret);
299,404✔
91
  return ret;
299,404✔
92
}
99,135✔
93

94
bool is_linear_combination(const lt::expr::expr_tree& assigntree) {
318,969✔
95
  using namespace libtensor::expr;
96

97
  auto is_valid_summand = [&assigntree](size_t id) {
1,672,694✔
98
    if (assigntree.get_vertex(id).check_type<node_ident>()) return true;
854,485✔
99

100
    if (assigntree.get_vertex(id).check_type<node_transform_base>()) {
349,843✔
101
      const node_transform<scalar_type>& nt =
79,455✔
102
            assigntree.get_vertex(id).template recast_as<node_transform<scalar_type>>();
243,472✔
103
      for (size_t i = 0; i < nt.get_perm().size(); ++i) {
721,099✔
104
        if (i != nt.get_perm()[i]) return false;  // Need identity permutation
566,515✔
105
      }
154,429✔
106

107
      // Only one subnode for transformation.
108
      if (assigntree.get_edges_out(id).size() != 1) return false;
154,584✔
109
      const size_t tid = assigntree.get_edges_out(id)[0];
154,584✔
110

111
      if (assigntree.get_vertex(tid).check_type<node_ident>()) {
154,584✔
112
        return true;
121,820✔
113
      }
114
    }
10,861✔
115
    return false;
139,135✔
116
  };
493,462✔
117

118
  bool found_add = false;
318,969✔
119
  for (auto it = assigntree.begin(); it != assigntree.end(); ++it) {
1,214,509✔
120
    if (assigntree.get_vertex(it).check_type<node_add>()) {
1,123,563✔
121
      if (found_add) return false;  // There should be exactly one add
170,816✔
122
      auto add_edges = assigntree.get_edges_out(it);
118,068✔
123
      for (auto aed = add_edges.begin(); aed != add_edges.end(); ++aed) {
207,089✔
124
        if (!is_valid_summand(*aed)) return false;
167,959✔
125
      }
28,187✔
126
      found_add = true;
39,130✔
127
    } else if (assigntree.get_vertex(it).check_type<node_assign>()) {
1,123,563✔
128
      if (assigntree.get_root() != assigntree.get_id(it)) return false;
318,969✔
129
    } else if (!is_valid_summand(assigntree.get_id(it))) {
791,816✔
130
      return false;
149,085✔
131
    }
132
  }
293,462✔
133

134
  return found_add;
90,946✔
135
}
105,290✔
136

137
template <size_t N>
138
void evaluate_linear_combination(const lt::expr::expr_tree& assigntree,
19,565✔
139
                                 lt::btensor<N, scalar_type>& result, bool add) {
140
  using namespace libtensor::expr;
141
  if (!is_linear_combination(assigntree)) {
19,565✔
142
    throw invalid_argument(
×
143
          "evaluate_linear_combination got a tree which is not a linear combination "
144
          "tree");
145
  }
146

147
  lt::btensor<N, scalar_type>* first_btensor = nullptr;
19,565✔
148
  std::unique_ptr<lt::btod_add<N>> operator_ptr;
19,565✔
149
  auto add_summand = [&assigntree, &operator_ptr, &first_btensor](size_t id) {
60,084✔
150
    eval_btensor_double::btensor_from_node<N, scalar_type> extract(assigntree, id);
40,519✔
151
    auto& btensor = dynamic_cast<lt::btensor<N, scalar_type>&>(extract.get_btensor());
40,519✔
152
    // Notice: No permutation allowed here
153
    const lt::permutation<N>& perm = extract.get_transf().get_perm();
40,519✔
154
    if (!perm.is_identity()) {
40,519✔
155
      throw runtime_error("Internal error: Caught non-identity permutation.");
×
156
    }
157

158
    const double c = extract.get_transf().get_scalar_tr().get_coeff();
40,519✔
159
    if (!operator_ptr) {
40,519✔
160
      // Operator not yet set up -> do so with first summand
161
      operator_ptr.reset(new lt::btod_add<N>(btensor, c));
19,565✔
162
      first_btensor = &btensor;
19,565✔
163
    } else {
6,155✔
164
      // Operator is set up -> just add summand
165
      operator_ptr->add_op(btensor, c);
20,954✔
166
    }
167
  };
12,773✔
168

169
  auto root_edges = assigntree.get_edges_out(assigntree.get_root());
19,565✔
170
  for (auto it = root_edges.begin(); it != root_edges.end(); ++it) {
39,130✔
171
    if (assigntree.get_vertex(*it).check_type<node_add>()) {
39,130✔
172
      auto add_edges = assigntree.get_edges_out(*it);
19,565✔
173
      for (auto aed = add_edges.begin(); aed != add_edges.end(); ++aed) {
60,084✔
174
        add_summand(*aed);
40,519✔
175
      }
12,773✔
176
      break;  // Just one add tree
13,410✔
177
    }
19,565✔
178
  }
6,155✔
179

180
  // Copy symmmetry over
181
  if (first_btensor == nullptr) {
19,565✔
182
    throw runtime_error("Internal error: Got nullptr where set pointer was expected.");
×
183
  }
184
  lt::block_tensor_ctrl<N, scalar_type> ctrl_to(result);
19,565✔
185
  lt::block_tensor_ctrl<N, scalar_type> ctrl_from(*first_btensor);
19,565✔
186
  lt::so_copy<N, scalar_type>(ctrl_from.req_const_symmetry())
19,565✔
187
        .perform(ctrl_to.req_symmetry());
19,565✔
188

189
  if (!add) lt::btod_set<N>(0.0).perform(result);
19,565✔
190
  operator_ptr->perform(result, 1.0);
19,565✔
191
}
19,565✔
192

193
/** Return the expression tree resulting from assigning tree to
194
 *  the passed result. Optionally the assignment adds elements instead of setting them.
195
 */
196
template <size_t N>
197
libtensor::expr::expr_tree assignment_tree(const ExpressionTree& tree,
299,404✔
198
                                           libtensor::btensor<N, scalar_type>& result,
199
                                           bool add) {
200
  using namespace libtensor::expr;
201
  if (add) throw not_implemented_error("add = true not tested so far");
299,404✔
202

203
  // Form the RHS of the assignment
204
  std::vector<std::shared_ptr<const lt::letter>> label_safe;
299,404✔
205
  for (size_t i = 0; i < N; ++i) {
1,123,182✔
206
    label_safe.push_back(std::make_shared<lt::letter>());
823,778✔
207
  }
272,542✔
208
  expr_rhs<N, scalar_type> rhs = tree.attach_letters<N>(label_safe);
299,404✔
209

210
  // Form the labels for libtensor
211
  std::vector<const lt::letter*> label_unsafe;
299,404✔
212
  for (size_t i = 0; i < N; ++i) {
1,123,182✔
213
    label_unsafe.push_back(label_safe[i].get());
823,778✔
214
  }
272,542✔
215
  label<N> label(label_unsafe);
299,404✔
216

217
  // Build the assignment tree, starting from the assignment node
218
  node_assign nassign(N, add);
299,404✔
219
  expr_tree assigntree(nassign);
299,404✔
220
  expr_tree::node_id_t id = assigntree.get_root();
299,404✔
221
  node_ident_any_tensor<N, scalar_type> nresult(result);  // LHS of assignment
299,404✔
222
  assigntree.add(id, nresult);
299,404✔
223

224
  // Check if permutations are needed
225
  lt::permutation<N> px = label.permutation_of(rhs.get_label());
299,404✔
226
  if (!px.is_identity()) {
299,404✔
227
    std::vector<size_t> perm(N);
82,960✔
228
    for (size_t i = 0; i < N; i++) perm[i] = px[i];
310,159✔
229

230
    node_transform<scalar_type> n3(perm, lt::scalar_transf<scalar_type>());
82,960✔
231
    id = assigntree.add(id, n3);
82,960✔
232
  }
82,960✔
233

234
  // Add permuted or unpermuted RHS
235
  assigntree.add(id, rhs.get_expr());
299,404✔
236

237
  return assigntree;
499,673✔
238
}
299,404✔
239
}  // namespace
240

241
/** Return an optimised form of the internal expression tree */
242
lt::expr::expr_tree ExpressionTree::optimised_tree() { return optimise_tree(*tree_ptr); }
×
243

244
template <size_t N>
245
libtensor::expr::expr_tree ExpressionTree::evaluation_tree(
×
246
      libtensor::btensor<N, scalar_type>& result, bool add) const {
247
  // This does exactly the same stuff as
248
  // lt::expr::eval_btensor<double>().evaluate(assigntree) except doing the evaluation.
249
  lt::expr::expr_tree assigntree = assignment_tree(*this, result, add);
×
250
  lt::expr::eval_tree_builder_btensor bld(assigntree);
×
251
  bld.build();
×
252
  return bld.get_tree();
×
253
}
×
254

255
template <size_t N>
256
void ExpressionTree::evaluate_to(lt::btensor<N, scalar_type>& result, bool add) const {
299,404✔
257
  lt::expr::expr_tree assigntree = optimise_tree(assignment_tree(*this, result, add));
299,404✔
258

259
  lt::BlasSequential seq;  // Switch to sequential BLAS for evaluations
299,404✔
260

261
  if (is_linear_combination(assigntree)) {
299,404✔
262
    evaluate_linear_combination(assigntree, result, add);
19,565✔
263
    return;
19,565✔
264
  }
265

266
  // Normal evaluation via libtensor
267
  lt::expr::eval_btensor<double>().evaluate(assigntree);
279,839✔
268
}
312,814✔
269

270
//
271
// Explicit instantiation
272
//
273

274
#define INSTANTIATE(DIM)                                               \
275
  template void ExpressionTree::evaluate_to(                           \
276
        libtensor::btensor<DIM, scalar_type>& result, bool add) const; \
277
                                                                       \
278
  template libtensor::expr::expr_tree ExpressionTree::evaluation_tree( \
279
        libtensor::btensor<DIM, scalar_type>& result, bool add) const;
280

281
INSTANTIATE(1)
282
INSTANTIATE(2)
283
INSTANTIATE(3)
284
INSTANTIATE(4)
285

286
#undef INSTANTIATE
287

288
}  // namespace libadcc
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc