TiledArray  0.7.0
cont_engine.h
Go to the documentation of this file.
1 /*
2  * This file is a part of TiledArray.
3  * Copyright (C) 2014 Virginia Tech
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * Justus Calvin
19  * Department of Chemistry, Virginia Tech
20  *
21  * cont_engine.h
22  * Mar 31, 2014
23  *
24  */
25 
26 #ifndef TILEDARRAY_EXPRESSIONS_CONT_ENGINE_H__INCLUDED
27 #define TILEDARRAY_EXPRESSIONS_CONT_ENGINE_H__INCLUDED
28 
32 #include <TiledArray/proc_grid.h>
33 
34 namespace TiledArray {
35  namespace expressions {
36 
37  // Forward declarations
38  template <typename, typename> class MultExpr;
39  template <typename, typename, typename> class ScalMultExpr;
40 
42 
44  template <typename Derived>
45  class ContEngine : public BinaryEngine<Derived> {
46  public:
47  // Class hierarchy typedefs
50  typedef ExprEngine<Derived>
52 
53  // Argument typedefs
54  typedef typename EngineTrait<Derived>::left_type
56  typedef typename EngineTrait<Derived>::right_type
58 
59  // Operational typedefs
60  typedef typename EngineTrait<Derived>::value_type
62  typedef typename EngineTrait<Derived>::scalar_type
68  typedef typename EngineTrait<Derived>::policy
72 
73  // Meta data typedefs
75  typedef typename EngineTrait<Derived>::trange_type
77  typedef typename EngineTrait<Derived>::shape_type
81 
82  protected:
83 
84 
85  // Import base class variables to this scope
86  using ExprEngine_::world_;
87  using ExprEngine_::vars_;
88  using ExprEngine_::perm_;
90  using ExprEngine_::shape_;
91  using ExprEngine_::pmap_;
95 
96  private:
97 
98  typedef enum {
99  no_trans = 1,
100  trans = 2,
101  permute_to_no_trans = 3,
102  } TensorOp;
103 
104  protected:
105 
107 
108  private:
109 
110  VariableList left_vars_;
111  VariableList right_vars_;
112  TensorOp left_op_;
113  TensorOp right_op_;
114  op_type op_;
115  TiledArray::detail::ProcGrid proc_grid_;
116  size_type K_;
117 
118 
119  static unsigned int
120  find(const VariableList& vars, std::string var, unsigned int i, const unsigned int n) {
121  for(; i < n; ++i) {
122  if(vars[i] == var)
123  break;
124  }
125 
126  return i;
127  }
128 
129  public:
130 
132 
136  template <typename L, typename R>
137  ContEngine(const MultExpr<L, R>& expr) :
138  BinaryEngine_(expr), factor_(1), left_vars_(), right_vars_(),
139  left_op_(permute_to_no_trans), right_op_(permute_to_no_trans), op_(),
140  proc_grid_(), K_(1u)
141  { }
142 
144 
149  template <typename L, typename R, typename S>
151  BinaryEngine_(expr), factor_(expr.factor()), left_vars_(), right_vars_(),
152  left_op_(permute_to_no_trans), right_op_(permute_to_no_trans), op_(),
153  proc_grid_(), K_(1u)
154  { }
155 
156  // Pull base class functions into this class.
157  using ExprEngine_::derived;
158  using ExprEngine_::vars;
159 
161 
167  void perm_vars(const VariableList& target_vars) {
168  // Only permute if the arguments can be permuted
169  if((left_op_ == permute_to_no_trans) || (right_op_ == permute_to_no_trans)) {
170 
171  // Compute ranks
172  const unsigned int result_rank = target_vars.dim();
173  const unsigned int inner_rank = (left_.vars().dim() +
174  right_.vars().dim() - result_rank) >> 1;
175  const unsigned int left_outer_rank = left_.vars().dim() - inner_rank;
176 
177  // Check that the left- and right-hand outer variables are correctly
178  // partitioned in the target variable list.
179  bool target_partitioned = true;
180  for(unsigned int i = 0u; i < left_outer_rank; ++i)
181  target_partitioned = target_partitioned &&
182  (find(target_vars, left_vars_[i], 0u, left_outer_rank) < left_outer_rank);
183 
184  // If target is properly partitioned, then arguments can be permuted
185  // to fit the target.
186  if(target_partitioned) {
187 
188  if(left_op_ == permute_to_no_trans) {
189  // Copy left-hand target variables to left and result variable lists.
190  for(unsigned int i = 0u; i < left_outer_rank; ++i) {
191  const std::string& var = target_vars[i];
192  const_cast<std::string&>(left_vars_[i]) = var;
193  const_cast<std::string&>(vars_[i]) = var;
194  }
195 
196  // Permute the left argument with the new variable list.
197  left_.perm_vars(left_vars_);
198  } else {
199  // Copy left-hand outer variables to that of result.
200  for(unsigned int i = 0u; i < left_outer_rank; ++i)
201  const_cast<std::string&>(vars_[i]) = left_vars_[i];
202  }
203 
204  if(right_op_ == permute_to_no_trans) {
205  // Copy right-hand target variables to right and result variable lists.
206  for(unsigned int i = left_outer_rank, j = inner_rank; i < result_rank; ++i, ++j) {
207  const std::string& var = target_vars[i];
208  const_cast<std::string&>(right_vars_[j]) = var;
209  const_cast<std::string&>(vars_[i]) = var;
210  }
211 
212  // Permute the left argument with the new variable list.
213  right_.perm_vars(right_vars_);
214  } else {
215  // Copy right-hand outer variables to that of result.
216  for(unsigned int i = left_outer_rank, j = inner_rank; i < result_rank; ++i, ++j)
217  const_cast<std::string&>(vars_[i]) = right_vars_[j];
218  }
219  }
220  }
221  }
222 
224 
228  void init_vars() {
229  const unsigned int left_rank = left_.vars().dim();
230  const unsigned int right_rank = right_.vars().dim();
231 
232  // Get non-const references to the argument variable lists.
233  std::vector<std::string>& left_vars =
234  const_cast<std::vector<std::string>&>(left_vars_.data());
235  left_vars.reserve(left_rank);
236  std::vector<std::string>& right_vars =
237  const_cast<std::vector<std::string>&>(right_vars_.data());
238  right_vars.reserve(right_rank);
239  std::vector<std::string>& result_vars =
240  const_cast<std::vector<std::string>&>(vars_.data());
241  result_vars.reserve(std::max(left_rank, right_rank));
242 
243  // Extract left-most result and inner variables from the left-hand argument.
244  for(unsigned int i = 0ul; i < left_rank; ++i) {
245  const std::string& var = left_.vars()[i];
246  if(find(right_.vars(), var, 0u, right_rank) == right_rank) {
247  // Store outer left variable
248  left_vars.push_back(var);
249  result_vars.push_back(var);
250  } else {
251  // Store inner left variable
252  right_vars.push_back(var);
253  }
254  }
255 
256  // Compute the inner and outer dimension ranks.
257  const unsigned int inner_rank = right_vars.size();
258  const unsigned int left_outer_rank = left_vars.size();
259  const unsigned int right_outer_rank = right_rank - inner_rank;
260  const unsigned int result_rank = left_outer_rank + right_outer_rank;
261 
262  // Resize result variables if necessary.
263  result_vars.reserve(result_rank);
264 
265  // Check for an outer product
266  if(inner_rank == 0u) {
267 
268  // Extract the right most outer variables from right hand argument.
269  for(unsigned int i = 0ul; i < right_rank; ++i) {
270  const std::string& var = right_.vars()[i];
271  right_vars.push_back(var);
272  result_vars.push_back(var);
273  }
274  return; // Quick exit
275  }
276 
277  // Initialize flags that will be used to determine the type of permutation
278  // that will be applied to the arguments (i.e. no permutation, transpose,
279  // or arbitrary permutation).
280  bool inner_vars_ordered = true, left_is_no_trans = true, left_is_trans = true,
281  right_is_no_trans = true, right_is_trans = true;
282 
283  // If the inner variable lists of the arguments are not in the same
284  // order, one of them will need to be permuted. Here, we determine which
285  // argument, left or right, will be permuted if a permutation is
286  // required. The argument with the lowest rank is preferred since it is
287  // likely to have the smaller memory footprint, or the fewest leaves to
288  // minimize the number of permutations in the expression.
289  const bool perm_left = (left_rank < right_rank) || ((left_rank == right_rank)
290  && (left_type::leaves <= right_type::leaves));
291 
292  // Extract variables from the right-hand argument, collect information
293  // about the layout of the variable lists, and ensure the inner variable
294  // lists are in the same order.
295  for(unsigned int i = 0ul; i < right_rank; ++i) {
296  const std::string& var = right_.vars()[i];
297  const unsigned int j = find(left_.vars(), var, 0u, left_rank);
298  if(j == left_rank) {
299  // Store outer right variable
300  right_vars.push_back(var);
301  result_vars.push_back(var);
302  } else {
303  const unsigned int x = left_vars.size() - left_outer_rank;
304 
305  // Collect information about the relative position of variables
306  inner_vars_ordered = inner_vars_ordered && (right_vars[x] == var);
307  left_is_no_trans = left_is_no_trans && (j >= left_outer_rank);
308  left_is_trans = left_is_trans && (j < inner_rank);
309  right_is_no_trans = right_is_no_trans && (i < inner_rank);
310  right_is_trans = right_is_trans && (i >= right_outer_rank);
311 
312  // Store inner right variable
313  if(inner_vars_ordered) {
314  // Left and right inner variable list order is equal.
315  left_vars.push_back(var);
316  } else if(perm_left) {
317  // Permute left so we need to store inner variables according to
318  // the order of the right-hand argument.
319  left_vars.push_back(var);
320  right_vars[x] = var;
321  left_is_no_trans = left_is_trans = false;
322  } else {
323  // Permute right so we need to store inner variables according to
324  // the order of the left-hand argument.
325  left_vars.push_back(right_vars[x]);
326  right_is_no_trans = right_is_trans = false;
327  }
328  }
329  }
330 
331  // Here we set the type of permutation that will be applied to the
332  // argument tensors. If an argument is in matrix form, permutation of
333  // the tiles is disabled.
334  if(left_is_no_trans) {
335  left_op_ = no_trans;
336  left_.permute_tiles(false);
337  } else if(left_is_trans) {
338  left_op_ = trans;
339  left_.permute_tiles(false);
340  } else {
341  left_.perm_vars(left_vars_);
342  }
343  if(right_is_no_trans) {
344  right_op_ = no_trans;
345  right_.permute_tiles(false);
346  } else if(right_is_trans) {
347  right_op_ = trans;
348  right_.permute_tiles(false);
349  } else {
350  right_.perm_vars(right_vars_);
351  }
352 
353  }
354 
356 
360  void init_struct(const VariableList& target_vars) {
361  // Initialize children
362  left_.init_struct(left_vars_);
363  right_.init_struct(right_vars_);
364 
365  // Initialize the tile operation in this function because it is used to
366  // evaluate the tiled range and shape.
367 
368  const madness::cblas::CBLAS_TRANSPOSE left_op =
369  (left_op_ == trans ? madness::cblas::Trans : madness::cblas::NoTrans);
370  const madness::cblas::CBLAS_TRANSPOSE right_op =
371  (right_op_ == trans ? madness::cblas::Trans : madness::cblas::NoTrans);
372 
373 
374  if(target_vars != vars_) {
375  // Initialize permuted structure
376  perm_ = ExprEngine_::make_perm(target_vars);
377  op_ = op_type(left_op, right_op, factor_, vars_.dim(), left_vars_.dim(),
378  right_vars_.dim(), (permute_tiles_ ? perm_ : Permutation()));
381  } else {
382  // Initialize non-permuted structure
383  op_ = op_type(left_op, right_op, factor_, vars_.dim(), left_vars_.dim(),
384  right_vars_.dim());
387  }
388 
391  }
392  }
393 
395 
400  void init_distribution(World* world, std::shared_ptr<pmap_interface> pmap) {
401  const unsigned int inner_rank = op_.gemm_helper().num_contract_ranks();
402  const unsigned int left_rank = op_.gemm_helper().left_rank();
403  const unsigned int right_rank = op_.gemm_helper().right_rank();
404  const unsigned int left_outer_rank = left_rank - inner_rank;
405 
406  // Get pointers to the argument sizes
407  const size_type* MADNESS_RESTRICT const left_tiles_size =
408  left_.trange().tiles_range().extent_data();
409  const size_type* MADNESS_RESTRICT const left_element_size =
410  left_.trange().elements_range().extent_data();
411  const size_type* MADNESS_RESTRICT const right_tiles_size =
412  right_.trange().tiles_range().extent_data();
413  const size_type* MADNESS_RESTRICT const right_element_size =
414  right_.trange().elements_range().extent_data();
415 
416  // Compute the fused sizes of the contraction
417  size_type M = 1ul, m = 1ul, N = 1ul, n = 1ul;
418  unsigned int i = 0u;
419  for(; i < left_outer_rank; ++i) {
420  M *= left_tiles_size[i];
421  m *= left_element_size[i];
422  }
423  for(; i < left_rank; ++i)
424  K_ *= left_tiles_size[i];
425  for(i = inner_rank; i < right_rank; ++i) {
426  N *= right_tiles_size[i];
427  n *= right_element_size[i];
428  }
429 
430  // Construct the process grid.
431  proc_grid_ = TiledArray::detail::ProcGrid(*world, M, N, m, n);
432 
433  // Initialize children
434  left_.init_distribution(world, proc_grid_.make_row_phase_pmap(K_));
435  right_.init_distribution(world, proc_grid_.make_col_phase_pmap(K_));
436 
437  // Initialize the process map in not already defined
438  if(! pmap)
439  pmap = proc_grid_.make_pmap();
441  }
442 
444 
448  // Compute iteration limits
449  const unsigned int left_rank = op_.gemm_helper().left_rank();
450  const unsigned int right_rank = op_.gemm_helper().right_rank();
451  const unsigned int inner_rank = op_.gemm_helper().num_contract_ranks();
452  const unsigned int left_outer_rank = left_rank - inner_rank;
453 
454  // Construct the trange input and compute the gemm sizes
455  typename trange_type::Ranges ranges(op_.gemm_helper().result_rank());
456  unsigned int i = 0ul;
457  for(unsigned int x = 0ul; x < left_outer_rank; ++x, ++i) {
458  const unsigned int pi = (perm ? perm[i] : i);
459  ranges[pi] = left_.trange().data()[x];
460  }
461  for(unsigned int x = inner_rank; x < right_rank; ++x, ++i) {
462  const unsigned int pi = (perm ? perm[i] : i);
463  ranges[pi] = right_.trange().data()[x];
464  }
465 
466 #ifndef NDEBUG
467 
468  // Get left and right tile extents.
469  const auto* MADNESS_RESTRICT const left_extent =
470  left_.trange().tiles_range().extent_data();
471  const auto* MADNESS_RESTRICT const right_extent =
472  right_.trange().tiles_range().extent_data();
473 
474  // Check that the contracted dimensions are have congruent tilings
475  for(unsigned int l = left_outer_rank, r = 0ul; l < left_rank; ++l, ++r) {
476  if(left_.trange().data()[l] != right_.trange().data()[r]) {
477  if(TiledArray::get_default_world().rank() == 0) {
478 
479  if(left_extent[l] == right_extent[r]) {
480  TA_USER_ERROR_MESSAGE( "The tilings of the contracted dimensions " \
481  "of the left- and right-hand arguments are not equal.");
482 
483  } else {
484  TA_USER_ERROR_MESSAGE( "The contracted dimensions of the left- " \
485  "and right-hand arguments are not congruent:" \
486  << "\n left = " << left_.trange() \
487  << "\n right = " << right_.trange() );
488 
489  TA_EXCEPTION("The contracted dimensions of the left- and " \
490  "right-hand expressions are not congruent.");
491  }
492  }
493 
494  TA_EXCEPTION("The contracted dimensions of the left- and "
495  "right-hand expressions are not congruent.");
496  }
497  }
498 #endif // NDEBUG
499 
500  return trange_type(ranges.begin(), ranges.end());
501  }
502 
504 
508  shape_gemm_helper(madness::cblas::NoTrans, madness::cblas::NoTrans,
509  op_.gemm_helper().result_rank(), op_.gemm_helper().left_rank(),
510  op_.gemm_helper().right_rank());
511  return left_.shape().gemm(right_.shape(), factor_, shape_gemm_helper);
512  }
513 
515 
520  shape_gemm_helper(madness::cblas::NoTrans, madness::cblas::NoTrans,
521  op_.gemm_helper().result_rank(), op_.gemm_helper().left_rank(),
522  op_.gemm_helper().right_rank());
523  return left_.shape().gemm(right_.shape(), factor_, shape_gemm_helper,
524  perm);
525  }
526 
528  // Define the impl type
529  typedef TiledArray::detail::Summa<typename left_type::dist_eval_type,
530  typename right_type::dist_eval_type, op_type, typename Derived::policy> impl_type;
531 
532  typename left_type::dist_eval_type left = left_.make_dist_eval();
533  typename right_type::dist_eval_type right = right_.make_dist_eval();
534 
535  std::shared_ptr<impl_type> pimpl =
536  std::make_shared<impl_type>(left, right, *world_, trange_, shape_,
537  pmap_, perm_, op_, K_, proc_grid_);
538 
539  return dist_eval_type(pimpl);
540  }
541 
543 
545  std::string make_tag() const {
546  std::stringstream ss;
547  ss << "[*]";
548  if(factor_ != scalar_type(1))
549  ss << "[" << factor_ << "]";
550  return ss.str();
551  }
552 
553 
555 
558  void print(ExprOStream os, const VariableList& target_vars) const {
559  ExprEngine_::print(os, target_vars);
560  os.inc();
561  left_.print(os, left_vars_);
562  right_.print(os, right_vars_);
563  os.dec();
564  }
565 
566  }; // class ContEngine
567 
568  } // namespace expressions
569 } // namespace TiledArray
570 
571 #endif // TILEDARRAY_EXPRESSIONS_CONT_ENGINE_H__INCLUDED
EngineTrait< Derived >::trange_type trange_type
Tiled range type.
Definition: cont_engine.h:76
std::string make_tag() const
Expression identification tag.
Definition: cont_engine.h:545
Multiplication expression.
Definition: cont_engine.h:39
trange_type make_trange(const Permutation &perm=Permutation()) const
Tiled range factory function.
Definition: cont_engine.h:447
std::shared_ptr< EngineParamOverride< Derived > > override_ptr_
The engine params overriding the default.
Definition: expr_engine.h:70
TiledArray::detail::ContractReduce< value_type, typename eval_trait< typename left_type::value_type >::type, typename eval_trait< typename right_type::value_type >::type, scalar_type > op_type
The tile operation type.
Definition: cont_engine.h:67
right_type right_
The right-hand argument.
Definition: binary_engine.h:78
EngineTrait< Derived >::right_type right_type
The right-hand expression type.
Definition: cont_engine.h:57
bool permute_tiles_
Result tile permutation flag (true == permute tile)
Definition: expr_engine.h:65
void print(ExprOStream os, const VariableList &target_vars) const
Expression print.
Definition: cont_engine.h:558
const Permutation & perm() const
Permutation accessor.
Definition: expr_engine.h:204
scalar_type factor_
Contraction scaling factor.
Definition: cont_engine.h:106
Permutation make_perm(const VariableList &target_vars) const
Permutation factory function.
Definition: expr_engine.h:168
void init_struct(const VariableList &target_vars)
Initialize result tensor structure.
Definition: cont_engine.h:360
BinaryEngine< Derived > BinaryEngine_
Binary base class type.
Definition: cont_engine.h:49
EngineTrait< MultEngine< Left, Right, Result > >::shape_type shape_type
Shape type.
Definition: binary_engine.h:59
World * world() const
World accessor.
Definition: expr_engine.h:194
KroneckerDeltaTile< _N >::numeric_type max(const KroneckerDeltaTile< _N > &arg)
EngineTrait< MultEngine< Left, Right, Result > >::op_type op_type
The tile operation type.
Definition: binary_engine.h:52
const shape_type & shape() const
Shape accessor.
Definition: expr_engine.h:214
derived_type & derived()
Cast this object to it&#39;s derived type.
Definition: expr_engine.h:186
const std::vector< std::string > & data() const
Multiplication expression.
Definition: cont_engine.h:38
const std::shared_ptr< pmap_interface > & pmap() const
Process map accessor.
Definition: expr_engine.h:219
VariableList vars_
The variable list of this expression.
Definition: expr_engine.h:64
EngineTrait< Derived >::value_type value_type
The result tile type.
Definition: cont_engine.h:61
EngineTrait< MultEngine< Left, Right, Result > >::size_type size_type
Size type.
Definition: binary_engine.h:57
shape_type make_shape() const
Non-permuting shape factory function.
Definition: cont_engine.h:506
std::shared_ptr< Pmap > make_pmap() const
Construct a cyclic process.
Definition: proc_grid.h:488
EngineTrait< MultEngine< Left, Right, Result > >::trange_type trange_type
Tiled range type.
Definition: binary_engine.h:58
EngineTrait< MultEngine< Left, Right, Result > >::dist_eval_type dist_eval_type
The distributed evaluator type.
Definition: binary_engine.h:54
unsigned int dim() const
Returns the number of strings in the variable list.
const VariableList & vars() const
Variable list accessor.
Definition: expr_engine.h:199
ContEngine< Derived > ContEngine_
This class type.
Definition: cont_engine.h:48
void perm_vars(const VariableList &target_vars)
Set the variable list for this expression.
Definition: cont_engine.h:167
EngineTrait< Derived >::left_type left_type
The left-hand expression type.
Definition: cont_engine.h:55
EngineTrait< Derived >::size_type size_type
Size type.
Definition: cont_engine.h:74
Variable list manages a list variable strings.
EngineTrait< Derived >::shape_type shape_type
Shape type.
Definition: cont_engine.h:78
ContEngine(const ScalMultExpr< L, R, S > &expr)
Constructor.
Definition: cont_engine.h:150
Multiplication expression engine.
Definition: cont_engine.h:45
shape_type make_shape(const Permutation &perm) const
Permuting shape factory function.
Definition: cont_engine.h:518
std::shared_ptr< pmap_interface > pmap_
The process map for the result tensor.
Definition: expr_engine.h:69
EngineTrait< Derived >::dist_eval_type dist_eval_type
The distributed evaluator type.
Definition: cont_engine.h:71
World * world_
The world where this expression will be evaluated.
Definition: expr_engine.h:63
ExprEngine< Derived > ExprEngine_
Expression engine base class type.
Definition: cont_engine.h:51
void inc()
Increment the number of tabs.
Definition: expr_trace.h:70
Contraction to *GEMM helper.
Definition: gemm_helper.h:39
Permutation of a sequence of objects indexed by base-0 indices.
Definition: permutation.h:119
trange_type make_trange() const
Non-permuting tiled range factory function.
#define TA_EXCEPTION(m)
Definition: error.h:72
Permutation perm_
The permutation that will be applied to the result.
Definition: expr_engine.h:66
void init_vars()
Initialize the variable list of this expression.
Definition: cont_engine.h:228
void init_distribution(World *world, std::shared_ptr< pmap_interface > pmap)
Initialize result tensor distribution.
Definition: cont_engine.h:400
Contract and (sum) reduce operation.
void init_distribution(World *world, const std::shared_ptr< pmap_interface > &pmap)
Initialize result tensor distribution.
Definition: expr_engine.h:151
shape_type shape_
The shape of the result tensor.
Definition: expr_engine.h:68
std::shared_ptr< Pmap > make_row_phase_pmap(const size_type cols) const
Construct row phased a cyclic process.
Definition: proc_grid.h:512
std::shared_ptr< Pmap > make_col_phase_pmap(const size_type rows) const
Construct column phased a cyclic process.
Definition: proc_grid.h:500
Distributed contraction evaluator implementation.
ContEngine(const MultExpr< L, R > &expr)
Constructor.
Definition: cont_engine.h:137
Expression output stream.
Definition: expr_trace.h:39
EngineTrait< Derived >::scalar_type scalar_type
Tile scalar type.
Definition: cont_engine.h:63
dist_eval_type make_dist_eval() const
Definition: cont_engine.h:527
A 2D processor grid.
Definition: proc_grid.h:59
trange_type trange_
The tiled range of the result tensor.
Definition: expr_engine.h:67
#define TA_USER_ERROR_MESSAGE(m)
Definition: error.h:118
left_type left_
The left-hand argument.
Definition: binary_engine.h:77
EngineTrait< Derived >::policy policy
The result policy type.
Definition: cont_engine.h:69
void print(ExprOStream &os, const VariableList &target_vars) const
Expression print.
Definition: expr_engine.h:230
EngineTrait< Derived >::pmap_interface pmap_interface
Process map interface type.
Definition: cont_engine.h:80
void dec()
Decrement the number of tabs.
Definition: expr_trace.h:73