Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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 migr
.claude/skills/matlab-matlab-upgrade-mex-ic/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 88% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 146% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 173% | 0% |
| case-22 | ✗→✓ | ▲ Improved | 173% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 200% | 0% |
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.
mxGetPr/mxGetPi, mxGetData/mxGetImagData, or mxSetPr/mxSetPimex -R2018a.cpp, .cxx) using the C Matrix API (mxGetPr/mxGetPi, etc.).F, .f90) using mxGetPr/mxGetPi via %val() pointersmxGetDoubles, mxGetComplexDoubles, etc.) from the start for better performance on complex data and modern, type-safe data access instead of legacy mxGetPr/mxGetPimatlab::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++ EnginemxGetDoubles, mxGetComplexDoubles, etc.) — already IC-compatible, no conversion neededFollow these 7 steps in order. Do NOT skip steps.
Before changing anything, understand the current state:
mxGetPi, mxIsComplex, mxCOMPLEX)Report to the user:
ASK the user before proceeding:
> "Where should I write the converted code? > - New file — creates a separate file (e.g., <name>_ic.c or <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):
<name>_ic.c / <name>_ic.F<name>_dual.c / <name>_dual.FIf 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.
> What is "SC/IC guarded"? A single source file that uses #if MX_HAS_INTERLEAVED_COMPLEX preprocessor guards to compile under both the Separate Complex API (mex -R2017b) and the Interleaved Complex API (mex -R2018a). The compiler defines MX_HAS_INTERLEAVED_COMPLEX=1 in IC mode and 0 in 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 / #else guards so the same source builds with both mex -R2017b file.c (SC mode) and mex -R2018a file.c (IC mode). Choose this if you support multiple MATLAB versions. > - IC-only — converts fully to IC API. Requires mex -R2018a to 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:
c#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):
fortran#if MX_HAS_INTERLEAVED_COMPLEX mwPointer mxGetComplexDoubles complex*16, pointer :: pc(:) call mxGetComplexDoubles(prhs(1), pc) #else mwPointer mxGetPr, mxGetPi mwPointer pr, pi pr = mxGetPr(prhs(1)) pi = mxGetPi(prhs(1)) #endif
> Fortran note: SC/IC guarded Fortran files MUST use .F extension (uppercase) so the MEX compiler invokes the C preprocessor. The .f or .f90 extension skips preprocessing and #if guards will not work. For .f90 files that cannot use preprocessor guards, use IC-only conversion.
If user chooses IC-only, convert directly without guards.
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/mxGetData on 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 always double*). 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).
In SC/IC guarded mode, minimize duplication by guarding only the accessor calls, not entire logic blocks.
C/C++ files:
c/* Guard only the pointer acquisition — logic stays shared */ double *pr_in, *pr_out; #if MX_HAS_INTERLEAVED_COMPLEX pr_in = mxGetDoubles(prhs[0]); #else pr_in = mxGetPr(prhs[0]); #endif plhs[0] = mxCreateDoubleMatrix(m, n, mxREAL); #if MX_HAS_INTERLEAVED_COMPLEX pr_out = mxGetDoubles(plhs[0]); #else pr_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):
fortranc Guard only the pointer acquisition #if MX_HAS_INTERLEAVED_COMPLEX pr_in = mxGetDoubles(prhs(1)) #else pr_in = mxGetPr(prhs(1)) #endif c Shared logic — no duplication call mxCopyPtrToReal8(pr_in, data, numElements) do i = 1, numElements data(i) = data(i) * factor end 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:
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.
Provide the exact build commands (adjust extension for language):
matlab% C/C++ files mex -R2018a <filename>.c % Explicit IC mode mex -R2017b <filename>.c % Explicit SC mode mex <filename>.c % Default (version-dependent) % For C++: same flags with .cpp or .cxx extension mex -R2018a <filename>.cpp % Explicit IC mode % Fortran files (.F with preprocessor guards) mex -R2018a <filename>.F % Explicit IC mode mex -R2017b <filename>.F % Explicit SC mode mex <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 .f or .f90, rename to .F or .F90 when adding #if guards.
Provide MATLAB test commands that compare the converted MEX against the original.
For IC-only conversions (separate source files):
matlab%% Build original with SC, converted with IC mex -R2017b originalFile.c -output func_sc % Explicit SC build mex -R2018a convertedFile_ic.c -output func_ic % Explicit IC build %% Compare outputs Z = 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):
matlab%% Build same source in all three modes mex -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 match Z = 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:
-R2017b (SC), -R2018a (IC), and default (no flag)mex -R2017b (confirms it was not modified)Add build information to the converted file's header comment:
c/* * Compile (Interleaved Complex API, R2018a+): * mex -R2018a filename.c * * Original (Separate Complex API): * mex originalFilename.c * * API Migration: * mxGetPr/mxGetPi -> mxGetComplexDoubles * mxSetPr/mxSetPi -> mxSetComplexDoubles */
IC conversion is not just API modernization — it delivers measurable performance gains for MEX functions that handle complex data.
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:
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.
z[i].real and z[i].imag are adjacent (same cache line). SC requires jumping between two arrays millions of elements apart.memcpy(dst, src, n * sizeof(mxComplexDouble)) vs two separate copies.fftw_complex, double _Complex (C99), and cuBLAS/cuFFT types — no pack/unpack needed.| 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."
After conversion, offer this benchmark to quantify the speedup:
matlab%% Benchmark SC vs IC MEX performance N = 1e7; % 10M complex elements Z = randn(1,N) + 1i*randn(1,N); numIter = 20; % Build both versions mex -R2017b originalFile.c -output func_sc mex -R2018a convertedFile_ic.c -output func_ic % Warmup for k = 1:3, func_sc(Z); func_ic(Z); end % Time SC tSC = zeros(numIter,1); for k = 1:numIter, tic; func_sc(Z); tSC(k) = toc; end % Time IC tIC = zeros(numIter,1); for k = 1:numIter, tic; func_ic(Z); tIC(k) = toc; end fprintf('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!');
mxDuplicateArray for in-place-style operations: plhs[0] = mxDuplicateArray(prhs[0]); z = mxGetComplexDoubles(plhs[0]); — then modify z[i] directly. Avoids manual allocation and lets MATLAB optimize the copy..real and .imag in one loop iteration with perfect spatial locality.mxMalloc: Create output as mxCOMPLEX and write directly into the buffer from mxGetComplexDoubles — no intermediate allocation.Summary of the most common conversions. Full table in references/api-mappings.md.
| 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 |
| 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 |
cmxComplexDouble /* struct with .real and .imag (both double) */ mxComplexSingle /* struct with .real and .imag (both float) */ mxComplexInt32 /* struct with .real and .imag (both int32_T) */
mxIsComplex() for complexity checks — never mxGetPi() != NULL. mxIsComplex works in both SC and IC modes, so it can be placed outside #if guards.mxGetElementSize() returns the full interleaved size (16 bytes for complex double). Do NOT multiply by 2._ic.c/_ic.cpp or _ic.F suffix for IC-only conversions; _dual.c/_dual.cpp/_dual.F for SC/IC guarded; or modify in place if user chooses#if/#else block 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..F (uppercase) for SC/IC guarded Fortran files so the C preprocessor processes #if MX_HAS_INTERLEAVED_COMPLEX guards. .f and .f90 do not invoke the preprocessor.mwPointer integers passed via %val() to copy routines (mxCopyPtrToReal8, mxCopyPtrToComplex16). In IC mode, use mxGetComplexDoubles which returns an mwPointer to the interleaved buffer.| 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.
Other measured skills in the registry, with their headline benchmark lift.