[Math][SAT] Hunting for extremal Latin squares using MaxSAT solver

(It's assumed that the reader is familiar with random walk, Latin squares and transversals.)

Extremal LS (Latin Square) is the one that has minimally/maximally known transversals (lower/upper bounds).

Here I'm going to describe how I found L15 with TS=170369 and added it to OEIS A091323. To my knowledge, this is a best lower bound for L15 as of autumn 2025.

For random walk, we simply modify a cell (or symbol) in LS and ask MaxSAT solver to fix other cells so that LS will become valid (all LS constaints for rows/columns would be valid). Modified cell clauses are added as hard clauses (MUST be satisfied), and all other clauses are added as soft (it would be GOOD to satisfy as many clauses as possible).

In plan English, we modify a random cell/symbol and ask MaxSAT solver to find minimum cells to be flipped/modified so that LS become valid.

I'll call this procedure 'LS repair'.

As of L15, 4-11 cells must be modified to 'repair' it.

For example, here we repair L15 by modifying 4 cells after setting cell at row=3 column=11 to 3 (marked with asterisk):

set row/col to i 3 11 3
original_sq:
0 1 2 3 4 5 6 7 8 9 a b c d e 
1 2 0 7 5 8 3 6 4 d b e 9 c a 
2 5 4 0 8 a 1 3 b 7 e d 6 9 c 
3 0 1 6 2 4 7 9 5 c 8 a d e b 
4 8 5 2 b e 0 1 a 6 c 9 3 7 d 
5 4 8 1 a b 2 0 e 3 d c 7 6 9 
6 7 3 c 1 0 9 d 2 b 5 4 e a 8 
7 3 6 9 0 2 d c 1 e 4 8 a b 5 
8 b a 5 e c 4 2 d 0 9 7 1 3 6 
9 6 7 d 3 1 c e 0 a 2 5 b 8 4 
a e b 4 d 9 8 5 c 2 6 3 0 1 7 
b a e 8 c d 5 4 9 1 7 6 2 0 3 
c d 9 a 7 6 e b 3 4 1 0 8 5 2 
d 9 c e 6 3 b a 7 8 0 2 5 4 1 
e c d b 9 7 a 8 6 5 3 1 4 2 0 
diff:
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
a . . . . . . . . . . 3*. . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
3 . . . . . . . . . . a . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
mutate() all_diffs 4
transv_cnt=00916445 short=0123456789abcde120758364dbe9ca25408a13b7ed69ca01624795c83deb4852be01a6c937d5481ab20e3dc769673c109d2b54ea8736902dc1e48ab58ba5ec42d097136967d31ce0a25b843eb4d985c26a017bae8cd549176203cd9a76eb3410852d9ce63ba7802541ecdb97a86531420

Sometimes, 11 cells are to be modified.

For example, here we repair L15 by modifying 11 cells after setting cell at row=0 column=4 to 8 (marked with asterisk):

init sq: 0123456789abcde120758364dbe9ca25408a13b7ed69c301624795c8adeb4852be01a6c937d5481ab20e3dc769673c109d2a54eb8736902dc1e48ba58ba5ec42d096137967d31ce0b25a84aeb4d985c267013bae8cd549173206cd9a76eb3410852d9ce63ba7802541ecdb97a86531420
transv cnt 174941
...
original_sq:
0 1 2 3 4 5 6 7 8 9 a b c d e 
1 2 0 7 5 8 3 6 4 d b e 9 c a 
2 5 4 0 8 a 1 3 b 7 e d 6 9 c 
3 0 1 6 2 4 7 9 5 c 8 a d e b 
4 8 5 2 b e 0 1 a 6 c 9 3 7 d 
5 4 8 1 a b 2 0 e 3 d c 7 6 9 
6 7 3 c 1 0 9 d 2 a 5 4 e b 8 
7 3 6 9 0 2 d c 1 e 4 8 b a 5 
8 b a 5 e c 4 2 d 0 9 6 1 3 7 
9 6 7 d 3 1 c e 0 b 2 5 a 8 4 
a e b 4 d 9 8 5 c 2 6 7 0 1 3 
b a e 8 c d 5 4 9 1 7 3 2 0 6 
c d 9 a 7 6 e b 3 4 1 0 8 5 2 
d 9 c e 6 3 b a 7 8 0 2 5 4 1 
e c d b 9 7 a 8 6 5 3 1 4 2 0 
diff:
. . 4 . 8*. . . 2 . . . . . . 
. . . . . . . . . . . . . . . 
c . 2 . 4 . . . . . . . . . 8 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . 8 . . . . . 2 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
2 . . . . . . . . . . . . . c 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
mutate() all_diffs 11
transv_cnt=01096177 short=0143856729abcde120758364dbe9cac5204a13b7ed698301624795c8adeb4852be01a6c937d5481ab20e3dc769673c109d8a54eb2736902dc1e48ba58ba5ec42d096137967d31ce0b25a84aeb4d985c267013bae8cd5491732062d9a76eb341085cd9ce63ba7802541ecdb97a86531420

Now the statistics.

Most LS's can be repaired with modification of 7 cells. Number of LS's 'repaired' and how many cells we had to 'fix':

1633483 mutate() all_diffs 4
2668510 mutate() all_diffs 6
3213221 mutate() all_diffs 7
2098325 mutate() all_diffs 8
 707907 mutate() all_diffs 9
  14225 mutate() all_diffs 10
    132 mutate() all_diffs 11

Now finding extremal LS is just random walk with that 'LS repair' procedure.

Here is a part of log file, where L15 with TS=171579 was mutated by setting 7 at row=8 and column=14 and 'repairing' other cells:

init sq: 0123456789abcde120758364dbe9ca25408a13b7ed69c301624795c8adeb4852be01a6c937d5481ab20e3dc769673c109d2b54ea8736902dc1e48ab58ba5ec42d097136967d31ce0a25b84aeb4d985c263017bae8cd549176203cd9a76eb3410852d9ce63ba7802541ecdb97a86531420
transv cnt 171579

...

set row/col to i 8 14 7
original_sq:
0 1 2 3 4 5 6 7 8 9 a b c d e 
1 2 0 7 5 8 3 6 4 d b e 9 c a 
2 5 4 0 8 a 1 3 b 7 e d 6 9 c 
3 0 1 6 2 4 7 9 5 c 8 a d e b 
4 8 5 2 b e 0 1 a 6 c 9 3 7 d 
5 4 8 1 a b 2 0 e 3 d c 7 6 9 
6 7 3 c 1 0 9 d 2 b 5 4 e a 8 
7 3 6 9 0 2 d c 1 e 4 8 a b 5 
8 b a 5 e c 4 2 d 0 9 7 1 3 6 
9 6 7 d 3 1 c e 0 a 2 5 b 8 4 
a e b 4 d 9 8 5 c 2 6 3 0 1 7 
b a e 8 c d 5 4 9 1 7 6 2 0 3 
c d 9 a 7 6 e b 3 4 1 0 8 5 2 
d 9 c e 6 3 b a 7 8 0 2 5 4 1 
e c d b 9 7 a 8 6 5 3 1 4 2 0 
diff:
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . 6 . . 7*
. . . . . . . . . . . . . . . 
. . . . . . . . . . 7 . . . 6 
. . . . . . . . . . 6 7 . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
mutate() all_diffs 6
transv_cnt=00170369 short=0123456789abcde120758364dbe9ca25408a13b7ed69c301624795c8adeb4852be01a6c937d5481ab20e3dc769673c109d2b54ea8736902dc1e48ab58ba5ec42d096137967d31ce0a25b84aeb4d985c273016bae8cd549167203cd9a76eb3410852d9ce63ba7802541ecdb97a86531420

'Mutated' LS has TS=170369.

Using such random walk, it's easy to find extremal LS up to L15, and maybe more, but my transversals counting function is getting very slow :-( I have to fix it somehow.

MaxSAT solver I used is UWrMaxSat by Marek Piotrów et al. I used it because it has IPAMIR interface (in other words, it can be loaded as .so module and be a bit faster).

All the files.

I recorded a fancy video of the process.

(the post first published at 20260101.)


List of my other blog posts. Subscribe to my news feed,
If you enjoy my work, you can support it on patreon.
Some time ago (before 24-Mar-2025) there was Disqus JS script for comments. I dropped it --- it was so motley, distracting, animated, with too much ads. I never liked it. Also, comments din't appeared correctly (Disqus was buggy). Also, my blog is too chamberlike --- not many people write comments here. So I decided to switch to the model I once had at least in 2020 --- send me your comments by email (don't forget to include URL to this blog post) and I will copy&paste it here manually.
Let's party like it's ~1993-1996, in this ultimate, radical and uncompromisingly primitive pre-web1.0-style blog and website. This website is best viewed under lynx/links/elinks/w3m.