Sorting to have continuous non negative values next to diagonal of square matrix
Show older comments
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
on 10 Nov 2014
Guillaume
on 10 Nov 2014
I don't think your question is unclear at all. In fact, it's a lot clearer than many questions we get.
I think you didn't get any reply because it's not a trivial problem to solve. (or maybe it's just bad luck and your question was missed). It's certainly not a problem domain I'm familiar with.
Matt J
on 10 Nov 2014
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.
Suppose I break this rule and use some arbitrary permutation of rows and columns, but still manage to keep -1's on the diagonal. Would you still reject that as a solution?
Manoj
on 11 Nov 2014
Accepted Answer
More Answers (2)
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
on 11 Nov 2014
Guillaume
on 11 Nov 2014
Yes, perms is certainly not feasible for 30 rows. As an alternative you could use randperm to try different combinations at random and stop after a given number of iterations.
Note that my algorithm above will stop immediately if it manages to have only positive number on the 2nd diagonal. Therefore, if your 30*30 matrix is mostly positive numbers, with randperm it should hopefully stop quickly.
If your matrix is mostly negative numbers, you could change the stop condition
if count == size(m, 1)-1
to
if count == maxpossiblecount
with
maxpossiblecount = min([size(m, 1)-1 sum(m(:) >= 0)]);
Guillaume
on 11 Nov 2014
My solution does not really care how the matrix starts, since it tries random (with randperm) or ordered (with perms) permutations of all the rows and columns.
What I was saying is that if your matrix is mostly positive numbers anywhere, then there would be many permutations that result in all numbers positive on the 2nd diagonal and the algorithm can stop quite quickly.
For example, modifying my code to use randperm
function [bestm, diagcount, step] = findbest(m, maxstep)
diagcount = countcontinuous(diag(m, 1));
bestm = m;
%allcombs = perms(1:size(m, 1));
%for comb = allcombs'
step = 0;
while step < maxstep
step = step+1;
comb = randperm(size(m, 1));
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
end
end
if step == maxstep
warning('%d steps reached. calculation stopped', maxstep);
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
And using it on a 30*30 matrix of mostly positive numbers
m = randi(20, 30)-6; %uniformly distributed between -5 and 14
[bestm, runcount, step] = findbest(m, 100000);
returns quite quickly.
When the number of possible answer diminishes, the algorithm becomes too expensive. At least, it'll still give you a better combination if not the best.
Manoj
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
on 12 Nov 2014
Manoj,
The version below might be more robust, but I say again, I am not sure how confident we can be in this approach. I'd be curious to have one of your large matrix examples to play with, however, if you're willing to attach one or several of them in a .mat file and tell us what the optimal number of continuous non-negative elements are on the diagonal. Part of the problem might be that non-default options may need to be passed to fmincon.
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=(speye(n));
nlc=[];
for i=1:2
M = fmincon(@(P) cost(P,map),P0,[],[],Aeq,beq,lb,ub,nlc,optimset('Display','none'));
P=double(bsxfun(@ge,M,max(M,[],2))); %round to nearest permutation matrix
%second pass
P0=P;
nlc=@nonlcon;
end
if any(Aeq*P(:)-beq) %check that it is a permutation
warning 'Approximate permutation not found'
end
result=P*A*P.',
function f=cost(P,map)
b=diag(P*map*P.',1);
m=length(b);
f=sum(diff(b).^2)-sum(b);
function [c,ceq]=nonlcon(P)
ceq=[];
c=P*P.'-eye(size(P));
Manoj
on 12 Nov 2014
Categories
Find more on Solver Outputs and Iterative Display 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!