Skip to main content

programming - How to debug the C code for LibraryLink?


Recently, I use the C to rewrite a Mathematica function NonzeroBasis[], please see here for fully descriptions.


Here, NonzeroBasis[i,p,u,U] calculates the values of $\{N_{i-p,p}(u),\cdots,N_{i-1,p}(u),N_{i,p}(u)\}$


The main algorithm as shown below:


enter image description here


LibraryLink Code



#include "WolframLibrary.h"

DLLEXPORT int NonzeroBasis(WolframLibraryData libData, mint Argc,
MArgument *Args, MArgument Res) {
/*define the argument-varible*/
mint i, p;
mreal u;
MTensor tensor_U;
mreal *U;
/*----------------------------*/

mint j, r;
mreal temp;
mreal *left, *right, *N;
MTensor tensor_N;
mint type = MType_Real;
mint dims[1];
mint rank = 1;
int err;
/*assign the argument-value to argument-varible*/
i = MArgument_getInteger(Args[0]);

p = MArgument_getInteger(Args[1]);
u = MArgument_getReal(Args[2]);
tensor_U = MArgument_getMTensor(Args[3]);
U = libData->MTensor_getRealData(tensor_U);
dims[0] = p + 1;
err = libData->MTensor_new(type, rank, dims, &tensor_N );
N = libData->MTensor_getRealData(tensor_N);
/*main implementation*/
N[0] = 1.0;
for(j = 1; j <= p; j++){

left[j] = u - U[i+1-j];
right[j] = U[i+j] - u;
saved = 0.0;
for(r = 0; r < j; r++){
temp = N[r]/(right[r+1] + left[j-r]);
N[r] = saved + right[r+1]*temp;
saved = left[j-r]*temp;
}
N[j] = saved;
}

/*return the result*/
MArgument_setReal(Res,tensor_N);
return LIBRARY_NO_ERROR;
}

However, when I compiled it via the following code in a Simplified Chinese operation system,


Needs["CCompilerDriver`"]
lib =
CreateLibrary[{"c:\\users\\Shutao TANG\\Desktop\\NonzeroBasis.c"},
"NonzeroBasis", "Debug" -> False]

(*load the .dll to Mathematica*)
NonzeroBasis =
LibraryFunctionLoad[lib, "NonzeroBasis",
{Integer, Integer, Real, {Real, 1}}, {Real, 1}]

it gives me this error(including garbled information)


enter image description here


Question



  • How to debug the C code for LibraryLink? Because I cannot find useful information for debugging from CreateLibrary::cmpper.





Update


Now I also implement the main algorithm in Mathematica. Note: the start index for the Mathematica is 1, while for C, the start index is 0.


mmaNonzeroBasis[i_, p_, u_, U_] :=
Module[{j, left, right, saved, r, temp, basis},
left = right = ConstantArray[0.0, {p + 1}];
basis = Prepend[ConstantArray[0.0, {p}], 1];
For[j = 1, j <= p, j++,
left[[j + 1]] = u - U[[i + 2 - j]];

right[[j + 1]] = U[[i + j + 1]] - u;
saved = 0.0;
For[r = 0, r < j, r++,
temp = basis[[r + 1]]/(right[[r + 2]] + left[[j - r + 1]]);
basis[[r + 1]] = saved + right[[r + 2]]*temp;
saved = left[[j - r + 1]]*temp
];
basis[[j + 1]] = saved;
];
basis

]

Thanks for Rolf Mertig's revision





  • add the variable declaration mreal saved of saved




  • replace the MArgument_setReal() with MArgument_setMTensor()






knots = {0, 0, 0, 0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 10};
mmaNonzeroBasis[9, 4, 4.5, knots]
NonzeroBasis[9, 4, 4.5, knots]
BSplineBasis[{4, knots}, #, 4.5] & /@ Range[5, 9]
(*{0.0104167,0.380208,0.545139,0.0616319,0.00260417}*)

enter image description here



Which is very strange. I don't know why? Could someone help me find the reason?



Answer



Update: The very likely reason for the garbled error messages is that you have a Chinese version of Visual Studio printing errors in Chinese, and there is a mismatch in the character encoding of these messages and how Mathematica tried to interpret them.




CreateLibrary has two very useful options: "ShellCommandFunction" and "ShellOutputFunction".


Set them both to Print.


The first will print the compiler command line. You will know how Mathematica is invoking the compiler precisely. You can use the same command line in a terminal and compile without Mathematica if you wish.


The second will print the output from the compiler. This usually has much more information about the error than what is shown through the cmperr messages. I always use this option with CreateLibrary because the limited error information shown through the messages just isn't sufficient.




More tips to debug LibraryLink stuff



Terminal output


The first step is to make sure your code compiles. Then you have to make sure it runs correctly. It might crash, and there might be messages in the terminal about the cause of the crash. Or you just might want to see debugging output written to the terminal.


Unfortunately it is not possible to see terminal output when running with the Front End. It often helps if you run the kernel in command line mode (i.e. no front end), because you will be able to see any information output to the terminal.


It is also possible to run the kernel with the front end, yet still attached to a terminal. I wrote a blog post about how to do this on OS X and Windows. It should be possible to use the same ideas on Linux too.


In short: on Windows just set up math.exe to be used as the kernel instead of MathKernel.exe.


Debugging features in LTemplate


I wrote the LTemplate package to simplify working with LibraryLink.


Note 1: I recommend getting comfortable with plain LibraryLink before starting with LTemplate. LTemplate makes LibraryLink easier to use, not easier to learn.


Note 2: LTemplate requires using C++, not C. It is necessary to have at least basic knowledge of C++ (basic understanding of classes, templates, exceptions).


LTemplate has a few features to simplify debugging:



The function mma::print can be used to print directly to the notebook instead of to the terminal, so you can see debugging output with the front end.


There is a streams interface for this: output to mma::mout the same way you would output to std::cout or std:cerr and flush the stream. The output will be Printed in the notebook.


Some C++ libraries, such as Armadillo, can be set up to send their own debugging output to a stream.


There is a massert macro provided, which works like the standard assert macro, but instead of printing to stderr and terminating the kernel process, it prints to the notebook and simply returns from the library function.


LTemplate also catches any runaway C++ exceptions (which may come from a library you use), to prevent a kernel crash in this case. For exceptions derived from std::exception it prints the description.


Finally, LTemplate makes error checking/handling in your library easy: use RAII and when there is a problem just throw an mma::LibraryError exception with a descriptive error message. With good error checking you will be able to avoid problems more often. With descriptive error messages you will be able to find the source of the problems faster.


Comments

Popular posts from this blog

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1....