I am interested in the equivalence relation on N x N binary matrices, in which two matrices are equivalent if one can be obtained by rotating/reflecting the other. I would like to obtain a list
binclasses = { {B1, m1},...{Bk, mk} }
,
where Bi is a representative element of class i, and mi is the size of that class. (Of course the requirement of binary matrices is somewhat artificial here, so the question could be rephrased for matrices taking values in a finite set). For N =2, I am looking to obtain the list
binclasses = {{{{0, 0}, {0, 0}}, 1}, {{{0, 0}, {0, 1}}, 4}, {{{0, 0}, {1, 1}},
4}, {{{0, 1}, {1, 0}}, 2}, {{{0, 1}, {1, 1}}, 4}, {{{1, 1}, {1, 1}},
1}}
My thought process so far has been as follows:
- Define a list
binlist
of all N x N binary matrices. - Choose an element
currmat
frombinlist
, and generate a listcurrsym
of all rotations and reflections ofcurrmat
. - Define a new list
binminsym
which isbinlist
with all elements ofcurrsym
removed. - Add the pair
{currmat, Length[currsym]}
to a new listbinclasses
. - Set
binlist = binminsym
, return to step 1.
I am guessing that this is not a particularly efficient approach, and am curious to know how one could better go about the problem.
EDIT A second update, I have improved on the original code, however, it still runs slowly (original edit left below this for information). In this variation I first group binary matrices by the number of 1s, and the number of adjacent 1s; this means that fewer list elements need be checked against each other. At the moment for the 4 x 4 case, this version takes a little over 4 minutes to identify the 8548 equivalence classes, compared to the 17 minutes of the previous method.
(* sTotal function gives total of all matrix entries *)
sTotal[matrix_] :=
Sum[matrix[[i, j]], {i, 1, Length[matrix]}, {j, 1, Length[matrix]}];
(* eTotal function gives total number of adjacent non-zero entries *)
halo[matrix_] :=
Module[{cmat},
cmat = ConstantArray[0, {Length[matrix] + 1, Length[matrix] + 1}];
cmat[[1 ;; Length[matrix], 1 ;; Length[matrix] ]] = matrix;
cmat]
eTotal[matrix_] :=
Sum[halo[matrix][[i, j]] (halo[matrix][[i, j + 1]] +
halo[matrix][[i + 1, j]]), {i, 1, Length[matrix]}, {j, 1,
Length[matrix]}];
Timing[
Clear[mdim, blist, bgrlist, binclasses, binreps, currbg, currmat,
currsym]
mdim = 4; (*matrix dimensions*)
blist = Tuples[{0, 1}, {mdim,
mdim}]; (* All square binary matrices of size mdim *)
(* blist grouped into sets of matrices with equal number of 1s, and \
equal adjacent 1s *)
bgrlist = GatherBy[blist, {sTotal, eTotal}];
(* Initialise list of equivalence classes *)
binclasses = {};
binreps = {};
]
Timing[
Do[
Do[
currbg = bgrlist[[i, j]];
While[Length[currbg] >= 1,
currmat = currbg[[1]];
currsym = symlist[currmat];
binclasses = Append[binclasses, {currmat, Length[currsym]}];
binreps = Append[binreps, currmat];
If[i != 1 + (mdim^2 /2 ),
binreps =
Append[binreps, ConstantArray[1, {mdim, mdim}] - currmat];
];
currbg = DeleteCases[currbg, x_ /; MemberQ[currsym, x] == True];
]
, {j, 1, Length[bgrlist[[i]]]}];
, {i, 1, Ceiling[Length[bgrlist]/2]}];
Length[binreps]
]
EDIT Here is my code for the above algorithm. Note, that I have defined the symmetries of the square as functions, even though some of them (eg. reflection in diagonal) are simply transposing the matrix. I did this for clarity (for readers on here), rather than efficiency!
(*Symmetry functions.*)
vsym[matrix_] := Reverse[matrix, {2}];
hsym[matrix_] := Reverse[matrix];
d1sym[matrix_] := Transpose[matrix];
r1sym[matrix_] := vsym[d1sym[matrix]];
d2sym[matrix_] := r1sym[vsym[matrix]];
(*List of all distinct symmetries of given matrix *)
symlist[matrix_] :=
DeleteDuplicates[{matrix, vsym[matrix], hsym[matrix], d1sym[matrix],
d2sym[matrix], r1sym[matrix], r1sym[r1sym[matrix]],
r1sym[r1sym[r1sym[matrix]]]}];
(* All square binary matrices of size mdim *)
binlist[mdim_] := binlist = Tuples[{0, 1}, {mdim, mdim}];
(* Initialise list of equivalence classes *)
binclasses = {};
binlist[2];
While[ Length[binlist] >= 1,
(currmat = binlist[[1]];
currsym = symlist[currmat];
binclasses = Append[binclasses, {currmat, Length[currsym]}];
binlist = DeleteCases[binlist, x_ /; MemberQ[currsym, x] == True]);
]