Prev
Next
Index->
contents
reference
index
search
external
Up->
CppAD
AD
ADValued
atomic
atomic_four
atomic_four_example
atomic_four_forward.cpp
atomic_four_forward.cpp
Headings->
Purpose
Function
Jacobian
Hessian
Define Atomic Function
Use Atomic Function
@(@\newcommand{\W}[1]{ \; #1 \; }
\newcommand{\R}[1]{ {\rm #1} }
\newcommand{\B}[1]{ {\bf #1} }
\newcommand{\D}[2]{ \frac{\partial #1}{\partial #2} }
\newcommand{\DD}[3]{ \frac{\partial^2 #1}{\partial #2 \partial #3} }
\newcommand{\Dpow}[2]{ \frac{\partial^{#1}}{\partial {#2}^{#1}} }
\newcommand{\dpow}[2]{ \frac{ {\rm d}^{#1}}{{\rm d}\, {#2}^{#1}} }@)@This is cppad-20221105 documentation . Here is a link to its
current documentation
.
Atomic Functions and Forward Mode: Example and Test
Purpose
This example demonstrates forward mode derivative calculation
using an atomic_four
function.
Function
For this example, the atomic function
@(@
g : \B{R}^3 \rightarrow \B{R}^2
@)@ is defined by
@[@
g(x) = \left( \begin{array}{c}
x_2 * x_2 \\
x_0 * x_1
\end{array} \right)
@]@
Jacobian
The corresponding Jacobian is
@[@
g^{(1)} (x) = \left( \begin{array}{ccc}
0 & 0 & 2 x_2 \\
x_1 & x_0 & 0
\end{array} \right)
@]@
Hessian
The Hessians of the component functions are
@[@
g_0^{(2)} ( x ) = \left( \begin{array}{ccc}
0 & 0 & 0 \\
0 & 0 & 0 \\
0 & 0 & 2
\end{array} \right)
\W{,}
g_1^{(2)} ( x ) = \left( \begin{array}{ccc}
0 & 1 & 0 \\
1 & 0 & 0 \\
0 & 0 & 0
\end{array} \right)
@]@
Define Atomic Function
// empty namespace
namespace {
//
class atomic_forward : public CppAD:: atomic_four< double > {
public :
atomic_forward ( const std:: string& name) :
CppAD:: atomic_four< double >( name)
{ }
private :
// for_type
bool for_type (
size_t call_id ,
const CppAD:: vector< CppAD:: ad_type_enum>& type_x ,
CppAD:: vector< CppAD:: ad_type_enum>& type_y ) override
{
bool ok = type_x. size () == 3 ; // n
ok &= type_y. size () == 2 ; // m
if ( ! ok )
return false ;
type_y[ 0 ] = type_x[ 2 ];
type_y[ 1 ] = std:: max ( type_x[ 0 ], type_x[ 1 ]);
return true ;
}
// forward
bool forward (
size_t call_id ,
const CppAD:: vector< bool >& select_y ,
size_t order_low ,
size_t order_up ,
const CppAD:: vector< double >& tx ,
CppAD:: vector< double >& ty ) override
{
size_t q = order_up + 1 ;
# ifndef NDEBUG
size_t n = tx. size () / q;
size_t m = ty. size () / q;
# endif
assert ( n == 3 );
assert ( m == 2 );
assert ( order_low <= order_up );
// this example only implements up to second order forward mode
bool ok = order_up <= 2 ;
if ( ! ok )
return ok;
// --------------------------------------------------------------
// Zero forward mode.
// This case must always be implemented
// g(x) = [ x_2 * x_2 ]
// [ x_0 * x_1 ]
// y^0 = f( x^0 )
if ( order_low <= 0 )
{ // y_0^0 = x_2^0 * x_2^0
ty[ 0 * q + 0 ] = tx[ 2 * q + 0 ] * tx[ 2 * q + 0 ];
// y_1^0 = x_0^0 * x_1^0
ty[ 1 * q + 0 ] = tx[ 0 * q + 0 ] * tx[ 1 * q + 0 ];
}
if ( order_up <= 0 )
return ok;
// --------------------------------------------------------------
// First order forward mode.
// This case is needed if first order forward mode is used.
// g'(x) = [ 0, 0, 2 * x_2 ]
// [ x_1, x_0, 0 ]
// y^1 = f'(x^0) * x^1
if ( order_low <= 1 )
{ // y_0^1 = 2 * x_2^0 * x_2^1
ty[ 0 * q + 1 ] = 2.0 * tx[ 2 * q + 0 ] * tx[ 2 * q + 1 ];
// y_1^1 = x_1^0 * x_0^1 + x_0^0 * x_1^1
ty[ 1 * q + 1 ] = tx[ 1 * q + 0 ] * tx[ 0 * q + 1 ];
ty[ 1 * q + 1 ] += tx[ 0 * q + 0 ] * tx[ 1 * q + 1 ];
}
if ( order_up <= 1 )
return ok;
// --------------------------------------------------------------
// Second order forward mode.
// This case is needed if second order forwrd mode is used.
// g'(x) = [ 0, 0, 2 x_2 ]
// [ x_1, x_0, 0 ]
//
// [ 0 , 0 , 0 ] [ 0 , 1 , 0 ]
// g_0''(x) = [ 0 , 0 , 0 ] g_1^{(2)} (x) = [ 1 , 0 , 0 ]
// [ 0 , 0 , 2 ] [ 0 , 0 , 0 ]
//
// y_0^2 = x^1 * g_0''( x^0 ) x^1 / 2! + g_0'( x^0 ) x^2
// = ( x_2^1 * 2.0 * x_2^1 ) / 2!
// + 2.0 * x_2^0 * x_2^2
ty[ 0 * q + 2 ] = tx[ 2 * q + 1 ] * tx[ 2 * q + 1 ];
ty[ 0 * q + 2 ] += 2.0 * tx[ 2 * q + 0 ] * tx[ 2 * q + 2 ];
//
// y_1^2 = x^1 * g_1''( x^0 ) x^1 / 2! + g_1'( x^0 ) x^2
// = ( x_1^1 * x_0^1 + x_0^1 * x_1^1) / 2
// + x_1^0 * x_0^2 + x_0^0 + x_1^2
ty[ 1 * q + 2 ] = tx[ 1 * q + 1 ] * tx[ 0 * q + 1 ];
ty[ 1 * q + 2 ] += tx[ 1 * q + 0 ] * tx[ 0 * q + 2 ];
ty[ 1 * q + 2 ] += tx[ 0 * q + 0 ] * tx[ 1 * q + 2 ];
// --------------------------------------------------------------
return ok;
}
} ;
}
Use Atomic Function
bool forward ( void )
{ // ok, eps
bool ok = true ;
double eps = 10 . * CppAD:: numeric_limits< double >:: epsilon ();
//
// AD, NearEqual
using CppAD:: AD;
using CppAD:: NearEqual;
//
// afun
atomic_forward afun ( "atomic_forward" );
//
// Create the function f(u) = g(u) for this example.
//
// n, u, au
size_t n = 3 ;
CPPAD_TESTVECTOR ( double ) u ( n);
u[ 0 ] = 1.00 ;
u[ 1 ] = 2.00 ;
u[ 2 ] = 3.00 ;
CPPAD_TESTVECTOR ( AD< double > ) au ( n);
for ( size_t j = 0 ; j < n; ++ j)
au[ j] = u[ j];
CppAD:: Independent ( au);
//
// m, ay
size_t m = 2 ;
CPPAD_TESTVECTOR ( AD< double > ) ay ( m);
CPPAD_TESTVECTOR ( AD< double > ) ax = au;
afun ( ax, ay);
//
// f
CppAD:: ADFun<double> f;
f. Dependent ( au, ay);
//
// check function value
double check = u[ 2 ] * u[ 2 ];
ok &= NearEqual ( Value ( ay[ 0 ]) , check, eps, eps);
check = u[ 0 ] * u[ 1 ];
ok &= NearEqual ( Value ( ay[ 1 ]) , check, eps, eps);
// ----------------------------------------------------------------
// zero order forward
//
// u0, y0
CPPAD_TESTVECTOR ( double ) u0 ( n), y0 ( m);
u0 = u;
y0 = f. Forward ( 0 , u0);
check = u[ 2 ] * u[ 2 ];
ok &= NearEqual ( y0[ 0 ] , check, eps, eps);
check = u[ 0 ] * u[ 1 ];
ok &= NearEqual ( y0[ 1 ] , check, eps, eps);
// ----------------------------------------------------------------
// first order forward
//
// check_jac
double check_jac[] = {
0.0 , 0.0 , 2.0 * u[ 2 ],
u[ 1 ], u[ 0 ], 0.0
} ;
//
// u1
CPPAD_TESTVECTOR ( double ) u1 ( n);
for ( size_t j = 0 ; j < n; j++)
u1[ j] = 0.0 ;
//
// y1, j
CPPAD_TESTVECTOR ( double ) y1 ( m);
for ( size_t j = 0 ; j < n; j++)
{ //
// u1, y1
// compute partial in j-th component direction
u1[ j] = 1.0 ;
y1 = f. Forward ( 1 , u1);
u1[ j] = 0.0 ;
//
// check this partial
for ( size_t i = 0 ; i < m; i++)
ok &= NearEqual ( y1[ i], check_jac[ i * n + j], eps, eps);
}
// ----------------------------------------------------------------
// second order forward
//
// check_hes_0
double check_hes_0[] = {
0.0 , 0.0 , 0.0 ,
0.0 , 0.0 , 0.0 ,
0.0 , 0.0 , 2.0
} ;
//
// check_hes_1
double check_hes_1[] = {
0.0 , 1.0 , 0.0 ,
1.0 , 0.0 , 0.0 ,
0.0 , 0.0 , 0.0
} ;
//
// u2
CPPAD_TESTVECTOR ( double ) u2 ( n);
for ( size_t j = 0 ; j < n; j++)
u2[ j] = 0.0 ;
//
// y2, j
CPPAD_TESTVECTOR ( double ) y2 ( m);
for ( size_t j = 0 ; j < n; j++)
{ //
// u1, y2
// first order forward in j-th direction
u1[ j] = 1.0 ;
f. Forward ( 1 , u1);
y2 = f. Forward ( 2 , u2);
//
// check y2 element of Hessian diagonal
ok &= NearEqual ( y2[ 0 ], check_hes_0[ j * n + j] / 2.0 , eps, eps);
ok &= NearEqual ( y2[ 1 ], check_hes_1[ j * n + j] / 2.0 , eps, eps);
//
// k
for ( size_t k = 0 ; k < n; k++) if ( k != j )
{ //
// u1, y2
u1[ k] = 1.0 ;
f. Forward ( 1 , u1);
y2 = f. Forward ( 2 , u2);
//
// y2 = (H_jj + H_kk + H_jk + H_kj) / 2.0
// y2 = (H_jj + H_kk) / 2.0 + H_jk
//
// check y2[0]
double H_jj = check_hes_0[ j * n + j];
double H_kk = check_hes_0[ k * n + k];
double H_jk = y2[ 0 ] - ( H_kk + H_jj) / 2.0 ;
ok &= NearEqual ( H_jk, check_hes_0[ j * n + k], eps, eps);
//
// check y2[1]
H_jj = check_hes_1[ j * n + j];
H_kk = check_hes_1[ k * n + k];
H_jk = y2[ 1 ] - ( H_kk + H_jj) / 2.0 ;
ok &= NearEqual ( H_jk, check_hes_1[ j * n + k], eps, eps);
//
// u1
u1[ k] = 0.0 ;
}
// u1
u1[ j] = 0.0 ;
}
// ----------------------------------------------------------------
return ok;
}
Input File: example/atomic_four/forward.cpp