opm-simulators
GraphColoring.hpp
1 /*
2  Copyright 2018 Equinor
3 
4  This file is part of the Open Porous Media project (OPM).
5 
6  OPM is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OPM is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OPM. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #ifndef OPM_GRAPHCOLORING_HEADER_INCLUDED
20 #define OPM_GRAPHCOLORING_HEADER_INCLUDED
21 
22 #include <opm/common/TimingMacros.hpp>
23 
24 #include <opm/grid/utility/SparseTable.hpp>
25 
26 #include <algorithm>
27 #include <cstddef>
28 #include <deque>
29 #include <limits>
30 #include <numeric>
31 #include <queue>
32 #include <string>
33 #include <tuple>
34 #include <vector>
35 
36 namespace Opm {
37 namespace Detail {
38 template <class Graph>
39 std::size_t colorGraphWelshPowell(const Graph& graph,
40  std::deque<typename Graph::VertexDescriptor>& orderedVertices,
41  std::vector<int>& colors,
42  int color,
43  int noVertices)
44 {
45  std::vector<int> forbidden(noVertices, false);
46  std::size_t noColored = 0;
47 
48  for (auto vertex = orderedVertices.begin(),
49  vertexEnd = orderedVertices.end(); vertex != vertexEnd; ++vertex) {
50  // Skip forbidden vertices
51  while (vertex != vertexEnd && forbidden[*vertex])
52  ++vertex;
53  if (vertex == vertexEnd) {
54  break;
55  }
56 
57  // Color Vertex
58  colors[*vertex] = color;
59  ++noColored;
60  // Forbid neighors
61  for (auto edge = graph.beginEdges(*vertex),
62  endEdge = graph.endEdges(*vertex); edge != endEdge; ++edge) {
63  forbidden[edge.target()] = true;
64  }
65  }
66  // forbidden vertices will be colored next for coloring
67  using Vertex = typename Graph::VertexDescriptor;
68  std::erase_if(orderedVertices,
69  [&forbidden](const Vertex& vertex)
70  { return !forbidden[vertex]; });
71 
72  return noColored;
73 }
74 
75 template <class Graph, class Functor>
76 std::size_t breadthFirstSearch(const Graph& graph,
77  typename Graph::VertexDescriptor root,
78  Functor functor)
79 {
80  std::vector<int> visited(graph.maxVertex() + 1, false);
81  using Vertex = typename Graph::VertexDescriptor;
82  std::queue<Vertex> nextVertices;
83  std::size_t noVisited = 0;
84  nextVertices.push(root);
85  visited[root] = true; // We do not visit root.
86 
87  while (!nextVertices.empty()) {
88  auto current = nextVertices.front();
89  for (auto edge = graph.beginEdges(current),
90  endEdge = graph.endEdges(current); edge != endEdge; ++edge) {
91  if (!visited[edge.target()]) {
92  visited[edge.target()] = true;
93  nextVertices.push(edge.target());
94  functor(edge.target());
95  ++noVisited;
96  }
97  }
98  nextVertices.pop();
99  }
100  return noVisited;
101 }
102 } // end namespace Detail
103 
104 
111 template <class Graph>
112 std::tuple<std::vector<int>, int, std::vector<std::size_t>>
113 colorVerticesWelshPowell(const Graph& graph)
114 {
115  using Vertex = typename Graph::VertexDescriptor;
116  std::deque<Vertex> orderedVertices;
117  auto noVertices = graph.maxVertex() + 1;
118  std::vector<int> degrees(noVertices, 0);
119  int maxDegree = 0;
120  std::ptrdiff_t firstDegreeChange = 0;
121 
122  // populate deque
123  for (auto vertex = graph.begin(),
124  endVertex = graph.end(); vertex != endVertex; ++vertex) {
125  auto currentVertex = *vertex;
126  auto& degree = degrees[currentVertex];
127 
128  for (auto edge = graph.beginEdges(currentVertex),
129  endEdge = graph.endEdges(currentVertex); edge != endEdge; ++edge) {
130  ++degree;
131  }
132 
133  if (degree >= maxDegree) {
134  orderedVertices.emplace_front(currentVertex);
135  ++firstDegreeChange;
136  if (degree > maxDegree) {
137  firstDegreeChange = 1;
138  maxDegree = degree;
139  }
140  } else {
141  orderedVertices.emplace_back(currentVertex);
142  }
143  }
144 
145  // order deque by descending degree
146  std::stable_sort(orderedVertices.begin() + firstDegreeChange,
147  orderedVertices.end(),
148  [&degrees](const Vertex& v1, const Vertex& v2)
149  { return degrees[v1] > degrees[v2]; });
150 
151  // Overwrite degree with color
152  auto& colors = degrees;
153  std::ranges::fill(colors, -1);
154 
155  int color = 0;
156  std::vector<std::size_t> verticesPerColor;
157  verticesPerColor.reserve(10);
158 
159  while (!orderedVertices.empty()) {
160  verticesPerColor.push_back(Detail::colorGraphWelshPowell(graph, orderedVertices,
161  colors, color++, noVertices));
162  }
163  return std::make_tuple(colors, color, verticesPerColor);
164 }
165 
167 template <class Graph>
168 std::vector<std::size_t>
169 reorderVerticesPreserving(const std::vector<int>& colors,
170  int noColors,
171  const std::vector<std::size_t>& verticesPerColor,
172  const Graph& graph)
173 {
174  std::vector<std::size_t> colorIndex(noColors, 0);
175  std::vector<std::size_t> indices(graph.maxVertex() + 1);
176  std::partial_sum(verticesPerColor.begin(),
177  verticesPerColor.begin() + verticesPerColor.size() - 1,
178  colorIndex.begin() + 1);
179 
180  for (const auto& vertex : graph) {
181  indices[vertex] = colorIndex[colors[vertex]]++;
182  }
183  return indices;
184 }
185 
187 template <class Graph>
188 std::vector<std::size_t>
189 reorderVerticesSpheres(const std::vector<int>& colors,
190  int noColors,
191  const std::vector<std::size_t>& verticesPerColor,
192  const Graph& graph,
193  typename Graph::VertexDescriptor root)
194 {
195  std::vector<std::size_t> colorIndex(noColors, 0);
196  const auto notVisitedTag = std::numeric_limits<std::size_t>::max();
197  std::vector<std::size_t> indices(graph.maxVertex() + 1, notVisitedTag);
198  using Vertex = typename Graph::VertexDescriptor;
199  std::partial_sum(verticesPerColor.begin(),
200  verticesPerColor.begin() + verticesPerColor.size() - 1,
201  colorIndex.begin() + 1);
202  std::size_t noVisited = 0;
203  auto numberer = [&colorIndex, &colors, &indices](Vertex vertex)
204  {
205  indices[vertex] = colorIndex[colors[vertex]]++;
206  };
207 
208  while (noVisited < graph.maxVertex() + 1) {
209  numberer(root);
210  ++noVisited; // root node already visited and not visited in BFS
211  noVisited += Detail::breadthFirstSearch(graph, root, numberer);
212  if (noVisited < graph.maxVertex() + 1) {
213  // Graph is disconnected search for not yet visited node
214  for (auto vertex : graph) {
215  if (indices[vertex] == notVisitedTag) {
216  // \todo make sure that this is a peripheral node!
217  root = vertex;
218  break;
219  }
220  }
221  }
222  }
223  return indices;
224 }
225 
234 enum class ColoringType { SYMMETRIC, LOWER, UPPER };
235 
246 template <class M>
248 getMatrixRowColoring(const M& matrix, ColoringType coloringType)
249 {
250  OPM_TIMEBLOCK(createMatrix);
251 
252  std::vector<std::size_t> color(matrix.N(), 0);
253  std::vector<std::size_t> rowIndices(matrix.N(), 0);
254 
255  std::iota(rowIndices.begin(), rowIndices.end(), 0);
256 
257  std::vector<std::size_t> colorCnt;
258  // These dynamic programming computations only rely on the following observation:
259  // level[row_i] = 1 + max{level[row_j]} for all j which i depend on
260  // This minimizes the level of each row, and every rows dependencies belong to a lower level set.
261  if (coloringType == ColoringType::SYMMETRIC) {
262  for (auto i = matrix.begin(); i != matrix.end(); ++i) {
263  for (auto a_ij = i->begin(); a_ij.index() != i.index(); ++a_ij) {
264  auto a_ji = matrix[a_ij.index()].find(i.index());
265  if (a_ji != matrix[a_ij.index()].end()) {
266  color[i.index()] = std::max({color[i.index()], color[a_ij.index()] + 1});
267  }
268  }
269  if (color[i.index()] >= colorCnt.size()) {
270  colorCnt.push_back(1);
271  } else {
272  ++colorCnt[color[i.index()]];
273  }
274  }
275  } else if (coloringType == ColoringType::UPPER) {
276  for (auto i = matrix.beforeEnd(); i != matrix.beforeBegin(); --i) {
277  for (auto a_ij = ++(i->find(i.index())); a_ij != i->end(); ++a_ij) {
278  color[i.index()] = std::max({color[i.index()], color[a_ij.index()] + 1});
279  }
280  if (color[i.index()] >= colorCnt.size()) {
281  colorCnt.push_back(1);
282  } else {
283  ++colorCnt[color[i.index()]];
284  }
285  }
286  } else if (coloringType == ColoringType::LOWER) {
287  for (auto i = matrix.begin(); i != matrix.end(); ++i) {
288  for (auto a_ij = i->begin(); a_ij.index() != i.index(); ++a_ij) {
289  color[i.index()] = std::max({color[i.index()], color[a_ij.index()] + 1});
290  }
291  if (color[i.index()] >= colorCnt.size()) {
292  colorCnt.push_back(1);
293  } else {
294  ++colorCnt[color[i.index()]];
295  }
296  }
297  }
298 
299  std::stable_sort(rowIndices.begin(),
300  rowIndices.end(),
301  [&color](const std::size_t a, const std::size_t b)
302  { return color[a] < color[b]; });
303 
304  return {rowIndices.data(), rowIndices.data() + rowIndices.size(),
305  colorCnt.data(), colorCnt.data() + colorCnt.size()};
306 }
307 
308 } // end namespace Opm
309 
310 #endif
std::vector< std::size_t > reorderVerticesPreserving(const std::vector< int > &colors, int noColors, const std::vector< std::size_t > &verticesPerColor, const Graph &graph)
! Reorder colored graph preserving order of vertices with the same color.
Definition: GraphColoring.hpp:169
std::tuple< std::vector< int >, int, std::vector< std::size_t > > colorVerticesWelshPowell(const Graph &graph)
Color the vertices of graph.
Definition: GraphColoring.hpp:113
This file contains a set of helper functions used by VFPProd / VFPInj.
Definition: blackoilbioeffectsmodules.hh:45
ColoringType
Specify coloring type.
Definition: GraphColoring.hpp:234
Opm::SparseTable< std::size_t > getMatrixRowColoring(const M &matrix, ColoringType coloringType)
This coloring algorithm interprets the sparsity structure of a matrix as a graph. ...
Definition: GraphColoring.hpp:248
std::vector< std::size_t > reorderVerticesSpheres(const std::vector< int > &colors, int noColors, const std::vector< std::size_t > &verticesPerColor, const Graph &graph, typename Graph::VertexDescriptor root)
! Reorder Vetrices in spheres
Definition: GraphColoring.hpp:189