使用 mexCallMATLAB 时避免复制数组

AFP*_*AFP 3 arrays matlab mex memcpy

我已经为 MATLAB 编写了一个 mex 文件。它调用 MATLABpinv函数来计算摩尔彭罗斯伪逆。我将这个函数命名为my_pinvmy_pinv获取一个数组并返回其伪逆,完全类似于pinv

A = magic(8); A = A(:,1:6)
b = 260*ones(8,1)
x = my_pinv(A)*b
Run Code Online (Sandbox Code Playgroud)

但是,在 mex 文件中,我必须复制输入数组的值才能使用mexCallMATLAB. 以下是内容my_pinv.cpp

#include <matrix.h>
#include <mex.h>
#include <string.h>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    #define PRHS_A prhs[0]
    #define PLHS_X plhs[0] 

    int M = mxGetM( PRHS_A ); // Get the dimensions of  A.
    int N = mxGetN( PRHS_A );

    double *A_ptr = mxGetPr( PRHS_A );
    mxArray *PINV_A =  mxCreateDoubleMatrix(M, N,  mxREAL);  /* Put input in an mxArray */
    memcpy(mxGetPr(PINV_A), A_ptr,  sizeof(double)*M*N);

    PLHS_X = mxCreateDoubleMatrix(N, M, mxREAL);  // Create the output matrix.

    mexCallMATLAB(1, &PLHS_X, 1, &PINV_A, "pinv");

}
Run Code Online (Sandbox Code Playgroud)

无论如何我跳过使用memcpy并直接使用输入数组, prhs[0], in mexCallMATLAB?我实际上不喜欢需要复制输入数组的值这一事实,尤其是当输入数组非常大时。

事实上,我希望能够使用类似

mexCallMATLAB(1, &PLHS_X, 1, &RHS_A, "pinv"); // (I know it is not right and the compiler would not like it but it is for the sake of example)
Run Code Online (Sandbox Code Playgroud)

而不是

mexCallMATLAB(1, &PLHS_X, 1, &PINV_A, "pinv");
Run Code Online (Sandbox Code Playgroud)

有人可以分享他/她在这方面的经验吗?

Amr*_*mro 5

mexCallMATLAB 具有以下签名:

int mexCallMATLAB(int nlhs, mxArray *plhs[], int nrhs,
    mxArray *prhs[], const char *functionName);
Run Code Online (Sandbox Code Playgroud)

出于某种原因,RHS 数组未标有const限定符。我不知道为什么......这解释了为什么你会收到编译错误:

// this is from Visual C++ 2013
error C2664: 'int mexCallMATLAB(int,mxArray*[],int,mxArray *[],const char *)' :
cannot convert argument 4 from 'const mxArray *[]' to 'mxArray *[]'
    Conversion loses qualifiers
Run Code Online (Sandbox Code Playgroud)

解决方案是显式抛弃常量,告诉编译器我们知道我们在做什么:

my_pinv.cpp

#include "mex.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    // validate arguments
    if(nrhs != 1 || nlhs > 1)
        mexErrMsgIdAndTxt("mex:error", "Wrong number of arguments.");
    //perhaps do more validations here..

    // out = pinv(in)
    mexCallMATLAB(1, plhs, 1, const_cast<mxArray**>(prhs), "pinv");
}
Run Code Online (Sandbox Code Playgroud)

现在在 MATLAB 中:

>> x = rand(4,3);
>> my_pinv(x) - pinv(x)
ans =
     0     0     0     0
     0     0     0     0
     0     0     0     0
Run Code Online (Sandbox Code Playgroud)

如果出于某种原因并且在某些极端情况下这证明有问题(我对此表示怀疑),则更安全的方法是使用以下方法复制数组:

mxArray *in = mxDuplicateArray(prhs[0]);
mexCallMATLAB(1, plhs, 1, &in, "pinv");
mxDestroyArray(in);
Run Code Online (Sandbox Code Playgroud)

如果您绝对想避免创建深层副本,则有一些未公开的函数可以创建共享数据副本(其中仅创建一个新的数组头,但数据是共享的)。