atomic_four_get_started.cpp

View page source

Getting Started with Atomic Functions: Example and Test

Purpose

This example demonstrates the minimal amount of information necessary for a atomic_four function.

Define Atomic Function

// empty namespace
namespace {
    // atomic_get_started
    class atomic_get_started : public CppAD::atomic_four<double> {
    public:
        // can use const char* name when calling this constructor
        atomic_get_started(const std::string& name) :
        CppAD::atomic_four<double>(name) // inform base class of 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
        {
            assert( call_id == 0 );       // default value
            assert( type_x.size() == 1 ); // n
            assert( type_y.size() == 1 ); // m
            //
            type_y[0] = type_x[0];
            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>&        taylor_x     ,
            CppAD::vector<double>&              taylor_y     ) override
        {
# ifndef NDEBUG
            size_t q = order_up + 1;
            size_t n = taylor_x.size() / q;
            size_t m = taylor_y.size() / q;
            assert( call_id == 0 );
            assert( order_low == 0);
            assert( order_up == 0);
            assert( n == 1 );
            assert( m == 1 );
            assert( m == select_y.size() );
# endif
            // return flag
            bool ok = order_up == 0;
            if( ! ok )
                return ok;

            // taylor_y
            // Order zero forward mode: y^0 = g( x^0 ) = 1 / x^0
            taylor_y[0] = 1.0 / taylor_x[0];
            //
            return ok;
        }
    };
}

Use Atomic Function

bool get_started(void)
{
    // ok, eps
    bool ok = true;
    double eps = 10. * CppAD::numeric_limits<double>::epsilon();
    //
    // afun
    atomic_get_started afun("atomic_get_started");
    //
    // n, m
    size_t n = 1;
    size_t m = 1;
    //
    // x0
    double  x0 = 0.5;
    //
    // ax
    CPPAD_TESTVECTOR( CppAD::AD<double> ) ax(n);
    ax[0]     = x0;
    CppAD::Independent(ax);
    //
    // au
    // call atomic function and store result in au
    CPPAD_TESTVECTOR( CppAD::AD<double> ) au(m);
    afun(ax, au);
    //
    // ay
    CPPAD_TESTVECTOR( CppAD::AD<double> ) ay(m);
    ay[0] = 1.0 + au[0];
    //
    // f
    // create f: x -> y and stop tape recording
    CppAD::ADFun<double> f(ax, ay);
    //
    // check
    double check = 1.0 + 1.0 / x0;
    //
    // ok
    // check ay[0]
    ok &= CppAD::NearEqual( Value(ay[0]) , check,  eps, eps);
    //
    // ok
    // check zero order forward mode
    CPPAD_TESTVECTOR( double ) x(n), y(m);
    x[0] = x0;
    y    = f.Forward(0, x);
    ok    &= CppAD::NearEqual(y[0] , check,  eps, eps);
    //
    return ok;
}