- 3.0.2 core module.
DerivativesCppadJIT-impl.h
Go to the documentation of this file.
1 /**********************************************************************************************************************
2 This file is part of the Control Toolbox (https://github.com/ethz-adrl/control-toolbox), copyright by ETH Zurich.
3 Licensed under the BSD-2 license (see LICENSE file in main directory)
4 **********************************************************************************************************************/
5 
6 #pragma once
7 
8 #ifdef CPPADCG
9 
10 namespace ct {
11 namespace core {
12 
13 
14 template <int IN_DIM, int OUT_DIM>
15 DerivativesCppadJIT<IN_DIM, OUT_DIM>::DerivativesCppadJIT(FUN_TYPE_CG& f, int inputDim, int outputDim)
16  : DerivativesBase(),
17  cgStdFun_(f),
18  inputDim_(inputDim),
19  outputDim_(outputDim),
20  compiled_(false),
21  libName_(""),
22  dynamicLib_(nullptr)
23 #ifdef LLVM_VERSION_MAJOR
24  ,
25  llvmModelLib_(nullptr)
26 #endif
27 {
28  update(f, inputDim, outputDim);
29 }
30 
31 template <int IN_DIM, int OUT_DIM>
32 DerivativesCppadJIT<IN_DIM, OUT_DIM>::DerivativesCppadJIT(const DerivativesCppadJIT& arg)
33  : DerivativesBase(arg),
34  cgStdFun_(arg.cgStdFun_),
35  inputDim_(arg.inputDim_),
36  outputDim_(arg.outputDim_),
37  compiled_(arg.compiled_),
38  libName_(arg.libName_),
39  dynamicLib_(nullptr)
40 #ifdef LLVM_VERSION_MAJOR
41  ,
42  llvmModelLib_(nullptr)
43 #endif
44 {
45  cgCppadFun_ = arg.cgCppadFun_;
46  if (compiled_)
47  {
48  if (arg.dynamicLib_) // in case of dynamic libraries
49  {
50  dynamicLib_ = internal::CGHelpers::loadDynamicLibCppad<double>(libName_);
51  model_ = std::shared_ptr<CppAD::cg::GenericModel<double>>(dynamicLib_->model(libName_));
52  }
53 #ifdef LLVM_VERSION_MAJOR
54  else if (arg.llvmModelLib_) // in case of regular JIT without dynamic lib
55  {
56  throw std::runtime_error("DerivativesCppadJIT: cloning of LLVM-JIT libraries is currently not supported.");
57  llvmModelLib_ = arg.llvmModelLib_; // TODO: this is not clean, we need to properly clone the llvm model lib
58  model_ = std::shared_ptr<CppAD::cg::GenericModel<double>>(llvmModelLib_->model(libName_));
59  }
60 #endif
61  else
62  throw std::runtime_error("DerivativesCppadJIT: undefined behaviour in copy constructor.");
63  }
64 }
65 
66 template <int IN_DIM, int OUT_DIM>
67 void DerivativesCppadJIT<IN_DIM, OUT_DIM>::update(FUN_TYPE_CG& f, const size_t inputDim, const size_t outputDim)
68 {
69  cgStdFun_ = f;
70  outputDim_ = outputDim;
71  inputDim_ = inputDim;
72  if (outputDim_ > 0 && inputDim_ > 0)
73  {
74  recordCg();
75  compiled_ = false;
76  libName_ = "";
77  dynamicLib_ = nullptr;
78  model_ = nullptr;
79  }
80 }
81 
82 template <int IN_DIM, int OUT_DIM>
83 auto DerivativesCppadJIT<IN_DIM, OUT_DIM>::clone() const -> DerivativesCppadJIT*
84 {
85  return new DerivativesCppadJIT<IN_DIM, OUT_DIM>(*this);
86 }
87 
88 template <int IN_DIM, int OUT_DIM>
89 auto DerivativesCppadJIT<IN_DIM, OUT_DIM>::forwardZero(const Eigen::VectorXd& x) -> OUT_TYPE_D
90 {
91  if (compiled_)
92  {
93  assert(model_->isForwardZeroAvailable() == true);
94  return model_->ForwardZero(x);
95  }
96  else
97  {
98  throw std::runtime_error("Error: Compile the library first by calling compileJIT(..)");
99  }
100 }
101 
102 template <int IN_DIM, int OUT_DIM>
103 auto DerivativesCppadJIT<IN_DIM, OUT_DIM>::jacobian(const Eigen::VectorXd& x) -> JAC_TYPE_D
104 {
105  if (outputDim_ <= 0)
106  throw std::runtime_error("Outdim dim smaller 0; Define output dim in DerivativesCppad constructor");
107 
108 
109  Eigen::VectorXd jac;
110  if (compiled_)
111  {
112  assert(model_->isJacobianAvailable() == true);
113  jac = model_->Jacobian(x);
114  JAC_TYPE_D out(outputDim_, x.rows());
115  out = JAC_TYPE_ROW_MAJOR::Map(jac.data(), outputDim_, x.rows());
116  return out;
117  }
118  else
119  throw std::runtime_error("Error: Compile the library first by calling compileJIT(..)");
120 }
121 
122 template <int IN_DIM, int OUT_DIM>
123 void DerivativesCppadJIT<IN_DIM, OUT_DIM>::sparseJacobian(const Eigen::VectorXd& x,
124  Eigen::VectorXd& jac,
125  Eigen::VectorXi& iRow,
126  Eigen::VectorXi& jCol)
127 {
128  if (outputDim_ <= 0)
129  throw std::runtime_error("Outdim dim smaller 0; Define output dim in DerivativesCppad constructor");
130 
131  if (compiled_)
132  {
133  assert(model_->isSparseJacobianAvailable() == true);
134  std::vector<double> input(x.data(), x.data() + x.rows() * x.cols());
135  std::vector<double> output;
136  model_->SparseJacobian(input, output, sparsityRowsJacobian_, sparsityColsJacobian_);
137  jac = Eigen::Map<Eigen::VectorXd>(output.data(), output.size(), 1);
138  iRow = sparsityRowsJacobianEigen_;
139  jCol = sparsityColsJacobianEigen_;
140  }
141  else
142  throw std::runtime_error("Error: Compile the library first by calling compileJIT(..)");
143 }
144 
145 template <int IN_DIM, int OUT_DIM>
146 Eigen::VectorXd DerivativesCppadJIT<IN_DIM, OUT_DIM>::sparseJacobianValues(const Eigen::VectorXd& x)
147 {
148  if (outputDim_ <= 0)
149  throw std::runtime_error("Outdim dim smaller 0; Define output dim in DerivativesCppad constructor");
150 
151  if (compiled_)
152  {
153  assert(model_->isSparseJacobianAvailable() == true);
154  std::vector<double> input(x.data(), x.data() + x.rows() * x.cols());
155  std::vector<double> output;
156  model_->SparseJacobian(input, output, sparsityRowsJacobian_, sparsityColsJacobian_);
157  return Eigen::Map<Eigen::VectorXd>(output.data(), output.size(), 1);
158  }
159  else
160  throw std::runtime_error("Error: Compile the library first by calling compileJIT(..)");
161 }
162 
163 template <int IN_DIM, int OUT_DIM>
164 auto DerivativesCppadJIT<IN_DIM, OUT_DIM>::hessian(const Eigen::VectorXd& x, const Eigen::VectorXd& lambda)
165  -> HES_TYPE_D
166 {
167  if (outputDim_ <= 0)
168  throw std::runtime_error("Outdim dim smaller 0; Define output dim in DerivativesCppad constructor");
169 
170  if (compiled_)
171  {
172  assert(model_->isHessianAvailable() == true);
173  Eigen::VectorXd hessian = model_->Hessian(x, lambda);
174  HES_TYPE_D out(x.rows(), x.rows());
175  out = HES_TYPE_ROW_MAJOR::Map(hessian.data(), x.rows(), x.rows());
176  return out;
177  }
178  else
179  {
180  throw std::runtime_error("Error: Compile the library first by calling compileJIT(..)");
181  }
182 }
183 
184 template <int IN_DIM, int OUT_DIM>
185 void DerivativesCppadJIT<IN_DIM, OUT_DIM>::sparseHessian(const Eigen::VectorXd& x,
186  const Eigen::VectorXd& lambda,
187  Eigen::VectorXd& hes,
188  Eigen::VectorXi& iRow,
189  Eigen::VectorXi& jCol)
190 {
191  if (outputDim_ <= 0)
192  throw std::runtime_error("Outdim dim smaller 0; Define output dim in DerivativesCppad constructor");
193 
194  if (compiled_)
195  {
196  assert(model_->isSparseJacobianAvailable() == true);
197  std::vector<double> input(x.data(), x.data() + x.rows() * x.cols());
198  std::vector<double> inputLambda(lambda.data(), lambda.data() + lambda.rows() * lambda.cols());
199  std::vector<double> output;
200  model_->SparseHessian(input, inputLambda, output, sparsityRowsHessian_, sparsityColsHessian_);
201  hes = Eigen::Map<Eigen::VectorXd>(output.data(), output.size(), 1);
202  iRow = sparsityRowsHessianEigen_;
203  jCol = sparsityColsHessianEigen_;
204  }
205  else
206  throw std::runtime_error("Error: Compile the library first by calling compileJIT(..)");
207 }
208 
209 template <int IN_DIM, int OUT_DIM>
210 Eigen::VectorXd DerivativesCppadJIT<IN_DIM, OUT_DIM>::sparseHessianValues(const Eigen::VectorXd& x,
211  const Eigen::VectorXd& lambda)
212 {
213  if (outputDim_ <= 0)
214  throw std::runtime_error("Outdim dim smaller 0; Define output dim in DerivativesCppad constructor");
215 
216  if (compiled_)
217  {
218  assert(model_->isSparseHessianAvailable() == true);
219  std::vector<double> input(x.data(), x.data() + x.rows() * x.cols());
220  std::vector<double> inputLambda(lambda.data(), lambda.data() + lambda.rows() * lambda.cols());
221  std::vector<double> output;
222  model_->SparseHessian(input, inputLambda, output, sparsityRowsHessian_, sparsityColsHessian_);
223  return Eigen::Map<Eigen::VectorXd>(output.data(), output.size(), 1);
224  }
225  else
226  throw std::runtime_error("Error: Compile the library first by calling compileJIT(..)");
227 }
228 
229 template <int IN_DIM, int OUT_DIM>
230 Eigen::Matrix<bool, Eigen::Dynamic, Eigen::Dynamic> DerivativesCppadJIT<IN_DIM, OUT_DIM>::getSparsityPatternJacobian()
231 {
232  assert(model_->isJacobianSparsityAvailable() == true);
233 
234  std::vector<bool> sparsityVec = model_->JacobianSparsityBool();
235  Eigen::Matrix<bool, Eigen::Dynamic, Eigen::Dynamic> sparsityMat(outputDim_, inputDim_);
236 
237  assert((int)(sparsityVec.size()) == outputDim_ * inputDim_);
238  for (int row = 0; row < outputDim_; ++row)
239  for (int col = 0; col < inputDim_; ++col)
240  sparsityMat(row, col) = sparsityVec[col + row * inputDim_];
241 
242  return sparsityMat;
243 }
244 
245 template <int IN_DIM, int OUT_DIM>
246 Eigen::Matrix<bool, Eigen::Dynamic, Eigen::Dynamic> DerivativesCppadJIT<IN_DIM, OUT_DIM>::getSparsityPatternHessian()
247 {
248  assert(model_->isHessianSparsityAvailable() == true);
249 
250  std::vector<bool> sparsityVec = model_->HessianSparsityBool();
251  Eigen::Matrix<bool, Eigen::Dynamic, Eigen::Dynamic> sparsityMat(inputDim_, inputDim_);
252 
253  assert(sparsityVec.size() == inputDim_ * inputDim_);
254  for (int row = 0; row < inputDim_; ++row)
255  for (int col = 0; col < inputDim_; ++col)
256  {
257  sparsityMat(row, col) = sparsityVec[col + row * inputDim_];
258  }
259 
260  return sparsityMat;
261 }
262 
263 template <int IN_DIM, int OUT_DIM>
264 void DerivativesCppadJIT<IN_DIM, OUT_DIM>::getSparsityPatternJacobian(Eigen::VectorXi& rows, Eigen::VectorXi& columns)
265 {
266  assert(model_->isJacobianSparsityAvailable() == true);
267 
268  rows = sparsityRowsJacobianEigen_;
269  columns = sparsityColsJacobianEigen_;
270 }
271 
272 template <int IN_DIM, int OUT_DIM>
273 size_t DerivativesCppadJIT<IN_DIM, OUT_DIM>::getNumNonZerosJacobian()
274 {
275  assert(model_->isJacobianSparsityAvailable() == true);
276  return sparsityRowsJacobian_.size();
277 }
278 
279 template <int IN_DIM, int OUT_DIM>
280 size_t DerivativesCppadJIT<IN_DIM, OUT_DIM>::getNumNonZerosHessian()
281 {
282  assert(model_->isJacobianSparsityAvailable() == true);
283  return sparsityRowsHessian_.size();
284 }
285 
286 template <int IN_DIM, int OUT_DIM>
287 void DerivativesCppadJIT<IN_DIM, OUT_DIM>::getSparsityPatternHessian(Eigen::VectorXi& rows, Eigen::VectorXi& columns)
288 {
289  assert(model_->isHessianSparsityAvailable() == true);
290 
291  rows = sparsityRowsHessianEigen_;
292  columns = sparsityColsHessianEigen_;
293 }
294 
295 template <int IN_DIM, int OUT_DIM>
296 void DerivativesCppadJIT<IN_DIM, OUT_DIM>::compileJIT(const DerivativesCppadSettings& settings,
297  const std::string& libName,
298  bool verbose)
299 {
300  if (compiled_)
301  return;
302 
303  struct timespec ts;
304  clock_gettime(CLOCK_MONOTONIC, &ts);
305 
306  // assigning a unique identifier to the library in order to avoid race conditions in JIT
307  std::string uniqueID =
308  std::to_string(std::hash<std::thread::id>()(std::this_thread::get_id())) + "_" + std::to_string(ts.tv_nsec);
309 
310  libName_ = libName + uniqueID;
311 
312  CppAD::cg::ModelCSourceGen<double> cgen(cgCppadFun_, libName_);
313 
314  cgen.setMultiThreading(settings.multiThreading_);
315  cgen.setCreateForwardZero(settings.createForwardZero_);
316  cgen.setCreateForwardOne(settings.createForwardOne_);
317  cgen.setCreateReverseOne(settings.createReverseOne_);
318  cgen.setCreateReverseTwo(settings.createReverseTwo_);
319  cgen.setCreateJacobian(settings.createJacobian_);
320  cgen.setCreateSparseJacobian(settings.createSparseJacobian_);
321  cgen.setCreateHessian(settings.createHessian_);
322  cgen.setCreateSparseHessian(settings.createSparseHessian_);
323  cgen.setMaxAssignmentsPerFunc(settings.maxAssignements_);
324 
325  CppAD::cg::ModelLibraryCSourceGen<double> libcgen(cgen);
326 
327  if (settings.useDynamicLibrary_)
328  {
329  std::string tempDir = "cppad_temp" + uniqueID;
330  if (verbose)
331  {
332  std::cout << "DerivativesCppadJIT: starting to compile dynamic library" << libName_ << std::endl;
333  std::cout << "DerivativesCppadJIT: in temporary directory " << tempDir << std::endl;
334  }
335 
336  // compile a dynamic library
337  CppAD::cg::DynamicModelLibraryProcessor<double> p(libcgen, libName_);
338 
339  if (settings.compiler_ == DerivativesCppadSettings::GCC)
340  {
341  CppAD::cg::GccCompiler<double> compiler;
342  compiler.setTemporaryFolder(tempDir);
343  dynamicLib_ = std::shared_ptr<CppAD::cg::DynamicLib<double>>(p.createDynamicLibrary(compiler));
344  }
345  else if (settings.compiler_ == DerivativesCppadSettings::CLANG)
346  {
347  CppAD::cg::ClangCompiler<double> compiler;
348  compiler.setTemporaryFolder(tempDir);
349  dynamicLib_ = std::shared_ptr<CppAD::cg::DynamicLib<double>>(p.createDynamicLibrary(compiler));
350  }
351  else
352  throw std::runtime_error("Unknown compiler type for dynamic library, support only gcc and clang.");
353 
354  // extract model
355  model_ = std::shared_ptr<CppAD::cg::GenericModel<double>>(dynamicLib_->model(libName_));
356  }
357  else // use regular JIT
358  {
359  if (verbose)
360  {
361  std::cout << "DerivativesCppadJIT: starting to compile with LLVM library " << libName_ << std::endl;
362  }
363 #ifdef LLVM_VERSION_MAJOR
364  // JIT compile source code
365  CppAD::cg::LlvmModelLibraryProcessor<double> p(libcgen);
366  llvmModelLib_ = p.create();
367 
368  //extract model
369  model_ = std::shared_ptr<CppAD::cg::GenericModel<double>>(llvmModelLib_->model(libName_));
370 #else
371  throw std::runtime_error("DerivativesCppadJIT: LLVM not installed.");
372 #endif
373  }
374 
375 
376  if (settings.generateSourceCode_)
377  {
378  if (verbose)
379  {
380  std::cout << "DerivativesCppadJIT: generating source-code... " << libName_ << std::endl;
381  }
382  CppAD::cg::SaveFilesModelLibraryProcessor<double> p2(libcgen);
383  p2.saveSources();
384  }
385 
386  compiled_ = true;
387 
388  if (model_->isJacobianSparsityAvailable())
389  {
390  if (verbose)
391  std::cout << "DerivativesCppadJIT: updating Jacobian sparsity pattern" << std::endl;
392 
393  model_->JacobianSparsity(sparsityRowsJacobian_, sparsityColsJacobian_);
394  sparsityRowsJacobianEigen_.resize(sparsityRowsJacobian_.size()); //rowsTmp.resize(rowsVec.size());
395  sparsityColsJacobianEigen_.resize(sparsityColsJacobian_.size()); //colsTmp.resize(rowsVec.size());
396 
397  Eigen::Matrix<size_t, Eigen::Dynamic, 1> rowsSizeT;
398  rowsSizeT.resize(sparsityRowsJacobian_.size());
399  Eigen::Matrix<size_t, Eigen::Dynamic, 1> colsSizeT;
400  colsSizeT.resize(sparsityColsJacobian_.size());
401 
402  rowsSizeT = Eigen::Map<Eigen::Matrix<size_t, Eigen::Dynamic, 1>>(
403  sparsityRowsJacobian_.data(), sparsityRowsJacobian_.size(), 1);
404  colsSizeT = Eigen::Map<Eigen::Matrix<size_t, Eigen::Dynamic, 1>>(
405  sparsityColsJacobian_.data(), sparsityColsJacobian_.size(), 1);
406 
407  sparsityRowsJacobianEigen_ = rowsSizeT.cast<int>();
408  sparsityColsJacobianEigen_ = colsSizeT.cast<int>();
409  }
410 
411  if (model_->isHessianSparsityAvailable())
412  {
413  if (verbose)
414  std::cout << "DerivativesCppadJIT: updating Hessian sparsity pattern" << std::endl;
415 
416  model_->HessianSparsity(sparsityRowsHessian_, sparsityColsHessian_);
417  sparsityRowsHessianEigen_.resize(sparsityRowsHessian_.size()); //rowsTmp.resize(rowsVec.size());
418  sparsityColsHessianEigen_.resize(sparsityColsHessian_.size()); //colsTmp.resize(rowsVec.size());
419 
420  Eigen::Matrix<size_t, Eigen::Dynamic, 1> rowsSizeT;
421  rowsSizeT.resize(sparsityRowsHessian_.size());
422  Eigen::Matrix<size_t, Eigen::Dynamic, 1> colsSizeT;
423  colsSizeT.resize(sparsityColsHessian_.size());
424 
425  rowsSizeT = Eigen::Map<Eigen::Matrix<size_t, Eigen::Dynamic, 1>>(
426  sparsityRowsHessian_.data(), sparsityRowsHessian_.size(), 1);
427  colsSizeT = Eigen::Map<Eigen::Matrix<size_t, Eigen::Dynamic, 1>>(
428  sparsityColsHessian_.data(), sparsityColsHessian_.size(), 1);
429 
430  sparsityRowsHessianEigen_ = rowsSizeT.cast<int>();
431  sparsityColsHessianEigen_ = colsSizeT.cast<int>();
432  }
433 
434  if (verbose)
435  std::cout << "DerivativesCppadJIT: compileJIT() completed." << std::endl;
436 }
437 
438 template <int IN_DIM, int OUT_DIM>
439 auto DerivativesCppadJIT<IN_DIM, OUT_DIM>::getDynamicLib() -> const std::shared_ptr<CppAD::cg::DynamicLib<double>>
440 {
441  if (dynamicLib_)
442  return dynamicLib_;
443  else
444  throw std::runtime_error("DerivativesCppADJIT: dynamic lib not compiled.");
445 }
446 
447 #ifdef LLVM_VERSION_MAJOR
448 template <int IN_DIM, int OUT_DIM>
449 auto DerivativesCppadJIT<IN_DIM, OUT_DIM>::getLlvmLib() -> const std::shared_ptr<CppAD::cg::LlvmModelLibrary<double>>
450 {
451  if (llvmModelLib_)
452  return llvmModelLib_;
453  else
454  throw std::runtime_error("DerivativesCppADJIT: llvm library not available.");
455 }
456 #endif
457 
458 template <int IN_DIM, int OUT_DIM>
459 void DerivativesCppadJIT<IN_DIM, OUT_DIM>::recordCg()
460 {
461  // input vector, needs to be dynamic size
462  Eigen::Matrix<CG_SCALAR, Eigen::Dynamic, 1> x(inputDim_);
463 
464  // declare x as independent
465  CppAD::Independent(x);
466 
467  // output vector, needs to be dynamic size
468  Eigen::Matrix<CG_SCALAR, Eigen::Dynamic, 1> y(outputDim_);
469 
470  y = cgStdFun_(x);
471 
472  // store operation sequence in f: x -> y and stop recording
473  CppAD::ADFun<CG_VALUE_TYPE> fCodeGen(x, y);
474 
475  fCodeGen.optimize();
476 
477  cgCppadFun_ = fCodeGen;
478 }
479 
480 
481 } /* namespace core */
482 } /* namespace ct */
483 
484 #endif
ct::core::StateVector< state_dim > x
const bool verbose
Definition: JacobianCGTest.h:19