Hello,
I have written a small set of templates to represent a 1-variable real
mathematical function, with the help of "expression templates",
mentioned in blitz++ for arrays, and a simple metafunction to calculate the
symbolic derivative.
I feel I have reinvented the wheel but I don't know which libs from boost to
reuse (MPL, function, bind...), or POOMA (the expr templates part of it).
I have written all the templates as purely compile-time objects with all the
function data stored in types. The main drawback is the inability
to use floating-point literals.
The objective is to store in compile-time arbitrary 1-var real functions
like:
f(x) = x^2 + sqrt(x - 3)
and then to have a metafunction to calculate f'(x), the derivative.
(Partials derivatives for later)...
Here is what I came up with. Not sure my nomenclature is correct, I came
across the terms in misc lib docs:
The parse tree is:
A "Function" can be a "Terminal", an "Application Expression", a "Unary
expression" or a "Binary Expression" (The interface is inadequate because
the user instantiates a new type (a new symbolic function) very verbosely).
A "Terminal" can be a "Literal", a "Variable" or a "Elementary function".
A "Literal" is a class template whose template argument is an int (drawback:
cannot store floating points this way, and I should have ).
A "Variable" just represents "x" in the usual notation "f(x)"
A "Elementary Function" is a wrapper around <cmath>'s standard and perhaps
boost::math's special 1-variable functions, like sin, cos, exp, ....
"Application Expression" is to represent sin ( x - 2 ), sort of like
callable type.
"Unary Expression" is to represent op R (I have 2 so far: +R or -R)
"Binary Expression" is to represent L op R (L / R)
Op is an "Operator" (plus, minus, multiplies, divides, power)...
As I said, I feel this must have been done already?
Is there a library I can reuse?
Is there any way to represent floating points while staying totally
compile-time?
Below is the code with an example call:
Best regards,
----------------------------------------------------------------------------
-------
#include <iostream>
#include <typeinfo>
#include <cmath>
#include
#include
namespace MathFunction {
template <typename T>
struct FunctionTag {
typedef T function;
};
// Terminals
template <typename T>
struct TerminalTag : FunctionTag< T > {
typedef T terminal;
};
template <int N>
struct Literal : TerminalTag< Literal<N> > {
typedef Literal<0> derivative;
static double eval(double variable)
{
return static_cast<double>(N);
}
};
struct Variable : TerminalTag<Variable> {
typedef Literal<1> derivative;
static double eval(double variable)
{
return variable;
}
};
template
struct UnaryExpression;
template
struct BinaryExpression;
struct plus;
struct minus;
struct multiplies;
struct divides;
struct power;
// Elementary functions
template <typename T>
struct ElemFunctionTag : FunctionTag< T > {
typedef T elementaryfunction;
};
struct Cos;
struct Sin : ElemFunctionTag<Sin> {
typedef Cos derivative;
static double eval(double variable)
{
return std::sin(variable);
}
};
struct Cos : ElemFunctionTag<Cos> {
typedef UnaryExpression derivative;
};
struct Tan : ElemFunctionTag<Tan> {
};
struct ATan : ElemFunctionTag<ATan> {};
struct Exp : ElemFunctionTag<Exp> {};
// Operators
template <typename T>
struct UnaryOperatorTag {
typedef T unaryoper;
};
template <typename T>
struct BinaryOperatorTag {
typedef T binaryoper;
};
struct plus : UnaryOperatorTag<plus>, BinaryOperatorTag<plus> {
static double eval(double var)
{
return var;
}
static double eval(double Lvar, double Rvar)
{
return Lvar + Rvar;
}
template
struct derivative {
typedef BinaryExpression
type;
};
};
struct minus : UnaryOperatorTag<minus>, BinaryOperatorTag<minus> {
static double eval(double var)
{
return -var;
}
static double eval(double Lvar, double Rvar)
{
return Lvar - Rvar;
}
template
struct derivative {
typedef BinaryExpression type;
};
};
struct multiplies : BinaryOperatorTag<multiplies> {
static double eval(double Lvar, double Rvar)
{
return Lvar * Rvar;
}
template
struct derivative {
typedef BinaryExpression left;
typedef BinaryExpression
right;
typedef BinaryExpression type;
};
};
struct divides : BinaryOperatorTag<divides> {
static double eval(double Lvar, double Rvar)
{
return Lvar/Rvar;
}
template
struct derivative {
typedef BinaryExpression left;
typedef BinaryExpression
right;
typedef BinaryExpression > bottom;
typedef BinaryExpression top;
typedef BinaryExpression type;
};
};
struct power : BinaryOperatorTag<power> {
static double eval(double Lvar, double Rvar)
{
return std::pow(Lvar,Rvar);
}
template
struct derivative {
typedef BinaryExpression > Rminus1;
typedef BinaryExpression right;
typedef BinaryExpression type;
};
};
struct root : BinaryOperatorTag<root> {
static double eval(double Lvar, double Rvar)
{
return std::pow(Rvar,1.0/Lvar);
}
};
// Expression
template
struct UnaryExpression : FunctionTag< UnaryExpression > {
BOOST_STATIC_ASSERT(( boost::is_sameOpTag::unaryoper,OpTag::value
));
BOOST_STATIC_ASSERT(( boost::is_same::value ));
typedef UnaryExpression derivative;
static double eval(double variable)
{
return OpTag::eval(R::eval(variable));
}
};
template
struct BinaryExpression : FunctionTag< BinaryExpression > {
BOOST_STATIC_ASSERT(( boost::is_same::value ));
BOOST_STATIC_ASSERT(( boost::is_sameOpTag::binaryoper,OpTag::value
));
BOOST_STATIC_ASSERT(( boost::is_same::value ));
typedef OpTag::derivative::type derivative;
static double eval(double variable)
{
return OpTag::eval(L::eval(variable), R::eval(variable));
}
};
template
struct ApplicationExpression : FunctionTag< ApplicationExpression > {
BOOST_STATIC_ASSERT(( boost::is_same::value
));
BOOST_STATIC_ASSERT(( boost::is_same::value ));
typedef BinaryExpression< ApplicationExpression,
multiplies, R::derivative > derivative;
static double eval(double variable)
{
return L::eval( R::eval(variable) );
}
};
template
struct Derivative {
BOOST_STATIC_ASSERT(( n>0 ));
typedef Derivative::type type;
};
template <typename F>
struct Derivative {
typedef F::derivative type;
};
// Main interface
template <typename K>
struct Function {
BOOST_STATIC_ASSERT(( boost::is_same::value ));
typedef Function derivative;
static double eval(double variable)
{
return K::eval(variable);
}
};
template <int N>
std::ostream& operator<<( std::ostream& os, const Literal<N>& )
{
os << N;
return os;
}
std::ostream& operator<<( std::ostream& os, const Variable& )
{
os << 'x';
return os;
}
template <typename T>
std::ostream& operator<<( std::ostream& os, const ElemFunctionTag<T>& )
{
os << T();
return os;
}
std::ostream& operator<<( std::ostream& os, const Sin& )
{
os << "sin x";
return os;
}
std::ostream& operator<<( std::ostream& os, const Cos& )
{
os << "cos x";
return os;
}
template
std::ostream& operator<<( std::ostream& os, const
ApplicationExpression& )
{
os << L() << '(' << R() << ')';
return os;
}
template
std::ostream& operator<<( std::ostream& os, const UnaryExpression&
)
{
os << OpTag() << R();
return os;
}
template
std::ostream& operator<<( std::ostream& os, const
BinaryExpression& )
{
os << '(' << L() << ' '<< OpTag() << ' ' << R() << ')';
return os;
}
std::ostream& operator<<( std::ostream& os, const plus& )
{
os << '+';
return os;
}
std::ostream& operator<<( std::ostream& os, const minus& )
{
os << '-';
return os;
}
std::ostream& operator<<( std::ostream& os, const multiplies& )
{
os << '*';
return os;
}
std::ostream& operator<<( std::ostream& os, const divides& )
{
os << '/';
return os;
}
std::ostream& operator<<( std::ostream& os, const power& )
{
os << '^';
return os;
}
std::ostream& operator<<( std::ostream& os, const root& )
{
os << 'V';
return os;
}
template <typename K>
std::ostream& operator<<( std::ostream& os, const Function<K>& )
{
os << "f(x) ="<< K();
return os;
}
}
int main(int argc, char* argv[])
{
using namespace MathFunction;
typedef Function< Sin > f;
typedef Derivative::type fprime;
std::cout<< f() <