868 lines
23 KiB
C++
868 lines
23 KiB
C++
# ifndef CPPAD_CORE_ABS_NORMAL_FUN_HPP
|
|
# define CPPAD_CORE_ABS_NORMAL_FUN_HPP
|
|
/* --------------------------------------------------------------------------
|
|
CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-17 Bradley M. Bell
|
|
|
|
CppAD is distributed under multiple licenses. This distribution is under
|
|
the terms of the
|
|
Eclipse Public License Version 1.0.
|
|
|
|
A copy of this license is included in the COPYING file of this distribution.
|
|
Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
|
|
-------------------------------------------------------------------------- */
|
|
/*
|
|
$begin abs_normal_fun$$
|
|
$spell
|
|
const
|
|
$$
|
|
|
|
|
|
$section Create An Abs-normal Representation of a Function$$
|
|
|
|
$head Syntax$$
|
|
$icode%f%.abs_normal_fun(%g%, %a%)%$$
|
|
|
|
$head f$$
|
|
The object $icode f$$ has prototype
|
|
$codei%
|
|
ADFun<%Base%>& %f%
|
|
%$$
|
|
It represents a function $latex f : \B{R}^n \rightarrow \B{R}^m$$.
|
|
We assume that the only non-smooth terms in the representation are
|
|
absolute value functions and use $latex s \in \B{Z}_+$$
|
|
to represent the number of these terms.
|
|
It is effectively $code const$$, except that some internal state
|
|
that is not relevant to the user; see
|
|
$cref/const ADFun/wish_list/const ADFun/$$.
|
|
|
|
$subhead n$$
|
|
We use $icode n$$ to denote the dimension of the domain space for $icode f$$.
|
|
|
|
$subhead m$$
|
|
We use $icode m$$ to denote the dimension of the range space for $icode f$$.
|
|
|
|
$subhead s$$
|
|
We use $icode s$$ to denote the number of absolute value terms in $icode f$$.
|
|
|
|
|
|
$head a$$
|
|
The object $icode a$$ has prototype
|
|
$codei%
|
|
ADFun<%Base%> %a%
|
|
%$$
|
|
The initial function representation in $icode a$$ is lost.
|
|
Upon return it represents the result of the absolute terms
|
|
$latex a : \B{R}^n \rightarrow \B{R}^s$$; see $latex a(x)$$ defined below.
|
|
Note that $icode a$$ is constructed by copying $icode f$$
|
|
and then changing the dependent variables. There may
|
|
be many calculations in this representation that are not necessary
|
|
and can be removed using
|
|
$codei%
|
|
%a%.optimize()
|
|
%$$
|
|
This optimization is not done automatically by $code abs_normal_fun$$
|
|
because it may take a significant amount of time.
|
|
|
|
$subhead zeta$$
|
|
Let $latex \zeta_0 ( x )$$
|
|
denote the argument for the first absolute value term in $latex f(x)$$,
|
|
$latex \zeta_1 ( x , |\zeta_0 (x)| )$$ for the second term, and so on.
|
|
|
|
$subhead a(x)$$
|
|
For $latex i = 0 , \ldots , {s-1}$$ define
|
|
$latex \[
|
|
a_i (x)
|
|
=
|
|
| \zeta_i ( x , a_0 (x) , \ldots , a_{i-1} (x ) ) |
|
|
\] $$
|
|
This defines $latex a : \B{R}^n \rightarrow \B{R}^s$$.
|
|
|
|
$head g$$
|
|
The object $icode g$$ has prototype
|
|
$codei%
|
|
ADFun<%Base%> %g%
|
|
%$$
|
|
The initial function representation in $icode g$$ is lost.
|
|
Upon return it represents the smooth function
|
|
$latex g : \B{R}^{n + s} \rightarrow \B{R}^{m + s}$$ is defined by
|
|
$latex \[
|
|
g( x , u )
|
|
=
|
|
\left[ \begin{array}{c} y(x, u) \\ z(x, u) \end{array} \right]
|
|
\] $$
|
|
were $latex y(x, u)$$ and $latex z(x, u)$$ are defined below.
|
|
|
|
$subhead z(x, u)$$
|
|
Define the smooth function
|
|
$latex z : \B{R}^{n + s} \rightarrow \B{R}^s$$ by
|
|
$latex \[
|
|
z_i ( x , u ) = \zeta_i ( x , u_0 , \ldots , u_{i-1} )
|
|
\] $$
|
|
Note that the partial of $latex z_i$$ with respect to $latex u_j$$ is zero
|
|
for $latex j \geq i$$.
|
|
|
|
$subhead y(x, u)$$
|
|
There is a smooth function
|
|
$latex y : \B{R}^{n + s} \rightarrow \B{R}^m$$
|
|
such that $latex y( x , u ) = f(x)$$ whenever $latex u = a(x)$$.
|
|
|
|
$head Affine Approximation$$
|
|
We define the affine approximations
|
|
$latex \[
|
|
\begin{array}{rcl}
|
|
y[ \hat{x} ]( x , u )
|
|
& = &
|
|
y ( \hat{x}, a( \hat{x} ) )
|
|
+ \partial_x y ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} )
|
|
+ \partial_u y ( \hat{x}, a( \hat{x} ) ) ( u - a( \hat{x} ) )
|
|
\\
|
|
z[ \hat{x} ]( x , u )
|
|
& = &
|
|
z ( \hat{x}, a( \hat{x} ) )
|
|
+ \partial_x z ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} )
|
|
+ \partial_u z ( \hat{x}, a( \hat{x} ) ) ( u - a( \hat{x} ) )
|
|
\end{array}
|
|
\] $$
|
|
It follows that
|
|
$latex \[
|
|
\begin{array}{rcl}
|
|
y( x , u )
|
|
& = &
|
|
y[ \hat{x} ]( x , u ) + o ( x - \hat{x}, u - a( \hat{x} ) )
|
|
\\
|
|
z( x , u )
|
|
& = &
|
|
z[ \hat{x} ]( x , u ) + o ( x - \hat{x}, u - a( \hat{x} ) )
|
|
\end{array}
|
|
\] $$
|
|
|
|
$head Abs-normal Approximation$$
|
|
|
|
$subhead Approximating a(x)$$
|
|
The function $latex a(x)$$ is not smooth, but it is equal to
|
|
$latex | z(x, u) |$$ when $latex u = a(x)$$.
|
|
Furthermore
|
|
$latex \[
|
|
z[ \hat{x} ]( x , u )
|
|
=
|
|
z ( \hat{x}, a( \hat{x} ) )
|
|
+ \partial_x z ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} )
|
|
+ \partial_u z ( \hat{x}, a( \hat{x} ) ) ( u - a( \hat{x} ) )
|
|
\] $$
|
|
The partial of $latex z_i$$ with respect to $latex u_j$$ is zero
|
|
for $latex j \geq i$$. It follows that
|
|
$latex \[
|
|
z_i [ \hat{x} ]( x , u )
|
|
=
|
|
z_i ( \hat{x}, a( \hat{x} ) )
|
|
+ \partial_x z_i ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} )
|
|
+ \sum_{j < i} \partial_{u(j)}
|
|
z_i ( \hat{x}, a( \hat{x} ) ) ( u_j - a_j ( \hat{x} ) )
|
|
\] $$
|
|
Considering the case $latex i = 0$$ we define
|
|
$latex \[
|
|
a_0 [ \hat{x} ]( x )
|
|
=
|
|
| z_0 [ \hat{x} ]( x , u ) |
|
|
=
|
|
\left|
|
|
z_0 ( \hat{x}, a( \hat{x} ) )
|
|
+ \partial_x z_0 ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} )
|
|
\right|
|
|
\] $$
|
|
It follows that
|
|
$latex \[
|
|
a_0 (x) = a_0 [ \hat{x} ]( x ) + o ( x - \hat{x} )
|
|
\] $$
|
|
In general, we define $latex a_i [ \hat{x} ]$$ using
|
|
$latex a_j [ \hat{x} ]$$ for $latex j < i$$ as follows:
|
|
$latex \[
|
|
a_i [ \hat{x} ]( x )
|
|
=
|
|
\left |
|
|
z_i ( \hat{x}, a( \hat{x} ) )
|
|
+ \partial_x z_i ( \hat{x}, a( \hat{x} ) ) ( x - \hat{x} )
|
|
+ \sum_{j < i} \partial_{u(j)}
|
|
z_i ( \hat{x}, a( \hat{x} ) )
|
|
( a_j [ \hat{x} ] ( x ) - a_j ( \hat{x} ) )
|
|
\right|
|
|
\] $$
|
|
It follows that
|
|
$latex \[
|
|
a (x) = a[ \hat{x} ]( x ) + o ( x - \hat{x} )
|
|
\] $$
|
|
Note that in the case where $latex z(x, u)$$ and $latex y(x, u)$$ are
|
|
affine,
|
|
$latex \[
|
|
a[ \hat{x} ]( x ) = a( x )
|
|
\] $$
|
|
|
|
|
|
$subhead Approximating f(x)$$
|
|
$latex \[
|
|
f(x)
|
|
=
|
|
y ( x , a(x ) )
|
|
=
|
|
y [ \hat{x} ] ( x , a[ \hat{x} ] ( x ) )
|
|
+ o( \Delta x )
|
|
\] $$
|
|
|
|
$head Correspondence to Literature$$
|
|
Using the notation
|
|
$latex Z = \partial_x z(\hat{x}, \hat{u})$$,
|
|
$latex L = \partial_u z(\hat{x}, \hat{u})$$,
|
|
$latex J = \partial_x y(\hat{x}, \hat{u})$$,
|
|
$latex Y = \partial_u y(\hat{x}, \hat{u})$$,
|
|
the approximation for $latex z$$ and $latex y$$ are
|
|
$latex \[
|
|
\begin{array}{rcl}
|
|
z[ \hat{x} ]( x , u )
|
|
& = &
|
|
z ( \hat{x}, a( \hat{x} ) ) + Z ( x - \hat{x} ) + L ( u - a( \hat{x} ) )
|
|
\\
|
|
y[ \hat{x} ]( x , u )
|
|
& = &
|
|
y ( \hat{x}, a( \hat{x} ) ) + J ( x - \hat{x} ) + Y ( u - a( \hat{x} ) )
|
|
\end{array}
|
|
\] $$
|
|
Moving the terms with $latex \hat{x}$$ together, we have
|
|
$latex \[
|
|
\begin{array}{rcl}
|
|
z[ \hat{x} ]( x , u )
|
|
& = &
|
|
z ( \hat{x}, a( \hat{x} ) ) - Z \hat{x} - L a( \hat{x} ) + Z x + L u
|
|
\\
|
|
y[ \hat{x} ]( x , u )
|
|
& = &
|
|
y ( \hat{x}, a( \hat{x} ) ) - J \hat{x} - Y a( \hat{x} ) + J x + Y u
|
|
\end{array}
|
|
\] $$
|
|
Using the notation
|
|
$latex c = z ( \hat{x}, \hat{u} ) - Z \hat{x} - L \hat{u}$$,
|
|
$latex b = y ( \hat{x}, \hat{u} ) - J \hat{x} - Y \hat{u}$$,
|
|
we have
|
|
$latex \[
|
|
\begin{array}{rcl}
|
|
z[ \hat{x} ]( x , u ) & = & c + Z x + L u
|
|
\\
|
|
y[ \hat{x} ]( x , u ) & = & b + J x + Y u
|
|
\end{array}
|
|
\] $$
|
|
Considering the affine case, where the approximations are exact,
|
|
and choosing $latex u = a(x) = |z(x, u)|$$, we obtain
|
|
$latex \[
|
|
\begin{array}{rcl}
|
|
z( x , a(x ) ) & = & c + Z x + L |z( x , a(x ) )|
|
|
\\
|
|
y( x , a(x ) ) & = & b + J x + Y |z( x , a(x ) )|
|
|
\end{array}
|
|
\] $$
|
|
This is Equation (2) of the
|
|
$cref/reference/abs_normal/Reference/$$.
|
|
|
|
$children%example/abs_normal/get_started.cpp
|
|
%$$
|
|
$head Example$$
|
|
The file $cref abs_get_started.cpp$$ contains
|
|
an example and test using this operation.
|
|
|
|
$end
|
|
-------------------------------------------------------------------------------
|
|
*/
|
|
/*!
|
|
file abs_normal_fun.hpp
|
|
Create an abs-normal representation of a function
|
|
*/
|
|
|
|
namespace CppAD { // BEGIN_CPPAD_NAMESPACE
|
|
/*!
|
|
Create an abs-normal representation of an ADFun object.
|
|
|
|
\tparam Base
|
|
base type for this abs-normal form and for the function beging represented;
|
|
i.e., f.
|
|
|
|
\param f
|
|
is the function that this object will represent in abs-normal form.
|
|
This is effectively const except that the play back state play_
|
|
is used.
|
|
*/
|
|
|
|
# define NOT_YET_COMPILING 0
|
|
|
|
template <class Base>
|
|
void ADFun<Base>::abs_normal_fun(ADFun<Base>& g, ADFun<Base>& a)
|
|
{ using namespace local;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Forward sweep to determine number of absolute value operations in f
|
|
// -----------------------------------------------------------------------
|
|
// The argument and result index in f for each absolute value operator
|
|
CppAD::vector<addr_t> f_abs_arg;
|
|
CppAD::vector<size_t> f_abs_res;
|
|
//
|
|
OpCode op; // this operator
|
|
const addr_t* arg = CPPAD_NULL; // arguments for this operator
|
|
size_t i_op; // index of this operator
|
|
size_t i_var; // variable index for this operator
|
|
play_.forward_start(op, arg, i_op, i_var);
|
|
CPPAD_ASSERT_UNKNOWN( op == BeginOp );
|
|
//
|
|
bool more_operators = true;
|
|
while( more_operators )
|
|
{
|
|
// next op
|
|
play_.forward_next(op, arg, i_op, i_var);
|
|
switch( op )
|
|
{ // absolute value operator
|
|
case AbsOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
|
|
f_abs_arg.push_back( arg[0] );
|
|
f_abs_res.push_back( i_var );
|
|
break;
|
|
|
|
case CSumOp:
|
|
// CSumOp has a variable number of arguments
|
|
play_.forward_csum(op, arg, i_op, i_var);
|
|
break;
|
|
|
|
case CSkipOp:
|
|
// CSkip has a variable number of arguments
|
|
play_.forward_cskip(op, arg, i_op, i_var);
|
|
break;
|
|
|
|
case EndOp:
|
|
more_operators = false;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
// ------------------------------------------------------------------------
|
|
// Forward sweep to create new recording
|
|
// ------------------------------------------------------------------------
|
|
// recorder for new operation sequence
|
|
recorder<Base> rec;
|
|
//
|
|
// number of variables in both operation sequences
|
|
// (the AbsOp operators are replace by InvOp operators)
|
|
const size_t num_var = play_.num_var_rec();
|
|
//
|
|
// mapping from old variable index to new variable index
|
|
CPPAD_ASSERT_UNKNOWN(
|
|
std::numeric_limits<addr_t>::max() >= num_var
|
|
);
|
|
CppAD::vector<addr_t> f2g_var(num_var);
|
|
for(i_var = 0; i_var < num_var; i_var++)
|
|
f2g_var[i_var] = addr_t( num_var ); // invalid (should not be used)
|
|
//
|
|
// record the independent variables in f
|
|
play_.forward_start(op, arg, i_op, i_var);
|
|
CPPAD_ASSERT_UNKNOWN( op == BeginOp );
|
|
more_operators = true;
|
|
while( more_operators )
|
|
{ switch( op )
|
|
{
|
|
// phantom variable
|
|
case BeginOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
|
|
CPPAD_ASSERT_UNKNOWN( arg[0] == 0 );
|
|
rec.PutArg(0);
|
|
f2g_var[i_var] = rec.PutOp(op);
|
|
break;
|
|
|
|
// independent variables
|
|
case InvOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 0, 1);
|
|
f2g_var[i_var] = rec.PutOp(op);
|
|
break;
|
|
|
|
// end of independent variables
|
|
default:
|
|
more_operators = false;
|
|
break;
|
|
}
|
|
if( more_operators )
|
|
play_.forward_next(op, arg, i_op, i_var);
|
|
}
|
|
// add one for the phantom variable
|
|
CPPAD_ASSERT_UNKNOWN( 1 + Domain() == i_var );
|
|
//
|
|
// record the independent variables corresponding AbsOp results
|
|
size_t index_abs;
|
|
for(index_abs = 0; index_abs < f_abs_res.size(); index_abs++)
|
|
f2g_var[ f_abs_res[index_abs] ] = rec.PutOp(InvOp);
|
|
//
|
|
// used to hold new argument vector
|
|
addr_t new_arg[6];
|
|
//
|
|
// Parameters in recording of f
|
|
const Base* f_parameter = play_.GetPar();
|
|
//
|
|
// now loop through the rest of the
|
|
more_operators = true;
|
|
index_abs = 0;
|
|
while( more_operators )
|
|
{ addr_t mask; // temporary used in some switch cases
|
|
switch( op )
|
|
{
|
|
// check setting of f_abs_arg and f_abs_res;
|
|
case AbsOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
|
|
CPPAD_ASSERT_UNKNOWN( f_abs_arg[index_abs] == arg[0] );
|
|
CPPAD_ASSERT_UNKNOWN( f_abs_res[index_abs] == i_var );
|
|
CPPAD_ASSERT_UNKNOWN( f2g_var[i_var] > 0 );
|
|
++index_abs;
|
|
break;
|
|
|
|
// These operators come at beginning of take and are handled above
|
|
case InvOp:
|
|
CPPAD_ASSERT_UNKNOWN(false);
|
|
break;
|
|
|
|
// ---------------------------------------------------------------
|
|
// Unary operators, argument a parameter, one result
|
|
case ParOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
|
|
new_arg[0] = rec.PutPar( f_parameter[ arg[0] ] );
|
|
rec.PutArg( new_arg[0] );
|
|
f2g_var[i_var] = rec.PutOp(op);
|
|
break;
|
|
|
|
// --------------------------------------------------------------
|
|
// Unary operators, argument a variable, one result
|
|
// (excluding the absolute value operator AbsOp)
|
|
case AcosOp:
|
|
case AcoshOp:
|
|
case AsinOp:
|
|
case AsinhOp:
|
|
case AtanOp:
|
|
case AtanhOp:
|
|
case CosOp:
|
|
case CoshOp:
|
|
case ExpOp:
|
|
case Expm1Op:
|
|
case LogOp:
|
|
case Log1pOp:
|
|
case SignOp:
|
|
case SinOp:
|
|
case SinhOp:
|
|
case SqrtOp:
|
|
case TanOp:
|
|
case TanhOp:
|
|
// some of these operators have an auxillary result; e.g.,
|
|
// sine and cosine are computed togeather.
|
|
CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 );
|
|
CPPAD_ASSERT_UNKNOWN( NumRes(op) == 1 || NumRes(op) == 2 );
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[0] ] ) < num_var );
|
|
new_arg[0] = f2g_var[ arg[0] ];
|
|
rec.PutArg( new_arg[0] );
|
|
f2g_var[i_var] = rec.PutOp( op );
|
|
break;
|
|
|
|
case ErfOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 3, 5);
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[0] ] ) < num_var );
|
|
// Error function is a special case
|
|
// second argument is always the parameter 0
|
|
// third argument is always the parameter 2 / sqrt(pi)
|
|
rec.PutArg( rec.PutPar( Base(0.0) ) );
|
|
rec.PutArg( rec.PutPar(
|
|
Base( 1.0 / std::sqrt( std::atan(1.0) ) )
|
|
) );
|
|
f2g_var[i_var] = rec.PutOp(op);
|
|
break;
|
|
// --------------------------------------------------------------
|
|
// Binary operators, left variable, right parameter, one result
|
|
case SubvpOp:
|
|
case DivvpOp:
|
|
case PowvpOp:
|
|
case ZmulvpOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[0] ] ) < num_var );
|
|
new_arg[0] = f2g_var[ arg[0] ];
|
|
new_arg[1] = rec.PutPar( f_parameter[ arg[1] ] );
|
|
rec.PutArg( new_arg[0], new_arg[1] );
|
|
f2g_var[i_var] = rec.PutOp(op);
|
|
break;
|
|
// ---------------------------------------------------
|
|
// Binary operators, left index, right variable, one result
|
|
case DisOp:
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var );
|
|
new_arg[0] = arg[0];
|
|
new_arg[1] = f2g_var[ arg[1] ];
|
|
rec.PutArg( new_arg[0], new_arg[1] );
|
|
f2g_var[i_var] = rec.PutOp(op);
|
|
break;
|
|
|
|
// --------------------------------------------------------------
|
|
// Binary operators, left parameter, right variable, one result
|
|
case AddpvOp:
|
|
case SubpvOp:
|
|
case MulpvOp:
|
|
case DivpvOp:
|
|
case PowpvOp:
|
|
case ZmulpvOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var );
|
|
new_arg[0] = rec.PutPar( f_parameter[ arg[0] ] );
|
|
new_arg[1] = f2g_var[ arg[1] ];
|
|
rec.PutArg( new_arg[0], new_arg[1] );
|
|
f2g_var[i_var] = rec.PutOp(op);
|
|
break;
|
|
// --------------------------------------------------------------
|
|
// Binary operators, left and right variables, one result
|
|
case AddvvOp:
|
|
case SubvvOp:
|
|
case MulvvOp:
|
|
case DivvvOp:
|
|
case ZmulvvOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[0] ] ) < num_var );
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var );
|
|
new_arg[0] = f2g_var[ arg[0] ];
|
|
new_arg[1] = f2g_var[ arg[1] ];
|
|
rec.PutArg( new_arg[0], new_arg[1] );
|
|
f2g_var[i_var] = rec.PutOp(op);
|
|
break;
|
|
// ---------------------------------------------------
|
|
// Conditional expression operators
|
|
case CExpOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 6, 1);
|
|
new_arg[0] = arg[0];
|
|
new_arg[1] = arg[1];
|
|
mask = 1;
|
|
for(size_t i = 2; i < 6; i++)
|
|
{ if( arg[1] & mask )
|
|
{ CPPAD_ASSERT_UNKNOWN( size_t(f2g_var[arg[i]]) < num_var );
|
|
new_arg[i] = f2g_var[ arg[i] ];
|
|
}
|
|
else
|
|
new_arg[i] = rec.PutPar( f_parameter[ arg[i] ] );
|
|
mask = mask << 1;
|
|
}
|
|
rec.PutArg(
|
|
new_arg[0] ,
|
|
new_arg[1] ,
|
|
new_arg[2] ,
|
|
new_arg[3] ,
|
|
new_arg[4] ,
|
|
new_arg[5]
|
|
);
|
|
f2g_var[i_var] = rec.PutOp(op);
|
|
break;
|
|
|
|
// --------------------------------------------------
|
|
// Operators with no arguments and no results
|
|
case EndOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 0, 0);
|
|
rec.PutOp(op);
|
|
more_operators = false;
|
|
break;
|
|
|
|
// ---------------------------------------------------
|
|
// Operations with two arguments and no results
|
|
case LepvOp:
|
|
case LtpvOp:
|
|
case EqpvOp:
|
|
case NepvOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 2, 0);
|
|
new_arg[0] = rec.PutPar( f_parameter[ arg[0] ] );
|
|
new_arg[1] = f2g_var[ arg[1] ];
|
|
rec.PutArg(new_arg[0], new_arg[1]);
|
|
rec.PutOp(op);
|
|
break;
|
|
//
|
|
case LevpOp:
|
|
case LtvpOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 2, 0);
|
|
new_arg[0] = f2g_var[ arg[0] ];
|
|
new_arg[1] = rec.PutPar( f_parameter[ arg[1] ] );
|
|
rec.PutArg(new_arg[0], new_arg[1]);
|
|
rec.PutOp(op);
|
|
break;
|
|
//
|
|
case LevvOp:
|
|
case LtvvOp:
|
|
case EqvvOp:
|
|
case NevvOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 2, 0);
|
|
new_arg[0] = f2g_var[ arg[0] ];
|
|
new_arg[1] = f2g_var[ arg[1] ];
|
|
rec.PutArg(new_arg[0], new_arg[1]);
|
|
rec.PutOp(op);
|
|
break;
|
|
|
|
// ---------------------------------------------------
|
|
// print forward operator
|
|
case PriOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 5, 0);
|
|
//
|
|
// arg[0]
|
|
new_arg[0] = arg[0];
|
|
//
|
|
// arg[1]
|
|
if( arg[0] & 1 )
|
|
{
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var );
|
|
new_arg[1] = f2g_var[ arg[1] ];
|
|
}
|
|
else
|
|
{ new_arg[1] = rec.PutPar( f_parameter[ arg[1] ] );
|
|
}
|
|
//
|
|
// arg[3]
|
|
if( arg[0] & 2 )
|
|
{
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[3] ] ) < num_var );
|
|
new_arg[3] = f2g_var[ arg[3] ];
|
|
}
|
|
else
|
|
{ new_arg[3] = rec.PutPar( f_parameter[ arg[3] ] );
|
|
}
|
|
new_arg[2] = rec.PutTxt( play_.GetTxt( arg[2] ) );
|
|
new_arg[4] = rec.PutTxt( play_.GetTxt( arg[4] ) );
|
|
//
|
|
rec.PutArg(
|
|
new_arg[0] ,
|
|
new_arg[1] ,
|
|
new_arg[2] ,
|
|
new_arg[3] ,
|
|
new_arg[4]
|
|
);
|
|
// no result
|
|
rec.PutOp(op);
|
|
break;
|
|
|
|
// ---------------------------------------------------
|
|
// VecAD operators
|
|
|
|
// Load using a parameter index
|
|
case LdpOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 3, 1);
|
|
new_arg[0] = arg[0];
|
|
new_arg[1] = arg[1];
|
|
new_arg[2] = arg[2];
|
|
rec.PutArg(
|
|
new_arg[0],
|
|
new_arg[1],
|
|
new_arg[2]
|
|
);
|
|
f2g_var[i_var] = rec.PutLoadOp(op);
|
|
break;
|
|
|
|
// Load using a variable index
|
|
case LdvOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 3, 1);
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var );
|
|
new_arg[0] = arg[0];
|
|
new_arg[1] = f2g_var[ arg[1] ];
|
|
new_arg[2] = arg[2];
|
|
rec.PutArg(
|
|
new_arg[0],
|
|
new_arg[1],
|
|
new_arg[2]
|
|
);
|
|
f2g_var[i_var] = rec.PutLoadOp(op);
|
|
break;
|
|
|
|
// Store a parameter using a parameter index
|
|
case StppOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 3, 0);
|
|
new_arg[0] = arg[0];
|
|
new_arg[1] = rec.PutPar( f_parameter[ arg[1] ] );
|
|
new_arg[2] = rec.PutPar( f_parameter[ arg[2] ] );
|
|
rec.PutArg(
|
|
new_arg[0],
|
|
new_arg[1],
|
|
new_arg[2]
|
|
);
|
|
rec.PutOp(op);
|
|
break;
|
|
|
|
// Store a parameter using a variable index
|
|
case StvpOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 3, 0);
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var );
|
|
new_arg[0] = arg[0];
|
|
new_arg[1] = f2g_var[ arg[1] ];
|
|
new_arg[2] = rec.PutPar( f_parameter[ arg[2] ] );
|
|
rec.PutArg(
|
|
new_arg[0],
|
|
new_arg[1],
|
|
new_arg[2]
|
|
);
|
|
rec.PutOp(op);
|
|
break;
|
|
|
|
// Store a variable using a parameter index
|
|
case StpvOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 3, 0);
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[2] ] ) < num_var );
|
|
new_arg[0] = arg[0];
|
|
new_arg[1] = rec.PutPar( f_parameter[ arg[1] ] );
|
|
new_arg[2] = f2g_var[ arg[2] ];
|
|
rec.PutArg(
|
|
new_arg[0],
|
|
new_arg[1],
|
|
new_arg[2]
|
|
);
|
|
rec.PutOp(op);
|
|
break;
|
|
|
|
// Store a variable using a variable index
|
|
case StvvOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 3, 0);
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[1] ] ) < num_var );
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[ arg[2] ] ) < num_var );
|
|
new_arg[0] = arg[0];
|
|
new_arg[1] = f2g_var[ arg[1] ];
|
|
new_arg[2] = f2g_var[ arg[2] ];
|
|
rec.PutArg(
|
|
new_arg[0],
|
|
new_arg[1],
|
|
new_arg[2]
|
|
);
|
|
break;
|
|
|
|
// -----------------------------------------------------------
|
|
// user atomic function call operators
|
|
|
|
case UserOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 4, 0);
|
|
// atomic_index, user_old, user_n, user_m
|
|
rec.PutArg(arg[0], arg[1], arg[2], arg[3]);
|
|
rec.PutOp(UserOp);
|
|
break;
|
|
|
|
case UsrapOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 1, 0);
|
|
new_arg[0] = rec.PutPar( f_parameter[ arg[0] ] );
|
|
rec.PutArg(new_arg[0]);
|
|
rec.PutOp(UsrapOp);
|
|
break;
|
|
|
|
case UsravOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 1, 0);
|
|
CPPAD_ASSERT_UNKNOWN( size_t( f2g_var[arg[0]] ) < num_var );
|
|
new_arg[0] = f2g_var[ arg[0] ];
|
|
rec.PutArg(new_arg[0]);
|
|
rec.PutOp(UsravOp);
|
|
break;
|
|
|
|
case UsrrpOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 1, 0);
|
|
new_arg[0] = rec.PutPar( f_parameter[ arg[0] ] );
|
|
rec.PutArg(new_arg[0]);
|
|
rec.PutOp(UsrrpOp);
|
|
break;
|
|
|
|
case UsrrvOp:
|
|
CPPAD_ASSERT_NARG_NRES(op, 0, 1);
|
|
f2g_var[i_var] = rec.PutOp(UsrrvOp);
|
|
break;
|
|
// ---------------------------------------------------
|
|
|
|
// all cases should be handled above
|
|
default:
|
|
CPPAD_ASSERT_UNKNOWN(false);
|
|
}
|
|
if( more_operators )
|
|
play_.forward_next(op, arg, i_op, i_var);
|
|
}
|
|
// Check a few expected results
|
|
CPPAD_ASSERT_UNKNOWN( rec.num_op_rec() == play_.num_op_rec() );
|
|
CPPAD_ASSERT_UNKNOWN( rec.num_var_rec() == play_.num_var_rec() );
|
|
CPPAD_ASSERT_UNKNOWN( rec.num_load_op_rec() == play_.num_load_op_rec() );
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Use rec to create the function g
|
|
// -----------------------------------------------------------------------
|
|
|
|
// number of variables in the recording
|
|
g.num_var_tape_ = rec.num_var_rec();
|
|
|
|
// dimension cskip_op vector to number of operators
|
|
g.cskip_op_.erase();
|
|
g.cskip_op_.extend( rec.num_op_rec() );
|
|
|
|
// independent variables in g: (x, u)
|
|
size_t s = f_abs_res.size();
|
|
size_t n = Domain();
|
|
g.ind_taddr_.resize(n + s);
|
|
// (x, u)
|
|
for(size_t j = 0; j < n; j++)
|
|
{ g.ind_taddr_[j] = f2g_var[ ind_taddr_[j] ];
|
|
CPPAD_ASSERT_UNKNOWN( g.ind_taddr_[j] == j + 1 );
|
|
}
|
|
for(size_t j = 0; j < s; j++)
|
|
{ g.ind_taddr_[n + j] = f2g_var[ f_abs_res[j] ];
|
|
CPPAD_ASSERT_UNKNOWN( g.ind_taddr_[n + j] == n + j + 1 );
|
|
}
|
|
|
|
// dependent variable in g: (y, z)
|
|
CPPAD_ASSERT_UNKNOWN( s == f_abs_arg.size() );
|
|
size_t m = Range();
|
|
g.dep_taddr_.resize(m + s);
|
|
for(size_t i = 0; i < m; i++)
|
|
{ g.dep_taddr_[i] = f2g_var[ dep_taddr_[i] ];
|
|
CPPAD_ASSERT_UNKNOWN( g.dep_taddr_[i] < num_var );
|
|
}
|
|
for(size_t i = 0; i < s; i++)
|
|
{ g.dep_taddr_[m + i] = f2g_var[ f_abs_arg[i] ];
|
|
CPPAD_ASSERT_UNKNOWN( g.dep_taddr_[m + i] < num_var );
|
|
}
|
|
|
|
// which dependent variables are parameters
|
|
g.dep_parameter_.resize(m + s);
|
|
for(size_t i = 0; i < m; i++)
|
|
g.dep_parameter_[i] = dep_parameter_[i];
|
|
for(size_t i = 0; i < s; i++)
|
|
g.dep_parameter_[m + i] = false;
|
|
|
|
// free memory allocated for sparse Jacobian calculation
|
|
// (the resutls are no longer valid)
|
|
g.for_jac_sparse_pack_.resize(0, 0);
|
|
g.for_jac_sparse_set_.resize(0, 0);
|
|
|
|
// free taylor coefficient memory
|
|
g.taylor_.free();
|
|
g.num_order_taylor_ = 0;
|
|
g.cap_order_taylor_ = 0;
|
|
|
|
// Transferring the recording swaps its vectors so do this last
|
|
// replace the recording in g (this ADFun object)
|
|
g.play_.get(rec);
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Create the function a
|
|
// ------------------------------------------------------------------------
|
|
|
|
// start with a copy of f
|
|
a = *this;
|
|
|
|
// dependent variables in a(x)
|
|
CPPAD_ASSERT_UNKNOWN( s == f_abs_arg.size() );
|
|
a.dep_taddr_.resize(s);
|
|
for(size_t i = 0; i < s; i++)
|
|
{ a.dep_taddr_[i] = f_abs_res[i];
|
|
CPPAD_ASSERT_UNKNOWN( a.dep_taddr_[i] < num_var );
|
|
}
|
|
|
|
// free memory allocated for sparse Jacobian calculation
|
|
// (the resutls are no longer valid)
|
|
a.for_jac_sparse_pack_.resize(0, 0);
|
|
a.for_jac_sparse_set_.resize(0, 0);
|
|
|
|
// free taylor coefficient memory
|
|
a.taylor_.free();
|
|
a.num_order_taylor_ = 0;
|
|
a.cap_order_taylor_ = 0;
|
|
}
|
|
|
|
} // END_CPPAD_NAMESPACE
|
|
|
|
# endif
|