SpECTRE Documentation Coverage Report
Current view: top level - DataStructures/Tensor/Expressions - TensorIndex.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 83.0 % 47 39
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            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;
        

Generated by: LCOV version 2.0-1