CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
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 
21 namespace CppAD {
22 namespace cg {
23 
24 template<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
34 
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 
99 template<class Base>
100 inline 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 
239 template<class Base>
240 inline 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
std::vector< CG< Scalar > > evaluate(ArrayView< const CG< Scalar > > indepNew, ArrayView< const CG< Scalar > > depOld)
Definition: evaluator.hpp:93
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)