Help with "mxGetPi is deprecated"

Hello
I am trying to compile an old mex function. The first lines are:
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[] )
{
mxArray *blk_cell_pr, *A_cell_pr;
double *A, *B, *AI, *BI, *blksize, *Atmp, *AItmp
....
The problem is that variables AI and BI use the deprecated function MxGetPi (AI = mxGetPi(prhs[1]), for instance) and I have no idea how to modify the code to accommodate the changes. I can use a flag (-R2017b) to compile it but sooner or later I will have to change the code (I've been doing that since 93).
I try the help files but I could not find an example and to be honest I did not understand a thing on the explanation on the consequences of mxGetPi being deprecated.
Any help will be appreciated.
Cheers
Ed

 Accepted Answer

James Tursa
James Tursa on 28 May 2018
Edited: James Tursa on 28 May 2018
With the R2018a release of MATLAB, complex real/imag data is stored in an interleaved fashion instead of the separate fashion of R2017b and earlier. If you compile with the -R2017b flag (the default for R2018a) then the complex data is passed into and out of your mex routine via a deep copy, which will slow you down but should still work (unless you are doing something like modifying an input variable inplace). The deep copy stuff is the penalty you pay for using the old memory model API (i.e., the -R2017b flag) in R2018a and later.
How to go about changing your code for -R2018a depends on what your code does. We can't advise you unless we see your code.

8 Comments

Many thanks.
In this particular case, the function to be compiled is not one of mine but freely available on the web. It is part of sdpt3 solver, https://github.com/sqlp/sdpt3/blob/master/Solver/Mexfun/mexmat.c
I took a look at this code. You can either use the -R2017b compile option and suffer the deep copy-in/copy-out penalty for complex variables, or you will have to completely rewrite the complex part of the code to be compatible with the -R2018a option. The rewrite would not be too difficult and only involves setting up the imag pointers differently (= real pointer + 1) and doing the real/imag pointer incrementing differently (incrementing by 2 instead of 1).
Many thanks. Could you send me some info, piece of code, example and etc that can use to modify the original file? I have no idea on how to do it. Do I have to change any variable declaration?
Here is a simple example showing what would need to be done to the code. The example is simply to sum up the squares of the individual real & imag element values.
>> version
ans =
'9.4.0.813654 (R2018a)'
>> mex example2017b.c
Building with 'Microsoft Visual C++ 2013 (C)'.
MEX completed successfully.
>> mex example2018a.c -R2018a
Building with 'Microsoft Visual C++ 2013 (C)'.
MEX completed successfully.
>> example2017b_m(z)
ans =
1.666743080124563e+07
>> example2017b(z)
ans =
1.666743080124648e+07
>> example2018a(z)
ans =
1.666743080124648e+07
>> tic;example2017b_m(z);toc
Elapsed time is 0.190337 seconds.
>> tic;example2017b(z);toc
Elapsed time is 0.179463 seconds.
>> tic;example2018a(z);toc
Elapsed time is 0.080647 seconds.
The code used is:
% example2017b_m
% R2017b Example code doing the equivalent of the following:
% z = any full double complex variable
% r = real(z); i = imag(z);
% result = sum(r(:).*r(:) + i(:).*i(:));
function result = example2017b_m(z)
r = real(z);
i = imag(z);
result = r(:)'*r(:) + i(:)'*i(:);
return
end
and
/* example2017b.c
* R2017b Example code doing the equivalent of the following:
* z = any full double complex variable
* r = real(z); i = imag(z);
* result = sum(r(:).*r(:) + i(:).*i(:));
*/
#include "mex.h"
double sumsquares(double *pr, double*pi, size_t n) {
double result = 0.0;
size_t i;
for( i=0; i<n; i++ ) {
result = result + pr[i]*pr[i] + pi[i]*pi[i];
}
return result;
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
double *pr, *pi;
double result;
size_t n;
if( nlhs > 1 ) {
mexErrMsgTxt("Too many outputs");
}
if( nrhs == 1 ) {
if( mxIsDouble(prhs[0]) && mxIsComplex(prhs[0]) && !mxIsSparse(prhs[0]) ) {
pr = mxGetPr(prhs[0]);
pi = mxGetPi(prhs[0]);
n = mxGetNumberOfElements(prhs[0]);
result = sumsquares(pr,pi,n);
} else {
mexErrMsgTxt("Need exactly one full double complex input");
}
} else {
mexErrMsgTxt("Need exactly one full double complex input");
}
plhs[0] = mxCreateDoubleScalar(result);
}
and
/* example2018a.c
* R2018a Example code doing the equivalent of the following:
* z = any full double complex variable
* r = real(z); i = imag(z);
* result = sum(r(:).*r(:) + i(:).*i(:));
*
* Converted from the example2017b.c code
*
* Caution: This code will crash MATLAB if used with -R2017b option
*/
#include "mex.h"
double sumsquares(double *pr, double*pi, size_t n) {
double result = 0.0;
size_t i;
for( i=0; i<n; i++ ) {
// result = result + pr[i]*pr[i] + pi[i]*pi[i];
result = result + (*pr)*(*pr) + (*pi)*(*pi);
pr += 2;
pi += 2;
}
return result;
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
double *pr, *pi;
double result;
size_t n;
if( nlhs > 1 ) {
mexErrMsgTxt("Too many outputs");
}
if( nrhs == 1 ) {
if( mxIsDouble(prhs[0]) && mxIsComplex(prhs[0]) && !mxIsSparse(prhs[0]) ) {
// pr = mxGetPr(prhs[0]);
// pi = mxGetPi(prhs[0]);
pr = (double *) mxGetData(prhs[0]);
pi = pr + 1;
n = mxGetNumberOfElements(prhs[0]);
result = sumsquares(pr,pi,n);
} else {
mexErrMsgTxt("Need exactly one full double complex input");
}
} else {
mexErrMsgTxt("Need exactly one full double complex input");
}
plhs[0] = mxCreateDoubleScalar(result);
}
In the example2017b_m m-code version above, the real(z) and imag(z) cause a deep copy of these values which slows the code down. You can see that in the timing. The example2017b C-mex code doesn't do a deep copy of the code if compiled and used with R2017b or earlier, but if you compile this code in R2018a with the -R2017b option (the default), then MATLAB will automatically do a deep copy of the complex data on entry to the mex routine. You can see that in the timing as well, which is very similar to the m-code version that also does a deep data copy. But the example2018a mex code, which is compiled with the -R2018a option, avoids the deep data copy and simply uses the complex data directly. It runs in less than half the time of the other two examples as a result, which is a significant savings of both time and memory.
What had to change to convert the original code using mxGetPr and mxGetPi to the new code? Basically, I did the following:
1) Converted mxGetPr(etc) to (double *)mxGetData(etc)
2) Converted mxGetPi(etc) to (double *)mxGetData(etc)+1
3) Changed the pr[i] and pi[i] dereferencing to * pr and * pi, and changed the i++ incrementing to pr+=2 and pi+=2. That is, to minimize changes to existing code, simply step through the double data two elements at a time to account for the interleaved real/imag data format.
Similar changes could be easily done to the code you are using to make it compatible with the -R2018a compile option and avoid the deep data copy of complex data.
Although I didn't include it in the above examples, you should definitely download the following from the FEX:
This can be included in your mex code so that you can check the compile and run versions of MATLAB being used to make sure your code is robust and doesn't crash MATLAB. E.g., you could put the following in the example2018a.c code to prevent a MATLAB crash:
#include "matlab_version.h"
:
if( TARGET_API_VERSION == R2017b ) {
mexErrMsgTxt("This code cannot be used with the R2017b memory model");
}
Thank you ever so much. I think I can modify the existing code to accommodate the new standard. Again, many thanks.
Dear all, thank you for your posts. I also met this problem too. But I am wondering, if I have an array of complex variable (image) instead of one complex variable, would the pi = pr + 1 still hold true? Or I would need to change to the size of the real-part of the variable? ie.
pi = pr + sizeof(real-part of the variable)
which is from the
/* example2018a.c
* R2018a Example code doing the equivalent of the following:
* z = any full double complex variable
* r = real(z); i = imag(z);
* result = sum(r(:).*r(:) + i(:).*i(:));
*
...
...
// pr = mxGetPr(prhs[0]);
// pi = mxGetPi(prhs[0]);
pr = (double *) mxGetData(prhs[0]);
pi = pr + 1;
...
I appreciate any hints and help! Thank you very much.
I assume by "... complex variable (image) ..." you mean the underlying data type isn't double, but is something else like uint8. In that case, the only thing you would need to change in my example code is the types involved. E.g. if the image was uint8, then change this
double *pr, *pi;
to this
unsigned char *pr, *pi;
And change this:
pr = (double *) mxGetData(prhs[0]);
to this:
pr = (unsigned char *) mxGetData(prhs[0]);
The pointer arithmetic you DO NOT change! You DO NOT adjust based on the bytes used by the underlying data ... this is done automatically for you by the C/C++ compiler. So
pi = pr + sizeof(real-part of the variable); <-- NO
but
pi = pr + 1; <-- YES, this works regardless of underlying data type

Sign in to comment.

More Answers (0)

Categories

Tags

Asked:

on 27 May 2018

Edited:

on 12 Nov 2018

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!