CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
operation_stack.hpp
1 #ifndef CPPAD_CG_OPERATION_STACK_HPP
2 #define CPPAD_CG_OPERATION_STACK_HPP
3 /* --------------------------------------------------------------------------
4  * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5  * Copyright (C) 2019 Joao Leal
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 
24 enum class StackNavigationStep {
25  Analyze, // analyze the current node (before visiting children)
26  ChildrenVisited, // post process node after children have been visited
27  Exit // leave node (go to the parent node)
28 };
29 
35 template<class Base>
37 private:
38  OperationNode<Base>* const _parent;
39  const size_t _node;
40 public:
41 
43  size_t nodeIndex) noexcept :
44  _parent(&parent),
45  _node(nodeIndex) {
46  }
47 
48  inline OperationNode<Base>& parent() {
49  return *_parent;
50  }
51 
52  inline OperationNode<Base>& node() {
53  return *_parent->getArguments()[_node].getOperation();
54  }
55 
56  inline size_t argumentIndex() const {
57  return _node;
58  }
59 };
60 
66 template<class Base>
68 public:
69  size_t parentNodeScope;
70  StackNavigationStep nextStep;
71 
73  size_t nodeIndex,
74  size_t parentNodeScope) noexcept :
75  SimpleOperationStackData<Base>(parent, nodeIndex),
76  parentNodeScope(parentNodeScope),
77  nextStep(StackNavigationStep::Analyze) {
78  }
79 
80 };
81 
87 template<class Element>
89 private:
90  std::vector<Element> _stack;
91 public:
92  inline BaseOperationStack() {
93  _stack.reserve(100);
94  }
95 
96  inline bool empty() const {
97  return _stack.empty();
98  }
99 
100  inline size_t size() const {
101  return _stack.size();
102  }
103 
104  inline void pop_back() {
105  _stack.pop_back();
106  }
107 
108  inline Element& back() {
109  return _stack.back();
110  }
111 
112  template<class... Args>
113  inline void emplace_back(Args&&... args) {
114  if (_stack.size() == _stack.capacity()) {
115  _stack.reserve((_stack.size() * 3) / 2 + 1);
116  }
117  _stack.emplace_back(std::forward<Args>(args)...);
118  }
119 
120  inline Element& operator[](size_t i) {
121  return _stack[i];
122  }
123 };
124 
125 template<class Base>
126 class OperationStack : public BaseOperationStack<OperationStackData<Base> > {
127 public:
128  inline void pushNodeArguments(OperationNode<Base>& node,
129  size_t parentNodeScope) {
130  auto& args = node.getArguments();
131 
132  // append in reverse order so that they are visited in correct forward order
133  for (auto itArg = args.rbegin(); itArg != args.rend(); ++itArg) {
134  if (itArg->getOperation() != nullptr) {
135  size_t index = std::distance(begin(args), itArg.base()) - 1;
136  this->emplace_back(node, index, parentNodeScope);
137  }
138  }
139  }
140 };
141 
142 template<class Base>
143 class SimpleOperationStack : public BaseOperationStack<SimpleOperationStackData<Base> > {
144 public:
145  inline void pushNodeArguments(OperationNode<Base>& node) {
146  auto& args = node.getArguments();
147 
148  // append in reverse order so that they are visited in correct forward order
149  for (auto itArg = args.rbegin(); itArg != args.rend(); ++itArg) {
150  if (itArg->getOperation() != nullptr) {
151  size_t index = std::distance(begin(args), itArg.base()) - 1;
152  this->emplace_back(node, index);
153  }
154  }
155  }
156 };
157 
177 template<class Base, typename FunctionAnalysis, typename FunctionPostProcess>
178 inline void depthFirstGraphNavigation(OperationNode<Base>& root,
179  size_t currentScopeColor,
180  FunctionAnalysis& nodeAnalysis,
181  FunctionPostProcess& nodePostProcessAnalysis,
182  bool processRoot) {
183  OperationStack<Base> stack;
184 
185  std::unique_ptr<OperationNode<Base>> fakeSuperRoot;
186  if (processRoot) {
187  fakeSuperRoot = OperationNode<Base>::makeTemporaryNode(CGOpCode::Alias, {}, {root});
188  stack.emplace_back(*fakeSuperRoot, 0, currentScopeColor);
189  } else {
190  stack.pushNodeArguments(root, currentScopeColor);
191  }
192 
193  while (!stack.empty()) {
194 
195  if (stack.back().nextStep == StackNavigationStep::Analyze) {
196  size_t i = stack.size() - 1; // do not use a reference because the stack may be resized
197 
198  bool complete = nodeAnalysis(stack[i], stack);
199 
200  if (complete)
201  stack[i].nextStep = StackNavigationStep::ChildrenVisited;
202  else
203  stack[i].nextStep = StackNavigationStep::Exit;
204 
205  } else if (stack.back().nextStep == StackNavigationStep::ChildrenVisited) {
206  nodePostProcessAnalysis(stack.back());
207  stack.back().nextStep = StackNavigationStep::Exit;
208  stack.pop_back();
209 
210  } else {
211  stack.pop_back();
212  }
213 
214  }
215 }
216 
230 template<class Base, typename FunctionAnalysis>
231 inline void depthFirstGraphNavigation(OperationNode<Base>& root,
232  FunctionAnalysis& nodeAnalysis,
233  bool processRoot) {
235 
236  std::unique_ptr<OperationNode<Base>> fakeSuperRoot;
237  if (processRoot) {
238  fakeSuperRoot = OperationNode<Base>::makeTemporaryNode(CGOpCode::Alias, {}, {root});
239  stack.emplace_back(*fakeSuperRoot, 0);
240  } else {
241  stack.pushNodeArguments(root);
242  }
243 
244  while (!stack.empty()) {
245  auto nodeEl = stack.back(); // copy
246  stack.pop_back();
247 
248  nodeAnalysis(nodeEl, stack);
249  }
250 }
251 
252 } // END cg namespace
253 } // END CppAD namespace
254 
255 #endif
const std::vector< Argument< Base > > & getArguments() const
static std::unique_ptr< OperationNode< Base > > makeTemporaryNode(CGOpCode op, const std::vector< size_t > &info, const std::vector< Argument< Base > > &args)