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