CppADCodeGen  2.3.0
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.setMaxAssigmentsPerFunction(_maxAssignPerFunc, &_sources);
131  langC.setParameterPrecision(_parameterPrecision);
132  _cache.str("");
133  _cache << _name << "_" << FUNCTION_SPARSE_REVERSE_TWO << "_indep" << j;
134  langC.setGenerateFunction(_cache.str());
135 
136  std::ostringstream code;
137  std::unique_ptr<VariableNameGenerator<Base> > nameGen(createVariableNameGenerator("px"));
138  LangCDefaultReverse2VarNameGenerator<Base> nameGenRev2(nameGen.get(), n, 1);
139 
140  handler.generateCode(code, langC, pxCustom, nameGenRev2, _atomicFunctions, subJobName);
141  }
142 }
143 
144 template<class Base>
145 void ModelCSourceGen<Base>::generateSparseReverseTwoSourcesNoAtomics(const std::map<size_t, std::vector<size_t> >& elements,
146  const std::vector<size_t>& evalRows,
147  const std::vector<size_t>& evalCols) {
148  using std::vector;
149 
150  const size_t m = _fun.Range();
151  const size_t n = _fun.Domain();
152 
153  // save compressed positions
154  std::map<size_t, std::map<size_t, size_t> > positions;
155  for (const auto& itJ1 : elements) {
156  size_t j1 = itJ1.first;
157  const std::vector<size_t>& row = itJ1.second;
158  std::map<size_t, size_t>& pos = positions[j1];
159 
160  for (size_t e = 0; e < row.size(); e++) {
161  size_t j2 = row[e];
162  pos[j2] = e;
163  }
164  }
165 
166  // we can use a new handler to reduce memory usage
167  CodeHandler<Base> handler;
168  handler.setJobTimer(_jobTimer);
169 
170  vector<CGBase> tx0(n);
171  handler.makeVariables(tx0);
172  if (_x.size() > 0) {
173  for (size_t i = 0; i < n; i++) {
174  tx0[i].setValue(_x[i]);
175  }
176  }
177 
178  CGBase tx1;
179  handler.makeVariable(tx1);
180  if (_x.size() > 0) {
181  tx1.setValue(Base(1.0));
182  }
183 
184  vector<CGBase> py(m); // (k+1)*m is not used because we are not interested in all values
185  handler.makeVariables(py);
186  if (_x.size() > 0) {
187  for (size_t i = 0; i < m; i++) {
188  py[i].setValue(Base(1.0));
189  }
190  }
191 
192  vector<CGBase> hessFlat(evalRows.size());
193 
194  CppAD::sparse_hessian_work work; // temporary structure for CPPAD
195  // "cppad.symmetric" may have missing values for functions using atomic
196  // functions which only provide half of the elements, but there is none here
197  work.color_method = "cppad.symmetric";
198  _fun.SparseHessian(tx0, py, _hessSparsity.sparsity, evalRows, evalCols, hessFlat, work);
199 
200  std::map<size_t, vector<CGBase> > hess;
201  for (const auto& itJ1 : elements) {
202  size_t j1 = itJ1.first;
203  hess[j1].resize(itJ1.second.size());
204  }
205 
206  // organize hessian elements
207  for (size_t el = 0; el < evalRows.size(); el++) {
208  size_t j1 = evalRows[el];
209  size_t j2 = evalCols[el];
210  size_t e = positions[j1][j2];
211 
212  hess[j1][e] = hessFlat[el];
213  }
214 
218  for (const auto& it : hess) {
219  size_t j = it.first;
220  const vector<CGBase>& row = it.second;
221 
222  _cache.str("");
223  _cache << "model (reverse two, indep " << j << ")";
224  const std::string subJobName = _cache.str();
225 
226  vector<CGBase> pxCustom(row.size());
227  for (size_t e = 0; e < row.size(); e++) {
228  pxCustom[e] = row[e] * tx1;
229  }
230 
231  LanguageC<Base> langC(_baseTypeName);
232  langC.setMaxAssigmentsPerFunction(_maxAssignPerFunc, &_sources);
233  langC.setParameterPrecision(_parameterPrecision);
234  _cache.str("");
235  _cache << _name << "_" << FUNCTION_SPARSE_REVERSE_TWO << "_indep" << j;
236  langC.setGenerateFunction(_cache.str());
237 
238  std::ostringstream code;
239  std::unique_ptr<VariableNameGenerator<Base> > nameGen(createVariableNameGenerator("px"));
240  LangCDefaultReverse2VarNameGenerator<Base> nameGenRev2(nameGen.get(), n, 1);
241 
242  handler.generateCode(code, langC, pxCustom, nameGenRev2, _atomicFunctions, subJobName);
243  }
244 }
245 
246 template<class Base>
248  size_t m = _fun.Range();
249  size_t n = _fun.Domain();
250 
251  _cache.str("");
252  _cache << _name << "_" << FUNCTION_REVERSE_TWO;
253  std::string model_function(_cache.str());
254  _cache.str("");
255 
256  LanguageC<Base> langC(_baseTypeName);
257  std::string argsDcl = langC.generateDefaultFunctionArgumentsDcl();
258  std::string args = langC.generateDefaultFunctionArguments();
259 
260  _cache << "#include <stdlib.h>\n"
262  "\n"
263  "int " << _name << "_" << FUNCTION_SPARSE_REVERSE_TWO << "(unsigned long pos, " << argsDcl << ");\n"
264  "void " << _name << "_" << FUNCTION_REVERSE_TWO_SPARSITY << "(unsigned long pos, unsigned long const** elements, unsigned long* nnz);\n"
265  "\n";
266  LanguageC<Base>::printFunctionDeclaration(_cache, "int", model_function, {_baseTypeName + " const tx[]",
267  _baseTypeName + " const ty[]",
268  _baseTypeName + " px[]",
269  _baseTypeName + " const py[]",
270  langC.generateArgumentAtomicDcl()});
271  _cache << " {\n"
272  " unsigned long ej, ePos, i, j, nnz, nnzMax;\n"
273  " unsigned long const* pos;\n"
274  " unsigned long* txPos;\n"
275  " unsigned long* txPosTmp;\n"
276  " unsigned long nnzTx;\n"
277  " " << _baseTypeName << " const * in[3];\n"
278  " " << _baseTypeName << "* out[1];\n"
279  " " << _baseTypeName << " x[" << n << "];\n"
280  " " << _baseTypeName << " w[" << m << "];\n"
281  " " << _baseTypeName << "* compressed;\n"
282  " int nonZeroW;\n"
283  " int ret;\n"
284  "\n"
285  " nonZeroW = 0;\n"
286  " for (i = 0; i < " << m << "; i++) {\n"
287  " if (py[i * 2] != 0.0) {\n"
288  " return 1; // error\n"
289  " }\n"
290  " w[i] = py[i * 2 + 1];\n"
291  " if(w[i] != 0.0) nonZeroW++;\n"
292  " }\n"
293  "\n"
294  " for (j = 0; j < " << n << "; j++) {\n"
295  " px[j * 2] = 0;\n"
296  " }\n"
297  "\n"
298  " if (nonZeroW == 0)\n"
299  " return 0; //nothing to do\n"
300  "\n"
301  " txPos = 0;\n"
302  " nnzTx = 0;\n"
303  " nnzMax = 0;\n"
304  " for (j = 0; j < " << n << "; j++) {\n"
305  " if (tx[j * 2 + 1] != 0.0) {\n"
306  " " << _name << "_" << FUNCTION_REVERSE_TWO_SPARSITY << "(j, &pos, &nnz);\n"
307  " if (nnz > nnzMax)\n"
308  " nnzMax = nnz;\n"
309  " else if (nnz == 0)\n"
310  " continue;\n"
311  " nnzTx++;\n"
312  " txPosTmp = (unsigned long*) realloc(txPos, nnzTx * sizeof(unsigned long));\n"
313  " if (txPosTmp != NULL) {\n"
314  " txPos = txPosTmp;\n"
315  " } else {\n"
316  " free(txPos);\n"
317  " return -1; // failure to allocate memory\n"
318  " }\n"
319  " txPos[nnzTx - 1] = j;\n"
320  " }\n"
321  " }\n"
322  "\n"
323  " if (nnzTx == 0) {\n"
324  " free(txPos);\n"
325  " return 0; // nothing to do\n"
326  " }\n"
327  "\n"
328  " for (j = 0; j < " << n << "; j++)\n"
329  " x[j] = tx[j * 2];\n"
330  "\n"
331  " compressed = (" << _baseTypeName << "*) malloc(nnzMax * sizeof(" << _baseTypeName << "));\n"
332  "\n"
333  " for (ej = 0; ej < nnzTx; ej++) {\n"
334  " j = txPos[ej];\n"
335  " " << _name << "_" << FUNCTION_REVERSE_TWO_SPARSITY << "(j, &pos, &nnz);\n"
336  "\n"
337  " in[0] = x;\n"
338  " in[1] = &tx[j * 2 + 1];\n"
339  " in[2] = w;\n"
340  " out[0] = compressed;\n";
341  if (!_loopTapes.empty()) {
342  _cache << " for (ePos = 0; ePos < nnz; ePos++)\n"
343  " compressed[ePos] = 0;\n"
344  "\n";
345  }
346  _cache << " ret = " << _name << "_" << FUNCTION_SPARSE_REVERSE_TWO << "(j, " << args << ");\n"
347  "\n"
348  " if (ret != 0) {\n"
349  " free(compressed);\n"
350  " free(txPos);\n"
351  " return ret;\n"
352  " }\n"
353  "\n"
354  " for (ePos = 0; ePos < nnz; ePos++) {\n"
355  " px[pos[ePos] * 2] += compressed[ePos];\n"
356  " }\n"
357  "\n"
358  " }\n"
359  " free(compressed);\n"
360  " free(txPos);\n"
361  " return 0;\n"
362  "};\n";
363 
364  _sources[model_function + ".c"] = _cache.str();
365  _cache.str("");
366 }
367 
368 } // END cg namespace
369 } // END CppAD namespace
370 
371 #endif
void setValue(const Base &val)
Definition: variable.hpp:54
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:489
void makeVariables(VectorCG &variables)
void makeVariable(AD< CGB > &variable)
virtual void setParameterPrecision(size_t p)
Definition: language_c.hpp:219
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")