CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
loop_free_model.hpp
1 #ifndef CPPAD_CG_LOOP_FREE_MODEL_INCLUDED
2 #define CPPAD_CG_LOOP_FREE_MODEL_INCLUDED
3 /* --------------------------------------------------------------------------
4  * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5  * Copyright (C) 2013 Ciengis
6  * Copyright (C) 2020 Joao Leal
7  *
8  * CppADCodeGen is distributed under multiple licenses:
9  *
10  * - Eclipse Public License Version 1.0 (EPL1), and
11  * - GNU General Public License Version 3 (GPL3).
12  *
13  * EPL1 terms and conditions can be found in the file "epl-v10.txt", while
14  * terms and conditions for the GPL3 can be found in the file "gpl3.txt".
15  * ----------------------------------------------------------------------------
16  * Author: Joao Leal
17  */
18 
19 namespace CppAD {
20 namespace cg {
21 
28 template <class Base>
29 class LoopFreeModel {
30 public:
31  using CGB = CppAD::cg::CG<Base>;
32  using Arg = Argument<Base>;
33  using VectorSet = std::vector<std::set<size_t> >;
34 protected:
38  ADFun<CGB> * const fun_;
42  std::vector<size_t> dependentIndexes_;
43  std::map<size_t, size_t> dependentOrig2Local;
47  VectorSet jacTapeSparsity_;
48  bool jacSparsity_;
59  // whether or not the hessian sparsities have been evaluated
60  bool hessSparsity_;
61 public:
62 
70  const std::vector<size_t>& dependentOrigIndexes) :
71  fun_(fun),
72  dependentIndexes_(dependentOrigIndexes),
73  jacSparsity_(false),
74  hessSparsity_(false) {
75  CPPADCG_ASSERT_KNOWN(fun != nullptr, "fun cannot be null")
76  CPPADCG_ASSERT_KNOWN(dependentOrigIndexes.size() <= fun->Range(), "invalid size")
77 
78  for (size_t il = 0; il < dependentIndexes_.size(); il++)
79  dependentOrig2Local[dependentIndexes_[il]] = il;
80  }
81 
82  LoopFreeModel(const LoopFreeModel<Base>&) = delete;
83  LoopFreeModel& operator=(const LoopFreeModel<Base>&) = delete;
84 
85  inline ADFun<CGB>& getTape() const {
86  return *fun_;
87  }
88 
89  inline size_t getTapeDependentCount() const {
90  return fun_->Range();
91  }
92 
93  inline size_t getTemporaryDependentCount() const {
94  return fun_->Range() - dependentIndexes_.size();
95  }
96 
97  inline size_t getTapeIndependentCount() const {
98  return fun_->Domain();
99  }
100 
105  inline const std::vector<size_t>& getOrigDependentIndexes() const {
106  return dependentIndexes_;
107  }
108 
109  inline size_t getLocalDependentIndex(size_t origI) const {
110  return dependentOrig2Local.at(origI);
111  }
112 
113  inline void evalJacobianSparsity() {
114  if (!jacSparsity_) {
115  jacTapeSparsity_ = jacobianSparsitySet<VectorSet, CGB>(*fun_);
116  jacSparsity_ = true;
117  }
118  }
119 
120  inline const VectorSet& getJacobianSparsity() const {
121  return jacTapeSparsity_;
122  }
123 
124  inline void evalHessianSparsity() {
125  if (!hessSparsity_) {
126  size_t mo = dependentIndexes_.size();
127  size_t m = fun_->Range();
128  size_t n = fun_->Domain();
129 
130  // hessian for the original equations
131  std::set<size_t> eqs;
132  if (mo != 0) {
133  for (size_t i = 0; i < mo; i++)
134  eqs.insert(eqs.end(), i);
135 
136  hessTapeOrigEqSparsity_ = hessianSparsitySet<std::vector<std::set<size_t> >, CGB>(*fun_, eqs);
137  }
138 
139  // hessian for the temporary variable equations
140  if (m != mo) {
141  eqs.clear();
142  for (size_t i = mo; i < m; i++)
143  eqs.insert(eqs.end(), i);
144  hessTapeTempSparsity_ = hessianSparsitySet<std::vector<std::set<size_t> >, CGB>(*fun_, eqs);
145  } else {
146  hessTapeTempSparsity_.resize(n);
147  }
148 
149  hessSparsity_ = true;
150  }
151  }
152 
153  inline const VectorSet& getHessianTempEqsSparsity() const {
154  CPPADCG_ASSERT_UNKNOWN(hessSparsity_)
155  return hessTapeTempSparsity_;
156  }
157 
158  inline const VectorSet& getHessianOrigEqsSparsity() const {
159  CPPADCG_ASSERT_UNKNOWN(hessSparsity_)
161  }
162 
174  const std::set<size_t>& iterations,
175  size_t iterCount,
176  const CG<Base>& value,
177  IndexOperationNode<Base>& iterationIndexOp) {
178  using namespace std;
179 
180  if (iterations.size() == iterCount) {
181  // present in all iterations
182 
183  return value;
184 
185  } else {
186 
187  // no point in creating branches where both branch sides are zero
188  if (value.isIdenticalZero())
189  return value;
190 
195  OperationNode<Base>* tmpDclVar = handler.makeNode(CGOpCode::TmpDcl);
196  Argument<Base> tmpArg(*tmpDclVar);
197 
198  set<size_t> usedIter;
199  OperationNode<Base>* cond = loops::createIndexConditionExpressionOp<Base>(handler, iterations, usedIter, iterCount - 1, iterationIndexOp);
200 
201  // if
202  OperationNode<Base>* ifStart = handler.makeNode(CGOpCode::StartIf, *cond);
203 
204  OperationNode<Base>* tmpAssign1 = handler.makeNode(CGOpCode::LoopIndexedTmp,{tmpArg, asArgument(value)});
205  OperationNode<Base>* ifAssign = handler.makeNode(CGOpCode::CondResult,{*ifStart, *tmpAssign1});
206 
207  // else
208  OperationNode<Base>* elseStart = handler.makeNode(CGOpCode::Else,{*ifStart, *ifAssign});
209 
210  OperationNode<Base>* tmpAssign2 = handler.makeNode(CGOpCode::LoopIndexedTmp,{tmpArg, Base(0)});
211  OperationNode<Base>* elseAssign = handler.makeNode(CGOpCode::CondResult,{*elseStart, *tmpAssign2});
212 
213  // end if
214  OperationNode<Base>* endIf = handler.makeNode(CGOpCode::EndIf,{*elseStart, *elseAssign});
215 
216  //
217  OperationNode<Base>* tmpVar = handler.makeNode(CGOpCode::Tmp,{tmpArg, *endIf});
218  return CG<Base>(*tmpVar);
219  }
220 
221  }
222 
235  inline std::map<size_t, std::map<size_t, CGB> > calculateJacobianHessianUsedByLoops(CodeHandler<Base>& handler,
236  std::map<LoopModel<Base>*, loops::HessianWithLoopsInfo<Base> >& loopHessInfo,
237  const std::vector<CGB>& x,
238  std::vector<CGB>& temps,
239  const VectorSet& noLoopEvalJacSparsity,
240  bool individualColoring) {
241  using namespace std;
242  using namespace CppAD::cg::loops;
243 
244  CPPADCG_ASSERT_UNKNOWN(hessSparsity_) // check that the sparsities have been evaluated
245 
246  size_t mo = dependentIndexes_.size();
247  size_t m = getTapeDependentCount();
248  size_t n = fun_->Domain();
249 
250  std::vector<std::vector<CGB> > vwNoLoop(loopHessInfo.size());
251  std::vector<map<size_t, map<size_t, CGB> > > vhessNoLoop(loopHessInfo.size());
252 
256  std::vector<std::set<size_t> > noLoopEvalHessTempsSparsity(n);
257 
258  for (const auto& itLoop2Info : loopHessInfo) {
259  const HessianWithLoopsInfo<Base>& info = itLoop2Info.second;
260 
261  addMatrixSparsity(info.noLoopEvalHessTempsSparsity, noLoopEvalHessTempsSparsity);
262  }
263  std::vector<size_t> hesRow, hesCol;
264  generateSparsityIndexes(noLoopEvalHessTempsSparsity, hesRow, hesCol);
265 
266  size_t l = 0;
267  for (const auto& itLoop2Info : loopHessInfo) {
268  LoopModel<Base>* loop = itLoop2Info.first;
269  const HessianWithLoopsInfo<Base>& info = itLoop2Info.second;
270 
271  const std::vector<IterEquationGroup<Base> >& eqGroups = loop->getEquationsGroups();
272  size_t nIterations = loop->getIterationCount();
273  size_t nEqGroups = eqGroups.size();
274 
275  std::vector<CGB>& wNoLoop = vwNoLoop[l];
276  wNoLoop.resize(m);
277  for (size_t inl = 0; inl < mo; inl++) {
278  wNoLoop[inl] = Base(0);
279  }
280 
281  for (size_t inl = mo; inl < m; inl++) {
282  size_t k = inl - mo;
283  const LoopPosition* posK = loop->getTempIndepIndexes(k);
284 
285  if (posK != nullptr) {
286 
287  for (size_t g = 0; g < nEqGroups; g++) {
288  const IterEquationGroup<Base>& group = eqGroups[g];
289 
290  CGB v = Base(0);
291 
292  for (size_t tapeI : group.tapeI) {
293  const map<size_t, CGB>& row = info.dyiDzk[tapeI];
294  typename map<size_t, CGB>::const_iterator itCol = row.find(posK->tape);
295  if (itCol != row.end()) {
296  const CGB& dydz = itCol->second;
297  v += dydz * info.w[tapeI];
298  }
299  }
300 
304  v = createConditionalOperation(handler,
305  group.iterations,
306  nIterations,
307  v,
308  *info.iterationIndexOp);
309 
310  wNoLoop[inl] += v;
311  }
312 
313  }
314  }
315 
316  l++;
317  }
318 
319  std::vector<map<size_t, CGB> > dyDx;
320  generateLoopForJacHes(*fun_, x, vwNoLoop, temps,
321  getJacobianSparsity(),
322  noLoopEvalJacSparsity,
323  dyDx,
324  hessTapeTempSparsity_,
325  noLoopEvalHessTempsSparsity,
326  vhessNoLoop,
327  individualColoring);
328 
329  // save Jacobian
330  map<size_t, map<size_t, CGB> > dzDx;
331  for (size_t inl = mo; inl < m; inl++) {
332  // dz_k/dx_v (for temporary variable)
333  size_t k = inl - mo;
334  dzDx[k] = dyDx[inl];
335  }
336 
337  // save Hessian
338  l = 0;
339  for (auto& itLoop2Info : loopHessInfo) {
340  HessianWithLoopsInfo<Base>& info = itLoop2Info.second;
341  info.dzDxx = vhessNoLoop[l];
342  l++;
343  }
344 
345  return dzDx;
346  }
347 
348  inline void calculateHessian4OrignalEquations(const std::vector<CGB>& x,
349  const std::vector<CGB>& w,
350  const VectorSet& noLoopEvalHessSparsity,
351  const std::vector<std::map<size_t, std::set<size_t> > >& noLoopEvalHessLocations,
352  std::vector<CGB>& hess) {
353  using namespace std;
354  using namespace CppAD::cg::loops;
355 
356  CPPADCG_ASSERT_UNKNOWN(hessSparsity_) // check that the sparsities have been evaluated
357 
358  std::vector<CGB> wNoLoop(getTapeDependentCount());
359  std::vector<CGB> hessNoLoop;
360 
364  std::vector<size_t> row, col;
365  generateSparsityIndexes(noLoopEvalHessSparsity, row, col);
366 
367  if (!row.empty()) {
368  hessNoLoop.resize(row.size());
369 
370  for (size_t inl = 0; inl < dependentIndexes_.size(); inl++) {
371  wNoLoop[inl] = w[dependentIndexes_[inl]];
372  }
373 
374  CppAD::sparse_hessian_work work; // temporary structure for CPPAD
375  // "cppad.symmetric" may have missing values for functions using
376  // atomic functions which only provide half of the elements
377  // (some values could be zeroed)
378  work.color_method = "cppad.general";
379  fun_->SparseHessian(x, wNoLoop, hessTapeOrigEqSparsity_, row, col, hessNoLoop, work);
380 
381  // save non-indexed hessian elements
382  for (size_t el = 0; el < row.size(); el++) {
383  size_t j1 = row[el];
384  size_t j2 = col[el];
385  const set<size_t>& locations = noLoopEvalHessLocations[j1].at(j2);
386  for (size_t itE : locations)
387  hess[itE] = hessNoLoop[el];
388  }
389  }
390  }
391 
392  virtual ~LoopFreeModel() {
393  delete fun_;
394  }
395 
396 };
397 
398 } // END cg namespace
399 } // END CppAD namespace
400 
401 #endif
const std::vector< size_t > & getOrigDependentIndexes() const
void calculateHessian4OrignalEquations(const std::vector< CGB > &x, const std::vector< CGB > &w, const VectorSet &noLoopEvalHessSparsity, const std::vector< std::map< size_t, std::set< size_t > > > &noLoopEvalHessLocations, std::vector< CGB > &hess)
STL namespace.
std::vector< size_t > dependentIndexes_
std::vector< std::map< size_t, CG< Base > > > dyiDzk
const LoopPosition * getTempIndepIndexes(size_t k) const
Definition: loop_model.hpp:357
std::map< size_t, std::map< size_t, CGB > > calculateJacobianHessianUsedByLoops(CodeHandler< Base > &handler, std::map< LoopModel< Base > *, loops::HessianWithLoopsInfo< Base > > &loopHessInfo, const std::vector< CGB > &x, std::vector< CGB > &temps, const VectorSet &noLoopEvalJacSparsity, bool individualColoring)
const size_t getIterationCount() const
Definition: loop_model.hpp:254
const std::vector< IterEquationGroup< Base > > & getEquationsGroups() const
Definition: loop_model.hpp:298
CG< Base > createConditionalOperation(CodeHandler< Base > &handler, const std::set< size_t > &iterations, size_t iterCount, const CG< Base > &value, IndexOperationNode< Base > &iterationIndexOp)
ADFun< CGB > *const fun_
LoopFreeModel(ADFun< CGB > *fun, const std::vector< size_t > &dependentOrigIndexes)
std::set< size_t > tapeI
equations indexes in tape of the loop model
std::set< size_t > iterations
iterations which only have these equations defined