TensorIndex.hpp
Go to the documentation of this file.
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 /// \file
5 /// Defines generic tensor indices used in `TensorExpression`s
6 
7 #pragma once
8 
9 #include <array>
10 #include <cassert>
11 #include <cstddef>
12 #include <type_traits>
13 
16 #include "Utilities/Requires.hpp"
17 #include "Utilities/TMPL.hpp"
18 
19 namespace TensorIndex_detail {
20 // The below values are used to separate upper indices from lower indices and
21 // spatial indices from spacetime indices.
22 //
23 // Tensor expressions perform as many calculations as possible in a constexpr
24 // context, which means working with fundamental types, specifically integer
25 // types, is easiest. By using sentinel values defined in one location we can
26 // easily control the encoding without having magic values floating around in
27 // many places. Furthermore, encoding all the information in the `size_t` means
28 // that when a failure occurs in one of the constexpr calculations it is
29 // reasonably easy to debug because, while encoded, the full type information is
30 // present. This approach can effectively be thought of as using specific bits
31 // in the `size_t` to mark information, using the size_t more as a bitfield than
32 // anything else. For human readability, we use base-10 numbers instead of
33 // base-2 values that would truly set individual bits.
34 //
35 // Spacetime indices are represented by values [0, `spatial_sentinel`) and
36 // spatial indices are represented by values
37 // [`spatial_sentinel`, `max_sentinel`). Lower spacetime indices are represented
38 // by values [0, `upper_sentinel`), and upper spacetime indices are represented
39 // by values [`upper_sentinel`, `spatial_sentinel`). Lower spatial indices are
40 // represented by values
41 // [`spatial_sentinel`, `spatial_sentinel` + `upper_sentinel`), and upper
42 // spatial indices are represented by values
43 // [`spatial_sentinel` + `upper_sentinel`, `max_sentinel`). Values equal to or
44 // above `max_sentinel` are considered invalid for representing an index.
45 static constexpr size_t spatial_sentinel = 1000;
46 static constexpr size_t upper_sentinel = 500;
47 static constexpr size_t upper_spatial_sentinel =
48  spatial_sentinel + upper_sentinel;
49 static constexpr size_t max_sentinel = 2000;
50 } // namespace TensorIndex_detail
51 
52 /*!
53  * \ingroup TensorExpressionsGroup
54  * \brief Represents the geeric indices in a TensorExpression
55  *
56  * \details
57  * Used to denote a tensor index in a tensor slot. This allows the following
58  * type of expressions to work:
59  * \code{.cpp}
60  * auto T = evaluate<ti_a, ti_b>(F(ti_a, ti_b) + S(ti_b, ti_a));
61  * \endcode
62  * where `decltype(ti_a) == TensorIndex<0>` and
63  * `decltype(ti_b) == TensorIndex<1>`. That is, `ti_a` and `ti_b` are
64  * placeholders for objects of type `TensorIndex<0>` and `TensorIndex<1>`,
65  * respectively.
66  */
67 template <std::size_t I,
68  Requires<(I < TensorIndex_detail::max_sentinel)> = nullptr>
69 struct TensorIndex {
70  using value_type = std::size_t;
71  using type = TensorIndex<I>;
72  static constexpr value_type value = I;
73  static constexpr UpLo valence =
74  ((I < TensorIndex_detail::upper_sentinel) or
75  (I >= TensorIndex_detail::spatial_sentinel and
76  I < TensorIndex_detail::upper_spatial_sentinel))
77  ? UpLo::Lo
78  : UpLo::Up;
79  static constexpr bool is_spacetime = I < TensorIndex_detail::spatial_sentinel;
80 };
81 
82 /*!
83  * \ingroup TensorExpressionsGroup
84  * \brief Returns the TensorIndex value of with opposite valence.
85  *
86  * \details The input value represents a TensorIndex value, which encodes
87  * both the valence of the index and whether the index is spacetime or
88  * spatial. This function returns the value that corresponds to the encoding of
89  * the TensorIndex with the same index type, but opposite valence.
90  *
91  * For example, 0 is the TensorIndex value for `ti_a`. If `i == 0`, then 500
92  * will be returned, which is the TensorIndex value for `ti_A`. If `i == 500`
93  * (representing `ti_A`), then 0 (representing `ti_a`) is returned.
94  *
95  * @param i a TensorIndex value that represents a generic index
96  * @return the TensorIndex value that encodes the generic index with the
97  * opposite valence
98  */
99 SPECTRE_ALWAYS_INLINE static constexpr size_t
100 get_tensorindex_value_with_opposite_valence(const size_t i) noexcept {
101  assert(i < TensorIndex_detail::max_sentinel); // NOLINT
102  if ((i >= TensorIndex_detail::upper_sentinel and
103  i < TensorIndex_detail::spatial_sentinel) or
104  (i >= TensorIndex_detail::upper_spatial_sentinel)) {
105  // `i` represents an upper index, so return the lower index's encoding
106  return i - TensorIndex_detail::upper_sentinel;
107  } else {
108  // `i` represents a lower index, so return the upper index's encoding
109  return i + TensorIndex_detail::upper_sentinel;
110  }
111 }
112 
113 /// @{
114 /*!
115  * \ingroup TensorExpressionsGroup
116  * \brief The available TensorIndexs to use in a TensorExpression
117  *
118  * \details The suffix following `ti_` indicates index properties:
119  * - Uppercase: contravariant/upper index
120  * - Lowercase: covariant/lower index
121  * - A/a - H/h: spacetime index
122  * - I/i - L/l: spatial index
123  *
124  * \snippet Test_AddSubtract.cpp use_tensor_index
125  */
126 static constexpr TensorIndex<0> ti_a{};
128 static constexpr TensorIndex<1> ti_b{};
130 static constexpr TensorIndex<2> ti_c{};
132 static constexpr TensorIndex<3> ti_d{};
134 static constexpr TensorIndex<4> ti_e{};
136 static constexpr TensorIndex<5> ti_f{};
138 static constexpr TensorIndex<6> ti_g{};
140 static constexpr TensorIndex<7> ti_h{};
146  ti_J{};
149  ti_K{};
152  ti_L{};
153 /// @}
154 
155 namespace tt {
156 /*!
157  * \ingroup TypeTraitsGroup TensorExpressionsGroup
158  * \brief Check if a type `T` is a TensorIndex used in TensorExpressions
159  */
160 template <typename T>
162 template <size_t I>
164 } // namespace tt
165 
166 namespace TensorIndex_detail {
167 template <auto&... TensorIndices>
168 struct make_tensorindex_list_impl {
169  static_assert(
170  (... and
171  tt::is_tensor_index<std::decay_t<decltype(TensorIndices)>>::value),
172  "Template parameters of make_tensorindex_list must be TensorIndex "
173  "objects.");
174  using type = tmpl::list<std::decay_t<decltype(TensorIndices)>...>;
175 };
176 } // namespace TensorIndex_detail
177 
178 /*!
179  * \ingroup TensorExpressionsGroup
180  * \brief Creates a TensorIndex type list from a list of TensorIndex objects
181  *
182  * @tparam TensorIndices list of generic index objects, e.g. `ti_a, ti_b`
183  */
184 template <auto&... TensorIndices>
185 using make_tensorindex_list =
186  typename TensorIndex_detail::make_tensorindex_list_impl<
187  TensorIndices...>::type;
std::false_type
UpLo
UpLo
Definition: IndexType.hpp:20
IndexType.hpp
TensorIndex
Represents the geeric indices in a TensorExpression.
Definition: TensorIndex.hpp:69
UpLo::Lo
@ Lo
Covariant, or Lower index.
SPECTRE_ALWAYS_INLINE
#define SPECTRE_ALWAYS_INLINE
Definition: ForceInline.hpp:16
cstddef
array
tt::is_tensor_index
Check if a type T is a TensorIndex used in TensorExpressions.
Definition: TensorIndex.hpp:161
std::decay_t
make_tensorindex_list
typename TensorIndex_detail::make_tensorindex_list_impl< TensorIndices... >::type make_tensorindex_list
Creates a TensorIndex type list from a list of TensorIndex objects.
Definition: TensorIndex.hpp:187
cassert
ForceInline.hpp
Requires.hpp
tt
Definition: TensorIndex.hpp:155
std::size_t
Requires
typename Requires_detail::requires_impl< B >::template_error_type_failed_to_meet_requirements_on_template_parameters Requires
Express requirements on the template parameters of a function or class, replaces std::enable_if_t
Definition: Requires.hpp:67
type_traits
TMPL.hpp
UpLo::Up
@ Up
Contravariant, or Upper index.