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
|