# ifndef CPPAD_ODE_EVALUATE_INCLUDED
# define CPPAD_ODE_EVALUATE_INCLUDED
# include <cppad/vector.hpp>
# include <cppad/ode_err_control.hpp>
# include <cppad/runge_45.hpp>
namespace CppAD { // BEGIN CppAD namespace
template <class Float>
class ode_evaluate_fun {
private:
const size_t m_;
const CppAD::vector<Float> x_;
public:
ode_evaluate_fun(size_t m, const CppAD::vector<Float> &x)
: m_(m), x_(x)
{ }
void Ode(
const Float &t,
const CppAD::vector<Float> &z,
CppAD::vector<Float> &h)
{
if( m_ == 0 )
ode_y(t, z, h);
if( m_ == 1 )
ode_z(t, z, h);
}
void ode_y(
const Float &t,
const CppAD::vector<Float> &y,
CppAD::vector<Float> &g)
{ // y_t = g(t, x, y)
CPPAD_ASSERT_UNKNOWN( y.size() == x_.size() );
size_t i;
size_t n = x_.size();
for(i = 0; i < n; i++)
g[i] = x_[i] * y[i];
// because y_i(0) = 1, solution for this equation is
// y_0 (t) = t
// y_1 (t) = exp(x_1 * t)
// y_2 (t) = exp(2 * x_2 * t)
// ...
}
void ode_z(
const Float &t ,
const CppAD::vector<Float> &z ,
CppAD::vector<Float> &h )
{ // z = [ y ; y_x ]
// z_t = h(t, x, z) = [ y_t , y_x_t ]
size_t i, j;
size_t n = x_.size();
CPPAD_ASSERT_UNKNOWN( z.size() == n + n * n );
// y_t
for(i = 0; i < n; i++)
{ h[i] = x_[i] * z[i];
// initialize y_x_t as zero
for(j = 0; j < n; j++)
h[n + i * n + j] = 0.;
}
for(i = 0; i < n; i++)
{ // partial of g_i w.r.t y_i
Float gi_yi = x_[i];
// partial of g_i w.r.t x_i
Float gi_xi = z[i];
// partial of y_i w.r.t x_i
Float yi_xi = z[n + i * n + i];
// derivative of yi_xi with respect to t
h[n + i * n + i] = gi_xi + gi_yi * yi_xi;
}
}
};
template <class Float>
class ode_evaluate_method {
private:
ode_evaluate_fun<Float> F;
public:
// constructor
ode_evaluate_method(size_t m, const CppAD::vector<Float> &x)
: F(m, x)
{ }
void step(
Float ta ,
Float tb ,
CppAD::vector<Float> &xa ,
CppAD::vector<Float> &xb ,
CppAD::vector<Float> &eb )
{ xb = CppAD::Runge45(F, 1, ta, tb, xa, eb);
}
size_t order(void)
{ return 4; }
};
template <class Float>
void ode_evaluate(
CppAD::vector<Float> &x ,
size_t m ,
CppAD::vector<Float> &fm )
{
typedef CppAD::vector<Float> Vector;
size_t n = x.size();
size_t ell;
CPPAD_ASSERT_KNOWN( m == 0 || m == 1,
"ode_evaluate: m is not zero or one"
);
CPPAD_ASSERT_KNOWN(
((m==0) & (fm.size()==n)) || ((m==1) & (fm.size()==n*n)),
"ode_evaluate: the size of fm is not correct"
);
if( m == 0 )
ell = n;
else ell = n + n * n;
// set up the case we are integrating
Float ti = 0.;
Float tf = 1.;
Float smin = 1e-3;
Float smax = 1.;
Float scur = 1.;
Float erel = 0.;
vector<Float> yi(ell), eabs(ell);
size_t i, j;
for(i = 0; i < ell; i++)
{ eabs[i] = 1e-7;
if( i < n )
yi[i] = 1.;
else yi[i] = 0.;
}
// return values
Vector yf(ell), ef(ell), maxabs(ell);
size_t nstep;
// construct ode method for taking one step
ode_evaluate_method<Float> method(m, x);
// solve differential equation
yf = OdeErrControl(method,
ti, tf, yi, smin, smax, scur, eabs, erel, ef, maxabs, nstep);
if( m == 0 )
{ for(i = 0; i < n; i++)
fm[i] = yf[i];
}
else
{ for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
fm[i * n + j] = yf[n + i * n + j];
}
return;
}
} // END CppAD namespace
# endif