MathCode Introduction

Introduction

MathCode is an application package which includes the following:

The performance of compiled generated C++ code is often approximately a factor of 1000 better than standard interpreted Mathematica, and often a factor of 100 better than code compiled using the internal Mathematica compiler.

The MathCode System

The MathCode application can easily be used just by executing the following command in Mathematica:

Needs["MathCode`"];

If you are compiling your own package, e.g. called foo, using MathCode, you also need to mention MathCodeContexts within the path of the package as below.

Code Generating Phase

The MathCode code generator translates a Mathematica package, here called foo, to a corresponding C++ source file, here called foo.cc. Additional files which are automatically produced are: the header file foo.h, the Math header file foo.mh which stores MathCode information, the MathLink related files footm.c, foo.tm, and fooif.cc which enable transparent calling of the C++ versions of functions in foo from Mathematica, and foomain.cc which contains the function main needed when building a stand-alone executable for foo.

Building Phase

The generated file foo.cc created from the package foo together with header file foo.h and additional files are compiled and linked into two executables. Numerical libraries may be included in the linking process.

Executing Phase

The produced executable foomain.exe can be used for stand-alone execution whereas fooml.exe is used when transparent calling from Mathematica via Mathlink of the compiled C++ functions in fooml.exe is desired.

MathCode Type Annotation

The MathCode system allows the user to associate type information with Mathematica variables and functions. This information is needed in order to generate efficient code in strongly typed languages like C++. Future versions of MathCode may support inference of some type information, but the current version requires specification of types for all variables and functions to be translated to C++.

Basic Types

The following basic types are supported by the current version of MathCode:

Support for these basic types are planned, but not yet implemented:

Declarations

Global Variables

The type of global variables is declared as follows:

Local Variables

Local variables are declared in a similar fashion, but within the body of a function definition:

Declared variables can be initialized by some initialization expression, just as in standard Mathematica.

Function Signatures

Function signatures of typed Mathematica functions are usually attached to the head of the function definitions. This does not change the behavior or performance of the Mathematica function, when executed interpretively within Mathematica, and is thus completely backwards compatible. The syntax has the following structure:

or for a one parameter function:

An example:

Arrays

A key data structure in Mathematica is the list structure. Nested list structures are commonly used to represent matrices and arrays. For example, a nested list {{2.1,3.1},{2.2,3.2}} is a two by two array of real numbers. The type of such (nested) list structures can be specified by array type annotations.

It is interesting to note that Mathematica internally implements lists as arrays. This has the advantage of providing constant time indexing operations.

Basic definition

Basic definitions of the variable x.

or shorter

Example

A three dimensional array of real numbers:

The sizes of array dimensions can be specified by values of integer variables, e.g. n and m below:

Unspecified Dimension Sizes

Typically, the sizes of arrays passed as function parameters or returned from a function is not known until the function is executed. Such unspecified array dimension sizes are indicated by the underscore (_) dimension placeholder. This new use of underscore is only valid within array type descriptors, as shown below:

Named dimension placeholders are local to the scope of the type.

Array sizes in function signatures

Named dimension placeholders makes it possible to document array dimension size constraints in function signatures. For example, the following function signature is used for a matrix multiplication function, which multiplies two matrix parameters amat and bmat, with type denoting the element type.

Dimension sizes of array arguments

Finding dimensions size at run time, e.g. for a function array parameter mat, can be done by calling the standard Mathematica function Dimensions indexed by 1 for the size first dimension, 2 for the second dimension, etc. If such dimension sizes are needed for the declaration of local arrays, e.g. localmat below, those local arrays must be declared in an inner Module or Block, as below:

Initialization of arrays in declarations

Elementwise initialization of a matrix by a scalar constant (general expressions currently not allowed) can be done for example as below for locally or globally declared variables:

which gives mat the following contents

(({{5., 5., 5.}, {5., 5., 5.}}))

Initialization by a constant matrix

Compilation to C++ code

MathCode provides facilities to compile typed Mathematica functions and variables to C++ code. Functions always reside in some package. If no package has been specified by the user, the default package Global is usually used. The compiler is invoked by calling CompilePackage. There are essentially two ways to compile functions:

The second way is used primarily to handle symbolic operations, e.g. symbolic integration, simplification, substitution, etc., which may be present in the function body to be compiled.

The default method of compiling typed functions is by normal compilation. Compilation with symbolic evaluation is used for functions mentioned in the list of names to the optional parameter EvaluateFunctions, and should only be used for functions which contain symbolic operations.

Calling the Code Generator

CompilePackage

The code generation is started by the command CompilePackage, e.g.:

which collects the variables and functions defined in the package context foo‘, i.e. corresponding to the symbols returned by Names["foo`*"] and for which there is a type definition.

By default MathCode compiles all typed functions and typed global variables within the package.

SetCompilationOptions

Additional information needed to guide the compilation process can be specified using optional parameters to CompilePackage, or by inserting calls to SetCompilationOptions within the package to be compiled.

Below we briefly examine the different items to be compiled, and some of the available options.

Compiling different items

Variable declarations

All typed global variables declared in a Mathematica to be compiled (e.g.  package foo) are translated to declarations in C++. Declarations are put into the header file foo.h and possible initialization code into the file foo.cc.

Functions

The default is to translate typed Mathematica functions into C++ without any symbolic evaluation. This produces target code similar to the original Mathematica code, i.e. loops in Mathematica become loops in C++, if-statements are still if-statements, etc. The main difference form the original Mathematica is, e.g.,  that iterators like Do, and Table are converted to for-loops in C++.

Functions with symbolic operations

Functions which contain symbolic operations cannot be directly translated to C++. Fortunately, in many cases symbolic operations can be eliminated by symbolic expansion. In this way, symbolic operations such as symbolic integration, derivation, series expansion, etc. can be performed by Mathematica before the final code generation stage. The resulting expression, which is assumed to contain only non-symbolic operations, is then passed to the code generator which performs common subexpression elimination to speed up and reduce the code size before finally translating to C++ code. For this to work reliably, the body of the “symbolic function” should not contain side-effects such as assignments to global variables or input/output.

For example, the function below contains a symbolic series expansion and a symbolic substitution:

Therefore, the function should be symbolically evaluated before final code generation, and be specified using the option EvaluateFunctions:

Main program function

In case a stand-alone executable should be created, the option MainFileAndFunction can be used to specify the C function main() needed in such an executable. The argument string specifies the text from which the file foomain.cc file is created. For example:

Building

The building process compiles all produced C++ files and links them into one (or two) executables.

The call MakeBinary[] builds all the files for both the stand-alone version of the application (e.g. foomain.exe), and for the interactively callable MathLink version (e.g. fooml.exe).

Installing

If compiled functions should be directly callable from within Mathematica, the code must be installed.

The call:

installs the binary fooml.exe into Mathematica. It first saves away the original interpreted functions and then creates function definition stubs of the compiled package in Mathematica. This enables calling the compiled functions from within Mathematica via MathLink.

Executing

Functions in the compiled and installed package can be executed by standard function call just like functions in any standard Mathematica package. If stand-alone execution is desired, just run the created stand-alone executable.

UnInstalling

When the compiled code should no longer be accessible and the MathLink connection be close down, UnInstallCode should be called:

This will restore the original interpreted version of the package, (called foo in the example).

Matrix Operations

In many engineering applications, matrices and matrix manipulation are very common. The availability of an easy-to-use and short-handed notation for manipulating matrices is important for these application domains. Thus, we have extended the Part ([[  ]])-operation in Mathematica to fulfill this objective.

The current basic set of matrix operations consists of operations on array sections. The syntax is inspired by the syntax used by Matlab and Fortran 90, and is supported by the MathCode generator for 1- and 2-dimensional matrices, and within Mathematica for arbitrary number of dimensions.

Create a small matrix A containing indexed symbols of the form a[i,j]:

A = Table[a[i, j], {i, 4}, {j, 5}]; A // MatrixForm

Below we extract row 2 and 3, using the Matlab-style notation A[[2|3,_]].

Compared to the standard Matlab syntax A(2:3,:) we have made a Mathematica compatible version by replacing colon as a binary range operator by vertical bar (|), and replacing colon as a placeholder for the whole range of a dimension by underscore (_).

A[[2 | 3]]  //  MatrixForm

Extraxt all but the first two columns, using A[[_, 3|_]] which corresponds to standard Matlab syntax A(:,3:). The notation 3|_ here means the range from the 3:rd to the last column.

A[[_, 3 | _]] // MatrixForm

Assign values to a submatrix of A:

A[[2 | 3, 2 | 3]]  = 
       {{1, 2}, {3, 4}};

A  //  MatrixForm

Implementing missing Mathematica functions

The MathCode translator directly supports a set of basic Mathematica functions and operations. There are still quite a number of standard Mathematica functions not yet included in this set. There are basically two ways to solve this problem:

Callbacks to Mathematica

Mathematica functions can be called from outside Mathematica if they are made back-callable via a CallBack declaration. For example, to be able call RotateLeft in Mathematica from generated code:

An example system.nb notebook

This particular notebook system.nb contains an example system package (note lower-case!) with an alternative implementation of the standard function RotateLeft, which earlier was not in the standard subset supported by the MathCode translator.

Initialization needed to use MathCode

Package header

Public Exported global symbols

Introduce symbols that should be exported outside the package.

End public section

Private implementation section

Define implementations of the functions and variables.

RotateLeft

Definition of RotateLeft:

End of private section

End of package

Compiling

Compile the system package into C++ code:

Building

Compile the C++ files to binaries and possibly link into an executable:

Install and test

RotateLeft test example.

Interfacing with external libraries

MathCode provides a mechanism to interface and call functions in external libraries and object modules which have been implemented in languages like Fortran, C, or C++.

Such functions need to be declared External, as for the Fortran subroutine fooext below, which has two input parameters and two output parameters:

Linking with external libraries

The object code of external libraries need to be linked with the generated code to make external functions callable. Additional parameters can be supplied to MakeBinary for this purpose:

Note that the object module name file3 in the above example would correspond e.g. to the object module named foo3.obj under Windows95, or foo3.o under Unix.

Alternatively, and usually more conveniently, options like NeedsExternalLibrary or NeedsExernalModule can be set by inserting calls to SetCompilationOptions into the package which needs to call the exernal functions, like in the package foo below:

Getting Started by Examples

The purpose of this section is to walk through some aspects of the MathCode system by showing complete application examples, to help the user become acquainted with some of the type declaration and code generation facilities.

The following applications will be presented:

The performance of the generated code is also measured. The performance figures shown have been obtained for Mathematica 3.0 under Windows95 on a Pentium 150 Mhz notebook computer. You should re-run these examples to obtain the correct performance figures for your platform.

This description is valid for execution under Sun Solaris on Sun Sparc workstations, Linux, and Windows95, which are the currently supported platforms for code generation.

Other facilities, such as type declarations and Matlab-like operations, works on all Mathematica supported platforms.

To be able to run the system, there must be a valid Mathematica license on your computer. Also, you must have installed the MathCode application package. See the manual for installation description in Chapter 9.

Compilation and code generation

As briefly mentioned in the introductory overview chapter, there are several options concerning the compilation and code generation facilities provided by MathCode for translation of typed Mathematica code:

These options are explained in more detail in the manual in Chapter 7 which covers code generation.

To use typed declarations and code generation facilities for functions and data structures in your own package, you always need to refer to the MathCode application by evaluating a Needs statement:

In order to invoke code generation functions from within your package, you also need to include the MathCodeLanguage context in the search path of your package, as below:

Two modes of code generation

The first example application is a rather contrived small Mathematica program called SinSurface, which has been designed to illustrate the two basic modes of the code generator: compiling without symbolic evaluation, which is performed by the code generation routine CompileFunction and compilation preceded by symbolic expansion, performed by CompileEvaluateFunction.

The result of the symbolic operations are expressions that should contain only non-symbolic, typically numeric, operations. This is typically the case since Mathematica always evaluates as far as possible. Thus, the function body is expanded (and simplified) into a usually huge symbolic expression before being transformed into C++ (or Fortran90) code. The result is rather unrecognizable compared to the original Mathematica function since both symbolic expansion and optimizations such as common subexpression elimination have been performed.