numerical-collection-cpp 0.10.0
A collection of algorithms in numerical analysis implemented in C++
Loading...
Searching...
No Matches
cuthill_mckee_ordering.h
Go to the documentation of this file.
1/*
2 * Copyright 2023 MusicScience37 (Kenta Kabashima)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
20#pragma once
21
22#include <cstddef>
23#include <limits>
24#include <set>
25#include <vector>
26
27#include <Eigen/Core>
28#include <Eigen/SparseCore>
29#include <hash_tables/maps/open_address_map_st.h>
30
35
36namespace num_collect::linear {
37
38namespace impl {
39
46template <typename StorageIndex>
48public:
50 using storage_index_type = StorageIndex;
51
53 using permutation_type = Eigen::PermutationMatrix<Eigen::Dynamic,
54 Eigen::Dynamic, storage_index_type>;
55
58
66 template <base::concepts::sparse_matrix MatrixType>
67 void operator()(const MatrixType& matrix, permutation_type& permutation) {
69 matrix.rows() == matrix.cols(), "Square matrix is required.");
70
71 const storage_index_type first_index = calculate_degrees(matrix);
72 process_indices(matrix, first_index);
74 permutation, static_cast<storage_index_type>(matrix.rows()));
75 }
76
77private:
92
97 public:
105 [[nodiscard]] auto operator()(const next_index_data& left,
106 const next_index_data& right) const noexcept -> bool {
107 if (left.previous_level_order != right.previous_level_order) {
108 return left.previous_level_order < right.previous_level_order;
109 }
110 if (left.degree != right.degree) {
111 return left.degree < right.degree;
112 }
113 return left.index < right.index;
114 }
115 };
116
124 template <base::concepts::sparse_matrix MatrixType>
125 [[nodiscard]] auto calculate_degrees(const MatrixType& matrix)
127 const auto size = static_cast<storage_index_type>(matrix.outerSize());
129 unused_index_to_degree_.reserve(static_cast<std::size_t>(size));
130
131 storage_index_type lowest_degree =
132 std::numeric_limits<storage_index_type>::max();
133 storage_index_type lowest_degree_index = 0;
134 for (storage_index_type i = 0; i < size; ++i) {
135 storage_index_type degree = 0;
136 for (typename MatrixType::InnerIterator iter(matrix, i); iter;
137 ++iter) {
138 ++degree;
139 }
140 unused_index_to_degree_.emplace(i, degree);
141 if (degree < lowest_degree) {
142 lowest_degree = degree;
143 lowest_degree_index = i;
144 }
145 }
146
147 return lowest_degree_index;
148 }
149
157 template <base::concepts::sparse_matrix MatrixType>
159 const MatrixType& matrix, storage_index_type first_index) {
160 processed_indices_.clear();
161 processed_indices_.reserve(static_cast<std::size_t>(matrix.rows()));
162 next_indices_.clear();
164 .index = first_index, .degree = 0, .previous_level_order = 0});
165
166 while (!next_indices_.empty()) {
168 next_indices_.clear();
169
170 // Remove from unused indices before checking adjacent indices.
171 // Also, duplicated indices are removed.
172 for (auto iter = current_indices_.begin();
173 iter != current_indices_.end();) {
174 if (unused_index_to_degree_.erase(iter->index)) {
175 ++iter;
176 } else {
177 iter = current_indices_.erase(iter);
178 }
179 }
180
181 // Now add indices to processed indices, and search for indices in
182 // the next level.
183 storage_index_type order = 0;
184 for (const next_index_data& data : current_indices_) {
185 processed_indices_.push_back(data.index);
186
187 for (typename MatrixType::InnerIterator iter(
188 matrix, data.index);
189 iter; ++iter) {
190 const storage_index_type* degree =
191 unused_index_to_degree_.try_get(iter.index());
192 if (degree == nullptr) {
193 continue;
194 }
195 next_indices_.emplace(next_index_data{.index = iter.index(),
196 .degree = *degree,
197 .previous_level_order = order});
198 }
199
200 ++order;
201 }
202 }
203
204 if (!unused_index_to_degree_.empty()) {
206 algorithm_failure, "Unused indices exist.");
207 }
208 }
209
217 permutation_type& permutation, storage_index_type size) {
218 permutation.resize(size);
219 storage_index_type moved_index = 0;
220 for (const storage_index_type& index : processed_indices_) {
221 permutation.indices()(index) = moved_index;
222 ++moved_index;
223 }
224 }
225
227 hash_tables::maps::open_address_map_st<storage_index_type,
230
232 std::set<next_index_data, next_index_data_less> current_indices_{};
233
235 std::set<next_index_data, next_index_data_less> next_indices_{};
236
238 std::vector<storage_index_type> processed_indices_{};
239};
240
241} // namespace impl
242
247template <typename StorageIndex>
249public:
251 using storage_index_type = StorageIndex;
252
254 using permutation_type = Eigen::PermutationMatrix<Eigen::Dynamic,
255 Eigen::Dynamic, storage_index_type>;
256
259
262
270 template <base::concepts::sparse_matrix MatrixType>
271 void operator()(const MatrixType& matrix, permutation_type& permutation) {
273 matrix, permutation);
274 }
275
284 template <typename MatrixType, unsigned int Mode>
286 const Eigen::SparseSelfAdjointView<MatrixType, Mode>& matrix,
287 permutation_type& permutation) {
288 Eigen::SparseMatrix<typename MatrixType::Scalar, Eigen::ColMajor,
290 matrix_as_ordinary_matrix = matrix;
291 operator()(matrix_as_ordinary_matrix, permutation);
292 }
293};
294
295} // namespace num_collect::linear
Class of exception on failure in algorithm.
Definition exception.h:93
Class to perform Cuthill-McKee ordering method golub2013, knabner2003.
void operator()(const Eigen::SparseSelfAdjointView< MatrixType, Mode > &matrix, permutation_type &permutation)
Create a permutation matrix from SparseSelfAdjointView object.
void operator()(const MatrixType &matrix, permutation_type &permutation)
Create a permutation matrix.
Eigen::PermutationMatrix< Eigen::Dynamic, Eigen::Dynamic, storage_index_type > permutation_type
Type of permutations.
permutation_type PermutationType
Type of permutations (for Eigen library).
StorageIndex storage_index_type
Type of indices in storages of indices.
Class of the implementation of Cuthill-McKee ordering method golub2013, knabner2003.
void process_indices(const MatrixType &matrix, storage_index_type first_index)
Process indices.
void create_permutation(permutation_type &permutation, storage_index_type size)
Create a permutation matrix.
hash_tables::maps::open_address_map_st< storage_index_type, storage_index_type > unused_index_to_degree_
Mapping of unused indices to degrees.
void operator()(const MatrixType &matrix, permutation_type &permutation)
Create a permutation matrix.
std::set< next_index_data, next_index_data_less > current_indices_
Indices processed in the current level.
StorageIndex storage_index_type
Type of indices in storages of indices.
std::vector< storage_index_type > processed_indices_
Processed indices.
auto calculate_degrees(const MatrixType &matrix) -> storage_index_type
Calculate degrees.
Eigen::PermutationMatrix< Eigen::Dynamic, Eigen::Dynamic, storage_index_type > permutation_type
Type of permutations.
std::set< next_index_data, next_index_data_less > next_indices_
Indices processed in the next level.
Definition of exceptions.
Definition of macros for logging.
#define NUM_COLLECT_LOG_AND_THROW(EXCEPTION_TYPE,...)
Write an error log and throw an exception for an error.
Namespace of solvers of linear equations.
Definition of NUM_COLLECT_PRECONDITION macro.
#define NUM_COLLECT_PRECONDITION(CONDITION,...)
Check whether a precondition is satisfied and throw an exception if not.
Definition of sparse_matrix concept.
auto operator()(const next_index_data &left, const next_index_data &right) const noexcept -> bool
Compare two objects.