CppAD: A C++ Algorithmic Differentiation Package
20130102
|
00001 /* $Id$ */ 00002 # ifndef CPPAD_OP_CODE_INCLUDED 00003 # define CPPAD_OP_CODE_INCLUDED 00004 00005 /* -------------------------------------------------------------------------- 00006 CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-12 Bradley M. Bell 00007 00008 CppAD is distributed under multiple licenses. This distribution is under 00009 the terms of the 00010 Eclipse Public License Version 1.0. 00011 00012 A copy of this license is included in the COPYING file of this distribution. 00013 Please visit http://www.coin-or.org/CppAD/ for information on other licenses. 00014 -------------------------------------------------------------------------- */ 00015 # include <string> 00016 # include <sstream> 00017 # include <iomanip> 00018 00019 # include <cppad/local/define.hpp> 00020 # include <cppad/local/cppad_assert.hpp> 00021 00022 // needed before one can use CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL 00023 # include <cppad/thread_alloc.hpp> 00024 00025 CPPAD_BEGIN_NAMESPACE 00026 /*! 00027 \defgroup op_code_hpp op_code.hpp 00028 \{ 00029 \file op_code.hpp 00030 Defines the OpCode enum type and functions related to it. 00031 00032 */ 00033 00034 00035 /*! 00036 Type used to distinguish different AD< \a Base > atomic operations. 00037 00038 Each of the operators ends with the characters Op. Ignoring the Op at the end, 00039 the operators appear in alphabetical order. Binary operation where both 00040 operands have type AD< \a Base > use the following convention for thier endings: 00041 \verbatim 00042 Ending Left-Operand Right-Operand 00043 pvOp parameter variable 00044 vpOp variable parameter 00045 vvOp variable variable 00046 \endverbatim 00047 For example, AddpvOp represents the addition operator where the left 00048 operand is a parameter and the right operand is a variable. 00049 */ 00050 enum OpCode { 00051 AbsOp, // abs(variable) 00052 AcosOp, // asin(variable) 00053 AddpvOp, // parameter + variable 00054 AddvvOp, // variable + variable 00055 AsinOp, // asin(variable) 00056 AtanOp, // atan(variable) 00057 BeginOp, // used to mark the beginning of the tape 00058 CExpOp, // CondExp(cop, left, right, trueCase, falseCase) 00059 ComOp, // Compare(cop, result, left, right) 00060 CosOp, // cos(variable) 00061 CoshOp, // cosh(variable) 00062 CSumOp, // Cummulative summation (has variable number of arguments) 00063 DisOp, // discrete::eval(index, variable) 00064 DivpvOp, // parameter / variable 00065 DivvpOp, // variable / parameter 00066 DivvvOp, // variable / variable 00067 EndOp, // used to mark the end of the tape 00068 ExpOp, // exp(variable) 00069 InvOp, // independent variable 00070 LdpOp, // z[parameter] 00071 LdvOp, // z[variable] 00072 LogOp, // log(variable) 00073 MulpvOp, // parameter * variable 00074 MulvvOp, // variable * variable 00075 ParOp, // parameter 00076 PowpvOp, // pow(parameter, variable) 00077 PowvpOp, // pow(variable, parameter) 00078 PowvvOp, // pow(variable, variable) 00079 PriOp, // PrintFor(text, parameter or variable, parameter or variable) 00080 SignOp, // sign(variable) 00081 SinOp, // sin(variable) 00082 SinhOp, // sinh(variable) 00083 SqrtOp, // sqrt(variable) 00084 StppOp, // z[parameter] = parameter 00085 StpvOp, // z[parameter] = variable 00086 StvpOp, // z[variable] = parameter 00087 StvvOp, // z[variable] = variable 00088 SubpvOp, // parameter - variable 00089 SubvpOp, // variable - parameter 00090 SubvvOp, // variable - variable 00091 TanOp, // tan(variable) 00092 TanhOp, // tan(variable) 00093 // user atomic operation codes 00094 UserOp, // start of a user atomic operaiton 00095 UsrapOp, // this user atomic argument is a parameter 00096 UsravOp, // this user atomic argument is a variable 00097 UsrrpOp, // this user atomic result is a parameter 00098 UsrrvOp, // this user atomic result is a variable 00099 NumberOp 00100 }; 00101 // Note that bin/check_op_code.sh assumes the pattern '^\tNumberOp$' occurs 00102 // at the end of this list and only at the end of this list. 00103 00104 /*! 00105 Table containing number of arguments for the corresponding operator. 00106 00107 The i-th element in this table specifes the number of arguments stored for each 00108 occurance of the operator that is the i-th value in the OpCode enum type. 00109 For example, for the first three OpCode enum values we have 00110 \verbatim 00111 OpCode j NumArgTable[j] Meaning 00112 AbsOp 0 1 index of variable we are taking absolute value of 00113 AcosOp 1 1 index of variable we are taking cosine of 00114 AddpvOp 1 2 indices of parameter and variable we are adding 00115 \endverbatim 00116 Note that the meaning of the arguments depends on the operator. 00117 */ 00118 const size_t NumArgTable[] = { 00119 1, // AbsOp 00120 1, // AcosOp 00121 2, // AddpvOp 00122 2, // AddvvOp 00123 1, // AsinOp 00124 1, // AtanOp 00125 0, // BeginOp 00126 6, // CExpOp 00127 4, // ComOp 00128 1, // CosOp 00129 1, // CoshOp 00130 0, // CSumOp (actually has a variable number of arguments, not zero) 00131 2, // DisOp 00132 2, // DivpvOp 00133 2, // DivvpOp 00134 2, // DivvvOp 00135 0, // EndOp 00136 1, // ExpOp 00137 0, // InvOp 00138 3, // LdpOp 00139 3, // LdvOp 00140 1, // LogOp 00141 2, // MulpvOp 00142 2, // MulvvOp 00143 1, // ParOp 00144 2, // PowpvOp 00145 2, // PowvpOp 00146 2, // PowvvOp 00147 5, // PriOp 00148 1, // SignOp 00149 1, // SinOp 00150 1, // SinhOp 00151 1, // SqrtOp 00152 3, // StppOp 00153 3, // StpvOp 00154 3, // StvpOp 00155 3, // StvvOp 00156 2, // SubpvOp 00157 2, // SubvpOp 00158 2, // SubvvOp 00159 1, // TanOp 00160 1, // TanhOp 00161 4, // UserOp 00162 1, // UsrapOp 00163 1, // UsravOp 00164 1, // UsrrpOp 00165 0 // UsrrvOp 00166 }; 00167 00168 /*! 00169 Fetch the number of arguments for a specified operator. 00170 00171 \return 00172 Number of arguments corresponding to the specified operator. 00173 00174 \param op 00175 Operator for which we are fetching the number of arugments. 00176 00177 - Check that argument taple size equal to NumberOp 00178 - Check that \c CPPAD_OP_CODE_TYPE can support all the operator codes. 00179 - Check that \c op is a valid operator value. 00180 */ 00181 inline size_t NumArg( OpCode op) 00182 { CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL; 00183 # ifndef NDEBUG 00184 // only do these checks once to save time 00185 static bool first = true; 00186 if( first ) 00187 { CPPAD_ASSERT_UNKNOWN( size_t(NumberOp) == 00188 sizeof(NumArgTable) / sizeof(NumArgTable[0]) 00189 ); 00190 CPPAD_ASSERT_UNKNOWN( size_t(NumberOp) <= 00191 std::numeric_limits<CPPAD_OP_CODE_TYPE>::max() 00192 ); 00193 first = false; 00194 } 00195 // do this check every time 00196 CPPAD_ASSERT_UNKNOWN( size_t(op) < size_t(NumberOp) ); 00197 # endif 00198 00199 return NumArgTable[(size_t) op]; 00200 } 00201 00202 /*! 00203 Number of variables resulting from the corresponding operation. 00204 00205 The i-th element in this table specifes the number of varibles for each 00206 occurance of the operator that is the i-th value in the OpCode enum type. 00207 For example, for the first three OpCode enum values we have 00208 \verbatim 00209 OpCode j NumResTable[j] Meaning 00210 AbsOp 0 1 variable that is the result of the absolute value 00211 AcosOp 1 2 acos(x) and sqrt(1-x*x) are required for this op 00212 AddpvOp 1 1 variable that is the result of the addition 00213 \endverbatim 00214 */ 00215 // alphabetical order (ignoring the Op at the end) 00216 const size_t NumResTable[] = { 00217 1, // AbsOp 00218 2, // AcosOp 00219 1, // AddpvOp 00220 1, // AddvvOp 00221 2, // AsinOp 00222 2, // AtanOp 00223 1, // BeginOp offsets first variable to have index one (not zero) 00224 1, // CExpOp 00225 0, // ComOp 00226 2, // CosOp 00227 2, // CoshOp 00228 1, // CSumOp 00229 1, // DisOp 00230 1, // DivpvOp 00231 1, // DivvpOp 00232 1, // DivvvOp 00233 0, // EndOp 00234 1, // ExpOp 00235 1, // InvOp 00236 1, // LdpOp 00237 1, // LdvOp 00238 1, // LogOp 00239 1, // MulpvOp 00240 1, // MulvvOp 00241 1, // ParOp 00242 3, // PowpvOp 00243 3, // PowvpOp 00244 3, // PowvvOp 00245 0, // PriOp 00246 1, // SignOp 00247 2, // SinOp 00248 2, // SinhOp 00249 1, // SqrtOp 00250 0, // StppOp 00251 0, // StpvOp 00252 0, // StvpOp 00253 0, // StvvOp 00254 1, // SubpvOp 00255 1, // SubvpOp 00256 1, // SubvvOp 00257 2, // TanOp 00258 2, // TanhOp 00259 0, // UserOp 00260 0, // UsrapOp 00261 0, // UsravOp 00262 0, // UsrrpOp 00263 1, // UsrrvOp 00264 0 // Last entry not used: avoids warning by g++ 4.3.2 when pycppad builds 00265 }; 00266 00267 /*! 00268 Fetch the number of variables resulting from the specified operation. 00269 00270 \return 00271 number of variables resulting from the specified operator. 00272 00273 \param op 00274 Operator for which we are fetching the number of result variables. 00275 */ 00276 inline size_t NumRes(OpCode op) 00277 { // check ensuring conversion to size_t is as expected 00278 CPPAD_ASSERT_UNKNOWN( size_t(NumberOp) == 00279 sizeof(NumResTable) / sizeof(NumResTable[0]) - 1 00280 ); 00281 // this test ensures that all indices are within the table 00282 CPPAD_ASSERT_UNKNOWN( size_t(op) < size_t(NumberOp) ); 00283 00284 return NumResTable[(size_t) op]; 00285 } 00286 00287 /*! 00288 Prints a single field corresponding to an operator. 00289 00290 A specified leader is printed in front of the value 00291 and then the value is left justified in the following width character. 00292 00293 \tparam Type 00294 is the type of the value we are printing. 00295 00296 \param os 00297 is the stream that we are printing to. 00298 00299 \param leader 00300 are characters printed before the value. 00301 00302 \param value 00303 is the value being printed. 00304 00305 \param width 00306 is the number of character to print the value in. 00307 If the value does not fit in the width, the value is replace 00308 by width '*' characters. 00309 */ 00310 template <class Type> 00311 void printOpField( 00312 std::ostream &os , 00313 const char * leader , 00314 const Type &value , 00315 size_t width ) 00316 { 00317 std::ostringstream buffer; 00318 std::string str; 00319 00320 // first print the leader 00321 os << leader; 00322 00323 // print the value into an internal buffer 00324 buffer << std::setw(width) << value; 00325 str = buffer.str(); 00326 00327 // length of the string 00328 size_t len = str.size(); 00329 if( len > width ) 00330 { size_t i; 00331 for(i = 0; i < width-1; i++) 00332 os << str[i]; 00333 os << "*"; 00334 return; 00335 } 00336 00337 // count number of spaces at begining 00338 size_t nspace = 0; 00339 while(str[nspace] == ' ' && nspace < len) 00340 nspace++; 00341 00342 // left justify the string 00343 size_t i = nspace; 00344 while( i < len ) 00345 os << str[i++]; 00346 00347 i = width - len + nspace; 00348 while(i--) 00349 os << " "; 00350 } 00351 00352 /*! 00353 Prints a single operator, its operands, and the corresponding result values. 00354 00355 \tparam Base 00356 Is the base type for these AD< \a Base > operations. 00357 00358 \tparam Value 00359 Determines the type of the values that we are printing. 00360 00361 \param os 00362 is the output stream that the information is printed on. 00363 00364 \param Rec 00365 Is the entire recording for the tape that this operator is in. 00366 00367 \param i_var 00368 is the index for the variable corresponding to the result of this operation 00369 (ignored if NumRes(op) == 0). 00370 00371 \param op 00372 The operator code (OpCode) for this operation. 00373 00374 \param ind 00375 is the vector of argument indices for this operation 00376 (must have NumArg(op) elements). 00377 00378 \param nfz 00379 is the number of forward sweep calculated values of type Value 00380 that correspond to this operation 00381 (ignored if NumRes(op) == 0). 00382 00383 \param fz 00384 points to the first forward calculated value 00385 that correspond to this operation 00386 (ignored if NumRes(op) == 0). 00387 00388 \param nrz 00389 is the number of reverse sweep calculated values of type Value 00390 that correspond to this operation 00391 (ignored if NumRes(op) == 0). 00392 00393 \param rz 00394 points to the first reverse calculated value 00395 that correspond to this operation 00396 (ignored if NumRes(op) == 0). 00397 */ 00398 template <class Base, class Value> 00399 void printOp( 00400 std::ostream &os , 00401 const player<Base> *Rec , 00402 size_t i_var , 00403 OpCode op , 00404 const addr_t *ind , 00405 size_t nfz , 00406 const Value *fz , 00407 size_t nrz , 00408 const Value *rz ) 00409 { size_t i; 00410 00411 CPPAD_ASSERT_KNOWN( 00412 ! thread_alloc::in_parallel() , 00413 "cannot print trace of AD operations in parallel mode" 00414 ); 00415 static const char *CompareOpName[] = 00416 { "Lt", "Le", "Eq", "Ge", "Gt", "Ne" }; 00417 static const char *OpName[] = { 00418 "Abs" , 00419 "Acos" , 00420 "Addpv" , 00421 "Addvv" , 00422 "Asin" , 00423 "Atan" , 00424 "Begin" , 00425 "CExp" , 00426 "Com" , 00427 "Cos" , 00428 "Cosh" , 00429 "CSum" , 00430 "Dis" , 00431 "Divpv" , 00432 "Divvp" , 00433 "Divvv" , 00434 "End" , 00435 "Exp" , 00436 "Inv" , 00437 "Ldp" , 00438 "Ldv" , 00439 "Log" , 00440 "Mulpv" , 00441 "Mulvv" , 00442 "Par" , 00443 "Powpv" , 00444 "Powvp" , 00445 "Powvv" , 00446 "Pri" , 00447 "Sign" , 00448 "Sin" , 00449 "Sinh" , 00450 "Sqrt" , 00451 "Stpp" , 00452 "Stpv" , 00453 "Stvp" , 00454 "Stvv" , 00455 "Subpv" , 00456 "Subvp" , 00457 "Subvv" , 00458 "Tan" , 00459 "Tanh" , 00460 "User" , 00461 "Usrap" , 00462 "Usrav" , 00463 "Usrrp" , 00464 "Usrrv" 00465 }; 00466 CPPAD_ASSERT_UNKNOWN( 00467 size_t(NumberOp) == sizeof(OpName) / sizeof(OpName[0]) 00468 ); 00469 00470 // print operator 00471 printOpField(os, "i=", i_var, 5); 00472 if( op == CExpOp ) 00473 { printOpField(os, "op=", OpName[op], 4); 00474 printOpField(os, "", CompareOpName[ ind[0] ], 3); 00475 } 00476 else if( op == ComOp ) 00477 { printOpField(os, "op=", OpName[op], 3); 00478 printOpField(os, "", CompareOpName[ ind[0] ], 4); 00479 } 00480 else printOpField(os, "op=", OpName[op], 7); 00481 00482 // print other fields 00483 size_t ncol = 5; 00484 switch( op ) 00485 { 00486 case CSumOp: 00487 /* 00488 ind[0] = number of addition variables in summation 00489 ind[1] = number of subtraction variables in summation 00490 ind[2] = index of parameter that initializes summation 00491 ind[3], ... , ind[2+ind[0]] = index for positive variables 00492 ind[3+ind[0]], ..., ind[2+ind[0]+ind[1]] = negative variables 00493 ind[3+ind[0]+ind[1]] = ind[0] + ind[1] 00494 */ 00495 CPPAD_ASSERT_UNKNOWN( ind[3+ind[0]+ind[1]] == ind[0]+ind[1] ); 00496 printOpField(os, " pr=", Rec->GetPar(ind[2]), ncol); 00497 for(i = 0; i < ind[0]; i++) 00498 printOpField(os, " +v=", ind[3+i], ncol); 00499 for(i = 0; i < ind[1]; i++) 00500 printOpField(os, " -v=", ind[3+ind[0]+i], ncol); 00501 break; 00502 00503 case LdpOp: 00504 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); 00505 printOpField(os, "off=", ind[0], ncol); 00506 printOpField(os, "idx=", ind[1], ncol); 00507 break; 00508 00509 case LdvOp: 00510 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); 00511 printOpField(os, "off=", ind[0], ncol); 00512 printOpField(os, " v=", ind[1], ncol); 00513 break; 00514 00515 case StppOp: 00516 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); 00517 printOpField(os, "off=", ind[0], ncol); 00518 printOpField(os, "idx=", ind[1], ncol); 00519 printOpField(os, " pr=", Rec->GetPar(ind[2]), ncol); 00520 break; 00521 00522 case StpvOp: 00523 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); 00524 printOpField(os, "off=", ind[0], ncol); 00525 printOpField(os, "idx=", ind[1], ncol); 00526 printOpField(os, " vr=", ind[2], ncol); 00527 break; 00528 00529 case StvpOp: 00530 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); 00531 printOpField(os, "off=", ind[0], ncol); 00532 printOpField(os, " vl=", ind[1], ncol); 00533 printOpField(os, " pr=", Rec->GetPar(ind[2]), ncol); 00534 break; 00535 00536 case StvvOp: 00537 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 3 ); 00538 printOpField(os, "off=", ind[0], ncol); 00539 printOpField(os, " vl=", ind[1], ncol); 00540 printOpField(os, " vr=", ind[2], ncol); 00541 break; 00542 00543 case AddvvOp: 00544 case DivvvOp: 00545 case MulvvOp: 00546 case PowvvOp: 00547 case SubvvOp: 00548 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); 00549 printOpField(os, " vl=", ind[0], ncol); 00550 printOpField(os, " vr=", ind[1], ncol); 00551 break; 00552 00553 case AddpvOp: 00554 case SubpvOp: 00555 case MulpvOp: 00556 case PowpvOp: 00557 case DivpvOp: 00558 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); 00559 printOpField(os, " pl=", Rec->GetPar(ind[0]), ncol); 00560 printOpField(os, " vr=", ind[1], ncol); 00561 break; 00562 00563 case DivvpOp: 00564 case PowvpOp: 00565 case SubvpOp: 00566 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); 00567 printOpField(os, " vl=", ind[0], ncol); 00568 printOpField(os, " pr=", Rec->GetPar(ind[1]), ncol); 00569 break; 00570 00571 case AbsOp: 00572 case AcosOp: 00573 case AsinOp: 00574 case AtanOp: 00575 case CosOp: 00576 case CoshOp: 00577 case ExpOp: 00578 case LogOp: 00579 case SignOp: 00580 case SinOp: 00581 case SinhOp: 00582 case SqrtOp: 00583 case UsravOp: 00584 case TanOp: 00585 case TanhOp: 00586 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 ); 00587 printOpField(os, " v=", ind[0], ncol); 00588 break; 00589 00590 case ParOp: 00591 case UsrapOp: 00592 case UsrrpOp: 00593 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 ); 00594 printOpField(os, " p=", Rec->GetPar(ind[0]), ncol); 00595 break; 00596 00597 case UserOp: 00598 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 4 ); 00599 { const char* name = user_atomic<Base>::name(ind[0]); 00600 printOpField(os, " f=", name, ncol); 00601 printOpField(os, " i=", ind[1], ncol); 00602 printOpField(os, " n=", ind[2], ncol); 00603 printOpField(os, " m=", ind[3], ncol); 00604 } 00605 break; 00606 00607 case PriOp: 00608 CPPAD_ASSERT_NARG_NRES(op, 5, 0); 00609 if( ind[0] & 1 ) 00610 printOpField(os, " v=", ind[1], ncol); 00611 else printOpField(os, " p=", Rec->GetPar(ind[1]), ncol); 00612 os << "before=\"" << Rec->GetTxt(ind[2]) << "\""; 00613 if( ind[0] & 2 ) 00614 printOpField(os, " v=", ind[3], ncol); 00615 else printOpField(os, " p=", Rec->GetPar(ind[3]), ncol); 00616 os << "after=\"" << Rec->GetTxt(ind[4]) << "\""; 00617 break; 00618 00619 case BeginOp: 00620 case EndOp: 00621 case InvOp: 00622 case UsrrvOp: 00623 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 0 ); 00624 break; 00625 00626 case DisOp: 00627 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 2 ); 00628 { const char* name = discrete<Base>::name(ind[0]); 00629 printOpField(os, " f=", name, ncol); 00630 printOpField(os, " x=", ind[1], ncol); 00631 } 00632 break; 00633 00634 00635 case CExpOp: 00636 CPPAD_ASSERT_UNKNOWN(ind[1] != 0); 00637 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 6 ); 00638 if( ind[1] & 1 ) 00639 printOpField(os, " vl=", ind[2], ncol); 00640 else printOpField(os, " pl=", Rec->GetPar(ind[2]), ncol); 00641 if( ind[1] & 2 ) 00642 printOpField(os, " vr=", ind[3], ncol); 00643 else printOpField(os, " pr=", Rec->GetPar(ind[3]), ncol); 00644 if( ind[1] & 4 ) 00645 printOpField(os, " vt=", ind[4], ncol); 00646 else printOpField(os, " pt=", Rec->GetPar(ind[4]), ncol); 00647 if( ind[1] & 8 ) 00648 printOpField(os, " vf=", ind[5], ncol); 00649 else printOpField(os, " pf=", Rec->GetPar(ind[5]), ncol); 00650 break; 00651 00652 case ComOp: 00653 CPPAD_ASSERT_UNKNOWN(ind[1] != 0); 00654 CPPAD_ASSERT_UNKNOWN( NumArg(op) == 4 ); 00655 if( ind[1] & 1 ) 00656 printOpField(os, "res=", 1, ncol); 00657 else printOpField(os, "res=", 0, ncol); 00658 if( ind[1] & 2 ) 00659 printOpField(os, " vl=", ind[2], ncol); 00660 else printOpField(os, " pl=", Rec->GetPar(ind[2]), ncol); 00661 if( ind[1] & 4 ) 00662 printOpField(os, " vr=", ind[3], ncol); 00663 else printOpField(os, " pr=", Rec->GetPar(ind[3]), ncol); 00664 break; 00665 00666 default: 00667 CPPAD_ASSERT_UNKNOWN(0); 00668 } 00669 size_t k; 00670 if( NumRes(op) > 0 && (op != BeginOp) ) 00671 { 00672 for(k = 0; k < nfz; k++) 00673 std::cout << "| fz[" << k << "]=" << fz[k]; 00674 for(k = 0; k < nrz; k++) 00675 std::cout << "| rz[" << k << "]=" << rz[k]; 00676 } 00677 std::cout << std::endl; 00678 } 00679 00680 /*! \} */ 00681 CPPAD_END_NAMESPACE 00682 # endif