CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
evaluator_cg.hpp
1 #ifndef CPPAD_CG_EVALUATOR_CG_INCLUDED
2 #define CPPAD_CG_EVALUATOR_CG_INCLUDED
3 /* --------------------------------------------------------------------------
4  * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5  * Copyright (C) 2016 Ciengis
6  * Copyright (C) 2020 Joao Leal
7  *
8  * CppADCodeGen is distributed under multiple licenses:
9  *
10  * - Eclipse Public License Version 1.0 (EPL1), and
11  * - GNU General Public License Version 3 (GPL3).
12  *
13  * EPL1 terms and conditions can be found in the file "epl-v10.txt", while
14  * terms and conditions for the GPL3 can be found in the file "gpl3.txt".
15  * ----------------------------------------------------------------------------
16  * Author: Joao Leal
17  */
18 
19 namespace CppAD {
20 namespace cg {
21 
26 template<class ScalarIn, class ScalarOut, class FinalEvaluatorType>
27 class EvaluatorCG : public EvaluatorOperations<ScalarIn, ScalarOut, CG<ScalarOut>, FinalEvaluatorType> {
32  friend EvaluatorBase<ScalarIn, ScalarOut, CG<ScalarOut>, FinalEvaluatorType>;
34 public:
35  using ActiveIn = CG<ScalarIn>;
36  using ActiveOut = CG<ScalarOut>;
39  using ArgIn = Argument<ScalarIn>;
41 protected:
43 protected:
51  std::map<const NodeIn*, std::vector<ScalarOut*>> atomicEvalResults_;
57  using EvaluatorBase<ScalarIn, ScalarOut, CG<ScalarOut>, FinalEvaluatorType>::evals_;
58 public:
59 
60  inline EvaluatorCG(CodeHandler<ScalarIn>& handler) :
61  Super(handler),
62  outHandler_(nullptr),
63  printOutPriOperations_(true) {
64  }
65 
70  inline void setPrintOutPrintOperations(bool print) {
71  printOutPriOperations_ = print;
72  }
73 
78  inline bool isPrintOutPrintOperations() const {
80  }
81 
82 protected:
83 
88  inline void analyzeOutIndeps(const ActiveOut* indep,
89  size_t n) {
90  CPPAD_ASSERT_KNOWN(indep != nullptr || n == 0, "null array with a non-zero size");
91  outHandler_ = findHandler(ArrayView<const ActiveOut>(indep, n));
92  }
93 
98  inline void clear() {
99  Super::clear();
100 
101  for (const auto& it : atomicEvalResults_) {
102  for (const ScalarOut* e : it.second) {
103  delete e;
104  }
105  }
106  atomicEvalResults_.clear();
107  }
108 
109 
114  void processActiveOut(const NodeIn& node,
115  ActiveOut& a) {
116  if (node.getName() != nullptr) {
117  if (a.getOperationNode() != nullptr) {
118  a.getOperationNode()->setName(*node.getName());
119  }
120  }
121  }
122 
127  inline ActiveOut evalPrint(const NodeIn& node) {
128  const std::vector<ArgIn>& args = node.getArguments();
129  CPPADCG_ASSERT_KNOWN(args.size() == 1, "Invalid number of arguments for print()")
130  ActiveOut out(this->evalArg(args, 0));
131 
132  if (printOutPriOperations_) {
133  const auto& nodePri = static_cast<const PrintOperationNode<ScalarIn>&>(node);
134  std::cout << nodePri.getBeforeString() << out << nodePri.getAfterString();
135  }
136 
137  if (out.getOperationNode() != nullptr) {
138  const auto& nodePri = static_cast<const PrintOperationNode<ScalarIn>&>(node);
139  ActiveOut out2(*outHandler_->makePrintNode(nodePri.getBeforeString(), *out.getOperationNode(), nodePri.getAfterString()));
140  if (out.isValueDefined())
141  out2.setValue(out.getValue());
142  return out2;
143  }
144 
145  return out;
146  }
147 
152  void evalAtomicOperation(const NodeIn& node) {
153  CGOpCode op = node.getOperationType();
154  CPPADCG_ASSERT_KNOWN(op == CGOpCode::AtomicForward || op == CGOpCode::AtomicReverse,
155  "Invalid operation type")
156 
157  // check if this node was previously determined
158  if (evals_[node] != nullptr) {
159  return; // *evals_[node];
160  }
161 
162  const std::vector<size_t>& info = node.getInfo();
163  const std::vector<Argument<ScalarIn> >& inArgs = node.getArguments();
164 
165  CPPADCG_ASSERT_KNOWN(info.size() == 3, "Invalid number of information data for atomic operation")
166  size_t p = info[2];
167  size_t p1 = p + 1;
168 
169  CPPADCG_ASSERT_KNOWN(inArgs.size() == 2 * p1, "Invalid number of information data for atomic operation")
170 
171  if (outHandler_ == nullptr) {
172  throw CGException("Evaluator is unable to determine the new CodeHandler for an atomic operation");
173  }
174 
175  std::vector<Argument<ScalarOut> > outArgs(inArgs.size());
176 
177  std::vector<std::vector<ScalarOut>> outVals(inArgs.size());
178  bool valuesDefined = true;
179  bool allParameters = true;
180 
181  for (size_t i = 0; i < inArgs.size(); i++) {
182  auto* a = inArgs[i].getOperation();
183  CPPADCG_ASSERT_KNOWN(a != nullptr, "Invalid argument for atomic operation")
184 
185  outArgs[i] = asArgument(makeArray(*a, outVals[i], valuesDefined, allParameters));
186  }
187 
188  this->saveEvaluation(node, ActiveOut(*outHandler_->makeNode(op, info, outArgs)));
189 
190  if (valuesDefined) {
191  const std::map<size_t, CGAbstractAtomicFun<ScalarIn>*>& afun = this->handler_.getAtomicFunctions();
192  size_t id = info[0];
193  size_t q = info[1];
194  if (op == CGOpCode::AtomicForward) {
195  auto itAFun = afun.find(id);
196  if (itAFun == afun.end()) {
197  if (allParameters)
198  throw CGException("Atomic function for ID ", id, " is not defined in evaluator");
199  else
200  return; // the atomic function is not available but it is not essential
201  }
202  auto& atomic = *itAFun->second;
203 
204  CppAD::vector<bool> vx, vy;
205  CppAD::vector<ActiveOut> tx(outVals[0].size()), ty(outVals[1].size());
206  for (size_t i = 0; i < tx.size(); ++i)
207  tx[i] = ActiveIn(outVals[0][i]);
208  for (size_t i = 0; i < ty.size(); ++i)
209  ty[i] = ActiveIn(outVals[1][i]);
210 
211  atomic.forward(q, p, vx, vy, tx, ty);
212 
213  std::vector<ScalarOut*>& yOut = atomicEvalResults_[&node];
214  assert(yOut.empty());
215  yOut.resize(ty.size());
216  for (size_t i = 0; i < ty.size(); ++i) {
217  if (ty[i].isValueDefined())
218  yOut[i] = new ScalarOut(ty[i].getValue());
219  }
220  }
221  }
222 
223  }
224 
229  inline ActiveOut evalArrayElement(const NodeIn& node) {
230  // check if this node was previously determined
231  if (evals_[node] != nullptr) {
232  return *evals_[node];
233  }
234 
235  const std::vector<ArgIn>& args = node.getArguments();
236  const std::vector<size_t>& info = node.getInfo();
237  CPPADCG_ASSERT_KNOWN(args.size() == 2, "Invalid number of arguments for array element")
238  CPPADCG_ASSERT_KNOWN(args[0].getOperation() != nullptr, "Invalid argument for array element")
239  CPPADCG_ASSERT_KNOWN(args[1].getOperation() != nullptr, "Invalid argument for array element")
240  CPPADCG_ASSERT_KNOWN(info.size() == 1, "Invalid number of information data for array element")
241  size_t index = info[0];
242 
243  ArgOut arrayArg = asArgument(makeArray(*args[0].getOperation()));
244 
245  auto& thisOps = static_cast<FinalEvaluatorType&>(*this);
246  const NodeIn& atomicNode = *args[1].getOperation();
247  thisOps.evalAtomicOperation(atomicNode); // atomic operation
248  ArgOut atomicArg = *evals_[atomicNode]->getOperationNode();
249 
250  ActiveOut out(*outHandler_->makeNode(CGOpCode::ArrayElement, {index}, {arrayArg, atomicArg}));
251 
252  auto it = atomicEvalResults_.find(&atomicNode);
253  if (it != atomicEvalResults_.end()) {
254  const std::vector<ScalarOut*>& yOut = it->second;
255  if (index < yOut.size() && yOut[index] != nullptr)
256  out.setValue(*yOut[index]);
257  }
258 
259  return out;
260  }
261 
262  inline ActiveOut makeArray(const NodeIn& node) {
263  if (node.getOperationType() == CGOpCode::ArrayCreation) {
264  return makeDenseArray(node);
265  } else {
266  return makeSparseArray(node);
267  }
268  }
269 
270  inline ActiveOut makeArray(const NodeIn& node,
271  std::vector<ScalarOut>& values,
272  bool& valuesDefined,
273  bool& allParameters) {
274  const std::vector<ActiveOut>* arrayActiveOut;
275  ActiveOut result;
276 
277  if (node.getOperationType() == CGOpCode::ArrayCreation) {
278  result = makeDenseArray(node);
279  arrayActiveOut = this->evalsArrays_[node.getHandlerPosition()];
280  } else {
281  result = makeSparseArray(node);
282  arrayActiveOut = this->evalsSparseArrays_[node.getHandlerPosition()];
283  }
284 
285  processArray(*arrayActiveOut, values, valuesDefined, allParameters);
286 
287  return result;
288  }
289 
290  inline ActiveOut makeDenseArray(const NodeIn& node) {
291  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::ArrayCreation, "Invalid array creation operation")
292  CPPADCG_ASSERT_KNOWN(node.getHandlerPosition() < this->handler_.getManagedNodesCount(), "this node is not managed by the code handler")
293 
294  // check if this node was previously determined
295  if (evals_[node] != nullptr) {
296  return *evals_[node];
297  }
298 
299  if (outHandler_ == nullptr) {
300  throw CGException("Evaluator is unable to determine the new CodeHandler for an array creation operation");
301  }
302 
303  // values
304  const std::vector<ActiveOut>& array = this->evalArrayCreationOperation(node);
305 
306  // makeDenseArray() never called directly by EvaluatorOperations
307  return *this->saveEvaluation(node, ActiveOut(*outHandler_->makeNode(CGOpCode::ArrayCreation, {}, asArguments(array))));
308  }
309 
310  inline ActiveOut makeSparseArray(const NodeIn& node) {
311  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::SparseArrayCreation, "Invalid sparse array creation operation")
312  CPPADCG_ASSERT_KNOWN(node.getHandlerPosition() < this->handler_.getManagedNodesCount(), "this node is not managed by the code handler")
313 
314  // check if this node was previously determined
315  if (evals_[node] != nullptr) {
316  return *evals_[node];
317  }
318 
319  if (outHandler_ == nullptr) {
320  throw CGException("Evaluator is unable to determine the new CodeHandler for a sparse array creation operation");
321  }
322 
323  // values
324  const std::vector<ActiveOut>& array = this->evalSparseArrayCreationOperation(node);
325 
326  // makeSparseArray() never called directly by EvaluatorOperations
327  return *this->saveEvaluation(node, ActiveOut(*outHandler_->makeNode(CGOpCode::SparseArrayCreation, node.getInfo(), asArguments(array))));
328  }
329 
330  static inline void processArray(const std::vector<ActiveOut>& array,
331  std::vector<ScalarOut>& values,
332  bool& valuesDefined,
333  bool& allParameters) {
334  values.resize(array.size());
335  for (size_t i = 0; i < array.size(); i++) {
336  if (!array[i].isValueDefined()) {
337  valuesDefined = false;
338  allParameters = false;
339  break;
340  } else {
341  values[i] = array[i].getValue();
342  if (!array[i].isParameter())
343  allParameters = false;
344  }
345  }
346  }
347 
348  static inline bool isParameters(const CppAD::vector<ActiveOut>& tx) {
349  for (size_t i = 0; i < tx.size(); i++) {
350  if (!tx[i].isParameter()) {
351  return false;
352  }
353  }
354  return true;
355  }
356 
357  static inline bool isValuesDefined(const std::vector<ArgOut>& tx) {
358  for (size_t i = 0; i < tx.size(); i++) {
359  if (tx[i].getOperationNode() != nullptr) {
360  return false;
361  }
362  }
363  return true;
364  }
365 
366 };
367 
371 template<class ScalarIn, class ScalarOut>
372 class Evaluator<ScalarIn, ScalarOut, CG<ScalarOut> > : public EvaluatorCG<ScalarIn, ScalarOut, Evaluator<ScalarIn, ScalarOut, CG<ScalarOut> > > {
373 protected:
375 public:
376 
377  inline Evaluator(CodeHandler<ScalarIn>& handler) :
378  Super(handler) {
379  }
380 };
381 
382 } // END cg namespace
383 } // END CppAD namespace
384 
385 #endif
CodeHandler< ScalarOut > * outHandler_
void evalAtomicOperation(const NodeIn &node)
const std::string * getName() const
const std::vector< Argument< Base > > & getArguments() const
size_t getHandlerPosition() const
std::map< const NodeIn *, std::vector< ScalarOut * > > atomicEvalResults_
void analyzeOutIndeps(const ActiveOut *indep, size_t n)
bool isPrintOutPrintOperations() const
CGOpCode getOperationType() const
void setPrintOutPrintOperations(bool print)
const std::map< size_t, CGAbstractAtomicFun< Base > *> & getAtomicFunctions() const
ActiveOut evalArrayElement(const NodeIn &node)
size_t getManagedNodesCount() const
void setName(const std::string &name)
void processActiveOut(const NodeIn &node, ActiveOut &a)
ActiveOut evalPrint(const NodeIn &node)
const std::vector< size_t > & getInfo() const