cholesky.cpp
Go to the documentation of this file.
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 BOOST_AUTO_TEST_CASE ( test_cholesky )
33 {
34  using namespace Eigen;
35  using namespace pinocchio;
36 
39  pinocchio::Data data(model);
40 
41  model.lowerPositionLimit.head<3>().fill(-1.);
42  model.upperPositionLimit.head<3>().fill(1.);
44  data.M.fill(0); // Only nonzero coeff of M are initialized by CRBA.
45  crba(model,data,q);
46 
48  data.M.triangularView<Eigen::StrictlyLower>() =
49  data.M.triangularView<Eigen::StrictlyUpper>().transpose();
50 
51  const Eigen::MatrixXd & U = data.U;
52  const Eigen::VectorXd & D = data.D;
53  const Eigen::MatrixXd & M = data.M;
54 
55  #ifndef NDEBUG
56  std::cout << "M = [\n" << M << "];" << std::endl;
57  std::cout << "U = [\n" << U << "];" << std::endl;
58  std::cout << "D = [\n" << D.transpose() << "];" << std::endl;
59  #endif
60 
61  BOOST_CHECK(M.isApprox(U*D.asDiagonal()*U.transpose() , 1e-12));
62 
63  Eigen::VectorXd v = Eigen::VectorXd::Random(model.nv);
64 // std::cout << "v = [" << v.transpose() << "]';" << std::endl;
65 
66  Eigen::VectorXd Uv = v; pinocchio::cholesky::Uv(model,data,Uv);
67  BOOST_CHECK(Uv.isApprox(U*v, 1e-12));
68 
69  Eigen::VectorXd Utv = v; pinocchio::cholesky::Utv(model,data,Utv);
70  BOOST_CHECK(Utv.isApprox(U.transpose()*v, 1e-12));
71 
72  Eigen::VectorXd Uiv = v; pinocchio::cholesky::Uiv(model,data,Uiv);
73  BOOST_CHECK(Uiv.isApprox(U.inverse()*v, 1e-12));
74 
75 
76  Eigen::VectorXd Utiv = v; pinocchio::cholesky::Utiv(model,data,Utiv);
77  BOOST_CHECK(Utiv.isApprox(U.transpose().inverse()*v, 1e-12));
78 
79  Eigen::VectorXd Miv = v; pinocchio::cholesky::solve(model,data,Miv);
80  BOOST_CHECK(Miv.isApprox(M.inverse()*v, 1e-12));
81 
82  Eigen::VectorXd Mv = v; Mv = pinocchio::cholesky::Mv(model,data,Mv);
83  BOOST_CHECK(Mv.isApprox(M*v, 1e-12));
84  Mv = v; pinocchio::cholesky::UDUtv(model,data,Mv);
85  BOOST_CHECK(Mv.isApprox(M*v, 1e-12));
86 }
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 BOOST_AUTO_TEST_CASE ( test_timings )
98 {
99  using namespace Eigen;
100  using namespace pinocchio;
101 
104  pinocchio::Data data(model);
105 
106  model.lowerPositionLimit.head<3>().fill(-1.);
107  model.upperPositionLimit.head<3>().fill(1.);
108  VectorXd q = randomConfiguration(model);
109  data.M.fill(0); // Only nonzero coeff of M are initialized by CRBA.
110  crba(model,data,q);
111 
112 
113  long flag = BOOST_BINARY(1111111);
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  const size_t NBT = 1;
123  std::cout << "(the time score in debug mode is not relevant) " ;
124  #endif
125 
126  bool verbose = flag & (flag-1) ; // True is two or more binaries of the flag are 1.
127  if(verbose) std::cout <<"--" << std::endl;
128 
129  if( flag >> 0 & 1 )
130  {
131  timer.tic();
132  SMOOTH(NBT)
133  {
134  pinocchio::cholesky::decompose(model,data);
135  }
136  if(verbose) std::cout << "Decompose =\t";
137  timer.toc(std::cout,NBT);
138  }
139 
140  if( flag >> 1 & 1 )
141  {
142  timer.tic();
143  Eigen::VectorXd v = Eigen::VectorXd::Random(model.nv);
144  Eigen::VectorXd res(model.nv);
145  SMOOTH(NBT)
146  {
147  Eigen::LDLT <Eigen::MatrixXd> Mchol(data.M);
148  res = Mchol.solve(v);
149  }
150  if(verbose) std::cout << "Eigen::LDLt =\t";
151  timer.toc(std::cout,NBT);
152  }
153 
154  if( flag >> 2 & 31 )
155  {
156  std::vector<Eigen::VectorXd> randvec(NBT);
157  for(size_t i=0;i<NBT;++i ) randvec[i] = Eigen::VectorXd::Random(model.nv);
158  Eigen::VectorXd zero = Eigen::VectorXd::Zero(model.nv);
159  Eigen::VectorXd res (model.nv);
160 
161 
162  if( flag >> 2 & 1 )
163  {
164  timer.tic();
165  SMOOTH(NBT)
166  {
167  pinocchio::cholesky::solve(model,data,randvec[_smooth]);
168  }
169  if(verbose) std::cout << "solve =\t\t";
170  timer.toc(std::cout,NBT);
171  }
172 
173  if( flag >> 3 & 1 )
174  {
175  timer.tic();
176  SMOOTH(NBT)
177  {
178  pinocchio::cholesky::Uv(model,data,randvec[_smooth]);
179  }
180  if(verbose) std::cout << "Uv =\t\t";
181  timer.toc(std::cout,NBT);
182  }
183 
184  if( flag >> 4 & 1 )
185  {
186  timer.tic();
187  SMOOTH(NBT)
188  {
189  pinocchio::cholesky::Uiv(model,data,randvec[_smooth]);
190  }
191  if(verbose) std::cout << "Uiv =\t\t";
192  timer.toc(std::cout,NBT);
193  }
194  if( flag >> 5 & 1 )
195  {
196  timer.tic();
197  Eigen::VectorXd res;
198  SMOOTH(NBT)
199  {
200  res = pinocchio::cholesky::Mv(model,data,randvec[_smooth]);
201  }
202  if(verbose) std::cout << "Mv =\t\t";
203  timer.toc(std::cout,NBT);
204  }
205  if( flag >> 6 & 1 )
206  {
207  timer.tic();
208  SMOOTH(NBT)
209  {
210  pinocchio::cholesky::UDUtv(model,data,randvec[_smooth]);
211  }
212  if(verbose) std::cout << "UDUtv =\t\t";
213  timer.toc(std::cout,NBT);
214  }
215  }
216 }
217 
218  BOOST_AUTO_TEST_CASE(test_Minv_from_cholesky)
219  {
220  using namespace Eigen;
221  using namespace pinocchio;
222 
225  pinocchio::Data data(model);
226 
227  model.lowerPositionLimit.head<3>().fill(-1.);
228  model.upperPositionLimit.head<3>().fill(1.);
229  VectorXd q = randomConfiguration(model);
230  crba(model,data,q);
231  data.M.triangularView<Eigen::StrictlyLower>() =
232  data.M.triangularView<Eigen::StrictlyUpper>().transpose();
233  MatrixXd Minv_ref(data.M.inverse());
234 
235  cholesky::decompose(model,data);
236  VectorXd v_unit(VectorXd::Unit(model.nv,0));
237 
238  VectorXd Ui_v_unit(model.nv);
239  VectorXd Ui_v_unit_ref(model.nv);
240 
241  for(int k = 0; k < model.nv; ++k)
242  {
243  v_unit = VectorXd::Unit(model.nv,k);
244  Ui_v_unit.setZero();
245  cholesky::internal::Miunit(model,data,k,Ui_v_unit);
246  Ui_v_unit_ref = v_unit;
247  cholesky::Uiv(model,data,Ui_v_unit_ref);
248  Ui_v_unit_ref.array() *= data.Dinv.array();
249  cholesky::Utiv(model,data,Ui_v_unit_ref);
250 
251  BOOST_CHECK(Ui_v_unit.isApprox(Ui_v_unit_ref));
252 
253  Ui_v_unit_ref = v_unit;
254  cholesky::solve(model,data,Ui_v_unit_ref);
255  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  MatrixXd Minv(model.nv,model.nv);
262  Minv.setZero();
263  cholesky::computeMinv(model,data,Minv);
264 
265  BOOST_CHECK(Minv.isApprox(Minv_ref));
266 
267  // Check second call to cholesky::computeMinv
268  cholesky::computeMinv(model,data,Minv);
269  BOOST_CHECK(Minv.isApprox(Minv_ref));
270 
271  // Call the second signature of cholesky::computeMinv
272  Data data_bis(model);
273  crba(model,data_bis,q);
274  cholesky::decompose(model,data_bis);
275  cholesky::computeMinv(model,data_bis);
276  BOOST_CHECK(data_bis.Minv.isApprox(Minv_ref));
277  }
278 
279 BOOST_AUTO_TEST_SUITE_END ()
bool verbose
double toc()
Definition: timer.hpp:68
const DataTpl< Scalar, Options, JointCollectionTpl >::MatrixXs & crba(const ModelTpl< Scalar, Options, JointCollectionTpl > &model, DataTpl< Scalar, Options, JointCollectionTpl > &data, const Eigen::MatrixBase< ConfigVectorType > &q)
Computes the upper triangular part of the joint space inertia matrix M by using the Composite Rigid B...
q
Mat & computeMinv(const ModelTpl< Scalar, Options, JointCollectionTpl > &model, const DataTpl< Scalar, Options, JointCollectionTpl > &data, const Eigen::MatrixBase< Mat > &Minv)
Computes the inverse of the joint space inertia matrix M from its Cholesky factorization.
const DataTpl< Scalar, Options, JointCollectionTpl >::MatrixXs & decompose(const ModelTpl< Scalar, Options, JointCollectionTpl > &model, DataTpl< Scalar, Options, JointCollectionTpl > &data)
Compute the Cholesky decomposition of the joint space inertia matrix M contained in data...
ConfigVectorType lowerPositionLimit
Lower joint configuration limit.
void randomConfiguration(const ModelTpl< Scalar, Options, JointCollectionTpl > &model, const Eigen::MatrixBase< ConfigVectorIn1 > &lowerLimits, const Eigen::MatrixBase< ConfigVectorIn2 > &upperLimits, const Eigen::MatrixBase< ReturnType > &qout)
Generate a configuration vector uniformly sampled among provided limits.
VectorXs D
Diagonal of the joint space intertia matrix obtained by a Cholesky Decomposition. ...
fill
void tic()
Definition: timer.hpp:63
D
Mat & Utiv(const ModelTpl< Scalar, Options, JointCollectionTpl > &model, const DataTpl< Scalar, Options, JointCollectionTpl > &data, const Eigen::MatrixBase< Mat > &v)
Perform the pivot inversion using the Cholesky decomposition stored in data and acting in place...
BOOST_AUTO_TEST_CASE(test_cholesky)
Definition: cholesky.cpp:32
MatrixXs M
The joint space inertia matrix (a square matrix of dim model.nv).
U
Definition: ocp.py:61
#define SMOOTH(s)
Definition: timer.hpp:38
Mat & Uv(const ModelTpl< Scalar, Options, JointCollectionTpl > &model, const DataTpl< Scalar, Options, JointCollectionTpl > &data, const Eigen::MatrixBase< Mat > &v)
Perform the sparse multiplication using the Cholesky decomposition stored in data and acting in plac...
Main pinocchio namespace.
Definition: timings.cpp:28
VectorXs Dinv
Diagonal inverse of the joint space intertia matrix obtained by a Cholesky Decomposition.
Mat & UDUtv(const ModelTpl< Scalar, Options, JointCollectionTpl > &model, const DataTpl< Scalar, Options, JointCollectionTpl > &data, const Eigen::MatrixBase< Mat > &m)
Performs the multiplication by using the Cholesky decomposition of M stored in data.
res
int nv
Dimension of the velocity vector space.
Mat & Uiv(const ModelTpl< Scalar, Options, JointCollectionTpl > &model, const DataTpl< Scalar, Options, JointCollectionTpl > &data, const Eigen::MatrixBase< Mat > &v)
Perform the pivot inversion using the Cholesky decomposition stored in data and acting in place...
Mat & solve(const ModelTpl< Scalar, Options, JointCollectionTpl > &model, const DataTpl< Scalar, Options, JointCollectionTpl > &data, const Eigen::MatrixBase< Mat > &y)
Return the solution of using the Cholesky decomposition stored in data given the entry ...
MatRes & Mv(const ModelTpl< Scalar, Options, JointCollectionTpl > &model, const DataTpl< Scalar, Options, JointCollectionTpl > &data, const Eigen::MatrixBase< Mat > &min, const Eigen::MatrixBase< MatRes > &mout)
Performs the multiplication by using the sparsity pattern of the M matrix.
ConfigVectorType upperPositionLimit
Upper joint configuration limit.
M
void humanoidRandom(ModelTpl< Scalar, Options, JointCollectionTpl > &model, bool usingFF=true)
Create a humanoid kinematic tree with 6-DOF limbs and random joint placements.
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > VectorXd
Definition: conversions.cpp:14
MatrixXs U
Joint space intertia matrix square root (upper trianglular part) computed with a Cholesky Decompositi...
Mat & Utv(const ModelTpl< Scalar, Options, JointCollectionTpl > &model, const DataTpl< Scalar, Options, JointCollectionTpl > &data, const Eigen::MatrixBase< Mat > &v)
Perform the sparse multiplication using the Cholesky decomposition stored in data and acting in plac...


pinocchio
Author(s):
autogenerated on Fri Jun 23 2023 02:38:29