andres::graph
multicut/kernighan-lin.hxx
1 #pragma once
2 #ifndef ANDRES_GRAPH_MULTICUT_KERNIGHAN_LIN_HXX
3 #define ANDRES_GRAPH_MULTICUT_KERNIGHAN_LIN_HXX
4 
5 #include <iomanip>
6 #include <stack>
7 #include <stdexcept>
8 #include <unordered_set>
9 #include <vector>
10 
11 #include "andres/graph/components.hxx"
12 #include "andres/graph/twocut/kernighan-lin.hxx"
13 
14 
15 
16 namespace andres {
17 namespace graph {
18 namespace multicut {
19 
20 struct Settings
21 {
22  size_t numberOfInnerIterations { std::numeric_limits<size_t>::max() };
23  size_t numberOfOuterIterations { 100 };
24  double epsilon { 1e-7 };
25  bool verbose { true };
26 };
27 
28 template<typename GRAPH, typename ECA, typename ELA>
29 inline
30 void kernighanLin(const GRAPH& graph, const ECA& edgeCosts, const ELA& inputLabels, ELA& outputLabels, const Settings settings = Settings())
31 {
32  struct Visitor
33  {
34  bool operator()(std::vector<size_t>& vertex_labels) const
35  {
36  return true;
37  }
38  } visitor;
39 
40  kernighanLin(graph, edgeCosts, inputLabels, outputLabels, visitor, settings);
41 }
42 
43 template<typename GRAPH, typename ECA, typename ELA, typename VIS>
44 inline
45 void kernighanLin(const GRAPH& graph, const ECA& edgeCosts, const ELA& inputLabels, ELA& outputLabels, VIS& visitor, const Settings settings = Settings())
46 {
47  struct SubgraphWithCut { // a subgraph with cut mask
48  SubgraphWithCut(const ELA& labels)
49  : labels_(labels) {}
50  bool vertex(const size_t v) const
51  { return true; }
52  bool edge(const size_t e) const
53  { return labels_[e] == 0; }
54 
55  const ELA& labels_;
56  };
57 
58  // build decomposition based on the current multicut
59  ComponentsBySearch<GRAPH> components;
60  components.build(graph, SubgraphWithCut(inputLabels));
61 
62  double starting_energy = .0;
63 
64  // check if the input multicut labeling is valid
65  for(size_t edge = 0; edge < graph.numberOfEdges(); ++edge)
66  {
67  outputLabels[edge] = inputLabels[edge];
68 
69  auto v0 = graph.vertexOfEdge(edge, 0);
70  auto v1 = graph.vertexOfEdge(edge, 1);
71 
72  if (inputLabels[edge])
73  starting_energy += edgeCosts[edge];
74 
75  if (static_cast<bool>(inputLabels[edge]) != !components.areConnected(v0, v1))
76  throw std::runtime_error("the input multicut labeling is invalid.");
77  }
78 
79  auto numberOfComponents = *std::max_element(components.labels_.begin(), components.labels_.end()) + 1;
80 
81  auto twocut_buffers = twocut::makeTwoCutBuffers(graph);
82 
83  twocut::TwoCutSettings twocut_settings;
84  twocut_settings.numberOfIterations = settings.numberOfInnerIterations;
85  twocut_settings.epsilon = settings.epsilon;
86 
87  // build partitions
88  std::vector<std::vector<size_t>> partitions(numberOfComponents);
89 
90  for (size_t i = 0; i < components.labels_.size(); ++i)
91  {
92  partitions[components.labels_[i]].push_back(i);
93  twocut_buffers.vertex_labels[i] = components.labels_[i];
94  }
95 
96  twocut_buffers.max_not_used_label = partitions.size();
97 
98  if (settings.verbose)
99  {
100  std::cout << "Starting energy: " << starting_energy << std::endl;
101  std::cout << std::setw(4) << "Iter" << std::setw(16) << "Total decrease" << std::setw(15) << "Pair updates" << std::setw(15) << "New sets" << std::setw(15) << "Num. of sets\n";
102  }
103 
104  auto last_good_vertex_labels = twocut_buffers.vertex_labels;
105 
106  // auxillary array for BFS/DFS
107  std::vector<char> visited(graph.numberOfVertices());
108 
109  // 1 if i-th partitioned changed since last iteration, 0 otherwise
110  std::vector<char> changed(numberOfComponents, 1);
111 
112  // interatively update bipartition in order to minimize the total cost of the multicut
113  for (size_t k = 0; k < settings.numberOfOuterIterations; ++k)
114  {
115  auto energy_decrease = .0;
116 
117  std::vector<std::unordered_set<size_t>> edges(numberOfComponents);
118  for (size_t e = 0; e < graph.numberOfEdges(); ++e)
119  if (outputLabels[e])
120  {
121  auto v0 = twocut_buffers.vertex_labels[graph.vertexOfEdge(e, 0)];
122  auto v1 = twocut_buffers.vertex_labels[graph.vertexOfEdge(e, 1)];
123 
124  edges[std::min(v0, v1)].insert(std::max(v0, v1));
125  }
126 
127  for (size_t i = 0; i < numberOfComponents; ++i)
128  if (!partitions[i].empty())
129  for (auto j : edges[i])
130  if (!partitions[j].empty() && (changed[j] || changed[i]))
131  {
132  auto ret = twocut::kernighanLin(graph, edgeCosts, partitions[i], partitions[j], twocut_buffers, twocut_settings);
133 
134  if (ret > settings.epsilon)
135  changed[i] = changed[j] = 1;
136 
137  energy_decrease += ret;
138 
139  if (partitions[i].size() == 0)
140  break;
141  }
142 
143  auto ee = energy_decrease;
144 
145  // remove partitions that became empty after the previous step
146  auto new_end = std::partition(partitions.begin(), partitions.end(), [](const std::vector<size_t>& s) { return !s.empty(); });
147  partitions.resize(new_end - partitions.begin());
148 
149  // try to intoduce new partitions
150  for (size_t i = 0, p_size = partitions.size(); i < p_size; ++i)
151  {
152  if (!changed[i])
153  continue;
154 
155  bool flag = true;
156 
157  while (flag)
158  {
159  std::vector<size_t> new_set;
160  energy_decrease += twocut::kernighanLin(graph, edgeCosts, partitions[i], new_set, twocut_buffers, twocut_settings);
161 
162  flag = !new_set.empty();
163 
164  if (!new_set.empty())
165  partitions.emplace_back(std::move(new_set));
166  }
167  }
168 
169  if (!visitor(twocut_buffers.vertex_labels))
170  break;
171 
172  if (energy_decrease == .0)
173  break;
174 
175  std::stack<size_t> S;
176 
177  std::fill(visited.begin(), visited.end(), 0);
178 
179  partitions.clear();
180  numberOfComponents = 0;
181 
182  // do connected component labeling on the original graph and form new pÑ„rtitions
183  for (size_t i = 0; i < graph.numberOfVertices(); ++i)
184  if (!visited[i])
185  {
186  S.push(i);
187  visited[i] = 1;
188 
189  auto label = twocut_buffers.vertex_labels[i];
190 
191  twocut_buffers.referenced_by[i] = numberOfComponents;
192 
193  partitions.emplace_back(std::vector<size_t>());
194  partitions.back().push_back(i);
195 
196  while (!S.empty())
197  {
198  auto v = S.top();
199  S.pop();
200 
201  for (auto it = graph.adjacenciesFromVertexBegin(v); it != graph.adjacenciesFromVertexEnd(v); ++it)
202  if (twocut_buffers.vertex_labels[it->vertex()] == label && !visited[it->vertex()])
203  {
204  S.push(it->vertex());
205  visited[it->vertex()] = 1;
206  twocut_buffers.referenced_by[it->vertex()] = numberOfComponents;
207  partitions.back().push_back(it->vertex());
208  }
209  }
210 
211  ++numberOfComponents;
212  }
213 
214  twocut_buffers.vertex_labels = twocut_buffers.referenced_by;
215 
216  twocut_buffers.max_not_used_label = numberOfComponents;
217 
218  bool didnt_change = true;
219  for (size_t i = 0; i < graph.numberOfEdges(); ++i)
220  {
221  auto v0 = graph.vertexOfEdge(i, 0);
222  auto v1 = graph.vertexOfEdge(i, 1);
223 
224  outputLabels[i] = twocut_buffers.vertex_labels[v0] == twocut_buffers.vertex_labels[v1] ? 0 : 1;
225 
226  if (static_cast<bool>(outputLabels[i]) != (last_good_vertex_labels[v0] != last_good_vertex_labels[v1]))
227  didnt_change = false;
228  }
229 
230  if (didnt_change)
231  break;
232 
233  // check if the shape of some partitions didn't change
234  changed.resize(numberOfComponents);
235  std::fill(changed.begin(), changed.end(), 0);
236 
237  std::fill(visited.begin(), visited.end(), 0);
238 
239  for (size_t i = 0; i < graph.numberOfVertices(); ++i)
240  if (!visited[i])
241  {
242  S.push(i);
243  visited[i] = 1;
244 
245  auto label_new = twocut_buffers.vertex_labels[i];
246  auto label_old = last_good_vertex_labels[i];
247 
248  while (!S.empty())
249  {
250  auto v = S.top();
251  S.pop();
252 
253  for (auto w = graph.verticesFromVertexBegin(v); w != graph.verticesFromVertexEnd(v); ++w)
254  {
255  if (last_good_vertex_labels[*w] == label_old && twocut_buffers.vertex_labels[*w] != label_new)
256  changed[label_new] = 1;
257 
258  if (visited[*w])
259  continue;
260 
261  if (twocut_buffers.vertex_labels[*w] == label_new)
262  {
263  S.push(*w);
264  visited[*w] = 1;
265 
266  if (last_good_vertex_labels[*w] != label_old)
267  changed[label_new] = 1;
268  }
269  }
270  }
271  }
272 
273  last_good_vertex_labels = twocut_buffers.vertex_labels;
274 
275  if (settings.verbose)
276  std::cout << std::setw(4) << k+1 << std::setw(16) << energy_decrease << std::setw(15) << ee << std::setw(15) << (energy_decrease - ee) << std::setw(14) << partitions.size() << std::endl;
277  }
278 }
279 
280 template<typename GraphVisitor, typename ECA, typename ELA>
281 inline
282 void kernighanLin(const CompleteGraph<GraphVisitor>& graph, const ECA& edgeCosts, const ELA& inputLabels, ELA& outputLabels, const Settings settings = Settings())
283 {
284  struct Visitor
285  {
286  bool operator()(ELA const& edge_labels) const
287  {
288  return true;
289  }
290  } visitor;
291 
292  kernighanLin(graph, edgeCosts, inputLabels, outputLabels, visitor, settings);
293 }
294 
295 template<typename GraphVisitor, typename ECA, typename ELA, typename VIS>
296 inline
297 void kernighanLin(const CompleteGraph<GraphVisitor>& graph, const ECA& edgeCosts, const ELA& inputLabels, ELA& outputLabels, VIS& visitor, const Settings settings = Settings())
298 {
299  struct SubgraphWithCut { // a subgraph with cut mask
300  SubgraphWithCut(const ELA& labels)
301  : labels_(labels) {}
302  bool vertex(const size_t v) const
303  { return true; }
304  bool edge(const size_t e) const
305  { return labels_[e] == 0; }
306 
307  const ELA& labels_;
308  };
309 
310  // build decomposition based on the current multicut
312  components.build(graph, SubgraphWithCut(inputLabels));
313 
314  double starting_energy = .0;
315 
316  // check if the input multicut labeling is valid
317  for(size_t edge = 0; edge < graph.numberOfEdges(); ++edge)
318  {
319  auto v0 = graph.vertexOfEdge(edge, 0);
320  auto v1 = graph.vertexOfEdge(edge, 1);
321 
322  if (inputLabels[edge])
323  starting_energy += edgeCosts[edge];
324 
325  if (static_cast<bool>(inputLabels[edge]) != !components.areConnected(v0, v1))
326  throw std::runtime_error("the input multicut labeling is invalid.");
327  }
328 
329  auto numberOfComponents = *std::max_element(components.labels_.begin(), components.labels_.end()) + 1;
330 
331  std::vector<std::vector<size_t>> partitions(numberOfComponents);
332 
333  auto twocut_buffers = twocut::makeTwoCutBuffers(graph);
334 
335  twocut::TwoCutSettings twocut_settings;
336  twocut_settings.numberOfIterations = settings.numberOfInnerIterations;
337  twocut_settings.epsilon = settings.epsilon;
338 
339  // build partitions
340  for (size_t i = 0; i < components.labels_.size(); ++i)
341  partitions[components.labels_[i]].push_back(i);
342 
343  if (settings.verbose)
344  {
345  std::cout << "Starting energy: " << starting_energy << std::endl;
346  std::cout << std::setw(4) << "Iter" << std::setw(16) << "Total decrease" << std::setw(15) << "Pair updates" << std::setw(15) << "New sets" << std::setw(15) << "Num. of sets\n";
347  }
348 
349  // interatively update bipartition in order to minimize the total cost of the multicut
350  for (size_t k = 0; k < settings.numberOfOuterIterations; ++k)
351  {
352  auto energy_decrease = .0;
353 
354  // update pairs of partitions
355  for (size_t i = 0; i < partitions.size() - 1; ++i)
356  for (auto j = i + 1; j < partitions.size(); ++j)
357  if (!partitions[j].empty())
358  energy_decrease += twocut::kernighanLin(graph, edgeCosts, partitions[i], partitions[j], twocut_buffers, twocut_settings);
359 
360  // remove partitions that became empty after the previous step
361  auto new_end = std::partition(partitions.begin(), partitions.end(), [](const std::vector<size_t>& s) { return !s.empty(); });
362  partitions.resize(new_end - partitions.begin());
363 
364  auto ee = energy_decrease;
365 
366  // try to intoduce new partitions
367  for (size_t i = 0, p_size = partitions.size(); i < p_size; ++i)
368  while (1)
369  {
370  std::vector<size_t> new_set;
371  energy_decrease += twocut::kernighanLin(graph, edgeCosts, partitions[i], new_set, twocut_buffers, twocut_settings);
372 
373  if (!new_set.empty())
374  partitions.emplace_back(std::move(new_set));
375  else
376  break;
377  }
378 
379  if (energy_decrease == .0)
380  break;
381 
382  for (size_t i = 0; i < partitions.size(); ++i)
383  for (size_t a = 0; a < partitions[i].size(); ++a)
384  {
385  for (size_t b = a + 1; b < partitions[i].size(); ++b)
386  outputLabels[graph.findEdge(partitions[i][a], partitions[i][b]).second] = 0;
387 
388  for (size_t j = i + 1; j < partitions.size(); ++j)
389  for (auto b : partitions[j])
390  outputLabels[graph.findEdge(partitions[i][a], b).second] = 1;
391  }
392 
393  if (!visitor(outputLabels))
394  break;
395 
396  if (settings.verbose)
397  std::cout << std::setw(4) << k+1 << std::setw(16) << energy_decrease << std::setw(15) << ee << std::setw(15) << (energy_decrease - ee) << std::setw(14) << partitions.size() << std::endl;
398  }
399 }
400 
401 } // of multicut
402 } // of graph
403 } // of andres
404 
405 #endif