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

            Line data    Source code
       1            1 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : /// \file
       5              : /// Defines 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 2.0-1