Main Content

インターリーブされた複素数 API を使用するように MEX ファイルをアップグレード

このトピックでは、インターリーブされた複素数 API を使用するように MEX ファイルをアップグレードする方法を説明します。-R2017b オプションを指定して mex コマンドを呼び出すと、引き続き実数/虚数分離型複素数 API を使用することができます。ただし、このオプションの使用の詳細については、インターリーブされた複素数 API を使用するために MEX ファイルをアップグレードする必要があるかを参照してください。

メモ

mex コマンドで -compatibleArrayDims オプションを使用して MEX ファイルをビルドする場合、まず 64 ビット API を使用するようにソース コードを更新しなければなりません。詳細については、64 ビット API を使用するように MEX ファイルをアップグレードを参照してください。

MEX ソース コードを更新するには、次のチェックリストを使用します。

  1. コードで、pr および pi ポインターの使用法と、関数 mxGetPr/mxGetPi および関数 mxGetData/mxGetImagData によって返されるポインターを確認してください。インターリーブされた複素数 API には、1 つのポインター (pa) があります。これは mxGetDoubles およびその他の型付きデータ関数によって返される値です。データを読み取る前に、入力配列の実数/複素数をチェックすることが重要です。複素配列で mxGetPr および mxGetData を呼び出した場合、インターリーブされた複素数 API の結果は実数/虚数分離型複素数 API と異なります。

  2. 編集する前にコードを準備します。

    コードを変更する前に、MEX 関数が -R2017b API で動作することを確認します。少なくとも想定される入出力のリストを作成するか、完全なテスト スイートを作成します。これらのテストを使用して、結果を更新されたソース コードと比較します。結果は両者で一致しなければなりません。

    すべてのソース ファイル、バイナリ ファイル、テスト ファイルのバックアップを取ります。

  3. 次の条件を確認することによって、既存のコードを繰り返しリファクタリングします。

  4. 変更のたびにインターリーブされた複素数 API を使用してコンパイルします。myMexFile.c をビルドするには、以下を入力します。

    mex -R2018a myMexFile.c
    myMexFile.F をビルドするには、以下を入力します。

    mex -R2018a myMexFile.F
  5. エラーと警告を解決します。

  6. リファクタリングの実施ごとにテストします。

    インターリーブされた複素数 API でコンパイル済みの MEX 関数を実行して、元のバイナリの結果と比較します。差異やエラーがある場合は、デバッガーを使用して原因を調べます。デバッガー機能の詳細については、コンパイラのドキュメンテーションを参照してください。

  7. MEX ヘルプ ファイルへのビルド情報の追加

mxIsComplex を使用して配列の実数/複素数を確認

配列に複素数要素があるかどうかを判定するために関数 mxGetPi を呼び出すコードの場合、代わりに関数 mxIsComplex を使用してください。この関数は -R2017b API と -R2018a API の両方でビルド可能です。コード内の次のパターンを検索します。

C ソース コードの置換変更後
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 */
}

両方の複素数表現をサポートするために MX_HAS_INTERLEAVED_COMPLEX を追加

-R2017b API と -R2018a API の両方でビルド可能なコードを作成するには、MX_HAS_INTERLEAVED_COMPLEX マクロを追加します。-R2018a オプションを使用して MEX ファイルをビルドすると、このマクロは true を返します。

次のコードを #if MX_HAS_INTERLEAVED_COMPLEX ステートメントでラップすると、このコードが -R2017b または -R2018a mex オプションのどちらかで確実にビルド可能になります。しかし、この例では、-R2018a を使用してビルドした場合に実行するコードがありません。

C ソース コードの置換変更後
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
}
Fortran ソース コードの置換変更後
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

型付きのデータ アクセス関数の使用

関数 mxGetData および mxGetImagData を使用するには、入力 mxArray の型を検証し、正しい型へのポインター出力を手動でキャストしなければなりません。型付きのデータ アクセス関数は、配列の型を検証し、正しいポインター型を返します。関数 mxGetInt16s および mxGetComplexInt16s を次のコードで使用して配列 int16 を処理する場合、対応する C の short int 型を記憶する必要はありません。

C ソース コードの置換変更後
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 */
        }
    }
}

複素 mxArrays の処理

次の例は、MATLAB® が 1 つの配列変数を使用してどのように複素配列を表すかを示します。

複素数 C mxArrays

次の複素 mxArray 変数があり、xy の実数と虚数を追加して配列 z を作成するとします。配列 x と配列 y は同じサイズです。

mxArray * x, y, z;

配列 x に 2 つのポインター xrxi を作成する代わりに、mxComplexDouble 型のポインター xc を 1 つ作成します。要素 xc[i] の実数部と虚数部にアクセスするには、xc[i].realxc[i].imag を使用します。

C ソース コードの置換変更後
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;

次のコードは、mxArray を出力引数にコピーします。このコードは、複素配列のテストとコピーの方法を示します。

C ソース コードの置換変更後
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];
}

複素数 Fortran mxArrays

2 つの double 型複素数の mxArrays があり、次のように定義された入力引数 xy を使用してそれらを Fortran 関数に渡すとします。

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

mxArray の実数部と虚数部を個別に変換するのではなく、関数 mxGetComplexDoubles を使用します。

Fortran ソース コードの置換変更後
      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

mxArray の実数/複素数の維持

次の C コード スニペットは、実数で double の入力配列 prhs[0] を複素数配列に変換する方法を示しています。次のコードでは、配列の虚数部を連続する数値で埋めるために使用される変数を設定します。

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

次のコードは、mxMakeArrayComplex を使用して、実数で double の入力配列をインターリーブされた複素数 mxArray に変換する方法を示しています。他の例については、mxMakeArrayComplex (C) を参照してください。

C ソース コードの置換変更後
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;
    }
}

実数/虚数分離型複素関数の置換

次の関数はインターリーブされた複素数 API にありません。複素数データを処理する場合は、インターリーブされた複素関数に置き換えなければなりません。

  • mxGetPi

  • mxSetPi

  • mxGetImagData

  • mxSetImagData

mxGetPr および mxGetPi への呼び出しを、mxGetComplexDoubles への呼び出しに置き換えることができます。この関数では、配列に mxComplexDouble 型の要素が含まれることが検証されます。同様に、mxSetPrmxSetPimxSetComplexDoubles に置き換わります。

関数 mxGetData および mxGetImagData は、配列の型をチェックしません。代わりに、ユーザーが戻り値を、入力によって指定される型と一致するポインター型にキャストしなければなりません。mxGetData および mxGetImagData への呼び出しを、mxGetComplexInt64s など、単一の適切な型付きデータ アクセス関数に置き換えてください。

C ソース コードの置換変更後
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);

mxGetElementSize を使用して配列のデータ サイズを計算

-R2018a API では、関数 mxGetElementSize (C) は、データ型 T の複素 mxArray に対して sizeof(std::complex<T>) を返します。この値は、-R2017b API の関数によって返される値の 2 倍です。同様に、-R2018a API の mxGetElementSize (Fortran) によって返される値は、-R2017b API の関数によって返される値の 2 倍となります。

廃止予定関数の置き換えの検討

次の関数は -R2017b API と -R2018a API の両方にあります。これらの関数を型付きデータ アクセス関数で置き換える必要はありませんが、型付きデータ関数は型チェックを提供します。また、mxGetPr を使用する場合に、複素配列処理に mxGetPi を選択するおそれがあります。このコード パターンは、-R2018a MEX 関数を作成する際にエラーになります。

mxGetPr への呼び出しを mxGetDoubles への呼び出しに置き換えることができます。この関数では、配列に mxDouble 型の要素が含まれることが検証されます。同様に、mxSetDoublesmxSetPr を置き換えます。関数 mxGetData および mxSetData への呼び出しを置き換えるには、mxGetInt64smxSetInt64s などの適切な型付きデータ アクセス関数を選択してください。

C ソース コードの置換変更後
double *y;
/*  create a pointer y to input matrix */
y = mxGetPr(prhs[1]);
mxDouble *p;
p = mxGetDoubles(prhs[1]);
Fortran ソース コードの置換変更後
      mwPointer pr
      mwPointer mxGetPr
C     Create a pointer to input matrix
      pr = mxGetPr(prhs(1))
      mwPointer pr
      mwPointer mxGetDoubles

      pr = mxGetDoubles(prhs(1))

MEX ヘルプ ファイルへのビルド情報の追加

ビルド情報が含まれるヘルプ ファイル (MEX 関数でのヘルプ ファイルの使用で説明) を作成することを検討してください。たとえば、以下のテキストを含むファイル displayTypesafeNumeric.m を作成します。

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

MATLAB コマンド プロンプトで、以下のように入力します。

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

参考

関連するトピック