CppADCodeGen  2.3.0
A C++ Algorithmic Differentiation Package with Source Code Generation
model_c_source_gen_loops_hess.hpp
1 #ifndef CPPAD_CG_MODEL_C_SOURCE_GEN_LOOPS_HESS_INCLUDED
2 #define CPPAD_CG_MODEL_C_SOURCE_GEN_LOOPS_HESS_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 
24 public:
25  size_t location; // location in the compressed hessian vector
26  size_t row;
27  unsigned short count; // number of times to be added to that location
28 
29  inline HessianElement() :
30  location(std::numeric_limits<size_t>::max()),
31  row(std::numeric_limits<size_t>::max()),
32  count(0) {
33  }
34 
35 };
36 
37 template<class Base>
38 std::pair<CG<Base>, IndexPattern*> createHessianContribution(CodeHandler<Base>& handler,
39  const std::vector<HessianElement>& positions,
40  const CG<Base>& ddfdxdx,
41  IndexOperationNode<Base>& iterationIndexOp,
42  std::vector<IfElseInfo<Base> >& ifElses);
43 
44 } // END loops namespace
45 
46 /***************************************************************************
47  * Methods related with loop insertion into the operation graph
48  **************************************************************************/
49 
50 template<class Base>
51 void ModelCSourceGen<Base>::analyseSparseHessianWithLoops(const std::vector<size_t>& lowerHessRows,
52  const std::vector<size_t>& lowerHessCols,
53  const std::vector<size_t>& lowerHessOrder,
54  std::vector<std::set<size_t> >& noLoopEvalJacSparsity,
55  std::vector<std::set<size_t> >& noLoopEvalHessSparsity,
56  std::vector<std::map<size_t, std::set<size_t> > >& noLoopEvalHessLocations,
57  std::map<LoopModel<Base>*, loops::HessianWithLoopsInfo<Base> >& loopHessInfo,
58  bool useSymmetry) {
59  using namespace std;
60  using namespace CppAD::cg::loops;
61 
62  size_t nonIndexdedEqSize = _funNoLoops != nullptr ? _funNoLoops->getOrigDependentIndexes().size() : 0;
63 
67  for (LoopModel<Base>* l : _loopTapes) {
68  l->evalJacobianSparsity();
69  l->evalHessianSparsity();
70  }
71 
72  if (_funNoLoops != nullptr) {
73  _funNoLoops->evalJacobianSparsity();
74  _funNoLoops->evalHessianSparsity();
75  }
76 
77  size_t m = _fun.Range();
78  size_t n = _fun.Domain();
79 
80  size_t nnz = lowerHessRows.size();
81 
82  noLoopEvalJacSparsity.resize(_funNoLoops != nullptr ? m : 0);
83  noLoopEvalHessSparsity.resize(_funNoLoops != nullptr ? n : 0);
84  noLoopEvalHessLocations.resize(noLoopEvalHessSparsity.size());
85 
86  loopHessInfo.clear();
87  for (LoopModel<Base>* loop : _loopTapes) {
88  HessianWithLoopsInfo<Base>& loopHessInfol = loopHessInfo[loop];
89  loopHessInfol = HessianWithLoopsInfo<Base>(*loop);
90 
91  // initialize Hessian information structure
92  loopHessInfol.noLoopEvalHessTempsSparsity.resize(_funNoLoops != nullptr ? n : 0);
93  }
94 
95  auto flipIndices = [&](const std::vector<set<size_t> >& groupHess,
96  size_t tape1,
97  size_t tape2) {
98  return useSymmetry && tape1 > tape2 && groupHess[tape2].find(tape1) != groupHess[tape2].end();
99  };
100 
101  auto flipIndices2 = [&](const std::vector<set<size_t> >& groupHess,
102  size_t tape1,
103  size_t tape2) {
104  return useSymmetry && groupHess[tape2].find(tape1) != groupHess[tape2].end();
105  };
106 
112  for (size_t eh = 0; eh < nnz; eh++) {
113  size_t j1 = lowerHessRows[eh];
114  size_t j2 = lowerHessCols[eh];
115  size_t e = lowerHessOrder[eh];
116 
117  if (_funNoLoops != nullptr) {
118  // considers only the pattern for the original equations and leaves out the temporaries
119  const std::vector<std::set<size_t> >& dydxx = _funNoLoops->getHessianOrigEqsSparsity();
120  if (dydxx.size() > 0) {
121  if (dydxx[j1].find(j2) != dydxx[j1].end()) {
125  noLoopEvalHessSparsity[j1].insert(j2);
126  noLoopEvalHessLocations[j1][j2].insert(e);
127  }
128  }
129  }
130 
131  for (LoopModel<Base>* loop : _loopTapes) {
132  size_t nIter = loop->getIterationCount();
133 
134  const std::vector<IterEquationGroup<Base> >& eqGroups = loop->getEquationsGroups();
135  const std::vector<set<size_t> >& loopJac = loop->getJacobianSparsity();
136  HessianWithLoopsInfo<Base>& loopInfo = loopHessInfo.at(loop);
137 
138  const std::vector<std::vector<LoopPosition> >& indexedIndepIndexes = loop->getIndexedIndepIndexes();
139  const std::vector<LoopPosition>& nonIndexedIndepIndexes = loop->getNonIndexedIndepIndexes();
140  const std::vector<LoopPosition>& temporaryIndependents = loop->getTemporaryIndependents();
141 
142  size_t nIndexed = indexedIndepIndexes.size();
143  size_t nNonIndexed = nonIndexedIndepIndexes.size();
144 
145  const LoopPosition* posJ1 = loop->getNonIndexedIndepIndexes(j1);
146  const LoopPosition* posJ2 = (j1 == j2) ? posJ1 : loop->getNonIndexedIndepIndexes(j2);
147 
148  size_t nEqGroups = loopInfo.equationGroups.size();
149 
150  for (size_t g = 0; g < nEqGroups; g++) {
151  const IterEquationGroup<Base>& group = eqGroups[g];
152  const std::vector<set<size_t> >& groupHess = group.getHessianSparsity();
153 
159  const std::vector<set<pairss> >& iter2tapeJJ = group.getHessianIndexedIndexedTapeIndexes(j1, j2);
160  for (size_t iteration = 0; iteration < iter2tapeJJ.size(); iteration++) {
161  const set<pairss>& tapePairs = iter2tapeJJ[iteration];
162 
163  for (const pairss& itPairs : tapePairs) {
164  size_t tape1 = itPairs.first;
165  size_t tape2 = itPairs.second;
166  pairss tape;
167 
168  bool flip = flipIndices(groupHess, tape1, tape2);
169  if (flip) {
170  tape = pairss(tape2, tape1); // work the symmetry
171  } else {
172  tape = itPairs;
173  }
174 
175  std::vector<HessianElement>& positions = loopInfo.equationGroups[g].indexedIndexedPositions[tape];
176  positions.resize(nIter);
177 
178  positions[iteration].location = e;
179  positions[iteration].row = j1;
180  positions[iteration].count++;
181 
182  loopInfo.equationGroups[g].evalHessSparsity[tape.first].insert(tape.second);
183  }
184  }
185 
186 
192  if (posJ2 != nullptr) {
193 
194  const std::vector<set<size_t> >& iter2tapeJ1OrigJ2 = group.getHessianIndexedNonIndexedTapeIndexes(j1, j2);
195  for (size_t iteration = 0; iteration < iter2tapeJ1OrigJ2.size(); iteration++) {
196  const set<size_t>& tapeJ1s = iter2tapeJ1OrigJ2[iteration];
197 
198  for (size_t tapeJ1 : tapeJ1s) {
199 
200  bool flip = flipIndices2(groupHess, tapeJ1, posJ2->tape);
201 
202  std::vector<HessianElement>* positions;
203  if (flip) {
204  positions = &loopInfo.equationGroups[g].nonIndexedIndexedPositions[pairss(posJ2->tape, tapeJ1)];
205  loopInfo.equationGroups[g].evalHessSparsity[posJ2->tape].insert(tapeJ1);
206  } else {
207  positions = &loopInfo.equationGroups[g].indexedNonIndexedPositions[pairss(tapeJ1, posJ2->tape)];
208  loopInfo.equationGroups[g].evalHessSparsity[tapeJ1].insert(posJ2->tape);
209  }
210 
211  positions->resize(nIter);
212  (*positions)[iteration].location = e;
213  (*positions)[iteration].row = j1;
214  (*positions)[iteration].count++;
215  }
216  }
217 
218  }
219  }
220 
226  if (_funNoLoops != nullptr) {
227  map<size_t, set<size_t> > iter2tapeJ1 = loop->getIndexedTapeIndexes(j1);
228  for (const auto& itIter : iter2tapeJ1) {
229  size_t iteration = itIter.first;
230  const set<size_t>& tapeJ1s = itIter.second;
231  const set<const IterEquationGroup<Base>*>& groups = loop->getIterationEquationsGroup()[iteration];
232 
233  for (size_t tapeJ1 : tapeJ1s) {
234  for (const IterEquationGroup<Base>* itg : groups) {
235  const IterEquationGroup<Base>& group = *itg;
236  size_t g = group.index;
237  HessianWithLoopsEquationGroupInfo<Base>& groupInfo = loopInfo.equationGroups[g];
238  const std::vector<set<size_t> >& groupHess = group.getHessianSparsity();
239 
240  set<size_t>::const_iterator itz = groupHess[tapeJ1].lower_bound(nIndexed + nNonIndexed);
241 
242  pairss pos(tapeJ1, j2);
243  bool used = false;
244 
245  // loop temporary variables
246  for (; itz != groupHess[tapeJ1].end(); ++itz) {
247  size_t tapeJ = *itz;
248  size_t k = temporaryIndependents[tapeJ - nIndexed - nNonIndexed].original;
249 
253  const set<size_t>& sparsity = _funNoLoops->getJacobianSparsity()[nonIndexdedEqSize + k];
254  if (sparsity.find(j2) != sparsity.end()) {
255  noLoopEvalJacSparsity[nonIndexdedEqSize + k].insert(j2); // element required
256 
257  size_t tapeK = loop->getTempIndepIndexes(k)->tape;
258 
259  used = true;
260 
261  set<size_t>& evals = groupInfo.indexedTempEvals[pos];
262  evals.insert(k);
263 
264  groupInfo.evalHessSparsity[tapeJ1].insert(tapeK);
265  }
266  }
267 
268 
269  if (used) {
270  std::vector<HessianElement>& positions = groupInfo.indexedTempPositions[pos];
271  positions.resize(nIter);
272 
273  positions[iteration].location = e;
274  positions[iteration].row = j1;
275  positions[iteration].count++;
276  }
277 
278  }
279 
280  }
281 
282  }
283  }
284 
290  if (posJ1 != nullptr) {
291 
292  for (size_t g = 0; g < nEqGroups; g++) {
293  const IterEquationGroup<Base>& group = eqGroups[g];
294 
295  const std::vector<set<size_t> >& iter2TapeJ2 = group.getHessianNonIndexedIndexedTapeIndexes(j1, j2);
296  for (size_t iteration = 0; iteration < iter2TapeJ2.size(); iteration++) {
297  const set<size_t>& tapeJ2s = iter2TapeJ2[iteration];
298 
299  for (size_t tapeJ2 : tapeJ2s) {
300  std::vector<HessianElement>& positions = loopInfo.equationGroups[g].nonIndexedIndexedPositions[pairss(posJ1->tape, tapeJ2)];
301  positions.resize(nIter);
302  positions[iteration].location = e;
303  positions[iteration].row = j1;
304  positions[iteration].count++;
305 
306  loopInfo.equationGroups[g].evalHessSparsity[posJ1->tape].insert(tapeJ2);
307  }
308  }
309  }
310  }
311 
317  bool jInNonIndexed = false;
318  pairss orig(j1, j2);
319 
320  if (posJ1 != nullptr && posJ2 != nullptr) {
321  for (size_t g = 0; g < nEqGroups; g++) {
322  const IterEquationGroup<Base>& group = eqGroups[g];
323 
324  const set<pairss>& orig1orig2 = group.getHessianNonIndexedNonIndexedIndexes();
325  if (orig1orig2.find(orig) != orig1orig2.end()) {
326  jInNonIndexed = true;
327 
328  loopInfo.equationGroups[g].nonIndexedNonIndexedEvals.insert(orig);
329  loopInfo.equationGroups[g].evalHessSparsity[posJ1->tape].insert(posJ2->tape);
330  }
331 
332  }
333 
334  if (jInNonIndexed)
335  loopInfo.nonIndexedNonIndexedPosition[orig] = e;
336  }
337 
343  if (_funNoLoops != nullptr && posJ1 != nullptr) {
344 
345  for (size_t g = 0; g < nEqGroups; g++) {
346  const IterEquationGroup<Base>& group = eqGroups[g];
347 
348  const set<size_t>& hessRow = group.getHessianSparsity()[posJ1->tape];
349  set<size_t>::const_iterator itz = hessRow.lower_bound(nIndexed + nNonIndexed);
350 
351  // loop temporary variables
352  for (; itz != hessRow.end(); ++itz) {
353  size_t tapeJ = *itz;
354  size_t k = temporaryIndependents[tapeJ - nIndexed - nNonIndexed].original;
355 
356  // Jacobian of g for k must have j2
357  const set<size_t>& gJacRow = _funNoLoops->getJacobianSparsity()[nonIndexdedEqSize + k];
358  if (gJacRow.find(j2) != gJacRow.end()) {
359  noLoopEvalJacSparsity[nonIndexdedEqSize + k].insert(j2); // element required
360 
361  if (!jInNonIndexed) {
362  jInNonIndexed = true;
363  CPPADCG_ASSERT_KNOWN(loopInfo.nonIndexedNonIndexedPosition.find(orig) == loopInfo.nonIndexedNonIndexedPosition.end(),
364  "Repeated hessian elements requested");
365  loopInfo.nonIndexedNonIndexedPosition[orig] = e;
366  }
367 
368  size_t tapeK = loop->getTempIndepIndexes(k)->tape;
369  loopInfo.equationGroups[g].nonIndexedTempEvals[orig].insert(k);
370  loopInfo.equationGroups[g].evalHessSparsity[posJ1->tape].insert(tapeK);
371  }
372 
373  }
374  }
375  }
376 
380  if (_funNoLoops != nullptr) {
381  const std::vector<set<size_t> >& gJac = _funNoLoops->getJacobianSparsity();
382  size_t nk = _funNoLoops->getTemporaryDependentCount();
383  size_t nOrigEq = _funNoLoops->getTapeDependentCount() - nk;
384 
385  const std::vector<set<size_t> >& dzdxx = _funNoLoops->getHessianTempEqsSparsity();
386 
387  std::vector<std::set<size_t> > usedTapeJ2(nEqGroups);
388 
389  for (size_t k1 = 0; k1 < nk; k1++) {
390  if (gJac[nOrigEq + k1].find(j1) == gJac[nOrigEq + k1].end()) {
391  continue;
392  }
393 
394  const LoopPosition* posK1 = loop->getTempIndepIndexes(k1);
395  if (posK1 == nullptr) {
396  continue;
397  }
398 
405  for (size_t g = 0; g < nEqGroups; g++) {
406  const IterEquationGroup<Base>& group = eqGroups[g];
407  const std::vector<set<size_t> >& groupHess = group.getHessianSparsity();
408  HessianWithLoopsEquationGroupInfo<Base>& groupHessInfo = loopInfo.equationGroups[g];
409 
410  const map<size_t, set<size_t> >& tapeJ22Iter = group.getHessianTempIndexedTapeIndexes(k1, j2);
411  for (const auto& ittj22iter : tapeJ22Iter) {
412  size_t tapeJ2 = ittj22iter.first;
413  const set<size_t>& iterations = ittj22iter.second;
414 
415  bool used = usedTapeJ2[g].find(tapeJ2) != usedTapeJ2[g].end();
416  bool added = false;
417 
418  for (size_t iteration : iterations) {
419  std::vector<HessianElement>* positions = nullptr;
420 
421  bool flip = flipIndices2(groupHess, posK1->tape, tapeJ2);
422  if (flip) {
423  if (!used) {
424  pairss pos(tapeJ2, j1);
425  positions = &groupHessInfo.indexedTempPositions[pos];
426  }
427  groupHessInfo.evalHessSparsity[tapeJ2].insert(posK1->tape);
428  } else {
429  if (!used) {
430  pairss pos(j1, tapeJ2);
431  positions = &groupHessInfo.tempIndexedPositions[pos];
432  }
433  groupHessInfo.evalHessSparsity[posK1->tape].insert(tapeJ2);
434  }
435 
436  if (positions != nullptr) {
437  positions->resize(nIter);
438 
439  (*positions)[iteration].location = e;
440  (*positions)[iteration].row = j1;
441  (*positions)[iteration].count++;
442  usedTapeJ2[g].insert(tapeJ2);
443  }
444 
445  if (!added) {
446  added = true;
447  set<size_t>& evals = groupHessInfo.indexedTempEvals[pairss(tapeJ2, j1)];
448  evals.insert(k1);
449  }
450  }
451 
452  }
453 
459  if (posJ2 != nullptr) {
460  const set<size_t>& hessRow = group.getHessianSparsity()[posK1->tape];
461 
462  if (hessRow.find(j2) != hessRow.end()) {
463  if (!jInNonIndexed) {
464  jInNonIndexed = true;
465  CPPADCG_ASSERT_KNOWN(loopInfo.nonIndexedNonIndexedPosition.find(orig) == loopInfo.nonIndexedNonIndexedPosition.end(),
466  "Repeated hessian elements requested");
467  loopInfo.nonIndexedNonIndexedPosition[orig] = e;
468  }
469 
470  groupHessInfo.tempNonIndexedEvals[orig].insert(k1);
471  groupHessInfo.evalHessSparsity[posK1->tape].insert(posJ2->tape);
472  }
473  }
474 
475 
481  // loop Hessian row
482  const set<size_t>& hessRow = group.getHessianSparsity()[posK1->tape];
483  set<size_t>::const_iterator itTapeJ2 = hessRow.lower_bound(nIndexed + nNonIndexed);
484  for (; itTapeJ2 != hessRow.end(); ++itTapeJ2) {
485  size_t tapeK2 = *itTapeJ2;
486  size_t k2 = loop->getTemporaryIndependents()[tapeK2 - nIndexed - nNonIndexed].original;
487 
488  const set<size_t>& jacZk2Row = gJac[nOrigEq + k2];
489  if (jacZk2Row.find(j2) != jacZk2Row.end()) { // is this check truly needed?
490 
491  if (!jInNonIndexed) {
492  jInNonIndexed = true;
493  CPPADCG_ASSERT_KNOWN(loopInfo.nonIndexedNonIndexedPosition.find(orig) == loopInfo.nonIndexedNonIndexedPosition.end(),
494  "Repeated hessian elements requested");
495  loopInfo.nonIndexedNonIndexedPosition[orig] = e;
496  }
497 
498  groupHessInfo.tempTempEvals[orig][k1].insert(k2);
499  groupHessInfo.evalHessSparsity[posK1->tape].insert(tapeK2);
500 
501  noLoopEvalJacSparsity[nOrigEq + k2].insert(j2); // @todo: repeated operation for each group (only one needed)
502  }
503  }
504  }
505 
506  //
507  noLoopEvalJacSparsity[nOrigEq + k1].insert(j1);
508 
514  if (dzdxx[j1].find(j2) != dzdxx[j1].end()) {
515 
516  for (size_t i = 0; i < loopJac.size(); i++) {
517  const set<size_t>& fJacRow = loopJac[i];
518 
519  if (fJacRow.find(posK1->tape) != fJacRow.end()) {
520  if (!jInNonIndexed) {
521  CPPADCG_ASSERT_KNOWN(loopInfo.nonIndexedNonIndexedPosition.find(orig) == loopInfo.nonIndexedNonIndexedPosition.end(),
522  "Repeated hessian elements requested");
523 
524  loopInfo.nonIndexedNonIndexedPosition[orig] = e;
525  jInNonIndexed = true;
526  }
527 
528  loopInfo.nonLoopNonIndexedNonIndexed[orig].insert(k1);
529  loopInfo.evalJacSparsity[i].insert(posK1->tape);
530  loopInfo.noLoopEvalHessTempsSparsity[j1].insert(j2);
531  }
532  }
533  }
534  }
535  }
536 
537  }
538  }
539 }
540 
541 template<class Base>
542 inline void addContribution(std::vector<std::pair<CG<Base>, IndexPattern*> >& indexedLoopResults,
543  size_t& hessLE,
544  const std::pair<CG<Base>, IndexPattern*>& val) {
545  if (!val.first.isIdenticalZero()) {
546  if (indexedLoopResults.size() <= hessLE) {
547  if (indexedLoopResults.capacity() <= hessLE) {
548  indexedLoopResults.reserve(3 * hessLE / 2 + 1);
549  }
550  indexedLoopResults.resize(hessLE + 1);
551  }
552  indexedLoopResults[hessLE++] = val;
553  }
554 }
555 
556 template<class Base>
558  std::vector<CGBase>& x,
559  std::vector<CGBase>& w,
560  const std::vector<size_t>& lowerHessRows,
561  const std::vector<size_t>& lowerHessCols,
562  const std::vector<size_t>& lowerHessOrder,
563  const std::map<size_t, size_t>& duplicates) {
564  using namespace std;
565  using namespace CppAD::cg::loops;
566 
567  handler.setZeroDependents(true);
568 
569  size_t nonIndexdedEqSize = _funNoLoops != nullptr ? _funNoLoops->getOrigDependentIndexes().size() : 0;
570 
571  size_t maxLoc = _hessSparsity.rows.size();
572  std::vector<CGBase> hess(maxLoc);
573 
574  std::vector<set<size_t> > noLoopEvalJacSparsity;
575  std::vector<set<size_t> > noLoopEvalHessSparsity;
576  std::vector<map<size_t, set<size_t> > > noLoopEvalHessLocations;
577  map<LoopModel<Base>*, HessianWithLoopsInfo<Base> > loopHessInfo;
578 
584  analyseSparseHessianWithLoops(lowerHessRows, lowerHessCols, lowerHessOrder,
585  noLoopEvalJacSparsity, noLoopEvalHessSparsity,
586  noLoopEvalHessLocations, loopHessInfo, true);
587 
588  /***********************************************************************
589  * generate the operation graph
590  **********************************************************************/
594  OperationNode<Base>* iterationIndexDcl = handler.makeIndexDclrNode(LoopModel<Base>::ITERATION_INDEX_NAME);
595 
596  // temporaries (zero order)
597  std::vector<CGBase> tmpsAlias;
598  if (_funNoLoops != nullptr) {
599  ADFun<CGBase>& fun = _funNoLoops->getTape();
600 
601  tmpsAlias.resize(fun.Range() - nonIndexdedEqSize);
602  for (size_t k = 0; k < tmpsAlias.size(); k++) {
603  // to be defined later
604  tmpsAlias[k] = CG<Base>(*handler.makeNode(CGOpCode::Alias));
605  }
606  }
607 
611  typename map<LoopModel<Base>*, HessianWithLoopsInfo<Base> >::iterator itLoop2Info;
612  for (itLoop2Info = loopHessInfo.begin(); itLoop2Info != loopHessInfo.end(); ++itLoop2Info) {
613  LoopModel<Base>& lModel = *itLoop2Info->first;
614  HessianWithLoopsInfo<Base>& info = itLoop2Info->second;
615 
619  info.loopStart = handler.makeLoopStartNode(*iterationIndexDcl, lModel.getIterationCount());
620 
621  info.iterationIndexOp = handler.makeIndexNode(*info.loopStart);
622  set<IndexOperationNode<Base>*> indexesOps;
623  indexesOps.insert(info.iterationIndexOp);
624 
628  std::vector<CGBase> indexedIndeps = createIndexedIndependents(handler, lModel, *info.iterationIndexOp);
629  info.x = createLoopIndependentVector(handler, lModel, indexedIndeps, x, tmpsAlias);
630 
631  info.w = createLoopDependentVector(handler, lModel, *info.iterationIndexOp);
632  }
633 
640  for (itLoop2Info = loopHessInfo.begin(); itLoop2Info != loopHessInfo.end(); ++itLoop2Info) {
641  LoopModel<Base>& lModel = *itLoop2Info->first;
642  HessianWithLoopsInfo<Base>& info = itLoop2Info->second;
643 
644  _cache.str("");
645  _cache << "model (Jacobian + Hessian, loop " << lModel.getLoopId() << ")";
646  std::string jobName = _cache.str();
647  _cache.str("");
648  startingJob("'" + jobName + "'", JobTimer::GRAPH);
649 
650  bool individualColoring = lModel.isContainsAtomics();
651  info.evalLoopModelJacobianHessian(individualColoring);
652 
653  finishedJob();
654  }
655 
659  // Jacobian for temporaries
660  map<size_t, map<size_t, CGBase> > dzDx;
661 
662  if (_funNoLoops != nullptr) {
663  ADFun<CGBase>& fun = _funNoLoops->getTape();
664  std::vector<CGBase> yNL(fun.Range());
665 
669  startingJob("'model (Jacobian + Hessian, temporaries)'", JobTimer::GRAPH);
670 
671  dzDx = _funNoLoops->calculateJacobianHessianUsedByLoops(handler,
672  loopHessInfo, x, yNL,
673  noLoopEvalJacSparsity,
674  false);
675 
676  finishedJob();
677 
678  for (size_t i = 0; i < tmpsAlias.size(); i++)
679  tmpsAlias[i].getOperationNode()->getArguments().push_back(asArgument(yNL[nonIndexdedEqSize + i]));
680 
681  for (itLoop2Info = loopHessInfo.begin(); itLoop2Info != loopHessInfo.end(); ++itLoop2Info) {
682  HessianWithLoopsInfo<Base>& info = itLoop2Info->second;
683  // not needed anymore:
684  info.dyiDzk.clear();
685  }
686 
690  _funNoLoops->calculateHessian4OrignalEquations(x, w,
691  noLoopEvalHessSparsity, noLoopEvalHessLocations,
692  hess);
693  }
694 
698  for (itLoop2Info = loopHessInfo.begin(); itLoop2Info != loopHessInfo.end(); ++itLoop2Info) {
699  LoopModel<Base>& lModel = *itLoop2Info->first;
700  size_t nIterations = lModel.getIterationCount();
701  HessianWithLoopsInfo<Base>& info = itLoop2Info->second;
702 
703  // store results in indexedLoopResults
704  size_t hessElSize = info.nonIndexedNonIndexedPosition.size();
705  for (size_t g = 0; g < info.equationGroups.size(); g++) {
706  HessianWithLoopsEquationGroupInfo<Base>& infog = info.equationGroups[g];
707  hessElSize += infog.indexedIndexedPositions.size() +
708  infog.indexedTempPositions.size() +
709  infog.nonIndexedIndexedPositions.size();
710  }
711 
712  if (hessElSize == 0)
713  continue; // no second order information
714 
715  std::vector<pair<CGBase, IndexPattern*> > indexedLoopResults(hessElSize);
716  size_t hessLE = 0;
717 
721  for (size_t g = 0; g < info.equationGroups.size(); g++) {
722 
723  HessianWithLoopsEquationGroupInfo<Base>& infog = info.equationGroups[g];
724 
725  /*******************************************************************
726  * indexed - indexed
727  */
728  for (const auto& it : infog.indexedIndexedPositions) {
729  size_t tapeJ1 = it.first.first;
730  size_t tapeJ2 = it.first.second;
731  const std::vector<HessianElement>& positions = it.second;
732 
733  addContribution(indexedLoopResults, hessLE,
734  createHessianContribution(handler, positions, infog.hess[tapeJ1].at(tapeJ2),
735  *info.iterationIndexOp, info.ifElses));
736  }
737 
742  for (const auto& it : infog.indexedNonIndexedPositions) {
743  size_t tapeJ1 = it.first.first;
744  size_t tapeJ2 = it.first.second;
745  const std::vector<HessianElement>& positions = it.second;
746 
747  addContribution(indexedLoopResults, hessLE,
748  createHessianContribution(handler, positions, infog.hess[tapeJ1].at(tapeJ2),
749  *info.iterationIndexOp, info.ifElses));
750  }
751 
755  if (!infog.indexedTempPositions.empty()) {
756  for (const auto& itEval : infog.indexedTempEvals) {
757  size_t tapeJ1 = itEval.first.first;
758  size_t j2 = itEval.first.second;
759  const set<size_t>& ks = itEval.second;
760 
761  const auto itPos = infog.indexedTempPositions.find(itEval.first);
762  if (itPos != infog.indexedTempPositions.end()) {
763  const std::vector<HessianElement>& positions = itPos->second;
764 
765  CGBase hessVal = Base(0);
766  for (size_t k : ks) {
767  size_t tapeK = lModel.getTempIndepIndexes(k)->tape;
768  hessVal += infog.hess[tapeJ1].at(tapeK) * dzDx[k][j2];
769  }
770 
771  addContribution(indexedLoopResults, hessLE,
772  createHessianContribution(handler, positions, hessVal,
773  *info.iterationIndexOp, info.ifElses));
774  }
775  }
776  }
777 
778  /*******************************************************************
779  * non-indexed - indexed
780  */
781  for (const auto& it : infog.nonIndexedIndexedPositions) {
782  size_t tapeJ1 = it.first.first;
783  size_t tapeJ2 = it.first.second;
784  const std::vector<HessianElement>& positions = it.second;
785 
786  addContribution(indexedLoopResults, hessLE,
787  createHessianContribution(handler, positions, infog.hess[tapeJ1].at(tapeJ2),
788  *info.iterationIndexOp, info.ifElses));
789  }
790 
791  /*******************************************************************
792  * temporary - indexed
793  *
794  * d f_i . d x_k1
795  * d x_l2 d z_k1 d x_j1
796  *
797  * -> usually done by (indexed - temporary) by exploiting the symmetry
798  */
799  if (!infog.tempIndexedPositions.empty()) {
800  for (const auto& itEval : infog.indexedTempEvals) {
801  size_t tapeJ2 = itEval.first.first;
802  size_t j1 = itEval.first.second;
803  const set<size_t>& ks = itEval.second;
804 
805  const auto itPos = infog.tempIndexedPositions.find(pairss(j1, tapeJ2));
806  if (itPos != infog.tempIndexedPositions.end()) {
807  const std::vector<HessianElement>& positions = itPos->second;
808  CGBase hessVal = Base(0);
809  for (size_t k : ks) {
810  size_t tapeK = lModel.getTempIndepIndexes(k)->tape;
811  hessVal += infog.hess[tapeK].at(tapeJ2) * dzDx[k][j1];
812  }
813 
814  addContribution(indexedLoopResults, hessLE,
815  createHessianContribution(handler, positions, hessVal,
816  *info.iterationIndexOp, info.ifElses));
817  }
818  }
819  }
820 
821  }
822 
823  /*******************************************************************
824  * contributions to a constant location
825  */
826  for (const auto& orig2PosIt : info.nonIndexedNonIndexedPosition) {
827  const pairss& orig = orig2PosIt.first;
828  size_t e = orig2PosIt.second;
829 
830  size_t j1 = orig.first;
831  size_t j2 = orig.second;
832  const LoopPosition* posJ1 = lModel.getNonIndexedIndepIndexes(j1);
833  const LoopPosition* posJ2 = lModel.getNonIndexedIndepIndexes(j2);
834 
835  // location
836  LinearIndexPattern* pattern = new LinearIndexPattern(0, 0, 0, e);
837  handler.manageLoopDependentIndexPattern(pattern);
838 
842  CGBase hessVal = Base(0);
843 
847  for (size_t g = 0; g < info.equationGroups.size(); g++) {
848  const IterEquationGroup<Base>& group = lModel.getEquationsGroups()[g];
849 
850  CGBase gHessVal = Base(0);
851  HessianWithLoopsEquationGroupInfo<Base>& infog = info.equationGroups[g];
852 
853  if (infog.nonIndexedNonIndexedEvals.find(orig) != infog.nonIndexedNonIndexedEvals.end()) {
854  gHessVal = infog.hess[posJ1->tape].at(posJ2->tape);
855  }
856 
860  const auto itNT = infog.nonIndexedTempEvals.find(orig);
861  if (itNT != infog.nonIndexedTempEvals.end()) {
862  const set<size_t>& ks = itNT->second;
863 
864  for (size_t k : ks) {
865  size_t tapeK = lModel.getTempIndepIndexes(k)->tape;
866  gHessVal += infog.hess[posJ1->tape].at(tapeK) * dzDx[k][j2];
867  }
868  }
869 
876  const auto itTN = infog.tempNonIndexedEvals.find(orig);
877  if (itTN != infog.tempNonIndexedEvals.end()) {
878  const set<size_t>& ks = itTN->second;
879 
880  for (size_t k1 : ks) {
881  size_t tapeK = lModel.getTempIndepIndexes(k1)->tape;
882  gHessVal += infog.hess[tapeK].at(posJ2->tape) * dzDx[k1][j1];
883  }
884  }
885 
889  const auto itTT = infog.tempTempEvals.find(orig);
890  if (itTT != infog.tempTempEvals.end()) {
891  const map<size_t, set<size_t> >& k1k2 = itTT->second;
892 
893  CGBase sum = Base(0);
894 
895  for (const auto& itzz : k1k2) {
896  size_t k1 = itzz.first;
897  const set<size_t>& k2s = itzz.second;
898  size_t tapeK1 = lModel.getTempIndepIndexes(k1)->tape;
899 
900  CGBase tmp = Base(0);
901  for (size_t k2 : k2s) {
902  size_t tapeK2 = lModel.getTempIndepIndexes(k2)->tape;
903 
904  tmp += infog.hess[tapeK1].at(tapeK2) * dzDx[k2][j2];
905  }
906 
907  sum += tmp * dzDx[k1][j1];
908  }
909 
910  gHessVal += sum;
911  }
912 
913  if (group.iterations.size() != nIterations) {
914  CGBase v = createHessianContribution(handler, *pattern, group.iterations,
915  nIterations, gHessVal,
916  *info.iterationIndexOp, info.ifElses);
917  addContribution(indexedLoopResults, hessLE, make_pair(v, (IndexPattern*) nullptr));
918  } else {
919  hessVal += gHessVal;
920  }
921 
922  }
923 
927  const auto itTT2 = info.nonLoopNonIndexedNonIndexed.find(orig);
928  if (itTT2 != info.nonLoopNonIndexedNonIndexed.end()) {
929  hessVal += info.dzDxx.at(j1).at(j2); // it is already the sum of ddz / dx_j1 dx_j2
930  }
931 
932  // place the result
933  addContribution(indexedLoopResults, hessLE, make_pair(hessVal, (IndexPattern*) pattern));
934  }
935 
936  indexedLoopResults.resize(hessLE);
937 
941  size_t assignOrAdd = 1;
942  set<IndexOperationNode<Base>*> indexesOps;
943  indexesOps.insert(info.iterationIndexOp);
944  info.loopEnd = createLoopEnd(handler, *info.loopStart, indexedLoopResults, indexesOps, assignOrAdd);
945 
946  for (size_t e : lowerHessOrder) {
947  // an additional alias variable is required so that each dependent variable can have its own ID
948  if (hess[e].isIdenticalZero()) {
949  hess[e] = CG<Base>(*handler.makeNode(CGOpCode::DependentMultiAssign, *info.loopEnd));
950 
951  } else if (hess[e].getOperationNode() != nullptr && hess[e].getOperationNode()->getOperationType() == CGOpCode::DependentMultiAssign) {
952  hess[e].getOperationNode()->getArguments().push_back(*info.loopEnd);
953 
954  } else {
955  hess[e] = handler.createCG(*handler.makeNode(CGOpCode::DependentMultiAssign,{asArgument(hess[e]), *info.loopEnd}));
956  }
957  }
958 
959  // not needed anymore:
960  for (size_t g = 0; g < info.equationGroups.size(); g++) {
961  info.equationGroups[g].hess.clear();
962  }
963  info.dzDxx.clear();
964 
968  moveNonIndexedOutsideLoop(handler, *info.loopStart, *info.loopEnd);
969  }
970 
974  // make use of the symmetry of the Hessian in order to reduce operations
975  for (const auto& it2 : duplicates) {
976  if (hess[it2.second].isVariable())
977  hess[it2.first] = CG<Base>(*handler.makeNode(CGOpCode::Alias, asArgument(hess[it2.second])));
978  else
979  hess[it2.first] = hess[it2.second].getValue();
980  }
981 
982  return hess;
983 }
984 
985 namespace loops {
986 
987 template<class Base>
988 std::pair<CG<Base>, IndexPattern*> createHessianContribution(CodeHandler<Base>& handler,
989  const std::vector<HessianElement>& positions,
990  const CG<Base>& ddfdxdx,
991  IndexOperationNode<Base>& iterationIndexOp,
992  std::vector<IfElseInfo<Base> >& ifElses) {
993  using namespace std;
994 
995  if (ddfdxdx.isIdenticalZero()) {
996  return make_pair(ddfdxdx, (IndexPattern*) nullptr);
997  }
998 
999  // combine iterations with the same number of additions
1000  map<size_t, map<size_t, size_t> > locations;
1001  for (size_t iter = 0; iter < positions.size(); iter++) {
1002  size_t c = positions[iter].count;
1003  if (c > 0) {
1004  locations[c][iter] = positions[iter].location;
1005  }
1006  }
1007 
1008  map<size_t, CG<Base> > results;
1009 
1010  // generate the index pattern for the Hessian compressed element
1011  for (const auto& countIt : locations) {
1012  size_t count = countIt.first;
1013 
1014  CG<Base> val = ddfdxdx;
1015  for (size_t c = 1; c < count; c++)
1016  val += ddfdxdx;
1017 
1018  results[count] = val;
1019  }
1020 
1021  if (results.size() == 1 && locations.begin()->second.size() == positions.size()) {
1022  // same expression present in all iterations
1023 
1024  // generate the index pattern for the Hessian compressed element
1025  IndexPattern* pattern = IndexPattern::detect(locations.begin()->second);
1026  handler.manageLoopDependentIndexPattern(pattern);
1027 
1028  return make_pair(results.begin()->second, pattern);
1029 
1030  } else {
1036  map<size_t, IfBranchData<Base> > branches;
1037 
1038  // try to find an existing if-else where these operations can be added
1039  for (const auto& countIt : locations) {
1040 
1041  size_t count = countIt.first;
1042  IfBranchData<Base> branch(results[count], countIt.second);
1043  branches[count] = branch;
1044  }
1045 
1046  CG<Base> v = createConditionalContribution(handler,
1047  branches,
1048  positions.size() - 1,
1049  positions.size(),
1050  iterationIndexOp,
1051  ifElses);
1052 
1053  return make_pair(v, (IndexPattern*) nullptr);
1054  }
1055 
1056 }
1057 
1061 template<class Base>
1062 CG<Base> createHessianContribution(CodeHandler<Base>& handler,
1063  LinearIndexPattern& pattern,
1064  const std::set<size_t>& iterations,
1065  size_t nIterations,
1066  const CG<Base>& ddfdxdx,
1067  IndexOperationNode<Base>& iterationIndexOp,
1068  std::vector<IfElseInfo<Base> >& ifElses) {
1069  using namespace std;
1070 
1071  if (ddfdxdx.isIdenticalZero()) {
1072  return ddfdxdx;
1073  }
1074 
1075  CPPADCG_ASSERT_UNKNOWN(pattern.getLinearSlopeDy() == 0); // must be a constant index
1076 
1077  if (iterations.size() == nIterations) {
1078  // same expression present in all iterations
1079  return ddfdxdx;
1080 
1081  } else {
1082 
1088  return createConditionalContribution(handler, pattern,
1089  iterations, nIterations - 1,
1090  ddfdxdx, iterationIndexOp,
1091  ifElses);
1092  }
1093 }
1094 
1095 template<class Base>
1096 inline void generateLoopForJacHes(ADFun<CG<Base> >& fun,
1097  const std::vector<CG<Base> >& x,
1098  const std::vector<std::vector<CG<Base> > >& vw,
1099  std::vector<CG<Base> >& y,
1100  const std::vector<std::set<size_t> >& jacSparsity,
1101  const std::vector<std::set<size_t> >& jacEvalSparsity,
1102  std::vector<std::map<size_t, CG<Base> > >& jac,
1103  const std::vector<std::set<size_t> >& hesSparsity,
1104  const std::vector<std::set<size_t> >& hesEvalSparsity,
1105  std::vector<std::map<size_t, std::map<size_t, CG<Base> > > >& vhess,
1106  bool individualColoring) {
1107  using namespace std;
1108 
1109  using CGB = CG<Base>;
1110 
1111  size_t m = fun.Range();
1112  size_t n = fun.Domain();
1113 
1114  jac.resize(m);
1115  vhess.resize(vw.size());
1116 
1117  if (!individualColoring) {
1122  // Jacobian for temporaries
1123  std::vector<size_t> jacRow, jacCol;
1124  generateSparsityIndexes(jacEvalSparsity, jacRow, jacCol);
1125 
1126  // Jacobian for equations outside loops
1127  std::vector<CGB> jacFlat(jacRow.size());
1128 
1132  std::vector<size_t> hesRow, hesCol;
1133  generateSparsityIndexes(hesEvalSparsity, hesRow, hesCol);
1134 
1135  std::vector<std::vector<CGB> > vhessFlat(vw.size());
1136  for (size_t l = 0; l < vw.size(); l++) {
1137  vhessFlat[l].resize(hesRow.size());
1138  }
1139 
1140  std::vector<CG<Base> > xl;
1141  if (x.size() == 0) {
1142  xl.resize(1); // does not depend on any variable but CppAD requires at least one
1143  xl[0] = Base(0);
1144  } else {
1145  xl = x;
1146  }
1147 
1149  sparseForJacHessian(fun, xl, vw,
1150  y,
1151  jacSparsity,
1152  jacRow, jacCol, jacFlat,
1153  hesSparsity,
1154  hesRow, hesCol, vhessFlat,
1155  work);
1156 
1157  // save Jacobian
1158  for (size_t el = 0; el < jacRow.size(); el++) {
1159  size_t i = jacRow[el];
1160  size_t j = jacCol[el];
1161 
1162  jac[i][j] = jacFlat[el];
1163  }
1164 
1165  // save Hessian
1166  for (size_t l = 0; l < vw.size(); l++) {
1167  std::vector<CGB>& hessFlat = vhessFlat[l];
1168  map<size_t, map<size_t, CGB> >& hess = vhess[l];
1169 
1170  for (size_t el = 0; el < hesRow.size(); el++) {
1171  size_t j1 = hesRow[el];
1172  size_t j2 = hesCol[el];
1173  hess[j1][j2] = hessFlat[el];
1174  }
1175  }
1176 
1177  } else {
1182  //transpose
1183  std::vector<set<size_t> > jacEvalSparsityT(n);
1184  addTransMatrixSparsity(jacEvalSparsity, jacEvalSparsityT);
1185 
1186  std::vector<CGB> tx1v(n);
1187 
1188  y = fun.Forward(0, x);
1189 
1190  for (size_t j1 = 0; j1 < n; j1++) {
1191  if (jacEvalSparsityT[j1].empty() && hesEvalSparsity[j1].empty()) {
1192  continue;
1193  }
1194 
1195  tx1v[j1] = Base(1);
1196  std::vector<CGB> dy = fun.Forward(1, tx1v);
1197  CPPADCG_ASSERT_UNKNOWN(dy.size() == m);
1198  tx1v[j1] = Base(0);
1199 
1200  // save Jacobian
1201  const set<size_t>& column = jacEvalSparsityT[j1];
1202  for (size_t i : column) {
1203  jac[i][j1] = dy[i];
1204  }
1205 
1206  const set<size_t>& hesRow = hesEvalSparsity[j1];
1207 
1208  if (!hesRow.empty()) {
1209 
1210  for (size_t l = 0; l < vw.size(); l++) {
1211 
1212  std::vector<CGB> px = fun.Reverse(2, vw[l]);
1213  CPPADCG_ASSERT_UNKNOWN(px.size() == 2 * n);
1214 
1215  // save Hessian
1216  map<size_t, CGB>& hessRow = vhess[l][j1];
1217  for (size_t j2 : hesRow) {
1218  hessRow[j2] = px[j2 * 2 + 1];
1219  }
1220  }
1221  }
1222  }
1223  }
1224 
1225 }
1226 
1227 } // END loops namespace
1228 
1229 } // END cg namespace
1230 } // END CppAD namespace
1231 
1232 #endif
void evalLoopModelJacobianHessian(bool individualColoring)
std::map< size_t, std::map< size_t, CG< Base > > > hess
size_t index
iteration group index/ID
STL namespace.
const std::vector< std::set< pairss > > & getHessianIndexedIndexedTapeIndexes(size_t origJ1, size_t origJ2) const
size_t getLoopId() const
Definition: loop_model.hpp:236
std::vector< std::map< size_t, CG< Base > > > dyiDzk
bool isContainsAtomics() const
Definition: loop_model.hpp:245
static IndexPattern * detect(const VectorSizeT &x2y)
const LoopPosition * getTempIndepIndexes(size_t k) const
Definition: loop_model.hpp:357
virtual std::vector< CGBase > prepareSparseHessianWithLoops(CodeHandler< Base > &handler, std::vector< CGBase > &indVars, std::vector< CGBase > &w, const std::vector< size_t > &lowerHessRows, const std::vector< size_t > &lowerHessCols, const std::vector< size_t > &lowerHessOrder, const std::map< size_t, size_t > &duplicates)
void analyseSparseHessianWithLoops(const std::vector< size_t > &lowerHessRows, const std::vector< size_t > &lowerHessCols, const std::vector< size_t > &lowerHessOrder, SparsitySetType &noLoopEvalJacSparsity, SparsitySetType &noLoopEvalHessSparsity, std::vector< std::map< size_t, std::set< size_t > > > &noLoopEvalHessLocations, std::map< LoopModel< Base > *, loops::HessianWithLoopsInfo< Base > > &loopHessInfo, bool useSymmetry)
const std::vector< LoopPosition > & getNonIndexedIndepIndexes() const
Definition: loop_model.hpp:316
const size_t getIterationCount() const
Definition: loop_model.hpp:254
const std::vector< IterEquationGroup< Base > > & getEquationsGroups() const
Definition: loop_model.hpp:298
void setZeroDependents(bool zeroDependents)
std::set< size_t > iterations
iterations which only have these equations defined