Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <array>
7 : #include <cmath>
8 : #include <cstddef>
9 : #include <limits>
10 : #include <type_traits>
11 : #include <utility>
12 :
13 : #include "DataStructures/Tensor/Expressions/NumberAsExpression.hpp"
14 : #include "DataStructures/Tensor/Expressions/TensorExpression.hpp"
15 : #include "DataStructures/Tensor/Expressions/TimeIndex.hpp"
16 : #include "Utilities/ForceInline.hpp"
17 : #include "Utilities/TMPL.hpp"
18 :
19 : namespace tenex {
20 : /// \ingroup TensorExpressionsGroup
21 : /// \brief Defines the tensor expression representing the square root of a
22 : /// tensor expression that evaluates to a rank 0 tensor
23 : ///
24 : /// \details The expression can have a non-zero number of indices as long as
25 : /// all indices are concrete time indices, as this represents a rank 0 tensor.
26 : ///
27 : /// For details on aliases and members defined in this class, as well as general
28 : /// `TensorExpression` terminology used in its members' documentation, see
29 : /// documentation for `TensorExpression`.
30 : ///
31 : /// \tparam T the type of the tensor expression of which to take the square
32 : /// root
33 : /// \tparam Args the TensorIndexs of the expression
34 : template <typename T, typename... Args>
35 1 : struct SquareRoot
36 : : public TensorExpression<SquareRoot<T, Args...>, typename T::type,
37 : typename T::symmetry, typename T::index_list,
38 : tmpl::list<Args...>> {
39 : static_assert(
40 : (... and tt::is_time_index<Args>::value),
41 : "Can only take the square root of a tensor expression that evaluates to "
42 : "a rank 0 tensor.");
43 :
44 : // === Index properties ===
45 : /// The type of the data being stored in the result of the expression
46 1 : using type = typename T::type;
47 : /// The ::Symmetry of the result of the expression
48 1 : using symmetry = typename T::symmetry;
49 : /// The list of \ref SpacetimeIndex "TensorIndexType"s of the result of the
50 : /// expression
51 1 : using index_list = typename T::index_list;
52 : /// The list of generic `TensorIndex`s of the result of the expression
53 1 : using args_list = tmpl::list<Args...>;
54 : /// The number of tensor indices in the result of the expression
55 1 : static constexpr auto num_tensor_indices = sizeof...(Args);
56 :
57 : // === Expression subtree properties ===
58 : /// The number of arithmetic tensor operations done in the subtree for the
59 : /// left operand
60 1 : static constexpr size_t num_ops_left_child = T::num_ops_subtree;
61 : /// The number of arithmetic tensor operations done in the subtree for the
62 : /// right operand. This is 0 because this expression represents a unary
63 : /// operation.
64 1 : static constexpr size_t num_ops_right_child = 0;
65 : /// The total number of arithmetic tensor operations done in this expression's
66 : /// whole subtree
67 1 : static constexpr size_t num_ops_subtree = num_ops_left_child + 1;
68 : /// The height of this expression's node in the expression tree relative to
69 : /// the closest `TensorAsExpression` leaf in its subtree
70 1 : static constexpr size_t height_relative_to_closest_tensor_leaf_in_subtree =
71 : T::height_relative_to_closest_tensor_leaf_in_subtree !=
72 : std::numeric_limits<size_t>::max()
73 : ? T::height_relative_to_closest_tensor_leaf_in_subtree + 1
74 : : T::height_relative_to_closest_tensor_leaf_in_subtree;
75 :
76 : // === Properties for splitting up subexpressions along the primary path ===
77 : // These definitions only have meaning if this expression actually ends up
78 : // being along the primary path that is taken when evaluating the whole tree.
79 : // See documentation for `TensorExpression` for more details.
80 : /// If on the primary path, whether or not the expression is an ending point
81 : /// of a leg
82 1 : static constexpr bool is_primary_end = T::is_primary_start;
83 : /// If on the primary path, this is the remaining number of arithmetic tensor
84 : /// operations that need to be done in the subtree of the child along the
85 : /// primary path, given that we will have already computed the whole subtree
86 : /// at the next lowest leg's starting point.
87 1 : static constexpr size_t num_ops_to_evaluate_primary_left_child =
88 : is_primary_end ? 0 : T::num_ops_to_evaluate_primary_subtree;
89 : /// If on the primary path, this is the remaining number of arithmetic tensor
90 : /// operations that need to be done in the right operand's subtree. No
91 : /// splitting is currently done, so this is just `num_ops_right_child`.
92 1 : static constexpr size_t num_ops_to_evaluate_primary_right_child =
93 : num_ops_right_child;
94 : /// If on the primary path, this is the remaining number of arithmetic tensor
95 : /// operations that need to be done for this expression's subtree, given that
96 : /// we will have already computed the subtree at the next lowest leg's
97 : /// starting point
98 1 : static constexpr size_t num_ops_to_evaluate_primary_subtree =
99 : num_ops_to_evaluate_primary_left_child +
100 : num_ops_to_evaluate_primary_right_child + 1;
101 : /// If on the primary path, whether or not the expression is a starting point
102 : /// of a leg
103 1 : static constexpr bool is_primary_start =
104 : num_ops_to_evaluate_primary_subtree >=
105 : detail::max_num_ops_in_sub_expression<type>;
106 : /// If on the primary path, whether or not the expression's child along the
107 : /// primary path is a subtree that contains a starting point of a leg along
108 : /// the primary path
109 1 : static constexpr bool primary_child_subtree_contains_primary_start =
110 : T::primary_subtree_contains_primary_start;
111 : /// If on the primary path, whether or not this subtree contains a starting
112 : /// point of a leg along the primary path
113 1 : static constexpr bool primary_subtree_contains_primary_start =
114 : is_primary_start or primary_child_subtree_contains_primary_start;
115 :
116 0 : SquareRoot(T t) : t_(std::move(t)) {}
117 0 : ~SquareRoot() override = default;
118 :
119 : /// \brief Assert that the LHS tensor of the equation does not also appear in
120 : /// this expression's subtree
121 : template <typename LhsTensor>
122 1 : SPECTRE_ALWAYS_INLINE void assert_lhs_tensor_not_in_rhs_expression(
123 : const gsl::not_null<LhsTensor*> lhs_tensor) const {
124 : if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T>) {
125 : t_.assert_lhs_tensor_not_in_rhs_expression(lhs_tensor);
126 : }
127 : }
128 :
129 : /// \brief Assert that each instance of the LHS tensor in the RHS tensor
130 : /// expression uses the same generic index order that the LHS uses
131 : ///
132 : /// \tparam LhsTensorIndices the list of generic `TensorIndex`s of the LHS
133 : /// result `Tensor` being computed
134 : /// \param lhs_tensor the LHS result `Tensor` being computed
135 : template <typename LhsTensorIndices, typename LhsTensor>
136 1 : SPECTRE_ALWAYS_INLINE void assert_lhs_tensorindices_same_in_rhs(
137 : const gsl::not_null<LhsTensor*> lhs_tensor) const {
138 : if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T>) {
139 : t_.template assert_lhs_tensorindices_same_in_rhs<LhsTensorIndices>(
140 : lhs_tensor);
141 : }
142 : }
143 :
144 : /// \brief Get the size of a component from a `Tensor` in this expression's
145 : /// subtree of the RHS `TensorExpression`
146 : ///
147 : /// \return the size of a component from a `Tensor` in this expression's
148 : /// subtree of the RHS `TensorExpression`
149 1 : SPECTRE_ALWAYS_INLINE size_t get_rhs_tensor_component_size() const {
150 : return t_.get_rhs_tensor_component_size();
151 : }
152 :
153 : /// \brief Returns the square root of the component of the tensor evaluated
154 : /// from the contained tensor expression
155 : ///
156 : /// \details
157 : /// SquareRoot only supports tensor expressions that evaluate to a rank 0
158 : /// Tensor. This is why `multi_index` is always an array of size 0.
159 : ///
160 : /// \param multi_index the multi-index of the component of which to take the
161 : /// square root
162 : /// \return the square root of the component of the tensor evaluated from the
163 : /// contained tensor expression
164 1 : SPECTRE_ALWAYS_INLINE decltype(auto) get(
165 : const std::array<size_t, num_tensor_indices>& multi_index) const {
166 : return sqrt(t_.get(multi_index));
167 : }
168 :
169 : /// \brief Returns the square root of the component of the tensor evaluated
170 : /// from the contained tensor expression
171 : ///
172 : /// \details
173 : /// SquareRoot only supports tensor expressions that evaluate to a rank 0
174 : /// Tensor. This is why `multi_index` is always an array of size 0.
175 : ///
176 : /// This function differs from `get` in that it takes into account whether we
177 : /// have already computed part of the result component at a lower subtree.
178 : /// In recursively computing this square root, the current result component
179 : /// will be substituted in for the most recent (highest) subtree below it that
180 : /// has already been evaluated.
181 : ///
182 : /// \param result_component the LHS tensor component to evaluate
183 : /// \param multi_index the multi-index of the component of which to take the
184 : /// square root
185 : /// \return the square root of the component of the tensor evaluated from the
186 : /// contained tensor expression
187 : template <typename ResultType>
188 1 : SPECTRE_ALWAYS_INLINE decltype(auto) get_primary(
189 : const ResultType& result_component,
190 : const std::array<size_t, num_tensor_indices>& multi_index) const {
191 : if constexpr (is_primary_end) {
192 : (void)multi_index;
193 : // We've already computed the whole child subtree on the primary path, so
194 : // just return the square root of the current result component
195 : return sqrt(result_component);
196 : } else {
197 : // We haven't yet evaluated the whole subtree for this expression, so
198 : // return the square root of this expression's subtree
199 : return sqrt(t_.get_primary(result_component, multi_index));
200 : }
201 : }
202 :
203 : /// \brief Successively evaluate the LHS Tensor's result component at each
204 : /// leg in this expression's subtree
205 : ///
206 : /// \details
207 : /// This function takes into account whether we have already computed part of
208 : /// the result component at a lower subtree. In recursively computing this
209 : /// square root, the current result component will be substituted in for the
210 : /// most recent (highest) subtree below it that has already been evaluated.
211 : ///
212 : /// \param result_component the LHS tensor component to evaluate
213 : /// \param multi_index the multi-index of the component of the result tensor
214 : /// to evaluate
215 : template <typename ResultType>
216 1 : SPECTRE_ALWAYS_INLINE void evaluate_primary_subtree(
217 : ResultType& result_component,
218 : const std::array<size_t, num_tensor_indices>& multi_index) const {
219 : if constexpr (primary_child_subtree_contains_primary_start) {
220 : // The primary child's subtree contains at least one leg, so recurse down
221 : // and evaluate that first
222 : t_.evaluate_primary_subtree(result_component, multi_index);
223 : }
224 :
225 : if constexpr (is_primary_start) {
226 : // We want to evaluate the subtree for this expression
227 : result_component = get_primary(result_component, multi_index);
228 : }
229 : }
230 :
231 : private:
232 : /// Operand expression
233 1 : T t_;
234 : };
235 : } // namespace tenex
236 :
237 : /// \ingroup TensorExpressionsGroup
238 : /// \brief Returns the tensor expression representing the square root of a
239 : /// tensor expression that evaluates to a rank 0 tensor
240 : ///
241 : /// \details
242 : /// `t` must be an expression that, when evaluated, would be a rank 0 tensor.
243 : /// For example, if `R` and `S` are Tensors, here is a non-exhaustive list of
244 : /// some of the acceptable forms that `t` could take:
245 : /// - `R()`
246 : /// - `R(ti::A, ti::a)`
247 : /// - `(R(ti::A, ti::B) * S(ti::a, ti::b))`
248 : /// - `R(ti::t, ti::t) + 1.0`
249 : ///
250 : /// \param t the tensor expression of which to take the square root
251 : template <typename T, typename X, typename Symm, typename IndexList,
252 : typename... Args>
253 1 : SPECTRE_ALWAYS_INLINE auto sqrt(
254 : const TensorExpression<T, X, Symm, IndexList, tmpl::list<Args...>>& t) {
255 : return tenex::SquareRoot<T, Args...>(~t);
256 : }
|