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