10

I have a non-fixed dimensional matrix M, from which I want to access a single element. The element's indices are contained in a vector J.

So for example:

M = rand(6,4,8,2);
J = [5 2 7 1];

output = M(5,2,7,1)

This time M has 4 dimensions, but this is not known in advance. This is dependent on the setup of the algorithm I'm writing. It could likewise be that

M = rand(6,4);
J = [3 1];

output = M(3,1)

so I can't simply use

output=M(J(1),J(2))

I was thinking of using sub2ind, but this also needs its variables comma separated..

@gnovice

this works, but I intend to use this kind of element extraction from the matrix M quite a lot. So if I have to create a temporary variable cellJ every time I access M, wouldn't this tremendously slow down the computation??

I could also write a separate function

function x= getM(M,J)
    x=M(J(1),J(2));
    % M doesn't change in this function, so no mem copy needed = passed by reference
end

and adapt this for different configurations of the algorithm. This is of course a speed vs flexibility consideration which I hadn't included in my question..

BUT: this is only available for getting the element, for setting there is no other way than actually using the indices (and preferably the linear index). I still think sub2ind is an option. The final result I had intended was something like:

function idx = getLinearIdx(J, size_M)
    idx = ...
end

RESULTS:

function lin_idx = Lidx_ml( J, M )%#eml
%LIDX_ML converts an array of indices J for a multidimensional array M to
%linear indices, directly useable on M
%
% INPUT
%   J       NxP matrix containing P sets of N indices
%   M       A example matrix, with same size as on which the indices in J
%           will be applicable.
%
% OUTPUT
%   lin_idx Px1 array of linear indices
%

% method 1
%lin_idx = zeros(size(J,2),1);
%for ii = 1:size(J,2)
%    cellJ = num2cell(J(:,ii)); 
%    lin_idx(ii) = sub2ind(size(M),cellJ{:}); 
%end

% method 2
sizeM = size(M);
J(2:end,:) = J(2:end,:)-1;
lin_idx = cumprod([1 sizeM(1:end-1)])*J;

end

method 2 is 20 (small number of index sets (=P) to convert) to 80 (large number of index sets (=P)) times faster than method 1. easy choice

Gunther Struyf
  • 11,158
  • 2
  • 34
  • 58
  • Yeah, I had misunderstood your question, and @gnovice's answer nicely solves the problem. – ely Apr 13 '12 at 18:32

2 Answers2

12

For the general case where J can be any length (which I assume always matches the number of dimensions in M), there are a couple options you have:

  1. You can place each entry of J in a cell of a cell array using the num2cell function, then create a comma-separated list from this cell array using the colon operator:

    cellJ = num2cell(J);
    output = M(cellJ{:});
    
  2. You can sidestep the sub2ind function and compute the linear index yourself with a little bit of math:

    sizeM = size(M);
    index = cumprod([1 sizeM(1:end-1)]) * (J(:) - [0; ones(numel(J)-1, 1)]);
    output = M(index);
    
gnovice
  • 125,304
  • 15
  • 256
  • 359
  • just finished editing the question, thanks! I think if I compile this to an emlmex function on configuration, the computational effort will be minimal :) I will post results when I have a little bit more of implementation – Gunther Struyf Apr 13 '12 at 18:50
  • 1
    @GuntherStruyf: Happy to help, although I get the feeling this may be a micro-optimization. I'm guessing that the performance bottleneck in your code won't be the time spent on indexing the matrix, so either of these solutions should be good. – gnovice Apr 13 '12 at 18:52
  • 1
    probably, but every little improvement is welcome 8hrs of calculation is a looooong time :P – Gunther Struyf Apr 13 '12 at 18:56
  • 4
    @GuntherStruyf: The best thing you can do is use the MATLAB Profiler to figure out where the most time is being spent and optimise that. Array indexing is probably only 0.1% of your runtime. Even if you magically eliminated this cost entirely, your total 8-hour runtime would be reduced by... 30 seconds. On the other hand, vectorising everything in sight, using MATLAB builtin functions (written in C) where ever possible, pre-allocating arrays before you start loops - these can all give handsome performance boosts. – Li-aung Yip Apr 14 '12 at 09:54
  • I just want to make my code 'clean' and I don't like quick-and-dirty solutions when there exist better ones (which requires a bit more effort) In this case and in my opinion, the num2cell method is the quick and dirty way, the other way is the index calculation. I'm glad there was such a simple and neat solution in this case. I'm familiar with the profiler, but thanks for the advice anyway. I'm constructing a new solving framework for an optimization problem, because the old framework was full of such quick and dirty fixes , and overall slow :/ – Gunther Struyf Apr 14 '12 at 14:56
0

Here is a version of gnovices option 2) which allows to process a whole matrix of subscripts, where each row contains one subscript. E.g for 3 subscripts:

J = [5 2 7 1 
     1 5 2 7
     4 3 9 2];

sizeM = size(M);
idx = cumprod([1 sizeX(1:end-1)])*(J - [zeros(size(J,1),1) ones(size(J,1),size(J,2)-1)]).';
Leander Moesinger
  • 2,449
  • 15
  • 28