1 # ifndef CPPAD_LOCAL_OPTIMIZE_GET_OP_USAGE_HPP
2 # define CPPAD_LOCAL_OPTIMIZE_GET_OP_USAGE_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 # include <cppad/local/optimize/cexp_info.hpp>
15 # include <cppad/local/optimize/usage.hpp>
16 # include <cppad/local/sweep/call_atomic.hpp>
17 
18 // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
19 namespace CppAD { namespace local { namespace optimize {
20 
21 /// Is this an addition or subtraction operator
op_add_or_sub(OpCode op)22 inline bool op_add_or_sub(
23     OpCode op ///< operator we are checking
24 )
25 {   bool result;
26     switch(op)
27     {
28         case AddpvOp:
29         case AddvvOp:
30         case SubpvOp:
31         case SubvpOp:
32         case SubvvOp:
33         result = true;
34         break;
35 
36         default:
37         result = false;
38         break;
39     }
40     return result;
41 }
42 
43 /*!
44 $begin optimize_op_inc_arg_usage$$
45 $spell
46     cexp
47     op
48     arg
49     csum
50     optimizer
51 $$
52 
53 $section
54 Increase Argument Usage and Propagate cexp_set From Result to Argument
55 $$
56 
57 $head Prototype$$
58 $srcthisfile%
59     0%// BEGIN_OP_INC_ARG_USAGE%// END_PROTOTYPE%1
60 %$$
61 
62 
63 $head play$$
64 is the player for the old operation sequence.
65 
66 $head check_csum$$
67 is result an addition or subtraction operator,
68 and the optimizer is allowed to generate cumulative sum operators.
69 
70 $head i_result$$
71 is the operator index for the result operator.
72 There are no posting waiting to be processed for the corresponding cexp_set.
73 
74 $head i_arg$$
75 is the operator index for the argument to the result operator.
76 There may be postings waiting to be processed for the corresponding cexp_set.
77 
78 $head op_usage$$
79 structure that holds the information for each of the operators.
80 The output value of op_usage[i_arg] is increased; to be specific,
81 If check_csum is true and the input value of op_usage[i_arg]
82 is usage_t(no_usage), its output value is usage_t(csum_usage).
83 Otherwise, the output value of op_usage[i_arg] is usage_t(yes_usage).
84 
85 $head cexp_set$$
86 This is a vector of sets with one set for each operator.
87 We denote the i-th set by set[i].
88 These are the conditional expression conditions that must be
89 satisfied for this argument to be used.
90 
91 $list number$$
92 In the special case where cexp_set.n_set() is zero,
93 cexp_set is not changed.
94 $lnext
95 If cexp_set.n_set() != 0 and op_usage[i_arg] == usage_t(no_usage),
96 the input value of set[i_arg] must be empty.
97 In this case the output value if set[i_arg] is equal to set[i_result]
98 (which may also be empty).
99 $lnext
100 If cexp_set.n_set() != 0 and op_usage[i_arg] != usage_t(no_usage),
101 the output value of set[i_arg] is the intersection of
102 its input value and set[i_result].
103 $lend
104 
105 $end
106 */
107 // BEGIN_OP_INC_ARG_USAGE
108 template <class Base>
op_inc_arg_usage(const player<Base> * play,bool check_csum,size_t i_result,size_t i_arg,pod_vector<usage_t> & op_usage,sparse::list_setvec & cexp_set)109 void op_inc_arg_usage(
110     const player<Base>*         play           ,
111     bool                        check_csum     ,
112     size_t                      i_result       ,
113     size_t                      i_arg          ,
114     pod_vector<usage_t>&        op_usage       ,
115     sparse::list_setvec&        cexp_set       )
116 // END_PROTOTYPE
117 {   // value of argument input on input to this routine
118     enum_usage arg_usage = enum_usage( op_usage[i_arg] );
119     //
120     // new value for usage
121     op_usage[i_arg] = usage_t(yes_usage);
122     if( check_csum )
123     {   if( arg_usage == no_usage )
124         {   OpCode op_a = play->GetOp(i_arg);
125             if( op_add_or_sub( op_a ) )
126             {   op_usage[i_arg] = usage_t(csum_usage);
127             }
128         }
129     }
130     //
131     // cexp_set
132     if( cexp_set.n_set() == 0 )
133         return;
134     //
135     if( arg_usage == no_usage )
136     {   // set[i_arg] = set[i_result]
137         // not necessary to process posts for set i_result
138         cexp_set.assignment(i_arg, i_result, cexp_set);
139     }
140     else
141     {   // set[i_arg] = set[i_arg] intersect set[i_result]
142         // is necessary to process postts for set i_arg
143         cexp_set.process_post(i_arg);
144         cexp_set.binary_intersection(i_arg, i_arg, i_result, cexp_set);
145     }
146     //
147     return;
148 }
149 
150 /*!
151 $begin optimize_get_op_usage$$
152 $spell
153     Ind
154     var
155     itr
156     dep_taddr
157     cexp
158     vecad
159     Addr
160     iterator
161     NumRes
162     PriOp
163     Exp
164     bool
165     Vec
166 $$
167 
168 $section
169 Use Reverse Activity Analysis to Get Usage Information for Each Operator
170 $$
171 
172 $head Prototype$$
173 $srcthisfile%
174     0%// BEGIN_GET_OP_USAGE%// END_PROTOTYPE%1
175 %$$
176 
177 $head Base$$
178 Base type for the operator; i.e., this operation was recorded
179 using $codei%AD<%Base%>%$$ and computations by this routine are done
180 using type $icode Base$$.
181 
182 $head Addr$$
183 Type used by random iterator for the player.
184 
185 $head cumulative_sum_op$$
186 If this is true (false), cumulative summation operator are allowed
187 (not allowed) to be generated by the optimization.
188 
189 $head compare_op$$
190 if this is true, arguments are considered used if they appear in compare
191 operators. This is a side effect because compare operators have boolean
192 results (and the result is not in the tape; i.e. NumRes(op) is zero
193 for these operators. (This is an example of a side effect.)
194 
195 $head print_for_op$$
196 if this is true, arguments are considered used if they appear in
197 print forward operators; i.e., PriOp.
198 This is also a side effect; i.e. NumRes(PriOp) is zero.
199 
200 $head conditional_skip$$
201 If this is true,
202 the conditional expression information cexp_info will be calculated.
203 This may be time intensive and may not have much benefit in the optimized
204 recording.
205 
206 
207 $head play$$
208 This is the operation sequence.
209 
210 $head random_itr$$
211 This is a random iterator for the operation sequence.
212 
213 $head dep_taddr$$
214 is a vector of indices for the dependent variables
215 (where the reverse activity analysis starts).
216 
217 $head cexp2op$$
218 The input size of this vector must be zero.
219 Upon return it has size equal to the number of conditional expressions,
220 CExpOp operators. The value $icode%cexp2op[%j%]%$$ is the operator
221 index corresponding to the $th j$$ conditional expressions.
222 
223 $head cexp_set$$
224 This is a vector of sets that is empty on input.
225 If $icode conditional_skip$$ is false, $icode cexp_usage$$ is not modified.
226 Otherwise, set[i] is a set of elements for the i-th operator.
227 Suppose that e is an element of set[i], j = e / 2, k = e % 2.
228 If the comparison for the j-th conditional expression is equal to bool(k),
229 the i-th operator can be skipped (is not used by any of the results).
230 Note that j indexes the CExpOp operators in the operation sequence.
231 
232 $head vecad_used$$
233 The input size of this vector must be zero.
234 Upon return it has size equal to the number of VecAD vectors
235 in the operations sequences; i.e., play->num_var_vecad_rec().
236 The VecAD vectors are indexed in the order that their indices appear
237 in the one large play->GetVecInd that holds all the VecAD vectors.
238 
239 $head op_usage$$
240 The input size of this vector must be zero.
241 Upon return it has size equal to the number of operators
242 in the operation sequence; i.e., num_op = play->nun_var_rec().
243 The value $icode%op_usage%[%i%]%$$ has been set to the usage for
244 the i-th operator in the operation sequence.
245 Atomic function calls are a special case,
246 the first and second AFunOp have usage corresponding to the entire call.
247 The arguments have the usage for particular parameter or variable.
248 This usage is only for creating variables, not for creating
249 dynamic parameters.
250 
251 $end
252 */
253 // BEGIN_GET_OP_USAGE
254 template <class Addr, class Base>
get_op_usage(bool conditional_skip,bool compare_op,bool print_for_op,bool cumulative_sum_op,const player<Base> * play,const play::const_random_iterator<Addr> & random_itr,const pod_vector<size_t> & dep_taddr,pod_vector<addr_t> & cexp2op,sparse::list_setvec & cexp_set,pod_vector<bool> & vecad_used,pod_vector<usage_t> & op_usage)255 void get_op_usage(
256     bool                                        conditional_skip    ,
257     bool                                        compare_op          ,
258     bool                                        print_for_op        ,
259     bool                                        cumulative_sum_op   ,
260     const player<Base>*                         play                ,
261     const play::const_random_iterator<Addr>&    random_itr          ,
262     const pod_vector<size_t>&                   dep_taddr           ,
263     pod_vector<addr_t>&                         cexp2op             ,
264     sparse::list_setvec&                        cexp_set            ,
265     pod_vector<bool>&                           vecad_used          ,
266     pod_vector<usage_t>&                        op_usage            )
267 // END_PROTOTYPE
268 {
269     CPPAD_ASSERT_UNKNOWN( cexp_set.n_set()  == 0 );
270     CPPAD_ASSERT_UNKNOWN( vecad_used.size() == 0 );
271     CPPAD_ASSERT_UNKNOWN( op_usage.size()   == 0 );
272 
273     // number of operators in the tape
274     const size_t num_op = play->num_op_rec();
275     //
276     // initialize mapping from variable index to operator index
277     CPPAD_ASSERT_UNKNOWN(
278         size_t( (std::numeric_limits<addr_t>::max)() ) >= num_op
279     );
280     // -----------------------------------------------------------------------
281     // information about current operator
282     OpCode        op;     // operator
283     const addr_t* arg;    // arguments
284     size_t        i_op;   // operator index
285     size_t        i_var;  // variable index of first result
286     // -----------------------------------------------------------------------
287     // information about atomic function calls
288     size_t atom_index=0, atom_old=0, atom_m=0, atom_n=0, atom_i=0, atom_j=0;
289     enum_atom_state atom_state;
290     //
291     // work space used by user atomic functions
292     vector<Base>         atom_x;    // value of parameters in x
293     vector<ad_type_enum> type_x;    // type for each argument
294     vector<size_t>       atom_ix;   // variables indices for argument vector
295     vector<bool>         depend_y;  // results that are used
296     vector<bool>         depend_x;  // arguments that are used
297     //
298     // parameter information (used by atomic function calls)
299 # ifndef NDEBUG
300     size_t num_par = play->num_par_rec();
301 # endif
302     CPPAD_ASSERT_UNKNOWN( num_par > 0 )
303     const Base* parameter = play->GetPar();
304     // -----------------------------------------------------------------------
305     // vecad information
306     size_t num_vecad      = play->num_var_vecad_rec();
307     size_t num_vecad_ind  = play->num_var_vecad_ind_rec();
308     //
309     vecad_used.resize(num_vecad);
310     for(size_t i = 0; i < num_vecad; i++)
311         vecad_used[i] = false;
312     //
313     vector<size_t> arg2vecad(num_vecad_ind);
314     for(size_t i = 0; i < num_vecad_ind; i++)
315         arg2vecad[i] = num_vecad; // invalid value
316     size_t arg_0 = 1; // value of arg[0] for theh first vecad
317     for(size_t i = 0; i < num_vecad; i++)
318     {
319         // mapping from arg[0] value to index for this vecad object.
320         arg2vecad[arg_0] = i;
321         //
322         // length of this vecad object
323         size_t length = play->GetVecInd(arg_0 - 1);
324         //
325         // set to proper index in GetVecInd for next VecAD arg[0] value
326         arg_0        += length + 1;
327     }
328     CPPAD_ASSERT_UNKNOWN( arg_0 == num_vecad_ind + 1 );
329     // -----------------------------------------------------------------------
330     // conditional expression information
331     //
332     size_t num_cexp_op = 0;
333     if( conditional_skip )
334     {   for(i_op = 0; i_op < num_op; ++i_op)
335         {   if( random_itr.get_op(i_op) == CExpOp )
336             {   // count the number of conditional expressions.
337                 ++num_cexp_op;
338             }
339         }
340     }
341     //
342     cexp2op.resize( num_cexp_op );
343     //
344     // number of sets
345     size_t num_set = 0;
346     if( conditional_skip && num_cexp_op > 0)
347         num_set = num_op;
348     //
349     // conditional expression index   = element / 2
350     // conditional expression compare = bool ( element % 2)
351     size_t end_set = 2 * num_cexp_op;
352     //
353     if( num_set > 0 )
354         cexp_set.resize(num_set, end_set);
355     // -----------------------------------------------------------------------
356     // initilaize operator usage for reverse dependency analysis.
357     op_usage.resize( num_op );
358     for(i_op = 0; i_op < num_op; ++i_op)
359         op_usage[i_op] = usage_t(no_usage);
360     for(size_t i = 0; i < dep_taddr.size(); i++)
361     {   i_op           = random_itr.var2op(dep_taddr[i]);
362         op_usage[i_op] = usage_t(yes_usage);    // dependent variables
363     }
364     // ----------------------------------------------------------------------
365     // Reverse pass to compute usage and cexp_set for each operator
366     // ----------------------------------------------------------------------
367     //
368     // Initialize reverse pass
369     size_t last_atom_i_op = 0;
370     size_t cexp_index     = num_cexp_op;
371     atom_state            = end_atom;
372     i_op = num_op;
373     while(i_op != 0 )
374     {   --i_op;
375         if( num_set > 0 )
376         {   // no more elements will be added to this set
377             cexp_set.process_post(i_op);
378         }
379         //
380         // this operator information
381         random_itr.op_info(i_op, op, arg, i_var);
382         //
383         // Is the result of this operation used.
384         // (This only makes sense when NumRes(op) > 0.)
385         usage_t use_result = op_usage[i_op];
386         //
387         bool check_csum = false;
388         switch( op )
389         {
390             // =============================================================
391             // normal operators
392             // =============================================================
393 
394             // Only one variable with index arg[0]
395             case SubvpOp:
396             check_csum = cumulative_sum_op;
397             //
398             case AbsOp:
399             case AcosOp:
400             case AcoshOp:
401             case AsinOp:
402             case AsinhOp:
403             case AtanOp:
404             case AtanhOp:
405             case CosOp:
406             case CoshOp:
407             case DivvpOp:
408             case ErfOp:
409             case ErfcOp:
410             case ExpOp:
411             case Expm1Op:
412             case LogOp:
413             case Log1pOp:
414             case PowvpOp:
415             case SignOp:
416             case SinOp:
417             case SinhOp:
418             case SqrtOp:
419             case TanOp:
420             case TanhOp:
421             case ZmulvpOp:
422             CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
423             if( use_result != usage_t(no_usage) )
424             {   size_t j_op = random_itr.var2op(size_t(arg[0]));
425                 op_inc_arg_usage(
426                     play, check_csum, i_op, j_op, op_usage, cexp_set
427                 );
428             }
429             break; // --------------------------------------------
430 
431             // Only one variable with index arg[1]
432             case AddpvOp:
433             case SubpvOp:
434             check_csum = cumulative_sum_op;
435             //
436             case DisOp:
437             case DivpvOp:
438             case MulpvOp:
439             case PowpvOp:
440             case ZmulpvOp:
441             CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
442             if( use_result != usage_t(no_usage) )
443             {   size_t j_op = random_itr.var2op(size_t(arg[1]));
444                 op_inc_arg_usage(
445                     play, check_csum, i_op, j_op, op_usage, cexp_set
446                 );
447             }
448             break; // --------------------------------------------
449 
450             // arg[0] and arg[1] are the only variables
451             case AddvvOp:
452             case SubvvOp:
453             check_csum = cumulative_sum_op;
454             //
455             case DivvvOp:
456             case MulvvOp:
457             case PowvvOp:
458             case ZmulvvOp:
459             CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
460             if( use_result != usage_t(no_usage) )
461             {   for(size_t i = 0; i < 2; i++)
462                 {   size_t j_op = random_itr.var2op(size_t(arg[i]));
463                     op_inc_arg_usage(
464                         play, check_csum, i_op, j_op, op_usage, cexp_set
465                     );
466                 }
467             }
468             break; // --------------------------------------------
469 
470             // Conditional expression operators
471             // arg[2], arg[3], arg[4], arg[5] are parameters or variables
472             case CExpOp:
473             CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
474             if( conditional_skip )
475             {   --cexp_index;
476                 cexp2op[ cexp_index ] = addr_t(i_op);
477             }
478             if( use_result != usage_t(no_usage) )
479             {   CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 );
480                 // propgate from result to left argument
481                 if( arg[1] & 1 )
482                 {   size_t j_op = random_itr.var2op(size_t(arg[2]));
483                     op_inc_arg_usage(
484                         play, check_csum, i_op, j_op, op_usage, cexp_set
485                     );
486                 }
487                 // propgate from result to right argument
488                 if( arg[1] & 2 )
489                 {   size_t j_op = random_itr.var2op(size_t(arg[3]));
490                     op_inc_arg_usage(
491                             play, check_csum, i_op, j_op, op_usage, cexp_set
492                     );
493                 }
494                 // are if_true and if_false cases the same variable
495                 bool same_variable = (arg[1] & 4) != 0;
496                 same_variable     &= (arg[1] & 8) != 0;
497                 same_variable     &= arg[4] == arg[5];
498                 //
499                 // if_true
500                 if( arg[1] & 4 )
501                 {   size_t j_op = random_itr.var2op(size_t(arg[4]));
502                     bool can_skip = conditional_skip & (! same_variable);
503                     can_skip     &= op_usage[j_op] == usage_t(no_usage);
504                     op_inc_arg_usage(
505                         play, check_csum, i_op, j_op, op_usage, cexp_set
506                     );
507                     if( can_skip )
508                     {   // j_op corresponds to the value used when the
509                         // comparison result is true. It can be skipped when
510                         // the comparison is false (0).
511                         size_t element = 2 * cexp_index + 0;
512                         cexp_set.post_element(j_op, element);
513                         //
514                         op_usage[j_op] = usage_t(yes_usage);
515                     }
516                 }
517                 //
518                 // if_false
519                 if( arg[1] & 8 )
520                 {   size_t j_op = random_itr.var2op(size_t(arg[5]));
521                     bool can_skip = conditional_skip & (! same_variable);
522                     can_skip     &= op_usage[j_op] == usage_t(no_usage);
523                     op_inc_arg_usage(
524                         play, check_csum, i_op, j_op, op_usage, cexp_set
525                     );
526                     if( can_skip )
527                     {   // j_op corresponds to the value used when the
528                         // comparison result is false. It can be skipped when
529                         // the comparison is true (0).
530                         size_t element = 2 * cexp_index + 1;
531                         cexp_set.post_element(j_op, element);
532                         //
533                         op_usage[j_op] = usage_t(yes_usage);
534                     }
535                 }
536             }
537             break;  // --------------------------------------------
538 
539             // Operations that are never used
540             // (new CSkip options are generated if conditional_skip is true)
541             case CSkipOp:
542             case ParOp:
543             break;
544 
545             // Operators that are always used
546             case InvOp:
547             case BeginOp:
548             case EndOp:
549             op_usage[i_op] = usage_t(yes_usage);
550             break;  // -----------------------------------------------
551 
552             // The print forward operator
553             case PriOp:
554             CPPAD_ASSERT_NARG_NRES(op, 5, 0);
555             if( print_for_op )
556             {   op_usage[i_op] = usage_t(yes_usage);
557                 if( arg[0] & 1 )
558                 {   // arg[1] is a variable
559                     size_t j_op = random_itr.var2op(size_t(arg[1]));
560                     op_inc_arg_usage(
561                         play, check_csum, i_op, j_op, op_usage, cexp_set
562                     );
563                 }
564                 if( arg[0] & 2 )
565                 {   // arg[3] is a variable
566                     size_t j_op = random_itr.var2op(size_t(arg[3]));
567                     op_inc_arg_usage(
568                         play, check_csum, i_op, j_op, op_usage, cexp_set
569                     );
570                 }
571             }
572             break; // -----------------------------------------------------
573 
574             // =============================================================
575             // Comparison operators
576             // =============================================================
577 
578             // Compare operators where arg[1] is only variable
579             case LepvOp:
580             case LtpvOp:
581             case EqpvOp:
582             case NepvOp:
583             CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
584             if( compare_op )
585             {   op_usage[i_op] = usage_t(yes_usage);
586                 //
587                 size_t j_op = random_itr.var2op(size_t(arg[1]));
588                 op_inc_arg_usage(
589                     play, check_csum, i_op, j_op, op_usage, cexp_set
590                 );
591             }
592             break; // ----------------------------------------------
593 
594             // Compare operators where arg[0] is only variable
595             case LevpOp:
596             case LtvpOp:
597             CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
598             if( compare_op )
599             {   op_usage[i_op] = usage_t(yes_usage);
600                 //
601                 size_t j_op = random_itr.var2op(size_t(arg[0]));
602                 op_inc_arg_usage(
603                     play, check_csum, i_op, j_op, op_usage, cexp_set
604                 );
605             }
606             break; // ----------------------------------------------
607 
608             // Compare operators where arg[0] and arg[1] are variables
609             case LevvOp:
610             case LtvvOp:
611             case EqvvOp:
612             case NevvOp:
613             CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
614             if( compare_op )
615             {   op_usage[i_op] = usage_t(yes_usage);
616                 //
617                 for(size_t i = 0; i < 2; i++)
618                 {   size_t j_op = random_itr.var2op(size_t(arg[i]));
619                     op_inc_arg_usage(
620                         play, check_csum, i_op, j_op, op_usage, cexp_set
621                     );
622                 }
623             }
624             break; // ----------------------------------------------
625 
626             // =============================================================
627             // VecAD operators
628             // =============================================================
629 
630             // load operator using a parameter index
631             case LdpOp:
632             CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
633             if( use_result != usage_t(no_usage) )
634             {   size_t i_vec = arg2vecad[ arg[0] ];
635                 vecad_used[i_vec] = true;
636             }
637             break; // --------------------------------------------
638 
639             // load operator using a variable index
640             case LdvOp:
641             CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
642             if( use_result != usage_t(no_usage) )
643             {   size_t i_vec = arg2vecad[ arg[0] ];
644                 vecad_used[i_vec] = true;
645                 //
646                 size_t j_op = random_itr.var2op(size_t(arg[1]));
647                 op_usage[j_op] = usage_t(yes_usage);
648             }
649             break; // --------------------------------------------
650 
651             // Store a variable using a parameter index
652             case StpvOp:
653             CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
654             if( vecad_used[ arg2vecad[ arg[0] ] ] )
655             {   op_usage[i_op] = usage_t(yes_usage);
656                 //
657                 size_t j_op = random_itr.var2op(size_t(arg[2]));
658                 op_usage[j_op] = usage_t(yes_usage);
659             }
660             break; // --------------------------------------------
661 
662             // Store a variable using a variable index
663             case StvvOp:
664             CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
665             if( vecad_used[ arg2vecad[ arg[0] ] ] )
666             {   op_usage[i_op] = usage_t(yes_usage);
667                 //
668                 size_t j_op = random_itr.var2op(size_t(arg[1]));
669                 op_usage[j_op] = usage_t(yes_usage);
670                 size_t k_op = random_itr.var2op(size_t(arg[2]));
671                 op_usage[k_op] = usage_t(yes_usage);
672             }
673             break; // -----------------------------------------------------
674 
675             // =============================================================
676             // cumulative summation operator
677             // ============================================================
678             case CSumOp:
679             CPPAD_ASSERT_UNKNOWN( NumRes(op) == 1 );
680             {
681                 for(size_t i = 5; i < size_t(arg[2]); i++)
682                 {   size_t j_op = random_itr.var2op(size_t(arg[i]));
683                     op_inc_arg_usage(
684                         play, check_csum, i_op, j_op, op_usage, cexp_set
685                     );
686                 }
687             }
688             break;
689 
690             // =============================================================
691             // user defined atomic operators
692             // ============================================================
693 
694             case AFunOp:
695             // start or end atomic operation sequence
696             if( atom_state == end_atom )
697             {   // reverse_user using random_itr instead of play
698                 atom_index        = size_t(arg[0]);
699                 atom_old          = size_t(arg[1]);
700                 atom_n            = size_t(arg[2]);
701                 atom_m            = size_t(arg[3]);
702                 atom_j            = atom_n;
703                 atom_i            = atom_m;
704                 atom_state        = ret_atom;
705                 // -------------------------------------------------------
706                 last_atom_i_op = i_op;
707                 CPPAD_ASSERT_UNKNOWN( i_op > atom_n + atom_m + 1 );
708                 CPPAD_ASSERT_UNKNOWN(
709                     op_usage[last_atom_i_op] == usage_t(no_usage)
710                 );
711 # ifndef NDEBUG
712                 if( cexp_set.n_set() > 0 )
713                 {   cexp_set.process_post(last_atom_i_op);
714                     CPPAD_ASSERT_UNKNOWN(
715                         cexp_set.number_elements(last_atom_i_op) == 0
716                     );
717                 }
718 # endif
719                 //
720                 atom_x.resize(  atom_n );
721                 type_x.resize( atom_n );
722                 atom_ix.resize( atom_n );
723                 //
724                 depend_y.resize( atom_m );
725                 depend_x.resize( atom_n );
726                 for(size_t i = 0; i < atom_m; i++)
727                     depend_y[ i ] = false;
728             }
729             else
730             {   // reverse_user using random_itr instead of play
731                 CPPAD_ASSERT_UNKNOWN( atom_state == start_atom );
732                 CPPAD_ASSERT_UNKNOWN( atom_n == size_t(arg[2]) );
733                 CPPAD_ASSERT_UNKNOWN( atom_m == size_t(arg[3]) );
734                 CPPAD_ASSERT_UNKNOWN( atom_j == 0 );
735                 CPPAD_ASSERT_UNKNOWN( atom_i == 0 );
736                 atom_state = end_atom;
737                 // -------------------------------------------------------
738                 CPPAD_ASSERT_UNKNOWN(
739                     i_op + atom_n + atom_m + 1 == last_atom_i_op
740                 );
741                 if( op_usage[last_atom_i_op] != usage_t(no_usage) )
742                 {   // call atomic function for this operation
743                     sweep::call_atomic_rev_depend<Base, Base>(
744                     atom_index, atom_old, atom_x, type_x, depend_x, depend_y
745                     );
746                     for(size_t j = 0; j < atom_n; j++)
747                     if( depend_x[j] )
748                     {   // The parameter or variable correspnding to the j-th
749                         // argument gets used
750                         op_usage[i_op + 1 + j] = true;
751                         if( type_x[j] == variable_enum )
752                         {   CPPAD_ASSERT_UNKNOWN( atom_ix[j] > 0 );
753                             if( depend_x[j] )
754                             {   size_t j_op = random_itr.var2op(atom_ix[j]);
755                                 op_inc_arg_usage(play, check_csum,
756                                     last_atom_i_op, j_op, op_usage, cexp_set
757                                 );
758                             }
759                         }
760                     }
761                 }
762                 // copy set infomation from last to first
763                 if( cexp_set.n_set() > 0 )
764                 {   cexp_set.process_post(last_atom_i_op);
765                     cexp_set.assignment(i_op, last_atom_i_op, cexp_set);
766                 }
767                 // copy usage information from last to first
768                 op_usage[i_op] = op_usage[last_atom_i_op];
769             }
770             break; // -------------------------------------------------------
771 
772             case FunapOp:
773             // parameter argument in an atomic operation sequence
774             CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
775             //
776             // reverse_user using random_itr instead of play
777             CPPAD_ASSERT_NARG_NRES(op, 1, 0);
778             CPPAD_ASSERT_UNKNOWN( 0 < atom_j && atom_j <= atom_n );
779             --atom_j;
780             if( atom_j == 0 )
781                 atom_state = start_atom;
782             // -------------------------------------------------------------
783             atom_ix[atom_j] = 0;
784             //
785             // parameter arguments
786             atom_x[atom_j] = parameter[arg[0]];
787             if( play->dyn_par_is()[arg[0]] )
788                 type_x[atom_j] = dynamic_enum;
789             else
790                 type_x[atom_j] = constant_enum;
791             //
792             break;
793 
794             case FunavOp:
795             // variable argument in an atomic operation sequence
796             CPPAD_ASSERT_UNKNOWN( 0 < arg[0] );
797             //
798             // reverse_user using random_itr instead of play
799             CPPAD_ASSERT_NARG_NRES(op, 1, 0);
800             CPPAD_ASSERT_UNKNOWN( 0 < atom_j && atom_j <= atom_n );
801             --atom_j;
802             if( atom_j == 0 )
803                 atom_state = start_atom;
804             // -------------------------------------------------------------
805             atom_ix[atom_j] = size_t(arg[0]);
806             //
807             // variable arguments as parameters
808             atom_x[atom_j] = CppAD::numeric_limits<Base>::quiet_NaN();
809             type_x[atom_j] = variable_enum;
810             //
811             break;
812 
813             case FunrvOp:
814             // variable result in an atomic operation sequence
815             //
816             // reverse_user using random_itr instead of play
817             CPPAD_ASSERT_NARG_NRES(op, 0, 1);
818             CPPAD_ASSERT_UNKNOWN( 0 < atom_i && atom_i <= atom_m );
819             --atom_i;
820             if( atom_i == 0 )
821                 atom_state = arg_atom;
822             // -------------------------------------------------------------
823             if( use_result )
824             {   depend_y[atom_i] = true;
825                 op_inc_arg_usage(
826                     play, check_csum, i_op, last_atom_i_op, op_usage, cexp_set
827                 );
828             }
829             break; // --------------------------------------------------------
830 
831             case FunrpOp:
832             CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
833             //
834             // reverse_user using random_itr instead of play
835             CPPAD_ASSERT_NARG_NRES(op, 1, 0);
836             CPPAD_ASSERT_UNKNOWN( 0 < atom_i && atom_i <= atom_m );
837             --atom_i;
838             if( atom_i == 0 )
839                 atom_state = arg_atom;
840             break;
841             // ============================================================
842 
843             // all cases should be handled above
844             default:
845             CPPAD_ASSERT_UNKNOWN(0);
846         }
847     }
848     return;
849 }
850 
851 } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
852 
853 # endif
854