CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
model_c_source_gen_loops_jac.hpp
1 #ifndef CPPAD_CG_MODEL_C_SOURCE_GEN_LOOPS_JAC_INCLUDED
2 #define CPPAD_CG_MODEL_C_SOURCE_GEN_LOOPS_JAC_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 
21 namespace loops {
22 
23 template<class Base>
24 std::pair<CG<Base>, IndexPattern*> createJacobianElement(CodeHandler<Base>& handler,
25  const std::vector<size_t>& positions,
26  const CG<Base>& dfdx,
27  IndexOperationNode<Base>& iterationIndexOp,
28  std::vector<IfElseInfo<Base> >& ifElses,
29  std::set<size_t>& allLocations);
30 
31 } // END loops namespace
32 
33 /***************************************************************************
34  * Methods related with loop insertion into the operation graph
35  **************************************************************************/
36 
37 template<class Base>
38 void ModelCSourceGen<Base>::analyseSparseJacobianWithLoops(const std::vector<size_t>& rows,
39  const std::vector<size_t>& cols,
40  const std::vector<size_t>& location,
41  std::vector<std::set<size_t> >& noLoopEvalSparsity,
42  std::vector<std::map<size_t, std::set<size_t> > >& noLoopEvalLocations,
43  std::map<LoopModel<Base>*, std::vector<std::set<size_t> > >& loopsEvalSparsities,
44  std::map<LoopModel<Base>*, std::vector<loops::JacobianWithLoopsRowInfo> >& loopEqInfo) {
45 
46  using namespace std;
47  using namespace loops;
48 
49  CPPADCG_ASSERT_UNKNOWN(rows.size() == cols.size());
50  CPPADCG_ASSERT_UNKNOWN(rows.size() == location.size());
51 
55  for (LoopModel<Base>* l : _loopTapes) {
56  l->evalJacobianSparsity();
57  }
58 
59  if (_funNoLoops != nullptr)
60  _funNoLoops->evalJacobianSparsity();
61 
65  size_t nonIndexdedEqSize = _funNoLoops != nullptr ? _funNoLoops->getOrigDependentIndexes().size() : 0;
66  noLoopEvalSparsity.resize(_funNoLoops != nullptr ? _funNoLoops->getTapeDependentCount() : 0);
67 
68  // tape equation -> original J -> locations
69  noLoopEvalLocations.resize(noLoopEvalSparsity.size());
70 
71  // loop -> equation -> row info
72  for (LoopModel<Base>* loop : _loopTapes) {
73  loopEqInfo[loop].resize(loop->getTapeDependentCount());
74  loopsEvalSparsities[loop].resize(loop->getTapeDependentCount());
75  }
76 
77  size_t nnz = rows.size();
78 
82  for (size_t el = 0; el < nnz; el++) {
83  size_t i = rows[el];
84  size_t j = cols[el];
85  size_t e = location[el];
86 
87  // find LOOP + get loop results
88  LoopModel<Base>* loop = nullptr;
89 
90  size_t tapeI;
91  size_t iteration;
92 
93  for (LoopModel<Base>* l : _loopTapes) {
94  const std::map<size_t, LoopIndexedPosition>& depIndexes = l->getOriginalDependentIndexes();
95  const auto iti = depIndexes.find(i);
96  if (iti != depIndexes.end()) {
97  loop = l;
98  tapeI = iti->second.tape;
99  iteration = iti->second.iteration;
100  break;
101  }
102  }
103 
104  if (loop == nullptr) {
108  CPPADCG_ASSERT_UNKNOWN(_funNoLoops != nullptr);
109  size_t il = _funNoLoops->getLocalDependentIndex(i);
110 
111  noLoopEvalSparsity[il].insert(j);
112  noLoopEvalLocations[il][j].insert(e);
113 
114  } else {
118  size_t iterations = loop->getIterationCount();
119 
120  const std::vector<std::vector<LoopPosition> >& indexedIndepIndexes = loop->getIndexedIndepIndexes();
121  const std::vector<LoopPosition>& nonIndexedIndepIndexes = loop->getNonIndexedIndepIndexes();
122  const std::vector<LoopPosition>& temporaryIndependents = loop->getTemporaryIndependents();
123 
124  size_t nIndexed = indexedIndepIndexes.size();
125  size_t nNonIndexed = nonIndexedIndepIndexes.size();
126 
127  const std::vector<std::set<size_t> >& loopSparsity = loop->getJacobianSparsity();
128  const std::set<size_t>& loopRow = loopSparsity[tapeI];
129 
130  JacobianWithLoopsRowInfo& rowInfo = loopEqInfo[loop][tapeI];
131 
132  std::set<size_t>& loopEvalRow = loopsEvalSparsities[loop][tapeI];
133 
138  const std::set<size_t>& tapeJs = loop->getIndexedTapeIndexes(iteration, j);
139  for (size_t tapeJ : tapeJs) {
140  if (loopRow.find(tapeJ) != loopRow.end()) {
141  loopEvalRow.insert(tapeJ);
142 
143  //this indexed variable must be request for all iterations
144  std::vector<size_t>& positions = rowInfo.indexedPositions[tapeJ];
145  positions.resize(iterations, (std::numeric_limits<size_t>::max)());
146  if (positions[iteration] != (std::numeric_limits<size_t>::max)()) {
147  throw CGException("Repeated Jacobian elements requested (equation ", i, ", variable ", j, ")");
148  }
149  positions[iteration] = e;
150  }
151  }
152 
156  const LoopPosition* pos = loop->getNonIndexedIndepIndexes(j);
157  bool jInNonIndexed = false;
158  if (pos != nullptr && loopRow.find(pos->tape) != loopRow.end()) {
159  loopEvalRow.insert(pos->tape);
160 
161  //this non-indexed element must be request for all iterations
162  std::vector<size_t>& positions = rowInfo.nonIndexedPositions[j];
163  positions.resize(iterations, (std::numeric_limits<size_t>::max)());
164  if (positions[iteration] != (std::numeric_limits<size_t>::max)()) {
165  throw CGException("Repeated Jacobian elements requested (equation ", i, ", variable ", j, ")");
166  }
167  positions[iteration] = e;
168 
169  rowInfo.nonIndexedEvals.insert(j);
170 
171  jInNonIndexed = true;
172  }
173 
177  if (_funNoLoops != nullptr) {
178  set<size_t>::const_iterator itz = loopRow.lower_bound(nIndexed + nNonIndexed);
179 
180  // loop temporary variables
181  for (; itz != loopRow.end(); ++itz) {
182  size_t tapeJ = *itz;
183  size_t k = temporaryIndependents[tapeJ - nIndexed - nNonIndexed].original;
184 
188  bool used = false;
189  const set<size_t>& sparsity = _funNoLoops->getJacobianSparsity()[nonIndexdedEqSize + k];
190  if (sparsity.find(j) != sparsity.end()) {
191  noLoopEvalSparsity[nonIndexdedEqSize + k].insert(j); // element required
192  if (!jInNonIndexed) {
193  std::vector<size_t>& positions = rowInfo.nonIndexedPositions[j];
194  positions.resize(iterations, (std::numeric_limits<size_t>::max)());
195  if (positions[iteration] != (std::numeric_limits<size_t>::max)()) {
196  throw CGException("Repeated Jacobian elements requested (equation ", i, ", variable ", j, ")");
197  }
198  positions[iteration] = e;
199  jInNonIndexed = true;
200  }
201  rowInfo.tmpEvals[j].insert(k);
202  used = true;
203  }
204 
205  if (used) {
206  // this temporary variable should be evaluated
207  loopEvalRow.insert(tapeJ);
208  }
209  }
210  }
211 
212  }
213  }
214 }
215 
216 template<class Base>
218  const std::vector<CGBase>& x,
219  bool forward) {
220  using namespace std;
221  using namespace CppAD::cg::loops;
222  //printSparsityPattern(_jacSparsity.rows, _jacSparsity.cols, "jacobian", _fun->Range());
223 
224  handler.setZeroDependents(true);
225 
226  size_t nonIndexdedEqSize = _funNoLoops != nullptr ? _funNoLoops->getOrigDependentIndexes().size() : 0;
227 
228  std::vector<set<size_t> > noLoopEvalSparsity;
229  std::vector<map<size_t, set<size_t> > > noLoopEvalLocations; // tape equation -> original J -> locations
230  map<LoopModel<Base>*, std::vector<set<size_t> > > loopsEvalSparsities;
231  map<LoopModel<Base>*, std::vector<JacobianWithLoopsRowInfo> > loopEqInfo;
232 
233  size_t nnz = _jacSparsity.rows.size();
234  std::vector<size_t> locations(nnz);
235  for (size_t e = 0; e < nnz; e++)
236  locations[e] = e;
237 
238  analyseSparseJacobianWithLoops(_jacSparsity.rows, _jacSparsity.cols, locations,
239  noLoopEvalSparsity, noLoopEvalLocations, loopsEvalSparsities, loopEqInfo);
240 
241 
242  std::vector<CGBase> jac(nnz);
243 
244  /***********************************************************************
245  * generate the operation graph
246  **********************************************************************/
247 
251  // temporaries (zero orders)
252  std::vector<CGBase> tmps;
253 
254  // Jacobian for temporaries
255  std::vector<map<size_t, CGBase> > dzDx(_funNoLoops != nullptr ? _funNoLoops->getTemporaryDependentCount() : 0);
256 
257  // Jacobian for equations outside loops
258  std::vector<CGBase> jacNoLoop;
259  if (_funNoLoops != nullptr) {
260  ADFun<CGBase>& fun = _funNoLoops->getTape();
261 
265  std::vector<CGBase> depNL = _funNoLoops->getTape().Forward(0, x);
266 
267  tmps.resize(depNL.size() - nonIndexdedEqSize);
268  for (size_t i = 0; i < tmps.size(); i++)
269  tmps[i] = depNL[nonIndexdedEqSize + i];
270 
274  std::vector<size_t> row, col;
275  generateSparsityIndexes(noLoopEvalSparsity, row, col);
276  jacNoLoop.resize(row.size());
277 
278  CppAD::sparse_jacobian_work work; // temporary structure for CPPAD
279  if (forward) {
280  fun.SparseJacobianForward(x, _funNoLoops->getJacobianSparsity(), row, col, jacNoLoop, work);
281  } else {
282  fun.SparseJacobianReverse(x, _funNoLoops->getJacobianSparsity(), row, col, jacNoLoop, work);
283  }
284 
285  for (size_t el = 0; el < row.size(); el++) {
286  size_t il = row[el];
287  size_t j = col[el];
288  if (il < nonIndexdedEqSize) {
289  // (dy_i/dx_v) elements from equations outside loops
290  const std::set<size_t>& locations = noLoopEvalLocations[il][j];
291  for (size_t itE : locations)
292  jac[itE] = jacNoLoop[el];
293  } else {
294  // dz_k/dx_v (for temporary variable)
295  size_t k = il - nonIndexdedEqSize;
296  dzDx[k][j] = jacNoLoop[el];
297  }
298  }
299  }
300 
301  /***********************************************************************
302  * Generate loop body
303  **********************************************************************/
304  OperationNode<Base>* iterationIndexDcl = handler.makeIndexDclrNode(LoopModel<Base>::ITERATION_INDEX_NAME);
305 
306  std::vector<CGBase> jacLoop;
307 
308  // loop loops :)
309  typename map<LoopModel<Base>*, std::vector<JacobianWithLoopsRowInfo> >::iterator itl2Eq;
310  for (itl2Eq = loopEqInfo.begin(); itl2Eq != loopEqInfo.end(); ++itl2Eq) {
311  LoopModel<Base>& lModel = *itl2Eq->first;
312  std::vector<JacobianWithLoopsRowInfo>& eqs = itl2Eq->second;
313  ADFun<CGBase>& fun = lModel.getTape();
314 
315  std::vector<IfElseInfo<Base> > ifElses;
316 
320  LoopStartOperationNode<Base>* loopStart = handler.makeLoopStartNode(*iterationIndexDcl, lModel.getIterationCount());
321 
322  IndexOperationNode<Base>* iterationIndexOp = handler.makeIndexNode(*loopStart);
323  std::set<IndexOperationNode<Base>*> indexesOps;
324  indexesOps.insert(iterationIndexOp);
325 
329  std::vector<CGBase> indexedIndeps = createIndexedIndependents(handler, lModel, *iterationIndexOp);
330  std::vector<CGBase> xl = createLoopIndependentVector(handler, lModel, indexedIndeps, x, tmps);
331 
332  std::vector<size_t> row, col;
333  generateSparsityIndexes(loopsEvalSparsities[&lModel], row, col);
334  jacLoop.resize(row.size());
335 
336  if (row.size() == 0) {
337  continue;
338  }
339 
340  CppAD::sparse_jacobian_work work; // temporary structure for CppAD
341  if (forward) {
342  fun.SparseJacobianForward(xl, lModel.getJacobianSparsity(), row, col, jacLoop, work);
343  } else {
344  fun.SparseJacobianReverse(xl, lModel.getJacobianSparsity(), row, col, jacLoop, work);
345  }
346 
347  // organize results
348  std::vector<std::map<size_t, CGBase> > dyiDxtape(lModel.getTapeDependentCount());
349  for (size_t el = 0; el < jacLoop.size(); el++) {
350  size_t tapeI = row[el];
351  size_t tapeJ = col[el];
352  dyiDxtape[tapeI][tapeJ] = jacLoop[el];
353  }
354 
355  // all assigned elements in the compressed Jacobian by this loop
356  std::set<size_t> allLocations;
357 
358  // store results in indexedLoopResults
359  size_t maxJacElSize = 0;
360  for (size_t tapeI = 0; tapeI < eqs.size(); tapeI++) {
361  JacobianWithLoopsRowInfo& rowInfo = eqs[tapeI];
362  maxJacElSize += rowInfo.indexedPositions.size();
363  maxJacElSize += rowInfo.nonIndexedPositions.size();
364  }
365 
366  std::vector<std::pair<CGBase, IndexPattern*> > indexedLoopResults(maxJacElSize);
367 
368  // create the dependents (jac elements) for indexed and constant
369  size_t jacLE = 0;
370  for (size_t tapeI = 0; tapeI < eqs.size(); tapeI++) {
371  JacobianWithLoopsRowInfo& rowInfo = eqs[tapeI];
372 
373  prepareSparseJacobianRowWithLoops(handler, lModel,
374  tapeI, rowInfo,
375  dyiDxtape, dzDx,
376  CGBase(1),
377  *iterationIndexOp, ifElses,
378  jacLE, indexedLoopResults, allLocations);
379  }
380 
381  indexedLoopResults.resize(jacLE);
382 
386  size_t assignOrAdd = 1;
387  LoopEndOperationNode<Base>* loopEnd = createLoopEnd(handler, *loopStart, indexedLoopResults, indexesOps, assignOrAdd);
388 
389  for (size_t e : allLocations) {
390  // an additional alias variable is required so that each dependent variable can have its own ID
391  jac[e] = handler.createCG(*handler.makeNode(CGOpCode::DependentRefRhs,{e}, {*loopEnd}));
392  }
393 
397  moveNonIndexedOutsideLoop(handler, *loopStart, *loopEnd);
398  }
399 
400  return jac;
401 }
402 
403 template<class Base>
405  LoopModel<Base>& lModel,
406  size_t tapeI,
407  const loops::JacobianWithLoopsRowInfo& rowInfo,
408  const std::vector<std::map<size_t, CGBase> >& dyiDxtape,
409  const std::vector<std::map<size_t, CGBase> >& dzDx,
410  const CGBase& py,
411  IndexOperationNode<Base>& iterationIndexOp,
412  std::vector<loops::IfElseInfo<Base> >& ifElses,
413  size_t& jacLE,
414  std::vector<std::pair<CG<Base>, IndexPattern*> >& indexedLoopResults,
415  std::set<size_t>& allLocations) {
416  using namespace std;
417  using namespace loops;
418 
422  // tape J index -> {locationIt0, locationIt1, ...}
423  for (const auto& itJ2Pos : rowInfo.indexedPositions) {
424  size_t tapeJ = itJ2Pos.first;
425  const std::vector<size_t>& positions = itJ2Pos.second;
426 
427  CGBase jacVal = dyiDxtape[tapeI].at(tapeJ) * py;
428 
429  indexedLoopResults[jacLE++] = createJacobianElement(handler, positions,
430  jacVal, iterationIndexOp, ifElses,
431  allLocations);
432  }
433 
437  // original J index -> {locationIt0, locationIt1, ...}
438  for (const auto& itJ2Pos : rowInfo.nonIndexedPositions) {
439  size_t j = itJ2Pos.first;
440  const std::vector<size_t>& positions = itJ2Pos.second;
441 
442  CGBase jacVal = Base(0);
443 
444  // non-indexed variables used directly
445  const LoopPosition* pos = lModel.getNonIndexedIndepIndexes(j);
446  if (pos != nullptr) {
447  size_t tapeJ = pos->tape;
448  const auto itVal = dyiDxtape[tapeI].find(tapeJ);
449  if (itVal != dyiDxtape[tapeI].end()) {
450  jacVal += itVal->second;
451  }
452  }
453 
454  // non-indexed variables used through temporary variables
455  const auto itks = rowInfo.tmpEvals.find(j);
456  if (itks != rowInfo.tmpEvals.end()) {
457  const std::set<size_t>& ks = itks->second;
458  for (size_t k : ks) {
459  size_t tapeJ = lModel.getTempIndepIndexes(k)->tape;
460 
461  jacVal += dyiDxtape[tapeI].at(tapeJ) * dzDx[k].at(j);
462  }
463  }
464 
465  jacVal *= py;
466 
467  indexedLoopResults[jacLE++] = createJacobianElement(handler, positions,
468  jacVal, iterationIndexOp, ifElses,
469  allLocations);
470  }
471 }
472 
473 
474 namespace loops {
475 
476 template<class Base>
477 std::pair<CG<Base>, IndexPattern*> createJacobianElement(CodeHandler<Base>& handler,
478  const std::vector<size_t>& positions,
479  const CG<Base>& dfdx,
480  IndexOperationNode<Base>& iterationIndexOp,
481  std::vector<IfElseInfo<Base> >& ifElses,
482  std::set<size_t>& allLocations) {
483  using namespace std;
484 
485  size_t nIter = positions.size();
486 
487  map<size_t, size_t> locations;
488  for (size_t iter = 0; iter < nIter; iter++) {
489  if (positions[iter] != (std::numeric_limits<size_t>::max)()) {
490  locations[iter] = positions[iter];
491  allLocations.insert(positions[iter]);
492  }
493  }
494 
495  IndexPattern* pattern;
496  if (locations.size() == nIter) {
497  // present in all iterations
498 
499  // generate the index pattern for the Jacobian compressed element
500  pattern = IndexPattern::detect(positions);
501  handler.manageLoopDependentIndexPattern(pattern);
502  } else {
509  // generate the index pattern for the Jacobian compressed element
510  pattern = IndexPattern::detect(locations);
511  handler.manageLoopDependentIndexPattern(pattern);
512  }
513 
514 
515  return createLoopResult(handler, locations, nIter,
516  dfdx, pattern, 1,
517  iterationIndexOp, ifElses);
518 
519 }
520 
521 } // END loops namespace
522 
523 } // END cg namespace
524 } // END CppAD namespace
525 
526 #endif
const std::set< size_t > & getIndexedTapeIndexes(size_t iteration, size_t origJ) const
Definition: loop_model.hpp:374
void analyseSparseJacobianWithLoops(const std::vector< size_t > &rows, const std::vector< size_t > &cols, const std::vector< size_t > &location, SparsitySetType &noLoopEvalSparsity, std::vector< std::map< size_t, std::set< size_t > > > &noLoopEvalLocations, std::map< LoopModel< Base > *, SparsitySetType > &loopsEvalSparsities, std::map< LoopModel< Base > *, std::vector< loops::JacobianWithLoopsRowInfo > > &loopEqInfo)
void prepareSparseJacobianRowWithLoops(CodeHandler< Base > &handler, LoopModel< Base > &lModel, size_t tapeI, const loops::JacobianWithLoopsRowInfo &rowInfo, const std::vector< std::map< size_t, CGBase > > &dyiDxtape, const std::vector< std::map< size_t, CGBase > > &dzDx, const CGBase &py, IndexOperationNode< Base > &iterationIndexOp, std::vector< loops::IfElseInfo< Base > > &ifElses, size_t &jacLE, std::vector< std::pair< CG< Base >, IndexPattern *> > &indexedLoopResults, std::set< size_t > &allLocations)
const std::vector< LoopPosition > & getTemporaryIndependents() const
Definition: loop_model.hpp:324
STL namespace.
ADFun< CGB > & getTape() const
Definition: loop_model.hpp:263
static IndexPattern * detect(const VectorSizeT &x2y)
const LoopPosition * getTempIndepIndexes(size_t k) const
Definition: loop_model.hpp:357
const std::vector< LoopPosition > & getNonIndexedIndepIndexes() const
Definition: loop_model.hpp:316
const size_t getIterationCount() const
Definition: loop_model.hpp:254
void setZeroDependents(bool zeroDependents)
const std::vector< std::vector< LoopPosition > > & getIndexedIndepIndexes() const
Definition: loop_model.hpp:309
virtual std::vector< CGBase > prepareSparseJacobianWithLoops(CodeHandler< Base > &handler, const std::vector< CGBase > &x, bool forward)