CppADCodeGen  HEAD
A C++ Algorithmic Differentiation Package with Source Code Generation
equation_pattern.hpp
1 #ifndef CPPAD_CG_EQUATION_PATTERN_INCLUDED
2 #define CPPAD_CG_EQUATION_PATTERN_INCLUDED
3 /* --------------------------------------------------------------------------
4  * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5  * Copyright (C) 2013 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 
26 template<class Base>
28 public:
29  using MapDep2Indep_type = std::map<size_t, const OperationNode<Base>*>;
35  std::vector<MapDep2Indep_type> arg2Independents;
36 };
37 
38 template<class Base>
40 public:
41  std::map<const OperationNode<Base>*, OperationIndexedIndependents<Base> > op2Arguments;
42 public:
43 
44  const OperationIndexedIndependents<Base>& arguments(const OperationNode<Base>* operation) const {
45  return op2Arguments.at(operation);
46  }
47 
48  bool isIndexedOperationArgument(const OperationNode<Base>* node, size_t argIndex) const {
49  const auto itIndexes = op2Arguments.find(node);
50  if (itIndexes == op2Arguments.end()) {
51  return false;
52  }
53  const OperationIndexedIndependents<Base>& indexedArgs = itIndexes->second;
54  return indexedArgs.arg2Independents.size() > argIndex && !indexedArgs.arg2Independents[argIndex].empty();
55  }
56 
57 };
58 
63 template<class Base>
64 class EquationPattern {
65 public:
66  const CG<Base>& depRef; // dependent reference
67  const size_t depRefIndex;
68  std::set<size_t> dependents;
74  std::map<size_t, std::map<const OperationNode<Base>*, OperationNode<Base>*> > operationEO2Reference;
75  // std::map<size_t, std::vector<OperationNode<Base>*> > operationEO2Reference;
85  std::map<const OperationNode<Base>*, std::set<size_t> > constOperationIndependents;
86 
87 private:
88  CodeHandler<Base>* const handler_;
89  size_t currDep_;
90  size_t minColor_;
91  size_t cmpColor_;
92 public:
93 
94  explicit EquationPattern(const CG<Base>& ref,
95  size_t iDepRef) :
96  depRef(ref),
97  depRefIndex(iDepRef),
98  dependents {iDepRef},
99  handler_(ref.getCodeHandler()) {
100  }
101 
102  EquationPattern(const EquationPattern<Base>& other) = delete;
103 
104  EquationPattern& operator=(const EquationPattern<Base>& rhs) = delete;
105 
106  bool testAdd(size_t iDep2,
107  const CG<Base>& dep2,
108  size_t& minColor,
110  IndexedIndependent<Base> independentsBackup = indexedOpIndep;
111  std::map<const OperationNode<Base>*, std::set<size_t> > constOperationIndependentsBackup = constOperationIndependents;
112  std::map<size_t, std::map<const OperationNode<Base>*, OperationNode<Base>*> > operation2ReferenceBackup = operationEO2Reference;
113 
114  currDep_ = iDep2;
115  minColor_ = minColor;
116  cmpColor_ = minColor_;
117 
118  bool equals = comparePath(depRef, dep2, iDep2, varColor);
119 
120  minColor = cmpColor_;
121 
122  if (equals) {
123  dependents.insert(iDep2);
124 
125  return true; // matches the reference pattern
126  } else {
127  // restore
128  indexedOpIndep.op2Arguments.swap(independentsBackup.op2Arguments);
129  constOperationIndependents.swap(constOperationIndependentsBackup);
130  operationEO2Reference.swap(operation2ReferenceBackup);
131 
132  return false; // cannot be added
133  }
134  }
135 
136  inline void findIndexedPath(size_t dep,
137  const std::vector<CG<Base> >& depVals,
138  CodeHandlerVector<Base, bool>& varIndexed,
139  std::set<const OperationNode<Base>*>& indexedOperations) {
140  findIndexedPath(depRef, depVals[dep], varIndexed, indexedOperations);
141  }
142 
143  std::set<const OperationNode<Base>*> findOperationsUsingIndependents(OperationNode<Base>& node) const {
144  std::set<const OperationNode<Base>*> ops;
145 
146  handler_->startNewOperationTreeVisit();
147 
148  findOperationsWithIndeps(node, ops);
149 
150  return ops;
151  }
152 
153  static inline void uncolor(OperationNode<Base>* node,
154  CodeHandlerVector<Base, bool>& varIndexed) {
155  if (node == nullptr || !varIndexed[*node])
156  return;
157 
158  varIndexed[*node] = false;
159 
160  const std::vector<Argument<Base> >& args = node->getArguments();
161  size_t size = args.size();
162  for (size_t a = 0; a < size; a++) {
163  uncolor(args[a].getOperation(), varIndexed);
164  }
165  }
166 
167  inline bool containsConstantIndependent(const OperationNode<Base>* operation, size_t argumentIndex) const {
168  const auto it = constOperationIndependents.find(operation);
169  if (it != constOperationIndependents.end()) {
170  if (it->second.find(argumentIndex) != it->second.end()) {
171  return true;
172  }
173  }
174  return false;
175  }
176 
182  using MapIndep2Dep_type = typename OperationIndexedIndependents<Base>::MapDep2Indep_type;
183 
184  // loop operations using independents
185  auto itop2a = indexedOpIndep.op2Arguments.begin();
186  while (itop2a != indexedOpIndep.op2Arguments.end()) {
187  const OperationNode<Base>* parentOp = itop2a->first;
188  OperationIndexedIndependents<Base>& arg2It = itop2a->second;
189 
190  bool emptyOp = true;
191 
192  // loop the arguments
193  size_t aSize = arg2It.arg2Independents.size();
194  for (size_t argIndex = 0; argIndex < aSize; argIndex++) {
195  MapIndep2Dep_type& dep2Ind = arg2It.arg2Independents[argIndex];
196 
197  if (dep2Ind.empty())
198  continue; // argument does not use independents
199 
200  // loop dependents (iterations)
201  bool isIndexed = false;
202  typename MapIndep2Dep_type::const_iterator itDep2Ind = dep2Ind.begin();
203  const OperationNode<Base>* indep = itDep2Ind->second;
204 
205  for (++itDep2Ind; itDep2Ind != dep2Ind.end(); ++itDep2Ind) {
206  if (indep != itDep2Ind->second) {
207  isIndexed = true;
208  break;
209  }
210  }
211 
212  if (!isIndexed) {
213  // make it a non indexed independent
214  constOperationIndependents[parentOp].insert(argIndex);
215 
216  // remove it from the indexed independents
217  dep2Ind.clear();
218  } else {
219  emptyOp = false;
220  }
221 
222  }
223 
224  if (emptyOp) {
225  indexedOpIndep.op2Arguments.erase(itop2a++);
226  } else {
227  ++itop2a;
228  }
229 
230  }
231 
232  }
233 
234  virtual ~EquationPattern() = default;
235 
236 private:
237 
238  bool comparePath(const CG<Base>& dep1,
239  const CG<Base>& dep2,
240  size_t dep2Index,
242  CodeHandler<Base>* h1 = dep1.getCodeHandler();
243  CodeHandler<Base>* h2 = dep2.getCodeHandler();
244 
245  if (h1 != h2) {
246  if (h1 != nullptr && h2 != nullptr)
247  throw CGException("Only one code handler allowed");
248  return false;
249  }
250 
251  if (dep1.isParameter() && dep2.isParameter()) {
252  return dep1.getValue() == dep2.getValue();
253 
254  } else if (dep1.isVariable() && dep2.isVariable()) {
255  OperationNode<Base>* depRefOp = dep1.getOperationNode();
256  OperationNode<Base>* dep2Op = dep2.getOperationNode();
257  CPPADCG_ASSERT_UNKNOWN(depRefOp->getOperationType() != CGOpCode::Inv)
258 
259  return comparePath(depRefOp, dep2Op, dep2Index, varColor);
260  }
261 
262  return false;
263  }
264 
265  bool comparePath(OperationNode<Base>* scRef,
266  OperationNode<Base>* sc2,
267  size_t dep2,
269  saveOperationReference(dep2, sc2, scRef);
270  if (dependents.size() == 1) {
271  saveOperationReference(depRefIndex, scRef, scRef);
272  }
273 
274  while (scRef->getOperationType() == CGOpCode::Alias) {
275  CPPADCG_ASSERT_KNOWN(scRef->getArguments().size() == 1, "Invalid number of arguments for alias")
276  OperationNode<Base>* sc = scRef->getArguments()[0].getOperation();
277  if (sc != nullptr && sc->getOperationType() == CGOpCode::Inv) break; // an alias is used to distinguish between indexed dependents and indexed independents
278  scRef = sc;
279  }
280  while (sc2->getOperationType() == CGOpCode::Alias) {
281  CPPADCG_ASSERT_KNOWN(sc2->getArguments().size() == 1, "Invalid number of arguments for alias")
282  OperationNode<Base>* sc = sc2->getArguments()[0].getOperation();
283  if (sc != nullptr && sc->getOperationType() == CGOpCode::Inv) break; // an alias is used to distinguish between indexed dependents and indexed independents
284  sc2 = sc;
285  }
286 
287  // check if these nodes where visited before
288  if (varColor[*sc2] >= minColor_ && varColor[*scRef] >= minColor_) {
298  if (varColor[*sc2] == varColor[*scRef])
299  return true;
300  }
301  varColor[*scRef] = cmpColor_;
302  varColor[*sc2] = cmpColor_;
303  cmpColor_++;
304 
305 
306  if (scRef->getOperationType() != sc2->getOperationType()) {
307  return false;
308  }
309 
310  CPPADCG_ASSERT_UNKNOWN(scRef->getOperationType() != CGOpCode::Inv)
311 
312  const std::vector<size_t>& info1 = scRef->getInfo();
313  const std::vector<size_t>& info2 = sc2->getInfo();
314  if (info1.size() != info2.size()) {
315  return false;
316  }
317 
318  for (size_t e = 0; e < info1.size(); e++) {
319  if (info1[e] != info2[e]) {
320  return false;
321  }
322  }
323 
324  const std::vector<Argument<Base> >& args1 = scRef->getArguments();
325  const std::vector<Argument<Base> >& args2 = sc2->getArguments();
326  size_t size = args1.size();
327  if (size != args2.size()) {
328  return false;
329  }
330  for (size_t a = 0; a < size; a++) {
331  const Argument<Base>& a1 = args1[a];
332  const Argument<Base>& a2 = args2[a];
333 
334  if (a1.getParameter() != nullptr) {
335  if (a2.getParameter() == nullptr || *a1.getParameter() != *a2.getParameter())
336  return false;
337  } else {
338  if (a2.getOperation() == nullptr) {
339  return false;
340  }
341  OperationNode<Base>* argRefOp = a1.getOperation();
342  OperationNode<Base>* arg2Op = a2.getOperation();
343  bool related;
344  if (argRefOp->getOperationType() == CGOpCode::Inv) {
345  related = saveIndependent(scRef, a, argRefOp, arg2Op);
346  } else {
347  related = comparePath(argRefOp, arg2Op, dep2, varColor);
348  }
349 
350  if (!related)
351  return false;
352  }
353  }
354 
355  return true; // same pattern
356  }
357 
358  inline void saveOperationReference(size_t dep2,
359  const OperationNode<Base>* sc2,
360  OperationNode<Base>* scRef) {
361  operationEO2Reference[dep2][sc2] = scRef;
362  }
363 
364  bool saveIndependent(const OperationNode<Base>* parentOp,
365  size_t argIndex,
366  const OperationNode<Base>* argRefOp,
367  const OperationNode<Base>* arg2Op) {
368  if (argRefOp->getOperationType() != CGOpCode::Inv || arg2Op->getOperationType() != CGOpCode::Inv) {
369  return false;
370  }
371 
376  const auto it = constOperationIndependents.find(parentOp);
377  if (it != constOperationIndependents.end()) {
378  if (it->second.find(argIndex) != it->second.end()) {
379  return false;
380  }
381  }
382 
383  OperationIndexedIndependents<Base>& opIndexedIndep = indexedOpIndep.op2Arguments[parentOp];
384  opIndexedIndep.arg2Independents.resize(parentOp != nullptr ? parentOp->getArguments().size() : 1);
385 
386  std::map<size_t, const OperationNode<Base>*>& dep2Indeps = opIndexedIndep.arg2Independents[argIndex];
387  if (dep2Indeps.empty())
388  dep2Indeps[depRefIndex] = argRefOp;
389  dep2Indeps[currDep_] = arg2Op;
390 
391  return true; // same pattern
392  }
393 
394  inline void findIndexedPath(const CG<Base>& depRef,
395  const CG<Base>& dep2,
396  CodeHandlerVector<Base, bool>& varIndexed,
397  std::set<const OperationNode<Base>*>& indexedOperations) {
398  if (depRef.isVariable() && dep2.isVariable()) {
399  OperationNode<Base>* depRefOp = depRef.getOperationNode();
400  OperationNode<Base>* dep2Op = dep2.getOperationNode();
401  if (depRefOp->getOperationType() != CGOpCode::Inv) {
402  findIndexedPath(depRefOp, dep2Op, varIndexed, indexedOperations);
403  } else {
404 
405  typename std::map<const OperationNode<Base>*, OperationIndexedIndependents<Base> >::iterator itop2a;
406  itop2a = indexedOpIndep.op2Arguments.find(nullptr);
407  if (itop2a != indexedOpIndep.op2Arguments.end() && !itop2a->second.arg2Independents[0].empty()) {
408  // depends on an index
409  indexedOperations.insert(nullptr);
410  }
411  }
412  }
413  }
414 
415  inline bool findIndexedPath(const OperationNode<Base>* scRef,
416  OperationNode<Base>* sc2,
417  CodeHandlerVector<Base, bool>& varIndexed,
418  std::set<const OperationNode<Base>*>& indexedOperations) {
419 
420  while (scRef->getOperationType() == CGOpCode::Alias) {
421  CPPADCG_ASSERT_KNOWN(scRef->getArguments().size() == 1, "Invalid number of arguments for alias")
422  OperationNode<Base>* sc = scRef->getArguments()[0].getOperation();
423  if (sc != nullptr && sc->getOperationType() == CGOpCode::Inv) break; // an alias is used to distinguish between indexed dependents and indexed independents
424  scRef = sc;
425  }
426  while (sc2->getOperationType() == CGOpCode::Alias) {
427  CPPADCG_ASSERT_KNOWN(sc2->getArguments().size() == 1, "Invalid number of arguments for alias")
428  OperationNode<Base>* sc = sc2->getArguments()[0].getOperation();
429  if (sc != nullptr && sc->getOperationType() == CGOpCode::Inv) break; // an alias is used to distinguish between indexed dependents and indexed independents
430  sc2 = sc;
431  }
432 
433  CPPADCG_ASSERT_UNKNOWN(scRef->getOperationType() == sc2->getOperationType())
434 
435  const std::vector<Argument<Base> >& argsRef = scRef->getArguments();
436 
437  typename std::map<const OperationNode<Base>*, OperationIndexedIndependents<Base> >::iterator itop2a;
438  bool searched = false;
439  bool indexedDependentPath = false;
440  bool usesIndexedIndependent = false; // directly uses an indexed independent
441 
442  size_t size = argsRef.size();
443  for (size_t a = 0; a < size; a++) {
444  OperationNode<Base>* argRefOp = argsRef[a].getOperation();
445  if (argRefOp != nullptr) {
446  bool indexedArg = false;
447  if (argRefOp->getOperationType() == CGOpCode::Inv) {
448  // same independent variable can be used in multiple iterations
449  if (!searched) {
450  itop2a = indexedOpIndep.op2Arguments.find(scRef);
451  searched = true;
452  }
453  if (itop2a != indexedOpIndep.op2Arguments.end() && !itop2a->second.arg2Independents[a].empty()) {
454  // depends on an index
455  indexedArg = true;
456  indexedDependentPath = true;
457  usesIndexedIndependent = true;
458  }
459  }
460 
461  if (!indexedArg) {
462  const std::vector<Argument<Base> >& args2 = sc2->getArguments();
463  CPPADCG_ASSERT_UNKNOWN(size == args2.size())
464  indexedDependentPath |= findIndexedPath(argsRef[a].getOperation(), args2[a].getOperation(), varIndexed, indexedOperations);
465  }
466  }
467  }
468 
469  varIndexed[*sc2] = indexedDependentPath;
470 
471  if (usesIndexedIndependent)
472  indexedOperations.insert(sc2);
473 
474  return indexedDependentPath;
475  }
476 
477  void findOperationsWithIndeps(OperationNode<Base>& node,
478  std::set<const OperationNode<Base>*>& ops) const {
479  if (handler_->isVisited(node))
480  return; // been here before
481 
482  handler_->markVisited(node);
483 
484  const std::vector<Argument<Base> >& args = node.getArguments();
485  size_t size = args.size();
486  for (size_t a = 0; a < size; a++) {
487  OperationNode<Base>* argOp = args[a].getOperation();
488  if (argOp != nullptr) {
489  if (argOp->getOperationType() == CGOpCode::Inv) {
490  ops.insert(&node);
491  } else {
492  findOperationsWithIndeps(*argOp, ops);
493  }
494  }
495  }
496  }
497 
498 };
499 
500 } // END cg namespace
501 } // END CppAD namespace
502 
503 #endif
std::map< const OperationNode< Base > *, std::set< size_t > > constOperationIndependents
std::map< size_t, std::map< const OperationNode< Base > *, OperationNode< Base > * > > operationEO2Reference
const Base & getValue() const
Definition: variable.hpp:45
const std::vector< Argument< Base > > & getArguments() const
bool isVariable() const
Definition: variable.hpp:30
std::vector< MapDep2Indep_type > arg2Independents
IndexedIndependent< Base > indexedOpIndep
CGOpCode getOperationType() const
CodeHandler< Base > * getCodeHandler() const
Definition: variable.hpp:22
bool isParameter() const
Definition: variable.hpp:35
const std::vector< size_t > & getInfo() const