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