EvaluateRank3TestHelpers.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/Literals.hpp"
15 #include "Utilities/TMPL.hpp"
16 
17 namespace TestHelpers::TensorExpressions {
18 
19 /// \ingroup TestingFrameworkGroup
20 /// \brief Test that evaluating a right hand side tensor expression containing a
21 /// single rank 3 tensor correctly assigns the data to the evaluated left hand
22 /// side tensor
23 ///
24 /// \details `TensorIndexA`, `TensorIndexB`, and `TensorIndexC` can be any type
25 /// of TensorIndex and are not necessarily `ti_a_t`, `ti_b_t`, and `ti_c_t`. The
26 /// "A", "B", and "C" suffixes just denote the ordering of the generic indices
27 /// of the RHS tensor expression. In the RHS tensor expression, it means
28 /// `TensorIndexA` is the first index used, `TensorIndexB` is the second index
29 /// used, and `TensorIndexC` is the third index used.
30 ///
31 /// If we consider the RHS tensor's generic indices to be (a, b, c), then this
32 /// test checks that the data in the evaluated LHS tensor is correct according
33 /// to the index orders of the LHS and RHS. The possible cases that are checked
34 /// are when the LHS tensor is evaluated with index orders: (a, b, c),
35 /// (a, c, b), (b, a, c), (b, c, a), (c, a, b), and (c, b, a).
36 ///
37 /// \tparam DataType the type of data being stored in the Tensors
38 /// \tparam RhsSymmetry the ::Symmetry of the RHS Tensor
39 /// \tparam RhsTensorIndexTypeList the RHS Tensor's typelist of
40 /// \ref SpacetimeIndex "TensorIndexType"s
41 /// \param tensorindex_a the first TensorIndex used on the RHS of the
42 /// TensorExpression, e.g. `ti_a`
43 /// \param tensorindex_b the second TensorIndex used on the RHS of the
44 /// TensorExpression, e.g. `ti_B`
45 /// \param tensorindex_c the third TensorIndex used on the RHS of the
46 /// TensorExpression, e.g. `ti_c`
47 template <typename DataType, typename RhsSymmetry,
48  typename RhsTensorIndexTypeList, typename TensorIndexA,
49  typename TensorIndexB, typename TensorIndexC>
50 void test_evaluate_rank_3_impl(const TensorIndexA& tensorindex_a,
51  const TensorIndexB& tensorindex_b,
52  const TensorIndexC& tensorindex_c) noexcept {
53  Tensor<DataType, RhsSymmetry, RhsTensorIndexTypeList> R_abc(5_st);
54  std::iota(R_abc.begin(), R_abc.end(), 0.0);
55 
56  // Used for enforcing the ordering of the symmetry and TensorIndexTypes of the
57  // LHS Tensor returned by `evaluate`
58  const std::int32_t rhs_symmetry_element_a = tmpl::at_c<RhsSymmetry, 0>::value;
59  const std::int32_t rhs_symmetry_element_b = tmpl::at_c<RhsSymmetry, 1>::value;
60  const std::int32_t rhs_symmetry_element_c = tmpl::at_c<RhsSymmetry, 2>::value;
61  using rhs_tensorindextype_a = tmpl::at_c<RhsTensorIndexTypeList, 0>;
62  using rhs_tensorindextype_b = tmpl::at_c<RhsTensorIndexTypeList, 1>;
63  using rhs_tensorindextype_c = tmpl::at_c<RhsTensorIndexTypeList, 2>;
64 
65  // L_{abc} = R_{abc}
66  // Use explicit type (vs auto) so the compiler checks the return type of
67  // `evaluate`
68  const Tensor<DataType, RhsSymmetry, RhsTensorIndexTypeList> L_abc =
69  ::TensorExpressions::evaluate<TensorIndexA, TensorIndexB, TensorIndexC>(
70  R_abc(tensorindex_a, tensorindex_b, tensorindex_c));
71 
72  // L_{acb} = R_{abc}
73  using L_acb_symmetry =
74  Symmetry<rhs_symmetry_element_a, rhs_symmetry_element_c,
75  rhs_symmetry_element_b>;
76  using L_acb_tensorindextype_list =
77  tmpl::list<rhs_tensorindextype_a, rhs_tensorindextype_c,
78  rhs_tensorindextype_b>;
79  const Tensor<DataType, L_acb_symmetry, L_acb_tensorindextype_list> L_acb =
80  ::TensorExpressions::evaluate<TensorIndexA, TensorIndexC, TensorIndexB>(
81  R_abc(tensorindex_a, tensorindex_b, tensorindex_c));
82 
83  // L_{bac} = R_{abc}
84  using L_bac_symmetry =
85  Symmetry<rhs_symmetry_element_b, rhs_symmetry_element_a,
86  rhs_symmetry_element_c>;
87  using L_bac_tensorindextype_list =
88  tmpl::list<rhs_tensorindextype_b, rhs_tensorindextype_a,
89  rhs_tensorindextype_c>;
90  const Tensor<DataType, L_bac_symmetry, L_bac_tensorindextype_list> L_bac =
91  ::TensorExpressions::evaluate<TensorIndexB, TensorIndexA, TensorIndexC>(
92  R_abc(tensorindex_a, tensorindex_b, tensorindex_c));
93 
94  // L_{bca} = R_{abc}
95  using L_bca_symmetry =
96  Symmetry<rhs_symmetry_element_b, rhs_symmetry_element_c,
97  rhs_symmetry_element_a>;
98  using L_bca_tensorindextype_list =
99  tmpl::list<rhs_tensorindextype_b, rhs_tensorindextype_c,
100  rhs_tensorindextype_a>;
101  const Tensor<DataType, L_bca_symmetry, L_bca_tensorindextype_list> L_bca =
102  ::TensorExpressions::evaluate<TensorIndexB, TensorIndexC, TensorIndexA>(
103  R_abc(tensorindex_a, tensorindex_b, tensorindex_c));
104 
105  // L_{cab} = R_{abc}
106  using L_cab_symmetry =
107  Symmetry<rhs_symmetry_element_c, rhs_symmetry_element_a,
108  rhs_symmetry_element_b>;
109  using L_cab_tensorindextype_list =
110  tmpl::list<rhs_tensorindextype_c, rhs_tensorindextype_a,
111  rhs_tensorindextype_b>;
112  const Tensor<DataType, L_cab_symmetry, L_cab_tensorindextype_list> L_cab =
113  ::TensorExpressions::evaluate<TensorIndexC, TensorIndexA, TensorIndexB>(
114  R_abc(tensorindex_a, tensorindex_b, tensorindex_c));
115 
116  // L_{cba} = R_{abc}
117  using L_cba_symmetry =
118  Symmetry<rhs_symmetry_element_c, rhs_symmetry_element_b,
119  rhs_symmetry_element_a>;
120  using L_cba_tensorindextype_list =
121  tmpl::list<rhs_tensorindextype_c, rhs_tensorindextype_b,
122  rhs_tensorindextype_a>;
123  const Tensor<DataType, L_cba_symmetry, L_cba_tensorindextype_list> L_cba =
124  ::TensorExpressions::evaluate<TensorIndexC, TensorIndexB, TensorIndexA>(
125  R_abc(tensorindex_a, tensorindex_b, tensorindex_c));
126 
127  const size_t dim_a = tmpl::at_c<RhsTensorIndexTypeList, 0>::dim;
128  const size_t dim_b = tmpl::at_c<RhsTensorIndexTypeList, 1>::dim;
129  const size_t dim_c = tmpl::at_c<RhsTensorIndexTypeList, 2>::dim;
130 
131  for (size_t i = 0; i < dim_a; ++i) {
132  for (size_t j = 0; j < dim_b; ++j) {
133  for (size_t k = 0; k < dim_c; ++k) {
134  // For L_{abc} = R_{abc}, check that L_{ijk} == R_{ijk}
135  CHECK(L_abc.get(i, j, k) == R_abc.get(i, j, k));
136  // For L_{acb} = R_{abc}, check that L_{ikj} == R_{ijk}
137  CHECK(L_acb.get(i, k, j) == R_abc.get(i, j, k));
138  // For L_{bac} = R_{abc}, check that L_{jik} == R_{ijk}
139  CHECK(L_bac.get(j, i, k) == R_abc.get(i, j, k));
140  // For L_{bca} = R_{abc}, check that L_{jki} == R_{ijk}
141  CHECK(L_bca.get(j, k, i) == R_abc.get(i, j, k));
142  // For L_{cab} = R_{abc}, check that L_{kij} == R_{ijk}
143  CHECK(L_cab.get(k, i, j) == R_abc.get(i, j, k));
144  // For L_{cba} = R_{abc}, check that L_{kji} == R_{ijk}
145  CHECK(L_cba.get(k, j, i) == R_abc.get(i, j, k));
146  }
147  }
148  }
149 }
150 
151 /// \ingroup TestingFrameworkGroup
152 /// \brief Iterate testing of evaluating single rank 3 Tensors on multiple Frame
153 /// types and dimension combinations
154 ///
155 /// We test various different symmetries across several functions to ensure that
156 /// the code works correctly with symmetries. This function tests one of the
157 /// following symmetries:
158 /// - <3, 2, 1> (`test_evaluate_rank_3_no_symmetry`)
159 /// - <2, 2, 1> (`test_evaluate_rank_3_ab_symmetry`)
160 /// - <2, 1, 2> (`test_evaluate_rank_3_ac_symmetry`)
161 /// - <2, 1, 1> (`test_evaluate_rank_3_bc_symmetry`)
162 /// - <1, 1, 1> (`test_evaluate_rank_3_abc_symmetry`)
163 ///
164 /// \details `TensorIndexA`, `TensorIndexB`, and `TensorIndexC` can be any type
165 /// of TensorIndex and are not necessarily `ti_a_t`, `ti_b_t`, and `ti_c_t`. The
166 /// "A", "B", and "C" suffixes just denote the ordering of the generic indices
167 /// of the RHS tensor expression. In the RHS tensor expression, it means
168 /// `TensorIndexA` is the first index used, `TensorIndexB` is the second index
169 /// used, and `TensorIndexC` is the third index used.
170 ///
171 /// Note: the functions dealing with symmetric indices have fewer template
172 /// parameters due to the indices having a shared \ref SpacetimeIndex
173 /// "TensorIndexType" and valence
174 ///
175 /// \tparam DataType the type of data being stored in the Tensors
176 /// \tparam TensorIndexTypeA the \ref SpacetimeIndex "TensorIndexType" of the
177 /// first index of the RHS Tensor
178 /// \tparam TensorIndexTypeB the \ref SpacetimeIndex "TensorIndexType" of the
179 /// second index of the RHS Tensor
180 /// \tparam TensorIndexTypeC the \ref SpacetimeIndex "TensorIndexType" of the
181 /// third index of the RHS Tensor
182 /// \tparam ValenceA the valence of the first index used on the RHS of the
183 /// TensorExpression
184 /// \tparam ValenceB the valence of the second index used on the RHS of the
185 /// TensorExpression
186 /// \tparam ValenceC the valence of the third index used on the RHS of the
187 /// TensorExpression
188 /// \param tensorindex_a the first TensorIndex used on the RHS of the
189 /// TensorExpression, e.g. `ti_a`
190 /// \param tensorindex_b the second TensorIndex used on the RHS of the
191 /// TensorExpression, e.g. `ti_B`
192 /// \param tensorindex_c the third TensorIndex used on the RHS of the
193 /// TensorExpression, e.g. `ti_c`
194 template <typename DataType,
195  template <size_t, UpLo, typename> class TensorIndexTypeA,
196  template <size_t, UpLo, typename> class TensorIndexTypeB,
197  template <size_t, UpLo, typename> class TensorIndexTypeC,
198  UpLo ValenceA, UpLo ValenceB, UpLo ValenceC, typename TensorIndexA,
199  typename TensorIndexB, typename TensorIndexC>
201  const TensorIndexA& tensorindex_a, const TensorIndexB& tensorindex_b,
202  const TensorIndexC& tensorindex_c) noexcept {
203 #define DIM_A(data) BOOST_PP_TUPLE_ELEM(0, data)
204 #define DIM_B(data) BOOST_PP_TUPLE_ELEM(1, data)
205 #define DIM_C(data) BOOST_PP_TUPLE_ELEM(2, data)
206 #define FRAME(data) BOOST_PP_TUPLE_ELEM(3, data)
207 
208 #define CALL_TEST_EVALUATE_RANK_3_IMPL(_, data) \
209  test_evaluate_rank_3_impl< \
210  DataType, Symmetry<3, 2, 1>, \
211  index_list<TensorIndexTypeA<DIM_A(data), ValenceA, FRAME(data)>, \
212  TensorIndexTypeB<DIM_B(data), ValenceB, FRAME(data)>, \
213  TensorIndexTypeC<DIM_C(data), ValenceC, FRAME(data)>>>( \
214  tensorindex_a, tensorindex_b, tensorindex_c);
215 
216  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_3_IMPL, (1, 2, 3), (1, 2, 3),
217  (1, 2, 3), (Frame::Grid, Frame::Inertial))
218 
219 #undef CALL_TEST_EVALUATE_RANK_3_IMPL
220 #undef FRAME
221 #undef DIM_C
222 #undef DIM_B
223 #undef DIM_A
224 }
225 
226 /// \ingroup TestingFrameworkGroup
227 /// \copydoc test_evaluate_rank_3_no_symmetry()
228 template <typename DataType,
229  template <size_t, UpLo, typename> class TensorIndexTypeAB,
230  template <size_t, UpLo, typename> class TensorIndexTypeC,
231  UpLo ValenceAB, UpLo ValenceC, typename TensorIndexA,
232  typename TensorIndexB, typename TensorIndexC>
234  const TensorIndexA& tensorindex_a, const TensorIndexB& tensorindex_b,
235  const TensorIndexC& tensorindex_c) noexcept {
236 #define DIM_AB(data) BOOST_PP_TUPLE_ELEM(0, data)
237 #define DIM_C(data) BOOST_PP_TUPLE_ELEM(1, data)
238 #define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)
239 
240 #define CALL_TEST_EVALUATE_RANK_3_IMPL(_, data) \
241  test_evaluate_rank_3_impl< \
242  DataType, Symmetry<2, 2, 1>, \
243  index_list<TensorIndexTypeAB<DIM_AB(data), ValenceAB, FRAME(data)>, \
244  TensorIndexTypeAB<DIM_AB(data), ValenceAB, FRAME(data)>, \
245  TensorIndexTypeC<DIM_C(data), ValenceC, FRAME(data)>>>( \
246  tensorindex_a, tensorindex_b, tensorindex_c);
247 
248  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_3_IMPL, (1, 2, 3), (1, 2, 3),
250 
251 #undef CALL_TEST_EVALUATE_RANK_3_IMPL
252 #undef FRAME
253 #undef DIM_C
254 #undef DIM_AB
255 }
256 
257 /// \ingroup TestingFrameworkGroup
258 /// \copydoc test_evaluate_rank_3_no_symmetry()
259 template <typename DataType,
260  template <size_t, UpLo, typename> class TensorIndexTypeAC,
261  template <size_t, UpLo, typename> class TensorIndexTypeB,
262  UpLo ValenceAC, UpLo ValenceB, typename TensorIndexA,
263  typename TensorIndexB, typename TensorIndexC>
265  const TensorIndexA& tensorindex_a, const TensorIndexB& tensorindex_b,
266  const TensorIndexC& tensorindex_c) noexcept {
267 #define DIM_AC(data) BOOST_PP_TUPLE_ELEM(0, data)
268 #define DIM_B(data) BOOST_PP_TUPLE_ELEM(1, data)
269 #define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)
270 
271 #define CALL_TEST_EVALUATE_RANK_3_IMPL(_, data) \
272  test_evaluate_rank_3_impl< \
273  DataType, Symmetry<2, 1, 2>, \
274  index_list<TensorIndexTypeAC<DIM_AC(data), ValenceAC, FRAME(data)>, \
275  TensorIndexTypeB<DIM_B(data), ValenceB, FRAME(data)>, \
276  TensorIndexTypeAC<DIM_AC(data), ValenceAC, FRAME(data)>>>( \
277  tensorindex_a, tensorindex_b, tensorindex_c);
278 
279  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_3_IMPL, (1, 2, 3), (1, 2, 3),
281 
282 #undef CALL_TEST_EVALUATE_RANK_3_IMPL
283 #undef FRAME
284 #undef DIM_B
285 #undef DIM_AC
286 }
287 
288 /// \ingroup TestingFrameworkGroup
289 /// \copydoc test_evaluate_rank_3_no_symmetry()
290 template <typename DataType,
291  template <size_t, UpLo, typename> class TensorIndexTypeA,
292  template <size_t, UpLo, typename> class TensorIndexTypeBC,
293  UpLo ValenceA, UpLo ValenceBC, typename TensorIndexA,
294  typename TensorIndexB, typename TensorIndexC>
296  const TensorIndexA& tensorindex_a, const TensorIndexB& tensorindex_b,
297  const TensorIndexC& tensorindex_c) noexcept {
298 #define DIM_A(data) BOOST_PP_TUPLE_ELEM(0, data)
299 #define DIM_BC(data) BOOST_PP_TUPLE_ELEM(1, data)
300 #define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)
301 
302 #define CALL_TEST_EVALUATE_RANK_3_IMPL(_, data) \
303  test_evaluate_rank_3_impl< \
304  DataType, Symmetry<2, 1, 1>, \
305  index_list<TensorIndexTypeA<DIM_A(data), ValenceA, FRAME(data)>, \
306  TensorIndexTypeBC<DIM_BC(data), ValenceBC, FRAME(data)>, \
307  TensorIndexTypeBC<DIM_BC(data), ValenceBC, FRAME(data)>>>( \
308  tensorindex_a, tensorindex_b, tensorindex_c);
309 
310  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_3_IMPL, (1, 2, 3), (1, 2, 3),
312 
313 #undef CALL_TEST_EVALUATE_RANK_3_IMPL
314 #undef FRAME
315 #undef DIM_BC
316 #undef DIM_A
317 }
318 
319 /// \ingroup TestingFrameworkGroup
320 /// \copydoc test_evaluate_rank_3_no_symmetry()
321 template <typename DataType,
322  template <size_t, UpLo, typename> class TensorIndexType, UpLo Valence,
323  typename TensorIndexA, typename TensorIndexB, typename TensorIndexC>
325  const TensorIndexA& tensorindex_a, const TensorIndexB& tensorindex_b,
326  const TensorIndexC& tensorindex_c) noexcept {
327 #define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)
328 #define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)
329 
330 #define CALL_TEST_EVALUATE_RANK_3_IMPL(_, data) \
331  test_evaluate_rank_3_impl< \
332  DataType, Symmetry<1, 1, 1>, \
333  index_list<TensorIndexType<DIM(data), Valence, FRAME(data)>, \
334  TensorIndexType<DIM(data), Valence, FRAME(data)>, \
335  TensorIndexType<DIM(data), Valence, FRAME(data)>>>( \
336  tensorindex_a, tensorindex_b, tensorindex_c);
337 
338  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_3_IMPL, (1, 2, 3),
340 
341 #undef CALL_TEST_EVALUATE_RANK_3_IMPL
342 #undef FRAME
343 #undef DIM
344 }
345 
346 } // namespace TestHelpers::TensorExpressions
Frame::Inertial
Definition: IndexType.hpp:44
TensorExpression.hpp
UpLo
UpLo
Definition: IndexType.hpp:20
Frame::Grid
Definition: IndexType.hpp:43
Literals.hpp
iterator
TestHelpers::TensorExpressions::test_evaluate_rank_3_ab_symmetry
void test_evaluate_rank_3_ab_symmetry(const TensorIndexA &tensorindex_a, const TensorIndexB &tensorindex_b, const TensorIndexC &tensorindex_c) noexcept
Iterate testing of evaluating single rank 3 Tensors on multiple Frame types and dimension combination...
Definition: EvaluateRank3TestHelpers.hpp:233
TestHelpers::TensorExpressions::test_evaluate_rank_3_no_symmetry
void test_evaluate_rank_3_no_symmetry(const TensorIndexA &tensorindex_a, const TensorIndexB &tensorindex_b, const TensorIndexC &tensorindex_c) noexcept
Iterate testing of evaluating single rank 3 Tensors on multiple Frame types and dimension combination...
Definition: EvaluateRank3TestHelpers.hpp:200
cstddef
TestHelpers::TensorExpressions::test_evaluate_rank_3_bc_symmetry
void test_evaluate_rank_3_bc_symmetry(const TensorIndexA &tensorindex_a, const TensorIndexB &tensorindex_b, const TensorIndexC &tensorindex_c) noexcept
Iterate testing of evaluating single rank 3 Tensors on multiple Frame types and dimension combination...
Definition: EvaluateRank3TestHelpers.hpp:295
std::int32_t
TestHelpers::TensorExpressions::test_evaluate_rank_3_impl
void test_evaluate_rank_3_impl(const TensorIndexA &tensorindex_a, const TensorIndexB &tensorindex_b, const TensorIndexC &tensorindex_c) noexcept
Test that evaluating a right hand side tensor expression containing a single rank 3 tensor correctly ...
Definition: EvaluateRank3TestHelpers.hpp:50
Symmetry
typename detail::SymmetryImpl< std::make_index_sequence< sizeof...(T)>, tmpl::integral_list< std::int32_t, T... > >::type Symmetry
Computes the canonical symmetry from the integers T
Definition: Symmetry.hpp:81
Tensor.hpp
TestHelpers::TensorExpressions::test_evaluate_rank_3_abc_symmetry
void test_evaluate_rank_3_abc_symmetry(const TensorIndexA &tensorindex_a, const TensorIndexB &tensorindex_b, const TensorIndexC &tensorindex_c) noexcept
Iterate testing of evaluating single rank 3 Tensors on multiple Frame types and dimension combination...
Definition: EvaluateRank3TestHelpers.hpp:324
numeric
TestHelpers::TensorExpressions::test_evaluate_rank_3_ac_symmetry
void test_evaluate_rank_3_ac_symmetry(const TensorIndexA &tensorindex_a, const TensorIndexB &tensorindex_b, const TensorIndexC &tensorindex_c) noexcept
Iterate testing of evaluating single rank 3 Tensors on multiple Frame types and dimension combination...
Definition: EvaluateRank3TestHelpers.hpp:264
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