Example Differentiating a Stack Machine Interpreter
Keywords: example differentiating stack machine interpreter test

| Prev | Next |  |  |  |  |  | stack_machine.cpp | Headings

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;
    	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., 1e-10, 1e-10);
    	ok   &= NearEqual(dy[1], 2., 1e-10, 1e-10);
    	ok   &= NearEqual(dy[2], 2., 1e-10, 1e-10);
    	ok   &= NearEqual(dy[3], .5, 1e-10, 1e-10);

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

    	return ok;
    }

`

Input File: example/stack_machine.cpp