Structure.hpp
Go to the documentation of this file.
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 /// \file
5 /// Defines class TensorStructure<Symmetry, Indices...>
6 
7 #pragma once
8 
9 #include <array>
10 #include <limits>
11 
15 #include "ErrorHandling/Assert.hpp"
16 #include "Utilities/Array.hpp" // IWYU pragma: export
19 #include "Utilities/Gsl.hpp"
20 #include "Utilities/MakeArray.hpp"
21 #include "Utilities/Requires.hpp"
22 #include "Utilities/TMPL.hpp"
23 
24 namespace Tensor_detail {
25 constexpr size_t number_of_independent_components(
26  const std::array<int, 0>& /*symm*/,
27  const std::array<size_t, 0>& /*dims*/) noexcept {
28  return 1;
29 }
30 
31 constexpr size_t number_of_independent_components(
32  const std::array<int, 1>& /*symm*/,
33  const std::array<size_t, 1>& dims) noexcept {
34  return dims[0];
35 }
36 
37 template <size_t Size>
38 constexpr size_t number_of_independent_components(
39  const std::array<int, Size>& symm,
40  const std::array<size_t, Size>& dims) noexcept {
41  size_t max_element = 0;
42  for (size_t i = 0; i < Size; ++i) {
43  // clang-tidy: internal STL and gls::at (don't need it in constexpr)
44  assert(symm[i] > 0); // NOLINT
45  max_element =
46  std::max(static_cast<size_t>(ce_abs(symm[i])), max_element); // NOLINT
47  }
48  assert(max_element > 0); // NOLINT
49  size_t total_independent_components = 1;
50  for (size_t symm_index = 1; symm_index <= max_element; ++symm_index) {
51  size_t number_of_indices_with_symm = 0;
52  size_t dim_of_index = 0;
53  for (size_t i = 0; i < Size; ++i) {
54  if (static_cast<size_t>(symm[i]) == symm_index) { // NOLINT
55  ++number_of_indices_with_symm;
56  dim_of_index = dims[i]; // NOLINT
57  }
58  }
59  assert(dim_of_index > 0); // NOLINT
60  assert(number_of_indices_with_symm > 0); // NOLINT
61  if (dim_of_index - 1 > number_of_indices_with_symm) {
62  total_independent_components *=
63  falling_factorial(dim_of_index + number_of_indices_with_symm - 1,
64  number_of_indices_with_symm) /
65  factorial(number_of_indices_with_symm);
66  } else {
67  total_independent_components *=
68  falling_factorial(dim_of_index + number_of_indices_with_symm - 1,
69  dim_of_index - 1) /
70  factorial(dim_of_index - 1);
71  }
72  }
73  return total_independent_components;
74 }
75 
76 template <size_t Size>
77 constexpr size_t number_of_components(
78  const std::array<size_t, Size>& dims) noexcept {
79  size_t number = 1;
80  for (size_t i = 0; i < Size; ++i) {
81  // clang-tidy: use gsl::at
82  number *= dims[i]; // NOLINT
83  }
84  return number;
85 }
86 
87 template <typename T, typename S, size_t Size>
88 constexpr void increment_tensor_index(cpp17::array<T, Size>& tensor_index,
89  const cpp17::array<S, Size>& dims) {
90  for (size_t i = 0; i < Size; ++i) {
91  if (++tensor_index[i] < static_cast<T>(dims[i])) {
92  return;
93  }
94  tensor_index[i] = 0;
95  }
96 }
97 
98 // index_to_swap_with takes the last two arguments as opposed to just one of
99 // them so that when the max constexpr steps is reached on clang it is reached
100 // in this function rather than in array.
101 template <size_t Rank>
102 constexpr size_t index_to_swap_with(
103  const cpp17::array<size_t, Rank>& tensor_index,
104  const cpp17::array<int, Rank>& sym, size_t index_to_swap_with,
105  const size_t current_index) noexcept {
106  // If you encounter infinite loop compilation errors here you are
107  // constructing very large Tensor's. If you are sure Tensor is
108  // the correct data structure you can extend the compiler limit
109  // by passing the flag -fconstexpr-steps=<SOME LARGER VALUE>
110  while (true) { // See source code comment on line above this one for fix
111  if (Rank == index_to_swap_with) {
112  return current_index;
113  } else if (tensor_index[current_index] <
114  tensor_index[index_to_swap_with] and
115  sym[current_index] == sym[index_to_swap_with]) {
116  return index_to_swap_with;
117  }
118  index_to_swap_with++;
119  }
120 }
121 
122 template <size_t Size, size_t SymmSize>
123 constexpr cpp17::array<size_t, Size> canonicalize_tensor_index(
124  cpp17::array<size_t, Size> tensor_index,
125  const cpp17::array<int, SymmSize>& symm) noexcept {
126  for (size_t i = 0; i < Size; ++i) {
127  const size_t temp = tensor_index[i];
128  const size_t swap = index_to_swap_with(tensor_index, symm, i, i);
129  tensor_index[i] = tensor_index[swap];
130  tensor_index[swap] = temp;
131  }
132  return tensor_index;
133 }
134 
135 template <size_t Rank>
136 constexpr size_t compute_collapsed_index(
137  const cpp17::array<size_t, Rank>& tensor_index,
138  const cpp17::array<size_t, Rank> dims) noexcept {
139  size_t collapsed_index = 0;
140  for (size_t i = Rank - 1; i < Rank; --i) {
141  collapsed_index = tensor_index[i] + dims[i] * collapsed_index;
142  }
143  return collapsed_index;
144 }
145 
146 template <typename Symm, size_t NumberOfComponents,
147  Requires<tmpl::size<Symm>::value != 0> = nullptr>
148 constexpr cpp17::array<size_t, NumberOfComponents> compute_collapsed_to_storage(
149  const cpp17::array<size_t, tmpl::size<Symm>::value>&
150  index_dimensions) noexcept {
151  cpp17::array<size_t, NumberOfComponents> collapsed_to_storage{};
152  auto tensor_index =
153  convert_to_cpp17_array(make_array<tmpl::size<Symm>::value>(size_t{0}));
154  size_t count{0};
155  for (auto& current_storage_index : collapsed_to_storage) {
156  // Compute canonical tensor_index, which, for symmetric get_tensor_index is
157  // in decreasing numerical order, e.g. (3,2) rather than (2,3).
158  const auto canonical_tensor_index = canonicalize_tensor_index(
159  tensor_index, make_cpp17_array_from_list<Symm>());
160  // If the tensor_index was already in the canonical form, then it must be a
161  // new unique entry and we add it to collapsed_to_storage_ as a new
162  // integer, thus increasing the size_. Else, the StorageIndex has already
163  // been determined so we look it up in the existing collapsed_to_storage
164  // table.
165  if (tensor_index == canonical_tensor_index) {
166  current_storage_index = count;
167  ++count;
168  } else {
169  current_storage_index = collapsed_to_storage[compute_collapsed_index(
170  canonical_tensor_index, index_dimensions)];
171  }
172  // Move to the next tensor_index.
173  increment_tensor_index(tensor_index, index_dimensions);
174  }
175  return collapsed_to_storage;
176 }
177 
178 template <typename Symm, size_t NumberOfComponents,
179  Requires<tmpl::size<Symm>::value == 0> = nullptr>
180 constexpr cpp17::array<size_t, 1> compute_collapsed_to_storage(
181  const cpp17::array<
182  size_t, tmpl::size<Symm>::value>& /*index_dimensions*/) noexcept {
183  return cpp17::array<size_t, 1>{{0}};
184 }
185 
186 template <typename Symm, size_t NumIndComps, size_t NumComps,
187  Requires<(tmpl::size<Symm>::value > 0)> = nullptr>
188 constexpr cpp17::array<cpp17::array<size_t, tmpl::size<Symm>::value>,
189  NumIndComps>
190 compute_storage_to_tensor(
191  const cpp17::array<size_t, NumComps>& collapsed_to_storage,
192  const cpp17::array<size_t, tmpl::size<Symm>::value>&
193  index_dimensions) noexcept {
194  constexpr size_t rank = tmpl::size<Symm>::value;
195  cpp17::array<cpp17::array<size_t, rank>, NumIndComps> storage_to_tensor{};
196  cpp17::array<size_t, rank> tensor_index =
197  convert_to_cpp17_array(make_array<rank>(size_t{0}));
198  for (const auto& current_storage_index : collapsed_to_storage) {
199  storage_to_tensor[current_storage_index] = canonicalize_tensor_index(
200  tensor_index, make_cpp17_array_from_list<Symm>());
201  increment_tensor_index(tensor_index, index_dimensions);
202  }
203  return storage_to_tensor;
204 }
205 
206 template <typename Symm, size_t NumIndComps, size_t NumComps,
207  Requires<(tmpl::size<Symm>::value == 0)> = nullptr>
208 constexpr cpp17::array<cpp17::array<int, 1>, NumIndComps>
209 compute_storage_to_tensor(
210  const cpp17::array<size_t, NumComps>& /*collapsed_to_storage*/,
211  const cpp17::array<size_t, tmpl::size<Symm>::value>&
212  /*index_dimensions*/) noexcept {
214 }
215 
216 template <size_t NumIndComps, typename T, size_t NumComps>
217 constexpr cpp17::array<size_t, NumIndComps> compute_multiplicity(
218  const cpp17::array<T, NumComps>& collapsed_to_storage) {
219  cpp17::array<size_t, NumIndComps> multiplicity =
220  convert_to_cpp17_array(make_array<NumIndComps>(size_t{0}));
221  for (const auto& current_storage_index : collapsed_to_storage) {
222  ++multiplicity[current_storage_index];
223  }
224  return multiplicity;
225 }
226 
227 template <size_t NumIndices>
228 struct ComponentNameImpl {
229  template <typename Structure, typename T>
230  static std::string apply(
231  const std::array<T, NumIndices>& tensor_index,
232  const std::array<std::string, NumIndices>& axis_labels) {
233  const size_t storage_index = Structure::get_storage_index(tensor_index);
234  std::array<std::string, Structure::rank()> labels = axis_labels;
235  constexpr auto index_dim = Structure::dims();
236  for (size_t i = 0; i < Structure::rank(); ++i) {
237  if (gsl::at(labels, i).length() == 0) {
238  if (gsl::at(Structure::index_types(), i) == IndexType::Spacetime) {
239  switch (gsl::at(index_dim, i)) {
240  case 2:
241  gsl::at(labels, i) = "tx";
242  break;
243  case 3:
244  gsl::at(labels, i) = "txy";
245  break;
246  case 4:
247  gsl::at(labels, i) = "txyz";
248  break;
249  default:
250  ERROR("Tensor dim["
251  << i
252  << "] must be 1,2,3, or 4 for default axis_labels. "
253  "Either pass a string or extend the function.");
254  }
255  } else {
256  switch (gsl::at(index_dim, i)) {
257  case 1:
258  gsl::at(labels, i) = "x";
259  break;
260  case 2:
261  gsl::at(labels, i) = "xy";
262  break;
263  case 3:
264  gsl::at(labels, i) = "xyz";
265  break;
266  default:
267  ERROR("Tensor dim["
268  << i
269  << "] must be 1,2, or 3 for default axis_labels. "
270  "Either pass a string or extend the function.");
271  }
272  }
273  } else {
274  if (gsl::at(axis_labels, i).length() != gsl::at(index_dim, i)) {
275  ERROR("Dimension mismatch: Tensor has dim = "
276  << gsl::at(index_dim, i) << ", but you specified "
277  << gsl::at(axis_labels, i).length() << " different labels in "
278  << gsl::at(axis_labels, i));
279  }
280  }
281  }
282  // Create string labeling get_tensor_index
284  const auto& canonical_tensor_index =
285  Structure::get_canonical_tensor_index(storage_index);
286  for (size_t r = 0; r < Structure::rank(); ++r) {
287  ss << gsl::at(labels, r)[gsl::at(canonical_tensor_index, r)];
288  }
289  return ss.str();
290  }
291 };
292 
293 template <>
294 struct ComponentNameImpl<0> {
295  template <typename Structure, typename T>
296  static std::string apply(const std::array<T, 0>& /*tensor_index*/,
297  const std::array<std::string, 0>& /*axis_labels*/) {
298  return "Scalar";
299  }
300 };
301 
302 /// \ingroup TensorGroup
303 /// A lookup table between each tensor_index and storage_index
304 ///
305 /// 1. tensor_index: (a, b, c,...). There are Dim^rank tensor_index's
306 /// 2. collapsed_index: a + Dim * (b + Dim * (c + ...)), there are Dim^rank
307 /// unique collapsed indices and there is a 1-1 map between
308 /// a tensor_index and a collapsed_index.
309 /// 3. storage_index: index into the storage vector of the Tensor. This depends
310 /// on symmetries of the tensor, rank and dimensionality.
311 /// There are size storage_index's.
312 /// \tparam Symm the symmetry of the Tensor
313 /// \tparam Indices list of tensor_index's giving the dimensionality and frame
314 /// of the index
315 template <typename Symm, typename... Indices>
316 struct Structure {
317  static_assert(
318  TensorMetafunctions::check_index_symmetry_v<Symm, Indices...>,
319  "Cannot construct a Tensor with a symmetric pair that are not the same.");
320  static_assert(tmpl::size<Symm>::value == sizeof...(Indices),
321  "The number of indices in Symmetry do not match the number of "
322  "indices given to the Structure.");
323  static_assert(
325  "All Indices passed to Structure must be of type TensorIndexType.");
326 
327  using index_list = tmpl::list<Indices...>;
328 
329  SPECTRE_ALWAYS_INLINE static constexpr size_t rank() noexcept {
330  return sizeof...(Indices);
331  }
332 
333  SPECTRE_ALWAYS_INLINE static constexpr size_t size() noexcept {
334  constexpr auto number_of_independent_components =
335  ::Tensor_detail::number_of_independent_components(
337  tmpl::conditional_t<sizeof...(Indices) != 0, Symm, int>>(),
338  make_array_from_list<tmpl::conditional_t<sizeof...(Indices) != 0,
339  index_list, size_t>>());
340  return number_of_independent_components;
341  }
342 
343  SPECTRE_ALWAYS_INLINE static constexpr size_t
344  number_of_components() noexcept {
345  constexpr auto number_of_components = ::Tensor_detail::number_of_components(
346  make_array_from_list<tmpl::conditional_t<sizeof...(Indices) != 0,
347  index_list, size_t>>());
348  return number_of_components;
349  }
350 
351  static constexpr auto collapsed_to_storage_ =
352  compute_collapsed_to_storage<Symm, number_of_components()>(
353  make_cpp17_array_from_list<tmpl::conditional_t<
354  sizeof...(Indices) == 0, size_t, index_list>>());
355  static constexpr auto storage_to_tensor_ = compute_storage_to_tensor<Symm,
356  size()>(
357  collapsed_to_storage_,
359  tmpl::conditional_t<sizeof...(Indices) == 0, size_t, index_list>>());
360  static constexpr auto multiplicity_ =
361  compute_multiplicity<size()>(collapsed_to_storage_);
362 
363  // Retrieves the dimensionality of the I'th index
364  template <int I>
365  SPECTRE_ALWAYS_INLINE static constexpr size_t dim() noexcept {
366  static_assert(sizeof...(Indices),
367  "A scalar does not have any indices from which you can "
368  "retrieve the dimensionality.");
369  return tmpl::at<index_list, tmpl::int32_t<I>>::value;
370  }
371 
372  SPECTRE_ALWAYS_INLINE static constexpr std::array<size_t, sizeof...(Indices)>
373  dims() noexcept {
374  constexpr auto dims = make_array_from_list<
375  tmpl::conditional_t<sizeof...(Indices) != 0, index_list, size_t>>();
376  return dims;
377  }
378 
379  SPECTRE_ALWAYS_INLINE static constexpr std::array<int, sizeof...(Indices)>
380  symmetries() noexcept {
381  return make_array_from_list<
382  tmpl::conditional_t<0 != sizeof...(Indices), Symm, int>>();
383  }
384 
385  SPECTRE_ALWAYS_INLINE static constexpr std::array<IndexType,
386  sizeof...(Indices)>
387  index_types() noexcept {
388  return std::array<IndexType, sizeof...(Indices)>{{Indices::index_type...}};
389  }
390 
391  /// Return array of the valence of each index
392  SPECTRE_ALWAYS_INLINE static constexpr std::array<UpLo, sizeof...(Indices)>
393  index_valences() noexcept {
394  return std::array<UpLo, sizeof...(Indices)>{{Indices::ul...}};
395  }
396 
397  /// Return array of the frame of each index
398  SPECTRE_ALWAYS_INLINE static constexpr auto index_frames() noexcept {
399  return std::tuple<typename Indices::Frame...>{};
400  }
401 
402  /*!
403  * \brief Get the canonical tensor_index array
404  *
405  * \details
406  * For a symmetric tensor \f$T_{(ab)}\f$ with an associated symmetry list
407  * `Symmetry<1, 1>`, this will return, e.g. `{{3, 2}}` rather than `{{2, 3}}`
408  * for that particular index.
409  * Note that this ordering is implementation defined.
410  */
411  template <size_t Rank = sizeof...(Indices),
412  std::enable_if_t<Rank != 0>* = nullptr>
413  SPECTRE_ALWAYS_INLINE static constexpr std::array<size_t, sizeof...(Indices)>
414  get_canonical_tensor_index(const size_t storage_index) noexcept {
415  constexpr auto storage_to_tensor = storage_to_tensor_;
416  return gsl::at(storage_to_tensor, storage_index);
417  }
418  template <size_t Rank = sizeof...(Indices),
419  std::enable_if_t<Rank == 0>* = nullptr>
421  get_canonical_tensor_index(const size_t /*storage_index*/) noexcept {
422  return std::array<size_t, 0>{};
423  }
424 
425  /// Get storage_index
426  /// \param args comma separated list of the index to return
427  template <typename... N>
428  SPECTRE_ALWAYS_INLINE static constexpr std::size_t get_storage_index(
429  const N... args) noexcept {
430  static_assert(sizeof...(Indices) == sizeof...(N),
431  "the number arguments must be equal to rank_");
432  constexpr auto collapsed_to_storage = collapsed_to_storage_;
433  return gsl::at(
434  collapsed_to_storage,
435  compute_collapsed_index(
436  canonicalize_tensor_index(
437  cpp17::array<size_t, sizeof...(N)>{
438  {static_cast<size_t>(args)...}},
440  tmpl::conditional_t<0 != sizeof...(Indices), Symm, int>>()),
441  make_cpp17_array_from_list<tmpl::conditional_t<
442  0 != sizeof...(Indices), index_list, size_t>>()));
443  }
444  /// Get storage_index
445  /// \param tensor_index the tensor_index of which to get the storage_index
446  template <typename I>
447  SPECTRE_ALWAYS_INLINE static constexpr std::size_t get_storage_index(
448  const std::array<I, sizeof...(Indices)>& tensor_index) noexcept {
449  constexpr auto collapsed_to_storage = collapsed_to_storage_;
450  return gsl::at(
451  collapsed_to_storage,
452  compute_collapsed_index(
453  canonicalize_tensor_index(
454  convert_to_cpp17_array(tensor_index),
456  tmpl::conditional_t<0 != sizeof...(Indices), Symm, int>>()),
457  make_cpp17_array_from_list<tmpl::conditional_t<
458  0 != sizeof...(Indices), index_list, size_t>>()));
459  }
460 
461  template <int... N, Requires<(sizeof...(N) > 0)> = nullptr>
462  SPECTRE_ALWAYS_INLINE static constexpr std::size_t
463  get_storage_index() noexcept {
464  static_assert(sizeof...(Indices) == sizeof...(N),
465  "the number arguments must be equal to rank_");
466  constexpr std::size_t storage_index =
467  collapsed_to_storage_[compute_collapsed_index(
468  canonicalize_tensor_index(
469  cpp17::array<size_t, sizeof...(N)>{{N...}},
470  make_cpp17_array_from_list<Symm>()),
471  make_cpp17_array_from_list<index_list>())];
472  return storage_index;
473  }
474 
475  /// Get the multiplicity of the storage_index
476  /// \param storage_index the storage_index of which to get the multiplicity
477  SPECTRE_ALWAYS_INLINE static constexpr size_t multiplicity(
478  const size_t storage_index) noexcept {
479  constexpr auto multiplicity = multiplicity_;
480  return gsl::at(multiplicity, storage_index);
481  }
482 
483  /// Get the array of collapsed index to storage_index
484  SPECTRE_ALWAYS_INLINE static constexpr std::array<size_t,
485  number_of_components()>
486  collapsed_to_storage() noexcept {
487  constexpr auto collapsed_to_storage = collapsed_to_storage_;
488  return collapsed_to_storage;
489  }
490 
491  /// Get the storage_index for the specified collapsed index
492  SPECTRE_ALWAYS_INLINE static constexpr int collapsed_to_storage(
493  const size_t i) noexcept {
494  constexpr auto collapsed_to_storage = collapsed_to_storage_;
495  return gsl::at(collapsed_to_storage, i);
496  }
497 
498  /// Get the array of tensor_index's corresponding to the storage_index's.
499  SPECTRE_ALWAYS_INLINE static constexpr const std::array<
500  std::array<size_t, sizeof...(Indices) == 0 ? 1 : sizeof...(Indices)>,
501  size()>&
502  storage_to_tensor_index() noexcept {
503  constexpr auto storage_to_tensor = storage_to_tensor_;
504  return storage_to_tensor;
505  }
506 
507  template <typename T>
508  SPECTRE_ALWAYS_INLINE static std::string component_name(
509  const std::array<T, rank()>& tensor_index,
510  const std::array<std::string, rank()>& axis_labels) {
511  return ComponentNameImpl<sizeof...(Indices)>::template apply<Structure>(
512  tensor_index, axis_labels);
513  }
514 };
515 } // namespace Tensor_detail
constexpr auto make_cpp17_array_from_list() -> cpp17::array< std::decay_t< decltype(tmpl::front< List >::value)>, tmpl::size< List >::value >
Make an array from a typelist that holds std::integral_constant&#39;s all of which have the same value_ty...
Definition: Array.hpp:210
#define ERROR(m)
prints an error message to the standard error stream and aborts the program.
Definition: Error.hpp:35
size_t collapsed_index(const Index< N > &index, const Index< N > &extents) noexcept
Get the collapsed index into a 1D array of the data corresponding to this Index. Note that the first ...
The TensorIndexType is a spacetime index.
Definition: IndexType.hpp:146
constexpr bool flat_all_v
A non-short-circuiting logical AND between bools &#39;B"".
Definition: TMPL.hpp:504
Defines function make_array.
T max_element(T... args)
Defines the type alias Requires.
constexpr size_t index_dim(const Tensor< Ts... > &) noexcept
Get dimensionality of i&#39;th tensor index.
Definition: Tensor.hpp:530
constexpr auto apply(F &&f, const DataBox< BoxTags > &box, Args &&... args)
Apply the function f with argument Tags TagsList from DataBox box
Definition: DataBox.hpp:1595
Inherits from std::true_type if T is a TensorIndexType.
Definition: IndexType.hpp:208
#define SPECTRE_ALWAYS_INLINE
Always inline a function. Only use this if you benchmarked the code.
Definition: ForceInline.hpp:20
constexpr std::array< T, Size > make_array(Args &&... args) noexcept(noexcept(MakeArray_detail::MakeArray< Size==0 >::template apply< T >(std::make_index_sequence<(Size==0 ? Size :Size - 1)>{}, std::forward< Args >(args)...)))
Create a std::array<T, Size>{{T(args...), T(args...), ...}}
Definition: MakeArray.hpp:68
constexpr uint64_t falling_factorial(const uint64_t x, const uint64_t n) noexcept
Compute the falling factorial of .
Definition: ConstantExpressions.hpp:71
Defines metafunctions used to comute the Symmetry<...> for a Tensor.
constexpr T ce_abs(const T &x) noexcept(noexcept(x< 0 ? -x :x))
Compute the absolute value of of its argument.
Definition: ConstantExpressions.hpp:168
Define simple functions for constant expressions.
T count(T... args)
Defines macro to always inline a function.
T size(T... args)
constexpr uint64_t factorial(const uint64_t n) noexcept
Compute the factorial of .
Definition: ConstantExpressions.hpp:86
Defines macro ASSERT.
constexpr void swap(T &a, T &b) noexcept
Definition: Algorithm.hpp:19
Wraps the template metaprogramming library used (brigand)
Defines functions and classes from the GSL.
IndexType
Indicates whether the TensorIndexType is Spatial or Spacetime.
Definition: IndexType.hpp:133
constexpr auto make_array_from_list() -> std::array< std::decay_t< decltype(tmpl::front< List >::value)>, tmpl::size< List >::value >
Make an array from a typelist that holds std::integral_constant&#39;s all of which have the same value_ty...
Definition: ConstantExpressions.hpp:302
Definition: Array.hpp:26
typename Requires_detail::requires_impl< B >::template_error_type_failed_to_meet_requirements_on_template_parameters Requires
Express requirements on the template parameters of a function or class, replaces std::enable_if_t ...
Definition: Requires.hpp:67
Defines metafunctions used by Tensor.
UpLo
Whether a TensorIndexType is covariant or contravariant.
Definition: IndexType.hpp:20
Defines classes representing tensor indices.
constexpr T & at(std::array< T, N > &arr, Size index)
Retrieve a entry from a container, with checks in Debug mode that the index being retrieved is valid...
Definition: Gsl.hpp:124