Giter Club home page Giter Club logo

tdoku's Introduction

Tdoku: A fast Sudoku Solver and Generator

Overview

This project contains an optimized Sudoku solver and puzzle generator for conventional 9x9 puzzles (as well as Sukaku "pencilmark" puzzles with clues given as negative instead of positive literals). It also contains two other solvers with several variations exploring different ideas for optimization visited during development. Lastly, it contains a benchmarking framework for comparing performance among these solvers and among several third party solvers of current or historical interest using a variety of datasets.

See https://t-dillon.github.io/tdoku for a long discussion of the thinking behind these solvers.

This project's solvers include:

Solver Description
src/solver_dpll_triad_simd.cc The fast solver, optimized with particular focus on speed of solving hard Sudoku instances, and by this measure the fastest of any solver I'm aware of. I'll refer to this one as "Tdoku".
src/solver_dpll_triad_scc.cc A DPLL-based solver for exploring how the puzzle representation can be optimized and strongly connected components can be exploited to reduce backtracking.
src/solver_basic.cc A very simple solver. Fast as simple solvers go, but mainly here to illustrate how poorly such solvers handle hard puzzles.

See here for details on third party solvers supported for benchmarking. For a glimpse of comparative performance (on modern hardware) at opposite ends of the difficulty spectrum, here are results for a range of benchmarked solvers on a dataset of ~49,000 very difficult puzzles with Sudoku Explainer ratings of 11 or higher:

|data/puzzles5_forum_hardest_1905_11+  |  puzzles/sec|  usec/puzzle|   %no_guess|  guesses/puzzle|
|--------------------------------------|------------:|------------:|-----------:|---------------:|
|minisat_augmented          S/shrc+./m+|       517.9 |     1,930.8 |       0.0% |         104.36 |
|fast_solv_9r2              E/sh..../m.|     2,256.3 |       443.2 |       0.0% |         171.66 |
|kudoku                     E/sh..../m.|     2,492.3 |       401.2 |       0.0% |         142.13 |
|norvig                     C/sh..../m.|       403.8 |     2,476.5 |       0.0% |         178.93 |
|bb_sudoku                  C/shrc../m.|     6,310.4 |       158.5 |       0.0% |         200.41 |
|fsss                       C/shrc../m.|     6,653.9 |       150.3 |       0.0% |         117.52 |
|jsolve                     C/shrc../m.|     7,480.5 |       133.7 |       0.0% |         100.21 |
|fsss2                      D/sh..../m.|    11,272.3 |        88.7 |       0.0% |         139.23 |
|jczsolve                   B/shr.../m.|    12,162.5 |        82.2 |       0.0% |         171.20 |
|sk_bforce2                 B/shrc-./m+|    13,454.2 |        74.3 |       0.0% |         122.64 |
|rust_sudoku                B/shr.../m.|    14,353.8 |        69.7 |       0.0% |         161.94 |
|tdoku                      T/shrc+./m+|    24,001.7 |        41.7 |       0.0% |          64.98 |

And here are results on the well-known and commonly-benchmarked dataset of ~49,000 generally very easy 17-clue puzzles:

|data/puzzles2_17_clue                 |  puzzles/sec|  usec/puzzle|   %no_guess|  guesses/puzzle|
|--------------------------------------|------------:|------------:|-----------:|---------------:|
|minisat_augmented          S/shrc+./m+|     5,052.6 |       197.9 |      76.0% |           1.06 |
|fast_solv_9r2              E/sh..../m.|    37,301.2 |        26.8 |      44.6% |           4.47 |
|kudoku                     E/sh..../m.|    40,634.7 |        24.6 |      44.6% |           4.57 |
|norvig                     C/sh..../m.|     8,696.8 |       115.0 |      44.6% |           4.84 |
|bb_sudoku                  C/shrc../m.|   148,555.5 |         6.7 |      76.0% |           1.55 |
|fsss                       C/shrc../m.|   194,872.7 |         5.1 |      76.0% |           0.94 |
|jsolve                     C/shrc../m.|   193,453.2 |         5.2 |      76.0% |           0.77 |
|fsss2_locked               D/shrc../m.|   274,000.0 |         3.6 |      76.0% |           0.95 |
|jczsolve                   B/shr.../m.|   281,424.2 |         3.6 |      70.5% |           1.76 |
|sk_bforce2                 B/shrc-./m+|   355,984.3 |         2.8 |      74.1% |           1.02 |
|rust_sudoku                B/shr.../m.|   400,162.5 |         2.5 |      70.5% |           1.74 |
|tdoku                      T/shrc+./m+|   364,354.6 |         2.7 |      78.7% |           0.61 |

And here is a chart comparing a narrower set of the fastest solvers on a wider range of datasets ordered roughly from easiest to hardest, and for each solver using the results from its most favorable tested compiler and compiler options:

Note: Tdoku makes heavy use of SIMD instructions up to various flavors of AVX-512 when available. As a result it achieves its best performance on recent Intel hardware like the Ice Lake laptop that produced the tables and chart above. With older processors there are moderate declines in performance down to SSSE3, and precipitous declines with SSE2. See the comment here for stats on the availability of SIMD instructions from the Steam hardware survey.

For configuration and full details of the runs used for the comparisons shown above, see here.

Building and Running

Build this project's solvers and run them through benchmarks as follows:

unzip data.zip
./BUILD.sh
./build/run_tests
./build/run_benchmark data/*

Building the project also produces a library containing the fast simd solver. You can build a simple test program that reads Sudoku (or 729-character pencilmark Sudoku) from stdin and displays the solution count and solution (if unique) like so:

gcc example/solve.c build/libtdoku_static.a -O3 -o solve -lstdc++ -lm
# count solutions:
./solve < data/puzzles0_kaggle
# find single solution:
./solve 1 < data/puzzles0_kaggle

Or for an example of using the shared library via python bindings try:

python3 example/solve.py data/puzzles0_kaggle

Benchmarking Other Solvers

This project is set up to facilitate benchmarking against a number of the fastest known solvers, as well as some solvers of historical interest. Also included is a solver based on minisat and able to test several different SAT encodings. See here for descriptions of these solvers. So set them up for benchmarking run the following commands:

git submodule update --init --recursive
other/setup_jczsolve.sh 
other/setup_rust_sudoku.sh 

With sources set up, the benchmarks found here were run using BENCH.sh by specifying an architecture, taskset CPU mask, and list of compiler/flag specifications as in the following example:

./BENCH.sh i7-4930k 0x8 gcc-6_O3_native clang-8_O3_native clang-8_O3_native_pgo ...

See CMakeLists.txt for the set of -D options to pass to BUILD.sh. If you want to build and benchmark all supported solvers just pass -DALL=on. See the benchmark program's help for details of how to run, solvers that have been built, and build info:

$ build/run_benchmark -h
usage: run_benchmark <options> puzzle_file_1 [...] 
options:
  -a                  // do rating
  -b                  // rate by backtracks
  -c [0|1]            // output csv instead of table [default 0]
  -e <seed>           // random seed [default random_device{}()]
  -h                  // display this help message
  -n <size>           // test set size [default 2500000]
  -p                  // expect 729 character pencilmark sudoku
  -r [0|1]            // randomly permute puzzles [default 1]
  -s solver_1,...     // which solvers to run [default all]
  -t <secs>           // target test time [default 20]
  -v [0|1]            // validate during warmup [default 1]
  -w <secs>           // target warmup time [default 10]
solvers: 
 minisat_minimal minisat_natural minisat_complete minisat_augmented _tdev_dpll_triad
 _tdev_dpll_triad_scc_i _tdev_dpll_triad_scc_h _tdev_dpll_triad_scc_ih _tdev_basic
 _tdev_basic_heuristic lhl_sudoku zerodoku fast_solv_9r2 kudoku norvig bb_sudoku
 fsss jsolve fsss2 fsss2_locked jczsolve sk_bforce2 tdoku
build info: Clang 8.0.1 -O3  -march=native

If you'd like to add another solver to the benchmark suite, see this commit for an example of how to do so. If you have another interesting comparison I'd love to hear from you!

Generating Puzzles

Tdoku includes a program for generating both conventional and pencilmark Sudoku. The process is governed by a customizable loss function that can drive towards low or high clue puzzles, hard or easy puzzles, or some combination of goals.

$ build/generate -h
usage: generate <options> <pattern_file>
options:
  -c <clue_weight>    scoring weight for number of clues
  -g <guess weight>   scoring weight(exponent) for geo mean guesses
  -r <random weight>  scoring weight for uniform noise
  -d <drop>           number of clues to drop before re-completing
  -e <num_evals>      number of permutations to eval for guess estimate
  -l <limit>          limit number of puzzles to generate
  -m [0|1]            minimize generated puzzles
  -n <pool size>      number of top scored puzzles to keep in pool
  -a [0|1]            display all puzzles (not just top scored)
  -p [0|1]            generate pencilmark puzzles
  -s [0|1|2]          solver for eval: 0=tdoku,1=minisat,2=gurobi
  -h                  display this help message

Examples:

# starting from 100 random conventional Sudoku puzzle seeds perform {-1+?} generation driving 
# towards hard puzzles (according to minisat evaluation of 50 random puzzle permutations). 
$ build/generate -p0 -c0 -g1 -d1 -n100 -e50

# starting from 100 random pencilmark Sudoku puzzle seeds perform {-2+?} generation driving  
# towards high-clue puzzles with a secondary emphasis on puzzle difficulty (according to minisat 
# evaluation of 5 random puzzle permutations). 
build/generate -c-1 -g0.01 -r0 -d2 -n100 -e5

tdoku's People

Contributors

bartp5 avatar petelliott avatar t-dillon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

tdoku's Issues

Does recursion beat a stack implementation?

I've written a few sudoku solvers, but I've avoided recursion because I wanted to make sure I didn't overflow the stack if I wanted to use it on larger boards. Reading some of the listed benchmark timings I'm wondering if there's some performance benefit to having the cpu essentially handle a stack with recursion. I've got two implementations, one I think is equivalent to the basic solver you have listed, the other uses additional bitmasks to do space based eliminations (I think the term's called cross hatching). They run about 1ms and 3ms per puzzle. If I template out specifically for the board size it seems to roughly improve performance by a factor of 10, but obviously my hardware's not the same and my code's probably not ideal.

The cross hatching approach seems to shave a significant amount of branching factor from the basic algorithm by cutting the guess work by a fourth, but it doesn't seem to pay off unless the board is complex.

I'd be curious to see what the benchmarks look like.

Build with flag -DMINISAT=on fails on mac

Hello Tom, thanks for this amazing project I am making a small side project a sudoku game where I was trying to generate some really difficult puzzles. can you help me with a config for that I tried one you shared in an issue earlier but it requires minisat to be enabled and when I am trying to build with minisat I am greeted with the following error?

I did install minisat using brew but still, the error comes up.

A small config for creating extremely hard puzzles would be a great help.

❯ ./BUILD.sh  -DMINISAT=on
-- The C compiler identification is AppleClang 13.0.0.13000029
-- The CXX compiler identification is AppleClang 13.0.0.13000029
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/ashish/tdoku/build
[  4%] Building CXX object CMakeFiles/tdoku_object.dir/src/solver_dpll_triad_simd.cc.o
[  8%] Building CXX object CMakeFiles/tdoku_object.dir/src/util.cc.o
[  8%] Built target tdoku_object
[ 12%] Linking CXX static library libtdoku_static.a
[ 12%] Built target tdoku_static
[ 16%] Linking CXX shared library libtdoku_shared.dylib
[ 16%] Built target tdoku_shared
[ 20%] Building CXX object CMakeFiles/run_benchmark.dir/src/run_benchmark.cc.o
[ 24%] Building CXX object CMakeFiles/run_benchmark.dir/src/util.cc.o
[ 28%] Building CXX object CMakeFiles/run_benchmark.dir/src/solver_dpll_triad_simd.cc.o
[ 32%] Building CXX object CMakeFiles/run_benchmark.dir/other/other_solvers.cc.o
[ 36%] Building CXX object CMakeFiles/run_benchmark.dir/other/other_minisat.cc.o
/Users/ashish/tdoku/other/other_minisat.cc:1:10: fatal error: 'minisat/core/Solver.h' file not found
#include <minisat/core/Solver.h>
         ^~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
make[2]: *** [CMakeFiles/run_benchmark.dir/other/other_minisat.cc.o] Error 1
make[1]: *** [CMakeFiles/run_benchmark.dir/all] Error 2
make: *** [all] Error 2
❯ brew install minisat
Warning: minisat 2.2.1 is already installed and up-to-date.
To reinstall 2.2.1, run:
  brew reinstall minisat

Python bindings for the tdoku solver

I don't know a lot of C or C++, but I tried to analyze the code as well as I could.
In ./include/tdoku.h there is a function

static inline size_t SolveSudoku(const char *input, size_t limit, uint32_t configuration,
                                 char *solution, size_t *num_guesses) {
    return TdokuSolverDpllTriadSimd(input, limit, configuration, solution, num_guesses);
}

I am just assuming that the TdokuSolverDpllTriadSimd is "imported" from ./src/solver_dpll_triad_simd.cc.
My situation is the following:
I have a single string representing a puzzle (that has only one solution) and want to find that one solution.

Let's say that I have a variable puzzle containing a str with a length of 81 or 729.
Now, I would like to have a function get_solution that takes a puzzle and returns a tuple (solution,num_guesses) (I don't need the regular return value.)

  1. Would you mind writing a small python script that exposes the function to python? If you are especially generous with your time, you could also explain your code and how to compile the files when using python.

  2. Another problem is that I am running Windows and the make command in BUILD.sh isn't running, but I might just use WSL or do you maybe know a way how to compile the whole folder just with gcc?

  3. Also, I haven't quite understood all the parameters in the generator. Especially not -c,-g,-r,-d,-e,-n. What is the effect on the Sudoku of those options, respectively.

Generate puzzles with difficulty

Hi Tom,

Thanks for writing this awesome sudoku generator!

I want to make a mobile app and i need some puzzles which categorized by easy/medium/hard/...

I simply try it like "build/generate -p0 -c0 -g1 -d1 -n100 -e50 -s0 -l5"
but i don't know how to use parameters to generate games with specified difficulty.

Can you help me please?

Sudokus with arbitrary shapes and sizes

Hi Tom! Really appreciate your work on this.

Can you please add support for generating and solving sudokus other than the classic 9x9 variant? If not as command-line arguments, maybe as compile-time options.

As you must be well aware sudokus are of interest to not only the human players, but also to AI researchers. With the recent advances in Neuro-Symbolic AI, Sudoku datasets are becoming a popular testbed once again. One often requires experimenting with different variants with larger sizes and rectangular boxes (like 6x6 sudokus with 2x3 boxes) to investigate interesting questions regarding scalability and generalisation.

Your repository contains one of the most complete treatments of sudoku solvers and generators, and I'm sure it would be of great utility to extend it with this feature. Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.