python-constraint2 2.0.0__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,238 @@
1
+ Metadata-Version: 2.3
2
+ Name: python-constraint2
3
+ Version: 2.0.0
4
+ Summary: python-constraint is a module for efficiently solving CSPs (Constraint Solving Problems) over finite domains.
5
+ License: BSD-2-Clause
6
+ Keywords: CSP,constraint solving problems,problem solver,SMT,satisfiability modulo theory,SAT
7
+ Author: Gustavo Niemeyer
8
+ Author-email: gustavo@niemeyer.net
9
+ Maintainer: Floris-Jan Willemsen
10
+ Maintainer-email: fjwillemsen97@gmail.com
11
+ Requires-Python: >=3.9,<3.14
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Education
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: License :: OSI Approved :: BSD License
18
+ Classifier: Natural Language :: English
19
+ Classifier: Programming Language :: C
20
+ Classifier: Programming Language :: Cython
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3.9
23
+ Classifier: Programming Language :: Python :: 3.10
24
+ Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
26
+ Classifier: Programming Language :: Python :: 3.13
27
+ Classifier: Topic :: Scientific/Engineering
28
+ Classifier: Topic :: Software Development
29
+ Project-URL: Documentation, http://python-constraint.github.io/python-constraint/
30
+ Project-URL: Repository, https://github.com/python-constraint/python-constraint
31
+ Description-Content-Type: text/x-rst
32
+
33
+ |License| |Build Status| |Docs| |Python Versions| |Downloads| |Status| |Code Coverage|
34
+
35
+ .. image:: https://github.com/python-constraint/python-constraint/raw/main/docs/assets/logo/N-Queens_problem_Python.svg
36
+ :align: center
37
+ :width: 50%
38
+
39
+ python-constraint
40
+ =================
41
+
42
+ | This software is now back to active development / maintainance status.
43
+ | IMPORTANT: the new version can be installed with `pip install python-constraint2`, as the original pip release will not be updated.
44
+ | For an overview of recent changes, visit the `Changelog <https://github.com/python-constraint/python-constraint/blob/main/CHANGELOG.md>`_.
45
+ | The complete documentation can be found `here <http://python-constraint.github.io/python-constraint/>`_.
46
+
47
+ .. contents::
48
+ :local:
49
+ :depth: 1
50
+
51
+ Introduction
52
+ ------------
53
+ The :code:`python-constraint` module offers efficient solvers for `Constraint Satisfaction Problems (CSPs) <https://en.wikipedia.org/wiki/Constraint_satisfaction_problem>`_ over finite domains in an accessible Python package.
54
+ CSP is class of problems which may be represented in terms of variables (a, b, ...), domains (a in [1, 2, 3], ...), and constraints (a < b, ...).
55
+
56
+ Examples
57
+ --------
58
+
59
+ Basics
60
+ ~~~~~~
61
+
62
+ This interactive Python session demonstrates basic operations:
63
+
64
+ .. code-block:: python
65
+
66
+ >>> from constraint import *
67
+ >>> problem = Problem()
68
+ >>> problem.addVariable("a", [1,2,3])
69
+ >>> problem.addVariable("b", [4,5,6])
70
+ >>> problem.getSolutions()
71
+ [{'a': 3, 'b': 6}, {'a': 3, 'b': 5}, {'a': 3, 'b': 4},
72
+ {'a': 2, 'b': 6}, {'a': 2, 'b': 5}, {'a': 2, 'b': 4},
73
+ {'a': 1, 'b': 6}, {'a': 1, 'b': 5}, {'a': 1, 'b': 4}]
74
+
75
+ >>> problem.addConstraint(lambda a, b: a*2 == b,
76
+ ("a", "b"))
77
+ >>> problem.getSolutions()
78
+ [{'a': 3, 'b': 6}, {'a': 2, 'b': 4}]
79
+
80
+ >>> problem = Problem()
81
+ >>> problem.addVariables(["a", "b"], [1, 2, 3])
82
+ >>> problem.addConstraint(AllDifferentConstraint())
83
+ >>> problem.getSolutions()
84
+ [{'a': 3, 'b': 2}, {'a': 3, 'b': 1}, {'a': 2, 'b': 3},
85
+ {'a': 2, 'b': 1}, {'a': 1, 'b': 2}, {'a': 1, 'b': 3}]
86
+
87
+ Rooks problem
88
+ ~~~~~~~~~~~~~
89
+
90
+ The following example solves the classical Eight Rooks problem:
91
+
92
+ .. code-block:: python
93
+
94
+ >>> problem = Problem()
95
+ >>> numpieces = 8
96
+ >>> cols = range(numpieces)
97
+ >>> rows = range(numpieces)
98
+ >>> problem.addVariables(cols, rows)
99
+ >>> for col1 in cols:
100
+ ... for col2 in cols:
101
+ ... if col1 < col2:
102
+ ... problem.addConstraint(lambda row1, row2: row1 != row2,
103
+ ... (col1, col2))
104
+ >>> solutions = problem.getSolutions()
105
+ >>> solutions
106
+ >>> solutions
107
+ [{0: 7, 1: 6, 2: 5, 3: 4, 4: 3, 5: 2, 6: 1, 7: 0},
108
+ {0: 7, 1: 6, 2: 5, 3: 4, 4: 3, 5: 2, 6: 0, 7: 1},
109
+ {0: 7, 1: 6, 2: 5, 3: 4, 4: 3, 5: 1, 6: 2, 7: 0},
110
+ {0: 7, 1: 6, 2: 5, 3: 4, 4: 3, 5: 1, 6: 0, 7: 2},
111
+ ...
112
+ {0: 7, 1: 5, 2: 3, 3: 6, 4: 2, 5: 1, 6: 4, 7: 0},
113
+ {0: 7, 1: 5, 2: 3, 3: 6, 4: 1, 5: 2, 6: 0, 7: 4},
114
+ {0: 7, 1: 5, 2: 3, 3: 6, 4: 1, 5: 2, 6: 4, 7: 0},
115
+ {0: 7, 1: 5, 2: 3, 3: 6, 4: 1, 5: 4, 6: 2, 7: 0},
116
+ {0: 7, 1: 5, 2: 3, 3: 6, 4: 1, 5: 4, 6: 0, 7: 2},
117
+ ...]
118
+
119
+
120
+ Magic squares
121
+ ~~~~~~~~~~~~~
122
+
123
+ This example solves a 4x4 magic square:
124
+
125
+ .. code-block:: python
126
+
127
+ >>> problem = Problem()
128
+ >>> problem.addVariables(range(0, 16), range(1, 16 + 1))
129
+ >>> problem.addConstraint(AllDifferentConstraint(), range(0, 16))
130
+ >>> problem.addConstraint(ExactSumConstraint(34), [0, 5, 10, 15])
131
+ >>> problem.addConstraint(ExactSumConstraint(34), [3, 6, 9, 12])
132
+ >>> for row in range(4):
133
+ ... problem.addConstraint(ExactSumConstraint(34),
134
+ [row * 4 + i for i in range(4)])
135
+ >>> for col in range(4):
136
+ ... problem.addConstraint(ExactSumConstraint(34),
137
+ [col + 4 * i for i in range(4)])
138
+ >>> solutions = problem.getSolutions()
139
+
140
+ Features
141
+ --------
142
+
143
+ The following solvers are available:
144
+
145
+ - Backtracking solver
146
+ - Optimized backtracking solver
147
+ - Recursive backtracking solver
148
+ - Minimum conflicts solver
149
+
150
+ .. role:: python(code)
151
+ :language: python
152
+
153
+ Predefined constraint types currently available:
154
+
155
+ - :python:`FunctionConstraint`
156
+ - :python:`AllDifferentConstraint`
157
+ - :python:`AllEqualConstraint`
158
+ - :python:`MaxSumConstraint`
159
+ - :python:`ExactSumConstraint`
160
+ - :python:`MinSumConstraint`
161
+ - :python:`MaxProdConstraint`
162
+ - :python:`MinProdConstraint`
163
+ - :python:`InSetConstraint`
164
+ - :python:`NotInSetConstraint`
165
+ - :python:`SomeInSetConstraint`
166
+ - :python:`SomeNotInSetConstraint`
167
+
168
+ API documentation
169
+ -----------------
170
+ Documentation for the module is available at: http://python-constraint.github.io/python-constraint/.
171
+ It can be built locally by running :code:`make clean html` from the `docs` folder.
172
+ For viewing RST files locally, `restview <https://pypi.org/project/restview/>`_ is recommended.
173
+
174
+ Download and install
175
+ --------------------
176
+
177
+ .. code-block:: shell
178
+
179
+ $ pip install python-constraint2
180
+
181
+ Testing
182
+ -------
183
+
184
+ Run :code:`nox` (tests for all supported Python versions in own virtual environment).
185
+
186
+ To test against your local Python version: make sure you have the development dependencies installed.
187
+ Run :code:`pytest` (optionally add :code:`--no-cov` if you have the C-extensions enabled).
188
+
189
+ Contributing
190
+ ------------
191
+
192
+ Feel free to contribute by `submitting pull requests <https://github.com/python-constraint/python-constraint/pulls>`_ or `opening issues <https://github.com/python-constraint/python-constraint/issues>`_.
193
+ Please refer to the `contribution guidelines <https://github.com/python-constraint/python-constraint/contribute>`_ before doing so.
194
+
195
+ Roadmap
196
+ -------
197
+
198
+ This GitHub organization and repository is a global effort to help to maintain :code:`python-constraint`, which was written by Gustavo Niemeyer and originaly located at https://labix.org/python-constraint.
199
+ For an overview of recent changes, visit the `Changelog <https://github.com/python-constraint/python-constraint/blob/main/CHANGELOG.md>`_.
200
+
201
+ Planned development:
202
+
203
+ - Add parallel-capable solver
204
+ - Add a string parser for constraints
205
+ - Versioned documentation
206
+
207
+ Contact
208
+ -------
209
+ - `Floris-Jan Willemsen <https://github.com/fjwillemsen>`_ <fjwillemsen97@gmail.com> (current maintainer)
210
+ - `Sébastien Celles <https://github.com/s-celles/>`_ <s.celles@gmail.com> (former maintainer)
211
+ - `Gustavo Niemeyer <https://github.com/niemeyer/>`_ <gustavo@niemeyer.net> (initial developer)
212
+
213
+ But it's probably better to `open an issue <https://github.com/python-constraint/python-constraint/issues>`_.
214
+
215
+ .. |License| image:: https://img.shields.io/pypi/l/python-constraint2
216
+ :alt: PyPI - License
217
+
218
+ .. |Build Status| image:: https://github.com/python-constraint/python-constraint/actions/workflows/build-test-python-package.yml/badge.svg
219
+ :target: https://github.com/python-constraint/python-constraint/actions/workflows/build-test-python-package.yml
220
+ :alt: Build Status
221
+
222
+ .. |Docs| image:: https://img.shields.io/github/actions/workflow/status/python-constraint/python-constraint/publish-documentation.yml?label=Docs
223
+ :target: http://python-constraint.github.io/python-constraint/
224
+ :alt: Documentation Status
225
+
226
+ .. |Python Versions| image:: https://img.shields.io/pypi/pyversions/python-constraint2
227
+ :alt: PyPI - Python Versions
228
+
229
+ .. |Downloads| image:: https://img.shields.io/pypi/dm/python-constraint2
230
+ :alt: PyPI - Downloads
231
+
232
+ .. |Status| image:: https://img.shields.io/pypi/status/python-constraint2
233
+ :alt: PyPI - Status
234
+
235
+ .. |Code Coverage| image:: https://coveralls.io/repos/github/python-constraint/python-constraint/badge.svg
236
+ :target: https://coveralls.io/github/python-constraint/python-constraint
237
+ :alt: Code Coverage
238
+
@@ -0,0 +1,22 @@
1
+ constraint/__init__.py,sha256=7on8Mi-QJJbw8EwG9dahObY7MgpOdTVkgky0Vs68kJA,1845
2
+ constraint/constraints.c,sha256=sLlLgqvL9ZFLe8-5DWPRj5qdbtJDEDUGQN_uPX89BkQ,1288172
3
+ constraint/constraints.py,sha256=rPdVdTTQflJVneGeTZ5q6WNYOU7mmmjx5boR7VC2uQs,31371
4
+ constraint/domain.c,sha256=j_4sp_6FzlDsn7UpF4oe3cgFiIOxAZSt5dST8jrgptU,359677
5
+ constraint/domain.py,sha256=XLzl619prCdB8vxSLAVQWQiFwEV2qRzyLqUJLqxmWrk,3031
6
+ constraint/problem.c,sha256=LP1YGVG46BA7ZJmpBlOmJ5Rr1tx0L-nxxUgfbmZJ2Xo,670534
7
+ constraint/problem.py,sha256=VnBMDLDSBmbqsHmzfh17TDraiXBM93IVA1wxz3XtOgs,9561
8
+ constraint/solvers.c,sha256=Ay7M4-l79f1ec8K6ZjXFaR4brwSLZuQfbg0UCjwWbz8,1020661
9
+ constraint/solvers.py,sha256=Wzt6THvpr3-LeuAZUWTl1cIundOwb5UFv0JJweMec20,23024
10
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ tests/setup_teardown.py,sha256=z4g_jdHrz4Lnd22MCkIE9HCpr-wipQKX7-5p46ZFnj4,1390
12
+ tests/test_compilation.py,sha256=En9Z9NcOxG9N0qIUCGwFib5Jq_uxdqg5_I79mm37Gj0,472
13
+ tests/test_constraint.py,sha256=mKhbaKr0kN77qXGHQPsnDrNs5lzEInpDzP9L84HwDaI,3426
14
+ tests/test_doctests.py,sha256=PUD7QeB-OufzwywquWS0NkVAU1zTUtkk_tcZikCtBzI,425
15
+ tests/test_problem.py,sha256=k01G64kzZ969mC6iQxW87oYisvDIPQiZi5Y8fJa02qw,806
16
+ tests/test_solvers.py,sha256=M28578HKaoRL5nBqBXSTktDCwNhqbN0ZdhcM6RyXZbA,2853
17
+ tests/test_some_not_in_set.py,sha256=xgSairFNF4T-CYdAWhIv3qoOixnhGjWUdY01GzIjwds,3496
18
+ tests/test_toml_file.py,sha256=JYpsOPWOY_-hIVPLw4J5kvLMf7qWxXN8c56JoNDhl3Y,2230
19
+ python_constraint2-2.0.0.dist-info/LICENSE,sha256=-29wB_JJKS4anSogQEG0yZqx5lPZc2m8iPJhKL-p45A,1358
20
+ python_constraint2-2.0.0.dist-info/METADATA,sha256=WwzCbJK7CQ3T8SMyNclOuNj5tlbub8DXd6qP9hhpguQ,9144
21
+ python_constraint2-2.0.0.dist-info/WHEEL,sha256=cnXks5W6K-GKE7eq3gbNbzgMUdWLplnoIULPQiXHuZs,98
22
+ python_constraint2-2.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.0.1
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-cp312-win_amd64
tests/__init__.py ADDED
File without changes
@@ -0,0 +1,40 @@
1
+ """General setup and teardown for the tests, and module-level tests."""
2
+ from pathlib import Path
3
+ import argparse
4
+
5
+
6
+ def unuse_extensions():
7
+ """Make sure C-extensions are not used by adding an underscore to .so files."""
8
+ dir = Path("./constraint")
9
+ assert dir.exists()
10
+ files = dir.glob("*.so")
11
+ for file in files:
12
+ if file.name[0] != "_":
13
+ # add a leading underscore
14
+ file.rename(f"{dir}/_{file.name}")
15
+
16
+
17
+ def use_extensions():
18
+ """Make sure C-extensions are used by removing the underscore to .so files."""
19
+ dir = Path("./constraint")
20
+ assert dir.exists()
21
+ files = dir.glob("_*.so")
22
+ for file in files:
23
+ if file.name[0] == "_":
24
+ # remove the leading underscore
25
+ file.rename(f"{dir}/{file.name[1:]}")
26
+
27
+
28
+ if __name__ == "__main__":
29
+ parser = argparse.ArgumentParser()
30
+ parser.add_argument("--enable_extensions", action="store_true")
31
+ parser.add_argument("--no-enable_extensions", dest="enable_extensions", action="store_false")
32
+ parser.set_defaults(enable_extensions=True)
33
+ args = parser.parse_args()
34
+ enable_extensions = args.enable_extensions
35
+ if enable_extensions is True:
36
+ use_extensions()
37
+ elif enable_extensions is False:
38
+ unuse_extensions()
39
+ else:
40
+ raise ValueError(f"Invalid value for {enable_extensions=}")
@@ -0,0 +1,17 @@
1
+ from pathlib import Path
2
+
3
+
4
+ def test_if_compiled():
5
+ """Test whether C-extensions are succesfully ran, if enabled."""
6
+ from constraint import check_if_compiled
7
+
8
+ # check if the .so files are commented
9
+ dir = Path("./constraint")
10
+ assert dir.exists()
11
+ files = list(dir.glob("_*.so"))
12
+
13
+ # check if the code uses C-extensions
14
+ if len(files) > 0:
15
+ assert check_if_compiled() is False
16
+ else:
17
+ assert check_if_compiled()
@@ -0,0 +1,127 @@
1
+ import constraint
2
+
3
+ from examples.abc import abc
4
+ from examples.coins import coins
5
+
6
+ # from examples.crosswords import crosswords
7
+ from examples.einstein import einstein
8
+ from examples.queens import queens
9
+ from examples.rooks import rooks
10
+ from examples.studentdesks import studentdesks
11
+
12
+ # from examples.sudoku import sudoku
13
+ # from examples.wordmath import (seisseisdoze, sendmoremoney, twotwofour)
14
+ # from examples.xsum import xsum
15
+
16
+
17
+ def test_abc():
18
+ solutions = abc.solve()
19
+ minvalue, minsolution = solutions
20
+ assert minvalue == 37
21
+ assert minsolution == {"a": 1, "c": 2, "b": 1}
22
+
23
+
24
+ def test_coins():
25
+ solutions = coins.solve()
26
+ assert len(solutions) == 2
27
+
28
+
29
+ def test_einstein():
30
+ solutions = einstein.solve()
31
+ expected_solutions = [
32
+ {
33
+ "nationality2": "dane",
34
+ "nationality3": "brit",
35
+ "nationality1": "norwegian",
36
+ "nationality4": "german",
37
+ "nationality5": "swede",
38
+ "color1": "yellow",
39
+ "color3": "red",
40
+ "color2": "blue",
41
+ "color5": "white",
42
+ "color4": "green",
43
+ "drink4": "coffee",
44
+ "drink5": "beer",
45
+ "drink1": "water",
46
+ "drink2": "tea",
47
+ "drink3": "milk",
48
+ "smoke5": "bluemaster",
49
+ "smoke4": "prince",
50
+ "smoke3": "pallmall",
51
+ "smoke2": "blends",
52
+ "smoke1": "dunhill",
53
+ "pet5": "dogs",
54
+ "pet4": "fish",
55
+ "pet1": "cats",
56
+ "pet3": "birds",
57
+ "pet2": "horses",
58
+ }
59
+ ]
60
+ assert solutions == expected_solutions
61
+
62
+
63
+ def test_queens():
64
+ solutions, size = queens.solve()
65
+ assert size == 8
66
+ for solution in solutions:
67
+ queens.showSolution(solution, size)
68
+
69
+
70
+ def test_rooks():
71
+ size = 8
72
+ solutions = rooks.solve(size)
73
+ assert len(solutions) == rooks.factorial(size)
74
+
75
+
76
+ def test_studentdesks():
77
+ solutions = studentdesks.solve()
78
+ expected_solutions = {
79
+ 1: "A",
80
+ 2: "E",
81
+ 3: "D",
82
+ 4: "E",
83
+ 5: "D",
84
+ 6: "A",
85
+ 7: "C",
86
+ 8: "B",
87
+ 9: "C",
88
+ 10: "B",
89
+ 11: "E",
90
+ 12: "D",
91
+ 13: "E",
92
+ 14: "D",
93
+ 15: "A",
94
+ 16: "C",
95
+ 17: "B",
96
+ 18: "C",
97
+ 19: "B",
98
+ 20: "A",
99
+ }
100
+ assert solutions == expected_solutions
101
+
102
+
103
+ def test_constraint_without_variables():
104
+ problem = constraint.Problem()
105
+ problem.addVariable("a", [1, 2, 3])
106
+ problem.addConstraint(lambda a: a * 2 == 6)
107
+ solutions = problem.getSolutions()
108
+ assert solutions == [{"a": 3}]
109
+
110
+
111
+ def test_multipliers():
112
+ """Test the multiplier functionality in the constraints."""
113
+ from constraint import MaxSumConstraint, ExactSumConstraint, MinSumConstraint
114
+
115
+ problem = constraint.Problem()
116
+ problem.addVariable("x", [-1, 0, 1, 2])
117
+ problem.addVariable("y", [1, 2])
118
+ problem.addConstraint(MaxSumConstraint(4, [2, 1]), ["x", "y"])
119
+ problem.addConstraint(ExactSumConstraint(4, [1, 2]), ["x", "y"])
120
+ problem.addConstraint(MinSumConstraint(0, [0.5, 1]), ["x"])
121
+
122
+ possible_solutions = [{"y": 2, "x": 0}, {"y": 1, "x": 2}]
123
+
124
+ # get the solutions
125
+ solutions = problem.getSolutions()
126
+ for solution in solutions:
127
+ assert solution in possible_solutions
tests/test_doctests.py ADDED
@@ -0,0 +1,10 @@
1
+ import doctest
2
+ import constraint.problem as problem
3
+ import constraint.domain as domain
4
+ import constraint.constraints as constraints
5
+ import constraint.solvers as solvers
6
+
7
+ assert doctest.testmod(problem)[0] == 0
8
+ assert doctest.testmod(domain)[0] == 0
9
+ assert doctest.testmod(constraints, extraglobs={'Problem': problem.Problem})[0] == 0
10
+ assert doctest.testmod(solvers, extraglobs={'Problem': problem.Problem})[0] == 0
tests/test_problem.py ADDED
@@ -0,0 +1,27 @@
1
+ from constraint import Constraint, Domain, Problem
2
+
3
+
4
+ def test_addVariable_support_domain_subclasses():
5
+ class MyCustomDomain(Domain):
6
+ pass
7
+
8
+ class MyConstraint(Constraint):
9
+ def __call__(self, variables, domains, assignments, forwardcheck=False):
10
+ assert isinstance(domains["x"], Domain)
11
+ assert isinstance(domains["y"], MyCustomDomain)
12
+ return True
13
+
14
+ problem = Problem()
15
+ problem.addVariable("x", [0, 1])
16
+ problem.addVariable("y", MyCustomDomain([0, 1]))
17
+ problem.addConstraint(MyConstraint())
18
+ solution = problem.getSolution()
19
+
20
+ possible_solutions = [
21
+ {"x": 0, "y": 0},
22
+ {"x": 0, "y": 1},
23
+ {"x": 1, "y": 0},
24
+ {"x": 1, "y": 1},
25
+ ]
26
+
27
+ assert solution in possible_solutions
tests/test_solvers.py ADDED
@@ -0,0 +1,72 @@
1
+ from constraint import Problem, MinConflictsSolver, BacktrackingSolver, OptimizedBacktrackingSolver, RecursiveBacktrackingSolver, MaxProdConstraint, MinProdConstraint, MinSumConstraint, FunctionConstraint
2
+
3
+
4
+ def test_min_conflicts_solver():
5
+ problem = Problem(MinConflictsSolver())
6
+ problem.addVariable("x", [0, 1])
7
+ problem.addVariable("y", [0, 1])
8
+
9
+ possible_solutions = [
10
+ {"x": 0, "y": 0},
11
+ {"x": 0, "y": 1},
12
+ {"x": 1, "y": 0},
13
+ {"x": 1, "y": 1},
14
+ ]
15
+
16
+ # test if all solutions are eventually found by iteration and adding the last solutions as a constraint
17
+ for _ in possible_solutions:
18
+ solution = problem.getSolution()
19
+ assert solution in possible_solutions
20
+ problem.addConstraint(FunctionConstraint(lambda x, y: (lambda x, y, xs, ys: x != xs or y != ys)(x, y, solution['x'], solution['y'])))
21
+
22
+ def test_optimized_backtracking_solver():
23
+ # setup the solvers
24
+ problem_bt = Problem(BacktrackingSolver())
25
+ problem_opt = Problem(OptimizedBacktrackingSolver())
26
+ problem_opt_nfwd = Problem(OptimizedBacktrackingSolver(forwardcheck=False))
27
+ problems = [problem_bt, problem_opt, problem_opt_nfwd]
28
+
29
+ # define the problem for all solvers
30
+ for problem in problems:
31
+ problem.addVariable("x", [-1, 0, 1, 2])
32
+ problem.addVariable("y", [1, 2])
33
+ problem.addConstraint(MaxProdConstraint(2), ["x", "y"])
34
+ problem.addConstraint(MinProdConstraint(1), ["x", "y"])
35
+ problem.addConstraint(MinSumConstraint(0), ["x"])
36
+
37
+ # get the solutions
38
+ true_solutions = [(2, 1), (1, 2), (1, 1)]
39
+ order = ["x", "y"]
40
+ solution = problem_bt.getSolution()
41
+ solution_tuple = tuple(solution[key] for key in order)
42
+
43
+ # validate a single solution
44
+ solution_opt = problem_opt.getSolution()
45
+ assert tuple(solution_opt[key] for key in order) in true_solutions
46
+
47
+ # validate all solutions
48
+ def validate(solutions_list, solutions_dict, size):
49
+ assert size == len(true_solutions)
50
+ assert solution_tuple in solutions_list
51
+ assert solution_tuple in solutions_dict
52
+ assert all(sol in solutions_list for sol in true_solutions)
53
+
54
+ validate(*problem_opt.getSolutionsAsListDict(order=order))
55
+ validate(*problem_opt_nfwd.getSolutionsAsListDict(order=order))
56
+
57
+ def test_recursive_backtracking_solver():
58
+ problem = Problem(RecursiveBacktrackingSolver())
59
+ problem.addVariable("x", [0, 1])
60
+ problem.addVariable("y", [0, 1])
61
+ solution = problem.getSolution()
62
+ solutions = problem.getSolutions()
63
+
64
+ possible_solutions = [
65
+ {"x": 0, "y": 0},
66
+ {"x": 0, "y": 1},
67
+ {"x": 1, "y": 0},
68
+ {"x": 1, "y": 1},
69
+ ]
70
+
71
+ assert solution in possible_solutions
72
+ assert all(sol in possible_solutions for sol in solutions)
@@ -0,0 +1,99 @@
1
+ from constraint import Domain, Variable, SomeNotInSetConstraint
2
+
3
+
4
+ def test_empty_constraint():
5
+ constrainer = SomeNotInSetConstraint(set())
6
+ v1, v2 = variables = [Variable("v1"), Variable("v2")]
7
+ assignments = {v1: "a", v2: "b"}
8
+
9
+ assert constrainer(variables, {}, assignments)
10
+
11
+
12
+ def test_no_overlap():
13
+ constrainer = SomeNotInSetConstraint(set("zy"))
14
+ v1, v2 = variables = [Variable("v1"), Variable("v2")]
15
+ assignments = {v1: "a", v2: "b"}
16
+
17
+ assert constrainer(variables, {}, assignments)
18
+
19
+
20
+ def test_some_overlap():
21
+ constrainer = SomeNotInSetConstraint(set("b"))
22
+ v1, v2 = variables = [Variable("v1"), Variable("v2")]
23
+ assignments = {v1: "a", v2: "b"}
24
+
25
+ assert constrainer(variables, {}, assignments)
26
+
27
+
28
+ def test_too_much_overlap():
29
+ constrainer = SomeNotInSetConstraint(set("ab"))
30
+ v1, v2 = variables = [Variable("v1"), Variable("v2")]
31
+ assignments = {v1: "a", v2: "b"}
32
+
33
+ assert not constrainer(variables, {}, assignments)
34
+
35
+
36
+ def test_exact():
37
+ constrainer = SomeNotInSetConstraint(set("abc"), n=2, exact=True)
38
+ v1, v2, v3 = variables = [Variable("v1"), Variable("v2"), Variable("v3")]
39
+
40
+ assignments = {v1: "a", v2: "y", v3: "z"}
41
+ assert constrainer(variables, {}, assignments)
42
+
43
+ assignments = {v1: "a", v2: "y"}
44
+ assert constrainer(variables, {}, assignments)
45
+
46
+ assignments = {v1: "a", v2: "b", v3: "z"}
47
+ assert not constrainer(variables, {}, assignments)
48
+
49
+ assignments = {v1: "a", v2: "b"}
50
+ assert not constrainer(variables, {}, assignments)
51
+
52
+ assignments = {v1: "a", v2: "b", v3: "c"}
53
+ assert not constrainer(variables, {}, assignments)
54
+
55
+ assignments = {v1: "x", v2: "y", v3: "z"}
56
+ assert not constrainer(variables, {}, assignments)
57
+
58
+
59
+ def test_forwardcheck():
60
+ constrainer = SomeNotInSetConstraint(set("abc"), n=2)
61
+ v1, v2, v3 = variables = [Variable("v1"), Variable("v2"), Variable("v3")]
62
+
63
+ domains = {v1: Domain(["a"]), v2: Domain(["b", "y"]), v3: Domain(["c", "z"])}
64
+ assert constrainer(variables, domains, {v1: "a"})
65
+ assert ["a"] == list(domains[v1])
66
+ assert ["b", "y"] == list(domains[v2])
67
+ assert ["c", "z"] == list(domains[v3])
68
+
69
+ assert constrainer(variables, domains, {v1: "a"}, True)
70
+ assert ["a"] == list(domains[v1])
71
+ assert ["y"] == list(domains[v2])
72
+ assert ["z"] == list(domains[v3])
73
+
74
+
75
+ def test_forwardcheck_empty_domain():
76
+ constrainer = SomeNotInSetConstraint(set("abc"))
77
+ v1, v2 = variables = [Variable("v1"), Variable("v2")]
78
+
79
+ domains = {v1: Domain(["a"]), v2: Domain(["b"])}
80
+ assert constrainer(variables, domains, {v1: "a"})
81
+ assert not constrainer(variables, domains, {v1: "a"}, True)
82
+
83
+
84
+ def test_forwardcheck_exact():
85
+ constrainer = SomeNotInSetConstraint(set("abc"), n=2, exact=True)
86
+ v1, v2, v3 = variables = [Variable("v1"), Variable("v2"), Variable("v3")]
87
+ assignments = {v1: "a"}
88
+
89
+ domains = {v1: Domain(["a", "x"]), v2: Domain(["b", "y"]), v3: Domain(["c", "z"])}
90
+ assert constrainer(variables, domains, assignments)
91
+ assert constrainer(variables, domains, assignments, True)
92
+ assert "b" not in domains[v2]
93
+ assert "y" in domains[v2]
94
+ assert "c" not in domains[v3]
95
+ assert "z" in domains[v3]
96
+
97
+ domains = {v1: Domain(["a", "x"]), v2: Domain(["b", "y"]), v3: Domain(["c"])}
98
+ assert constrainer(variables, domains, assignments)
99
+ assert not constrainer(variables, domains, assignments, True)