[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
6.2.2 The cosine function
The GiNaC header file ‘inifcns.h’ contains the line
DECLARE_FUNCTION_1P(cos) |
which declares to all programs using GiNaC that there is a function ‘cos’
that takes one ex
as an argument. This is all they need to know to use
this function in expressions.
The implementation of the cosine function is in ‘inifcns_trans.cpp’. Here
is its REGISTER_FUNCTION
line:
REGISTER_FUNCTION(cos, eval_func(cos_eval). evalf_func(cos_evalf). derivative_func(cos_deriv). latex_name("\\cos")); |
There are four options defined for the cosine function. One of them
(latex_name
) gives the function a proper name for LaTeX output; the
other three indicate the C++ functions in which the "brains" of the cosine
function are defined.
The eval_func()
option specifies the C++ function that implements
the eval()
method, GiNaC's anonymous evaluator. This function takes
the same number of arguments as the associated symbolic function (one in this
case) and returns the (possibly transformed or in some way simplified)
symbolically evaluated function (See section Automatic evaluation and canonicalization of expressions, for a description
of the automatic evaluation process). If no (further) evaluation is to take
place, the eval_func()
function must return the original function
with .hold()
, to avoid a potential infinite recursion. If your
symbolic functions produce a segmentation fault or stack overflow when
using them in expressions, you are probably missing a .hold()
somewhere.
The eval_func()
function for the cosine looks something like this
(actually, it doesn't look like this at all, but it should give you an idea
what is going on):
static ex cos_eval(const ex & x) { if ("x is a multiple of 2*Pi") return 1; else if ("x is a multiple of Pi") return -1; else if ("x is a multiple of Pi/2") return 0; // more rules... else if ("x has the form 'acos(y)'") return y; else if ("x has the form 'asin(y)'") return sqrt(1-y^2); // more rules... else return cos(x).hold(); } |
This function is called every time the cosine is used in a symbolic expression:
{ ... e = cos(Pi); // this calls cos_eval(Pi), and inserts its return value into // the actual expression cout << e << endl; // prints '-1' ... } |
In this way, cos(4*Pi)
automatically becomes 1,
cos(asin(a+b))
becomes sqrt(1-(a+b)^2)
, etc. If no reasonable
symbolic transformation can be done, the unmodified function is returned
with .hold()
.
GiNaC doesn't automatically transform cos(2)
to ‘-0.416146...’.
The user has to call evalf()
for that. This is implemented in a
different function:
static ex cos_evalf(const ex & x) { if (is_a<numeric>(x)) return cos(ex_to<numeric>(x)); else return cos(x).hold(); } |
Since we are lazy we defer the problem of numeric evaluation to somebody else,
in this case the cos()
function for numeric
objects, which in
turn hands it over to the cos()
function in CLN. The .hold()
isn't really needed here, but reminds us that the corresponding eval()
function would require it in this place.
Differentiation will surely turn up and so we need to tell cos
what its first derivative is (higher derivatives, .diff(x,3)
for
instance, are then handled automatically by basic::diff
and
ex::diff
):
static ex cos_deriv(const ex & x, unsigned diff_param) { return -sin(x); } |
The second parameter is obligatory but uninteresting at this point. It specifies which parameter to differentiate in a partial derivative in case the function has more than one parameter, and its main application is for correct handling of the chain rule.
An implementation of the series expansion is not needed for cos()
as
it doesn't have any poles and GiNaC can do Taylor expansion by itself (as
long as it knows what the derivative of cos()
is). tan()
, on
the other hand, does have poles and may need to do Laurent expansion:
static ex tan_series(const ex & x, const relational & rel, int order, unsigned options) { // Find the actual expansion point const ex x_pt = x.subs(rel); if ("x_pt is not an odd multiple of Pi/2") throw do_taylor(); // tell function::series() to do Taylor expansion // On a pole, expand sin()/cos() return (sin(x)/cos(x)).series(rel, order+2, options); } |
The series()
implementation of a function must return a
pseries
object, otherwise your code will crash.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |