CppADCodeGen 2.4.3
A C++ Algorithmic Differentiation Package with Source Code Generation
Loading...
Searching...
No Matches
model_c_source_gen_loops_rev1.hpp
1#ifndef CPPAD_CG_MODEL_C_SOURCE_GEN_LOOPS_REV1_INCLUDED
2#define CPPAD_CG_MODEL_C_SOURCE_GEN_LOOPS_REV1_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
30template<class Base>
31void ModelCSourceGen<Base>::prepareSparseReverseOneWithLoops(const std::map<size_t, std::vector<size_t> >& elements) {
32 using namespace std;
33 using namespace CppAD::cg::loops;
34
35 //printSparsityPattern(_jacSparsity.rows, _jacSparsity.cols, "jacobian", _fun.Range());
36
37 size_t n = _fun.Domain();
38
39 CodeHandler<Base> handler;
40 handler.setJobTimer(_jobTimer);
41 handler.setZeroDependents(false);
42
43 auto& indexJrowDcl = *handler.makeIndexDclrNode("jrow");
44 auto& indexIterationDcl = *handler.makeIndexDclrNode(LoopModel<Base>::ITERATION_INDEX_NAME);
45 auto& iterationIndexOp = *handler.makeIndexNode(indexIterationDcl);
46 auto& jrowIndexOp = *handler.makeIndexNode(indexJrowDcl);
47
48 std::vector<OperationNode<Base>*> localNodes(4);
49 localNodes[0] = &indexJrowDcl;
50 localNodes[1] = &indexIterationDcl;
51 localNodes[2] = &iterationIndexOp;
52 localNodes[3] = &jrowIndexOp;
53
54 size_t nonIndexdedEqSize = _funNoLoops != nullptr ? _funNoLoops->getOrigDependentIndexes().size() : 0;
55
56 std::vector<set<size_t> > noLoopEvalSparsity;
57 std::vector<map<size_t, set<size_t> > > noLoopEvalLocations; // tape equation -> original J -> locations
58 map<LoopModel<Base>*, std::vector<set<size_t> > > loopsEvalSparsities;
59 map<LoopModel<Base>*, std::vector<JacobianWithLoopsRowInfo> > loopEqInfo;
60
61 size_t nnz = _jacSparsity.rows.size();
62 std::vector<size_t> rows(nnz);
63 std::vector<size_t> cols(nnz);
64 std::vector<size_t> locations(nnz);
65
66 size_t p = 0;
67 for (const auto& itI : elements) {//loop dependents/equations
68 size_t i = itI.first;
69 const std::vector<size_t>& r = itI.second;
70
71 for (size_t e = 0; e < r.size(); e++) { // loop variables
72 rows[p] = i;
73 cols[p] = r[e];
74 locations[p] = e;
75 p++;
76 }
77 }
78 CPPADCG_ASSERT_UNKNOWN(p == nnz);
79
80 analyseSparseJacobianWithLoops(rows, cols, locations,
81 noLoopEvalSparsity, noLoopEvalLocations, loopsEvalSparsities, loopEqInfo);
82
83 std::vector<CGBase> x(n);
84 handler.makeVariables(x);
85 if (_x.size() > 0) {
86 for (size_t i = 0; i < n; i++) {
87 x[i].setValue(_x[i]);
88 }
89 }
90
91 CGBase py;
92 handler.makeVariable(py);
93 if (_x.size() > 0) {
94 py.setValue(Base(1.0));
95 }
96
97 /***********************************************************************
98 * generate the operation graph
99 **********************************************************************/
100
104 // temporaries (zero orders)
105 std::vector<CGBase> tmps;
106
107 // jacobian for temporaries
108 std::vector<map<size_t, CGBase> > dzDx(_funNoLoops != nullptr ? _funNoLoops->getTemporaryDependentCount() : 0);
109
110 /*******************************************************************
111 * equations NOT in loops
112 ******************************************************************/
113 if (_funNoLoops != nullptr) {
114 ADFun<CGBase>& fun = _funNoLoops->getTape();
115
119 std::vector<CGBase> depNL = _funNoLoops->getTape().Forward(0, x);
120
121 tmps.resize(depNL.size() - nonIndexdedEqSize);
122 for (size_t i = 0; i < tmps.size(); i++)
123 tmps[i] = depNL[nonIndexdedEqSize + i];
124
128 bool hasAtomics = isAtomicsUsed(); // TODO: improve this by checking only the current fun
129 std::vector<map<size_t, CGBase> > dydx = generateLoopRev1Jac(fun, _funNoLoops->getJacobianSparsity(), noLoopEvalSparsity, x, hasAtomics);
130
131 const std::vector<size_t>& dependentIndexes = _funNoLoops->getOrigDependentIndexes();
132 map<size_t, std::vector<CGBase> > jacNl; // by row
133
134 for (size_t inl = 0; inl < nonIndexdedEqSize; inl++) {
135 size_t i = dependentIndexes[inl];
136
137 // prepare space for the jacobian of the original equations
138 std::vector<CGBase>& row = jacNl[i];
139 row.resize(dydx[inl].size());
140
141 for (const auto& itjv : dydx[inl]) {
142 size_t j = itjv.first;
143 // (dy_i/dx_v) elements from equations outside loops
144 const set<size_t>& locations = noLoopEvalLocations[inl][j];
145
146 CPPADCG_ASSERT_UNKNOWN(locations.size() == 1); // one jacobian element should not be placed in several locations
147 size_t e = *locations.begin();
148
149 row[e] = itjv.second * py;
150
151 _nonLoopRev1Elements[i].insert(e);
152 }
153 }
154
155 // dz_k/dx_v (for temporary variable)
156 for (size_t inl = nonIndexdedEqSize; inl < dydx.size(); inl++) {
157 size_t k = inl - nonIndexdedEqSize;
158
159 for (const auto& itjv : dydx[inl]) {
160 size_t j = itjv.first;
161 dzDx[k][j] = itjv.second;
162 }
163 }
164
168 typename map<size_t, std::vector<CGBase> >::iterator itJ;
169 for (itJ = jacNl.begin(); itJ != jacNl.end(); ++itJ) {
170 createReverseOneWithLoopsNL(handler, itJ->first, 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 const std::vector<std::vector<LoopPosition> >& dependentIndexes = lModel.getDependentIndexes();
183 size_t nIterations = lModel.getIterationCount();
184
185 _cache.str("");
186 _cache << "model (reverse one, loop " << lModel.getLoopId() << ")";
187 std::string jobName = _cache.str();
188
192 startingJob("'" + jobName + "'", JobTimer::GRAPH);
193
194 std::vector<CGBase> indexedIndeps = createIndexedIndependents(handler, lModel, iterationIndexOp);
195 std::vector<CGBase> xl = createLoopIndependentVector(handler, lModel, indexedIndeps, x, tmps);
196
197 bool hasAtomics = isAtomicsUsed(); // TODO: improve this by checking only the current fun
198 std::vector<map<size_t, CGBase> > dyiDxtape = generateLoopRev1Jac(fun, lModel.getJacobianSparsity(), loopsEvalSparsities[&lModel], xl, hasAtomics);
199
200 finishedJob();
201
205 std::vector<std::pair<CGBase, IndexPattern*> > indexedLoopResults;
206 for (size_t tapeI = 0; tapeI < info.size(); tapeI++) {
207 const JacobianWithLoopsRowInfo& rowInfo = info[tapeI];
208
209 size_t maxRowEls = rowInfo.indexedPositions.size() + rowInfo.nonIndexedPositions.size();
210 if (maxRowEls == 0)
211 continue; // nothing to do (possibly an equation assigned to a constant value)
212
216 map<size_t, size_t> irow2It;
217 for (size_t it = 0; it < nIterations; it++) {
218 size_t i = dependentIndexes[tapeI][it].original;
219 if (i < _fun.Range()) // some equations are not present in all iteration
220 irow2It[i] = it;
221 }
222
223 std::unique_ptr<IndexPattern> itPattern(IndexPattern::detect(irow2It));
224 auto* iterationIndexPatternOp = handler.makeIndexAssignNode(indexIterationDcl, *itPattern.get(), jrowIndexOp);
225 iterationIndexOp.makeAssigmentDependent(*iterationIndexPatternOp);
226
230 std::set<size_t> allLocations;
231 indexedLoopResults.resize(maxRowEls);
232 size_t jacLE = 0;
233
234 std::vector<IfElseInfo<Base> > ifElses;
235
236 prepareSparseJacobianRowWithLoops(handler, lModel,
237 tapeI, rowInfo,
238 dyiDxtape, dzDx,
239 py,
240 iterationIndexOp, ifElses,
241 jacLE, indexedLoopResults, allLocations);
242 indexedLoopResults.resize(jacLE);
243
244 std::vector<CGBase> pxCustom(indexedLoopResults.size());
245
246 for (size_t i = 0; i < indexedLoopResults.size(); i++) {
247 const CGBase& val = indexedLoopResults[i].first;
248 IndexPattern* ip = indexedLoopResults[i].second;
249
250 pxCustom[i] = createLoopDependentFunctionResult(handler, i, val, ip, iterationIndexOp);
251 }
252
256 std::map<size_t, std::set<size_t> >& row2position = _loopRev1Groups[&lModel][tapeI];
257
258 for (size_t it = 0; it < nIterations; it++) {
259 size_t i = dependentIndexes[tapeI][it].original;
260 if (i < _fun.Range()) { // some equations are not present in all iteration
261 std::set<size_t> positions;
262
263 for (const auto& itc : rowInfo.indexedPositions) {
264 const std::vector<size_t>& positionsC = itc.second;
265 if (positionsC[it] != (std::numeric_limits<size_t>::max)()) // not all elements are requested for all iterations
266 positions.insert(positionsC[it]);
267 }
268 for (const auto& itc : rowInfo.nonIndexedPositions) {
269 const std::vector<size_t>& positionsC = itc.second;
270 if (positionsC[it] != (std::numeric_limits<size_t>::max)()) // not all elements are requested for all iterations
271 positions.insert(positionsC[it]);
272 }
273
274 if (!positions.empty())
275 row2position[i].swap(positions);
276 }
277 }
278
282 LanguageC<Base> langC(_baseTypeName);
283 langC.setFunctionIndexArgument(indexJrowDcl);
284 langC.setParameterPrecision(_parameterPrecision);
285
286 _cache.str("");
287 std::ostringstream code;
288 std::unique_ptr<VariableNameGenerator<Base> > nameGen(createVariableNameGenerator("dw"));
289 LangCDefaultHessianVarNameGenerator<Base> nameGenHess(nameGen.get(), "dy", n);
290
294 _cache.str("");
295 _cache << "model (reverse one, loop " << lModel.getLoopId() << ", group " << tapeI << ")";
296 string jobName = _cache.str();
297 handler.generateCode(code, langC, pxCustom, nameGenHess, _atomicFunctions, jobName);
298
299 _cache.str("");
300 generateFunctionNameLoopRev1(_cache, lModel, tapeI);
301 std::string functionName = _cache.str();
302
303 std::string argsDcl = langC.generateFunctionArgumentsDcl();
304
305 _cache.str("");
306 _cache << "#include <stdlib.h>\n"
307 "#include <math.h>\n"
308 "\n"
310 "\n"
311 "void " << functionName << "(" << argsDcl << ") {\n";
312 nameGenHess.customFunctionVariableDeclarations(_cache);
313 _cache << langC.generateIndependentVariableDeclaration() << "\n";
314 _cache << langC.generateDependentVariableDeclaration() << "\n";
315 _cache << langC.generateTemporaryVariableDeclaration(false, false,
317 handler.getExternalFuncMaxReverseOrder()) << "\n";
318 nameGenHess.prepareCustomFunctionVariables(_cache);
319
320 // code inside the loop
321 _cache << code.str();
322
323 nameGenHess.finalizeCustomFunctionVariables(_cache);
324 _cache << "}\n\n";
325
326 _sources[functionName + ".c"] = _cache.str();
327 _cache.str("");
328
332 if (tapeI + 1 < info.size() || &lModel != loopEqInfo.rbegin()->first) {
333 handler.resetNodes(); // uncolor nodes
334 }
335 }
336
337 }
338
342 string functionRev1 = _name + "_" + FUNCTION_SPARSE_REVERSE_ONE;
343 _sources[functionRev1 + ".c"] = generateGlobalForRevWithLoopsFunctionSource(elements,
344 _loopRev1Groups, _nonLoopRev1Elements,
345 functionRev1, _name, _baseTypeName, "dep",
346 generateFunctionNameLoopRev1);
350 _cache.str("");
351 generateSparsity1DSource2(_name + "_" + FUNCTION_REVERSE_ONE_SPARSITY, elements);
352 _sources[_name + "_" + FUNCTION_REVERSE_ONE_SPARSITY + ".c"] = _cache.str();
353 _cache.str("");
354}
355
356template<class Base>
358 size_t i,
359 std::vector<CG<Base> >& jacRow) {
360 size_t n = _fun.Domain();
361
362 _cache.str("");
363 _cache << "model (forward one, dep " << i << ") no loop";
364 const std::string jobName = _cache.str();
365
366 LanguageC<Base> langC(_baseTypeName);
367 langC.setMaxAssignmentsPerFunction(_maxAssignPerFunc, &_sources);
368 langC.setParameterPrecision(_parameterPrecision);
369 _cache.str("");
370 _cache << _name << "_" << FUNCTION_SPARSE_REVERSE_ONE << "_noloop_dep" << i;
371 langC.setGenerateFunction(_cache.str());
372
373 std::ostringstream code;
374 std::unique_ptr<VariableNameGenerator<Base> > nameGen(createVariableNameGenerator("dw"));
375 LangCDefaultHessianVarNameGenerator<Base> nameGenHess(nameGen.get(), "dy", n);
376
377 handler.generateCode(code, langC, jacRow, nameGenHess, _atomicFunctions, jobName);
378
379 handler.resetNodes();
380}
381
382template<class Base>
383std::vector<std::map<size_t, CG<Base> > > ModelCSourceGen<Base>::generateLoopRev1Jac(ADFun<CGBase>& fun,
384 const SparsitySetType& sparsity,
385 const SparsitySetType& evalSparsity,
386 const std::vector<CGBase>& x,
387 bool constainsAtomics) {
388 using namespace std;
389
390 size_t m = fun.Range();
391
392 std::vector<map<size_t, CGBase> > dyDx(m);
393
394 if (!constainsAtomics) {
395 std::vector<size_t> row, col;
396 generateSparsityIndexes(evalSparsity, row, col);
397
398 if (row.size() == 0)
399 return dyDx; // nothing to do
400
401 std::vector<CGBase> jacLoop(row.size());
402
403 CppAD::sparse_jacobian_work work; // temporary structure for CppAD
404 fun.SparseJacobianReverse(x, sparsity, row, col, jacLoop, work);
405
406 // organize results
407 for (size_t el = 0; el < jacLoop.size(); el++) {
408 size_t i = row[el];
409 size_t j = col[el];
410 dyDx[i][j] = jacLoop[el];
411 }
412
413 } else {
414
415 std::vector<CGBase> w(m);
416
417 for (size_t i = 0; i < m; i++) {
418 const set<size_t>& row = evalSparsity[i];
419
420 if (row.empty())
421 continue;
422
423 fun.Forward(0, x);
424
425 w[i] = Base(1);
426 std::vector<CGBase> dw = fun.Reverse(1, w);
427 CPPADCG_ASSERT_UNKNOWN(dw.size() == fun.Domain());
428 w[i] = Base(0);
429
430 map<size_t, CGBase>& dyIDx = dyDx[i];
431
432 for (size_t j : row) {
433 dyIDx[j] = dw[j];
434 }
435 }
436
437 }
438
439 return dyDx;
440}
441
442template<class Base>
443void ModelCSourceGen<Base>::generateFunctionNameLoopRev1(std::ostringstream& cache,
444 const LoopModel<Base>& loop,
445 size_t tapeI) {
446 generateFunctionNameLoopRev1(cache, _name, loop, tapeI);
447}
448
449template<class Base>
450void ModelCSourceGen<Base>::generateFunctionNameLoopRev1(std::ostringstream& cache,
451 const std::string& modelName,
452 const LoopModel<Base>& loop,
453 size_t tapeI) {
454 cache << modelName << "_" << FUNCTION_SPARSE_REVERSE_ONE <<
455 "_loop" << loop.getLoopId() << "_g" << tapeI;
456}
457
458} // END cg namespace
459} // END CppAD namespace
460
461#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 size_t getIterationCount() const
size_t getLoopId() const
ADFun< CGB > & getTape() const
const std::vector< std::vector< LoopPosition > > & getDependentIndexes() const
virtual void prepareSparseReverseOneWithLoops(const std::map< size_t, std::vector< size_t > > &elements)