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_t` and `ti_b_t`. 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 /// \param tensorindex_a the first TensorIndex used on the RHS of the
40 /// TensorExpression, e.g. `ti_a`
41 /// \param tensorindex_b the second TensorIndex used on the RHS of the
42 /// TensorExpression, e.g. `ti_B`
43 template <typename DataType, typename RhsSymmetry,
44  typename RhsTensorIndexTypeList, typename TensorIndexA,
45  typename TensorIndexB>
46 void test_evaluate_rank_2_impl(const TensorIndexA& tensorindex_a,
47  const TensorIndexB& tensorindex_b) noexcept {
48  Tensor<DataType, RhsSymmetry, RhsTensorIndexTypeList> R_ab(5_st);
49  std::iota(R_ab.begin(), R_ab.end(), 0.0);
50 
51  // L_{ab} = R_{ab}
52  // Use explicit type (vs auto) so the compiler checks the return type of
53  // `evaluate`
54  const Tensor<DataType, RhsSymmetry, RhsTensorIndexTypeList> L_ab =
55  ::TensorExpressions::evaluate<TensorIndexA, TensorIndexB>(
56  R_ab(tensorindex_a, tensorindex_b));
57 
58  // L_{ba} = R_{ab}
59  using L_ba_tensorindextype_list =
60  tmpl::list<tmpl::at_c<RhsTensorIndexTypeList, 1>,
61  tmpl::at_c<RhsTensorIndexTypeList, 0>>;
62  const Tensor<DataType, RhsSymmetry, L_ba_tensorindextype_list> L_ba =
63  ::TensorExpressions::evaluate<TensorIndexB, TensorIndexA>(
64  R_ab(tensorindex_a, tensorindex_b));
65 
66  const size_t dim_a = tmpl::at_c<RhsTensorIndexTypeList, 0>::dim;
67  const size_t dim_b = tmpl::at_c<RhsTensorIndexTypeList, 1>::dim;
68 
69  for (size_t i = 0; i < dim_a; ++i) {
70  for (size_t j = 0; j < dim_b; ++j) {
71  // For L_{ab} = R_{ab}, check that L_{ij} == R_{ij}
72  CHECK(L_ab.get(i, j) == R_ab.get(i, j));
73  // For L_{ba} = R_{ab}, check that L_{ji} == R_{ij}
74  CHECK(L_ba.get(j, i) == R_ab.get(i, j));
75  }
76  }
77 }
78 
79 /// \ingroup TestingFrameworkGroup
80 /// \brief Iterate testing of evaluating single rank 2 Tensors on multiple Frame
81 /// types and dimension combinations
82 ///
83 /// We test nonsymmetric indices and symmetric indices across two functions to
84 /// ensure that the code works correctly with symmetries. This function tests
85 /// one of the following symmetries:
86 /// - <2, 1> (`test_evaluate_rank_2_no_symmetry`)
87 /// - <1, 1> (`test_evaluate_rank_2_symmetric`)
88 ///
89 /// \details `TensorIndexA` and `TensorIndexB` can be any type of TensorIndex
90 /// and are not necessarily `ti_a_t` and `ti_b_t`. The "A" and "B" suffixes just
91 /// denote the ordering of the generic indices of the RHS tensor expression. In
92 /// the RHS tensor expression, it means `TensorIndexA` is the first index used
93 /// and `TensorIndexB` is the second index used.
94 ///
95 /// Note: `test_evaluate_rank_2_symmetric` has fewer template parameters due to
96 /// the two indices having a shared \ref SpacetimeIndex "TensorIndexType" and
97 /// and valence
98 ///
99 /// \tparam DataType the type of data being stored in the Tensors
100 /// \tparam TensorIndexTypeA the \ref SpacetimeIndex "TensorIndexType" of the
101 /// first index of the RHS Tensor
102 /// \tparam TensorIndexTypeB the \ref SpacetimeIndex "TensorIndexType" of the
103 /// second index of the RHS Tensor
104 /// \tparam ValenceA the valence of the first index used on the RHS of the
105 /// TensorExpression
106 /// \tparam ValenceB the valence of the second index used on the RHS of the
107 /// TensorExpression
108 /// \param tensorindex_a the first TensorIndex used on the RHS of the
109 /// TensorExpression, e.g. `ti_a`
110 /// \param tensorindex_b the second TensorIndex used on the RHS of the
111 /// TensorExpression, e.g. `ti_B`
112 template <
113  typename DataType, template <size_t, UpLo, typename> class TensorIndexTypeA,
114  template <size_t, UpLo, typename> class TensorIndexTypeB, UpLo ValenceA,
115  UpLo ValenceB, typename TensorIndexA, typename TensorIndexB>
117  const TensorIndexA& tensorindex_a,
118  const TensorIndexB& tensorindex_b) noexcept {
119 #define DIM_A(data) BOOST_PP_TUPLE_ELEM(0, data)
120 #define DIM_B(data) BOOST_PP_TUPLE_ELEM(1, data)
121 #define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)
122 
123 #define CALL_TEST_EVALUATE_RANK_2_IMPL(_, data) \
124  test_evaluate_rank_2_impl< \
125  DataType, Symmetry<2, 1>, \
126  index_list<TensorIndexTypeA<DIM_A(data), ValenceA, FRAME(data)>, \
127  TensorIndexTypeB<DIM_B(data), ValenceB, FRAME(data)>>>( \
128  tensorindex_a, tensorindex_b);
129 
130  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_2_IMPL, (1, 2, 3), (1, 2, 3),
132 
133 #undef CALL_TEST_EVALUATE_RANK_2_IMPL
134 #undef FRAME
135 #undef DIM_B
136 #undef DIM_A
137 }
138 
139 /// \ingroup TestingFrameworkGroup
140 /// \copydoc test_evaluate_rank_2_no_symmetry()
141 template <
142  typename DataType, template <size_t, UpLo, typename> class TensorIndexType,
143  UpLo Valence, typename TensorIndexA, typename TensorIndexB>
145  const TensorIndexA& tensorindex_a,
146  const TensorIndexB& tensorindex_b) noexcept {
147 #define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)
148 #define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)
149 
150 #define CALL_TEST_EVALUATE_RANK_2_IMPL(_, data) \
151  test_evaluate_rank_2_impl< \
152  DataType, Symmetry<1, 1>, \
153  index_list<TensorIndexType<DIM(data), Valence, FRAME(data)>, \
154  TensorIndexType<DIM(data), Valence, FRAME(data)>>, \
155  TensorIndexA, TensorIndexB>(tensorindex_a, tensorindex_b);
156 
157  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_2_IMPL, (1, 2, 3),
159 
160 #undef CALL_TEST_EVALUATE_RANK_2_IMPL
161 #undef FRAME
162 #undef DIM
163 }
164 
165 } // namespace TestHelpers::TensorExpressions
Frame::Inertial
Definition: IndexType.hpp:44
TensorExpression.hpp
TestHelpers::TensorExpressions::test_evaluate_rank_2_no_symmetry
void test_evaluate_rank_2_no_symmetry(const TensorIndexA &tensorindex_a, const TensorIndexB &tensorindex_b) noexcept
Iterate testing of evaluating single rank 2 Tensors on multiple Frame types and dimension combination...
Definition: EvaluateRank2TestHelpers.hpp:116
UpLo
UpLo
Definition: IndexType.hpp:20
Frame::Grid
Definition: IndexType.hpp:43
TestHelpers::TensorExpressions::test_evaluate_rank_2_symmetric
void test_evaluate_rank_2_symmetric(const TensorIndexA &tensorindex_a, const TensorIndexB &tensorindex_b) noexcept
Iterate testing of evaluating single rank 2 Tensors on multiple Frame types and dimension combination...
Definition: EvaluateRank2TestHelpers.hpp:144
iterator
cstddef
TestHelpers::TensorExpressions::test_evaluate_rank_2_impl
void test_evaluate_rank_2_impl(const TensorIndexA &tensorindex_a, const TensorIndexB &tensorindex_b) noexcept
Test that evaluating a right hand side tensor expression containing a single rank 2 tensor correctly ...
Definition: EvaluateRank2TestHelpers.hpp:46
Tensor.hpp
numeric
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