1 # ifndef CPPAD_LOCAL_OPTIMIZE_GET_OPT_OP_INFO_HPP
2 # define CPPAD_LOCAL_OPTIMIZE_GET_OPT_OP_INFO_HPP
3
4 /* --------------------------------------------------------------------------
5 CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-17 Bradley M. Bell
6
7 CppAD is distributed under multiple licenses. This distribution is under
8 the terms of the
9 Eclipse Public License Version 1.0.
10
11 A copy of this license is included in the COPYING file of this distribution.
12 Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
13 -------------------------------------------------------------------------- */
14 /*!
15 \file opt_op_info.hpp
16 Create operator information tables
17 */
18
19 # include <cppad/local/optimize/opt_op_info.hpp>
20 # include <cppad/local/optimize/match_op.hpp>
21 # include <cppad/local/optimize/cexp_info.hpp>
22 # include <cppad/local/optimize/usage.hpp>
23
24 // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
25 namespace CppAD { namespace local { namespace optimize {
26
27 /// Is this an addition or subtraction operator
add_or_subtract(OpCode op)28 inline bool add_or_subtract(OpCode op)
29 { bool result;
30 switch(op)
31 {
32 case AddpvOp:
33 case AddvvOp:
34 case SubpvOp:
35 case SubvpOp:
36 case SubvvOp:
37 result = true;
38 break;
39
40 default:
41 result = false;
42 break;
43 }
44 return result;
45 }
46
47
48 /*!
49 Increarse argument usage and propagate cexp_set from result to argument.
50
51 \param play
52 is the player for the old operation sequence.
53
54 \param sum_result
55 is result an addition or subtraction operator (passed for speed so
56 do not need to call add_or_subtract for result).
57
58 \param i_result
59 is the operator index for the result operator.
60
61 \param i_arg
62 is the operator index for the argument to the result operator.
63
64 \param opt_op_info
65 structure that holds the information for each of the operators.
66 The output value of opt_op_info[i_arg].usage is increased; to be specific,
67 If sum_result is true and the input value of opt_op_info[i_arg].usage
68 is no_usage, its output value is csum_usage.
69 Otherwise, the output value of opt_op_info[i_arg].usage is yes_usage.
70
71 \param cexp_set
72 This is a vector of sets with one set for each operator. We denote
73 the i-th set by set[i].
74
75 \li
76 In the special case where cexp_set.n_set() is zero,
77 cexp_set is not changed.
78
79 \li
80 If cexp_set.n_set() != 0 and opt_op_info[i_arg].usage == no_usage,
81 the input value of set[i_arg] must be empty.
82 In this case the output value if set[i_arg] is equal to set[i_result]
83 (which may also be empty).
84
85 \li
86 If cexp_set.n_set() != 0 and opt_op_info[i_arg].usage != no_usage,
87 the output value of set[i_arg] is the intersection of
88 its input value and set[i_result].
89 */
90 template <class Base>
usage_cexp_result2arg(const player<Base> * play,bool sum_result,size_t i_result,size_t i_arg,vector<struct_opt_op_info> & opt_op_info,sparse_list & cexp_set)91 inline void usage_cexp_result2arg(
92 const player<Base>* play ,
93 bool sum_result ,
94 size_t i_result ,
95 size_t i_arg ,
96 vector<struct_opt_op_info>& opt_op_info ,
97 sparse_list& cexp_set )
98 {
99 // cexp_set
100 if( cexp_set.n_set() > 0 )
101 { if( opt_op_info[i_arg].usage == no_usage )
102 { // set[i_arg] = set[i_result]
103 cexp_set.assignment(i_arg, i_result, cexp_set);
104 }
105 else
106 { // set[i_arg] = set[i_arg] intersect set[i_result]
107 cexp_set.binary_intersection(i_arg, i_arg, i_result, cexp_set);
108 }
109 }
110 // usage
111 bool csum = sum_result && opt_op_info[i_arg].usage == no_usage;
112 if( csum )
113 { OpCode op_a = play->GetOp(i_arg);
114 csum = add_or_subtract( op_a );
115 }
116 if( csum )
117 opt_op_info[i_arg].usage = csum_usage;
118 else
119 opt_op_info[i_arg].usage = yes_usage;
120 //
121 return;
122 }
123
124 /*!
125 Get variable to operator map and operator basic operator information
126
127 \tparam Base
128 base type for the operator; i.e., this operation was recorded
129 using AD< \a Base > and computations by this routine are done using type
130 \a Base.
131
132 \param conditional_skip
133 If conditional_skip this is true, the conditional expression information
134 cexp_info will be calculated.
135 This may be time intensive and may not have much benefit in the optimized
136 recording.
137
138 \param compare_op
139 if this is true, arguments are considered used if they appear in compare
140 operators. This is a side effect because compare operators have boolean
141 results (and the result is not in the tape; i.e. NumRes(op) is zero
142 for these operators. (This is an example of a side effect.)
143
144 \param print_for_op
145 if this is true, arguments are considered used if they appear in
146 print forward operators; i.e., PriOp.
147 This is also a side effect; i.e. NumRes(PriOp) is zero.
148
149 \param play
150 This is the old operation sequence.
151
152 \param dep_taddr
153 is a vector of variable indices for the dependent variables.
154
155 \param cexp_info
156 The input size of this vector must be zero.
157 If conditional_skip is false, cexp_info is not changed.
158 Otherwise,
159 upon return cexp_info has size equal to the number of conditional expressions
160 in the operation sequence; i.e., the number of CExpOp operators.
161 The value cexp_info[j] is the information corresponding to the j-th
162 conditional expression in the operation sequence.
163 This vector is in the same order as the operation sequence; i.e.
164 if j1 > j2, cexp_info[j1].i_op > cexp_info[j2].i_op.
165 Note that skip_op_true and skip_op_false could be part of this structure,
166 but then we would allocate and deallocate two vectors for each conditonal
167 expression in the operation sequence.
168
169 \param skip_op_true
170 This vector of sets is empty on input.
171 Upon return, the j-th set is the operators that are not used when
172 comparison result for cexp_info[j] is true.
173 Note that UsrapOp, UsravOp, UsrrpOp, and UsrrvOp, are not in this
174 set and should be skipped when the corresponding UserOp are skipped.
175
176 \param skip_op_false
177 This vector of sets is empty on input.
178 Upon return, the j-th set is the operators that are not used when
179 comparison result for cexp_info[j] is false.
180 Note that UsrapOp, UsravOp, UsrrpOp, and UsrrvOp, are not in this
181 set and should be skipped when the corresponding UserOp are skipped.
182
183 \param vecad_used
184 The input size of this vector must be zero.
185 Upon retun it has size equal to the number of VecAD vectors
186 in the operations sequences; i.e., play->num_vecad_vec_rec().
187 The VecAD vectors are indexed in the order that thier indices apprear
188 in the one large play->GetVecInd that holds all the VecAD vectors.
189
190 \param opt_op_info
191 The input size of this vector must be zero.
192 Upon return it has size equal to the number of operators
193 in the operation sequence; i.e., num_op = play->nun_var_rec().
194 The value opt_op_info[i]
195 have been set to the values corresponding to the i-th operator
196 in the operation sequence.
197 */
198
199 template <class Base>
get_opt_op_info(bool conditional_skip,bool compare_op,bool print_for_op,const player<Base> * play,const vector<size_t> & dep_taddr,vector<struct_cexp_info> & cexp_info,sparse_list & skip_op_true,sparse_list & skip_op_false,vector<bool> & vecad_used,vector<struct_opt_op_info> & opt_op_info)200 void get_opt_op_info(
201 bool conditional_skip ,
202 bool compare_op ,
203 bool print_for_op ,
204 const player<Base>* play ,
205 const vector<size_t>& dep_taddr ,
206 vector<struct_cexp_info>& cexp_info ,
207 sparse_list& skip_op_true ,
208 sparse_list& skip_op_false ,
209 vector<bool>& vecad_used ,
210 vector<struct_opt_op_info>& opt_op_info )
211 {
212 CPPAD_ASSERT_UNKNOWN( cexp_info.size() == 0 );
213 CPPAD_ASSERT_UNKNOWN( vecad_used.size() == 0 );
214 CPPAD_ASSERT_UNKNOWN( opt_op_info.size() == 0 );
215
216 // number of operators in the tape
217 const size_t num_op = play->num_op_rec();
218 opt_op_info.resize( num_op );
219 //
220 // initialize mapping from variable index to operator index
221 CPPAD_ASSERT_UNKNOWN(
222 std::numeric_limits<addr_t>::max() >= num_op
223 );
224 //
225 // information set by forward_user
226 size_t user_old=0, user_m=0, user_n=0, user_i=0, user_j=0;
227 enum_user_state user_state;
228 //
229 // ----------------------------------------------------------------------
230 // Forward pass to determine num_cexp_op
231 // ----------------------------------------------------------------------
232 OpCode op; // operator
233 const addr_t* arg; // arguments
234 size_t i_op; // operator index
235 size_t i_var; // variable index of first result
236 i_op = 0;
237 play->get_op_info(i_op, op, arg, i_var);
238 CPPAD_ASSERT_UNKNOWN( op == BeginOp );
239 CPPAD_ASSERT_UNKNOWN( NumRes(BeginOp) == 1 );
240 CPPAD_ASSERT_UNKNOWN( i_op == 0 );
241 CPPAD_ASSERT_UNKNOWN( i_var == 0 );
242 //
243 size_t num_cexp_op = 0;
244 user_state = start_user;
245 while(op != EndOp)
246 { // next operator
247 play->get_op_info(++i_op, op, arg, i_var);
248 //
249 if( op == CExpOp )
250 { // count the number of conditional expressions.
251 ++num_cexp_op;
252 }
253 }
254 // vector that maps conditional expression index to operator index
255 vector<size_t> cexp2op( num_cexp_op );
256 // ----------------------------------------------------------------------
257 // Reverse pass to compute usage and cexp_set for each operator
258 // ----------------------------------------------------------------------
259 // work space used by user defined atomic functions
260 typedef std::set<size_t> size_set;
261 vector<Base> user_x; // parameters in x as integers
262 vector<size_t> user_ix; // variables indices for argument vector
263 vector<size_set> user_r_set; // set sparsity pattern for result
264 vector<size_set> user_s_set; // set sparisty pattern for argument
265 vector<bool> user_r_bool; // bool sparsity pattern for result
266 vector<bool> user_s_bool; // bool sparisty pattern for argument
267 vectorBool user_r_pack; // pack sparsity pattern for result
268 vectorBool user_s_pack; // pack sparisty pattern for argument
269 //
270 atomic_base<Base>* user_atom = CPPAD_NULL; // current user atomic function
271 bool user_pack = false; // sparsity pattern type is pack
272 bool user_bool = false; // sparsity pattern type is bool
273 bool user_set = false; // sparsity pattern type is set
274 // -----------------------------------------------------------------------
275 // vecad information
276 size_t num_vecad = play->num_vecad_vec_rec();
277 size_t num_vecad_ind = play->num_vec_ind_rec();
278 //
279 vecad_used.resize(num_vecad);
280 for(size_t i = 0; i < num_vecad; i++)
281 vecad_used[i] = false;
282 //
283 vector<size_t> arg2vecad(num_vecad_ind);
284 for(size_t i = 0; i < num_vecad_ind; i++)
285 arg2vecad[i] = num_vecad; // invalid value
286 size_t arg_0 = 1; // value of arg[0] for theh first vecad
287 for(size_t i = 0; i < num_vecad; i++)
288 {
289 // mapping from arg[0] value to index for this vecad object.
290 arg2vecad[arg_0] = i;
291 //
292 // length of this vecad object
293 size_t length = play->GetVecInd(arg_0 - 1);
294 //
295 // set to proper index in GetVecInd for next VecAD arg[0] value
296 arg_0 += length + 1;
297 }
298 CPPAD_ASSERT_UNKNOWN( arg_0 == num_vecad_ind + 1 );
299 // -----------------------------------------------------------------------
300 // parameter information (used by atomic function calls)
301 size_t num_par = play->num_par_rec();
302 const Base* parameter = CPPAD_NULL;
303 if( num_par > 0 )
304 parameter = play->GetPar();
305 // -----------------------------------------------------------------------
306 // Set of conditional expressions comparisons that usage of each
307 /// operator depends on. The operator can be skipped if any of the
308 // comparisons results in the set holds. A set for operator i_op is
309 // not defined and left empty when opt_op_info[i_op].usage = no_usage.
310 /// It is also left empty for the result of any VecAD operations.
311 sparse_list cexp_set;
312 //
313 // number of sets
314 size_t num_set = 0;
315 if( conditional_skip && num_cexp_op > 0)
316 num_set = num_op;
317 //
318 // conditional expression index = element / 2
319 // conditional expression compare = bool ( element % 2)
320 size_t end_set = 2 * num_cexp_op;
321 //
322 if( num_set > 0 )
323 cexp_set.resize(num_set, end_set);
324 // -----------------------------------------------------------------------
325 //
326 // initialize operator usage
327 for(size_t i = 0; i < num_op; i++)
328 opt_op_info[i].usage = no_usage;
329 for(size_t i = 0; i < dep_taddr.size(); i++)
330 { i_op = play->var2op(dep_taddr[i]);
331 opt_op_info[i_op].usage = yes_usage; // dependent variables
332 }
333 //
334 // Initialize reverse pass
335 size_t last_user_i_op = 0;
336 size_t cexp_index = num_cexp_op;
337 user_state = end_user;
338 i_op = num_op;
339 while(i_op != 0 )
340 { --i_op;
341 //
342 // this operator information
343 play->get_op_info(i_op, op, arg, i_var);
344 //
345 // Is the result of this operation used.
346 // (This only makes sense when NumRes(op) > 0.)
347 enum_usage use_result = opt_op_info[i_op].usage;
348 //
349 bool sum_op = false;
350 switch( op )
351 {
352 // =============================================================
353 // normal operators
354 // =============================================================
355
356 // Only one variable with index arg[0]
357 case SubvpOp:
358 sum_op = true;
359 //
360 case AbsOp:
361 case AcosOp:
362 case AcoshOp:
363 case AsinOp:
364 case AsinhOp:
365 case AtanOp:
366 case AtanhOp:
367 case CosOp:
368 case CoshOp:
369 case DivvpOp:
370 case ErfOp:
371 case ExpOp:
372 case Expm1Op:
373 case LogOp:
374 case Log1pOp:
375 case PowvpOp:
376 case SignOp:
377 case SinOp:
378 case SinhOp:
379 case SqrtOp:
380 case TanOp:
381 case TanhOp:
382 case ZmulvpOp:
383 CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
384 if( use_result != no_usage )
385 { size_t j_op = play->var2op(arg[0]);
386 usage_cexp_result2arg(
387 play, sum_op, i_op, j_op, opt_op_info, cexp_set
388 );
389 }
390 break; // --------------------------------------------
391
392 // Only one variable with index arg[1]
393 case AddpvOp:
394 case SubpvOp:
395 sum_op = true;
396 //
397 case DisOp:
398 case DivpvOp:
399 case MulpvOp:
400 case PowpvOp:
401 case ZmulpvOp:
402 CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
403 if( use_result != no_usage )
404 { size_t j_op = play->var2op(arg[1]);
405 usage_cexp_result2arg(
406 play, sum_op, i_op, j_op, opt_op_info, cexp_set
407 );
408 }
409 break; // --------------------------------------------
410
411 // arg[0] and arg[1] are the only variables
412 case AddvvOp:
413 case SubvvOp:
414 sum_op = true;
415 //
416 case DivvvOp:
417 case MulvvOp:
418 case PowvvOp:
419 case ZmulvvOp:
420 CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
421 if( use_result != no_usage )
422 { for(size_t i = 0; i < 2; i++)
423 { size_t j_op = play->var2op(arg[i]);
424 usage_cexp_result2arg(
425 play, sum_op, i_op, j_op, opt_op_info, cexp_set
426 );
427 }
428 }
429 break; // --------------------------------------------
430
431 // Conditional expression operators
432 // arg[2], arg[3], arg[4], arg[5] are parameters or variables
433 case CExpOp:
434 --cexp_index;
435 cexp2op[ cexp_index ] = i_op;
436 CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
437 if( use_result != no_usage )
438 { CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 );
439 // propgate from result to left argument
440 if( arg[1] & 1 )
441 { size_t j_op = play->var2op(arg[2]);
442 usage_cexp_result2arg(
443 play, sum_op, i_op, j_op, opt_op_info, cexp_set
444 );
445 }
446 // propgate from result to right argument
447 if( arg[1] & 2 )
448 { size_t j_op = play->var2op(arg[3]);
449 usage_cexp_result2arg(
450 play, sum_op, i_op, j_op, opt_op_info, cexp_set
451 );
452 }
453 // are if_true and if_false cases the same variable
454 bool same_variable = bool(arg[1] & 4) && bool(arg[1] & 8);
455 same_variable &= arg[4] == arg[5];
456 //
457 // if_true
458 if( arg[1] & 4 )
459 { size_t j_op = play->var2op(arg[4]);
460 bool can_skip = conditional_skip & (! same_variable);
461 can_skip &= opt_op_info[j_op].usage == no_usage;
462 usage_cexp_result2arg(
463 play, sum_op, i_op, j_op, opt_op_info, cexp_set
464 );
465 if( can_skip )
466 { // j_op corresponds to the value used when the
467 // comparison result is true. It can be skipped when
468 // the comparison is false (0).
469 size_t element = 2 * cexp_index + 0;
470 cexp_set.add_element(j_op, element);
471 //
472 opt_op_info[j_op].usage = yes_usage;
473 }
474 }
475 //
476 // if_false
477 if( arg[1] & 8 )
478 { size_t j_op = play->var2op(arg[5]);
479 bool can_skip = conditional_skip & (! same_variable);
480 can_skip &= opt_op_info[j_op].usage == no_usage;
481 usage_cexp_result2arg(
482 play, sum_op, i_op, j_op, opt_op_info, cexp_set
483 );
484 if( can_skip )
485 { // j_op corresponds to the value used when the
486 // comparison result is false. It can be skipped when
487 // the comparison is true (0).
488 size_t element = 2 * cexp_index + 1;
489 cexp_set.add_element(j_op, element);
490 //
491 opt_op_info[j_op].usage = yes_usage;
492 }
493 }
494 }
495 break; // --------------------------------------------
496
497 // Operations that are never used
498 // (new CSkip options are generated if conditional_skip is true)
499 case CSkipOp:
500 case ParOp:
501 break;
502
503 // Operators that are always used
504 case InvOp:
505 case BeginOp:
506 case EndOp:
507 opt_op_info[i_op].usage = yes_usage;
508 break; // -----------------------------------------------
509
510 // The print forward operator
511 case PriOp:
512 CPPAD_ASSERT_NARG_NRES(op, 5, 0);
513 if( print_for_op )
514 { opt_op_info[i_op].usage = yes_usage;
515 if( arg[0] & 1 )
516 { // arg[1] is a variable
517 size_t j_op = play->var2op(arg[1]);
518 usage_cexp_result2arg(
519 play, sum_op, i_op, j_op, opt_op_info, cexp_set
520 );
521 }
522 if( arg[0] & 2 )
523 { // arg[3] is a variable
524 size_t j_op = play->var2op(arg[3]);
525 usage_cexp_result2arg(
526 play, sum_op, i_op, j_op, opt_op_info, cexp_set
527 );
528 }
529 }
530 break; // -----------------------------------------------------
531
532 // =============================================================
533 // Comparison operators
534 // =============================================================
535
536 // Compare operators where arg[1] is only variable
537 case LepvOp:
538 case LtpvOp:
539 case EqpvOp:
540 case NepvOp:
541 CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
542 if( compare_op )
543 { opt_op_info[i_op].usage = yes_usage;
544 //
545 size_t j_op = play->var2op(arg[1]);
546 usage_cexp_result2arg(
547 play, sum_op, i_op, j_op, opt_op_info, cexp_set
548 );
549 }
550 break; // ----------------------------------------------
551
552 // Compare operators where arg[0] is only variable
553 case LevpOp:
554 case LtvpOp:
555 CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
556 if( compare_op )
557 { opt_op_info[i_op].usage = yes_usage;
558 //
559 size_t j_op = play->var2op(arg[0]);
560 usage_cexp_result2arg(
561 play, sum_op, i_op, j_op, opt_op_info, cexp_set
562 );
563 }
564 break; // ----------------------------------------------
565
566 // Compare operators where arg[0] and arg[1] are variables
567 case LevvOp:
568 case LtvvOp:
569 case EqvvOp:
570 case NevvOp:
571 if( compare_op )
572 {CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );}
573 if( compare_op )
574 { opt_op_info[i_op].usage = yes_usage;
575 //
576 for(size_t i = 0; i < 2; i++)
577 { size_t j_op = play->var2op(arg[i]);
578 usage_cexp_result2arg(
579 play, sum_op, i_op, j_op, opt_op_info, cexp_set
580 );
581 }
582 }
583 break; // ----------------------------------------------
584
585 // =============================================================
586 // VecAD operators
587 // =============================================================
588
589 // load operator using a parameter index
590 case LdpOp:
591 CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
592 if( use_result != no_usage )
593 { size_t i_vec = arg2vecad[ arg[0] ];
594 vecad_used[i_vec] = true;
595 }
596 break; // --------------------------------------------
597
598 // load operator using a variable index
599 case LdvOp:
600 CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
601 if( use_result != no_usage )
602 { size_t i_vec = arg2vecad[ arg[0] ];
603 vecad_used[i_vec] = true;
604 //
605 size_t j_op = play->var2op(arg[1]);
606 opt_op_info[j_op].usage = yes_usage;
607 }
608 break; // --------------------------------------------
609
610 // Store a variable using a parameter index
611 case StpvOp:
612 CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
613 if( vecad_used[ arg2vecad[ arg[0] ] ] )
614 { opt_op_info[i_op].usage = yes_usage;
615 //
616 size_t j_op = play->var2op(arg[2]);
617 opt_op_info[j_op].usage = yes_usage;
618 }
619 break; // --------------------------------------------
620
621 // Store a variable using a variable index
622 case StvvOp:
623 CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
624 if( vecad_used[ arg2vecad[ arg[0] ] ] )
625 { opt_op_info[i_op].usage = yes_usage;
626 //
627 size_t j_op = play->var2op(arg[1]);
628 opt_op_info[j_op].usage = yes_usage;
629 size_t k_op = play->var2op(arg[2]);
630 opt_op_info[k_op].usage = yes_usage;
631 }
632 break; // -----------------------------------------------------
633
634 // =============================================================
635 // cumulative summation operator
636 // ============================================================
637 case CSumOp:
638 CPPAD_ASSERT_UNKNOWN( NumRes(op) == 1 );
639 {
640 size_t num_add = size_t( arg[0] );
641 size_t num_sub = size_t( arg[1] );
642 for(size_t i = 0; i < num_add + num_sub; i++)
643 { size_t j_op = play->var2op( arg[3 + i] );
644 usage_cexp_result2arg(
645 play, sum_op, i_op, j_op, opt_op_info, cexp_set
646 );
647 }
648 }
649 // =============================================================
650 // user defined atomic operators
651 // ============================================================
652
653 case UserOp:
654 // start or end atomic operation sequence
655 if( user_state == end_user )
656 { // revese_user using opt_op_info instead of play
657 size_t user_index = arg[0];
658 user_old = arg[1];
659 user_n = arg[2];
660 user_m = arg[3];
661 user_j = user_n;
662 user_i = user_m;
663 user_state = ret_user;
664 user_atom = atomic_base<Base>::class_object(user_index);
665 // -------------------------------------------------------
666 last_user_i_op = i_op;
667 CPPAD_ASSERT_UNKNOWN( i_op > user_n + user_m + 1 );
668 CPPAD_ASSERT_UNKNOWN(
669 opt_op_info[last_user_i_op].usage == no_usage
670 );
671 # ifndef NDEBUG
672 if( cexp_set.n_set() > 0 )
673 { sparse_list_const_iterator itr(cexp_set, last_user_i_op);
674 CPPAD_ASSERT_UNKNOWN( *itr == cexp_set.end() );
675 }
676 # endif
677 //
678 user_x.resize( user_n );
679 user_ix.resize( user_n );
680 //
681 user_pack = user_atom->sparsity() ==
682 atomic_base<Base>::pack_sparsity_enum;
683 user_bool = user_atom->sparsity() ==
684 atomic_base<Base>::bool_sparsity_enum;
685 user_set = user_atom->sparsity() ==
686 atomic_base<Base>::set_sparsity_enum;
687 CPPAD_ASSERT_UNKNOWN( user_pack || user_bool || user_set );
688 //
689 // Note that q is one for this call the sparsity calculation
690 if( user_pack )
691 { user_r_pack.resize( user_m );
692 user_s_pack.resize( user_n );
693 for(size_t i = 0; i < user_m; i++)
694 user_r_pack[ i ] = false;
695 }
696 if( user_bool )
697 { user_r_bool.resize( user_m );
698 user_s_bool.resize( user_n );
699 for(size_t i = 0; i < user_m; i++)
700 user_r_bool[ i ] = false;
701 }
702 if( user_set )
703 { user_s_set.resize(user_n);
704 user_r_set.resize(user_m);
705 for(size_t i = 0; i < user_m; i++)
706 user_r_set[i].clear();
707 }
708 }
709 else
710 { // reverse_user using opt_op_info instead of play
711 CPPAD_ASSERT_UNKNOWN( user_state == start_user );
712 CPPAD_ASSERT_UNKNOWN( user_n == size_t(arg[2]) );
713 CPPAD_ASSERT_UNKNOWN( user_m == size_t(arg[3]) );
714 CPPAD_ASSERT_UNKNOWN( user_j == 0 );
715 CPPAD_ASSERT_UNKNOWN( user_i == 0 );
716 user_state = end_user;
717 // -------------------------------------------------------
718 CPPAD_ASSERT_UNKNOWN(
719 i_op + user_n + user_m + 1 == last_user_i_op
720 );
721 // call users function for this operation
722 user_atom->set_old(user_old);
723 bool user_ok = false;
724 size_t user_q = 1; // as if sum of dependent variables
725 if( user_pack )
726 { user_ok = user_atom->rev_sparse_jac(
727 user_q, user_r_pack, user_s_pack, user_x
728 );
729 if( ! user_ok ) user_ok = user_atom->rev_sparse_jac(
730 user_q, user_r_pack, user_s_pack
731 );
732 }
733 if( user_bool )
734 { user_ok = user_atom->rev_sparse_jac(
735 user_q, user_r_bool, user_s_bool, user_x
736 );
737 if( ! user_ok ) user_ok = user_atom->rev_sparse_jac(
738 user_q, user_r_bool, user_s_bool
739 );
740 }
741 if( user_set )
742 { user_ok = user_atom->rev_sparse_jac(
743 user_q, user_r_set, user_s_set, user_x
744 );
745 if( ! user_ok ) user_ok = user_atom->rev_sparse_jac(
746 user_q, user_r_set, user_s_set
747 );
748 }
749 if( ! user_ok )
750 { std::string s =
751 "Optimizing an ADFun object"
752 " that contains the atomic function\n\t";
753 s += user_atom->afun_name();
754 s += "\nCurrent atomic_sparsity is set to ";
755 //
756 if( user_set )
757 s += "set_sparsity_enum.\n";
758 if( user_bool )
759 s += "bool_sparsity_enum.\n";
760 if( user_pack )
761 s += "pack_sparsity_enum.\n";
762 //
763 s += "This version of rev_sparse_jac returned false";
764 CPPAD_ASSERT_KNOWN(false, s.c_str() );
765 }
766
767 if( opt_op_info[last_user_i_op].usage != no_usage )
768 for(size_t j = 0; j < user_n; j++)
769 if( user_ix[j] > 0 )
770 { // This user argument is a variable
771 bool use_arg_j = false;
772 if( user_set )
773 { if( ! user_s_set[j].empty() )
774 use_arg_j = true;
775 }
776 if( user_bool )
777 { if( user_s_bool[j] )
778 use_arg_j = true;
779 }
780 if( user_pack )
781 { if( user_s_pack[j] )
782 use_arg_j = true;
783 }
784 if( use_arg_j )
785 { size_t j_op = play->var2op(user_ix[j]);
786 usage_cexp_result2arg(play,
787 sum_op, last_user_i_op, j_op, opt_op_info, cexp_set
788 );
789 }
790 }
791 // copy set infomation from last to first
792 if( cexp_set.n_set() > 0 )
793 cexp_set.assignment(i_op, last_user_i_op, cexp_set);
794 // copy user information from last to all the user operators
795 // for this call
796 for(size_t j = 0; j < user_n + user_m + 1; ++j)
797 opt_op_info[i_op + j].usage = opt_op_info[last_user_i_op].usage;
798 }
799 break; // -------------------------------------------------------
800
801 case UsrapOp:
802 // parameter argument in an atomic operation sequence
803 CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
804 //
805 // reverse_user using opt_op_info instead of play
806 CPPAD_ASSERT_NARG_NRES(op, 1, 0);
807 CPPAD_ASSERT_UNKNOWN( 0 < user_j && user_j < user_n );
808 --user_j;
809 if( user_j == 0 )
810 user_state = start_user;
811 // -------------------------------------------------------------
812 user_ix[user_j] = 0;
813 //
814 // parameter arguments
815 user_x[user_j] = parameter[arg[0]];
816 //
817 break;
818
819 case UsravOp:
820 // variable argument in an atomic operation sequence
821 CPPAD_ASSERT_UNKNOWN( 0 < arg[0] );
822 //
823 // reverse_user using opt_op_info instead of play
824 CPPAD_ASSERT_NARG_NRES(op, 1, 0);
825 CPPAD_ASSERT_UNKNOWN( 0 < user_j && user_j <= user_n );
826 --user_j;
827 if( user_j == 0 )
828 user_state = start_user;
829 // -------------------------------------------------------------
830 user_ix[user_j] = arg[0];
831 //
832 // variable arguments as parameters
833 user_x[user_j] = CppAD::numeric_limits<Base>::quiet_NaN();
834 //
835 break;
836
837 case UsrrvOp:
838 // variable result in an atomic operation sequence
839 //
840 // reverse_user using opt_op_info instead of play
841 CPPAD_ASSERT_NARG_NRES(op, 0, 1);
842 CPPAD_ASSERT_UNKNOWN( 0 < user_i && user_i <= user_m );
843 --user_i;
844 if( user_i == 0 )
845 user_state = arg_user;
846 // -------------------------------------------------------------
847 if( use_result )
848 { if( user_set )
849 user_r_set[user_i].insert(0);
850 if( user_bool )
851 user_r_bool[user_i] = true;
852 if( user_pack )
853 user_r_pack[user_i] = true;
854 //
855 usage_cexp_result2arg(
856 play, sum_op, i_op, last_user_i_op, opt_op_info, cexp_set
857 );
858 }
859 break; // --------------------------------------------------------
860
861 case UsrrpOp:
862 CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
863 //
864 // reverse_user using opt_op_info instead of play
865 CPPAD_ASSERT_NARG_NRES(op, 0, 1);
866 CPPAD_ASSERT_UNKNOWN( 0 < user_i && user_i < user_m );
867 --user_i;
868 if( user_i == 0 )
869 user_state = arg_user;
870 break;
871 // ============================================================
872
873 // all cases should be handled above
874 default:
875 CPPAD_ASSERT_UNKNOWN(0);
876 }
877 }
878 // ----------------------------------------------------------------------
879 // compute previous in opt_op_info
880 // ----------------------------------------------------------------------
881 sparse_list hash_table_op;
882 hash_table_op.resize(CPPAD_HASH_TABLE_SIZE, num_op);
883 //
884 user_state = start_user;
885 for(i_op = 0; i_op < num_op; ++i_op)
886 { opt_op_info[i_op].previous = 0;
887
888 if( opt_op_info[i_op].usage == yes_usage )
889 switch( play->GetOp(i_op) )
890 {
891 case NumberOp:
892 CPPAD_ASSERT_UNKNOWN(false);
893 break;
894
895 case BeginOp:
896 case CExpOp:
897 case CSkipOp:
898 case CSumOp:
899 case EndOp:
900 case InvOp:
901 case LdpOp:
902 case LdvOp:
903 case ParOp:
904 case PriOp:
905 case StppOp:
906 case StpvOp:
907 case StvpOp:
908 case StvvOp:
909 case UserOp:
910 case UsrapOp:
911 case UsravOp:
912 case UsrrpOp:
913 case UsrrvOp:
914 // these operators never match pevious operators
915 break;
916
917 case AbsOp:
918 case AcosOp:
919 case AcoshOp:
920 case AddpvOp:
921 case AddvvOp:
922 case AsinOp:
923 case AsinhOp:
924 case AtanOp:
925 case AtanhOp:
926 case CosOp:
927 case CoshOp:
928 case DisOp:
929 case DivpvOp:
930 case DivvpOp:
931 case DivvvOp:
932 case EqpvOp:
933 case EqvvOp:
934 case ErfOp:
935 case ExpOp:
936 case Expm1Op:
937 case LepvOp:
938 case LevpOp:
939 case LevvOp:
940 case LogOp:
941 case Log1pOp:
942 case LtpvOp:
943 case LtvpOp:
944 case LtvvOp:
945 case MulpvOp:
946 case MulvvOp:
947 case NepvOp:
948 case NevvOp:
949 case PowpvOp:
950 case PowvpOp:
951 case PowvvOp:
952 case SignOp:
953 case SinOp:
954 case SinhOp:
955 case SqrtOp:
956 case SubpvOp:
957 case SubvpOp:
958 case SubvvOp:
959 case TanOp:
960 case TanhOp:
961 case ZmulpvOp:
962 case ZmulvpOp:
963 case ZmulvvOp:
964 // check for a previous match
965 match_op(play, opt_op_info, i_op, hash_table_op );
966 if( opt_op_info[i_op].previous != 0 )
967 { // like a unary operator that assigns i_op equal to previous.
968 size_t previous = opt_op_info[i_op].previous;
969 bool sum_op = false;
970 CPPAD_ASSERT_UNKNOWN( previous < i_op );
971 usage_cexp_result2arg(
972 play, sum_op, i_op, previous, opt_op_info, cexp_set
973 );
974 }
975 break;
976 }
977 }
978 // ----------------------------------------------------------------------
979 // compute cexp_info
980 // ----------------------------------------------------------------------
981 if( cexp_set.n_set() == 0 )
982 return;
983 //
984 // initialize information for each conditional expression
985 cexp_info.resize(num_cexp_op);
986 skip_op_true.resize(num_cexp_op, num_op);
987 skip_op_false.resize(num_cexp_op, num_op);
988 //
989 for(size_t i = 0; i < num_cexp_op; i++)
990 { CPPAD_ASSERT_UNKNOWN(
991 opt_op_info[i].previous == 0 || opt_op_info[i].usage == yes_usage
992 );
993 i_op = cexp2op[i];
994 play->get_op_info(i_op, op, arg, i_var);
995 CPPAD_ASSERT_UNKNOWN( op == CExpOp );
996 //
997 struct_cexp_info info;
998 info.i_op = i_op;
999 info.cop = CompareOp( arg[0] );
1000 info.flag = arg[1];
1001 info.left = arg[2];
1002 info.right = arg[3];
1003 //
1004 // max_left_right
1005 size_t index = 0;
1006 if( arg[1] & 1 )
1007 index = std::max(index, info.left);
1008 if( arg[1] & 2 )
1009 index = std::max(index, info.right);
1010 CPPAD_ASSERT_UNKNOWN( index > 0 );
1011 info.max_left_right = index;
1012 //
1013 cexp_info[i] = info;
1014 };
1015 // Determine which operators can be conditionally skipped
1016 i_op = 0;
1017 while(i_op < num_op)
1018 { size_t j_op = i_op;
1019 bool keep = opt_op_info[i_op].usage != no_usage;
1020 keep &= opt_op_info[i_op].usage != csum_usage;
1021 keep &= opt_op_info[i_op].previous == 0;
1022 if( keep )
1023 { sparse_list_const_iterator itr(cexp_set, i_op);
1024 if( *itr != cexp_set.end() )
1025 { if( play->GetOp(i_op) == UserOp )
1026 { // i_op is the first operations in this user atomic call.
1027 // Find the last operation in this call.
1028 ++j_op;
1029 while( play->GetOp(j_op) != UserOp )
1030 { switch( play->GetOp(j_op) )
1031 { case UsrapOp:
1032 case UsravOp:
1033 case UsrrpOp:
1034 case UsrrvOp:
1035 break;
1036
1037 default:
1038 CPPAD_ASSERT_UNKNOWN(false);
1039 }
1040 ++j_op;
1041 }
1042 }
1043 }
1044 while( *itr != cexp_set.end() )
1045 { size_t element = *itr;
1046 size_t index = element / 2;
1047 bool compare = bool( element % 2 );
1048 if( compare == false )
1049 { // cexp_info[index].skip_op_false.push_back(i_op);
1050 skip_op_false.add_element(index, i_op);
1051 if( j_op != i_op )
1052 { // cexp_info[index].skip_op_false.push_back(j_op);
1053 skip_op_false.add_element(index, j_op);
1054 }
1055 }
1056 else
1057 { // cexp_info[index].skip_op_true.push_back(i_op);
1058 skip_op_true.add_element(index, i_op);
1059 if( j_op != i_op )
1060 { // cexp_info[index].skip_op_true.push_back(j_op);
1061 skip_op_true.add_element(index, j_op);
1062 }
1063 }
1064 ++itr;
1065 }
1066 }
1067 CPPAD_ASSERT_UNKNOWN( i_op <= j_op );
1068 i_op += (1 + j_op) - i_op;
1069 }
1070 return;
1071 }
1072
1073 } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
1074
1075 # endif
1076