Since the number of possible row-and-column-permutations of any given matrix is finite (namely, a $t\times n$ matrix has at most $t!\times n!$ possible permutations), one way to canonicalize a matrix over row-and-column-swaps would be to take the lexicographical minimum of those possible permutations. For example, your sample matrix
| 1 3 2 2 2 |
| 1 4 2 2 2 |
| 0 1 0 1 1 |
| 0 1 1 0 0 |
can be unambiguously represented as the concatenation of all its rows:
13222142220101101100
And then we can compute all possible permutations of its rows and columns, for example using a brute-force Python program like this:
import itertools
def canonicalize(NROWS, NCOLS, m):
possibilities = set()
for rows in itertools.permutations(range(NROWS)):
for cols in itertools.permutations(range(NCOLS)):
p = ''.join(m[r*NCOLS+c] for r in rows for c in cols)
possibilities.add(p)
for p in sorted(possibilities):
print(p)
return min(possibilities)
canonicalize(4, 5, '13222142220101101100')
This gives us 1440 possible permutations for 13222142220101101100
. (Notice that 1440 is much smaller than $(4\times 5)! \sim 2.4\times 10^{18}$. It happens to be exactly half of $4!\times 5! = 2880$ because your matrix has those two duplicate columns; but I don't think that's a useful observation in general.) Those 1440 possible permutations, in order from lexicographically least to greatest, are:
00011011011222312224
00011011011222412223
00011011101223212242
[...skip a few...]
42221322211100010110
42221322211101010100
42221322211110010010
So if we canonicalize matrices by taking the lexicographical minimum possible permutation, then the canonical form of your matrix would be
| 0 0 0 1 1 |
| 0 1 1 0 1 |
| 1 2 2 2 3 |
| 1 2 2 2 4 |
Unfortunately there is no known algorithm that can perform this canonicalization quickly for arbitrary matrices. Brute force gets prohibitively slow for large matrices. You can think up tricks (such as "If the matrix has a unique least element, that element must appear in the upper left corner"); however, these tricks don't work for arbitrary matrices (such as your example matrix with five 0s and seven 1s. It is mere coincidence that your unique greatest element 4 ended up in the lower right corner).
I know there is no known algorithm to perform this canonicalization quickly, because if there were, then we could use that algorithm to perform graph isomorphism quickly: just canonicalize the adjacency matrix of each graph and see if they're canonically identical. For example, if we're given these two graphs...

...then we just compute the adjacency matrices for the left-hand graph and the right-hand graph...
0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 1
1 0 0 1 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0 0
1 0 0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0
0 1 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 1 0
0 0 1 0 0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 1
0 0 0 1 1 0 0 1 0 0 1 0 0 0 1 0 1 0 0 0
0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 1 0 1 0 0
0 0 0 0 0 1 1 0 0 1 0 0 1 0 0 0 1 0 1 0
0 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 1
1 0 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 1 0
...and then we run them through our canonicalization algorithm...
>>> left
'0110000001100100001010011000000110010000001001100000011001000000100110000001100101000010011000000110'
>>> right
'0100010001101000100001010001000010100010000101000110001010000100010100001000101000010001011000100010'
>>> canonicalize(10, 10, left)
'0000000111000011100000010110000010000011011000000110010010001001100000011000010001000001101000110000'
>>> canonicalize(10, 10, right)
'0000000111000011100000010110000010000011011000000110010010001001100000011000010001000001101000110000'
>>> assert canonicalize(10, 10, left) == canonicalize(10, 10, right)
Yep, these graphs are isomorphic! But the graph of a pentagonal prism (swapping a couple of edges in the Moebius graph) is not isomorphic to either one, because its canonicalization is different:
>>> pentagonal_prism
'0110000010100100000110011000000110010000001001100000011001000000100110000001100110000010010100000110'
>>> canonicalize(10, 10, pentagonal_prism)
'0000000111000011100000010110000010000011011000000110010010001001000100011010000001001100001000000110'
>>> assert canonicalize(10, 10, pentagonal_prism) != canonicalize(10, 10, left)
The existence of this reduction indicates that canonicalize
cannot be polynomial-time in every case, because if it were polynomial-time, then we would have a proof that GI was in P, and in fact no such proof has been discovered yet. The Python implementation of canonicalize
that I sketched above has factorial complexity — much greater than polynomial complexity.
The Nauty/Traces library has an ad-hoc algorithm to "efficiently" (on average, for non-pathological inputs) find a canonicalization of any graph, which means that it can "efficiently" find a canonicalization of any matrix whose entries are only 0 or 1. However, Nauty/Traces' chosen canonicalization is not related to lexicographical comparison. I don't have any special knowledge of Nauty/Traces' algorithm. For more technical information, see my blog post "Canonicalizing {0,1}-matrices with Nauty/Traces" (2020-01-11).
The code in my blog post happens to canonicalize left
and right
into
'1110000000000111000000011010000001010100000001110000001011000010000011010000001111000000101010000001'
and pentagonal_prism
into
'1110000000000100101000001001010000110100000101100000011100000010000101010000101010100000011100000010'
I do not know how to reduce your problem "canonicalize a general matrix" to "canonicalize a {0,1}-matrix." If you can find such a reduction, then you could use Nauty/Traces' {0,1}-canonicalization algorithm to solve your problem "efficiently" for general matrices. I expect that the reduction exists, but I don't know how to do it.