EvaluateRank2TestHelpers.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <cstddef>
7 #include <iterator>
8 #include <numeric>
9 
13 #include "Utilities/GenerateInstantiations.hpp"
14 #include "Utilities/TMPL.hpp"
15 
16 namespace TestHelpers::TensorExpressions {
17 
18 /// \ingroup TestingFrameworkGroup
19 /// \brief Test that evaluating a right hand side tensor expression containing a
20 /// single rank 2 tensor correctly assigns the data to the evaluated left hand
21 /// side tensor
22 ///
23 /// \details `TensorIndexA` and `TensorIndexB` can be any type of TensorIndex
24 /// and are not necessarily `ti_a` and `ti_b`. The "A" and "B" suffixes just
25 /// denote the ordering of the generic indices of the RHS tensor expression. In
26 /// the RHS tensor expression, it means `TensorIndexA` is the first index used
27 /// and `TensorIndexB` is the second index used.
28 ///
29 /// If we consider the RHS tensor's generic indices to be (a, b), then this test
30 /// checks that the data in the evaluated LHS tensor is correct according to the
31 /// index orders of the LHS and RHS. The two possible cases that are checked are
32 /// when the LHS tensor is evaluated with index order (a, b) and when it is
33 /// evaluated with the index order (b, a).
34 ///
35 /// \tparam DataType the type of data being stored in the Tensors
36 /// \tparam RhsSymmetry the ::Symmetry of the RHS Tensor
37 /// \tparam RhsTensorIndexTypeList the RHS Tensor's typelist of
38 /// \ref SpacetimeIndex "TensorIndexType"s
39 /// \tparam TensorIndexA the first TensorIndex used on the RHS of the
40 /// TensorExpression, e.g. `ti_a`
41 /// \tparam TensorIndexB the second TensorIndex used on the RHS of the
42 /// TensorExpression, e.g. `ti_B`
43 template <typename DataType, typename RhsSymmetry,
44  typename RhsTensorIndexTypeList, auto& TensorIndexA,
45  auto& TensorIndexB>
46 void test_evaluate_rank_2_impl() noexcept {
47  Tensor<DataType, RhsSymmetry, RhsTensorIndexTypeList> R_ab(5_st);
48  std::iota(R_ab.begin(), R_ab.end(), 0.0);
49 
50  // L_{ab} = R_{ab}
51  // Use explicit type (vs auto) so the compiler checks the return type of
52  // `evaluate`
53  const Tensor<DataType, RhsSymmetry, RhsTensorIndexTypeList> L_ab =
54  ::TensorExpressions::evaluate<TensorIndexA, TensorIndexB>(
55  R_ab(TensorIndexA, TensorIndexB));
56 
57  // L_{ba} = R_{ab}
58  using L_ba_tensorindextype_list =
59  tmpl::list<tmpl::at_c<RhsTensorIndexTypeList, 1>,
60  tmpl::at_c<RhsTensorIndexTypeList, 0>>;
61  const Tensor<DataType, RhsSymmetry, L_ba_tensorindextype_list> L_ba =
62  ::TensorExpressions::evaluate<TensorIndexB, TensorIndexA>(
63  R_ab(TensorIndexA, TensorIndexB));
64 
65  const size_t dim_a = tmpl::at_c<RhsTensorIndexTypeList, 0>::dim;
66  const size_t dim_b = tmpl::at_c<RhsTensorIndexTypeList, 1>::dim;
67 
68  for (size_t i = 0; i < dim_a; ++i) {
69  for (size_t j = 0; j < dim_b; ++j) {
70  // For L_{ab} = R_{ab}, check that L_{ij} == R_{ij}
71  CHECK(L_ab.get(i, j) == R_ab.get(i, j));
72  // For L_{ba} = R_{ab}, check that L_{ji} == R_{ij}
73  CHECK(L_ba.get(j, i) == R_ab.get(i, j));
74  }
75  }
76 }
77 
78 /// \ingroup TestingFrameworkGroup
79 /// \brief Iterate testing of evaluating single rank 2 Tensors on multiple Frame
80 /// types and dimension combinations
81 ///
82 /// We test nonsymmetric indices and symmetric indices across two functions to
83 /// ensure that the code works correctly with symmetries. This function tests
84 /// one of the following symmetries:
85 /// - <2, 1> (`test_evaluate_rank_2_no_symmetry`)
86 /// - <1, 1> (`test_evaluate_rank_2_symmetric`)
87 ///
88 /// \details `TensorIndexA` and `TensorIndexB` can be any type of TensorIndex
89 /// and are not necessarily `ti_a` and `ti_b`. The "A" and "B" suffixes just
90 /// denote the ordering of the generic indices of the RHS tensor expression. In
91 /// the RHS tensor expression, it means `TensorIndexA` is the first index used
92 /// and `TensorIndexB` is the second index used.
93 ///
94 /// Note: `test_evaluate_rank_2_symmetric` has fewer template parameters due to
95 /// the two indices having a shared \ref SpacetimeIndex "TensorIndexType" and
96 /// and valence
97 ///
98 /// \tparam DataType the type of data being stored in the Tensors
99 /// \tparam TensorIndexTypeA the \ref SpacetimeIndex "TensorIndexType" of the
100 /// first index of the RHS Tensor
101 /// \tparam TensorIndexTypeB the \ref SpacetimeIndex "TensorIndexType" of the
102 /// second index of the RHS Tensor
103 /// \tparam ValenceA the valence of the first index used on the RHS of the
104 /// TensorExpression
105 /// \tparam ValenceB the valence of the second index used on the RHS of the
106 /// TensorExpression
107 /// \tparam TensorIndexA the first TensorIndex used on the RHS of the
108 /// TensorExpression, e.g. `ti_a`
109 /// \tparam TensorIndexB the second TensorIndex used on the RHS of the
110 /// TensorExpression, e.g. `ti_B`
111 template <typename DataType,
112  template <size_t, UpLo, typename> class TensorIndexTypeA,
113  template <size_t, UpLo, typename> class TensorIndexTypeB,
114  UpLo ValenceA, UpLo ValenceB, auto& TensorIndexA, auto& TensorIndexB>
116 #define DIM_A(data) BOOST_PP_TUPLE_ELEM(0, data)
117 #define DIM_B(data) BOOST_PP_TUPLE_ELEM(1, data)
118 #define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)
119 
120 #define CALL_TEST_EVALUATE_RANK_2_IMPL(_, data) \
121  test_evaluate_rank_2_impl< \
122  DataType, Symmetry<2, 1>, \
123  index_list<TensorIndexTypeA<DIM_A(data), ValenceA, FRAME(data)>, \
124  TensorIndexTypeB<DIM_B(data), ValenceB, FRAME(data)>>, \
125  TensorIndexA, TensorIndexB>();
126 
127  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_2_IMPL, (1, 2, 3), (1, 2, 3),
129 
130 #undef CALL_TEST_EVALUATE_RANK_2_IMPL
131 #undef FRAME
132 #undef DIM_B
133 #undef DIM_A
134 }
135 
136 /// \ingroup TestingFrameworkGroup
137 /// \copydoc test_evaluate_rank_2_no_symmetry()
138 template <typename DataType,
139  template <size_t, UpLo, typename> class TensorIndexType, UpLo Valence,
140  auto& TensorIndexA, auto& TensorIndexB>
142 #define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)
143 #define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)
144 
145 #define CALL_TEST_EVALUATE_RANK_2_IMPL(_, data) \
146  test_evaluate_rank_2_impl< \
147  DataType, Symmetry<1, 1>, \
148  index_list<TensorIndexType<DIM(data), Valence, FRAME(data)>, \
149  TensorIndexType<DIM(data), Valence, FRAME(data)>>, \
150  TensorIndexA, TensorIndexB>();
151 
152  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_2_IMPL, (1, 2, 3),
154 
155 #undef CALL_TEST_EVALUATE_RANK_2_IMPL
156 #undef FRAME
157 #undef DIM
158 }
159 
160 } // namespace TestHelpers::TensorExpressions
Frame::Inertial
Definition: IndexType.hpp:44
TensorExpression.hpp
UpLo
UpLo
Definition: IndexType.hpp:20
Frame::Grid
Definition: IndexType.hpp:43
iterator
TestHelpers::TensorExpressions::test_evaluate_rank_2_no_symmetry
void test_evaluate_rank_2_no_symmetry() noexcept
Iterate testing of evaluating single rank 2 Tensors on multiple Frame types and dimension combination...
Definition: EvaluateRank2TestHelpers.hpp:115
TestHelpers::TensorExpressions::test_evaluate_rank_2_impl
void test_evaluate_rank_2_impl() noexcept
Test that evaluating a right hand side tensor expression containing a single rank 2 tensor correctly ...
Definition: EvaluateRank2TestHelpers.hpp:46
cstddef
Tensor.hpp
numeric
TestHelpers::TensorExpressions::test_evaluate_rank_2_symmetric
void test_evaluate_rank_2_symmetric() noexcept
Iterate testing of evaluating single rank 2 Tensors on multiple Frame types and dimension combination...
Definition: EvaluateRank2TestHelpers.hpp:141
GENERATE_INSTANTIATIONS
#define GENERATE_INSTANTIATIONS(INSTANTIATION_MACRO,...)
Macro useful for generating many explicit instantiations of function or class templates.
Definition: GenerateInstantiations.hpp:160
TMPL.hpp
Evaluate.hpp
cpp2b::iota
constexpr void iota(ForwardIterator first, ForwardIterator last, T value)
Definition: Numeric.hpp:20