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