12

As the title says, how can I add a trailing singleton dimension to a matrix in Matlab?

A=ones(3,3,1); gives a 3x3 matrix, while A=ones(1,3,3); gives a 1x3x3 matrix

Adding to the specific problem:

I have an application where I have N MxM matrices and I need to stack them, the result becoming a MxMxN matrix. However, N can be 1, and if it is, I need the matrix to be MxMx1.

Note: I am aware that this makes little sense for Matlab scripts, as Loren suggested, in Matlab there are infinite "singleton" dimensions after non singleton ones. However, this is otherwise in the mex environment, where mxGetNumberOfDimensions is used.

Ander Biguri
  • 35,140
  • 11
  • 74
  • 120
  • I thought a 3x3 matrix and a 3x3x1 3D array were really the same in Matlab. Isn't that the case in mex files? – Luis Mendo Sep 15 '15 at 10:13
  • Have you tried simply reshaping it to `[M M 1]`? – Andras Deak -- Слава Україні Sep 15 '15 at 10:18
  • @LuisMendo In mex files all data is (as it is internally in Matlab) a 1D array with dimensions data attached to it. While codding a mex wrapper, if you need to check if the data is correctly input, you need functions like `mxGetNumberOfDimensions` or `mxGetDimensions`. If an input has to be 3D but one of the dimensions can be 1, then `mxGetNumberOfDimensions==3` can not be used! – Ander Biguri Sep 15 '15 at 10:18
  • @AndrasDeak I tried indeed. Does not work. – Ander Biguri Sep 15 '15 at 10:19
  • @AnderBiguri Can't you just use `mxGetNumberOfDimensions<=3`? That would automatically declare an MxNx1 array as valid – Luis Mendo Sep 15 '15 at 10:21
  • @AnderBiguri, it works if the singleton is not the final one. Unfortunately it gets squeezed automatically if it's in the last position... There has to be a way. Probably with `bsxfun`;) – Andras Deak -- Слава Україні Sep 15 '15 at 10:21
  • @LuisMendo but if `mxGetNumberOfDimensions<3` then the user is inputing the data wrongly! Unless N=1, then is good. Basically I want the user to be very explicit of how its using my mex code, so if N=1, then the user needs to make sure there are 3dims. – Ander Biguri Sep 15 '15 at 10:22
  • @AnderBiguri So `N` is defined in advance? Can't you then use `if (N>1 && mxGetNumberOfDimensions==3) || (N==1 && mxGetNumberOfDimensions==2)`? – Luis Mendo Sep 15 '15 at 10:24
  • @LuisMendo I could, but N is not that easy to get in that part of the code. Basically I have a 400~ lines mex wrapper whos only purpose is to check if the inputs are correct, and then pass them to C. I could rearrange the whole code to get `N` earlier and then be able to do that, but its complicated. I also like the idea of "forcing" the user to do the rigth thing, so he/she learns how to use it properly. If this question has no solution, I will do what you suggested, but I preffer to add a singleton dimension. – Ander Biguri Sep 15 '15 at 10:27
  • If I understand correctly, the question then is, can mex distinguish between MxM and MxMx1? (Matlab can't) – Luis Mendo Sep 15 '15 at 10:29
  • @LuisMendo mex can. If the matrix were MxMx1, then `mxGetNumberOfDimensions=3`. My question is more if Matlab can do it. – Ander Biguri Sep 15 '15 at 11:07
  • 2
    Matlab's `ndim` function reads `Trailing singleton dimensions are ignored.` which suggests that it's possible to add singleton dimensions to arrays, or that sentence would be futile. I wouldn't know how though... Good question. For the moment, I would look for a solution on mex' site. – JJM Driessen Sep 15 '15 at 11:36
  • 1
    You say "I also like the idea of "forcing" the user to do the rigth thing, so he/she learns how to use it properly." -it sounds like you also want to force the user to do something in a non-Matlab way. In Matlab MxM is the same as MxMx1. Don't make more work for yourself. Just detail this in your help/documentation as is likely done for many built-in functions. – horchler Sep 15 '15 at 16:03
  • @horchler I may go that way, still I am interested if it is possible. – Ander Biguri Sep 15 '15 at 20:55
  • 1
    @AnderBiguri Regarding "_If the matrix were MxMx1, then mxGetNumberOfDimensions=3._", I do not think this is correct. Can you demonstrate this? – chappjc Sep 15 '15 at 21:31
  • 1
    @JJMDriessen I don't think that suggests that. What it suggests is that there are an infinite number of *implicit* trailing singleton dimensions, which are ignored by that function since they don't really exist. Try `size(M,454235)` for any `M`. – chappjc Sep 15 '15 at 22:14

2 Answers2

11

The trailing singleton is dropped by MATLAB even from the point of view of the MEX API. The docs for mxSetDimensions say as much:

MATLAB® automatically removes any trailing singleton dimensions specified in the dims argument. For example, if ndim equals 5 and dims equals [4 1 7 1 1], the resulting array has the dimensions 4-by-1-by-7.

Again, that's from the mxSetDimensions docs.

Here's a test to try this with mxSetDimensions or mxCreateNumericArray:

// mexSizeTest.cpp
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[],int nrhs, const mxArray *prhs[])
{
    if (nrhs!=1)
        mexErrMsgTxt("Just one input please.");

    mxArray *M = mxDuplicateArray(prhs[0]);
    const mwSize ndimsOrig = mxGetNumberOfDimensions(M);
    const mwSize *dimsOrig = mxGetDimensions(M);

    mexPrintf("Number of dimensions (input): %d\n\n", ndimsOrig);

    const mwSize ndims = ndimsOrig + 1;
    mwSize *dims = (mwSize*) mxMalloc(ndims*sizeof(mwSize));
    for (int i=0; i<ndimsOrig; ++i) dims[i] = dimsOrig[i];
    dims[ndims-1] = 1;

    mexPrintf("Target dimensions: [");
    for (int i=0; i<ndims-1; ++i) mexPrintf("%d ",dims[i]);
    mexPrintf("%d]\n\n",dims[ndims-1]);

    mexPrintf("Reshaping to #dims = %d with trailing singleton.\n", ndims);
    mxSetDimensions(M, dims, ndims);
    mexPrintf("Number of Dimensions: %d\n\n", mxGetNumberOfDimensions(M));

    // Let's be dangerous to see if the 1 is just hiding under the hood
    const mwSize *dimsSet = mxGetDimensions(M);
    mexPrintf("Being dangerous: %d\n\n", dimsSet[ndims-1]); // !!!
    mxDestroyArray(M);

    mexPrintf("Creating fresh mxArray of #dims = %d with trailing singleton.\n",
            ndims);
    M = mxCreateNumericArray(ndims, dims, mxDOUBLE_CLASS, mxREAL);
    mexPrintf("Number of Dimensions: %d\n",mxGetNumberOfDimensions(M));
    mxDestroyArray(M); mxFree(dims);
}

The MATLAB test:

>> M = rand(24,55,1);
>> size(M)
ans =
    24    55
>> ndims(M)
ans =
     2
>> size(M,454235) % note we can check the 454235th dimension
ans =
     1

Side note with that size(M,454235) test: This is what the ndim docs mean when they say Trailing singleton dimensions are ignored. They're really not ignored, they just aren't really there!

The MEX test (mexSizeTest.cpp):

>> mexSizeTest(M)
Number of dimensions (input): 2

Target dimensions: [24 55 1]

Reshaping to #dims = 3 with trailing singleton.
Number of Dimensions: 2

Being dangerous: -994713024

Creating fresh mxArray of #dims = 3 with trailing singleton.
Number of Dimensions: 2

I suggest to adapt your code to handle the case where mxGetNumberOfDimensions returns 2.

chappjc
  • 30,359
  • 6
  • 75
  • 132
  • @AnderBiguri Glad that helps. Note I fixed a mistake (it's now like it is on my machine, not a cut-paste Frankenstein, so the results are still correct). – chappjc Sep 15 '15 at 22:38
5

You can use permute to inject a trailing default singleton dimension into a non-trailing position. For instance:

A = rand(50,50); % produces a 50 x 50 matrix
B = permute(A, [1 3 2]); % produces a 50 x 1 x 50 array

Here, 3 refers to the third dimension in the array, an implicit dimension of size 1.

Alan
  • 1,889
  • 2
  • 18
  • 30
jiggunjer
  • 1,905
  • 1
  • 19
  • 18
  • No sorry, you missed the point of the question. Also, it was answered and accepted 2 years ago. – Ander Biguri Sep 28 '17 at 10:41
  • 1
    No I am answering the question in the title, which Google used to bring me here before I had the answer. You don't understand SO if you think it matters that an answer was accepted 2 years ago. I'll suffer downvotes from people like you so I can help the next Googler. – jiggunjer Sep 28 '17 at 11:27
  • 1
    Sorry, I did not meant to offend you. I did ask the question, I know it matters because that is the answer of the question in the body. I should add a word to the title to make it clearer perhaps, but the body asks about a *trailing* singleton dimension. I donwvoted because regardless of what google says, this does not answer the question I asked. – Ander Biguri Sep 28 '17 at 12:06
  • 3
    Thanks jiggunjer. I, too, got here via Google, and your answer is the one I needed. – Zac-K Apr 22 '18 at 10:16