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