Как перебрать матричные элементы в функции mex C++ для MATLAB?

Я пытаюсь индексировать написать внешнюю функцию C++ для MATLAB, используя mex для работы с матрицами, и не могу использовать многомерное индексирование. Здесь приведены примеры, но я не нашел, как решить проблему, описанную ниже. У меня есть образец матрицы:

>> mat
mat =
 1    10
 2    20
 3    30
 4    40
 5    50

В настоящее время я использую линейный индекс через матрицу, которая работает:

#include <mex.h>
#include <iostream>
using namespace std;
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 
{ 
 //1.get pointer to input graph_list and allocate it    
    double *graph_list = mxGetPr(prhs[0]);
    mwSize mrows = mxGetM(prhs[0]);
    mwSize ncols = mxGetN(prhs[0]);  
    cout<< mrows<<" rows\n";
    cout<< ncols<<" cols\n";
    int mm, nn;
    for (nn=0;nn<ncols;nn++) {
        for (mm=0;mm<mrows;mm++){
            cout << graph_list[nn*(mrows) +mm]  <<"\n";            
        }
    }     
}

Это производит:

>> mexTryAlex(mat)    
5 rows
2 cols
1
2
3
4
5
10
20
30
40
50

Когда я изменяю определение graph_list и пытаюсь выполнить двумерную индексацию на graph_list, возникает ошибка компиляции с mex:

double **graph_list = mxGetPr(prhs[0]);
cout << graph_list[nn][mm];

РЕДАКТИРОВАТЬ: здесь получено сообщение об ошибке

>> mex mexTryAlex.cpp
Warning: You are using gcc version "4.4.3-4ubuntu5)".  The version
     currently supported with MEX is "4.3.4".
     For a list of currently supported compilers see: 
     http://www.mathworks.com/support/compilers/current_release/
mexTryAlex.cpp: In function ‘void mexFunction(int, mxArray**, int, const mxArray**)’:
mexTryAlex.cpp:16: error: cannot convert ‘double*’ to ‘double**’ in initialization
mex: compile of ' "mexTryAlex.cpp"' failed.
??? Error using ==> mex at 208
Unable to complete successfully.

3 ответа

Решение

Компилятор говорит все это.

В C двумерный массив похож на массив массивов. Следовательно, двумерный массив принципиально отличается от одномерного; это массив указателей, в котором каждый элемент содержит указатель на массив (следовательно, двойной указатель, double**).

Ты спрашиваешь mxGetPr() вернуть double**, но возвращает double*Например, указатель на первый элемент одномерного массива. Этот одномерный массив может быть проиндексирован только линейно.

Я предполагаю, что MATLAB делает это таким образом, чтобы поддерживать согласованность индексирования массивов - действительно ли вы ожидаете / хотите получить double**** для 4-D массива?

Более того, mxGetPr() не может быть перегружен типом возвращаемого значения (в конце концов, это C).

Чтобы иметь возможность двойного индексирования одномерного массива, вы можете прокрасться в небольшом макросе:

#define A(i,j) A[(i) + (j)*numrows]

и использовать это так

double *A = mxGetPr(...);
int numrows = 4;   /* or get with mxGetM() or so) */

double blah = A(3,2); /* call to MACRO */

Очевидно, что, как и во всех макросах, есть несколько вещей, на которые нужно обратить внимание:

  1. нет проверки границ
  2. C основан на 0, а Matlab 1, что делает все индексы различными
  3. Все массивы должны называться "А"

Вы можете написать функцию для устранения этих недостатков:

double getValue(double** array, int row, int* dims);

(или использовать mxCalcSingleSubscript как указал Shai), но это не улучшает выразительную силу ИМХО:

double blah = getValue(array, 3,4, dims);
/* or the ugliness from mxCalcSingleSubscript(); */

Вы также можете написать на C++, сделать класс типа Matrix с operator(), построить его с указателем и размерами от mxGetPr() а также mxGetDims() и т.д., скомпилируйте в Matlab используя g++ или эквивалент, но это вносит целый ряд других проблем и добавляет гораздо больше сложности, чем необходимо для большинства случаев.

Поэтому, чтобы избежать всего этого беспорядка, я просто всегда вычисляю индекс на месте:)

Наличие матричного класса является наиболее простым способом решения подобных проблем. Есть из чего выбирать, поэтому не пишите самостоятельно. Armadillo довольно хорош и также интегрируется с LAPACK, если вы используете это. http://arma.sourceforge.net/docs.html

Смотрите пример ниже

#include <mex.h>
#include <iostream>
#include <armadillo>
using namespace std;
using namespace arma;

//creates an armadillo matrix from a matlab matrix
mat armaMatrix(const mxArray *matlabMatrix[]){
    mwSize mrows = mxGetM(matlabMatrix[0]);
    mwSize ncols = mxGetN(matlabMatrix[0]);
    double *values = mxGetPr(matlabMatrix[0]);

    return mat(values, nrows, ncols);
}

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 
{
    mat graph_list = armaMatrix(prhs);

    //print the matrix
    cout << graph_list<<"\n";
    //print the first column
    cout << graph_list(span::all,0) <<"\n";
}

Как указал Роди, mxGetPr возвращает указатель на одномерный массив. Следовательно, вы не можете рассматривать его как двумерный массив в C++.
Что вы можете сделать, это использовать mxCalcSingleSubscript функция для преобразования подписок ND в один одномерный индекс.

Другие вопросы по тегам