I want to use C++ Eigen library in Mathematica Librarylink function.
Currently, I have no problem dealing with real arrays.
But I don't know what is the best way to deal with complex array in Librarylink in terms of C++.
So it is a C++ question in essence, but it is also a librarylink question.
In mma librarylink, the header file WolframLibrary.h
provide type mcomplex
, it is actually a structure which has length 2 real array representing real and imaginary part respectively, it is defined as
typedef struct {mreal ri[2];} mcomplex;
I don't know whether it is standard or not, but it seems awkward, and more importantly it is not directly compatible with Eigen.
While in C++, there is standard header <complex>
. After including this header, we can define complex number simply as
std::complex<double> cc(1.0,2.0);
Eigen library directly compatible with this complex type. For example, we could do the following
#include <iostream>
#include <complex>
#include <Eigen/Dense>
using namespace std;
using namespace Eigen;
int main(){
complex<double> *cc; //define complex double pointer
Matrix2cd m=Matrix2cd::Random(); //Eigen 2x2 complex matrix object
cc=m.data(); //m.data() return a C array pointer and assign it to cc
for(int i=0;i<4;i++)
cout<<cc[i]<<endl; //output cc array, the data that cc points to
//is the same as Eigen object m
}
Now I give an example of using C++ Eigen in librarylink function, served for testing and reference.
libnewsource = "#include<Eigen/Dense>
#include<Eigen/LU>
#include \"WolframLibrary.h\"
DLLEXPORT mint WolframLibrary_getVersion(){return \
WolframLibraryVersion;}
DLLEXPORT int WolframLibrary_initialize( WolframLibraryData \
libData) {return 0;}
DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData \
libData) {}
EXTERN_C DLLEXPORT int eigeninverse(WolframLibraryData libData, \
mint Argc, MArgument *Args, MArgument Res) {
using namespace Eigen;
int err; // error code
//input-------------
MTensor m1;
double *ptm1;
const mint *dim;
//output------------
MTensor outT;
double *out;
//input------------------
m1 = MArgument_getMTensor(Args[0]);
ptm1=libData->MTensor_getRealData(m1);
dim=libData->MTensor_getDimensions(m1);
//output--------------------
err = libData->MTensor_new(MType_Real, 2, dim, &outT);
out=libData->MTensor_getRealData(outT);
Map<MatrixXd> eigenm(ptm1,dim[0],dim[1]); //use Map to convert \
array pointed by ptm1 to Eigen matrix type
Map<MatrixXd>(out,dim[0],dim[1])=eigenm.inverse(); \
//use Map to convert Eigen matrix into out array
MArgument_setMTensor(Res,outT);
return LIBRARY_NO_ERROR;
}";
Now we can load it with below code
LibraryFunctionUnload[eigeninverse];
libnew = CreateLibrary[libnewsource, "eigeninverse","CompileOptions"->"-x c++"];
eigeninverse =
LibraryFunctionLoad[libnew, "eigeninverse", {{Real, 2}}, {Real, 2}];
About compiler setting:
For mingw user: make sure to set compiler as x86_ 64-w64-mingw32-g++.exe
, not x86_64-w64-mingw32-gcc.exe
. This is what I use in init.m
file
CCompilerDriver`$CCompiler=
{"Compiler"->GenericCCompiler,
"CompilerInstallation"->"C:/mingw-w64/mingw64",
"CompilerName"->"x86_64-w64-mingw32-g++.exe",
"CompileOptions"->"-O2 -x c"}
To notice there is "CompileOptions"->"-O2 -x c"
. -x c
is g++ option to treat file as c. I found without this option, normal Compile
will give errors like
"The function compiledFunction0 was not loaded from the file "somepath/.../compiledFunction0.dll "
And to create librarylink function that written in C++, you should add CompileOptions"->"-x c++
to CreateLibrary
to threat file as C++.
My Question
What is the best way to transform the above code to support complex matrix. The problem mainly focus on how to convert efficiently between Mathematica mcomplex
and C++ standard complex<double>
. And I personally feel that dealing with real and imaginary part separately kind of awkward. I don't know whether it is possible or not that we modify WolframLibrary.h
to make it support C++ standard?
std::complex<double>
is guaranteed. It is identical tomcomplex
. Thus you canreinterpret_cast
one to the other when needed. $\endgroup$reinterpret_cast
$\endgroup$mcomplex
, ismcomplex
also a standard? $\endgroup$reinterpret_cast<new_type>(value)
castsvalue
into the typenew_type
much as(new_type) value
would have in C. By "identical to mcomplex" Szabolcs just means that there isn't a difference in how the two are stored in memory, only in how they are handled. Both are stored of a pair of double-values in memory; because of this, just reinterpreting one as the other will work (it wouldn't work if, for example, one of them used float and the other used double). $\endgroup$