Литье комплекс в реал без копирования данных в MATLAB R2018a и новее - PullRequest
0 голосов
/ 03 октября 2018

Поскольку MATLAB R2018a , комплекснозначные матрицы хранятся внутри как один блок данных, а действительный и мнимый компоненты каждого элемента матрицы хранятся рядом друг с другом - они называют этот «чередующийся комплекс»,(Раньше такие матрицы имели два блока данных, один для всех реальных компонентов, один для всех мнимых компонентов - «отдельный комплекс».)

Я полагаю, поскольку хранилище теперь позволяет это сделать, что это должно быть возможнопреобразовать массив с комплексным значением в массив с вещественным значением, содержащий в два раза больше элементов, без копирования данных.

В MATLAB есть функция typecast, которая преобразует массив вдругой тип без копирования данных.Его можно использовать, например, для приведения массива с 16 8-битными значениями в массив с 2 двойными числами с плавающей запятой.Это происходит без копирования данных, битовая комбинация интерпретируется как новый тип.

К сожалению, эта функция вообще не работает с массивами со сложными значениями.

Янамереваясь повторить этот код:

A = fftn(randn(40,60,20)); % some random complex-valued array
assert(~isreal(A))

sz = size(A);
B = reshape(A,1,[]);        % make into a vector
B = cat(1,real(B),imag(B)); % interleave real and imaginary values
B = reshape(B,[2,sz]);      % reshape back to original shape, with a new first dimension
assert(isreal(B))

Матрицы A и B содержат (в R2018a и новее) точно такие же данные в абсолютно одинаковом порядке.Однако, чтобы добраться до B, нам пришлось дважды скопировать данные.

Я попытался создать MEX-файл, который делает это, но я не вижу, как создать новый массив, который ссылается на данные ввходной массив.Этот MEX-файл работает, но вызывает сбой MATLAB при очистке переменных, потому что есть два массива, которые ссылаются на одни и те же данные, не осознавая, что они совместно используют данные (т. Е. Счетчик ссылок не увеличивается).

// Build with:
//    mex -R2018a typecast_complextoreal.cpp

#include <mex.h>

#if MX_HAS_INTERLEAVED_COMPLEX==0
#error "This MEX-file must be compiled with the -R2018a flag"
#endif

#include <vector>

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

   // Validate input
   if(nrhs != 1) {
      mexErrMsgTxt("One input argument expected");
   }
   if(!mxIsDouble(prhs[0]) && !mxIsSingle(prhs[0])) {
    mexErrMsgTxt("Only floating-point arrays are supported");
   }

   // Get input array sizes
   mwSize nDims = mxGetNumberOfDimensions(prhs[0]);
   mwSize const* inSizes = mxGetDimensions(prhs[0]);

   // Create a 0x0 output matrix of the same type, but real-valued
   std::vector<mwSize> outSizes(nDims + 1, 0);
   plhs[0] = mxCreateNumericMatrix(0, 0, mxGetClassID(prhs[0]), mxREAL);

   // Set the output array data pointer to the input array's
   // NOTE! This is illegal, and causes MATLAB to crash when freeing both
   // input and output arrays, because it tries to free the same data
   // twice
   mxSetData(plhs[0], mxGetData(prhs[0]));

   // Set the output array sizes
   outSizes[0] = mxIsComplex(prhs[0]) ? 2 : 1;
   for(size_t ii = 0; ii < nDims; ++ii) {
      outSizes[ii + 1] = inSizes[ii];
   }
   mxSetDimensions(plhs[0], outSizes.data(), outSizes.size());
}

Я хотел бы услышать о любых идеях о том, как действовать отсюда.Мне не обязательно исправлять MEX-файл, если решение - просто код MATLAB, тем лучше.

Ответы [ 3 ]

0 голосов
/ 08 октября 2018

Вот формулировка из документации :

Поскольку многие математические библиотеки используют чередованное сложное представление, использование одного и того же представления в ваших функциях MEX устраняет необходимость в переводе данных.,Это упрощает ваш код и потенциально ускоряет обработку, когда задействованы большие наборы данных.

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

typedef struct {
    double real;
    double imag;
} mxComplexDouble ;

Итак, мы хотим (интерпретировать) приведение массива mxComplexDouble к другому двойному сложному типу, который используется в математической библиотеке.

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

typedef struct {
    double real;
    double imag;
} other_complex ;

И она определила функцию для использования массива своего собственного комплексного типа.

void do_something(const other_complex* math_array);

Здесь мы хотим отправить комплексный массив MATLAB в математическую библиотеку:

mxComplexDouble matlab_array[5];
other_complex* math_array =  (other_complex*)matlab_array;
do_something(math_array);

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

Но в отношении строгого правила наложения имен * любое использование math_array приводит к неопределенному поведению .Даже приведение к массиву двойных чисел запрещено:

double* double_array = (double*)matlab_array; 
printf("%f",double_array[0]); // Unedfined behavior

Для этого нам нужно выделить новый массив и скопировать байт данных, используя байт memcpy.Это безопасный способ предотвращения неопределенного поведения.

Единственное исключение из правила строгого псевдонима - это стандартные сложные типы данных, которые определены в заголовках complex.h и complex в c и c ++ соответственно и могут быть определены только комплексные типы с плавающей запятой [float, double и long double].Таким образом, мы можем безопасно привести std::complex<double>* к double*.

Выводы:

  1. typecast может быть запрещено для новых сложных типов данных MATLAB из-за строгое правило псевдонимов , но, как объяснено, новые сложные типы данных не могут быть использованы так дешево в других библиотеках.Только использование memcpy может рассматриваться как эффективный способ копирования всех данных.

  2. Использование typcast для других типов данных представляется сомнительным.Я могу (и, вероятно, должен) предположить, что MATLAB использовал некоторые приемы компилятора для предотвращения неопределенного поведения, в противном случае, когда мы обращаемся к элементу данных, если MATLAB не копировал его побайтово, то это приводит к неопределенному поведению. (Обратите внимание, что в любом случаеприведение между совместимыми типами, такими как int32 и uint32, а также приведение любого типа к типу c ++ char имеет четко определенное поведение).Более того, требуется, чтобы все mex-файлы были скомпилированы с соответствующей опцией, чтобы отключить строгий псевдоним.Но в настоящее время у нас есть много скомпилированных mex-файлов, которые, если мы отправим ему результат typecast, приведут к неопределенному поведению.Поэтому использование typecast должно быть максимально ограничено.

* Я нашел несколько блогов, которые лучше объясняют концепцию, чем пост SO.Например, здесь или здесь или здесь .

0 голосов
/ 13 октября 2018

См. Это представление FEX, которое может сделать комплекс -> 2 дает реинтерпретацию без копирования данных (оно может даже указывать на внутренние смежные подразделы данных без копии):

https://www.mathworks.com/matlabcentral/fileexchange/65842-sharedchild-creates-a-shared-data-copy-of-a-contiguous-subsection-of-an-existing-variable

0 голосов
/ 07 октября 2018

Этот вопрос напомнил мне о некоторых сообщениях в блоге, касающихся редактирования памяти на месте с помощью MEX, о которых было сказано следующее:

[M] Одификация исходных данных напрямую не рекомендуетсяи официально не поддерживается.Неправильное выполнение может легко привести к падению Matlab. ref

Это, в лучшем случае,бесполезный беспорядок. ref

Сказав это, у меня нет решения для вас, но я мог бы предложитьОбходной путь.

Видя, как MATLAB позволяет нам вызывать библиотеки Python, мы можем выполнять такого рода манипуляции внутри Python и возвращать данные обратно в MATLAB только тогда, когда мы достигаем стадии, когда выполнение в Python невозможно или нежелательно.,В зависимости от того, когда этот этап может быть, эта идея может быть либо действительным подходом, либо совершенно бесполезным предложением.

Взгляните на пример ниже, он должен быть довольно очевидным:

np = py.importlib.import_module('numpy');
sp = py.importlib.import_module('scipy.fftpack');

% Create a double array in python:
arrC = sp.fftn(np.random.rand(uint8(4), uint8(3), uint8(2)));
%{
arrC = 
  Python ndarray with properties:

           T: [1×1 py.numpy.ndarray]
        base: [1×1 py.NoneType]
      ctypes: [1×1 py.numpy.core._internal._ctypes]
        data: [1×4 py.memoryview]
       dtype: [1×1 py.numpy.dtype]
       flags: [1×1 py.numpy.flagsobj]
        flat: [1×1 py.numpy.flatiter]
        imag: [1×1 py.numpy.ndarray]
    itemsize: [1×1 py.int]
      nbytes: [1×1 py.int]
        ndim: [1×1 py.int]
        real: [1×1 py.numpy.ndarray]
       shape: [1×3 py.tuple]
        size: [1×1 py.int]
     strides: [1×3 py.tuple]
    [[[ 13.99586491+0.j           0.70305071+0.j        ]
      [ -1.33719563-1.3820106j   -0.74083670+0.25893033j]
      [ -1.33719563+1.3820106j   -0.74083670-0.25893033j]]

     [[ -0.43914391+0.8336674j    0.08835445-0.50821244j]
      [  1.07089829-0.35245746j   0.44890850-0.9650458j ]
      [  2.09813180+1.34942678j  -1.20877832+0.71191772j]]

     [[ -2.93525342+0.j          -0.69644042+0.j        ]
      [  0.16165913-1.29739125j  -0.84443177+0.26884365j]
      [  0.16165913+1.29739125j  -0.84443177-0.26884365j]]

     [[ -0.43914391-0.8336674j    0.08835445+0.50821244j]
      [  2.09813180-1.34942678j  -1.20877832-0.71191772j]
      [  1.07089829+0.35245746j   0.44890850+0.9650458j ]]]
%}

% Make sure that python sees it as a "complex double" (aka complex128)
assert( isequal(arrC.dtype, np.dtype(np.complex128)) );

% Return a (real) double view:
arrR = arrC.view(np.float64);
%{
arrR = 
  Python ndarray with properties:

           T: [1×1 py.numpy.ndarray]
        base: [1×1 py.numpy.ndarray]
      ctypes: [1×1 py.numpy.core._internal._ctypes]
        data: [1×4 py.memoryview]
       dtype: [1×1 py.numpy.dtype]
       flags: [1×1 py.numpy.flagsobj]
        flat: [1×1 py.numpy.flatiter]
        imag: [1×1 py.numpy.ndarray]
    itemsize: [1×1 py.int]
      nbytes: [1×1 py.int]
        ndim: [1×1 py.int]
        real: [1×1 py.numpy.ndarray]
       shape: [1×3 py.tuple]
        size: [1×1 py.int]
     strides: [1×3 py.tuple]
    [[[ 13.99586491   0.           0.70305071   0.        ]
      [ -1.33719563  -1.3820106   -0.7408367    0.25893033]
      [ -1.33719563   1.3820106   -0.7408367   -0.25893033]]

     [[ -0.43914391   0.8336674    0.08835445  -0.50821244]
      [  1.07089829  -0.35245746   0.4489085   -0.9650458 ]
      [  2.0981318    1.34942678  -1.20877832   0.71191772]]

     [[ -2.93525342   0.          -0.69644042   0.        ]
      [  0.16165913  -1.29739125  -0.84443177   0.26884365]
      [  0.16165913   1.29739125  -0.84443177  -0.26884365]]

     [[ -0.43914391  -0.8336674    0.08835445   0.50821244]
      [  2.0981318   -1.34942678  -1.20877832  -0.71191772]
      [  1.07089829   0.35245746   0.4489085    0.9650458 ]]]
%}

% Do something else with it in python
...

% Bring data to MATLAB:
sz = cellfun(@int64, cell(arrR.shape));
B = permute(reshape(double(py.array.array('d', arrR.flatten('C').tolist())),sz),[2,1,3]);

Последний этап может быть недостаточно эффективным, чтобы свести на нет выигрыш в производительности / памяти, поскольку ранее данные не копировались, поэтому, вероятно, было бы неплохо сохранить его для самого конца и максимально сократить количество данных заранее..


Экспериментируя с этим, я понял, что существует большая сложность 1 , связанная с передачей нескалярных complex данных из MATLAB в Python.и обратно (просто сравните вывод MATLAB для np.asarray(1+1i) против np.asarray([1+1i, 1+1i])).Возможно, причиной этого является ограниченная поддержка complex non- ndarray массивов в python.

Если , вы (в отличие от меня) знаете, что делать с memoryview объектами (т. Е. С содержимым поля data поля ndarrayобъекты) - вы можете получить указатель и, возможно, передать его C, чтобы получить полезные результаты.


1 Это возможно, но вам нужно num2cell ваши данные такчто он может быть передан в виде списка Python (и наоборот).Ситуацию также можно улучшить, отредактировав некоторые файлы MATLAB .

...