Line data Source code
1 1 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : /// \file
5 : /// Defines generic tensor indices used in `TensorExpression`s
6 :
7 : #pragma once
8 :
9 : #include <array>
10 : #include <cassert>
11 : #include <cstddef>
12 : #include <type_traits>
13 :
14 : #include "DataStructures/Tensor/IndexType.hpp"
15 : #include "Utilities/ForceInline.hpp"
16 : #include "Utilities/Requires.hpp"
17 : #include "Utilities/TMPL.hpp"
18 :
19 : namespace tenex {
20 : namespace TensorIndex_detail {
21 : // The below values are used to separate upper indices from lower indices and
22 : // spatial indices from spacetime indices.
23 : //
24 : // Tensor expressions perform as many calculations as possible in a constexpr
25 : // context, which means working with fundamental types, specifically integer
26 : // types, is easiest. By using sentinel values defined in one location we can
27 : // easily control the encoding without having magic values floating around in
28 : // many places. Furthermore, encoding all the information in the `size_t` means
29 : // that when a failure occurs in one of the constexpr calculations it is
30 : // reasonably easy to debug because, while encoded, the full type information is
31 : // present. This approach can effectively be thought of as using specific bits
32 : // in the `size_t` to mark information, using the size_t more as a bitfield than
33 : // anything else. For human readability, we use base-10 numbers instead of
34 : // base-2 values that would truly set individual bits.
35 : //
36 : // Spacetime indices are represented by values [0, `spatial_sentinel`) and
37 : // spatial indices are represented by values
38 : // [`spatial_sentinel`, `max_sentinel`). Lower spacetime indices are represented
39 : // by values [0, `upper_sentinel`), and upper spacetime indices are represented
40 : // by values [`upper_sentinel`, `spatial_sentinel`). Lower spatial indices are
41 : // represented by values
42 : // [`spatial_sentinel`, `spatial_sentinel` + `upper_sentinel`), and upper
43 : // spatial indices are represented by values
44 : // [`spatial_sentinel` + `upper_sentinel`, `max_sentinel`). Values equal to or
45 : // above `max_sentinel` are considered invalid for representing an index.
46 : static constexpr size_t spatial_sentinel = 1000;
47 : static constexpr size_t upper_sentinel = 500;
48 : static constexpr size_t upper_spatial_sentinel =
49 : spatial_sentinel + upper_sentinel;
50 : static constexpr size_t max_sentinel = 2000;
51 : } // namespace TensorIndex_detail
52 : } // namespace tenex
53 :
54 : /*!
55 : * \ingroup TensorExpressionsGroup
56 : * \brief Represents the geeric indices in a TensorExpression
57 : *
58 : * \details
59 : * Used to denote a tensor index in a tensor slot. This allows the following
60 : * type of expressions to work:
61 : * \code{.cpp}
62 : * auto T = evaluate<ti::a, ti::b>(F(ti::a, ti::b) + S(ti::b, ti::a));
63 : * \endcode
64 : * where `decltype(ti::a) == TensorIndex<0>` and
65 : * `decltype(ti::b) == TensorIndex<1>`. That is, `ti::a` and `ti::b` are
66 : * placeholders for objects of type `TensorIndex<0>` and `TensorIndex<1>`,
67 : * respectively.
68 : */
69 : template <std::size_t I,
70 : Requires<(I < tenex::TensorIndex_detail::max_sentinel)> = nullptr>
71 1 : struct TensorIndex {
72 0 : using value_type = std::size_t;
73 0 : using type = TensorIndex<I>;
74 0 : static constexpr value_type value = I;
75 0 : static constexpr UpLo valence =
76 : ((I < tenex::TensorIndex_detail::upper_sentinel) or
77 : (I >= tenex::TensorIndex_detail::spatial_sentinel and
78 : I < tenex::TensorIndex_detail::upper_spatial_sentinel))
79 : ? UpLo::Lo
80 : : UpLo::Up;
81 0 : static constexpr bool is_spacetime =
82 : I < tenex::TensorIndex_detail::spatial_sentinel;
83 : };
84 :
85 : /*!
86 : * \ingroup TensorExpressionsGroup
87 : * \brief Contains definitions for the available `TensorIndex`s to use in a
88 : * `TensorExpression`
89 : */
90 1 : namespace ti {
91 : /// @{
92 : /*!
93 : * \brief The available `TensorIndex`s to use in a `TensorExpression`
94 : *
95 : * \details The suffix following `ti::` indicates index properties:
96 : * - Uppercase: contravariant/upper index
97 : * - Lowercase: covariant/lower index
98 : * - A/a - H/h: generic spacetime index
99 : * - I/i - M/m: generic spatial index
100 : * - T/t: concrete time index (defined as a spacetime `TensorIndex`)
101 : *
102 : * \snippet Test_AddSubtract.cpp use_tensor_index
103 : *
104 : * If you want to support a new generic index, definitions for the upper and
105 : * lower versions of the index must be added as unique `TensorIndex` types, e.g.
106 : * \code
107 : * static constexpr TensorIndex<UNIQUE_INTEGER_IN_PROPER_RANGE_LOWER> ti::x{};
108 : * static constexpr TensorIndex<UNIQUE_INTEGER_IN_PROPER_RANGE_UPPER> ti::X{};
109 : * \endcode
110 : * where `UNIQUE_INTEGER_IN_PROPER_RANGE_LOWER` and
111 : * `UNIQUE_INTEGER_IN_PROPER_RANGE_UPPER` are unique, but related integers that
112 : * fall in the integer ranges that properly encode the index's properties
113 : * according to the `_sentinel` values defined at the top of
114 : * `src/DataStructures/Tensor/Expressions/TensorIndex.hpp`. This enables the new
115 : * index to be distinguishable from others and for the upper and lower versions
116 : * to be recognized as related by opposite valence. See comments there on these
117 : * integer ranges to properly encode the new index (both upper and lower
118 : * definitions) that you wish to add. In short, you should simply be able to
119 : * continue the pattern used for the existing `TensorIndex` types that are
120 : * already defined. For example, if `ti::M`/`ti::m` is the highest-valued
121 : * generic spatial index currently defined and you want to add `ti::N`/`ti::n`
122 : * as a new generic spatial index, you can simply define `ti::N` and `ti::n`'s
123 : * unique integer values to be `INTEGER_VALUE_FOR_M + 1` and
124 : * `INTEGER_VALUE_FOR_m + 1`, respectively. For adding a new generic spacetime
125 : * index, you should be able to do the same thing with respect to the upper and
126 : * lower versions of the highest-valued currently defined generic spacetime
127 : * `TensorIndex`.
128 : */
129 1 : static constexpr TensorIndex<0> a{};
130 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel> A{};
131 1 : static constexpr TensorIndex<1> b{};
132 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 1> B{};
133 1 : static constexpr TensorIndex<2> c{};
134 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 2> C{};
135 1 : static constexpr TensorIndex<3> d{};
136 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 3> D{};
137 1 : static constexpr TensorIndex<4> e{};
138 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 4> E{};
139 1 : static constexpr TensorIndex<5> f{};
140 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 5> F{};
141 1 : static constexpr TensorIndex<6> g{};
142 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 6> G{};
143 1 : static constexpr TensorIndex<7> h{};
144 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 7> H{};
145 :
146 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel - 1> t{};
147 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel - 1>
148 1 : T{};
149 :
150 1 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel> i{};
151 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel>
152 1 : I{};
153 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 1>
154 1 : j{};
155 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel +
156 : 1>
157 1 : J{};
158 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 2>
159 1 : k{};
160 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel +
161 : 2>
162 1 : K{};
163 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 3>
164 1 : l{};
165 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel +
166 : 3>
167 1 : L{};
168 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 4>
169 1 : m{};
170 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel +
171 : 4>
172 1 : M{};
173 : static constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 5>
174 1 : n{};
175 : static constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel +
176 : 5>
177 1 : N{};
178 : /// @}
179 : } // namespace ti
180 :
181 : namespace tenex {
182 : /*!
183 : * \ingroup TensorExpressionsGroup
184 : * \brief Returns the TensorIndex value of with opposite valence.
185 : *
186 : * \details The input value represents a TensorIndex value, which encodes
187 : * both the valence of the index and whether the index is spacetime or
188 : * spatial. This function returns the value that corresponds to the encoding of
189 : * the TensorIndex with the same index type, but opposite valence.
190 : *
191 : * For example, 0 is the TensorIndex value for `ti::a`. If `i == 0`, then 500
192 : * will be returned, which is the TensorIndex value for `ti::A`. If `i == 500`
193 : * (representing `ti::A`), then 0 (representing `ti::a`) is returned.
194 : *
195 : * @param i a TensorIndex value that represents a generic index
196 : * @return the TensorIndex value that encodes the generic index with the
197 : * opposite valence
198 : */
199 : SPECTRE_ALWAYS_INLINE static constexpr size_t
200 1 : get_tensorindex_value_with_opposite_valence(const size_t i) {
201 : assert(i < TensorIndex_detail::max_sentinel); // NOLINT
202 : if ((i >= TensorIndex_detail::upper_sentinel and
203 : i < TensorIndex_detail::spatial_sentinel) or
204 : (i >= TensorIndex_detail::upper_spatial_sentinel)) {
205 : // `i` represents an upper index, so return the lower index's encoding
206 : return i - TensorIndex_detail::upper_sentinel;
207 : } else {
208 : // `i` represents a lower index, so return the upper index's encoding
209 : return i + TensorIndex_detail::upper_sentinel;
210 : }
211 : }
212 : } // namespace tenex
213 :
214 1 : namespace tt {
215 : /*!
216 : * \ingroup TypeTraitsGroup TensorExpressionsGroup
217 : * \brief Check if a type `T` is a TensorIndex used in TensorExpressions
218 : */
219 : template <typename T>
220 1 : struct is_tensor_index : std::false_type {};
221 : template <size_t I>
222 0 : struct is_tensor_index<TensorIndex<I>> : std::true_type {};
223 :
224 : template <typename T>
225 : struct is_time_index;
226 : } // namespace tt
227 :
228 : namespace tenex {
229 : namespace detail {
230 : template <auto&... TensorIndices>
231 : struct make_tensorindex_list_impl {
232 : static_assert(
233 : (... and
234 : tt::is_tensor_index<std::decay_t<decltype(TensorIndices)>>::value),
235 : "Template parameters of make_tensorindex_list must be TensorIndex "
236 : "objects.");
237 : using type = tmpl::list<std::decay_t<decltype(TensorIndices)>...>;
238 : };
239 :
240 : template <typename TensorIndexList>
241 : struct tensorindex_list_is_valid_impl;
242 :
243 : template <typename... TensorIndices>
244 : struct tensorindex_list_is_valid_impl<tmpl::list<TensorIndices...>> {
245 : static_assert(
246 : (... and tt::is_tensor_index<TensorIndices>::value),
247 : "Template parameters of tensorindex_list_is_valid must be TensorIndex "
248 : "types.");
249 : static constexpr bool value = tmpl::is_set<TensorIndices...>::value;
250 : };
251 :
252 : template <typename TensorIndexList1, typename TensorIndexList2,
253 : bool ListsSameSize>
254 : struct generic_indices_at_same_positions_impl;
255 :
256 : template <typename... TensorIndices1, typename... TensorIndices2>
257 : struct generic_indices_at_same_positions_impl<
258 : tmpl::list<TensorIndices1...>, tmpl::list<TensorIndices2...>, true> {
259 : static_assert((... and (tt::is_tensor_index<TensorIndices1>::value and
260 : tt::is_tensor_index<TensorIndices2>::value)),
261 : "Template parameters of generic_indices_at_same_positions_impl "
262 : "must be lists containing TensorIndex types.");
263 : using type = std::bool_constant<(
264 : ... and (std::is_same_v<TensorIndices1, TensorIndices2> or
265 : (tt::is_time_index<TensorIndices1>::value and
266 : tt::is_time_index<TensorIndices2>::value)))>;
267 : };
268 :
269 : template <typename... TensorIndices1, typename... TensorIndices2>
270 : struct generic_indices_at_same_positions_impl<
271 : tmpl::list<TensorIndices1...>, tmpl::list<TensorIndices2...>, false> {
272 : static_assert((... and (tt::is_tensor_index<TensorIndices1>::value)),
273 : "The first template parameter of "
274 : "generic_indices_at_same_positions_impl must be a list "
275 : "containing TensorIndex types.");
276 : static_assert((... and (tt::is_tensor_index<TensorIndices2>::value)),
277 : "The second template parameter of "
278 : "generic_indices_at_same_positions_impl must be a list "
279 : "containing TensorIndex types.");
280 : using type = std::bool_constant<false>;
281 : };
282 :
283 : template <typename TensorIndexList>
284 : struct remove_time_indices;
285 : } // namespace detail
286 :
287 : /*!
288 : * \ingroup TensorExpressionsGroup
289 : * \brief Determine whether or not a given list of TensorIndexs is valid
290 : * to be used with a tensor
291 : *
292 : * \details A list of TensorIndexs is considered valid if the subset of generic
293 : * indices are a set. Indices with opposite valences are unique, e.g. one
294 : * instance each of `ti::a` and `ti::A` is valid. An arbitrary number of
295 : * concrete time indices, regardless of valence, is also valid.
296 : *
297 : * @tparam TensorIndexList list of generic index types, e.g. the types of
298 : * `ti::a, ti::b`
299 : */
300 : template <typename TensorIndexList>
301 1 : struct tensorindex_list_is_valid;
302 :
303 : template <typename... TensorIndices>
304 0 : struct tensorindex_list_is_valid<tmpl::list<TensorIndices...>> {
305 0 : static constexpr bool value = detail::tensorindex_list_is_valid_impl<
306 : typename detail::remove_time_indices<
307 : tmpl::list<TensorIndices...>>::type>::value;
308 : };
309 :
310 : /*!
311 : * \ingroup TensorExpressionsGroup
312 : * \brief Determine whether or not two lists of TensorIndexs contain the same
313 : * generic indices at the same positions
314 : *
315 : * \tparam TensorIndexList1 the first TensorIndex list
316 : * \tparam TensorIndexList2 the second TensorIndex list
317 : */
318 : template <typename TensorIndexList1, typename TensorIndexList2>
319 1 : using generic_indices_at_same_positions =
320 : typename detail::generic_indices_at_same_positions_impl<
321 : TensorIndexList1, TensorIndexList2,
322 : tmpl::size<TensorIndexList1>::value ==
323 : tmpl::size<TensorIndexList2>::value>::type;
324 : } // namespace tenex
325 :
326 : /*!
327 : * \ingroup TensorExpressionsGroup
328 : * \brief Creates a TensorIndex type list from a list of TensorIndex objects
329 : *
330 : * @tparam TensorIndices list of generic index objects, e.g. `ti::a, ti::b`
331 : */
332 : template <auto&... TensorIndices>
333 1 : using make_tensorindex_list =
334 : typename tenex::detail::make_tensorindex_list_impl<TensorIndices...>::type;
|