19#include <boost/shared_array.hpp>
23#include "FindSeparator.h"
27namespace gtsam {
namespace partition {
29 typedef boost::shared_array<idx_t> sharedInts;
38 std::pair<idx_t, sharedInts> separatorMetis(idx_t n,
const sharedInts& xadj,
39 const sharedInts& adjncy,
const sharedInts& adjwgt,
bool verbose) {
42 std::vector<idx_t> vwgt;
43 idx_t options[METIS_NOPTIONS];
44 METIS_SetDefaultOptions(options);
46 sharedInts part_(
new idx_t[n]);
54 printf(
"**********************************************************************\n");
55 printf(
"Graph Information ---------------------------------------------------\n");
56 printf(
" #Vertices: %d, #Edges: %u\n", n, *(xadj.get()+n) / 2);
57 printf(
"\nND Partitioning... -------------------------------------------\n");
62 METIS_ComputeVertexSeparator(&n, xadj.get(), adjncy.get(),
63 &vwgt[0], options, &sepsize, part_.get());
69 printf(
" Sep size: \t\t %d\n", sepsize);
70 printf(
"**********************************************************************\n");
73 return std::make_pair(sepsize, part_);
82 std::pair<int, sharedInts> edgeMetis(idx_t n,
const sharedInts& xadj,
const sharedInts& adjncy,
83 const sharedInts& adjwgt,
bool verbose) {
86 std::vector<idx_t> vwgt;
87 idx_t options[METIS_NOPTIONS];
88 METIS_SetDefaultOptions(options);
89 options[METIS_OPTION_IPTYPE] = METIS_IPTYPE_GROW;
90 options[METIS_OPTION_NCUTS] = 1;
91 options[METIS_OPTION_UFACTOR] = 200;
93 sharedInts part_(
new idx_t[n]);
101 printf(
"**********************************************************************\n");
102 printf(
"Graph Information ---------------------------------------------------\n");
103 printf(
" #Vertices: %d, #Edges: %u\n", n, *(xadj.get()+n) / 2);
104 printf(
"\nND Partitioning... -------------------------------------------\n");
113 static_cast<void>(adjwgt);
114 const int status = METIS_PartGraphRecursive(
115 &n, &ncon, xadj.get(), adjncy.get(), &vwgt[0],
nullptr,
nullptr,
116 &nparts,
nullptr,
nullptr, options, &edgecut, part_.get());
117 if (status != METIS_OK) {
118 throw std::runtime_error(
119 "METIS_PartGraphRecursive failed with error code " +
120 std::to_string(status) +
121 "; check the graph inputs and METIS installation");
123 std::cout <<
"Finished bisection:" << edgecut << std::endl;
127 printf(
"\nTiming Information --------------------------------------------------\n");
129 printf(
" Edge cuts: \t\t %d\n", edgecut);
130 printf(
"**********************************************************************\n");
133 return std::make_pair(edgecut, part_);
142 template <
class GenericGraph>
143 void prepareMetisGraph(
const GenericGraph& graph,
const std::vector<size_t>& keys,
WorkSpace& workspace,
144 sharedInts* ptr_xadj, sharedInts* ptr_adjncy, sharedInts* ptr_adjwgt) {
146 typedef std::vector<int> Weights;
147 typedef std::vector<int> Neighbors;
148 typedef std::pair<Neighbors, Weights> NeighborsInfo;
151 std::vector<int>& dictionary = workspace.dictionary;
152 workspace.prepareDictionary(keys);
155 int numNodes = keys.size();
157 std::vector<NeighborsInfo> adjacencyMap;
158 adjacencyMap.resize(numNodes);
159 std::cout <<
"Number of nodes: " << adjacencyMap.size() << std::endl;
162 for(
const typename GenericGraph::value_type& factor: graph){
163 index1 = dictionary[factor->key1.index];
164 index2 = dictionary[factor->key2.index];
165 std::cout <<
"index1: " << index1 << std::endl;
166 std::cout <<
"index2: " << index2 << std::endl;
168 if (index1 >= 0 && index2 >= 0) {
169 std::pair<Neighbors, Weights>& adjacencyMap1 = adjacencyMap[index1];
170 std::pair<Neighbors, Weights>& adjacencyMap2 = adjacencyMap[index2];
172 adjacencyMap1.first.push_back(index2);
173 adjacencyMap1.second.push_back(factor->weight);
174 adjacencyMap2.first.push_back(index1);
175 adjacencyMap2.second.push_back(factor->weight);
176 }
catch(std::exception& e){
177 std::cout << e.what() << std::endl;
184 *ptr_xadj = sharedInts(
new idx_t[numNodes+1]);
185 *ptr_adjncy = sharedInts(
new idx_t[numEdges*2]);
186 *ptr_adjwgt = sharedInts(
new idx_t[numEdges*2]);
187 sharedInts& xadj = *ptr_xadj;
188 sharedInts& adjncy = *ptr_adjncy;
189 sharedInts& adjwgt = *ptr_adjwgt;
190 int ind_xadj = 0, ind_adjncy = 0;
191 for(
const NeighborsInfo& info: adjacencyMap) {
192 *(xadj.get() + ind_xadj) = ind_adjncy;
193 std::copy(info.first .begin(), info.first .end(), adjncy.get() + ind_adjncy);
194 std::copy(info.second.begin(), info.second.end(), adjwgt.get() + ind_adjncy);
195 assert(info.first.size() == info.second.size());
196 ind_adjncy += info.first.size();
199 if (ind_xadj != numNodes)
throw std::runtime_error(
"prepareMetisGraph_: ind_xadj != numNodes");
200 *(xadj.get() + ind_xadj) = ind_adjncy;
204 template<
class GenericGraph>
205 std::optional<MetisResult> separatorPartitionByMetis(
const GenericGraph& graph,
206 const std::vector<size_t>& keys,
WorkSpace& workspace,
bool verbose) {
208 size_t numKeys = keys.size();
210 std::cout << graph.size() <<
" factors,\t" << numKeys <<
" nodes;\t" << std::endl;
212 sharedInts xadj, adjncy, adjwgt;
214 prepareMetisGraph<GenericGraph>(graph, keys, workspace, &xadj, &adjncy, &adjwgt);
217 const auto [sepsize, part] = separatorMetis(numKeys, xadj, adjncy, adjwgt, verbose);
218 if (!sepsize)
return std::optional<MetisResult>();
223 result.C.reserve(sepsize);
224 result.A.reserve(numKeys - sepsize);
225 result.B.reserve(numKeys - sepsize);
226 int* ptr_part = part.get();
227 std::vector<size_t>::const_iterator itKey = keys.begin();
228 std::vector<size_t>::const_iterator itKeyLast = keys.end();
229 while(itKey != itKeyLast) {
230 switch(*(ptr_part++)) {
231 case 0: result.A.push_back(*(itKey++));
break;
232 case 1: result.B.push_back(*(itKey++));
break;
233 case 2: result.C.push_back(*(itKey++));
break;
234 default:
throw std::runtime_error(
"separatorPartitionByMetis: invalid results from Metis ND!");
239 std::cout <<
"total key: " << keys.size()
240 <<
" result(A,B,C) = " << result.A.size() <<
", " << result.B.size() <<
", "
241 << result.C.size() <<
"; sepsize from Metis = " << sepsize << std::endl;
245 if(result.C.size() !=
size_t(sepsize)) {
246 std::cout <<
"total key: " << keys.size()
247 <<
" result(A,B,C) = " << result.A.size() <<
", " << result.B.size() <<
", " << result.C.size()
248 <<
"; sepsize from Metis = " << sepsize << std::endl;
249 throw std::runtime_error(
"separatorPartitionByMetis: invalid sepsize from Metis ND!");
256 template<
class GenericGraph>
257 std::optional<MetisResult> edgePartitionByMetis(
const GenericGraph& graph,
258 const std::vector<size_t>& keys,
WorkSpace& workspace,
bool verbose) {
261 if (graph.size() == 1 && keys.size() == 2) {
263 result.A.push_back(keys.front());
264 result.B.push_back(keys.back());
269 size_t numKeys = keys.size();
270 if (verbose) std::cout << graph.size() <<
" factors,\t" << numKeys <<
" nodes;\t" << std::endl;
271 sharedInts xadj, adjncy, adjwgt;
272 prepareMetisGraph<GenericGraph>(graph, keys, workspace, &xadj, &adjncy, &adjwgt);
275 const auto [edgecut, part] = edgeMetis(numKeys, xadj, adjncy, adjwgt, verbose);
279 result.A.reserve(numKeys);
280 result.B.reserve(numKeys);
281 int* ptr_part = part.get();
282 std::vector<size_t>::const_iterator itKey = keys.begin();
283 std::vector<size_t>::const_iterator itKeyLast = keys.end();
284 while(itKey != itKeyLast) {
285 if (*ptr_part != 0 && *ptr_part != 1)
286 std::cout << *ptr_part <<
"!!!" << std::endl;
287 switch(*(ptr_part++)) {
288 case 0: result.A.push_back(*(itKey++));
break;
289 case 1: result.B.push_back(*(itKey++));
break;
290 default:
throw std::runtime_error(
"edgePartitionByMetis: invalid results from Metis ND!");
295 std::cout <<
"the size of two submaps in the reduced graph: " << result.A.size()
296 <<
" " << result.B.size() << std::endl;
299 for(
const typename GenericGraph::value_type& factor: graph){
300 int key1 = factor->key1.index;
301 int key2 = factor->key2.index;
304 if (std::find(result.A.begin(), result.A.end(), key1) != result.A.end()) std::cout <<
"A ";
305 if (std::find(result.B.begin(), result.B.end(), key1) != result.B.end()) std::cout <<
"B ";
308 if (std::find(result.A.begin(), result.A.end(), key2) != result.A.end()) std::cout <<
"A ";
309 if (std::find(result.B.begin(), result.B.end(), key2) != result.B.end()) std::cout <<
"B ";
310 std::cout <<
"weight " << factor->weight;;
313 if ((std::find(result.A.begin(), result.A.end(), key1) != result.A.end() &&
314 std::find(result.B.begin(), result.B.end(), key2) != result.B.end()) ||
315 (std::find(result.B.begin(), result.B.end(), key1) != result.B.end() &&
316 std::find(result.A.begin(), result.A.end(), key2) != result.A.end())){
318 std::cout <<
" CUT ";
320 std::cout << std::endl;
322 std::cout <<
"edgeCut: " << edgeCut << std::endl;
329 bool isLargerIsland(
const std::vector<size_t>& island1,
const std::vector<size_t>& island2) {
330 return island1.size() > island2.size();
335 void printIsland(
const std::vector<size_t>& island) {
336 std::cout <<
"island: ";
337 for(
const size_t key: island)
338 std::cout << key <<
" ";
339 std::cout << std::endl;
342 void printIslands(
const std::list<std::vector<size_t> >& islands) {
343 for(
const std::vector<std::size_t>& island: islands)
347 void printNumCamerasLandmarks(
const std::vector<size_t>& keys,
const std::vector<Symbol>& int2symbol) {
348 int numCamera = 0, numLandmark = 0;
349 for(
const size_t key: keys)
350 if (int2symbol[key].chr() ==
'x')
354 std::cout <<
"numCamera: " << numCamera <<
" numLandmark: " << numLandmark << std::endl;
358 template<
class GenericGraph>
359 void addLandmarkToPartitionResult(
const GenericGraph& graph,
const std::vector<size_t>& landmarkKeys,
363 std::vector<size_t>& A = partitionResult.A;
364 std::vector<size_t>& B = partitionResult.B;
365 std::vector<size_t>& C = partitionResult.C;
366 std::vector<int>& dictionary = workspace.dictionary;
367 std::fill(dictionary.begin(), dictionary.end(), -1);
368 for(
const size_t a: A)
370 for(
const size_t b: B)
373 throw std::runtime_error(
"addLandmarkToPartitionResult: C is not empty");
377 for(
const typename GenericGraph::value_type& factor: graph) {
378 i = factor->key1.index;
379 j = factor->key2.index;
380 if (dictionary[j] == 0)
382 else if (dictionary[j] == -1)
383 dictionary[j] = dictionary[i];
385 if (dictionary[j] != dictionary[i])
392 for(
const size_t j: landmarkKeys) {
393 switch(dictionary[j]) {
394 case 0: C.push_back(j);
break;
395 case 1: A.push_back(j);
break;
396 case 2: B.push_back(j);
break;
397 default: std::cout << j <<
": " << dictionary[j] << std::endl;
398 throw std::runtime_error(
"addLandmarkToPartitionResult: wrong status for landmark");
403#define REDUCE_CAMERA_GRAPH
406 template<
class GenericGraph>
407 std::optional<MetisResult> findPartitoning(
const GenericGraph& graph,
const std::vector<size_t>& keys,
409 const std::optional<std::vector<Symbol> >& int2symbol,
const bool reduceGraph) {
410 std::optional<MetisResult> result;
411 GenericGraph reducedGraph;
412 std::vector<size_t> keyToPartition;
413 std::vector<size_t> cameraKeys, landmarkKeys;
415 if (!int2symbol.has_value())
416 throw std::invalid_argument(
"findSeparator: int2symbol must be valid!");
419 cameraKeys.reserve(keys.size());
420 landmarkKeys.reserve(keys.size());
421 for(
const size_t key: keys) {
422 if((*int2symbol)[key].chr() ==
'x')
423 cameraKeys.push_back(key);
425 landmarkKeys.push_back(key);
428 keyToPartition = cameraKeys;
429 workspace.prepareDictionary(keyToPartition);
430 const std::vector<int>& dictionary = workspace.dictionary;
431 reduceGenericGraph(graph, cameraKeys, landmarkKeys, dictionary, reducedGraph);
432 std::cout <<
"original graph: V" << keys.size() <<
", E" << graph.size()
433 <<
" --> reduced graph: V" << cameraKeys.size() <<
", E" << reducedGraph.size() << std::endl;
434 result = edgePartitionByMetis(reducedGraph, keyToPartition, workspace, verbose);
436 result = separatorPartitionByMetis(graph, keys, workspace, verbose);
438 if (!result.has_value()) {
439 std::cout <<
"metis failed!" << std::endl;
444 addLandmarkToPartitionResult(graph, landmarkKeys, *result, workspace);
445 std::cout <<
"the separator size: " << result->C.size() <<
" landmarks" << std::endl;
452 template<
class GenericGraph>
453 int findSeparator(
const GenericGraph& graph,
const std::vector<size_t>& keys,
454 const int minNodesPerMap,
WorkSpace& workspace,
bool verbose,
455 const std::optional<std::vector<Symbol> >& int2symbol,
const bool reduceGraph,
456 const int minNrConstraintsPerCamera,
const int minNrConstraintsPerLandmark) {
458 std::optional<MetisResult> result = findPartitoning(graph, keys, workspace,
459 verbose, int2symbol, reduceGraph);
462 typedef std::vector<size_t> Island;
463 std::list<Island> islands;
465 std::list<Island> islands_in_A = findIslands(graph, result->A, workspace,
466 minNrConstraintsPerCamera, minNrConstraintsPerLandmark);
468 std::list<Island> islands_in_B = findIslands(graph, result->B, workspace,
469 minNrConstraintsPerCamera, minNrConstraintsPerLandmark);
471 islands.insert(islands.end(), islands_in_A.begin(), islands_in_A.end());
472 islands.insert(islands.end(), islands_in_B.begin(), islands_in_B.end());
473 islands.sort(isLargerIsland);
474 size_t numIsland0 = islands.size();
493 size_t oldSize = islands.size();
495 if (islands.size() < 2) {
496 std::cout <<
"numIsland: " << numIsland0 << std::endl;
497 throw std::runtime_error(
"findSeparator: found fewer than 2 submaps!");
500 std::list<Island>::reference island = islands.back();
501 if ((
int)island.size() >= minNodesPerMap)
break;
502 result->C.insert(result->C.end(), island.begin(), island.end());
505 if (islands.size() != oldSize){
506 if (verbose) std::cout << oldSize <<
"-" << oldSize - islands.size() <<
" submap(s);\t" << std::endl;
509 if (verbose) std::cout << oldSize <<
" submap(s);\t" << std::endl;
513 std::vector<int>& partitionTable = workspace.partitionTable;
514 std::fill(partitionTable.begin(), partitionTable.end(), -1);
515 for(
const size_t key: result->C)
516 partitionTable[key] = 0;
518 for(
const Island& island: islands) {
520 for(
const size_t key: island) {
521 partitionTable[key] = idx;
525 return islands.size();
Global functions in a separate testing namespace.
Definition chartTesting.h:28
the metis Nest dissection result
Definition FindSeparator.h:23
Definition PartitionWorkSpace.h:19