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