deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
graph_coloring.h
Go to the documentation of this file.
1
2// -----------------------------------------------------------------------------
3//
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
5// Copyright (C) 2013 - 2026 by the deal.II authors
6//
7// This file is part of the deal.II library.
8//
9// Detailed license information governing the source code and contributions
10// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
11//
12// -----------------------------------------------------------------------------
13
14#ifndef dealii_graph_coloring_h
15# define dealii_graph_coloring_h
16
17
18# include <deal.II/base/config.h>
19
24# include <deal.II/base/types.h>
25
26# include <Kokkos_Macros.hpp>
27
28# include <algorithm>
29# include <functional>
30# include <set>
31# include <string>
32# include <unordered_map>
33# include <unordered_set>
34# include <vector>
35
36
38
39class SparsityPattern;
40
45{
46 namespace internal
47 {
57 inline bool
59 const std::vector<types::global_dof_index> &indices1,
60 const std::vector<types::global_dof_index> &indices2)
61 {
62 // we assume that both arrays are sorted, so we can walk
63 // them in lockstep and see if we encounter an index that's
64 // in both arrays. once we reach the end of either array,
65 // we know that there is no intersection
66 std::vector<types::global_dof_index>::const_iterator p = indices1.begin(),
67 q = indices2.begin();
68 while ((p != indices1.end()) && (q != indices2.end()))
69 {
70 if (*p < *q)
71 ++p;
72 else if (*p > *q)
73 ++q;
74 else
75 // conflict found!
76 return true;
77 }
78
79 // no conflict found!
80 return false;
81 }
82
83
112 template <typename Iterator>
113 std::vector<std::vector<Iterator>>
115 const Iterator &begin,
117 const std::function<std::vector<types::global_dof_index>(
118 const Iterator &)> &get_conflict_indices)
119 {
120 // Number of iterators.
121 unsigned int n_iterators = 0;
122
123 // Create a map from conflict indices to iterators
124 std::unordered_map<types::global_dof_index, std::vector<Iterator>>
125 indices_to_iterators;
126 for (Iterator it = begin; it != end; ++it)
127 {
128 const std::vector<types::global_dof_index> conflict_indices =
129 get_conflict_indices(it);
130 const unsigned int n_conflict_indices = conflict_indices.size();
131 for (unsigned int i = 0; i < n_conflict_indices; ++i)
132 indices_to_iterators[conflict_indices[i]].push_back(it);
133 ++n_iterators;
134 }
135
136 // create the very first zone which contains only the first
137 // iterator. then create the other zones. keep track of all the
138 // iterators that have already been assigned to a zone
139 std::vector<std::vector<Iterator>> zones(1,
140 std::vector<Iterator>(1, begin));
141 std::set<Iterator> used_it;
142 used_it.insert(begin);
143 while (used_it.size() != n_iterators)
144 {
145 // loop over the elements of the previous zone. for each element of
146 // the previous zone, get the conflict indices and from there get
147 // those iterators that are conflicting with the current element
148 typename std::vector<Iterator>::iterator previous_zone_it(
149 zones.back().begin());
150 typename std::vector<Iterator>::iterator previous_zone_end(
151 zones.back().end());
152 std::vector<Iterator> new_zone;
153 for (; previous_zone_it != previous_zone_end; ++previous_zone_it)
154 {
155 const std::vector<types::global_dof_index> conflict_indices =
156 get_conflict_indices(*previous_zone_it);
157
158 const unsigned int n_conflict_indices(conflict_indices.size());
159 for (unsigned int i = 0; i < n_conflict_indices; ++i)
160 {
161 const std::vector<Iterator> &conflicting_elements =
162 indices_to_iterators[conflict_indices[i]];
163 for (unsigned int j = 0; j < conflicting_elements.size(); ++j)
164 {
165 // check that the iterator conflicting with the current
166 // one is not associated to a zone yet and if so, assign
167 // it to the current zone. mark it as used
168 //
169 // we can shortcut this test if the conflicting iterator
170 // is the current iterator
171 if ((conflicting_elements[j] != *previous_zone_it) &&
172 (used_it.count(conflicting_elements[j]) == 0))
173 {
174 new_zone.push_back(conflicting_elements[j]);
175 used_it.insert(conflicting_elements[j]);
176 }
177 }
178 }
179 }
180
181 // If there are iterators in the new zone, then the zone is added to
182 // the partition. Otherwise, the graph is disconnected and we need to
183 // find an iterator on the other part of the graph. start the whole
184 // process again with the first iterator that hasn't been assigned to
185 // a zone yet
186 if (new_zone.size() != 0)
187 zones.push_back(new_zone);
188 else
189 for (Iterator it = begin; it != end; ++it)
190 if (used_it.count(it) == 0)
191 {
192 zones.push_back(std::vector<Iterator>(1, it));
193 used_it.insert(it);
194 break;
195 }
196 }
197
198 return zones;
199 }
200
201
202
225 template <typename Iterator>
226 void
228 std::vector<Iterator> &partition,
229 const std::function<std::vector<types::global_dof_index>(
230 const Iterator &)> &get_conflict_indices,
231 std::vector<std::vector<Iterator>> &partition_coloring)
232 {
233 partition_coloring.clear();
234
235 // Number of zones composing the partitioning.
236 const unsigned int partition_size(partition.size());
237 std::vector<unsigned int> sorted_vertices(partition_size);
238 std::vector<int> degrees(partition_size);
239 std::vector<std::vector<types::global_dof_index>> conflict_indices(
240 partition_size);
241 std::vector<std::vector<unsigned int>> graph(partition_size);
242
243 // Get the conflict indices associated to each iterator. The
244 // conflict_indices have to be sorted so we can more easily find conflicts
245 // later on
246 for (unsigned int i = 0; i < partition_size; ++i)
247 {
248 conflict_indices[i] = get_conflict_indices(partition[i]);
249 std::sort(conflict_indices[i].begin(), conflict_indices[i].end());
250 }
251
252 // Compute the degree of each vertex of the graph using the
253 // intersection of the conflict indices.
254 for (unsigned int i = 0; i < partition_size; ++i)
255 for (unsigned int j = i + 1; j < partition_size; ++j)
256 // If the two iterators share indices then we increase the degree of
257 // the vertices and create an "edge" in the graph.
258 if (have_nonempty_intersection(conflict_indices[i],
259 conflict_indices[j]))
260 {
261 ++degrees[i];
262 ++degrees[j];
263 graph[i].push_back(j);
264 graph[j].push_back(i);
265 }
266
267 // Sort the vertices by decreasing degree.
268 std::vector<int>::iterator degrees_it;
269 for (unsigned int i = 0; i < partition_size; ++i)
270 {
271 // Find the largest element.
272 degrees_it = std::max_element(degrees.begin(), degrees.end());
273 sorted_vertices[i] = degrees_it - degrees.begin();
274 // Put the largest element to -1 so it cannot be chosen again.
275 *degrees_it = -1;
276 }
277
278 // Color the graph.
279 std::vector<std::unordered_set<unsigned int>> colors_used;
280 for (unsigned int i = 0; i < partition_size; ++i)
281 {
282 const unsigned int current_vertex(sorted_vertices[i]);
283 bool new_color(true);
284 // Try to use an existing color, i.e., try to find a color which is
285 // not associated to one of the vertices linked to current_vertex.
286 // Loop over the color.
287 for (unsigned int j = 0; j < partition_coloring.size(); ++j)
288 {
289 // Loop on the vertices linked to current_vertex. If one vertex
290 // linked to current_vertex is already using the color j, this
291 // color cannot be used anymore.
292 bool unused_color(true);
293 for (const auto adjacent_vertex : graph[current_vertex])
294 if (colors_used[j].count(adjacent_vertex) == 1)
295 {
296 unused_color = false;
297 break;
298 }
299 if (unused_color)
300 {
301 partition_coloring[j].push_back(partition[current_vertex]);
302 colors_used[j].insert(current_vertex);
303 new_color = false;
304 break;
305 }
306 }
307 // Add a new color.
308 if (new_color)
309 {
310 partition_coloring.push_back(
311 std::vector<Iterator>(1, partition[current_vertex]));
312 std::unordered_set<unsigned int> tmp;
313 tmp.insert(current_vertex);
314 colors_used.push_back(tmp);
315 }
316 }
317 }
318
319
320
330 template <typename Iterator>
331 std::vector<std::vector<Iterator>>
333 const std::vector<std::vector<std::vector<Iterator>>> &partition_coloring)
334 {
335 std::vector<std::vector<Iterator>> coloring;
336
337 // Count the number of iterators in each color.
338 const unsigned int partition_size(partition_coloring.size());
339 std::vector<std::vector<unsigned int>> colors_counter(partition_size);
340 for (unsigned int i = 0; i < partition_size; ++i)
341 {
342 const unsigned int n_colors(partition_coloring[i].size());
343 colors_counter[i].resize(n_colors);
344 for (unsigned int j = 0; j < n_colors; ++j)
345 colors_counter[i][j] = partition_coloring[i][j].size();
346 }
347
348 // Find the partition with the largest number of colors for the even
349 // partition.
350 unsigned int i_color(0);
351 unsigned int max_even_n_colors(0);
352 const unsigned int colors_size(colors_counter.size());
353 for (unsigned int i = 0; i < colors_size; i += 2)
354 {
355 if (max_even_n_colors < colors_counter[i].size())
356 {
357 max_even_n_colors = colors_counter[i].size();
358 i_color = i;
359 }
360 }
361 coloring.resize(max_even_n_colors);
362 for (unsigned int j = 0; j < colors_counter[i_color].size(); ++j)
363 coloring[j] = partition_coloring[i_color][j];
364
365 for (unsigned int i = 0; i < partition_size; i += 2)
366 {
367 if (i != i_color)
368 {
369 std::unordered_set<unsigned int> used_k;
370 for (unsigned int j = 0; j < colors_counter[i].size(); ++j)
371 {
372 // Find the color in the current partition with the largest
373 // number of iterators.
374 std::vector<unsigned int>::iterator it;
375 it = std::max_element(colors_counter[i].begin(),
376 colors_counter[i].end());
377 unsigned int min_iterators(static_cast<unsigned int>(-1));
378 unsigned int pos(0);
379 // Find the color of coloring with the least number of colors
380 // among the colors that have not been used yet.
381 for (unsigned int k = 0; k < max_even_n_colors; ++k)
382 if (used_k.count(k) == 0)
383 if (colors_counter[i_color][k] < min_iterators)
384 {
385 min_iterators = colors_counter[i_color][k];
386 pos = k;
387 }
388 colors_counter[i_color][pos] += *it;
389 // Concatenate the current color with the existing coloring.
390 coloring[pos].insert(
391 coloring[pos].end(),
392 partition_coloring[i][it - colors_counter[i].begin()]
393 .begin(),
394 partition_coloring[i][it - colors_counter[i].begin()]
395 .end());
396 used_k.insert(pos);
397 // Put the number of iterators to the current color to zero.
398 *it = 0;
399 }
400 }
401 }
402
403 // If there is more than one partition, do the same thing that we did for
404 // the even partitions to the odd partitions
405 if (partition_size > 1)
406 {
407 unsigned int max_odd_n_colors(0);
408 for (unsigned int i = 1; i < partition_size; i += 2)
409 {
410 if (max_odd_n_colors < colors_counter[i].size())
411 {
412 max_odd_n_colors = colors_counter[i].size();
413 i_color = i;
414 }
415 }
416 coloring.resize(max_even_n_colors + max_odd_n_colors);
417 for (unsigned int j = 0; j < colors_counter[i_color].size(); ++j)
418 coloring[max_even_n_colors + j] = partition_coloring[i_color][j];
419
420 for (unsigned int i = 1; i < partition_size; i += 2)
421 {
422 if (i != i_color)
423 {
424 std::unordered_set<unsigned int> used_k;
425 for (unsigned int j = 0; j < colors_counter[i].size(); ++j)
426 {
427 // Find the color in the current partition with the
428 // largest number of iterators.
429 std::vector<unsigned int>::iterator it;
430 it = std::max_element(colors_counter[i].begin(),
431 colors_counter[i].end());
432 unsigned int min_iterators(static_cast<unsigned int>(-1));
433 unsigned int pos(0);
434 // Find the color of coloring with the least number of
435 // colors among the colors that have not been used yet.
436 for (unsigned int k = 0; k < max_odd_n_colors; ++k)
437 if (used_k.count(k) == 0)
438 if (colors_counter[i_color][k] < min_iterators)
439 {
440 min_iterators = colors_counter[i_color][k];
441 pos = k;
442 }
443 colors_counter[i_color][pos] += *it;
444 // Concatenate the current color with the existing
445 // coloring.
446 coloring[max_even_n_colors + pos].insert(
447 coloring[max_even_n_colors + pos].end(),
448 partition_coloring[i][it - colors_counter[i].begin()]
449 .begin(),
450 partition_coloring[i][it - colors_counter[i].begin()]
451 .end());
452 used_k.insert(pos);
453 // Put the number of iterators to the current color to
454 // zero.
455 *it = 0;
456 }
457 }
458 }
459 }
460
461 return coloring;
462 }
463 } // namespace internal
464
465
543 template <typename Iterator>
544 std::vector<std::vector<Iterator>>
546 const Iterator &begin,
548 const std::function<std::vector<types::global_dof_index>(
549 const std_cxx20::type_identity_t<Iterator> &)> &get_conflict_indices)
550 {
551 Assert(begin != end,
553 "GraphColoring is not prepared to deal with empty ranges!"));
554
555 // Create the partitioning.
556 std::vector<std::vector<Iterator>> partitioning =
557 internal::create_partitioning(begin, end, get_conflict_indices);
558
559 // Color the iterators within each partition.
560 // Run the coloring algorithm on each zone in parallel
561 const unsigned int partitioning_size(partitioning.size());
562 std::vector<std::vector<std::vector<Iterator>>> partition_coloring(
563 partitioning_size);
564
566 for (unsigned int i = 0; i < partitioning_size; ++i)
567 tasks += Threads::new_task(&internal::make_dsatur_coloring<Iterator>,
568 partitioning[i],
569 get_conflict_indices,
570 partition_coloring[i]);
571 tasks.join_all();
572
573 // Gather the colors together.
574 return internal::gather_colors(partition_coloring);
575 }
576
583 unsigned int
584 color_sparsity_pattern(const SparsityPattern &sparsity_pattern,
585 std::vector<unsigned int> &color_indices);
586
587} // namespace GraphColoring
588
590
591
592//---------------------------- graph_coloring.h ---------------------------
593// end of #ifndef dealii_graph_coloring_h
594#endif
595//---------------------------- graph_coloring.h ---------------------------
*  iterator end()
*  *  iterator begin()
***mech_lbc_system increment_interpolation_handlers push_back(scale_z_handler)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define Assert(cond, exc)
static ::ExceptionBase & ExcMessage(std::string arg1)
Task< RT > new_task(const std::function< RT()> &function)
std::size_t size
Definition mpi.cc:733
std::vector< std::vector< Iterator > > gather_colors(const std::vector< std::vector< std::vector< Iterator > > > &partition_coloring)
std::vector< std::vector< Iterator > > create_partitioning(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, const std::function< std::vector< types::global_dof_index >(const Iterator &)> &get_conflict_indices)
bool have_nonempty_intersection(const std::vector< types::global_dof_index > &indices1, const std::vector< types::global_dof_index > &indices2)
void make_dsatur_coloring(std::vector< Iterator > &partition, const std::function< std::vector< types::global_dof_index >(const Iterator &)> &get_conflict_indices, std::vector< std::vector< Iterator > > &partition_coloring)
std::vector< std::vector< Iterator > > make_graph_coloring(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, const std::function< std::vector< types::global_dof_index >(const std_cxx20::type_identity_t< Iterator > &)> &get_conflict_indices)
unsigned int color_sparsity_pattern(const SparsityPattern &sparsity_pattern, std::vector< unsigned int > &color_indices)
typename type_identity< T >::type type_identity_t
Definition type_traits.h:93