|
Prev
| Next
|
|
|
|
|
|
atomic_four_lin_ode_reverse.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
.
Atomic Linear ODE Reverse Mode: Example and Test
Purpose
This example demonstrates using reverse mode with
the atomic_four_lin_ode
class.
f(u)
For this example, the function @(@
f(u) = z(r, u)
@)@ where
@(@
z(t, u)
@)@ solves the following ODE
@[@
z_t (t, u) =
\left( \begin{array}{cccc}
0 & 0 & 0 & 0 \\
u_4 & 0 & 0 & 0 \\
0 & u_5 & 0 & 0 \\
0 & 0 & u_6 & 0 \\
\end{array} \right)
z(t, u)
\W{,}
z(0, u) =
\left( \begin{array}{c}
u_0 \\
u_1 \\
u_2 \\
u_3 \\
\end{array} \right)
@]@
Solution
The actual solution to this ODE is
@[@
z(t, u) =
\left( \begin{array}{l}
u_0 \\
u_1 + u_4 u_0 t \\
u_2 + u_5 u_1 t + u_5 u_4 u_0 t^2 / 2 \\
u_3 + u_6 u_2 t + u_6 u_5 u_1 t^2 / 2 + u_6 u_5 u_4 u_0 t^3 / 6
\end{array} \right)
@]@
g(u)
@[@
z_2 (t, u) = u_2 + u_5 u_1 t + u_5 u_4 u_0 t^2 / 2
@]@Fix @(@
r
@)@ and define @(@
g(u) = [ \partial_u z(r, u) ]^\R{T}
@)@.
It follows that
@[@
g(u)
=
\left( \begin{array}{c}
u_5 u_4 r^2 / 2 \\
u_5 r \\
1 \\
0 \\
u_5 u_0 r^2 / 2 \\
u_t r + u_4 u_0 r^2 / 2 \\
0
\end{array} \right)
@]@
Source
# include <cppad/cppad.hpp>
# include <cppad/example/atomic_four/lin_ode/lin_ode.hpp>
namespace { // BEGIN_EMPTY_NAMESPACE
template <class Scalar, class Vector>
Vector Z(Scalar t, const Vector& u)
{ size_t nz = 4;
Vector z(nz);
//
z[0] = u[0];
z[1] = u[1] + u[4]*u[0]*t;
z[2] = u[2] + u[5]*u[1]*t + u[5]*u[4]*u[0]*t*t/2.0;
z[3] = u[3] + u[6]*u[2]*t + u[6]*u[5]*u[1]*t*t/2.0
+ u[6]*u[5]*u[4]*u[0]*t*t*t/6.0;
//
return z;
}
template <class Scalar, class Vector>
Vector G(Scalar t, const Vector& u)
{ size_t nu = 7;
Vector g(nu);
//
g[0] = u[5]*u[4]*t*t/2.0;
g[1] = u[5]*t;
g[2] = Scalar(1.0);
g[3] = Scalar(0.0);
g[4] = u[5]*u[0]*t*t/2.0;
g[5] = u[1]*t + u[4]*u[0]*t*t/2.0;
g[6] = Scalar(0.0);
//
return g;
}
} // END_EMPTY_NAMESPACE
bool reverse(void)
{ // ok
bool ok = true;
//
// AD, NearEqual, eps99
using CppAD::AD;
using CppAD::NearEqual;
double eps99 = std::numeric_limits<double>::epsilon() * 99.0;
// -----------------------------------------------------------------------
// Record f
// -----------------------------------------------------------------------
//
// afun
CppAD::atomic_lin_ode<double> afun("atomic_lin_ode");
//
// m, r
size_t m = 4;
double r = 2.0;
double step = 2.0;
//
// pattern, transpose
size_t nr = m;
size_t nc = m;
size_t nnz = 3;
CppAD::sparse_rc< CppAD::vector<size_t> > pattern(nr, nc, nnz);
for(size_t k = 0; k < nnz; ++k)
{ size_t i = k + 1;
size_t j = k;
pattern.set(k, i, j);
}
bool transpose = false;
//
// ny, ay
size_t ny = m;
CPPAD_TESTVECTOR( AD<double> ) ay(ny);
//
// nu, au
size_t nu = nnz + m;
CPPAD_TESTVECTOR( AD<double> ) au(nu);
for(size_t j = 0; j < nu; ++j)
au[j] = AD<double>(j + 1);
CppAD::Independent(au);
//
// ax
CPPAD_TESTVECTOR( AD<double> ) ax(nnz + m);
for(size_t k = 0; k < nnz; ++k)
ax[k] = au[m + k];
for(size_t i = 0; i < m; ++i)
ax[nnz + i] = au[i];
//
// ay
size_t call_id = afun.set(r, step, pattern, transpose);
afun(call_id, ax, ay);
//
// f
CppAD::ADFun<double> f(au, ay);
// -----------------------------------------------------------------------
// ar, check_f
CppAD::Independent(au);
AD<double> ar = r;
ay = Z(ar, au);
CppAD::ADFun<double> check_f(au, ay);
// -----------------------------------------------------------------------
// reverse mode on f
// -----------------------------------------------------------------------
//
// u
CPPAD_TESTVECTOR(double) u(nu);
for(size_t j = 0; j < nu; ++j)
u[j] = double( j + 2 );
//
// y
// zero order forward mode computation of f(u)
CPPAD_TESTVECTOR(double) y(ny);
y = f.Forward(0, u);
//
// ok
CPPAD_TESTVECTOR(double) check_y = check_f.Forward(0, u);
for(size_t i = 0; i < ny; ++i)
ok &= NearEqual(y[i], check_y[i], eps99, eps99);
//
// w, ok
CPPAD_TESTVECTOR(double) w(ny), dw(nu), check_dw(nu);
for(size_t i = 0; i < ny; ++i)
w[i] = 0.0;
for(size_t i = 0; i < ny; ++i)
{ w[i] = 1.0;
dw = f.Reverse(1, w);
check_dw = check_f.Reverse(1, w);
for(size_t j = 0; j < nu; ++j)
ok &= NearEqual(dw[j], check_dw[j], eps99, eps99);
w[i] = 0.0;
}
// -----------------------------------------------------------------------
// Record g
// -----------------------------------------------------------------------
//
// af
CppAD::ADFun< AD<double>, double> af = f.base2ad();
//
// au
CppAD::Independent(au);
CPPAD_TESTVECTOR( AD<double> ) aw(ny), adw(nu);
af.Forward(0, au);
for(size_t i = 0; i < ny; ++i)
aw[i] = 0.0;
aw[2] = 1.0;
adw = af.Reverse(1, aw);
// g
CppAD::ADFun<double> g(au, adw);
// -----------------------------------------------------------------------
// check_g
CppAD::Independent(au);
ay = G(ar, au);
CppAD::ADFun<double> check_g(au, ay);
// -----------------------------------------------------------------------
//
// v
// zero order forward mode computation of g(u)
CPPAD_TESTVECTOR(double) v(nu);
v = g.Forward(0, u);
//
// ok
CPPAD_TESTVECTOR(double) check_v = check_g.Forward(0, u);
for(size_t i = 0; i < nu; ++i)
ok &= NearEqual(v[i], check_v[i], eps99, eps99);
//
// w, ok
w.resize(nu);
for(size_t i = 0; i < nu; ++i)
w[i] = 0.0;
for(size_t i = 0; i < nu; ++i)
{ w[i] = 1.0;
dw = g.Reverse(1, w);
check_dw = check_g.Reverse(1, w);
for(size_t j = 0; j < nu; ++j)
ok &= NearEqual(dw[j], check_dw[j], eps99, eps99);
w[i] = 0.0;
}
// -----------------------------------------------------------------------
return ok;
}
Input File: example/atomic_four/lin_ode/reverse.cpp