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

Generated by: LCOV version 1.14