Main Content

Upgrade MEX Files to Use Interleaved Complex API

This topic describes how to upgrade your MEX files to use the interleaved complex API. You can continue to use the separate complex API by calling the mex command with the -R2017b option. However, for more information about using this option, see Do I Need to Upgrade My MEX Files to Use Interleaved Complex API?

Note

If you build your MEX files using the mex command with the -compatibleArrayDims option, then you first must update the source code to use the 64-bit API. For information, see Upgrade MEX Files to Use 64-Bit API.

To update your MEX source code, use the following checklist.

  1. Review your code for usage of pr and pi pointers, the pointers returned by the mxGetPr/mxGetPi and mxGetData/mxGetImagData functions. In the interleaved complex API, there is one pointer, pa, the value returned by mxGetDoubles and the other typed data functions. It is important to check an input array for complexity before attempting to read the data. Calls to mxGetPr and mxGetData on complex arrays return different results in the interleaved complex API than in the separate complex API.

  2. Prepare your code before editing.

    Before modifying your code, verify that the MEX function works with the -R2017b API. At a minimum, build a list of expected inputs and outputs, or create a full test suite. Use these tests to compare the results with the updated source code. The results should be identical.

    Back up all source, binary, and test files.

  3. Iteratively refactor your existing code by checking for the following conditions.

  4. After each change, compile using the interleaved complex API. To build myMexFile.c, type:

    mex -R2018a myMexFile.c
    To build myMexFile.F, type:

    mex -R2018a myMexFile.F
  5. Resolve failures and warnings.

  6. Test after each refactoring.

    Compare the results of running your MEX function compiled with the interleaved complex API with the results from your original binary. If there are any differences or failures, use a debugger to investigate the cause. For information on the capabilities of your debugger, refer to your compiler documentation.

  7. Add Build Information to MEX Help File

Check Array Complexity Using mxIsComplex

If your code calls the mxGetPi function to determine if an array has complex elements, use the mxIsComplex function instead. This function builds with both the -R2017b and -R2018a APIs. Search your code for the following patterns.

Replace C Source Code:With:
mxArray *pa;
...
if (mxGetPi(pa)) { 
    /* process complex array */
}
mxArray *pa;
...
if (mxIsComplex(pa)) { 
    /* process complex array */
}
double *ptr;
ptr = mxGetPi(pa);
if (ptr != NULL) { 
    /* process complex array */
}

Add MX_HAS_INTERLEAVED_COMPLEX to Support Both Complex Number Representations

To write code that builds with both the -R2017b and -R2018a APIs, add the MX_HAS_INTERLEAVED_COMPLEX macro. This macro returns true if you build the MEX file with the -R2018a option.

Wrapping the following code in an #if MX_HAS_INTERLEAVED_COMPLEX statement ensures that this code will build with either the -R2017b or -R2018a mex option. However, in this example, there is no code to execute when built with -R2018a.

Replace C Source Code:With:
static void
analyze_double(const mxArray *array_ptr)
{
    mwSize total_num_of_elements, index;
    double *pr, *pi;
    total_num_of_elements = mxGetNumberOfElements(array_ptr);
    pr = mxGetPr(array_ptr);
    pi = mxGetPi(array_ptr);
    for (index=0; index<total_num_of_elements; index++)  {
        if (mxIsComplex(array_ptr)) {
            mexPrintf("%g + %gi\n", *pr++, *pi++);
        }
        else {
            mexPrintf("%g\n", *pr++);
        }
    }
}
static void
analyze_double(const mxArray *array_ptr)
{
    mwSize total_num_of_elements, index;
    total_num_of_elements = mxGetNumberOfElements(array_ptr);

    #if MX_HAS_INTERLEAVED_COMPLEX
        /* interleaved complex API processing */
        mxComplexDouble *pc;
        mxDouble *p;
        if (mxIsComplex(array_ptr)) {
            pc = mxGetComplexDoubles(array_ptr);
            for (index=0; index<total_num_of_elements; index++)  {
                mexPrintf(" = %g + %gi\n",(*pc).real,(*pc).imag);
                pc++;
            }
        }
        else {
            p = mxGetDoubles(array_ptr);
            for (index=0; index<total_num_of_elements; index++)  {
                mexPrintf(" = %g\n", *p++);
            }
        }
    #else
        /* separate complex API processing */
        double *pr, *pi;
        pr = mxGetPr(array_ptr);
        pi = mxGetPi(array_ptr);
        for (index=0; index<total_num_of_elements; index++)  {
            if (mxIsComplex(array_ptr)) {
                mexPrintf("%g + %gi\n", *pr++, *pi++);
            }
            else {
                mexPrintf("%g\n", *pr++);
            }
        }
    #endif
}
Replace Fortran Source Code:With:
mwPointer prhs(*), pr
pr = mxGetPr(prhs(1))
mwPointer prhs(*), pr
#if MX_HAS_INTERLEAVED_COMPLEX
      pr = mxGetDoubles(prhs(1))
#else
      pr = mxGetPr(prhs(1))
#endif

Use Typed Data Access Functions

To use the mxGetData and mxGetImagData functions, you must verify the type of the input mxArray and manually cast the pointer output to the correct type. The typed data access functions verify the type of the array and return the correct pointer type. When you use the mxGetInt16s and mxGetComplexInt16s functions in the following code to process an int16 array, you do not need to remember the corresponding C type, short int.

Replace C Source Code:With:
static void
analyze_int16(const mxArray *array_ptr)
{
    short int  *pr, *pi;
    pr = (short int *)mxGetData(array_ptr);
    pi = (short int *)mxGetImagData(array_ptr);
    
    if (mxIsComplex(array_ptr)) {
         /* process complex data *pr,*pi */
    }
    else {
        /* process real data *pr */
    }
}
static void
analyze_int16(const mxArray *array_ptr)
{
    mxComplexInt16 *pc;
    mxInt16 *p;
    if(mxIsComplex(array_ptr)) {
        pc = mxGetComplexInt16s(array_ptr);
        /* process complex data (*pc).real,(*pc).imag */
        }
    }
    else {
        p = mxGetInt16s(array_ptr);
        /* process real data *p */
        }
    }
}

Handle Complex mxArrays

The following examples show how MATLAB® uses one array variable to represent a complex array.

Complex C mxArrays

Suppose that you have the following complex mxArray variables and want to add the real numbers and the imaginary numbers of x and y to create array z. Arrays x and y are the same size.

mxArray * x, y, z;

Instead of creating two pointers xr and xi for array x, create one pointer xc of type mxComplexDouble. To access the real and imaginary parts of element xc[i], use xc[i].real and xc[i].imag.

Replace C Source Code:With:
double  *xr, *xi, *yr, *yi, *zr, *zi;
/* get pointers to the real and imaginary parts of the arrays */
xr = mxGetPr(x);
xi = mxGetPi(x);
yr = mxGetPr(y);
yi = mxGetPi(y);
zr = mxGetPr(z);
zi = mxGetPi(z);

...
/* perform addition on element i */
zr[i] = xr[i] + yr[i];
zi[i] = xi[i] + yi[i];
/* get pointers to the complex arrays */
mxComplexDouble * xc = mxGetComplexDoubles(x);
mxComplexDouble * yc = mxGetComplexDoubles(y);
mxComplexDouble * zc = mxGetComplexDoubles(z);

...
/* perform addition on element i */
zc[i].real = xc[i].real + yc[i].real;
zc[i].imag = xc[i].imag + yc[i].imag;

The following code copies an mxArray into an output argument. The code shows how to test for and copy complex arrays.

Replace C Source Code:With:
mxGetPr(plhs[0])[0] = mxGetPr(prhs[0])[index];
if (mxIsComplex(prhs[0])) {
    mxGetPi(plhs[0])[0] = mxGetPi(prhs[0])[index];
}
if (mxIsComplex(prhs[0])) {
   mxGetComplexDoubles(plhs[0])[0] = mxGetComplexDoubles(prhs[0])[index];
}
else {
   mxGetDoubles(plhs[0])[0] = mxGetDoubles(prhs[0])[index];
}

Complex Fortran mxArrays

Suppose that you have two complex double mxArrays and want to pass them to a Fortran function with input arguments x and y defined as follows.

complex*16 x(*), y(*)

Instead of separately converting the real and imaginary parts of each mxArray, use the mxGetComplexDoubles function.

Replace Fortran Source Code:With:
      mwPointer mxGetPr, mxGetPi

C     Copy the data into native COMPLEX Fortran arrays.
      call mxCopyPtrToComplex16(
     +    mxGetPr(prhs(1)),
     +    mxGetPi(prhs(1)),x,nx)
      call mxCopyPtrToComplex16(
     +    mxGetPr(prhs(2)),
     +    mxGetPi(prhs(2)),y,ny)
      mwPointer mxGetComplexDoubles
      integer*4 status
      integer*4 mxCopyPtrToComplex16, mxCopyComplex16ToPtr

C     Copy the data into native COMPLEX Fortran arrays.
      status = 
     +   mxCopyPtrToComplex16(mxGetComplexDoubles(prhs(1)),x,nx)
C     Test status for error conditions

      status = 
     +   mxCopyPtrToComplex16(mxGetComplexDoubles(prhs(2)),y,ny)
C     Test status for error conditions

Maintain Complexity of mxArray

This C code snippet shows how to convert a real, double, input array prhs[0] into a complex array. The following code sets up the variables used to fill the complex part of the array with consecutive numbers.

// code to check number of arguments and expected types
mwSize rows = mxGetM(prhs[0]);
mwSize cols = mxGetN(prhs[0]);
mwSize sz = mxGetElementSize(prhs[0]);

The following code shows how to use mxMakeArrayComplex to convert a real, double, input array to an interleaved complex mxArray. For more examples, see mxMakeArrayComplex (C).

Replace C Source Code:With:
plhs[0] = mxDuplicateArray(prhs[0]);

mxDouble *dc = (mxDouble*)mxMalloc(rows*cols*sz);
mxSetImagData(plhs[0], dc);
for (int i = 0 ; i < rows*cols ; i++)
{
    dc[i] = i+1;
}
plhs[0] = mxDuplicateArray(prhs[0]);

if (mxMakeArrayComplex(plhs[0])) {
    mxComplexDouble *dt = mxGetComplexDoubles(plhs[0]);
    for (int i = 0 ; i < rows*cols ; i++)
    {
        dt[i].imag = i+1;
    }
}

Replace Separate Complex Functions

The following functions are not in the interleaved complex API. You must replace them with interleaved complex functions when handling complex data.

  • mxGetPi

  • mxSetPi

  • mxGetImagData

  • mxSetImagData

You can replace calls to mxGetPr and mxGetPi with a call to mxGetComplexDoubles. This function verifies that your array contains elements of type mxComplexDouble. Likewise, mxSetComplexDoubles replaces mxSetPr and mxSetPi.

The mxGetData and mxGetImagData functions do not check the type of the array. Instead, you must cast the return value to the pointer type that matches the type specified by the input. Replace calls to mxGetData and mxGetImagData with the single, appropriate, typed data access function, for example, mxGetComplexInt64s.

Replace C Source Code:With:
mxArray *pa;
mwSize numElements; 

int64_T  *pr, *pi;
pr = (int64_T *)mxGetData(pa);
pi = (int64_T *)mxGetImagData(pa);
numElements = mxGetNumberOfElements(pa);
mxArray *pa;
mwSize numElements; 

mxComplexInt64 *pc;
pc = mxGetComplexInt64s(pa);
numElements = mxGetNumberOfElements(pa);

Calculate Array Data Size with mxGetElementSize

In the -R2018a API, the mxGetElementSize (C) function returns sizeof(std::complex<T>) for a complex mxArray with data type T. This value is twice the value returned by the function in the -R2017b API. Similarly, mxGetElementSize (Fortran) in the -R2018a API returns twice the value as the function in the -R2017b API.

Consider Replacing To-Be-Phased-Out Functions

The following functions are in both the -R2017b and the -R2018a APIs. While you do not need replace them with typed data access functions, the typed data functions provide type checking. Also, if you use mxGetPr, you might choose mxGetPi for any complex array processing. This code pattern causes errors when writing -R2018a MEX functions.

You can replace calls to mxGetPr with a call to mxGetDoubles. This function verifies that your array contains elements of type mxDouble. Likewise, mxSetDoubles replaces mxSetPr. To replace calls to mxGetData and mxSetData functions, choose the appropriate typed data access function, for example, mxGetInt64s and mxSetInt64s.

Replace C Source Code:With:
double *y;
/*  create a pointer y to input matrix */
y = mxGetPr(prhs[1]);
mxDouble *p;
p = mxGetDoubles(prhs[1]);
Replace Fortran Source Code:With:
      mwPointer pr
      mwPointer mxGetPr
C     Create a pointer to input matrix
      pr = mxGetPr(prhs(1))
      mwPointer pr
      mwPointer mxGetDoubles

      pr = mxGetDoubles(prhs(1))

Add Build Information to MEX Help File

Consider creating a help file, described in Use Help Files with MEX Functions, that contains build information. For example, create a file displayTypesafeNumeric.m containing the following text.

% displayTypesafeNumeric.m Help file for displayTypesafeNumeric C MEX function
% 
% Use the following command to build this MEX file:
% mex -R2018a displayTypesafeNumeric.c

At the MATLAB command prompt, type:

help displayTypesafeNumeric
displayTypesafeNumeric.m Help file for displayTypesafeNumeric C MEX function
  
  Use the following command to build this MEX file:
  mex -R2018a displayTypesafeNumeric.c

See Also

Related Topics