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 
126 template <typename CategoryTag>
127 class MEMILIO_ENABLE_EBO Index<CategoryTag> : public TypeSafe<size_t, Index<CategoryTag>>,
128  public OperatorComparison<Index<CategoryTag>>,
129  public OperatorAdditionSubtraction<Index<CategoryTag>>,
130  public OperatorScalarMultiplicationDivision<Index<CategoryTag>, size_t>
131 {
132 public:
134 
135  static constexpr size_t size = 1;
136  static constexpr bool has_duplicates = false;
137 
138  static constexpr Index Zero()
139  {
140  return Index((size_t)0);
141  }
142 
146  Index(CategoryTag val)
147  requires std::is_enum_v<CategoryTag>
149  {
150  }
151 
156  explicit Index(size_t val)
157  : TypeSafe<size_t, Index<CategoryTag>>(val)
158  {
159  }
160 
165  template <class IOContext>
166  void serialize(IOContext& io) const
167  {
168  mio::serialize(io, size_t(*this));
169  }
170 
175  template <class IOContext>
176  static IOResult<Index> deserialize(IOContext& io)
177  {
178  BOOST_OUTCOME_TRY(auto&& i, mio::deserialize(io, Tag<size_t>{}));
179  return success(Index(i));
180  }
181 };
182 
189 template <typename... CategoryTag>
190 class Index
191 {
192 public:
193  static constexpr size_t size = sizeof...(CategoryTag);
194  static constexpr bool has_duplicates = has_duplicates_v<CategoryTag...>;
195 
197  static Index constexpr Zero()
198  {
199  return Index(Index<CategoryTag>::Zero()...);
200  }
201 
203  Index(Index<CategoryTag> const&... _indices)
204  : indices{_indices...}
205  {
206  }
207 
209  template <class... IndexArgs>
210  requires(sizeof...(IndexArgs) > 1)
211  Index(IndexArgs&&... subindices)
212  : indices(details::concatenate_indices_impl(std::forward<IndexArgs>(subindices)...))
213  {
214  }
215 
216 private:
218  Index(const std::tuple<Index<CategoryTag>...>& _indices)
219  : indices(_indices)
220  {
221  }
222 
223 public:
224  // comparison operators
225  bool operator==(Index const& other) const
226  {
227  return indices == other.indices;
228  }
229 
230  bool operator!=(Index const& other) const
231  {
232  return !(this == other);
233  }
234 
235  bool operator<(Index const& other) const
236  {
237  // use apply to unfold both tuples, then use a fold expression to evaluate a pairwise less
238  return std::apply(
239  [&other](auto&&... indices_) {
240  return std::apply(
241  [&](auto&&... other_indices_) {
242  return ((indices_ < other_indices_) && ...);
243  },
244  other.indices);
245  },
246  indices);
247  }
248 
249  bool operator<=(Index const& other) const
250  {
251  return (*this == other) || (*this < other);
252  }
253 
258  template <class IOContext>
259  void serialize(IOContext& io) const
260  {
261  auto obj = io.create_object("MultiIndex");
262  obj.add_element("Indices", indices);
263  }
264 
269  template <class IOContext>
270  static IOResult<Index> deserialize(IOContext& io)
271  {
272  auto obj = io.expect_object("MultiIndex");
273  auto tup = obj.expect_element("Indices", Tag<decltype(indices)>{});
274  return mio::apply(
275  io,
276  [](auto&& tup_) {
277  return Index(tup_);
278  },
279  tup);
280  }
281 
282  std::tuple<Index<CategoryTag>...> indices;
283 };
284 
286 template <size_t Tag, class... CategoryTags>
287 struct type_at_index<Tag, ::mio::Index<CategoryTags...>> : public type_at_index<Tag, CategoryTags...> {
288 };
289 
291 template <class Tag, class... CategoryTags>
292 struct index_of_type<Tag, ::mio::Index<CategoryTags...>> : public index_of_type<Tag, CategoryTags...> {
293 };
294 
296 template <class... CategoryTags>
297 struct index_of_type<Index<CategoryTags...>, Index<CategoryTags...>> {
298  static constexpr std::size_t value = 0;
299 };
300 
302 template <size_t I, typename... CategoryTags>
303 constexpr typename std::tuple_element<I, std::tuple<Index<CategoryTags>...>>::type&
305 {
306  if constexpr (sizeof...(CategoryTags) == 1) {
307  static_assert(I == 0, "I must be equal to zero for an Index with just one template parameter");
308  return i;
309  }
310  else {
311  return std::get<I>(i.indices);
312  }
313 }
314 
316 template <size_t I, typename... CategoryTags>
317 constexpr typename std::tuple_element<I, std::tuple<Index<CategoryTags>...>>::type const&
318 get(Index<CategoryTags...> const& i) noexcept
319 {
320  if constexpr (sizeof...(CategoryTags) == 1) {
321  static_assert(I == 0, "I must be equal to zero for an Index with just one template parameter");
322  return i;
323  }
324  else {
325  return std::get<I>(i.indices);
326  }
327 }
328 
330 template <typename Tag, typename... CategoryTags>
331 constexpr Index<Tag>& get(Index<CategoryTags...>& i) noexcept
332 {
333  if constexpr (sizeof...(CategoryTags) == 1) {
334  using IndexTag = std::tuple_element_t<0, std::tuple<CategoryTags...>>;
336  "Tags must match for an Index with just one template parameter");
337  return i;
338  }
339  else {
340  return std::get<Index<Tag>>(i.indices);
341  }
342 }
343 
345 template <typename Tag, typename... CategoryTags>
346 constexpr Index<Tag> const& get(Index<CategoryTags...> const& i) noexcept
347 {
348  if constexpr (sizeof...(CategoryTags) == 1) {
349  using IndexTag = std::tuple_element_t<0, std::tuple<CategoryTags...>>;
351  "Tags must match for an Index with just one template parameter");
352  return i;
353  }
354  else {
355  return std::get<Index<Tag>>(i.indices);
356  }
357 }
358 
364 template <class... IndexArgs>
365 decltype(auto) concatenate_indices(IndexArgs&&... args)
366 {
367  using MergedIndex =
368  decltype(details::tuple_to_index(details::concatenate_indices_impl(std::declval<IndexArgs>()...)));
369  return MergedIndex(std::forward<IndexArgs>(args)...);
370 }
371 
372 namespace details
373 {
375 template <class... CategoryTags, class SuperIndex>
376 inline Index<CategoryTags...> reduce_index_impl(const SuperIndex& i, mio::Tag<Index<CategoryTags...>>)
377 {
378  // the subindex may not be trivially constructible, so we pass its type using mio::Tag
379  // the type has to be passed as an argument to determine its CategoryTags
380 
381  // below, we use get<index_of_type<>> instead of get<> directly to handle categories that are not unique
382  // (that is, `get<CategoryTags>(i)...` fails to compile for SuperIndex=Index<T, T>)
383  return Index<CategoryTags...>{get<index_of_type_v<CategoryTags, SuperIndex>>(i)...};
384 }
385 
387 template <class... CategoryTags, class... Subset>
388 inline Index<CategoryTags...> extend_index_impl(const Index<Subset...>& i, const size_t fill_value,
390 {
391  using SuperIndex = Index<CategoryTags...>;
392  using SubIndex = Index<Subset...>;
393  // The superindex may not be trivially constructible, so we pass its type using mio::Tag.
394  // The type has to be passed as an argument to determine its CategoryTags.
395 
396  return SuperIndex{[&]() {
397  // This is an IIFE, which is invoked for each category (note the '...' after the function call).
398  // So CategoryTags without a '...' is seen by each IIFE as exactly one category from this variadic template.
399  if constexpr (is_type_in_list_v<CategoryTags, Subset...>) {
400  // We use get<index_of_type<>> instead of get<> directly to handle categories that are not unique
401  // (that is, `get<CategoryTags>(i)...` fails to compile for SuperIndex=Index<T, T>)
402  return get<index_of_type_v<CategoryTags, SubIndex>>(i);
403  }
404  else {
405  return Index<CategoryTags>(fill_value);
406  }
407  }()...};
408 }
409 } // namespace details
410 
421 template <class SubIndex, class SuperIndex>
422 decltype(auto) reduce_index(const SuperIndex& index)
423 {
424  if constexpr (SubIndex::size == 1 && std::is_base_of_v<Index<SubIndex>, SubIndex>) {
425  // this case handles reducing from e.g. Index<AgeGroup, ...> directly to AgeGroup
426  // the default case would instead reduce to Index<AgeGroup>, which may cause conversion errors
428  }
429  else {
431  }
432 }
433 template <class Enum, class SuperIndex>
434  requires std::is_enum_v<Enum>
435 Index<Enum> reduce_index(const SuperIndex& index)
436 {
438 }
451 template <class SuperIndex, class SubIndex>
452 SuperIndex extend_index(const SubIndex& index, size_t fill_value = 0)
453 {
454  return details::extend_index_impl(index, fill_value, mio::Tag<SuperIndex>{});
455 }
456 
457 } // namespace mio
458 
459 #endif
Typesafe wrapper for a size_t index associated with a Tag type.
Definition: index.h:131
void serialize(IOContext &io) const
serialize this.
Definition: index.h:166
Index(size_t val)
Constructor from size_t.
Definition: index.h:156
static constexpr Index Zero()
Definition: index.h:138
Index(CategoryTag val) requires std
Constructor from enum, if CategoryTag is an enum.
Definition: index.h:146
static IOResult< Index > deserialize(IOContext &io)
deserialize an object of this class.
Definition: index.h:176
An Index with more than one template parameter combines several Index objects.
Definition: index.h:191
void serialize(IOContext &io) const
serialize this.
Definition: index.h:259
Index(const std::tuple< Index< CategoryTag >... > &_indices)
Internal constructor from a tuple.
Definition: index.h:218
bool operator<=(Index const &other) const
Definition: index.h:249
static constexpr Index Zero()
Construct an Index filled with zeroes.
Definition: index.h:197
bool operator==(Index const &other) const
Definition: index.h:225
bool operator<(Index const &other) const
Definition: index.h:235
bool operator!=(Index const &other) const
Definition: index.h:230
Index(Index< CategoryTag > const &... _indices)
Constructor from individual Indices.
Definition: index.h:203
std::tuple< Index< CategoryTag >... > indices
Definition: index.h:282
static constexpr size_t size
Definition: index.h:193
requires(sizeof...(IndexArgs) > 1) Index(IndexArgs &&... subindices)
Constructor from mixed Indices and MultiIndices.
Definition: index.h:210
static IOResult< Index > deserialize(IOContext &io)
deserialize an object of this class.
Definition: index.h:270
base class to add default operator +, +=, -, -= to a class derived from TypeSafe.
Definition: type_safe.h:175
base class to add operator <, <=, >, >= to a class derived from TypeSafe.
Definition: type_safe.h:228
base class to add operator *, *=, /, /= with a scalar to a class derived from TypeSafe.
Definition: type_safe.h:202
Typesafe wrapper around any type to make function arguments, tuple elements, etc.
Definition: type_safe.h:59
#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:388
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:376
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:861
void serialize(IOContext &io, const T &t)
Save data that describes an object in a format determined by the given context.
Definition: io.h:837
boost::outcome_v2::in_place_type_t< T > Tag
Type that is used for overload resolution.
Definition: io.h:408
auto i
Definition: io.h:810
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:482
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:422
auto success()
Create an object that is implicitly convertible to a succesful IOResult<void>.
Definition: io.h:360
decltype(auto) concatenate_indices(IndexArgs &&... args)
Combine several Indexs into one MultiIndex.
Definition: index.h:365
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:452
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:304
boost::outcome_v2::unchecked< T, IOStatus > IOResult
Value-or-error type for operations that return a value but can fail.
Definition: io.h:354
Definition: io.h:95
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