How to replace kth diagonal in a matrix in octave? - matrix

I am trying to replace the sub and super diagonals of a matrix in Octave.
This is the code I am using:
A=[-3 -2 -1 0 1 2 3;0.1 0.2 0.2 0.5 0.6 -0.1 0]'
P=zeros(4,4)
for (k=1:7)
j=A(k,1)
diag(P,j)=A(k,2)
end
This is the error I got: diag(0,_): subscripts must be either integers 1 to (2^63)-1 or logicals
But all the little parts are okay. diag(P,-3) works fine, but when I ask to replace in the loop it refuses!
What can I do about it? Is this: diag(P,j)=e, not the right code to substitute super and sub diagonals?

The reason you're getting an error is that diag(P,j) is not a reference to the diagonal of P, it is a function that returns the values on that diagonal. So what you're doing is assigning the value A(k,2) to the return value of the function and, since it's never assigned to a variable name, the value is lost and nothing changes.
To fix your loop, you would need to provide indices into P and assign to those. One way is to use logical indexing to tell MATLAB which values in P to change. For example,
P = zeros(4)
M = logical(diag([1,1,1], -1))
P(M) = 3
gives us
P =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
M =
0 0 0 0
1 0 0 0
0 1 0 0
0 0 1 0
P =
0 0 0 0
3 0 0 0
0 3 0 0
0 0 3 0
The unfortunate part is that we can't specify both which diagonal we want to create and the size of the resulting matrix, so we have to calculate the number of elements on the diagonal before creating it.
A=[-3 -2 -1 0 1 2 3;0.1 0.2 0.2 0.5 0.6 -0.1 0].'
n=4; % Number of rows/columns in P...
% If we want a non-square matrix, we'll have to do more math
P=zeros(n);
for k=1:2*n-1 % Remove hardcoded values to make the code more general.
j=A(k,1);
diag_length = n-abs(j);
M=diag(true(1,diag_length),j); % Create logical array with true on jth diagonal
P(M)=A(k,2);
end
The result is:
P =
0.5000 0.6000 -0.1000 0
0.2000 0.5000 0.6000 -0.1000
0.2000 0.2000 0.5000 0.6000
0.1000 0.2000 0.2000 0.5000
Another approach is to use spdiags. One of the uses of spdiags takes the columns of one matrix and uses them to build the diagonals of the output matrix. You pass the indices of the diagonals to set, and the matrix of values for each of the diagonals, along with the matrix size.
If we only pass one value for each diagonal, spdiags will only set one value, so we'll have to duplicate the input vector n times. (spdiags will happily throw away values, but won't fill them in.)
A=[-3 -2 -1 0 1 2 3;0.1 0.2 0.2 0.5 0.6 -0.1 0].'
n = 4;
diag_idx = A(:,1).'; % indices of diagonals
diag_val = A(:,2).'; % corresponding values
diag_val = repmat(diag_val, n, 1); % duplicate values n times
P = spdiags(diag_val, diag_idx, n, n);
P = full(P);
That last line is because spdiags creates a sparse matrix. full turns it into a regular matrix. The final value of P is what you'd expect:
P =
0.5000 0.6000 -0.1000 0
0.2000 0.5000 0.6000 -0.1000
0.2000 0.2000 0.5000 0.6000
0.1000 0.2000 0.2000 0.5000
Of course, if you're into one-liners, you can combine all of these commands together.
P = full(spdiags(repmat(A(:,2).', n, 1), A(:,1).', n, n));

Related

Most efficent way of finding submatrices of a matrix [matlab]

Say we have a matrix of zeros and ones
0 1 1 1 0 0 0
1 1 1 1 0 1 1
0 0 1 0 0 1 0
0 1 1 0 1 1 1
0 0 0 0 0 0 1
0 0 0 0 0 0 1
and we want to find all the submatrices (we just need the row indices and column indices of the corners) with these properties:
contain at least L ones and L zeros
contain max H elements
i.e. take the previous matrix with L=1 and H=5, the submatrix 1 2 1 4 (row indices 1 2 and column indices 1 4)
0 1 1 1
1 1 1 1
satisfies the property 1 but has 8 elements (bigger than 5) so it is not good;
the matrix 4 5 1 2
0 1
0 0
is good because satisfies both the properties.
The objective is then to find all the submatrices with min area 2*L, max area H and containg at least L ones and L zeros.
If we consider a matrix as a rectangle it is easy to find all the possibile subrectangles with max area H and min area 2*L by looking at the divisors of all the numbers from H to 2*L.
For example, with H=5 and L=1 all the possibile subrectangles/submatrices are given by the divisors of
H=5 -> divisors [1 5] -> possibile rectangles of area 5 are 1x5 and 5x1
4 -> divisors [1 2 4] -> possibile rectangles of area 4 are 1x4 4x1 and 2x2
3 -> divisors [1 3] -> possibile rectangles of area 3 are 3x1 and 1x3
2*L=2 -> divisors [1 2] -> possibile rectangles of area 2 are 2x1 and 1x2
I wrote this code, which, for each number finds its divisors and cycles over them to find the submatrices. To find the submatrices it does this: take for example a 1x5 submatrix, what the code does is to fix the first line of the matrix and move step by step (along all the columns of the matrix) the submatrix from the left edge of the matrix to the right edge of the matrix, then the code fixes the second row of the matrix and moves the submatrix along all the columns from left to right, and so on until it arrives at the last row.
It does this for all the 1x5 submatrices, then it considers the 5x1 submatrices, then the 1x4, then the 4x1, then the 2x2, etc.
The code do the job in 2 seconds (it finds all the submatrices) but for big matrices, i.e. 200x200, a lot of minutes are needed to find all the submatrices. So I wonder if there are more efficient ways to do the job, and eventually which is the most efficient.
This is my code:
clc;clear all;close all
%% INPUT
P= [0 1 1 1 0 0 0 ;
1 1 1 1 0 1 1 ;
0 0 1 0 0 1 0 ;
0 1 1 0 1 1 1 ;
0 0 0 0 0 0 1 ;
0 0 0 0 0 0 1];
L=1; % a submatrix has to containg at least L ones and L zeros
H=5; % max area of a submatrix
[R,C]=size(P); % rows and columns of P
sub=zeros(1,6); % initializing the matrix containing the indexes of each submatrix (columns 1-4), their area (5) and the counter (6)
counter=1; % no. of submatrices found
%% FIND ALL RECTANGLES OF AREA >= 2*L & <= H
%
% idea: all rectangles of a certain area can be found using the area's divisors
% e.g. divisors(6)=[1 2 3 6] -> rectangles: 1x6 6x1 2x3 and 3x2
tic
for sH = H:-1:2*L % find rectangles of area H, H-1, ..., 2*L
div_sH=divisors(sH); % find all divisors of sH
disp(['_______AREA ', num2str(sH), '_______'])
for i = 1:round(length(div_sH)/2) % cycle over all couples of divisors
div_small=div_sH(i);
div_big=div_sH(end-i+1);
if div_small <= R && div_big <= C % rectangle with long side <= C and short side <= R
for j = 1:R-div_small+1 % cycle over all possible rows
for k = 1:C-div_big+1 % cycle over all possible columns
no_of_ones=length(find(P(j:j-1+div_small,k:k-1+div_big))); % no. of ones in the current submatrix
if no_of_ones >= L && no_of_ones <= sH-L % if the submatrix contains at least L ones AND L zeros
% row indexes columns indexes area position
sub(counter,:)=[j,j-1+div_small , k,k-1+div_big , div_small*div_big , counter]; % save the submatrix
counter=counter+1;
end
end
end
disp([' [', num2str(div_small), 'x', num2str(div_big), '] submatrices: ', num2str(size(sub,1))])
end
if div_small~=div_big % if the submatrix is a square, skip this part (otherwise there will be duplicates in sub)
if div_small <= C && div_big <= R % rectangle with long side <= R and short side <= C
for j = 1:C-div_small+1 % cycle over all possible columns
for k = 1:R-div_big+1 % cycle over all possible rows
no_of_ones=length(find(P(k:k-1+div_big,j:j-1+div_small)));
if no_of_ones >= L && no_of_ones <= sH-L
sub(counter,:)=[k,k-1+div_big,j,j-1+div_small , div_big*div_small, counter];
counter=counter+1;
end
end
end
disp([' [', num2str(div_big), 'x', num2str(div_small), '] submatrices: ', num2str(size(sub,1))])
end
end
end
end
fprintf('\ntime: %2.2fs\n\n',toc)
Here is a solution centered around 2D matrix convolution. The rough idea is to convolve P for each submatrix shape with a second matrix such that each element of the resulting matrix indicates how many ones are in the submatrix having its top left corner at said element. Like this you get all solutions for a single shape in one go, without having to loop over rows/columns, greatly speeding things up (it takes less than a second for a 200x200 matrix on my 8 years old laptop)
P= [0 1 1 1 0 0 0
1 1 1 1 0 1 1
0 0 1 0 0 1 0
0 1 1 0 1 1 1
0 0 0 0 0 0 1
0 0 0 0 0 0 1];
L=1; % a submatrix has to containg at least L ones and L zeros
H=5; % max area of a submatrix
submats = [];
for sH = H:-1:2*L
div_sH=divisors(sH); % find all divisors of sH
for i = 1:length(div_sH) % cycle over all couples of divisors
%number of rows of the current submatrix
nrows=div_sH(i);
% number of columns of the current submatrix
ncols=div_sH(end-i+1);
% perpare matrix to convolve P with
m = zeros(nrows*2-1,ncols*2-1);
m(1:nrows,1:ncols) = 1;
% get the number of ones in the top left corner each submatrix
submatsums = conv2(P,m,'same');
% set values where the submatrices go outside P invalid
validsums = zeros(size(P))-1;
validsums(1:(end-nrows+1),1:(end-ncols+1)) = submatsums(1:(end-nrows+1),1:(end-ncols+1));
% get the indexes where the number of ones and zeros is >= L
topLeftIdx = find(validsums >= L & validsums<=sH-L);
% save submatrixes in following format: [index, nrows, ncols]
% You can ofc use something different, but it seemed the simplest way to me
submats = [submats ; [topLeftIdx bsxfun(#times,[nrows ncols],ones(length(topLeftIdx),1))]];
end
end
First, I suggest that you combine finding the allowable sub-matrix sizes.
for smaller = 1:sqrt(H)
for larger = 2*L:H/smaller
# add smaller X larger and larger x smaller to your shapes list
Next, start with the smallest rectangles in the shapes. Note that any solution to a small rectangle can be extended in any direction, to the area limit of H, and the added elements will not invalidate the solution you found. This will identify many solutions without bothering to check the populations within.
Keep track of the solutions you've found. As you work your way toward larger rectangles, you can avoid checking anything already in your solutions set. If you keep that in a hash table, checking membership is O(1). All you'll need to check thereafter will be larger blocks of mostly-1 adjacent to mostly-0. This should speed up the processing somewhat.
Is that enough of a nudge to help?

Find maximum number of distinct square matrix in a mxn matrix

There is a m x n matrix which contains either 0 or 1. A square submatrix of 2x2 is defined which contains only 0. If such square submatrix is cut from the original matrix then we have to find out the maximum number of such square sub matrices which can be cut from the original matrix. Cutting strictly means no 2 square sub matrix can overlap.
For ex -
This is a 5x5 matrix
0 0 0 1 0
0 0 0 0 0
1 0 0 0 0
0 0 0 1 0
0 0 0 0 0
If we cut a square submatrix of 2x2 starting from (0,0) then the remaining matrix is
0 1 0
0 0 0
1 0 0 0 0
0 0 0 1 0
0 0 0 0 0
Further 2x2 square sub matrices can be cut
In this give input maximum 3 such matrices can be cut. If I mark them with 'a'
a a 0 1 0
a a a a 0
1 0 a a 0
a a 0 1 0
a a 0 0 0
I have tried the backtracking/recursive approach but it can work only for lower size input. Can anybody suggest a more efficeint approach?
Edit: I have mark matrix elements with "a" to show that this is one sub matrix which can be cut. We have to report only maximum number of 2x2 submatrix (containing all 0) which can be taen from this matrix
Just for the sake of completeness, I changed the script to do some crude recursion, you were right it's difficult to not resort to a recursive way of doing it...
The idea:
f(matrix,count)
IF count > length THEN
length = count
add all options to L
IF L is empty THEN
return
FOR each option in L
FOR each position in option
set position in matrix to 1
f(matrix,count+1)
FOR each position in option
set position in matrix to 0
where options are all 2x2 submatrices with only 0s that are currently in matrix
length = 0
set M to the matrix with 1s and 0s
f(M,0)
In python:
import copy
def possibilities(y):
l = len(y[0]) # horizontal length of matrix
h = len(y) # verticle length of matrix
sub = 2 # length of square submatrix you want to shift in this case 2x2
length = l-sub+1
hieght = h-sub+1
x = [[0,0],[0,1],
[1,0],[1,1]]
# add all 2x2 to list L
L=[]
for i in range(hieght):
for j in range(length):
if y[x[0][0]][x[0][1]]==0 and y[x[1][0]][x[1][1]]==0 and y[x[2][0]][x[2][1]]==0 and y[x[3][0]][x[3][1]]==0:
# create a copy of x
c = copy.deepcopy(x)
L.append(c)
for k in x: # shift submatrix to the right 1
k[1]+=1
(x[0][1],x[1][1],x[2][1],x[3][1]) = (0,1,0,1)
for k in x: # shift submatrix down 1
k[0]+=1
return L
def f(matrix,count):
global length
if count > length:
length = count
L = possibilities(matrix)
if not L:
return
for option in L:
for position in option:
matrix[position[0]][position[1]]=1
f(matrix,count+1)
# reset back to 0
for position in option:
matrix[position[0]][position[1]]=0
length = 0
# matrix
M = [[0,0,1,0,0,0],
[0,0,0,0,0,0],
[1,1,0,0,0,0],
[0,1,1,0,0,0]]
f(M,0)
print(length)

Omitting zeroes from matrix calculation in Octave

I'm performing input-output calculations in Octave. I have several matrices/vectors in the formula:
F = f' * (I-A)^-1 * Y
All vectors probably contain zeroes. I would like to omit them from the calculation and just return 0 instead. Any help would be greatly appreciated!
Miranda
What do you mean when you say "omit them"?
If you want to remove zeros from a vector you can do this:
octave:1> x=[1,2,0,3,4,0,5];
octave:2> x(find(x==0))=[]
x =
1 2 3 4 5
The logic is: x==0 will test each element of x (in this case the test is if it equals zero) and will return a vector of 0's and 1's (0 if the test is false for that element and 1 otherwise)
So:
octave:1> x=[1,2,0,3,4,0,5];
octave:2> x==0
ans =
0 0 1 0 0 1 0
The find() function will return the index value of any non-zero element of it's argument, hence:
octave:3> find(x==0)
ans =
3 6
And then you are just indexing and removing when you do something like:
octave:5> x([3, 6]) = []
x =
1 2 3 4 5
But instead you do it with the output of the find() function (which is the vector [3,6] in this case)
You can do the same for matrices:
octave:7> A = [1,2,0;4,5,0]
A =
1 2 0
4 5 0
octave:8> A(find(A==0))=[]
A =
1
4
2
5
Then use the reshape() function to turn it back into a matrix.

How to rearrange the elements of a matrix in Matlab according to the order of elements in another matrix?

I have a vector A in Matlab of dimension (mxn)x1 composed by real numbers greater or equal then zero, e.g. m=3,n=4 A=[1;0;0;0;0.4;0.7;0.5;0.6;0.8;0;1;6] which looks like
A=[1
0
0
---
0
0.4
0.7
---
0.5
0.6
0.8
---
0
1
6]
We can see that A is composed by n subvectors of dimension m. I have a vector B of dimension gx1, with g greater or equal than m, composed by ones and zeros such that the total number of ones is equal to m, e.g. g=9 B=[1;0;0;0;0;0;0;1;1] which looks like
B=[1
0
0
0
0
0
0
1
1]
I want to create a matrix C of dimension gxn in which the entries of each subvector of A are placed in correspondence of the ones in g for each column of B, e.g.
C=[1 | 0 | 0.5 | 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.4| 0.6 | 1
0 | 0.7| 0.8 | 6]
Loops are fine only if very fast. Real dimensions of matrices are very large (e.g. mxn=100000, g=50000)
Approach #1 (bsxfun based linear indexing)
C = zeros(g,n) %// Pre-allocate
idx = bsxfun(#plus,find(B),[0:n-1]*g) %// Indices where A elements are to be put
C(idx)= A %// Put A elements
Approach #2 (Direct replacement)
C = zeros(g,n)
C(find(B),:) = reshape(A,m,[])
Pre-allocation: For faster pre-allocation you can do this instead in both of the above mentioned approaches -
C(g,n) = 0;
You can also try the repmat and logical indexing approach. First, reshape your data so that it's in the right matrix form, so in your case 3 x 4, then use B as a logical mask and replicate it for as many times as we have columns in your matrix, then do an assignment. You would have to allocate a matrix that is the size of your desired output before doing this assignment. Something like this:
%// Your example data
m = 3; n = 4;
A = [1;0;0;0;0.4;0.7;0.5;0.6;0.8;0;1;6];
B = logical([1 0 0 0 0 0 0 1 1]).';
%// Relevant code
Am = reshape(A, m, n);
Bm = repmat(B, 1, n);
C = zeros(numel(B), 4);
C(Bm) = Am;
C is the desired result, where we get:
C =
1.0000 0 0.5000 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.4000 0.6000 1.0000
0 0.7000 0.8000 6.0000
My gut feeling is that this will be slower than the bsxfun approach, but the above is more readable if you're not familiar with how bsxfun works.

Counting subrows in each row of a matrix in Matlab?

I need an algorithm in Matlab which counts how many adjacent and non-overlapping (1,1) I have in each row of a matrix A mx(n*2) without using loops. E.g.
A=[1 1 1 0 1 1 0 0 0 1; 1 0 1 1 1 1 0 0 1 1] %m=2, n=5
Then I want
B=[2;3] %mx1
Specific case
Assuming A to have ones and zeros only, this could be one way -
B = sum(reshape(sum(reshape(A',2,[]))==2,size(A,2)/2,[]))
General case
If you are looking for a general approach that must work for all integers and a case where you can specify the pattern of numbers, you may use this -
patt = [0 1] %%// pattern to be found out
B = sum(reshape(ismember(reshape(A',2,[])',patt,'rows'),[],2))
Output
With patt = [1 1], B = [2 3]
With patt = [0 1], B = [1 0]
you can use transpose then reshape so each consecutive values will now be in a row, then compare the top and bottom row (boolean compare or compare the sum of each row to 2), then sum the result of the comparison and reshape the result to your liking.
in code, it would look like:
A=[1 1 1 0 1 1 0 0 0 1; 1 0 1 1 1 1 0 0 1 1] ;
m = size(A,1) ;
n = size(A,2)/2 ;
Atemp = reshape(A.' , 2 , [] , m ) ;
B = squeeze(sum(sum(Atemp)==2))
You could pack everything in one line of code if you want, but several lines is usually easier for comprehension. For clarity, the Atemp matrix looks like that:
Atemp(:,:,1) =
1 1 1 0 0
1 0 1 0 1
Atemp(:,:,2) =
1 1 1 0 1
0 1 1 0 1
You'll notice that each row of the original A matrix has been broken down in 2 rows element-wise. The second line will simply compare the sum of each row with 2, then sum the valid result of the comparisons.
The squeeze command is only to remove the singleton dimensions not necessary anymore.
you can use imresize , for example
imresize(A,[size(A,1),size(A,2)/2])>0.8
ans =
1 0 1 0 0
0 1 1 0 1
this places 1 where you have [1 1] pairs... then you can just use sum
For any pair type [x y] you can :
x=0; y=1;
R(size(A,1),size(A,2)/2)=0; % prealocarting memory
for n=1:size(A,1)
b=[A(n,1:2:end)' A(n,2:2:end)']
try
R(n,find(b(:,1)==x & b(:,2)==y))=1;
end
end
R =
0 0 0 0 1
0 0 0 0 0
With diff (to detect start and end of each run of ones) and accumarray (to group runs of the same row; each run contributes half its length rounded down):
B = diff([zeros(1,size(A,1)); A.'; zeros(1,size(A,1))]); %'// columnwise is easier
[is js] = find(B==1); %// rows and columns of starts of runs of ones
[ie je] = find(B==-1); %// rows and columns of ends of runs of ones
result = accumarray(js, floor((ie-is)/2)); %// sum values for each row of A

Resources