CppADCodeGen 2.4.3
A C++ Algorithmic Differentiation Package with Source Code Generation
Loading...
Searching...
No Matches
collect_variable.hpp
1#ifndef CPPAD_CG_COLLECT_VARIABLE_INCLUDED
2#define CPPAD_CG_COLLECT_VARIABLE_INCLUDED
3/* --------------------------------------------------------------------------
4 * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5 * Copyright (C) 2016 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#include <cppad/cg/evaluator/evaluator_solve.hpp>
19#include <cppad/cg/lang/dot/dot.hpp>
20
21namespace CppAD {
22namespace cg {
23
24template<class Base>
26 const SourceCodePath& path1,
27 const SourceCodePath& path2,
28 size_t lastCommon) {
29 CPPADCG_ASSERT_UNKNOWN(lastCommon >= 0);
30 CPPADCG_ASSERT_UNKNOWN(path1.size() > lastCommon);
31 CPPADCG_ASSERT_UNKNOWN(path2.size() > lastCommon);
32
33#ifndef NDEBUG
37 for(size_t i = 0;i < lastCommon; ++i) {
38 // compare with the first path
39 CPPADCG_ASSERT_UNKNOWN(path2[i] == path1[i]);
40 }
41#endif
42
43 //std::cout << "common sub-expression (before):\n";
44 //printExpression(*path1[lastCommon].node);
45
46 SourceCodePath leftPath(path1.begin() + lastCommon, path1.end());
47 SourceCodePath rightPath(path2.begin() + lastCommon, path2.end());
48
49 CG<Base> subExpression = collectVariableAddSub(leftPath, rightPath);
50
51 //std::cout << "common sub-expression (after):\n";
52 //printExpression(subExpression);
53
57 std::vector<CG<Base>*> replace(lastCommon + 1, nullptr);
58 //replace.back() = &subExpression;
59 std::vector<const std::vector<CG<Base>*>*> replaceOnPath{&replace};
60
61 SourceCodePath initPath(path1.begin(), path1.begin() + lastCommon + 1);
62 std::vector<const SourceCodePath*> initPaths{&initPath};
63
64 //std::map<OperationNode<Base>*, CG<Base> > replacementNodes;
65 //replacementNodes[path1[lastCommon].node] = subExpression; // it might be used in more places within the model
66
67 // use same independent variables
68 std::vector<CG<Base>> indep(this->_independentVariables.size());
69 for (size_t i = 0; i < indep.size(); ++i)
70 indep[i] = CG<Base>(*this->_independentVariables[i]);
71
72 CG<Base> expressionOrig(expression);
73 CG<Base> expression2;
74
75 size_t bifurcations = 0;
76 BidirGraph<Base> graphToCommon = findPathGraph(expression, *path1[lastCommon].node, bifurcations);
77 std::map<const PathNodeEdges<Base>*, CG<Base>> replacementNodes;
78 replacementNodes[&graphToCommon[*path1[lastCommon].node]] = subExpression;
79
80 EvaluatorCloneSolve<Base> e(*this, graphToCommon, replacementNodes);
81 e.evaluate(indep.data(), indep.size(),
82 &expression2, &expressionOrig, 1);
83
84
85 //std::cout << "expression before replacement:\n";
86 //printExpression(expressionOrig);
87 //printDotExpression(expressionOrig);
88
89 //std::cout << "expression after replacement:\n";
90 //printExpression(expression2);
91 // TODO:
92 // exp(x)*exp(x+1) = exp(x+x+1)
93 // pow(2, x)*pow(2, x+1) = pow(2, x+x+1)
94 // 1 / (2*x) + 2 / (3*x) = (1*3 + 2*2) / (6*x)
95
96 return expression2;
97}
98
99template<class Base>
100inline CG<Base> CodeHandler<Base>::collectVariableAddSub(const SourceCodePath& pathLeft,
101 const SourceCodePath& pathRight) {
105 if(pathLeft[0].node == nullptr) {
106 throw CGException("Invalid path!");
107 } else if(pathLeft[0].node != pathRight[0].node) {
108 throw CGException("The first element in each path must be the same.");
109 }
110
111 auto& expression = *pathLeft[0].node;
112
113 auto initCommonOp = expression.getOperationType();
114 if (initCommonOp != CGOpCode::Add &&
115 initCommonOp != CGOpCode::Sub) {
116 throw CGException("Invalid path! It must start with either an addition or subtraction.");
117 }
118
119 if(pathLeft.back().node == nullptr || pathRight.back().node == nullptr)
120 throw CGException("Invalid path! It must end with a non null node.");
121
122 if(pathLeft.back().node != pathRight.back().node)
123 throw CGException("Invalid paths! They must end with the same node.");
124
125 OperationNode<Base>& var = *pathLeft.back().node;
126
130 isCollectableVariableAddSub(pathLeft, pathRight, true);
131
135 CG<Base> zero(0);
136 std::array<std::vector<CG<Base>*>, 2> replace; // replacements for the existing operations
137
138 std::vector<const SourceCodePath*> paths{&pathLeft, &pathRight};
139
140 CG<Base> cSum;
141
142 for (size_t j = 0; j < paths.size(); ++j) {
143 const auto& p = *paths[j];
144 replace[j] = std::vector<CG<Base>*>(p.size(), nullptr);
145 replace[j].back() = &zero; // the last node is always removed (its the variable we want to extract)
146
150 CG<Base> c = 1;
151 if (initCommonOp == CGOpCode::Sub && j == 1) {
152 c = -1;
153 }
154
155 for (size_t i = 1; i < p.size() - 1; ++i) { // the last node is what we want to extract
156 const auto* node = p[i].node;
157 if (node == nullptr) {
158 throw CGException("Failed to combine multiple occurrences of a variable into one expression");
159 }
160
161 auto op = node->getOperationType();
162
163 if (op == CGOpCode::Add) {
164 continue;
165
166 } else if (op == CGOpCode::Sub) {
167 if (p[i - 1].argIndex == 1)
168 c = -c;
169 else
170 continue;
171
172 } else if (op == CGOpCode::UnMinus) {
173 c = -c;
174
175 } else if (op == CGOpCode::Mul) {
176 CPPADCG_ASSERT_UNKNOWN(p[i].argIndex == 0 || p[i].argIndex == 1);
177 const auto& pArgs = node->getArguments();
178 c *= (p[i].argIndex == 0) ? pArgs[1] : pArgs[0];
179
180 } else if (op == CGOpCode::Div) {
181 CPPADCG_ASSERT_UNKNOWN(p[i].argIndex == 0);
182 c /= CG<Base>(node->getArguments()[1]);
183
184 } else if (op == CGOpCode::Alias) {
185 continue;
186
187 } else {
188 // should never get here
189 CPPADCG_ASSERT_UNKNOWN(false);
190 }
191
192 }
193
194 //
195 cSum += c;
196
197 for (size_t i1 = p.size() - 1; i1 > 0; --i1) {
198 size_t i = i1 - 1;
199 if (p[i].node->getOperationType() == CGOpCode::Mul ||
200 p[i].node->getOperationType() == CGOpCode::UnMinus ||
201 p[i].node->getOperationType() == CGOpCode::Div ||
202 p[i].node->getOperationType() == CGOpCode::Alias) {
203 replace[j][i] = &zero;
204 } else {
205 break;
206 }
207 }
208 }
209
210 const auto& replaceLeft = replace[0];
211 const auto& replaceRight = replace[1];
212 bool keepCommon = replaceLeft[1] == nullptr || replaceRight[1] == nullptr;
213
217 CG<Base> expression2;
218 if (keepCommon) {
219 std::vector<const std::vector<CG<Base>*>*> replaceOnPath{&replaceLeft, &replaceRight};
220 EvaluatorCloneSolve<Base> e(*this, paths, replaceOnPath);
221
222 std::vector<CG<Base>> indep(this->_independentVariables.size());
223 for (size_t i = 0; i < indep.size(); ++i)
224 indep[i] = CG<Base>(*this->_independentVariables[i]);
225
226 CG<Base> expressionOrig(expression);
227
228 e.evaluate(indep.data(), indep.size(),
229 &expression2, &expressionOrig, 1);
230 }
231
235 CG<Base> v(var);
236 return cSum * v + expression2;
237}
238
239template<class Base>
240inline bool CodeHandler<Base>::isCollectableVariableAddSub(const SourceCodePath& pathLeft,
241 const SourceCodePath& pathRight,
242 bool throwEx) {
246 std::vector<const SourceCodePath*> paths{&pathLeft, &pathRight};
247 for (const SourceCodePath* p: paths) {
248 for (size_t i = 1; i < p->size() - 1; ++i) { // the last node does not have to be +, -, or *
249 const auto* node = (*p)[i].node;
250
251 if (node == nullptr) {
252 if (throwEx)
253 throw CGException("Failed to combine multiple occurrences of a variable into one expression");
254 return false;
255 }
256
257 auto op = node->getOperationType();
258 if (op != CGOpCode::Add &&
259 op != CGOpCode::Sub &&
260 op != CGOpCode::Mul &&
261 op != CGOpCode::UnMinus &&
262 op != CGOpCode::Alias) {
263
264 if (op == CGOpCode::Div) {
265 if ((*p)[i].argIndex != 0) {
266 if (throwEx)
267 throw CGException("Unable to combine operations which are present in denominators (not implemented yet).");
268 return false;
269 } else {
270 continue;
271 }
272 }
273
274 if (throwEx)
275 throw CGException("Failed to combine multiple occurrences of a variable into one expression."
276 " Unable to combine operations diverging at a '", op, "' operation.");
277 return false;
278 }
279 }
280 }
281
282 return true;
283}
284
285} // END cg namespace
286} // END CppAD namespace
287
288#endif
bool isCollectableVariableAddSub(const SourceCodePath &pathLeft, const SourceCodePath &pathRight, bool throwEx)
CGB collectVariableAddSub(const SourceCodePath &pathLeft, const SourceCodePath &pathRight)
CGB collectVariable(Node &expression, const SourceCodePath &path1, const SourceCodePath &path2, size_t bifPos)
std::vector< ActiveOut > evaluate(ArrayView< const ActiveOut > indepNew, ArrayView< const CG< ScalarIn > > depOld)
Definition evaluator.hpp:93