TRIQS/nda 1.3.0
Multi-dimensional array library for C++
Loading...
Searching...
No Matches
group_indices.hpp
Go to the documentation of this file.
1// Copyright (c) 2019-2024 Simons Foundation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0.txt
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// Authors: Thomas Hahn, Olivier Parcollet, Nils Wentzell
16
22#pragma once
23
24#include "./layout/idx_map.hpp"
26#include "./stdutil/array.hpp"
27#include "./traits.hpp"
28
29#include <algorithm>
30#include <array>
31#include <cstddef>
32#include <cstdint>
33#include <numeric>
34#include <stdexcept>
35
36namespace nda {
37
38 namespace detail {
39
40 // Check if given integer arrays correspond to a partition of the set `J = {0, 1, ..., R-1}`.
41 template <size_t R, size_t... Rs>
42 constexpr bool is_partition_of_indices(std::array<int, Rs> const &...grps) {
43 // check that the total number of array elements is equal to R
44 constexpr auto Rstot = (Rs + ...);
45 static_assert(Rstot == R, "Error in nda::detail::is_partition_of_indices: Number of indices has to be equal to the rank");
46
47 // check that every element of J is in one and only one group
48 auto count = stdutil::make_initialized_array<R>(0); // FIXME : not necessary in C++20
49 auto l = [&](auto &&grp) mutable -> void {
50 for (int u = 0; u < grp.size(); ++u) {
51 if (grp[u] < 0) throw std::runtime_error("Error in nda::detail::is_partition_of_indices: Negative indices are not allowed");
52 if (grp[u] >= R) throw std::runtime_error("Error in nda::detail::is_partition_of_indices: Indices larger than the rank are not allowed");
53 count[grp[u]]++;
54 }
55 };
56 (l(grps), ...);
57
58 return (count == stdutil::make_initialized_array<R>(1));
59 }
60
61 // Given an original stride order and a partition of the indices, determine the new stride order of the grouped
62 // nda::idx_map.
63 template <size_t R, size_t... Rs>
64 constexpr std::array<int, sizeof...(Rs)> stride_order_of_grouped_idx_map(std::array<int, R> const &stride_order,
65 std::array<int, Rs> const &...grps) {
66 // inverse permutation of the stride order
67 auto mem_pos = permutations::inverse(stride_order);
68
69 // check if the indices/dimensions in a group are consecutive in memory and find the slowest varying index within
70 // the group
71 auto min_mem_pos = [&mem_pos](auto &&grp) {
72 int min = R, max = 0;
73 for (int idx : grp) {
74 int v = mem_pos[idx];
75 if (v > max) max = v;
76 if (v < min) min = v;
77 }
78 bool idx_are_consecutive_in_memory = (max - min + 1 == grp.size());
79 if (!idx_are_consecutive_in_memory)
80 throw std::runtime_error("Error in nda::detail::stride_order_of_grouped_idx_map: Indices are not consecutive in memory");
81 return min;
82 };
83
84 // number of groups in the partition
85 constexpr int Ngrps = sizeof...(Rs);
86
87 // array containing the slowest varying index within each group
88 std::array<int, Ngrps> min_mem_positions{min_mem_pos(grps)...};
89
90 // rank each group by its slowest varying index, i.e. the slowest group gets rank 0 and the fastest rank Ngrps-1
91 std::array<int, Ngrps> mem_pos_out = stdutil::make_initialized_array<Ngrps>(0); // FIXME : not necessary in C++20
92 for (int u = 0; u < Ngrps; ++u) {
93 for (int i = 0; i < Ngrps; ++i) {
94 if (min_mem_positions[i] < min_mem_positions[u]) ++mem_pos_out[u];
95 }
96 }
97
98 // the new stride order is the inverse of mem_pos_out
99 return permutations::inverse(mem_pos_out);
100 }
101
102 } // namespace detail
103
113 template <int... Is>
114 struct idx_group_t {
116 static constexpr std::array<int, sizeof...(Is)> as_std_array{Is...};
117 };
118
123 template <int... Is>
124 inline idx_group_t<Is...> idx_group = {}; // NOLINT (global variable template is what we want here)
125
156 template <int Rank, uint64_t StaticExtents, uint64_t StrideOrder, layout_prop_e LayoutProp, typename... IdxGrps>
158 // compile-time checks
159 static_assert(LayoutProp == layout_prop_e::contiguous, "Error in nda::group_indices_layout: Only contiguous layouts are supported");
160 static_assert(detail::is_partition_of_indices<Rank>(IdxGrps::as_std_array...),
161 "Error in nda::group_indices_layout: Given index groups are not a valid partition");
162
163 // decoded original stride order
165
166 // number of elements in a group
167 auto total_len_of_a_grp = [&idxm](auto &&grp) {
168 return std::accumulate(grp.begin(), grp.end(), 1L, [&idxm](long l, long u) { return l * idxm.lengths()[u]; });
169 };
170
171 // minimum stride of a group
172 auto min_stride_of_a_grp = [&idxm](auto &&grp) {
173 return std::accumulate(grp.begin(), grp.end(), idxm.size(), [&idxm](long s, long u) { return std::min(s, idxm.strides()[u]); });
174 };
175
176 // new rank, shape, strides and stride order
177 static constexpr int new_rank = sizeof...(IdxGrps);
178 std::array<long, new_rank> new_extents{total_len_of_a_grp(IdxGrps::as_std_array)...};
179 std::array<long, new_rank> new_strides{min_stride_of_a_grp(IdxGrps::as_std_array)...};
180 constexpr auto new_stride_order = encode(detail::stride_order_of_grouped_idx_map(stride_order, IdxGrps::as_std_array...));
181
183 }
184
187} // namespace nda
Provides utility functions for std::array.
Layout that specifies how to map multi-dimensional indices to a linear/flat index.
Definition idx_map.hpp:103
long size() const noexcept
Get the total number of elements.
Definition idx_map.hpp:160
idx_group_t< Is... > idx_group
Variable template for an nda::idx_group_t.
layout_prop_e
Compile-time guarantees of the memory layout of an array/view.
Definition traits.hpp:222
auto group_indices_layout(idx_map< Rank, StaticExtents, StrideOrder, LayoutProp > const &idxm, IdxGrps...)
Given an nda::idx_map and a partition of its indices, return a new nda::idx_map with the grouped indi...
constexpr std::array< Int, N > inverse(std::array< Int, N > const &p)
Inverse of a permutation.
constexpr uint64_t encode(std::array< int, N > const &a)
Encode a std::array<int, N> in a uint64_t.
constexpr std::array< T, R > make_initialized_array(T v)
Create a new std::array object initialized with a specific value.
Definition array.hpp:168
Provides a class that maps multi-dimensional indices to a linear index and vice versa.
Provides utilities to work with permutations and to compactly encode/decode std::array objects.
A group of indices.
static constexpr std::array< int, sizeof...(Is)> as_std_array
std::array containing the indices.
Provides type traits for the nda library.