Contents

Counting and generating inequivalent binary relations

Posted on September 15, 2025 by Daniel Vitek.
Tagged: , .
There’s a standard genre of group theory exercise that goes like, “Given a group GG acting on a set XX, count the number of GG-orbits in XX.” The intended approach is to use Burnside’s lemma GX/G=gGFix(G).|G| \cdot |X/G| = \sum_{g \in G} |\mathrm{Fix}(G)|.

For some recent work, I wanted to generate all binary relations on a finite set up to equivalence under permuting the elements of the finite set. In order to check my code, I wanted to count the number of binary relations up to equivalence. This smells like a Burnside’s-lemma problem! Today we’ll solve that problem, then spend some time talking about how to actually generate all binary relations.

Fixing notation

Let SS be a finite set, and consider the set XX of all binary relations on SS. Let the permutation group Aut(S)\mathrm{Aut}(S) act on XX by relabeling the elements of SS; if we view a relation RR as an S×S|S| \times |S| 0/1-matrix MRM_R (with ones corresponding to pairs a,ba, b with aRbaRb), this action simultaneously permutes the rows and columns of MRM_R. Then X/Aut(S)|X/\mathrm{Aut}(S)| counts the number of binary relations on a finite set up to relabeling the elements of the set.

In our problem, it’s clear how big Aut(S)|\mathrm{Aut}(S)| is, so all of the work is going to come in figuring out the sizes of the fixed-point sets. A general fact that’s useful in Burnside-lemma problems is that the size of the fixed-point set is constant on conjugacy classes of GG, so we can collect the summands into conjugacy classes gGFix(g)=πCl(G)πFix(π).\sum_{g \in G} |\mathrm{Fix}(g)| = \sum_{\pi \in \mathrm{Cl}(G)} |\pi| |\mathrm{Fix}(\pi)|.

Since Aut(S)\mathrm{Aut}(S) is a finite symmetric group, its conjugacy classes are indexed by cycle type α=(α1,α2,,αS)\alpha = (\alpha_1, \alpha_2, \cdots, \alpha_{|S|}) as the cycle type ranges over all solutions in nonnegative integers to i=1Siαi=S\sum_{i = 1}^{|S|} i\alpha_i = |S|; i.e., over all integer partitions of S|S|. Calculating the number of permutations of a given cycle type is classical, so the only new ingredient will be calculating the size of the fixed-point set.

The grid

Here it’s helpful to draw a picture. Let’s consider the permutation on six elements (1)(23)(456)(1)(23)(456) with cycle type (1,1,1,0,0,0)(1,1,1,0,0,0). This permutation moves the elements of the matrix MRM_R of a relation as shown below:
The orbits of the action of the permutation  on the matrix of a relation.

How many 0/1-matrices does this permutation fix? Well, the matrix elements along any orbit of this action should be constant, so the permutation fixes 2#orbits2^{\text{\#} orbits} matrices. Okay, so how many orbits are there? Each orbit is contained in a grid cell in the picture above, so we can work cell-by-cell. The number of orbits in a cell of height hh and width ww is simply gcd(h,w)\gcd(h, w), so that the total number of orbits of the action of a permutation with cycle type α\alpha is simply i,j=1ngcd(i,j)αiαj\sum_{i,j = 1}^n \gcd(i,j)\alpha_i\alpha_j.

Checking our work

Putting everything together and doing a tiny bit of cancellation, we get the formula X/Aut(S)=αS2i,j=1Sgcd(i,j)αiαji=1Siαiαi!.\left|X/\mathrm{Aut}(S)\right| = \sum_{\alpha \bot |S|} \frac{2^{\sum_{i,j=1}^{|S|} \gcd(i,j)\alpha_i\alpha_j}}{\prod_{i=1}^{|S|}i^{\alpha_i}\alpha_i!}. For S=1,2,|S| = 1, 2, \dots, we get the sequence of counts 2,10,104,3044,291968,96928992,112282908928,458297100061728,.2, 10, 104, 3044, 291968, 96928992, 112282908928, 458297100061728, \dots. This is OEIS sequence A000595, so we’ve done everything correctly.

One fun aspect of the formula above is that it’s a bunch of different powers of two divided by various integers that are definitely not powers of two, yet somehow the total is guaranteed to still be an integer. This is typical of Burnside problems. In fact, in our specific instance we could abandon the binary-relation interpretation, allow our matrices to be 0/1/2-valued, and replace 22 by 33 in the formula above, and we’d know we’d still get an integer out. The same goes for 4 or 5, so replacing 22 by xx we get for each S|S| a polynomial with rational coefficients that is nonetheless always integer-valued. Here are the first few polynomials for S=1,2,|S| = 1, 2, \dots:

x,x42+x22,x96+x52+x33,x1624+x104+x88+x63+x44,.x, \frac{x^4}{2}+\frac{x^2}{2}, \frac{x^9}{6}+\frac{x^5}{2}+\frac{x^3}{3}, \frac{x^{16}}{24} + \frac{x^{10}}{4}+\frac{x^8}{8}+\frac{x^6}{3}+\frac{x^4}{4}, \dots.

We can see that the maximal-degree term comes from the cycle type (S,0,,0)(|S|,0,\dots,0) and the minimal-degree term comes from the cycle type (0,,0,1)(0,\dots,0,1). This suggests that the number of inequivalent binary relations should grow as the intuitive guess 2n2/n!2^{n^2}/n!. A little bit more work is needed to make this precise since it could a priori be the case that there are a large number of slightly-lower-degree terms with smaller denominators that eventually overwhelm the leading-term asymptotics.
Indeed, in some sense this happens for x=1x=1 where all the polynomials are equal to 1 rather than decaying as O(1/S!)O(1/|S|!).

Divertimento: a Möbius trick

For large nn, there’s a cute little way to speed up the summation i,j=1ngcd(i,j)αiαj\sum_{i,j=1}^n \gcd(i,j) \alpha_i \alpha_j, which naively takes O(n2logn)O(n^2\log n) time (assuming we have O(1)O(1) access to the αi\alpha_i). The trick is to find coefficients cdc_d such that i,j=1ngcd(i,j)αiαj=d=1ncdi,j=1n/dαdiαdj=d=1ncd(i=1nαdi)2.\sum_{i,j=1}^n \gcd(i,j) \alpha_i \alpha_j = \sum_{d=1}^n c_d \sum_{i,j=1}^{\lfloor n/d \rfloor} \alpha_{di}\alpha_{dj} = \sum_{d=1}^n c_d \left(\sum_{i=1}^n \alpha_{di}\right)^2. We can compute all the stride-dd sums i=1n/dαdi\sum_{i=1}^{\lfloor n/d \rfloor} \alpha_{di} in O(nlogn)O(n \log n) time, so the only question left is what the coefficients cdc_d should be. The easiest way to determine this is to match the coefficients on an arbitrary αiαj\alpha_i\alpha_j term. Letting g=gcd(i,j)g = \gcd(i,j), we find that we need dgcd=g\sum_{d | g} c_d = g. Then we can just apply Möbius inversion to get cd=kdμ(k)dk=ϕ(d),c_d = \sum_{k \mid d} \mu(k)\frac{d}{k} = \phi(d), where ϕ(d)\phi(d) is the Euler phi function. Of course, now we need all the ϕ(d)\phi(d) values for 1dn1 \le d \le n, but these can be computed via a sieve-based approach in time
The time analysis here uses Mertens’ theorem that px1p=O(loglogx).\sum_{p \le x} \frac{1}{p} = O(\log \log x). Note that when computing the full Burnside sum we should obviously cache these values.
O(nloglogn)O(n \log \log n). Hence the dominant term is the O(nlogn)O(n \log n) computation of the stride-dd sums, so we’ve shaved off a full factor of nn from the computation!

Of course, in our broader context nn is so small (since the number of partitions grows quickly enough to overwhelm my computer around n=40n = 40) as to make this improvement barely noticeable.

Combinatorial enumeration

What if we want to actually generate representatives of each equivalence class of binary relations on a set of size nn? It’s one thing to count them; it’s another to actually generate them. The obvious and very stupid strategy is:
  1. Enumerate all 2n22^{n^2} binary relations.
  2. For each relation, check whether it is equivalent to any relation we’ve already seen.
  3. If not, add it to our list.

Thankfully, there is a far more practical approach, due to Read
R. C. Read, “Every one a winner, or how to avoid isomorphism search when cataloguing combinatorial configurations,” Annals of Discrete Mathematics 2 (1978), 107–120.
, that avoids equivalence testing almost entirely. Somehow the frustratingly-vague term “combinatorial enumeration” seems to have attached itself to this approach (Read uses the far-better phrase “orderly algorithm”), which is applicable to a number of problems similar to our enumerating-binary-relations focus for today.

The setup

Let us suppose that we have some set SS with an isomorphism relation \cong, and we want to generate a single representative from each isomorphism class. The setup for an orderly algorithm requires several choices of auxiliary data:

We should also have some algorithm to determine whether an element of SS is canonical, as well as some way of constructing the ordered list L0=(CS0,)\mathcal{L}_0 = (\mathcal{C} \cap S_0, \prec). The data will need to satisfy two conditions–canonical covering and weak monotonicity–which we discuss below. First, though, let us describe how an orderly algorithm proceeds.

The central idea is that we construct the ordered list Lq+1=(CSq+1,)\mathcal{L}_{q+1} = (C \cap S_{q+1}, \prec) from the ordered list Lq\mathcal{L}_q and the augmentation operation AA; we can then recover C=L0⨿L1\mathcal{C} = \mathcal{L}_0 \amalg \mathcal{L}_1 \cdots. To build Lq+1\mathcal{L}_{q+1}, we begin with the empty list. For each element eLqe \in \mathcal{L}_q, we form the list A(e)A(e). Then, for each fA(e)f \in A(e), we append ff to Lq\mathcal{L}_q iff ff is greater than the \prec-maximal element of Lq\mathcal{L}_q
Taking this to be vacuously true when Lq+1\mathcal{L}_{q+1} is empty.
and fCf \in \mathcal{C}. The key point is that while we should store Lq+1\mathcal{L}_{q+1} somewhere as we are building it up
Even if we intend to consume Lq+1\mathcal{L}_{q+1} towards some other end goal, we do need to store it in order to generate Lq+2\mathcal{L}_{q+2}.
, we never need to search over Lq+1\mathcal{L}_{q+1}; we simply have to store the current maximal element instead.

The conditions

Let’s now turn to what conditions we should require of our data for this orderly algorithm to work. First, if all elements of CSq+1\mathcal{C} \cap S_{q+1} are going to appear in Lq+1\mathcal{L}_{q+1}, then each of them must be generated in some augmentation step. This is what we call canonical covering: namely, that we have CSq+1eCSqA(e).\mathcal{C} \cap S_{q+1} \subseteq \bigcup_{e \in \mathcal{C} \cap S_q} A(e).

This is not enough to ensure that all canonical elements of Sq+1S_{q+1} actually appear in Lq+1\mathcal{L}_{q+1}, however. We must also choose to append them, which means they must be maximal in the \prec-order at the first time they are generated in an augmentation step. Tracing this backwards gives us weak monotonicity: namely, if e,fCSq+1e, f \in \mathcal{C} \cap S_{q+1} with e<fe < f, then we must have

min{xCSq:eA(x)}min{xCSq:fA(x)}.\min \left\{x \in \mathcal{C} \cap S_q : e \in A(x) \right\} \preceq \min \left\{x \in \mathcal{C} \cap S_q : f \in A(x) \right\}.

It turns out that these two conditions are enough to ensure that the orderly algorithm generates all elements of C\mathcal{C}. The only argument that needs to be made is that if an element of Sq+1S_{q+1} is not appended to Lq+1\mathcal{L}_{q+1} the first time it is generated, then it cannot be appended any subsequent time, which is an easy monovariance argument.

Binary relations

Let us now turn to the specific example of binary relations, following the construction of Read.
Note that Read uses the language of digraphs rather than binary relations.
For a binary relation RR on {1,2,,n}\{1, 2, \cdots, n\}, define code(R)=1i,jn1[iRj]2n(ni)+(nj).\operatorname{code}(R) = \sum_{1 \le i, j \le n}\mathbf{1}_{[iRj]} 2^{n(n-i)+(n-j)}. This code is simply the 0/10/1-matrix MRM_R read out row-by-row in binary. As discussed above, we need to fix notions for the parameter qq, the ordering \prec, the canonical set C\mathcal{C}, and the augmentation operation AA. We take these in a somewhat-haphazard order:

Note that canonicity-testing will still be somewhat expensive, but we won’t have to run it very often. In addition, in this setup we can stop early if we observe some permutation σ\sigma such that code(R)>code(σ(R)).\operatorname{code}(R) > \operatorname{code}(\sigma(R)). We could also do this early stopping in a slightly-less-naive generation algorithm than that above: namely, enumerate all 2n22^{n^2} binary relations, discarding any whose codes are not \prec-minimal in their SnS_n orbit; the advantage of our orderly algorithm is that we have to test far fewer candidates for canonicity.

An initial guess might be that we will filter binary relations by the size of the underlying set. While this decomposition is appropriate in some contexts
See for instance §5 of Read on rooted trees.
, we will instead fix the size nn of the underlying set and use an alternate definition for qq:

Hence qq will vary over the range 0qn20 \le q \le n^2, though by taking complements we may further restrict qn2/2q \le n^2/2. This makes L0\mathcal{L}_0 very easy to construct indeed.

Augmentation must then increase the number of ones in the matrix of a relation somehow. Read gives two augmentation operations that will work: a relatively naive version, and a more efficient version.

Both augmentation operations produce ordered lists by using the \prec ordering on relations. We now need to check that both augmentation operations are canonical-covering and weakly-monotone. We’ll do that below; for now, let’s work a couple explicit examples.

Two elements

Let’s work through generating all 10 equivalence classes of binary relations on {1,2}\{1, 2\}, using both augmentation operations. Given a relation RR on {1,2}\{1,2\} with code r11r12r21r22r_{11}r_{12}r_{21}r_{22}, the non-identity element σ\sigma of S2S_2 sends RR to the relation with code rσ(1)σ(1)rσ(1)σ(2)rσ(2)σ(1)rσ(2)σ(2)=r22r21r12r11.r_{\sigma(1)\sigma(1)}r_{\sigma(1)\sigma(2)}r_{\sigma(2)\sigma(1)}r_{\sigma(2)\sigma(2)} = r_{22}r_{21}r_{12}r_{11}. In other words, it reverses the code. So the canonicity check is quite simple: is the code greater than its reversal?

We begin with the A0A_0 case, and have initially L0=[0000].\mathcal{L}_0 = [0000].

Note that we could have stopped after generating L2\mathcal{L}_2, since in the binary-relation setting we can obtain (non-canonical) representatives of the remaining equivalence classes by taking the elements of L1\mathcal{L}_1 and L0\mathcal{L}_0 and swapping all zeroes and ones.

For the A1A_1 case, we will start at the same point with L0=[0000].\mathcal{L}_0 = [0000]. Since all of the zeroes of 00000000 are trailing, we have A1(0000)=A0(0000)=[1000010000100001]A_1(0000) = A_0(0000) = [1000 \prec 0100 \prec 0010 \prec 0001]. Hence L1=[10000100]\mathcal{L}_1 = [1000 \prec 0100] as above, but the differences start at L2\mathcal{L}_2.

Notice that the A1A_1 traces contain no “wasted” candidates that get skipped due to duplication with an earlier augmentation; this is the sense in which A1A_1 is more efficient than A0A_0.

Property checking

Let us now verify that the A0A_0 augmentation is canonical-covering and weakly-monotone, following the arguments in §2 of Read. The corresponding arguments for A1A_1 are similar in spirit but noticeably more intricate, and we direct the interested reader to Read’s paper.

For any σSn\sigma \in S_n, write πσSn2\pi_\sigma \in S_{n^2} for the permutation on code positions induced by the simultaneous row-and-column action of σ\sigma on matrices, so that code(σR)p=code(R)πσ(p)\operatorname{code}(\sigma R)_p = \operatorname{code}(R)_{\pi_\sigma(p)} for any relation RR on {1,,n}\{1, \ldots, n\} and any code position pp. The key observation is that if two relations YY and XX differ only at one code position kk, then σY\sigma Y and σX\sigma X differ only at the position p:=πσ1(k)p^* := \pi_\sigma^{-1}(k).

Canonical covering

Let YCSq+1Y \in \mathcal{C} \cap S_{q+1}, and let kk be the position of the rightmost 1 in code(Y)\operatorname{code}(Y). Let XX be the relation obtained by flipping this 1 to a 0, so that XSqX \in S_q and YA0(X)Y \in A_0(X). We claim that XX is canonical; this suffices for canonical covering, since we have exhibited YY as the image of a canonical element of SqS_q under A0A_0.

Suppose for contradiction that some σSn\sigma \in S_n satisfies code(σX)>code(X)\operatorname{code}(\sigma X) > \operatorname{code}(X), and let jj be the first position where they disagree, so that (σX)j=1(\sigma X)_j = 1 and Xj=0X_j = 0. Since XX’s qq ones are all in positions 1,,k11, \ldots, k - 1, and σX\sigma X has the same weight qq, we must have j<kj < k: for if jkj \ge k, then σX\sigma X agrees with XX on all qq of XX’s 1s, accounting for its entire weight, so (σX)j(\sigma X)_j could not be 1.

Now compare σY\sigma Y to YY, recalling that they differ only at code position p:=πσ1(k)p^* := \pi_\sigma^{-1}(k) where (σY)p=1(\sigma Y)_{p^*} = 1 and (σX)p=0(\sigma X)_{p^*} = 0. Note pjp^* \ne j, since (σX)j=10=(σX)p(\sigma X)_j = 1 \ne 0 = (\sigma X)_{p^*}. Consider two sub-cases.

Either way, σ\sigma violates the canonicity of YY. This is a contradiction, so XX must have been canonical after all.

Weak monotonicity. Let e,fCSq+1e, f \in \mathcal{C} \cap S_{q+1} with efe \prec f, i.e., code(e)>code(f)\operatorname{code}(e) > \operatorname{code}(f). We must show that the \prec-minimum canonical predecessor of ee under A0A_0 is \preceq the \prec-minimum canonical predecessor of ff under A0A_0.

For any canonical RSq+1R \in S_{q+1}, the canonical covering argument shows that flipping the rightmost 1 of RR produces a canonical element of SqS_q. This flip also produces the element with the largest possible code (since we are subtracting the smallest power of 2), so it is the \prec-minimum canonical predecessor of RR under A0A_0. Write XeX_e and XfX_f for the rightmost-1 flips of ee and ff, and kek_e and kfk_f for the positions of their rightmost 1s; we must show code(Xe)code(Xf)\operatorname{code}(X_e) \ge \operatorname{code}(X_f).

If kekfk_e \ge k_f, then 2n2ke2n2kf2^{n^2 - k_e} \le 2^{n^2 - k_f}, so code(Xe)=code(e)2n2kecode(e)2n2kf>code(f)2n2kf=code(Xf),\operatorname{code}(X_e) = \operatorname{code}(e) - 2^{n^2 - k_e} \ge \operatorname{code}(e) - 2^{n^2 - k_f} > \operatorname{code}(f) - 2^{n^2 - k_f} = \operatorname{code}(X_f), and we are done. Assume instead that ke<kfk_e < k_f, and compare the prefixes e[1ke1]e[1 \ldots k_e - 1] and f[1ke1]f[1 \ldots k_e - 1].

First, we rule out e[1ke1]<f[1ke1]e[1 \ldots k_e - 1] < f[1 \ldots k_e - 1]: if this held, then at the first differing position j<kej < k_e we would have ej=0e_j = 0 and fj=1f_j = 1, and since the largest possible contribution of positions >j> j to code(e)\operatorname{code}(e) is 2n2j12^{n^2 - j} - 1 (strictly less than the 2n2j2^{n^2 - j} contributed by fjf_j), we would have code(e)<code(f)\operatorname{code}(e) < \operatorname{code}(f), a contradiction.

If e[1ke1]>f[1ke1]e[1 \ldots k_e - 1] > f[1 \ldots k_e - 1], then XeX_e and XfX_f first disagree at the first position j<kej < k_e where ee and ff disagree, where (Xe)j=ej=1(X_e)_j = e_j = 1 and (Xf)j=fj=0(X_f)_j = f_j = 0; hence code(Xe)>code(Xf)\operatorname{code}(X_e) > \operatorname{code}(X_f).

Otherwise ee and ff have identical prefixes, containing all qq of ee’s 1s other than the rightmost. Since ff has total weight q+1q + 1 and a 1 at kf>kek_f > k_e, its prefix already accounts for qq of its 1s, so ff has no 1s in positions ke,,kf1k_e, \ldots, k_f - 1. Then XeX_e and XfX_f both have 1s at exactly the positions of the common prefix, and nowhere else, so Xe=XfX_e = X_f and code(Xe)=code(Xf)\operatorname{code}(X_e) = \operatorname{code}(X_f).

In all cases code(Xe)code(Xf)\operatorname{code}(X_e) \ge \operatorname{code}(X_f), as required.