Prev Next stack_machine.cpp Headings

@(@\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 .
Example Differentiating a Stack Machine Interpreter

# include <cstring>
# include <cstddef>
# include <cstdlib>
# include <cctype>
# include <cassert>
# include <stack>

# include <cppad/cppad.hpp>

namespace {
// Begin empty namespace ------------------------------------------------

bool is_number( const std::string &s )
{   char ch = s[0];
    bool number = (std::strchr("0123456789.", ch) != 0);
    return number;
}
bool is_binary( const std::string &s )
{   char ch = s[0];
    bool binary = (strchr("+-*/.", ch) != 0);
    return binary;
}
bool is_variable( const std::string &s )
{   char ch = s[0];
    bool variable = ('a' <= ch) & (ch <= 'z');
    return variable;
}

void StackMachine(
    std::stack< std::string >          &token_stack  ,
    CppAD::vector< CppAD::AD<double> > &variable     )
{   using std::string;
    using std::stack;

    using CppAD::AD;

    stack< AD<double> > value_stack;
    string              token;
    AD<double>          value_one;
    AD<double>          value_two;

    while( ! token_stack.empty() )
    {   string s = token_stack.top();
        token_stack.pop();

        if( is_number(s) )
        {   value_one = std::atof( s.c_str() );
            value_stack.push( value_one );
        }
        else if( is_variable(s) )
        {   value_one = variable[ size_t(s[0]) - size_t('a') ];
            value_stack.push( value_one );
        }
        else if( is_binary(s) )
        {   assert( value_stack.size() >= 2 );
            value_one = value_stack.top();
            value_stack.pop();
            value_two = value_stack.top();
            value_stack.pop();

            switch( s[0] )
            {
                case '+':
                value_stack.push(value_one + value_two);
                break;

                case '-':
                value_stack.push(value_one - value_two);
                break;

                case '*':
                value_stack.push(value_one * value_two);
                break;

                case '/':
                value_stack.push(value_one / value_two);
                break;

                default:
                assert(0);
            }
        }
        else if( s[0] == '=' )
        {   assert( value_stack.size() >= 1 );
            assert( token_stack.size() >= 1 );
            //
            s = token_stack.top();
            token_stack.pop();
            //
            assert( is_variable( s ) );
            value_one = value_stack.top();
            value_stack.pop();
            //
            variable[ size_t(s[0]) - size_t('a') ] = value_one;
        }
        else assert(0);
    }
    return;
}

// End empty namespace -------------------------------------------------------
}

bool StackMachine(void)
{   bool ok = true;

    using std::string;
    using std::stack;

    using CppAD::AD;
    using CppAD::NearEqual;
    double eps99 = 99.0 * std::numeric_limits<double>::epsilon();
    using CppAD::vector;

    // The users program in that stack machine language
    const char *program[] = {
        "1.0", "a", "+", "=", "b",  // b = a + 1
        "2.0", "b", "*", "=", "c",  // c = b * 2
        "3.0", "c", "-", "=", "d",  // d = c - 3
        "4.0", "d", "/", "=", "e"   // e = d / 4
    };
    size_t n_program = sizeof( program ) / sizeof( program[0] );

    // put the program in the token stack
    stack< string > token_stack;
    size_t i = n_program;
    while(i--)
        token_stack.push( program[i] );

    // domain space vector
    size_t n = 1;
    vector< AD<double> > X(n);
    X[0] = 0.;

    // declare independent variables and start tape recording
    CppAD::Independent(X);

    // x[0] corresponds to a in the stack machine
    vector< AD<double> > variable(26);
    variable[0] = X[0];

    // calculate the resutls of the program
    StackMachine( token_stack , variable);

    // range space vector
    size_t m = 4;
    vector< AD<double> > Y(m);
    Y[0] = variable[1];   // b = a + 1
    Y[1] = variable[2];   // c = (a + 1) * 2
    Y[2] = variable[3];   // d = (a + 1) * 2 - 3
    Y[3] = variable[4];   // e = ( (a + 1) * 2 - 3 ) / 4

    // create f : X -> Y and stop tape recording
    CppAD::ADFun<double> f(X, Y);

    // use forward mode to evaluate function at different argument value
    size_t p = 0;
    vector<double> x(n);
    vector<double> y(m);
    x[0] = 1.;
    y    = f.Forward(p, x);

    // check function values
    ok &= (y[0] == x[0] + 1.);
    ok &= (y[1] == (x[0] + 1.) * 2.);
    ok &= (y[2] == (x[0] + 1.) * 2. - 3.);
    ok &= (y[3] == ( (x[0] + 1.) * 2. - 3.) / 4.);

    // Use forward mode (because x is shorter than y) to calculate Jacobian
    p = 1;
    vector<double> dx(n);
    vector<double> dy(m);
    dx[0] = 1.;
    dy    = f.Forward(p, dx);
    ok   &= NearEqual(dy[0], 1., eps99, eps99);
    ok   &= NearEqual(dy[1], 2., eps99, eps99);
    ok   &= NearEqual(dy[2], 2., eps99, eps99);
    ok   &= NearEqual(dy[3], .5, eps99, eps99);

    // Use Jacobian routine (which automatically decides which mode to use)
    dy = f.Jacobian(x);
    ok   &= NearEqual(dy[0], 1., eps99, eps99);
    ok   &= NearEqual(dy[1], 2., eps99, eps99);
    ok   &= NearEqual(dy[2], 2., eps99, eps99);
    ok   &= NearEqual(dy[3], .5, eps99, eps99);

    return ok;
}

Input File: example/general/stack_machine.cpp