CppADCodeGen 2.4.3
A C++ Algorithmic Differentiation Package with Source Code Generation
Loading...
Searching...
No Matches
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
18namespace CppAD {
19namespace cg {
20
24enum 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
35template<class Base>
37private:
38 OperationNode<Base>* const _parent;
39 const size_t _node;
40public:
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
66template<class Base>
68public:
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
87template<class Element>
89private:
90 std::vector<Element> _stack;
91public:
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
125template<class Base>
126class OperationStack : public BaseOperationStack<OperationStackData<Base> > {
127public:
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
142template<class Base>
143class SimpleOperationStack : public BaseOperationStack<SimpleOperationStackData<Base> > {
144public:
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
177template<class Base, typename FunctionAnalysis, typename FunctionPostProcess>
178inline void depthFirstGraphNavigation(OperationNode<Base>& root,
179 size_t currentScopeColor,
180 FunctionAnalysis& nodeAnalysis,
181 FunctionPostProcess& nodePostProcessAnalysis,
182 bool processRoot) {
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
230template<class Base, typename FunctionAnalysis>
231inline void depthFirstGraphNavigation(OperationNode<Base>& root,
232 FunctionAnalysis& nodeAnalysis,
233 bool processRoot) {
234 SimpleOperationStack<Base> stack;
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
static std::unique_ptr< OperationNode< Base > > makeTemporaryNode(CGOpCode op, const std::vector< size_t > &info, const std::vector< Argument< Base > > &args)
const std::vector< Argument< Base > > & getArguments() const