Vectorize shuffle for blocks of matrix rows - matlab

I have a simple 5x15 matrix called mat_orig. I would like to divide this matrix into non-overlapping blocks, whereby each block is of length 3. That is equivalent to put three non-overlapping rows into one block (i.e. there are 5 blocks of size 3x5).
I would then like to shuffle mat_orig and generate a new matrix. I am using randi to randomly draw five blocks.
I succeeded in finishing my task using the code below. However, I am wondering if I can get rid of the for-loop? Can I apply vectorization?
mat_orig = reshape(1:75, 5, 15)';
block_length = 3;
num_blocks = size(mat_orig, 1) / block_length;
rand_blocks = randi(num_blocks, num_blocks, 1);
mat_shuffled = nan(size(mat_orig));
for r = 0 : num_blocks - 1
start_row_orig = r * block_length + 1;
end_row_orig = r * block_length + block_length;
start_row_random_blocks = ...
rand_blocks(r + 1) * block_length - block_length + 1;
end_row_random_blocks = ...
rand_blocks(r + 1) * block_length;
mat_shuffled(start_row_orig:end_row_orig, :) = ...
mat_orig(start_row_random_blocks:end_row_random_blocks, :);
end

You can do this with implicit expansion to create the blocks' indices, see the code comments for details.
mat_orig = reshape(1:75, 5, 15)';
block_length = 3;
num_blocks = size(mat_orig,1) / block_length;
% Get the shuffle order (can repeat blocks)
shuffIdx = randi( num_blocks, num_blocks, 1 ).';
% Expand these indices to fill the blocks
% This uses implicit expansion to create a matrix, and
% will only work in R2016b or newer.
% For older versions, use 'bsxfun'
shuffIdx = block_length * shuffIdx - (block_length-1:-1:0).';
% Create the shuffled output
mat_shuffled = mat_orig( shuffIdx(:), : );

Related

Create a dynamic matlab function using arrayfun or some other way

I am looking for a way to create a dynamic functions in length with multiple inputs, tried doing it this way but it seems over kill am guessing slow too, is there a better way of doing this to be compact and fast.
Problem want to create a cos with inputs from nX3 matrix sum(A*cos(W*t + F)) where A, W, F are columns from the matrix sum them all up then divide by its norm. Here is what I have so far .
% example input can have n rows
A = [1 2 3; 4 5 6];
item.fre = 0;
item.amp = 0;
item.pha = 0;
items = repmat(item, size(A, 1), 1);
for i = 1:size(A, 1)
items(i).fre = A(i, 1);
items(i).amp = A(i, 2);
items(i).pha = A(i, 3);
end
fun = #(t) sum(cell2mat(arrayfun(#(i) i.amp*cos(2*pi*t*i.fre + i.pha), items, 'un',0)));
% test run all this steps just to get a norm vector
time = 1:10;
testSignal = fun(time);
testSignal = testSignal/norm(testSignal);
I agree with a comment made by Cris Luengo to forget about anonymous functions and structures, you should try the simplest solution first. It looks like you're trying to add cosines with different amplitudes, frequencies, and phases. Here is how I would do it to make it very readable
A = [1 2 3; 4 5 6];
freq = A(:, 1);
amp = A(:, 2);
phase = A(:, 3);
time = 1:.01:10;
testSignal = zeros(size(time));
for i = 1:length(freq)
testSignal = testSignal + amp(i) * cos(2*pi*freq(i) * time + phase(i));
end
testSignal = testSignal/norm(testSignal);
plot(time, testSignal)
grid on
You could eliminate the amp, phase, and freq variables by accessing the columns of A directly, but that would make the code much less readable.

How to store two variables (x.y) from a loop?

I am generating two different coordinates (x, y) within a loop. In my code, I have just realised that it is saving the last variable from the loop. I am, however, trying to save all the iterations from the setsize variable. I already tried to save using something like:
circleposition = [0:length(setsize) x(i),y(i)];
But, it seems that I am not doing it correctly, getting the following error:
Subscript indices must either be real positive integers or logicals.-
Error using vertcat
Dimensions of matrices being concatenated are not consistent.
Here is my original code:
setsize = 9;
r = 340;
cx = 500;
cy = 500;
anglesegment = 2 * pi/setsize;
circleposition = [];
for i = drange (0:setsize)
x = r * cos(i*anglesegment) + cx;
y = r * sin(i*anglesegment) + cy;
circleposition = [x,y];
end
Output:
circleposition =
0 1.0000
840.0000 500.0000
It runs only with the first/last iteration. I need to get 9 x's and 9 y's (depending the setsize, variable).
It's kind of hard to follow, which error message comes from which attempt, but let's have a look.
I don't have access to the Parallel Computing Toolbox, which seems necessary to use the for-loop over distributed range drange, but I assume, this loop can be replaced by for i = 0:setsize for testing.
Now, when starting at i = 0, you would try to access x(0) and y(0), which is not allowed (Subscript indices must either be real positive integers or logicals). Also, you would get 10 values instead of 9, as you stated in your question. So, let's start at i = 1.
To store all 9 pairs of x and y, your circleposition should be an 9 x 2 array So, initialize that by, for example, circleposition = zeros(setsize, 2).
Last, you need to use proper indexing to store [x, y] at the i-th row of circleposition, i.e. circleposition(i, :).
So, the corrected code (attention on the replaced drange part) could look like this:
setsize = 9;
r = 340;
cx = 500;
cy = 500;
anglesegment = 2 * pi/setsize;
circleposition = zeros(setsize, 2); % Initialize circleposition appropriately
for i = 1:setsize % Start at i = 1
x = r * cos(i*anglesegment) + cx;
y = r * sin(i*anglesegment) + cy;
circleposition(i, :) = [x, y]; % Correct indexing of the row
end
circleposition % Output
The output would then be:
circleposition =
760.46 718.55
559.04 834.83
330.00 794.45
180.50 616.29
180.50 383.71
330.00 205.55
559.04 165.17
760.46 281.45
840.00 500.00
On the second error (Error using vertcat. Dimensions of matrices being concatenated are not consistent.): I don't see, where you used vertical concatenation at all!?
Hear is code that works:
setsize = 9;
r = 340;
cx = 500;
cy = 500;
anglesegment = 2 * pi/setsize;
circleposition = zeros(setsize + 1, 2); % Changed from circleposition = []
for i = drange (0:setsize)
x = r * cos(i*anglesegment) + cx;
y = r * sin(i*anglesegment) + cy;
circleposition((i+1),:) = [x,y]; % Changed from circleposition = [x,y];
end
Explanation:
The fix was Changing circleposition = [x,y]; to circleposition((i+1),:) = [x,y]. Without ((i+1),:), you are changing the data of circleposition, not adding to it.
Changing circleposition = []; to circleposition = zeros(setsize + 1, 2); was not required, its just recommended to allocate memory for speed, not an issue for small number of elements.

Very slow execution of user defined convolution function for neural network in MATLAB

I have an implementation of a convolution neural network in MATLAB (from the open source DeepLearnToolbox). The following code finds the convolution of different weights and parameters:
z = z + convn(net.layers{l - 1}.a{i}, net.layers{l}.k{i}{j}, 'valid');
To update the tool, I have implemented my own fixed-point scheme based convolution using the following code:
function result = convolution(image, kernal)
% find dimensions of output
row = size(image,1) - size(kernal,1) + 1;
col = size(image,2) - size(kernal,2) + 1;
zdim = size(image,3);
%create output matrix
output = zeros(row, col);
% flip the kernal
kernal_flipped = fliplr(flipud(kernal));
%find rows and col of kernal for loop iteration
row_ker = size(kernal_flipped,1);
col_ker = size(kernal_flipped,2);
for k = 1 : zdim
for i = 0 : row-1
for j = 0 : col-1
sum = fi(0,1,8,7);
prod = fi(0,1,8,7);
for k_row = 1 : row_ker
for k_col = 1 : col_ker
a = image(k_row+i, k_col+j, k);
b = kernal_flipped(k_row,k_col);
prod = a * b;
% convert to fixed point
prod = fi((product/16384), 1, 8, 7);
sum = fi((sum + prod), 1, 8, 7);
end
end
output(i+1, j+1, k) = sum;
end
end
end
result = output;
end
The problem is that when I use my convolution implementation in the bigger application, it is super slow.
Any suggestions how to improve its execution time?
MATLAB doesn't support fixed point 2D convolution, but knowing that convolution can be written as matrix multiplication and that MATLAB has support for fixed point matrix multiplication you can use im2col to convert the image into column format and multiply it by the kernel to convolve them.
row = size(image,1) - size(kernal,1) + 1;
col = size(image,2) - size(kernal,2) + 1;
zdim = size(image,3);
output = zeros(row, col);
kernal_flipped = fliplr(flipud(kernal));
fi_kernel = fi(kernal_flipped(:).', 1, 8, 7) / 16384;
sz = size(kernal_flipped);
sz_img = size(image);
% Use the generated indexes to convert the image into column format
idx_col = im2col(reshape(1:numel(image)/zdim,sz_img(1:2)),sz,'sliding');
image = reshape(image,[],zdim);
for k = 1:zdim
output(:,:,k) = double(fi_kernel * reshape(image(idx_col,k),size(idx_col)));
end

Implement those functions using matlab

I have an array of samples of ECG signals 1250x1 double let us called it "a".
I need to implement 4 functions which represent features are used to characterize the signals. Energy, 4th Power,Nonlinear Energy and Curve Length
I manged to implement Energy and 4th Power
for i=1:1250
energy = sum(a.^2,i);
power4th = sum(a.^4,i);
end
Which produce 2 array (energy and power4th)
How I can produce the other 2 array? let us called them NonLE and CL.
Use vectorization instead of for loops to solve all 4 of the formulas you need
% generate some random numbers
a = rand(1000,1);
Energy = sum(a.^2);
Power4 = sum(a.^4);
NLEnergy = sum(-a(3:end).*a(1:end-2) + a(2:end).^2);
CurveLength = sum(a(2:end) - a(1:end-1));
The . operator allows element by element operations in a vector.
Actually I think you can implement your formulas without using for loop. You can use matrix multiplication characteristic. Try the code below:
len = 1250;
a = randi(10, len, 1); % // You didn' t give your vector so I generated random a..
Energy = ones(1, len) * (a.^2);
power4th = ones(1, len) * (a.^4);
NonLE = ones(1, len - 2) * ( -a(3:end) .* a(1:end-2) ) + ones(1, len - 1) * ( a(2:end).^2 );
CL = ones(1, len - 1) * ( a(2:end) - a(1:end-1) );
You don't really need a for loop for 3 of them:
energy = sum(a.^2);
power_4th = sum(a.^4);
curve_length = sum(diff(a));
For the last one, you can do something like:
nonLE = 0;
for k = 3 : length(a)
nonLE = nonLE + a(k - 1)^2 - a(k) * a(k - 2);
end

Sum of Absolute differences between images in Matlab

I want to implement sum of absolute difference in Matlab to establish a similarity metric between between one video frame and 5 frames either side of this frame (i.e. past and future frames). I only need the SAD value for the co-located pixel in each frame, rather than a full search routine, such as full search.
Obviously I could implement this as nested loops such as:
bs = 2; % block size
for (z_i = -bs:1:bs)
for (z_j = -bs:1:bs)
I1(1+bs:end-bs,1+bs:end-bs) = F1(1+bs+z_i:end-bs+z_i, 1+bs+z_j:end-bs+z_j);
I2(1+bs:end-bs,1+bs:end-bs) = F2(1+bs+z_i:end-bs+z_i, 1+bs+z_j:end-bs+z_j);
sad(:,:) = sad(:,:) + abs( I1(:,:) - I2(:,:));
end
end
However I'm wondering is there a more efficient way of doing it than this? At the very least I guess I should define the above code snippet as a function?
Any recommendations would be grateful accepted!
You should use the command im2col in MATLAB you will be able to do so in Vectorized manner.
Just arrange each neighborhood in columns (For each frame).
Put them in 3D Matrix and apply you operation on the 3rd dimension.
Code Snippet
I used Wikipedia's definition of "Sum of Absolute Differences".
The demo script:
```
% Sum of Absolute Differences Demo
numRows = 10;
numCols = 10;
refBlockRadius = 1;
refBlockLength = (2 * refBlockRadius) + 1;
mImgSrc = randi([0, 255], [numRows, numCols]);
mRefBlock = randi([0, 255], [refBlockLength, refBlockLength]);
mSumAbsDiff = SumAbsoluteDifferences(mImgSrc, mRefBlock);
```
The Function SumAbsoluteDifferences:
```
function [ mSumAbsDiff ] = SumAbsoluteDifferences( mInputImage, mRefBlock )
%UNTITLED2 Summary of this function goes here
% Detailed explanation goes here
numRows = size(mInputImage, 1);
numCols = size(mInputImage, 2);
blockLength = size(mRefBlock, 1);
blockRadius = (blockLength - 1) / 2;
mInputImagePadded = padarray(mInputImage, [blockRadius, blockRadius], 'replicate', 'both');
mBlockCol = im2col(mInputImagePadded, [blockLength, blockLength], 'sliding');
mSumAbsDiff = sum(abs(bsxfun(#minus, mBlockCol, mRefBlock(:))));
mSumAbsDiff = col2im(mSumAbsDiff, [blockLength, blockLength], [(numRows + blockLength - 1), (numCols + blockLength - 1)]);
end
```
Enjoy...