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:
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)
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
ofsaved
replace the
MArgument_setReal()
withMArgument_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}*)
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 Print
ed 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
Post a Comment