2

I would like to pass data between Eigen Matrix/Vector and mex arrays. In the following code, I defined a mex array called y_output, which contains a cell array. The variable y_output will be passed to MATLAB. Each element in y_output is a vector but with different lengths. I would like to pass a pointer that points to Eigen vectors to the mex array y_output.

Notice that the data stored in y will be modified with a user-defined function. After calling the function, I would assume that the data stored in y_output will be modified corresponding. However, I cannot directly pass the pointer from y_output to y. Is there any way to make it possible? Thanks!

This question is similar but different from the one at Pass C++ Eigen matrix to Matlab mex output. This question is asking how to pass an array of matrices, while the question in that link is asking how to pass a matrix.

#include "mex.h"
#include "matrix.h"
#include <Eigen>


void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{


// prhs[0]: a cell array of length T, each element is a vector with different lengths
mwSize T =  mxGetNumberOfElements(prhs[0]);
mwSize* n = new mwSize[T];
Eigen::VectorXd* z = new Eigen::VectorXd[T];

for(int t=0; t<T; t++){
    n[t] = mxGetNumberOfElements(mxGetCell(prhs[0], t));
    z[t] = Eigen::Map<Eigen::VectorXd>(mxGetPr(mxGetCell(prhs[0], t)), n[t]);
}

// create a cell matrix with T rows and one columns
mxArray* y_output = mxCreateCellMatrix(T,1);

// create corresponding Eigen objects
Eigen::VectorXd* y = new Eigen::VectorXd[T]();

Eigen::VectorXd y_temp(n[0]); y_temp.setZero();

for(int t=0; t<T; t++){
    mxSetCell(y_output, t, mxCreateDoubleMatrix(n[t], 1, mxREAL));

    y[t] = Eigen::VectorXd::Zero(n[t]);
    y_temp.resize(n[t]);

    Eigen::Map<Eigen::VectorXd> y[t](mxGetPr(mxGetCell(y_output, t)), n[t]); // This is not correct!



}


// Myfun(y, z);

// set output
plhs[0] = y_output;

}
Bayes
  • 45
  • 1
  • 11
  • I think you misunderstood what `Eigen::Map` does. It can interpret memory allocated by others as Eigen objects, but not the other way around. – chtz Mar 09 '19 at 14:54
  • Thanks for the comment. Did you mean that I cannot pass the pointer to the content in `y_output` that is a vector of doubles to Eigen Vector `y[t]`? With the following code, I can copy the data, but I do not want just to copy the data but also to modify the content in `y[t]` so that the content in `y_output` is also modified. `y[t] = Eigen::Map(mxGetPr(mxGetCell(y_output, t)), n[t]);` – Bayes Mar 09 '19 at 15:21
  • Possible duplicate of [Pass C++ Eigen matrix to Matlab mex output](https://stackoverflow.com/questions/43163426/pass-c-eigen-matrix-to-matlab-mex-output) – Cris Luengo Mar 09 '19 at 17:10
  • In the linked duplicate I explain how to avoid a copy by first allocating a MATLAB array of the right size, using `Eigen::Map` to create an Eigen matrix or vector over that, and using that in your Eigen code to put the answer in. This question adds the complication of the cell array, but it seems you know how to create such an array already. – Cris Luengo Mar 09 '19 at 17:13
  • @CrisLuengo It is almost a duplicate, though as you said yourself, this question wants not to only pass a single matrix, but an array of matrices ... – chtz Mar 09 '19 at 20:32
  • @chtz: It is not about the questions being identical, it is about the answers being identical. Instead of copy-pasting that answer here, we vote to close as duplicate. We try to avoid duplicating contents. Questions marked as duplicate are valuable, they allow people to find their answers more easily. But duplicate answers are pointless. – Cris Luengo Mar 09 '19 at 22:39

2 Answers2

1

I have figured a way to achieve what I wanted to. The crucial step is to call my own function first, and in the end, assign the data stored in y to y_output. I included the modified code below so that it can be helpful to anyone who is interested in.

#include "mex.h"
#include "matrix.h"
#include <Eigen>


void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{


// prhs[0]: a cell array of length T, each element is a vector with different lengths
mwSize T =  mxGetNumberOfElements(prhs[0]);
mwSize* n = new mwSize[T];
Eigen::VectorXd* z = new Eigen::VectorXd[T];

for(int t=0; t<T; t++){
    n[t] = mxGetNumberOfElements(mxGetCell(prhs[0], t));
    z[t] = Eigen::Map<Eigen::VectorXd>(mxGetPr(mxGetCell(prhs[0], t)), n[t]);
}

// create a cell matrix with T rows and one columns
mxArray* y_output = mxCreateCellMatrix(T,1);

// create corresponding Eigen objects
Eigen::VectorXd* y = new Eigen::VectorXd[T]();


 // Myfun(y, z);

double* ptemp;
for(int t=0; t<T; t++){
    mxSetCell(y_output, t, mxCreateDoubleMatrix(n[t], 1, mxREAL));

    ptemp = mxGetPr(mxGetCell(y_output, t));
    // assign the data stored in y[t] to the contents in y_output.
    for(int i=0; i<n[t]; i++){
        //mxGetPr(mxGetCell(y_output, t))[i] = y[t](i);
        ptemp[i] = y[t](i);
    }


}

ptemp = NULL;


// set output
plhs[0] = y_output;

}
Bayes
  • 45
  • 1
  • 11
  • 1
    You don’t need to copy, though this certainly works. – Cris Luengo Mar 09 '19 at 17:09
  • Thanks for the suggestion!@CrisLuengo. I have updated my code to avoid repeated call to the `mxGetCell`. I was trying to avoid copying the data between cell array `y_output` and `y`, but I was failed to do so in my original question. That's why I came up with this compromise. – Bayes Mar 09 '19 at 17:59
1

Your solution copies data around both for the input and the output of MyFunc. It is possible to pass both arguments using a std::vector of Eigen::Map objects. The following code is based on your answer. Lines starting with //-- were removed from your code and replaced by the line which follow.

As a sidenote: Avoid allocating arrays with new (in 99.99% of all cases), but use a std::vector instead. This takes care of deallocating all resources when the object gets out of scope. Also, in MyFunc you don't have to guess the size of y and z.

#include "mex.h"
#include "matrix.h"
//-- #include <Eigen> <-- this should be <Eigen/Eigen>, but likely Eigen/Core suffices
//                        Perhaps you also need to change your include-path 
#include <Eigen/Core>

#include <vector>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{

    // prhs[0]: a cell array of length T, each element is a vector with different lengths
    mwSize T =  mxGetNumberOfElements(prhs[0]);
    //-- mwSize* n = new mwSize[T];
    std::vector<mwSize> n(T);
    //-- Eigen::VectorXd* z = new Eigen::VectorXd[T];
    std::vector<Eigen::Map<const Eigen::VectorXd> > z; // input vector of Maps
    z.reserve(T);

    for(int t=0; t<T; t++){
        // Note: You don't actually seem to need n[t], except for creating y_output
        n[t] = mxGetNumberOfElements(mxGetCell(prhs[0], t));
        //-- z[t] = Eigen::Map<Eigen::VectorXd>(mxGetPr(mxGetCell(prhs[0], t)), n[t]);
        z.emplace_back(mxGetPr(mxGetCell(prhs[0], t)), n[t]);
    }

    // create a cell matrix with T rows and one columns
    mxArray* y_output = mxCreateCellMatrix(T,1);

    // create corresponding Eigen objects
    //-- Eigen::VectorXd* y = new Eigen::VectorXd[T]();
    std::vector<Eigen::Map<Eigen::VectorXd> > y; // output vector of Maps
    y.reserve(T);

    // This must be called after setting up y: 
    //-- Myfun(y, z);

    //-- double* ptemp;
    for(int t=0; t<T; t++){
        mxSetCell(y_output, t, mxCreateDoubleMatrix(n[t], 1, mxREAL));

        //-- ptemp = mxGetPr(mxGetCell(y_output, t));
        //-- // assign the data stored in y[t] to the contents in y_output.
        //-- for(int i=0; i<n[t]; i++){
        //--     //mxGetPr(mxGetCell(y_output, t))[i] = y[t](i);
        //--     ptemp[i] = y[t](i);
        //-- }
        y.emplace_back(mxGetPr(mxGetCell(y_output, t)), n[t]);
    }

    // Now call Myfun, but the function now needs to accept vectors of Eigen::Map, instead of pointers to VectorXd
    // It should be possible to keep the Code inside Myfun unchanged
    // Myfun(y, z);

    //-- ptemp = NULL;

    // set output
    plhs[0] = y_output;

}
chtz
  • 17,329
  • 4
  • 26
  • 56
  • Thank you so much for helping me out on this. This is exactly what I was looking for. The point of using `std::vector` is taken. I need to learn more about C++. – Bayes Mar 09 '19 at 22:20