sudoku-smt-solvers 0.1.0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- sudoku_smt_solvers-0.1.0/LICENSE +21 -0
- sudoku_smt_solvers-0.1.0/PKG-INFO +161 -0
- sudoku_smt_solvers-0.1.0/README.md +143 -0
- sudoku_smt_solvers-0.1.0/pyproject.toml +30 -0
- sudoku_smt_solvers-0.1.0/setup.cfg +4 -0
- sudoku_smt_solvers-0.1.0/setup.py +57 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers/__init__.py +4 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers/benchmarks/__init__.py +5 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers/benchmarks/benchmark_runner.py +211 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers/solvers/__init__.py +4 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers/solvers/cvc5_solver.py +207 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers/solvers/dpll_solver.py +175 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers/solvers/dpllt_solver.py +211 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers/solvers/sudoku_error.py +8 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers/solvers/z3_solver.py +160 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers.egg-info/PKG-INFO +161 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers.egg-info/SOURCES.txt +29 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers.egg-info/dependency_links.txt +1 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers.egg-info/requires.txt +3 -0
- sudoku_smt_solvers-0.1.0/sudoku_smt_solvers.egg-info/top_level.txt +1 -0
- sudoku_smt_solvers-0.1.0/tests/test_benchmark_runner.py +152 -0
- sudoku_smt_solvers-0.1.0/tests/test_cvc5_solver.py +149 -0
- sudoku_smt_solvers-0.1.0/tests/test_dfs_solver.py +67 -0
- sudoku_smt_solvers-0.1.0/tests/test_dpll_solver.py +78 -0
- sudoku_smt_solvers-0.1.0/tests/test_dpllt_solver.py +77 -0
- sudoku_smt_solvers-0.1.0/tests/test_hole_digger.py +138 -0
- sudoku_smt_solvers-0.1.0/tests/test_las_vegas.py +141 -0
- sudoku_smt_solvers-0.1.0/tests/test_parser.py +0 -0
- sudoku_smt_solvers-0.1.0/tests/test_profiler.py +0 -0
- sudoku_smt_solvers-0.1.0/tests/test_sudoku_generator.py +79 -0
- sudoku_smt_solvers-0.1.0/tests/test_z3_solver.py +93 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 liamjdavis
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,161 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: sudoku_smt_solvers
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: A collection of SAT and SMT solvers for solving Sudoku puzzles
|
5
|
+
Home-page: https://liamjdavis.github.io/sudoku-smt-solvers
|
6
|
+
Author: Liam Davis
|
7
|
+
Author-email: ljdavis27@amherst.edu
|
8
|
+
License: MIT
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
11
|
+
Classifier: Operating System :: OS Independent
|
12
|
+
Requires-Python: >=3.9
|
13
|
+
Description-Content-Type: text/markdown
|
14
|
+
License-File: LICENSE
|
15
|
+
Requires-Dist: cvc5
|
16
|
+
Requires-Dist: pysat
|
17
|
+
Requires-Dist: z3-solver
|
18
|
+
|
19
|
+
# Sudoku-SMT-Solvers
|
20
|
+
|
21
|
+
[![Pytest + CI/CD](https://github.com/liamjdavis/Sudoku-SMT-Solvers/actions/workflows/test.yml/badge.svg)](ttps://github.com/liamjdavis/Sudoku-SMT-Solvers/actions/workflows/test.yml)
|
22
|
+
[![Coverage Status](https://coveralls.io/repos/github/liamjdavis/Sudoku-SMT-Solvers/badge.svg)](https://coveralls.io/github/liamjdavis/Sudoku-SMT-Solvers)
|
23
|
+
[![Docs Build Deployment](https://github.com/liamjdavis/Sudoku-SMT-Solvers/actions/workflows/docs.yml/badge.svg)](https://github.com/liamjdavis/Sudoku-SMT-Solvers/actions/workflows/docs.yml)
|
24
|
+
[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://liamjdavis.github.io/sudoku-smt-solvers)
|
25
|
+
|
26
|
+
|
27
|
+
## About
|
28
|
+
This repository contains the code for the study "Evaluating SMT-Based Solvers on Sudoku". Created by Liam Davis (@liamjdavis) and Ryan Ji (@TairanJ) as their for COSC-241 Artificial Intelligence at Amherst College, it evaluates the efficacy of SMT-Based Solvers by benchmarking three modern SMT solvers (DPLL(T), Z3, and CVC5) against the DPLL algorithm on a collection of 100 25x25 Sudoku puzzles of varying difficulty.
|
29
|
+
|
30
|
+
Along with the study, we also published `sudoku-smt-solvers`, a Python package that provides the various SMT-based Sudoku solvers and benchmarking tools we built for this study. The package features DPLL(T), Z3, and CVC5 solvers optimized for 25x25 Sudoku puzzles, a puzzle generator for creating test cases, and a comprehensive benchmarking suite. Available through pip, it offers a simple API for solving Sudoku puzzles using state-of-the-art SMT solvers while facilitating performance comparisons between different solving approaches.
|
31
|
+
|
32
|
+
The study aims to answer three research questions:
|
33
|
+
1. How have logical solvers evolved over time in terms of performance and capability?
|
34
|
+
2. How do different encodings of Sudoku affect the efficiency and scalability of these solvers?
|
35
|
+
3. Are there specific features or optimizations in SMT solvers that provide a significant advantage over traditional SAT solvers for this class of problem?
|
36
|
+
|
37
|
+
## Getting started
|
38
|
+
### Installation
|
39
|
+
To run the code locally, you can install with `pip`
|
40
|
+
|
41
|
+
```bash
|
42
|
+
pip install sudoku-smt-solvers
|
43
|
+
```
|
44
|
+
|
45
|
+
### Solvers
|
46
|
+
This package includes the DPLL solver and three modern SMT solvers:
|
47
|
+
* DPLL(T)
|
48
|
+
* CVC5
|
49
|
+
* Z3
|
50
|
+
|
51
|
+
To run any of the solvers on a 25x25 Sudoku puzzle, you can create an instance of the solver class and call the solve method in a file at the root (Sudoku-smt-solvers). Here is an example using Z3:
|
52
|
+
|
53
|
+
```python
|
54
|
+
from sudoku_smt_solvers.solvers.z3_solver import Z3Solver
|
55
|
+
|
56
|
+
# Example grid (25x25)
|
57
|
+
grid = [[0] * 25 for _ in range(25)]
|
58
|
+
solver = Z3Solver(grid)
|
59
|
+
solution = solver.solve()
|
60
|
+
|
61
|
+
if solution:
|
62
|
+
print(f"Solution:\n\n{solution}")
|
63
|
+
else:
|
64
|
+
print("No solution exists.")
|
65
|
+
```
|
66
|
+
|
67
|
+
### Sudoku Generator
|
68
|
+
This package also includes a generator for creating Sudoku puzzles to be used as benchmarks. To generate a puzzle, create an instance of the `SudokuGenerator` class and call the `generate` method. Here is an example:
|
69
|
+
|
70
|
+
```python
|
71
|
+
from sudoku_smt_solvers.benchmarks.sudoku_generator.sudoku_generator import SudokuGenerator
|
72
|
+
|
73
|
+
generator = SudokuGenerator(size = 25, givens = 80, timeout = 5, difficulty = "Medium", puzzles_dir = "benchmarks/puzzles", solutions_dir = "benchmarks/solutions")
|
74
|
+
|
75
|
+
generator.generate()
|
76
|
+
```
|
77
|
+
|
78
|
+
Due to the computational complexity of generating large sudoku puzzles, it is recommended that you run multiple generator instances in parallel to create benchmarks.
|
79
|
+
|
80
|
+
### Benchmark Runner
|
81
|
+
To run the benchmarks you created on all four solvers, create an instance of the `BenchmarkRunner` class and call the `run_benchmarks` method. Here is an example:
|
82
|
+
|
83
|
+
```python
|
84
|
+
from sudoku_smt_solvers.benchmarks.benchmark_runner import BenchmarkRunner
|
85
|
+
|
86
|
+
runner = BenchmarkRunner(
|
87
|
+
puzzles_dir='resources/benchmarks/puzzles/',
|
88
|
+
solutions_dir='resources/benchmarks/solutions/',
|
89
|
+
results_dir='results/'
|
90
|
+
)
|
91
|
+
runner.run_benchmarks()
|
92
|
+
```
|
93
|
+
|
94
|
+
## Contributing
|
95
|
+
|
96
|
+
We welcome contributions in the form of new solvers, additions to our benchmark suite, or anything that improves the tool! Here's how to get started:
|
97
|
+
|
98
|
+
### Development Setup
|
99
|
+
|
100
|
+
1. **Fork and Clone**:
|
101
|
+
Begin by forking the repository and cloning your fork locally:
|
102
|
+
```bash
|
103
|
+
git clone https://github.com/yourusername/Sudoku-SMT-Solvers.git
|
104
|
+
cd Sudoku-SMT-Solvers
|
105
|
+
```
|
106
|
+
|
107
|
+
2. **Create and Activate a Virtual Environment**:
|
108
|
+
Set up a Python virtual environment to isolate your dependencies:
|
109
|
+
```bash
|
110
|
+
python3 -m venv venv
|
111
|
+
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
|
112
|
+
```
|
113
|
+
|
114
|
+
3. **Install Dependencies**:
|
115
|
+
Install the required dependencies from the `requirements.txt` file:
|
116
|
+
```bash
|
117
|
+
pip install -r requirements.txt
|
118
|
+
```
|
119
|
+
|
120
|
+
4. **Set Up Pre-Commit Hooks**:
|
121
|
+
Install and configure pre-commit hooks to maintain code quality:
|
122
|
+
```bash
|
123
|
+
pip install pre-commit
|
124
|
+
pre-commit install
|
125
|
+
```
|
126
|
+
|
127
|
+
To manually run the hooks and verify code compliance, use:
|
128
|
+
```bash
|
129
|
+
pre-commit run
|
130
|
+
```
|
131
|
+
|
132
|
+
5. **Testing and Coverage Requirements**:
|
133
|
+
- Write tests for any new code or modifications.
|
134
|
+
- Use `pytest` for running tests:
|
135
|
+
```bash
|
136
|
+
pytest
|
137
|
+
```
|
138
|
+
- Ensure the test coverage is at least 90%:
|
139
|
+
|
140
|
+
6. **Add and Commit Your Changes**:
|
141
|
+
- Follow the existing code style and structure.
|
142
|
+
- Verify that all pre-commit hooks pass and the test coverage meets the minimum requirement.
|
143
|
+
```bash
|
144
|
+
git add .
|
145
|
+
git commit -m "Description of your changes"
|
146
|
+
```
|
147
|
+
|
148
|
+
7. **Push Your Branch**:
|
149
|
+
Push your changes to your forked repository:
|
150
|
+
```bash
|
151
|
+
git push origin your-branch-name
|
152
|
+
```
|
153
|
+
|
154
|
+
8. **Open a PR for us to review**
|
155
|
+
---
|
156
|
+
|
157
|
+
Thank you for your interest in contributing to Sudoku-SMT-Solvers! Your efforts help make this project better for everyone.
|
158
|
+
|
159
|
+
|
160
|
+
## Contact Us
|
161
|
+
For any questions or support, please reach out to Liam at ljdavis27 at amherst.edu and Ryan at tji26 at amherst.edu
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# Sudoku-SMT-Solvers
|
2
|
+
|
3
|
+
[![Pytest + CI/CD](https://github.com/liamjdavis/Sudoku-SMT-Solvers/actions/workflows/test.yml/badge.svg)](ttps://github.com/liamjdavis/Sudoku-SMT-Solvers/actions/workflows/test.yml)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/github/liamjdavis/Sudoku-SMT-Solvers/badge.svg)](https://coveralls.io/github/liamjdavis/Sudoku-SMT-Solvers)
|
5
|
+
[![Docs Build Deployment](https://github.com/liamjdavis/Sudoku-SMT-Solvers/actions/workflows/docs.yml/badge.svg)](https://github.com/liamjdavis/Sudoku-SMT-Solvers/actions/workflows/docs.yml)
|
6
|
+
[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://liamjdavis.github.io/sudoku-smt-solvers)
|
7
|
+
|
8
|
+
|
9
|
+
## About
|
10
|
+
This repository contains the code for the study "Evaluating SMT-Based Solvers on Sudoku". Created by Liam Davis (@liamjdavis) and Ryan Ji (@TairanJ) as their for COSC-241 Artificial Intelligence at Amherst College, it evaluates the efficacy of SMT-Based Solvers by benchmarking three modern SMT solvers (DPLL(T), Z3, and CVC5) against the DPLL algorithm on a collection of 100 25x25 Sudoku puzzles of varying difficulty.
|
11
|
+
|
12
|
+
Along with the study, we also published `sudoku-smt-solvers`, a Python package that provides the various SMT-based Sudoku solvers and benchmarking tools we built for this study. The package features DPLL(T), Z3, and CVC5 solvers optimized for 25x25 Sudoku puzzles, a puzzle generator for creating test cases, and a comprehensive benchmarking suite. Available through pip, it offers a simple API for solving Sudoku puzzles using state-of-the-art SMT solvers while facilitating performance comparisons between different solving approaches.
|
13
|
+
|
14
|
+
The study aims to answer three research questions:
|
15
|
+
1. How have logical solvers evolved over time in terms of performance and capability?
|
16
|
+
2. How do different encodings of Sudoku affect the efficiency and scalability of these solvers?
|
17
|
+
3. Are there specific features or optimizations in SMT solvers that provide a significant advantage over traditional SAT solvers for this class of problem?
|
18
|
+
|
19
|
+
## Getting started
|
20
|
+
### Installation
|
21
|
+
To run the code locally, you can install with `pip`
|
22
|
+
|
23
|
+
```bash
|
24
|
+
pip install sudoku-smt-solvers
|
25
|
+
```
|
26
|
+
|
27
|
+
### Solvers
|
28
|
+
This package includes the DPLL solver and three modern SMT solvers:
|
29
|
+
* DPLL(T)
|
30
|
+
* CVC5
|
31
|
+
* Z3
|
32
|
+
|
33
|
+
To run any of the solvers on a 25x25 Sudoku puzzle, you can create an instance of the solver class and call the solve method in a file at the root (Sudoku-smt-solvers). Here is an example using Z3:
|
34
|
+
|
35
|
+
```python
|
36
|
+
from sudoku_smt_solvers.solvers.z3_solver import Z3Solver
|
37
|
+
|
38
|
+
# Example grid (25x25)
|
39
|
+
grid = [[0] * 25 for _ in range(25)]
|
40
|
+
solver = Z3Solver(grid)
|
41
|
+
solution = solver.solve()
|
42
|
+
|
43
|
+
if solution:
|
44
|
+
print(f"Solution:\n\n{solution}")
|
45
|
+
else:
|
46
|
+
print("No solution exists.")
|
47
|
+
```
|
48
|
+
|
49
|
+
### Sudoku Generator
|
50
|
+
This package also includes a generator for creating Sudoku puzzles to be used as benchmarks. To generate a puzzle, create an instance of the `SudokuGenerator` class and call the `generate` method. Here is an example:
|
51
|
+
|
52
|
+
```python
|
53
|
+
from sudoku_smt_solvers.benchmarks.sudoku_generator.sudoku_generator import SudokuGenerator
|
54
|
+
|
55
|
+
generator = SudokuGenerator(size = 25, givens = 80, timeout = 5, difficulty = "Medium", puzzles_dir = "benchmarks/puzzles", solutions_dir = "benchmarks/solutions")
|
56
|
+
|
57
|
+
generator.generate()
|
58
|
+
```
|
59
|
+
|
60
|
+
Due to the computational complexity of generating large sudoku puzzles, it is recommended that you run multiple generator instances in parallel to create benchmarks.
|
61
|
+
|
62
|
+
### Benchmark Runner
|
63
|
+
To run the benchmarks you created on all four solvers, create an instance of the `BenchmarkRunner` class and call the `run_benchmarks` method. Here is an example:
|
64
|
+
|
65
|
+
```python
|
66
|
+
from sudoku_smt_solvers.benchmarks.benchmark_runner import BenchmarkRunner
|
67
|
+
|
68
|
+
runner = BenchmarkRunner(
|
69
|
+
puzzles_dir='resources/benchmarks/puzzles/',
|
70
|
+
solutions_dir='resources/benchmarks/solutions/',
|
71
|
+
results_dir='results/'
|
72
|
+
)
|
73
|
+
runner.run_benchmarks()
|
74
|
+
```
|
75
|
+
|
76
|
+
## Contributing
|
77
|
+
|
78
|
+
We welcome contributions in the form of new solvers, additions to our benchmark suite, or anything that improves the tool! Here's how to get started:
|
79
|
+
|
80
|
+
### Development Setup
|
81
|
+
|
82
|
+
1. **Fork and Clone**:
|
83
|
+
Begin by forking the repository and cloning your fork locally:
|
84
|
+
```bash
|
85
|
+
git clone https://github.com/yourusername/Sudoku-SMT-Solvers.git
|
86
|
+
cd Sudoku-SMT-Solvers
|
87
|
+
```
|
88
|
+
|
89
|
+
2. **Create and Activate a Virtual Environment**:
|
90
|
+
Set up a Python virtual environment to isolate your dependencies:
|
91
|
+
```bash
|
92
|
+
python3 -m venv venv
|
93
|
+
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
|
94
|
+
```
|
95
|
+
|
96
|
+
3. **Install Dependencies**:
|
97
|
+
Install the required dependencies from the `requirements.txt` file:
|
98
|
+
```bash
|
99
|
+
pip install -r requirements.txt
|
100
|
+
```
|
101
|
+
|
102
|
+
4. **Set Up Pre-Commit Hooks**:
|
103
|
+
Install and configure pre-commit hooks to maintain code quality:
|
104
|
+
```bash
|
105
|
+
pip install pre-commit
|
106
|
+
pre-commit install
|
107
|
+
```
|
108
|
+
|
109
|
+
To manually run the hooks and verify code compliance, use:
|
110
|
+
```bash
|
111
|
+
pre-commit run
|
112
|
+
```
|
113
|
+
|
114
|
+
5. **Testing and Coverage Requirements**:
|
115
|
+
- Write tests for any new code or modifications.
|
116
|
+
- Use `pytest` for running tests:
|
117
|
+
```bash
|
118
|
+
pytest
|
119
|
+
```
|
120
|
+
- Ensure the test coverage is at least 90%:
|
121
|
+
|
122
|
+
6. **Add and Commit Your Changes**:
|
123
|
+
- Follow the existing code style and structure.
|
124
|
+
- Verify that all pre-commit hooks pass and the test coverage meets the minimum requirement.
|
125
|
+
```bash
|
126
|
+
git add .
|
127
|
+
git commit -m "Description of your changes"
|
128
|
+
```
|
129
|
+
|
130
|
+
7. **Push Your Branch**:
|
131
|
+
Push your changes to your forked repository:
|
132
|
+
```bash
|
133
|
+
git push origin your-branch-name
|
134
|
+
```
|
135
|
+
|
136
|
+
8. **Open a PR for us to review**
|
137
|
+
---
|
138
|
+
|
139
|
+
Thank you for your interest in contributing to Sudoku-SMT-Solvers! Your efforts help make this project better for everyone.
|
140
|
+
|
141
|
+
|
142
|
+
## Contact Us
|
143
|
+
For any questions or support, please reach out to Liam at ljdavis27 at amherst.edu and Ryan at tji26 at amherst.edu
|
@@ -0,0 +1,30 @@
|
|
1
|
+
[tool.pre-commit]
|
2
|
+
config = ".pre-commit-config.yaml"
|
3
|
+
|
4
|
+
[tool.black]
|
5
|
+
line-length = 88
|
6
|
+
target-version = ["py312"]
|
7
|
+
exclude = '''
|
8
|
+
/(
|
9
|
+
\.direnv
|
10
|
+
| \.eggs
|
11
|
+
| \.git
|
12
|
+
| \.hg
|
13
|
+
| \.ipynb_checkpoints
|
14
|
+
| \.mypy_cache
|
15
|
+
| \.nox
|
16
|
+
| \.pytest_cache
|
17
|
+
| \.ruff_cache
|
18
|
+
| \.tox
|
19
|
+
| \.env
|
20
|
+
| \.svn
|
21
|
+
| __pypackages__
|
22
|
+
| _build
|
23
|
+
| buck-out
|
24
|
+
| build
|
25
|
+
| dist
|
26
|
+
| venv
|
27
|
+
| sudoku_smt_solvers/solvers/sudoku_error\.py
|
28
|
+
| tests/conftest\.py
|
29
|
+
)/
|
30
|
+
'''
|
@@ -0,0 +1,57 @@
|
|
1
|
+
import os
|
2
|
+
import setuptools
|
3
|
+
import re
|
4
|
+
|
5
|
+
NAME = "sudoku_smt_solvers"
|
6
|
+
AUTHOR = "Liam Davis"
|
7
|
+
AUTHOR_EMAIL = "ljdavis27@amherst.edu"
|
8
|
+
DESCRIPTION = "A collection of SAT and SMT solvers for solving Sudoku puzzles"
|
9
|
+
LICENSE = "MIT"
|
10
|
+
URL = "https://liamjdavis.github.io/sudoku-smt-solvers"
|
11
|
+
README = "README.md"
|
12
|
+
CLASSIFIERS = [
|
13
|
+
"Programming Language :: Python :: 3",
|
14
|
+
"License :: OSI Approved :: MIT License",
|
15
|
+
"Operating System :: OS Independent",
|
16
|
+
]
|
17
|
+
INSTALL_REQUIRES = [
|
18
|
+
"cvc5",
|
19
|
+
"pysat",
|
20
|
+
"z3-solver",
|
21
|
+
]
|
22
|
+
ENTRY_POINTS = {}
|
23
|
+
SCRIPTS = []
|
24
|
+
|
25
|
+
HERE = os.path.dirname(__file__)
|
26
|
+
|
27
|
+
|
28
|
+
def read(file):
|
29
|
+
with open(os.path.join(HERE, file), "r") as fh:
|
30
|
+
return fh.read()
|
31
|
+
|
32
|
+
|
33
|
+
VERSION = re.search(
|
34
|
+
r'__version__ = [\'"]([^\'"]*)[\'"]', read(NAME.replace("-", "_") + "/__init__.py")
|
35
|
+
).group(1)
|
36
|
+
LONG_DESCRIPTION = read(README)
|
37
|
+
|
38
|
+
|
39
|
+
if __name__ == "__main__":
|
40
|
+
setuptools.setup(
|
41
|
+
name=NAME,
|
42
|
+
version=VERSION,
|
43
|
+
packages=setuptools.find_packages(),
|
44
|
+
author=AUTHOR,
|
45
|
+
author_email=AUTHOR_EMAIL,
|
46
|
+
description=DESCRIPTION,
|
47
|
+
long_description=LONG_DESCRIPTION,
|
48
|
+
long_description_content_type="text/markdown",
|
49
|
+
license=LICENSE,
|
50
|
+
url=URL,
|
51
|
+
classifiers=CLASSIFIERS,
|
52
|
+
install_requires=INSTALL_REQUIRES,
|
53
|
+
entry_points=ENTRY_POINTS,
|
54
|
+
scripts=SCRIPTS,
|
55
|
+
include_package_data=True,
|
56
|
+
python_requires=">=3.9",
|
57
|
+
)
|
@@ -0,0 +1,5 @@
|
|
1
|
+
from .benchmark_runner import BenchmarkRunner
|
2
|
+
from .sudoku_generator.dfs_solver import DFSSolver
|
3
|
+
from .sudoku_generator.sudoku_generator import SudokuGenerator
|
4
|
+
from .sudoku_generator.las_vegas import LasVegasGenerator
|
5
|
+
from .sudoku_generator.hole_digger import HoleDigger
|
@@ -0,0 +1,211 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
import time
|
4
|
+
import multiprocessing
|
5
|
+
from typing import Dict, List, Optional
|
6
|
+
|
7
|
+
from ..solvers import CVC5Solver, DPLLSolver, DPLLTSolver, Z3Solver
|
8
|
+
|
9
|
+
|
10
|
+
class BenchmarkRunner:
|
11
|
+
"""A benchmark runner for comparing different Sudoku solver implementations.
|
12
|
+
|
13
|
+
This class manages running performance benchmarks across multiple Sudoku solvers,
|
14
|
+
collecting metrics like solve time and propagation counts, and saving results
|
15
|
+
to CSV files.
|
16
|
+
|
17
|
+
Attributes:
|
18
|
+
puzzles_dir (str): Directory containing puzzle JSON files
|
19
|
+
results_dir (str): Directory where benchmark results are saved
|
20
|
+
timeout (int): Maximum time in seconds allowed for each solver attempt
|
21
|
+
solvers (dict): Dictionary mapping solver names to solver classes
|
22
|
+
"""
|
23
|
+
|
24
|
+
def __init__(
|
25
|
+
self,
|
26
|
+
puzzles_dir: str = "benchmarks/puzzles",
|
27
|
+
results_dir: str = "benchmarks/results",
|
28
|
+
timeout: int = 120,
|
29
|
+
):
|
30
|
+
"""Initialize the benchmark runner.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
puzzles_dir: Directory containing puzzle JSON files
|
34
|
+
results_dir: Directory where benchmark results will be saved
|
35
|
+
timeout: Maximum time in seconds allowed for each solver attempt
|
36
|
+
"""
|
37
|
+
self.puzzles_dir = puzzles_dir
|
38
|
+
self.results_dir = results_dir
|
39
|
+
self.timeout = timeout
|
40
|
+
self.solvers = {
|
41
|
+
"CVC5": CVC5Solver,
|
42
|
+
"DPLL": DPLLSolver,
|
43
|
+
"DPLL(T)": DPLLTSolver,
|
44
|
+
"Z3": Z3Solver,
|
45
|
+
}
|
46
|
+
os.makedirs(results_dir, exist_ok=True)
|
47
|
+
|
48
|
+
def load_puzzle(self, puzzle_id: str) -> Optional[List[List[int]]]:
|
49
|
+
puzzle_path = os.path.join(self.puzzles_dir, f"{puzzle_id}.json")
|
50
|
+
try:
|
51
|
+
with open(puzzle_path, "r") as f:
|
52
|
+
data = json.load(f)
|
53
|
+
for key in ["grid", "puzzle", "gridc"]:
|
54
|
+
if key in data:
|
55
|
+
return data[key]
|
56
|
+
print(
|
57
|
+
f"No valid grid found in {puzzle_id}. Available keys: {list(data.keys())}"
|
58
|
+
)
|
59
|
+
return None
|
60
|
+
except Exception as e:
|
61
|
+
print(f"Error loading puzzle {puzzle_id}: {e}")
|
62
|
+
return None
|
63
|
+
|
64
|
+
def _solve_with_timeout(self, solver_class, puzzle, queue):
|
65
|
+
solver = solver_class(puzzle)
|
66
|
+
result = solver.solve()
|
67
|
+
# Pack both the result and propagation count
|
68
|
+
queue.put((result, getattr(solver, "propagated_clauses", 0)))
|
69
|
+
|
70
|
+
def run_solver(self, solver_name: str, puzzle: List[List[int]]) -> Dict:
|
71
|
+
"""Run a single solver on a puzzle and collect results with timeout.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
solver_name: Name of the solver to use
|
75
|
+
puzzle: 2D list representing the Sudoku puzzle
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
Dict containing:
|
79
|
+
status: 'sat', 'unsat', 'timeout', or 'error'
|
80
|
+
solve_time: Time taken in seconds
|
81
|
+
propagations: Number of clause propagations (if available)
|
82
|
+
"""
|
83
|
+
solver_class = self.solvers[solver_name]
|
84
|
+
|
85
|
+
# Create queue for getting results
|
86
|
+
ctx = multiprocessing.get_context("spawn")
|
87
|
+
queue = ctx.Queue()
|
88
|
+
|
89
|
+
# Create process for solving
|
90
|
+
process = ctx.Process(
|
91
|
+
target=self._solve_with_timeout, args=(solver_class, puzzle, queue)
|
92
|
+
)
|
93
|
+
|
94
|
+
start_time = time.time()
|
95
|
+
process.start()
|
96
|
+
process.join(timeout=self.timeout)
|
97
|
+
|
98
|
+
solve_time = time.time() - start_time
|
99
|
+
|
100
|
+
if process.is_alive():
|
101
|
+
process.terminate()
|
102
|
+
process.join()
|
103
|
+
return {"status": "timeout", "solve_time": self.timeout, "propagations": 0}
|
104
|
+
|
105
|
+
# Get result and propagation count from queue
|
106
|
+
try:
|
107
|
+
result, propagations = queue.get_nowait()
|
108
|
+
return {
|
109
|
+
"status": "sat" if result else "unsat",
|
110
|
+
"solve_time": solve_time,
|
111
|
+
"propagations": propagations,
|
112
|
+
}
|
113
|
+
except:
|
114
|
+
return {"status": "error", "solve_time": solve_time, "propagations": 0}
|
115
|
+
|
116
|
+
def run_benchmarks(self) -> None:
|
117
|
+
"""Run all solvers on all puzzles and save results.
|
118
|
+
|
119
|
+
Executes benchmarks for each solver on each puzzle, collecting performance
|
120
|
+
metrics and saving results to a timestamped CSV file.
|
121
|
+
|
122
|
+
The CSV output includes:
|
123
|
+
- Solver name
|
124
|
+
- Puzzle unique ID
|
125
|
+
- Solution status
|
126
|
+
- Solve time
|
127
|
+
- Propagation count
|
128
|
+
|
129
|
+
Also calculates and stores aggregate statistics per solver:
|
130
|
+
- Total puzzles attempted
|
131
|
+
- Number of puzzles solved
|
132
|
+
- Total and average solving times
|
133
|
+
- Total and average propagation counts
|
134
|
+
"""
|
135
|
+
results = {
|
136
|
+
solver_name: {
|
137
|
+
"puzzles": {},
|
138
|
+
"stats": {
|
139
|
+
"total_puzzles": 0,
|
140
|
+
"solved_count": 0,
|
141
|
+
"total_time": 0,
|
142
|
+
"total_propagations": 0,
|
143
|
+
"avg_time": 0,
|
144
|
+
"avg_propagations": 0,
|
145
|
+
},
|
146
|
+
}
|
147
|
+
for solver_name in self.solvers
|
148
|
+
}
|
149
|
+
|
150
|
+
puzzle_files = [f for f in os.listdir(self.puzzles_dir) if f.endswith(".json")]
|
151
|
+
print(f"Found {len(puzzle_files)} puzzle files") # Debug
|
152
|
+
|
153
|
+
for puzzle_file in puzzle_files:
|
154
|
+
puzzle_id = puzzle_file[:-5]
|
155
|
+
puzzle = self.load_puzzle(puzzle_id)
|
156
|
+
|
157
|
+
if not puzzle:
|
158
|
+
print(f"Failed to load puzzle: {puzzle_id}") # Debug
|
159
|
+
continue
|
160
|
+
|
161
|
+
for solver_name in self.solvers:
|
162
|
+
print(f"Running {solver_name} on puzzle {puzzle_id}")
|
163
|
+
result = self.run_solver(solver_name, puzzle)
|
164
|
+
print(f"Result: {result}") # Debug
|
165
|
+
|
166
|
+
results[solver_name]["puzzles"][puzzle_id] = result
|
167
|
+
|
168
|
+
stats = results[solver_name]["stats"]
|
169
|
+
stats["total_puzzles"] += 1
|
170
|
+
if result["status"] == "sat":
|
171
|
+
stats["solved_count"] += 1
|
172
|
+
stats["total_time"] += result["solve_time"]
|
173
|
+
stats["total_propagations"] += result["propagations"]
|
174
|
+
|
175
|
+
# Calculate averages
|
176
|
+
for solver_name, solver_stats in results.items():
|
177
|
+
stats = solver_stats["stats"]
|
178
|
+
total_puzzles = stats["total_puzzles"]
|
179
|
+
if total_puzzles > 0:
|
180
|
+
stats["avg_time"] = stats["total_time"] / total_puzzles
|
181
|
+
stats["avg_propagations"] = stats["total_propagations"] / total_puzzles
|
182
|
+
print(f"Stats for {solver_name}: {stats}") # Debug
|
183
|
+
|
184
|
+
# Save results
|
185
|
+
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
186
|
+
|
187
|
+
# Debug CSV data
|
188
|
+
csv_data = []
|
189
|
+
for solver_name, solver_results in results.items():
|
190
|
+
for puzzle_id, puzzle_result in solver_results["puzzles"].items():
|
191
|
+
row = {
|
192
|
+
"solver": solver_name,
|
193
|
+
"puzzle_id": puzzle_id,
|
194
|
+
"status": puzzle_result["status"],
|
195
|
+
"solve_time": puzzle_result["solve_time"],
|
196
|
+
"propagations": puzzle_result["propagations"],
|
197
|
+
}
|
198
|
+
csv_data.append(row)
|
199
|
+
print(f"Adding CSV row: {row}") # Debug
|
200
|
+
|
201
|
+
csv_path = os.path.join(self.results_dir, f"benchmark_{timestamp}.csv")
|
202
|
+
print(f"Writing {len(csv_data)} rows to CSV") # Debug
|
203
|
+
|
204
|
+
with open(csv_path, "w") as f:
|
205
|
+
if csv_data:
|
206
|
+
headers = csv_data[0].keys()
|
207
|
+
f.write(",".join(headers) + "\n")
|
208
|
+
for row in csv_data:
|
209
|
+
f.write(",".join(str(row[h]) for h in headers) + "\n")
|
210
|
+
|
211
|
+
print(f"Benchmark results saved to {csv_path}")
|