1 
2 # ifndef CPPAD_LOCAL_OPTIMIZE_OPTIMIZE_RUN_HPP
3 # define CPPAD_LOCAL_OPTIMIZE_OPTIMIZE_RUN_HPP
4 
5 /* --------------------------------------------------------------------------
6 CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-17 Bradley M. Bell
7 
8 CppAD is distributed under multiple licenses. This distribution is under
9 the terms of the
10                     Eclipse Public License Version 1.0.
11 
12 A copy of this license is included in the COPYING file of this distribution.
13 Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
14 -------------------------------------------------------------------------- */
15 
16 # include <stack>
17 # include <iterator>
18 # include <cppad/local/optimize/usage.hpp>
19 # include <cppad/local/optimize/get_opt_op_info.hpp>
20 # include <cppad/local/optimize/old2new.hpp>
21 # include <cppad/local/optimize/size_pair.hpp>
22 # include <cppad/local/optimize/csum_variable.hpp>
23 # include <cppad/local/optimize/csum_stacks.hpp>
24 # include <cppad/local/optimize/cexp_info.hpp>
25 # include <cppad/local/optimize/record_pv.hpp>
26 # include <cppad/local/optimize/record_vp.hpp>
27 # include <cppad/local/optimize/record_vv.hpp>
28 # include <cppad/local/optimize/record_csum.hpp>
29 
30 /*!
31 \file optimize_run.hpp
32 Convert a player object to an optimized recorder object
33 */
34 // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
35 namespace CppAD { namespace local { namespace optimize  {
36 /*!
37 Convert a player object to an optimized recorder object
38 
39 \tparam Base
40 base type for the operator; i.e., this operation was recorded
41 using AD< \a Base > and computations by this routine are done using type
42 \a Base.
43 
44 
45 \param options
46 \li
47 If the sub-string "no_conditional_skip" appears,
48 conditional skip operations will not be generated.
49 This may make the optimize routine use significantly less memory
50 and take significantly less time.
51 \li
52 If the sub-string "no_compare_op" appears,
53 then comparison operators will be removed from the optimized tape.
54 These operators are necessary for the compare_change function to be
55 be meaningful in the resulting recording.
56 On the other hand, they are not necessary and take extra time
57 when compare_change is not used.
58 \li
59 If the sub-string "no_print_for" appears,
60 then print forward (PriOp) operators will be removed from the optimized tape.
61 These operators are useful for reporting problems evaluating derivatives
62 at independent variable values different from those used to record a function.
63 
64 \param n
65 is the number of independent variables on the tape.
66 
67 \param dep_taddr
68 On input this vector contains the indices for each of the dependent
69 variable values in the operation sequence corresponding to \a play.
70 Upon return it contains the indices for the same variables but in
71 the operation sequence corresponding to \a rec.
72 
73 \param play
74 This is the operation sequence that we are optimizing.
75 It is essentially const, except for play back state which
76 changes while it plays back the operation seqeunce.
77 
78 \param rec
79 The input contents of this recording does not matter.
80 Upon return, it contains an optimized verison of the
81 operation sequence corresponding to \a play.
82 */
83 
84 template <class Base>
optimize_run(const std::string & options,size_t n,CppAD::vector<size_t> & dep_taddr,const player<Base> * play,recorder<Base> * rec)85 void optimize_run(
86 	const std::string&           options   ,
87 	size_t                       n         ,
88 	CppAD::vector<size_t>&       dep_taddr ,
89 	const player<Base>*          play      ,
90 	recorder<Base>*              rec       )
91 {
92 	bool conditional_skip = true;
93 	bool compare_op       = true;
94 	bool print_for_op     = true;
95 	size_t index = 0;
96 	while( index < options.size() )
97 	{	while( index < options.size() && options[index] == ' ' )
98 			++index;
99 		std::string option;
100 		while( index < options.size() && options[index] != ' ' )
101 			option += options[index++];
102 		if( option != "" )
103 		{	if( option == "no_conditional_skip" )
104 				conditional_skip = false;
105 			else if( option == "no_compare_op" )
106 				compare_op = false;
107 			else if( option == "no_print_for_op" )
108 				print_for_op = false;
109 			else
110 			{	option += " is not a valid optimize option";
111 				CPPAD_ASSERT_KNOWN( false , option.c_str() );
112 			}
113 		}
114 	}
115 	// number of operators in the player
116 	const size_t num_op = play->num_op_rec();
117 	CPPAD_ASSERT_UNKNOWN(
118 		num_op < size_t( std::numeric_limits<addr_t>::max() )
119 	);
120 
121 	// number of variables in the player
122 	const size_t num_var = play->num_var_rec();
123 
124 	// number of  VecAD indices
125 	size_t num_vecad_ind   = play->num_vec_ind_rec();
126 
127 	// number of VecAD vectors
128 	size_t num_vecad_vec   = play->num_vecad_vec_rec();
129 
130 	// operator information
131 	vector<struct_cexp_info>  cexp_info;
132 	sparse_list               skip_op_true;
133 	sparse_list               skip_op_false;
134 	vector<bool>              vecad_used;
135 	vector<struct_opt_op_info>    opt_op_info;
136 	get_opt_op_info(
137 		conditional_skip,
138 		compare_op,
139 		print_for_op,
140 		play,
141 		dep_taddr,
142 		cexp_info,
143 		skip_op_true,
144 		skip_op_false,
145 		vecad_used,
146 		opt_op_info
147 	);
148 
149 	// nan with type Base
150 	Base base_nan = Base( std::numeric_limits<double>::quiet_NaN() );
151 
152 	// -------------------------------------------------------------
153 	// information for current operator
154 	size_t        i_op;   // index
155 	OpCode        op;     // operator
156 	const addr_t* arg;    // arguments
157 	size_t        i_var;  // variable index of primary (last) result
158 
159 	enum_user_state user_state;
160 	// -------------------------------------------------------------
161 	// conditional expression information
162 	//
163 	// Size of the conditional expression information structure.
164 	// This is equal to the number of conditional expressions when
165 	// conditional_skip is true, otherwise it is zero.
166 	size_t num_cexp = cexp_info.size();
167 	CPPAD_ASSERT_UNKNOWN( conditional_skip || num_cexp == 0 );
168 	//
169 	// sort the conditional expression information by max_left_right
170 	// this is the conditional skip order
171 	vector<size_t> cskip_order(num_cexp);
172 	if( num_cexp > 0 )
173 	{	CppAD::vector<size_t> keys(num_cexp);
174 		for(size_t i = 0; i < num_cexp; i++)
175 			keys[i] = cexp_info[i].max_left_right;
176 		CppAD::index_sort(keys, cskip_order);
177 	}
178 	// initial index in conditional skip order
179 	size_t cskip_order_next = 0;
180 	//
181 	// initialize index in conditional expression order
182 	size_t cexp_next = 0;
183 
184 	// mapping from conditional expression index to conditional skip
185 	// information on new tape
186 	vector<struct_cskip_new> cskip_new(num_cexp);
187 	//
188 	// flag used to indicate that there is no conditional skip
189 	// for this conditional expression
190 	for(size_t i = 0; i < num_cexp; i++)
191 		cskip_new[i].i_arg = 0;
192 	// -------------------------------------------------------------
193 
194 	// Erase all information in the old recording
195 	rec->free();
196 
197 	// initialize mapping from old VecAD index to new VecAD index
198 	CPPAD_ASSERT_UNKNOWN(
199 		std::numeric_limits<addr_t>::max() >= num_vecad_ind
200 	);
201 	CppAD::vector<addr_t> new_vecad_ind(num_vecad_ind);
202 	for(size_t i = 0; i < num_vecad_ind; i++)
203 		new_vecad_ind[i] = addr_t( num_vecad_ind ); // invalid index
204 	{
205 		size_t j = 0;     // index into the old set of indices
206 		for(size_t i = 0; i < num_vecad_vec; i++)
207 		{	// length of this VecAD
208 			size_t length = play->GetVecInd(j);
209 			if( vecad_used[i] )
210 			{	// Put this VecAD vector in new recording
211 				CPPAD_ASSERT_UNKNOWN(length < num_vecad_ind);
212 				new_vecad_ind[j] = rec->PutVecInd(length);
213 				for(size_t k = 1; k <= length; k++) new_vecad_ind[j+k] =
214 					rec->PutVecInd(
215 						rec->PutPar(
216 							play->GetPar(
217 								play->GetVecInd(j+k)
218 				) ) );
219 			}
220 			// start of next VecAD
221 			j       += length + 1;
222 		}
223 		CPPAD_ASSERT_UNKNOWN( j == num_vecad_ind );
224 	}
225 	//
226 	// Mapping from old operator index to new operator information
227 	// (zero is invalid except for old2new[0].new_op and old2new[0].i_var)
228 	vector<struct_old2new> old2new(num_op);
229 	for(size_t i = 0; i < num_op; i++)
230 	{	old2new[i].new_op  = 0;
231 		old2new[i].new_var = 0;
232 	}
233 
234 
235 	// temporary buffer for new argument values
236 	addr_t new_arg[6];
237 
238 	// temporary work space used by record_csum
239 	// (decalared here to avoid realloaction of memory)
240 	struct_csum_stacks csum_work;
241 
242 	// tempory used to hold a size_pair
243 	struct_size_pair size_pair;
244 
245 	user_state = start_user;
246 	i_var      = 0;
247 	for(i_op = 0; i_op < num_op; ++i_op)
248 	{	addr_t mask; // temporary used in some switch cases
249 		//
250 		// this operator information
251 		size_t i_tmp;
252 		play->get_op_info(i_op, op, arg, i_tmp);
253 		if( NumRes(op) > 0 )
254 			i_var = i_tmp;
255 		//
256 		// determine if we should insert a conditional skip here
257 		bool skip  = conditional_skip;
258 		skip      &= cskip_order_next < num_cexp;
259 		skip      &= op != BeginOp;
260 		skip      &= op != InvOp;
261 		skip      &= user_state == start_user;
262 		if( skip )
263 		{	size_t j = cskip_order[cskip_order_next];
264 			if( NumRes(op) > 0 )
265 				skip &= cexp_info[j].max_left_right < i_var;
266 			else
267 				skip &= cexp_info[j].max_left_right <= i_var;
268 		}
269 		if( skip )
270 		{	size_t j = cskip_order[cskip_order_next];
271 			cskip_order_next++;
272 			size_t n_true   = skip_op_true.number_elements(j);
273 			size_t n_false  = skip_op_false.number_elements(j);
274 			skip &= n_true > 0 || n_false > 0;
275 			if( skip )
276 			{	CPPAD_ASSERT_UNKNOWN( NumRes(CSkipOp) == 0 );
277 				size_t n_arg   = 7 + n_true + n_false;
278 				// reserve space for the arguments to this operator but
279 				// delay setting them until we have all the new addresses
280 				cskip_new[j].i_arg = rec->ReserveArg(n_arg);
281 				// i_arg == 0 is used to check if conditional expression
282 				// has been skipped.
283 				CPPAD_ASSERT_UNKNOWN( cskip_new[j].i_arg > 0 );
284 				// There is no corresponding old operator in this case
285 				rec->PutOp(CSkipOp);
286 			}
287 		}
288 		if( op == UserOp )
289 		{	if( user_state == start_user )
290 				user_state = end_user;
291 			else
292 			{	CPPAD_ASSERT_UNKNOWN( user_state == end_user );
293 				user_state = start_user;
294 			}
295 		}
296 		size_t         previous;
297 		//
298 		CPPAD_ASSERT_UNKNOWN(
299 			std::numeric_limits<addr_t>::max() >= rec->num_op_rec()
300 		);
301 		//
302 		if( opt_op_info[i_op].usage != yes_usage )
303 		{	if( op == CExpOp )
304 				++cexp_next;
305 		}
306 		else switch( op )
307 		{
308 			case BeginOp:
309 			CPPAD_ASSERT_NARG_NRES(op, 1, 1);
310 			// Put BeginOp at beginning of recording
311 			old2new[i_op].new_op  = addr_t( rec->num_op_rec() );
312 			old2new[i_op].new_var = rec->PutOp(BeginOp);
313 			rec->PutArg(arg[0]);
314 			break;
315 
316 			// --------------------------------------------------------------
317 			// Unary operators, argument a variable, one result
318 			case AbsOp:
319 			case AcosOp:
320 			case AcoshOp:
321 			case AsinOp:
322 			case AsinhOp:
323 			case AtanOp:
324 			case AtanhOp:
325 			case CosOp:
326 			case CoshOp:
327 			case ErfOp:
328 			case ExpOp:
329 			case Expm1Op:
330 			case LogOp:
331 			case Log1pOp:
332 			case SignOp:
333 			case SinOp:
334 			case SinhOp:
335 			case SqrtOp:
336 			case TanOp:
337 			case TanhOp:
338 			previous = opt_op_info[i_op].previous;
339 			if( previous > 0 )
340 			{	size_t j_op = previous;
341 				old2new[i_op].new_var = old2new[j_op].new_var;
342 			}
343 			else
344 			{	//
345 				new_arg[0]   = old2new[ play->var2op(arg[0]) ].new_var;
346 				rec->PutArg( new_arg[0] );
347 				//
348 				old2new[i_op].new_op  = addr_t( rec->num_op_rec() );
349 				old2new[i_op].new_var = rec->PutOp(op);
350 				CPPAD_ASSERT_UNKNOWN(
351 					new_arg[0] < old2new[play->var2op(i_var)].new_var
352 				);
353 				if( op == ErfOp )
354 				{	CPPAD_ASSERT_NARG_NRES(op, 3, 5);
355 					// Error function is a special case
356 					// second argument is always the parameter 0
357 					// third argument is always the parameter 2 / sqrt(pi)
358 					CPPAD_ASSERT_UNKNOWN( NumArg(ErfOp) == 3 );
359 					rec->PutArg( rec->PutPar( Base(0.0) ) );
360 					rec->PutArg( rec->PutPar(
361 						Base( 1.0 / std::sqrt( std::atan(1.0) ) )
362 					) );
363 				}
364 				else
365 				{	// some of these operators have an auxillary result;
366 					// e.g. sine and cosine are computed together.
367 					CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 );
368 					CPPAD_ASSERT_UNKNOWN( NumRes(op) ==1 || NumRes(op) == 2 );
369 				}
370 			}
371 			break;
372 			// ---------------------------------------------------
373 			// Binary operators, left variable, right parameter, one result
374 			case SubvpOp:
375 			// check if this is the top of a csum connection
376 			if( opt_op_info[i_op].usage == csum_usage )
377 				break;
378 			if( opt_op_info[ play->var2op(arg[0]) ].usage == csum_usage )
379 			{
380 				// convert to a sequence of summation operators
381 				size_pair = record_csum(
382 					play                ,
383 					opt_op_info         ,
384 					old2new             ,
385 					i_var               ,
386 					rec                 ,
387 					csum_work
388 				);
389 				old2new[i_op].new_op  = addr_t( size_pair.i_op );
390 				old2new[i_op].new_var = addr_t( size_pair.i_var );
391 				// abort rest of this case
392 				break;
393 			}
394 			case DivvpOp:
395 			case PowvpOp:
396 			case ZmulvpOp:
397 			previous = opt_op_info[i_op].previous;
398 			if( previous > 0 )
399 			{	size_t j_op = previous;
400 				old2new[i_op].new_var = old2new[j_op].new_var;
401 			}
402 			else
403 			{	//
404 				size_pair = record_vp(
405 					play                ,
406 					old2new             ,
407 					i_op                ,
408 					rec
409 				);
410 				old2new[i_op].new_op  = addr_t( size_pair.i_op );
411 				old2new[i_op].new_var = addr_t( size_pair.i_var );
412 			}
413 			break;
414 			// ---------------------------------------------------
415 			// Binary operators, left index, right variable, one result
416 			case DisOp:
417 			CPPAD_ASSERT_NARG_NRES(op, 2, 1);
418 			previous = opt_op_info[i_op].previous;
419 			if( previous > 0 )
420 			{	size_t j_op = previous;
421 				old2new[i_op].new_var = old2new[j_op].new_var;
422 			}
423 			else
424 			{	//
425 				new_arg[0] = arg[0];
426 				new_arg[1] = old2new[ play->var2op(arg[1]) ].new_var;
427 				rec->PutArg( new_arg[0], new_arg[1] );
428 				//
429 				old2new[i_op].new_op  = addr_t( rec->num_op_rec() );
430 				old2new[i_op].new_var = rec->PutOp(op);
431 				CPPAD_ASSERT_UNKNOWN(
432 					new_arg[1] < old2new[play->var2op(i_var)].new_var
433 				);
434 			}
435 			break;
436 
437 			// ---------------------------------------------------
438 			// Binary operators, left parameter, right variable, one result
439 			case SubpvOp:
440 			case AddpvOp:
441 			// check if this is the top of a csum connection
442 			if( opt_op_info[i_op].usage == csum_usage )
443 				break;
444 			if( opt_op_info[ play->var2op(arg[1]) ].usage == csum_usage )
445 			{
446 				// convert to a sequence of summation operators
447 				size_pair = record_csum(
448 					play                ,
449 					opt_op_info         ,
450 					old2new             ,
451 					i_var               ,
452 					rec                 ,
453 					csum_work
454 				);
455 				old2new[i_op].new_op  = addr_t( size_pair.i_op );
456 				old2new[i_op].new_var = addr_t( size_pair.i_var );
457 				// abort rest of this case
458 				break;
459 			}
460 			case DivpvOp:
461 			case MulpvOp:
462 			case PowpvOp:
463 			case ZmulpvOp:
464 			previous = opt_op_info[i_op].previous;
465 			if( previous > 0 )
466 			{	size_t j_op = previous;
467 				old2new[i_op].new_var = old2new[j_op].new_var;
468 			}
469 			else
470 			{	//
471 				size_pair = record_pv(
472 					play                ,
473 					old2new             ,
474 					i_op                ,
475 					rec
476 				);
477 				old2new[i_op].new_op  = addr_t( size_pair.i_op );
478 				old2new[i_op].new_var = addr_t( size_pair.i_var );
479 			}
480 			break;
481 			// ---------------------------------------------------
482 			// Binary operator, left and right variables, one result
483 			case AddvvOp:
484 			case SubvvOp:
485 			// check if this is the top of a csum connection
486 			if( opt_op_info[i_op].usage == csum_usage )
487 				break;
488 			if(
489 				opt_op_info[ play->var2op(arg[0]) ].usage == csum_usage ||
490 				opt_op_info[ play->var2op(arg[1]) ].usage == csum_usage
491 			)
492 			{
493 				// convert to a sequence of summation operators
494 				size_pair = record_csum(
495 					play                ,
496 					opt_op_info         ,
497 					old2new             ,
498 					i_var               ,
499 					rec                 ,
500 					csum_work
501 				);
502 				old2new[i_op].new_op  = addr_t( size_pair.i_op );
503 				old2new[i_op].new_var = addr_t( size_pair.i_var );
504 				// abort rest of this case
505 				break;
506 			}
507 			case DivvvOp:
508 			case MulvvOp:
509 			case PowvvOp:
510 			case ZmulvvOp:
511 			previous = opt_op_info[i_op].previous;
512 			if( previous > 0 )
513 			{	size_t j_op = previous;
514 				old2new[i_op].new_var = old2new[j_op].new_var;
515 			}
516 			else
517 			{	//
518 				size_pair = record_vv(
519 					play                ,
520 					old2new             ,
521 					i_op                ,
522 					rec
523 				);
524 				old2new[i_op].new_op  = addr_t( size_pair.i_op );
525 				old2new[i_op].new_var = addr_t( size_pair.i_var );
526 			}
527 			break;
528 			// ---------------------------------------------------
529 			// Conditional expression operators
530 			case CExpOp:
531 			CPPAD_ASSERT_NARG_NRES(op, 6, 1);
532 			new_arg[0] = arg[0];
533 			new_arg[1] = arg[1];
534 			mask = 1;
535 			for(size_t i = 2; i < 6; i++)
536 			{	if( arg[1] & mask )
537 				{	new_arg[i] = old2new[ play->var2op(arg[i]) ].new_var;
538 					CPPAD_ASSERT_UNKNOWN(
539 						size_t(new_arg[i]) < num_var
540 					);
541 				}
542 				else	new_arg[i] = rec->PutPar(
543 						play->GetPar( arg[i] )
544 				);
545 				mask = mask << 1;
546 			}
547 			rec->PutArg(
548 				new_arg[0] ,
549 				new_arg[1] ,
550 				new_arg[2] ,
551 				new_arg[3] ,
552 				new_arg[4] ,
553 				new_arg[5]
554 			);
555 			old2new[i_op].new_op  = addr_t( rec->num_op_rec() );
556 			old2new[i_op].new_var = rec->PutOp(op);
557 			//
558 			// The new addresses for left and right are used during
559 			// fill in the arguments for the CSkip operations. This does not
560 			// affect max_left_right which is used during this sweep.
561 			if( conditional_skip )
562 			{	CPPAD_ASSERT_UNKNOWN( cexp_next < num_cexp );
563 				CPPAD_ASSERT_UNKNOWN( cexp_info[cexp_next].i_op == i_op );
564 				cskip_new[ cexp_next ].left  = new_arg[2];
565 				cskip_new[ cexp_next ].right = new_arg[3];
566 				++cexp_next;
567 			}
568 			break;
569 			// ---------------------------------------------------
570 			// Operations with no arguments and no results
571 			case EndOp:
572 			CPPAD_ASSERT_NARG_NRES(op, 0, 0);
573 			old2new[i_op].new_op = addr_t( rec->num_op_rec() );
574 			rec->PutOp(op);
575 			break;
576 			// ---------------------------------------------------
577 			// Operations with two arguments and no results
578 			case LepvOp:
579 			case LtpvOp:
580 			case EqpvOp:
581 			case NepvOp:
582 			CPPAD_ASSERT_NARG_NRES(op, 2, 0);
583 			new_arg[0] = rec->PutPar( play->GetPar(arg[0]) );
584 			new_arg[1] = old2new[ play->var2op(arg[1]) ].new_var;
585 			rec->PutArg(new_arg[0], new_arg[1]);
586 			old2new[i_op].new_op = addr_t( rec->num_op_rec() );
587 			rec->PutOp(op);
588 			break;
589 			//
590 			case LevpOp:
591 			case LtvpOp:
592 			CPPAD_ASSERT_NARG_NRES(op, 2, 0);
593 			new_arg[0] = old2new[ play->var2op(arg[0]) ].new_var;
594 			new_arg[1] = rec->PutPar( play->GetPar(arg[1]) );
595 			rec->PutArg(new_arg[0], new_arg[1]);
596 			old2new[i_op].new_op = addr_t( rec->num_op_rec() );
597 			rec->PutOp(op);
598 			break;
599 			//
600 			case LevvOp:
601 			case LtvvOp:
602 			case EqvvOp:
603 			case NevvOp:
604 			CPPAD_ASSERT_NARG_NRES(op, 2, 0);
605 			new_arg[0] = old2new[ play->var2op(arg[0]) ].new_var;
606 			new_arg[1] = old2new[ play->var2op(arg[1]) ].new_var;
607 			rec->PutArg(new_arg[0], new_arg[1]);
608 			old2new[i_op].new_op = addr_t( rec->num_op_rec() );
609 			rec->PutOp(op);
610 			break;
611 
612 			// ---------------------------------------------------
613 			// Operations with no arguments and one result
614 			case InvOp:
615 			CPPAD_ASSERT_NARG_NRES(op, 0, 1);
616 			old2new[i_op].new_op  = addr_t( rec->num_op_rec() );
617 			old2new[i_op].new_var = rec->PutOp(op);
618 			break;
619 
620 			// ---------------------------------------------------
621 			// Unary operators, argument a parameter, one result
622 			case ParOp:
623 			CPPAD_ASSERT_NARG_NRES(op, 1, 1);
624 			new_arg[0] = rec->PutPar( play->GetPar(arg[0] ) );
625 			rec->PutArg( new_arg[0] );
626 			//
627 			old2new[i_op].new_op  = addr_t( rec->num_op_rec() );
628 			old2new[i_op].new_var = rec->PutOp(op);
629 			break;
630 
631 			// ---------------------------------------------------
632 			// print forward operator
633 			case PriOp:
634 			CPPAD_ASSERT_NARG_NRES(op, 5, 0);
635 			// arg[0]
636 			new_arg[0] = arg[0];
637 			//
638 			// arg[1]
639 			if( arg[0] & 1 )
640 			{	new_arg[1] = old2new[ play->var2op(arg[1]) ].new_var;
641 				CPPAD_ASSERT_UNKNOWN( size_t(new_arg[1]) < num_var );
642 			}
643 			else
644 			{	new_arg[1] = rec->PutPar( play->GetPar( arg[1] ) );
645 			}
646 			//
647 			// arg[3]
648 			if( arg[0] & 2 )
649 			{	new_arg[3] = old2new[ play->var2op(arg[3]) ].new_var;
650 				CPPAD_ASSERT_UNKNOWN( size_t(new_arg[3]) < num_var );
651 			}
652 			else
653 			{	new_arg[3] = rec->PutPar( play->GetPar( arg[3] ) );
654 			}
655 			new_arg[2] = rec->PutTxt( play->GetTxt(arg[2]) );
656 			new_arg[4] = rec->PutTxt( play->GetTxt(arg[4]) );
657 			//
658 			rec->PutArg(
659 				new_arg[0] ,
660 				new_arg[1] ,
661 				new_arg[2] ,
662 				new_arg[3] ,
663 				new_arg[4]
664 			);
665 			// new operator
666 			old2new[i_op].new_op  = addr_t( rec->num_op_rec() );
667 			// no new variable
668 			rec->PutOp(op);
669 			break;
670 
671 			// ---------------------------------------------------
672 			// VecAD operators
673 
674 			// Load using a parameter index
675 			case LdpOp:
676 			CPPAD_ASSERT_NARG_NRES(op, 3, 1);
677 			new_arg[0] = new_vecad_ind[ arg[0] ];
678 			new_arg[1] = arg[1];
679 			CPPAD_ASSERT_UNKNOWN(
680 				std::numeric_limits<addr_t>::max() >= rec->num_load_op_rec()
681 			);
682 			new_arg[2] = addr_t( rec->num_load_op_rec() );
683 			CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind );
684 			rec->PutArg(
685 				new_arg[0],
686 				new_arg[1],
687 				new_arg[2]
688 			);
689 			old2new[i_op].new_op  = addr_t( rec->num_op_rec() );
690 			old2new[i_op].new_var = rec->PutLoadOp(op);
691 			break;
692 
693 			// Load using a variable index
694 			case LdvOp:
695 			CPPAD_ASSERT_NARG_NRES(op, 3, 1);
696 			new_arg[0] = new_vecad_ind[ arg[0] ];
697 			new_arg[1] = old2new[ play->var2op(arg[1]) ].new_var;
698 			CPPAD_ASSERT_UNKNOWN(
699 				std::numeric_limits<addr_t>::max() >= rec->num_load_op_rec()
700 			);
701 			new_arg[2] = addr_t( rec->num_load_op_rec() );
702 			CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind );
703 			CPPAD_ASSERT_UNKNOWN( size_t(new_arg[1]) < num_var );
704 			rec->PutArg(
705 				new_arg[0],
706 				new_arg[1],
707 				new_arg[2]
708 			);
709 			old2new[i_op].new_op  = addr_t( rec->num_op_rec() );
710 			old2new[i_op].new_var = rec->PutLoadOp(op);
711 			break;
712 
713 			// Store a parameter using a parameter index
714 			case StppOp:
715 			CPPAD_ASSERT_NARG_NRES(op, 3, 0);
716 			new_arg[0] = new_vecad_ind[ arg[0] ];
717 			new_arg[1] = rec->PutPar( play->GetPar(arg[1]) );
718 			new_arg[2] = rec->PutPar( play->GetPar(arg[2]) );
719 			CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind );
720 			rec->PutArg(
721 				new_arg[0],
722 				new_arg[1],
723 				new_arg[2]
724 			);
725 			old2new[i_op].new_op = addr_t( rec->num_op_rec() );
726 			rec->PutOp(op);
727 			break;
728 
729 			// Store a parameter using a variable index
730 			case StvpOp:
731 			CPPAD_ASSERT_NARG_NRES(op, 3, 0);
732 			new_arg[0] = new_vecad_ind[ arg[0] ];
733 			new_arg[1] = old2new[ play->var2op(arg[1]) ].new_var;
734 			new_arg[2] = rec->PutPar( play->GetPar(arg[2]) );
735 			CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind );
736 			CPPAD_ASSERT_UNKNOWN( size_t(new_arg[1]) < num_var );
737 			rec->PutArg(
738 				new_arg[0],
739 				new_arg[1],
740 				new_arg[2]
741 			);
742 			old2new[i_op].new_op = addr_t( rec->num_op_rec() );
743 			rec->PutOp(op);
744 			break;
745 
746 			// Store a variable using a parameter index
747 			case StpvOp:
748 			CPPAD_ASSERT_NARG_NRES(op, 3, 0);
749 			new_arg[0] = new_vecad_ind[ arg[0] ];
750 			new_arg[1] = rec->PutPar( play->GetPar(arg[1]) );
751 			new_arg[2] = old2new[ play->var2op(arg[2]) ].new_var;
752 			CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind );
753 			CPPAD_ASSERT_UNKNOWN( size_t(new_arg[2]) < num_var );
754 			rec->PutArg(
755 				new_arg[0],
756 				new_arg[1],
757 				new_arg[2]
758 			);
759 			old2new[i_op].new_op = addr_t( rec->num_op_rec() );
760 			rec->PutOp(op);
761 			break;
762 
763 			// Store a variable using a variable index
764 			case StvvOp:
765 			CPPAD_ASSERT_NARG_NRES(op, 3, 0);
766 			new_arg[0] = new_vecad_ind[ arg[0] ];
767 			new_arg[1] = old2new[ play->var2op(arg[1]) ].new_var;
768 			new_arg[2] = old2new[ play->var2op(arg[2]) ].new_var;
769 			CPPAD_ASSERT_UNKNOWN( size_t(new_arg[0]) < num_vecad_ind );
770 			CPPAD_ASSERT_UNKNOWN( size_t(new_arg[1]) < num_var );
771 			CPPAD_ASSERT_UNKNOWN( size_t(new_arg[2]) < num_var );
772 			rec->PutArg(
773 				new_arg[0],
774 				new_arg[1],
775 				new_arg[2]
776 			);
777 			old2new[i_op].new_op = addr_t( rec->num_op_rec() );
778 			rec->PutOp(op);
779 			break;
780 
781 			// -----------------------------------------------------------
782 			// user atomic function call operators
783 
784 			case UserOp:
785 			CPPAD_ASSERT_NARG_NRES(op, 4, 0);
786 			// user_old, user_n, user_m
787 			rec->PutArg(arg[0], arg[1], arg[2], arg[3]);
788 			old2new[i_op].new_op = addr_t( rec->num_op_rec() );
789 			rec->PutOp(UserOp);
790 			break;
791 
792 			case UsrapOp:
793 			CPPAD_ASSERT_NARG_NRES(op, 1, 0);
794 			new_arg[0] = rec->PutPar( play->GetPar(arg[0]) );
795 			rec->PutArg(new_arg[0]);
796 			old2new[i_op].new_op = addr_t( rec->num_op_rec() );
797 			rec->PutOp(UsrapOp);
798 			break;
799 
800 			case UsravOp:
801 			CPPAD_ASSERT_NARG_NRES(op, 1, 0);
802 			new_arg[0] = old2new[ play->var2op(arg[0]) ].new_var;
803 			if( size_t(new_arg[0]) < num_var )
804 			{	rec->PutArg(new_arg[0]);
805 				old2new[i_op].new_op = addr_t( rec->num_op_rec() );
806 				rec->PutOp(UsravOp);
807 			}
808 			else
809 			{	// This argument does not affect the result and
810 				// has been optimized out so use nan in its place.
811 				new_arg[0] = rec->PutPar( base_nan );
812 				rec->PutArg(new_arg[0]);
813 				old2new[i_op].new_op = addr_t( rec->num_op_rec() );
814 				rec->PutOp(UsrapOp);
815 			}
816 			break;
817 
818 			case UsrrpOp:
819 			CPPAD_ASSERT_NARG_NRES(op, 1, 0);
820 			new_arg[0] = rec->PutPar( play->GetPar(arg[0]) );
821 			rec->PutArg(new_arg[0]);
822 			old2new[i_op].new_op = addr_t( rec->num_op_rec() );
823 			rec->PutOp(UsrrpOp);
824 			break;
825 
826 			case UsrrvOp:
827 			CPPAD_ASSERT_NARG_NRES(op, 0, 1);
828 			old2new[i_op].new_op  = addr_t( rec->num_op_rec() );
829 			old2new[i_op].new_var = rec->PutOp(UsrrvOp);
830 			break;
831 			// ---------------------------------------------------
832 
833 			// all cases should be handled above
834 			default:
835 			CPPAD_ASSERT_UNKNOWN(false);
836 
837 		}
838 	}
839 	// modify the dependent variable vector to new indices
840 	for(size_t i = 0; i < dep_taddr.size(); i++ )
841 	{	dep_taddr[i] = old2new[ play->var2op(dep_taddr[i]) ].new_var;
842 		CPPAD_ASSERT_UNKNOWN( size_t(dep_taddr[i]) < num_var );
843 	}
844 
845 # ifndef NDEBUG
846 	for(i_op = 0; i_op < num_op; i_op++)
847 	{	play->get_op_info(i_op, op, arg, i_var);
848 		if( NumRes(op) > 0 )
849 			CPPAD_ASSERT_UNKNOWN(
850 				size_t(old2new[i_op].new_op) < rec->num_op_rec()
851 			);
852 	}
853 # endif
854 	// make sure that all the conditional expressions have been
855 	// checked to see if they are still present
856 	CPPAD_ASSERT_UNKNOWN( cskip_order_next == num_cexp );
857 	// fill in the arguments for the CSkip operations
858 	for(size_t i = 0; i < num_cexp; i++)
859 	{	// if cskip_new[i].i_arg == 0, this conditional expression was skipped
860 		if( cskip_new[i].i_arg > 0 )
861 		{	struct_cexp_info info = cexp_info[i];
862 			size_t n_true  = skip_op_true.number_elements(i);
863 			size_t n_false = skip_op_false.number_elements(i);
864 			size_t i_arg   = cskip_new[i].i_arg;
865 			size_t left    = cskip_new[i].left;
866 			size_t right   = cskip_new[i].right;
867 			rec->ReplaceArg(i_arg++, info.cop   );
868 			rec->ReplaceArg(i_arg++, info.flag  );
869 			rec->ReplaceArg(i_arg++, left  );
870 			rec->ReplaceArg(i_arg++, right );
871 			rec->ReplaceArg(i_arg++, n_true     );
872 			rec->ReplaceArg(i_arg++, n_false    );
873 			sparse_list::const_iterator itr_true(skip_op_true, i);
874 			while( *itr_true != skip_op_true.end() )
875 			{	i_op = *itr_true;
876 				// opt_op_info[i_op].usage == yes_usage
877 				CPPAD_ASSERT_UNKNOWN( old2new[i_op].new_op != 0 );
878 				rec->ReplaceArg(i_arg++, old2new[i_op].new_op );
879 				//
880 				++itr_true;
881 			}
882 			sparse_list::const_iterator itr_false(skip_op_false, i);
883 			while( *itr_false != skip_op_false.end() )
884 			{	i_op   = *itr_false;
885 				// opt_op_info[i_op].usage == yes_usage
886 				CPPAD_ASSERT_UNKNOWN( old2new[i_op].new_op != 0 );
887 				rec->ReplaceArg(i_arg++, old2new[i_op].new_op );
888 				//
889 				++itr_false;
890 			}
891 			rec->ReplaceArg(i_arg++, n_true + n_false);
892 # ifndef NDEBUG
893 			size_t n_arg   = 7 + n_true + n_false;
894 			CPPAD_ASSERT_UNKNOWN( cskip_new[i].i_arg + n_arg == i_arg );
895 # endif
896 		}
897 	}
898 }
899 
900 } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
901 
902 # endif
903