CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
language_dot.hpp
1 #ifndef CPPAD_CG_LANGUAGE_DOT_INCLUDED
2 #define CPPAD_CG_LANGUAGE_DOT_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 
27 template<class Base>
28 class LanguageDot : public Language<Base> {
29 protected:
30  static const std::string _C_STATIC_INDEX_ARRAY;
31  static const std::string _C_SPARSE_INDEX_ARRAY;
32 protected:
33  // information from the code handler (not owned)
35  // new line characters
36  std::string _endline;
37  // output stream for the generated source code
38  std::ostringstream _code;
39  // creates the variable names
41  // auxiliary string stream
42  std::ostringstream _ss;
43  //
44  size_t _independentSize;
45  //
46  size_t _minTemporaryVarID;
47  // maps the variable IDs to the their position in the dependent vector
48  // (some IDs may be the same as the independent variables when dep = indep)
49  std::map<size_t, size_t> _dependentIDs;
50  // the dependent variable vector
51  const ArrayView<CG<Base> >* _dependent;
52  // whether or not to ignore assignment of constant zero values to dependent variables
53  bool _ignoreZeroDepAssign;
54  // the name of the file to be created without the extension
55  std::string _filename;
56  //
57  std::vector<const LoopStartOperationNode <Base>*> _currentLoops;
58  // the maximum precision used to print values
59  size_t _parameterPrecision;
60  //
61  bool _combineParameterNodes;
62  //
63  std::string _indepNodeStyle;
64  //
65  std::string _depNodeStyle;
66 private:
67  std::vector<int> varIds_;
68  size_t parIdx_;
69 public:
70 
75  _info(nullptr),
76  _endline("\n"),
77  _nameGen(nullptr),
78  _independentSize(0), // not really required (but it avoids warnings)
79  _minTemporaryVarID(0), // not really required (but it avoids warnings)
80  _dependent(nullptr),
81  _ignoreZeroDepAssign(false),
82  _filename("algorithm"),
83  _parameterPrecision(std::numeric_limits<Base>::digits10),
84  _combineParameterNodes(true),
85  parIdx_(0) { // not really required (but it avoids warnings)
86  }
87 
88  inline virtual ~LanguageDot() {
89  }
90 
91  inline bool isIgnoreZeroDepAssign() const {
92  return _ignoreZeroDepAssign;
93  }
94 
95  inline void setIgnoreZeroDepAssign(bool ignore) {
96  _ignoreZeroDepAssign = ignore;
97  }
98 
99  inline void setFilename(const std::string& name) {
100  _filename = name;
101  }
102 
109  virtual size_t getParameterPrecision() const {
110  return _parameterPrecision;
111  }
112 
119  virtual void setParameterPrecision(size_t p) {
120  _parameterPrecision = p;
121  }
122 
126  inline void setIndepNodeStyle(const std::string& indepNodeStyle) {
127  _indepNodeStyle = indepNodeStyle;
128  }
129 
133  inline const std::string& getIndepNodeStyle() const {
134  return _indepNodeStyle;
135  }
136 
140  inline void setDepNodeStyle(const std::string& depNodeStyle) {
141  _depNodeStyle = depNodeStyle;
142  }
143 
147  inline const std::string& getDepNodeStyle() const {
148  return _depNodeStyle;
149  }
150 
154  inline void setCombineParameterNodes(bool combineParameterNodes) {
155  _combineParameterNodes = combineParameterNodes;
156  }
157 
161  inline bool isCombineParameterNodes() const {
162  return _combineParameterNodes;
163  }
164 
165  /***************************************************************************
166  * STATIC
167  **************************************************************************/
168  static inline void printIndexCondExpr(std::ostringstream& out,
169  const std::vector<size_t>& info,
170  const std::string& index) {
171  CPPADCG_ASSERT_KNOWN(info.size() > 1 && info.size() % 2 == 0, "Invalid number of information elements for an index condition expression operation")
172 
173  size_t infoSize = info.size();
174  for (size_t e = 0; e < infoSize; e += 2) {
175  if (e > 0) {
176  out << " or "; // or
177  }
178  size_t min = info[e];
179  size_t max = info[e + 1];
180  if (min == max) {
181  out << index << "==" << min;
182  } else if (min == 0) {
183  out << index << "≤" << max;
184  } else if (max == (std::numeric_limits<size_t>::max)()) {
185  out << min << "≤" << index;
186  } else {
187  if (infoSize != 2)
188  out << "(";
189 
190  if (max - min == 1)
191  out << min << "==" << index << " or " << index << "==" << max;
192  else
193  out << min << "≤" << index << " and " << index << "≤" << max;
194 
195  if (infoSize != 2)
196  out << ")";
197  }
198  }
199  }
200 
201  /***************************************************************************
202  *
203  **************************************************************************/
204 
205  inline void printStaticIndexArray(std::ostringstream& os,
206  const std::string& name,
207  const std::vector<size_t>& values);
208 
209  inline void printStaticIndexMatrix(std::ostringstream& os,
210  const std::string& name,
211  const std::map<size_t, std::map<size_t, size_t> >& values);
212 
213  /***************************************************************************
214  * index patterns
215  **************************************************************************/
216  static inline void generateNames4RandomIndexPatterns(const std::set<RandomIndexPattern*>& randomPatterns);
217 
218  inline void printRandomIndexPatternDeclaration(std::ostringstream& os,
219  const std::set<RandomIndexPattern*>& randomPatterns);
220 
221  static void indexPattern2String(std::ostream& os,
222  const IndexPattern& ip,
223  const OperationNode<Base>& index);
224 
225  static void indexPattern2String(std::ostream& os,
226  const IndexPattern& ip,
227  const std::vector<const OperationNode<Base>*>& indexes);
228 
229  static inline void linearIndexPattern2String(std::ostream& os,
230  const LinearIndexPattern& lip,
231  const OperationNode<Base>& index);
232 
233  /***************************************************************************
234  * protected
235  **************************************************************************/
236 protected:
237 
238  void generateSourceCode(std::ostream& out,
239  std::unique_ptr<LanguageGenerationData<Base> > info) override {
240  // clean up
241  _code.str("");
242  _ss.str("");
243  _currentLoops.clear();
244  _dependentIDs.clear();
245  parIdx_ = 0;
246 
247  // save some info
248  _info = info.get();
249  _independentSize = info->independent.size();
250  _dependent = &info->dependent;
251  _nameGen = &info->nameGen;
252  _minTemporaryVarID = info->minTemporaryVarID;
253  const ArrayView<CG<Base> >& dependent = info->dependent;
254  const std::vector<OperationNode<Base>*>& variableOrder = info->variableOrder;
255 
256  varIds_.resize(_minTemporaryVarID + variableOrder.size());
257  std::fill(varIds_.begin(), varIds_.end(), 0);
258 
259  _code << "digraph {" << _endline << _endline;
260 
264  generateNames4RandomIndexPatterns(info->indexRandomPatterns);
265 
269  //generate names for the independent variables
270  _code << "subgraph indep {" << _endline;
271  _code << " rank=min" << _endline;
272  if(!_indepNodeStyle.empty()) {
273  _code << "node [" << _indepNodeStyle << "]" << _endline;
274  }
275  for (size_t j = 0; j < _independentSize; j++) {
276  OperationNode<Base>& op = *info->independent[j];
277 
278  _code << " v" << op.getHandlerPosition() << " [label=\"";
279  if (op.getName() == nullptr) {
280  _code << _nameGen->generateIndependent(op, getVariableID(op));
281  } else {
282  _code << *op.getName();
283  }
284  _code << "\"]" << _endline;
285 
286  }
287  _code << "}" << _endline;
288 
289  // generate names for the dependent variables (must be after naming independents)
290  _code << "subgraph dep {" << _endline;
291  _code << " rank=max" << _endline;
292  if(!_depNodeStyle.empty()) {
293  _code << "node [" << _depNodeStyle << "]" << _endline;
294  }
295  for (size_t i = 0; i < dependent.size(); i++) {
296 
297  OperationNode<Base>* node = dependent[i].getOperationNode();
298  if (node != nullptr && node->getOperationType() != CGOpCode::LoopEnd) {
299  _code << " y" << i << " [label=\"";
300  if(node->getName() == nullptr) {
301  if (node->getOperationType() == CGOpCode::LoopIndexedDep) {
302  size_t pos = node->getInfo()[0];
303  const IndexPattern* ip = info->loopDependentIndexPatterns[pos];
304  _code << _nameGen->generateIndexedDependent(*node, getVariableID(*node), *ip);
305 
306  } else {
307  _code << _nameGen->generateDependent(i);
308  }
309  } else {
310  _code << *node->getName();
311  }
312  _code << "\"]" << _endline;
313  }
314  }
315  _code << "}" << _endline;
316 
317 
321  printRandomIndexPatternDeclaration(_ss, _info->indexRandomPatterns);
322 
326  const std::vector<FuncArgument>& indArg = _nameGen->getIndependent();
327  const std::vector<FuncArgument>& depArg = _nameGen->getDependent();
328  const std::vector<FuncArgument>& tmpArg = _nameGen->getTemporary();
329  CPPADCG_ASSERT_KNOWN(indArg.size() > 0 && !depArg.empty(),
330  "There must be at least one dependent and one independent argument")
331  CPPADCG_ASSERT_KNOWN(tmpArg.size() == 3,
332  "There must be three temporary variables")
333 
337  // dependent variables indexes that are copies of other dependent variables
338  std::set<size_t> dependentDuplicates;
339 
340  for (size_t i = 0; i < dependent.size(); i++) {
341  OperationNode<Base>* node = dependent[i].getOperationNode();
342  if (node != nullptr) {
343  CGOpCode type = node->getOperationType();
344  if (type != CGOpCode::Inv && type != CGOpCode::LoopEnd) {
345  size_t varID = getVariableID(*node);
346  if (varID > 0) {
347  auto it2 = _dependentIDs.find(varID);
348  if (it2 == _dependentIDs.end()) {
349  _dependentIDs[getVariableID(*node)] = i;
350  } else {
351  // there can be several dependent variables with the same ID
352  dependentDuplicates.insert(i);
353  }
354  }
355  }
356  }
357  }
358 
362  if (variableOrder.size() > 0) {
363  // generate names for temporary variables
364  for (OperationNode<Base>* node : variableOrder) {
365  CGOpCode op = node->getOperationType();
366  if (!isDependent(*node) && op != CGOpCode::IndexDeclaration) {
367  // variable names for temporaries must always be created since they might have been used before with a different name/id
368  if (requiresVariableName(*node) && op != CGOpCode::ArrayCreation && op != CGOpCode::SparseArrayCreation) {
369  node->setName(_nameGen->generateTemporary(*node, getVariableID(*node)));
370  } else if (op == CGOpCode::ArrayCreation) {
371  node->setName(_nameGen->generateTemporaryArray(*node, getVariableID(*node)));
372  } else if (op == CGOpCode::SparseArrayCreation) {
373  node->setName(_nameGen->generateTemporarySparseArray(*node, getVariableID(*node)));
374  }
375  }
376  }
377 
381  for (OperationNode<Base>* it : variableOrder) {
382  // check if a new function should start
383  OperationNode<Base>& node = *it;
384 
385  // a dependent variable assigned by a loop does require any source code (its done inside the loop)
386  if (node.getOperationType() == CGOpCode::DependentRefRhs) {
387  continue; // nothing to do (this operation is right hand side only)
388  } else if (node.getOperationType() == CGOpCode::TmpDcl) { // temporary variable declaration does not need any source code here
389  continue; // nothing to do (bogus operation)
390  }
391 
392  printExpressionNoVarCheck(node);
393  }
394 
395  }
396 
397  // dependent duplicates
398  if (!dependentDuplicates.empty()) {
399  _code << "// variable duplicates: " << dependentDuplicates.size() << _endline;
400 
401  for (size_t index : dependentDuplicates) {
402  const CG<Base>& dep = dependent[index];
403  OperationNode<Base>* depNode = dep.getOperationNode();
404 
405  _code << makeNodeName(*depNode);
406  _code << " -> y" << index;
407  _code << _endline;
408  }
409  }
410 
411  for (size_t i = 0; i < dependent.size(); i++) {
412  if (!dependent[i].isParameter() && dependent[i].getOperationNode()->getOperationType() != CGOpCode::Inv) {
413  _code << makeNodeName(*dependent[i].getOperationNode());
414  _code << " -> y" << i;
415  _code << _endline;
416  }
417  }
418 
419  // constant dependent variables
420  bool commentWritten = false;
421  for (size_t i = 0; i < dependent.size(); i++) {
422  if (dependent[i].isParameter()) {
423  if (!_ignoreZeroDepAssign || !dependent[i].isIdenticalZero()) {
424  if (!commentWritten) {
425  _code << "// dependent variables without operations" << _endline;
426  commentWritten = true;
427  }
428 
429  _code << makeNodeName(dependent[i].getValue());
430  _code << " -> y" << i;
431  _code << _endline;
432  }
433  } else if (dependent[i].getOperationNode()->getOperationType() == CGOpCode::Inv) {
434  if (!commentWritten) {
435  _code << "// dependent variables without operations" << _endline;
436  commentWritten = true;
437  }
438 
439  _code << makeNodeName(*dependent[i].getOperationNode());
440  _code << " -> y" << i;
441  _code << _endline;
442  }
443  }
444 
445  _code << _endline << "}" << _endline; // digraph
446 
447  // a single source file
448  out << _code.str();
449  }
450 
451  inline size_t getVariableID(const OperationNode<Base>& node) const {
452  return _info->varId[node];
453  }
454 
456  size_t totalUseCount,
457  size_t opCount) const override {
458  CGOpCode op = var.getOperationType();
459  if (totalUseCount > 1) {
460  return op != CGOpCode::ArrayElement && op != CGOpCode::Index && op != CGOpCode::IndexDeclaration && op != CGOpCode::Tmp;
461  } else {
462  return (op == CGOpCode::ArrayCreation ||
463  op == CGOpCode::SparseArrayCreation ||
464  op == CGOpCode::AtomicForward ||
465  op == CGOpCode::AtomicReverse ||
466  op == CGOpCode::ComLt ||
467  op == CGOpCode::ComLe ||
468  op == CGOpCode::ComEq ||
469  op == CGOpCode::ComGe ||
470  op == CGOpCode::ComGt ||
471  op == CGOpCode::ComNe ||
472  op == CGOpCode::LoopIndexedDep ||
473  op == CGOpCode::LoopIndexedTmp ||
474  op == CGOpCode::IndexAssign ||
475  op == CGOpCode::Assign) &&
476  op != CGOpCode::CondResult;
477  }
478  }
479 
480  virtual bool requiresVariableName(const OperationNode<Base>& var) const {
481  CGOpCode op = var.getOperationType();
482  return (_info->totalUseCount.get(var) > 1 &&
483  op != CGOpCode::AtomicForward &&
484  op != CGOpCode::AtomicReverse &&
485  op != CGOpCode::LoopStart &&
486  op != CGOpCode::LoopEnd &&
487  op != CGOpCode::Index &&
488  op != CGOpCode::IndexAssign &&
489  op != CGOpCode::StartIf &&
490  op != CGOpCode::ElseIf &&
491  op != CGOpCode::Else &&
492  op != CGOpCode::EndIf &&
493  op != CGOpCode::CondResult &&
494  op != CGOpCode::LoopIndexedTmp &&
495  op != CGOpCode::Tmp);
496  }
497 
505  virtual bool directlyAssignsVariable(const OperationNode<Base>& var) const {
506  CGOpCode op = var.getOperationType();
507  return isCondAssign(op) ||
508  op == CGOpCode::ArrayCreation ||
509  op == CGOpCode::SparseArrayCreation ||
510  op == CGOpCode::AtomicForward ||
511  op == CGOpCode::AtomicReverse ||
512  op == CGOpCode::DependentMultiAssign ||
513  op == CGOpCode::LoopStart ||
514  op == CGOpCode::LoopEnd ||
515  op == CGOpCode::IndexAssign ||
516  op == CGOpCode::StartIf ||
517  op == CGOpCode::ElseIf ||
518  op == CGOpCode::Else ||
519  op == CGOpCode::EndIf ||
520  op == CGOpCode::CondResult ||
521  op == CGOpCode::IndexDeclaration;
522  }
523 
524  bool requiresVariableArgument(enum CGOpCode op, size_t argIndex) const override {
525  return op == CGOpCode::CondResult;
526  }
527 
528  inline const std::string& createVariableName(OperationNode<Base>& var) {
529  CGOpCode op = var.getOperationType();
530  CPPADCG_ASSERT_UNKNOWN(getVariableID(var) > 0)
531  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::AtomicForward)
532  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::AtomicReverse)
533  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::LoopStart)
534  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::LoopEnd)
535  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::Index)
536  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::IndexAssign)
537  CPPADCG_ASSERT_UNKNOWN(op != CGOpCode::IndexDeclaration)
538 
539  if (var.getName() == nullptr) {
540  if (op == CGOpCode::ArrayCreation) {
541  var.setName(_nameGen->generateTemporaryArray(var, getVariableID(var)));
542 
543  } else if (op == CGOpCode::SparseArrayCreation) {
544  var.setName(_nameGen->generateTemporarySparseArray(var, getVariableID(var)));
545 
546  } else if (op == CGOpCode::LoopIndexedDep) {
547  size_t pos = var.getInfo()[0];
548  const IndexPattern* ip = _info->loopDependentIndexPatterns[pos];
549  var.setName(_nameGen->generateIndexedDependent(var, getVariableID(var), *ip));
550 
551  } else if (op == CGOpCode::LoopIndexedIndep) {
552  size_t pos = var.getInfo()[1];
553  const IndexPattern* ip = _info->loopIndependentIndexPatterns[pos];
554  var.setName(_nameGen->generateIndexedIndependent(var, getVariableID(var), *ip));
555 
556  } else if (getVariableID(var) <= _independentSize) {
557  // independent variable
558  var.setName(_nameGen->generateIndependent(var, getVariableID(var)));
559 
560  } else if (getVariableID(var) < _minTemporaryVarID) {
561  // dependent variable
562  auto it = _dependentIDs.find(getVariableID(var));
563  CPPADCG_ASSERT_UNKNOWN(it != _dependentIDs.end());
564 
565  size_t index = it->second;
566  var.setName(_nameGen->generateDependent(index));
567 
568  } else if (op == CGOpCode::LoopIndexedTmp || op == CGOpCode::Tmp) {
569  CPPADCG_ASSERT_KNOWN(var.getArguments().size() >= 1, "Invalid number of arguments for loop indexed temporary operation")
570  OperationNode<Base>* tmpVar = var.getArguments()[0].getOperation();
571  CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation");
572  return createVariableName(*tmpVar);
573 
574  } else {
575  // temporary variable
576  var.setName(_nameGen->generateTemporary(var, getVariableID(var)));
577  }
578  }
579 
580  return *var.getName();
581  }
582 
583  bool requiresVariableDependencies() const override {
584  return false;
585  }
586 
587  virtual std::string print(const Argument<Base>& arg) {
588  if (arg.getOperation() != nullptr) {
589  // expression
590  return printExpression(*arg.getOperation());
591  } else {
592  // parameter
593  return printParameter(*arg.getParameter());
594  }
595  }
596 
597  virtual std::string printExpression(OperationNode<Base>& node) {
598  if (getVariableID(node) == 0) {
599  // print expression code
600  return printExpressionNoVarCheck(node);
601  } else {
602  return makeNodeName(node);
603  }
604  }
605 
606  virtual std::string printParameter(const Base& value) {
607  if(!_combineParameterNodes) {
608  std::string name = makeNodeName(value);
609 
610  _code << name;
611  _code << " [label=\"";
612  _code << std::setprecision(_parameterPrecision) << value;
613  _code << "\"]" << _endline;
614 
615  return name;
616  } else {
617  return makeNodeName(value);
618  }
619  }
620 
621  inline virtual std::string makeNodeName(const OperationNode<Base>& node) {
622  return "v" + std::to_string(node.getHandlerPosition());
623  }
624 
625  inline std::string makeNodeName(const Argument<Base>& arg) {
626  if (arg.getOperation() != nullptr) {
627  // expression
628  return makeNodeName(*arg.getOperation());
629  } else {
630  // parameter
631  return makeNodeName(*arg.getParameter());
632  }
633  }
634 
635  inline virtual std::string makeNodeName(const Base& value) {
636  if(_combineParameterNodes) {
637  // node name for parameters which have the same node for the same value
638  _ss.str("");
639  _ss << "\"" << std::setprecision(_parameterPrecision) << value << "\"";
640  return _ss.str();
641  } else {
642  std::string name = "p" + std::to_string(parIdx_);
643  parIdx_++;
644  return name;
645  }
646  }
647 
648  inline std::string printNodeDeclaration(const OperationNode<Base>& op,
649  const std::ostringstream& label,
650  const std::string& shape = "") {
651  return printNodeDeclaration(op, label.str(), shape);
652  }
653 
654  virtual std::string printNodeDeclaration(const OperationNode<Base>& op,
655  const std::string& label = "",
656  const std::string& shape = "") {
657  std::string name = makeNodeName(op);
658 
659  _code << name << " [label=\"";
660  if(!label.empty()) {
661  _code << label;
662  } else {
663  _code << op.getOperationType();
664  }
665  _code << "\"";
666  if (!shape.empty()) {
667  _code << ", shape=" << shape;
668  }
669  _code << "]" << _endline;
670 
671  return name;
672  }
673 
674  inline void printEdges(const std::string& name,
675  const OperationNode<Base>& node,
676  const std::string& style = "") {
677  const auto& args = node.getArguments();
678 
679  std::vector<std::string> aNames(args.size());
680  for (size_t i = 0; i < args.size(); ++i) {
681  aNames[i] = print(args[i]);
682  }
683 
684  for (size_t i = 0; i < args.size(); ++i) {
685  if (i > 0)
686  _code << " ";
687  printEdge(aNames[i], name, style);
688  }
689  _code << _endline;
690  }
691 
692  inline void printEdges(const std::string& name,
693  const OperationNode<Base>& node,
694  const std::vector<std::string>& args,
695  const std::string& style = "") {
696  size_t na = node.getArguments().size();
697  size_t nna = args.size();
698  CPPADCG_ASSERT_UNKNOWN(na >= nna)
699 
700  for (size_t i = 0; i < na; ++i) {
701  if (i > 0)
702  _code << " ";
703  if(i < nna && !args[i].empty()) {
704  printEdge(args[i], name, style);
705  } else {
706  std::string n = print(node.getArguments()[i]);
707  printEdge(n, name, style);
708  }
709  }
710  _code << _endline;
711  }
712 
713  inline void printEdges(const std::string& name,
714  const OperationNode<Base>& node,
715  const std::vector<std::string>& args,
716  const std::vector<std::string>& styles) {
717  size_t na = node.getArguments().size();
718  size_t nna = args.size();
719  size_t ns = styles.size();
720  CPPADCG_ASSERT_UNKNOWN(na >= nna);
721  CPPADCG_ASSERT_UNKNOWN(na >= ns);
722 
723  std::string style;
724  for (size_t i = 0; i < args.size(); ++i) {
725  if (i > 0)
726  _code << " ";
727 
728  style = i < ns ? styles[i] : "";
729  if(i < nna && !args[i].empty()) {
730  printEdge(args[i], name, style);
731  } else {
732  std::string n = print(node.getArguments()[i]);
733  printEdge(n, name, style);
734  }
735  }
736  _code << _endline;
737  }
738 
739  inline void printEdge(const OperationNode<Base>& from,
740  const std::string& to,
741  const std::string& style = "") {
742  _code << makeNodeName(from);
743  _code << " -> " << to;
744  if (!style.empty())
745  _code << "[" << style << "]";
746  }
747 
748  inline void printEdge(const std::string& from,
749  const std::string& to,
750  const std::string& style = "") {
751  _code << from << " -> " << to;
752  if (!style.empty())
753  _code << "[" << style << "]";
754  }
755 
756  virtual std::string printExpressionNoVarCheck(OperationNode<Base>& node) {
757  CGOpCode op = node.getOperationType();
758  switch (op) {
759  case CGOpCode::ArrayCreation:
760  return printArrayCreationOp(node);
761  case CGOpCode::SparseArrayCreation:
762  return printSparseArrayCreationOp(node);
763  case CGOpCode::ArrayElement:
764  return printArrayElementOp(node);
765  case CGOpCode::Assign:
766  return printAssignOp(node);
767 
768  case CGOpCode::Abs:
769  case CGOpCode::Acos:
770  case CGOpCode::Asin:
771  case CGOpCode::Atan:
772  case CGOpCode::Cosh:
773  case CGOpCode::Cos:
774  case CGOpCode::Exp:
775  case CGOpCode::Log:
776  case CGOpCode::Sign:
777  case CGOpCode::Sinh:
778  case CGOpCode::Sin:
779  case CGOpCode::Sqrt:
780  case CGOpCode::Tanh:
781  case CGOpCode::Tan:
782 #if CPPAD_USE_CPLUSPLUS_2011
783  case CGOpCode::Erf:
784  case CGOpCode::Erfc:
785  case CGOpCode::Asinh:
786  case CGOpCode::Acosh:
787  case CGOpCode::Atanh:
788  case CGOpCode::Expm1:
789  case CGOpCode::Log1p:
790 #endif
791  return printUnaryFunction(node);
792  case CGOpCode::AtomicForward: // atomicFunction.forward(q, p, vx, vy, tx, ty)
793  return printAtomicForwardOp(node);
794  case CGOpCode::AtomicReverse: // atomicFunction.reverse(p, tx, ty, px, py)
795  return printAtomicReverseOp(node);
796  case CGOpCode::Add:
797  return printOperationAdd(node);
798  case CGOpCode::Alias:
799  return printOperationAlias(node);
800  case CGOpCode::ComLt:
801  case CGOpCode::ComLe:
802  case CGOpCode::ComEq:
803  case CGOpCode::ComGe:
804  case CGOpCode::ComGt:
805  case CGOpCode::ComNe:
806  return printConditionalAssignment(node);
807  case CGOpCode::Div:
808  return printOperationDiv(node);
809  case CGOpCode::Inv:
810  // do nothing
811  return makeNodeName(node);
812  case CGOpCode::Mul:
813  return printOperationMul(node);
814  case CGOpCode::Pow:
815  return printPowFunction(node);
816  case CGOpCode::Pri:
817  // do nothing
818  return makeNodeName(node);
819  case CGOpCode::Sub:
820  return printOperationMinus(node);
821 
822  case CGOpCode::UnMinus:
823  return printOperationUnaryMinus(node);
824 
825  case CGOpCode::DependentMultiAssign:
826  return printDependentMultiAssign(node);
827 
828  case CGOpCode::Index:
829  return makeNodeName(node); // nothing to do
830 
831  case CGOpCode::IndexAssign:
832  return printIndexAssign(node);
833  case CGOpCode::IndexDeclaration:
834  return makeNodeName(node); // already done
835 
836  case CGOpCode::LoopStart:
837  return printLoopStart(node);
838  case CGOpCode::LoopIndexedIndep:
839  return printLoopIndexedIndep(node);
840  case CGOpCode::LoopIndexedDep:
841  return printLoopIndexedDep(node);
842  case CGOpCode::LoopIndexedTmp:
843  return printLoopIndexedTmp(node);
844  case CGOpCode::TmpDcl:
845  // nothing to do
846  return makeNodeName(node);
847  case CGOpCode::Tmp:
848  return printTmpVar(node);
849  case CGOpCode::LoopEnd:
850  return printLoopEnd(node);
851  case CGOpCode::IndexCondExpr:
852  return printIndexCondExprOp(node);
853  case CGOpCode::StartIf:
854  return printStartIf(node);
855  case CGOpCode::ElseIf:
856  return printElseIf(node);
857  case CGOpCode::Else:
858  return printElse(node);
859  case CGOpCode::EndIf:
860  return printEndIf(node);
861  case CGOpCode::CondResult:
862  return printCondResult(node);
863  default:
864  throw CGException("Unknown operation code '", op, "'.");
865  }
866  }
867 
868  virtual std::string printAssignOp(OperationNode<Base>& node) {
869  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 1, "Invalid number of arguments for assign operation")
870 
871  return print(node.getArguments()[0]);
872  }
873 
874  virtual std::string printPowFunction(OperationNode<Base>& op) {
875  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for pow() function")
876 
877  std::string a0 = print(op.getArguments()[0]);
878  std::string a1 = print(op.getArguments()[1]);
879 
880  std::string name = printNodeDeclaration(op);
881 
882  printEdges(name, op, std::vector<std::string>{a0, a1}, std::vector<std::string>{"label=\"$1\"", "label=\"$2\""});
883 
884  return name;
885  }
886 
887  virtual std::string printUnaryFunction(OperationNode<Base>& op) {
888  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for an unary function")
889 
890  std::string a0 = print(op.getArguments()[0]);
891 
892  // TODO: improve this
893  _ss.str("");
894  _ss << op.getOperationType();
895  std::string label = _ss.str();
896  auto it = label.find('(');
897  if (it != std::string::npos) {
898  label = label.substr(0, it);
899  }
900  std::string name = printNodeDeclaration(op, label);
901 
902  printEdges(name, op, std::vector<std::string> {a0});
903 
904  return name;
905  }
906 
907  virtual std::string printOperationAlias(OperationNode<Base>& op) {
908  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for alias")
909 
910  std::string a0 = print(op.getArguments()[0]);
911 
912  std::string name = printNodeDeclaration(op);
913 
914  printEdges(name, op, std::vector<std::string> {a0});
915 
916  return name;
917  }
918 
919  virtual std::string printOperationAdd(OperationNode<Base>& op) {
920  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for addition")
921 
922  const Argument<Base>& left = op.getArguments()[0];
923  const Argument<Base>& right = op.getArguments()[1];
924 
925  std::string a0 = print(left);
926  std::string a1 = print(right);
927 
928  std::string name = printNodeDeclaration(op, "+");
929 
930  printEdges(name, op, std::vector<std::string> {a0, a1});
931 
932  return name;
933  }
934 
935  virtual std::string printOperationMinus(OperationNode<Base>& op) {
936  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for subtraction")
937 
938  const Argument<Base>& left = op.getArguments()[0];
939  const Argument<Base>& right = op.getArguments()[1];
940 
941  std::string a0 = print(left);
942  std::string a1 = print(right);
943 
944  std::string name = printNodeDeclaration(op);
945 
946  printEdges(name, op, std::vector<std::string> {a0, a1}, std::vector<std::string>{"label=\"$1\"", "label=\"$2\""});
947 
948  return name;
949  }
950 
951  virtual std::string printOperationDiv(OperationNode<Base>& op) {
952  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for division")
953 
954  const Argument<Base>& left = op.getArguments()[0];
955  const Argument<Base>& right = op.getArguments()[1];
956 
957  std::string a0 = print(left);
958  std::string a1 = print(right);
959 
960  std::string name = printNodeDeclaration(op);
961 
962  printEdges(name, op, std::vector<std::string> {a0, a1}, std::vector<std::string>{"label=\"$1\"", "label=\"$2\""});
963 
964  return name;
965  }
966 
967  virtual std::string printOperationMul(OperationNode<Base>& op) {
968  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 2, "Invalid number of arguments for multiplication")
969 
970  const Argument<Base>& left = op.getArguments()[0];
971  const Argument<Base>& right = op.getArguments()[1];
972 
973  std::string a0 = print(left);
974  std::string a1 = print(right);
975 
976  std::string name = printNodeDeclaration(op, "×");
977 
978  printEdges(name, op, std::vector<std::string> {a0, a1});
979 
980  return name;
981  }
982 
983  virtual std::string printOperationUnaryMinus(OperationNode<Base>& op) {
984  CPPADCG_ASSERT_KNOWN(op.getArguments().size() == 1, "Invalid number of arguments for unary minus")
985 
986  const Argument<Base>& arg = op.getArguments()[0];
987 
988  std::string a0 = print(arg);
989 
990  std::string name = printNodeDeclaration(op);
991 
992  printEdges(name, op, std::vector<std::string> {a0});
993 
994  return name;
995  }
996 
997  virtual std::string printConditionalAssignment(OperationNode<Base>& node) {
998  CPPADCG_ASSERT_UNKNOWN(getVariableID(node) > 0)
999 
1000  const std::vector<Argument<Base> >& args = node.getArguments();
1001  const Argument<Base>& left = args[0];
1002  const Argument<Base>& right = args[1];
1003  const Argument<Base>& trueCase = args[2];
1004  const Argument<Base>& falseCase = args[3];
1005 
1006  std::string a0 = print(left);
1007  std::string a1 = print(right);
1008  std::string a2 = print(trueCase);
1009  std::string a3 = print(falseCase);
1010 
1011  std::string name = printNodeDeclaration(node, "", "diamond");
1012 
1016  printEdges(name, node, std::vector<std::string> {a0, a1, a2, a3},
1017  std::vector<std::string>{"label=\"left\"", "label=\"right\"", "label=\"true\"", "label=\"false\""});
1018 
1019  return name;
1020  }
1021 
1022  virtual std::string printArrayCreationOp(OperationNode<Base>& op);
1023 
1024  virtual std::string printSparseArrayCreationOp(OperationNode<Base>& op);
1025 
1026  inline size_t printArrayCreationUsingLoop(const std::string& arrayName,
1027  const OperationNode<Base>& array,
1028  size_t startj,
1029  const size_t* indexes);
1030 
1031  virtual std::string printArrayElementOp(OperationNode<Base>& op);
1032 
1033  virtual std::string printAtomicForwardOp(OperationNode<Base>& atomicFor) {
1034  CPPADCG_ASSERT_KNOWN(atomicFor.getInfo().size() == 3, "Invalid number of information elements for atomic forward operation")
1035  int q = atomicFor.getInfo()[1];
1036  int p = atomicFor.getInfo()[2];
1037  size_t p1 = p + 1;
1038  const std::vector<Argument<Base> >& opArgs = atomicFor.getArguments();
1039  CPPADCG_ASSERT_KNOWN(opArgs.size() == p1 * 2, "Invalid number of arguments for atomic forward operation")
1040 
1041  size_t id = atomicFor.getInfo()[0];
1042 
1043  std::string name = printNodeDeclaration(atomicFor, _info->atomicFunctionId2Name.at(id) + ".forward(" + std::to_string(q) + ", " + std::to_string(p) + ", tx, ty)");
1044 
1048  std::vector<std::string> args(opArgs.size()); // argument node names
1049 
1050  std::vector<OperationNode<Base>*> tx(p1), ty(p1);
1051  for (size_t k = 0; k < p1; k++) {
1052  tx[k] = opArgs[0 * p1 + k].getOperation();
1053  ty[k] = opArgs[1 * p1 + k].getOperation();
1054 
1055  args[0 * p1 + k] = print(*tx[k]);
1056  args[1 * p1 + k] = print(*ty[k]);
1057  }
1058 
1059  for (size_t k = 0; k < p1; k++) {
1060  printEdge(args[0 * p1 + k], name, "label=\"tx" + std::to_string(k) + "\"");
1061  _code << " ";
1062 
1063  printEdge(args[1 * p1 + k], name, "label=\"ty" + std::to_string(k) + "\"");
1064  _code << " ";
1065  }
1066  _code << _endline;
1067 
1068  CPPADCG_ASSERT_KNOWN(tx[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1069  CPPADCG_ASSERT_KNOWN(p == 0 || tx[1]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type")
1070  CPPADCG_ASSERT_KNOWN(ty[p]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1071 
1072  return name;
1073  }
1074 
1075  virtual std::string printAtomicReverseOp(OperationNode<Base>& atomicRev) {
1076  CPPADCG_ASSERT_KNOWN(atomicRev.getInfo().size() == 2, "Invalid number of information elements for atomic reverse operation")
1077  int p = atomicRev.getInfo()[1];
1078  size_t p1 = p + 1;
1079  const std::vector<Argument<Base> >& opArgs = atomicRev.getArguments();
1080  CPPADCG_ASSERT_KNOWN(opArgs.size() == p1 * 4, "Invalid number of arguments for atomic reverse operation")
1081 
1082  size_t id = atomicRev.getInfo()[0];
1083 
1084  std::string name = printNodeDeclaration(atomicRev, _info->atomicFunctionId2Name.at(id) + ".reverse(" + std::to_string(p) + ", tx, px, py)");
1085 
1089  std::vector<std::string> args(opArgs.size()); // argument node names
1090 
1091  std::vector<OperationNode<Base>*> tx(p1), px(p1), py(p1);
1092  for (size_t k = 0; k < p1; k++) {
1093  tx[k] = opArgs[0 * p1 + k].getOperation();
1094  px[k] = opArgs[2 * p1 + k].getOperation();
1095  py[k] = opArgs[3 * p1 + k].getOperation();
1096 
1097  args[0 * p1 + k] = print(*tx[k]);
1098  args[1 * p1 + k] = print(opArgs[1 * p1 + k]); // todo: consider not showing this
1099  args[2 * p1 + k] = print(*px[k]);
1100  args[3 * p1 + k] = print(*py[k]);
1101  }
1102 
1103  for (size_t k = 0; k < p1; k++) {
1104  printEdge(args[0 * p1 + k], name, "label=\"tx" + std::to_string(k) + "\"");
1105  _code << " ";
1106 
1107  printEdge(args[1 * p1 + k], name, "label=\"ty" + std::to_string(k) + "\"");
1108  _code << " ";
1109 
1110  printEdge(args[2 * p1 + k], name, "label=\"px" + std::to_string(k) + "\"");
1111  _code << " ";
1112 
1113  printEdge(args[3 * p1 + k], name, "label=\"py" + std::to_string(k) + "\"");
1114  _code << " ";
1115  }
1116  _code << _endline;
1117 
1118  CPPADCG_ASSERT_KNOWN(tx[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1119  CPPADCG_ASSERT_KNOWN(p == 0 || tx[1]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type")
1120 
1121  CPPADCG_ASSERT_KNOWN(px[0]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1122 
1123  CPPADCG_ASSERT_KNOWN(py[0]->getOperationType() == CGOpCode::SparseArrayCreation, "Invalid array type")
1124  CPPADCG_ASSERT_KNOWN(p == 0 || py[1]->getOperationType() == CGOpCode::ArrayCreation, "Invalid array type")
1125 
1126  return name;
1127  }
1128 
1129  virtual std::string printDependentMultiAssign(OperationNode<Base>& node) {
1130  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::DependentMultiAssign, "Invalid node type")
1131  CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments")
1132 
1133  std::string name = printNodeDeclaration(node, "+=");
1134 
1135  const std::vector<Argument<Base> >& args = node.getArguments();
1136  for (size_t a = 0; a < args.size(); a++) {
1137  bool useArg = false;
1138  const Argument<Base>& arg = args[a];
1139  std::string aName = print(arg);
1140 
1141  if (arg.getParameter() != nullptr) {
1142  useArg = true;
1143  } else {
1144  CGOpCode op = arg.getOperation()->getOperationType();
1145  useArg = op != CGOpCode::DependentRefRhs && op != CGOpCode::LoopEnd && op != CGOpCode::EndIf;
1146  }
1147 
1148  if (useArg) {
1149  printEdge(aName, name, "label=\"+=\"");
1150  _code << _endline;
1151  break;
1152  } else {
1153  printEdge(aName, name, "color=grey");
1154  _code << _endline;
1155  }
1156  }
1157 
1158  return name;
1159  }
1160 
1161  virtual std::string printLoopStart(OperationNode<Base>& node) {
1162  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopStart, "Invalid node type")
1163 
1164  auto& lnode = static_cast<LoopStartOperationNode<Base>&> (node);
1165  _currentLoops.push_back(&lnode);
1166 
1170  const std::string& jj = *lnode.getIndex().getName();
1171 
1172  _ss.str("");
1173  if (lnode.getIterationCountNode() != nullptr) {
1174  _ss << "for " << jj << " ∈ [0, " << lnode.getIterationCountNode()->getIndex().getName() << "-1]";
1175  } else {
1176  _ss << "for " << jj << " ∈ [0, " << (lnode.getIterationCount() - 1) << "]";
1177  }
1178  std::string name = printNodeDeclaration(node, _ss, "parallelogram");
1179 
1183  if (lnode.getIterationCountNode() != nullptr) {
1184  // is label ready necessary?
1185  printEdge(*lnode.getIterationCountNode(), name, "label=\"" + (*lnode.getIterationCountNode()->getIndex().getName()) + "\"");
1186  _code << _endline;
1187  }
1188 
1189  printEdge(lnode.getIndex(), name, "label=\"index " + jj + "\"");
1190  _code << _endline;
1191 
1192  return name;
1193  }
1194 
1195  virtual std::string printLoopEnd(OperationNode<Base>& node) {
1196  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopEnd, "Invalid node type")
1197 
1198  std::string name = printNodeDeclaration(node);
1199 
1200  printEdges(name, node, "color=grey");
1201 
1202  _currentLoops.pop_back();
1203 
1204  return name;
1205  }
1206 
1207  virtual std::string printLoopIndexedDep(OperationNode<Base>& node) {
1208  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for loop indexed dependent operation")
1209 
1210  std::string name = printNodeDeclaration(node);
1211 
1212  // LoopIndexedDep
1213  printEdges(name, node);
1214 
1215  return name;
1216  }
1217 
1218  virtual std::string printLoopIndexedIndep(OperationNode<Base>& node) {
1219  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopIndexedIndep, "Invalid node type")
1220  CPPADCG_ASSERT_KNOWN(node.getInfo().size() == 1, "Invalid number of information elements for loop indexed independent operation")
1221 
1222  size_t pos = node.getInfo()[1];
1223  const IndexPattern* ip = _info->loopIndependentIndexPatterns[pos];
1224  _ss << _nameGen->generateIndexedIndependent(node, getVariableID(node), *ip);
1225 
1226  std::string name = printNodeDeclaration(node, _ss);
1227 
1228  printEdges(name, node);
1229 
1230  return name;
1231  }
1232 
1233  virtual std::string printLoopIndexedTmp(OperationNode<Base>& node) {
1234  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::LoopIndexedTmp, "Invalid node type")
1235  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 2, "Invalid number of arguments for loop indexed temporary operation")
1236  OperationNode<Base>* tmpVar = node.getArguments()[0].getOperation();
1237  CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation")
1238 
1239  std::string name = printNodeDeclaration(node);
1240 
1241  printEdges(name, node);
1242 
1243  return name;
1244  }
1245 
1246  virtual std::string printTmpVar(OperationNode<Base>& node) {
1247  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::Tmp, "Invalid node type")
1248  CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments for temporary variable usage operation")
1249 #ifndef NDEBUG
1250  OperationNode<Base>* tmpVar = node.getArguments()[0].getOperation();
1251  CPPADCG_ASSERT_KNOWN(tmpVar != nullptr && tmpVar->getOperationType() == CGOpCode::TmpDcl, "Invalid arguments for loop indexed temporary operation")
1252 #endif
1253  // do nothing
1254 
1255  return makeNodeName(node);
1256  }
1257 
1258  virtual std::string printIndexAssign(OperationNode<Base>& node) {
1259  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::IndexAssign, "Invalid node type")
1260  CPPADCG_ASSERT_KNOWN(node.getArguments().size() > 0, "Invalid number of arguments for an index assignment operation")
1261 
1262  auto& inode = static_cast<IndexAssignOperationNode<Base>&> (node);
1263 
1264  const IndexPattern& ip = inode.getIndexPattern();
1265  _ss.str("");
1266  _ss << (*inode.getIndex().getName()) << " = ";
1267  indexPattern2String(_ss, ip, inode.getIndexPatternIndexes());
1268 
1269  std::string name = printNodeDeclaration(node, _ss);
1270 
1274  for (const auto* idx: inode.getIndexPatternIndexes()) {
1275  printEdge(*idx, name);
1276  _code << " ";
1277  }
1278  _code << _endline;
1279 
1280  return name;
1281  }
1282 
1283  virtual std::string printIndexCondExprOp(OperationNode<Base>& node) {
1284  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::IndexCondExpr, "Invalid node type")
1285  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 1, "Invalid number of arguments for an index condition expression operation")
1286  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an index condition expression operation")
1287  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation()->getOperationType() == CGOpCode::Index, "Invalid argument for an index condition expression operation")
1288 
1289  const std::vector<size_t>& info = node.getInfo();
1290 
1291  auto& iterationIndexOp = static_cast<IndexOperationNode<Base>&> (*node.getArguments()[0].getOperation());
1292  const std::string& index = *iterationIndexOp.getIndex().getName();
1293 
1294  _ss.str("");
1295  printIndexCondExpr(_ss, info, index);
1296 
1297  std::string name = printNodeDeclaration(node, _ss);
1298 
1299  return name;
1300  }
1301 
1302  virtual std::string printStartIf(OperationNode<Base>& node) {
1307  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::StartIf, "Invalid node type")
1308  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for an 'if start' operation")
1309  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an 'if start' operation")
1310 
1311  //printIndexCondExprOp(*node.getArguments()[0].getOperation());
1312  std::string name = printNodeDeclaration(node, "", "diamond");
1313 
1314  printEdges(name, node, std::vector<std::string>{},
1315  std::vector<std::string>{"label=\"condition\""});
1316 
1317  return name;
1318  }
1319 
1320  virtual std::string printElseIf(OperationNode<Base>& node) {
1326  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::ElseIf, "Invalid node type")
1327  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 2, "Invalid number of arguments for an 'else if' operation")
1328  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an 'else if' operation")
1329  CPPADCG_ASSERT_KNOWN(node.getArguments()[1].getOperation() != nullptr, "Invalid argument for an 'else if' operation")
1330 
1331  std::string name = printNodeDeclaration(node, "", "diamond");
1332 
1333  printEdges(name, node, std::vector<std::string>{},
1334  std::vector<std::string>{"label=\"false\"", "label=\"condition\""});
1335 
1336  return name;
1337  }
1338 
1339  virtual std::string printElse(OperationNode<Base>& node) {
1344  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::Else, "Invalid node type")
1345  CPPADCG_ASSERT_KNOWN(node.getArguments().size() >= 1, "Invalid number of arguments for an 'else' operation")
1346 
1347  std::string name = printNodeDeclaration(node, "", "diamond");
1348 
1349  printEdges(name, node, std::vector<std::string>{},
1350  std::vector<std::string>{"label=\"false\""});
1351 
1352  return name;
1353  }
1354 
1355  virtual std::string printEndIf(OperationNode<Base>& node) {
1356  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::EndIf, "Invalid node type for an 'end if' operation")
1357 
1358  std::string name = printNodeDeclaration(node, "", "diamond");
1359 
1360  printEdges(name, node);
1361 
1362  return name;
1363  }
1364 
1365  virtual std::string printCondResult(OperationNode<Base>& node) {
1366  CPPADCG_ASSERT_KNOWN(node.getOperationType() == CGOpCode::CondResult, "Invalid node type")
1367  CPPADCG_ASSERT_KNOWN(node.getArguments().size() == 2, "Invalid number of arguments for an assignment inside an if/else operation")
1368  CPPADCG_ASSERT_KNOWN(node.getArguments()[0].getOperation() != nullptr, "Invalid argument for an an assignment inside an if/else operation")
1369  CPPADCG_ASSERT_KNOWN(node.getArguments()[1].getOperation() != nullptr, "Invalid argument for an an assignment inside an if/else operation")
1370 
1371  print(node.getArguments()[0]); // condition start (e.g if or else if)
1372  print(node.getArguments()[1]); // temporary result
1373 
1374  std::string name = printNodeDeclaration(node, "", "diamond");
1375 
1376  printEdges(name, node);
1377 
1378  return name;
1379  }
1380 
1381  inline bool isDependent(const OperationNode<Base>& arg) const {
1382  if (arg.getOperationType() == CGOpCode::LoopIndexedDep) {
1383  return true;
1384  }
1385  size_t id = getVariableID(arg);
1386  return id > _independentSize && id < _minTemporaryVarID;
1387  }
1388 
1389  virtual void getComparison(std::ostream& os, enum CGOpCode op) const {
1390  switch (op) {
1391  case CGOpCode::ComLt:
1392  os << "<";
1393  return;
1394 
1395  case CGOpCode::ComLe:
1396  os << "≤";
1397  return;
1398 
1399  case CGOpCode::ComEq:
1400  os << "==";
1401  return;
1402 
1403  case CGOpCode::ComGe:
1404  os << "≥";
1405  return;
1406 
1407  case CGOpCode::ComGt:
1408  os << ">";
1409  return;
1410 
1411  case CGOpCode::ComNe:
1412  os << "≠";
1413  return;
1414 
1415  default:
1416  CPPAD_ASSERT_UNKNOWN(0)
1417  break;
1418  }
1419  throw CGException("Invalid comparison operator code"); // should never get here
1420  }
1421 
1422  static bool isFunction(enum CGOpCode op) {
1423  return isUnaryFunction(op) || op == CGOpCode::Pow;
1424  }
1425 
1426  static bool isUnaryFunction(enum CGOpCode op) {
1427  switch (op) {
1428  case CGOpCode::Abs:
1429  case CGOpCode::Acos:
1430  case CGOpCode::Asin:
1431  case CGOpCode::Atan:
1432  case CGOpCode::Cosh:
1433  case CGOpCode::Cos:
1434  case CGOpCode::Exp:
1435  case CGOpCode::Log:
1436  case CGOpCode::Sign:
1437  case CGOpCode::Sinh:
1438  case CGOpCode::Sin:
1439  case CGOpCode::Sqrt:
1440  case CGOpCode::Tanh:
1441  case CGOpCode::Tan:
1442 #if CPPAD_USE_CPLUSPLUS_2011
1443  case CGOpCode::Erf:
1444  case CGOpCode::Erfc:
1445  case CGOpCode::Asinh:
1446  case CGOpCode::Acosh:
1447  case CGOpCode::Atanh:
1448  case CGOpCode::Expm1:
1449  case CGOpCode::Log1p:
1450 #endif
1451  return true;
1452  default:
1453  return false;
1454  }
1455  }
1456 
1457  static bool isCondAssign(enum CGOpCode op) {
1458  switch (op) {
1459  case CGOpCode::ComLt:
1460  case CGOpCode::ComLe:
1461  case CGOpCode::ComEq:
1462  case CGOpCode::ComGe:
1463  case CGOpCode::ComGt:
1464  case CGOpCode::ComNe:
1465  return true;
1466  default:
1467  return false;
1468  }
1469  }
1470 };
1471 
1472 template<class Base>
1473 const std::string LanguageDot<Base>::_C_STATIC_INDEX_ARRAY = "index"; // NOLINT(cert-err58-cpp)
1474 
1475 template<class Base>
1476 const std::string LanguageDot<Base>::_C_SPARSE_INDEX_ARRAY = "idx"; // NOLINT(cert-err58-cpp)
1477 
1478 } // END cg namespace
1479 } // END CppAD namespace
1480 
1481 #endif
const std::map< size_t, std::string > & atomicFunctionId2Name
Definition: language.hpp:69
virtual std::string printAtomicReverseOp(OperationNode< Base > &atomicRev)
virtual std::string generateTemporary(const OperationNode< Base > &variable, size_t id)=0
virtual std::string printElse(OperationNode< Base > &node)
virtual std::string generateIndexedDependent(const OperationNode< Base > &var, size_t id, const IndexPattern &ip)=0
const std::string & getIndepNodeStyle() const
size_t printArrayCreationUsingLoop(const std::string &arrayName, const OperationNode< Base > &array, size_t startj, const size_t *indexes)
void generateSourceCode(std::ostream &out, std::unique_ptr< LanguageGenerationData< Base > > info) override
virtual std::string generateIndexedIndependent(const OperationNode< Base > &var, size_t id, const IndexPattern &ip)=0
const std::string * getName() const
const std::vector< Argument< Base > > & getArguments() const
virtual const std::vector< FuncArgument > & getTemporary() const
void setIndepNodeStyle(const std::string &indepNodeStyle)
STL namespace.
virtual std::string printConditionalAssignment(OperationNode< Base > &node)
size_t getHandlerPosition() const
virtual std::string printAtomicForwardOp(OperationNode< Base > &atomicFor)
const CodeHandlerVector< Base, size_t > & varId
Definition: language.hpp:49
virtual std::string printStartIf(OperationNode< Base > &node)
void setDepNodeStyle(const std::string &depNodeStyle)
bool createsNewVariable(const OperationNode< Base > &var, size_t totalUseCount, size_t opCount) const override
void printRandomIndexPatternDeclaration(std::ostringstream &os, const std::set< RandomIndexPattern *> &randomPatterns)
virtual std::string generateDependent(size_t index)=0
bool requiresVariableDependencies() const override
const CodeHandlerVector< Base, size_t > & totalUseCount
Definition: language.hpp:95
CGOpCode getOperationType() const
virtual std::string printLoopStart(OperationNode< Base > &node)
virtual bool directlyAssignsVariable(const OperationNode< Base > &var) const
const std::string & getDepNodeStyle() const
virtual std::string generateTemporaryArray(const OperationNode< Base > &variable, size_t id)=0
virtual const std::vector< FuncArgument > & getDependent() const
virtual std::string generateTemporarySparseArray(const OperationNode< Base > &variable, size_t id)=0
virtual const std::vector< FuncArgument > & getIndependent() const
virtual std::string printIndexAssign(OperationNode< Base > &node)
virtual std::string printElseIf(OperationNode< Base > &node)
virtual std::string generateIndependent(const OperationNode< Base > &variable, size_t id)=0
void setName(const std::string &name)
virtual size_t getParameterPrecision() const
virtual void setParameterPrecision(size_t p)
size_t size() const noexcept
Definition: array_view.hpp:202
const std::vector< size_t > & getInfo() const