SubgraphBuilder.cpp
Go to the documentation of this file.
1 /* ----------------------------------------------------------------------------
2 
3  * GTSAM Copyright 2010-2019, Georgia Tech Research Corporation,
4  * Atlanta, Georgia 30332-0415
5  * All Rights Reserved
6  * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7 
8  * See LICENSE for the license information
9 
10  * -------------------------------------------------------------------------- */
11 
18 #include <gtsam/base/DSFVector.h>
19 #include <gtsam/base/FastMap.h>
23 #include <gtsam/linear/Errors.h>
26 
27 #include <boost/algorithm/string.hpp>
28 #include <boost/archive/text_iarchive.hpp>
29 #include <boost/archive/text_oarchive.hpp>
30 #include <boost/serialization/vector.hpp>
31 
32 #include <algorithm>
33 #include <cmath>
34 #include <fstream>
35 #include <iomanip>
36 #include <iostream>
37 #include <numeric> // accumulate
38 #include <queue>
39 #include <random>
40 #include <set>
41 #include <stdexcept>
42 #include <string>
43 #include <utility>
44 #include <vector>
45 
46 using std::cout;
47 using std::endl;
48 using std::ostream;
49 using std::vector;
50 
51 namespace gtsam {
52 
53 /*****************************************************************************/
54 /* sort the container and return permutation index with default comparator */
55 template <typename Container>
56 static vector<size_t> sort_idx(const Container &src) {
57  typedef typename Container::value_type T;
58  const size_t n = src.size();
59  vector<std::pair<size_t, T> > tmp;
60  tmp.reserve(n);
61  for (size_t i = 0; i < n; i++) tmp.emplace_back(i, src[i]);
62 
63  /* sort */
64  std::stable_sort(tmp.begin(), tmp.end());
65 
66  /* copy back */
67  vector<size_t> idx;
68  idx.reserve(n);
69  for (size_t i = 0; i < n; i++) {
70  idx.push_back(tmp[i].first);
71  }
72  return idx;
73 }
74 
75 /****************************************************************************/
76 Subgraph::Subgraph(const vector<size_t> &indices) {
77  edges_.reserve(indices.size());
78  for (const size_t &index : indices) {
79  const Edge e{index, 1.0};
80  edges_.push_back(e);
81  }
82 }
83 
84 /****************************************************************************/
85 vector<size_t> Subgraph::edgeIndices() const {
86  vector<size_t> eid;
87  eid.reserve(size());
88  for (const Edge &edge : edges_) {
89  eid.push_back(edge.index);
90  }
91  return eid;
92 }
93 
94 /****************************************************************************/
95 void Subgraph::save(const std::string &fn) const {
96  std::ofstream os(fn.c_str());
97  boost::archive::text_oarchive oa(os);
98  oa << *this;
99  os.close();
100 }
101 
102 /****************************************************************************/
103 Subgraph Subgraph::load(const std::string &fn) {
104  std::ifstream is(fn.c_str());
105  boost::archive::text_iarchive ia(is);
106  Subgraph subgraph;
107  ia >> subgraph;
108  is.close();
109  return subgraph;
110 }
111 
112 /****************************************************************************/
113 ostream &operator<<(ostream &os, const Subgraph::Edge &edge) {
114  if (edge.weight != 1.0)
115  os << edge.index << "(" << std::setprecision(2) << edge.weight << ")";
116  else
117  os << edge.index;
118  return os;
119 }
120 
121 /****************************************************************************/
122 ostream &operator<<(ostream &os, const Subgraph &subgraph) {
123  os << "Subgraph" << endl;
124  for (const auto &e : subgraph.edges()) {
125  os << e << ", ";
126  }
127  return os;
128 }
129 
130 /*****************************************************************************/
132 
133 /***************************************************************************************/
134 void SubgraphBuilderParameters::print(ostream &os) const {
135  os << "SubgraphBuilderParameters" << endl
136  << "skeleton: " << skeletonTranslator(skeletonType) << endl
137  << "skeleton weight: " << skeletonWeightTranslator(skeletonWeight)
138  << endl
139  << "augmentation weight: "
140  << augmentationWeightTranslator(augmentationWeight) << endl;
141 }
142 
143 /*****************************************************************************/
144 ostream &operator<<(ostream &os, const SubgraphBuilderParameters &p) {
145  p.print(os);
146  return os;
147 }
148 
149 /*****************************************************************************/
152  std::string s = src;
153  boost::algorithm::to_upper(s);
154  if (s == "NATURALCHAIN")
155  return NATURALCHAIN;
156  else if (s == "BFS")
157  return BFS;
158  else if (s == "KRUSKAL")
159  return KRUSKAL;
160  throw std::invalid_argument(
161  "SubgraphBuilderParameters::skeletonTranslator undefined string " + s);
162  return KRUSKAL;
163 }
164 
165 /****************************************************************/
167  if (s == NATURALCHAIN)
168  return "NATURALCHAIN";
169  else if (s == BFS)
170  return "BFS";
171  else if (s == KRUSKAL)
172  return "KRUSKAL";
173  else
174  return "UNKNOWN";
175 }
176 
177 /****************************************************************/
180  std::string s = src;
181  boost::algorithm::to_upper(s);
182  if (s == "EQUAL")
183  return EQUAL;
184  else if (s == "RHS")
185  return RHS_2NORM;
186  else if (s == "LHS")
187  return LHS_FNORM;
188  else if (s == "RANDOM")
189  return RANDOM;
190  throw std::invalid_argument(
191  "SubgraphBuilderParameters::skeletonWeightTranslator undefined string " +
192  s);
193  return EQUAL;
194 }
195 
196 /****************************************************************/
198  SkeletonWeight w) {
199  if (w == EQUAL)
200  return "EQUAL";
201  else if (w == RHS_2NORM)
202  return "RHS";
203  else if (w == LHS_FNORM)
204  return "LHS";
205  else if (w == RANDOM)
206  return "RANDOM";
207  else
208  return "UNKNOWN";
209 }
210 
211 /****************************************************************/
214  const std::string &src) {
215  std::string s = src;
216  boost::algorithm::to_upper(s);
217  if (s == "SKELETON") return SKELETON;
218  // else if (s == "STRETCH") return STRETCH;
219  // else if (s == "GENERALIZED_STRETCH") return GENERALIZED_STRETCH;
220  throw std::invalid_argument(
221  "SubgraphBuilder::Parameters::augmentationWeightTranslator undefined "
222  "string " +
223  s);
224  return SKELETON;
225 }
226 
227 /****************************************************************/
230  if (w == SKELETON) return "SKELETON";
231  // else if ( w == STRETCH ) return "STRETCH";
232  // else if ( w == GENERALIZED_STRETCH ) return "GENERALIZED_STRETCH";
233  else
234  return "UNKNOWN";
235 }
236 
237 /****************************************************************/
240  const vector<double> &weights) const {
241  const SubgraphBuilderParameters &p = parameters_;
242  switch (p.skeletonType) {
244  return natural_chain(gfg);
245  break;
247  return bfs(gfg);
248  break;
250  return kruskal(gfg, ordering, weights);
251  break;
252  default:
253  std::cerr << "SubgraphBuilder::buildTree undefined skeleton type" << endl;
254  break;
255  }
256  return vector<size_t>();
257 }
258 
259 /****************************************************************/
260 vector<size_t> SubgraphBuilder::unary(const GaussianFactorGraph &gfg) const {
261  vector<size_t> unaryFactorIndices;
262  size_t index = 0;
263  for (const auto &factor : gfg) {
264  if (factor->size() == 1) {
265  unaryFactorIndices.push_back(index);
266  }
267  index++;
268  }
269  return unaryFactorIndices;
270 }
271 
272 /****************************************************************/
274  const GaussianFactorGraph &gfg) const {
275  vector<size_t> chainFactorIndices;
276  size_t index = 0;
277  for (const GaussianFactor::shared_ptr &gf : gfg) {
278  if (gf->size() == 2) {
279  const Key k0 = gf->keys()[0], k1 = gf->keys()[1];
280  if ((k1 - k0) == 1 || (k0 - k1) == 1) chainFactorIndices.push_back(index);
281  }
282  index++;
283  }
284  return chainFactorIndices;
285 }
286 
287 /****************************************************************/
288 vector<size_t> SubgraphBuilder::bfs(const GaussianFactorGraph &gfg) const {
289  const VariableIndex variableIndex(gfg);
290  /* start from the first key of the first factor */
291  Key seed = gfg[0]->keys()[0];
292 
293  const size_t n = variableIndex.size();
294 
295  /* each vertex has self as the predecessor */
296  vector<size_t> bfsFactorIndices;
297  bfsFactorIndices.reserve(n - 1);
298 
299  /* Initialize */
300  std::queue<size_t> q;
301  q.push(seed);
302 
303  std::set<size_t> flags;
304  flags.insert(seed);
305 
306  /* traversal */
307  while (!q.empty()) {
308  const size_t head = q.front();
309  q.pop();
310  for (const size_t index : variableIndex[head]) {
311  const GaussianFactor &gf = *gfg[index];
312  for (const size_t key : gf.keys()) {
313  if (flags.count(key) == 0) {
314  q.push(key);
315  flags.insert(key);
316  bfsFactorIndices.push_back(index);
317  }
318  }
319  }
320  }
321  return bfsFactorIndices;
322 }
323 
324 /****************************************************************/
327  const vector<double> &weights) const {
328  const VariableIndex variableIndex(gfg);
329  const size_t n = variableIndex.size();
330  const vector<size_t> sortedIndices = sort_idx(weights);
331 
332  /* initialize buffer */
333  vector<size_t> treeIndices;
334  treeIndices.reserve(n - 1);
335 
336  // container for acsendingly sorted edges
337  DSFVector dsf(n);
338 
339  size_t count = 0;
340  double sum = 0.0;
341  for (const size_t index : sortedIndices) {
342  const GaussianFactor &gf = *gfg[index];
343  const auto keys = gf.keys();
344  if (keys.size() != 2) continue;
345  const size_t u = ordering.find(keys[0])->second,
346  v = ordering.find(keys[1])->second;
347  if (dsf.find(u) != dsf.find(v)) {
348  dsf.merge(u, v);
349  treeIndices.push_back(index);
350  sum += weights[index];
351  if (++count == n - 1) break;
352  }
353  }
354  return treeIndices;
355 }
356 
357 /****************************************************************/
358 vector<size_t> SubgraphBuilder::sample(const vector<double> &weights,
359  const size_t t) const {
360  std::mt19937 rng(42); // TODO(frank): allow us to use a different seed
361  WeightedSampler<std::mt19937> sampler(&rng);
362  return sampler.sampleWithoutReplacement(t, weights);
363 }
364 
365 /****************************************************************/
367  const auto &p = parameters_;
368  const auto inverse_ordering = Ordering::Natural(gfg);
369  const FastMap<Key, size_t> forward_ordering = inverse_ordering.invert();
370  const size_t n = inverse_ordering.size(), m = gfg.size();
371 
372  // Make sure the subgraph preconditioner does not include more than half of
373  // the edges beyond the spanning tree, or we might as well solve the whole
374  // thing.
375  size_t numExtraEdges = n * p.augmentationFactor;
376  const size_t numRemaining = m - (n - 1);
377  numExtraEdges = std::min(numExtraEdges, numRemaining / 2);
378 
379  // Calculate weights
380  vector<double> weights = this->weights(gfg);
381 
382  // Build spanning tree.
383  const vector<size_t> tree = buildTree(gfg, forward_ordering, weights);
384  if (tree.size() != n - 1) {
385  throw std::runtime_error(
386  "SubgraphBuilder::operator() failure: tree.size() != n-1, might be caused by disconnected graph");
387  }
388 
389  // Downweight the tree edges to zero.
390  for (const size_t index : tree) {
391  weights[index] = 0.0;
392  }
393 
394  /* decide how many edges to augment */
395  vector<size_t> offTree = sample(weights, numExtraEdges);
396 
397  vector<size_t> subgraph = unary(gfg);
398  subgraph.insert(subgraph.end(), tree.begin(), tree.end());
399  subgraph.insert(subgraph.end(), offTree.begin(), offTree.end());
400 
401  return Subgraph(subgraph);
402 }
403 
404 /****************************************************************/
406  const GaussianFactorGraph &gfg) const {
407  const size_t m = gfg.size();
408  Weights weight;
409  weight.reserve(m);
410 
411  for (const GaussianFactor::shared_ptr &gf : gfg) {
412  switch (parameters_.skeletonWeight) {
414  weight.push_back(1.0);
415  break;
418  boost::dynamic_pointer_cast<JacobianFactor>(gf)) {
419  weight.push_back(jf->getb().norm());
420  } else if (HessianFactor::shared_ptr hf =
421  boost::dynamic_pointer_cast<HessianFactor>(gf)) {
422  weight.push_back(hf->linearTerm().norm());
423  }
424  } break;
427  boost::dynamic_pointer_cast<JacobianFactor>(gf)) {
428  weight.push_back(std::sqrt(jf->getA().squaredNorm()));
429  } else if (HessianFactor::shared_ptr hf =
430  boost::dynamic_pointer_cast<HessianFactor>(gf)) {
431  weight.push_back(std::sqrt(hf->information().squaredNorm()));
432  }
433  } break;
434 
436  weight.push_back(std::rand() % 100 + 1.0);
437  break;
438 
439  default:
440  throw std::invalid_argument(
441  "SubgraphBuilder::weights: undefined weight scheme ");
442  break;
443  }
444  }
445  return weight;
446 }
447 
448 /*****************************************************************************/
450  const GaussianFactorGraph &gfg, const Subgraph &subgraph,
451  const bool clone) {
452  auto subgraphFactors = boost::make_shared<GaussianFactorGraph>();
453  subgraphFactors->reserve(subgraph.size());
454  for (const auto &e : subgraph) {
455  const auto factor = gfg[e.index];
456  subgraphFactors->push_back(clone ? factor->clone() : factor);
457  }
458  return subgraphFactors;
459 }
460 
461 /**************************************************************************************************/
462 std::pair<GaussianFactorGraph::shared_ptr, GaussianFactorGraph::shared_ptr> //
464  const Subgraph &subgraph) {
465  // Get the subgraph by calling cheaper method
466  auto subgraphFactors = buildFactorSubgraph(factorGraph, subgraph, false);
467 
468  // Now, copy all factors then set subGraph factors to zero
469  auto remaining = boost::make_shared<GaussianFactorGraph>(factorGraph);
470 
471  for (const auto &e : subgraph) {
472  remaining->remove(e.index);
473  }
474 
475  return std::make_pair(subgraphFactors, remaining);
476 }
477 
478 /*****************************************************************************/
479 
480 } // namespace gtsam
void print(const Matrix &A, const string &s, ostream &stream)
Definition: Matrix.cpp:155
GaussianFactorGraph::shared_ptr buildFactorSubgraph(const GaussianFactorGraph &gfg, const Subgraph &subgraph, const bool clone)
enum gtsam::SubgraphBuilderParameters::Skeleton skeletonType
Matrix3f m
size_t size() const
Definition: FactorGraph.h:306
EdgeIndices edgeIndices() const
vector of errors
static enum @843 ordering
#define min(a, b)
Definition: datatypes.h:19
boost::shared_ptr< This > shared_ptr
shared_ptr to this class
ArrayXcf v
Definition: Cwise_arg.cpp:1
static std::mt19937 rng
int n
size_t size() const
Fast sampling without replacement.
EIGEN_DEVICE_FUNC const SqrtReturnType sqrt() const
void save(const std::string &fn) const
static vector< size_t > sort_idx(const Container &src)
std::vector< size_t > sample(const std::vector< double > &weights, const size_t t) const
IsDerived< DERIVEDFACTOR > push_back(boost::shared_ptr< DERIVEDFACTOR > factor)
Add a factor directly using a shared_ptr.
Definition: FactorGraph.h:166
std::pair< GaussianFactorGraph::shared_ptr, GaussianFactorGraph::shared_ptr > splitFactorGraph(const GaussianFactorGraph &factorGraph, const Subgraph &subgraph)
size_t size() const
The number of variable entries. This is equal to the number of unique variable Keys.
Definition: VariableIndex.h:80
A faster implementation for DSF, which uses vector rather than btree.
std::vector< double > Weights
size_t find(size_t key) const
Find the label of the set in which {key} lives.
Definition: DSFVector.cpp:44
constexpr int first(int i)
Implementation details for constexpr functions.
std::vector< size_t > natural_chain(const GaussianFactorGraph &gfg) const
boost::shared_ptr< This > shared_ptr
A shared_ptr to this class.
Eigen::Triplet< double > T
static Subgraph load(const std::string &fn)
std::vector< size_t > kruskal(const GaussianFactorGraph &gfg, const FastMap< Key, size_t > &ordering, const std::vector< double > &weights) const
Array< double, 1, 3 > e(1./3., 0.5, 2.)
const mpreal sum(const mpreal tab[], const unsigned long int n, int &status, mp_rnd_t mode=mpreal::get_default_rnd())
Definition: mpreal.h:2381
RealScalar s
virtual Subgraph operator()(const GaussianFactorGraph &jfg) const
EIGEN_DEVICE_FUNC const Scalar & q
Linear Factor Graph where all factors are Gaussians.
const Edges & edges() const
RowVector3d w
boost::shared_ptr< This > shared_ptr
shared_ptr to this class
std::vector< size_t > buildTree(const GaussianFactorGraph &gfg, const FastMap< Key, size_t > &ordering, const std::vector< double > &weights) const
traits
Definition: chartTesting.h:28
A thin wrapper around std::map that uses boost&#39;s fast_pool_allocator.
ofstream os("timeSchurFactors.csv")
EIGEN_DEVICE_FUNC SegmentReturnType head(Index n)
This is the const version of head(Index).
Definition: BlockMethods.h:919
const KeyVector & keys() const
Access the factor&#39;s involved variable keys.
Definition: Factor.h:124
std::vector< size_t > sampleWithoutReplacement(size_t numSamples, const std::vector< double > &weights)
static AugmentationWeight augmentationWeightTranslator(const std::string &s)
float * p
void merge(const size_t &i1, const size_t &i2)
Merge the sets containing i1 and i2. Does nothing if i1 and i2 are already in the same set...
Definition: DSFVector.cpp:54
std::vector< size_t > unary(const GaussianFactorGraph &gfg) const
static Skeleton skeletonTranslator(const std::string &s)
const KeyVector keys
boost::shared_ptr< This > shared_ptr
shared_ptr to this class
friend std::ostream & operator<<(std::ostream &os, const Subgraph &subgraph)
static Ordering Natural(const FACTOR_GRAPH &fg)
Return a natural Ordering. Typically used by iterative solvers.
std::uint64_t Key
Integer nonlinear key type.
Definition: types.h:61
Point2 t(10, 10)
Weights weights(const GaussianFactorGraph &gfg) const
std::vector< size_t > bfs(const GaussianFactorGraph &gfg) const
static SkeletonWeight skeletonWeightTranslator(const std::string &s)


gtsam
Author(s):
autogenerated on Sat May 8 2021 02:44:50