matlab-upgrade-mex-ic について
C、C++、Fortran の MEX ソースを Separate Complex API から Interleaved Complex API に移行する MATLAB スキルです。ソース変更、SC/IC 互換性の選択、ビルド、結果検証を支援します。
主な機能
- 01旧 Separate Complex API 呼び出しの特定
- 02旧アクセサーから型付き IC API への対応付け
- 03IC 専用または SC/IC 条件付きソースへの対応
- 04明示的な MATLAB API モードでのビルド
- 05変換後の MEX 出力の検証
活用シーン
- 01`mex -R2018a` 向けの旧 MEX ソースの更新
- 02SC と IC の両ビルドに対応するソースの維持
- 03変換後の複素配列の結果確認
- 04旧 C、C++、Fortran MEX コードの点検
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ matlab/ matlab-agentic-toolkit --skill matlab-upgrade-mex-icインストール後、Claude Code で/matlab-upgrade-mex-icを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
manifest.yamlreferences/api-mappings.mdpitfalls.md詳細
MEX ファイルをインターリーブ複素数 API にアップグレードする
MathWorks のアップグレード手順に従い、SC/IC ガード付きビルドで MX_HAS_INTERLEAVED_COMPLEX を使用する場合の注意点、必須変更と推奨変更、および検証を考慮して、C、C++、Fortran の MEX ソースファイルを Separate Complex(SC)API から Interleaved Complex(IC)API に変換します。
使用する場合
mxGetPr/mxGetPi、mxGetData/mxGetImagData、またはmxSetPr/mxSetPiを使用する C、C++、または Fortran の MEX ファイルを扱う場合mex -R2018aを使用してビルドしたい場合- インターリーブ複素数、IC MEX、または MEX コードのモダナイズについて質問された場合
- File Exchange、GitHub、または R2018a より前の時代のレガシー MEX コードを扱う場合
- C Matrix API(
mxGetPr/mxGetPiなど)を使用する C++ MEX ファイル(.cpp、.cxx)を扱う場合 %val()ポインターを介してmxGetPr/mxGetPiを使用する Fortran MEX ファイル(.F、.f90)を扱う場合- 大きな複素数配列で MEX 関数が遅いと報告された場合(ループではなく呼び出しのオーバーヘッド)
- 複素数データに対する MEX のパフォーマンスを改善する方法を質問された場合
使用しない場合
- 新しい MEX 関数を一から作成する場合 — 変換は不要ですが、複素数データのパフォーマンス向上と、レガシーの
mxGetPr/mxGetPiに代わる、最新の型安全なデータアクセスのため、最初から IC API(mxGetDoubles、mxGetComplexDoublesなど)を使用するよう案内してください - C++ MEX API(
matlab::mex::Function)を使用する場合 — API がまったく異なります。このスキルの対象は、C Matrix API(mex.h)を使用する C++ ファイルであり、MATLAB Data API for C++ や C++ Engine ではありません - MEX ファイルが型付きアクセサー(
mxGetDoubles、mxGetComplexDoublesなど)をすでに使用している場合 — すでに IC 互換であり、変換は不要です
ワークフロー
以下の 7 つの手順を順番に実行します。手順を省略しないでください。
手順 1:アップグレード前の確認
変更を加える前に、現在の状態を把握します。
- ソースファイル全体を読みます
- SC API の呼び出しをすべて特定します(主な API の対応関係を参照)
- MEX が扱うデータ型(double、single、int32 など)を記録します
- 複素数データを扱うかどうかを記録します(
mxGetPi、mxIsComplex、mxCOMPLEX)
ユーザーに次の内容を報告します。
- 見つかった SC API 呼び出しの数
- 複素数を処理するファイルかどうか(SC モードでは、複素数配列にデインターリーブ/再インターリーブのオーバーヘッドが発生します)
- パフォーマンスへの影響: ファイルが複素数データを扱う場合、SC MEX 呼び出しでは MATLAB 境界で O(n) のデインターリーブ/再インターリーブコピーが発生することを伝えます。大きな配列では、IC への変換によってこのオーバーヘッドを完全に解消できます(IC 変換によるパフォーマンス上のメリットを参照)。
- 気付いたその他の問題(エラーチェックの不足など)
手順 2:出力ファイルの決定
作業を進める前に、ユーザーに確認します。
「変換したコードはどこに書き出しますか?
- 新しいファイル — 元のファイルは変更せずに、別のファイル(例:
<name>_ic.cまたは<name>_dual.c)を作成します- 元のファイルを直接変更 — 元のファイルを直接編集します。ファイルがバージョン管理下にあり、必要に応じて元に戻せる場合に選んでください。」
ユーザーが新しいファイルを選択した場合は、次の名前を既定値として使用します(またはユーザー指定の名前を使用します)。
- IC のみ:
<name>_ic.c/<name>_ic.F - SC/IC ガード付き:
<name>_dual.c/<name>_dual.F
ユーザーが元のファイルを直接変更を選択した場合は、ファイルを復元できることを確認します(例:「このファイルは git で追跡されています。必要に応じて git checkout <file> で元に戻せます。」)。その後、元のファイルを直接編集します。
書き込み先のファイルと、その理由をユーザーに伝えます。
ステップ 3:判断ポイント — IC のみ、または SC/IC ガード付き?
「SC/IC ガード付き」とは何ですか?
#if MX_HAS_INTERLEAVED_COMPLEXプリプロセッサーガードを使用して、分離複素数 API(mex -R2017b)とインターリーブ複素数 API(mex -R2018a)の両方でコンパイルできる、単一のソースファイルです。コンパイラーは IC モードではMX_HAS_INTERLEAVED_COMPLEX=1、SC モードでは0を定義するため、ガードによってビルド時に正しいコードパスが選択されます。
続行する前に、ユーザーに確認してください:
「この MEX ファイルを、旧 SC API と新 IC API の両方でコンパイルできるようにする必要がありますか?
- SC/IC ガード付き — コードを
#if MX_HAS_INTERLEAVED_COMPLEX/#elseガードで囲み、同じソースからmex -R2017b file.c(SC モード)とmex -R2018a file.c(IC モード)の両方でビルドできるようにします。複数の MATLAB バージョンをサポートする場合は、こちらを選択してください。- IC のみ — IC API に完全に変換します。ビルドには
mex -R2018aが必要です。コードはシンプルになりますが、R2018a 以降でのみ動作します。」
ユーザーが SC/IC ガード付き を選択した場合は、#if MX_HAS_INTERLEAVED_COMPLEX プリプロセッサーパターンを使用してください:
C/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 ファイル(.F プリプロセス済みソース):
#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 に関する注意: SC/IC ガード付きの Fortran ファイルでは、MEX コンパイラーが C プリプロセッサーを呼び出すように、拡張子に
.F(大文字)を必ず使用してください。拡張子が.fまたは.f90の場合はプリプロセスがスキップされ、#ifガードが機能しません。プリプロセッサーガードを使用できない.f90ファイルでは、IC のみの変換を使用してください。
ユーザーが IC のみ を選択した場合は、ガードを使わずに直接変換してください。
ステップ 4:段階的なリファクタリング
IC への完全なアップグレードでは、古いデータアクセス API をすべて変換する必要があります。この表にあるパターンをすべて変換してください:
| 古いパターン | 置き換え先 | 理由 |
|---|---|---|
データアクセス用の mxGetPi(arr) | mxGetComplexDoubles(arr) + .imag | IC には存在しません |
mxSetPi(arr, ptr) | mxSetComplexDoubles(arr, ptr) | IC には存在しません |
mxGetImagData(arr) | 型固有の複素数アクセサー | IC には存在しません |
mxSetImagData(arr, ptr) | 型固有の複素数セッター | IC には存在しません |
複素数かどうかの確認に使う mxGetPi(arr) != NULL | mxIsComplex(arr) — SC と IC の両方で動作します | IC には存在しません |
mxGetPr(arr) | mxGetDoubles(arr) | IC では複素数配列に対して誤った結果になります。型指定もありません |
mxSetPr(arr, ptr) | mxSetDoubles(arr, ptr) | IC では複素数配列に対して誤った結果になります。型指定もありません |
mxGetData(arr) + void* キャスト | 型固有のアクセサー(mxGetDoubles、mxGetInt32s など) | IC では複素数配列に対して誤った結果になります。型指定もありません |
mxSetData(arr, ptr) | 型固有のセッター(mxSetDoubles、mxSetInt32s など) | IC では複素数配列に対して誤った結果になります。型指定もありません |
なぜすべてが必須なのですか? IC モードで複素数配列に対して
mxGetPr/mxGetDataを使うと、インターリーブされたデータ(実数部と虚数部が交互に並ぶ)へのポインターが返されます。実数のみのデータとして反復処理すると、気付かないうちに誤った結果になります。実数のみの配列では引き続き動作しますが、型指定がありません(void*または常にdouble*です)。IC にアップグレードする際は、正確性と型安全性のため、すべてを一度に変換してください。
よくある変換ミス(複素数かどうかの確認、出力の割り当て、バッファーサイズ、Fortran のインターリーブレイアウト)については、references/pitfalls.md を参照してください。
SC/IC ガード付きモードでのコード重複の削減
SC/IC ガード付きモードでは、ロジック全体ではなくアクセサー呼び出しだけをガードして、コードの重複を最小限にしてください。
C/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 ファイル(.F):
c 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
複素数データでは、モードによってレイアウトが異なるため(インターリーブ構造体と分離配列)、ロジック全体を囲む #if/#else ブロックは避けられません。
経験則:
- 実数データのパス — アクセサーだけをガードし、ループは共通化します
- 複素数データのパス — ブロック全体をガードします(データレイアウトが異なるため、ループ本体も別にする必要があります)。C、C++、Fortran での複素数ループの例については、
references/api-mappings.mdの「SC/IC Guarded Pattern」を参照してください。
関数の完全な対応表については、references/api-mappings.md を参照してください。
ステップ 5:ビルド
正確なビルドコマンドを指定してください(言語に合わせて拡張子を調整してください)。
% 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)
ファイルが SC/IC ガード付きの場合は、-R2017b(SC)、-R2018a(IC)、デフォルト(フラグなし)の 3 つすべてのビルドモードをテストするようユーザーに伝えてください。
Fortran: プリプロセッサガードを機能させるには、ファイルの拡張子を大文字の
.Fにする必要があります。ソースが.fまたは.f90の場合は、#ifガードを追加する際に.Fまたは.F90に名前を変更してください。
ステップ 6:検証
変換後の MEX と元の MEX を比較する MATLAB テストコマンドを指定してください。
IC 専用の変換(ソースファイルを分ける場合):
%% 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!');
SC/IC ガード付きファイル(同じソース、3 つのビルドモード):
%% 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!');
必ず次のテストを行ってください:
- 複素数入力(MEX が複素データを扱う場合)
- 実数入力(MEX が実数データを扱う場合)
- エッジケース:空の配列、スカラー、大きな配列
- 3 つすべてのビルドモード:
-R2017b(SC)、-R2018a(IC)、デフォルト(フラグなし) - 元のソースファイルを
mex -R2017bで引き続きコンパイルできること(変更されていないことを確認します)
ステップ 7:ドキュメント化
変換後のファイルのヘッダーコメントにビルド情報を追加してください。
/*
* 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 変換によるパフォーマンス上の利点
IC 変換は単なる API の最新化ではなく、複素数データを扱う MEX 関数に測定可能なパフォーマンス向上をもたらします。
IC の方が高速な理由
R2018a 以降、MATLAB は複素数配列を内部的にインターリーブ形式([re0, im0, re1, im1, ...])で格納します。SC API でコンパイルされた MEX 関数が呼び出されると:
- 呼び出し時: MATLAB は入力をデインターリーブし、連続した 1 つのバッファーを 2 つの別々の実数/虚数配列に分割します。これは O(n) のコピーです。
- 終了時: MATLAB は出力を再インターリーブし、別々のバッファーを 1 つにまとめます。これも O(n) のコピーです。
1,000 万要素の複素数倍精度配列では、境界を越える複素数配列 1 つにつき約 80 MB になります(N × 16 バイト)。複素数入力 1 つと複素数出力 1 つを持つ MEX では合計約 160 MB、複素数入力を 3 つ持つ MEX では入力だけで約 240 MB のコピーが発生します。
IC MEX 関数は MATLAB のネイティブレイアウトでデータにアクセスするため、境界でのコピーはゼロです。
その他のパフォーマンス上の利点
- キャッシュ局所性:
z[i].realとz[i].imagは隣接しています(同じキャッシュライン)。SC では、数百万要素離れた 2 つの配列間を移動する必要があります。 - 一括コピーでの
memcpyは 1 回: 複素数配列の複製は、別々に 2 回コピーする代わりに、memcpy(dst, src, n * sizeof(mxComplexDouble))で 1 回で済みます。 - FFTW/BLAS/LAPACK との相互運用: IC レイアウトは
fftw_complex、double _Complex(C99)、cuBLAS/cuFFT 型とバイト単位で互換性があるため、パック/アンパックは不要です。 - SIMD に適している: コンパイラーは、1 回の操作で複素数全体を読み込むパック倍精度命令を生成できます。
パフォーマンスを理由に IC へ移行すべきケース
| シナリオ | 高速化の効果 |
|---|---|
| 大きな複素数配列(>100K 要素) | 高 — 境界でのコピーが支配的です |
| ループ内で MEX を頻繁に呼び出す | 高 — コピーのオーバーヘッドが蓄積します |
| MEX が FFTW/BLAS の呼び出しをラップする | 高 — パック/アンパックが不要になります |
| 小さな配列(<1K 要素) | 低 — コピーのオーバーヘッドは無視できます |
実数のみのデータ(mxGetPi なし) | なし — デインターリーブは発生しません |
ユーザーに次のように伝えてください:「[N] 要素の複素数配列の場合、SC API では MATLAB が複素数配列の引数 1 つにつき約 [N×16/1e6] MB をコピーします。境界を越える複素数配列が [K] 個あると、呼び出し 1 回あたり約 [K×N×16/1e6] MB になります。IC ではこれを完全になくせます。」
ベンチマークのテンプレート
変換後、速度向上を定量化するため、次のベンチマークを提示してください。
%% 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を使用する:plhs[0] = mxDuplicateArray(prhs[0]); z = mxGetComplexDoubles(plhs[0]);— その後、z[i]を直接変更します。手動での割り当てを避け、MATLAB によるコピーの最適化を可能にします。 - 単一パスのループ: IC モードでは、共役複素数化やスケーリングの際に、空間局所性を最大限に活かしながら、1 回のループ反復で
.realと.imagの両方を処理できます。 - 不要な
mxMallocを避ける: 出力をmxCOMPLEXとして作成し、mxGetComplexDoublesから取得したバッファーに直接書き込みます。中間の割り当ては不要です。
主要な API の対応関係
最も一般的な変換の概要です。全表は references/api-mappings.md を参照してください。
複素データへのアクセス
| SC API | IC API | 種別 |
|---|---|---|
mxGetPr + mxGetPi | mxGetComplexDoubles | 必須 |
mxSetPr + mxSetPi | mxSetComplexDoubles | 必須 |
mxGetImagData | 型固有の複素アクセサー | 必須 |
mxSetImagData | 型固有の複素セッター | 必須 |
mxGetPi(x) != NULL | mxIsComplex(x) | 必須 |
実数データへのアクセス(IC では非推奨)
| SC API | IC API | 状態 |
|---|---|---|
mxGetPr | mxGetDoubles | 非推奨 — 置き換えてください |
mxSetPr | mxSetDoubles | 非推奨 — 置き換えてください |
mxGetData | mxGetDoubles/mxGetInt32s/etc. | 非推奨 — 置き換えてください |
mxSetData | mxSetDoubles/mxSetInt32s/etc. | 非推奨 — 置き換えてください |
複素データ型(IC モード)
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) */
規約
- 書き込みを行う前に、必ず新しいファイルを作成するか、既存ファイルをそのまま変更するかを確認してください
- 変更ごとに、その理由(IC には存在しない、または複素配列/型指定なしの場合に誤った結果になる)を必ず説明してください
- 変換前に、SC/IC の条件付きモードを使うか、IC 専用にするかを必ず確認してください
- 検証用のテストコマンドを必ず提示してください
- 複素数かどうかの確認には
mxIsComplex()を使用してください —mxGetPi() != NULLは使用しないでください。mxIsComplexは SC モードと IC モードの両方で動作するため、#ifガードの外に配置できます。 - IC モードでのバッファーサイズ:
mxGetElementSize()はインターリーブされた完全なサイズ(複素倍精度の場合は 16 バイト)を返します。2 倍しないでください。 - ファイル名(新しいファイルを作成する場合): IC 専用の変換では
_ic.c/_ic.cppまたは_ic.Fのサフィックス、SC/IC 条件付きの変換では_dual.c/_dual.cpp/_dual.Fを使用します。ユーザーが選択した場合は、ファイルをそのまま変更してもかまいません。 - C89 との互換性(C ファイルのみ): 各
#if/#elseブロックの先頭、または関数の先頭で変数を宣言してください。MEX コンパイラーでは、宣言を文より前に置く必要がある C89 の規則が適用される場合があります。C++ ファイルにはこの制限はありません。 - Fortran のファイル拡張子: SC/IC 条件付きの Fortran ファイルには
.F(大文字)を使用し、C プリプロセッサが#if MX_HAS_INTERLEAVED_COMPLEXガードを処理するようにしてください。.fと.f90ではプリプロセッサは実行されません。 - Fortran のポインター処理: Fortran MEX では、データポインターは
mwPointer整数であり、コピー用ルーチン(mxCopyPtrToReal8、mxCopyPtrToComplex16)に%val()を介して渡します。IC モードでは、インターリーブされたバッファーへのmwPointerを返すmxGetComplexDoublesを使用してください。
よくある間違い
| 間違い | 問題となる理由 | 正しい方法 |
|---|---|---|
| 確認せずに変更する | バージョン管理されていない場合、ユーザーが動作する SC 版を失う可能性があります | 必ず「新しいファイルを作成しますか、それとも既存ファイルをそのまま変更しますか?」と確認してください |
mxGetPr/mxGetData の呼び出しを変更せずに残す | MathWorks は IC MEX 関数での使用を推奨していません | 型付きアクセサー(mxGetDoubles、mxGetInt32s など)に置き換えてください |
複素バッファーに numElements * mxGetElementSize(arr) * 2 を使う | mxGetElementSize は IC モードですでに 16 を返します | 2 倍せず、numElements * mxGetElementSize(arr) を使用してください |
複素数かどうかの確認に mxGetPi(arr) != NULL を使う | mxGetPi は IC モードには存在しません | mxIsComplex(arr) を使用してください |
| 互換性の要件を確認せずに変換する | ユーザーは古い MATLAB でもファイルを動作させる必要があるかもしれません | 必ず「IC 専用ですか、それとも SC/IC 条件付きですか?」と確認してください |
| 検証手順を提示しない | MEX で誤った結果に気づかないままになると、深刻な問題につながります | 古い出力と新しい出力を比較するテストコマンドを必ず提示してください |
Copyright 2026 The MathWorks, Inc.