1 Computer Algebra

There are many Computer Algebra systems available that are capabale of performing symbolic mathematics, such as reduce, maxima, mathematica, and matlab. Some are free, others are pricey. The use of these packages itself is a major affair that can require a whole course (Math 250 for example) that can teach only the most basic operations.
We will take a different approach, with the attitude that is better to learn the underlying programming and math skills, and design our own short and simple symbolic math “software” programs by writing C++ code that uses the cln and GiNaC libraries.

Cln is a huge library of numeric and symbolic object classes, and GiNaC is a library that extends the cln classes and adds class methods or functions that act on specific class members. It is an example of object oriented programming at its finest. Naturally both packages were designed and written by physicists to do Feynmann diagram calculations in high energy physics.

1.1 Object oriented programming

differs from procedural programming in a fundamental way. In C, which is not object oriented, it is possible to define very complex, heterogeneous data types called structs. Here is an example of a struct;

struct Atom{  
       float position[3];  /* (x,y,z) for atom */  
       char *name;         /* a string such as Li, Mn, Ar,... */  
       float momentum[3];  /* (p_x, p_y, p_z) */  
       int color[3];       /* RGB numbers for the color it is displayed with*/  
} Atoms[200];

which is the variable declaration for an array of 200 such objects, each one having a variety of attributes. The struct members are the position[3], name, color[3] variables in the declaration. If in the program we wish to print out the values of these things, we would use dot notation

for(n=0;n<200;n++){  
printf("%s at\n", Atoms[n].name);  
printf("(x,y,z)=(%f,%f,%f)\n", Atoms[n].position[0], Atoms[n].position[1], \  
                               Atoms[n].position[2]);

What C++ offers above and beyond this is the elevation of a struct to a class, which differs in that a class can have functions, called class methods that act on class members as members themselves. Furthermore one can create subclasses of a parent class, and these subclasses inherit class members and properties of the parent, and can have additional members and properties of their own.

A second feature of C++ is its input/output routines. These are highly overloaded in the sense that when an object such as a string or int or float is passed to the output routine cout, you do not need to tell it how to print by providing a format string like you do with printf, the machine will figure out how to format the object for printing. This of course takes time, and C++ is somewhat slower than C.
If you introduce a new class of objects, you can overload the cout operator and tell it how to print instances of your new object.

The third feature of C++ is its relaxation of the rules regarding variable declaration. In C, you must declare all variables before any are assigned values or used. In C++ this is not required, and you can introduce or declare variables at any point in the program.

It is important to remember that C++ is C, with these additions and enhancements. Most C programs will compile just fine with C++ compilers, but the reverse is not true.

1.2 Examples

In C we would write

#include<stdio.h>  
#include<math.h>  
int n;  
float dx,x,y;  
main(){  
dx=0.01;  
for(n=0;n<100;n++){  
x=(float)n*dx;  
y=sin(x);  
printf("%d\t%f\t%f\n",n,x,y);  
}  
}

and compile the program with

gcc -o program program.c -lm

and in C++ we have

#include<iostream.h>  
#include<math.h>  
int n;  
float dx,x,y;  
main(){  
dx=0.01;  
for(n=0;n<100;n++){  
x=(float)n*dx;  
y=sin(x);  
cout << "n = " << n << "\t" << x << "\t" << y << endl;  
}  
}

and compile with

g++ -o program program.c -lm

although the -lm may not be necessary with many compilers.

1.3 An object-oriented, symbolic math program

Let’s write a program that will differentiate the function

                2
f (x)  =   e- ax
ten times symbolically, using the GiNaC library. In GiNaC every one of the thousands of available objects/classes knows how to symbolically differentitate itself. We simply need to declare our external function, and invoke its diff member method.
#include <iostream>  
#include <ginac/ginac.h>  
using namespace std;  
using namespace GiNaC;  
 
 
int n;  
 
main(){  
symbol x("x");  
symbol a("a");  
const ex f=exp(-a*x*x);  
for(n=1;n<=10;n++){  
cout << f.diff(x,n) << endl;  
}  
}

We compile and run this program, and get the output below;

sohrab:~$ g++ -o test2 test2.cc -lcln -lginac  
sohrab:~$ ./test2  
-2*a*exp(-a*xˆ2)*x  
 
-2*a*exp(-a*xˆ2)+4*aˆ2*exp(-a*xˆ2)*xˆ2  
 
-8*aˆ3*exp(-a*xˆ2)*xˆ3+12*aˆ2*exp(-a*xˆ2)*x  
 
12*aˆ2*exp(-a*xˆ2)-48*aˆ3*exp(-a*xˆ2)*xˆ2+16*aˆ4*exp(-a*xˆ2)*xˆ4  
 
-120*aˆ3*exp(-a*xˆ2)*x+160*aˆ4*exp(-a*xˆ2)*xˆ3-32*aˆ5*exp(-a*xˆ2)*xˆ5  
-120*aˆ3*exp(-a*xˆ2)+64*aˆ6*exp(-a*xˆ2)*xˆ6+720*aˆ4*exp(-a*xˆ2)*xˆ2  
                                                   -480*aˆ5*exp(-a*xˆ2)*xˆ4  
 
1344*aˆ6*exp(-a*xˆ2)*xˆ5-128*aˆ7*exp(-a*xˆ2)*xˆ7+1680*aˆ4*exp(-a*xˆ2)*x  
                                                   -3360*aˆ5*exp(-a*xˆ2)*xˆ3  
 
1680*aˆ4*exp(-a*xˆ2)+13440*aˆ6*exp(-a*xˆ2)*xˆ4-3584*aˆ7*exp(-a*xˆ2)*xˆ6  
                     +256*aˆ8*exp(-a*xˆ2)*xˆ8-13440*aˆ5*exp(-a*xˆ2)*xˆ2  
 
80640*aˆ6*exp(-a*xˆ2)*xˆ3-48384*aˆ7*exp(-a*xˆ2)*xˆ5+9216*aˆ8*exp(-a*xˆ2)*xˆ7  
                         -512*aˆ9*exp(-a*xˆ2)*xˆ9-30240*aˆ5*exp(-a*xˆ2)*x  
 
-30240*aˆ5*exp(-a*xˆ2)+302400*aˆ6*exp(-a*xˆ2)*xˆ2-403200*aˆ7*exp(-a*xˆ2)*xˆ4  
          +161280*aˆ8*exp(-a*xˆ2)*xˆ6-23040*aˆ9*exp(-a*xˆ2)*xˆ8  
          +1024*aˆ10*exp(-a*xˆ2)*xˆ10

1.4 Polynomial arithmetic

Below is another example illustrating some of the many polynomial functions for commuting variables.

#include <iostream>  
#include <ginac/ginac.h>  
using namespace std;  
using namespace GiNaC;  
 
main(){  
symbol x("x");  
symbol y("y");  
ex Poly1=4*pow(x,3)*y +5*pow(y,2)*x -6*x*y;  
ex Poly2=Poly1.diff(x,1);  
ex Poly3=Poly1*Poly2;  
ex Poly4=pow(x,2)-4*x+4;  
ex Poly5=(x-2)*(x-1);  
ex Poly6=Poly4/Poly5;  
cout << Poly1.ldegree(x) << endl;  
cout << Poly2.coeff(x,2) << endl;  
cout << Poly6.normal() << endl;  
}

Compile and run this program and determine what each of these operations does.

1.5 Series expansion

Below is yet another example illustrating how you can write programs that manipulate series symbolically.

#include <iostream>  
#include <ginac/ginac.h>  
using namespace std;  
using namespace GiNaC;  
 
main(){  
symbol x("x");  
symbol c("c");  
ex f=1/sqrt(1-pow(x,2)/pow(c,2));  
ex sf=f.series(x,6);  
cout << sf << endl;  
}

Compile and run this program.

1.6 Exercises

1. Find the coefficient of x1 in Large

          x
f(x) = -x----
       e  - 1

2. Find the first ten derivatives of Large

f (x) = ---x---
        eax - 1
with respect to x.

3. Read through the GiNaC tutorial pages at http://talkitoaster.uwp.edu/290/GiNaC/ and write a short program performing a symbolic operation not discussed in this handout.