CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
model_c_source_gen_loops_for1.hpp
1 #ifndef CPPAD_CG_MODEL_C_SOURCE_GEN_LOOPS_FOR1_INCLUDED
2 #define CPPAD_CG_MODEL_C_SOURCE_GEN_LOOPS_FOR1_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 /***************************************************************************
22  * Methods related with loop insertion into the operation graph
23  **************************************************************************/
24 
25 template<class Base>
26 void ModelCSourceGen<Base>::prepareSparseForwardOneWithLoops(const std::map<size_t, std::vector<size_t> >& elements) {
27  using namespace std;
28  using namespace CppAD::cg::loops;
29  //printSparsityPattern(_jacSparsity.rows, _jacSparsity.cols, "jacobian", _fun.Range());
30 
31  size_t n = _fun.Domain();
32 
33  CodeHandler<Base> handler;
34  handler.setJobTimer(_jobTimer);
35  handler.setZeroDependents(false);
36 
37  auto& indexJcolDcl = *handler.makeIndexDclrNode("jcol");
38  auto& indexLocalItDcl = *handler.makeIndexDclrNode("it");
39  auto& indexLocalItCountDcl = *handler.makeIndexDclrNode("itCount");
40  auto& indexIterationDcl = *handler.makeIndexDclrNode(LoopModel<Base>::ITERATION_INDEX_NAME);
41  auto& iterationIndexOp = *handler.makeIndexNode(indexIterationDcl);
42  auto& jcolIndexOp = *handler.makeIndexNode(indexJcolDcl);
43 
44  std::vector<OperationNode<Base>*> localNodes(6);
45  localNodes[0] = &indexJcolDcl;
46  localNodes[1] = &indexLocalItDcl;
47  localNodes[2] = &indexLocalItCountDcl;
48  localNodes[3] = &indexIterationDcl;
49  localNodes[4] = &iterationIndexOp;
50  localNodes[5] = &jcolIndexOp;
51 
52  size_t nonIndexdedEqSize = _funNoLoops != nullptr ? _funNoLoops->getOrigDependentIndexes().size() : 0;
53 
54  std::vector<set<size_t> > noLoopEvalSparsity;
55  std::vector<map<size_t, set<size_t> > > noLoopEvalLocations; // tape equation -> original J -> locations
56  map<LoopModel<Base>*, std::vector<set<size_t> > > loopsEvalSparsities;
57  map<LoopModel<Base>*, std::vector<JacobianWithLoopsRowInfo> > loopEqInfo;
58 
59  size_t nnz = _jacSparsity.rows.size();
60  std::vector<size_t> rows(nnz);
61  std::vector<size_t> cols(nnz);
62  std::vector<size_t> locations(nnz);
63 
64  size_t p = 0;
65  for (const auto& itJ : elements) {//loop variables
66  size_t j = itJ.first;
67  const std::vector<size_t>& r = itJ.second;
68 
69  for (size_t e = 0; e < r.size(); e++) { // loop equations
70  rows[p] = r[e];
71  cols[p] = j;
72  locations[p] = e;
73  p++;
74  }
75  }
76  CPPADCG_ASSERT_UNKNOWN(p == nnz);
77 
78  analyseSparseJacobianWithLoops(rows, cols, locations,
79  noLoopEvalSparsity, noLoopEvalLocations, loopsEvalSparsities, loopEqInfo);
80 
81  std::vector<CGBase> x(n);
82  handler.makeVariables(x);
83  if (_x.size() > 0) {
84  for (size_t i = 0; i < n; i++) {
85  x[i].setValue(_x[i]);
86  }
87  }
88 
89  CGBase dx;
90  handler.makeVariable(dx);
91  if (_x.size() > 0) {
92  dx.setValue(Base(1.0));
93  }
94 
95  /***********************************************************************
96  * generate the operation graph
97  **********************************************************************/
98 
102  // temporaries (zero orders)
103  std::vector<CGBase> tmps;
104 
105  // Jacobian for temporaries
106  map<size_t, map<size_t, CGBase> > dzDx;
107 
108  /*******************************************************************
109  * equations NOT in loops
110  ******************************************************************/
111  if (_funNoLoops != nullptr) {
112  ADFun<CGBase>& fun = _funNoLoops->getTape();
113 
117  std::vector<CGBase> depNL = _funNoLoops->getTape().Forward(0, x);
118 
119  tmps.resize(depNL.size() - nonIndexdedEqSize);
120  for (size_t i = 0; i < tmps.size(); i++)
121  tmps[i] = depNL[nonIndexdedEqSize + i];
122 
126  bool hasAtomics = isAtomicsUsed(); // TODO: improve this by checking only the current fun
127  map<size_t, map<size_t, CGBase> > dydxT = generateLoopFor1Jac(fun,
128  _funNoLoops->getJacobianSparsity(),
129  noLoopEvalSparsity,
130  x, hasAtomics);
131 
132  map<size_t, std::vector<CGBase> > jacNl; // by column
133  for (const auto& itDydxT : dydxT) {
134  size_t j = itDydxT.first;
135  const map<size_t, CGBase>& dydxjT = itDydxT.second;
136 
137  // prepare space for the Jacobian of the original equations
138  std::vector<CGBase>& col = jacNl[j];
139  col.resize(elements.at(j).size());
140 
141  for (const auto& itiv : dydxjT) {
142  size_t inl = itiv.first;
143 
144  if (inl < nonIndexdedEqSize) {
145  // (dy_i/dx_v) elements from equations outside loops
146  const set<size_t>& locations = noLoopEvalLocations[inl][j];
147 
148  CPPADCG_ASSERT_UNKNOWN(locations.size() == 1); // one Jacobian element should not be placed in several locations
149  size_t e = *locations.begin();
150 
151  col[e] = itiv.second * dx;
152 
153  _nonLoopFor1Elements[j].insert(e);
154  } else {
155  // dz_k/dx_v (for temporary variable)
156  size_t k = inl - nonIndexdedEqSize;
157  dzDx[k][j] = itiv.second;
158  }
159 
160  }
161  }
162 
166  typename map<size_t, std::vector<CGBase> >::iterator itJ;
167  for (itJ = jacNl.begin(); itJ != jacNl.end(); ++itJ) {
168  size_t j = itJ->first;
169  if (_nonLoopFor1Elements.find(j) != _nonLoopFor1Elements.end()) // make sure there are elements
170  createForwardOneWithLoopsNL(handler, j, itJ->second);
171  }
172  }
173 
174  /***********************************************************************
175  * equations in loops
176  **********************************************************************/
177  typename map<LoopModel<Base>*, std::vector<JacobianWithLoopsRowInfo> >::iterator itl2Eq;
178  for (itl2Eq = loopEqInfo.begin(); itl2Eq != loopEqInfo.end(); ++itl2Eq) {
179  LoopModel<Base>& lModel = *itl2Eq->first;
180  const std::vector<JacobianWithLoopsRowInfo>& info = itl2Eq->second;
181  ADFun<CGBase>& fun = lModel.getTape();
182 
183  //size_t nIndexed = lModel.getIndexedIndepIndexes().size();
184  //size_t nNonIndexed = lModel.getNonIndexedIndepIndexes().size();
185 
186  _cache.str("");
187  _cache << "model (forward one, loop " << lModel.getLoopId() << ")";
188  std::string jobName = _cache.str();
189 
193  startingJob("'" + jobName + "'", JobTimer::GRAPH);
194 
195  std::vector<CGBase> indexedIndeps = createIndexedIndependents(handler, lModel, iterationIndexOp);
196  std::vector<CGBase> xl = createLoopIndependentVector(handler, lModel, indexedIndeps, x, tmps);
197 
198  bool hasAtomics = isAtomicsUsed(); // TODO: improve this by checking only the current fun
199  map<size_t, map<size_t, CGBase> > dyiDxtapeT = generateLoopFor1Jac(fun,
200  lModel.getJacobianSparsity(),
201  loopsEvalSparsities[&lModel],
202  xl, hasAtomics);
203 
204  finishedJob();
205 
206  /*******************************************************************
207  * create Jacobian column groups
208  * for the contributions of the equations in loops
209  ******************************************************************/
211 
212  generateForOneColumnGroups(lModel, info, nnz, n, loopGroups);
213 
214  /*******************************************************************
215  * generate the operation graph for each Jacobian column subgroup
216  ******************************************************************/
217  for (size_t g = 0; g < loopGroups.size(); g++) {
218  JacobianColGroup<Base>& group = *loopGroups[g];
219 
223  LoopStartOperationNode<Base>* loopStart = nullptr;
224 
225  map<size_t, set<size_t> > localIterCount2Jcols;
226 
227  for (const auto& itJcol2It : group.jCol2Iterations) {
228  size_t jcol = itJcol2It.first;
229  size_t itCount = itJcol2It.second.size();
230  localIterCount2Jcols[itCount].insert(jcol);
231  }
232 
233  bool createsLoop = localIterCount2Jcols.size() != 1 || // is there a different number of iterations
234  localIterCount2Jcols.begin()->first != 1; // is there always just one iteration?
235 
242  map<size_t, map<size_t, size_t> > jcol2localIt2ModelIt;
243 
244  for (const auto& itJcol2It : group.jCol2Iterations) {
245  size_t jcol = itJcol2It.first;
246 
247  map<size_t, size_t>& localIt2ModelIt = jcol2localIt2ModelIt[jcol];
248  size_t localIt = 0;
249  set<size_t>::const_iterator itIt;
250  for (itIt = itJcol2It.second.begin(); itIt != itJcol2It.second.end(); ++itIt, localIt++) {
251  localIt2ModelIt[localIt] = *itIt;
252  }
253  }
254 
259  std::unique_ptr<IndexPattern> itPattern(Plane2DIndexPattern::detectPlane2D(jcol2localIt2ModelIt));
260 
261  if (itPattern.get() == nullptr) {
262  // did not match!
263  itPattern.reset(new Random2DIndexPattern(jcol2localIt2ModelIt));
264  }
265 
269  IndexOperationNode<Base>* localIterIndexOp = nullptr;
270  IndexOperationNode<Base>* localIterCountIndexOp = nullptr;
271  IndexAssignOperationNode<Base>* itCountAssignOp = nullptr;
272  std::unique_ptr<IndexPattern> indexLocalItCountPattern;
273 
274  if (createsLoop) {
275  map<size_t, size_t> jcol2litCount;
276 
277  for (const auto& itJcol2Its : group.jCol2Iterations) {
278  size_t jcol = itJcol2Its.first;
279  jcol2litCount[jcol] = itJcol2Its.second.size();
280  }
281 
282  indexLocalItCountPattern.reset(IndexPattern::detect(jcol2litCount));
283 
284  if (IndexPattern::isConstant(*indexLocalItCountPattern.get())) {
285  size_t itCount = group.jCol2Iterations.begin()->second.size();
286  loopStart = handler.makeLoopStartNode(indexLocalItDcl, itCount);
287  } else {
288  itCountAssignOp = handler.makeIndexAssignNode(indexLocalItCountDcl, *indexLocalItCountPattern.get(), jcolIndexOp);
289  localIterCountIndexOp = handler.makeIndexNode(*itCountAssignOp);
290  loopStart = handler.makeLoopStartNode(indexLocalItDcl, *localIterCountIndexOp);
291  }
292 
293  localIterIndexOp = handler.makeIndexNode(*loopStart);
294  }
295 
296 
297  auto* iterationIndexPatternOp = handler.makeIndexAssignNode(indexIterationDcl, *itPattern.get(), &jcolIndexOp, localIterIndexOp);
298  iterationIndexOp.makeAssigmentDependent(*iterationIndexPatternOp);
299 
300  map<size_t, set<size_t> > jcol2CompressedLoc;
301  std::vector<pair<CG<Base>, IndexPattern*> > indexedLoopResults;
302 
303  indexedLoopResults = generateForwardOneGroupOps(handler, lModel, info,
304  group, iterationIndexOp,
305  dx, dyiDxtapeT, dzDx,
306  jcol2CompressedLoc);
307 
308  _loopFor1Groups[&lModel][g] = jcol2CompressedLoc;
309 
310  LoopEndOperationNode<Base>* loopEnd = nullptr;
311  std::vector<CGBase> pxCustom;
312  if (createsLoop) {
316  size_t assignOrAdd = 1;
317  set<IndexOperationNode<Base>*> indexesOps;
318  indexesOps.insert(&iterationIndexOp);
319  loopEnd = createLoopEnd(handler, *loopStart, indexedLoopResults, indexesOps, assignOrAdd);
320 
324  moveNonIndexedOutsideLoop(handler, *loopStart, *loopEnd);
325 
329  pxCustom.resize(1);
330  // {0} : must point to itself since there is only one dependent
331  pxCustom[0] = handler.createCG(*handler.makeNode(CGOpCode::DependentRefRhs,{0}, {*loopEnd}));
332 
333  } else {
337  pxCustom.resize(indexedLoopResults.size());
338  for (size_t i = 0; i < indexedLoopResults.size(); i++) {
339  const CGBase& val = indexedLoopResults[i].first;
340  IndexPattern* ip = indexedLoopResults[i].second;
341 
342  pxCustom[i] = createLoopDependentFunctionResult(handler, i, val, ip, iterationIndexOp);
343  }
344 
345  }
346 
347  LanguageC<Base> langC(_baseTypeName);
348  langC.setFunctionIndexArgument(indexJcolDcl);
349  langC.setParameterPrecision(_parameterPrecision);
350 
351  _cache.str("");
352  std::ostringstream code;
353  std::unique_ptr<VariableNameGenerator<Base> > nameGen(createVariableNameGenerator("dy"));
354  LangCDefaultHessianVarNameGenerator<Base> nameGenHess(nameGen.get(), "dx", n);
355 
359  _cache.str("");
360  _cache << "model (forward one, loop " << lModel.getLoopId() << ", group " << g << ")";
361  string jobName = _cache.str();
362  handler.generateCode(code, langC, pxCustom, nameGenHess, _atomicFunctions, jobName);
363 
364  _cache.str("");
365  generateFunctionNameLoopFor1(_cache, lModel, g);
366  std::string functionName = _cache.str();
367 
368  std::string argsDcl = langC.generateFunctionArgumentsDcl();
369 
370  _cache.str("");
371  _cache << "#include <stdlib.h>\n"
372  "#include <math.h>\n"
373  "\n"
375  "\n"
376  "void " << functionName << "(" << argsDcl << ") {\n";
377  nameGenHess.customFunctionVariableDeclarations(_cache);
378  _cache << langC.generateIndependentVariableDeclaration() << "\n";
379  _cache << langC.generateDependentVariableDeclaration() << "\n";
380  _cache << langC.generateTemporaryVariableDeclaration(false, false,
382  handler.getExternalFuncMaxReverseOrder()) << "\n";
383  nameGenHess.prepareCustomFunctionVariables(_cache);
384 
385  // code inside the loop
386  _cache << code.str();
387 
388  nameGenHess.finalizeCustomFunctionVariables(_cache);
389  _cache << "}\n\n";
390 
391  _sources[functionName + ".c"] = _cache.str();
392  _cache.str("");
393 
397  if (g + 1 < loopGroups.size()) {
398  handler.resetNodes(); // uncolor nodes
399  }
400  }
401 
402  }
403 
407  string functionFor1 = _name + "_" + FUNCTION_SPARSE_FORWARD_ONE;
408  _sources[functionFor1 + ".c"] = generateGlobalForRevWithLoopsFunctionSource(elements,
409  _loopFor1Groups, _nonLoopFor1Elements,
410  functionFor1, _name, _baseTypeName, "indep",
411  generateFunctionNameLoopFor1);
415  _cache.str("");
416  generateSparsity1DSource2(_name + "_" + FUNCTION_FORWARD_ONE_SPARSITY, elements);
417  _sources[_name + "_" + FUNCTION_FORWARD_ONE_SPARSITY + ".c"] = _cache.str();
418  _cache.str("");
419 }
420 
421 template<class Base>
423  size_t j,
424  std::vector<CG<Base> >& jacCol) {
425  size_t n = _fun.Domain();
426 
427  _cache.str("");
428  _cache << "model (forward one, indep " << j << ") no loop";
429  const std::string jobName = _cache.str();
430 
431  LanguageC<Base> langC(_baseTypeName);
432  langC.setMaxAssignmentsPerFunction(_maxAssignPerFunc, &_sources);
433  langC.setParameterPrecision(_parameterPrecision);
434  _cache.str("");
435  _cache << _name << "_" << FUNCTION_SPARSE_FORWARD_ONE << "_noloop_indep" << j;
436  langC.setGenerateFunction(_cache.str());
437 
438  std::ostringstream code;
439  std::unique_ptr<VariableNameGenerator<Base> > nameGen(createVariableNameGenerator("dy"));
440  LangCDefaultHessianVarNameGenerator<Base> nameGenHess(nameGen.get(), "dx", n);
441 
442  handler.generateCode(code, langC, jacCol, nameGenHess, _atomicFunctions, jobName);
443 
444  handler.resetNodes();
445 }
446 
447 
448 namespace loops {
449 
454  size_t jcol;
455  std::set<size_t> iterations;
456 
457  inline Forward1Jcol2Iter() {
458  }
459 
460  inline Forward1Jcol2Iter(size_t col,
461  const std::set<size_t>& iters) :
462  jcol(col),
463  iterations(iters) {
464  }
465 };
466 
467 inline bool operator<(const Forward1Jcol2Iter& l, const Forward1Jcol2Iter& r) {
468  if (l.jcol < r.jcol)
469  return true;
470  else if (l.jcol > r.jcol)
471  return false;
472 
473  return compare(l.iterations, r.iterations) == -1;
474 }
475 
479 template<class Base>
480 class JacobianTermContrib {
481 public:
482  std::set<size_t> indexed;
483  std::set<size_t> nonIndexed; // maximum one element
484 public:
485 
486  inline bool empty() const {
487  return indexed.empty() && nonIndexed.empty();
488  }
489 
490  inline size_t size() const {
491  return indexed.size() + nonIndexed.size();
492  }
493 };
494 
495 template<class Base>
496 bool operator<(const JacobianTermContrib<Base>& l, const JacobianTermContrib<Base>& r) {
497  int c = compare(l.indexed, r.indexed);
498  if (c != 0) return c == -1;
499  c = compare(l.nonIndexed, r.nonIndexed);
500  if (c != 0) return c == -1;
501  return false;
502 }
503 
508 template<class Base>
509 class JacobianColGroup : public JacobianTermContrib<Base> {
510 public:
511  // all the required iterations for each jcol
512  std::map<size_t, std::set<size_t> > jCol2Iterations;
513  // all iterations
514  std::set<size_t> iterations;
515  // if-else branches
516  std::vector<IfElseInfo<Base> > ifElses;
517 public:
518 
520  const Forward1Jcol2Iter& jcol2Iters) :
522  iterations(jcol2Iters.iterations) {
523  jCol2Iterations[jcol2Iters.jcol] = jcol2Iters.iterations;
524  }
525 };
526 
527 template<class Base>
528 void generateForOneColumnGroups(const LoopModel<Base>& lModel,
529  const std::vector<JacobianWithLoopsRowInfo>& loopEqInfo,
530  size_t max,
531  size_t n,
533  using namespace std;
534  using namespace CppAD::cg::loops;
535 
539  map<size_t, map<size_t, set<size_t> > > indexed2jcol2Iter;
540  map<size_t, set<size_t> > nonIndexed2Iter;
541 
542  map<JacobianTermContrib<Base>, set<size_t> > contrib2jcols = groupForOneByContrib(lModel, loopEqInfo,
543  n,
544  indexed2jcol2Iter,
545  nonIndexed2Iter);
546 
547  loopGroups.reserve(contrib2jcols.size() * 2); // TODO: improve this
548  std::map<JacobianTermContrib<Base>, JacobianColGroup<Base>*> c2subgroups;
549 
550  for (const auto& itC : contrib2jcols) {
551  const JacobianTermContrib<Base>& c = itC.first;
552  const set<size_t>& jcols = itC.second;
553 
557  subgroupForOneByContrib(loopEqInfo, c, jcols,
558  indexed2jcol2Iter, nonIndexed2Iter,
559  loopGroups, c2subgroups);
560  }
561 
562 }
563 
570 template<class Base>
571 std::map<JacobianTermContrib<Base>, std::set<size_t> > groupForOneByContrib(const LoopModel<Base>& lModel,
572  const std::vector<JacobianWithLoopsRowInfo>& loopEqInfo,
573  size_t n,
574  std::map<size_t, std::map<size_t, std::set<size_t> > >& indexed2jcol2Iter,
575  std::map<size_t, std::set<size_t> >& nonIndexed2Iter) {
576 
577  using namespace std;
578 
582  std::vector<JacobianTermContrib<Base> > jcols(n);
583 
584  size_t nIterations = lModel.getIterationCount();
585 
586  const std::vector<std::vector<LoopPosition> >& indexedIndepIndexes = lModel.getIndexedIndepIndexes();
587 
588  for (size_t i = 0; i < loopEqInfo.size(); i++) {
589  const JacobianWithLoopsRowInfo& row = loopEqInfo[i];
590 
591  // indexed
592  for (const auto& it : row.indexedPositions) {
593  size_t tapeJ = it.first;
594  const std::vector<size_t>& positions = it.second;
595  map<size_t, set<size_t> >& jcol2Iter = indexed2jcol2Iter[tapeJ];
596 
597  for (size_t iter = 0; iter < nIterations; iter++) {
598  // it is present in all iterations but the user might request fewer elements in the Jacobian
599  // (it may be because the equation might not exist for this iteration)
600  if (positions[iter] != (std::numeric_limits<size_t>::max)()) {
601  size_t j = indexedIndepIndexes[tapeJ][iter].original;
602  jcols[j].indexed.insert(tapeJ);
603  jcol2Iter[j].insert(iter);
604  }
605  }
606  }
607 
608  // non-indexed
609  for (const auto& it : row.nonIndexedPositions) {
610  size_t j = it.first;
611  const std::vector<size_t>& positions = it.second;
612  set<size_t>& jcol2Iter = nonIndexed2Iter[j];
613  bool used = false;
614 
615  for (size_t iter = 0; iter < nIterations; iter++) {
616  // it is present in all iterations but the user might request fewer elements in the Jacobian
617  // (it may be because the equation might not exist for this iteration)
618  if (positions[iter] != (std::numeric_limits<size_t>::max)()) {
619  used = true;
620  jcol2Iter.insert(iter);
621  }
622  }
623  if (used) {
624  jcols[j].nonIndexed.insert(j);
625  }
626  }
627 
628  }
629 
633  map<JacobianTermContrib<Base>, set<size_t> > contrib2jcols;
634  for (size_t j = 0; j < n; j++) {
635  if (!jcols[j].empty())
636  contrib2jcols[jcols[j]].insert(j);
637  }
638 
639  return contrib2jcols;
640 }
641 
648 template<class Base>
649 inline void subgroupForOneByContrib(const std::vector<JacobianWithLoopsRowInfo>& loopEqInfo,
650  const JacobianTermContrib<Base>& c,
651  const std::set<size_t>& jcols,
652  const std::map<size_t, std::map<size_t, std::set<size_t> > >& indexed2jcol2Iter,
653  const std::map<size_t, std::set<size_t> >& nonIndexed2Iter,
655  std::map<JacobianTermContrib<Base>, JacobianColGroup<Base>*>& c2subgroups) {
656  using namespace std;
657 
658  map<Forward1Jcol2Iter, JacobianTermContrib<Base> > contribs;
659 
660  map<size_t, set<size_t> >::const_iterator itJcol2Iter;
661 
662  // indexed
663  for (size_t tapeJ : c.indexed) {
664  map<size_t, set<size_t> > jcol2Iters = filterBykeys(indexed2jcol2Iter.at(tapeJ), jcols);
665  for (itJcol2Iter = jcol2Iters.begin(); itJcol2Iter != jcol2Iters.end(); ++itJcol2Iter) {
666  Forward1Jcol2Iter k(itJcol2Iter->first, itJcol2Iter->second);
667  contribs[k].indexed.insert(tapeJ);
668  }
669  }
670 
671  // non-indexed
672  for (size_t j : c.nonIndexed) {
673  // probably present in all iterations but the user might request fewer elements in the Jacobian
674  // (it may be because the equation might not exist for this iteration)
675  const set<size_t>& iters = nonIndexed2Iter.at(j);
676  Forward1Jcol2Iter k(j, iters);
677  contribs[k].nonIndexed.insert(j);
678  }
679 
683  for (const auto& itK2C : contribs) {
684  const Forward1Jcol2Iter& jcol2Iters = itK2C.first;
685  const JacobianTermContrib<Base>& hc = itK2C.second;
686 
687  typename map<JacobianTermContrib<Base>, JacobianColGroup<Base>*>::const_iterator its = c2subgroups.find(hc);
688  if (its != c2subgroups.end()) {
689  JacobianColGroup<Base>* sg = its->second;
690  sg->jCol2Iterations[jcol2Iters.jcol] = jcol2Iters.iterations;
691  sg->iterations.insert(jcol2Iters.iterations.begin(), jcol2Iters.iterations.end());
692  } else {
693  JacobianColGroup<Base>* sg = new JacobianColGroup<Base>(hc, jcol2Iters);
694  subGroups.push_back(sg);
695  c2subgroups[hc] = sg;
696  }
697  }
698 }
699 
700 template<class Base>
701 std::vector<std::pair<CG<Base>, IndexPattern*> > generateForwardOneGroupOps(CodeHandler<Base>& handler,
702  const LoopModel<Base>& lModel,
703  const std::vector<JacobianWithLoopsRowInfo>& info,
704  JacobianColGroup<Base>& group,
705  IndexOperationNode<Base>& iterationIndexOp,
706  const CG<Base>& dx,
707  const std::map<size_t, std::map<size_t, CG<Base> > >& dyiDxtapeT,
708  const std::map<size_t, std::map<size_t, CG<Base> > >& dzDx,
709  std::map<size_t, std::set<size_t> >& jcol2CompressedLoc) {
710  using namespace std;
711  using namespace CppAD::cg::loops;
712 
713  using CGBase = CG<Base>;
714 
715  const std::vector<std::vector<LoopPosition> >& indexedIndepIndexes = lModel.getIndexedIndepIndexes();
716 
717  size_t jacElSize = group.size();
718 
719  std::vector<pair<CGBase, IndexPattern*> > indexedLoopResults(jacElSize * info.size());
720  size_t jacLE = 0;
721 
722  map<size_t, size_t> iter2jcols;
723 
724  for (size_t tapeI = 0; tapeI < info.size(); tapeI++) {
725  const JacobianWithLoopsRowInfo& jlrw = info[tapeI];
726 
730  // tape J index -> {locationIt0, locationIt1, ...}
731  for (size_t tapeJ : group.indexed) {
732 
733  map<size_t, std::vector<size_t> >::const_iterator itPos = jlrw.indexedPositions.find(tapeJ);
734  if (itPos != jlrw.indexedPositions.end()) {
735  const std::vector<size_t>& positions = itPos->second; // compressed positions
736 
737  const std::vector<LoopPosition>& tapeJPos = indexedIndepIndexes[tapeJ];
738  iter2jcols.clear();
739  for (size_t iter : group.iterations) {
740  if (positions[iter] != (std::numeric_limits<size_t>::max)()) { // the element must have been requested
741  CPPADCG_ASSERT_UNKNOWN(tapeJPos[iter].original != (std::numeric_limits<size_t>::max)()); // the equation must exist for this iteration
742  iter2jcols[iter] = tapeJPos[iter].original;
743  }
744  }
745 
746  if (!iter2jcols.empty()) {
747  CGBase val = dyiDxtapeT.at(tapeJ).at(tapeI) * dx;
748  indexedLoopResults[jacLE++] = createForwardOneElement(handler, group, positions, iter2jcols,
749  val, iterationIndexOp, jcol2CompressedLoc);
750  }
751  }
752  }
753 
754 
758  // original J index -> {locationIt0, locationIt1, ...}
759  for (size_t j : group.nonIndexed) {
760 
761  map<size_t, std::vector<size_t> >::const_iterator itPos = jlrw.nonIndexedPositions.find(j);
762  if (itPos == jlrw.nonIndexedPositions.end()) {
763  continue;
764  }
765  const std::vector<size_t>& positions = itPos->second;
766 
767  iter2jcols.clear();
768  for (size_t iter : group.iterations) {
769  if (positions[iter] != (std::numeric_limits<size_t>::max)()) {// the element must have been requested
770  CPPADCG_ASSERT_UNKNOWN(lModel.getDependentIndexes()[tapeI][iter].original != (std::numeric_limits<size_t>::max)()); // the equation must exist for this iteration
771  iter2jcols[iter] = j;
772  }
773  }
774  if (!iter2jcols.empty()) {
775 
776  CGBase jacVal = Base(0);
777 
778  // non-indexed variables used directly
779  const LoopPosition* pos = lModel.getNonIndexedIndepIndexes(j);
780  if (pos != nullptr) {
781  size_t tapeJ = pos->tape;
782 
783  const map<size_t, CG<Base> >& dyiDxJtapeT = dyiDxtapeT.at(tapeJ);
784  typename map<size_t, CGBase>::const_iterator itVal = dyiDxJtapeT.find(tapeI);
785  if (itVal != dyiDxJtapeT.end()) {
786  jacVal += itVal->second;
787  }
788  }
789 
790  // non-indexed variables used through temporary variables
791  map<size_t, set<size_t> >::const_iterator itks = jlrw.tmpEvals.find(j);
792  if (itks != jlrw.tmpEvals.end()) {
793  const set<size_t>& ks = itks->second;
794  for (size_t k : ks) {
795  size_t tapeJ = lModel.getTempIndepIndexes(k)->tape;
796 
797  jacVal += dyiDxtapeT.at(tapeJ).at(tapeI) * dzDx.at(k).at(j);
798  }
799  }
800 
801  CGBase val = jacVal * dx;
802  indexedLoopResults[jacLE++] = createForwardOneElement(handler, group, positions, iter2jcols,
803  val, iterationIndexOp, jcol2CompressedLoc);
804  }
805  }
806  }
807 
808  indexedLoopResults.resize(jacLE);
809 
810  return indexedLoopResults;
811 }
812 
813 template<class Base>
814 std::pair<CG<Base>, IndexPattern*> createForwardOneElement(CodeHandler<Base>& handler,
815  JacobianColGroup<Base>& group,
816  const std::vector<size_t>& positions,
817  const std::map<size_t, size_t>& iter2jcols,
818  const CG<Base>& dfdx,
819  IndexOperationNode<Base>& iterationIndexOp,
820  std::map<size_t, std::set<size_t> >& jcol2CompressedLoc) {
821  using namespace std;
822 
826  map<size_t, size_t> locationsIter2Pos;
827 
828  for (const auto& itIt : iter2jcols) {
829  size_t iter = itIt.first;
830  size_t jcol = itIt.second;
831  CPPADCG_ASSERT_UNKNOWN(positions[iter] != (std::numeric_limits<size_t>::max)());
832  locationsIter2Pos[iter] = positions[iter];
833  jcol2CompressedLoc[jcol].insert(positions[iter]);
834  }
835 
836  // generate the index pattern for the Jacobian compressed element
837  IndexPattern* pattern = IndexPattern::detect(locationsIter2Pos);
838  handler.manageLoopDependentIndexPattern(pattern);
839 
840  size_t assignOrAdd = 1;
841  return createLoopResult(handler, locationsIter2Pos, positions.size(),
842  dfdx, pattern, assignOrAdd,
843  iterationIndexOp, group.ifElses);
844 }
845 
846 } // END loops namespace
847 
848 template<class Base>
849 std::map<size_t, std::map<size_t, CG<Base> > > ModelCSourceGen<Base>::generateLoopFor1Jac(ADFun<CGBase>& fun,
850  const SparsitySetType& sparsity,
851  const SparsitySetType& evalSparsity,
852  const std::vector<CGBase>& x,
853  bool constainsAtomics) {
854  using namespace std;
855 
856  size_t n = fun.Domain();
857 
858  map<size_t, map<size_t, CGBase> > dyDxT;
859 
860  if (!constainsAtomics) {
861  std::vector<size_t> row, col;
862  generateSparsityIndexes(evalSparsity, row, col);
863 
864  if (row.size() == 0)
865  return dyDxT; // nothing to do
866 
867  std::vector<CGBase> jacLoop(row.size());
868 
869  CppAD::sparse_jacobian_work work; // temporary structure for CppAD
870  fun.SparseJacobianForward(x, sparsity, row, col, jacLoop, work);
871 
872  // organize results
873  for (size_t el = 0; el < jacLoop.size(); el++) {
874  size_t i = row[el];
875  size_t j = col[el];
876  dyDxT[j][i] = jacLoop[el];
877  }
878 
879  } else {
880  //transpose
881  std::vector<set<size_t> > evalSparsityT(n);
882  addTransMatrixSparsity(evalSparsity, evalSparsityT);
883 
884  std::vector<CGBase> dx(n);
885 
886  for (size_t j = 0; j < n; j++) {
887  const set<size_t>& column = evalSparsityT[j];
888 
889  if (column.empty())
890  continue;
891 
892  fun.Forward(0, x);
893 
894  dx[j] = Base(1);
895  std::vector<CGBase> dy = fun.Forward(1, dx);
896  CPPADCG_ASSERT_UNKNOWN(dy.size() == fun.Range());
897  dx[j] = Base(0);
898 
899  map<size_t, CGBase>& dyDxJT = dyDxT[j];
900 
901  for (size_t i : column) {
902  dyDxJT[i] = dy[i];
903  }
904  }
905 
906  }
907 
908  return dyDxT;
909 }
910 
911 template<class Base>
912 void ModelCSourceGen<Base>::generateFunctionNameLoopFor1(std::ostringstream& cache,
913  const LoopModel<Base>& loop,
914  size_t g) {
915  generateFunctionNameLoopFor1(cache, _name, loop, g);
916 }
917 
918 template<class Base>
919 void ModelCSourceGen<Base>::generateFunctionNameLoopFor1(std::ostringstream& cache,
920  const std::string& modelName,
921  const LoopModel<Base>& loop,
922  size_t g) {
923  cache << modelName << "_" << FUNCTION_SPARSE_FORWARD_ONE <<
924  "_loop" << loop.getLoopId() << "_g" << g;
925 }
926 
927 } // END cg namespace
928 } // END CppAD namespace
929 
930 #endif
STL namespace.
void setValue(const Base &val)
Definition: variable.hpp:54
static Plane2DIndexPattern * detectPlane2D(const std::map< size_t, std::map< size_t, size_t > > &x2y2z)
size_t getLoopId() const
Definition: loop_model.hpp:236
ADFun< CGB > & getTape() const
Definition: loop_model.hpp:263
const std::vector< int > & getExternalFuncMaxForwardOrder() const
static IndexPattern * detect(const VectorSizeT &x2y)
void makeVariables(VectorCG &variables)
virtual void prepareSparseForwardOneWithLoops(const std::map< size_t, std::vector< size_t > > &elements)
const LoopPosition * getTempIndepIndexes(size_t k) const
Definition: loop_model.hpp:357
virtual void setMaxAssignmentsPerFunction(size_t maxAssignmentsPerFunction, std::map< std::string, std::string > *sources)
Definition: language_c.hpp:252
void makeVariable(AD< CGB > &variable)
virtual void setParameterPrecision(size_t p)
Definition: language_c.hpp:237
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 > > & getDependentIndexes() const
Definition: loop_model.hpp:291
const std::vector< std::vector< LoopPosition > > & getIndexedIndepIndexes() const
Definition: loop_model.hpp:309
const std::vector< int > & getExternalFuncMaxReverseOrder() const
virtual void generateCode(std::ostream &out, Language< Base > &lang, CppAD::vector< CGB > &dependent, VariableNameGenerator< Base > &nameGen, const std::string &jobName="source")