testHybridNonlinearISAM.cpp
Go to the documentation of this file.
1 /* ----------------------------------------------------------------------------
2 
3  * GTSAM Copyright 2010, 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 
22 #include <gtsam/geometry/Pose2.h>
29 
30 #include <numeric>
31 
32 #include "Switching.h"
33 
34 // Include for test suite
36 
37 using namespace std;
38 using namespace gtsam;
46 
47 /* ****************************************************************************/
48 // Test if we can perform elimination incrementally.
49 TEST(HybridNonlinearISAM, IncrementalElimination) {
50  Switching switching(3);
54 
55  // Create initial factor graph
56  // * * *
57  // | | |
58  // X0 -*- X1 -*- X2
59  // \*-M0-*/
60  graph1.push_back(switching.nonlinearFactorGraph.at(0)); // P(X0)
61  graph1.push_back(switching.nonlinearFactorGraph.at(1)); // P(X0, X1 | M0)
62  graph1.push_back(switching.nonlinearFactorGraph.at(2)); // P(X1, X2 | M1)
63  graph1.push_back(switching.nonlinearFactorGraph.at(5)); // P(M0)
64 
65  initial.insert<double>(X(0), 1);
66  initial.insert<double>(X(1), 2);
67  initial.insert<double>(X(2), 3);
68 
69  // Run update step
70  isam.update(graph1, initial);
71 
72  // Check that after update we have 3 hybrid Bayes net nodes:
73  // P(X0 | X1, M0) and P(X1, X2 | M0, M1), P(M0, M1)
74  HybridGaussianISAM bayesTree = isam.bayesTree();
75  EXPECT_LONGS_EQUAL(3, bayesTree.size());
76  EXPECT(bayesTree[X(0)]->conditional()->frontals() == KeyVector{X(0)});
77  EXPECT(bayesTree[X(0)]->conditional()->parents() == KeyVector({X(1), M(0)}));
78  EXPECT(bayesTree[X(1)]->conditional()->frontals() == KeyVector({X(1), X(2)}));
79  EXPECT(bayesTree[X(1)]->conditional()->parents() == KeyVector({M(0), M(1)}));
80 
81  /********************************************************/
82  // New factor graph for incremental update.
84  initial = Values();
85 
86  graph1.push_back(switching.nonlinearFactorGraph.at(3)); // P(X1)
87  graph2.push_back(switching.nonlinearFactorGraph.at(4)); // P(X2)
88  graph2.push_back(switching.nonlinearFactorGraph.at(6)); // P(M0, M1)
89 
90  isam.update(graph2, initial);
91 
92  bayesTree = isam.bayesTree();
93  // Check that after the second update we have
94  // 1 additional hybrid Bayes net node:
95  // P(X1, X2 | M0, M1)
96  EXPECT_LONGS_EQUAL(3, bayesTree.size());
97  EXPECT(bayesTree[X(2)]->conditional()->frontals() == KeyVector({X(1), X(2)}));
98  EXPECT(bayesTree[X(2)]->conditional()->parents() == KeyVector({M(0), M(1)}));
99 }
100 
101 /* ****************************************************************************/
102 // Test if we can incrementally do the inference
103 TEST(HybridNonlinearISAM, IncrementalInference) {
104  Switching switching(3);
107  Values initial;
108 
109  // Create initial factor graph
110  // * * *
111  // | | |
112  // X0 -*- X1 -*- X2
113  // | |
114  // *-M0 - * - M1
115  graph1.push_back(switching.nonlinearFactorGraph.at(0)); // P(X0)
116  graph1.push_back(switching.nonlinearFactorGraph.at(1)); // P(X0, X1 | M0)
117  graph1.push_back(switching.nonlinearFactorGraph.at(3)); // P(X1)
118  graph1.push_back(switching.nonlinearFactorGraph.at(5)); // P(M0)
119 
120  initial.insert<double>(X(0), 1);
121  initial.insert<double>(X(1), 2);
122 
123  // Run update step
124  isam.update(graph1, initial);
125  HybridGaussianISAM bayesTree = isam.bayesTree();
126 
127  auto discreteConditional_m0 = bayesTree[M(0)]->conditional()->asDiscrete();
128  EXPECT(discreteConditional_m0->keys() == KeyVector({M(0)}));
129 
130  /********************************************************/
131  // New factor graph for incremental update.
133  initial = Values();
134 
135  initial.insert<double>(X(2), 3);
136 
137  graph2.push_back(switching.nonlinearFactorGraph.at(2)); // P(X1, X2 | M1)
138  graph2.push_back(switching.nonlinearFactorGraph.at(4)); // P(X2)
139  graph2.push_back(switching.nonlinearFactorGraph.at(6)); // P(M0, M1)
140 
141  isam.update(graph2, initial);
142  bayesTree = isam.bayesTree();
143 
144  /********************************************************/
145  // Run batch elimination so we can compare results.
146  const Ordering ordering {X(0), X(1), X(2)};
147 
148  // Now we calculate the actual factors using full elimination
149  const auto [expectedHybridBayesTree, expectedRemainingGraph] =
150  switching.linearizedFactorGraph
151  .BaseEliminateable::eliminatePartialMultifrontal(ordering);
152 
153  // The densities on X(1) should be the same
154  auto x0_conditional = dynamic_pointer_cast<GaussianMixture>(
155  bayesTree[X(0)]->conditional()->inner());
156  auto expected_x0_conditional = dynamic_pointer_cast<GaussianMixture>(
157  (*expectedHybridBayesTree)[X(0)]->conditional()->inner());
158  EXPECT(assert_equal(*x0_conditional, *expected_x0_conditional));
159 
160  // The densities on X(1) should be the same
161  auto x1_conditional = dynamic_pointer_cast<GaussianMixture>(
162  bayesTree[X(1)]->conditional()->inner());
163  auto expected_x1_conditional = dynamic_pointer_cast<GaussianMixture>(
164  (*expectedHybridBayesTree)[X(1)]->conditional()->inner());
165  EXPECT(assert_equal(*x1_conditional, *expected_x1_conditional));
166 
167  // The densities on X(2) should be the same
168  auto x2_conditional = dynamic_pointer_cast<GaussianMixture>(
169  bayesTree[X(2)]->conditional()->inner());
170  auto expected_x2_conditional = dynamic_pointer_cast<GaussianMixture>(
171  (*expectedHybridBayesTree)[X(2)]->conditional()->inner());
172  EXPECT(assert_equal(*x2_conditional, *expected_x2_conditional));
173 
174  // We only perform manual continuous elimination for 0,0.
175  // The other discrete probabilities on M(1) are calculated the same way
176  const Ordering discreteOrdering{M(0), M(1)};
177  HybridBayesTree::shared_ptr discreteBayesTree =
178  expectedRemainingGraph->BaseEliminateable::eliminateMultifrontal(
179  discreteOrdering);
180 
181  DiscreteValues m00;
182  m00[M(0)] = 0, m00[M(1)] = 0;
183  DiscreteConditional decisionTree =
184  *(*discreteBayesTree)[M(1)]->conditional()->asDiscrete();
185  double m00_prob = decisionTree(m00);
186 
187  auto discreteConditional = bayesTree[M(1)]->conditional()->asDiscrete();
188 
189  // Test the probability values with regression tests.
190  DiscreteValues assignment;
191  EXPECT(assert_equal(0.0952922, m00_prob, 1e-5));
192  assignment[M(0)] = 0;
193  assignment[M(1)] = 0;
194  EXPECT(assert_equal(0.0952922, (*discreteConditional)(assignment), 1e-5));
195  assignment[M(0)] = 1;
196  assignment[M(1)] = 0;
197  EXPECT(assert_equal(0.282758, (*discreteConditional)(assignment), 1e-5));
198  assignment[M(0)] = 0;
199  assignment[M(1)] = 1;
200  EXPECT(assert_equal(0.314175, (*discreteConditional)(assignment), 1e-5));
201  assignment[M(0)] = 1;
202  assignment[M(1)] = 1;
203  EXPECT(assert_equal(0.307775, (*discreteConditional)(assignment), 1e-5));
204 
205  // Check if the clique conditional generated from incremental elimination
206  // matches that of batch elimination.
207  auto expectedChordal = expectedRemainingGraph->eliminateMultifrontal();
208  auto actualConditional = dynamic_pointer_cast<DecisionTreeFactor>(
209  bayesTree[M(1)]->conditional()->inner());
210  // Account for the probability terms from evaluating continuous FGs
211  DiscreteKeys discrete_keys = {{M(0), 2}, {M(1), 2}};
212  vector<double> probs = {0.095292197, 0.31417524, 0.28275772, 0.30777485};
213  auto expectedConditional =
214  std::make_shared<DecisionTreeFactor>(discrete_keys, probs);
215  EXPECT(assert_equal(*expectedConditional, *actualConditional, 1e-6));
216 }
217 
218 /* ****************************************************************************/
219 // Test if we can approximately do the inference
220 TEST(HybridNonlinearISAM, Approx_inference) {
221  Switching switching(4);
222  HybridNonlinearISAM incrementalHybrid;
224  Values initial;
225 
226  // Add the 3 hybrid factors, x0-x1, x1-x2, x2-x3
227  for (size_t i = 1; i < 4; i++) {
228  graph1.push_back(switching.nonlinearFactorGraph.at(i));
229  }
230 
231  // Add the Gaussian factors, 1 prior on X(0),
232  // 3 measurements on X(1), X(2), X(3)
233  graph1.push_back(switching.nonlinearFactorGraph.at(0));
234  for (size_t i = 4; i <= 7; i++) {
235  graph1.push_back(switching.nonlinearFactorGraph.at(i));
236  initial.insert<double>(X(i - 4), i - 3);
237  }
238 
239  // Create ordering.
241  for (size_t j = 0; j < 4; j++) {
242  ordering.push_back(X(j));
243  }
244 
245  // Now we calculate the actual factors using full elimination
246  const auto [unprunedHybridBayesTree, unprunedRemainingGraph] =
247  switching.linearizedFactorGraph
248  .BaseEliminateable::eliminatePartialMultifrontal(ordering);
249 
250  size_t maxNrLeaves = 5;
251  incrementalHybrid.update(graph1, initial);
252  HybridGaussianISAM bayesTree = incrementalHybrid.bayesTree();
253 
254  bayesTree.prune(maxNrLeaves);
255 
256  /*
257  unpruned factor is:
258  Choice(m3)
259  0 Choice(m2)
260  0 0 Choice(m1)
261  0 0 0 Leaf 0.11267528
262  0 0 1 Leaf 0.18576102
263  0 1 Choice(m1)
264  0 1 0 Leaf 0.18754662
265  0 1 1 Leaf 0.30623871
266  1 Choice(m2)
267  1 0 Choice(m1)
268  1 0 0 Leaf 0.18576102
269  1 0 1 Leaf 0.30622428
270  1 1 Choice(m1)
271  1 1 0 Leaf 0.30623871
272  1 1 1 Leaf 0.5
273 
274  pruned factors is:
275  Choice(m3)
276  0 Choice(m2)
277  0 0 Leaf 0
278  0 1 Choice(m1)
279  0 1 0 Leaf 0.18754662
280  0 1 1 Leaf 0.30623871
281  1 Choice(m2)
282  1 0 Choice(m1)
283  1 0 0 Leaf 0
284  1 0 1 Leaf 0.30622428
285  1 1 Choice(m1)
286  1 1 0 Leaf 0.30623871
287  1 1 1 Leaf 0.5
288  */
289 
290  auto discreteConditional_m0 = *dynamic_pointer_cast<DiscreteConditional>(
291  bayesTree[M(0)]->conditional()->inner());
292  EXPECT(discreteConditional_m0.keys() == KeyVector({M(0), M(1), M(2)}));
293 
294  // Get the number of elements which are greater than 0.
295  auto count = [](const double &value, int count) {
296  return value > 0 ? count + 1 : count;
297  };
298  // Check that the number of leaves after pruning is 5.
299  EXPECT_LONGS_EQUAL(5, discreteConditional_m0.fold(count, 0));
300 
301  // Check that the hybrid nodes of the bayes net match those of the pre-pruning
302  // bayes net, at the same positions.
303  auto &unprunedLastDensity = *dynamic_pointer_cast<GaussianMixture>(
304  unprunedHybridBayesTree->clique(X(3))->conditional()->inner());
305  auto &lastDensity = *dynamic_pointer_cast<GaussianMixture>(
306  bayesTree[X(3)]->conditional()->inner());
307 
308  std::vector<std::pair<DiscreteValues, double>> assignments =
309  discreteConditional_m0.enumerate();
310  // Loop over all assignments and check the pruned components
311  for (auto &&av : assignments) {
312  const DiscreteValues &assignment = av.first;
313  const double value = av.second;
314 
315  if (value == 0.0) {
316  EXPECT(lastDensity(assignment) == nullptr);
317  } else {
318  CHECK(lastDensity(assignment));
319  EXPECT(assert_equal(*unprunedLastDensity(assignment),
320  *lastDensity(assignment)));
321  }
322  }
323 }
324 
325 /* ****************************************************************************/
326 // Test approximate inference with an additional pruning step.
327 TEST(HybridNonlinearISAM, Incremental_approximate) {
328  Switching switching(5);
329  HybridNonlinearISAM incrementalHybrid;
331  Values initial;
332 
333  /***** Run Round 1 *****/
334  // Add the 3 hybrid factors, x0-x1, x1-x2, x2-x3
335  for (size_t i = 1; i < 4; i++) {
336  graph1.push_back(switching.nonlinearFactorGraph.at(i));
337  }
338 
339  // Add the Gaussian factors, 1 prior on X(0),
340  // 3 measurements on X(1), X(2), X(3)
341  graph1.push_back(switching.nonlinearFactorGraph.at(0));
342  initial.insert<double>(X(0), 1);
343  for (size_t i = 5; i <= 7; i++) {
344  graph1.push_back(switching.nonlinearFactorGraph.at(i));
345  initial.insert<double>(X(i - 4), i - 3);
346  }
347 
348  // Run update with pruning
349  size_t maxComponents = 5;
350  incrementalHybrid.update(graph1, initial);
351  incrementalHybrid.prune(maxComponents);
352  HybridGaussianISAM bayesTree = incrementalHybrid.bayesTree();
353 
354  // Check if we have a bayes tree with 4 hybrid nodes,
355  // each with 2, 4, 8, and 5 (pruned) leaves respetively.
356  EXPECT_LONGS_EQUAL(4, bayesTree.size());
358  2, bayesTree[X(0)]->conditional()->asMixture()->nrComponents());
360  3, bayesTree[X(1)]->conditional()->asMixture()->nrComponents());
362  5, bayesTree[X(2)]->conditional()->asMixture()->nrComponents());
364  5, bayesTree[X(3)]->conditional()->asMixture()->nrComponents());
365 
366  /***** Run Round 2 *****/
368  graph2.push_back(switching.nonlinearFactorGraph.at(4)); // x3-x4
369  graph2.push_back(switching.nonlinearFactorGraph.at(8)); // x4 measurement
370  initial = Values();
371  initial.insert<double>(X(4), 5);
372 
373  // Run update with pruning a second time.
374  incrementalHybrid.update(graph2, initial);
375  incrementalHybrid.prune(maxComponents);
376  bayesTree = incrementalHybrid.bayesTree();
377 
378  // Check if we have a bayes tree with pruned hybrid nodes,
379  // with 5 (pruned) leaves.
380  CHECK_EQUAL(5, bayesTree.size());
382  5, bayesTree[X(3)]->conditional()->asMixture()->nrComponents());
384  5, bayesTree[X(4)]->conditional()->asMixture()->nrComponents());
385 }
386 
387 /* ************************************************************************/
388 // A GTSAM-only test for running inference on a single-legged robot.
389 // The leg links are represented by the chain X-Y-Z-W, where X is the base and
390 // W is the foot.
391 // We use BetweenFactor<Pose2> as constraints between each of the poses.
392 TEST(HybridNonlinearISAM, NonTrivial) {
393  /*************** Run Round 1 ***************/
396 
397  // Add a prior on pose x0 at the origin.
398  // A prior factor consists of a mean and
399  // a noise model (covariance matrix)
400  Pose2 prior(0.0, 0.0, 0.0); // prior mean is at origin
401  auto priorNoise = noiseModel::Diagonal::Sigmas(
402  Vector3(0.3, 0.3, 0.1)); // 30cm std on x,y, 0.1 rad on theta
404 
405  // create a noise model for the landmark measurements
406  auto poseNoise = noiseModel::Isotropic::Sigma(3, 0.1);
407 
408  // We model a robot's single leg as X - Y - Z - W
409  // where X is the base link and W is the foot link.
410 
411  // Add connecting poses similar to PoseFactors in GTD
412  fg.emplace_shared<BetweenFactor<Pose2>>(X(0), Y(0), Pose2(0, 1.0, 0),
413  poseNoise);
414  fg.emplace_shared<BetweenFactor<Pose2>>(Y(0), Z(0), Pose2(0, 1.0, 0),
415  poseNoise);
416  fg.emplace_shared<BetweenFactor<Pose2>>(Z(0), W(0), Pose2(0, 1.0, 0),
417  poseNoise);
418 
419  // Create initial estimate
420  Values initial;
421  initial.insert(X(0), Pose2(0.0, 0.0, 0.0));
422  initial.insert(Y(0), Pose2(0.0, 1.0, 0.0));
423  initial.insert(Z(0), Pose2(0.0, 2.0, 0.0));
424  initial.insert(W(0), Pose2(0.0, 3.0, 0.0));
425 
426  // Don't run update now since we don't have discrete variables involved.
427 
428  using PlanarMotionModel = BetweenFactor<Pose2>;
429 
430  /*************** Run Round 2 ***************/
431  // Add odometry factor with discrete modes.
432  Pose2 odometry(1.0, 0.0, 0.0);
433  KeyVector contKeys = {W(0), W(1)};
434  auto noise_model = noiseModel::Isotropic::Sigma(3, 1.0);
435  auto still = std::make_shared<PlanarMotionModel>(W(0), W(1), Pose2(0, 0, 0),
436  noise_model),
437  moving = std::make_shared<PlanarMotionModel>(W(0), W(1), odometry,
438  noise_model);
439  std::vector<PlanarMotionModel::shared_ptr> components = {moving, still};
440  auto mixtureFactor = std::make_shared<MixtureFactor>(
441  contKeys, DiscreteKeys{gtsam::DiscreteKey(M(1), 2)}, components);
442  fg.push_back(mixtureFactor);
443 
444  // Add equivalent of ImuFactor
445  fg.emplace_shared<BetweenFactor<Pose2>>(X(0), X(1), Pose2(1.0, 0.0, 0),
446  poseNoise);
447  // PoseFactors-like at k=1
448  fg.emplace_shared<BetweenFactor<Pose2>>(X(1), Y(1), Pose2(0, 1, 0),
449  poseNoise);
450  fg.emplace_shared<BetweenFactor<Pose2>>(Y(1), Z(1), Pose2(0, 1, 0),
451  poseNoise);
452  fg.emplace_shared<BetweenFactor<Pose2>>(Z(1), W(1), Pose2(-1, 1, 0),
453  poseNoise);
454 
455  initial.insert(X(1), Pose2(1.0, 0.0, 0.0));
456  initial.insert(Y(1), Pose2(1.0, 1.0, 0.0));
457  initial.insert(Z(1), Pose2(1.0, 2.0, 0.0));
458  // The leg link did not move so we set the expected pose accordingly.
459  initial.insert(W(1), Pose2(0.0, 3.0, 0.0));
460 
461  // Update without pruning
462  // The result is a HybridBayesNet with 1 discrete variable M(1).
463  // P(X | measurements) = P(W0|Z0, W1, M1) P(Z0|Y0, W1, M1) P(Y0|X0, W1, M1)
464  // P(X0 | X1, W1, M1) P(W1|Z1, X1, M1) P(Z1|Y1, X1, M1)
465  // P(Y1 | X1, M1)P(X1 | M1)P(M1)
466  // The MHS tree is a 1 level tree for time indices (1,) with 2 leaves.
467  inc.update(fg, initial);
468 
470  initial = Values();
471 
472  /*************** Run Round 3 ***************/
473  // Add odometry factor with discrete modes.
474  contKeys = {W(1), W(2)};
475  still = std::make_shared<PlanarMotionModel>(W(1), W(2), Pose2(0, 0, 0),
476  noise_model);
477  moving =
478  std::make_shared<PlanarMotionModel>(W(1), W(2), odometry, noise_model);
479  components = {moving, still};
480  mixtureFactor = std::make_shared<MixtureFactor>(
481  contKeys, DiscreteKeys{gtsam::DiscreteKey(M(2), 2)}, components);
482  fg.push_back(mixtureFactor);
483 
484  // Add equivalent of ImuFactor
485  fg.emplace_shared<BetweenFactor<Pose2>>(X(1), X(2), Pose2(1.0, 0.0, 0),
486  poseNoise);
487  // PoseFactors-like at k=1
488  fg.emplace_shared<BetweenFactor<Pose2>>(X(2), Y(2), Pose2(0, 1, 0),
489  poseNoise);
490  fg.emplace_shared<BetweenFactor<Pose2>>(Y(2), Z(2), Pose2(0, 1, 0),
491  poseNoise);
492  fg.emplace_shared<BetweenFactor<Pose2>>(Z(2), W(2), Pose2(-2, 1, 0),
493  poseNoise);
494 
495  initial.insert(X(2), Pose2(2.0, 0.0, 0.0));
496  initial.insert(Y(2), Pose2(2.0, 1.0, 0.0));
497  initial.insert(Z(2), Pose2(2.0, 2.0, 0.0));
498  initial.insert(W(2), Pose2(0.0, 3.0, 0.0));
499 
500  // Now we prune!
501  // P(X | measurements) = P(W0|Z0, W1, M1) P(Z0|Y0, W1, M1) P(Y0|X0, W1, M1)
502  // P(X0 | X1, W1, M1) P(W1|W2, Z1, X1, M1, M2)
503  // P(Z1| W2, Y1, X1, M1, M2) P(Y1 | W2, X1, M1, M2)
504  // P(X1 | W2, X2, M1, M2) P(W2|Z2, X2, M1, M2)
505  // P(Z2|Y2, X2, M1, M2) P(Y2 | X2, M1, M2)
506  // P(X2 | M1, M2) P(M1, M2)
507  // The MHS at this point should be a 2 level tree on (1, 2).
508  // 1 has 2 choices, and 2 has 4 choices.
509  inc.update(fg, initial);
510  inc.prune(2);
511 
513  initial = Values();
514 
515  /*************** Run Round 4 ***************/
516  // Add odometry factor with discrete modes.
517  contKeys = {W(2), W(3)};
518  still = std::make_shared<PlanarMotionModel>(W(2), W(3), Pose2(0, 0, 0),
519  noise_model);
520  moving =
521  std::make_shared<PlanarMotionModel>(W(2), W(3), odometry, noise_model);
522  components = {moving, still};
523  mixtureFactor = std::make_shared<MixtureFactor>(
524  contKeys, DiscreteKeys{gtsam::DiscreteKey(M(3), 2)}, components);
525  fg.push_back(mixtureFactor);
526 
527  // Add equivalent of ImuFactor
528  fg.emplace_shared<BetweenFactor<Pose2>>(X(2), X(3), Pose2(1.0, 0.0, 0),
529  poseNoise);
530  // PoseFactors-like at k=3
531  fg.emplace_shared<BetweenFactor<Pose2>>(X(3), Y(3), Pose2(0, 1, 0),
532  poseNoise);
533  fg.emplace_shared<BetweenFactor<Pose2>>(Y(3), Z(3), Pose2(0, 1, 0),
534  poseNoise);
535  fg.emplace_shared<BetweenFactor<Pose2>>(Z(3), W(3), Pose2(-3, 1, 0),
536  poseNoise);
537 
538  initial.insert(X(3), Pose2(3.0, 0.0, 0.0));
539  initial.insert(Y(3), Pose2(3.0, 1.0, 0.0));
540  initial.insert(Z(3), Pose2(3.0, 2.0, 0.0));
541  initial.insert(W(3), Pose2(0.0, 3.0, 0.0));
542 
543  // Keep pruning!
544  inc.update(fg, initial);
545  inc.prune(3);
546 
548  initial = Values();
549 
550  HybridGaussianISAM bayesTree = inc.bayesTree();
551 
552  // The final discrete graph should not be empty since we have eliminated
553  // all continuous variables.
554  auto discreteTree = bayesTree[M(3)]->conditional()->asDiscrete();
555  EXPECT_LONGS_EQUAL(3, discreteTree->size());
556 
557  // Test if the optimal discrete mode assignment is (1, 1, 1).
558  DiscreteFactorGraph discreteGraph;
559  discreteGraph.push_back(discreteTree);
560  DiscreteValues optimal_assignment = discreteGraph.optimize();
561 
562  DiscreteValues expected_assignment;
563  expected_assignment[M(1)] = 1;
564  expected_assignment[M(2)] = 1;
565  expected_assignment[M(3)] = 1;
566 
567  EXPECT(assert_equal(expected_assignment, optimal_assignment));
568 
569  // Test if pruning worked correctly by checking that
570  // we only have 3 leaves in the last node.
571  auto lastConditional = bayesTree[X(3)]->conditional()->asMixture();
572  EXPECT_LONGS_EQUAL(3, lastConditional->nrComponents());
573 }
574 
575 /* ************************************************************************* */
576 int main() {
577  TestResult tr;
578  return TestRegistry::runAllTests(tr);
579 }
#define CHECK(condition)
Definition: Test.h:108
const char Y
Matrix< RealScalar, Dynamic, Dynamic > M
Definition: bench_gemm.cpp:51
TEST(HybridNonlinearISAM, IncrementalElimination)
size_t size() const
IsDerived< DERIVEDFACTOR > emplace_shared(Args &&... args)
Emplace a shared pointer to factor of given type.
Definition: FactorGraph.h:196
static int runAllTests(TestResult &result)
Eigen::Vector3d Vector3
Definition: Vector.h:43
const HybridGaussianISAM & bayesTree() const
Point2 prior(const Point2 &x)
Prior on a single pose.
Definition: simulated2D.h:88
std::shared_ptr< This > shared_ptr
IsDerived< DERIVEDFACTOR > push_back(std::shared_ptr< DERIVEDFACTOR > factor)
Add a factor directly using a shared_ptr.
Definition: FactorGraph.h:190
bool assert_equal(const Matrix &expected, const Matrix &actual, double tol)
Definition: Matrix.cpp:40
void update(const HybridNonlinearFactorGraph &newFactors, const Values &initialValues, const std::optional< size_t > &maxNrLeaves={}, const std::optional< Ordering > &ordering={})
Values initial
MatrixXd L
Definition: LLT_example.cpp:6
Definition: BFloat16.h:88
Key W(std::uint64_t j)
static enum @1107 ordering
void prune(const size_t maxNumberLeaves)
Prune the underlying Bayes tree.
void prune(const size_t maxNumberLeaves)
Prune the underlying Bayes tree.
HybridGaussianFactorGraph linearizedFactorGraph
Definition: Switching.h:124
#define Z
Definition: icosphere.cpp:21
A conditional of gaussian mixtures indexed by discrete variables, as part of a Bayes Network...
#define EXPECT(condition)
Definition: Test.h:150
Array< double, 1, 3 > e(1./3., 0.5, 2.)
NonlinearISAM isam(relinearizeInterval)
Linear Factor Graph where all factors are Gaussians.
NonlinearFactorGraph graph2()
traits
Definition: chartTesting.h:28
auto priorNoise
std::vector< float > Values
#define EXPECT_LONGS_EQUAL(expected, actual)
Definition: Test.h:154
HybridNonlinearFactorGraph nonlinearFactorGraph
Definition: Switching.h:123
const sharedFactor at(size_t i) const
Definition: FactorGraph.h:343
std::pair< Key, size_t > DiscreteKey
Definition: DiscreteKey.h:38
Chordal Bayes Net, the result of eliminating a factor graph.
void insert(Key j, const Value &val)
Definition: Values.cpp:155
Pose2 odometry(2.0, 0.0, 0.0)
FastVector< Key > KeyVector
Define collection type once and for all - also used in wrappers.
Definition: Key.h:86
2D Pose
#define X
Definition: icosphere.cpp:20
std::ptrdiff_t j
#define CHECK_EQUAL(expected, actual)
Definition: Test.h:131
DiscreteKeys is a set of keys that can be assembled using the & operator.
Definition: DiscreteKey.h:41


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:38:23