tensor.h
Go to the documentation of this file.
1 /*
2  * This file is a part of TiledArray.
3  * Copyright (C) 2013 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  */
19 
20 #ifndef TILEDARRAY_TENSOR_TENSOR_H__INCLUDED
21 #define TILEDARRAY_TENSOR_TENSOR_H__INCLUDED
22 
23 #include "TiledArray/math/blas.h"
30 #include "TiledArray/util/logger.h"
31 namespace TiledArray {
32 
33 // Forward declare Tensor for type traits
34 template <typename T, typename A>
35 class Tensor;
36 
37 namespace detail {
38 
40 template <typename T, typename A>
41 struct TraceIsDefined<Tensor<T, A>, enable_if_numeric_t<T>> : std::true_type {};
42 
43 } // namespace detail
44 
46 
49 template <typename T, typename A>
50 class Tensor {
51  // meaningful error if T& is not assignable, see
52  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48101
53  static_assert(
54  std::is_assignable<std::add_lvalue_reference_t<T>, T>::value,
55  "Tensor<T>: T must be an assignable type (e.g. cannot be const)");
56 
57  public:
59  typedef Range range_type;
62  typedef typename range_type::ordinal_type
64  typedef A allocator_type;
65  typedef
66  typename allocator_type::value_type value_type;
67  typedef
68  typename allocator_type::reference reference;
69  typedef typename allocator_type::const_reference
71  typedef typename allocator_type::pointer pointer;
72  typedef typename allocator_type::const_pointer
74  typedef typename allocator_type::difference_type
76  typedef pointer iterator;
82 
83  private:
84  template <typename X>
85  using numeric_t = typename TiledArray::detail::numeric_type<X>::type;
86 
88 
90  class Impl : public allocator_type {
91  public:
93 
95  Impl() : allocator_type(), range_(), data_(NULL) {}
96 
98 
100  explicit Impl(const range_type& range)
101  : allocator_type(), range_(range), data_(NULL) {
102  data_ = allocator_type::allocate(range.volume());
103  }
104 
106 
108  explicit Impl(range_type&& range)
109  : allocator_type(), range_(range), data_(NULL) {
110  data_ = allocator_type::allocate(range.volume());
111  }
112 
113  ~Impl() {
114  math::destroy_vector(range_.volume(), data_);
115  allocator_type::deallocate(data_, range_.volume());
116  data_ = NULL;
117  }
118 
119  range_type range_;
120  pointer data_;
121  }; // class Impl
122 
123  template <typename... Ts>
124  struct is_tensor {
125  static constexpr bool value = detail::is_tensor<Ts...>::value ||
126  detail::is_tensor_of_tensor<Ts...>::value;
127  };
128 
129  template <typename U,
130  typename std::enable_if<detail::is_scalar_v<U>>::type* = nullptr>
131  static void default_init(index1_type, U*) {}
132 
133  template <typename U,
134  typename std::enable_if<!detail::is_scalar_v<U>>::type* = nullptr>
135  static void default_init(index1_type n, U* u) {
137  }
138 
139  std::shared_ptr<Impl> pimpl_;
140  static const range_type empty_range_;
141 
142  public:
143  // Compiler generated functions
144  Tensor() : pimpl_() {}
145  Tensor(const Tensor_& other) : pimpl_(other.pimpl_) {}
146  Tensor(Tensor_&& other) : pimpl_(std::move(other.pimpl_)) {}
147  ~Tensor() {}
148  Tensor_& operator=(const Tensor_& other) {
149  pimpl_ = other.pimpl_;
150  return *this;
151  }
153  pimpl_ = std::move(other.pimpl_);
154  return *this;
155  }
156 
158 
162  explicit Tensor(const range_type& range)
163  : pimpl_(std::make_shared<Impl>(range)) {
164  default_init(range.volume(), pimpl_->data_);
165  }
166 
168 
171  template <
172  typename Value,
173  typename std::enable_if<std::is_same<Value, value_type>::value &&
174  detail::is_tensor<Value>::value>::type* = nullptr>
175  Tensor(const range_type& range, const Value& value)
176  : pimpl_(std::make_shared<Impl>(range)) {
177  const auto n = pimpl_->range_.volume();
178  pointer MADNESS_RESTRICT const data = pimpl_->data_;
179  Clone<Value, Value> cloner;
180  for (size_type i = 0ul; i < n; ++i)
181  new (data + i) value_type(cloner(value));
182  }
183 
185 
188  template <typename Value, typename std::enable_if<
189  detail::is_numeric_v<Value>>::type* = nullptr>
190  Tensor(const range_type& range, const Value& value)
191  : pimpl_(std::make_shared<Impl>(range)) {
192  detail::tensor_init([value]() -> Value { return value; }, *this);
193  }
194 
196  template <typename InIter,
197  typename std::enable_if<
199  !std::is_pointer<InIter>::value>::type* = nullptr>
200  Tensor(const range_type& range, InIter it)
201  : pimpl_(std::make_shared<Impl>(range)) {
202  auto n = range.volume();
203  pointer MADNESS_RESTRICT const data = pimpl_->data_;
204  for (size_type i = 0ul; i < n; ++i, ++it) data[i] = *it;
205  }
206 
207  template <typename U>
208  Tensor(const Range& range, const U* u)
209  : pimpl_(std::make_shared<Impl>(range)) {
210  math::uninitialized_copy_vector(range.volume(), u, pimpl_->data_);
211  }
212 
213  Tensor(const Range& range, std::initializer_list<T> il)
214  : Tensor(range, il.begin()) {}
215 
217 
222  template <
223  typename T1,
224  typename std::enable_if<
225  is_tensor<T1>::value && !std::is_same<T1, Tensor_>::value &&
226  !detail::has_conversion_operator_v<T1, Tensor_>>::type* = nullptr>
227  explicit Tensor(const T1& other)
228  : pimpl_(std::make_shared<Impl>(detail::clone_range(other))) {
229  auto op = [](const numeric_t<T1> arg) -> numeric_t<T1> { return arg; };
230 
231  detail::tensor_init(op, *this, other);
232  }
233 
235 
240  template <
241  typename T1, typename Perm,
242  typename std::enable_if<is_tensor<T1>::value &&
243  detail::is_permutation_v<Perm>>::type* = nullptr>
244  Tensor(const T1& other, const Perm& perm)
245  : pimpl_(std::make_shared<Impl>(outer(perm) * other.range())) {
246  auto op = [](const numeric_t<T1> arg) -> numeric_t<T1> { return arg; };
247 
248  detail::tensor_init(op, outer(perm), *this, other);
249 
250  // If we actually have a ToT the inner permutation was not applied above so
251  // we do that now
252  constexpr bool is_tot = detail::is_tensor_of_tensor_v<Tensor_>;
253  constexpr bool is_bperm = detail::is_bipartite_permutation_v<Perm>;
254  // tile ops pass bipartite permutations here even if this is a plain tensor
255  // static_assert(is_tot || (!is_tot && !is_bperm), "Permutation type does
256  // not match Tensor_");
257  if constexpr (is_tot && is_bperm) {
258  if (inner_size(perm) != 0) {
259  auto inner_perm = inner(perm);
261  for (auto& x : *this) x = p(x, inner_perm);
262  }
263  }
264  }
265 
267 
272  template <typename T1, typename Op,
273  typename std::enable_if_t<
274  is_tensor<T1>::value &&
275  !detail::is_permutation_v<std::decay_t<Op>>>* = nullptr>
276  Tensor(const T1& other, Op&& op)
277  : pimpl_(std::make_shared<Impl>(detail::clone_range(other))) {
278  detail::tensor_init(op, *this, other);
279  }
280 
282 
288  template <
289  typename T1, typename Op, typename Perm,
290  typename std::enable_if_t<is_tensor<T1>::value &&
291  detail::is_permutation_v<Perm>>* = nullptr>
292  Tensor(const T1& other, Op&& op, const Perm& perm)
293  : pimpl_(std::make_shared<Impl>(outer(perm) * other.range())) {
294  detail::tensor_init(op, outer(perm), *this, other);
295  // If we actually have a ToT the inner permutation was not applied above so
296  // we do that now
297  constexpr bool is_tot = detail::is_tensor_of_tensor_v<Tensor_>;
298  constexpr bool is_bperm = detail::is_bipartite_permutation_v<Perm>;
299  // tile ops pass bipartite permutations here even if this is a plain tensor
300  // static_assert(is_tot || (!is_tot && !is_bperm), "Permutation type does
301  // not match Tensor_");
302  if constexpr (is_tot && is_bperm) {
303  if (inner_size(perm) != 0) {
304  auto inner_perm = inner(perm);
306  for (auto& x : *this) x = p(x, inner_perm);
307  }
308  }
309  }
310 
312 
319  template <typename T1, typename T2, typename Op,
320  typename std::enable_if<is_tensor<T1, T2>::value>::type* = nullptr>
321  Tensor(const T1& left, const T2& right, Op&& op)
322  : pimpl_(std::make_shared<Impl>(detail::clone_range(left))) {
323  detail::tensor_init(op, *this, left, right);
324  }
325 
327 
336  template <
337  typename T1, typename T2, typename Op, typename Perm,
338  typename std::enable_if<is_tensor<T1, T2>::value &&
339  detail::is_permutation_v<Perm>>::type* = nullptr>
340  Tensor(const T1& left, const T2& right, Op&& op, const Perm& perm)
341  : pimpl_(std::make_shared<Impl>(outer(perm) * left.range())) {
342  detail::tensor_init(op, outer(perm), *this, left, right);
343  // If we actually have a ToT the inner permutation was not applied above so
344  // we do that now
345  constexpr bool is_tot = detail::is_tensor_of_tensor_v<Tensor_>;
346  constexpr bool is_bperm = detail::is_bipartite_permutation_v<Perm>;
347  // tile ops pass bipartite permutations here even if this is a plain tensor
348  // static_assert(is_tot || (!is_tot && !is_bperm), "Permutation type does
349  // not match Tensor_");
350  if constexpr (is_tot && is_bperm) {
351  if (inner_size(perm) != 0) {
352  auto inner_perm = inner(perm);
354  for (auto& x : *this) x = p(x, inner_perm);
355  }
356  }
357  }
358 
359  Tensor_ clone() const {
360  Tensor_ result;
361  if (pimpl_) {
362  result = detail::tensor_op<Tensor_>(
363  [](const numeric_type value) -> numeric_type { return value; },
364  *this);
365  }
366  return result;
367  }
368 
369  template <typename T1,
370  typename std::enable_if<is_tensor<T1>::value>::type* = nullptr>
371  Tensor_& operator=(const T1& other) {
372  pimpl_ = std::make_shared<Impl>(detail::clone_range(other));
374  [](reference MADNESS_RESTRICT tr,
375  typename T1::const_reference MADNESS_RESTRICT t1) { tr = t1; },
376  *this, other);
377 
378  return *this;
379  }
380 
382 
384  const range_type& range() const {
385  return (pimpl_ ? pimpl_->range_ : empty_range_);
386  }
387 
389 
393  TA_ASSERT(pimpl_);
394  return pimpl_->range_;
395  }
396 
398 
400  ordinal_type size() const { return (pimpl_ ? pimpl_->range_.volume() : 0ul); }
401 
403 
409  template <typename Ordinal,
410  std::enable_if_t<std::is_integral<Ordinal>::value>* = nullptr>
411  const_reference operator[](const Ordinal ord) const {
412  TA_ASSERT(pimpl_);
413  TA_ASSERT(pimpl_->range_.includes(ord));
414  return pimpl_->data_[ord];
415  }
416 
418 
424  template <typename Ordinal,
425  std::enable_if_t<std::is_integral<Ordinal>::value>* = nullptr>
426  reference operator[](const Ordinal ord) {
427  TA_ASSERT(pimpl_);
428  TA_ASSERT(pimpl_->range_.includes(ord));
429  return pimpl_->data_[ord];
430  }
431 
433 
439  template <typename Index,
440  std::enable_if_t<detail::is_integral_range_v<Index>>* = nullptr>
441  const_reference operator[](const Index& i) const {
442  TA_ASSERT(pimpl_);
443  TA_ASSERT(pimpl_->range_.includes(i));
444  return pimpl_->data_[pimpl_->range_.ordinal(i)];
445  }
446 
448 
454  template <typename Index,
455  std::enable_if_t<detail::is_integral_range_v<Index>>* = nullptr>
456  reference operator[](const Index& i) {
457  TA_ASSERT(pimpl_);
458  TA_ASSERT(pimpl_->range_.includes(i));
459  return pimpl_->data_[pimpl_->range_.ordinal(i)];
460  }
461 
463 
469  template <typename Integer,
470  std::enable_if_t<std::is_integral_v<Integer>>* = nullptr>
471  const_reference operator[](const std::initializer_list<Integer>& i) const {
472  TA_ASSERT(pimpl_);
473  TA_ASSERT(pimpl_->range_.includes(i));
474  return pimpl_->data_[pimpl_->range_.ordinal(i)];
475  }
476 
478 
484  template <typename Integer,
485  std::enable_if_t<std::is_integral_v<Integer>>* = nullptr>
486  reference operator[](const std::initializer_list<Integer>& i) {
487  TA_ASSERT(pimpl_);
488  TA_ASSERT(pimpl_->range_.includes(i));
489  return pimpl_->data_[pimpl_->range_.ordinal(i)];
490  }
491 
493 
499  template <typename Index,
500  std::enable_if_t<detail::is_integral_range_v<Index>>* = nullptr>
501  const_reference operator()(const Index& i) const {
502  TA_ASSERT(pimpl_);
503  TA_ASSERT(pimpl_->range_.includes(i));
504  return pimpl_->data_[pimpl_->range_.ordinal(i)];
505  }
506 
508 
514  template <typename Index,
515  std::enable_if_t<detail::is_integral_range_v<Index>>* = nullptr>
516  reference operator()(const Index& i) {
517  TA_ASSERT(pimpl_);
518  TA_ASSERT(pimpl_->range_.includes(i));
519  return pimpl_->data_[pimpl_->range_.ordinal(i)];
520  }
521 
523 
529  template <typename Integer,
530  std::enable_if_t<std::is_integral_v<Integer>>* = nullptr>
531  const_reference operator()(const std::initializer_list<Integer>& i) const {
532  TA_ASSERT(pimpl_);
533  TA_ASSERT(pimpl_->range_.includes(i));
534  return pimpl_->data_[pimpl_->range_.ordinal(i)];
535  }
536 
538 
544  template <typename Integer,
545  std::enable_if_t<std::is_integral_v<Integer>>* = nullptr>
546  reference operator()(const std::initializer_list<Integer>& i) {
547  TA_ASSERT(pimpl_);
548  TA_ASSERT(pimpl_->range_.includes(i));
549  return pimpl_->data_[pimpl_->range_.ordinal(i)];
550  }
551 
553 
558  template <
559  typename... Index,
560  std::enable_if_t<detail::is_integral_list<Index...>::value>* = nullptr>
561  const_reference operator()(const Index&... i) const {
562  TA_ASSERT(pimpl_);
563  TA_ASSERT(pimpl_->range_.includes(i...));
564  return pimpl_->data_[pimpl_->range_.ordinal(i...)];
565  }
566 
568 
573  template <
574  typename... Index,
575  std::enable_if_t<detail::is_integral_list<Index...>::value>* = nullptr>
576  reference operator()(const Index&... i) {
577  TA_ASSERT(pimpl_);
578  TA_ASSERT(pimpl_->range_.includes(i...));
579  return pimpl_->data_[pimpl_->range_.ordinal(i...)];
580  }
581 
583 
585  const_iterator begin() const { return (pimpl_ ? pimpl_->data_ : NULL); }
586 
588 
590  iterator begin() { return (pimpl_ ? pimpl_->data_ : NULL); }
591 
593 
595  const_iterator end() const {
596  return (pimpl_ ? pimpl_->data_ + pimpl_->range_.volume() : NULL);
597  }
598 
600 
603  return (pimpl_ ? pimpl_->data_ + pimpl_->range_.volume() : NULL);
604  }
605 
607 
609  const_pointer data() const { return (pimpl_ ? pimpl_->data_ : NULL); }
610 
612 
614  pointer data() { return (pimpl_ ? pimpl_->data_ : NULL); }
615 
617 
620  bool empty() const { return !pimpl_; }
621 
623 
627  template <typename Archive,
628  typename std::enable_if<madness::archive::is_output_archive<
629  Archive>::value>::type* = nullptr>
630  void serialize(Archive& ar) {
631  if (pimpl_) {
632  ar & pimpl_->range_.volume();
633  ar& madness::archive::wrap(pimpl_->data_, pimpl_->range_.volume());
634  ar & pimpl_->range_;
635  } else {
636  ar& ordinal_type(0ul);
637  }
638  }
639 
641 
645  template <typename Archive,
646  typename std::enable_if<madness::archive::is_input_archive<
647  Archive>::value>::type* = nullptr>
648  void serialize(Archive& ar) {
649  ordinal_type n = 0ul;
650  ar& n;
651  if (n) {
652  std::shared_ptr<Impl> temp = std::make_shared<Impl>();
653  temp->data_ = temp->allocate(n);
654  try {
655  // need to construct elements of data_ using placement new in case its
656  // default ctor is not trivial N.B. for fundamental types and standard
657  // alloc this incurs no overhead (Eigen::aligned_alloc OK also)
658  auto* data_ptr = temp->data_;
659  for (ordinal_type i = 0; i != n; ++i, ++data_ptr)
660  new (static_cast<void*>(data_ptr)) value_type;
661 
662  ar& madness::archive::wrap(temp->data_, n);
663  ar & temp->range_;
664  } catch (...) {
665  temp->deallocate(temp->data_, n);
666  throw;
667  }
668 
669  pimpl_ = temp;
670  } else {
671  pimpl_.reset();
672  }
673  }
674 
676 
678  void swap(Tensor_& other) { std::swap(pimpl_, other.pimpl_); }
679 
680  // clang-format off
682 
699  // clang-format on
701  template <typename Index1, typename Index2,
702  typename = std::enable_if_t<detail::is_integral_range_v<Index1> &&
703  detail::is_integral_range_v<Index2>>>
705  const Index2& upper_bound) {
706  TA_ASSERT(pimpl_);
708  BlockRange(pimpl_->range_, lower_bound, upper_bound), pimpl_->data_);
709  }
710 
711  template <typename Index1, typename Index2,
712  typename = std::enable_if_t<detail::is_integral_range_v<Index1> &&
713  detail::is_integral_range_v<Index2>>>
715  const Index1& lower_bound, const Index2& upper_bound) const {
716  TA_ASSERT(pimpl_);
718  BlockRange(pimpl_->range_, lower_bound, upper_bound), pimpl_->data_);
719  }
721 
722  // clang-format off
724 
739  // clang-format on
741  template <typename Index1, typename Index2,
742  typename = std::enable_if_t<std::is_integral_v<Index1> &&
743  std::is_integral_v<Index2>>>
745  const std::initializer_list<Index1>& lower_bound,
746  const std::initializer_list<Index2>& upper_bound) {
747  TA_ASSERT(pimpl_);
749  BlockRange(pimpl_->range_, lower_bound, upper_bound), pimpl_->data_);
750  }
751 
752  template <typename Index1, typename Index2,
753  typename = std::enable_if_t<std::is_integral_v<Index1> &&
754  std::is_integral_v<Index2>>>
756  const std::initializer_list<Index1>& lower_bound,
757  const std::initializer_list<Index2>& upper_bound) const {
758  TA_ASSERT(pimpl_);
760  BlockRange(pimpl_->range_, lower_bound, upper_bound), pimpl_->data_);
761  }
763 
764  // clang-format off
766 
796  // clang-format on
798  template <typename PairRange,
799  typename = std::enable_if_t<detail::is_gpair_range_v<PairRange>>>
801  const PairRange& bounds) const {
803  BlockRange(pimpl_->range_, bounds), pimpl_->data_);
804  }
805 
806  template <typename PairRange,
807  typename = std::enable_if_t<detail::is_gpair_range_v<PairRange>>>
810  BlockRange(pimpl_->range_, bounds), pimpl_->data_);
811  }
813 
814  // clang-format off
816 
827  // clang-format on
829  template <typename Index,
830  typename = std::enable_if_t<std::is_integral_v<Index>>>
832  const std::initializer_list<std::initializer_list<Index>>& bounds) const {
834  BlockRange(pimpl_->range_, bounds), pimpl_->data_);
835  }
836 
837  template <typename Index,
838  typename = std::enable_if_t<std::is_integral_v<Index>>>
840  const std::initializer_list<std::initializer_list<Index>>& bounds) {
842  BlockRange(pimpl_->range_, bounds), pimpl_->data_);
843  }
845 
847 
851  template <typename Perm,
852  typename = std::enable_if_t<detail::is_permutation_v<Perm>>>
853  Tensor_ permute(const Perm& perm) const {
854  constexpr bool is_tot = detail::is_tensor_of_tensor_v<Tensor_>;
855  [[maybe_unused]] constexpr bool is_bperm =
856  detail::is_bipartite_permutation_v<Perm>;
857  // tile ops pass bipartite permutations here even if this is a plain tensor
858  // static_assert(is_tot || (!is_tot && !is_bperm), "Permutation type does
859  // not match Tensor_");
860  if constexpr (!is_tot) {
861  if constexpr (is_bperm) {
862  TA_ASSERT(inner_size(perm) == 0); // ensure this is a plain permutation
863  return Tensor_(*this, outer(perm));
864  } else
865  return Tensor_(*this, perm);
866  } else {
867  // If we have a ToT we need to apply the permutation in two steps. The
868  // first step is identical to the non-ToT case (permute the outer modes)
869  // the second step does the inner modes
870  Tensor_ rv(*this, outer(perm));
871  if constexpr (is_bperm) {
872  if (inner_size(perm) != 0) {
873  auto inner_perm = inner(perm);
875  for (auto& inner_t : rv) inner_t = p(inner_t, inner_perm);
876  }
877  }
878  return rv;
879  }
880  abort(); // unreachable
881  }
882 
884 
888  template <typename Index,
889  std::enable_if_t<detail::is_integral_range_v<Index>>* = nullptr>
890  Tensor_& shift_to(const Index& bound_shift) {
891  TA_ASSERT(pimpl_);
892  pimpl_->range_.inplace_shift(bound_shift);
893  return *this;
894  }
895 
897 
901  template <typename Integer,
902  std::enable_if_t<std::is_integral_v<Integer>>* = nullptr>
903  Tensor_& shift_to(const std::initializer_list<Integer>& bound_shift) {
904  TA_ASSERT(pimpl_);
905  pimpl_->range_.template inplace_shift<std::initializer_list<Integer>>(
906  bound_shift);
907  return *this;
908  }
909 
911 
915  template <typename Index,
916  std::enable_if_t<detail::is_integral_range_v<Index>>* = nullptr>
917  Tensor_ shift(const Index& bound_shift) const {
918  TA_ASSERT(pimpl_);
919  Tensor_ result = clone();
920  result.shift_to(bound_shift);
921  return result;
922  }
923 
925 
929  template <typename Integer,
930  std::enable_if_t<std::is_integral_v<Integer>>* = nullptr>
931  Tensor_ shift(const std::initializer_list<Integer>& bound_shift) const {
932  TA_ASSERT(pimpl_);
933  Tensor_ result = clone();
934  result.template shift_to<std::initializer_list<Integer>>(bound_shift);
935  return result;
936  }
937 
938  // Generic vector operations
939 
941 
948  template <typename Right, typename Op,
949  typename std::enable_if<is_tensor<Right>::value>::type* = nullptr>
950  Tensor_ binary(const Right& right, Op&& op) const {
951  return Tensor_(*this, right, op);
952  }
953 
955 
964  template <
965  typename Right, typename Op, typename Perm,
966  typename std::enable_if<is_tensor<Right>::value &&
967  detail::is_permutation_v<Perm>>::type* = nullptr>
968  Tensor_ binary(const Right& right, Op&& op, const Perm& perm) const {
969  constexpr bool is_tot = detail::is_tensor_of_tensor_v<Tensor_>;
970  [[maybe_unused]] constexpr bool is_bperm =
971  detail::is_bipartite_permutation_v<Perm>;
972  // tile ops pass bipartite permutations here even if this is a plain tensor
973  // static_assert(is_tot || (!is_tot && !is_bperm), "Permutation type does
974  // not match Tensor_");
975  if constexpr (!is_tot) {
976  if constexpr (is_bperm) {
977  TA_ASSERT(inner_size(perm) == 0); // ensure this is a plain permutation
978  return Tensor_(*this, right, op, outer(perm));
979  } else
980  return Tensor_(*this, right, op, perm);
981  } else {
982  // AFAIK the other branch fundamentally relies on raw pointer arithmetic,
983  // which won't work for ToTs.
984  auto temp = binary(right, std::forward<Op>(op));
986  return p(temp, perm);
987  }
988  abort(); // unreachable
989  }
990 
992 
1003  template <typename Right, typename Op,
1004  typename std::enable_if<is_tensor<Right>::value>::type* = nullptr>
1005  Tensor_& inplace_binary(const Right& right, Op&& op) {
1006  detail::inplace_tensor_op(op, *this, right);
1007  return *this;
1008  }
1009 
1011 
1017  template <typename Op>
1018  Tensor_ unary(Op&& op) const {
1019  return Tensor_(*this, op);
1020  }
1021 
1023 
1032  template <typename Op, typename Perm,
1033  typename = std::enable_if_t<detail::is_permutation_v<Perm>>>
1034  Tensor_ unary(Op&& op, const Perm& perm) const {
1035  constexpr bool is_tot = detail::is_tensor_of_tensor_v<Tensor_>;
1036  [[maybe_unused]] constexpr bool is_bperm =
1037  detail::is_bipartite_permutation_v<Perm>;
1038  // tile ops pass bipartite permutations here even if this is a plain tensor
1039  // static_assert(is_tot || (!is_tot && !is_bperm), "Permutation type does
1040  // not match Tensor_");
1041  if constexpr (!is_tot) {
1042  if constexpr (is_bperm) {
1043  TA_ASSERT(inner_size(perm) == 0); // ensure this is a plain permutation
1044  return Tensor_(*this, op, outer(perm));
1045  } else
1046  return Tensor_(*this, op, perm);
1047  } else {
1048  auto temp = unary(std::forward<Op>(op));
1050  return p(temp, perm);
1051  }
1052  abort(); // unreachable
1053  }
1054 
1056 
1061  template <typename Op>
1063  detail::inplace_tensor_op(op, *this);
1064  return *this;
1065  }
1066 
1067  // Scale operation
1068 
1070 
1075  template <typename Scalar, typename std::enable_if<
1076  detail::is_numeric_v<Scalar>>::type* = nullptr>
1077  Tensor_ scale(const Scalar factor) const {
1078  return unary(
1079  [factor](const numeric_type a) -> numeric_type { return a * factor; });
1080  }
1081 
1083 
1090  template <typename Scalar, typename Perm,
1091  typename = std::enable_if_t<detail::is_numeric_v<Scalar> &&
1092  detail::is_permutation_v<Perm>>>
1093  Tensor_ scale(const Scalar factor, const Perm& perm) const {
1094  return unary(
1095  [factor](const numeric_type a) -> numeric_type { return a * factor; },
1096  perm);
1097  }
1098 
1100 
1104  template <typename Scalar, typename std::enable_if<
1105  detail::is_numeric_v<Scalar>>::type* = nullptr>
1106  Tensor_& scale_to(const Scalar factor) {
1107  return inplace_unary(
1108  [factor](numeric_type& MADNESS_RESTRICT res) { res *= factor; });
1109  }
1110 
1111  // Addition operations
1112 
1114 
1119  template <typename Right,
1120  typename std::enable_if<is_tensor<Right>::value>::type* = nullptr>
1121  Tensor_ add(const Right& right) const {
1122  return binary(
1123  right,
1124  [](const numeric_type l, const numeric_t<Right> r) -> numeric_type {
1125  return l + r;
1126  });
1127  }
1128 
1130 
1137  template <
1138  typename Right, typename Perm,
1139  typename std::enable_if<is_tensor<Right>::value &&
1140  detail::is_permutation_v<Perm>>::type* = nullptr>
1141  Tensor_ add(const Right& right, const Perm& perm) const {
1142  return binary(
1143  right,
1144  [](const numeric_type l, const numeric_t<Right> r) -> numeric_type {
1145  return l + r;
1146  },
1147  perm);
1148  }
1149 
1151 
1158  template <
1159  typename Right, typename Scalar,
1160  typename std::enable_if<is_tensor<Right>::value &&
1161  detail::is_numeric_v<Scalar>>::type* = nullptr>
1162  Tensor_ add(const Right& right, const Scalar factor) const {
1163  return binary(right,
1164  [factor](const numeric_type l, const numeric_t<Right> r)
1165  -> numeric_type { return (l + r) * factor; });
1166  }
1167 
1169 
1178  template <typename Right, typename Scalar, typename Perm,
1179  typename std::enable_if<
1180  is_tensor<Right>::value && detail::is_numeric_v<Scalar> &&
1181  detail::is_permutation_v<Perm>>::type* = nullptr>
1182  Tensor_ add(const Right& right, const Scalar factor, const Perm& perm) const {
1183  return binary(
1184  right,
1185  [factor](const numeric_type l, const numeric_t<Right> r)
1186  -> numeric_type { return (l + r) * factor; },
1187  perm);
1188  }
1189 
1191 
1195  Tensor_ add(const numeric_type value) const {
1196  return unary(
1197  [value](const numeric_type a) -> numeric_type { return a + value; });
1198  }
1199 
1201 
1207  template <typename Perm,
1208  typename = std::enable_if_t<detail::is_permutation_v<Perm>>>
1209  Tensor_ add(const numeric_type value, const Perm& perm) const {
1210  return unary(
1211  [value](const numeric_type a) -> numeric_type { return a + value; },
1212  perm);
1213  }
1214 
1216 
1220  template <typename Right,
1221  typename std::enable_if<is_tensor<Right>::value>::type* = nullptr>
1222  Tensor_& add_to(const Right& right) {
1223  return inplace_binary(right, [](numeric_type& MADNESS_RESTRICT l,
1224  const numeric_t<Right> r) { l += r; });
1225  }
1226 
1228 
1234  template <
1235  typename Right, typename Scalar,
1236  typename std::enable_if<is_tensor<Right>::value &&
1237  detail::is_numeric_v<Scalar>>::type* = nullptr>
1238  Tensor_& add_to(const Right& right, const Scalar factor) {
1239  return inplace_binary(
1240  right, [factor](numeric_type& MADNESS_RESTRICT l,
1241  const numeric_t<Right> r) { (l += r) *= factor; });
1242  }
1243 
1245 
1248  Tensor_& add_to(const numeric_type value) {
1249  return inplace_unary(
1250  [value](numeric_type& MADNESS_RESTRICT res) { res += value; });
1251  }
1252 
1253  // Subtraction operations
1254 
1256 
1261  template <typename Right,
1262  typename std::enable_if<is_tensor<Right>::value>::type* = nullptr>
1263  Tensor_ subt(const Right& right) const {
1264  return binary(
1265  right,
1266  [](const numeric_type l, const numeric_t<Right> r) -> numeric_type {
1267  return l - r;
1268  });
1269  }
1270 
1272 
1279  template <
1280  typename Right, typename Perm,
1281  typename std::enable_if<is_tensor<Right>::value &&
1282  detail::is_permutation_v<Perm>>::type* = nullptr>
1283  Tensor_ subt(const Right& right, const Perm& perm) const {
1284  return binary(
1285  right,
1286  [](const numeric_type l, const numeric_t<Right> r) -> numeric_type {
1287  return l - r;
1288  },
1289  perm);
1290  }
1291 
1294 
1301  template <
1302  typename Right, typename Scalar,
1303  typename std::enable_if<is_tensor<Right>::value &&
1304  detail::is_numeric_v<Scalar>>::type* = nullptr>
1305  Tensor_ subt(const Right& right, const Scalar factor) const {
1306  return binary(right,
1307  [factor](const numeric_type l, const numeric_t<Right> r)
1308  -> numeric_type { return (l - r) * factor; });
1309  }
1310 
1313 
1322  template <typename Right, typename Scalar, typename Perm,
1323  typename std::enable_if<
1324  is_tensor<Right>::value && detail::is_numeric_v<Scalar> &&
1325  detail::is_permutation_v<Perm>>::type* = nullptr>
1326  Tensor_ subt(const Right& right, const Scalar factor,
1327  const Perm& perm) const {
1328  return binary(
1329  right,
1330  [factor](const numeric_type l, const numeric_t<Right> r)
1331  -> numeric_type { return (l - r) * factor; },
1332  perm);
1333  }
1334 
1336 
1339  Tensor_ subt(const numeric_type value) const { return add(-value); }
1340 
1342 
1348  template <typename Perm,
1349  typename = std::enable_if_t<detail::is_permutation_v<Perm>>>
1350  Tensor_ subt(const numeric_type value, const Perm& perm) const {
1351  return add(-value, perm);
1352  }
1353 
1355 
1359  template <typename Right,
1360  typename std::enable_if<is_tensor<Right>::value>::type* = nullptr>
1361  Tensor_& subt_to(const Right& right) {
1362  return inplace_binary(right, [](numeric_type& MADNESS_RESTRICT l,
1363  const numeric_t<Right> r) { l -= r; });
1364  }
1365 
1367 
1373  template <
1374  typename Right, typename Scalar,
1375  typename std::enable_if<is_tensor<Right>::value &&
1376  detail::is_numeric_v<Scalar>>::type* = nullptr>
1377  Tensor_& subt_to(const Right& right, const Scalar factor) {
1378  return inplace_binary(
1379  right, [factor](numeric_type& MADNESS_RESTRICT l,
1380  const numeric_t<Right> r) { (l -= r) *= factor; });
1381  }
1382 
1384 
1386  Tensor_& subt_to(const numeric_type value) { return add_to(-value); }
1387 
1388  // Multiplication operations
1389 
1391 
1396  template <typename Right,
1397  typename std::enable_if<is_tensor<Right>::value>::type* = nullptr>
1398  Tensor_ mult(const Right& right) const {
1399  return binary(
1400  right,
1401  [](const numeric_type l, const numeric_t<Right> r) -> numeric_type {
1402  return l * r;
1403  });
1404  }
1405 
1407 
1414  template <
1415  typename Right, typename Perm,
1416  typename std::enable_if<is_tensor<Right>::value &&
1417  detail::is_permutation_v<Perm>>::type* = nullptr>
1418  Tensor_ mult(const Right& right, const Perm& perm) const {
1419  return binary(
1420  right,
1421  [](const numeric_type l, const numeric_t<Right> r) -> numeric_type {
1422  return l * r;
1423  },
1424  perm);
1425  }
1426 
1428 
1435  template <
1436  typename Right, typename Scalar,
1437  typename std::enable_if<is_tensor<Right>::value &&
1438  detail::is_numeric_v<Scalar>>::type* = nullptr>
1439  Tensor_ mult(const Right& right, const Scalar factor) const {
1440  return binary(right,
1441  [factor](const numeric_type l, const numeric_t<Right> r)
1442  -> numeric_type { return (l * r) * factor; });
1443  }
1444 
1446 
1455  template <typename Right, typename Scalar, typename Perm,
1456  typename std::enable_if<
1457  is_tensor<Right>::value && detail::is_numeric_v<Scalar> &&
1458  detail::is_permutation_v<Perm>>::type* = nullptr>
1459  Tensor_ mult(const Right& right, const Scalar factor,
1460  const Perm& perm) const {
1461  return binary(
1462  right,
1463  [factor](const numeric_type l, const numeric_t<Right> r)
1464  -> numeric_type { return (l * r) * factor; },
1465  perm);
1466  }
1467 
1469 
1473  template <typename Right,
1474  typename std::enable_if<is_tensor<Right>::value>::type* = nullptr>
1475  Tensor_& mult_to(const Right& right) {
1476  return inplace_binary(right, [](numeric_type& MADNESS_RESTRICT l,
1477  const numeric_t<Right> r) { l *= r; });
1478  }
1479 
1481 
1487  template <
1488  typename Right, typename Scalar,
1489  typename std::enable_if<is_tensor<Right>::value &&
1490  detail::is_numeric_v<Scalar>>::type* = nullptr>
1491  Tensor_& mult_to(const Right& right, const Scalar factor) {
1492  return inplace_binary(
1493  right, [factor](numeric_type& MADNESS_RESTRICT l,
1494  const numeric_t<Right> r) { (l *= r) *= factor; });
1495  }
1496 
1497  // Negation operations
1498 
1500 
1502  Tensor_ neg() const {
1503  return unary([](const numeric_type r) -> numeric_type { return -r; });
1504  }
1505 
1507 
1511  template <typename Perm,
1512  typename = std::enable_if_t<detail::is_permutation_v<Perm>>>
1513  Tensor_ neg(const Perm& perm) const {
1514  return unary([](const numeric_type l) -> numeric_type { return -l; }, perm);
1515  }
1516 
1518 
1521  return inplace_unary([](numeric_type& MADNESS_RESTRICT l) { l = -l; });
1522  }
1523 
1525 
1528  Tensor_ conj() const {
1529  TA_ASSERT(pimpl_);
1530  return scale(detail::conj_op());
1531  }
1532 
1534 
1539  template <typename Scalar, typename std::enable_if<
1540  detail::is_numeric_v<Scalar>>::type* = nullptr>
1541  Tensor_ conj(const Scalar factor) const {
1542  TA_ASSERT(pimpl_);
1543  return scale(detail::conj_op(factor));
1544  }
1545 
1547 
1552  template <typename Perm,
1553  typename = std::enable_if_t<detail::is_permutation_v<Perm>>>
1554  Tensor_ conj(const Perm& perm) const {
1555  TA_ASSERT(pimpl_);
1556  return scale(detail::conj_op(), perm);
1557  }
1558 
1560 
1567  template <
1568  typename Scalar, typename Perm,
1569  typename std::enable_if<detail::is_numeric_v<Scalar> &&
1570  detail::is_permutation_v<Perm>>::type* = nullptr>
1571  Tensor_ conj(const Scalar factor, const Perm& perm) const {
1572  TA_ASSERT(pimpl_);
1573  return scale(detail::conj_op(factor), perm);
1574  }
1575 
1577 
1580  TA_ASSERT(pimpl_);
1581  return scale_to(detail::conj_op());
1582  }
1583 
1585 
1589  template <typename Scalar, typename std::enable_if<
1590  detail::is_numeric_v<Scalar>>::type* = nullptr>
1591  Tensor_& conj_to(const Scalar factor) {
1592  TA_ASSERT(pimpl_);
1593  return scale_to(detail::conj_op(factor));
1594  }
1595 
1596  // GEMM operations
1597 
1599 
1608  template <typename U, typename AU, typename V>
1609  Tensor_ gemm(const Tensor<U, AU>& other, const V factor,
1610  const math::GemmHelper& gemm_helper) const {
1612  "TA::Tensor<T>::gemm without custom element op is only "
1613  "applicable to plain tensors");
1614  // Check that this tensor is not empty and has the correct rank
1615  TA_ASSERT(pimpl_);
1616  TA_ASSERT(pimpl_->range_.rank() == gemm_helper.left_rank());
1617 
1618  // Check that the arguments are not empty and have the correct ranks
1619  TA_ASSERT(!other.empty());
1620  TA_ASSERT(other.range().rank() == gemm_helper.right_rank());
1621 
1622  // Construct the result Tensor
1623  Tensor_ result(gemm_helper.make_result_range<range_type>(pimpl_->range_,
1624  other.range()));
1625 
1626  // Check that the inner dimensions of left and right match
1628  gemm_helper.left_right_congruent(pimpl_->range_.lobound_data(),
1629  other.range().lobound_data()));
1631  gemm_helper.left_right_congruent(pimpl_->range_.upbound_data(),
1632  other.range().upbound_data()));
1633  TA_ASSERT(gemm_helper.left_right_congruent(pimpl_->range_.extent_data(),
1634  other.range().extent_data()));
1635 
1636  // Compute gemm dimensions
1638  integer m = 1, n = 1, k = 1;
1639  gemm_helper.compute_matrix_sizes(m, n, k, pimpl_->range_, other.range());
1640 
1641  // Get the leading dimension for left and right matrices.
1642  const integer lda =
1643  (gemm_helper.left_op() == TiledArray::math::blas::NoTranspose ? k : m);
1644  const integer ldb =
1645  (gemm_helper.right_op() == TiledArray::math::blas::NoTranspose ? n : k);
1646 
1647  math::blas::gemm(gemm_helper.left_op(), gemm_helper.right_op(), m, n, k,
1648  factor, pimpl_->data_, lda, other.data(), ldb,
1649  numeric_type(0), result.data(), n);
1650 
1651 #ifdef TA_ENABLE_TILE_OPS_LOGGING
1655  auto apply = [](auto& fnptr, const Range& arg) {
1656  return fnptr ? fnptr(arg) : arg;
1657  };
1658  auto tformed_left_range =
1659  apply(logger.gemm_left_range_transform, pimpl_->range_);
1660  auto tformed_right_range =
1661  apply(logger.gemm_right_range_transform, other.range());
1662  auto tformed_result_range =
1663  apply(logger.gemm_result_range_transform, result.range());
1664  if ((!logger.gemm_result_range_filter ||
1665  logger.gemm_result_range_filter(tformed_result_range)) &&
1666  (!logger.gemm_left_range_filter ||
1667  logger.gemm_left_range_filter(tformed_left_range)) &&
1668  (!logger.gemm_right_range_filter ||
1669  logger.gemm_right_range_filter(tformed_right_range))) {
1670  logger << "TA::Tensor::gemm=: left=" << tformed_left_range
1671  << " right=" << tformed_right_range
1672  << " result=" << tformed_result_range << std::endl;
1674  .gemm_print_contributions) {
1675  if (!TiledArray::TileOpsLogger<T>::get_instance().gemm_printer) {
1676  // must use custom printer if result's range transformed
1677  if (!logger.gemm_result_range_transform)
1678  logger << result << std::endl;
1679  else
1680  logger << make_map(result.data(), tformed_result_range)
1681  << std::endl;
1682  } else {
1684  *logger.log, tformed_left_range, this->data(),
1685  tformed_right_range, other.data(), tformed_right_range,
1686  result.data());
1687  }
1688  }
1689  }
1690  }
1691 #endif // TA_ENABLE_TILE_OPS_LOGGING
1692 
1693  return result;
1694  }
1695 
1697 
1740  template <typename U, typename AU, typename V, typename AV, typename W>
1741  Tensor_& gemm(const Tensor<U, AU>& left, const Tensor<V, AV>& right,
1742  const W factor, const math::GemmHelper& gemm_helper) {
1743  static_assert(
1745  "TA::Tensor<T>::gemm without custom element op is only applicable to "
1746  "plain tensors");
1747  if (this->empty()) {
1748  *this = left.gemm(right, factor, gemm_helper);
1749  } else {
1750  // Check that this tensor is not empty and has the correct rank
1751  TA_ASSERT(pimpl_);
1752  TA_ASSERT(pimpl_->range_.rank() == gemm_helper.result_rank());
1753 
1754  // Check that the arguments are not empty and have the correct ranks
1755  TA_ASSERT(!left.empty());
1756  TA_ASSERT(left.range().rank() == gemm_helper.left_rank());
1757  TA_ASSERT(!right.empty());
1758  TA_ASSERT(right.range().rank() == gemm_helper.right_rank());
1759 
1760  // Check that the outer dimensions of left match the corresponding
1761  // dimensions in result
1763  left.range().lobound_data(),
1764  pimpl_->range_.lobound_data()));
1766  left.range().upbound_data(),
1767  pimpl_->range_.upbound_data()));
1768  TA_ASSERT(gemm_helper.left_result_congruent(
1769  left.range().extent_data(), pimpl_->range_.extent_data()));
1770 
1771  // Check that the outer dimensions of right match the corresponding
1772  // dimensions in result
1774  right.range().lobound_data(),
1775  pimpl_->range_.lobound_data()));
1777  right.range().upbound_data(),
1778  pimpl_->range_.upbound_data()));
1779  TA_ASSERT(gemm_helper.right_result_congruent(
1780  right.range().extent_data(), pimpl_->range_.extent_data()));
1781 
1782  // Check that the inner dimensions of left and right match
1784  gemm_helper.left_right_congruent(left.range().lobound_data(),
1785  right.range().lobound_data()));
1787  gemm_helper.left_right_congruent(left.range().upbound_data(),
1788  right.range().upbound_data()));
1789  TA_ASSERT(gemm_helper.left_right_congruent(left.range().extent_data(),
1790  right.range().extent_data()));
1791 
1792  // Compute gemm dimensions
1794  integer m, n, k;
1795  gemm_helper.compute_matrix_sizes(m, n, k, left.range(), right.range());
1796 
1797  // Get the leading dimension for left and right matrices.
1798  const integer lda =
1799  (gemm_helper.left_op() == TiledArray::math::blas::NoTranspose ? k
1800  : m);
1801  const integer ldb =
1802  (gemm_helper.right_op() == TiledArray::math::blas::NoTranspose ? n
1803  : k);
1804 
1805  // may need to split gemm into multiply + accumulate for tracing purposes
1806 #ifdef TA_ENABLE_TILE_OPS_LOGGING
1807  {
1808  const bool twostep =
1811  .gemm_print_contributions;
1812  std::unique_ptr<T[]> data_copy;
1813  size_t tile_volume;
1814  if (twostep) {
1815  tile_volume = range().volume();
1816  data_copy = std::make_unique<T[]>(tile_volume);
1817  std::copy(pimpl_->data_, pimpl_->data_ + tile_volume,
1818  data_copy.get());
1819  }
1820  non_distributed::gemm(gemm_helper.left_op(), gemm_helper.right_op(), m,
1821  n, k, factor, left.data(), lda, right.data(), ldb,
1822  twostep ? numeric_type(0) : numeric_type(1),
1823  pimpl_->data_, n);
1824 
1828  auto apply = [](auto& fnptr, const Range& arg) {
1829  return fnptr ? fnptr(arg) : arg;
1830  };
1831  auto tformed_left_range =
1832  apply(logger.gemm_left_range_transform, left.range());
1833  auto tformed_right_range =
1834  apply(logger.gemm_right_range_transform, right.range());
1835  auto tformed_result_range =
1836  apply(logger.gemm_result_range_transform, pimpl_->range_);
1837  if ((!logger.gemm_result_range_filter ||
1838  logger.gemm_result_range_filter(tformed_result_range)) &&
1839  (!logger.gemm_left_range_filter ||
1840  logger.gemm_left_range_filter(tformed_left_range)) &&
1841  (!logger.gemm_right_range_filter ||
1842  logger.gemm_right_range_filter(tformed_right_range))) {
1843  logger << "TA::Tensor::gemm+: left=" << tformed_left_range
1844  << " right=" << tformed_right_range
1845  << " result=" << tformed_result_range << std::endl;
1847  .gemm_print_contributions) {
1849  .gemm_printer) { // default printer
1850  // must use custom printer if result's range transformed
1851  if (!logger.gemm_result_range_transform)
1852  logger << *this << std::endl;
1853  else
1854  logger << make_map(pimpl_->data_, tformed_result_range)
1855  << std::endl;
1856  } else {
1858  *logger.log, tformed_left_range, left.data(),
1859  tformed_right_range, right.data(), tformed_right_range,
1860  pimpl_->data_);
1861  }
1862  }
1863  }
1864  }
1865 
1866  if (twostep) {
1867  for (size_t v = 0; v != tile_volume; ++v) {
1868  pimpl_->data_[v] += data_copy[v];
1869  }
1870  }
1871  }
1872 #else // TA_ENABLE_TILE_OPS_LOGGING
1873  math::blas::gemm(gemm_helper.left_op(), gemm_helper.right_op(), m, n, k,
1874  factor, left.data(), lda, right.data(), ldb,
1875  numeric_type(1), pimpl_->data_, n);
1876 #endif // TA_ENABLE_TILE_OPS_LOGGING
1877  }
1878 
1879  return *this;
1880  }
1881 
1882  template <typename U, typename AU, typename V, typename AV,
1883  typename ElementMultiplyAddOp,
1884  typename = std::enable_if_t<std::is_invocable_r_v<
1885  void, std::remove_reference_t<ElementMultiplyAddOp>,
1886  value_type&, const U&, const V&>>>
1887  Tensor_& gemm(const Tensor<U, AU>& left, const Tensor<V, AV>& right,
1888  const math::GemmHelper& gemm_helper,
1889  ElementMultiplyAddOp&& elem_muladd_op) {
1890  // Check that the arguments are not empty and have the correct ranks
1891  TA_ASSERT(!left.empty());
1892  TA_ASSERT(left.range().rank() == gemm_helper.left_rank());
1893  TA_ASSERT(!right.empty());
1894  TA_ASSERT(right.range().rank() == gemm_helper.right_rank());
1895 
1896  // Check that the inner dimensions of left and right match
1898  gemm_helper.left_right_congruent(left.range().lobound_data(),
1899  right.range().lobound_data()));
1901  gemm_helper.left_right_congruent(left.range().upbound_data(),
1902  right.range().upbound_data()));
1903  TA_ASSERT(gemm_helper.left_right_congruent(left.range().extent_data(),
1904  right.range().extent_data()));
1905 
1906  if (this->empty()) { // initialize, if empty
1907  *this = Tensor_(gemm_helper.make_result_range<range_type>(left.range(),
1908  right.range()));
1909  } else {
1910  // Check that the outer dimensions of left match the corresponding
1911  // dimensions in result
1913  left.range().lobound_data(),
1914  pimpl_->range_.lobound_data()));
1916  left.range().upbound_data(),
1917  pimpl_->range_.upbound_data()));
1918  TA_ASSERT(gemm_helper.left_result_congruent(
1919  left.range().extent_data(), pimpl_->range_.extent_data()));
1920 
1921  // Check that the outer dimensions of right match the corresponding
1922  // dimensions in result
1924  right.range().lobound_data(),
1925  pimpl_->range_.lobound_data()));
1927  right.range().upbound_data(),
1928  pimpl_->range_.upbound_data()));
1929  TA_ASSERT(gemm_helper.right_result_congruent(
1930  right.range().extent_data(), pimpl_->range_.extent_data()));
1931  }
1932 
1933  // Compute gemm dimensions
1935  integer M, N, K;
1936  gemm_helper.compute_matrix_sizes(M, N, K, left.range(), right.range());
1937 
1938  // Get the leading dimension for left and right matrices.
1939  const integer lda =
1940  (gemm_helper.left_op() == TiledArray::math::blas::NoTranspose ? K : M);
1941  const integer ldb =
1942  (gemm_helper.right_op() == TiledArray::math::blas::NoTranspose ? N : K);
1943 
1944  for (integer m = 0; m != M; ++m) {
1945  for (integer n = 0; n != N; ++n) {
1946  auto c_offset = m * N + n;
1947  for (integer k = 0; k != K; ++k) {
1948  auto a_offset =
1949  gemm_helper.left_op() == TiledArray::math::blas::NoTranspose
1950  ? m * lda + k
1951  : k * lda + m;
1952  auto b_offset =
1953  gemm_helper.right_op() == TiledArray::math::blas::NoTranspose
1954  ? k * ldb + n
1955  : n * ldb + k;
1956  elem_muladd_op(*(pimpl_->data_ + c_offset), *(left.data() + a_offset),
1957  *(right.data() + b_offset));
1958  }
1959  }
1960  }
1961 
1962  return *this;
1963  }
1964 
1965  // Reduction operations
1966 
1968 
1973  template <typename TileType = Tensor_,
1975  decltype(auto) trace() const {
1976  return TiledArray::trace(*this);
1977  }
1978 
1980 
1995  template <typename ReduceOp, typename JoinOp, typename Scalar>
1996  decltype(auto) reduce(ReduceOp&& reduce_op, JoinOp&& join_op,
1997  Scalar identity) const {
1998  return detail::tensor_reduce(reduce_op, join_op, identity, *this);
1999  }
2000 
2002 
2020  template <typename Right, typename ReduceOp, typename JoinOp, typename Scalar,
2021  typename std::enable_if<is_tensor<Right>::value>::type* = nullptr>
2022  decltype(auto) reduce(const Right& other, ReduceOp&& reduce_op,
2023  JoinOp&& join_op, Scalar identity) const {
2024  return detail::tensor_reduce(reduce_op, join_op, identity, *this, other);
2025  }
2026 
2028 
2030  numeric_type sum() const {
2031  auto sum_op = [](numeric_type& MADNESS_RESTRICT res,
2032  const numeric_type arg) { res += arg; };
2033  return reduce(sum_op, sum_op, numeric_type(0));
2034  }
2035 
2037 
2040  auto mult_op = [](numeric_type& MADNESS_RESTRICT res,
2041  const numeric_type arg) { res *= arg; };
2042  return reduce(mult_op, mult_op, numeric_type(1));
2043  }
2044 
2046 
2049  auto square_op = [](scalar_type& MADNESS_RESTRICT res,
2050  const numeric_type arg) {
2051  res += TiledArray::detail::norm(arg);
2052  };
2053  auto sum_op = [](scalar_type& MADNESS_RESTRICT res, const scalar_type arg) {
2054  res += arg;
2055  };
2056  return reduce(square_op, sum_op, scalar_type(0));
2057  }
2058 
2060 
2064  template <typename ResultType = scalar_type>
2065  ResultType norm() const {
2066  return std::sqrt(static_cast<ResultType>(squared_norm()));
2067  }
2068 
2070 
2072  template <typename Numeric = numeric_type>
2074  typename std::enable_if<
2075  detail::is_strictly_ordered<Numeric>::value>::type* = nullptr) const {
2076  auto min_op = [](numeric_type& MADNESS_RESTRICT res,
2077  const numeric_type arg) { res = std::min(res, arg); };
2078  return reduce(min_op, min_op, std::numeric_limits<numeric_type>::max());
2079  }
2080 
2082 
2084  template <typename Numeric = numeric_type>
2086  typename std::enable_if<
2087  detail::is_strictly_ordered<Numeric>::value>::type* = nullptr) const {
2088  auto max_op = [](numeric_type& MADNESS_RESTRICT res,
2089  const numeric_type arg) { res = std::max(res, arg); };
2090  return reduce(max_op, max_op, std::numeric_limits<scalar_type>::min());
2091  }
2092 
2094 
2097  auto abs_min_op = [](scalar_type& MADNESS_RESTRICT res,
2098  const numeric_type arg) {
2099  res = std::min(res, std::abs(arg));
2100  };
2101  auto min_op = [](scalar_type& MADNESS_RESTRICT res, const scalar_type arg) {
2102  res = std::min(res, arg);
2103  };
2104  return reduce(abs_min_op, min_op, std::numeric_limits<scalar_type>::max());
2105  }
2106 
2108 
2111  auto abs_max_op = [](scalar_type& MADNESS_RESTRICT res,
2112  const numeric_type arg) {
2113  res = std::max(res, std::abs(arg));
2114  };
2115  auto max_op = [](scalar_type& MADNESS_RESTRICT res, const scalar_type arg) {
2116  res = std::max(res, arg);
2117  };
2118  return reduce(abs_max_op, max_op, scalar_type(0));
2119  }
2120 
2122 
2128  template <typename Right,
2129  typename std::enable_if<is_tensor<Right>::value>::type* = nullptr>
2130  numeric_type dot(const Right& other) const {
2131  auto mult_add_op = [](numeric_type& res, const numeric_type l,
2132  const numeric_t<Right> r) { res += l * r; };
2133  auto add_op = [](numeric_type& MADNESS_RESTRICT res,
2134  const numeric_type value) { res += value; };
2135  return reduce(other, mult_add_op, add_op, numeric_type(0));
2136  }
2137 
2139 
2145  template <typename Right,
2146  typename std::enable_if<is_tensor<Right>::value>::type* = nullptr>
2147  numeric_type inner_product(const Right& other) const {
2148  auto mult_add_op = [](numeric_type& res, const numeric_type l,
2149  const numeric_t<Right> r) {
2150  res += TiledArray::detail::inner_product(l, r);
2151  };
2152  auto add_op = [](numeric_type& MADNESS_RESTRICT res,
2153  const numeric_type value) { res += value; };
2154  return reduce(other, mult_add_op, add_op, numeric_type(0));
2155  }
2156 
2157 }; // class Tensor
2158 
2159 template <typename T, typename A>
2160 const typename Tensor<T, A>::range_type Tensor<T, A>::empty_range_;
2161 
2162 template <typename T, typename A>
2163 bool operator==(const Tensor<T, A>& a, const Tensor<T, A>& b) {
2164  return a.range() == b.range() &&
2165  std::equal(a.data(), a.data() + a.size(), b.data());
2166 }
2167 template <typename T, typename A>
2168 bool operator!=(const Tensor<T, A>& a, const Tensor<T, A>& b) {
2169  return !(a == b);
2170 }
2171 
2172 namespace detail {
2173 
2179 template <typename T, typename A>
2180 struct Trace<Tensor<T, A>, detail::enable_if_numeric_t<T>> {
2181  decltype(auto) operator()(const Tensor<T>& t) const {
2182  using size_type = typename Tensor<T>::size_type;
2183  using value_type = typename Tensor<T>::value_type;
2184  const auto range = t.range();
2185 
2186  // Get pointers to the range data
2187  const size_type n = range.rank();
2188  const auto* MADNESS_RESTRICT const lower = range.lobound_data();
2189  const auto* MADNESS_RESTRICT const upper = range.upbound_data();
2190  const auto* MADNESS_RESTRICT const stride = range.stride_data();
2191 
2192  // Search for the largest lower bound and the smallest upper bound
2193  const size_type lower_max = *std::max_element(lower, lower + n);
2194  const size_type upper_min = *std::min_element(upper, upper + n);
2195 
2196  value_type result = 0;
2197 
2198  if (lower_max >= upper_min) return result; // No diagonal element in tile
2199 
2200  // Compute the first and last ordinal index
2201  size_type first = 0ul, last = 0ul, trace_stride = 0ul;
2202  for (size_type i = 0ul; i < n; ++i) {
2203  const size_type lower_i = lower[i];
2204  const size_type stride_i = stride[i];
2205 
2206  first += (lower_max - lower_i) * stride_i;
2207  last += (upper_min - lower_i) * stride_i;
2208  trace_stride += stride_i;
2209  }
2210 
2211  // Compute the trace
2212  const value_type* MADNESS_RESTRICT const data = &t[first];
2213  for (; first < last; first += trace_stride) result += data[first];
2214 
2215  return result;
2216  }
2217 };
2218 
2220 template <typename T, typename A>
2221 struct transform<Tensor<T, A>> {
2222  template <typename Op, typename T1>
2223  Tensor<T, A> operator()(Op&& op, T1&& t1) const {
2224  return Tensor<T, A>(std::forward<T1>(t1), std::forward<Op>(op));
2225  }
2226  template <typename Op, typename Perm, typename T1,
2227  typename = std::enable_if_t<
2228  detail::is_permutation_v<std::remove_reference_t<Perm>>>>
2229  Tensor<T, A> operator()(Op&& op, Perm&& perm, T1&& t1) const {
2230  return Tensor<T, A>(std::forward<T1>(t1), std::forward<Op>(op),
2231  std::forward<Perm>(perm));
2232  }
2233  template <typename Op, typename T1, typename T2>
2234  Tensor<T, A> operator()(Op&& op, T1&& t1, T2&& t2) const {
2235  return Tensor<T, A>(std::forward<T1>(t1), std::forward<T2>(t2),
2236  std::forward<Op>(op));
2237  }
2238  template <typename Op, typename Perm, typename T1, typename T2,
2239  typename = std::enable_if_t<
2240  detail::is_permutation_v<std::remove_reference_t<Perm>>>>
2241  Tensor<T, A> operator()(Op&& op, Perm&& perm, T1&& t1, T2&& t2) const {
2242  return Tensor<T, A>(std::forward<T1>(t1), std::forward<T2>(t2),
2243  std::forward<Op>(op), std::forward<Perm>(perm));
2244  }
2245 };
2246 } // namespace detail
2247 
2248 #ifndef TILEDARRAY_HEADER_ONLY
2249 
2250 extern template class Tensor<double, Eigen::aligned_allocator<double>>;
2251 extern template class Tensor<float, Eigen::aligned_allocator<float>>;
2252 extern template class Tensor<int, Eigen::aligned_allocator<int>>;
2253 extern template class Tensor<long, Eigen::aligned_allocator<long>>;
2254 // extern template
2255 // class Tensor<std::complex<double>,
2256 // Eigen::aligned_allocator<std::complex<double> > >; extern template class
2257 // Tensor<std::complex<float>, Eigen::aligned_allocator<std::complex<float> >
2258 // >;
2259 
2260 #endif // TILEDARRAY_HEADER_ONLY
2261 
2262 } // namespace TiledArray
2263 
2264 #endif // TILEDARRAY_TENSOR_TENSOR_H__INCLUDED
Tensor_ shift(const Index &bound_shift) const
Shift the lower and upper bound of this range.
Definition: tensor.h:917
R make_result_range(const Left &left, const Right &right) const
Construct a result range based on left and right ranges.
Definition: gemm_helper.h:165
detail::TensorInterface< const T, BlockRange > block(const Index1 &lower_bound, const Index2 &upper_bound) const
Constructs a view of the block defined by lower_bound and upper_bound.
Definition: tensor.h:714
TiledArray::detail::numeric_type< T >::type numeric_type
the numeric type that supports T
Definition: tensor.h:79
reference operator[](const Index &i)
Element accessor.
Definition: tensor.h:456
const index1_type * lobound_data() const
Range lower bound data accessor.
Definition: range.h:685
Tensor< T, A > Tensor_
This class type.
Definition: tensor.h:55
Scalar tensor_reduce(ReduceOp &&reduce_op, JoinOp &&join_op, Scalar identity, const T1 &tensor1, const Ts &... tensors)
Reduction operation for contiguous tensors.
Definition: kernels.h:665
Contraction to *GEMM helper.
Definition: gemm_helper.h:40
ResultType norm() const
Vector 2-norm.
Definition: tensor.h:2065
Tensor(const Range &range, const U *u)
Definition: tensor.h:208
Tensor_ & shift_to(const Index &bound_shift)
Shift the lower and upper bound of this tensor.
Definition: tensor.h:890
archive_array< T > wrap(const T *, unsigned int)
iterator end()
Iterator factory.
Definition: tensor.h:602
auto clone_range(const T &tensor)
Create a copy of the range of the tensor.
Definition: utility.h:47
detail::TensorInterface< T, BlockRange > block(const Index1 &lower_bound, const Index2 &upper_bound)
Constructs a view of the block defined by lower_bound and upper_bound.
Definition: tensor.h:704
numeric_type sum() const
Sum of elements.
Definition: tensor.h:2030
::blas::Op Op
Definition: blas.h:46
unsigned int left_rank() const
Left-hand argument rank accessor.
Definition: gemm_helper.h:138
Tensor_ & subt_to(const numeric_type value)
Subtract a constant from this tensor.
Definition: tensor.h:1386
Tensor_ & mult_to(const Right &right)
Multiply this tensor by right.
Definition: tensor.h:1475
allocator_type::pointer pointer
Element pointer type.
Definition: tensor.h:71
Range range_type
Tensor range type.
Definition: tensor.h:59
Tensor_ mult(const Right &right, const Scalar factor) const
Scale and multiply this by right to create a new tensor.
Definition: tensor.h:1439
pointer iterator
Element iterator type.
Definition: tensor.h:76
Tensor_ add(const Right &right, const Perm &perm) const
Add this and other to construct a new, permuted tensor.
Definition: tensor.h:1141
const range_type & range() const
Tensor range object accessor.
Definition: tensor.h:384
detail::TensorInterface< const T, BlockRange > block(const PairRange &bounds) const
Constructs a view of the block defined by its bounds .
Definition: tensor.h:800
Tensor(const range_type &range, InIter it)
Construct an evaluated tensor.
Definition: tensor.h:200
void swap(Bitset< B > &b0, Bitset< B > &b1)
Definition: bitset.h:565
blas::Op left_op() const
Definition: gemm_helper.h:275
int64_t integer
Definition: blas.h:44
bool right_result_congruent(const Right &right, const Result &result) const
Definition: gemm_helper.h:221
detail::TensorInterface< const T, BlockRange > block(const std::initializer_list< Index1 > &lower_bound, const std::initializer_list< Index2 > &upper_bound) const
Constructs a view of the block defined by lower_bound and upper_bound.
Definition: tensor.h:755
bool operator==(const BlockRange &r1, const BlockRange &r2)
BlockRange equality comparison.
Definition: block_range.h:433
decltype(auto) trace() const
Generalized tensor trace.
Definition: tensor.h:1975
void reduce_op(ReduceOp &&reduce_op, JoinOp &&join_op, const Result &identity, const std::size_t n, Result &result, const Args *const ... args)
Definition: vector_op.h:628
Tensor(const range_type &range)
Construct tensor.
Definition: tensor.h:162
Tensor(Tensor_ &&other)
Definition: tensor.h:146
bool empty() const
Test if the tensor is empty.
Definition: tensor.h:620
Tensor< T, A > operator()(Op &&op, T1 &&t1) const
Definition: tensor.h:2223
Type trait for extracting the scalar type of tensors and arrays.
Definition: type_traits.h:744
bool left_right_congruent(const Left &left, const Right &right) const
Definition: gemm_helper.h:238
constexpr const bool is_tensor_of_tensor_v
Definition: type_traits.h:155
std::enable_if_t< trace_is_defined_v< T >, U > enable_if_trace_is_defined_t
SFINAE type for enabling code when the trace operation is defined.
Definition: trace.h:61
Tensor_ & operator=(const Tensor_ &other)
Definition: tensor.h:148
Tensor_ conj(const Scalar factor, const Perm &perm) const
Create a complex conjugated, scaled, and permuted copy of this tensor.
Definition: tensor.h:1571
Tensor_ scale(const Scalar factor, const Perm &perm) const
Construct a scaled and permuted copy of this tensor.
Definition: tensor.h:1093
Tensor_ & shift_to(const std::initializer_list< Integer > &bound_shift)
Shift the lower and upper bound of this tensor.
Definition: tensor.h:903
Tensor_ gemm(const Tensor< U, AU > &other, const V factor, const math::GemmHelper &gemm_helper) const
Contract this tensor with other.
Definition: tensor.h:1609
Tensor_ add(const numeric_type value) const
Add a constant to a copy of this tensor.
Definition: tensor.h:1195
Tensor_ shift(const std::initializer_list< Integer > &bound_shift) const
Shift the lower and upper bound of this range.
Definition: tensor.h:931
scalar_type abs_max() const
Absolute maximum element.
Definition: tensor.h:2110
Tensor_ & add_to(const Right &right)
Add other to this tensor.
Definition: tensor.h:1222
range_type::index1_type index1_type
1-index type
Definition: tensor.h:60
Tensor_ binary(const Right &right, Op &&op) const
Use a binary, element wise operation to construct a new tensor.
Definition: tensor.h:950
Tensor(const T1 &left, const T2 &right, Op &&op, const Perm &perm)
Copy, modify, and permute the data from left, and right.
Definition: tensor.h:340
constexpr bool operator!=(const DenseShape &a, const DenseShape &b)
Definition: dense_shape.h:382
Tensor_ clone() const
Definition: tensor.h:359
static std::enable_if_t< Singleton< D >::derived_is_default_constructible, D & > get_instance()
Definition: singleton.h:70
TA_1INDEX_TYPE index1_type
Definition: range.h:49
KroneckerDeltaTile< _N >::numeric_type min(const KroneckerDeltaTile< _N > &arg)
decltype(auto) trace(const Tile< Arg > &arg)
Sum the hyper-diagonal elements a tile.
Definition: tile.h:1477
Tensor< T, A > operator()(Op &&op, Perm &&perm, T1 &&t1) const
Definition: tensor.h:2229
bool left_result_congruent(const Left &left, const Result &result) const
Definition: gemm_helper.h:205
TensorMap< T, Range_, OpResult > make_map(T *const data, const Index &lower_bound, const Index &upper_bound)
Definition: tensor_map.h:53
const index1_type * upbound_data() const
Range upper bound data accessor.
Definition: range.h:710
Tensor_ add(const Right &right, const Scalar factor) const
Scale and add this and other to construct a new tensor.
Definition: tensor.h:1162
Tensor< T, A > operator()(Op &&op, T1 &&t1, T2 &&t2) const
Definition: tensor.h:2234
void swap(Tensor_ &other)
Swap tensor data.
Definition: tensor.h:678
Tensor_ & inplace_unary(Op &&op)
Use a unary, element wise operation to modify this tensor.
Definition: tensor.h:1062
Tensor_ subt(const Right &right, const Perm &perm) const
Subtract right from this and return the result permuted by perm.
Definition: tensor.h:1283
reference operator()(const Index &i)
Element accessor.
Definition: tensor.h:516
detail::TensorInterface< T, BlockRange > block(const PairRange &bounds)
Constructs a view of the block defined by its bounds .
Definition: tensor.h:808
Tensor_ & inplace_binary(const Right &right, Op &&op)
Use a binary, element wise operation to modify this tensor.
Definition: tensor.h:1005
Tensor_ mult(const Right &right, const Perm &perm) const
Multiply this by right to create a new, permuted tensor.
Definition: tensor.h:1418
Range that references a subblock of another range.
Definition: block_range.h:34
Tensor(const Tensor_ &other)
Definition: tensor.h:145
Tensor_ conj() const
Create a complex conjugated copy of this tensor.
Definition: tensor.h:1528
TILEDARRAY_FORCE_INLINE auto inner_product(const L l, const R r)
Inner product of a real value and a numeric value.
Definition: complex.h:67
numeric_type max(typename std::enable_if< detail::is_strictly_ordered< Numeric >::value >::type *=nullptr) const
Maximum element.
Definition: tensor.h:2085
auto outer(const Permutation &p)
Definition: permutation.h:820
const_pointer data() const
Data direct access.
Definition: tensor.h:609
numeric_type dot(const Right &other) const
Vector dot (not inner!) product.
Definition: tensor.h:2130
Tensor_ conj(const Perm &perm) const
Create a complex conjugated and permuted copy of this tensor.
Definition: tensor.h:1554
Tensor_ scale(const Scalar factor) const
Construct a scaled copy of this tensor.
Definition: tensor.h:1077
scalar_type squared_norm() const
Square of vector 2-norm.
Definition: tensor.h:2048
detail::TensorInterface< T, BlockRange > block(const std::initializer_list< Index1 > &lower_bound, const std::initializer_list< Index2 > &upper_bound)
Constructs a view of the block defined by lower_bound and upper_bound.
Definition: tensor.h:744
reference operator()(const Index &... i)
Element accessor.
Definition: tensor.h:576
Tensor_ mult(const Right &right) const
Multiply this by right to create a new tensor.
Definition: tensor.h:1398
std::enable_if<!(detail::is_scalar_v< Arg > &&detail::is_scalar_v< Result >)>::type uninitialized_copy_vector(const std::size_t n, const Arg *const arg, Result *const result)
Definition: vector_op.h:674
std::enable_if_t< is_numeric_v< T >, U > enable_if_numeric_t
SFINAE type for enabling code when T is a numeric type.
Definition: type_traits.h:649
std::enable_if<!detail::is_scalar_v< Arg > >::type destroy_vector(const std::size_t n, Arg *const arg)
Definition: vector_op.h:706
Tensor_ & gemm(const Tensor< U, AU > &left, const Tensor< V, AV > &right, const W factor, const math::GemmHelper &gemm_helper)
Contract two tensors and accumulate the scaled result to this tensor.
Definition: tensor.h:1741
Tensor_ & add_to(const Right &right, const Scalar factor)
Add other to this tensor, and scale the result.
Definition: tensor.h:1238
const_pointer const_iterator
Element const iterator type.
Definition: tensor.h:77
#define TA_ASSERT(EXPR,...)
Definition: error.h:39
const_iterator end() const
Iterator factory.
Definition: tensor.h:595
auto inner_size(const Permutation &p)
Definition: permutation.h:822
unsigned int result_rank() const
Result rank accessor.
Definition: gemm_helper.h:133
range_type & range()
Tensor range object mutable accessor.
Definition: tensor.h:392
Tensor_ & subt_to(const Right &right)
Subtract right from this tensor.
Definition: tensor.h:1361
void ignore_tile_position(bool b)
Definition: utility.h:81
Tensor_ subt(const numeric_type value, const Perm &perm) const
Subtract a constant from a permuted copy of this tensor.
Definition: tensor.h:1350
Tensor_ permute(const Perm &perm) const
Create a permuted copy of this tensor.
Definition: tensor.h:853
KroneckerDeltaTile< _N >::numeric_type max(const KroneckerDeltaTile< _N > &arg)
void gemm(Op op_a, Op op_b, const integer m, const integer n, const integer k, const S1 alpha, const T1 *a, const integer lda, const T2 *b, const integer ldb, const S2 beta, T3 *c, const integer ldc)
Definition: blas.h:71
Tensor_ subt(const Right &right) const
Subtract right from this and return the result.
Definition: tensor.h:1263
detail::TensorInterface< T, BlockRange > block(const std::initializer_list< std::initializer_list< Index >> &bounds)
Constructs a view of the block defined by its bounds .
Definition: tensor.h:839
const_reference operator[](const Ordinal ord) const
Const element accessor.
Definition: tensor.h:411
Tensor_ & operator=(const T1 &other)
Definition: tensor.h:371
iterator begin()
Iterator factory.
Definition: tensor.h:590
detail::TensorInterface< const T, BlockRange > block(const std::initializer_list< std::initializer_list< Index >> &bounds) const
Constructs a view of the block defined by its bounds .
Definition: tensor.h:831
Tensor_ & mult_to(const Right &right, const Scalar factor)
Scale and multiply this tensor by right.
Definition: tensor.h:1491
Tensor_ & subt_to(const Right &right, const Scalar factor)
Subtract right from and scale this tensor.
Definition: tensor.h:1377
numeric_type product() const
Product of elements.
Definition: tensor.h:2039
Tensor_ subt(const Right &right, const Scalar factor, const Perm &perm) const
Definition: tensor.h:1326
Tensor_ neg(const Perm &perm) const
Create a negated and permuted copy of this tensor.
Definition: tensor.h:1513
void serialize(Archive &ar)
Output serialization function.
Definition: tensor.h:630
std::enable_if<!(detail::is_scalar_v< Arg > &&detail::is_scalar_v< Result >)>::type uninitialized_fill_vector(const std::size_t n, const Arg &arg, Result *const result)
Definition: vector_op.h:691
range_type::ordinal_type size_type
Size type (to meet the container concept)
Definition: tensor.h:63
Permute a tile.
Definition: permute.h:134
void compute_matrix_sizes(blas::integer &m, blas::integer &n, blas::integer &k, const Left &left, const Right &right) const
Compute the matrix dimension that can be used in a *GEMM call.
Definition: gemm_helper.h:254
allocator_type::value_type value_type
Array element type.
Definition: tensor.h:66
Tensor(const T1 &left, const T2 &right, Op &&op)
Copy and modify the data from left, and right.
Definition: tensor.h:321
const_reference operator()(const Index &i) const
Const element accessor.
Definition: tensor.h:501
blas::Op right_op() const
Definition: gemm_helper.h:276
Tensor_ & operator=(Tensor_ &&other)
Definition: tensor.h:152
Tensor_ & scale_to(const Scalar factor)
Scale this tensor.
Definition: tensor.h:1106
Tensor_ add(const Right &right) const
Add this and other to construct a new tensors.
Definition: tensor.h:1121
Tensor interface for external data.
unsigned int right_rank() const
Right-hand argument rank accessor.
Definition: gemm_helper.h:143
Tensor_ mult(const Right &right, const Scalar factor, const Perm &perm) const
Scale and multiply this by right to create a new, permuted tensor.
Definition: tensor.h:1459
scalar_type abs_min() const
Absolute minimum element.
Definition: tensor.h:2096
reference operator[](const std::initializer_list< Integer > &i)
Element accessor.
Definition: tensor.h:486
Tensor_ & add_to(const numeric_type value)
Add a constant to this tensor.
Definition: tensor.h:1248
Tensor_ subt(const numeric_type value) const
Subtract a constant from a copy of this tensor.
Definition: tensor.h:1339
reference operator()(const std::initializer_list< Integer > &i)
Element accessor.
Definition: tensor.h:546
Tensor(const T1 &other, const Perm &perm)
Construct a permuted tensor copy.
Definition: tensor.h:244
allocator_type::reference reference
Element reference type.
Definition: tensor.h:68
const_reference operator[](const std::initializer_list< Integer > &i) const
Const element accessor.
Definition: tensor.h:471
Tensor_ binary(const Right &right, Op &&op, const Perm &perm) const
Use a binary, element wise operation to construct a new, permuted tensor.
Definition: tensor.h:968
auto abs(const ComplexConjugate< T > &a)
Definition: complex.h:270
btas::Tensor< T, Range, Storage > gemm(const btas::Tensor< T, Range, Storage > &left, const btas::Tensor< T, Range, Storage > &right, Scalar factor, const TiledArray::math::GemmHelper &gemm_helper)
Definition: btas.h:596
pointer data()
Data direct access.
Definition: tensor.h:614
decltype(auto) reduce(ReduceOp &&reduce_op, JoinOp &&join_op, Scalar identity) const
Unary reduction operation.
Definition: tensor.h:1996
reference operator[](const Ordinal ord)
Element accessor.
Definition: tensor.h:426
numeric_type min(typename std::enable_if< detail::is_strictly_ordered< Numeric >::value >::type *=nullptr) const
Minimum element.
Definition: tensor.h:2073
const_reference operator()(const Index &... i) const
Const element accessor.
Definition: tensor.h:561
ComplexConjugate< S > conj_op(const S factor)
ComplexConjugate operator factory function.
Definition: complex.h:204
Tensor_ subt(const Right &right, const Scalar factor) const
Definition: tensor.h:1305
Tensor(const Range &range, std::initializer_list< T > il)
Definition: tensor.h:213
Tensor_ unary(Op &&op) const
Use a unary, element wise operation to construct a new tensor.
Definition: tensor.h:1018
Tensor_ add(const numeric_type value, const Perm &perm) const
Add a constant to a permuted copy of this tensor.
Definition: tensor.h:1209
Tensor_ add(const Right &right, const Scalar factor, const Perm &perm) const
Scale and add this and other to construct a new, permuted tensor.
Definition: tensor.h:1182
const_reference operator[](const Index &i) const
Const element accessor.
Definition: tensor.h:441
ordinal_type size() const
Tensor dimension size accessor.
Definition: tensor.h:400
Tensor_ & gemm(const Tensor< U, AU > &left, const Tensor< V, AV > &right, const math::GemmHelper &gemm_helper, ElementMultiplyAddOp &&elem_muladd_op)
Definition: tensor.h:1887
Tensor(const T1 &other, Op &&op)
Copy and modify the data from other.
Definition: tensor.h:276
TILEDARRAY_FORCE_INLINE R norm(const R r)
Wrapper function for std::norm
Definition: complex.h:93
Tensor_ & neg_to()
Negate elements of this tensor.
Definition: tensor.h:1520
ordinal_type volume() const
Range volume accessor.
Definition: range.h:783
allocator_type::const_pointer const_pointer
Element const pointer type.
Definition: tensor.h:73
Tensor(const T1 &other)
Construct a copy of a tensor interface object.
Definition: tensor.h:227
Type trait for extracting the numeric type of tensors and arrays.
Definition: type_traits.h:709
range_type::ordinal_type ordinal_type
Ordinal type.
Definition: tensor.h:61
Tensor_ conj(const Scalar factor) const
Create a complex conjugated and scaled copy of this tensor.
Definition: tensor.h:1541
Tensor< T, A > operator()(Op &&op, Perm &&perm, T1 &&t1, T2 &&t2) const
Definition: tensor.h:2241
allocator_type::const_reference const_reference
Element reference type.
Definition: tensor.h:70
Tensor_ & conj_to()
Complex conjugate this tensor.
Definition: tensor.h:1579
void inplace_tensor_op(Op &&op, TR &result, const Ts &... tensors)
In-place tensor operations with contiguous data.
Definition: kernels.h:197
Tensor_ unary(Op &&op, const Perm &perm) const
Use a unary, element wise operation to construct a new, permuted tensor.
Definition: tensor.h:1034
numeric_type inner_product(const Right &other) const
Vector inner product.
Definition: tensor.h:2147
void tensor_init(Op &&op, TR &result, const Ts &... tensors)
Initialize tensor with contiguous tensor arguments.
Definition: kernels.h:421
An N-dimensional tensor object.
Definition: tensor.h:50
allocator_type::difference_type difference_type
Difference type.
Definition: tensor.h:75
const_iterator begin() const
Iterator factory.
Definition: tensor.h:585
const_reference operator()(const std::initializer_list< Integer > &i) const
Const element accessor.
Definition: tensor.h:531
T identity()
identity for group of objects of type T
Tensor(const range_type &range, const Value &value)
Construct a tensor with a fill value.
Definition: tensor.h:175
Tensor(const T1 &other, Op &&op, const Perm &perm)
Copy, modify, and permute the data from other.
Definition: tensor.h:292
std::size_t ordinal_type
Ordinal type, to conform to TWG spec.
Definition: range.h:59
const index1_type * extent_data() const
Range extent data accessor.
Definition: range.h:735
Tensor_ & conj_to(const Scalar factor)
Complex conjugate and scale this tensor.
Definition: tensor.h:1591
Tensor_ neg() const
Create a negated copy of this tensor.
Definition: tensor.h:1502
unsigned int rank() const
Rank accessor.
Definition: range.h:669
Create a deep copy of a tile.
Definition: clone.h:128
TiledArray::detail::scalar_type< T >::type scalar_type
the scalar type that supports T
Definition: tensor.h:81
A allocator_type
Allocator type.
Definition: tensor.h:64
A (hyperrectangular) interval on , space of integer -indices.
Definition: range.h:46
auto inner(const Permutation &p)
Definition: permutation.h:813