Interaction with Mathematica
Wolfram
LibraryLink allows dynamic libraries to be directly loaded into the
Mathematica kernel so that functions in the libraries can be immediately called from
Mathematica. You can exchange not only C-like data types such as integers, reals, packed arrays, and strings, but also arbitrary
Mathematica expressions. In addition, there are useful functions such as sending errors and calling back to
Mathematica.
This section describes the functions that
Mathematica provides for working with Wolfram Libraries.
Mathematica functions for working with libraries.
LibraryFunctionLoad loads a function from a library and returns a
LibraryFunction.
Out[1]= | |
Here the
LibraryFunction is called with an integer argument.
Out[2]= | |
LibraryFunctionInformation returns information about the
LibraryFunction, such as the library from which it was loaded, as well as the argument and return types.
Out[3]= | |
Library Specification
The first argument to
LibraryFunctionLoad is the library to load. This can be given as an absolute file name such as C:\Libraries\myLibrary.dll. However, it is often more convenient to give a relative specification to the library, finding it from a path. Another problem is that different platforms use different file name extensions for libraries. The conventions are summarized in the following table.
Windows | dll |
Unix and Linux | so |
Mac OS X | dylib |
Extensions for dynamic libraries on different systems.
Mathematica provides a solution to this with
FindLibrary and
$LibraryPath.
FindLibrary can take a platform-independent specification of a library, look for it on
$LibraryPath, and, if it finds the library, return the actual file for your system.
The following searches on
$LibraryPath and finds the library suitable for your platform; in this example it is Windows.
Out[4]= | |
More information on how
$LibraryPath is set up is found in
"Locating Libraries".
Function Name
The second argument to
LibraryFunctionLoad gives the name of the function to load. This function should be exported from the library, as described in
"Library Structure". Note that if you compile the library as C++ you should probably export it with a C naming convention; this is also described in
"Library Structure".
Type Specification
The third and fourth arguments to
LibraryFunctionLoad specify the types of the arguments and the return type.
"Boolean" | mbool | Boolean |
Integer | mint | machine integer |
Real | double | machine double |
Complex | mcomplex | machine complex double |
{base,rank} | MTensor | tensor of specified base type and rank |
{base,rank,memory} | MTensor | tensor with specified memory management |
"UTF8String" | char * | UTF8 string |
LinkObject | MLINK | arguments and result written to MathLink |
"Void" | | no result (return only) |
Argument and return type specification.
The types that specify tensors are designed to map directly to
Mathematica packed arrays and also to use of tensors in the
Mathematica compiler. This gives the system great efficiency and allows libraries access to many tensor operations. Tensors can specify the type of each element and the rank explicitly, or they can be left unspecified. Unspecified types and rank give a lot of flexibility for applications that work with tensors. Note that tensors are only supported for
Integer,
Real, and
Complex.
{Integer, 1} | tensor of specified base type and rank |
{Real, _} | tensor with specified base type and arbitrary rank |
{_,_} | tensor with arbitrary base type and arbitrary rank |
Tensor type specification.
When a tensor is passed as an argument you can also specify how its memory is handled. This is discussed in detail in the next section.
In the code of your library function you collect the data from each type from the argument array. Macros for
MArgument have been provided to make this collection easy and straightforward. Sample code is shown below.
mint I0 = MArgument_getInteger(Args[0]);
double R0 = MArgument_getReal(Args[1]);
mcomplex C0 = MArgument_getComplex(Args[2]);
MTensor T0 = MArgument_getMTensor(Args[3]);
When you return from the library function, you must assign to the result. A sample that stores a double is shown below.
MArgument_setReal(Res, R1);
If you specify tensor input but with arbitrary type and rank, you can find the actual type and rank with the
callback functions, as in the following.
type = libData->MTensor_getType( T0);
rank = libData->MTensor_getRank( T0);
MTensor_getType returns an integral value that represents the type of the result.
MType_Integer | an MTensor that contains machine integers |
MType_Real | an MTensor that contains machine doubles |
MType_Complex | an MTensor that contains complex numbers (real and imaginary part being machine doubles) |
Results for MTensor_getType.
If the library function takes no arguments then you simply pass an empty list for the input specification.
Out[7]= | |
Out[8]= | |
If the library function does not return a result, you can use a return specification of
.
Out[9]= | |
Memory Management of MTensors
Types such as mint, double, and mcomplex are passed to and returned from libraries by value, as is common for calling C functions. The usage in the library is really independent of that in
Mathematica.
By contrast, an
MTensor is a pointer to a data structure, and thus they are passed by reference. A consequence is that you have to think about how its memory is managed.
Mathematica chooses a default technique that is safe and simple, but if you want to pass in large amounts of data or save it for future usage then you need to think about its management.
MTensor Input Arguments
When you pass an
MTensor to a library function you have a number of options that determine how this is done.
{Integer, 1} | pass in a copy of the MTensor and automatically clean |
{Integer, 1, Automatic} | pass in a copy of the MTensor and automatically clean |
{Integer, 1, "Constant"} | pass in a reference to the MTensor that should not be modified |
{Integer, 1, "Manual"} | pass in a copy of the MTensor and do not automatically clean |
{Integer, 1, "Shared"} | pass in a reference to the MTensor shared between a library and Mathematica |
Memory management possibilities for MTensor arguments to library functions.
Automatic Passing
If you select automatic passing this means that the
MTensor is copied before the function is called and it will be cleaned when the function returns. This would be suitable for a function such as the following.
DLLEXPORT int demo_TI_R(WolframLibraryData libData,
mint Argc, MArgument *Args, MArgument Res) {
MTensor T0;
mint I0;
double R0;
T0 = MArgument_getMTensor(Args[0]);
I0 = MArgument_getInteger(Args[1]);
R0 = libData->MTensorVector_getReal(T0, I0);
MArgument_setReal(Res, R0);
return LIBRARY_NO_ERROR;
}
In this function there is no need to free the
MTensor. However, you could not save a reference to the
MTensor and use it once the function has finished.
An
MTensor passed with automatic passing is owned for both read and write access by the library function only as long as the function call is active.
Constant Passing
If you select constant passing, this means that a reference to the
MTensor is passed in and it is assumed that your function will not modify the
MTensor in any way. This effectively gives you very fast read-only access to the
MTensor data. If your code breaks this assumption and does modify the data, a grave error could result in your
Mathematica session.
An
MTensor passed with constant passing is owned by the library function for read access only as long as the function call is active.
Manual Passing
If you select manual passing this means that the
MTensor is copied before the function is called and it is not cleaned when the function returns. This would be suitable for a function such as the following.
DLLEXPORT int demo1_TI_R(WolframLibraryData libData,
mint Argc, MArgument *Args, MArgument Res) {
MTensor T0;
mint I0;
double R0;
T0 = MArgument_getMTensor(Args[0]);
I0 = MArgument_getInteger(Args[1]);
R0 = funStruct->MTensorVector_getReal( T0, I0);
libData->MTensor_free(T0);
MArgument_setReal(Res, R0);
return LIBRARY_NO_ERROR;
}
Note how the function frees the
MTensor. If it were not freed, the memory would be lost. However, instead of freeing the tensor, you could save the tensor and use it in some other part of the library. Finally, if you finish with the tensor you should call
MTensor_free. An alternative would be to return it from a library function to
Mathematica in which ownership would pass back to
Mathematica.
An
MTensor passed with manual passing is completely owned by the library; this continues until the
MTensor is freed or passed back to
Mathematica.
Shared Passing
If you select shared passing this means that the
MTensor is not copied before the function is called, it is just passed directly to the library function. This would be suitable for a function such as the following.
DLLEXPORT int demo2_TI_R(WolframLibraryData libData,
mint Argc, MArgument *Args, MArgument Res) {
MTensor T0;
mint I0;
double R0;
T0 = MArgument_getMTensor(Args[0]);
I0 = MArgument_getInteger(Args[1]);
R0 = funStruct->MTensorVector_getReal( T0, I0);
libData->MTensor_disown( T0);
MArgument_setReal(Res, R0);
return LIBRARY_NO_ERROR;
}
When you use shared pass in arguments, the
MTensor is shared between
Mathematica and your library.
Mathematica keeps the
MTensor in a table to make sure that its memory does not get collected. When your library no longer wants to use the
MTensor, you should call
MTensor_disown.
An
MTensor passed with shared passing is shared between the library and
Mathematica. It will be kept alive until the library and
Mathematica have finished all usages.
This loads a sample function using
to pass in the arguments.
Out[1]= | |
This creates a packed array suitable for using as an argument for the function.
Out[3]= | |
Here you can call the function passing in the array.
Out[4]= | |
However, if you call the function with an argument that is not a packed array, the call will work, but you will get a warning message. This is because
Mathematica had to convert the input into a packed array, thereby copying the data and losing one of the advantages of using shared passing.
Out[6]= | |
MTensor Return
When you return an
MTensor from a library function you also control how its memory is managed.
{Integer, 1} | return a reference to the MTensor for Mathematica |
{Integer, 1, Automatic} | return a reference to the MTensor for Mathematica |
{Integer, 1, "Shared"} | return a reference to the MTensor shared between a library and Mathematica |
Memory management possibilities for MTensor results from library functions.
Automatic Return
If you select automatic return this means that the
MTensor goes directly back from the library to
Mathematica, which will use it as the result of the library function.
DLLEXPORT int demo_I_T(WolframLibraryData libData,
mint Argc, MArgument *Args, MArgument Res) {
MTensor T0;
mint i, I0, rank, dims[1];
I0 = MArgument_getInteger(Args[0]);
dims[0] = I0;
T0 = libData->MTensor_new(MType_Integer, 1, dims);
for ( i = 1; i <= I0; i++) {
libData->MTensorVector_setInteger( T0, i, i*2);
}
MArgument_setMTensor(Res, T0);
return LIBRARY_NO_ERROR;
}
If the
MTensor is owned by the library, i.e. it was created in the library or passed in with manual passing, then once it has been returned to
Mathematica it is no longer owned by the library, which should not make any usage of it whatsoever. This is shown in the function above.
If the
MTensor is shared between the library and
Mathematica then automatic return does not change anything in the ownership of the
MTensor. However, it is rather strange to return a shared
MTensor from a library since
Mathematica already has a reference to it.
Shared Return
If you select shared return this means that the
MTensor will be shared between the library and
Mathematica. Technically this is implemented by adding it to the sharing table when the function returns. This makes sure the
MTensor does not get collected. When your library no longer wants to use the
MTensor, you should call
MTensor_disown (or
MTensor_disownAll).
Note that if you call
MTensor_disown (or
MTensor_disownAll) before the
MTensor has been added to the sharing table it will have no effect and a warning message will be issued.
MTensor Memory Management Summary
One way to understand the three different types of memory management for an
MTensor is to consider how the
MTensor is "owned" between the different components.
For automatic passing, the
MTensor is owned by the library function only when that call to the library is active. The function can return the
MTensor whether it has been modified or not, but whatever happens, the
MTensor cannot be used after the function has returned.
For manual passing, the
MTensor is owned by the library after the function has been called. The
MTensor can be used at any time after the function, for example, in another function. The
MTensor will continue to be owned by the library until either it is returned by a library function or it is passed to a call to
MTensor_free. If you want to return an
MTensor that was passed in with manual passing but keep ownership in the library, you will have to copy it with a call to
MTensor_clone or return it from a function that uses shared return.
For shared passing and return, ownership of the
MTensor is shared between
Mathematica and the library. If the
Mathematica expression that holds the packed array for the
MTensor can no longer be reached,
Mathematica will no longer have ownership. The library will also keep ownership until you have called
MTensor_disown on the
MTensor. Note that you have to call
MTensor_disown on an
MTensor as many times as you have passed it into and returned from a function. For example, if you pass the same packed array into a library three times, then you need to call
MTensor_disown three times. The function
MTensor_disownAll can be useful to remove all references, and the function
MTensor_shareCount gives the actual number of times the
MTensor is shared.
A final thing to remember is that if any errors arise these might transfer control away from the parts of your library function that free memory. In this case you might want to insert your own error handler; this is discussed in the
section on errors.
String Arguments
String arguments are passed in with characters encoded using UTF8. When a string is passed as an argument, memory management for the string is left completely up to the program, similar to the manual passing for an
MTensor.
DLLEXPORT int countSubstring(WolframLibraryData libData,
mint Argc, MArgument *Args, MArgument Res)
{
char *instring = MArgument_getUTF8String(Args[0]);
char *substring = MArgument_getUTF8String(Args[1]);
mint i, n = strlen(instring);
mint slen = strlen(substring);
mint c = 0;
if (n > slen) {
n -= slen;
for (i = 0; i <= n; i++) {
if (!strncmp(instring + i, substring, slen)) {
c++;
}
}
}
MArgument_setInteger(Res, c);
libData->UTF8String_disown(instring);
libData->UTF8String_disown(substring);
return LIBRARY_NO_ERROR;
}
Note how the memory for both string arguments is released using
UTF8String_disown. If this is not done and you do not otherwise keep a reference to the string argument in your program, the memory will simply be lost. When a string is returned as a result,
Mathematica accesses the memory to convert it to its own internal string format, but does not attempt to free the memory. Thus, if your program allocates a string for a result, it also needs to free that memory, but in a separate function from the one setting the string result, since
Mathematica needs to access the string memory after the library function has returned.
LinkObject Arguments and Result
If you want to send arguments or get results from the library function that are not covered by the
MTensor basic numerical types, you can use a
LinkObject for arguments and the result. This will let you send the structure of any
Mathematica expression to your library.
For
LinkObject arguments and the result, the library is called in a different way than for basic numerical types. When the function is called,
Mathematica writes a list with all the arguments onto a
MathLink connection; this is done with
LinkWrite, the way
Mathematica writes expressions over
MathLink. Then it calls the library function. The library function must read the arguments from the link, do its work, and then write its result back onto the link.
Mathematica then reads the result from the link with
LinkRead, the way that
Mathematica reads expressions from
MathLink.
An example of a function that works for
LinkObject arguments and result is shown below.
DLLEXPORT mint reverseString( WolframLibraryData libData, MLINK mlp)
{
mint res = 0;
int i1, i2, sum;
int len;
const char *inStr = NULL;
char* outStr = NULL;
if ( !MLCheckFunction( mlp, "List", &len))
goto retPt;
if ( len != 1)
goto retPt;
if(! MLGetString(mlp, &inStr))
goto retPt;
if ( ! MLNewPacket(mlp) )
goto retPt;
outStr = reverseStringImpl(inStr);
res = MLPutString( mlp,outStr);
retPt:
if ( inStr != NULL)
MLReleaseString(mlp, inStr);
if ( outStr != NULL)
free(inStr);
return res;
}
This loads a sample function that uses
LinkObject arguments and result.
Out[7]= | |
Out[8]= | |
To use
MathLink for communicating with
Mathematica, the "mathlink.h" header must be included before the "WolframLibrary.h" header.
Locating Libraries
The first argument to
LibraryFunctionLoad is the library to load. This can be given as an absolute file name such as C:\Libraries\myLibrary.dll. However, it is more convenient to give a relative specification to the library, finding it from a path. Also, since different platforms use different extensions for dynamic libraries, it can also be a problem to give the extension. The conventions for extensions for dynamic libraries are summarized in the following.
Windows | dll |
Unix and Linux | so |
Mac OS X | dylib |
Extensions for libraries on different systems.
Functions in
Mathematica that work with dynamic libraries automatically solve this problem. If the input name does not have a file extension, then one suitable for your platform is added. This lets you work with libraries in ways that are independent of the machine on which you work. In addition,
Mathematica provides a path,
$LibraryPath, to use to find libraries.
A sample setting for
$LibraryPath is shown below. Note that it includes a number of
Mathematica applications that contain libraries.
Out[1]= | |
FindLibrary is the function that finds libraries. It is called by other commands such as
LibraryFunctionLoad.
FindLibrary first fixes the extension of the library, then looks for the library as an absolute name, and finally looks for the library on
$LibraryPath. It returns the file if it is found.
The following searches on
$LibraryPath and finds the library suitable for your platform; in this example it is Windows.
Out[9]= | |
You can also use a name with an extension, but note that this will only work on a platform for which the extension is appropriate.
Out[10]= | |
You can add elements to
$LibraryPath. However, a number of folders are automatically added; this includes any
LibraryResources folder found in a
Mathematica application in
$UserBaseDirectory/Applications or
$BaseDirectory/Applications. This is similar to the way that
J/Link can load Java classes, and
DatabaseLink can load database resources. It gives a convenient way to bundle libraries with your
Mathematica work.
Installing Your Own Libraries
If you want to add dynamic libraries to use in your
Mathematica session you have a number of options. These are summarized here.
Absolute Pathname
You can assign an absolute pathname to a function such as
LibraryFunctionLoad. This is easy to set up, but will cause problems if you move your work to a different computer.
Setting $LibraryPath
You can add the location of your libraries to
$LibraryPath. This gives more abstraction over the use of an absolute pathname, since you can set the location once in a variable and use it many times.
In addition, you can use
Block to temporarily change the setting of
$LibraryPath. An example is shown below.
$BaseDirectory and $UserBaseDirectory
$LibraryPath always includes two locations,
$UserBaseDirectory/SystemFiles/LibraryResources/$SystemID and
$BaseDirectory/SystemFiles/LibraryResources/$SystemID. If you place your library here it will be found by library loading functions.
This shows the location in
$BaseDirectory.
Out[3]= | |
Applications
If you deliver your code as a
Mathematica application you can also include libraries that will be placed on
$LibraryPath. You should include a
LibraryResources directory with a folder that matches
$SystemID. A sample application is shown below.
MyApplication
MyApplication.m
Kernel
init.m
FrontEnd
Documentation
English
LibraryResources
Windows
libraries for use on Windows
Windows-x86-64
libraries for use on 64 bit Windows
Linux
libraries for use on Linux
Linux-x86-64
libraries for use on 64 bit Linux
MacOSX-x86
libraries for use on MacOSX
MacOSX-x86-64
libraries for use on 64 bit MacOSX
Library Dependencies
If your library depends on other libraries you will need to make sure that the dependent libraries are available. You could do this by changing the environment in which
Mathematica runs to install these extra libraries in some system location, or by changing a path environment variable such as LD_LIBRARY_PATH (for Linux) or PATH (for Windows).
An alternative is to use
LibraryLoad to load the dependent libraries before loading your own library.
LibraryLoad does not return a function (unlike
LibraryFunctionLoad), it merely exists to load dependent libraries.
Under Mac OS X, dynamic libraries (which have a file extension of dylib) have a more complicated mechanism where they can be set up to search in particular places for dependent libraries. You should consult the description of commands such as
otool and
install_name_tool.
If you have trouble loading a library, you can use
$LibraryError to find out more about why it did not load. In some cases this returns information about which dependent libraries cannot be loaded.
Library Version Information
You can get information about the version of a library with the
LibraryLink Package. This supplies some extra tools for working with shared libraries.
To use the package you have to load it.
Out[2]= | |
Here,
LibraryVersionInformation returns a list of rules that shows information about the library.
Out[3]= | |
You can also get the version information as a string with
LibraryVersionString, as shown in the following.
Out[4]= | |
Problems Loading a Library
If you have problems loading a library, you might want to use
$LibraryError to find out more about why it did not load. This is found in the
LibraryLink` context, but you do not have to load the package to use it.
Here, a library used for calendar calculations is loaded.
Out[1]= | |
The library has dependencies on other libraries. Normally,
Mathematica will load the other libraries first. However, if you just try to load the calendar library directly (and you have not already used a calendar function) this load instruction will fail, as in the following.
You can use
$LibraryError to find out more about the error, as shown below.
Windows does not give the names of dependent libraries that cannot be found. On other platforms this information is available.