andres::graph
twocut-lifted/kernighan-lin.hxx
1 #pragma once
2 #ifndef ANDRES_GRAPH_TWOCUT_LIFTED_KERNIGHAN_LIN_HXX
3 #define ANDRES_GRAPH_TWOCUT_LIFTED_KERNIGHAN_LIN_HXX
4 
5 #include <limits>
6 #include <vector>
7 
8 namespace andres {
9 namespace graph {
10 namespace twocut_lifted {
11 
13  std::size_t numberOfIterations { std::numeric_limits<std::size_t>::max() };
14  double epsilon { 1e-9 };
15 };
16 
17 template<typename ORIGINAL_GRAPH>
18 struct TwoCutBuffers {
19  TwoCutBuffers(const ORIGINAL_GRAPH& graph)
20  : differences(graph.numberOfVertices()),
21  is_moved(graph.numberOfVertices()),
22  referenced_by(graph.numberOfVertices()),
23  vertex_labels(graph.numberOfVertices())
24  {}
25  std::vector<double> differences;
26  std::vector<char> is_moved;
27  std::size_t max_not_used_label;
28  std::vector<std::size_t> referenced_by;
29  std::vector<std::size_t> vertex_labels;
30 };
31 
32 template<typename ORIGINAL_GRAPH, typename LIFTED_GRAPH, typename SET, typename ECA>
33 inline double
35  const ORIGINAL_GRAPH& original_graph,
36  const LIFTED_GRAPH& lifted_graph,
37  const ECA& edge_costs,
38  SET& A,
39  SET& B,
41  const TwoCutSettings settings
42 )
43 {
44  struct Move
45  {
46  int v { -1 };
47  double difference { std::numeric_limits<double>::lowest() };
48  std::size_t new_label;
49  };
50 
51  auto gain_from_merging = .0;
52 
53  auto compute_differences = [&](const SET& A, std::size_t label_A, std::size_t label_B)
54  {
55  for (long int i = 0; i < A.size(); ++i)
56  {
57  double diffExt = .0;
58  double diffInt = .0;
59  std::size_t ref_cnt = 0;
60 
61  for (auto it = lifted_graph.adjacenciesFromVertexBegin(A[i]); it != lifted_graph.adjacenciesFromVertexEnd(A[i]); ++it)
62  {
63  const auto lbl = buffer.vertex_labels[it->vertex()];
64 
65  if (lbl == label_A)
66  diffInt += edge_costs[it->edge()];
67  else if (lbl == label_B)
68  diffExt += edge_costs[it->edge()];
69  }
70 
71  for (auto it = original_graph.adjacenciesFromVertexBegin(A[i]); it != original_graph.adjacenciesFromVertexEnd(A[i]); ++it)
72  if (buffer.vertex_labels[it->vertex()] == label_B)
73  ++ref_cnt;
74 
75  buffer.differences[A[i]] = diffExt - diffInt;
76  buffer.referenced_by[A[i]] = ref_cnt;
77  buffer.is_moved[A[i]] = 0;
78 
79  gain_from_merging += diffExt;
80  }
81  };
82 
83 
84  if (A.empty())
85  return .0;
86 
87  auto label_A = buffer.vertex_labels[A[0]];
88  auto label_B = (!B.empty()) ? buffer.vertex_labels[B[0]] : buffer.max_not_used_label;
89 
90  compute_differences(A, label_A, label_B);
91  compute_differences(B, label_B, label_A);
92 
93  gain_from_merging /= 2.0;
94 
95  std::vector<std::size_t> border;
96 
97  for (auto a : A)
98  if (buffer.referenced_by[a] > 0)
99  border.push_back(a);
100 
101  for (auto b : B)
102  if (buffer.referenced_by[b] > 0)
103  border.push_back(b);
104 
105  std::vector<Move> moves;
106  double cumulative_diff = .0;
107  std::pair<double, std::size_t> max_move { std::numeric_limits<double>::lowest(), 0 };
108 
109  for (std::size_t k = 0; k < settings.numberOfIterations; ++k)
110  {
111  Move m;
112 
113  if (B.empty() && k == 0)
114  {
115  for (auto a : A)
116  if (buffer.differences[a] > m.difference)
117  {
118  m.v = a;
119  m.difference = buffer.differences[a];
120  }
121  }
122  else
123  {
124  std::size_t size = border.size();
125 
126  for (std::size_t i = 0; i < size; )
127  if (buffer.referenced_by[border[i]] == 0)
128  std::swap(border[i], border[--size]);
129  else
130  {
131  if (buffer.differences[border[i]] > m.difference)
132  {
133  m.v = border[i];
134  m.difference = buffer.differences[m.v];
135  }
136 
137  ++i;
138  }
139 
140  border.erase(border.begin() + size, border.end());
141  }
142 
143  if (m.v == -1)
144  break;
145 
146  const auto old_label = buffer.vertex_labels[m.v];
147 
148  if (old_label == label_A)
149  m.new_label = label_B;
150  else
151  m.new_label = label_A;
152 
153  // update differences and references
154  for (auto it = lifted_graph.adjacenciesFromVertexBegin(m.v); it != lifted_graph.adjacenciesFromVertexEnd(m.v); ++it)
155  {
156  if (buffer.is_moved[it->vertex()])
157  continue;
158 
159  const auto lbl = buffer.vertex_labels[it->vertex()];
160 
161  // edge to an element of the new set
162  if (lbl == m.new_label)
163  buffer.differences[it->vertex()] -= 2.0*edge_costs[it->edge()];
164  // edge to an element of the old set
165  else if (lbl == old_label)
166  buffer.differences[it->vertex()] += 2.0*edge_costs[it->edge()];
167  }
168 
169  for (auto it = original_graph.adjacenciesFromVertexBegin(m.v); it != original_graph.adjacenciesFromVertexEnd(m.v); ++it)
170  {
171  if (buffer.is_moved[it->vertex()])
172  continue;
173 
174  const auto lbl = buffer.vertex_labels[it->vertex()];
175 
176  // edge to an element of the new set
177  if (lbl == m.new_label)
178  --buffer.referenced_by[it->vertex()];
179  // edge to an element of the old set
180  else if (lbl == old_label)
181  {
182  ++buffer.referenced_by[it->vertex()];
183 
184  if (buffer.referenced_by[it->vertex()] == 1)
185  border.push_back(it->vertex());
186  }
187  }
188 
189  buffer.vertex_labels[m.v] = m.new_label;
190  buffer.referenced_by[m.v] = 0;
191  buffer.differences[m.v] = std::numeric_limits<double>::lowest();
192  buffer.is_moved[m.v] = 1;
193  moves.push_back(m);
194 
195  cumulative_diff += m.difference;
196 
197  if (cumulative_diff > max_move.first)
198  max_move = std::make_pair(cumulative_diff, moves.size());
199  }
200 
201 
202  if (gain_from_merging > max_move.first && gain_from_merging > settings.epsilon)
203  {
204  A.insert(A.end(), B.begin(), B.end());
205 
206  for (auto a : A)
207  buffer.vertex_labels[a] = label_A;
208 
209  for (auto b : B)
210  buffer.vertex_labels[b] = label_A;
211 
212  B.clear();
213 
214  return gain_from_merging;
215  }
216  else if (max_move.first > settings.epsilon)
217  {
218  // revert some changes
219  for (std::size_t i = max_move.second; i < moves.size(); ++i)
220  {
221  buffer.is_moved[moves[i].v] = 0;
222 
223  if (moves[i].new_label == label_B)
224  buffer.vertex_labels[moves[i].v] = label_A;
225  else
226  buffer.vertex_labels[moves[i].v] = label_B;
227  }
228 
229  // make sure that this is unique label
230  if (B.empty())
231  ++buffer.max_not_used_label;
232 
233  A.erase(std::partition(A.begin(), A.end(), [&](std::size_t a) { return !buffer.is_moved[a]; }), A.end());
234  B.erase(std::partition(B.begin(), B.end(), [&](std::size_t b) { return !buffer.is_moved[b]; }), B.end());
235 
236  for (std::size_t i = 0; i < max_move.second; ++i)
237  // move vertex to the other set
238  if (moves[i].new_label == label_B)
239  B.push_back(moves[i].v);
240  else
241  A.push_back(moves[i].v);
242 
243  return max_move.first;
244  }
245  else
246  for (std::size_t i = 0; i < moves.size(); ++i)
247  if (moves[i].new_label == label_B)
248  buffer.vertex_labels[moves[i].v] = label_A;
249  else
250  buffer.vertex_labels[moves[i].v] = label_B;
251 
252  return .0;
253 }
254 
255 template<typename ORIGINAL_GRAPH, typename LIFTED_GRAPH, typename SET, typename ECA>
256 inline double
258  const ORIGINAL_GRAPH& original_graph,
259  const LIFTED_GRAPH& lifted_graph,
260  const ECA& edge_costs,
261  SET& A,
262  SET& B,
263  const TwoCutSettings settings
264 ) {
265  TwoCutBuffers<ORIGINAL_GRAPH> buffer(original_graph);
266  return kernighanLin(original_graph, lifted_graph, edge_costs, A, B, buffer, settings);
267 }
268 
269 }
270 }
271 }
272 
273 #endif