GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: unittest/cholesky.cpp Lines: 141 141 100.0 %
Date: 2024-01-23 21:41:47 Branches: 378 738 51.2 %

Line Branch Exec Source
1
//
2
// Copyright (c) 2015-2019 CNRS INRIA
3
//
4
5
/*
6
 * Validate the sparse Cholesky decomposition of the mass matrix.  The code
7
 * tests both the numerical value and the computation time. For a strong
8
 * computation benchmark, see benchmark/timings.
9
 *
10
 */
11
12
#include "pinocchio/spatial/se3.hpp"
13
#include "pinocchio/multibody/model.hpp"
14
#include "pinocchio/multibody/data.hpp"
15
#include "pinocchio/algorithm/crba.hpp"
16
#include "pinocchio/algorithm/cholesky.hpp"
17
#include "pinocchio/parsers/sample-models.hpp"
18
#include "pinocchio/utils/timer.hpp"
19
#include "pinocchio/algorithm/joint-configuration.hpp"
20
21
#include <iostream>
22
#ifdef NDEBUG
23
#  include <Eigen/Cholesky>
24
#endif
25
26
#include <boost/test/unit_test.hpp>
27
#include <boost/utility/binary.hpp>
28
29
30
BOOST_AUTO_TEST_SUITE ( BOOST_TEST_MODULE )
31
32
















4
BOOST_AUTO_TEST_CASE ( test_cholesky )
33
{
34
  using namespace Eigen;
35
  using namespace pinocchio;
36
37
4
  pinocchio::Model model;
38
2
  pinocchio::buildModels::humanoidRandom(model,true);
39
4
  pinocchio::Data data(model);
40
41

2
  model.lowerPositionLimit.head<3>().fill(-1.);
42

2
  model.upperPositionLimit.head<3>().fill(1.);
43
4
  VectorXd q = randomConfiguration(model);
44
2
  data.M.fill(0); // Only nonzero coeff of M are initialized by CRBA.
45
2
  crba(model,data,q);
46
47
2
  pinocchio::cholesky::decompose(model,data);
48
2
  data.M.triangularView<Eigen::StrictlyLower>() =
49

4
  data.M.triangularView<Eigen::StrictlyUpper>().transpose();
50
51
2
  const Eigen::MatrixXd & U = data.U;
52
2
  const Eigen::VectorXd & D = data.D;
53
2
  const Eigen::MatrixXd & M = data.M;
54
55
  #ifndef NDEBUG
56


2
    std::cout << "M = [\n" << M << "];" << std::endl;
57


2
    std::cout << "U = [\n" << U << "];" << std::endl;
58


2
    std::cout << "D = [\n" << D.transpose() << "];" << std::endl;
59
  #endif
60
61





2
  BOOST_CHECK(M.isApprox(U*D.asDiagonal()*U.transpose() , 1e-12));
62
63

4
  Eigen::VectorXd v = Eigen::VectorXd::Random(model.nv);
64
// std::cout << "v = [" << v.transpose() << "]';" << std::endl;
65
66

4
  Eigen::VectorXd Uv = v; pinocchio::cholesky::Uv(model,data,Uv);
67




2
  BOOST_CHECK(Uv.isApprox(U*v, 1e-12));
68
69

4
  Eigen::VectorXd Utv = v; pinocchio::cholesky::Utv(model,data,Utv);
70




2
  BOOST_CHECK(Utv.isApprox(U.transpose()*v, 1e-12));
71
72

4
  Eigen::VectorXd Uiv = v; pinocchio::cholesky::Uiv(model,data,Uiv);
73




2
  BOOST_CHECK(Uiv.isApprox(U.inverse()*v, 1e-12));
74
75
76

4
  Eigen::VectorXd Utiv = v; pinocchio::cholesky::Utiv(model,data,Utiv);
77





2
  BOOST_CHECK(Utiv.isApprox(U.transpose().inverse()*v, 1e-12));
78
79

4
  Eigen::VectorXd Miv = v; pinocchio::cholesky::solve(model,data,Miv);
80




2
  BOOST_CHECK(Miv.isApprox(M.inverse()*v, 1e-12));
81
82

4
  Eigen::VectorXd Mv = v; Mv = pinocchio::cholesky::Mv(model,data,Mv);
83




2
  BOOST_CHECK(Mv.isApprox(M*v, 1e-12));
84

2
  Mv = v;                 pinocchio::cholesky::UDUtv(model,data,Mv);
85




2
  BOOST_CHECK(Mv.isApprox(M*v, 1e-12));
86
2
}
87
88
89
/* The flag triger the following timers:
90
 * 000001: sparse UDUt cholesky
91
 * 000010: dense Eigen LDLt cholesky (with pivot)
92
 * 000100: sparse resolution
93
 * 001000: sparse U*v multiplication
94
 * 010000: sparse U\v substitution
95
 * 100000: sparse M*v multiplication without Cholesky
96
 */
97
















4
BOOST_AUTO_TEST_CASE ( test_timings )
98
{
99
  using namespace Eigen;
100
  using namespace pinocchio;
101
102
4
  pinocchio::Model model;
103
2
  pinocchio::buildModels::humanoidRandom(model,true);
104
4
  pinocchio::Data data(model);
105
106

2
  model.lowerPositionLimit.head<3>().fill(-1.);
107

2
  model.upperPositionLimit.head<3>().fill(1.);
108
4
  VectorXd q = randomConfiguration(model);
109
2
  data.M.fill(0); // Only nonzero coeff of M are initialized by CRBA.
110
2
  crba(model,data,q);
111
112
113
2
  long flag = BOOST_BINARY(1111111);
114
4
  PinocchioTicToc timer(PinocchioTicToc::US);
115
  #ifdef NDEBUG
116
    #ifdef _INTENSE_TESTING_
117
      const size_t NBT = 1000*1000;
118
    #else
119
      const size_t NBT = 10;
120
    #endif
121
  #else
122
2
    const size_t NBT = 1;
123
2
    std::cout << "(the time score in debug mode is not relevant)  " ;
124
  #endif
125
126
2
  bool verbose = flag & (flag-1) ; // True is two or more binaries of the flag are 1.
127

2
  if(verbose) std::cout <<"--" << std::endl;
128
129
2
  if( flag >> 0 & 1 )
130
    {
131
2
      timer.tic();
132
4
      SMOOTH(NBT)
133
      {
134
2
	pinocchio::cholesky::decompose(model,data);
135
      }
136

2
      if(verbose) std::cout << "Decompose =\t";
137
2
      timer.toc(std::cout,NBT);
138
    }
139
140
2
  if( flag >> 1 & 1 )
141
    {
142
2
      timer.tic();
143

4
      Eigen::VectorXd v = Eigen::VectorXd::Random(model.nv);
144
4
      Eigen::VectorXd res(model.nv);
145
4
      SMOOTH(NBT)
146
      {
147
2
	Eigen::LDLT <Eigen::MatrixXd> Mchol(data.M);
148

2
	res = Mchol.solve(v);
149
      }
150

2
      if(verbose) std::cout << "Eigen::LDLt =\t";
151
2
      timer.toc(std::cout,NBT);
152
    }
153
154
2
  if( flag >> 2 & 31 )
155
    {
156
4
      std::vector<Eigen::VectorXd> randvec(NBT);
157

4
      for(size_t i=0;i<NBT;++i ) randvec[i] = Eigen::VectorXd::Random(model.nv);
158

4
      Eigen::VectorXd zero = Eigen::VectorXd::Zero(model.nv);
159
4
      Eigen::VectorXd res (model.nv);
160
161
162
2
      if( flag >> 2 & 1 )
163
	{
164
2
	  timer.tic();
165
4
	  SMOOTH(NBT)
166
	  {
167
2
	    pinocchio::cholesky::solve(model,data,randvec[_smooth]);
168
	  }
169

2
	  if(verbose) std::cout << "solve =\t\t";
170
2
	  timer.toc(std::cout,NBT);
171
	}
172
173
2
      if( flag >> 3 & 1 )
174
	{
175
2
	  timer.tic();
176
4
	  SMOOTH(NBT)
177
	  {
178
2
	    pinocchio::cholesky::Uv(model,data,randvec[_smooth]);
179
	  }
180

2
	  if(verbose) std::cout << "Uv =\t\t";
181
2
	  timer.toc(std::cout,NBT);
182
	}
183
184
2
      if( flag >> 4 & 1 )
185
	{
186
2
	  timer.tic();
187
4
	  SMOOTH(NBT)
188
	  {
189
2
	    pinocchio::cholesky::Uiv(model,data,randvec[_smooth]);
190
	  }
191

2
	  if(verbose) std::cout << "Uiv =\t\t";
192
2
	  timer.toc(std::cout,NBT);
193
	}
194
2
      if( flag >> 5 & 1 )
195
	{
196
2
	  timer.tic();
197
4
	  Eigen::VectorXd res;
198
4
	  SMOOTH(NBT)
199
	  {
200
2
	    res = pinocchio::cholesky::Mv(model,data,randvec[_smooth]);
201
	  }
202

2
	  if(verbose) std::cout << "Mv =\t\t";
203
2
	  timer.toc(std::cout,NBT);
204
	}
205
2
      if( flag >> 6 & 1 )
206
	{
207
2
    timer.tic();
208
4
    SMOOTH(NBT)
209
    {
210
2
      pinocchio::cholesky::UDUtv(model,data,randvec[_smooth]);
211
    }
212

2
    if(verbose) std::cout << "UDUtv =\t\t";
213
2
    timer.toc(std::cout,NBT);
214
	}
215
    }
216
2
}
217
218
















4
  BOOST_AUTO_TEST_CASE(test_Minv_from_cholesky)
219
  {
220
    using namespace Eigen;
221
    using namespace pinocchio;
222
223
4
    pinocchio::Model model;
224
2
    pinocchio::buildModels::humanoidRandom(model,true);
225
4
    pinocchio::Data data(model);
226
227

2
    model.lowerPositionLimit.head<3>().fill(-1.);
228

2
    model.upperPositionLimit.head<3>().fill(1.);
229
4
    VectorXd q = randomConfiguration(model);
230
2
    crba(model,data,q);
231
2
    data.M.triangularView<Eigen::StrictlyLower>() =
232

4
    data.M.triangularView<Eigen::StrictlyUpper>().transpose();
233

4
    MatrixXd Minv_ref(data.M.inverse());
234
235
2
    cholesky::decompose(model,data);
236

4
    VectorXd v_unit(VectorXd::Unit(model.nv,0));
237
238
4
    VectorXd Ui_v_unit(model.nv);
239
4
    VectorXd Ui_v_unit_ref(model.nv);
240
241
66
    for(int k = 0; k < model.nv; ++k)
242
    {
243

64
      v_unit = VectorXd::Unit(model.nv,k);
244
64
      Ui_v_unit.setZero();
245
64
      cholesky::internal::Miunit(model,data,k,Ui_v_unit);
246
64
      Ui_v_unit_ref = v_unit;
247
64
      cholesky::Uiv(model,data,Ui_v_unit_ref);
248

64
      Ui_v_unit_ref.array() *= data.Dinv.array();
249
64
      cholesky::Utiv(model,data,Ui_v_unit_ref);
250
251



64
      BOOST_CHECK(Ui_v_unit.isApprox(Ui_v_unit_ref));
252
253
64
      Ui_v_unit_ref = v_unit;
254
64
      cholesky::solve(model,data,Ui_v_unit_ref);
255



64
      BOOST_CHECK(Ui_v_unit.isApprox(Ui_v_unit_ref));
256
257
//      std::cout << "Ui_v_unit : " << Ui_v_unit.transpose() << std::endl;
258
//      std::cout << "Ui_v_unit_ref : " << Ui_v_unit_ref.transpose() << std::endl << std::endl;
259
    }
260
261
4
    MatrixXd Minv(model.nv,model.nv);
262
2
    Minv.setZero();
263
2
    cholesky::computeMinv(model,data,Minv);
264
265



2
    BOOST_CHECK(Minv.isApprox(Minv_ref));
266
267
    // Check second call to cholesky::computeMinv
268
2
    cholesky::computeMinv(model,data,Minv);
269



2
    BOOST_CHECK(Minv.isApprox(Minv_ref));
270
271
    // Call the second signature of cholesky::computeMinv
272
4
    Data data_bis(model);
273
2
    crba(model,data_bis,q);
274
2
    cholesky::decompose(model,data_bis);
275
2
    cholesky::computeMinv(model,data_bis);
276



2
    BOOST_CHECK(data_bis.Minv.isApprox(Minv_ref));
277
2
  }
278
279
BOOST_AUTO_TEST_SUITE_END ()