index.h Source File

CPP API: index.h Source File
index.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: Daniel Abele
5 *
6 * Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20 #ifndef INDEX_H
21 #define INDEX_H
22 
23 #include "memilio/io/io.h"
27 
28 #include <cstddef>
29 #include <tuple>
30 #include <type_traits>
31 #include <utility>
32 
33 namespace mio
34 {
35 
36 template <typename... CategoryTags>
37 class Index;
38 
39 namespace details
40 {
41 
43 template <class... T>
45 
46 } // namespace details
47 
49 template <typename... CategoryTags>
51 
52 namespace details
53 {
54 
56 template <class... Tags>
57 std::tuple<Index<Tags>...> get_tuple(const Index<Tags...>& i)
58 {
59  if constexpr (sizeof...(Tags) == 1) {
60  return std::tuple(i);
61  }
62  else {
63  return i.indices;
64  }
65 }
66 
68 template <class Enum>
69 std::tuple<Index<Enum>> get_tuple(Enum i)
71 {
72  return std::tuple(Index<Enum>(i));
73 }
74 
76 template <class... IndexArgs>
77  requires((std::is_enum_v<IndexArgs> || IsMultiIndex<IndexArgs>) && ...)
78 decltype(auto) concatenate_indices_impl(IndexArgs&&... args)
79 {
80  return std::tuple_cat(details::get_tuple(args)...);
81 }
82 
87 template <class... T>
88 Index<T...> tuple_to_index(std::tuple<Index<T>...>);
89 
90 } // namespace details
91 
116 template <typename CategoryTag>
117 class MEMILIO_ENABLE_EBO Index<CategoryTag> : public TypeSafe<size_t, Index<CategoryTag>>,
118  public OperatorComparison<Index<CategoryTag>>,
119  public OperatorAdditionSubtraction<Index<CategoryTag>>,
120  public OperatorScalarMultiplicationDivision<Index<CategoryTag>, size_t>
121 {
122 public:
124 
125  static constexpr size_t size = 1;
126  static constexpr bool has_duplicates = false;
127 
128  static constexpr Index Zero()
129  {
130  return Index((size_t)0);
131  }
132 
136  Index(CategoryTag val)
137  requires std::is_enum_v<CategoryTag>
139  {
140  }
141 
146  explicit Index(size_t val)
147  : TypeSafe<size_t, Index<CategoryTag>>(val)
148  {
149  }
150 
155  template <class IOContext>
156  void serialize(IOContext& io) const
157  {
158  mio::serialize(io, size_t(*this));
159  }
160 
165  template <class IOContext>
166  static IOResult<Index> deserialize(IOContext& io)
167  {
168  BOOST_OUTCOME_TRY(auto&& i, mio::deserialize(io, Tag<size_t>{}));
169  return success(Index(i));
170  }
171 };
172 
179 template <typename... CategoryTag>
180 class Index
181 {
182 public:
183  static constexpr size_t size = sizeof...(CategoryTag);
184  static constexpr bool has_duplicates = has_duplicates_v<CategoryTag...>;
185 
187  static Index constexpr Zero()
188  {
189  return Index(Index<CategoryTag>::Zero()...);
190  }
191 
193  Index(Index<CategoryTag> const&... _indices)
194  : indices{_indices...}
195  {
196  }
197 
199  template <class... IndexArgs>
200  requires(sizeof...(IndexArgs) > 1)
201  Index(IndexArgs&&... subindices)
202  : indices(details::concatenate_indices_impl(std::forward<IndexArgs>(subindices)...))
203  {
204  }
205 
206 private:
208  Index(const std::tuple<Index<CategoryTag>...>& _indices)
209  : indices(_indices)
210  {
211  }
212 
213 public:
214  // comparison operators
215  bool operator==(Index const& other) const
216  {
217  return indices == other.indices;
218  }
219 
220  bool operator!=(Index const& other) const
221  {
222  return !(this == other);
223  }
224 
225  bool operator<(Index const& other) const
226  {
227  // use apply to unfold both tuples, then use a fold expression to evaluate a pairwise less
228  return std::apply(
229  [&other](auto&&... indices_) {
230  return std::apply(
231  [&](auto&&... other_indices_) {
232  return ((indices_ < other_indices_) && ...);
233  },
234  other.indices);
235  },
236  indices);
237  }
238 
239  bool operator<=(Index const& other) const
240  {
241  return (*this == other) || (*this < other);
242  }
243 
248  template <class IOContext>
249  void serialize(IOContext& io) const
250  {
251  auto obj = io.create_object("MultiIndex");
252  obj.add_element("Indices", indices);
253  }
254 
259  template <class IOContext>
260  static IOResult<Index> deserialize(IOContext& io)
261  {
262  auto obj = io.expect_object("MultiIndex");
263  auto tup = obj.expect_element("Indices", Tag<decltype(indices)>{});
264  return mio::apply(
265  io,
266  [](auto&& tup_) {
267  return Index(tup_);
268  },
269  tup);
270  }
271 
272  std::tuple<Index<CategoryTag>...> indices;
273 };
274 
276 template <size_t Tag, class... CategoryTags>
277 struct type_at_index<Tag, ::mio::Index<CategoryTags...>> : public type_at_index<Tag, CategoryTags...> {
278 };
279 
281 template <class Tag, class... CategoryTags>
282 struct index_of_type<Tag, ::mio::Index<CategoryTags...>> : public index_of_type<Tag, CategoryTags...> {
283 };
284 
286 template <class... CategoryTags>
287 struct index_of_type<Index<CategoryTags...>, Index<CategoryTags...>> {
288  static constexpr std::size_t value = 0;
289 };
290 
292 template <size_t I, typename... CategoryTags>
293 constexpr typename std::tuple_element<I, std::tuple<Index<CategoryTags>...>>::type&
295 {
296  if constexpr (sizeof...(CategoryTags) == 1) {
297  static_assert(I == 0, "I must be equal to zero for an Index with just one template parameter");
298  return i;
299  }
300  else {
301  return std::get<I>(i.indices);
302  }
303 }
304 
306 template <size_t I, typename... CategoryTags>
307 constexpr typename std::tuple_element<I, std::tuple<Index<CategoryTags>...>>::type const&
308 get(Index<CategoryTags...> const& i) noexcept
309 {
310  if constexpr (sizeof...(CategoryTags) == 1) {
311  static_assert(I == 0, "I must be equal to zero for an Index with just one template parameter");
312  return i;
313  }
314  else {
315  return std::get<I>(i.indices);
316  }
317 }
318 
320 template <typename Tag, typename... CategoryTags>
321 constexpr Index<Tag>& get(Index<CategoryTags...>& i) noexcept
322 {
323  if constexpr (sizeof...(CategoryTags) == 1) {
324  using IndexTag = std::tuple_element_t<0, std::tuple<CategoryTags...>>;
326  "Tags must match for an Index with just one template parameter");
327  return i;
328  }
329  else {
330  return std::get<Index<Tag>>(i.indices);
331  }
332 }
333 
335 template <typename Tag, typename... CategoryTags>
336 constexpr Index<Tag> const& get(Index<CategoryTags...> const& i) noexcept
337 {
338  if constexpr (sizeof...(CategoryTags) == 1) {
339  using IndexTag = std::tuple_element_t<0, std::tuple<CategoryTags...>>;
341  "Tags must match for an Index with just one template parameter");
342  return i;
343  }
344  else {
345  return std::get<Index<Tag>>(i.indices);
346  }
347 }
348 
354 template <class... IndexArgs>
355 decltype(auto) concatenate_indices(IndexArgs&&... args)
356 {
357  using MergedIndex =
358  decltype(details::tuple_to_index(details::concatenate_indices_impl(std::declval<IndexArgs>()...)));
359  return MergedIndex(std::forward<IndexArgs>(args)...);
360 }
361 
362 namespace details
363 {
365 template <class... CategoryTags, class SuperIndex>
366 inline Index<CategoryTags...> reduce_index_impl(const SuperIndex& i, mio::Tag<Index<CategoryTags...>>)
367 {
368  // the subindex may not be trivially constructible, so we pass its type using mio::Tag
369  // the type has to be passed as an argument to determine its CategoryTags
370 
371  // below, we use get<index_of_type<>> instead of get<> directly to handle categories that are not unique
372  // (that is, `get<CategoryTags>(i)...` fails to compile for SuperIndex=Index<T, T>)
373  return Index<CategoryTags...>{get<index_of_type_v<CategoryTags, SuperIndex>>(i)...};
374 }
375 
377 template <class... CategoryTags, class... Subset>
378 inline Index<CategoryTags...> extend_index_impl(const Index<Subset...>& i, const size_t fill_value,
380 {
381  using SuperIndex = Index<CategoryTags...>;
382  using SubIndex = Index<Subset...>;
383  // The superindex may not be trivially constructible, so we pass its type using mio::Tag.
384  // The type has to be passed as an argument to determine its CategoryTags.
385 
386  return SuperIndex{[&]() {
387  // This is an IIFE, which is invoked for each category (note the '...' after the function call).
388  // So CategoryTags without a '...' is seen by each IIFE as exactly one category from this variadic template.
389  if constexpr (is_type_in_list_v<CategoryTags, Subset...>) {
390  // We use get<index_of_type<>> instead of get<> directly to handle categories that are not unique
391  // (that is, `get<CategoryTags>(i)...` fails to compile for SuperIndex=Index<T, T>)
392  return get<index_of_type_v<CategoryTags, SubIndex>>(i);
393  }
394  else {
395  return Index<CategoryTags>(fill_value);
396  }
397  }()...};
398 }
399 } // namespace details
400 
411 template <class SubIndex, class SuperIndex>
412 decltype(auto) reduce_index(const SuperIndex& index)
413 {
414  if constexpr (SubIndex::size == 1 && std::is_base_of_v<Index<SubIndex>, SubIndex>) {
415  // this case handles reducing from e.g. Index<AgeGroup, ...> directly to AgeGroup
416  // the default case would instead reduce to Index<AgeGroup>, which may cause conversion errors
418  }
419  else {
421  }
422 }
423 template <class Enum, class SuperIndex>
424  requires std::is_enum_v<Enum>
425 Index<Enum> reduce_index(const SuperIndex& index)
426 {
428 }
441 template <class SuperIndex, class SubIndex>
442 SuperIndex extend_index(const SubIndex& index, size_t fill_value = 0)
443 {
444  return details::extend_index_impl(index, fill_value, mio::Tag<SuperIndex>{});
445 }
446 
447 } // namespace mio
448 
449 #endif
An Index with a single template parameter is a typesafe wrapper for size_t that is associated with a ...
Definition: index.h:121
void serialize(IOContext &io) const
serialize this.
Definition: index.h:156
Index(size_t val)
Constructor from size_t.
Definition: index.h:146
static constexpr Index Zero()
Definition: index.h:128
Index(CategoryTag val) requires std
Constructor from enum, if CategoryTag is an enum.
Definition: index.h:136
static IOResult< Index > deserialize(IOContext &io)
deserialize an object of this class.
Definition: index.h:166
An Index with more than one template parameter combines several Index objects.
Definition: index.h:181
void serialize(IOContext &io) const
serialize this.
Definition: index.h:249
Index(const std::tuple< Index< CategoryTag >... > &_indices)
Internal constructor from a tuple.
Definition: index.h:208
bool operator<=(Index const &other) const
Definition: index.h:239
static constexpr Index Zero()
Construct an Index filled with zeroes.
Definition: index.h:187
bool operator==(Index const &other) const
Definition: index.h:215
bool operator<(Index const &other) const
Definition: index.h:225
bool operator!=(Index const &other) const
Definition: index.h:220
Index(Index< CategoryTag > const &... _indices)
Constructor from individual Indices.
Definition: index.h:193
std::tuple< Index< CategoryTag >... > indices
Definition: index.h:272
static constexpr size_t size
Definition: index.h:183
requires(sizeof...(IndexArgs) > 1) Index(IndexArgs &&... subindices)
Constructor from mixed Indices and MultiIndices.
Definition: index.h:200
static IOResult< Index > deserialize(IOContext &io)
deserialize an object of this class.
Definition: index.h:260
base class to add default operator +, +=, -, -= to a class derived from TypeSafe.
Definition: type_safe.h:152
base class to add operator <, <=, >, >= to a class derived from TypeSafe.
Definition: type_safe.h:205
base class to add operator *, *=, /, /= with a scalar to a class derived from TypeSafe.
Definition: type_safe.h:179
typesafe wrapper around any type to make function arguments, tuple elements, etc.
Definition: type_safe.h:42
#define MEMILIO_ENABLE_EBO
Definition: compiler_diagnostics.h:75
trait_value< T >::RETURN_TYPE & value(T &x)
Definition: ad.hpp:3308
void is_multi_index_impl(Index< T... >)
Function definition that accepts a MultiIndex, used for the definition of IsMultiIndex.
Index< CategoryTags... > extend_index_impl(const Index< Subset... > &i, const size_t fill_value, mio::Tag< Index< CategoryTags... >>)
Creates and returns a SuperIndex from SubIndex, using entries from the given SubIndex or fill_value.
Definition: index.h:378
Index< T... > tuple_to_index(std::tuple< Index< T >... >)
Function declaration that allows type conversion from a tuple of single-category indices to MultiInde...
Index< CategoryTags... > reduce_index_impl(const SuperIndex &i, mio::Tag< Index< CategoryTags... >>)
Extracts CategoryTags from the tagged Index and returns a subindex of SuperIndex with the given categ...
Definition: index.h:366
std::tuple< Index< Tags >... > get_tuple(const Index< Tags... > &i)
Obtain a tuple of single-category indices from a Index or MultiIndex.
Definition: index.h:57
int size(Comm comm)
Return the size of the given communicator.
Definition: miompi.cpp:75
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
IOResult< T > deserialize(IOContext &io, Tag< T > tag)
Restores an object from the data stored in an IO context.
Definition: io.h:860
void serialize(IOContext &io, const T &t)
Save data that describes an object in a format determined by the given context.
Definition: io.h:836
boost::outcome_v2::in_place_type_t< T > Tag
Type that is used for overload resolution.
Definition: io.h:407
auto i
Definition: io.h:809
concept IsMultiIndex
A MultiIndex is an Index with any number of categories. Does accept empty or single category indices.
Definition: index.h:50
details::ApplyResultT< F, T... > apply(IOContext &io, F f, const IOResult< T > &... rs)
Evaluate a function with zero or more unpacked IOResults as arguments.
Definition: io.h:481
constexpr bool has_duplicates_v
Checks whether Type has any duplicates.
Definition: metaprogramming.h:223
requires(!std::is_trivial_v< T >) void BinarySerializerObject
Definition: binary_serializer.h:333
decltype(auto) reduce_index(const SuperIndex &index)
Create a SubIndex by copying values from SuperIndex.
Definition: index.h:412
auto success()
Create an object that is implicitly convertible to a succesful IOResult<void>.
Definition: io.h:359
decltype(auto) concatenate_indices(IndexArgs &&... args)
Combine several Indexs into one MultiIndex.
Definition: index.h:355
SuperIndex extend_index(const SubIndex &index, size_t fill_value=0)
Create a SuperIndex by copying values from SubIndex, filling new categories with fill_value.
Definition: index.h:442
constexpr std::tuple_element< I, std::tuple< Index< CategoryTags >... > >::type & get(Index< CategoryTags... > &i) noexcept
Retrieves the Index (by reference) at the Ith position of a MultiIndex.
Definition: index.h:294
boost::outcome_v2::unchecked< T, IOStatus > IOResult
Value-or-error type for operations that return a value but can fail.
Definition: io.h:353
Definition: io.h:94
Tests whether the list Types contains any type multiple times.
Definition: metaprogramming.h:198
Finds the index of a Type in the list Types.
Definition: metaprogramming.h:180
static constexpr std::size_t value
Definition: metaprogramming.h:181
Finds the type at the Index-th position in the list Types.
Definition: metaprogramming.h:107