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