testHybridNonlinearFactorGraph.cpp
Go to the documentation of this file.
1 /* ----------------------------------------------------------------------------
2  * GTSAM Copyright 2010, Georgia Tech Research Corporation,
3  * Atlanta, Georgia 30332-0415
4  * All Rights Reserved
5  * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
6  * See LICENSE for the license information
7  * -------------------------------------------------------------------------- */
8 
19 #include <gtsam/base/utilities.h>
23 #include <gtsam/geometry/Pose2.h>
35 
36 #include "Switching.h"
37 
38 // Include for test suite
40 
41 using namespace std;
42 using namespace gtsam;
47 
48 /* ****************************************************************************
49  * Test that any linearizedFactorGraph gaussian factors are appended to the
50  * existing gaussian factor graph in the hybrid factor graph.
51  */
54 
55  // Add a simple prior factor to the nonlinear factor graph
56  fg.emplace_shared<PriorFactor<double>>(X(0), 0, Isotropic::Sigma(1, 0.1));
57 
58  // Linearization point
59  Values linearizationPoint;
60  linearizationPoint.insert<double>(X(0), 0);
61 
62  // Linearize the factor graph.
63  HybridGaussianFactorGraph ghfg = *fg.linearize(linearizationPoint);
64  EXPECT_LONGS_EQUAL(1, ghfg.size());
65 
66  // Check that the error is the same for the nonlinear values.
67  const VectorValues zero{{X(0), Vector1(0)}};
68  const HybridValues hybridValues{zero, {}, linearizationPoint};
69  EXPECT_DOUBLES_EQUAL(fg.error(hybridValues), ghfg.error(hybridValues), 1e-9);
70 }
71 
72 /***************************************************************************
73  * Test equality for Hybrid Nonlinear Factor Graph.
74  */
78 
79  // Test empty factor graphs
80  EXPECT(assert_equal(graph1, graph2));
81 
82  auto f0 = std::make_shared<PriorFactor<Pose2>>(
83  1, Pose2(), noiseModel::Isotropic::Sigma(3, 0.001));
84  graph1.push_back(f0);
86 
87  auto f1 = std::make_shared<BetweenFactor<Pose2>>(
88  1, 2, Pose2(), noiseModel::Isotropic::Sigma(3, 0.1));
89  graph1.push_back(f1);
91 
92  // Test non-empty graphs
93  EXPECT(assert_equal(graph1, graph2));
94 }
95 
96 /***************************************************************************
97  * Test that the resize method works correctly for a HybridNonlinearFactorGraph.
98  */
101  auto nonlinearFactor = std::make_shared<BetweenFactor<double>>();
102  fg.push_back(nonlinearFactor);
103 
104  auto discreteFactor = std::make_shared<DecisionTreeFactor>();
105  fg.push_back(discreteFactor);
106 
107  auto dcFactor = std::make_shared<HybridNonlinearFactor>();
108  fg.push_back(dcFactor);
109 
110  EXPECT_LONGS_EQUAL(fg.size(), 3);
111 
112  fg.resize(0);
113  EXPECT_LONGS_EQUAL(fg.size(), 0);
114 }
115 
116 /***************************************************************************/
117 namespace test_motion {
118 gtsam::DiscreteKey m1(M(1), 2);
119 auto noise_model = noiseModel::Isotropic::Sigma(1, 1.0);
120 std::vector<NoiseModelFactor::shared_ptr> components = {
121  std::make_shared<MotionModel>(X(0), X(1), 0.0, noise_model),
122  std::make_shared<MotionModel>(X(0), X(1), 1.0, noise_model)};
123 } // namespace test_motion
124 
125 /***************************************************************************
126  * Test that the resize method works correctly for a
127  * HybridGaussianFactorGraph.
128  */
130  using namespace test_motion;
131 
133  auto nonlinearFactor = std::make_shared<BetweenFactor<double>>(
134  X(0), X(1), 0.0, Isotropic::Sigma(1, 0.1));
135  hnfg.push_back(nonlinearFactor);
136  auto discreteFactor = std::make_shared<DecisionTreeFactor>();
137  hnfg.push_back(discreteFactor);
138 
139  auto dcFactor = std::make_shared<HybridNonlinearFactor>(m1, components);
140  hnfg.push_back(dcFactor);
141 
142  Values linearizationPoint;
143  linearizationPoint.insert<double>(X(0), 0);
144  linearizationPoint.insert<double>(X(1), 1);
145 
146  // Generate `HybridGaussianFactorGraph` by linearizing
147  HybridGaussianFactorGraph gfg = *hnfg.linearize(linearizationPoint);
148 
149  EXPECT_LONGS_EQUAL(gfg.size(), 3);
150 
151  gfg.resize(0);
152  EXPECT_LONGS_EQUAL(gfg.size(), 0);
153 }
154 
155 /*****************************************************************************
156  * Test push_back on HFG makes the correct distinction.
157  */
160 
161  auto nonlinearFactor = std::make_shared<BetweenFactor<double>>();
162  fg.push_back(nonlinearFactor);
163 
164  EXPECT_LONGS_EQUAL(fg.size(), 1);
165 
167 
168  auto discreteFactor = std::make_shared<DecisionTreeFactor>();
169  fg.push_back(discreteFactor);
170 
171  EXPECT_LONGS_EQUAL(fg.size(), 1);
172 
174 
175  auto dcFactor = std::make_shared<HybridNonlinearFactor>();
176  fg.push_back(dcFactor);
177 
178  EXPECT_LONGS_EQUAL(fg.size(), 1);
179 
180  // Now do the same with HybridGaussianFactorGraph
182 
183  auto gaussianFactor = std::make_shared<JacobianFactor>();
184  ghfg.push_back(gaussianFactor);
185 
186  EXPECT_LONGS_EQUAL(ghfg.size(), 1);
187 
188  ghfg = HybridGaussianFactorGraph();
189  ghfg.push_back(discreteFactor);
190 
191  EXPECT_LONGS_EQUAL(ghfg.size(), 1);
192 
193  ghfg = HybridGaussianFactorGraph();
194  ghfg.push_back(dcFactor);
195 
197  hgfg2.push_back(ghfg.begin(), ghfg.end());
198 
199  EXPECT_LONGS_EQUAL(ghfg.size(), 1);
200 
203  auto noise = noiseModel::Isotropic::Sigma(3, 1.0);
204  factors.emplace_shared<PriorFactor<Pose2>>(0, Pose2(0, 0, 0), noise);
205  factors.emplace_shared<PriorFactor<Pose2>>(1, Pose2(1, 0, 0), noise);
206  factors.emplace_shared<PriorFactor<Pose2>>(2, Pose2(2, 0, 0), noise);
207  // TODO(Varun) This does not currently work. It should work once HybridFactor
208  // becomes a base class of NoiseModelFactor.
209  // hnfg.push_back(factors.begin(), factors.end());
210 
211  // EXPECT_LONGS_EQUAL(3, hnfg.size());
212 }
213 
214 /* ****************************************************************************/
215 // Test hybrid nonlinear factor graph errorTree
217  Switching s(3);
218 
219  HybridNonlinearFactorGraph graph = s.nonlinearFactorGraph;
220  Values values = s.linearizationPoint;
221 
222  auto error_tree = graph.errorTree(s.linearizationPoint);
223 
224  auto dkeys = graph.discreteKeys();
225  DiscreteKeys discrete_keys(dkeys.begin(), dkeys.end());
226 
227  // Compute the sum of errors for each factor.
228  auto assignments = DiscreteValues::CartesianProduct(discrete_keys);
229  std::vector<double> leaves(assignments.size());
230  for (auto &&factor : graph) {
231  for (size_t i = 0; i < assignments.size(); ++i) {
232  leaves[i] +=
233  factor->error(HybridValues(VectorValues(), assignments[i], values));
234  }
235  }
236  // Swap i=1 and i=2 to give correct ordering.
237  double temp = leaves[1];
238  leaves[1] = leaves[2];
239  leaves[2] = temp;
240  AlgebraicDecisionTree<Key> expected_error(discrete_keys, leaves);
241 
242  EXPECT(assert_equal(expected_error, error_tree, 1e-7));
243 }
244 
245 /****************************************************************************
246  * Test construction of switching-like hybrid factor graph.
247  */
249  Switching self(3);
250 
251  EXPECT_LONGS_EQUAL(7, self.nonlinearFactorGraph.size());
252  EXPECT_LONGS_EQUAL(7, self.linearizedFactorGraph.size());
253 }
254 
255 /****************************************************************************
256  * Test linearization on a switching-like hybrid factor graph.
257  */
259  Switching self(3);
260 
261  // Linearize here:
262  HybridGaussianFactorGraph actualLinearized =
263  *self.nonlinearFactorGraph.linearize(self.linearizationPoint);
264 
265  EXPECT_LONGS_EQUAL(7, actualLinearized.size());
266 }
267 
268 /****************************************************************************
269  * Test elimination tree construction
270  */
272  Switching self(3);
273 
274  // Create ordering.
276  for (size_t k = 0; k < self.K; k++) ordering.push_back(X(k));
277 
278  // Create elimination tree.
279  HybridEliminationTree etree(self.linearizedFactorGraph, ordering);
280  EXPECT_LONGS_EQUAL(1, etree.roots().size())
281 }
282 
283 /****************************************************************************
284  *Test elimination function by eliminating x0 in *-x0-*-x1 graph.
285  */
286 TEST(GaussianElimination, Eliminate_x0) {
287  Switching self(3);
288 
289  // Gather factors on x1, has a simple Gaussian and a hybrid factor.
291  // Add gaussian prior
292  factors.push_back(self.linearizedFactorGraph[0]);
293  // Add first hybrid factor
294  factors.push_back(self.linearizedFactorGraph[1]);
295 
296  // Eliminate x0
297  const Ordering ordering{X(0)};
298 
300  CHECK(result.first);
301  EXPECT_LONGS_EQUAL(1, result.first->nrFrontals());
302  CHECK(result.second);
303  // Has two keys, x2 and m1
304  EXPECT_LONGS_EQUAL(2, result.second->size());
305 }
306 
307 /****************************************************************************
308  * Test elimination function by eliminating x1 in x0-*-x1-*-x2 chain.
309  * m0/ \m1
310  */
311 TEST(HybridsGaussianElimination, Eliminate_x1) {
312  Switching self(3);
313 
314  // Gather factors on x1, will be two hybrid factors (with x0 and x2, resp.).
316  factors.push_back(self.linearizedFactorGraph[1]); // involves m0
317  factors.push_back(self.linearizedFactorGraph[2]); // involves m1
318 
319  // Eliminate x1
320  const Ordering ordering{X(1)};
321 
323  CHECK(result.first);
324  EXPECT_LONGS_EQUAL(1, result.first->nrFrontals());
325  CHECK(result.second);
326  // Note: separator keys should include m1, m2.
327  EXPECT_LONGS_EQUAL(4, result.second->size());
328 }
329 
330 /****************************************************************************
331  * Helper method to generate gaussian factor graphs with a specific mode.
332  */
334  Values linearizationPoint) {
336  graph.addPrior<double>(X(0), 0, Isotropic::Sigma(1, 0.1));
337 
338  auto between_x0_x1 = std::make_shared<MotionModel>(X(0), X(1), between,
339  Isotropic::Sigma(1, 1.0));
340 
341  graph.push_back(between_x0_x1);
342 
343  return graph.linearize(linearizationPoint);
344 }
345 
346 /****************************************************************************
347  * Test elimination function by eliminating x0 and x1 in graph.
348  */
349 TEST(HybridGaussianElimination, EliminateHybrid_2_Variable) {
350  Switching self(2, 1.0, 0.1);
351 
352  auto factors = self.linearizedFactorGraph;
353 
354  // Eliminate x0
355  const Ordering ordering{X(0), X(1)};
356 
357  const auto [hybridConditional, factorOnModes] =
359 
360  auto hybridGaussianConditional =
361  dynamic_pointer_cast<HybridGaussianConditional>(
362  hybridConditional->inner());
363 
364  CHECK(hybridGaussianConditional);
365  // Frontals = [x0, x1]
366  EXPECT_LONGS_EQUAL(2, hybridGaussianConditional->nrFrontals());
367  // 1 parent, which is the mode
368  EXPECT_LONGS_EQUAL(1, hybridGaussianConditional->nrParents());
369 
370  // This is now a discreteFactor
371  auto discreteFactor = dynamic_pointer_cast<DecisionTreeFactor>(factorOnModes);
372  CHECK(discreteFactor);
373  EXPECT_LONGS_EQUAL(1, discreteFactor->discreteKeys().size());
374  EXPECT(discreteFactor->root_->isLeaf() == false);
375 }
376 
377 /****************************************************************************
378  * Test partial elimination
379  */
380 TEST(HybridNonlinearFactorGraph, Partial_Elimination) {
381  Switching self(3);
382 
383  auto linearizedFactorGraph = self.linearizedFactorGraph;
384 
385  // Create ordering of only continuous variables.
387  for (size_t k = 0; k < self.K; k++) ordering.push_back(X(k));
388 
389  // Eliminate partially i.e. only continuous part.
390  const auto [hybridBayesNet, remainingFactorGraph] =
391  linearizedFactorGraph.eliminatePartialSequential(ordering);
392 
393  CHECK(hybridBayesNet);
394  EXPECT_LONGS_EQUAL(3, hybridBayesNet->size());
395  EXPECT(hybridBayesNet->at(0)->frontals() == KeyVector{X(0)});
396  EXPECT(hybridBayesNet->at(0)->parents() == KeyVector({X(1), M(0)}));
397  EXPECT(hybridBayesNet->at(1)->frontals() == KeyVector{X(1)});
398  EXPECT(hybridBayesNet->at(1)->parents() == KeyVector({X(2), M(0), M(1)}));
399  EXPECT(hybridBayesNet->at(2)->frontals() == KeyVector{X(2)});
400  EXPECT(hybridBayesNet->at(2)->parents() == KeyVector({M(0), M(1)}));
401 
402  CHECK(remainingFactorGraph);
403  EXPECT_LONGS_EQUAL(3, remainingFactorGraph->size());
404  EXPECT(remainingFactorGraph->at(0)->keys() == KeyVector({M(0)}));
405  EXPECT(remainingFactorGraph->at(1)->keys() == KeyVector({M(1), M(0)}));
406  EXPECT(remainingFactorGraph->at(2)->keys() == KeyVector({M(0), M(1)}));
407 }
408 
409 /* ****************************************************************************/
411  Switching self(3);
412  HybridNonlinearFactorGraph fg = self.nonlinearFactorGraph;
413 
414  {
415  HybridValues values(VectorValues(), DiscreteValues{{M(0), 0}, {M(1), 0}},
416  self.linearizationPoint);
417  // regression
418  EXPECT_DOUBLES_EQUAL(152.791759469, fg.error(values), 1e-9);
419  }
420  {
421  HybridValues values(VectorValues(), DiscreteValues{{M(0), 0}, {M(1), 1}},
422  self.linearizationPoint);
423  // regression
424  EXPECT_DOUBLES_EQUAL(151.598612289, fg.error(values), 1e-9);
425  }
426  {
427  HybridValues values(VectorValues(), DiscreteValues{{M(0), 1}, {M(1), 0}},
428  self.linearizationPoint);
429  // regression
430  EXPECT_DOUBLES_EQUAL(151.703972804, fg.error(values), 1e-9);
431  }
432  {
433  HybridValues values(VectorValues(), DiscreteValues{{M(0), 1}, {M(1), 1}},
434  self.linearizationPoint);
435  // regression
436  EXPECT_DOUBLES_EQUAL(151.609437912, fg.error(values), 1e-9);
437  }
438 }
439 
440 /* ****************************************************************************/
442  Switching self(3);
443 
444  // Get nonlinear factor graph and add linear factors to be holistic
445  HybridNonlinearFactorGraph fg = self.nonlinearFactorGraph;
446  fg.add(self.linearizedFactorGraph);
447 
448  // Optimize to get HybridValues
450  self.linearizedFactorGraph.eliminateSequential();
451  HybridValues hv = bn->optimize();
452 
453  // Print and verify
454  // fg.print();
455  // std::cout << "\n\n\n" << std::endl;
456  // fg.printErrors(
457  // HybridValues(hv.continuous(), DiscreteValues(),
458  // self.linearizationPoint));
459 }
460 
461 /****************************************************************************
462  * Test full elimination
463  */
464 TEST(HybridNonlinearFactorGraph, Full_Elimination) {
465  Switching self(3);
466 
467  auto linearizedFactorGraph = self.linearizedFactorGraph;
468 
469  // We first do a partial elimination
470  DiscreteBayesNet discreteBayesNet;
471 
472  {
473  // Create ordering.
475  for (size_t k = 0; k < self.K; k++) ordering.push_back(X(k));
476 
477  // Eliminate partially.
478  const auto [hybridBayesNet_partial, remainingFactorGraph_partial] =
479  linearizedFactorGraph.eliminatePartialSequential(ordering);
480 
481  DiscreteFactorGraph discrete_fg;
482  // TODO(Varun) Make this a function of HybridGaussianFactorGraph?
483  for (auto &factor : (*remainingFactorGraph_partial)) {
484  auto df = dynamic_pointer_cast<DiscreteFactor>(factor);
485  assert(df);
486  discrete_fg.push_back(df);
487  }
488 
489  ordering.clear();
490  for (size_t k = 0; k < self.K - 1; k++) ordering.push_back(M(k));
491  discreteBayesNet =
493  }
494 
495  // Create ordering.
497  for (size_t k = 0; k < self.K; k++) ordering.push_back(X(k));
498  for (size_t k = 0; k < self.K - 1; k++) ordering.push_back(M(k));
499 
500  // Eliminate partially.
501  HybridBayesNet::shared_ptr hybridBayesNet =
502  linearizedFactorGraph.eliminateSequential(ordering);
503 
504  CHECK(hybridBayesNet);
505  EXPECT_LONGS_EQUAL(5, hybridBayesNet->size());
506  // p(x0 | x1, m0)
507  EXPECT(hybridBayesNet->at(0)->frontals() == KeyVector{X(0)});
508  EXPECT(hybridBayesNet->at(0)->parents() == KeyVector({X(1), M(0)}));
509  // p(x1 | x2, m0, m1)
510  EXPECT(hybridBayesNet->at(1)->frontals() == KeyVector{X(1)});
511  EXPECT(hybridBayesNet->at(1)->parents() == KeyVector({X(2), M(0), M(1)}));
512  // p(x2 | m0, m1)
513  EXPECT(hybridBayesNet->at(2)->frontals() == KeyVector{X(2)});
514  EXPECT(hybridBayesNet->at(2)->parents() == KeyVector({M(0), M(1)}));
515  // P(m0 | m1)
516  EXPECT(hybridBayesNet->at(3)->frontals() == KeyVector{M(0)});
517  EXPECT(hybridBayesNet->at(3)->parents() == KeyVector({M(1)}));
518  EXPECT(
519  dynamic_pointer_cast<DiscreteConditional>(hybridBayesNet->at(3)->inner())
520  ->equals(*discreteBayesNet.at(0)));
521  // P(m1)
522  EXPECT(hybridBayesNet->at(4)->frontals() == KeyVector{M(1)});
523  EXPECT_LONGS_EQUAL(0, hybridBayesNet->at(4)->nrParents());
524  EXPECT(
525  dynamic_pointer_cast<DiscreteConditional>(hybridBayesNet->at(4)->inner())
526  ->equals(*discreteBayesNet.at(1)));
527 }
528 
529 /****************************************************************************
530  * Test printing
531  */
533  Switching self(3);
534 
535  auto linearizedFactorGraph = self.linearizedFactorGraph;
536 
537  // Create ordering.
539  for (size_t k = 0; k < self.K; k++) ordering.push_back(X(k));
540 
541  // Eliminate partially.
542  const auto [hybridBayesNet, remainingFactorGraph] =
543  linearizedFactorGraph.eliminatePartialSequential(ordering);
544 
545 #ifdef GTSAM_DT_MERGING
546  string expected_hybridFactorGraph = R"(
547 size: 7
548 factor 0:
549  A[x0] = [
550  10
551 ]
552  b = [ -10 ]
553  No noise model
554 factor 1:
555 HybridGaussianFactor
556 Hybrid [x0 x1; m0]{
557  Choice(m0)
558  0 Leaf :
559  A[x0] = [
560  -1
561 ]
562  A[x1] = [
563  1
564 ]
565  b = [ -1 ]
566  No noise model
567 
568  1 Leaf :
569  A[x0] = [
570  -1
571 ]
572  A[x1] = [
573  1
574 ]
575  b = [ -0 ]
576  No noise model
577 
578 }
579 factor 2:
580 HybridGaussianFactor
581 Hybrid [x1 x2; m1]{
582  Choice(m1)
583  0 Leaf :
584  A[x1] = [
585  -1
586 ]
587  A[x2] = [
588  1
589 ]
590  b = [ -1 ]
591  No noise model
592 
593  1 Leaf :
594  A[x1] = [
595  -1
596 ]
597  A[x2] = [
598  1
599 ]
600  b = [ -0 ]
601  No noise model
602 
603 }
604 factor 3:
605  A[x1] = [
606  10
607 ]
608  b = [ -10 ]
609  No noise model
610 factor 4:
611  A[x2] = [
612  10
613 ]
614  b = [ -10 ]
615  No noise model
616 factor 5: P( m0 ):
617  Leaf 0.5
618 
619 factor 6: P( m1 | m0 ):
620  Choice(m1)
621  0 Choice(m0)
622  0 0 Leaf 0.33333333
623  0 1 Leaf 0.6
624  1 Choice(m0)
625  1 0 Leaf 0.66666667
626  1 1 Leaf 0.4
627 
628 )";
629 #else
630  string expected_hybridFactorGraph = R"(
631 size: 7
632 factor 0:
633  A[x0] = [
634  10
635 ]
636  b = [ -10 ]
637  No noise model
638 factor 1:
639 Hybrid [x0 x1; m0]{
640  Choice(m0)
641  0 Leaf:
642  A[x0] = [
643  -1
644 ]
645  A[x1] = [
646  1
647 ]
648  b = [ -1 ]
649  No noise model
650 
651  1 Leaf:
652  A[x0] = [
653  -1
654 ]
655  A[x1] = [
656  1
657 ]
658  b = [ -0 ]
659  No noise model
660 
661 }
662 factor 2:
663 Hybrid [x1 x2; m1]{
664  Choice(m1)
665  0 Leaf:
666  A[x1] = [
667  -1
668 ]
669  A[x2] = [
670  1
671 ]
672  b = [ -1 ]
673  No noise model
674 
675  1 Leaf:
676  A[x1] = [
677  -1
678 ]
679  A[x2] = [
680  1
681 ]
682  b = [ -0 ]
683  No noise model
684 
685 }
686 factor 3:
687  A[x1] = [
688  10
689 ]
690  b = [ -10 ]
691  No noise model
692 factor 4:
693  A[x2] = [
694  10
695 ]
696  b = [ -10 ]
697  No noise model
698 factor 5: P( m0 ):
699  Choice(m0)
700  0 Leaf 0.5
701  1 Leaf 0.5
702 
703 factor 6: P( m1 | m0 ):
704  Choice(m1)
705  0 Choice(m0)
706  0 0 Leaf 0.33333333
707  0 1 Leaf 0.6
708  1 Choice(m0)
709  1 0 Leaf 0.66666667
710  1 1 Leaf 0.4
711 
712 )";
713 #endif
714 
715  EXPECT(assert_print_equal(expected_hybridFactorGraph, linearizedFactorGraph));
716 
717  // Expected output for hybridBayesNet.
718  string expected_hybridBayesNet = R"(
719 size: 3
720 conditional 0: Hybrid P( x0 | x1 m0)
721  Discrete Keys = (m0, 2),
722  logNormalizationConstant: 1.38862
723 
724  Choice(m0)
725  0 Leaf p(x0 | x1)
726  R = [ 10.0499 ]
727  S[x1] = [ -0.0995037 ]
728  d = [ -9.85087 ]
729  logNormalizationConstant: 1.38862
730  No noise model
731 
732  1 Leaf p(x0 | x1)
733  R = [ 10.0499 ]
734  S[x1] = [ -0.0995037 ]
735  d = [ -9.95037 ]
736  logNormalizationConstant: 1.38862
737  No noise model
738 
739 conditional 1: Hybrid P( x1 | x2 m0 m1)
740  Discrete Keys = (m0, 2), (m1, 2),
741  logNormalizationConstant: 1.3935
742 
743  Choice(m1)
744  0 Choice(m0)
745  0 0 Leaf p(x1 | x2)
746  R = [ 10.099 ]
747  S[x2] = [ -0.0990196 ]
748  d = [ -9.99901 ]
749  logNormalizationConstant: 1.3935
750  No noise model
751 
752  0 1 Leaf p(x1 | x2)
753  R = [ 10.099 ]
754  S[x2] = [ -0.0990196 ]
755  d = [ -9.90098 ]
756  logNormalizationConstant: 1.3935
757  No noise model
758 
759  1 Choice(m0)
760  1 0 Leaf p(x1 | x2)
761  R = [ 10.099 ]
762  S[x2] = [ -0.0990196 ]
763  d = [ -10.098 ]
764  logNormalizationConstant: 1.3935
765  No noise model
766 
767  1 1 Leaf p(x1 | x2)
768  R = [ 10.099 ]
769  S[x2] = [ -0.0990196 ]
770  d = [ -10 ]
771  logNormalizationConstant: 1.3935
772  No noise model
773 
774 conditional 2: Hybrid P( x2 | m0 m1)
775  Discrete Keys = (m0, 2), (m1, 2),
776  logNormalizationConstant: 1.38857
777 
778  Choice(m1)
779  0 Choice(m0)
780  0 0 Leaf p(x2)
781  R = [ 10.0494 ]
782  d = [ -10.1489 ]
783  mean: 1 elements
784  x2: -1.0099
785  logNormalizationConstant: 1.38857
786  No noise model
787 
788  0 1 Leaf p(x2)
789  R = [ 10.0494 ]
790  d = [ -10.1479 ]
791  mean: 1 elements
792  x2: -1.0098
793  logNormalizationConstant: 1.38857
794  No noise model
795 
796  1 Choice(m0)
797  1 0 Leaf p(x2)
798  R = [ 10.0494 ]
799  d = [ -10.0504 ]
800  mean: 1 elements
801  x2: -1.0001
802  logNormalizationConstant: 1.38857
803  No noise model
804 
805  1 1 Leaf p(x2)
806  R = [ 10.0494 ]
807  d = [ -10.0494 ]
808  mean: 1 elements
809  x2: -1
810  logNormalizationConstant: 1.38857
811  No noise model
812 
813 )";
814  EXPECT(assert_print_equal(expected_hybridBayesNet, *hybridBayesNet));
815 }
816 
817 /****************************************************************************
818  * Simple PlanarSLAM example test with 2 poses and 2 landmarks (each pose
819  * connects to 1 landmark) to expose issue with default decision tree creation
820  * in hybrid elimination. The hybrid factor is between the poses X0 and X1.
821  * The issue arises if we eliminate a landmark variable first since it is not
822  * connected to a HybridFactor.
823  */
824 TEST(HybridNonlinearFactorGraph, DefaultDecisionTree) {
826 
827  // Add a prior on pose x0 at the origin.
828  // A prior factor consists of a mean and a noise model (covariance matrix)
829  Pose2 prior(0.0, 0.0, 0.0); // prior mean is at origin
830  auto priorNoise = noiseModel::Diagonal::Sigmas(
831  Vector3(0.3, 0.3, 0.1)); // 30cm std on x,y, 0.1 rad on theta
833 
834  using PlanarMotionModel = BetweenFactor<Pose2>;
835 
836  // Add odometry factor
837  Pose2 odometry(2.0, 0.0, 0.0);
838  auto noise_model = noiseModel::Isotropic::Sigma(3, 1.0);
839  std::vector<NoiseModelFactor::shared_ptr> motion_models = {
840  std::make_shared<PlanarMotionModel>(X(0), X(1), Pose2(0, 0, 0),
841  noise_model),
842  std::make_shared<PlanarMotionModel>(X(0), X(1), odometry, noise_model)};
843  fg.emplace_shared<HybridNonlinearFactor>(DiscreteKey{M(1), 2}, motion_models);
844 
845  // Add Range-Bearing measurements to from X0 to L0 and X1 to L1.
846  // create a noise model for the landmark measurements
847  auto measurementNoise = noiseModel::Diagonal::Sigmas(
848  Vector2(0.1, 0.2)); // 0.1 rad std on bearing, 20cm on range
849  // create the measurement values - indices are (pose id, landmark id)
850  Rot2 bearing11 = Rot2::fromDegrees(45), bearing22 = Rot2::fromDegrees(90);
851  double range11 = std::sqrt(4.0 + 4.0), range22 = 2.0;
852 
853  // Add Bearing-Range factors
855  X(0), L(0), bearing11, range11, measurementNoise);
857  X(1), L(1), bearing22, range22, measurementNoise);
858 
859  // Create (deliberately inaccurate) initial estimate
860  Values initialEstimate;
861  initialEstimate.insert(X(0), Pose2(0.5, 0.0, 0.2));
862  initialEstimate.insert(X(1), Pose2(2.3, 0.1, -0.2));
863  initialEstimate.insert(L(0), Point2(1.8, 2.1));
864  initialEstimate.insert(L(1), Point2(4.1, 1.8));
865 
866  // We want to eliminate variables not connected to DCFactors first.
867  const Ordering ordering{L(0), L(1), X(0), X(1)};
868 
869  HybridGaussianFactorGraph linearized = *fg.linearize(initialEstimate);
870 
871  // This should NOT fail
872  const auto [hybridBayesNet, remainingFactorGraph] =
874  EXPECT_LONGS_EQUAL(4, hybridBayesNet->size());
875  EXPECT_LONGS_EQUAL(1, remainingFactorGraph->size());
876 }
877 
892  const std::vector<double> &means, const std::vector<double> &sigmas,
893  DiscreteKey &m1, double x0_measurement, double measurement_noise = 1e-3) {
894  auto model0 = noiseModel::Isotropic::Sigma(1, sigmas[0]);
895  auto model1 = noiseModel::Isotropic::Sigma(1, sigmas[1]);
896  auto prior_noise = noiseModel::Isotropic::Sigma(1, measurement_noise);
897 
898  auto f0 =
899  std::make_shared<BetweenFactor<double>>(X(0), X(1), means[0], model0);
900  auto f1 =
901  std::make_shared<BetweenFactor<double>>(X(0), X(1), means[1], model1);
902 
903  // Create HybridNonlinearFactor
904  // We take negative since we want
905  // the underlying scalar to be log(\sqrt(|2πΣ|))
906  std::vector<NonlinearFactorValuePair> factors{{f0, 0.0}, {f1, 0.0}};
907 
908  HybridNonlinearFactor mixtureFactor(m1, factors);
909 
911  hfg.push_back(mixtureFactor);
912 
913  hfg.push_back(PriorFactor<double>(X(0), x0_measurement, prior_noise));
914 
915  return hfg;
916 }
917 } // namespace test_relinearization
918 
919 /* ************************************************************************* */
928  using namespace test_relinearization;
929 
930  DiscreteKey m1(M(1), 2);
931 
932  Values values;
933  double x0 = 0.0, x1 = 1.75;
934  values.insert(X(0), x0);
935  values.insert(X(1), x1);
936 
937  std::vector<double> means = {0.0, 2.0}, sigmas = {1e-0, 1e-0};
938 
940 
941  {
942  auto bn = hfg.linearize(values)->eliminateSequential();
943  HybridValues actual = bn->optimize();
944 
946  VectorValues{{X(0), Vector1(0.0)}, {X(1), Vector1(-1.75)}},
947  DiscreteValues{{M(1), 0}});
948 
949  EXPECT(assert_equal(expected, actual));
950 
951  DiscreteValues dv0{{M(1), 0}};
952  VectorValues cont0 = bn->optimize(dv0);
953  double error0 = bn->error(HybridValues(cont0, dv0));
954 
955  // TODO(Varun) Perform importance sampling to estimate error?
956 
957  // regression
958  EXPECT_DOUBLES_EQUAL(0.69314718056, error0, 1e-9);
959 
960  DiscreteValues dv1{{M(1), 1}};
961  VectorValues cont1 = bn->optimize(dv1);
962  double error1 = bn->error(HybridValues(cont1, dv1));
963  EXPECT_DOUBLES_EQUAL(error0, error1, 1e-9);
964  }
965 
966  {
967  // Add measurement on x1
968  auto prior_noise = noiseModel::Isotropic::Sigma(1, 1e-3);
969  hfg.push_back(PriorFactor<double>(X(1), means[1], prior_noise));
970 
971  auto bn = hfg.linearize(values)->eliminateSequential();
972  HybridValues actual = bn->optimize();
973 
975  VectorValues{{X(0), Vector1(0.0)}, {X(1), Vector1(0.25)}},
976  DiscreteValues{{M(1), 1}});
977 
978  EXPECT(assert_equal(expected, actual));
979 
980  {
981  DiscreteValues dv{{M(1), 0}};
982  VectorValues cont = bn->optimize(dv);
983  double error = bn->error(HybridValues(cont, dv));
984  // regression
985  EXPECT_DOUBLES_EQUAL(2.12692448787, error, 1e-9);
986  }
987  {
988  DiscreteValues dv{{M(1), 1}};
989  VectorValues cont = bn->optimize(dv);
990  double error = bn->error(HybridValues(cont, dv));
991  // regression
992  EXPECT_DOUBLES_EQUAL(0.126928487854, error, 1e-9);
993  }
994  }
995 }
996 
997 /* ************************************************************************* */
1005 TEST(HybridNonlinearFactorGraph, DifferentCovariances) {
1006  using namespace test_relinearization;
1007 
1008  DiscreteKey m1(M(1), 2);
1009 
1010  Values values;
1011  double x0 = 1.0, x1 = 1.0;
1012  values.insert(X(0), x0);
1013  values.insert(X(1), x1);
1014 
1015  std::vector<double> means = {0.0, 0.0}, sigmas = {1e2, 1e-2};
1016 
1017  // Create FG with HybridNonlinearFactor and prior on X1
1019  // Linearize
1020  auto hgfg = hfg.linearize(values);
1021  // and eliminate
1022  auto hbn = hgfg->eliminateSequential();
1023 
1024  VectorValues cv;
1025  cv.insert(X(0), Vector1(0.0));
1026  cv.insert(X(1), Vector1(0.0));
1027 
1028  // Check that the error values at the MLE point μ.
1029  AlgebraicDecisionTree<Key> errorTree = hbn->errorTree(cv);
1030 
1031  DiscreteValues dv0{{M(1), 0}};
1032  DiscreteValues dv1{{M(1), 1}};
1033 
1034  // regression
1035  EXPECT_DOUBLES_EQUAL(9.90348755254, errorTree(dv0), 1e-9);
1036  EXPECT_DOUBLES_EQUAL(0.69314718056, errorTree(dv1), 1e-9);
1037 
1038  DiscreteConditional expected_m1(m1, "0.5/0.5");
1039  DiscreteConditional actual_m1 = *(hbn->at(2)->asDiscrete());
1040 
1041  EXPECT(assert_equal(expected_m1, actual_m1));
1042 }
1043 
1045  using namespace test_relinearization;
1046 
1047  DiscreteKey m1(M(1), 2);
1048 
1049  Values values;
1050  double x0 = 0.0, x1 = 0.8;
1051  values.insert(X(0), x0);
1052  values.insert(X(1), x1);
1053 
1054  std::vector<double> means = {0.0, 1.0}, sigmas = {1e-2, 1e-2};
1055 
1056  double prior_sigma = 1e-2;
1057  // Create FG with HybridNonlinearFactor and prior on X1
1059  CreateFactorGraph(means, sigmas, m1, 0.0, prior_sigma);
1061  X(1), 1.2, noiseModel::Isotropic::Sigma(1, prior_sigma)));
1062 
1063  // Linearize
1064  auto hgfg = hfg.linearize(values);
1065  // and eliminate
1066  auto hbn = hgfg->eliminateSequential();
1067 
1068  HybridValues delta = hbn->optimize();
1069  values = values.retract(delta.continuous());
1070 
1071  Values expected_first_result;
1072  expected_first_result.insert(X(0), 0.0666666666667);
1073  expected_first_result.insert(X(1), 1.13333333333);
1074  EXPECT(assert_equal(expected_first_result, values));
1075 
1076  // Re-linearize
1077  hgfg = hfg.linearize(values);
1078  // and eliminate
1079  hbn = hgfg->eliminateSequential();
1080  delta = hbn->optimize();
1081  HybridValues result(delta.continuous(), delta.discrete(),
1082  values.retract(delta.continuous()));
1083 
1084  HybridValues expected_result(
1085  VectorValues{{X(0), Vector1(0)}, {X(1), Vector1(0)}},
1086  DiscreteValues{{M(1), 1}}, expected_first_result);
1087  EXPECT(assert_equal(expected_result, result));
1088 }
1089 
1090 /* *************************************************************************
1091  */
1092 int main() {
1093  TestResult tr;
1094  return TestRegistry::runAllTests(tr);
1095 }
1096 /* *************************************************************************
1097  */
TestRegistry::runAllTests
static int runAllTests(TestResult &result)
Definition: TestRegistry.cpp:27
gtsam::EliminateableFactorGraph::eliminateSequential
std::shared_ptr< BayesNetType > eliminateSequential(OptionalOrderingType orderingType={}, const Eliminate &function=EliminationTraitsType::DefaultEliminate, OptionalVariableIndex variableIndex={}) const
Definition: EliminateableFactorGraph-inst.h:29
Pose2.h
2D Pose
DiscreteBayesNet.h
GaussianFactorGraph.h
Linear Factor Graph where all factors are Gaussians.
gtsam::HybridValues
Definition: HybridValues.h:37
gtsam::GaussianFactorGraph::shared_ptr
std::shared_ptr< This > shared_ptr
shared_ptr to this class
Definition: GaussianFactorGraph.h:82
gtsam::DiscreteFactorGraph
Definition: DiscreteFactorGraph.h:98
test_constructor::f1
auto f1
Definition: testHybridNonlinearFactor.cpp:56
gtsam::Values::size
size_t size() const
Definition: Values.h:178
s
RealScalar s
Definition: level1_cplx_impl.h:126
e
Array< double, 1, 3 > e(1./3., 0.5, 2.)
gtsam::FactorGraph::error
double error(const HybridValues &values) const
Definition: FactorGraph-inst.h:66
EXPECT_LONGS_EQUAL
#define EXPECT_LONGS_EQUAL(expected, actual)
Definition: Test.h:154
EXPECT
#define EXPECT(condition)
Definition: Test.h:150
gtsam::Vector2
Eigen::Vector2d Vector2
Definition: Vector.h:42
HybridNonlinearFactorGraph.h
Nonlinear hybrid factor graph that uses type erasure.
test_constructor::sigmas
Vector1 sigmas
Definition: testHybridNonlinearFactor.cpp:52
TestHarness.h
DiscreteFactorGraph.h
gtsam::HybridNonlinearFactorGraph
Definition: HybridNonlinearFactorGraph.h:33
gtsam::HybridValues::retract
HybridValues retract(const VectorValues &delta) const
Definition: HybridValues.cpp:79
gtsam::VectorValues::insert
iterator insert(const std::pair< Key, Vector > &key_value)
Definition: VectorValues.cpp:88
Switching.h
gtsam::assert_print_equal
bool assert_print_equal(const std::string &expected, const V &actual, const std::string &s="")
Definition: TestableAssertions.h:352
simple_graph::factors
const GaussianFactorGraph factors
Definition: testJacobianFactor.cpp:213
simple::graph2
NonlinearFactorGraph graph2()
Definition: testInitializePose3.cpp:72
gtsam::PriorFactor
Definition: nonlinear/PriorFactor.h:30
different_sigmas::values
HybridValues values
Definition: testHybridBayesNet.cpp:195
gtsam::NonlinearFactorGraph::linearize
std::shared_ptr< GaussianFactorGraph > linearize(const Values &linearizationPoint) const
Linearize a nonlinear factor graph.
Definition: NonlinearFactorGraph.cpp:239
main
int main()
Definition: testHybridNonlinearFactorGraph.cpp:1092
X
#define X
Definition: icosphere.cpp:20
gtsam::DiscreteKeys
DiscreteKeys is a set of keys that can be assembled using the & operator.
Definition: DiscreteKey.h:41
GaussianBayesNet.h
Chordal Bayes Net, the result of eliminating a factor graph.
gtsam::Vector3
Eigen::Vector3d Vector3
Definition: Vector.h:43
gtsam::EliminationTree
Definition: EliminationTree.h:51
gtsam::means
Point2Pair means(const std::vector< Point2Pair > &abPointPairs)
Calculate the two means of a set of Point2 pairs.
Definition: Point2.cpp:116
gtsam::KeyVector
FastVector< Key > KeyVector
Define collection type once and for all - also used in wrappers.
Definition: Key.h:92
result
Values result
Definition: OdometryOptimize.cpp:8
test_relinearization::CreateFactorGraph
static HybridNonlinearFactorGraph CreateFactorGraph(const std::vector< double > &means, const std::vector< double > &sigmas, DiscreteKey &m1, double x0_measurement, double measurement_noise=1e-3)
Create a Factor Graph by directly specifying all the factors instead of creating conditionals first....
Definition: testHybridNonlinearFactorGraph.cpp:891
test_motion::noise_model
auto noise_model
Definition: testHybridNonlinearFactorGraph.cpp:119
utilities.h
TestableAssertions.h
Provides additional testing facilities for common data structures.
gtsam::AlgebraicDecisionTree< Key >
gtsam::EliminationTree::roots
const FastVector< sharedNode > & roots() const
Definition: EliminationTree.h:152
gtsam::Switching
ϕ(X(0)) .. ϕ(X(k),X(k+1)) .. ϕ(X(k);z_k) .. ϕ(M(0)) .. ϕ(M(k),M(k+1))
Definition: Switching.h:122
gtsam::FactorGraph::at
const sharedFactor at(size_t i) const
Definition: FactorGraph.h:306
odometry
Pose2 odometry(2.0, 0.0, 0.0)
gtsam::GaussianFactorGraph
Definition: GaussianFactorGraph.h:73
Vector1
Eigen::Matrix< double, 1, 1 > Vector1
Definition: testEvent.cpp:33
PriorFactor.h
gtsam::FactorGraph::resize
virtual void resize(size_t size)
Definition: FactorGraph.h:367
HybridFactor.h
gtsam.examples.PlanarManipulatorExample.delta
def delta(g0, g1)
Definition: PlanarManipulatorExample.py:45
gtsam::DiscreteBayesNet
Definition: DiscreteBayesNet.h:38
gtsam::VectorValues
Definition: VectorValues.h:74
test_constructor::f0
auto f0
Definition: testHybridNonlinearFactor.cpp:55
BetweenFactor.h
x1
Pose3 x1
Definition: testPose3.cpp:663
gtsam::noiseModel::Isotropic
Definition: NoiseModel.h:541
gtsam::EliminateableFactorGraph::eliminatePartialSequential
std::pair< std::shared_ptr< BayesNetType >, std::shared_ptr< FactorGraphType > > eliminatePartialSequential(const Ordering &ordering, const Eliminate &function=EliminationTraitsType::DefaultEliminate, OptionalVariableIndex variableIndex={}) const
Definition: EliminateableFactorGraph-inst.h:151
gtsam::EliminateDiscrete
std::pair< DiscreteConditional::shared_ptr, DecisionTreeFactor::shared_ptr > EliminateDiscrete(const DiscreteFactorGraph &factors, const Ordering &frontalKeys)
Main elimination function for DiscreteFactorGraph.
Definition: DiscreteFactorGraph.cpp:205
gtsam::BearingRangeFactor
Definition: sam/BearingRangeFactor.h:34
gtsam::NonlinearFactorGraph
Definition: NonlinearFactorGraph.h:55
zero
EIGEN_DONT_INLINE Scalar zero()
Definition: svd_common.h:296
cholesky::expected
Matrix expected
Definition: testMatrix.cpp:971
gtsam::NonlinearFactorGraph::addPrior
void addPrior(Key key, const T &prior, const SharedNoiseModel &model=nullptr)
Definition: NonlinearFactorGraph.h:199
x0
static Symbol x0('x', 0)
gtsam::HybridGaussianFactorGraph
Definition: HybridGaussianFactorGraph.h:104
L
MatrixXd L
Definition: LLT_example.cpp:6
test_motion::m1
gtsam::DiscreteKey m1(M(1), 2)
gtsam::HybridValues::insert
void insert(Key j, const Vector &value)
Definition: HybridValues.cpp:85
gtsam::Point2
Vector2 Point2
Definition: Point2.h:32
gtsam::EliminateHybrid
std::pair< HybridConditional::shared_ptr, std::shared_ptr< Factor > > EliminateHybrid(const HybridGaussianFactorGraph &factors, const Ordering &keys)
Main elimination function for HybridGaussianFactorGraph.
Definition: HybridGaussianFactorGraph.cpp:414
priorNoise
auto priorNoise
Definition: doc/Code/OdometryExample.cpp:6
test_relinearization
Definition: testHybridNonlinearFactorGraph.cpp:878
EXPECT_DOUBLES_EQUAL
#define EXPECT_DOUBLES_EQUAL(expected, actual, threshold)
Definition: Test.h:161
gtsam::HybridEliminationTree
Definition: HybridEliminationTree.h:31
ordering
static enum @1096 ordering
TEST
TEST(HybridNonlinearFactorGraph, GaussianFactorGraph)
Definition: testHybridNonlinearFactorGraph.cpp:52
TestResult
Definition: TestResult.h:26
gtsam::FactorGraph::size
size_t size() const
Definition: FactorGraph.h:297
batchGFG
GaussianFactorGraph::shared_ptr batchGFG(double between, Values linearizationPoint)
Definition: testHybridNonlinearFactorGraph.cpp:333
HybridEliminationTree.h
HybridNonlinearFactor.h
A set of nonlinear factors indexed by a set of discrete keys.
gtsam::Rot2
Definition: Rot2.h:35
gtsam::DiscreteConditional
Definition: DiscreteConditional.h:37
gtsam
traits
Definition: chartTesting.h:28
error
static double error
Definition: testRot3.cpp:37
NoiseModel.h
test_motion
Definition: testHybridNonlinearFactorGraph.cpp:117
NonlinearFactorGraph.h
Factor Graph consisting of non-linear factors.
gtsam::DiscreteValues
Definition: DiscreteValues.h:34
gtsam::FactorGraph::push_back
IsDerived< DERIVEDFACTOR > push_back(std::shared_ptr< DERIVEDFACTOR > factor)
Add a factor directly using a shared_ptr.
Definition: FactorGraph.h:147
DiscreteDistribution.h
gtsam::Values
Definition: Values.h:65
CHECK
#define CHECK(condition)
Definition: Test.h:108
gtsam::DiscreteKey
std::pair< Key, size_t > DiscreteKey
Definition: DiscreteKey.h:38
std
Definition: BFloat16.h:88
gtsam::FactorGraph::end
const_iterator end() const
Definition: FactorGraph.h:342
gtsam::Values::insert
void insert(Key j, const Value &val)
Definition: Values.cpp:155
gtsam::HybridNonlinearFactorGraph::linearize
std::shared_ptr< HybridGaussianFactorGraph > linearize(const Values &continuousValues) const
Linearize all the continuous factors in the HybridNonlinearFactorGraph.
Definition: HybridNonlinearFactorGraph.cpp:139
gtsam::testing::between
T between(const T &t1, const T &t2)
Definition: lieProxies.h:36
gtsam::assert_equal
bool assert_equal(const Matrix &expected, const Matrix &actual, double tol)
Definition: Matrix.cpp:40
gtsam::FactorGraph::begin
const_iterator begin() const
Definition: FactorGraph.h:339
gtsam::Switching::linearizedFactorGraph
HybridGaussianFactorGraph linearizedFactorGraph
Definition: Switching.h:126
different_sigmas::prior
const auto prior
Definition: testHybridBayesNet.cpp:188
test_motion::components
std::vector< NoiseModelFactor::shared_ptr > components
Definition: testHybridNonlinearFactorGraph.cpp:120
gtsam::HybridBayesNet::shared_ptr
std::shared_ptr< HybridBayesNet > shared_ptr
Definition: HybridBayesNet.h:41
gtsam::FactorGraph::add
IsDerived< DERIVEDFACTOR > add(std::shared_ptr< DERIVEDFACTOR > factor)
add is a synonym for push_back.
Definition: FactorGraph.h:171
graph
NonlinearFactorGraph graph
Definition: doc/Code/OdometryExample.cpp:2
model1
noiseModel::Isotropic::shared_ptr model1
Definition: testEssentialMatrixFactor.cpp:26
gtsam::Ordering
Definition: inference/Ordering.h:33
ceres::sqrt
Jet< T, N > sqrt(const Jet< T, N > &f)
Definition: jet.h:418
i
int i
Definition: BiCGSTAB_step_by_step.cpp:9
gtsam::HybridNonlinearFactor
Implementation of a discrete-conditioned hybrid factor.
Definition: HybridNonlinearFactor.h:58
gtsam::FactorGraph::emplace_shared
IsDerived< DERIVEDFACTOR > emplace_shared(Args &&... args)
Emplace a shared pointer to factor of given type.
Definition: FactorGraph.h:153
gtsam::Pose2
Definition: Pose2.h:39
BearingRangeFactor.h
a single factor contains both the bearing and the range to prevent handle to pair bearing and range f...
gtsam::BetweenFactor
Definition: BetweenFactor.h:40
M
Matrix< RealScalar, Dynamic, Dynamic > M
Definition: bench_gemm.cpp:51


gtsam
Author(s):
autogenerated on Fri Oct 4 2024 03:08:41