Thursday, April 21, 2011

Mathematica as a C code generator

Mathematica is able to generate C code ready to be compiled and ultimately capable to produce a stand alone executable file. Nice !. There are restrictions on the extent of what can be be processed in this way but it can be really useful in practice as it is right now.

The generation of the executable can be done within Mathematica or outside Mathematica. The Mathematica 8.0 help for my Linux Fedora 64bits requires a minor adjustment in order to work. For Linux systems:

"Libraries" -> "WolframRTL_Static_Minimal.lib"

must be replaced by

"Libraries" -> "WolframRTL_Static_Minimal"


(Thanks to Abdul Dakkak for helping on this)

How do we compile the generated code outside Mathematica? This issue is detailed next



  •  The Mathematica code that generates the C code for a simple example taken from the help is

code =
  CProgram[
   CInclude["stdio.h"],
   CInclude["compute.h"],
   CFunction["int", 
    "main", {{"int", "argc"}, {"char", 
      CDereference[CArray["arg", {}]]}},
    CBlock[{
      CDeclare["double", CAssign["num1", 20.4]],
      CDeclare["double", "num2"],
      CDeclare["WolframLibraryData", 
       CAssign["libData", 
        CCall["WolframLibraryData_new", {"WolframLibraryVersion"}]]],
      CCall["Initialize_compute", {"libData"}],
      CCall["compute", {"libData", "num1", CAddress["num2"]}],
      CCall["printf", {CString["%5.2f\n"], "num2"}],
      CCall["WolframLibraryData_free", {"libData"}],
      CReturn[0]}]]
   ];
cStr = ToCCodeString[code]

mainSource = Export[FileNameJoin[{outDir, "main.c"}], cStr, "Text"]

c = Compile[{{x}}, x^2];
CCodeGenerate[c, "compute", FileNameJoin[{outDir, "compute.c"}], 
  "CodeTarget" -> "WolframRTL"];
CCodeGenerate[c, "compute", FileNameJoin[{outDir, "compute.h"}], 
  "CodeTarget" -> "WolframRTLHeader"];

where
outDir
must be defined according the user's desires


  • The generated main.c file with a minor manual retouching in the printf function is


#include "compute.h"

#include "WolframRTL.h"


int main(int argc, char *arg[])
{
double num1 = 20.4;

double num2;
WolframLibraryData libData = WolframLibraryData_new(WolframLibraryVersion);
Initialize_compute(libData);

compute(libData, num1, &num2);
printf("%5.2f\n", num2);

WolframLibraryData_free(libData);
return 0;
}



  • The generated file compute.c is

#include "math.h"

#include "WolframRTL.h"

static WolframCompileLibrary_Functions funStructCompile;

static mbool initialize = 1;

#include "compute.h"

DLLEXPORT int Initialize_compute(WolframLibraryData libData)
{

if( initialize)
{
funStructCompile = libData->compileLibraryFunctions;
initialize = 0;
}

return 0;
}

DLLEXPORT void Uninitialize_compute(WolframLibraryData libData)
{
if( !initialize)
{

initialize = 1;
}
}

DLLEXPORT int compute(WolframLibraryData libData, mreal A1, mreal *Res)
{

mreal R0_0;
mreal R0_1;
R0_0 = A1;
R0_1 = R0_0 * R0_0;
*Res = R0_1;

funStructCompile->WolframLibraryData_cleanUp(libData, 1);
return 0;
}


  • The generated file compute .h is

#include "WolframLibrary.h"

EXTERN_C DLLEXPORT int Initialize_compute(WolframLibraryData libData);

EXTERN_C DLLEXPORT void Uninitialize_compute(WolframLibraryData libData);

EXTERN_C DLLEXPORT int compute(WolframLibraryData libData, mreal A1, mreal *Res);



  • Finally, the Makefile that takes care of the compilation is

CXX = /usr/bin/c++

MATHEMATICA_INCLUDE_PATH = /usr/local/Wolfram/Mathematica/8.0/SystemFiles/IncludeFiles/C

MATHEMATICA_LIB_PATH = /usr/local/Wolfram/Mathematica/8.0/SystemFiles/Libraries/Linux-x86-64


a.out: main.c compute.o
 ${CXX} -I${MATHEMATICA_INCLUDE_PATH} -L${MATHEMATICA_LIB_PATH} -lm -lWolframRTL -lmkl_intel_lp64 -lmkl_intel_thread -lmkl_core -liomp5 -pthread compute.o main.c

compute.o: compute.c
 ${CXX} -c -I${MATHEMATICA_INCLUDE_PATH}  -L${MATHEMATICA_LIB_PATH}  compute.c 



Of course, make sure that the libraries are in the right place and eventually you will need to add the path of the Mathematica libraries in  ld.so.conf and apply /sbin/ldconfig





  • A more elaborated example taking a real matrix input and a real matrix output is here
  • A similar example implemented for complex matrices here

1 comment:

  1. Thanks for the insight. Is there any way I can contact you if I need something further on Mathematica. Also have you ever used Mathematica to run any Digital Signal Processor (DSP). Your help will be greatly appreciated.

    ReplyDelete