How to call Mathematica functions from the interpreter
It is possible to invoke functions from libraries in an interactive
Root session. However, because the Mathematica libraries have not been
compiled
with the compiler option Position Independent Code, it is not
possible
to call these routines from a shared library. One must build a main
program
or libraries with explicit references to the list of functions that one
intend to call
from the interpreter.
In the first method one directly links to the Mathematica function.
The procedure to follow is the following:
1. Create files to be processed by rootcint
The file MathematicaFunctions.h below must include the list of
prototypes
for the functions to be called. In this example, the function callMathematicaGamma(x)
is declared. Add as many such lines than you have functions.
MathematicaFunctions.h
#ifndef MathematicaFunctions_H
#define MathematicaFunctions_H
int startMathematica();
void stopMathematica();
double callMathematicaGamma(double x);
#endif // MathematicaFunctions_H
We also define the functions startMathematica()
and stopMathematica()
which are needed in order to avoid the time-consuming restarting of the
Mathematica engine each time the function is evaluated.
MathematicaFunctions.cxx
#include "/afs/cern.ch/project/parc/math50/AddOns/MathLink/DeveloperKit/Linux/CompilerAdditions/mathlink.h"
#include "MathematicaMacros.h"
#include <string>
#include <iostream>
MLINK lp;
MLEnvironment env;
int startMathematica() {
env = MLInitialize(NULL);
if (env == NULL) return 1;
int argc = 4;
char *argv[5] = {static_cast<char*>("-linkname"), static_cast<char*>("/afs/cern.ch/project/parc/math50/bin/math -mathlink"), static_cast<char*>("-linkmode"), static_cast<char*>("launch"), NULL};
lp = MLOpen(argc, argv);
if (lp == NULL) return 1;
std::cout << "Started Mathematica Engine..." << std::endl;
return 0;
}
void stopMathematica() {
MLClose(lp);
MLDeinitialize(env);
}
double callMathematicaGamma(double x) {
double result;
MLPutFunction(lp, "Gamma", 1);
MLPutDouble(lp, x);
MLEndPacket(lp);
while (MLNextPacket(lp) != RETURNPKT) MLNewPacket(lp);
MLGetDouble(lp, &result);
return result;
}
The file MathematicaMacros.h can be downloaded here. It contains the macro definitions
needed for Mathematica. If you ever need to generate it for your
platform you can do it the following way:
touch MathematicaMacros.h
/afs/cern.ch/project/parc/math50/AddOns/MathLink/DeveloperKit/Linux/CompilerAdditions/mprep \
-prototypes MathematicaMacros.h > MathematicaMacros.h
Then you might add ifdefs protecting against multiple inclusion and
commenting out the mathlink.h include because it might confuse rootcint. In principle, you could
run the preprocessor on the file referencing the Mathematica library.
However, this is not a good practice for several reasons. One is that
your file gets heavily modified so you will need to always keep a copy
without the macro definitions and preprocess the file after every
change. Two, if you have several files referencing the Mathematica
library you will end up with multiple definitions of the same macro.
So, it is better to preprocess an empty file once for all and include
it when needed.
The file MathematicaLinkDef.h below includes the directives for
rootcint.
It declares the list of functions callable from the interpreter.
MathematicaLinkDef.h
#ifdef __CINT__
#pragma link C++ function startMathematica();
#pragma link C++ function callMathematicaGamma(double );
#pragma link C++ function stopMathematica();
#endif
We are now ready to generate the dictionary and interface function for
the interpreter (here called Mathematica.cxx). Execute the following
shell script
command:
rootcint -f Mathematica.cxx -c MathematicaFunctions.h MathematicaLinkDef.h
2. Make your own main program
As an example of main program, you can copy the standard Root main
program
and add to it the references to the Mathematica functions as
illustrated in
the example below:
rootMathematica.cxx
//////////////////////////////////////////////////////////////////////////
// //
// RMain //
// //
// Main program used to create RINT application with Mathematica support//
// //
//////////////////////////////////////////////////////////////////////////
#include "TRint.h"
#include "MathematicaFunctions.h"
//______________________________________________________________________________
int main(int argc, char **argv)
{
if (0) {
// dummy block to force the Mathematica functions to be linked by C++
callMathematicaGamma(1.0);
}
// Create an interactive ROOT application
TRint *theApp = new TRint("Rint", &argc, argv);
// and enter the event loop...
theApp->Run();
delete theApp;
return 0;
}
3. Compile and link
Modify your Makefile to include the files above and reference the
libraries.
Below is an example valid on Linux with the g++ compiler. Examples
of
linking a Root application are given for your platform in
${ROOTSYS}/test/Makefile. This example references the Mathematica software on the CERN afs
cell. Please update this reference to your own installation of Mathematica.
g++ -I $ROOTSYS/include \
-I/afs/cern.ch/project/parc/math50/AddOns/MathLink/DeveloperKit/Linux/CompilerAdditions \
rootMathematica.cxx Mathematica.cxx MathematicaFunctions.cxx \
/afs/cern.ch/project/parc/math50/AddOns/MathLink/DeveloperKit/Linux/CompilerAdditions/libML.a \
-o rootMathematica `root-config --glibs` -lrt
This command invokes the C++ compiler and creates an executable module
called rootMathematica. Your path to Mathematica may vary
depending on your system. We are now ready to execute this interactive
module.
The example below shows a few commands typed in an interactive
session.
./rootMathematica
root [0] startMathematica()
Started Mathematica Engine...
(int)0
root [1] callMathematicaGamma(5.0)
(double)2.40000000000000000e+01
root [2] callMathematicaGamma(5.45)
(double)4.83037558000228415e+01
root [3] stopMathematica()
root [4] .q
WARNING! If you use
Mathematica this way, normally the Mathematica process will be shut
down anyway when you quit ROOT. However, if you use Mathematica from a
separate wrapper or shared library, make sure that you stop Mathematica
at the end of your application as Mathematica licences might be limited
(as is the case at CERN) and restarting ROOT might create each time a
new process eating up the licences!