Line data Source code
1 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 : 14 : #include "DataStructures/Tensor/IndexType.hpp" 15 : #include "Utilities/ForceInline.hpp" 16 : #include "Utilities/Requires.hpp" 17 : #include "Utilities/TMPL.hpp" 18 : 19 : namespace tenex { 20 : namespace TensorIndex_detail { 21 : // The below values are used to separate upper indices from lower indices and 22 : // spatial indices from spacetime indices. 23 : // 24 : // Tensor expressions perform as many calculations as possible in a constexpr 25 : // context, which means working with fundamental types, specifically integer 26 : // types, is easiest. By using sentinel values defined in one location we can 27 : // easily control the encoding without having magic values floating around in 28 : // many places. Furthermore, encoding all the information in the `size_t` means 29 : // that when a failure occurs in one of the constexpr calculations it is 30 : // reasonably easy to debug because, while encoded, the full type information is 31 : // present. This approach can effectively be thought of as using specific bits 32 : // in the `size_t` to mark information, using the size_t more as a bitfield than 33 : // anything else. For human readability, we use base-10 numbers instead of 34 : // base-2 values that would truly set individual bits. 35 : // 36 : // Spacetime indices are represented by values [0, `spatial_sentinel`) and 37 : // spatial indices are represented by values 38 : // [`spatial_sentinel`, `max_sentinel`). Lower spacetime indices are represented 39 : // by values [0, `upper_sentinel`), and upper spacetime indices are represented 40 : // by values [`upper_sentinel`, `spatial_sentinel`). Lower spatial indices are 41 : // represented by values 42 : // [`spatial_sentinel`, `spatial_sentinel` + `upper_sentinel`), and upper 43 : // spatial indices are represented by values 44 : // [`spatial_sentinel` + `upper_sentinel`, `max_sentinel`). Values equal to or 45 : // above `max_sentinel` are considered invalid for representing an index. 46 : static constexpr size_t spatial_sentinel = 1000; 47 : static constexpr size_t upper_sentinel = 500; 48 : static constexpr size_t upper_spatial_sentinel = 49 : spatial_sentinel + upper_sentinel; 50 : static constexpr size_t max_sentinel = 2000; 51 : } // namespace TensorIndex_detail 52 : } // namespace tenex 53 : 54 : /*! 55 : * \ingroup TensorExpressionsGroup 56 : * \brief Represents the geeric indices in a TensorExpression 57 : * 58 : * \details 59 : * Used to denote a tensor index in a tensor slot. This allows the following 60 : * type of expressions to work: 61 : * \code{.cpp} 62 : * auto T = evaluate<ti::a, ti::b>(F(ti::a, ti::b) + S(ti::b, ti::a)); 63 : * \endcode 64 : * where `decltype(ti::a) == TensorIndex<0>` and 65 : * `decltype(ti::b) == TensorIndex<1>`. That is, `ti::a` and `ti::b` are 66 : * placeholders for objects of type `TensorIndex<0>` and `TensorIndex<1>`, 67 : * respectively. 68 : */ 69 : template <std::size_t I, 70 : Requires<(I < tenex::TensorIndex_detail::max_sentinel)> = nullptr> 71 1 : struct TensorIndex { 72 0 : using value_type = std::size_t; 73 0 : using type = TensorIndex<I>; 74 0 : static constexpr value_type value = I; 75 0 : static constexpr UpLo valence = 76 : ((I < tenex::TensorIndex_detail::upper_sentinel) or 77 : (I >= tenex::TensorIndex_detail::spatial_sentinel and 78 : I < tenex::TensorIndex_detail::upper_spatial_sentinel)) 79 : ? UpLo::Lo 80 : : UpLo::Up; 81 0 : static constexpr bool is_spacetime = 82 : I < tenex::TensorIndex_detail::spatial_sentinel; 83 : }; 84 : 85 : /*! 86 : * \ingroup TensorExpressionsGroup 87 : * \brief Contains definitions for the available `TensorIndex`s to use in a 88 : * `TensorExpression` 89 : */ 90 1 : namespace ti { 91 : /// @{ 92 : /*! 93 : * \brief The available `TensorIndex`s to use in a `TensorExpression` 94 : * 95 : * \details The suffix following `ti::` indicates index properties: 96 : * - Uppercase: contravariant/upper index 97 : * - Lowercase: covariant/lower index 98 : * - A/a - H/h: generic spacetime index 99 : * - I/i - M/m: generic spatial index 100 : * - T/t: concrete time index (defined as a spacetime `TensorIndex`) 101 : * 102 : * \snippet Test_AddSubtract.cpp use_tensor_index 103 : * 104 : * If you want to support a new generic index, definitions for the upper and 105 : * lower versions of the index must be added as unique `TensorIndex` types, e.g. 106 : * \code 107 : * static constexpr TensorIndex<UNIQUE_INTEGER_IN_PROPER_RANGE_LOWER> ti::x{}; 108 : * static constexpr TensorIndex<UNIQUE_INTEGER_IN_PROPER_RANGE_UPPER> ti::X{}; 109 : * \endcode 110 : * where `UNIQUE_INTEGER_IN_PROPER_RANGE_LOWER` and 111 : * `UNIQUE_INTEGER_IN_PROPER_RANGE_UPPER` are unique, but related integers that 112 : * fall in the integer ranges that properly encode the index's properties 113 : * according to the `_sentinel` values defined at the top of 114 : * `src/DataStructures/Tensor/Expressions/TensorIndex.hpp`. This enables the new 115 : * index to be distinguishable from others and for the upper and lower versions 116 : * to be recognized as related by opposite valence. See comments there on these 117 : * integer ranges to properly encode the new index (both upper and lower 118 : * definitions) that you wish to add. In short, you should simply be able to 119 : * continue the pattern used for the existing `TensorIndex` types that are 120 : * already defined. For example, if `ti::M`/`ti::m` is the highest-valued 121 : * generic spatial index currently defined and you want to add `ti::N`/`ti::n` 122 : * as a new generic spatial index, you can simply define `ti::N` and `ti::n`'s 123 : * unique integer values to be `INTEGER_VALUE_FOR_M + 1` and 124 : * `INTEGER_VALUE_FOR_m + 1`, respectively. For adding a new generic spacetime 125 : * index, you should be able to do the same thing with respect to the upper and 126 : * lower versions of the highest-valued currently defined generic spacetime 127 : * `TensorIndex`. 128 : */ 129 1 : static constexpr TensorIndex<0> a{}; 130 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel> A{}; 131 1 : static constexpr TensorIndex<1> b{}; 132 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 1> B{}; 133 1 : static constexpr TensorIndex<2> c{}; 134 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 2> C{}; 135 1 : static constexpr TensorIndex<3> d{}; 136 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 3> D{}; 137 1 : static constexpr TensorIndex<4> e{}; 138 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 4> E{}; 139 1 : static constexpr TensorIndex<5> f{}; 140 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 5> F{}; 141 1 : static constexpr TensorIndex<6> g{}; 142 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 6> G{}; 143 1 : static constexpr TensorIndex<7> h{}; 144 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 7> H{}; 145 : 146 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel - 1> t{}; 147 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel - 1> 148 1 : T{}; 149 : 150 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel> i{}; 151 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel> 152 1 : I{}; 153 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 1> 154 1 : j{}; 155 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel + 156 : 1> 157 1 : J{}; 158 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 2> 159 1 : k{}; 160 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel + 161 : 2> 162 1 : K{}; 163 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 3> 164 1 : l{}; 165 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel + 166 : 3> 167 1 : L{}; 168 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 4> 169 1 : m{}; 170 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel + 171 : 4> 172 1 : M{}; 173 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 5> 174 1 : n{}; 175 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel + 176 : 5> 177 1 : N{}; 178 : /// @} 179 : } // namespace ti 180 : 181 : namespace tenex { 182 : /*! 183 : * \ingroup TensorExpressionsGroup 184 : * \brief Returns the TensorIndex value of with opposite valence. 185 : * 186 : * \details The input value represents a TensorIndex value, which encodes 187 : * both the valence of the index and whether the index is spacetime or 188 : * spatial. This function returns the value that corresponds to the encoding of 189 : * the TensorIndex with the same index type, but opposite valence. 190 : * 191 : * For example, 0 is the TensorIndex value for `ti::a`. If `i == 0`, then 500 192 : * will be returned, which is the TensorIndex value for `ti::A`. If `i == 500` 193 : * (representing `ti::A`), then 0 (representing `ti::a`) is returned. 194 : * 195 : * @param i a TensorIndex value that represents a generic index 196 : * @return the TensorIndex value that encodes the generic index with the 197 : * opposite valence 198 : */ 199 : SPECTRE_ALWAYS_INLINE static constexpr size_t 200 1 : get_tensorindex_value_with_opposite_valence(const size_t i) { 201 : assert(i < TensorIndex_detail::max_sentinel); // NOLINT 202 : if ((i >= TensorIndex_detail::upper_sentinel and 203 : i < TensorIndex_detail::spatial_sentinel) or 204 : (i >= TensorIndex_detail::upper_spatial_sentinel)) { 205 : // `i` represents an upper index, so return the lower index's encoding 206 : return i - TensorIndex_detail::upper_sentinel; 207 : } else { 208 : // `i` represents a lower index, so return the upper index's encoding 209 : return i + TensorIndex_detail::upper_sentinel; 210 : } 211 : } 212 : } // namespace tenex 213 : 214 1 : namespace tt { 215 : /*! 216 : * \ingroup TypeTraitsGroup TensorExpressionsGroup 217 : * \brief Check if a type `T` is a TensorIndex used in TensorExpressions 218 : */ 219 : template <typename T> 220 1 : struct is_tensor_index : std::false_type {}; 221 : template <size_t I> 222 0 : struct is_tensor_index<TensorIndex<I>> : std::true_type {}; 223 : 224 : template <typename T> 225 : struct is_time_index; 226 : } // namespace tt 227 : 228 : namespace tenex { 229 : namespace detail { 230 : template <auto&... TensorIndices> 231 : struct make_tensorindex_list_impl { 232 : static_assert( 233 : (... and 234 : tt::is_tensor_index<std::decay_t<decltype(TensorIndices)>>::value), 235 : "Template parameters of make_tensorindex_list must be TensorIndex " 236 : "objects."); 237 : using type = tmpl::list<std::decay_t<decltype(TensorIndices)>...>; 238 : }; 239 : 240 : template <typename TensorIndexList> 241 : struct tensorindex_list_is_valid_impl; 242 : 243 : template <typename... TensorIndices> 244 : struct tensorindex_list_is_valid_impl<tmpl::list<TensorIndices...>> { 245 : static_assert( 246 : (... and tt::is_tensor_index<TensorIndices>::value), 247 : "Template parameters of tensorindex_list_is_valid must be TensorIndex " 248 : "types."); 249 : static constexpr bool value = tmpl::is_set<TensorIndices...>::value; 250 : }; 251 : 252 : template <typename TensorIndexList1, typename TensorIndexList2, 253 : bool ListsSameSize> 254 : struct generic_indices_at_same_positions_impl; 255 : 256 : template <typename... TensorIndices1, typename... TensorIndices2> 257 : struct generic_indices_at_same_positions_impl< 258 : tmpl::list<TensorIndices1...>, tmpl::list<TensorIndices2...>, true> { 259 : static_assert((... and (tt::is_tensor_index<TensorIndices1>::value and 260 : tt::is_tensor_index<TensorIndices2>::value)), 261 : "Template parameters of generic_indices_at_same_positions_impl " 262 : "must be lists containing TensorIndex types."); 263 : using type = std::bool_constant<( 264 : ... and (std::is_same_v<TensorIndices1, TensorIndices2> or 265 : (tt::is_time_index<TensorIndices1>::value and 266 : tt::is_time_index<TensorIndices2>::value)))>; 267 : }; 268 : 269 : template <typename... TensorIndices1, typename... TensorIndices2> 270 : struct generic_indices_at_same_positions_impl< 271 : tmpl::list<TensorIndices1...>, tmpl::list<TensorIndices2...>, false> { 272 : static_assert((... and (tt::is_tensor_index<TensorIndices1>::value)), 273 : "The first template parameter of " 274 : "generic_indices_at_same_positions_impl must be a list " 275 : "containing TensorIndex types."); 276 : static_assert((... and (tt::is_tensor_index<TensorIndices2>::value)), 277 : "The second template parameter of " 278 : "generic_indices_at_same_positions_impl must be a list " 279 : "containing TensorIndex types."); 280 : using type = std::bool_constant<false>; 281 : }; 282 : 283 : template <typename TensorIndexList> 284 : struct remove_time_indices; 285 : } // namespace detail 286 : 287 : /*! 288 : * \ingroup TensorExpressionsGroup 289 : * \brief Determine whether or not a given list of TensorIndexs is valid 290 : * to be used with a tensor 291 : * 292 : * \details A list of TensorIndexs is considered valid if the subset of generic 293 : * indices are a set. Indices with opposite valences are unique, e.g. one 294 : * instance each of `ti::a` and `ti::A` is valid. An arbitrary number of 295 : * concrete time indices, regardless of valence, is also valid. 296 : * 297 : * @tparam TensorIndexList list of generic index types, e.g. the types of 298 : * `ti::a, ti::b` 299 : */ 300 : template <typename TensorIndexList> 301 1 : struct tensorindex_list_is_valid; 302 : 303 : template <typename... TensorIndices> 304 0 : struct tensorindex_list_is_valid<tmpl::list<TensorIndices...>> { 305 0 : static constexpr bool value = detail::tensorindex_list_is_valid_impl< 306 : typename detail::remove_time_indices< 307 : tmpl::list<TensorIndices...>>::type>::value; 308 : }; 309 : 310 : /*! 311 : * \ingroup TensorExpressionsGroup 312 : * \brief Determine whether or not two lists of TensorIndexs contain the same 313 : * generic indices at the same positions 314 : * 315 : * \tparam TensorIndexList1 the first TensorIndex list 316 : * \tparam TensorIndexList2 the second TensorIndex list 317 : */ 318 : template <typename TensorIndexList1, typename TensorIndexList2> 319 1 : using generic_indices_at_same_positions = 320 : typename detail::generic_indices_at_same_positions_impl< 321 : TensorIndexList1, TensorIndexList2, 322 : tmpl::size<TensorIndexList1>::value == 323 : tmpl::size<TensorIndexList2>::value>::type; 324 : } // namespace tenex 325 : 326 : /*! 327 : * \ingroup TensorExpressionsGroup 328 : * \brief Creates a TensorIndex type list from a list of TensorIndex objects 329 : * 330 : * @tparam TensorIndices list of generic index objects, e.g. `ti::a, ti::b` 331 : */ 332 : template <auto&... TensorIndices> 333 1 : using make_tensorindex_list = 334 : typename tenex::detail::make_tensorindex_list_impl<TensorIndices...>::type;