SpECTRE Documentation Coverage Report
Current view: top level - DataStructures/Tensor/Expressions - TensorIndex.hpp Hit Total Coverage
Commit: 058fd9f3a53606b32c6beec17aafdb5fcf4268be Lines: 39 47 83.0 %
Date: 2024-04-27 02:05:51
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 1.14