CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
model_c_source_gen_rev2.hpp
1 #ifndef CPPAD_CG_MODEL_C_SOURCE_GEN_REV2_INCLUDED
2 #define CPPAD_CG_MODEL_C_SOURCE_GEN_REV2_INCLUDED
3 /* --------------------------------------------------------------------------
4  * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5  * Copyright (C) 2012 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 template<class Base>
23 
24  determineHessianSparsity();
25 
30  std::vector<size_t> evalRows, evalCols;
31  determineSecondOrderElements4Eval(evalRows, evalCols);
32 
33  // elements[var]{vars}
34  std::map<size_t, std::vector<size_t> > elements;
35  for (size_t e = 0; e < evalCols.size(); e++) {
36  elements[evalRows[e]].push_back(evalCols[e]);
37  }
38 
39  if (!_loopTapes.empty()) {
43  prepareSparseReverseTwoWithLoops(elements);
44  return;
45  }
46 
47  if (!evalRows.empty()) {
48 
49  startingJob("'model (reverse two)'", JobTimer::SOURCE_GENERATION);
50 
51  if (isAtomicsUsed()) {
52  generateSparseReverseTwoSourcesWithAtomics(elements);
53  } else {
54  generateSparseReverseTwoSourcesNoAtomics(elements, evalRows, evalCols);
55  }
56 
57  finishedJob();
58 
59  _cache.str("");
60  }
61 
62  generateGlobalDirectionalFunctionSource(FUNCTION_SPARSE_REVERSE_TWO,
63  "indep",
64  FUNCTION_REVERSE_TWO_SPARSITY,
65  elements);
66 }
67 
68 template<class Base>
69 void ModelCSourceGen<Base>::generateSparseReverseTwoSourcesWithAtomics(const std::map<size_t, std::vector<size_t> >& elements) {
70  using std::vector;
71 
72  const size_t m = _fun.Range();
73  const size_t n = _fun.Domain();
74  //const size_t k = 1;
75  const size_t p = 2;
76 
77  vector<CGBase> tx1v(n);
78 
79  for (const auto& it : elements) {
80  size_t j = it.first;
81  const std::vector<size_t>& cols = it.second;
82 
83  _cache.str("");
84  _cache << "model (reverse two, indep " << j << ")";
85  const std::string subJobName = _cache.str();
86 
87  startingJob("'" + subJobName + "'", JobTimer::GRAPH);
88 
89  CodeHandler<Base> handler;
90  handler.setJobTimer(_jobTimer);
91 
92  vector<CGBase> tx0(n);
93  handler.makeVariables(tx0);
94  if (_x.size() > 0) {
95  for (size_t i = 0; i < n; i++) {
96  tx0[i].setValue(_x[i]);
97  }
98  }
99 
100  CGBase tx1;
101  handler.makeVariable(tx1);
102  if (_x.size() > 0) {
103  tx1.setValue(Base(1.0));
104  }
105 
106  vector<CGBase> py(m); // (k+1)*m is not used because we are not interested in all values
107  handler.makeVariables(py);
108  if (_x.size() > 0) {
109  for (size_t i = 0; i < m; i++) {
110  py[i].setValue(Base(1.0));
111  }
112  }
113 
114  _fun.Forward(0, tx0);
115 
116  tx1v[j] = tx1;
117  _fun.Forward(1, tx1v);
118  tx1v[j] = Base(0);
119  vector<CGBase> px = _fun.Reverse(2, py);
120  CPPADCG_ASSERT_UNKNOWN(px.size() == 2 * n);
121 
122  vector<CGBase> pxCustom;
123  for (size_t jj : cols) {
124  pxCustom.push_back(px[jj * p + 1]); // not interested in all values
125  }
126 
127  finishedJob();
128 
129  LanguageC<Base> langC(_baseTypeName);
130  langC.setMaxAssignmentsPerFunction(_maxAssignPerFunc, &_sources);
131  langC.setMaxOperationsPerAssignment(_maxOperationsPerAssignment);
132  langC.setParameterPrecision(_parameterPrecision);
133  _cache.str("");
134  _cache << _name << "_" << FUNCTION_SPARSE_REVERSE_TWO << "_indep" << j;
135  langC.setGenerateFunction(_cache.str());
136 
137  std::ostringstream code;
138  std::unique_ptr<VariableNameGenerator<Base> > nameGen(createVariableNameGenerator("px"));
139  LangCDefaultReverse2VarNameGenerator<Base> nameGenRev2(nameGen.get(), n, 1);
140 
141  handler.generateCode(code, langC, pxCustom, nameGenRev2, _atomicFunctions, subJobName);
142  }
143 }
144 
145 template<class Base>
146 void ModelCSourceGen<Base>::generateSparseReverseTwoSourcesNoAtomics(const std::map<size_t, std::vector<size_t> >& elements,
147  const std::vector<size_t>& evalRows,
148  const std::vector<size_t>& evalCols) {
149  using std::vector;
150 
151  const size_t m = _fun.Range();
152  const size_t n = _fun.Domain();
153 
154  // save compressed positions
155  std::map<size_t, std::map<size_t, size_t> > positions;
156  for (const auto& itJ1 : elements) {
157  size_t j1 = itJ1.first;
158  const std::vector<size_t>& row = itJ1.second;
159  std::map<size_t, size_t>& pos = positions[j1];
160 
161  for (size_t e = 0; e < row.size(); e++) {
162  size_t j2 = row[e];
163  pos[j2] = e;
164  }
165  }
166 
167  // we can use a new handler to reduce memory usage
168  CodeHandler<Base> handler;
169  handler.setJobTimer(_jobTimer);
170 
171  vector<CGBase> tx0(n);
172  handler.makeVariables(tx0);
173  if (_x.size() > 0) {
174  for (size_t i = 0; i < n; i++) {
175  tx0[i].setValue(_x[i]);
176  }
177  }
178 
179  CGBase tx1;
180  handler.makeVariable(tx1);
181  if (_x.size() > 0) {
182  tx1.setValue(Base(1.0));
183  }
184 
185  vector<CGBase> py(m); // (k+1)*m is not used because we are not interested in all values
186  handler.makeVariables(py);
187  if (_x.size() > 0) {
188  for (size_t i = 0; i < m; i++) {
189  py[i].setValue(Base(1.0));
190  }
191  }
192 
193  vector<CGBase> hessFlat(evalRows.size());
194 
195  CppAD::sparse_hessian_work work; // temporary structure for CPPAD
196  // "cppad.symmetric" may have missing values for functions using atomic
197  // functions which only provide half of the elements, but there is none here
198  work.color_method = "cppad.symmetric";
199  _fun.SparseHessian(tx0, py, _hessSparsity.sparsity, evalRows, evalCols, hessFlat, work);
200 
201  std::map<size_t, vector<CGBase> > hess;
202  for (const auto& itJ1 : elements) {
203  size_t j1 = itJ1.first;
204  hess[j1].resize(itJ1.second.size());
205  }
206 
207  // organize hessian elements
208  for (size_t el = 0; el < evalRows.size(); el++) {
209  size_t j1 = evalRows[el];
210  size_t j2 = evalCols[el];
211  size_t e = positions[j1][j2];
212 
213  hess[j1][e] = hessFlat[el];
214  }
215 
219  for (const auto& it : hess) {
220  size_t j = it.first;
221  const vector<CGBase>& row = it.second;
222 
223  _cache.str("");
224  _cache << "model (reverse two, indep " << j << ")";
225  const std::string subJobName = _cache.str();
226 
227  vector<CGBase> pxCustom(row.size());
228  for (size_t e = 0; e < row.size(); e++) {
229  pxCustom[e] = row[e] * tx1;
230  }
231 
232  LanguageC<Base> langC(_baseTypeName);
233  langC.setMaxAssignmentsPerFunction(_maxAssignPerFunc, &_sources);
234  langC.setMaxOperationsPerAssignment(_maxOperationsPerAssignment);
235  langC.setParameterPrecision(_parameterPrecision);
236  _cache.str("");
237  _cache << _name << "_" << FUNCTION_SPARSE_REVERSE_TWO << "_indep" << j;
238  langC.setGenerateFunction(_cache.str());
239 
240  std::ostringstream code;
241  std::unique_ptr<VariableNameGenerator<Base> > nameGen(createVariableNameGenerator("px"));
242  LangCDefaultReverse2VarNameGenerator<Base> nameGenRev2(nameGen.get(), n, 1);
243 
244  handler.generateCode(code, langC, pxCustom, nameGenRev2, _atomicFunctions, subJobName);
245  }
246 }
247 
248 template<class Base>
250  size_t m = _fun.Range();
251  size_t n = _fun.Domain();
252 
253  _cache.str("");
254  _cache << _name << "_" << FUNCTION_REVERSE_TWO;
255  std::string model_function(_cache.str());
256  _cache.str("");
257 
258  LanguageC<Base> langC(_baseTypeName);
259  std::string argsDcl = langC.generateDefaultFunctionArgumentsDcl();
260  std::string args = langC.generateDefaultFunctionArguments();
261 
262  _cache << "#include <stdlib.h>\n"
264  "\n"
265  "int " << _name << "_" << FUNCTION_SPARSE_REVERSE_TWO << "(unsigned long pos, " << argsDcl << ");\n"
266  "void " << _name << "_" << FUNCTION_REVERSE_TWO_SPARSITY << "(unsigned long pos, unsigned long const** elements, unsigned long* nnz);\n"
267  "\n";
268  LanguageC<Base>::printFunctionDeclaration(_cache, "int", model_function, {_baseTypeName + " const tx[]",
269  _baseTypeName + " const ty[]",
270  _baseTypeName + " px[]",
271  _baseTypeName + " const py[]",
272  langC.generateArgumentAtomicDcl()});
273  _cache << " {\n"
274  " unsigned long ej, ePos, i, j, nnz, nnzMax;\n"
275  " unsigned long const* pos;\n"
276  " unsigned long* txPos;\n"
277  " unsigned long* txPosTmp;\n"
278  " unsigned long nnzTx;\n"
279  " " << _baseTypeName << " const * in[3];\n"
280  " " << _baseTypeName << "* out[1];\n"
281  " " << _baseTypeName << " x[" << n << "];\n"
282  " " << _baseTypeName << " w[" << m << "];\n"
283  " " << _baseTypeName << "* compressed;\n"
284  " int nonZeroW;\n"
285  " int ret;\n"
286  "\n"
287  " nonZeroW = 0;\n"
288  " for (i = 0; i < " << m << "; i++) {\n"
289  " if (py[i * 2] != 0.0) {\n"
290  " return 1; // error\n"
291  " }\n"
292  " w[i] = py[i * 2 + 1];\n"
293  " if(w[i] != 0.0) nonZeroW++;\n"
294  " }\n"
295  "\n"
296  " for (j = 0; j < " << n << "; j++) {\n"
297  " px[j * 2] = 0;\n"
298  " }\n"
299  "\n"
300  " if (nonZeroW == 0)\n"
301  " return 0; //nothing to do\n"
302  "\n"
303  " txPos = 0;\n"
304  " nnzTx = 0;\n"
305  " nnzMax = 0;\n"
306  " for (j = 0; j < " << n << "; j++) {\n"
307  " if (tx[j * 2 + 1] != 0.0) {\n"
308  " " << _name << "_" << FUNCTION_REVERSE_TWO_SPARSITY << "(j, &pos, &nnz);\n"
309  " if (nnz > nnzMax)\n"
310  " nnzMax = nnz;\n"
311  " else if (nnz == 0)\n"
312  " continue;\n"
313  " nnzTx++;\n"
314  " txPosTmp = (unsigned long*) realloc(txPos, nnzTx * sizeof(unsigned long));\n"
315  " if (txPosTmp != NULL) {\n"
316  " txPos = txPosTmp;\n"
317  " } else {\n"
318  " free(txPos);\n"
319  " return -1; // failure to allocate memory\n"
320  " }\n"
321  " txPos[nnzTx - 1] = j;\n"
322  " }\n"
323  " }\n"
324  "\n"
325  " if (nnzTx == 0) {\n"
326  " free(txPos);\n"
327  " return 0; // nothing to do\n"
328  " }\n"
329  "\n"
330  " for (j = 0; j < " << n << "; j++)\n"
331  " x[j] = tx[j * 2];\n"
332  "\n"
333  " compressed = (" << _baseTypeName << "*) malloc(nnzMax * sizeof(" << _baseTypeName << "));\n"
334  "\n"
335  " for (ej = 0; ej < nnzTx; ej++) {\n"
336  " j = txPos[ej];\n"
337  " " << _name << "_" << FUNCTION_REVERSE_TWO_SPARSITY << "(j, &pos, &nnz);\n"
338  "\n"
339  " in[0] = x;\n"
340  " in[1] = &tx[j * 2 + 1];\n"
341  " in[2] = w;\n"
342  " out[0] = compressed;\n";
343  if (!_loopTapes.empty()) {
344  _cache << " for (ePos = 0; ePos < nnz; ePos++)\n"
345  " compressed[ePos] = 0;\n"
346  "\n";
347  }
348  _cache << " ret = " << _name << "_" << FUNCTION_SPARSE_REVERSE_TWO << "(j, " << args << ");\n"
349  "\n"
350  " if (ret != 0) {\n"
351  " free(compressed);\n"
352  " free(txPos);\n"
353  " return ret;\n"
354  " }\n"
355  "\n"
356  " for (ePos = 0; ePos < nnz; ePos++) {\n"
357  " px[pos[ePos] * 2] += compressed[ePos];\n"
358  " }\n"
359  "\n"
360  " }\n"
361  " free(compressed);\n"
362  " free(txPos);\n"
363  " return 0;\n"
364  "};\n";
365 
366  _sources[model_function + ".c"] = _cache.str();
367  _cache.str("");
368 }
369 
370 } // END cg namespace
371 } // END CppAD namespace
372 
373 #endif
void setValue(const Base &val)
Definition: variable.hpp:54
void setMaxOperationsPerAssignment(size_t maxOperationsPerAssignment)
Definition: language_c.hpp:273
static void printFunctionDeclaration(std::ostringstream &out, const std::string &returnType, const std::string &functionName, const std::vector< std::string > &arguments, const std::vector< std::string > &arguments2={})
Definition: language_c.hpp:538
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
virtual void generateSparseReverseTwoSourcesNoAtomics(const std::map< size_t, std::vector< size_t > > &elements, const std::vector< size_t > &evalRows, const std::vector< size_t > &evalCols)
virtual void generateCode(std::ostream &out, Language< Base > &lang, CppAD::vector< CGB > &dependent, VariableNameGenerator< Base > &nameGen, const std::string &jobName="source")