manpagez: man pages & more
info ginac
Home | html | info | man
[ < ] [ > ]   [ << ] [ 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] [ ? ]
© manpagez.com 2000-2024
Individual documents may contain additional copyright information.