python-constraint2 2.0.0__cp39-cp39-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.
constraint/problem.py ADDED
@@ -0,0 +1,256 @@
1
+ """Module containing the code for problem definitions."""
2
+
3
+ import copy
4
+ from operator import itemgetter
5
+ from typing import Callable, Optional, Union
6
+ from collections.abc import Sequence
7
+
8
+ from constraint.constraints import Constraint, FunctionConstraint
9
+ from constraint.domain import Domain
10
+ from constraint.solvers import OptimizedBacktrackingSolver
11
+
12
+
13
+ class Problem:
14
+ """Class used to define a problem and retrieve solutions."""
15
+
16
+ def __init__(self, solver=None):
17
+ """Initialization method.
18
+
19
+ Args:
20
+ solver (instance of a :py:class:`Solver`): Problem solver (default :py:class:`OptimizedBacktrackingSolver`)
21
+ """
22
+ self._solver = solver or OptimizedBacktrackingSolver()
23
+ self._constraints = []
24
+ self._variables = {}
25
+
26
+ def reset(self):
27
+ """Reset the current problem definition.
28
+
29
+ Example:
30
+ >>> problem = Problem()
31
+ >>> problem.addVariable("a", [1, 2])
32
+ >>> problem.reset()
33
+ >>> problem.getSolution()
34
+ >>>
35
+ """
36
+ del self._constraints[:]
37
+ self._variables.clear()
38
+
39
+ def setSolver(self, solver):
40
+ """Change the problem solver currently in use.
41
+
42
+ Example:
43
+ >>> solver = OptimizedBacktrackingSolver()
44
+ >>> problem = Problem(solver)
45
+ >>> problem.getSolver() is solver
46
+ True
47
+
48
+ Args:
49
+ solver (instance of a :py:class:`Solver`): New problem
50
+ solver
51
+ """
52
+ self._solver = solver
53
+
54
+ def getSolver(self):
55
+ """Obtain the problem solver currently in use.
56
+
57
+ Example:
58
+ >>> solver = OptimizedBacktrackingSolver()
59
+ >>> problem = Problem(solver)
60
+ >>> problem.getSolver() is solver
61
+ True
62
+
63
+ Returns:
64
+ instance of a :py:class:`Solver` subclass: Solver currently in use
65
+ """
66
+ return self._solver
67
+
68
+ def addVariable(self, variable, domain):
69
+ """Add a variable to the problem.
70
+
71
+ Example:
72
+ >>> problem = Problem()
73
+ >>> problem.addVariable("a", [1, 2])
74
+ >>> problem.getSolution() in ({'a': 1}, {'a': 2})
75
+ True
76
+
77
+ Args:
78
+ variable (hashable object): Object representing a problem
79
+ variable
80
+ domain (list, tuple, or instance of :py:class:`Domain`): Set of items
81
+ defining the possible values that the given variable may
82
+ assume
83
+ """
84
+ if variable in self._variables:
85
+ msg = f"Tried to insert duplicated variable {repr(variable)}"
86
+ raise ValueError(msg)
87
+ if isinstance(domain, Domain):
88
+ domain = copy.deepcopy(domain)
89
+ elif hasattr(domain, "__getitem__"):
90
+ domain = Domain(domain)
91
+ else:
92
+ msg = "Domains must be instances of subclasses of the Domain class"
93
+ raise TypeError(msg)
94
+ if not domain:
95
+ raise ValueError("Domain is empty")
96
+ self._variables[variable] = domain
97
+
98
+ def addVariables(self, variables: Sequence, domain):
99
+ """Add one or more variables to the problem.
100
+
101
+ Example:
102
+ >>> problem = Problem()
103
+ >>> problem.addVariables(["a", "b"], [1, 2, 3])
104
+ >>> solutions = problem.getSolutions()
105
+ >>> len(solutions)
106
+ 9
107
+ >>> {'a': 3, 'b': 1} in solutions
108
+ True
109
+
110
+ Args:
111
+ variables (sequence of hashable objects): Any object
112
+ containing a sequence of objects represeting problem
113
+ variables
114
+ domain (list, tuple, or instance of :py:class:`Domain`): Set of items
115
+ defining the possible values that the given variables
116
+ may assume
117
+ """
118
+ for variable in variables:
119
+ self.addVariable(variable, domain)
120
+
121
+ def addConstraint(self, constraint: Union[Constraint, Callable], variables: Optional[Sequence] = None):
122
+ """Add a constraint to the problem.
123
+
124
+ Example:
125
+ >>> problem = Problem()
126
+ >>> problem.addVariables(["a", "b"], [1, 2, 3])
127
+ >>> problem.addConstraint(lambda a, b: b == a+1, ["a", "b"])
128
+ >>> solutions = problem.getSolutions()
129
+ >>>
130
+
131
+ Args:
132
+ constraint (instance of :py:class:`Constraint` or function to be wrapped by :py:class:`FunctionConstraint`):
133
+ Constraint to be included in the problem
134
+ variables (set or sequence of variables): :py:class:`Variables` affected
135
+ by the constraint (default to all variables). Depending
136
+ on the constraint type the order may be important.
137
+ """
138
+ if not isinstance(constraint, Constraint):
139
+ if callable(constraint):
140
+ constraint = FunctionConstraint(constraint)
141
+ else:
142
+ msg = "Constraints must be instances of subclasses " "of the Constraint class"
143
+ raise ValueError(msg)
144
+ self._constraints.append((constraint, variables))
145
+
146
+ def getSolution(self):
147
+ """Find and return a solution to the problem.
148
+
149
+ Example:
150
+ >>> problem = Problem()
151
+ >>> problem.getSolution() is None
152
+ True
153
+ >>> problem.addVariables(["a"], [42])
154
+ >>> problem.getSolution()
155
+ {'a': 42}
156
+
157
+ Returns:
158
+ dictionary mapping variables to values: Solution for the
159
+ problem
160
+ """
161
+ domains, constraints, vconstraints = self._getArgs()
162
+ if not domains:
163
+ return None
164
+ return self._solver.getSolution(domains, constraints, vconstraints)
165
+
166
+ def getSolutions(self):
167
+ """Find and return all solutions to the problem.
168
+
169
+ Example:
170
+ >>> problem = Problem()
171
+ >>> problem.getSolutions() == []
172
+ True
173
+ >>> problem.addVariables(["a"], [42])
174
+ >>> problem.getSolutions()
175
+ [{'a': 42}]
176
+
177
+ Returns:
178
+ list of dictionaries mapping variables to values: All
179
+ solutions for the problem
180
+ """
181
+ domains, constraints, vconstraints = self._getArgs()
182
+ if not domains:
183
+ return []
184
+ return self._solver.getSolutions(domains, constraints, vconstraints)
185
+
186
+ def getSolutionIter(self):
187
+ """Return an iterator to the solutions of the problem.
188
+
189
+ Example:
190
+ >>> problem = Problem()
191
+ >>> list(problem.getSolutionIter()) == []
192
+ True
193
+ >>> problem.addVariables(["a"], [42])
194
+ >>> iter = problem.getSolutionIter()
195
+ >>> next(iter)
196
+ {'a': 42}
197
+ >>> next(iter)
198
+ Traceback (most recent call last):
199
+ ...
200
+ StopIteration
201
+ """
202
+ domains, constraints, vconstraints = self._getArgs()
203
+ if not domains:
204
+ return iter(())
205
+ return self._solver.getSolutionIter(domains, constraints, vconstraints)
206
+
207
+ def getSolutionsOrderedList(self, order: list[str] = None) -> list[tuple]:
208
+ """Returns the solutions as a list of tuples, with each solution tuple ordered according to `order`."""
209
+ solutions: list[dict] = self.getSolutions()
210
+ if order is None or len(order) == 1:
211
+ return list(tuple(solution.values()) for solution in solutions)
212
+ get_in_order = itemgetter(*order)
213
+ return list(get_in_order(params) for params in solutions)
214
+
215
+ def getSolutionsAsListDict(
216
+ self, order: list[str] = None, validate: bool = True
217
+ ) -> tuple[list[tuple], dict[tuple, int], int]: # noqa: E501
218
+ """Returns the searchspace as a list of tuples, a dict of the searchspace for fast lookups and the size."""
219
+ solutions_list = self.getSolutionsOrderedList(order)
220
+ size_list = len(solutions_list)
221
+ solutions_dict: dict = dict(zip(solutions_list, range(size_list)))
222
+ if validate:
223
+ # check for duplicates
224
+ size_dict = len(solutions_dict)
225
+ if size_list != size_dict:
226
+ raise ValueError(
227
+ f"{size_list - size_dict} duplicate parameter configurations in searchspace, should not happen."
228
+ )
229
+ return (
230
+ solutions_list,
231
+ solutions_dict,
232
+ size_list,
233
+ )
234
+
235
+ def _getArgs(self):
236
+ domains = self._variables.copy()
237
+ allvariables = domains.keys()
238
+ constraints = []
239
+ for constraint, variables in self._constraints:
240
+ if not variables:
241
+ variables = list(allvariables)
242
+ constraints.append((constraint, variables))
243
+ vconstraints = {}
244
+ for variable in domains:
245
+ vconstraints[variable] = []
246
+ for constraint, variables in constraints:
247
+ for variable in variables:
248
+ vconstraints[variable].append((constraint, variables))
249
+ for constraint, variables in constraints[:]:
250
+ constraint.preProcess(variables, domains, constraints, vconstraints)
251
+ for domain in domains.values():
252
+ domain.resetState()
253
+ if not domain:
254
+ return None, None, None
255
+ # doArc8(getArcs(domains, constraints), domains, {})
256
+ return domains, constraints, vconstraints