|
Prev
| Next
|
|
|
|
|
|
atomic_three_base2ad.cpp |
|
@(@\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
.
base2ad with Atomic Operations: Example and Test
Source Code
# include <cppad/cppad.hpp>
namespace { // isolate items below to this file
//
// abbreviations
using CppAD::AD;
using CppAD::vector;
//
class atomic_base2ad : public CppAD::atomic_three<double> {
//
public:
atomic_base2ad(const std::string& name) :
CppAD::atomic_three<double>(name)
{ }
private:
// ------------------------------------------------------------------------
// type
bool for_type(
const vector<double>& parameter_x ,
const vector<CppAD::ad_type_enum>& type_x ,
vector<CppAD::ad_type_enum>& type_y ) override
{ assert( parameter_x.size() == type_x.size() );
bool ok = type_x.size() == 1; // n
ok &= type_y.size() == 1; // m
if( ! ok )
return false;
type_y[0] = type_x[0];
return true;
}
// ----------------------------------------------------------------------
// forward mode
// ----------------------------------------------------------------------
template <class Scalar>
bool template_forward(
size_t p ,
size_t q ,
const vector<Scalar>& tx ,
vector<Scalar>& ty )
{
# ifndef NDEBUG
size_t n = tx.size() / (q + 1);
size_t m = ty.size() / (q + 1);
# endif
assert( n == 1 );
assert( m == 1 );
// return flag
bool ok = q == 0;
if( ! ok )
return ok;
// Order zero forward mode.
// This case must always be implemented
// y^0 = g( x^0 ) = 1 / x^0
Scalar g = 1. / tx[0];
if( p <= 0 )
ty[0] = g;
return ok;
}
// forward mode routines called by ADFun<Base> objects
bool forward(
const vector<double>& parameter_x ,
const vector<CppAD::ad_type_enum>& type_x ,
size_t need_y ,
size_t p ,
size_t q ,
const vector<double>& tx ,
vector<double>& ty ) override
{ return template_forward(p, q, tx, ty);
}
// forward mode routines called by ADFun< AD<Base> , Base> objects
bool forward(
const vector< AD<double> >& aparameter_x ,
const vector<CppAD::ad_type_enum>& type_x ,
size_t need_y ,
size_t p ,
size_t q ,
const vector< AD<double> >& atx ,
vector< AD<double> >& aty ) override
{ return template_forward(p, q, atx, aty);
}
// ----------------------------------------------------------------------
// reverse mode
// ----------------------------------------------------------------------
template <class Scalar>
bool template_reverse(
size_t q ,
const vector<Scalar>& tx ,
const vector<Scalar>& ty ,
vector<Scalar>& px ,
const vector<Scalar>& py )
{
# ifndef NDEBUG
size_t n = tx.size() / (q + 1);
size_t m = ty.size() / (q + 1);
# endif
assert( n == 1 );
assert( m == 1 );
// return flag
bool ok = q == 0;
if( ! ok )
return ok;
// Order zero reverse mode.
// y^0 = g( x^0 ) = 1 / x^0
// y^1 = g'( x^0 ) * x^1 = - x^1 / (x^0 * x^0)
px[0] = - py[0] / ( tx[0] * tx[0] );
return ok;
}
// reverse mode routines called by ADFun<Base> objects
bool reverse(
const vector<double>& parameter_x ,
const vector<CppAD::ad_type_enum>& type_x ,
size_t q ,
const vector<double>& tx ,
const vector<double>& ty ,
vector<double>& px ,
const vector<double>& py ) override
{ return template_reverse(q, tx, ty, px, py);
}
// reverse mode routines called by ADFun<Base> objects
bool reverse(
const vector< AD<double> >& aparameter_x ,
const vector<CppAD::ad_type_enum>& type_x ,
size_t q ,
const vector< AD<double> >& atx ,
const vector< AD<double> >& aty ,
vector< AD<double> >& apx ,
const vector< AD<double> >& apy ) override
{ return template_reverse(q, atx, aty, apx, apy);
}
}; // End of atomic_base2ad class
} // End empty namespace
bool base2ad(void)
{ bool ok = true;
using CppAD::NearEqual;
double eps = 10. * CppAD::numeric_limits<double>::epsilon();
//
// Create the atomic base2ad object
atomic_base2ad afun("atomic_base2ad");
//
// Create the function f(x) = 1 / g(x) = x
//
size_t n = 1;
double x0 = 0.5;
vector< AD<double> > ax(n);
ax[0] = x0;
// declare independent variables and start tape recording
CppAD::Independent(ax);
// range space vector
size_t m = 1;
vector< AD<double> > av(m);
// call atomic function and store g(x) in au[0]
vector< AD<double> > au(m);
afun(ax, au); // u = 1 / x
// now use AD division to invert to invert the operation
av[0] = 1.0 / au[0]; // v = 1 / u = x
// create f: x -> y and stop tape recording
CppAD::ADFun<double> f;
f.Dependent (ax, av); // g(x) = x
// check function value
double check = x0;
ok &= NearEqual( Value(av[0]) , check, eps, eps);
// check zero order forward mode
size_t q;
vector<double> x_q(n), v_q(m);
q = 0;
x_q[0] = x0;
v_q = f.Forward(q, x_q);
ok &= NearEqual(v_q[0] , check, eps, eps);
// check first order reverse
vector<double> dw(n), w(m);
w[0] = 1.0;
dw = f.Reverse(q+1, w);
check = 1.0;
ok &= NearEqual(dw[0] , check, eps, eps);
// create ag : x -> y
CppAD::ADFun< AD<double> , double > af ( f.base2ad() );
// check zero order forward mode
vector< AD<double> > ax_q(n), av_q(m);
q = 0;
ax_q[0] = x0;
av_q = af.Forward(q, ax_q);
check = x0;
ok &= NearEqual( Value(av_q[0]) , check, eps, eps);
// check first order reverse
vector< AD<double> > adw(n), aw(m);
aw[0] = 1.0;
adw = af.Reverse(q+1, aw);
check = 1.0;
ok &= NearEqual( Value(adw[0]) , check, eps, eps);
return ok;
}
Input File: example/atomic_three/base2ad.cpp