SpECTRE Documentation Coverage Report
Current view: top level - DataStructures/Tensor/Expressions - DataTypeSupport.hpp Hit Total Coverage
Commit: d0fc80462417e83e5cddfa1b9901bb4a9b6af4d6 Lines: 1 1 100.0 %
Date: 2024-03-29 00:33:31
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 which types are allowed, whether operations with certain types are
       6             : /// allowed, and other type-specific properties and configuration for
       7             : /// `TensorExpression`s
       8             : ///
       9             : /// \details
      10             : /// To add support for a data type, modify the templates in this file and the
      11             : /// arithmetic operator overloads as necessary. Then, add tests as appropriate.
      12             : 
      13             : #pragma once
      14             : 
      15             : #include <complex>
      16             : #include <cstddef>
      17             : #include <limits>
      18             : #include <type_traits>
      19             : 
      20             : #include "DataStructures/ComplexDataVector.hpp"
      21             : #include "DataStructures/DataVector.hpp"
      22             : #include "DataStructures/Tensor/Expressions/TensorExpression.hpp"
      23             : #include "DataStructures/VectorImpl.hpp"
      24             : #include "Utilities/NoSuchType.hpp"
      25             : 
      26             : namespace tenex {
      27             : template <typename DataType>
      28             : struct NumberAsExpression;
      29             : 
      30             : namespace detail {
      31             : /// @{
      32             : /// \brief Whether or not `TensorExpression`s supports using a given type as a
      33             : /// numeric term
      34             : ///
      35             : /// \details
      36             : /// To make it possible to use a new numeric data type as a term in
      37             : /// `TensorExpression`s, add the type to this alias and adjust other templates
      38             : /// in this file, as necessary.
      39             : ///
      40             : /// \tparam X the arithmetic data type
      41             : template <typename X>
      42             : struct is_supported_number_datatype
      43             :     : std::disjunction<std::is_same<X, double>,
      44             :                        std::is_same<X, std::complex<double>>> {};
      45             : 
      46             : template <typename X>
      47             : constexpr bool is_supported_number_datatype_v =
      48             :     is_supported_number_datatype<X>::value;
      49             : /// @}
      50             : 
      51             : /// @{
      52             : /// \brief Whether or not `Tensor`s with the given data type are currently
      53             : /// supported by `TensorExpression`s
      54             : ///
      55             : /// \details
      56             : /// To make it possible to use a new data type in a `Tensor` term in
      57             : /// `TensorExpression`s, add the type to this alias and adjust other templates
      58             : /// in this file, as necessary.
      59             : ///
      60             : /// \tparam X the `Tensor` data type
      61             : template <typename X>
      62             : struct is_supported_tensor_datatype
      63             :     : std::disjunction<
      64             :           std::is_same<X, double>, std::is_same<X, std::complex<double>>,
      65             :           std::is_same<X, DataVector>, std::is_same<X, ComplexDataVector>> {};
      66             : 
      67             : template <typename X>
      68             : constexpr bool is_supported_tensor_datatype_v =
      69             :     is_supported_tensor_datatype<X>::value;
      70             : /// @}
      71             : 
      72             : /// \brief If the given type is a derived `VectorImpl` type, get the base
      73             : /// `VectorImpl` type, else return the given type
      74             : ///
      75             : /// \tparam T the given type
      76             : /// \tparam IsVector whether or not the given type is a `VectorImpl` type
      77             : template <typename T, bool IsVector = is_derived_of_vector_impl_v<T>>
      78             : struct upcast_if_derived_vector_type;
      79             : 
      80             : /// If `T` is not a `VectorImpl`, the `type` is just the input
      81             : template <typename T>
      82             : struct upcast_if_derived_vector_type<T, false> {
      83             :   using type = T;
      84             : };
      85             : /// If `T` is a `VectorImpl`, the `type` is the base `VectorImpl` type
      86             : template <typename T>
      87             : struct upcast_if_derived_vector_type<T, true> {
      88             :   using upcasted_type = typename T::BaseType;
      89             :   // if we have a derived VectorImpl, get base type, else T is a base VectorImpl
      90             :   // and we use that
      91             :   using type =
      92             :       tmpl::conditional_t<std::is_base_of_v<MarkAsVectorImpl, upcasted_type>,
      93             :                           upcasted_type, T>;
      94             : };
      95             : 
      96             : /// \brief Get the complex-valued partner type to a given type
      97             : ///
      98             : /// \details
      99             : /// This is used to define pairings between real-valued types and their
     100             : /// complex-valued counterparts. For example, `double`'s complex-valued partner
     101             : /// is `std::complex<double>` and `DataVector`'s complex-valued partner is
     102             : /// `ComplexDataVector`. Keeping track of this is useful in determining which
     103             : /// operations can and can't be performed in `TensorExpression`s.
     104             : ///
     105             : /// To make `TensorExpression`s aware of a new pairing, modify a current
     106             : /// template specialization or add a new one.
     107             : ///
     108             : /// \tparam X the given type
     109             : template <typename X, bool IsArithmetic = std::is_arithmetic_v<X>>
     110             : struct get_complex_datatype;
     111             : 
     112             : /// If the type is not arithmetic, the complex partner to this type is not
     113             : /// known
     114             : template <typename X>
     115             : struct get_complex_datatype<X, false> {
     116             :   using type = NoSuchType;
     117             : };
     118             : /// If the type is arithmetic, the complex partner to `X` is `std::complex<X>`
     119             : template <typename X>
     120             : struct get_complex_datatype<X, true> {
     121             :   using type = std::complex<X>;
     122             : };
     123             : /// The complex partner to `DataVector` is `ComplexDataVector`
     124             : template <>
     125             : struct get_complex_datatype<DataVector> {
     126             :   using type = ComplexDataVector;
     127             : };
     128             : 
     129             : /// @{
     130             : /// \brief Whether or not a given type is the complex-valued partner to another
     131             : /// given type
     132             : ///
     133             : /// \details
     134             : /// See `get_complex_datatype` for which pairings are defined
     135             : ///
     136             : /// \tparam MaybeComplexDataType the given type to check for being the complex
     137             : /// partner to the other type
     138             : /// \tparam OtherDataType the other type
     139             : template <typename MaybeComplexDataType, typename OtherDataType>
     140             : struct is_complex_datatype_of
     141             :     : std::is_same<typename get_complex_datatype<OtherDataType>::type,
     142             :                    MaybeComplexDataType> {};
     143             : template <typename OtherDataType>
     144             : struct is_complex_datatype_of<NoSuchType, OtherDataType> : std::false_type {};
     145             : 
     146             : template <typename MaybeComplexDataType, typename OtherDataType>
     147             : constexpr bool is_complex_datatype_of_v =
     148             :     is_complex_datatype_of<MaybeComplexDataType, OtherDataType>::value;
     149             : /// @}
     150             : 
     151             : /// \brief Whether or not a given type is assignable to another within
     152             : /// `TensorExpression`s
     153             : ///
     154             : /// \details
     155             : /// This is used to define which types can be assigned to which when evaluating
     156             : /// the result of a `TensorExpression`. For example, you can assign a
     157             : /// `DataVector` to a `double`, but not vice versa.
     158             : ///
     159             : /// To enable assignment between two types that is not yet supported, modify a
     160             : /// current template specialization or add a new one.
     161             : ///
     162             : /// \tparam LhsDataType the type being assigned
     163             : /// \tparam RhsDataType the type to assign the `LhsDataType` to
     164             : template <typename LhsDataType, typename RhsDataType>
     165             : struct is_assignable;
     166             : 
     167             : /// Can assign a type to itself
     168             : template <typename LhsDataType, typename RhsDataType>
     169             : struct is_assignable : std::is_same<LhsDataType, RhsDataType> {};
     170             : /// Can assign a complex numeric type to its underlying real-valued numeric type
     171             : template <typename X>
     172             : struct is_assignable<std::complex<X>, X> : std::true_type {};
     173             : /// Can assign the LHS `VectorImpl` to the RHS `VectorImpl` if `VectorImpl`
     174             : /// allows it
     175             : template <typename ValueType1, typename VectorType1, size_t StaticSize1,
     176             :           typename ValueType2, typename VectorType2, size_t StaticSize2>
     177             : struct is_assignable<VectorImpl<ValueType1, VectorType1, StaticSize1>,
     178             :                      VectorImpl<ValueType2, VectorType2, StaticSize2>>
     179             :     : ::VectorImpl_detail::is_assignable<VectorType1, VectorType2> {};
     180             : /// Can assign a `VectorImpl` to its value type, e.g. can assign a `DataVector`
     181             : /// to a `double`
     182             : template <typename ValueType, typename VectorType, size_t StaticSize>
     183             : struct is_assignable<VectorImpl<ValueType, VectorType, StaticSize>, ValueType>
     184             :     : std::true_type {};
     185             : /// Can assign a complex-valued `VectorImpl` to its real component's type, e.g.
     186             : /// can assign a `ComplexDataVector` to a `double` because the underlying type
     187             : /// of `ComplexDataVector` is `std::complex<double>`, whose real component is a
     188             : /// `double`
     189             : template <typename ValueType, typename VectorType, size_t StaticSize>
     190             : struct is_assignable<
     191             :     VectorImpl<std::complex<ValueType>, VectorType, StaticSize>, ValueType>
     192             :     : std::true_type {};
     193             : 
     194             : /// \brief Whether or not a given type is assignable to another within
     195             : /// `TensorExpression`s
     196             : ///
     197             : /// \details
     198             : /// See `is_assignable` for which assignments are permitted
     199             : template <typename LhsDataType, typename RhsDataType>
     200             : constexpr bool is_assignable_v = is_assignable<
     201             :     typename upcast_if_derived_vector_type<LhsDataType>::type,
     202             :     typename upcast_if_derived_vector_type<RhsDataType>::type>::value;
     203             : 
     204             : /// \brief Get the data type of a binary operation between two data types
     205             : /// that may occur in a `TensorExpression`
     206             : ///
     207             : /// \details
     208             : /// This is used to define the resulting types of binary arithmetic operations
     209             : /// within `TensorExpression`s, e.g. `double OP double = double` and
     210             : /// `double OP DataVector = DataVector`.
     211             : ///
     212             : /// To enable binary operations between two types that is not yet supported,
     213             : /// modify a current template specialization or add a new one.
     214             : ///
     215             : /// \tparam X1 the data type of one operand
     216             : /// \tparam X2 the data type of the other operand
     217             : template <typename X1, typename X2>
     218             : struct get_binop_datatype_impl;
     219             : 
     220             : /// No template specialization was matched, so it's not a known pairing
     221             : template <typename X1, typename X2>
     222             : struct get_binop_datatype_impl {
     223             :   using type = NoSuchType;
     224             : };
     225             : /// A binary operation between two terms of the same type will yield a result
     226             : /// with that type
     227             : template <typename X>
     228             : struct get_binop_datatype_impl<X, X> {
     229             :   using type = X;
     230             : };
     231             : /// A binary operation between two `VectorImpl`s of the same type will
     232             : /// yield the shared derived `VectorImpl`, e.g.
     233             : /// `DataVector OP DataVector = DataVector`
     234             : template <typename ValueType, typename VectorType, size_t StaticSize>
     235             : struct get_binop_datatype_impl<VectorImpl<ValueType, VectorType, StaticSize>,
     236             :                                VectorImpl<ValueType, VectorType, StaticSize>> {
     237             :   using type = VectorType;
     238             : };
     239             : /// @{
     240             : /// A binary operation between a type `T` and `std::complex<T>` yields a
     241             : /// `std::complex<T>`
     242             : template <typename T>
     243             : struct get_binop_datatype_impl<T, std::complex<T>> {
     244             :   using type = std::complex<T>;
     245             : };
     246             : template <typename T>
     247             : struct get_binop_datatype_impl<std::complex<T>, T> {
     248             :   using type = std::complex<T>;
     249             : };
     250             : /// @}
     251             : /// @{
     252             : /// A binary operation between a `VectorImpl` and its underlying value type
     253             : /// yields the `VectorImpl`, e.g. `DataVector OP double = DataVector`
     254             : template <typename ValueType, typename VectorType, size_t StaticSize>
     255             : struct get_binop_datatype_impl<VectorImpl<ValueType, VectorType, StaticSize>,
     256             :                                ValueType> {
     257             :   using type = VectorType;
     258             : };
     259             : template <typename ValueType, typename VectorType, size_t StaticSize>
     260             : struct get_binop_datatype_impl<ValueType,
     261             :                                VectorImpl<ValueType, VectorType, StaticSize>> {
     262             :   using type = VectorType;
     263             : };
     264             : /// @}
     265             : /// @{
     266             : /// A binary operation between a complex-valued `VectorImpl` and its real
     267             : /// component's type yields the `VectorImpl`, e.g.
     268             : /// `ComplexDataVector OP double = ComplexDataVector`
     269             : template <typename ValueType, typename VectorType, size_t StaticSize>
     270             : struct get_binop_datatype_impl<
     271             :     VectorImpl<std::complex<ValueType>, VectorType, StaticSize>, ValueType> {
     272             :   using type = VectorType;
     273             : };
     274             : template <typename ValueType, typename VectorType, size_t StaticSize>
     275             : struct get_binop_datatype_impl<
     276             :     ValueType, VectorImpl<std::complex<ValueType>, VectorType, StaticSize>> {
     277             :   using type = VectorType;
     278             : };
     279             : /// @}
     280             : /// @{
     281             : /// A binary operation between a real-valued `VectorImpl` and the complex-valued
     282             : /// partner to the `VectorImpl`'s underlying type yields the complex partner
     283             : /// type of the `VectorImpl`, e.g.
     284             : /// `std::complex<double> OP DataVector = ComplexDataVector`
     285             : ///
     286             : /// \note Blaze supports multiplication between a `std::complex<double>` and a
     287             : /// `DataVector`, but does not support addition, subtraction, or division
     288             : /// between these two types. This specialization of `get_binop_datatype_impl`
     289             : /// simply defines that the result of any of the binary operations should be
     290             : /// `ComplexDataVector`. Because Blaze doesn't support addition, subtraction,
     291             : /// and division between these two types, the `AddSub` and `Divide` classes
     292             : /// disallow this type combination in their class definitions to prevent these
     293             : /// operations. That way, if Blaze support is later added for e.g. division,
     294             : /// we simply need to remove the assert in `Divide` that prevents it.
     295             : template <typename ValueType, typename VectorType, size_t StaticSize>
     296             : struct get_binop_datatype_impl<VectorImpl<ValueType, VectorType, StaticSize>,
     297             :                                std::complex<ValueType>> {
     298             :   using type = typename get_complex_datatype<VectorType>::type;
     299             : };
     300             : template <typename ValueType, typename VectorType, size_t StaticSize>
     301             : struct get_binop_datatype_impl<std::complex<ValueType>,
     302             :                                VectorImpl<ValueType, VectorType, StaticSize>> {
     303             :   using type = typename get_complex_datatype<VectorType>::type;
     304             : };
     305             : /// @}
     306             : /// @{
     307             : /// A binary operation between a `DataVector` and a `ComplexDataVector` yields a
     308             : /// `ComplexDataVector`
     309             : template <>
     310             : struct get_binop_datatype_impl<typename ComplexDataVector::BaseType,
     311             :                                typename DataVector::BaseType> {
     312             :   using type = ComplexDataVector;
     313             : };
     314             : template <>
     315             : struct get_binop_datatype_impl<typename DataVector::BaseType,
     316             :                                typename ComplexDataVector::BaseType> {
     317             :   using type = ComplexDataVector;
     318             : };
     319             : /// @}
     320             : 
     321             : /// \brief Get the data type of a binary operation between two data types
     322             : /// that may occur in a `TensorExpression`
     323             : ///
     324             : /// \details
     325             : /// See `get_binop_datatype_impl` for which data type combinations have a
     326             : /// defined result type
     327             : ///
     328             : /// \tparam X1 the data type of one operand
     329             : /// \tparam X2 the data type of the other operand
     330             : template <typename X1, typename X2>
     331             : struct get_binop_datatype {
     332             :   using type = typename get_binop_datatype_impl<
     333             :       typename upcast_if_derived_vector_type<X1>::type,
     334             :       typename upcast_if_derived_vector_type<X2>::type>::type;
     335             : 
     336             :   static_assert(
     337             :       not std::is_same_v<type, NoSuchType>,
     338             :       "You are attempting to perform a binary arithmetic operation between "
     339             :       "two data types, but the data type of the result is not known within "
     340             :       "TensorExpressions. See tenex::detail::get_binop_datatype_impl.");
     341             : };
     342             : 
     343             : /// @{
     344             : /// \brief Whether or not it is permitted to perform binary arithmetic
     345             : /// operations with the given types within `TensorExpression`
     346             : ///
     347             : /// \details
     348             : /// See `get_binop_datatype_impl` for which data type combinations have a
     349             : /// defined result type
     350             : ///
     351             : /// \tparam X1 the data type of one operand
     352             : /// \tparam X2 the data type of the other operand
     353             : template <typename X1, typename X2>
     354             : struct binop_datatypes_are_supported
     355             :     : std::negation<std::is_same<
     356             :           typename get_binop_datatype_impl<
     357             :               typename upcast_if_derived_vector_type<X1>::type,
     358             :               typename upcast_if_derived_vector_type<X2>::type>::type,
     359             :           NoSuchType>> {};
     360             : 
     361             : template <typename X1, typename X2>
     362             : constexpr bool binop_datatypes_are_supported_v =
     363             :     binop_datatypes_are_supported<X1, X2>::value;
     364             : /// @}
     365             : 
     366             : /// \brief Whether or not it is permitted to perform binary arithmetic
     367             : /// operations with `Tensor`s with the given types within `TensorExpression`s
     368             : ///
     369             : /// \details
     370             : /// This is used to define which data types can be contained by the two
     371             : /// `Tensor`s in a binary operation, e.g.
     372             : /// `Tensor<ComplexDataVector>() OP Tensor<DataVector>()` is permitted, but
     373             : /// `Tensor<DataVector>() OP Tensor<double>()` is not.
     374             : ///
     375             : /// To enable binary operations between `Tensor`s with types that are not yet
     376             : /// supported, modify a current template specialization or add a new one.
     377             : ///
     378             : /// \tparam X1 the data type of one `Tensor` operand
     379             : /// \tparam X2 the data type of the other `Tensor` operand
     380             : template <typename X1, typename X2>
     381             : struct tensor_binop_datatypes_are_supported_impl;
     382             : 
     383             : /// Can only do `Tensor<X1>() OP Tensor<X2>()` if `X1 == X2` or if `X1` and
     384             : /// `X2` are real/complex partners like `DataVector` and `ComplexDataVector`
     385             : /// (see `is_complex_datatype_of`)
     386             : template <typename X1, typename X2>
     387             : struct tensor_binop_datatypes_are_supported_impl
     388             :     : std::disjunction<std::is_same<X1, X2>, is_complex_datatype_of<X1, X2>,
     389             :                        is_complex_datatype_of<X2, X1>> {};
     390             : 
     391             : /// @{
     392             : /// \brief Whether or not it is permitted to perform binary arithmetic
     393             : /// operations with `Tensor`s with the given types within `TensorExpression`s
     394             : ///
     395             : /// \details
     396             : /// See `tensor_binop_datatypes_are_supported_impl` for which data type
     397             : /// combinations are permitted
     398             : ///
     399             : /// \tparam X1 the data type of one `Tensor` operand
     400             : /// \tparam X2 the data type of the other `Tensor` operand
     401             : template <typename X1, typename X2>
     402             : struct tensor_binop_datatypes_are_supported
     403             :     : tensor_binop_datatypes_are_supported_impl<X1, X2> {
     404             :   static_assert(
     405             :       is_supported_tensor_datatype_v<X1> and is_supported_tensor_datatype_v<X2>,
     406             :       "Cannot perform binary operations between the two Tensors with the "
     407             :       "given data types because at least one of the data types is not "
     408             :       "supported by TensorExpressions. See "
     409             :       "tenex::detail::is_supported_tensor_datatype.");
     410             : };
     411             : 
     412             : template <typename X1, typename X2>
     413             : constexpr bool tensor_binop_datatypes_are_supported_v =
     414             :     tensor_binop_datatypes_are_supported<X1, X2>::value;
     415             : /// @}
     416             : 
     417             : /// \brief Whether or not it is permitted to perform binary arithmetic
     418             : /// operations with `TensorExpression`s, based on their data types
     419             : ///
     420             : /// \details
     421             : /// This is used to define which data types can be contained by the two
     422             : /// `TensorExpression`s in a binary operation, e.g.
     423             : /// `Tensor<DataVector>() OP double` and
     424             : /// `Tensor<ComplexDataVector>() OP Tensor<DataVector>()` are permitted, but
     425             : /// `Tensor<DataVector>() OP Tensor<double>()` is not. This differs from
     426             : /// `tensor_binop_datatypes_are_supported` in that
     427             : /// `tensorexpression_binop_datatypes_are_supported_impl` handles all derived
     428             : /// `TensorExpression` types, whether they represent `Tensor`s or numbers.
     429             : /// `tensor_binop_datatypes_are_supported` only handles the cases where both
     430             : /// `TensorExpression`s represent `Tensor`s.
     431             : ///
     432             : /// To enable binary operations between `TensorExpression`s with types that
     433             : /// are not yet supported, modify a current template specialization or add a
     434             : /// new one.
     435             : ///
     436             : /// \tparam T1 the first `TensorExpression` operand
     437             : /// \tparam T2 the second `TensorExpression` operand
     438             : template <typename T1, typename T2>
     439             : struct tensorexpression_binop_datatypes_are_supported_impl;
     440             : 
     441             : /// Since `T1` and `T2` represent `Tensor`s, check if we can do
     442             : /// `Tensor<T1::type>() OP Tensor<T2::type>()`
     443             : template <typename T1, typename T2>
     444             : struct tensorexpression_binop_datatypes_are_supported_impl
     445             :     : tensor_binop_datatypes_are_supported_impl<typename T1::type,
     446             :                                                 typename T2::type> {};
     447             : /// @{
     448             : /// Can do `Tensor<X>() OP NUMBER` if we can do `X OP NUMBER`
     449             : template <typename TensorExpressionType, typename NumberType>
     450             : struct tensorexpression_binop_datatypes_are_supported_impl<
     451             :     TensorExpressionType, NumberAsExpression<NumberType>>
     452             :     : binop_datatypes_are_supported<typename TensorExpressionType::type,
     453             :                                     NumberType> {
     454             :   static_assert(
     455             :       is_supported_tensor_datatype_v<typename TensorExpressionType::type> and
     456             :           is_supported_number_datatype_v<NumberType>,
     457             :       "Cannot perform binary operations between Tensor and number with the "
     458             :       "given data types because at least one of the data types is not "
     459             :       "supported by TensorExpressions. See "
     460             :       "tenex::detail::is_supported_number_datatype and "
     461             :       "tenex::detail::is_supported_tensor_datatype.");
     462             : };
     463             : template <typename NumberType, typename TensorExpressionType>
     464             : struct tensorexpression_binop_datatypes_are_supported_impl<
     465             :     NumberAsExpression<NumberType>, TensorExpressionType>
     466             :     : tensorexpression_binop_datatypes_are_supported_impl<
     467             :           TensorExpressionType, NumberAsExpression<NumberType>> {};
     468             : /// @}
     469             : 
     470             : /// @{
     471             : /// \brief Whether or not it is permitted to perform binary arithmetic
     472             : /// operations with `TensorExpression`s, based on their data types
     473             : ///
     474             : /// \details
     475             : /// See `tensorexpression_binop_datatypes_are_supported_impl` for which data
     476             : /// type combinations are permitted
     477             : ///
     478             : /// \tparam T1 the first `TensorExpression` operand
     479             : /// \tparam T2 the second `TensorExpression` operand
     480             : template <typename T1, typename T2>
     481             : struct tensorexpression_binop_datatypes_are_supported
     482             :     : tensorexpression_binop_datatypes_are_supported_impl<T1, T2> {
     483             :   static_assert(
     484             :       std::is_base_of_v<Expression, T1> and std::is_base_of_v<Expression, T2>,
     485             :       "Template arguments to "
     486             :       "tenex::detail::tensorexpression_binop_datatypes_are_supported must be "
     487             :       "TensorExpressions.");
     488             : };
     489             : 
     490             : template <typename X1, typename X2>
     491             : constexpr bool tensorexpression_binop_datatypes_are_supported_v =
     492             :     tensorexpression_binop_datatypes_are_supported<X1, X2>::value;
     493             : /// @}
     494             : 
     495             : /// \brief The maximum number of arithmetic tensor operations allowed in a
     496             : /// `TensorExpression` subtree before having it be a splitting point in the
     497             : /// overall RHS expression, according to the data type held by the `Tensor`s in
     498             : /// the expression
     499             : ///
     500             : /// \details
     501             : /// To enable splitting for `TensorExpression`s with data type, define a
     502             : /// template specialization below for your data type and set the `value`.
     503             : ///
     504             : /// Before defining a max operations cap for some data type, the change should
     505             : /// first be justified by benchmarking many different tensor expressions before
     506             : /// and after introducing the new cap. The optimal cap will likely be
     507             : /// hardware-dependent, so fine-tuning this would ideally involve benchmarking
     508             : /// on each hardware architecture and then controling the value based on the
     509             : /// hardware.
     510             : template <typename DataType>
     511             : struct max_num_ops_in_sub_expression_impl {
     512             :   // effectively, no splitting for any unspecialized template type
     513             :   static constexpr size_t value = std::numeric_limits<size_t>::max();
     514             : };
     515             : 
     516             : /// \brief When the data type of the result of a `TensorExpression` is
     517             : /// `DataVector`, the maximum number of arithmetic tensor operations allowed in
     518             : /// a subtree before having it be a splitting point in the overall RHS
     519             : /// expression
     520             : ///
     521             : /// \details
     522             : /// The current value set for when the data type is `DataVector` was benchmarked
     523             : /// by compiling with clang-10 Release and running on Intel(R) Xeon(R)
     524             : /// CPU E5-2630 v4 @ 2.20GHz.
     525             : template <>
     526             : struct max_num_ops_in_sub_expression_impl<DataVector> {
     527             :   static constexpr size_t value = 8;
     528             : };
     529             : 
     530             : /// \brief When the data type of the result of a `TensorExpression` is
     531             : /// `ComplexDataVector`, the maximum number of arithmetic tensor operations
     532             : /// allowed in a subtree before having it be a splitting point in the overall
     533             : /// RHS expression
     534             : ///
     535             : /// \details
     536             : /// The current value set for when the data type is `ComplexDataVector` is set
     537             : /// to the value for `DataVector`, but the best `value` for `ComplexDataVector`
     538             : /// should also be investigated and fine-tuned.
     539             : template <>
     540             : struct max_num_ops_in_sub_expression_impl<ComplexDataVector> {
     541             :   static constexpr size_t value =
     542             :       max_num_ops_in_sub_expression_impl<DataVector>::value;
     543             : };
     544             : 
     545             : /// \brief Get maximum number of arithmetic tensor operations allowed in a
     546             : /// `TensorExpression` subtree before having it be a splitting point in the
     547             : /// overall RHS expression, according to the `DataType` held by the `Tensor`s in
     548             : /// the expression
     549             : template <typename DataType>
     550             : inline constexpr size_t max_num_ops_in_sub_expression =
     551             :     max_num_ops_in_sub_expression_impl<DataType>::value;
     552             : }  // namespace detail
     553             : }  // namespace tenex

Generated by: LCOV version 1.14