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