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

Choose an element currmat from binlist, and generate a list currsym of all rotations and reflections of currmat.

Define a new list binminsym which is binlist with all elements of currsym removed.

Add the pair {currmat, Length[currsym]} to a new list binclasses.

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.

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!

Welcome to SE! You'll probably get more feedback if you also post the code you have so far.
–
Teake NutmaAug 6 '13 at 8:35

This may help, but I don't have time right now to write up a full answer here.
–
rcollyerAug 6 '13 at 13:11

@Owen Daniel Is there some constraint that my answer did not satisfy?
–
HectorAug 13 '13 at 13:27

@Hector. No you matched the constraints, but as I pointed out, my code runs significantly faster than yours did, I'm waiting to see whether somebody can actually provide a well optimized version.
–
Owen DanielAug 13 '13 at 16:31

@OwenDaniel Faster than 4 seconds for the 4x4 case? I just cannot believe it.
–
HectorAug 13 '13 at 18:31

Thanks for the response. There seem to be a couple of errors in the code, and you're missing two of the symmetries (r2 and r3, which are r1 applied to themselves). This seems to be quite a bit slower than the code I posted above when doing the 4 x 4 case, I guess because Tally is comparing every pair. Thanks again.
–
Owen DanielAug 8 '13 at 8:08

Tally does not make all possible comparisons. For example, Tally[Array[a[#, Mod[#, 2]] &, {4}], (Print[#1, #2];Last[#1] === Last[#2]) &] will print only 4 comparisons. However, Tally does not remove from the pool as you do. As for the missing symmetries, you can put them easily into the code.
–
HectorAug 8 '13 at 18:40

For comparison: Four seconds on Hector's machine corresponds to about six seconds on mine. This solution is a little slower than Hector's, but it should scale better to the 5x5 case. I was surprised not to get a speed-up; this is a linear-time algorithm, but apparently the cost of calling Union[toInteger/@(allSyms@toMatrix[#])] for every single matrix is enough to nullify any advantage in the 4x4 case.

I thought it would be interesting to implement a few other (still linear-time) solutions using Hector's matrix functions and compare performance.

Here is the same solution, but with allSyms memoized (i.e. the integers corresponding to the matrices symmetric to the matrix corresponding to a given integer are remembered in the definition of the function sortedSymInts):

Here are two reimplementations of my previous solution, using Hector's matrix functions. The first memoizes integers representing matrices symmetric to matrices which have already been seen. The second memoizes the matrices themselves, and only uses toMatrix to generate the initial list.

Surprisingly (to me), the last is the fastest. Apparently it is faster to memoize the matrices than to call toInteger and then memoize the integers. The hash function used by Mathematica's rule processing code must be very fast for lists (or perhaps slow for integers???).

(1) The 3.98 seconds were in my little Surface Pro. I got 6 seconds too in my 6-core AMD 2.70Ghz desktop. (2) It is interesting that hashing a matrix and looking up the hashed matrix is faster than transforming the matrix, hashing the integer, and looking up the integer. (3) It would be interesting to see if compiling toInteger makes any difference.
–
HectorAug 15 '13 at 22:20

Hi, thanks for your answer. There is a small typo in display result, where you meant an2. Your code appears to be counting the number of distinct symmetries for each matrix, however is not sorting them into equivalence classes (I don't think!). I think that the sorting stage is the computationally heavy part! Thanks again.
–
Owen DanielAug 8 '13 at 8:24

@OwenDaniel Thank you. Sorry for the typo. I misinterpreted the question but now it is a starting point for me. Sorry.
–
ubpdqnAug 8 '13 at 8:31

Hi. Just tried it out on the 4 x 4 case and I got: "No more memory available. Mathematica kernel has shut down. Try quitting other applications and then retry."! Will try again on another computer later!
–
Owen DanielAug 8 '13 at 14:45

Mathematica is a registered trademark of Wolfram Research, Inc. While the mark is used herein with the limited permission of Wolfram Research, Stack Exchange and this site disclaim all affiliation therewith.