8

Given two real rectangular matrices, $A$ and $B$, how can I tell if they are equal up to a permutation of their rows/column without trying all possible permutations?

(This is closely related to the question I asked yesterday, Algorithm to determine matrix equivalence, but which seems too specific/complicated to receive a response. Maybe I'll be luckier with this simpler one!

  • 3
    The graph isomorphism problem ( http://en.wikipedia.org/wiki/Graph_isomorphism_problem ) is notoriously hard, and your problem generalizes it (to see why, just let A and B be the incidence matrices of two graph). A polynomial-time solution to your problem would entail a polynomial-time solution to the graph isomorphism problem. So I don't think there will be a good way. – darij grinberg Feb 27 '14 at 16:43
  • @darij: from a 300x200 matrix A with random permutation to B the procedure in my second answer gives the result in 5 seconds (where the time is mainly used for display of the big matrices). – Gottfried Helms Feb 27 '14 at 19:49

6 Answers6

3

[Update 2] With respect to the earlier question to which the OP has linked it becomes more important to mention, that the following method is only simple, if there is some function over the element of the rows of of the columns, which provides unique "keys" for each row/column. Matrices with restricted entries, for instance $0,1$ or $-1,0,1$ might have no such functions. The problem will become much more complicated - but this was not apparent from the question in the OP... [/Update 2]


Here is a worked example for the case, that rows & columns are both permuted, so $$ B = P_L \cdot A \cdot P_R ' $$ where the $P_L,P_R$ are permutation matrices and the apostroph indicates the transpose. Our goal is, to find $P_L,P_R$ given $A,B$. By the logic of the procedure it is clear, that the matrices need not be square, any rectangular form is possible.

The key-idea is that of the sorting which was introduced in he answer of @xavier

The improvement of that proposal is that -when we sort the rows- we do not refer to values in a single column as sorting key, but to a key, made from the whole row instead. The most simple idea is to use the row-sum; and to overcome possible ambiguities we may add to square-sums/cube-sums along the rows or even more sophisticated keys but which are insensitive to the order of the columns. In the example I've provided the sums and the squaresums (but I only used the sums, because they were sufficient/unique in that example).

Here is the excerpt done in my matrix-language MatMate (I think it is very near to selfexplanatory pseudocode)

A = randomu(4,4)              // generate some 4x4 randommatrix
B = A[ 4´2´1´3 , 2´4´3´1 ]    // extract columns/rows given by index-lists

Here $B$ is a reorganisation of the matrix $A$ in rows and columns simultaneously; the first list gives the indices from where the rows, the second, from where the columns are taken. This means: $B$ is a left- and right-permutation of $A$. And the goal is to reconstruct that permutation from the data in A and B in terms of permutation-matrices $P_L,P_R$.

Data:

      A : 
   91.73       69.77       16.26       82.57
   36.79       84.42       32.95       27.90
   77.47       71.80       46.60       48.18
   32.79       30.66       24.67       14.92

      B : 
   30.66       14.92       24.67       32.79
   84.42       27.90       32.95       36.79
   69.77       82.57       16.26       91.73
   71.80       48.18       46.60       77.47

First we build the sorting keys for $A$. We prepare the sorting of the rows and for this we introduce the matrix $AA$ which contains the sums and the squaresums of each row:

[152] AA = sumzl(A)||sqsumzl(A)
      AA : 
  260.33    20363.50
  182.06    10344.05
  244.04    15648.79
  103.04     2846.58

Now to get a permuation-matrix we need the sorting-index; that is we need an indirect sorting which gives a list which can then be used as index rowwise into $A$ (or into $AA$) as result:

[153] idxA = isortsp(AA)  

      idxA : 
    4.00        2.00        3.00        1.00

From this we generate a permutation-matrix $T_{l1}$

[154] TL1 = einh(4)[idxA',*] 

      TL1 : 
    0.00        0.00        0.00        1.00
    0.00        1.00        0.00        0.00
    0.00        0.00        1.00        0.00
    1.00        0.00        0.00        0.00

and test, whether this index /this permutation gives indeed the rows-sorted $AA$ (or $A$):

[155] chk = TL1 * AA        

      chk : 
  103.04     2846.58
  182.06    10344.05
  244.04    15648.79
  260.33    20363.50

Now we do the same with the matrix $B$ :

[156] BB = sumzl(B)||sqsumzl(B)    

      BB : 
  103.04     2846.58
  182.06    10344.05
  260.33    20363.50
  244.04    15648.79


[157] idxB =  isortsp(BB) 

      idxB : 
    1.00        2.00        4.00        3.00

[158] TL2 = einh(4)[idxB',*]      

  TL2 : 
1.00        0.00        0.00        0.00
0.00        1.00        0.00        0.00
0.00        0.00        0.00        1.00
0.00        0.00        1.00        0.00


[159] chk = TL2 * BB       

      chk : 
  103.04     2846.58
  182.06    10344.05
  244.04    15648.79
  260.33    20363.50

and indeed, also $BB$ (and thus $B$) gets sorted by that permutation. Now to arrive at $B$ starting from $A$ we need the first permutation in its order and the second as inverse/transpose:

[160] chk = TL2' * TL1 * AA   

      chk : 
  103.04     2846.58
  182.06    10344.05
  260.33    20363.50
  244.04    15648.79

and this shows, that we get the rows of $AA$ into the rows of $BB$ (and thus that of $A$ into that of $B$). We put that two permutation together in one permutation-matrix $P_L$

[161] PL = Tl2' * TL1     

      PL : 
    0.00        0.00        0.00        1.00
    0.00        1.00        0.00        0.00
    1.00        0.00        0.00        0.00
    0.00        0.00        1.00        0.00



[162] chk = PL * A

      chk : 
   32.79       30.66       24.67       14.92
   36.79       84.42       32.95       27.90
   91.73       69.77       16.26       82.57
   77.47       71.80       46.60       48.18

and we see, that the columns are not yet in the same order as in $B$ but the rows are now in the same order (compare the documented matrices $A$ and $B$ at the beginning of this excerpt)

Now we do the same for the columns. To make things easier I just use the transposed of $A$ and $B$ and do then the same procedure as before:

[163] AA = sumzl(A')||sqsumzl(A')    // use the transposes of A
                                     // to apply the logic to the columns 

      AA : 
  238.78    16844.07
  256.65    18089.03
  120.48     4130.28
  173.57    10139.54

[164] idxA = isortsp(AA) 

      idxA :     
    3.00        4.00        1.00        2.00

[165] TL1 = einh(4)[idxA',*]      

      TL1 : 
    0.00        0.00        1.00        0.00
    0.00        0.00        0.00        1.00
    1.00        0.00        0.00        0.00
    0.00        1.00        0.00        0.00

[166] chk = TL1*AA

      chk : 
  120.48     4130.28
  173.57    10139.54
  238.78    16844.07
  256.65    18089.03


[167] BB = sumzl(B')||sqsumzl(B')     // use the transpose of B to allow to
                                      // apply the logic to the columns
      BB : 
  256.65    18089.03
  173.57    10139.54
  120.48     4130.28
  238.78    16844.07


[168] idxB = isortsp(BB)
      idxB : 
    3.00        2.00        4.00        1.00

[169] TL2 = einh(4)[*,idxB']

      TL2 : 
    0.00        0.00        1.00        0.00
    0.00        1.00        0.00        0.00
    0.00        0.00        0.00        1.00
    1.00        0.00        0.00        0.00

[170] chk = TL2 * BB    

      chk : 
  120.48     4130.28
  173.57    10139.54
  238.78    16844.07
  256.65    18089.03


[171] chk = TL2' * TL1 * AA    

      chk : 
  256.65    18089.03
  173.57    10139.54
  120.48     4130.28
  238.78    16844.07

[172] PR = Tl2' * TL1
      PR : 
    0.00        1.00        0.00        0.00
    0.00        0.00        0.00        1.00
    0.00        0.00        1.00        0.00
    1.00        0.00        0.00        0.00

Now we check the complete permutation:

[173] chk = PL * A * PR'

      chk : 
   30.66       14.92       24.67       32.79
   84.42       27.90       32.95       36.79
   69.77       82.57       16.26       91.73
   71.80       48.18       46.60       77.47

and the result equals the matrix $B$, so we've indeed found the solution.

Because we needed only sorting this applicable also to nonsquare matrices, and there is also no more question of invertibility.


[update]
After I've looked into your previous question: if this is not only a theoretical but a practical problem, you can use that software MatMate (which however is not very well maintained by documentation, minor bugs and help for installation... Email me if you need help. See this link .

A complete script for the task. Here I use n of rows = 16 and n of columns=12 but is easily configurable:

;******  MatMate Version 0.1108 Beta *****************************

nr,nc=16,12                // number rows, number columns
A = randomu(nr,nc)
       idxr,idxc = randomindex(nr),randomindex(nc)
B = A[idxr,idxc]           // B is then a random permutation of A
                           // by rows and by columns 
//========== Adaption of rows ======================
AA = sumzl(A)              // or: sqsumzl(A) 
                           // or take something else if not unique
   idxA = isortsp(AA)      // I(ndirect) SORT (along) SP (=column)
   TL1 = einh(nr)[idxA , * ]     // "einh(n)" is n x n unit-matrix
       // chk = TL1 * AA

BB = sumzl(B)               // use the same function as for A
   idxB = isortsp(BB) 
   TL2  = einh(nr)[idxB , * ] 
    // chk = TL2 * BB
    // 
    // chk = TL2' * TL1 * AA
PL = Tl2'*TL1
    // chk = PL * A


//========== Adaption of columns ======================
AA = sumzl(A')             // or: sqsumzl(A') 
                           // or take something else if not unique
   idxA = isortsp(AA)
   TL1 = einh(nc)[idxA , * ] 
    // chk = TL1 * AA

BB = sumzl(B')              // use the same function as for A'
   idxB = isortsp(BB) 
   TL2  = einh(nc)[idxB , * ] 
    // chk = TL2 * BB
    // 
    // chk = TL2' * TL1 * AA
PR = Tl2 ' * TL1

//========== Check result =============================

chk = PL * A * PR' - B  // should be zero

// if indeed zero, then B = PL *A * PR 
// and we have the solution

// ====================================================
  • I am very grateful for the effort you put into answering this. I have to think about the points your raise, I'm a bit confused about the 'key' now. This is indeed a practical question, not just a curiosity, but I'm afraid that it would be very difficult to use matmate because this is part of a much larger project. But thanks for bringing it to my attention! – djangology Feb 27 '14 at 18:16
  • @djangology - if the concept is understood I think it is easy to implement elsewhere; for instance ususally I work now in Pari/GP and it could be translated nearly line by line (for instance using "vecsort" in Pari/GP instead of "isortsp" here and so on) – Gottfried Helms Feb 27 '14 at 18:21
3

This includes the graph isomorphism problem for bipartite graphs, so is likely to be hard. (To see this, lookat the adjacency matrix A which is $1$ at position $(i,j)$ if vertex number $i$ on the left side is connected to vertex number $j$ on the right side.)

Some heuristics from GIP carry over to this case. Of course, for most matrices just looking at column and row sums will help. In greater generality computing the spectrum of both matrices can help (although not in finding an actual rearrangement).

Eric
  • 565
1

You can sort the rows / columns and then check equality. And to sort, you can use the lexicographical order.

xavierm02
  • 7,515
  • +1: that was fast and simple and is far better than my first idea! – Gottfried Helms Feb 27 '14 at 12:38
  • 1
    I'm not sure if that works. For any matrix $X$, let $X'$ be the matrix obtained by sorting the rows and then the columns. Let $A = \left(\begin{array}{cc}3&2\4&1\end{array}\right)$. Sorting the rows lexicographically, the matrix remains the same. Next, sorting the columns lexicographically, we find that $A' = \left(\begin{array}{cc}2&3\1&4\end{array}\right)$. Similarly, let $B = A' = \left(\begin{array}{cc}2&3\1&4\end{array}\right)$. We have that $B' = \left(\begin{array}{cc}1&4\2&3\end{array}\right)$. Clearly, $A$ and $B$ are equal up to permutation, but $A' \neq B'$. – yori Feb 27 '14 at 14:26
  • Maybe one can keep sorting the rows and columns until they stabilize? At least for 2x2 matrices, this process ends, I think. Maybe one can prove this for general matrices? And what is the maximum number of required iterations? – yori Feb 27 '14 at 14:32
  • 1
    It seems though that xavierm02's trick would always work if the two matrices are equivalent up to a row OR column permutation. – djangology Feb 27 '14 at 15:06
  • The sorting problem can be overcome. I've done one example in an extra answer – Gottfried Helms Feb 27 '14 at 15:40
  • 2
    Consider $$\left( \begin{array}{cccccccc} 0 & 0 & 0 & 0 & 0 & 1 & 1 & 1 \ 0 & 0 & 0 & 0 & 1 & 0 & 1 & 1 \ 0 & 0 & 1 & 1 & 0 & 0 & 0 & 1 \ 1 & 1 & 0 & 1 & 0 & 0 & 0 & 0 \ \end{array} \right)$$ and $$\left( \begin{array}{cccccccc} 0 & 0 & 0 & 0 & 0 & 1 & 1 & 1 \ 0 & 0 & 0 & 0 & 1 & 0 & 1 & 1 \ 0 & 1 & 1 & 1 & 0 & 0 & 0 & 0 \ 1 & 0 & 0 & 1 & 0 & 0 & 0 & 1 \ \end{array} \right)$$. Both are sorted both column and row wise, but you just need to swap the 2 last rows and the first and third column to see the equivalence. – Coolwater Aug 16 '21 at 12:49
1

For square matrices, invertible, this is easy to show. Assume some (yet unknown) permutation matrix $P$ with the property that $$ P \cdot A = B$$ then if $A$ is invertible one can find $P$ by $$ P = B\cdot A^{-1} $$ and simply inspect, whether it is a proper permutation-matrix.

If A and/or B are not invertible one can do (pivoted) Gaussian elimination from the right side until $P$ becomes visible. If the $A$ and $B$ are not square, one can try to use the pseudoinverse.

  • Unfortunately these aren't square matrices so the trick of inverting them won't work. I had considered it which is why I added the qualifier 'rectangular' to my original post --- apologies if that was not clear! I had not thought of using a pseudoinverse though --- this seems like it would be the best way to tackle this because it would answer the more general problem of whether the two matrices are related by a linear combination (i.e. given two rectangular matrices $A_{m \times n$,$B_{m \times n}$, is there a square matrix $M_{n \times n}$ such that A=BM). But I'm not sure about this either. – djangology Feb 27 '14 at 15:11
  • 3
    Also, the equality would be $P\cdot A \cdot Q = B$, not $P\cdot A = B$, and this is way harder to deal with. – darij grinberg Feb 27 '14 at 16:44
  • @darij: true, I found that, too. My first idea was to use diagonalization of the symmetric $A^* = A \cdot A'$ and $B^* = B \cdot B' $ and apply the finding routine to the matrices of eigenvectors in the given way to find first the permutation P and after that the permutation Q. That works, however because the columns of the eigenvectors are not completely normed in the eigen-computation (the signs might change) one needs to check and possibly correct manually... so this is all not very good. But see now by better, second answer which seems to be a perfect valid solution. – Gottfried Helms Feb 27 '14 at 16:50
0

The sorting approach was helpful for a similiar problem for me: check if two truth tables for two logic circuits are isomorph only allowing permutations of rows and columns: https://electronics.stackexchange.com/questions/339815/how-to-effectively-determine-if-given-truth-table-is-equal-to-another-one-when/676363#676363

There one only have 0 or 1 as value so I wasn't able to find a second sorting function. For -1, 0 and 1 the second sorting function might be exp(). Anyway - this will not lead to unique keys most of the time but you can form groups of columns and rows that have equal keys.

This way you only have to permute inside of those groups and can hold rows and columns with unique keys in place. In many cases this will reduce the number of permutations needed to check for graph isomorphism (as a brute force algo would imply). For example for a specific 8x7 matrix it reduces the iterations needed from 5806080 to 432.

  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center. – Community Aug 03 '23 at 13:29
-1

I think this problem is related to the problem of equivalence of two graphs (just as Darij Grinberg mentioned above), and I think I've found a pretty fast polynomial solution for the graphs. I wrote up the explanation starting with http://babkin-cep.blogspot.com/2018/06/graph-equivalence-1-overall-algorithm.html (the informal proof of why I think it works is in the parts 3 and 4, and in case if someone notices any errors in it, I would be interested to hear about them). It can probably be adapter to the matrices as well.