CppADCodeGen 2.4.3
A C++ Algorithmic Differentiation Package with Source Code Generation
Loading...
Searching...
No Matches
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
19namespace CppAD {
20namespace cg {
21
22namespace loops {
23
25public:
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
38template<class Base>
39std::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
51template<class Base>
52void 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
542template<class Base>
543inline 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
557template<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
986namespace loops {
987
988template<class Base>
989std::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
1062template<class Base>
1063CG<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
1096template<class Base>
1097inline 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
const Base & getValue() const
Definition variable.hpp:45
void setZeroDependents(bool zeroDependents)
static IndexPattern * detect(const VectorSizeT &x2y)
const std::vector< std::set< pairss > > & getHessianIndexedIndexedTapeIndexes(size_t origJ1, size_t origJ2) const
std::set< size_t > iterations
iterations which only have these equations defined
size_t index
iteration group index/ID
const std::vector< IterEquationGroup< Base > > & getEquationsGroups() const
bool isContainsAtomics() const
const size_t getIterationCount() const
size_t getLoopId() const
const LoopPosition * getTempIndepIndexes(size_t k) const
const std::vector< LoopPosition > & getNonIndexedIndepIndexes() const
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)
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)
std::map< size_t, std::map< size_t, CG< Base > > > hess
std::vector< std::map< size_t, CG< Base > > > dyiDzk
void evalLoopModelJacobianHessian(bool individualColoring)