Skill v1.0.1
currentAutomated scan100/100+3 new
version: "1.0.1" name: matlab-upgrade-mex-ic description: > Upgrade C, C++, and Fortran MEX source files from the Separate Complex (SC) API to the Interleaved Complex (IC) API. Use when converting mxGetPr/mxGetPi to mxGetComplexDoubles, migrating MEX files to -R2018a, adding MX_HAS_INTERLEAVED_COMPLEX guards for SC/IC guarded builds, or modernizing legacy MEX code that uses mxGetData/mxSetData/mxGetImagData/mxSetImagData. Covers C (.c), C++ (.cpp, .cxx), and Fortran (.F, .f90) MEX functions. Triggers on: interleaved complex, IC MEX, mex upgrade, mex migration, separate complex, mxGetPi, mxGetPr replacement, -R2018a, complex MEX, Fortran MEX, .F MEX file, C++ MEX, .cpp MEX file, MEX performance, MEX slow complex, MEX call overhead, improve MEX performance complex. license: https://www.mathworks.com/content/dam/mathworks/license/pmrl/license.md metadata: author: MathWorks version: "1.0"
Upgrade MEX Files to Interleaved Complex API
Convert C, C++, and Fortran MEX source files from the Separate Complex (SC) API to the Interleaved Complex (IC) API, following the MathWorks upgrade workflow with guardrails for SC/IC guarded builds using MX_HAS_INTERLEAVED_COMPLEX, required vs recommended changes, and verification.
When to Use
- User has a C, C++, or Fortran MEX file using
mxGetPr/mxGetPi,mxGetData/mxGetImagData, ormxSetPr/mxSetPi - User wants to build with
mex -R2018a - User asks about interleaved complex, IC MEX, or modernizing MEX code
- User has legacy MEX code from File Exchange, GitHub, or pre-R2018a era
- User has a C++ MEX file (
.cpp,.cxx) using the C Matrix API (mxGetPr/mxGetPi, etc.) - User has a Fortran MEX file (
.F,.f90) usingmxGetPr/mxGetPivia%val()pointers - User reports a MEX function is slow on large complex arrays (call overhead, not loop)
- User asks how to improve MEX performance for complex data
When NOT to Use
- Writing a new MEX function from scratch — no conversion needed, but advise the user to use IC APIs (
mxGetDoubles,mxGetComplexDoubles, etc.) from the start for better performance on complex data and modern, type-safe data access instead of legacymxGetPr/mxGetPi - C++ MEX API (
matlab::mex::Function) — different API entirely; this skill covers C++ files that use the C Matrix API (mex.h), not the MATLAB Data API for C++ and C++ Engine - MEX file already uses typed accessors (
mxGetDoubles,mxGetComplexDoubles, etc.) — already IC-compatible, no conversion needed
Workflow
Follow these 7 steps in order. Do NOT skip steps.
Step 1: Pre-Upgrade Verification
Before changing anything, understand the current state:
- Read the source file completely
- Identify all SC API calls (see Key API Mappings)
- Note which data types the MEX handles (double, single, int32, etc.)
- Note whether it handles complex data (
mxGetPi,mxIsComplex,mxCOMPLEX)
Report to the user:
- Number of SC API calls found
- Whether file processes complex numbers (complex arrays in SC mode incur deinterleave/re-interleave overhead)
- Performance impact: If the file handles complex data, note that SC MEX calls incur O(n) deinterleave/re-interleave copies at the MATLAB boundary. For large arrays, IC conversion eliminates this overhead entirely (see Performance Benefits).
- Any other issues noticed (missing error checking, etc.)
Step 2: Output File Decision
ASK the user before proceeding:
"Where should I write the converted code?- New file — creates a separate file (e.g.,<name>_ic.cor<name>_dual.c), preserving the original untouched- Modify in place — edits the original file directly. Choose this if the file is under version control and you can revert if needed."
If user chooses new file, use these naming defaults (or a user-specified name):
- IC-only:
<name>_ic.c/<name>_ic.F - SC/IC guarded:
<name>_dual.c/<name>_dual.F
If user chooses modify in place, confirm the file is recoverable (e.g., "This file is tracked by git — you can revert with git checkout <file> if needed."). Then edit the original directly.
Tell the user which file you're writing to and why.
Step 3: Decision Point — IC-Only or SC/IC Guarded?
What is "SC/IC guarded"? A single source file that uses#if MX_HAS_INTERLEAVED_COMPLEXpreprocessor guards to compile under both the Separate Complex API (mex -R2017b) and the Interleaved Complex API (mex -R2018a). The compiler definesMX_HAS_INTERLEAVED_COMPLEX=1in IC mode and0in SC mode, so the guards select the correct code path at build time.
ASK the user before proceeding:
"Do you need this MEX file to compile under both the old SC API and the new IC API?- SC/IC guarded — wraps code in#if MX_HAS_INTERLEAVED_COMPLEX/#elseguards so the same source builds with bothmex -R2017b file.c(SC mode) andmex -R2018a file.c(IC mode). Choose this if you support multiple MATLAB versions.- IC-only — converts fully to IC API. Requiresmex -R2018ato build. Simpler code but only works on R2018a+."
If user chooses SC/IC guarded, use the #if MX_HAS_INTERLEAVED_COMPLEX preprocessor pattern:
C/C++ files:
#if MX_HAS_INTERLEAVED_COMPLEX/* IC code path */mxComplexDouble *pc = mxGetComplexDoubles(prhs[0]);#else/* SC code path */double *pr = mxGetPr(prhs[0]);double *pi = mxGetPi(prhs[0]);#endif
Fortran files (.F preprocessed source):
#if MX_HAS_INTERLEAVED_COMPLEXmwPointer mxGetComplexDoublescomplex*16, pointer :: pc(:)call mxGetComplexDoubles(prhs(1), pc)#elsemwPointer mxGetPr, mxGetPimwPointer pr, pipr = mxGetPr(prhs(1))pi = mxGetPi(prhs(1))#endif
Fortran note: SC/IC guarded Fortran files MUST use.Fextension (uppercase) so the MEX compiler invokes the C preprocessor. The.for.f90extension skips preprocessing and#ifguards will not work. For.f90files that cannot use preprocessor guards, use IC-only conversion.
If user chooses IC-only, convert directly without guards.
Step 4: Iterative Refactoring
All legacy data-access APIs are REQUIRED to be converted for a complete IC upgrade. Convert every pattern in this table:
| Legacy Pattern | Replacement | Reason | |
|---|---|---|---|
mxGetPi(arr) for data access | mxGetComplexDoubles(arr) + .imag | Does not exist in IC | |
mxSetPi(arr, ptr) | mxSetComplexDoubles(arr, ptr) | Does not exist in IC | |
mxGetImagData(arr) | Type-specific complex accessor | Does not exist in IC | |
mxSetImagData(arr, ptr) | Type-specific complex setter | Does not exist in IC | |
mxGetPi(arr) != NULL for complexity check | mxIsComplex(arr) — works in both SC and IC | Does not exist in IC | |
mxGetPr(arr) | mxGetDoubles(arr) | Wrong results on complex arrays in IC; untyped | |
mxSetPr(arr, ptr) | mxSetDoubles(arr, ptr) | Wrong results on complex arrays in IC; untyped | |
mxGetData(arr) + void* cast | Type-specific accessor (mxGetDoubles, mxGetInt32s, etc.) | Wrong results on complex arrays in IC; untyped | |
mxSetData(arr, ptr) | Type-specific setter (mxSetDoubles, mxSetInt32s, etc.) | Wrong results on complex arrays in IC; untyped |
Why all REQUIRED?mxGetPr/mxGetDataon complex arrays in IC mode return a pointer to interleaved data (real and imaginary interleaved) — iterating as if it were real-only gives wrong results silently. On real-only arrays they still work, but are untyped (void*or alwaysdouble*). When upgrading to IC, convert everything in one pass for correctness and type safety.
See references/pitfalls.md for common conversion mistakes (complexity checks, output allocation, buffer sizing, Fortran interleaved layout).
Reducing Code Duplication in SC/IC Guarded Mode
In SC/IC guarded mode, minimize duplication by guarding only the accessor calls, not entire logic blocks.
C/C++ files:
/* Guard only the pointer acquisition — logic stays shared */double *pr_in, *pr_out;#if MX_HAS_INTERLEAVED_COMPLEXpr_in = mxGetDoubles(prhs[0]);#elsepr_in = mxGetPr(prhs[0]);#endifplhs[0] = mxCreateDoubleMatrix(m, n, mxREAL);#if MX_HAS_INTERLEAVED_COMPLEXpr_out = mxGetDoubles(plhs[0]);#elsepr_out = mxGetPr(plhs[0]);#endif/* Shared logic — no duplication */for (i = 0; i < numElements; i++) {pr_out[i] = pr_in[i] * factor;}
Fortran files (`.F`):
c Guard only the pointer acquisition#if MX_HAS_INTERLEAVED_COMPLEXpr_in = mxGetDoubles(prhs(1))#elsepr_in = mxGetPr(prhs(1))#endifc Shared logic — no duplicationcall mxCopyPtrToReal8(pr_in, data, numElements)do i = 1, numElementsdata(i) = data(i) * factorend do
For complex data, the layout differs between modes (interleaved struct vs separate arrays), so full #if/#else blocks around the logic are unavoidable.
Rule of thumb:
- Real data paths — guard only the accessor, share the loop
- Complex data paths — guard the entire block (different data layouts require different loop bodies). See
references/api-mappings.md§ "SC/IC Guarded Pattern" for full complex loop examples in C, C++, and Fortran.
See references/api-mappings.md for the complete function mapping table.
Step 5: Build
Provide the exact build commands (adjust extension for language):
% C/C++ filesmex -R2018a <filename>.c % Explicit IC modemex -R2017b <filename>.c % Explicit SC modemex <filename>.c % Default (version-dependent)% For C++: same flags with .cpp or .cxx extensionmex -R2018a <filename>.cpp % Explicit IC mode% Fortran files (.F with preprocessor guards)mex -R2018a <filename>.F % Explicit IC modemex -R2017b <filename>.F % Explicit SC modemex <filename>.F % Default (version-dependent)
If the file is SC/IC guarded, remind the user to test all three build modes: -R2017b (SC), -R2018a (IC), and default (no flag).
Fortran: Files must use.F(uppercase) extension for preprocessor guards to work. If the source is.for.f90, rename to.For.F90when adding#ifguards.
Step 6: Verify
Provide MATLAB test commands that compare the converted MEX against the original.
For IC-only conversions (separate source files):
%% Build original with SC, converted with ICmex -R2017b originalFile.c -output func_sc % Explicit SC buildmex -R2018a convertedFile_ic.c -output func_ic % Explicit IC build%% Compare outputsZ = complex(rand(4,4), rand(4,4));assert(isequal(func_sc(Z), func_ic(Z)), 'Output mismatch!');
For SC/IC guarded files (same source, three build modes):
%% Build same source in all three modesmex -R2017b convertedFile.c -output func_sc % Explicit SC (forces separate complex)mex -R2018a convertedFile.c -output func_ic % Explicit IC (forces interleaved complex)mex convertedFile.c -output func_default % Default (no API flag)%% Compare outputs — all three must matchZ = complex(rand(4,4), rand(4,4));out_sc = func_sc(Z);out_ic = func_ic(Z);out_default = func_default(Z);assert(isequal(out_sc, out_ic), 'SC vs IC mismatch!');assert(isequal(out_sc, out_default), 'SC vs default mismatch!');
Always test with:
- Complex inputs (if the MEX handles complex data)
- Real inputs (if the MEX handles real data)
- Edge cases: empty arrays, scalars, large arrays
- All three build modes:
-R2017b(SC),-R2018a(IC), and default (no flag) - The original source file still compiles with
mex -R2017b(confirms it was not modified)
Step 7: Document
Add build information to the converted file's header comment:
/** Compile (Interleaved Complex API, R2018a+):* mex -R2018a filename.c** Original (Separate Complex API):* mex originalFilename.c** API Migration:* mxGetPr/mxGetPi -> mxGetComplexDoubles* mxSetPr/mxSetPi -> mxSetComplexDoubles*/
Performance Benefits of IC Conversion
IC conversion is not just API modernization — it delivers measurable performance gains for MEX functions that handle complex data.
Why IC Is Faster
Since R2018a, MATLAB stores complex arrays internally in interleaved format ([re0, im0, re1, im1, ...]). When a MEX function compiled with the SC API is called:
- On entry: MATLAB deinterleaves the input — splitting one contiguous buffer into two separate real/imag arrays. This is an O(n) copy.
- On exit: MATLAB re-interleaves the output — merging separate buffers back. Another O(n) copy.
For a 10M-element complex double array, that's ~80 MB per complex array crossing the boundary (N × 16 bytes). A MEX with one complex input and one complex output pays ~160 MB total; a MEX with three complex inputs pays ~240 MB on input alone.
IC MEX functions access data in MATLAB's native layout — zero copy at the boundary.
Additional Performance Advantages
- Cache locality:
z[i].realandz[i].imagare adjacent (same cache line). SC requires jumping between two arrays millions of elements apart. - Single memcpy for bulk copy: Duplicating a complex array is one
memcpy(dst, src, n * sizeof(mxComplexDouble))vs two separate copies. - FFTW/BLAS/LAPACK interop: IC layout is byte-for-byte compatible with
fftw_complex,double _Complex(C99), and cuBLAS/cuFFT types — no pack/unpack needed. - SIMD-friendly: Compilers can emit packed-double instructions that load a full complex number in one operation.
When Performance Justifies IC Migration
| Scenario | Speedup Significance | |
|---|---|---|
| Large complex arrays (>100K elements) | High — boundary copy dominates | |
| Frequent MEX calls in a loop | High — copy overhead accumulates | |
| MEX wraps FFTW/BLAS calls | High — eliminates pack/unpack | |
| Small arrays (<1K elements) | Low — copy overhead negligible | |
Real-only data (no mxGetPi) | None — no deinterleave occurs |
Tell the user: "For your [N]-element complex array, the SC API forces MATLAB to copy ~[N×16/1e6] MB per complex array argument. With [K] complex arrays crossing the boundary, that's ~[K×N×16/1e6] MB per call. IC eliminates this entirely."
Benchmark Template
After conversion, offer this benchmark to quantify the speedup:
%% Benchmark SC vs IC MEX performanceN = 1e7; % 10M complex elementsZ = randn(1,N) + 1i*randn(1,N);numIter = 20;% Build both versionsmex -R2017b originalFile.c -output func_scmex -R2018a convertedFile_ic.c -output func_ic% Warmupfor k = 1:3, func_sc(Z); func_ic(Z); end% Time SCtSC = zeros(numIter,1);for k = 1:numIter, tic; func_sc(Z); tSC(k) = toc; end% Time ICtIC = zeros(numIter,1);for k = 1:numIter, tic; func_ic(Z); tIC(k) = toc; endfprintf('SC median: %.2f ms\n', median(tSC)*1000);fprintf('IC median: %.2f ms\n', median(tIC)*1000);fprintf('Speedup: %.1fx\n', median(tSC)/median(tIC));assert(isequal(func_sc(Z), func_ic(Z)), 'Output mismatch!');
Optimization Tips
- Use `mxDuplicateArray` for in-place-style operations:
plhs[0] = mxDuplicateArray(prhs[0]); z = mxGetComplexDoubles(plhs[0]);— then modifyz[i]directly. Avoids manual allocation and lets MATLAB optimize the copy. - Single-pass loops: In IC mode, conjugation or scaling touches both
.realand.imagin one loop iteration with perfect spatial locality. - Avoid unnecessary `mxMalloc`: Create output as
mxCOMPLEXand write directly into the buffer frommxGetComplexDoubles— no intermediate allocation.
Key API Mappings
Summary of the most common conversions. Full table in references/api-mappings.md.
Complex Data Access
| SC API | IC API | Type | |
|---|---|---|---|
mxGetPr + mxGetPi | mxGetComplexDoubles | Required | |
mxSetPr + mxSetPi | mxSetComplexDoubles | Required | |
mxGetImagData | Type-specific complex accessor | Required | |
mxSetImagData | Type-specific complex setter | Required | |
mxGetPi(x) != NULL | mxIsComplex(x) | Required |
Real Data Access (Not Recommended in IC)
| SC API | IC API | Status | |
|---|---|---|---|
mxGetPr | mxGetDoubles | Not recommended — replace | |
mxSetPr | mxSetDoubles | Not recommended — replace | |
mxGetData | mxGetDoubles/mxGetInt32s/etc. | Not recommended — replace | |
mxSetData | mxSetDoubles/mxSetInt32s/etc. | Not recommended — replace |
Complex Data Types (IC mode)
mxComplexDouble /* struct with .real and .imag (both double) */mxComplexSingle /* struct with .real and .imag (both float) */mxComplexInt32 /* struct with .real and .imag (both int32_T) */
Conventions
- Always ask whether to create a new file or modify in place before writing
- Always explain the reason for each change (does not exist in IC, or wrong results on complex arrays / untyped)
- Always ask whether to use SC/IC guarded mode or IC-only before converting
- Always provide verification test commands
- Use `mxIsComplex()` for complexity checks — never
mxGetPi() != NULL.mxIsComplexworks in both SC and IC modes, so it can be placed outside#ifguards. - Buffer sizing in IC mode:
mxGetElementSize()returns the full interleaved size (16 bytes for complex double). Do NOT multiply by 2. - File naming (when creating new file):
_ic.c/_ic.cppor_ic.Fsuffix for IC-only conversions;_dual.c/_dual.cpp/_dual.Ffor SC/IC guarded; or modify in place if user chooses - C89 compatibility (C files only): Declare variables at the top of each
#if/#elseblock or at the top of the function. MEX compilers may enforce C89 rules where declarations must precede statements. C++ files do not have this restriction. - Fortran file extensions: Use
.F(uppercase) for SC/IC guarded Fortran files so the C preprocessor processes#if MX_HAS_INTERLEAVED_COMPLEXguards..fand.f90do not invoke the preprocessor. - Fortran pointer handling: In Fortran MEX, data pointers are
mwPointerintegers passed via%val()to copy routines (mxCopyPtrToReal8,mxCopyPtrToComplex16). In IC mode, usemxGetComplexDoubleswhich returns anmwPointerto the interleaved buffer.
Common Mistakes
| Mistake | Why It's Wrong | Correct Approach | |
|---|---|---|---|
| Modifying without asking | User may lose the working SC version if not under version control | Always ask: new file or modify in place? | |
Leaving mxGetPr/mxGetData calls unchanged | Not recommended by MathWorks in IC MEX functions | Replace with typed accessors (mxGetDoubles, mxGetInt32s, etc.) | |
numElements * mxGetElementSize(arr) * 2 for complex buffer | mxGetElementSize already returns 16 in IC mode | Use numElements * mxGetElementSize(arr) without doubling | |
Using mxGetPi(arr) != NULL to check complexity | mxGetPi doesn't exist in IC mode | Use mxIsComplex(arr) | |
| Converting without asking about compatibility needs | User may need the file to work on older MATLAB | Always ask: IC-only or SC/IC guarded? | |
| Not providing verification steps | Silent wrong answers are catastrophic in MEX | Always provide test commands comparing old vs new output |
Copyright 2026 The MathWorks, Inc.