Vectorisation of code for insertion of n x n matrices in a 3D array along the diagonal of a large matrix - arrays

As in my earlier question I am trying to insert small square matrices along the diagonal of a large matrix. However, these matrices are now contained in a 3D array, and have different values. As before, overlapping values are to be added, and the small matrices are only inserted where they can fit fully inside the large matrix. The step dimension will always be equal to 1.
I have achieved an answer through the use of for-loops, but am attempting to vectorise this code for efficiency. How would I do this? The current, unvectorised code is shown below.
function M = TestDiagonal2()
N = 10;
n = 2;
maxRand = 3;
deepMiniM = randi(maxRand,n,n,N+1-n);
M = zeros(N);
for i = 1:N+1-n
M(i:i+n-1,i:i+n-1) = M(i:i+n-1,i:i+n-1) + deepMiniM(:,:,i);
end
end
The desired result is an NxN matrix with n+1 diagonals populated:
3 1 0 0 0 0 0 0 0 0
4 5 3 0 0 0 0 0 0 0
0 3 3 3 0 0 0 0 0 0
0 0 1 6 3 0 0 0 0 0
0 0 0 4 4 4 0 0 0 0
0 0 0 0 2 3 2 0 0 0
0 0 0 0 0 2 6 2 0 0
0 0 0 0 0 0 4 2 2 0
0 0 0 0 0 0 0 3 3 1
0 0 0 0 0 0 0 0 3 3

This makes use of implicit expansion, as well as sparse to add values at coincident indices, and (:) indexing to linearize a matrix in the usual column-major order.
ind1 = repmat((1:n).', n, 1) + (0:N-n); % column indices for the sum
ind2 = repelem((1:n).', n) + (0:N-n); % row indices for the sum
M = full(sparse(ind1(:), ind2(:), deepMiniM(:), N, N)); % sum over those indices

Related

How to balance unique values in an array Matlab

I have a vector
Y = [1 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0]
1 occurs 17 times
0 occurs 21 times
How can I randomly remove 0s so that both values have equal amounts, such as 1 (17 times) and 0 (17 times)?
This should also work on much bigger matrix.
Starting with your example
Y = [1 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0]
You can do the following:
% Get the indices of the value which is more common (`0` here)
zeroIdx = find(~Y); % equivalent to find(Y==0)
% Get random indices to remove
remIdx = randperm(nnz(~Y), nnz(~Y) - nnz(Y));
% Remove elements
Y(zeroIdx(remIdx)) = [];
You could combine the last two lines, but I think it would be less clear.
The randperm line is choosing the correct number of elements to remove from random indices between 1 and the number of zeros.
If the data can only have two values
Values are assumed to be 0 and 1. The most common value is randomly removed to equalize their counts:
Y = [1 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0]; % data
ind0 = find(Y==0); % indices of zeros
ind1 = find(Y==1); % indices of ones
t(1,1:numel(ind0)) = ind0(randperm(numel(ind0))); % random permutation of indices of zeros
t(2,1:numel(ind1)) = ind1(randperm(numel(ind1))); % same for ones. Pads shorter row with 0
t = t(:, all(t,1)); % keep only columns that don't have padding
result = Y(sort(t(:))); % linearize, sort and use those indices into the data
Generalization for more than two values
Values are arbitrary. All values except the least common one are randomly removed to equalize their counts:
Y = [0 1 2 0 2 1 1 2 0 2 1 2 2 0 0]; % data
vals = [0 1 2]; % or use vals = unique(Y), but absent values will not be detected
t = [];
for k = 1:numel(vals) % loop over values
ind_k = find(Y==vals(k));
t(k, 1:numel(ind_k)) = ind_k(randperm(numel(ind_k)));
end
t = t(:, all(t,1));
result = Y(sort(t(:)));

How to copy and paste a small matrix into a bigger matrix without for-loop

For example, we have a small matrix
B = [5 2,
3 4]
and the bigger one
A = [1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1]
Now I want paste B into A so that A looks like
A = [1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 5 2
0 0 0 3 4]
That means the values of A of the bottom right has been replaced. I would like to do this without using a for-loop. How is that possible?
PS:
A is always an eye(n) matrix (n is a constant).
B is a square matrix and has a variable size but is always less or equal to A
Find the relevant row and column subscripts of A and put B there.
A(end-size(B,1)+1:end, end-size(B,2)+1:end)=B
It works even if B is not a square matrix.

How to change elements of a matrix with reference to a vector of column indices without using for-loop?

I have a matrix
a =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
and b vector
b =
1 2 3 4 5 5
I want to replace value of each row in a matrix with reference value of b matrix value and finally generate a matrix as follows without using for loop.
a_new =
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
0 0 0 0 1
if first element of b, b(1) = 1 so change take first row of a vector and make first element as 1 because b(1) = 1.
How can I implement this without using for loop?
Sure. You only need to build a linear index from b and use it to fill the values in a:
a = zeros(6,5); % original matrix
b = [1 2 3 4 5 5]; % row or column vector with column indices into a
ind = (1:size(a,1)) + (b(:).'-1)*size(a,1); % build linear index
a(ind) = 1; % fill value at those positions
Same as Luis Mendo's answer, but using the dedicated function sub2ind:
a( sub2ind(size(a),(1:numel(b)).',b(:)) ) = 1
Also via the subscript to indices conversion way,
a = zeros(6,5);
b = [1 2 3 4 5 5];
idx = sub2ind(size(a), [1:6], b); % 1:6 just to create the row index per b entry
a(idx) = 1
any of these methods works in Octave:
bsxfun(#eq, [1:5 5]',(1:5))
[1:5 5].' == (1:5)

Array block splitting in MATLAB

Say we have a vector containing zeros interspersed blocks of ones of varying length, such as:
0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0
I would like to transform this into a 2D array as follows. Each row contains zeros only and one of the blocks. I.e. the number of rows of the 2D array would be the number of blocks at the end. The above array would transform to:
0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0
I.e the first block ends up in the first row, the second block in the second row etc.
Question
This exercise is rather trivial using loops. My question is if there is a neat way using MATLAB matrix operations, MATLAB functions and array indexing to do this?
Off the top of my head you could use bwlabel (from the Image Processing Toolbox) to assign each cluster of 1's a unique value. You could then use bsxfun to check equality between the labeled version and the unique labels which will automatically expand it out into a matrix.
a = [0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0];
b = bwlabel(a);
out = bsxfun(#eq, (1:max(b))', b);
Without the image processing toolbox you could do effectively the same thing with:
C = cumsum(~a);
out = bsxfun(#eq, unique(C(logical(a))).', C);
I tried this one
N = 100; % set array size
A = randi(2,N,1)-1; % generate random array filled with 0 and 1
d = diff([0;A]); % if 1 : starting point of block
% if -1 : end point of block
ir = find(A); % Find A==1 which will be row index
ic = cumsum(d(ir)>0); % Set col index
% assemble array
% if you want output as full array
A_new = accumarray([ir,ic],ones(size(ir)),[length(A),ic(end)]);
% if you want output as sparse array
A_new = sparse(ir,ic,ones(size(ir)),length(A),ic(end),length(ir));
% display routine
figure;spy(A,'r');hold on;spy([zeros(size(A)),A_new]);
Turns out it is faster than #Suever 's solution (Compared tic toc time with size 10000, 1000 trial). Also, if you use sparse instead of accumarray, then it is much faster
Suever_time = 7~8 sec
Accumarray = 2~3 sec
Sparse = 0.2~0.3 sec
However, his one is much shorter and neat!

Gaussian elimation - Linear equation matrices, algorithm

Let's assume that we have a simple matrix 3rows x 7cols.
The matrix includes only zeros (0) and (1) like:
1 0 1 1 1 0 0
0 0 1 1 0 0 0
0 0 1 0 1 1 0
Senario:
If we know the sum of non-zeros in each row,
(in first row is 4, in second row is 2, in third row is 3.) (blue line)
additional, if we know the sum of each col (1 , 0, 3, 2, 2, 1, 0) (green line)
also if we know the sum of each diagonal from the top-left to bottom-right (1,0,1,2,3,0,1,1,0)(red lines) anti-clockwise
and finally we know the sum of each diagonal from the bottom-left to top-right (0,0,2,1,3,2,1,0,0) (yellow lines)
My question is:
With these values as input (and the lenght of matrix 3x7),
4, 2, 3
1, 0, 3, 2, 2, 1, 0
1, 0, 1, 2, 3, 0, 1, 1, 0
0, 0, 2, 1, 3, 2, 1, 0, 0
How we can draw the first matrix?
After a lot of thoughts I came to the conclusion that this is a linear equation system with 3x7 unknown values and some equations.
Right?
How can I make an algorithm in C, or whatever, to solve these equations?
Should I use a method like gausian equation?
Any help would be greatly appreciated!
Start with the first column. You know the top and bottom values (from the first values of the red & yellow lists). Subtract the sum of these two from the first in the green list, and now you have the middle value as well.
Now just work to the right.
Subtract the first column's middle value from the next value in the red list, and you have the second column's top value. Subtract that same middle value from the next value in the yellow list, and you have the second column's bottom value. Subtract the sum of these two from the next value in the green list, and now you have the middle value for the second column.
et cetera
If you're going to code this up, you can see that the first two columns are a special case, and that'll make the code ugly. I'd suggest using two "ghost" columns of all zeros to the left so that you can use a single method for determining the top, bottom, and middle values for each column.
This is also easily generalizable. You'll just have to use (#rows)-1 ghost columns.
Enjoy.
You can use singular value decomposition to compute a non zero least squares solution to a system of linear homogeneous (and non homogeneous) equations in matrix form.
For a quick overview see:
http://campar.in.tum.de/twiki/pub/Chair/TeachingWs05ComputerVision/3DCV_svd_000.pdf
You should first write out your systems as a matrix equation in the form Ax = b, where x is the 21 unknowns as a column vector, and A is the 28 x 21 matrix that forms the linear system when multiplied out. You essentially need to a compute the matrix A of linear equations, compute the singular value decomposition of A and plug the results into the equation as shown in equation 9.17
There are plenty of libraries that will compute the SVD for you in C, so you only need to formulate the matrix and perform the computations in 9.17. The most difficult part is probably understanding how it all works, with a library SVD function there is relatively little code needed.
To get you started on how to form the equation of linear systems, consider a simple 3 x 3 case.
Suppose that our system is a matrix of the form
1 0 1
0 1 0
1 0 1
We would have the following inputs to the linear system:
2 1 2 (sum of rows - row)
2 1 2 (sum of colums - col)
1 0 3 0 1 (sum of first diagonal sets - t2b)
1 0 3 0 1 (sum of second diagonal sets - b2t)
so now we create a matrix for the linear system
A a1 a2 a3 b1 b2 b3 c1 c2 c3 unknowns (x) = result (b)
sum of row 1 [ 1 1 1 0 0 0 0 0 0 ] [a1] [2]
sum of row 2 [ 0 0 0 1 1 1 0 0 0 ] [a2] [1]
sum of row 3 [ 0 0 0 0 0 0 1 1 1 ] [a3] [2]
sum of col 1 [ 1 0 0 1 0 0 1 0 0 ] [b1] [2]
sum of col 2 [ 0 1 0 0 1 0 0 1 0 ] [b2] [1]
sum of col 3 [ 0 0 1 0 0 1 0 0 1 ] [b3] [2]
sum of t2b 1 [ 1 0 0 0 0 0 0 0 0 ] [c1] [1]
sum of t2b 2 [ 0 1 0 1 0 0 0 0 0 ] [c2] [0]
sum or t2b 3 [ 0 0 1 0 1 0 1 0 0 ] [c3] [3]
sum of t2b 4 [ 0 0 0 0 0 1 0 1 0 ] [0]
sum of t2b 5 [ 0 0 0 0 0 0 0 0 1 ] [1]
sum of b2t 1 [ 0 0 0 0 0 0 1 0 0 ] [1]
sum of b2t 2 [ 0 0 0 1 0 0 0 1 0 ] [0]
sum of b2t 3 [ 1 0 0 0 1 0 0 0 1 ] [3]
sum of b2t 4 [ 0 1 0 0 0 1 0 0 0 ] [0]
sum of b2t 5 [ 0 0 1 0 0 0 0 0 0 ] [1]
When you multiply out Ax, you see that you get the linear system of equations. For example if you multiply out the first row by the unkown column, you get
a1 + a2 + a3 = 2
All you have to do is put a 1 in any of the colums that appear in the equation and 0 elsewhere.
Now all you have to do is compute the SVD of A and plug the result into equation 9.17 to compute the unknowns.
I recommend SVD because it can be computed efficiently. If you would prefer, you can augment the matrix A with the result vector b (A|b) and put A in reduced row echelon form to obtain the result.
For an array of 10x15 ones and zeros, you would be trying to find 150 unknowns and have 10+15+2*(10+15-1) = 73 equations if you ignore that the values are limited to being either one or zero. Obviously you can't create a linear system on that basis which has a unique solution.
So is that constraint enough to give a unique solution?
For a 4x4 matrix with the following sums there are two solutions:
- 1 1 1 1
| 1 1 1 1
\ 0 1 1 0 1 1 0
/ 0 1 1 0 1 1 0
0 0 1 0
1 0 0 0
0 0 0 1
0 1 0 0
0 1 0 0
0 0 0 1
1 0 0 0
0 0 1 0
So I wouldn't expect there to be a unique solution for larger matrices - the same symmetry would exist in many places:
- 1 1 0 0 1 1
| 1 1 0 0 1 1
\ 0 1 0 0 1 0 1 0 0 1 0
/ 0 1 0 0 1 0 1 0 0 1 0
0 0 0 0 1 0
1 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 1
0 1 0 0 0 0
0 1 0 0 0 0
0 0 0 0 0 1
0 0 0 0 0 0
0 0 0 0 0 0
1 0 0 0 0 0
0 0 0 0 1 0
How about this as another variation
Count the amount of unknown squares each sum passes through
While there are unsolved cells
Solve all the cells which are passed through by a sum with only one unknown square
Cells are solved by simply subtracting off all the known cells from the sum
Update the amount of unknown squares each sum passes through
No boundary cases but very similar to the previous answer. This would first solve all the corners, then those adjacent to the corners, then those one step more interior from that, and so on...
Edit: Also zero out any paths that have a sum of zero, that should solve any that are solvable (I think)

Resources