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}} }@)@
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