I am working on Maxima scripts that produce C code for calculating the
Einstein tensor, given a metric tensor (and possibly its derivatives) as input.
Maxima is an open source symbolic-math program. Maxima
can be obtained from Sourceforge here.
In order to compile Maxima you will need to download and install a lisp compiler, such
as clisp.
The Maxima documentation and
Maxima mailing list archives are both
maintained by the UT Math department.
Maxima already has a built-in package called CTENS that will calculate the Einstein tensor (and Christoffel symbols, inverse metric tensor, etc) for you! I am still writing my own scripts that will allow more flexibility (especially in regard to the different types of inputs the scripts will accept), but the built-in package is a nice way to check your work. Also, calculating the tensor is only half the battle. Creating C-code is the more difficult other half. Some of these implementation issues are discussed below.
Implementation Issues:
- Analytic Derivatives
Maxima performs these with much the same syntax
as Maple : diff(f,x,n) will return the nth dervivative of f with respect to x.
- Producing C code
Maxima currently only has the capability to produce Fortran code. However, it can output Maxima code, and the Maxima syntax is very similar to C syntax (single = for assignment, semicolon at the end of each line). However, a few issues still remain, most of which involve writing routines to substitute one syntax for another:
- Exponents
C will not accept something of the form x^a if either x or a is a non-integer. Maxima uses the x^a notation. Therefore, each occurence of x^a must be replaced with a call to the pow function. I found the following Maxima routine on the Maxima archives that will do this:
subpow(val):=
block([],
val: ev(val),
val: subst(["^"='pow,%e=euler],val));
However, this introduces a new issue, that being: often you will end up with a call to pow(x,-1), which is not preferable to simply 1/x. Also, a call to pow(x,2) is not preferable to x*x. A regular expression to fix the latter case is straightforward:
s/pow\(([A-Za-z]+),2\)/\(\1\*\1\)/g
However, to fix the former case is more complex, since nested calls to pow may be involved and a regex isn't powerful enough to count open and close parentheses. A more complicated search and replace will be required, but at this point hasn't been done because it is not vital.
An unexpected scenario also showed up, namely that a fraction exponent, such as 3/2, in C is treated as an integer (in this case, 3/2 is treated as 1). Maxima is quite fond of fractions, so I had to write another routine to replace all integers with reals, for example replacing 2 with 2.0, without modifying any variable names that may contain integers and without "doubling up" on the decimal point. So far the following regex has proven sufficient for this:
s/([^A-Za-z_0-9\.])([0-9]+)([^\.])/\1\2\.0\3/g
It ensures the digit isn't preceeded by any variable-legal characters and isn't followed by a decimal point (to avoid placing a second decimal point), and inserts a decimal in the appropriate place. I didn't realize it before, but even a negative match in a regex, such as [^\.] will be replaced in a search and replace. In other words, the trailing character, even if it isn't a decimal, will be lost unless you explicitly replace it (hence the reason I enclosed it in parens to make it "\3" and replaced it at the end).
- Uppercase
Maxima, when outputting Maxima code, likes to use uppercase letters. This is problematic, since C-code is case-sensitive and Maxima is not. Some variables are output in all uppercase. Also, transcendental function calls such as sin are output in uppercase. I have written a perl script to convert all variable names to lower case, which consists of applying a simple regular expression to each line. I have not yet resolved the issue of transcendental functions because it is not a pressing concern at this time.
- Derivative Notation
When performing an analytic derivative, sometimes a derivative must be left in df/dx form. When Maxima does this, it uses the notation:
which is an "unevaluated call" to the diff function. I have written a a perl script that takes this syntax and replaces it with a variable name like dtgzt (meaning the first derivative of gzt with respect to t). The script simply applies the following regexp to each line of code:
s/\'DIFF\(([A-Za-z]*),([A-Za-z]),1\)/d\2\1/g
If you have any advice on solving any of the above issues, or any other comments or suggestions, please email me (remove dashes from email address)
Return to index