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