Matlab how to find boundary submatrix - matlab

let a and b are sequence of k consecutive natural numbers a = (a_i) and b = (b_i). If A an n x n matrix, then A[a,b] (a nontrivial submatrix of A which is obtained from eliminating elements that doesnt belong in rows a and columns b) is called boundary submatrix if
i) a_1 = 1 or a_1 > 1 and A[(a_1 - 1),b] = 0
or
ii) b_1 = 1 or b_1 > 1 and A[a,(b_1 - 1)] = 0
(yes the matrix usually got some zero entries)
what is the easiest code to understand for this case?
I tried to make and array for the sequence using columnk but it seems difficult.
Example for matrix
B =
1 1 1 0
1 2 1 1
0 1 2 2
0 0 3 2
if we pick a = 2,3 and b = 3,4 we get
B(a,b) =
1 1
2 2
and the example of its boundary submatrices of B would be its every principal submatrix, and B([2,3],[2,3]) since B([2,3],1)=0

Related

How to create all permutations of a 2-column cell-array?

I created a cell array of shape m x 2, each element of which is a matrix of shape d x d.
For example like this:
A = cell(8, 2);
for row = 1:8
for col = 1:2
A{row, col} = rand(3, 3);
end
end
More generally, I can represent A as follows:
where each A_{ij} is a matrix.
Now, I need to randomly pick a matrix from each row of A, because A has m rows in total, so eventually I will pick out m matrices, which we call a combination.
Obviously, since there are only two picks for each row, there are a total of 2^m possible combinations.
My question is, how to get these 2^m combinations quickly?
It can be seen that the above problem is actually finding the Cartesian product of the following sets:
2^m is actually a binary number, so we can use those to create linear indices. You'll get an array containing 1s and 0s, something like [1 1 0 0 1 0 1 0 1], which we can treat as column "indices", using a 0 to indicate the first column and a 1 to indicate the second.
m = size(A, 1);
% Build all binary numbers and create a logical matrix
bin_idx = dec2bin(0:(2^m -1)) == '1';
row = 3; % Loop here over size(bin_idx,1) for all possible permutations
linear_idx = [find(~bin_idx(row,:)) find(bin_idx(row,:))+m];
A{linear_idx} % the combination as specified by the permutation in out(row)
On my R2007b version this runs virtually instant for m = 20.
NB: this will take m * 2^m bytes of memory to store bin_idx. Where that's just 20 MB for m = 20, that's already 30 GB for m = 30, i.e. you'll be running out of memory fairly quickly, and that's for just storing permutations as booleans! If m is large in your case, you can't store all of your possibilities anyway, so I'd just select a random one:
bin_idx = rand(m, 1); % Generate m random numbers
bin_idx(bin_idx > 0.5) = 1; % Set half to 1
bin_idx(bin_idx < 0.5) = 0; % and half to 0
Old, slow answer for large m
perms()1 gives you all possible permutations of a given set. However, it does not take duplicate entries into account, so you'll need to call unique() to get the unique rows.
unique(perms([1,1,2,2]), 'rows')
ans =
1 1 2 2
1 2 1 2
1 2 2 1
2 1 1 2
2 1 2 1
2 2 1 1
The only thing left now is to somehow do this over all possible amounts of 1s and 2s. I suggest using a simple loop:
m = 5;
out = [];
for ii = 1:m
my_tmp = ones(m,1);
my_tmp(ii:end) = 2;
out = [out; unique(perms(my_tmp),'rows')];
end
out = [out; ones(1,m)]; % Tack on the missing all-ones row
out =
2 2 2 2 2
1 2 2 2 2
2 1 2 2 2
2 2 1 2 2
2 2 2 1 2
2 2 2 2 1
1 1 2 2 2
1 2 1 2 2
1 2 2 1 2
1 2 2 2 1
2 1 1 2 2
2 1 2 1 2
2 1 2 2 1
2 2 1 1 2
2 2 1 2 1
2 2 2 1 1
1 1 1 2 2
1 1 2 1 2
1 1 2 2 1
1 2 1 1 2
1 2 1 2 1
1 2 2 1 1
2 1 1 1 2
2 1 1 2 1
2 1 2 1 1
2 2 1 1 1
1 1 1 1 2
1 1 1 2 1
1 1 2 1 1
1 2 1 1 1
2 1 1 1 1
1 1 1 1 1
NB: I've not initialised out, which will be slow especially for large m. Of course out = zeros(2^m, m) will be its final size, but you'll need to juggle the indices within the for loop to account for the changing sizes of the unique permutations.
You can create linear indices from out using find()
linear_idx = [find(out(row,:)==1);find(out(row,:)==2)+size(A,1)];
A{linear_idx} % the combination as specified by the permutation in out(row)
Linear indices are row-major in MATLAB, thus whenever you need the matrix in column 1, simply use its row number and whenever you need the second column, use the row number + size(A,1), i.e. the total number of rows.
Combining everything together:
A = cell(8, 2);
for row = 1:8
for col = 1:2
A{row, col} = rand(3, 3);
end
end
m = size(A,1);
out = [];
for ii = 1:m
my_tmp = ones(m,1);
my_tmp(ii:end) = 2;
out = [out; unique(perms(my_tmp),'rows')];
end
out = [out; ones(1,m)];
row = 3; % Loop here over size(out,1) for all possible permutations
linear_idx = [find(out(row,:)==1).';find(out(row,:)==2).'+m];
A{linear_idx} % the combination as specified by the permutation in out(row)
1 There's a note in the documentation:
perms(v) is practical when length(v) is less than about 10.

Finding the column index for the 1 in each row of a matrix

I have the following matrix in Matlab:
M = [0 0 1
1 0 0
0 1 0
1 0 0
0 0 1];
Each row has exactly one 1. How can I (without looping) determine a column vector so that the first element is a 2 if there is a 1 in the second column, the second element is a 3 for a one in the third column etc.? The above example should turn into:
M = [ 3
1
2
1
3];
You can actually solve this with simple matrix multiplication.
result = M * (1:size(M, 2)).';
3
1
2
1
3
This works by multiplying your M x 3 matrix with a 3 x 1 array where the elements of the 3x1 are simply [1; 2; 3]. Briefly, for each row of M, element-wise multiplication is performed with the 3 x 1 array. Only the 1's in the row of M will yield anything in the result. Then the result of this element-wise multiplication is summed. Because you only have one "1" per row, the result is going to be the column index where that 1 is located.
So for example for the first row of M.
element_wise_multiplication = [0 0 1] .* [1 2 3]
[0, 0, 3]
sum(element_wise_multiplication)
3
Update
Based on the solutions provided by #reyryeng and #Luis below, I decided to run a comparison to see how the performance of the various methods compared.
To setup the test matrix (M) I created a matrix of the form specified in the original question and varied the number of rows. Which column had the 1 was chosen randomly using randi([1 nCols], size(M, 1)). Execution times were analyzed using timeit.
When run using M of type double (MATLAB's default) you get the following execution times.
If M is a logical, then the matrix multiplication takes a hit due to the fact that it has to be converted to a numerical type prior to matrix multiplication, whereas the other two have a bit of a performance improvement.
Here is the test code that I used.
sizes = round(linspace(100, 100000, 100));
times = zeros(numel(sizes), 3);
for k = 1:numel(sizes)
M = generateM(sizes(k));
times(k,1) = timeit(#()M * (1:size(M, 2)).');
M = generateM(sizes(k));
times(k,2) = timeit(#()max(M, [], 2), 2);
M = generateM(sizes(k));
times(k,3) = timeit(#()find(M.'), 2);
end
figure
plot(range, times / 1000);
legend({'Multiplication', 'Max', 'Find'})
xlabel('Number of rows in M')
ylabel('Execution Time (ms)')
function M = generateM(nRows)
M = zeros(nRows, 3);
col = randi([1 size(M, 2)], 1, size(M, 1));
M(sub2ind(size(M), 1:numel(col), col)) = 1;
end
You can also abuse find and observe the row positions of the transpose of M. You have to transpose the matrix first as find operates in column major order:
M = [0 0 1
1 0 0
0 1 0
1 0 0
0 0 1];
[out,~] = find(M.');
Not sure if this is faster than matrix multiplication though.
Yet another approach: use the second output of max:
[~, result] = max(M.', [], 1);
Or, as suggested by #rayryeng, use max along the second dimension instead of transposing M:
[~, result] = max(M, [], 2);
For
M = [0 0 1
1 0 0
0 1 0
1 0 0
0 0 1];
this gives
result =
3 1 2 1 3
If M contains more than one 1 in a given row, this will give the index of the first such 1.

Vector of indexes to create matrix by accessing elements of another matrix

Consider an n-by-k matrix M and an p-by-1 vector of indexes V ranging from 1 to n. How can I create the p-by-k matrix C where each row corresponds to the entry of M referred to by the value in each row of V.
Example
M = 1 1
1 2
1 3
1 4
and
V = 2
1
3
What I require is the matrix
C = 1 2
1 1
1 3
To assign the rows V of matrix M to a matrix C, you would write:
C = M(V,:);

How to traverse two same size matrices and compare them

I have two matrices filled with 0s and 1s
e.g.
A = [ 0 0 1 0,
1 0 1 0 ]
B = [ 1 1 1 1
0 0 0 0 ]
and I'd like to compared the values form the same position against each other and return a 2x2 matrice
R = [ TP(1) FN(3)
FP(2) TN(2) ]
TP = returns the amount of times A has the value 1, and B has the value 1
FN = returns the amount of times A has the value 0, and B has the value 1
FP = returns the amount of times A has the value 1, and B has the value 0
TN = returns the amount of times A has the value 0, and B has the value 0
How do i get each individual number in A and B?
Approach #1: Comparison based using bsxfun -
pA = [1 0 1 0] %// pattern for A
pB = [1 1 0 0] %// pattern for B
%// Find matches for A against pattern-A and pattern-B for B using bsxfun(#eq.
%// Then, perform AND for detecting combined matches
matches = bsxfun(#eq,A(:),pA) & bsxfun(#eq,B(:),pB)
%// Sum up the matches to give us the desired counts
R = reshape(sum(matches),2,[]).'
Output -
R =
1 3
2 2
Approach #2: Finding decimal numbers -
Step-1: Find decimal numbers corresponding to the combined A's and B's
>> dec_nums = histc(bin2dec(num2str([B(:) A(:)],'%1d')),0:3)
dec_nums =
2
2
3
1
Step-2: Re-order the decimal numbers such that they line up as needed in the problem
>> R = reshape(flipud(dec_nums),2,[])'
R =
1 3
2 2
Use logical operators & and ~ applied on the linearized versions of A and B, and then nnz (or sum) to count the true values:
R = [nnz(A(:)&B(:)) nnz(~A(:)&B(:)); nnz(A(:)&~B(:)) nnz(~A(:)&~B(:))];

Finding all possible “lists” of possible pairs in Matlab

I have been thinking about a problem for the last few days but as I am a beginner in MATLAB, I have no clue how to solve it. Here is the background. Suppose that you have a symmetric N×N matrix where each element is either 0 or 1, and N = (1,2,...,n).
For example:
A =
0 1 1 0
1 0 0 1
1 0 0 0
0 1 0 0
If A(i,j) == 1, then it is possible to form the pair (i,j) and if A(i,j)==0 then it is NOT possible to form the pair (i,j). For example, (1,2) is a possible pair, as A(1,2)==A(2,1)==1 but (3,4) is NOT a possible pair as A(3,4)==A(4,3)==0.
Here is the problem. Suppose that a member of the set N only can for a pair with at most one other distinct member of the set N (i.e., if 1 forms a pair with 2, then 1 cannot form a pair with 3). How can I find all possible “lists” of possible pairs? In the above example, one “list” would only consist of the pair (1,2). If this pair is formed, then it is not possible to form any other pairs. Another “list” would be: ((1,3),(2,4)). I have searched the forum and found that the latter “list” is the maximal matching that can be found, e.g., by using a bipartite graph approach. However, I am not necessarily only interested to find the maximal matching; I am interested in finding ALL possible “lists” of possible pairs.
Another example:
A =
0 1 1 1
1 0 0 1
1 0 0 0
1 1 0 0
In this example, there are three possible lists:
(1,2)
((1,3),(2,4))
(1,4)
I hope that you can understand my question, and I apologize if am unclear. I appreciate all help I can get. Many thanks!
This might be a fast approach.
Code
%// Given data, A
A =[ 0 1 1 1;
1 0 0 1;
1 0 0 0;
1 1 0 0];
%%// The lists will be stored in 'out' as a cell array and can be accessed as out{1}, out{2}, etc.
out = cell(size(A,1)-1,1);
%%// Code that detects the lists using "selective" diagonals
for k = 1:size(A,1)-1
[x,y] = find(triu(A,k).*(~triu(ones(size(A)),k+1)));
out(k) = {[x y]};
end
out(cellfun('isempty',out))=[]; %%// Remove empty lists
%%// Verification - Print out the lists
for k = 1:numel(out)
disp(out{k})
end
Output
1 2
1 3
2 4
1 4
EDIT 1
Basically I will calculate all the the pairwise indices of the matrix to satisfy the criteria set in the question and then simply map them over the given matrix. The part of finding the "valid" indices is obviously the tedious part in it and in this code with some aggressive approach is expensive too when dealing with input matrices of sizes more than 10.
Code
%// Given data, A
A = [0 1 1 1; 1 0 1 1; 1 1 0 1; 1 1 1 0]
%%// Get all pairwise combinations starting with 1
all_combs = sortrows(perms(1:size(A,1)));
all_combs = all_combs(all_combs(:,1)==1,:);
%%// Get the "valid" indices
all_combs_diff = diff(all_combs,1,2);
valid_ind_mat = all_combs(all(all_combs_diff(:,1:2:end)>0,2),:);
valid_ind_mat = valid_ind_mat(all(diff(valid_ind_mat(:,1:2:end),1,2)>0,2),:);
%%// Map the ones of A onto the valid indices to get the lists in a matrix and then cell array
out_cell = mat2cell(valid_ind_mat,repmat(1,[1 size(valid_ind_mat,1)]),repmat(2,[1 size(valid_ind_mat,2)/2]));
A_masked = A(sub2ind(size(A),valid_ind_mat(:,1:2:end),valid_ind_mat(:,2:2:end)));
out_cell(~A_masked)={[]};
%%// Remove empty lists
out_cell(all(cellfun('isempty',out_cell),2),:)=[];
%%// Verification - Print out the lists
disp('Lists =');
for k1 = 1:size(out_cell,1)
disp(strcat(' List',num2str(k1),':'));
for k2 = 1:size(out_cell,2)
if ~isempty(out_cell{k1,k2})
disp(out_cell{k1,k2})
end
end
end
Output
A =
0 1 1 1
1 0 1 1
1 1 0 1
1 1 1 0
Lists =
List1:
1 2
3 4
List2:
1 3
2 4
List3:
1 4
2 3
I'm sure there's a faster way to do it, but here's the obvious solution:
%// Set top half to 0, and find indices of all remaining 1's
A(triu(A)==1) = 0;
[ii,jj] = find(A);
%// Put these in a matrix for further processing
P = [ii jj];
%// Sort indices into 'lists' of the kind you defined
X = repmat({}, size(P,1),1);
for ii = 1:size(P,1)-1
X{ii}{1} = P(ii,:);
for jj = ii+1:size(P,1)
if ~any(ismember(P(ii,:), P(jj,:)))
X{ii}{end+1} = P(jj,:); end
end
end