Sorting to have continuous non negative values next to diagonal of square matrix

1 view (last 30 days)
Hello
I have a square matrices of different sizes. I want to reorder the rows of the matrix in such a way that I get the maximum number of continuous nonnegative values at the positions occurring immediately to the next of the diagonal matrix. The diagonal of the matrix will always be -1.
For example in square matrix 5*5
% 1 2 3 4 5
[-1 1 2 -1 3 % 1
0 -1 -1 -1 4 % 2
5 -1 -1 6 7 % 3
-1 -1 8 -1 9 % 4
10 11 12 13 -1] % 5
In this case I want maximum number of continuous non negative values on the positions (1,2),(2,3),(3,4),(4,5). Currently we can see that (1,2) has a positive value and since (2,3) has a negative value. But if we rearrange the rows then we can have more continuous values.
One more point to be noted is that the values in the rows and column are interlinked. What I mean by this is that suppose you swap rows 2 and 5 , then you need to swap columns 2 and 5 as well. This will ensure that diagonal matrix will remain -1.
If we do this we get
% 1 2 3 4 5
[-1 3 2 -1 1 % 1
10 -1 12 13 11 % 2
5 7 -1 6 7 % 3
-1 9 8 -1 -1 % 4
0 4 -1 -1 -1] % 5
We swap rows 2 and 5 first and then followed by swapping columns 2 and 5, we have 3 continuous non negative values at (1,2),(2,3) and (3,4).
% 1 2 3 4 5
[-1 9 8 -1 -1 % 1
13 -1 12 10 11 % 2
6 7 -1 5 -1 % 3
-1 3 2 -1 1 % 4
-1 4 -1 0 -1] % 5
And if we swap rows 1 and 4 and also columns 1 and 4 , we get continuous values at all 4 positions and by sorting in such a way we get maximum of continuous non negative values.
Note that I just want to get the maximum of continuous non negative values, may be in another example for a 5*5 matrix, it could just have 1 or 2 continuous values, but that should be the maximum.
I hope I have explained my problem and any help would be appreciated. This is just an example and I have square matrices of the size 30*30 on which I need to perform the operation.
  6 Comments
Manoj
Manoj on 11 Nov 2014
Edited: Manoj on 11 Nov 2014
Yes the solution would not be what I want. The rule has to be maintained because I get those particular values because of its positions and hence the swapping of both the rows and cols is mandatory.
Manoj
Manoj on 11 Nov 2014
And I also need to know the initial and final positions of the rows. As in the original rows have ended up where after we are done with all the swapping.

Sign in to comment.

Accepted Answer

Matt J
Matt J on 17 Nov 2014
Edited: Matt J on 17 Nov 2014
The code below does the job, I believe. Or at least, I find that it works with all of your sample inputs, and with a worst case time of 8.5 seconds on my machine (for Input_six).
Note that the sorting problem really turns out to be a Longest Path Problem which is known to be NP-hard. Therefore, sparsity of the non-negative values is key to keeping the computation time low.
function [Msorted,rowperm] = mySort(M)
%Sorts input M to achieve a maximum length string of non-negative values on
%diag(Msorted,1). Optionally, also, returns "rowperm", an index vector
%satisfying
%
% Msorted=M(rowperm,rowperm)
n=size(M,1);
p=LongestPath(M>=0);
rowperm=[p,setdiff(1:n,p)];
Msorted=M(rowperm,rowperm);
function out=LongestPath(G,options)
%Finds a longest path through adjacency matrix, G
if nargin<2
options=1:size(G,1);
end
m=length(options);
c=cell(m,1);
for i=1:m
j=options(i);
tmp=G;
tmp(:,j)=0;
suboptions=find(tmp(j,:));
if isempty(suboptions)%nowhere to go
c{j}=j;
else
c{j}=[j,LongestPath(tmp,suboptions)];
end
end
L=cellfun('length',c);
[~,idx]=max(L);
out=c{idx};
  1 Comment
Manoj
Manoj on 17 Nov 2014
Hello Matt
I ran the code and it seems to be working perfectly for all my input matrices so far. Thanks a ton for all your time and effort to solve this problem, really appreciate it.
Best Regards
Manoj

Sign in to comment.

More Answers (2)

Guillaume
Guillaume on 10 Nov 2014
Edited: Guillaume on 11 Nov 2014
I'm really not familiar with this problem domain (optimisation). The only way I can think of doing what you want is to try all the permutations and see which one is the best. This is only reasonable for small matrices otherwise computation time will be prohibitive.
You would use perms to generate all possible combinations of row/column indices and diag to extract the diagonal for testing:
function bestm = findbest()
m =[-1 1 2 -1 3 % 1
0 -1 -1 -1 4 % 2
5 -1 -1 6 7 % 3
-1 -1 8 -1 9 % 4
10 11 12 13 -1]; % 5
diagcount = countcontinuous(diag(m, 1));
bestm = m;
allcombs = perms(1:size(m, 1));
for comb = allcombs'
mswappedrow=m(comb, :);
mswapped = mswappedrow(:, comb);
count = countcontinuous(diag(mswapped, 1));
if count > diagcount
diagcount = count;
bestm = mswapped;
end
if count == size(m, 1)-1
break; %it's the maximum possible anyway
end
end
function count = countcontinuous(diagonal)
positive = diagonal' > 0;
transitions = diff([0 positive 0]); % a 1 is a transition from 0 to 1, a -1 from 1 to 0
%because we've put a 0 before and after we're sure to have the same number of transitions
%from 0 to 1 and from one to 0
runlength = find(transitions == -1) - find(transitions == 1);
count = max(runlength);
end
  6 Comments
Manoj
Manoj on 11 Nov 2014
Oh I understood what you were saying about positive numbers now. Unfortunately , my problem has very few solutions since there are quite a lot of negative numbers. So the solution which you have provided with randperm may not be ideal in this case. That is why I do not have any shortcut methods to reduce the search area for the solution.
Manoj
Manoj on 11 Nov 2014
Edited: Manoj on 11 Nov 2014
I was thinking of other ways to solve this problem and reduce the search area. What if I set a criteria for the bigger square matrices(>20*20) where the criteria would be a number for these continuous values.
Eg: I set criteria==15, then I just need a sorted matrix which will give me 15 continuous values next to the diagonal.
Would this make it easier or would I be complicating it further?
Thanks for all your help

Sign in to comment.


Matt J
Matt J on 11 Nov 2014
Edited: Matt J on 11 Nov 2014
I'm not sure how robust an approach this is, but it did give me one of the non-unique solutions to your posted example
result =
-1 6 7 -1 5
8 -1 9 -1 -1
12 13 -1 11 10
-1 -1 4 -1 0
2 -1 3 1 -1
Basically, the idea is to use fmincon from the Optimization Toolbox to solve this as a nonlinear minimization problem. The code below searches for the permutation matrix P such that if A is the original matrix, then the permuted off-diagonal
b=diag(P*(A>=0)*P.',1)
will have a large sum and be smooth. I would have liked to have added the nonlinear constraint
P*P.'-eye(size(P))=0
to enforce the fact that P should be orthogonal, but when I do that, I find the optimization gets trapped very easily at local minima or non-solutions. Not so surprising, since this is a highly non-convex constraint. Without the non-linear constraint, though, the whole thing is really a quadratic program, and quadprog() would probably perform even better. I wouldn't take the trouble to recast the input data into quadprog's syntax until you're sure that it's working, though.
A= [-1 1 2 -1 3 % 1
0 -1 -1 -1 4 % 2
5 -1 -1 6 7 % 3
-1 -1 8 -1 9 % 4
10 11 12 13 -1] % 5 ]
n=size(A,1);
map=(A>=0);
Aeq= [kron(speye(n), ones(1,n));... %column sum
kron(ones(1,n), speye(n))]; %row sum
beq=ones(2*n,1);
lb=zeros(size(A));
ub=ones(size(A));
[P0,fval] = fmincon(@(P) cost(P,map),speye(n),[],[],Aeq,beq,lb,ub);
P=double(bsxfun(@ge,P0,max(P0,[],2))); %round to nearest permutation matrix
if any(Aeq*P(:)-beq) %check that it is a permutation
error 'Approximate permutation not found'
end
result=P*A*P.',
function f=cost(P,map)
b=diag(P*map*P.',1);
f=sum(diff(b).^2)-sum(b);
  4 Comments
Manoj
Manoj on 12 Nov 2014
Hello
Thanks once again , I will attach some of these matrices in .mat file as early as possible once I get the optimal solutions for each of them manually.
Manoj
Manoj on 13 Nov 2014
Edited: Manoj on 14 Nov 2014
Hello Matt
I am attaching few of the large matrices in a mat file and also an excel file for better viewing. My aim apart from getting these diagonal to be non negative would also be to know the initial and final positions of the original rows.
I am not sure if this is of help but the way those numbers works is that suppose a position for ex. (1,2) has a -1 then the position (2,1) also will be a -1.
Similarly for ex. (3,4) is a non negative number say 1, then the position (4,3) will also be a non negative number (0,1,2..)
Thanks once again

Sign in to comment.

Categories

Find more on Operating on Diagonal Matrices in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!