Using modualr arithmetic for vector indices in matlab - matlab

I have a vector n values and i want to split it into groups n groups of 3 adjacent values if it is considered to have a ring topology.
what i am trying to do is this:
vector = [some values];
groups = {};
for i = 1:size(vector)(2)
groups{i} = [vector(mod(i-1, size(vector)(2));
vector(i);
vector(mod(i+1, size(vector)(2))];
endfor
so if n = 10 and i = 1, groups should be [vector(10); vector(1); vector(2)]
it would be pretty straightforward in most programming languages to just use the mod operator, but i am having trouble working out how to do this using matlab as it doesnt use 0 as the initial index of a vector, so if i = 1, then mod(i-1) = 0 which is an illegal index value. Also i = n would be a problem as mod(n, n) = 0.
i have worked out a pretty hack-ish solution in:
vector = [some values];
groups = {};
for i = 1:size(vector)(2)
if i == 1
groups{i} = [vector(size(vector)(2));
vector(1);
vector(2)];
elseif i == size(vector)(2)
groups{i} = [vector(size(vector)(2)-1);
vector(size(vector)(2);
vector(1)];
else
groups{i} = [vector(i-1);
vector(i);
vector(i+1)];
endif
endfor
but it is pretty inelegant and i feel like there should be a better way to do it..
is there some operator that allows you to perform modular arithmetic over vector indexes?

Indeed, the 1-based indexing method of matlab can be quite irritating sometimes...
You can simply add 1 to your 0-based indices, though
n = numel(vector);
for ii = 1:n
idx0 = mod( ii + [-2, 1, 0], n ); % 0-based indices
group{ii} = vector( idx0 + 1 );
end
Now, good Matlab practices are about vectorization, here's one way to vectorize this for-loop
idx0 = mod( bsxfun( #plus, 1:n, [-2;1;0] ), n ); % all indices at once
group = mat2cell( vector( idx0+1 ).', ones(n,1), 3 );
You can lookup mat2cell in mathworks web site for more information. You can also read about bsxfun here at SO for more examples and information.
PS,
It is best not to use i as a variable name in Matlab.

Related

How to programmatically compute this summation

I want to compute the above summation, for a given 'x'. The summation is to be carried out over a block of lengths specified by an array , for example block_length = [5 4 3]. The summation is carried as follows: from -5 to 5 across one dimension, -4 to 4 in the second dimension and -3 to 3 in the last dimension.
The pseudo code will be something like this:
sum = 0;
for i = -5:5
for j = -4:4
for k = -3:3
vec = [i j k];
tv = vec * vec';
sum = sum + 1/(1+tv)*cos(2*pi*x*vec'));
end
end
end
The problem is that I want to find the sum when the number of dimensions are not known ahead of time, using some kind of variable nested loops hopefully. Matlab uses combvec, but it returns all possible combinations of vectors, which is not required as we only compute the sum. When there are many dimensions, combvec returning all combinations is not feasible memory wise.
Appreciate any ideas towards solutions.
PS: I want to do this at high number of dimensions, for example 650, as in machine learning.
Based on https://www.mathworks.com/matlabcentral/answers/345551-function-with-varying-number-of-for-loops I came up with the following code (I haven't tested it for very large number of indices!):
function sum = fun(x, block_length)
sum = 0;
n = numel(block_length); % Number of loops
vec = -ones(1, n) .* block_length; % Index vector
ready = false;
while ~ready
tv = vec * vec';
sum = sum + 1/(1+tv)*cos(2*pi*x*vec');
% Update the index vector:
ready = true; % Assume that the WHILE loop is ready
for k = 1:n
vec(k) = vec(k) + 1;
if vec(k) <= block_length(k)
ready = false;
break; % v(k) increased successfully, leave "for k" loop
end
vec(k) = -1 * block_length(k); % v(k) reached the limit, reset it
end
end
end
where x and block_length should be both 1-x-n vectors.
The idea is that, instead of using explicitly nested loops, we use a vector of indices.
How good/efficient is this when tackling the suggested use case where block_length can have 650 elements? Not much! Here's a "quick" test using merely 16 dimensions and a [-1, 1] range for the indices:
N = 16; tic; in = 0.1 * ones(1, N); sum = fun(in, ones(size(in))), toc;
which yields an elapsed time of 12.7 seconds on my laptop.

average bins along a dimension of a nd array in matlab

To compute the mean of every bins along a dimension of a nd array in matlab, for example, average every 10 elements along dim 4 of a 4d array
x = reshape(1:30*30*20*300,30,30,20,300);
n = 10;
m = size(x,4)/10;
y = nan(30,30,20,m);
for ii = 1 : m
y(:,:,:,ii) = mean(x(:,:,:,(1:n)+(ii-1)*n),4);
end
It looks a bit silly. I think there must be better ways to average the bins?
Besides, is it possible to make the script applicable to general cases, namely, arbitray ndims of array and along an arbitray dim to average?
For the second part of your question you can use this:
x = reshape(1:30*30*20*300,30,30,20,300);
dim = 4;
n = 10;
m = size(x,dim)/10;
y = nan(30,30,20,m);
idx1 = repmat({':'},1,ndims(x));
idx2 = repmat({':'},1,ndims(x));
for ii = 1 : m
idx1{dim} = ii;
idx2{dim} = (1:n)+(ii-1)*n;
y(idx1{:}) = mean(x(idx2{:}),dim);
end
For the first part of the question here is an alternative using cumsum and diff, but it may not be better then the loop solution:
function y = slicedmean(x,slice_size,dim)
s = cumsum(x,dim);
idx1 = repmat({':'},1,ndims(x));
idx2 = repmat({':'},1,ndims(x));
idx1{dim} = slice_size;
idx2{dim} = slice_size:slice_size:size(x,dim);
y = cat(dim,s(idx1{:}),diff(s(idx2{:}),[],dim))/slice_size;
end
Here is a generic solution, using the accumarray function. I haven't tested how fast it is. There might be some room for improvement though.
Basically, accumarray groups the value in x following a matrix of customized index for your question
x = reshape(1:30*30*20*300,30,30,20,300);
s = size(x);
% parameters for averaging
dimAv = 4;
n = 10;
% get linear index
ix = (1:numel(x))';
% transform them to a matrix of index per dimension
% this is a customized version of ind2sub
pcum = [1 cumprod(s(1:end-1))];
sub = zeros(numel(ix),numel(s));
for i = numel(s):-1:1,
ixtmp = rem(ix-1, pcum(i)) + 1;
sub(:,i) = (ix - ixtmp)/pcum(i) + 1;
ix = ixtmp;
end
% correct index for the given dimension
sub(:,dimAv) = floor((sub(:,dimAv)-1)/n)+1;
% run the accumarray to compute the average
sout = s;
sout(dimAv) = ceil(sout(dimAv)/n);
y = accumarray(sub,x(:), sout, #mean);
If you need a faster and memory efficient operation, you'll have to write your own mex function. It shouldn't be so difficult, I think !

How to fill columns of a matrix with random numbers of specific range of a 100*5 matrix?

I've a matrix of order 100*5 . Now the objective is to fill each columns of the matrix with random integer within a specific range. Now the problem is for every column the range of the random number changes. For instance, for the first column, the range is 1 to 100 , for the second its -10 to 1 and so on till 5th column.
This is what I've tried:
b = [0,100;-10,1;0,1;-1,1;10,20]
range = b(:,2) - b(:,1)
offset = b(:,1)
A = round(rand(100,5) * range - offset)
which is from this question. However this generates an error,
Error using * Inner matrix dimensions must agree.
What's possibly causing this and how to resolve it ?
lets bsxfun this thing!
A = round(bsxfun(#minus,bsxfun(#times,rand(100,5) ,range'), offset'))
As an alternative solution, you could use repmat to complete what you already had:
b = [0, 100; -10, 1; 0, 1; -1, 1; 10, 20].';
rng = b(2, :) - b(1, :);
ofst = b(1, :);
A = round(rand(100,5) .* repmat(rng, 100, 1) + repmat(ofst, 100, 1));
You don't have to define rng or ofst, and this can be simply written as:
A = round(rand(10,5) .* repmat(diff(b), 10, 1) + repmat(b(1,:), 10, 1));
Out of curiousity I wrote this quick benchmark* to compare to Ander's bsxfun method. It appears that bsxfun has some initial overhead which means for 5 columns (test other cases yourself) and less than a few thousand rows, repmat is quicker. Above this, the creation of additional large arrays by repmat probably causes a slow down, and we see bsxfun is much quicker.
For future readers if this doesn't apply to you: with broadcasting introduced from R2016b you may find you can dodge using bsxfun and repmat entirely.
*benchmarking code. Tested on Windows 64-bit R2015b, your mileage may vary etc.
function benchie()
b = [0, 100; -10, 1; 0, 1; -1, 1; 10, 20].';
Tb = [];
Tr = [];
K = 20;
for k = 1:K
n = 2^k;
fb = #()bsxfunMethod(b,n);
fr = #()repmatMethod(b,n);
Tb(end+1) = timeit(fb);
Tr(end+1) = timeit(fr);
end
figure; plot(2.^(1:K), Tb, 2.^(1:K), Tr); legend('bsxfun', 'repmat');
end
function bsxfunMethod(b, n)
round(bsxfun(#minus,bsxfun(#times, rand(n,5), diff(b)), b(1,:)));
end
function repmatMethod(b, n)
round(rand(n,5) .* repmat(diff(b), n, 1) + repmat(b(1,:), n, 1));
end
You can use arrayfun, even though I don't see any harm in using loops and writing more readable code as in Steve's answer.
A = cell2mat(arrayfun(#(imin, imax) randi([imin, imax], 100, 1), b(:,1), b(:,2), 'uni', 0)')
You can do this with randi, passing in rows of b to its first argument:
b = [0,100;-10,1;0,1;-1,1;10,20];
A = zeros(100,5);
f=#(ii)randi(b(ii,:),100,1);
for ii = 1:size(A,2)
A(:,ii) = f(ii);
end
I suspect there is a way of doing this without looping through rows/columns, probably with bsxfun.

Efficient way to apply arrayfun to a matrix (i.e. R^N to R^M)

I have a function that transforms R^N to R^M. For simplicity, lets just let it be the identity function #(z) z where z may be a vector. I want to apply a function to a list of parameters of size K x N and have it map to K x M output.
Here is my attempt:
function out_matrix = array_fun_matrix(f, vals)
for i=1:size(vals,1)
f_val = f(vals(i,:));
if(size(f_val,1) > 1) %Need to stack up rows, so convert as required.
f_val = f_val';
end
out_matrix(i,:) = f_val;
end
end
You can try it with
array_fun_matrix(#(z) z(1)^2 + z(2)^2 + z(3), [0 1 0; 1 1 1; 1 2 1; 2 2 2])
The question: Is there a better and more efficient way to do this with vectorization, etc.? Did I miss a built-in function?
Examples of non-vectorizable functions: There are many, usually involving elaborate sub-steps and numerical solutions. A trivial example is something like looking for the numerical solution to an equation, which in term is using numerical quadrature. i.e. let params = [b c] and solve for the a such that int_0^a ((z + b)^2) dz = c
(I know here you could do some calculus, but the integral here is stripped down). Implementing this example,
find_equilibrium = #(param) fzero(#(a) integral(#(x) (x + param(1)).^2 - param(2), 0, a), 1)
array_fun_matrix(find_equilibrium, [0 1; 0 .8])
You can use the cellfun function, but you'll need to manipulate your data a bit:
function out_matrix = array_fun_matrix(f, vals)
% Convert your data to a cell array:
cellVals = mat2cell(vals, ones(1,size(vals,1)));
% apply the function:
out_cellArray = cellfun(f, cellVals, 'UniformOutput', false);
% Convert back to matrix:
out_matrix = cell2mat(out_cellArray);
end
If you don't like this implementation, you can improve the performance of yours by preallocating the out_matrix:
function out_matrix = array_fun_matrix(f, vals)
firstOutput = f(vals(1,:));
out_matrix = zeros(size(vals,1), length(firstOutput)); % preallocate for speed.
for i=1:size(vals,1)
f_val = f(vals(i,:));
if(size(f_val,1) > 1) %Need to stack up rows, so convert as required.
f_val = f_val';
end
out_matrix(i,:) = f_val;
end
end

Get index of current element of a 2-D matrix within arrayfun

Let M be some matrix:
M = rand(1000, 2000);
Consider the following code example:
A = zeros(size(M));
for row = 1:1000
for col = 1:2000
A(row, col) = M(row,col)*(row + col);
end
end
How to compute the matrix A without for loops?
There is arrayfun function, but I don't know how get the index of the current element:
A = arrayfun(#(x)(x*(index(1) + index(2))), M); %// but how to get index???
Perhaps there are other solutions (and without extra loops)?
You can do something simple like as follows to get a matrix that will represent row+col and then multiply that by M
M = rand(1000, 2000);
rowPlusCol = bsxfun(#plus,(1:size(M,1)).',1:size(M,2));
A = M.*rowPlusCol;
From my experience bsxfun is an extremely powerful function and can definitely save some run time, and this is a perfect example of that.
Here's an alternative solution, boasting another fancy one-liner, for the sake of diversity:
A = M .* hankel(2:size(M, 1) + 1, size(M, 1) + 1:sum(size(M)));
I don't think it's possible with arrayfun.
You can get row and column numbers using meshgrid and then do some simple matrix math.
M = rand(1000, 2000);
[cols,rows] = meshgrid(1:size(M,2), 1:size(M,1));
A = M .* (cols + rows);