MRPT  1.9.9
CGraphPartitioner_impl.h
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2018, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #if !defined(CGRAPHPARTITIONER_H)
11 #error "This file can't be included from outside of CGraphPartitioner.h"
12 #endif
13 
14 namespace mrpt::graphs
15 {
16 /*---------------------------------------------------------------
17  SpectralPartition
18  ---------------------------------------------------------------*/
19 template <class GRAPH_MATRIX, typename num_t>
21  GRAPH_MATRIX& in_A, std::vector<uint32_t>& out_part1,
22  std::vector<uint32_t>& out_part2, num_t& out_cut_value, bool forceSimetry)
23 {
24  size_t nodeCount; // Nodes count
25  GRAPH_MATRIX Adj, eigenVectors, eigenValues;
26 
27  // Check matrix is square:
28  if (in_A.cols() != int(nodeCount = in_A.rows()))
29  THROW_EXCEPTION("Weights matrix is not square!!");
30 
31  // Shi & Malik's method
32  //-----------------------------------------------------------------
33 
34  // forceSimetry?
35  if (forceSimetry)
36  {
37  Adj.setSize(nodeCount, nodeCount);
38  for (size_t i = 0; i < nodeCount; i++)
39  for (size_t j = i; j < nodeCount; j++)
40  Adj(i, j) = Adj(j, i) = 0.5f * (in_A(i, j) + in_A(j, i));
41  }
42  else
43  Adj = in_A;
44 
45  // Compute eigen-vectors of laplacian:
46  GRAPH_MATRIX LAPLACIAN;
47  Adj.laplacian(LAPLACIAN);
48 
49  LAPLACIAN.eigenVectors(eigenVectors, eigenValues);
50 
51  // Execute the bisection
52  // ------------------------------------
53  // Second smallest eigen-vector
54  double mean = 0;
55  size_t colNo = 1; // second smallest
56  size_t nRows = eigenVectors.rows();
57 
58  // for (i=0;i<eigenVectors.cols();i++) mean+=eigenVectors(colNo,i);
59  for (size_t i = 0; i < nRows; i++) mean += eigenVectors(i, colNo);
60  mean /= nRows;
61 
62  out_part1.clear();
63  out_part2.clear();
64 
65  for (size_t i = 0; i < nRows; i++)
66  {
67  if (eigenVectors(i, colNo) >= mean)
68  out_part1.push_back(i);
69  else
70  out_part2.push_back(i);
71  }
72 
73  // Special and strange case: Constant eigenvector: Split nodes in two
74  // equally sized parts arbitrarily:
75  // ------------------------------------------------------------------
76  if (!out_part1.size() || !out_part2.size())
77  {
78  out_part1.clear();
79  out_part2.clear();
80  // Assign 50%-50%:
81  for (int i = 0; i < Adj.cols(); i++)
82  if (i <= Adj.cols() / 2)
83  out_part1.push_back(i);
84  else
85  out_part2.push_back(i);
86  }
87 
88  // Compute the N-cut value
89  out_cut_value = nCut(Adj, out_part1, out_part2);
90 }
91 
92 /*---------------------------------------------------------------
93  RecursiveSpectralPartition
94  ---------------------------------------------------------------*/
95 template <class GRAPH_MATRIX, typename num_t>
97  GRAPH_MATRIX& in_A, std::vector<std::vector<uint32_t>>& out_parts,
98  num_t threshold_Ncut, bool forceSimetry, bool useSpectralBisection,
99  bool recursive, unsigned minSizeClusters, const bool verbose)
100 {
101  MRPT_START
102 
103  size_t nodeCount;
104  std::vector<uint32_t> p1, p2;
105  num_t cut_value;
106  size_t i, j;
107  GRAPH_MATRIX Adj;
108 
109  out_parts.clear();
110 
111  // Check matrix is square:
112  if (in_A.cols() != int(nodeCount = in_A.rows()))
113  THROW_EXCEPTION("Weights matrix is not square!!");
114 
115  if (nodeCount == 1)
116  {
117  // Don't split, there is just a node!
118  p1.push_back(0);
119  out_parts.push_back(p1);
120  return;
121  }
122 
123  // forceSimetry?
124  if (forceSimetry)
125  {
126  Adj.setSize(nodeCount, nodeCount);
127  for (i = 0; i < nodeCount; i++)
128  for (j = i; j < nodeCount; j++)
129  Adj(i, j) = Adj(j, i) = 0.5f * (in_A(i, j) + in_A(j, i));
130  }
131  else
132  Adj = in_A;
133 
134  // Make bisection
135  if (useSpectralBisection)
136  SpectralBisection(Adj, p1, p2, cut_value, false);
137  else
138  exactBisection(Adj, p1, p2, cut_value, false);
139 
140  if (verbose)
141  std::cout << format(
142  "Cut:%u=%u+%u,nCut=%.02f->", (unsigned int)nodeCount,
143  (unsigned int)p1.size(), (unsigned int)p2.size(), cut_value);
144 
145  // Is it a useful partition?
146  if (cut_value > threshold_Ncut || p1.size() < minSizeClusters ||
147  p2.size() < minSizeClusters)
148  {
149  if (verbose) std::cout << "->NO!" << std::endl;
150 
151  // No:
152  p1.clear();
153  for (i = 0; i < nodeCount; i++) p1.push_back(i);
154  out_parts.push_back(p1);
155  }
156  else
157  {
158  if (verbose) std::cout << "->YES!" << std::endl;
159 
160  // Yes:
161  std::vector<std::vector<uint32_t>> p1_parts, p2_parts;
162 
163  if (recursive)
164  {
165  // Split "p1":
166  // --------------------------------------------
167  // sub-matrix:
168  GRAPH_MATRIX A_1(p1.size(), p1.size());
169  for (i = 0; i < p1.size(); i++)
170  for (j = 0; j < p1.size(); j++) A_1(i, j) = in_A(p1[i], p1[j]);
171 
172  RecursiveSpectralPartition(
173  A_1, p1_parts, threshold_Ncut, forceSimetry,
174  useSpectralBisection, recursive, minSizeClusters);
175 
176  // Split "p2":
177  // --------------------------------------------
178  // sub-matrix:
179  GRAPH_MATRIX A_2(p2.size(), p2.size());
180  for (i = 0; i < p2.size(); i++)
181  for (j = 0; j < p2.size(); j++) A_2(i, j) = in_A(p2[i], p2[j]);
182 
183  RecursiveSpectralPartition(
184  A_2, p2_parts, threshold_Ncut, forceSimetry,
185  useSpectralBisection, recursive, minSizeClusters);
186 
187  // Build "out_parts" from "p1_parts" + "p2_parts"
188  // taken care of indexes mapping!
189  // -------------------------------------------------------------------------------------
190  // remap p1 nodes:
191  for (i = 0; i < p1_parts.size(); i++)
192  {
193  for (j = 0; j < p1_parts[i].size(); j++)
194  p1_parts[i][j] = p1[p1_parts[i][j]];
195  out_parts.push_back(p1_parts[i]);
196  }
197 
198  // remap p2 nodes:
199  for (i = 0; i < p2_parts.size(); i++)
200  {
201  for (j = 0; j < p2_parts[i].size(); j++)
202  p2_parts[i][j] = p2[p2_parts[i][j]];
203 
204  out_parts.push_back(p2_parts[i]);
205  }
206  }
207  else
208  {
209  // Force bisection only:
210  out_parts.clear();
211  out_parts.push_back(p1);
212  out_parts.push_back(p2);
213  }
214  }
215 
216  MRPT_END
217 }
218 
219 /*---------------------------------------------------------------
220  nCut
221  ---------------------------------------------------------------*/
222 template <class GRAPH_MATRIX, typename num_t>
224  const GRAPH_MATRIX& in_A, const std::vector<uint32_t>& in_part1,
225  const std::vector<uint32_t>& in_part2)
226 {
227  unsigned int i, j;
228  size_t size1 = in_part1.size();
229  size_t size2 = in_part2.size();
230 
231  // Compute the N-cut value
232  // -----------------------------------------------
233  num_t cut_AB = 0;
234  for (i = 0; i < size1; i++)
235  for (j = 0; j < size2; j++) cut_AB += in_A(in_part1[i], in_part2[j]);
236 
237  num_t assoc_AA = 0;
238  for (i = 0; i < size1; i++)
239  for (j = i; j < size1; j++)
240  if (i != j) assoc_AA += in_A(in_part1[i], in_part1[j]);
241 
242  num_t assoc_BB = 0;
243  for (i = 0; i < size2; i++)
244  for (j = i; j < size2; j++)
245  if (i != j) assoc_BB += in_A(in_part2[i], in_part2[j]);
246 
247  num_t assoc_AV = assoc_AA + cut_AB;
248  num_t assoc_BV = assoc_BB + cut_AB;
249 
250  if (!cut_AB)
251  return 0;
252  else
253  return cut_AB / assoc_AV + cut_AB / assoc_BV;
254 }
255 
256 /*---------------------------------------------------------------
257  exactBisection
258  ---------------------------------------------------------------*/
259 template <class GRAPH_MATRIX, typename num_t>
261  GRAPH_MATRIX& in_A, std::vector<uint32_t>& out_part1,
262  std::vector<uint32_t>& out_part2, num_t& out_cut_value, bool forceSimetry)
263 {
264  size_t nodeCount; // Nodes count
265  size_t i, j;
266  GRAPH_MATRIX Adj;
267  std::vector<bool> partition, bestPartition;
268  std::vector<uint32_t> part1, part2;
269  num_t partCutValue, bestCutValue = std::numeric_limits<num_t>::max();
270  bool end = false;
271 
272  // Check matrix is square:
273  if (in_A.cols() != int(nodeCount = in_A.rows()))
274  THROW_EXCEPTION("Weights matrix is not square!!");
275 
276  ASSERT_(nodeCount >= 2);
277 
278  // forceSimetry?
279  if (forceSimetry)
280  {
281  Adj.setSize(nodeCount, nodeCount);
282  for (i = 0; i < nodeCount; i++)
283  for (j = i; j < nodeCount; j++)
284  Adj(i, j) = Adj(j, i) = 0.5f * (in_A(i, j) + in_A(j, i));
285  }
286  else
287  Adj = in_A;
288 
289  // Brute force: compute all posible partitions:
290  //-----------------------------------------------------------------
291 
292  // First combination: 1000...0000
293  // Last combination: 1111...1110
294  partition.clear();
295  partition.resize(nodeCount, false);
296  partition[0] = true;
297 
298  while (!end)
299  {
300  // Build partitions from binary vector:
301  part1.clear();
302  part2.clear();
303 
304  for (i = 0; i < nodeCount; i++)
305  {
306  if (partition[i])
307  part2.push_back(i);
308  else
309  part1.push_back(i);
310  }
311 
312  // Compute the n-cut:
313  partCutValue = nCut(Adj, part1, part2);
314 
315  if (partCutValue < bestCutValue)
316  {
317  bestCutValue = partCutValue;
318  bestPartition = partition;
319  }
320 
321  // Next combo in the binary vector:
322  i = 0;
323  bool carry = false;
324  do
325  {
326  carry = partition[i];
327  partition[i] = !partition[i];
328  i++;
329  } while (carry && i < nodeCount);
330 
331  // End criterion:
332  end = true;
333  for (i = 0; end && i < nodeCount; i++) end = end && partition[i];
334 
335  }; // End of while
336 
337  // Return the best partition:
338  out_cut_value = bestCutValue;
339 
340  out_part1.clear();
341  out_part2.clear();
342 
343  for (i = 0; i < nodeCount; i++)
344  {
345  if (bestPartition[i])
346  out_part2.push_back(i);
347  else
348  out_part1.push_back(i);
349  }
350 }
351 
352 }
353 
#define MRPT_START
Definition: exceptions.h:262
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:41
EIGEN_STRONG_INLINE bool eigenVectors(MATRIX1 &eVecs, MATRIX2 &eVals) const
[For square matrices only] Compute the eigenvectors and eigenvalues (sorted), both returned as matric...
Abstract graph and tree data structures, plus generic graph algorithms.
static void SpectralBisection(GRAPH_MATRIX &in_A, std::vector< uint32_t > &out_part1, std::vector< uint32_t > &out_part2, num_t &out_cut_value, bool forceSimetry=true)
Performs the spectral bisection of a graph.
#define ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:113
static num_t nCut(const GRAPH_MATRIX &in_A, const std::vector< uint32_t > &in_part1, const std::vector< uint32_t > &in_part2)
Returns the normaliced cut of a graph, given its adjacency matrix A and a bisection: ...
GLuint GLuint end
Definition: glext.h:3528
static void exactBisection(GRAPH_MATRIX &in_A, std::vector< uint32_t > &out_part1, std::vector< uint32_t > &out_part2, num_t &out_cut_value, bool forceSimetry=true)
Performs an EXACT minimum n-Cut graph bisection, (Use CGraphPartitioner::SpectralBisection for a fast...
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:16
#define MRPT_END
Definition: exceptions.h:266
EIGEN_STRONG_INLINE void eigenValues(VECTOR &eVals) const
[For square matrices only] Compute the eigenvectors and eigenvalues (sorted), and return only the eig...
static void RecursiveSpectralPartition(GRAPH_MATRIX &in_A, std::vector< std::vector< uint32_t >> &out_parts, num_t threshold_Ncut=1, bool forceSimetry=true, bool useSpectralBisection=true, bool recursive=true, unsigned minSizeClusters=1, const bool verbose=false)
Performs the spectral recursive partition into K-parts for a given graph.
EIGEN_STRONG_INLINE double mean() const
Computes the mean of the entire matrix.



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 7d5e6d718 Fri Aug 24 01:51:28 2018 +0200 at lun nov 2 08:35:50 CET 2020