1 # ifndef CPPAD_LOCAL_OPTIMIZE_GET_CEXP_INFO_HPP
2 # define CPPAD_LOCAL_OPTIMIZE_GET_CEXP_INFO_HPP
3 /* --------------------------------------------------------------------------
4 CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-20 Bradley M. Bell
5 
6 CppAD is distributed under the terms of the
7              Eclipse Public License Version 2.0.
8 
9 This Source Code may also be made available under the following
10 Secondary License when the conditions for such availability set forth
11 in the Eclipse Public License, Version 2.0 are satisfied:
12       GNU General Public License, Version 2.0 or later.
13 ---------------------------------------------------------------------------- */
14 
15 # include <cppad/local/optimize/match_op.hpp>
16 # include <cppad/local/optimize/cexp_info.hpp>
17 # include <cppad/local/optimize/usage.hpp>
18 
19 // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
20 namespace CppAD { namespace local { namespace optimize {
21 
22 /*!
23 $begin optimize_get_cexp_info.hpp$$
24 $spell
25     cexp
26     itr
27     op
28     iterator
29     bool
30     Exp
31     deallocate
32     Funap
33     Funav
34     Funrp
35     Funrv
36     num
37     var
38 $$
39 
40 $section Information for Each Conditional Expression$$
41 
42 $head Syntax$$
43 $codei%get_cexp_info(
44     %play%,
45     %random_itr%,
46     %op_previous%,
47     %op_usage%,
48     %cexp2op%,
49     %cexp_set%,
50     %cexp_info%,
51     %skip_op_true%,
52     %skip_op_false%
53 )%$$
54 
55 $head Prototype$$
56 $srcthisfile%
57     0%// BEGIN_PROTOTYPE%// END_PROTOTYPE%1
58 %$$
59 
60 
61 $head Restrictions$$
62 Do not call this routine unless you are optimizing conditional expressions
63 and there are conditional expressions in the operation sequence.
64 
65 $head Base$$
66 base type for the operator; i.e., this operation was recorded
67 using AD<Base> and computations by this routine are done using type Base.
68 
69 $head play$$
70 This is the old operation sequence.
71 
72 $head random_itr$$
73 This is a random iterator for the old operation sequence.
74 
75 $head cexp2op$$
76 This is the number of conditional expressions in the operation sequence
77 and must be non-zero.
78 
79 $head cexp_set$$
80 This is a vector of sets, the i-th set, set[i],
81 is a set of elements for the i-th operator.
82 If e is an element of set[i], let j = e / 2 and k = e % 2.
83 If the comparison for the j-th conditional expression is equal to bool(k),
84 the i-th operator can be skipped (is not used by any of the results).
85 Note that j indexes the subset of operators that are conditional expressions
86 in the old operation sequence.
87 
88 $head cexp_info$$
89 The input size of this vector must be zero.
90 Upon return cexp_info has size equal to the number of conditional expressions
91 in the operation sequence; i.e., the number of CExpOp operators.
92 The value cexp_info[j] is the information corresponding to the j-th
93 conditional expression in the operation sequence.
94 This vector is in the same order as the operation sequence; i.e.
95 if j1 > j2, cexp_info[j1].i_op > cexp_info[j2].i_op.
96 Note that skip_op_true and skip_op_false could be part of this structure,
97 but then we would allocate and deallocate two vectors for each conditional
98 expression in the operation sequence.
99 
100 $head skip_op_true$$
101 This vector of sets is empty on input.
102 Upon return, the j-th set is the operators that are not used when
103 comparison result for cexp_info[j] is true.
104 Note that FunapOp, FunavOp, FunrpOp, and FunrvOp, are not in this
105 set and should be skipped when the corresponding AFunOp are skipped.
106 
107 $head skip_op_false$$
108 This vector of sets is empty on input.
109 Upon return, the j-th set is the operators that are not used when
110 comparison result for cexp_info[j] is false.
111 Note that FunapOp, FunavOp, FunrpOp, and FunrvOp, are not in this
112 set and should be skipped when the corresponding AFunOp are skipped.
113 
114 $head op_previous$$
115 This argument has size equal to the number of operators
116 in the operation sequence; i.e., num_op = play->nun_var_rec().
117 If op_previous[i] == 0, no replacement was found for the i-th operator.
118 If op_previous[i] != 0, op_usage[ op_previous[i] ] == usage_t(yes_usage).
119 
120 $head op_usage$$
121 This argument has size equal to the number of operators
122 in the operation sequence; i.e., num_op = play->nun_var_rec().
123 The value op_usage[i] is the usage for
124 the i-th operator in the operation sequence.
125 
126 $end
127 */
128 
129 // BEGIN_PROTOTYPE
130 template <class Addr, class Base>
get_cexp_info(const player<Base> * play,const play::const_random_iterator<Addr> & random_itr,const pod_vector<addr_t> & op_previous,const pod_vector<usage_t> & op_usage,const pod_vector<addr_t> & cexp2op,const sparse::list_setvec & cexp_set,vector<struct_cexp_info> & cexp_info,sparse::list_setvec & skip_op_true,sparse::list_setvec & skip_op_false)131 void get_cexp_info(
132     const player<Base>*                         play                ,
133     const play::const_random_iterator<Addr>&    random_itr          ,
134     const pod_vector<addr_t>&                   op_previous         ,
135     const pod_vector<usage_t>&                  op_usage            ,
136     const pod_vector<addr_t>&                   cexp2op             ,
137     const sparse::list_setvec&                  cexp_set            ,
138     vector<struct_cexp_info>&                   cexp_info           ,
139     sparse::list_setvec&                        skip_op_true        ,
140     sparse::list_setvec&                        skip_op_false       )
141 // END_PROTOTYPE
142 {
143     CPPAD_ASSERT_UNKNOWN( cexp_set.n_set() > 0  );
144     CPPAD_ASSERT_UNKNOWN( cexp_info.size() == 0 );
145 
146     // number of operators in the tape
147     const size_t num_op = play->num_op_rec();
148     CPPAD_ASSERT_UNKNOWN( op_usage.size() == num_op );
149     CPPAD_ASSERT_UNKNOWN( op_previous.size() == num_op );
150     //
151     // number of conditional expressions in the tape
152     size_t num_cexp_op = cexp2op.size();
153     //
154     // initialize mapping from variable index to operator index
155     CPPAD_ASSERT_UNKNOWN(
156         size_t( (std::numeric_limits<addr_t>::max)() ) >= num_op
157     );
158     // ----------------------------------------------------------------------
159     // compute cexp_info
160     // ----------------------------------------------------------------------
161     //
162     // initialize information for each conditional expression
163     cexp_info.resize(num_cexp_op);
164     skip_op_true.resize(num_cexp_op, num_op);
165     skip_op_false.resize(num_cexp_op, num_op);
166     //
167     for(size_t i = 0; i < num_cexp_op; i++)
168     {   size_t i_op = size_t( cexp2op[i] );
169         CPPAD_ASSERT_UNKNOWN(
170             op_previous[i_op] == 0 || op_usage[i_op] == usage_t(yes_usage)
171         );
172         OpCode        op;     // operator
173         const addr_t* arg;    // arguments
174         size_t        i_var;  // variable index of first result
175         random_itr.op_info(i_op, op, arg, i_var);
176         CPPAD_ASSERT_UNKNOWN( op == CExpOp );
177         //
178         struct_cexp_info info;
179         info.i_op       = addr_t(i_op);
180         info.cop        = CompareOp( arg[0] );
181         info.flag       = static_cast<unsigned char>(arg[1]);
182         info.left       = arg[2];
183         info.right      = arg[3];
184         //
185         // max_left_right
186         addr_t index    = 0;
187         if( arg[1] & 1 )
188             index = std::max<addr_t>(index, info.left);
189         if( arg[1] & 2 )
190             index = std::max<addr_t>(index, info.right);
191         info.max_left_right = index;
192         //
193         cexp_info[i] = info;
194     };
195     // Determine which operators can be conditionally skipped
196     size_t i_op = 0;
197     while(i_op < num_op)
198     {   size_t j_op = i_op;
199         bool keep = op_usage[i_op] != usage_t(no_usage);
200         keep     &= op_usage[i_op] != usage_t(csum_usage);
201         keep     &= op_previous[i_op] == 0;
202         if( keep )
203         {   sparse::list_setvec_const_iterator itr(cexp_set, i_op);
204             if( *itr != cexp_set.end() )
205             {   if( play->GetOp(i_op) == AFunOp )
206                 {   // i_op is the first operations in this atomic function call.
207                     // Find the last operation in this call.
208                     ++j_op;
209                     while( play->GetOp(j_op) != AFunOp )
210                     {   switch( play->GetOp(j_op) )
211                         {   case FunapOp:
212                             case FunavOp:
213                             case FunrpOp:
214                             case FunrvOp:
215                             break;
216 
217                             default:
218                             CPPAD_ASSERT_UNKNOWN(false);
219                         }
220                         ++j_op;
221                     }
222                 }
223             }
224             while( *itr != cexp_set.end() )
225             {   size_t element = *itr;
226                 size_t index   = element / 2;
227                 bool   compare = bool( element % 2 );
228                 if( compare == false )
229                 {   // cexp_info[index].skip_op_false.push_back(i_op);
230                     skip_op_false.post_element(index, i_op);
231                     if( j_op != i_op )
232                     {   // cexp_info[index].skip_op_false.push_back(j_op);
233                         skip_op_false.post_element(index, j_op);
234                     }
235                 }
236                 else
237                 {   // cexp_info[index].skip_op_true.push_back(i_op);
238                     skip_op_true.post_element(index, i_op);
239                     if( j_op != i_op )
240                     {   // cexp_info[index].skip_op_true.push_back(j_op);
241                         skip_op_true.post_element(index, j_op);
242                     }
243                 }
244                 ++itr;
245             }
246         }
247         CPPAD_ASSERT_UNKNOWN( i_op <= j_op );
248         i_op += (1 + j_op) - i_op;
249     }
250     // process postings for skip_op_false, skip_op_true
251     for(size_t i = 0; i < num_cexp_op; ++i)
252     {   skip_op_false.process_post(i);
253         skip_op_true.process_post(i);
254     }
255     return;
256 }
257 
258 } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
259 
260 # endif
261