CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
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 
18 namespace CppAD {
19 namespace cg {
20 
21 /***************************************************************************
22  * Methods related with loop insertion into the operation graph
23  **************************************************************************/
24 
30 template<class Base>
31 void 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 
356 template<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 
382 template<class Base>
383 std::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 
442 template<class Base>
443 void ModelCSourceGen<Base>::generateFunctionNameLoopRev1(std::ostringstream& cache,
444  const LoopModel<Base>& loop,
445  size_t tapeI) {
446  generateFunctionNameLoopRev1(cache, _name, loop, tapeI);
447 }
448 
449 template<class Base>
450 void 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
STL namespace.
void setValue(const Base &val)
Definition: variable.hpp:54
size_t getLoopId() const
Definition: loop_model.hpp:236
ADFun< CGB > & getTape() const
Definition: loop_model.hpp:263
const std::vector< int > & getExternalFuncMaxForwardOrder() const
static IndexPattern * detect(const VectorSizeT &x2y)
void makeVariables(VectorCG &variables)
virtual void setMaxAssignmentsPerFunction(size_t maxAssignmentsPerFunction, std::map< std::string, std::string > *sources)
Definition: language_c.hpp:252
void makeVariable(AD< CGB > &variable)
virtual void setParameterPrecision(size_t p)
Definition: language_c.hpp:237
const size_t getIterationCount() const
Definition: loop_model.hpp:254
void setZeroDependents(bool zeroDependents)
const std::vector< std::vector< LoopPosition > > & getDependentIndexes() const
Definition: loop_model.hpp:291
const std::vector< int > & getExternalFuncMaxReverseOrder() const
virtual void prepareSparseReverseOneWithLoops(const std::map< size_t, std::vector< size_t > > &elements)
virtual void generateCode(std::ostream &out, Language< Base > &lang, CppAD::vector< CGB > &dependent, VariableNameGenerator< Base > &nameGen, const std::string &jobName="source")