andres::graph
multicut-lifted/kernighan-lin.hxx
1 #pragma once
2 #ifndef ANDRES_GRAPH_MULTICUT_LIFTED_KERNIGHAN_LIN_HXX
3 #define ANDRES_GRAPH_MULTICUT_LIFTED_KERNIGHAN_LIN_HXX
4 
5 #include <iomanip>
6 #include <stdexcept>
7 #include <unordered_set>
8 #include <vector>
9 #include <stack>
10 
11 #include "andres/graph/components.hxx"
12 #include "andres/graph/twocut-lifted/kernighan-lin.hxx"
13 
14 namespace andres {
15 namespace graph {
16 namespace multicut_lifted {
17 
19  std::size_t numberOfInnerIterations { std::numeric_limits<std::size_t>::max() };
20  std::size_t numberOfOuterIterations { 100 };
21  double epsilon { 1e-7 };
22  bool verbose { true };
23 };
24 
25 template<typename ORIGINAL_GRAPH, typename LIFTED_GRAPH, typename ECA, typename ELA, typename VIS>
26 inline
28  const ORIGINAL_GRAPH& original_graph,
29  const LIFTED_GRAPH& lifted_graph,
30  const ECA& edgeCosts,
31  const ELA& inputLabels,
32  ELA& outputLabels,
33  VIS& visitor,
34  const KernighanLinSettings settings = KernighanLinSettings())
35 {
36  struct SubgraphWithCut { // a subgraph with cut mask
37  SubgraphWithCut(const ELA& labels, std::vector<std::size_t> const& edge_in_lifted_graph)
38  : labels_(labels), edge_in_lifted_graph_(edge_in_lifted_graph)
39  {}
40  bool vertex(const std::size_t v) const
41  { return true; }
42  bool edge(const std::size_t e) const
43  { return labels_[edge_in_lifted_graph_[e]] == 0; }
44 
45  std::vector<std::size_t> const& edge_in_lifted_graph_;
46  const ELA& labels_;
47  };
48 
49  std::vector<std::size_t> edge_in_lifted_graph(original_graph.numberOfEdges());
50  for (std::size_t i = 0; i < original_graph.numberOfEdges(); ++i)
51  {
52  auto v0 = original_graph.vertexOfEdge(i, 0);
53  auto v1 = original_graph.vertexOfEdge(i, 1);
54 
55  edge_in_lifted_graph[i] = lifted_graph.findEdge(v0, v1).second;
56  }
57 
58  // build decomposition based on the current multicut
60  components.build(original_graph, SubgraphWithCut(inputLabels, edge_in_lifted_graph));
61 
62  double current_energy_value = .0;
63 
64  // check if the input multicut labeling is valid
65  for(std::size_t edge = 0; edge < lifted_graph.numberOfEdges(); ++edge)
66  {
67  outputLabels[edge] = inputLabels[edge];
68 
69  auto v0 = lifted_graph.vertexOfEdge(edge, 0);
70  auto v1 = lifted_graph.vertexOfEdge(edge, 1);
71 
72  if (inputLabels[edge])
73  current_energy_value += 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  std::vector<std::vector<std::size_t>> partitions(numberOfComponents);
82 
83  twocut_lifted::TwoCutSettings twocut_settings;
84  twocut_settings.numberOfIterations = settings.numberOfInnerIterations;
85  twocut_settings.epsilon = settings.epsilon;
86 
87  twocut_lifted::TwoCutBuffers<ORIGINAL_GRAPH> twocut_buffers(original_graph);
88 
89  // build partitions
90  for (std::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: " << current_energy_value << std::endl;
101  std::cout << std::setw(4) << "Iter" << std::setw(16) << "True decrease" << std::setw(15) << "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  std::vector<char> visited(original_graph.numberOfVertices());
107 
108  // 1 if i-th partitioned changed since last iteration, 0 otherwise
109  std::vector<char> changed(numberOfComponents, 1);
110 
111  // interatively update bipartition in order to minimize the total cost of the multicut
112  for (std::size_t k = 0; k < settings.numberOfOuterIterations; ++k)
113  {
114  auto energy_decrease = .0;
115 
116  std::vector<std::unordered_set<std::size_t>> edges(numberOfComponents);
117  for (std::size_t e = 0; e < original_graph.numberOfEdges(); ++e)
118  {
119  auto v0 = twocut_buffers.vertex_labels[original_graph.vertexOfEdge(e, 0)];
120  auto v1 = twocut_buffers.vertex_labels[original_graph.vertexOfEdge(e, 1)];
121 
122  if (v0 != v1)
123  edges[std::min(v0, v1)].insert(std::max(v0, v1));
124  }
125 
126  for (std::size_t i = 0; i < numberOfComponents && !visitor.time_limit_exceeded(); ++i)
127  if (!partitions[i].empty())
128  for (auto j = edges[i].begin(); j != edges[i].end() && !visitor.time_limit_exceeded(); ++j)
129  if (!partitions[*j].empty() && (changed[*j] || changed[i]))
130  {
131  auto ret = twocut_lifted::kernighanLin(original_graph, lifted_graph, edgeCosts, partitions[i], partitions[*j], twocut_buffers, twocut_settings);
132 
133  if (ret > settings.epsilon)
134  changed[i] = changed[*j] = 1;
135 
136  energy_decrease += ret;
137 
138  if (partitions[i].size() == 0)
139  break;
140  }
141 
142  auto ee = energy_decrease;
143 
144  // remove partitions that became empty after the previous step
145  auto new_end = std::partition(partitions.begin(), partitions.end(), [](const std::vector<std::size_t>& s) { return !s.empty(); });
146  partitions.resize(new_end - partitions.begin());
147 
148  // try to intoduce new partitions
149  for (std::size_t i = 0, p_size = partitions.size(); i < p_size && !visitor.time_limit_exceeded(); ++i)
150  {
151  if (!changed[i])
152  continue;
153 
154  bool flag = true;
155 
156  while (flag && !visitor.time_limit_exceeded())
157  {
158  std::vector<std::size_t> new_set;
159  energy_decrease += twocut_lifted::kernighanLin(original_graph, lifted_graph, edgeCosts, partitions[i], new_set, twocut_buffers, twocut_settings);
160 
161  flag = !new_set.empty();
162 
163  if (!new_set.empty())
164  partitions.emplace_back(std::move(new_set));
165  }
166  }
167 
168  if (energy_decrease == .0)
169  break;
170 
171  std::stack<std::size_t> S;
172 
173  std::fill(visited.begin(), visited.end(), 0);
174 
175  // do connected component labeling on the original graph
176  numberOfComponents = 0;
177  for (std::size_t i = 0; i < original_graph.numberOfVertices(); ++i)
178  if (!visited[i])
179  {
180  S.push(i);
181  visited[i] = 1;
182 
183  auto label = twocut_buffers.vertex_labels[i];
184 
185  twocut_buffers.referenced_by[i] = numberOfComponents;
186 
187  while (!S.empty())
188  {
189  auto v = S.top();
190  S.pop();
191 
192  for (auto it = original_graph.adjacenciesFromVertexBegin(v); it != original_graph.adjacenciesFromVertexEnd(v); ++it)
193  if (twocut_buffers.vertex_labels[it->vertex()] == label && !visited[it->vertex()])
194  {
195  S.push(it->vertex());
196  visited[it->vertex()] = 1;
197  twocut_buffers.referenced_by[it->vertex()] = numberOfComponents;
198  }
199  }
200 
201  ++numberOfComponents;
202  }
203 
204  // compute new true energy
205  double new_energy_value = .0;
206  for (std::size_t i = 0; i < lifted_graph.numberOfEdges(); ++i)
207  {
208  auto v0 = lifted_graph.vertexOfEdge(i, 0);
209  auto v1 = lifted_graph.vertexOfEdge(i, 1);
210 
211  if (twocut_buffers.referenced_by[v0] != twocut_buffers.referenced_by[v1])
212  new_energy_value += edgeCosts[i];
213  }
214 
215  // if the new true energy is higher, than the current one, revert the changes and terminate
216  if (new_energy_value >= current_energy_value - settings.epsilon)
217  {
218  for (std::size_t i = 0; i < lifted_graph.numberOfEdges(); ++i)
219  {
220  auto v0 = lifted_graph.vertexOfEdge(i, 0);
221  auto v1 = lifted_graph.vertexOfEdge(i, 1);
222 
223  outputLabels[i] = last_good_vertex_labels[v0] == last_good_vertex_labels[v1] ? 0 : 1;
224  }
225 
226  break;
227  }
228 
229  // otherwise, form new partitions
230  partitions.clear();
231  partitions.resize(numberOfComponents);
232 
233  for (std::size_t i = 0; i < original_graph.numberOfVertices(); ++i)
234  {
235  twocut_buffers.vertex_labels[i] = twocut_buffers.referenced_by[i];
236 
237  partitions[twocut_buffers.vertex_labels[i]].push_back(i);
238  }
239 
240  twocut_buffers.max_not_used_label = numberOfComponents;
241 
242  bool didnt_change = true;
243  for (std::size_t i = 0; i < lifted_graph.numberOfEdges(); ++i)
244  {
245  auto v0 = lifted_graph.vertexOfEdge(i, 0);
246  auto v1 = lifted_graph.vertexOfEdge(i, 1);
247 
248  outputLabels[i] = twocut_buffers.vertex_labels[v0] == twocut_buffers.vertex_labels[v1] ? 0 : 1;
249 
250  if (static_cast<bool>(outputLabels[i]) != (last_good_vertex_labels[v0] != last_good_vertex_labels[v1]))
251  didnt_change = false;
252  }
253 
254  if (didnt_change)
255  break;
256 
257  // check if the shape of some partitions didn't change
258  changed.clear();
259  changed.resize(numberOfComponents);
260 
261  std::fill(visited.begin(), visited.end(), 0);
262 
263  for (std::size_t i = 0; i < original_graph.numberOfVertices(); ++i)
264  if (!visited[i])
265  {
266  S.push(i);
267  visited[i] = 1;
268 
269  auto label_new = twocut_buffers.vertex_labels[i];
270  auto label_old = last_good_vertex_labels[i];
271 
272  while (!S.empty())
273  {
274  auto v = S.top();
275  S.pop();
276 
277  for (auto w = original_graph.verticesFromVertexBegin(v); w != original_graph.verticesFromVertexEnd(v); ++w)
278  {
279  if (last_good_vertex_labels[*w] == label_old && twocut_buffers.vertex_labels[*w] != label_new)
280  changed[label_new] = 1;
281 
282  if (visited[*w])
283  continue;
284 
285  if (twocut_buffers.vertex_labels[*w] == label_new)
286  {
287  S.push(*w);
288  visited[*w] = 1;
289 
290  if (last_good_vertex_labels[*w] != label_old)
291  changed[label_new] = 1;
292  }
293  }
294  }
295  }
296 
297  last_good_vertex_labels = twocut_buffers.vertex_labels;
298 
299  if (!visitor(last_good_vertex_labels) || visitor.time_limit_exceeded())
300  break;
301 
302  if (settings.verbose)
303  std::cout << std::setw(4) << k+1 << std::setw(16) << current_energy_value - new_energy_value << std::setw(15) << energy_decrease << std::setw(15) << ee << std::setw(15) << (energy_decrease - ee) << std::setw(14) << partitions.size() << std::endl;
304 
305  current_energy_value = new_energy_value;
306  }
307 }
308 
309 template<typename ORIGINAL_GRAPH, typename LIFTED_GRAPH, typename ECA, typename ELA>
311  const ORIGINAL_GRAPH& original_graph,
312  const LIFTED_GRAPH& lifted_graph,
313  const ECA& edgeCosts,
314  const ELA& inputLabels,
315  ELA& outputLabels,
316  const KernighanLinSettings settings = KernighanLinSettings())
317 {
318  struct Visitor {
319  constexpr bool operator()(std::vector<std::size_t>& vertex_labels)
320  { return true; }
321  constexpr bool time_limit_exceeded()
322  { return false; }
323  } visitor;
324 
325  kernighanLin(original_graph, lifted_graph, edgeCosts, inputLabels, outputLabels, visitor, settings);
326 }
327 
328 }
329 }
330 }
331 
332 #endif