testGaussianFactorGraph.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 
26 #include <gtsam/base/debug.h>
28 
31 
32 using namespace std;
33 using namespace gtsam;
34 
35 typedef std::tuple<size_t, size_t, double> SparseTriplet;
37  if (get<0>(a) == get<0>(b) && get<1>(a) == get<1>(b) &&
38  get<2>(a) == get<2>(b)) return true;
39 
40  cout << "not equal:" << endl;
41  cout << "\texpected: "
42  "(" << get<0>(a) << ", " << get<1>(a) << ") = " << get<2>(a) << endl;
43  cout << "\tactual: "
44  "(" << get<0>(b) << ", " << get<1>(b) << ") = " << get<2>(b) << endl;
45  return false;
46 }
47 
48 /* ************************************************************************* */
49 TEST(GaussianFactorGraph, initialization) {
50  // Create empty graph
52  SharedDiagonal unit2 = noiseModel::Unit::Create(2);
53 
54  fg.emplace_shared<JacobianFactor>(0, 10*I_2x2, -1.0*Vector::Ones(2), unit2);
55  fg.emplace_shared<JacobianFactor>(0, -10*I_2x2,1, 10*I_2x2, Vector2(2.0, -1.0), unit2);
56  fg.emplace_shared<JacobianFactor>(0, -5*I_2x2, 2, 5*I_2x2, Vector2(0.0, 1.0), unit2);
57  fg.emplace_shared<JacobianFactor>(1, -5*I_2x2, 2, 5*I_2x2, Vector2(-1.0, 1.5), unit2);
58 
59  EXPECT_LONGS_EQUAL(4, (long)fg.size());
60 
61  // Test sparse, which takes a vector and returns a matrix, used in MATLAB
62  // Note that this the augmented vector and the RHS is in column 7
63  Matrix expectedIJS =
64  (Matrix(3, 21) <<
65  1., 2., 1., 2., 3., 4., 3., 4., 3., 4., 5., 6., 5., 6., 6., 7., 8., 7., 8., 7., 8.,
66  1., 2., 7., 7., 1., 2., 3., 4., 7., 7., 1., 2., 5., 6., 7., 3., 4., 5., 6., 7., 7.,
67  10., 10., -1., -1., -10., -10., 10., 10., 2., -1., -5., -5., 5., 5.,
68  1., -5., -5., 5., 5., -1., 1.5).finished();
69  Matrix actualIJS = fg.sparseJacobian_();
70  EQUALITY(expectedIJS, actualIJS);
71 }
72 
73 /* ************************************************************************* */
74 TEST(GaussianFactorGraph, sparseJacobian) {
75  // Create factor graph:
76  // x1 x2 x3 x4 x5 b
77  // 1 2 3 0 0 4
78  // 5 6 7 0 0 8
79  // 9 10 0 11 12 13
80  // 0 0 0 14 15 16
81 
82  // Expected
83  Matrix expected = (Matrix(16, 3) <<
84  1., 1., 2.,
85  1., 2., 4.,
86  1., 3., 6.,
87  2., 1.,10.,
88  2., 2.,12.,
89  2., 3.,14.,
90  1., 6., 8.,
91  2., 6.,16.,
92  3., 1.,18.,
93  3., 2.,20.,
94  3., 4.,22.,
95  3., 5.,24.,
96  4., 4.,28.,
97  4., 5.,30.,
98  3., 6.,26.,
99  4., 6.,32.).finished();
100 
101  // expected: in matlab format - NOTE the transpose!)
102  Matrix expectedMatlab = expected.transpose();
103 
105  SharedDiagonal model = noiseModel::Isotropic::Sigma(2, 0.5);
106  const Key x123 = 0, x45 = 1;
107  gfg.add(x123, (Matrix(2, 3) << 1, 2, 3, 5, 6, 7).finished(),
108  Vector2(4, 8), model);
109  gfg.add(x123, (Matrix(2, 3) << 9, 10, 0, 0, 0, 0).finished(),
110  x45, (Matrix(2, 2) << 11, 12, 14, 15.).finished(),
111  Vector2(13, 16), model);
112 
113  Matrix actual = gfg.sparseJacobian_();
114 
115  EXPECT(assert_equal(expectedMatlab, actual));
116 
117  // SparseTriplets
118  auto boostActual = gfg.sparseJacobian();
119  // check the triplets size...
120  EXPECT_LONGS_EQUAL(16, boostActual.size());
121  // check content
122  for (int i = 0; i < 16; i++) {
124  SparseTriplet(expected(i, 0) - 1, expected(i, 1) - 1, expected(i, 2)),
125  boostActual.at(i)));
126  }
127 }
128 
129 /* ************************************************************************* */
131  // Create factor graph:
132  // x1 x2 x3 x4 x5 b
133  // 1 2 3 0 0 4
134  // 5 6 7 0 0 8
135  // 9 10 0 11 12 13
136  // 0 0 0 14 15 16
137 
138  Matrix A00 = (Matrix(2, 3) << 1, 2, 3, 5, 6, 7).finished();
139  Matrix A10 = (Matrix(2, 3) << 9, 10, 0, 0, 0, 0).finished();
140  Matrix A11 = (Matrix(2, 2) << 11, 12, 14, 15).finished();
141 
143  SharedDiagonal model = noiseModel::Unit::Create(2);
144  gfg.add(0, A00, Vector2(4., 8.), model);
145  gfg.add(0, A10, 1, A11, Vector2(13., 16.), model);
146 
147  Matrix Ab(4, 6);
148  Ab << 1, 2, 3, 0, 0, 4, 5, 6, 7, 0, 0, 8, 9, 10, 0, 11, 12, 13, 0, 0, 0, 14, 15, 16;
149 
150  // augmented versions
152  EXPECT(assert_equal(Ab.transpose() * Ab, gfg.augmentedHessian()));
153 
154  // jacobian
155  Matrix A = Ab.leftCols(Ab.cols() - 1);
156  Vector b = Ab.col(Ab.cols() - 1);
157  const auto [actualA, actualb] = gfg.jacobian();
158  EXPECT(assert_equal(A, actualA));
159  EXPECT(assert_equal(b, actualb));
160 
161  // hessian
162  Matrix L = A.transpose() * A;
163  Vector eta = A.transpose() * b;
164  const auto [actualL, actualEta] = gfg.hessian();
165  EXPECT(assert_equal(L, actualL));
166  EXPECT(assert_equal(eta, actualEta));
167 
168  // hessianBlockDiagonal
169  VectorValues expectLdiagonal; // Make explicit that diagonal is sum-squares of columns
170  expectLdiagonal.insert(0, Vector3(1 + 25 + 81, 4 + 36 + 100, 9 + 49));
171  expectLdiagonal.insert(1, Vector2(121 + 196, 144 + 225));
172  EXPECT(assert_equal(expectLdiagonal, gfg.hessianDiagonal()));
173 
174  // hessianBlockDiagonal
175  map<Key, Matrix> actualBD = gfg.hessianBlockDiagonal();
176  LONGS_EQUAL(2, actualBD.size());
177  EXPECT(assert_equal(A00.transpose() * A00 + A10.transpose() * A10, actualBD[0]));
178  EXPECT(assert_equal(A11.transpose() * A11, actualBD[1]));
179 }
180 
181 /* ************************************************************************* */
185  Key x1 = 2, x2 = 0, l1 = 1;
186  SharedDiagonal unit2 = noiseModel::Unit::Create(2);
187  // linearized prior on x1: c[_x1_]+x1=0 i.e. x1=-c[_x1_]
188  fg.emplace_shared<JacobianFactor>(x1, 10 * I_2x2, -1.0 * Vector::Ones(2), unit2);
189  // odometry between x1 and x2: x2-x1=[0.2;-0.1]
190  fg.emplace_shared<JacobianFactor>(x2, 10 * I_2x2, x1, -10 * I_2x2, Vector2(2.0, -1.0), unit2);
191  // measurement between x1 and l1: l1-x1=[0.0;0.2]
192  fg.emplace_shared<JacobianFactor>(l1, 5 * I_2x2, x1, -5 * I_2x2, Vector2(0.0, 1.0), unit2);
193  // measurement between x2 and l1: l1-x2=[-0.2;0.3]
194  fg.emplace_shared<JacobianFactor>(x2, -5 * I_2x2, l1, 5 * I_2x2, Vector2(-1.0, 1.5), unit2);
195  return fg;
196 }
197 
198 /* ************************************************************************* */
201 
202  // Construct expected gradient
203  // 2*f(x) = 100*(x1+c[X(1)])^2 + 100*(x2-x1-[0.2;-0.1])^2 + 25*(l1-x1-[0.0;0.2])^2 +
204  // 25*(l1-x2-[-0.2;0.3])^2
205  // worked out: df/dx1 = 100*[0.1;0.1] + 100*[0.2;-0.1]) + 25*[0.0;0.2] = [10+20;10-10+5] = [30;5]
206  VectorValues expected{{1, Vector2(5.0, -12.5)},
207  {2, Vector2(30.0, 5.0)},
208  {0, Vector2(-25.0, 17.5)}};
209 
210  // Check the gradient at delta=0
211  VectorValues zero = VectorValues::Zero(expected);
212  VectorValues actual = fg.gradient(zero);
213  EXPECT(assert_equal(expected, actual));
215 
216  // Check the gradient at the solution (should be zero)
217  VectorValues solution = fg.optimize();
218  VectorValues actual2 = fg.gradient(solution);
219  EXPECT(assert_equal(VectorValues::Zero(solution), actual2));
220 }
221 
222 /* ************************************************************************* */
223 TEST(GaussianFactorGraph, transposeMultiplication) {
225 
226  Errors e = {Vector2(0.0, 0.0), Vector2(15.0, 0.0), Vector2(0.0, -5.0),
227  Vector2(-7.5, -5.0)};
228 
230  expected.insert(1, Vector2(-37.5, -50.0));
231  expected.insert(2, Vector2(-150.0, 25.0));
232  expected.insert(0, Vector2(187.5, 25.0));
233 
234  VectorValues actual = A.transposeMultiply(e);
235  EXPECT(assert_equal(expected, actual));
236 }
237 
238 /* ************************************************************************* */
239 TEST(GaussianFactorGraph, eliminate_empty) {
240  // eliminate an empty factor
242  gfg.add(JacobianFactor());
243  const auto [actualBN, remainingGFG] = gfg.eliminatePartialSequential(Ordering());
244 
245  // expected Bayes net is empty
246  GaussianBayesNet expectedBN;
247 
248  // expected remaining graph should be the same as the original, still containing the empty factor
249  GaussianFactorGraph expectedLF = gfg;
250 
251  // check if the result matches
252  EXPECT(assert_equal(*actualBN, expectedBN));
253  EXPECT(assert_equal(*remainingGFG, expectedLF));
254 }
255 
256 /* ************************************************************************* */
259  const auto [A, b] = gfg.jacobian();
260  const auto [AtA, eta] = gfg.hessian();
261  EXPECT(assert_equal(A.transpose() * A, AtA));
262  EXPECT(assert_equal(A.transpose() * b, eta));
263  Matrix expectedAtA(6, 6);
264  expectedAtA << 125, 0, -25, 0, -100, 0, //
265  0, 125, 0, -25, 0, -100, //
266  -25, 0, 50, 0, -25, 0, //
267  0, -25, 0, 50, 0, -25, //
268  -100, 0, -25, 0, 225, 0, //
269  0, -100, 0, -25, 0, 225;
270  EXPECT(assert_equal(expectedAtA, AtA));
271 }
272 
273 /* ************************************************************************* */
274 TEST(GaussianFactorGraph, multiplyHessianAdd) {
276 
277  const VectorValues x{{0, Vector2(1, 2)}, {1, Vector2(3, 4)}, {2, Vector2(5, 6)}};
278 
280  expected.insert(0, Vector2(-450, -450));
281  expected.insert(1, Vector2(0, 0));
282  expected.insert(2, Vector2(950, 1050));
283 
284  VectorValues actual;
285  gfg.multiplyHessianAdd(1.0, x, actual);
286  EXPECT(assert_equal(expected, actual));
287 
288  // now, do it with non-zero y
289  gfg.multiplyHessianAdd(1.0, x, actual);
290  EXPECT(assert_equal(2 * expected, actual));
291 }
292 
293 /* ************************************************************************* */
296  gfg.emplace_shared<HessianFactor>(1, 2, 100 * I_2x2, Z_2x2, Vector2(0.0, 1.0),
297  400 * I_2x2, Vector2(1.0, 1.0), 3.0);
298  return gfg;
299 }
300 
301 /* ************************************************************************* */
302 TEST(GaussianFactorGraph, multiplyHessianAdd2) {
304 
305  // brute force
306  const auto [AtA, eta] = gfg.hessian();
307  Vector X(6);
308  X << 1, 2, 3, 4, 5, 6;
309  Vector Y(6);
310  Y << -450, -450, 300, 400, 2950, 3450;
311  EXPECT(assert_equal(Y, AtA * X));
312 
313  const VectorValues x {{0, Vector2(1, 2)}, {1, Vector2(3, 4)}, {2, Vector2(5, 6)}};
315  expected.insert(0, Vector2(-450, -450));
316  expected.insert(1, Vector2(300, 400));
317  expected.insert(2, Vector2(2950, 3450));
318 
319  VectorValues actual;
320  gfg.multiplyHessianAdd(1.0, x, actual);
321  EXPECT(assert_equal(expected, actual));
322 
323  // now, do it with non-zero y
324  gfg.multiplyHessianAdd(1.0, x, actual);
325  EXPECT(assert_equal(2 * expected, actual));
326 }
327 
328 /* ************************************************************************* */
329 TEST(GaussianFactorGraph, matricesMixed) {
331  const auto [A, b] = gfg.jacobian(); // incorrect !
332  const auto [AtA, eta] = gfg.hessian(); // correct
333  EXPECT(assert_equal(A.transpose() * A, AtA));
334  Vector expected = -(Vector(6) << -25, 17.5, 5, -13.5, 29, 4).finished();
335  EXPECT(assert_equal(expected, eta));
336  EXPECT(assert_equal(A.transpose() * b, eta));
337 }
338 
339 /* ************************************************************************* */
340 TEST(GaussianFactorGraph, gradientAtZero) {
343  VectorValues actual = gfg.gradientAtZero();
344  expected.insert(0, Vector2(-25, 17.5));
345  expected.insert(1, Vector2(5, -13.5));
346  expected.insert(2, Vector2(29, 4));
347  EXPECT(assert_equal(expected, actual));
348 }
349 
350 /* ************************************************************************* */
352  // 2 variables, frontal has dim=4
353  VerticalBlockMatrix blockMatrix(KeyVector{4, 2, 1}, 4);
354  blockMatrix.matrix() << 1.0, 0.0, 2.0, 0.0, 3.0, 0.0, 0.1, 0.0, 1.0, 0.0, 2.0, 0.0, 3.0, 0.2, 0.0,
355  0.0, 3.0, 0.0, 4.0, 0.0, 0.3, 0.0, 0.0, 0.0, 3.0, 0.0, 4.0, 0.4;
356  GaussianConditional cg(KeyVector{1, 2}, 1, blockMatrix);
357 
359  init_graph.push_back(GaussianFactor::shared_ptr());
360  init_graph.push_back(GaussianConditional(cg));
361 
362  GaussianFactorGraph exp_graph =
363  createGaussianFactorGraphWithHessianFactor(); // Created separately
364  exp_graph.push_back(GaussianFactor::shared_ptr());
365  exp_graph.push_back(GaussianConditional(cg));
366 
367  GaussianFactorGraph actCloned = init_graph.clone();
368  EXPECT(assert_equal(init_graph, actCloned)); // Same as the original version
369 
370  // Apply an in-place change to init_graph and compare
371  JacobianFactor::shared_ptr jacFactor0 =
372  std::dynamic_pointer_cast<JacobianFactor>(init_graph.at(0));
373  CHECK(jacFactor0);
374  jacFactor0->getA(jacFactor0->begin()) *= 7.;
375  EXPECT(assert_inequal(init_graph, exp_graph));
376  EXPECT(assert_equal(exp_graph, actCloned));
377 }
378 
379 /* ************************************************************************* */
382  init_graph.push_back(GaussianFactor::shared_ptr());
383  GaussianFactorGraph actNegation = init_graph.negate();
384  GaussianFactorGraph expNegation;
385  expNegation.push_back(init_graph.at(0)->negate());
386  expNegation.push_back(init_graph.at(1)->negate());
387  expNegation.push_back(init_graph.at(2)->negate());
388  expNegation.push_back(init_graph.at(3)->negate());
389  expNegation.push_back(init_graph.at(4)->negate());
390  expNegation.push_back(GaussianFactor::shared_ptr());
391  EXPECT(assert_equal(expNegation, actNegation));
392 }
393 
394 /* ************************************************************************* */
395 TEST(GaussianFactorGraph, hessianDiagonal) {
398  Matrix infoMatrix = gfg.hessian().first;
399  Vector d = infoMatrix.diagonal();
400 
401  VectorValues actual = gfg.hessianDiagonal();
402  expected.insert(0, d.segment<2>(0));
403  expected.insert(1, d.segment<2>(2));
404  expected.insert(2, d.segment<2>(4));
405  EXPECT(assert_equal(expected, actual));
406 }
407 
408 /* ************************************************************************* */
409 TEST(GaussianFactorGraph, DenseSolve) {
412  VectorValues actual = fg.optimizeDensely();
413  EXPECT(assert_equal(expected, actual));
414 }
415 
416 /* ************************************************************************* */
419  gfg.emplace_shared<JacobianFactor>(1, I_1x1, Z_1x1,
420  noiseModel::Isotropic::Sigma(1, 1.0));
421 
423  values.insert(1, I_1x1);
424 
425  // We are testing the normal distribution PDF where info matrix Σ = 1,
426  // mean mu = 0 and x = 1.
427  // Therefore factor squared error: y = 0.5 * (Σ*x - mu)^2 =
428  // 0.5 * (1.0 - 0)^2 = 0.5
429  // NOTE the 0.5 constant is a part of the factor error.
430  EXPECT_DOUBLES_EQUAL(0.5, gfg.error(values), 1e-12);
431 
432  // The gaussian PDF value is: exp^(-0.5 * (Σ*x - mu)^2) / sqrt(2 * PI)
433  // Ignore the denominator and we get: exp^(-0.5 * (1.0)^2) = exp^(-0.5)
434  double expected = exp(-0.5);
435  EXPECT_DOUBLES_EQUAL(expected, gfg.probPrime(values), 1e-12);
436 }
437 
438 /* ************************************************************************* */
439 int main() {
440  TestResult tr;
441  return TestRegistry::runAllTests(tr);
442 }
443 /* ************************************************************************* */
Provides additional testing facilities for common data structures.
#define CHECK(condition)
Definition: Test.h:108
#define EQUALITY(expected, actual)
Definition: Test.h:127
const char Y
Scalar * b
Definition: benchVecAdd.cpp:17
bool triplet_equal(SparseTriplet a, SparseTriplet b)
IsDerived< DERIVEDFACTOR > emplace_shared(Args &&... args)
Emplace a shared pointer to factor of given type.
Definition: FactorGraph.h:196
double probPrime(const VectorValues &c) const
static int runAllTests(TestResult &result)
Eigen::Vector3d Vector3
Definition: Vector.h:43
Global debugging flags.
noiseModel::Diagonal::shared_ptr model
Matrix expected
Definition: testMatrix.cpp:971
static GaussianFactorGraph createGaussianFactorGraphWithHessianFactor()
EIGEN_DONT_INLINE Scalar zero()
Definition: svd_common.h:296
A matrix with column blocks of pre-defined sizes. Used in JacobianFactor and GaussianConditional.
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
std::pair< std::shared_ptr< BayesNetType >, std::shared_ptr< FactorGraphType > > eliminatePartialSequential(const Ordering &ordering, const Eliminate &function=EliminationTraitsType::DefaultEliminate, OptionalVariableIndex variableIndex={}) const
Eigen::MatrixXd Matrix
Definition: base/Matrix.h:39
leaf::MyValues values
const Matrix & matrix() const
MatrixXd L
Definition: LLT_example.cpp:6
Definition: BFloat16.h:88
Matrix augmentedHessian(const Ordering &ordering) const
Pose3 x2(Rot3::Ypr(0.0, 0.0, 0.0), l2)
iterator insert(const std::pair< Key, Vector > &key_value)
static GaussianFactorGraph createSimpleGaussianFactorGraph()
Factor graph with 2 2D factors on 3 2D variables.
Matrix< SCALARA, Dynamic, Dynamic, opt_A > A
Definition: bench_gemm.cpp:48
double error(const VectorValues &x) const
#define EXPECT_DOUBLES_EQUAL(expected, actual, threshold)
Definition: Test.h:161
VectorValues optimize(const Eliminate &function=EliminationTraitsType::DefaultEliminate) const
std::vector< std::tuple< int, int, double > > sparseJacobian(const Ordering &ordering, size_t &nrows, size_t &ncols) const
VariableSlots describes the structure of a combined factor in terms of where each block comes from in...
EIGEN_DEVICE_FUNC const ExpReturnType exp() const
size_t size() const
Definition: FactorGraph.h:334
GaussianFactorGraph negate() const
void add(const GaussianFactor &factor)
bool assert_inequal(const Matrix &A, const Matrix &B, double tol)
Definition: Matrix.cpp:60
Eigen::VectorXd Vector
Definition: Vector.h:38
std::tuple< size_t, size_t, double > SparseTriplet
virtual std::map< Key, Matrix > hessianBlockDiagonal() const
#define EXPECT(condition)
Definition: Test.h:150
virtual GaussianFactorGraph clone() const
Array< double, 1, 3 > e(1./3., 0.5, 2.)
Conditional Gaussian Base class.
Linear Factor Graph where all factors are Gaussians.
void multiplyHessianAdd(double alpha, const VectorValues &x, VectorValues &y) const
virtual VectorValues hessianDiagonal() const
Matrix augmentedJacobian(const Ordering &ordering) const
#define LONGS_EQUAL(expected, actual)
Definition: Test.h:134
std::pair< Matrix, Vector > hessian(const Ordering &ordering) const
gtsam::Key l1
noiseModel::Diagonal::shared_ptr SharedDiagonal
Definition: NoiseModel.h:743
TEST(GaussianFactorGraph, initialization)
traits
Definition: chartTesting.h:28
VectorValues transposeMultiply(const Errors &e) const
VectorValues optimizeDensely() const
Eigen::Vector2d Vector2
Definition: Vector.h:42
#define EXPECT_LONGS_EQUAL(expected, actual)
Definition: Test.h:154
std::pair< Matrix, Vector > jacobian(const Ordering &ordering) const
const sharedFactor at(size_t i) const
Definition: FactorGraph.h:343
Pose3 x1
Definition: testPose3.cpp:663
A Gaussian factor using the canonical parameters (information form)
Chordal Bayes Net, the result of eliminating a factor graph.
void insert(Key j, const Value &val)
Definition: Values.cpp:155
VectorValues gradient(const VectorValues &x0) const
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > Matrix
FastVector< Key > KeyVector
Define collection type once and for all - also used in wrappers.
Definition: Key.h:86
#define X
Definition: icosphere.cpp:20
set noclip points set clip one set noclip two set bar set border lt lw set xdata set ydata set zdata set x2data set y2data set boxwidth set dummy x
Eigen::Matrix< double, Eigen::Dynamic, 1 > Vector
virtual VectorValues gradientAtZero() const
std::uint64_t Key
Integer nonlinear key type.
Definition: types.h:102


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