CppAD: A C++ Algorithmic Differentiation Package  20171217
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
match_op.hpp
Go to the documentation of this file.
1 # ifndef CPPAD_LOCAL_OPTIMIZE_MATCH_OP_HPP
2 # define CPPAD_LOCAL_OPTIMIZE_MATCH_OP_HPP
3 /* --------------------------------------------------------------------------
4 CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-17 Bradley M. Bell
5 
6 CppAD is distributed under multiple licenses. This distribution is under
7 the terms of the
8  Eclipse Public License Version 1.0.
9 
10 A copy of this license is included in the COPYING file of this distribution.
11 Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
12 -------------------------------------------------------------------------- */
14 /*!
15 \file match_op.hpp
16 Check if current operator matches a previous operator.
17 */
18 // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
19 namespace CppAD { namespace local { namespace optimize {
20 /*!
21 Search for a previous operator that matches the current one.
22 
23 If an argument for the current operator is a variable,
24 and the argument has previous match,
25 the previous match for the argument is used when checking for a match
26 for the current operator.
27 
28 \param play
29 This is the old operation sequence.
30 
31 \param opt_op_info
32 Mapping from operator index to operator information.
33 The input value of opt_op_info[current].previous is assumed to be zero.
34 If a match if found,
35 the output value of opt_op_info[current].previous is set to the
36 matching operator index, otherwise it is left as is.
37 Note that opt_op_info[current].previous < current.
38 
39 \param current
40 is the index of the current operator which must be an unary
41 or binary operator. Note that NumArg(ErfOp) == 3 but it is effectivey
42 a unary operator and is allowed otherwise NumArg( opt_op_info[current].op) < 3.
43 It is assumed that hash_table_op is initialized as a vector of emtpy
44 sets. After this initialization, the value of current inceases with
45 each call to match_op.
46 
47 \li
48 This must be a unary or binary
49 operator; hence, NumArg( opt_op_info[current].op ) is one or two.
50 There is one exception, NumRes( ErfOp ) == 3, but arg[0]
51 is the only true arguments (the others are always the same).
52 
53 \li
54 This must not be a VecAD load or store operation; i.e.,
55 LtpvOp, LtvpOp, LtvvOp, StppOp, StpvOp, StvpOp, StvvOp.
56 It also must not be an independent variable operator InvOp.
57 
58 \param hash_table_op
59 is a vector of sets,
60 hash_table_op.n_set() == CPPAD_HASH_TABLE_SIZE and
61 hash_table_op.end() == opt_op_info.size().
62 If i_op is an element of set[j],
63 then the operation opt_op_info[i_op] has hash code j,
64 and opt_op_info[i_op] does not match any other element of set[j].
65 An entry will be added each time match_op is called
66 and a match for the current operator is not found.
67 */
68 template <class Base>
69 void match_op(
70  const player<Base>* play ,
71  vector<struct_opt_op_info>& opt_op_info ,
72  size_t current ,
73  sparse_list& hash_table_op )
74 { //
75  size_t num_op = play->num_op_rec();
76  //
77  CPPAD_ASSERT_UNKNOWN( num_op == opt_op_info.size() );
78  CPPAD_ASSERT_UNKNOWN( opt_op_info[current].previous == 0 );
80  hash_table_op.n_set() == CPPAD_HASH_TABLE_SIZE
81  );
82  CPPAD_ASSERT_UNKNOWN( hash_table_op.end() == num_op );
83  CPPAD_ASSERT_UNKNOWN( current < num_op );
84  //
85  // current operator
86  OpCode op;
87  const addr_t* arg;
88  size_t i_var;
89  play->get_op_info(current, op, arg, i_var);
90  CPPAD_ASSERT_UNKNOWN( 0 < NumArg(op) );
91  CPPAD_ASSERT_UNKNOWN( NumArg(op) <= 3 );
92  //
93  pod_vector<bool> variable(3);
94  arg_is_variable(op, arg, variable);
95  CPPAD_ASSERT_UNKNOWN( variable.size() == 3 );
96  //
97  // If j-th argument to current operator has a previous operator,
98  // this is the j-th argument for previous operator.
99  // Otherwise, it is the j-th argument for the current operator.
100  addr_t arg_match[3];
101  size_t num_arg = NumArg(op);
102  for(size_t j = 0; j < num_arg; ++j)
103  { arg_match[j] = arg[j];
104  if( variable[j] )
105  { size_t j_op = play->var2op(arg[j]);
106  size_t previous = opt_op_info[j_op].previous;
107  if( previous != 0 )
108  { // a previous match, be the end of the line; i.e.,
109  // it does not have a previous match.
110  CPPAD_ASSERT_UNKNOWN( opt_op_info[previous].previous == 0 );
111  //
112  OpCode op_p;
113  const addr_t* arg_p;
114  size_t i_var_p;
115  play->get_op_info(previous, op_p, arg_p, i_var_p);
116  //
117  CPPAD_ASSERT_UNKNOWN( NumRes(op_p) > 0 );
118  arg_match[j] = addr_t( i_var_p );
119  }
120  }
121  }
122  size_t code = optimize_hash_code(op, num_arg, arg_match);
123  //
124  // iterator for the set with this hash code
125  sparse_list_const_iterator itr(hash_table_op, code);
126  //
127  // check for a match
128  size_t count = 0;
129  while( *itr != num_op )
130  { ++count;
131  //
132  // candidate previous for current operator
133  size_t candidate = *itr;
134  CPPAD_ASSERT_UNKNOWN( candidate < current );
135  CPPAD_ASSERT_UNKNOWN( opt_op_info[candidate].previous == 0 );
136  //
137  OpCode op_c;
138  const addr_t* arg_c;
139  size_t i_var_c;
140  play->get_op_info(candidate, op_c, arg_c, i_var_c);
141  //
142  // check for a match
143  bool match = op == op_c;
144  if( match )
145  { for(size_t j = 0; j < num_arg; j++)
146  { if( variable[j] )
147  { size_t previous =
148  opt_op_info[ play->var2op(arg_c[j]) ].previous;
149  if( previous != 0 )
150  { // must be end of the line for a previous match
152  opt_op_info[previous].previous == 0
153  );
154  //
155  OpCode op_p;
156  const addr_t* arg_p;
157  size_t i_var_p;
158  play->get_op_info(previous, op_p, arg_p, i_var_p);
159  //
160  match &= arg_match[j] == addr_t( i_var_p );
161  }
162  else match &= arg_match[j] == arg_c[j];
163  }
164  else
165  { match &= arg_match[j] == arg_c[j];
166  }
167  }
168  }
169  if( match )
170  { opt_op_info[current].previous = static_cast<addr_t>( candidate );
171  return;
172  }
173  ++itr;
174  }
175 
176  // special case where operator is commutative
177  if( (op == AddvvOp) | (op == MulvvOp ) )
178  { CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 );
179  std::swap( arg_match[0], arg_match[1] );
180  //
181  code = optimize_hash_code(op, num_arg, arg_match);
182  sparse_list_const_iterator itr_swap(hash_table_op, code);
183  while( *itr_swap != num_op )
184  {
185  size_t candidate = *itr_swap;
186  CPPAD_ASSERT_UNKNOWN( candidate < current );
187  CPPAD_ASSERT_UNKNOWN( opt_op_info[candidate].previous == 0 );
188  //
189  OpCode op_c;
190  const addr_t* arg_c;
191  size_t i_var_c;
192  play->get_op_info(candidate, op_c, arg_c, i_var_c);
193  //
194  bool match = op == op_c;
195  if( match )
196  { for(size_t j = 0; j < num_arg; j++)
197  { CPPAD_ASSERT_UNKNOWN( variable[j] )
198  size_t previous =
199  opt_op_info[ play->var2op(arg_c[j]) ].previous;
200  if( previous != 0 )
202  opt_op_info[previous].previous == 0
203  );
204  //
205  OpCode op_p;
206  const addr_t* arg_p;
207  size_t i_var_p;
208  play->get_op_info(previous, op_p, arg_p, i_var_p);
209  //
210  match &= arg_match[j] == addr_t( i_var_p );
211  }
212  else
213  match &= arg_match[j] == arg_c[j];
214  }
215  }
216  if( match )
217  { opt_op_info[current].previous = static_cast<addr_t>(candidate);
218  return;
219  }
220  ++itr_swap;
221  }
222  }
223  CPPAD_ASSERT_UNKNOWN( count < 11 );
224  if( count == 10 )
225  { // restart the list
226  hash_table_op.clear(code);
227  }
228  // no match was found, add this operator the the set for this hash code
229  hash_table_op.add_element(code, current);
230 }
231 
232 } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
233 
234 # endif
size_t end(void) const
Fetch end for this vector of sets object.
Vector of sets of positive integers, each set stored as a singly linked list.
Definition: sparse_list.hpp:35
size_t arg_is_variable(OpCode op, const addr_t *arg, pod_vector< bool > &is_variable)
Determines which arguments are variaibles for an operator.
Definition: op_code.hpp:911
void add_element(size_t i, size_t element)
Add one element to a set.
The CppAD Simple Vector template class.
Definition: vector.hpp:338
CPPAD_TAPE_ADDR_TYPE addr_t
Definition: declare_ad.hpp:44
size_t optimize_hash_code(OpCode op, size_t num_arg, const addr_t *arg)
Specialized hash code for a CppAD operator and its arguments (used during optimization).
size_t NumArg(OpCode op)
Number of arguments for a specified operator.
Definition: op_code.hpp:175
Class used to store and play back an operation sequence recording.
Definition: declare_ad.hpp:27
size_t NumRes(OpCode op)
Number of variables resulting from the specified operation.
Definition: op_code.hpp:281
void match_op(const player< Base > *play, vector< struct_opt_op_info > &opt_op_info, size_t current, sparse_list &hash_table_op)
Search for a previous operator that matches the current one.
Definition: match_op.hpp:69
size_t size(void) const
number of elements currently in this vector.
Definition: vector.hpp:387
OpCode
Type used to distinguish different AD&lt; Base &gt; atomic operations.
Definition: op_code.hpp:49
void get_op_info(size_t op_index, OpCode &op, const addr_t *&op_arg, size_t &var_index) const
fetch the information corresponding to an operator
Definition: player.hpp:485
size_t size(void) const
current number of elements in this vector.
Definition: pod_vector.hpp:79
CppAD hashing utility.
#define CPPAD_ASSERT_UNKNOWN(exp)
Check that exp is true, if not terminate execution.
void clear(size_t target)
Assign the empty set to one of the sets.
size_t num_op_rec(void) const
Fetch number of operators in the recording.
Definition: player.hpp:622
size_t n_set(void) const
Fetch n_set for vector of sets object.
size_t var2op(size_t var_index) const
fetch the operator corresponding to a primary variable
Definition: player.hpp:461
cons_iterator for one set of positive integers in a sparse_list object.
#define CPPAD_HASH_TABLE_SIZE
the codes retruned by hash_code are between zero and CPPAD_HASH_TABLE_SIZE minus one.
Definition: base_hash.hpp:82