gtsam 4.2
gtsam
Loading...
Searching...
No Matches
FindSeparator-inl.h
1/*
2 * FindSeparator-inl.h
3 *
4 * Created on: Nov 23, 2010
5 * Updated: Feb 20. 2014
6 * Author: nikai
7 * Author: Andrew Melim
8 * Description: find the separator of bisectioning for a given graph
9 */
10
11#pragma once
12
13#include <stdexcept>
14#include <iostream>
15#include <vector>
16#include <boost/tuple/tuple.hpp>
17#include <boost/shared_array.hpp>
18
19#include <gtsam/base/timing.h>
20
21#include "FindSeparator.h"
22
23#include <metis.h>
24
25extern "C" {
26#include <metislib.h>
27}
28
29
30
31namespace gtsam { namespace partition {
32
33 typedef boost::shared_array<idx_t> sharedInts;
34
35 /* ************************************************************************* */
42 std::pair<int, sharedInts> separatorMetis(idx_t n, const sharedInts& xadj,
43 const sharedInts& adjncy, const sharedInts& adjwgt, bool verbose) {
44
45 // control parameters
46 std::vector<idx_t> vwgt; // the weights of the vertices
47 idx_t options[METIS_NOPTIONS];
48 METIS_SetDefaultOptions(options); // use defaults
49 idx_t sepsize; // the size of the separator, output
50 sharedInts part_(new idx_t[n]); // the partition of each vertex, output
51
52 // set uniform weights on the vertices
53 vwgt.assign(n, 1);
54
55 // TODO: Fix at later time
56 //boost::timer::cpu_timer TOTALTmr;
57 if (verbose) {
58 printf("**********************************************************************\n");
59 printf("Graph Information ---------------------------------------------------\n");
60 printf(" #Vertices: %d, #Edges: %u\n", n, *(xadj.get()+n) / 2);
61 printf("\nND Partitioning... -------------------------------------------\n");
62 //TOTALTmr.start()
63 }
64
65 // call metis parition routine
66 METIS_ComputeVertexSeparator(&n, xadj.get(), adjncy.get(),
67 &vwgt[0], options, &sepsize, part_.get());
68
69 if (verbose) {
70 //boost::cpu_times const elapsed_times(timer.elapsed());
71 //printf("\nTiming Information --------------------------------------------------\n");
72 //printf(" Total: \t\t %7.3f\n", elapsed_times);
73 printf(" Sep size: \t\t %d\n", sepsize);
74 printf("**********************************************************************\n");
75 }
76
77 return std::make_pair(sepsize, part_);
78 }
79
80 /* ************************************************************************* */
81 void modefied_EdgeComputeSeparator(idx_t *nvtxs, idx_t *xadj, idx_t *adjncy, idx_t *vwgt,
82 idx_t *adjwgt, idx_t *options, idx_t *edgecut, idx_t *part)
83 {
84 idx_t i, ncon;
85 graph_t *graph;
86 real_t *tpwgts2;
87 ctrl_t *ctrl;
88 ctrl = SetupCtrl(METIS_OP_OMETIS, options, 1, 3, nullptr, nullptr);
89 ctrl->iptype = METIS_IPTYPE_GROW;
90 //if () == nullptr)
91 // return METIS_ERROR_INPUT;
92
93 InitRandom(ctrl->seed);
94
95 graph = SetupGraph(ctrl, *nvtxs, 1, xadj, adjncy, vwgt, nullptr, nullptr);
96
97 AllocateWorkSpace(ctrl, graph);
98
99 ncon = graph->ncon;
100 ctrl->ncuts = 1;
101
102 /* determine the weights of the two partitions as a function of the weight of the
103 target partition weights */
104
105 tpwgts2 = rwspacemalloc(ctrl, 2*ncon);
106 for (i=0; i<ncon; i++) {
107 tpwgts2[i] = rsum((2>>1), ctrl->tpwgts+i, ncon);
108 tpwgts2[ncon+i] = 1.0 - tpwgts2[i];
109 }
110 /* perform the bisection */
111 *edgecut = MultilevelBisect(ctrl, graph, tpwgts2);
112
113 // ConstructMinCoverSeparator(&ctrl, &graph, 1.05);
114 // *edgecut = graph->mincut;
115 // *sepsize = graph.pwgts[2];
116 icopy(*nvtxs, graph->where, part);
117 std::cout << "Finished bisection:" << *edgecut << std::endl;
118 FreeGraph(&graph);
119
120 FreeCtrl(&ctrl);
121 }
122
123 /* ************************************************************************* */
129 std::pair<int, sharedInts> edgeMetis(idx_t n, const sharedInts& xadj, const sharedInts& adjncy,
130 const sharedInts& adjwgt, bool verbose) {
131
132 // control parameters
133 std::vector<idx_t> vwgt; // the weights of the vertices
134 idx_t options[METIS_NOPTIONS];
135 METIS_SetDefaultOptions(options); // use defaults
136 idx_t edgecut; // the number of edge cuts, output
137 sharedInts part_(new idx_t[n]); // the partition of each vertex, output
138
139 // set uniform weights on the vertices
140 vwgt.assign(n, 1);
141
142 //TODO: Fix later
143 //boost::timer TOTALTmr;
144 if (verbose) {
145 printf("**********************************************************************\n");
146 printf("Graph Information ---------------------------------------------------\n");
147 printf(" #Vertices: %d, #Edges: %u\n", n, *(xadj.get()+n) / 2);
148 printf("\nND Partitioning... -------------------------------------------\n");
149 //cleartimer(TOTALTmr);
150 //starttimer(TOTALTmr);
151 }
152
153 //int wgtflag = 1; // only edge weights
154 //int numflag = 0; // c style numbering starting from 0
155 //int nparts = 2; // partition the graph to 2 submaps
156 modefied_EdgeComputeSeparator(&n, xadj.get(), adjncy.get(), &vwgt[0], adjwgt.get(),
157 options, &edgecut, part_.get());
158
159
160 if (verbose) {
161 //stoptimer(TOTALTmr);
162 printf("\nTiming Information --------------------------------------------------\n");
163 //printf(" Total: \t\t %7.3f\n", gettimer(TOTALTmr));
164 printf(" Edge cuts: \t\t %d\n", edgecut);
165 printf("**********************************************************************\n");
166 }
167
168 return std::make_pair(edgecut, part_);
169 }
170
171 /* ************************************************************************* */
177 template <class GenericGraph>
178 void prepareMetisGraph(const GenericGraph& graph, const std::vector<size_t>& keys, WorkSpace& workspace,
179 sharedInts* ptr_xadj, sharedInts* ptr_adjncy, sharedInts* ptr_adjwgt) {
180
181 typedef std::vector<int> Weights;
182 typedef std::vector<int> Neighbors;
183 typedef std::pair<Neighbors, Weights> NeighborsInfo;
184
185 // set up dictionary
186 std::vector<int>& dictionary = workspace.dictionary;
187 workspace.prepareDictionary(keys);
188
189 // prepare for {adjacencyMap}, a pair of neighbor indices and the correponding edge weights
190 int numNodes = keys.size();
191 int numEdges = 0;
192 std::vector<NeighborsInfo> adjacencyMap;
193 adjacencyMap.resize(numNodes);
194 std::cout << "Number of nodes: " << adjacencyMap.size() << std::endl;
195 int index1, index2;
196
197 for(const typename GenericGraph::value_type& factor: graph){
198 index1 = dictionary[factor->key1.index];
199 index2 = dictionary[factor->key2.index];
200 std::cout << "index1: " << index1 << std::endl;
201 std::cout << "index2: " << index2 << std::endl;
202 // if both nodes are in the current graph, i.e. not a joint factor between frontal and separator
203 if (index1 >= 0 && index2 >= 0) {
204 std::pair<Neighbors, Weights>& adjacencyMap1 = adjacencyMap[index1];
205 std::pair<Neighbors, Weights>& adjacencyMap2 = adjacencyMap[index2];
206 try{
207 adjacencyMap1.first.push_back(index2);
208 adjacencyMap1.second.push_back(factor->weight);
209 adjacencyMap2.first.push_back(index1);
210 adjacencyMap2.second.push_back(factor->weight);
211 }catch(std::exception& e){
212 std::cout << e.what() << std::endl;
213 }
214 numEdges++;
215 }
216 }
217
218 // prepare for {xadj}, {adjncy}, and {adjwgt}
219 *ptr_xadj = sharedInts(new idx_t[numNodes+1]);
220 *ptr_adjncy = sharedInts(new idx_t[numEdges*2]);
221 *ptr_adjwgt = sharedInts(new idx_t[numEdges*2]);
222 sharedInts& xadj = *ptr_xadj;
223 sharedInts& adjncy = *ptr_adjncy;
224 sharedInts& adjwgt = *ptr_adjwgt;
225 int ind_xadj = 0, ind_adjncy = 0;
226 for(const NeighborsInfo& info: adjacencyMap) {
227 *(xadj.get() + ind_xadj) = ind_adjncy;
228 std::copy(info.first .begin(), info.first .end(), adjncy.get() + ind_adjncy);
229 std::copy(info.second.begin(), info.second.end(), adjwgt.get() + ind_adjncy);
230 assert(info.first.size() == info.second.size());
231 ind_adjncy += info.first.size();
232 ind_xadj ++;
233 }
234 if (ind_xadj != numNodes) throw std::runtime_error("prepareMetisGraph_: ind_xadj != numNodes");
235 *(xadj.get() + ind_xadj) = ind_adjncy;
236 }
237
238 /* ************************************************************************* */
239 template<class GenericGraph>
240 boost::optional<MetisResult> separatorPartitionByMetis(const GenericGraph& graph,
241 const std::vector<size_t>& keys, WorkSpace& workspace, bool verbose) {
242 // create a metis graph
243 size_t numKeys = keys.size();
244 if (verbose)
245 std::cout << graph.size() << " factors,\t" << numKeys << " nodes;\t" << std::endl;
246
247 sharedInts xadj, adjncy, adjwgt;
248
249 prepareMetisGraph<GenericGraph>(graph, keys, workspace, &xadj, &adjncy, &adjwgt);
250
251 // run ND on the graph
252 size_t sepsize;
253 sharedInts part;
254 boost::tie(sepsize, part) = separatorMetis(numKeys, xadj, adjncy, adjwgt, verbose);
255 if (!sepsize) return boost::optional<MetisResult>();
256
257 // convert the 0-1-2 from Metis to 1-2-0, so that the separator is 0, as later
258 // we will have more submaps
259 MetisResult result;
260 result.C.reserve(sepsize);
261 result.A.reserve(numKeys - sepsize);
262 result.B.reserve(numKeys - sepsize);
263 int* ptr_part = part.get();
264 std::vector<size_t>::const_iterator itKey = keys.begin();
265 std::vector<size_t>::const_iterator itKeyLast = keys.end();
266 while(itKey != itKeyLast) {
267 switch(*(ptr_part++)) {
268 case 0: result.A.push_back(*(itKey++)); break;
269 case 1: result.B.push_back(*(itKey++)); break;
270 case 2: result.C.push_back(*(itKey++)); break;
271 default: throw std::runtime_error("separatorPartitionByMetis: invalid results from Metis ND!");
272 }
273 }
274
275 if (verbose) {
276 std::cout << "total key: " << keys.size()
277 << " result(A,B,C) = " << result.A.size() << ", " << result.B.size() << ", "
278 << result.C.size() << "; sepsize from Metis = " << sepsize << std::endl;
279 //throw runtime_error("separatorPartitionByMetis:stop for debug");
280 }
281
282 if(result.C.size() != sepsize) {
283 std::cout << "total key: " << keys.size()
284 << " result(A,B,C) = " << result.A.size() << ", " << result.B.size() << ", " << result.C.size()
285 << "; sepsize from Metis = " << sepsize << std::endl;
286 throw std::runtime_error("separatorPartitionByMetis: invalid sepsize from Metis ND!");
287 }
288
289 return result;
290 }
291
292 /* *************************************************************************/
293 template<class GenericGraph>
294 boost::optional<MetisResult> edgePartitionByMetis(const GenericGraph& graph,
295 const std::vector<size_t>& keys, WorkSpace& workspace, bool verbose) {
296
297 // a small hack for handling the camera1-camera2 case used in the unit tests
298 if (graph.size() == 1 && keys.size() == 2) {
299 MetisResult result;
300 result.A.push_back(keys.front());
301 result.B.push_back(keys.back());
302 return result;
303 }
304
305 // create a metis graph
306 size_t numKeys = keys.size();
307 if (verbose) std::cout << graph.size() << " factors,\t" << numKeys << " nodes;\t" << std::endl;
308 sharedInts xadj, adjncy, adjwgt;
309 prepareMetisGraph<GenericGraph>(graph, keys, workspace, &xadj, &adjncy, &adjwgt);
310
311 // run metis on the graph
312 int edgecut;
313 sharedInts part;
314 boost::tie(edgecut, part) = edgeMetis(numKeys, xadj, adjncy, adjwgt, verbose);
315
316 // convert the 0-1-2 from Metis to 1-2-0, so that the separator is 0, as later we will have more submaps
317 MetisResult result;
318 result.A.reserve(numKeys);
319 result.B.reserve(numKeys);
320 int* ptr_part = part.get();
321 std::vector<size_t>::const_iterator itKey = keys.begin();
322 std::vector<size_t>::const_iterator itKeyLast = keys.end();
323 while(itKey != itKeyLast) {
324 if (*ptr_part != 0 && *ptr_part != 1)
325 std::cout << *ptr_part << "!!!" << std::endl;
326 switch(*(ptr_part++)) {
327 case 0: result.A.push_back(*(itKey++)); break;
328 case 1: result.B.push_back(*(itKey++)); break;
329 default: throw std::runtime_error("edgePartitionByMetis: invalid results from Metis ND!");
330 }
331 }
332
333 if (verbose) {
334 std::cout << "the size of two submaps in the reduced graph: " << result.A.size()
335 << " " << result.B.size() << std::endl;
336 int edgeCut = 0;
337
338 for(const typename GenericGraph::value_type& factor: graph){
339 int key1 = factor->key1.index;
340 int key2 = factor->key2.index;
341 // print keys and their subgraph assignment
342 std::cout << key1;
343 if (std::find(result.A.begin(), result.A.end(), key1) != result.A.end()) std::cout <<"A ";
344 if (std::find(result.B.begin(), result.B.end(), key1) != result.B.end()) std::cout <<"B ";
345
346 std::cout << key2;
347 if (std::find(result.A.begin(), result.A.end(), key2) != result.A.end()) std::cout <<"A ";
348 if (std::find(result.B.begin(), result.B.end(), key2) != result.B.end()) std::cout <<"B ";
349 std::cout << "weight " << factor->weight;;
350
351 // find vertices that were assigned to sets A & B. Their edge will be cut
352 if ((std::find(result.A.begin(), result.A.end(), key1) != result.A.end() &&
353 std::find(result.B.begin(), result.B.end(), key2) != result.B.end()) ||
354 (std::find(result.B.begin(), result.B.end(), key1) != result.B.end() &&
355 std::find(result.A.begin(), result.A.end(), key2) != result.A.end())){
356 edgeCut ++;
357 std::cout << " CUT ";
358 }
359 std::cout << std::endl;
360 }
361 std::cout << "edgeCut: " << edgeCut << std::endl;
362 }
363
364 return result;
365 }
366
367 /* ************************************************************************* */
368 bool isLargerIsland(const std::vector<size_t>& island1, const std::vector<size_t>& island2) {
369 return island1.size() > island2.size();
370 }
371
372 /* ************************************************************************* */
373 // debug functions
374 void printIsland(const std::vector<size_t>& island) {
375 std::cout << "island: ";
376 for(const size_t key: island)
377 std::cout << key << " ";
378 std::cout << std::endl;
379 }
380
381 void printIslands(const std::list<std::vector<size_t> >& islands) {
382 for(const std::vector<std::size_t>& island: islands)
383 printIsland(island);
384 }
385
386 void printNumCamerasLandmarks(const std::vector<size_t>& keys, const std::vector<Symbol>& int2symbol) {
387 int numCamera = 0, numLandmark = 0;
388 for(const size_t key: keys)
389 if (int2symbol[key].chr() == 'x')
390 numCamera++;
391 else
392 numLandmark++;
393 std::cout << "numCamera: " << numCamera << " numLandmark: " << numLandmark << std::endl;
394 }
395
396 /* ************************************************************************* */
397 template<class GenericGraph>
398 void addLandmarkToPartitionResult(const GenericGraph& graph, const std::vector<size_t>& landmarkKeys,
399 MetisResult& partitionResult, WorkSpace& workspace) {
400
401 // set up cameras in the dictionary
402 std::vector<size_t>& A = partitionResult.A;
403 std::vector<size_t>& B = partitionResult.B;
404 std::vector<size_t>& C = partitionResult.C;
405 std::vector<int>& dictionary = workspace.dictionary;
406 std::fill(dictionary.begin(), dictionary.end(), -1);
407 for(const size_t a: A)
408 dictionary[a] = 1;
409 for(const size_t b: B)
410 dictionary[b] = 2;
411 if (!C.empty())
412 throw std::runtime_error("addLandmarkToPartitionResult: C is not empty");
413
414 // set up landmarks
415 size_t i,j;
416 for(const typename GenericGraph::value_type& factor: graph) {
417 i = factor->key1.index;
418 j = factor->key2.index;
419 if (dictionary[j] == 0) // if the landmark is already in the separator, continue
420 continue;
421 else if (dictionary[j] == -1)
422 dictionary[j] = dictionary[i];
423 else {
424 if (dictionary[j] != dictionary[i])
425 dictionary[j] = 0;
426 }
427// if (j == 67980)
428// std::cout << "dictionary[67980]" << dictionary[j] << std::endl;
429 }
430
431 for(const size_t j: landmarkKeys) {
432 switch(dictionary[j]) {
433 case 0: C.push_back(j); break;
434 case 1: A.push_back(j); break;
435 case 2: B.push_back(j); break;
436 default: std::cout << j << ": " << dictionary[j] << std::endl;
437 throw std::runtime_error("addLandmarkToPartitionResult: wrong status for landmark");
438 }
439 }
440 }
441
442#define REDUCE_CAMERA_GRAPH
443
444 /* ************************************************************************* */
445 template<class GenericGraph>
446 boost::optional<MetisResult> findPartitoning(const GenericGraph& graph, const std::vector<size_t>& keys,
447 WorkSpace& workspace, bool verbose,
448 const boost::optional<std::vector<Symbol> >& int2symbol, const bool reduceGraph) {
449 boost::optional<MetisResult> result;
450 GenericGraph reducedGraph;
451 std::vector<size_t> keyToPartition;
452 std::vector<size_t> cameraKeys, landmarkKeys;
453 if (reduceGraph) {
454 if (!int2symbol.is_initialized())
455 throw std::invalid_argument("findSeparator: int2symbol must be valid!");
456
457 // find out all the landmark keys, which are to be eliminated
458 cameraKeys.reserve(keys.size());
459 landmarkKeys.reserve(keys.size());
460 for(const size_t key: keys) {
461 if((*int2symbol)[key].chr() == 'x')
462 cameraKeys.push_back(key);
463 else
464 landmarkKeys.push_back(key);
465 }
466
467 keyToPartition = cameraKeys;
468 workspace.prepareDictionary(keyToPartition);
469 const std::vector<int>& dictionary = workspace.dictionary;
470 reduceGenericGraph(graph, cameraKeys, landmarkKeys, dictionary, reducedGraph);
471 std::cout << "original graph: V" << keys.size() << ", E" << graph.size()
472 << " --> reduced graph: V" << cameraKeys.size() << ", E" << reducedGraph.size() << std::endl;
473 result = edgePartitionByMetis(reducedGraph, keyToPartition, workspace, verbose);
474 } else // call Metis to partition the graph to A, B, C
475 result = separatorPartitionByMetis(graph, keys, workspace, verbose);
476
477 if (!result.is_initialized()) {
478 std::cout << "metis failed!" << std::endl;
479 return boost::none;
480 }
481
482 if (reduceGraph) {
483 addLandmarkToPartitionResult(graph, landmarkKeys, *result, workspace);
484 std::cout << "the separator size: " << result->C.size() << " landmarks" << std::endl;
485 }
486
487 return result;
488 }
489
490 /* ************************************************************************* */
491 template<class GenericGraph>
492 int findSeparator(const GenericGraph& graph, const std::vector<size_t>& keys,
493 const int minNodesPerMap, WorkSpace& workspace, bool verbose,
494 const boost::optional<std::vector<Symbol> >& int2symbol, const bool reduceGraph,
495 const int minNrConstraintsPerCamera, const int minNrConstraintsPerLandmark) {
496
497 boost::optional<MetisResult> result = findPartitoning(graph, keys, workspace,
498 verbose, int2symbol, reduceGraph);
499
500 // find the island in A and B, and make them separated submaps
501 typedef std::vector<size_t> Island;
502 std::list<Island> islands;
503
504 std::list<Island> islands_in_A = findIslands(graph, result->A, workspace,
505 minNrConstraintsPerCamera, minNrConstraintsPerLandmark);
506
507 std::list<Island> islands_in_B = findIslands(graph, result->B, workspace,
508 minNrConstraintsPerCamera, minNrConstraintsPerLandmark);
509
510 islands.insert(islands.end(), islands_in_A.begin(), islands_in_A.end());
511 islands.insert(islands.end(), islands_in_B.begin(), islands_in_B.end());
512 islands.sort(isLargerIsland);
513 size_t numIsland0 = islands.size();
514
515#ifdef NDEBUG
516// verbose = true;
517// if (!int2symbol) throw std::invalid_argument("findSeparator: int2symbol is not set!");
518// std::cout << "sep size: " << result->C.size() << "; ";
519// printNumCamerasLandmarks(result->C, *int2symbol);
520// std::cout << "no. of island: " << islands.size() << "; ";
521// std::cout << "island size: ";
522// for(const Island& island: islands)
523// std::cout << island.size() << " ";
524// std::cout << std::endl;
525
526// for(const Island& island: islands) {
527// printNumCamerasLandmarks(island, int2symbol);
528// }
529#endif
530
531 // absorb small components into the separator
532 size_t oldSize = islands.size();
533 while(true) {
534 if (islands.size() < 2) {
535 std::cout << "numIsland: " << numIsland0 << std::endl;
536 throw std::runtime_error("findSeparator: found fewer than 2 submaps!");
537 }
538
539 std::list<Island>::reference island = islands.back();
540 if ((int)island.size() >= minNodesPerMap) break;
541 result->C.insert(result->C.end(), island.begin(), island.end());
542 islands.pop_back();
543 }
544 if (islands.size() != oldSize){
545 if (verbose) std::cout << oldSize << "-" << oldSize - islands.size() << " submap(s);\t" << std::endl;
546 }
547 else{
548 if (verbose) std::cout << oldSize << " submap(s);\t" << std::endl;
549 }
550
551 // generate the node map
552 std::vector<int>& partitionTable = workspace.partitionTable;
553 std::fill(partitionTable.begin(), partitionTable.end(), -1);
554 for(const size_t key: result->C)
555 partitionTable[key] = 0;
556 int idx = 0;
557 for(const Island& island: islands) {
558 idx++;
559 for(const size_t key: island) {
560 partitionTable[key] = idx;
561 }
562 }
563
564 return islands.size();
565 }
566
567}} //namespace
Timing utilities.
Global functions in a separate testing namespace.
Definition chartTesting.h:28
the metis Nest dissection result
Definition FindSeparator.h:24
Definition PartitionWorkSpace.h:19