11
$\begingroup$

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?

$\endgroup$
  • 1
    $\begingroup$ not sure but to me the question is actually two fold: the garbled error messages indicate that there might be a problem with the messaging system and/or more specifically messages coming from the CreateLibrary/CCompilerDriver subsystem when used with (I guess) language settings set to chinese. Rolfs answer shows how the messages should look like and how they would help to eliminate the problems in your code. I would suggest to file a bug report to WRI about those messages... $\endgroup$ – Albert Retey Jun 22 '16 at 8:37
  • 1
    $\begingroup$ I think it would be best if you reverted this edit and asked a separate question about the debugger. $\endgroup$ – Szabolcs Jun 22 '16 at 9:15
  • 1
    $\begingroup$ @AlbertRetey Given the existing answers, attaching the debugger would best be made a separate question. There are other ways to debug too (which I wrote an answer about), and why the error are garbled is yet again a separate question (also answered here). I am very interesting in how to use a debugger with Mathematica and I do not know the answer to that. But changing to question to that has just invalidated the answers ... $\endgroup$ – Szabolcs Jun 22 '16 at 9:19
  • 1
    $\begingroup$ @Shutao Thanks! Don't forget to post the question about attaching a debugger! I am very interested in that too. $\endgroup$ – Szabolcs Jun 22 '16 at 11:10
  • 4
    $\begingroup$ @Szabolcs: I absolutely agree that making that an extra question and probably changing the title is a good idea. I also see and appreciate that both answer provide very valid and useful information. On the other hand everything but using a debugger to debug C code seems to be a workaround and not the "best way to debug". IMHO it is a shame that we Mathematica users do have to think about ways how to debug code at all, be it Mathematica or C/C++ via LibraryLink. I would expect useful debugging tools to be available out of the box for a language existing for 20+ years... $\endgroup$ – Albert Retey Jun 22 '16 at 13:09
15
$\begingroup$

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.

$\endgroup$
  • $\begingroup$ @ShutaoTANG Could be this, depending on your version of Mathematica: mathematica.stackexchange.com/q/41682/12 $\endgroup$ – Szabolcs Jun 22 '16 at 9:10
  • $\begingroup$ @ShutaoTANG Sorry, I didn't see this. In that case it should work ... sorry for the obvious question, but did you load <<CCompilerDriver` before evaluating CCompilers[]? What is very strange is that CreateLibrary works but CCompilers[] doesn't ... Either both should work or neither. $\endgroup$ – Szabolcs Jun 22 '16 at 11:57
6
$\begingroup$

For me (Windows 10) I get a readable error message like enter image description here

So I add mint saved;. Compiling again I get enter image description here

which leads me to change your MArgument_setReal(Res,tensor_N); to MArgument_setMTensor(Res,tensor_N); and then things work.

BTW: I use

Needs["CCompilerDriver`"];
srccode="
#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 saved;
  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_setMTensor(Res,tensor_N);
  return LIBRARY_NO_ERROR; 
}
";
lib = CreateLibrary[srccode, "NonzeroBasis"]
NonzeroBasis =
  LibraryFunctionLoad[lib, "NonzeroBasis",
                      {Integer, Integer, Real, {Real, 1}}, {Real, 1}]
$\endgroup$
2
$\begingroup$

Perhaps you need to define MTensor for variable left and right. The modified code is:

#include "WolframLibrary.h"

DLLEXPORT int NonzeroBasis(WolframLibraryData libData, mint Argc,
                       MArgument *Args, MArgument Res) {
  /*define the argument-varible*/
  mreal saved;
  mint i, p;
  mreal u;
  MTensor tensor_U;
  mreal *U;
  /*----------------------------*/
  mint j, r;
  mreal temp;
  mreal *left, *right, *N;
  MTensor left_t, right_t, 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, 1, dims, &tensor_N );
  N = libData->MTensor_getRealData(tensor_N);
  err = libData->MTensor_new(type, 1, dims, &left_t );
  left = libData->MTensor_getRealData(left_t);
  err = libData->MTensor_new(type, 1, dims, &right_t );
  right = libData->MTensor_getRealData(right_t);

  /*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_setMTensor(Res,tensor_N);
  return LIBRARY_NO_ERROR; 
}

This time it is giving the same results as other functions.

enter image description here

$\endgroup$
  • $\begingroup$ Amazing. How did you debug this? Or did you know that MTensor creature well enough? $\endgroup$ – Rolf Mertig Jun 24 '16 at 13:40
  • $\begingroup$ @RolfMertig No I never used Library Link before, I just feel that the left and right pointer has not been defined a dimension, which doesn't look right in C. So I did same initialization with other tensor objects. $\endgroup$ – happy fish Jun 24 '16 at 13:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.