NUCS 2.3.0__tar.gz → 3.0.0__tar.gz
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.
- {nucs-2.3.0 → nucs-3.0.0/NUCS.egg-info}/PKG-INFO +1 -1
- {nucs-2.3.0/NUCS.egg-info → nucs-3.0.0}/PKG-INFO +1 -1
- {nucs-2.3.0 → nucs-3.0.0}/docs/source/conf.py +2 -2
- {nucs-2.3.0 → nucs-3.0.0}/nucs/constants.py +5 -3
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/golomb/golomb_problem.py +11 -6
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/queens/__main__.py +21 -11
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/propagators.py +4 -2
- {nucs-2.3.0 → nucs-3.0.0}/nucs/solvers/backtrack_solver.py +38 -35
- {nucs-2.3.0 → nucs-3.0.0}/nucs/solvers/bound_consistency_algorithm.py +14 -14
- nucs-3.0.0/nucs/solvers/choice_points.py +88 -0
- nucs-3.0.0/nucs/solvers/heuristics.py +193 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/solvers/multiprocessing_solver.py +1 -4
- {nucs-2.3.0 → nucs-3.0.0}/nucs/solvers/shaving_consistency_algorithm.py +59 -131
- {nucs-2.3.0 → nucs-3.0.0}/nucs/solvers/solver.py +44 -11
- {nucs-2.3.0 → nucs-3.0.0}/pyproject.toml +1 -1
- nucs-2.3.0/nucs/solvers/choice_points.py +0 -84
- nucs-2.3.0/nucs/solvers/heuristics.py +0 -165
- {nucs-2.3.0 → nucs-3.0.0}/LICENSE.md +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/NUCS.egg-info/SOURCES.txt +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/NUCS.egg-info/dependency_links.txt +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/NUCS.egg-info/requires.txt +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/NUCS.egg-info/top_level.txt +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/README.md +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/alpha/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/alpha/__main__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/alpha/alpha_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/bibd/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/bibd/__main__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/bibd/bibd_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/donald/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/donald/__main__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/donald/donald_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/golomb/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/golomb/__main__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/knapsack/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/knapsack/__main__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/knapsack/knapsack_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/magic_sequence/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/magic_sequence/__main__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/magic_sequence/magic_sequence_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/magic_square/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/magic_square/__main__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/magic_square/magic_square_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/quasigroup/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/quasigroup/__main__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/quasigroup/quasigroup_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/queens/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/queens/queens_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/schur_lemma/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/schur_lemma/__main__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/schur_lemma/schur_lemma_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/sports_tournament_scheduling/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/sports_tournament_scheduling/__main__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/sports_tournament_scheduling/sports_tournament_scheduling_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/sudoku/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/examples/sudoku/sudoku_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/numba_helper.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/numpy_helper.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/problems/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/problems/latin_square_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/problems/problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/affine_eq_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/affine_geq_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/affine_leq_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/alldifferent_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/and_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/count_eq_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/dummy_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/element_lic_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/element_liv_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/exactly_eq_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/exactly_true_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/gcc_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/lexicographic_leq_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/max_eq_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/max_leq_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/min_eq_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/min_geq_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/propagators/relation_propagator.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/solvers/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/nucs/solvers/consistency_algorithms.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/setup.cfg +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_alpha.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_bibd.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_donald.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_golomb.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_knapsack.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_latin_square.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_magic_sequence.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_magic_square.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_quasigroup.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_queens.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_schur_lemma.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_sports_tournament_scheduling.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/examples/test_sudokus.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/problems/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/problems/test_problem.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_affine_eq.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_affine_geq.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_affine_leq.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_alldifferent.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_count_eq.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_element_lic.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_element_liv.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_exactly_eq.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_gcc.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_lexicographic_leq.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_max_eq.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_max_leq.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_min_eq.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_min_geq.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/propagators/test_relation.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/solvers/__init__.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/solvers/test_backtrack_solver.py +0 -0
- {nucs-2.3.0 → nucs-3.0.0}/tests/solvers/test_multiprocessing_solver.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: NUCS
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: A Numpy and Numba based Python library for solving Constraint Satisfaction Problems over finite domains
|
|
5
5
|
Author-email: Yan Georget <yan.georget@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/yangeorget/nucs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: NUCS
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: A Numpy and Numba based Python library for solving Constraint Satisfaction Problems over finite domains
|
|
5
5
|
Author-email: Yan Georget <yan.georget@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/yangeorget/nucs
|
|
@@ -29,8 +29,10 @@ PROBLEM_UNBOUND = 1 # returned when the filtering of a problem has been complet
|
|
|
29
29
|
PROBLEM_BOUND = 2 # returned when a problem is solved
|
|
30
30
|
|
|
31
31
|
SIGNATURE_COMPUTE_DOMAINS = int64(int32[:, :], int32[:]) # domains, parameters
|
|
32
|
-
SIGNATURE_DOM_HEURISTIC = int64(
|
|
33
|
-
|
|
32
|
+
SIGNATURE_DOM_HEURISTIC = int64(
|
|
33
|
+
int32[:, :, :], uint16[:, :], uint8[:], int64 # shr_domains_stack # dom_update_stack # stacks_top # dom_idx
|
|
34
|
+
)
|
|
35
|
+
SIGNATURE_VAR_HEURISTIC = int64(int32[:, :, :], uint8[:]) # shr_domains_stack, stacks_top
|
|
34
36
|
SIGNATURE_CONSISTENCY_ALG = int64(
|
|
35
37
|
int64[:], # statistics
|
|
36
38
|
uint8[:], # algorithms
|
|
@@ -57,7 +59,7 @@ TYPE_CONSISTENCY_ALG = types.FunctionType(SIGNATURE_CONSISTENCY_ALG)
|
|
|
57
59
|
|
|
58
60
|
NUMBA_DISABLE_JIT = os.getenv("NUMBA_DISABLE_JIT")
|
|
59
61
|
|
|
60
|
-
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(name)s - %(message)s"
|
|
62
|
+
LOG_FORMAT = "%(asctime)s - %(processName)s - %(levelname)s - %(name)s - %(funcName)s - %(message)s"
|
|
61
63
|
LOG_LEVEL_DEBUG = "DEBUG"
|
|
62
64
|
LOG_LEVEL_INFO = "INFO"
|
|
63
65
|
LOG_LEVEL_WARNING = "WARNING"
|
|
@@ -127,7 +127,7 @@ def golomb_consistency_algorithm(
|
|
|
127
127
|
shr_domains_stack: NDArray,
|
|
128
128
|
not_entailed_propagators_stack: NDArray,
|
|
129
129
|
dom_update_stack: NDArray,
|
|
130
|
-
|
|
130
|
+
stacks_top: NDArray,
|
|
131
131
|
triggered_propagators: NDArray,
|
|
132
132
|
compute_domains_addrs: NDArray,
|
|
133
133
|
) -> int:
|
|
@@ -139,7 +139,7 @@ def golomb_consistency_algorithm(
|
|
|
139
139
|
"""
|
|
140
140
|
# first prune the search space
|
|
141
141
|
mark_nb = (1 + int(math.sqrt(8 * len(dom_indices_arr) + 1))) // 2
|
|
142
|
-
ni_var_idx = first_not_instantiated_var_heuristic(shr_domains_stack
|
|
142
|
+
ni_var_idx = first_not_instantiated_var_heuristic(shr_domains_stack, stacks_top) # no domains shared between vars
|
|
143
143
|
if 1 < ni_var_idx < mark_nb - 1: # otherwise useless
|
|
144
144
|
used_distance = np.zeros(sum_first(mark_nb - 2) + 1, dtype=np.bool)
|
|
145
145
|
# a reusable array for storing the minimal sum of different integers:
|
|
@@ -148,7 +148,7 @@ def golomb_consistency_algorithm(
|
|
|
148
148
|
# the following will mark at most sum(n-3) numbers as used
|
|
149
149
|
# hence there will be at least n-2 unused numbers greater than 0
|
|
150
150
|
for var_idx in range(index(mark_nb, ni_var_idx - 2, ni_var_idx - 1) + 1):
|
|
151
|
-
dist = shr_domains_stack[0, dom_indices_arr[var_idx], MIN] # no offset
|
|
151
|
+
dist = shr_domains_stack[stacks_top[0], dom_indices_arr[var_idx], MIN] # no offset
|
|
152
152
|
if dist < len(used_distance):
|
|
153
153
|
used_distance[dist] = True
|
|
154
154
|
# let's compute the sum of non-used numbers
|
|
@@ -161,9 +161,14 @@ def golomb_consistency_algorithm(
|
|
|
161
161
|
for i in range(ni_var_idx - 1, mark_nb - 1):
|
|
162
162
|
for j in range(i + 1, mark_nb):
|
|
163
163
|
dom_idx = dom_indices_arr[index(mark_nb, i, j)]
|
|
164
|
-
shr_domains_stack[0, dom_idx, MIN] = minimal_sum[j - i] # no offset
|
|
164
|
+
shr_domains_stack[stacks_top[0], dom_idx, MIN] = minimal_sum[j - i] # no offset
|
|
165
165
|
add_propagators(
|
|
166
|
-
triggered_propagators,
|
|
166
|
+
triggered_propagators,
|
|
167
|
+
not_entailed_propagators_stack,
|
|
168
|
+
stacks_top,
|
|
169
|
+
shr_domains_propagators,
|
|
170
|
+
dom_idx,
|
|
171
|
+
MIN,
|
|
167
172
|
)
|
|
168
173
|
return bound_consistency_algorithm(
|
|
169
174
|
statistics,
|
|
@@ -179,7 +184,7 @@ def golomb_consistency_algorithm(
|
|
|
179
184
|
shr_domains_stack,
|
|
180
185
|
not_entailed_propagators_stack,
|
|
181
186
|
dom_update_stack,
|
|
182
|
-
|
|
187
|
+
stacks_top,
|
|
183
188
|
triggered_propagators,
|
|
184
189
|
compute_domains_addrs,
|
|
185
190
|
)
|
|
@@ -32,17 +32,27 @@ if __name__ == "__main__":
|
|
|
32
32
|
parser.add_argument("--ff", type=bool, action=argparse.BooleanOptionalAction, default=False)
|
|
33
33
|
args = parser.parse_args()
|
|
34
34
|
problem = QueensProblem(args.n)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
solver = (
|
|
36
|
+
MultiprocessingSolver(
|
|
37
|
+
[
|
|
38
|
+
BacktrackSolver(
|
|
39
|
+
problem,
|
|
40
|
+
consistency_alg_idx=CONSISTENCY_ALG_SHAVING if args.shaving else CONSISTENCY_ALG_BC,
|
|
41
|
+
var_heuristic_idx=(
|
|
42
|
+
VAR_HEURISTIC_SMALLEST_DOMAIN if args.ff else VAR_HEURISTIC_FIRST_NOT_INSTANTIATED
|
|
43
|
+
),
|
|
44
|
+
log_level=args.log_level,
|
|
45
|
+
)
|
|
46
|
+
for problem in problem.split(args.processors, 0)
|
|
47
|
+
]
|
|
48
|
+
)
|
|
49
|
+
if args.processors > 1
|
|
50
|
+
else BacktrackSolver(
|
|
51
|
+
problem,
|
|
52
|
+
consistency_alg_idx=CONSISTENCY_ALG_SHAVING if args.shaving else CONSISTENCY_ALG_BC,
|
|
53
|
+
var_heuristic_idx=VAR_HEURISTIC_SMALLEST_DOMAIN if args.ff else VAR_HEURISTIC_FIRST_NOT_INSTANTIATED,
|
|
54
|
+
log_level=args.log_level,
|
|
55
|
+
)
|
|
46
56
|
)
|
|
47
57
|
solver.solve_all()
|
|
48
58
|
print(solver.get_statistics())
|
|
@@ -141,13 +141,15 @@ def pop_propagator(triggered_propagators: NDArray, previous_prop_idx: int) -> in
|
|
|
141
141
|
@njit(cache=True)
|
|
142
142
|
def add_propagators(
|
|
143
143
|
triggered_propagators: NDArray,
|
|
144
|
-
|
|
144
|
+
not_entailed_propagators_stack: NDArray,
|
|
145
|
+
stacks_top: NDArray,
|
|
145
146
|
shr_domains_propagators: NDArray,
|
|
146
147
|
dom_idx: int,
|
|
147
148
|
bound: int,
|
|
148
149
|
) -> None:
|
|
150
|
+
cp_top_idx = stacks_top[0]
|
|
149
151
|
for prop_idx in range(len(triggered_propagators)):
|
|
150
|
-
if shr_domains_propagators[dom_idx, bound, prop_idx] and
|
|
152
|
+
if shr_domains_propagators[dom_idx, bound, prop_idx] and not_entailed_propagators_stack[cp_top_idx, prop_idx]:
|
|
151
153
|
triggered_propagators[prop_idx] = True
|
|
152
154
|
# np.logical_or(triggered_propagators, shr_domains_propagators[dom_idx, bound], triggered_propagators)
|
|
153
155
|
# np.logical_and(triggered_propagators, not_entailed_propagators, triggered_propagators)
|
|
@@ -19,7 +19,6 @@ from numba import njit # type: ignore
|
|
|
19
19
|
from numpy.typing import NDArray
|
|
20
20
|
|
|
21
21
|
from nucs.constants import (
|
|
22
|
-
LOG_FORMAT,
|
|
23
22
|
LOG_LEVEL_INFO,
|
|
24
23
|
NUMBA_DISABLE_JIT,
|
|
25
24
|
PROBLEM_BOUND,
|
|
@@ -96,10 +95,9 @@ class BacktrackSolver(Solver):
|
|
|
96
95
|
:param var_heuristic_idx: the index of the heuristic for selecting a variable/domain
|
|
97
96
|
:param dom_heuristic_idx: the index of the heuristic for reducing a domain
|
|
98
97
|
:param stack_max_height: the maximal choice point stack height
|
|
98
|
+
:param log_level: the log level as a string
|
|
99
99
|
"""
|
|
100
|
-
|
|
101
|
-
logger.debug("Initializing BacktrackSolver")
|
|
102
|
-
super().__init__(problem)
|
|
100
|
+
super().__init__(problem, log_level)
|
|
103
101
|
logger.info(f"BacktrackSolver uses variable heuristic {var_heuristic_idx}")
|
|
104
102
|
self.var_heuristic_idx = var_heuristic_idx
|
|
105
103
|
logger.info(f"BacktrackSolver uses domain heuristic {dom_heuristic_idx}")
|
|
@@ -111,13 +109,13 @@ class BacktrackSolver(Solver):
|
|
|
111
109
|
self.shr_domains_stack = np.empty((stack_max_height, self.problem.variable_nb, 2), dtype=np.int32)
|
|
112
110
|
self.not_entailed_propagators_stack = np.empty((stack_max_height, self.problem.propagator_nb), dtype=np.bool)
|
|
113
111
|
self.dom_update_stack = np.empty((stack_max_height, 2), dtype=np.uint16)
|
|
114
|
-
self.
|
|
112
|
+
self.stacks_top = np.ones((1,), dtype=np.uint8)
|
|
115
113
|
logger.info(f"Choice points stack has a maximal height of {stack_max_height}")
|
|
116
114
|
cp_init(
|
|
117
115
|
self.shr_domains_stack,
|
|
118
116
|
self.not_entailed_propagators_stack,
|
|
119
117
|
self.dom_update_stack,
|
|
120
|
-
self.
|
|
118
|
+
self.stacks_top,
|
|
121
119
|
np.array(problem.shr_domains_lst),
|
|
122
120
|
)
|
|
123
121
|
logger.debug("Choice points initialized")
|
|
@@ -168,6 +166,7 @@ class BacktrackSolver(Solver):
|
|
|
168
166
|
:param update_domain_fct: the function to update the domain of the variable
|
|
169
167
|
:return: the solution if it exists or None
|
|
170
168
|
"""
|
|
169
|
+
logger.debug(f"Optimizing variable {variable_idx}")
|
|
171
170
|
compute_domains_addrs, var_heuristic_addrs, dom_heuristic_addrs, consistency_alg_addrs = (
|
|
172
171
|
get_function_addresses()
|
|
173
172
|
)
|
|
@@ -187,7 +186,7 @@ class BacktrackSolver(Solver):
|
|
|
187
186
|
self.shr_domains_stack,
|
|
188
187
|
self.not_entailed_propagators_stack,
|
|
189
188
|
self.dom_update_stack,
|
|
190
|
-
self.
|
|
189
|
+
self.stacks_top,
|
|
191
190
|
self.triggered_propagators,
|
|
192
191
|
self.consistency_alg_idx,
|
|
193
192
|
self.var_heuristic_idx,
|
|
@@ -205,11 +204,12 @@ class BacktrackSolver(Solver):
|
|
|
205
204
|
self.shr_domains_stack,
|
|
206
205
|
self.not_entailed_propagators_stack,
|
|
207
206
|
self.dom_update_stack,
|
|
208
|
-
self.
|
|
207
|
+
self.stacks_top,
|
|
209
208
|
self.triggered_propagators,
|
|
210
209
|
)
|
|
211
210
|
update_domain_fct(
|
|
212
|
-
self.shr_domains_stack
|
|
211
|
+
self.shr_domains_stack,
|
|
212
|
+
self.stacks_top,
|
|
213
213
|
self.problem.dom_indices_arr,
|
|
214
214
|
self.problem.dom_offsets_arr,
|
|
215
215
|
variable_idx,
|
|
@@ -222,6 +222,7 @@ class BacktrackSolver(Solver):
|
|
|
222
222
|
Returns an iterator over the solutions.
|
|
223
223
|
:return: an iterator
|
|
224
224
|
"""
|
|
225
|
+
logger.info("Solving and iterating over the solutions")
|
|
225
226
|
compute_domains_addrs, var_heuristic_addrs, dom_heuristic_addrs, consistency_alg_addrs = (
|
|
226
227
|
get_function_addresses()
|
|
227
228
|
)
|
|
@@ -240,7 +241,7 @@ class BacktrackSolver(Solver):
|
|
|
240
241
|
self.shr_domains_stack,
|
|
241
242
|
self.not_entailed_propagators_stack,
|
|
242
243
|
self.dom_update_stack,
|
|
243
|
-
self.
|
|
244
|
+
self.stacks_top,
|
|
244
245
|
self.triggered_propagators,
|
|
245
246
|
self.consistency_alg_idx,
|
|
246
247
|
self.var_heuristic_idx,
|
|
@@ -255,10 +256,9 @@ class BacktrackSolver(Solver):
|
|
|
255
256
|
yield solution
|
|
256
257
|
if not backtrack(
|
|
257
258
|
self.statistics,
|
|
258
|
-
self.shr_domains_stack,
|
|
259
259
|
self.not_entailed_propagators_stack,
|
|
260
260
|
self.dom_update_stack,
|
|
261
|
-
self.
|
|
261
|
+
self.stacks_top,
|
|
262
262
|
self.triggered_propagators,
|
|
263
263
|
self.problem.shr_domains_propagators,
|
|
264
264
|
):
|
|
@@ -291,8 +291,10 @@ class BacktrackSolver(Solver):
|
|
|
291
291
|
Enqueues the solution that optimizes a variable.
|
|
292
292
|
:param variable_idx: the index of the variable
|
|
293
293
|
:param update_domain_fct: the function to update the domain of the variable
|
|
294
|
+
:param processor_idx: the index of the processor
|
|
294
295
|
:param solution_queue: the solution queue
|
|
295
296
|
"""
|
|
297
|
+
logger.debug(f"Optimizing variable {variable_idx} and queuing solutions found")
|
|
296
298
|
compute_domains_addrs, var_heuristic_addrs, dom_heuristic_addrs, consistency_alg_addrs = (
|
|
297
299
|
get_function_addresses()
|
|
298
300
|
)
|
|
@@ -311,7 +313,7 @@ class BacktrackSolver(Solver):
|
|
|
311
313
|
self.shr_domains_stack,
|
|
312
314
|
self.not_entailed_propagators_stack,
|
|
313
315
|
self.dom_update_stack,
|
|
314
|
-
self.
|
|
316
|
+
self.stacks_top,
|
|
315
317
|
self.triggered_propagators,
|
|
316
318
|
self.consistency_alg_idx,
|
|
317
319
|
self.var_heuristic_idx,
|
|
@@ -330,11 +332,12 @@ class BacktrackSolver(Solver):
|
|
|
330
332
|
self.shr_domains_stack,
|
|
331
333
|
self.not_entailed_propagators_stack,
|
|
332
334
|
self.dom_update_stack,
|
|
333
|
-
self.
|
|
335
|
+
self.stacks_top,
|
|
334
336
|
self.triggered_propagators,
|
|
335
337
|
)
|
|
336
338
|
update_domain_fct(
|
|
337
|
-
self.shr_domains_stack
|
|
339
|
+
self.shr_domains_stack,
|
|
340
|
+
self.stacks_top,
|
|
338
341
|
self.problem.dom_indices_arr,
|
|
339
342
|
self.problem.dom_offsets_arr,
|
|
340
343
|
variable_idx,
|
|
@@ -348,6 +351,7 @@ class BacktrackSolver(Solver):
|
|
|
348
351
|
:param processor_idx: the index of the processor
|
|
349
352
|
:param solution_queue: the solution queue
|
|
350
353
|
"""
|
|
354
|
+
logger.info("Solving and queuing solutions found")
|
|
351
355
|
compute_domains_addrs, var_heuristic_addrs, dom_heuristic_addrs, consistency_alg_addrs = (
|
|
352
356
|
get_function_addresses()
|
|
353
357
|
)
|
|
@@ -366,7 +370,7 @@ class BacktrackSolver(Solver):
|
|
|
366
370
|
self.shr_domains_stack,
|
|
367
371
|
self.not_entailed_propagators_stack,
|
|
368
372
|
self.dom_update_stack,
|
|
369
|
-
self.
|
|
373
|
+
self.stacks_top,
|
|
370
374
|
self.triggered_propagators,
|
|
371
375
|
self.consistency_alg_idx,
|
|
372
376
|
self.var_heuristic_idx,
|
|
@@ -381,10 +385,9 @@ class BacktrackSolver(Solver):
|
|
|
381
385
|
solution_queue.put((processor_idx, solution, self.statistics))
|
|
382
386
|
if not backtrack(
|
|
383
387
|
self.statistics,
|
|
384
|
-
self.shr_domains_stack,
|
|
385
388
|
self.not_entailed_propagators_stack,
|
|
386
389
|
self.dom_update_stack,
|
|
387
|
-
self.
|
|
390
|
+
self.stacks_top,
|
|
388
391
|
self.triggered_propagators,
|
|
389
392
|
self.problem.shr_domains_propagators,
|
|
390
393
|
):
|
|
@@ -397,7 +400,7 @@ def reset(
|
|
|
397
400
|
shr_domains_stack: NDArray,
|
|
398
401
|
not_entailed_propagators_stack: NDArray,
|
|
399
402
|
dom_update_stack: NDArray,
|
|
400
|
-
|
|
403
|
+
stacks_top: NDArray,
|
|
401
404
|
triggered_propagators: NDArray,
|
|
402
405
|
) -> None:
|
|
403
406
|
"""
|
|
@@ -405,14 +408,15 @@ def reset(
|
|
|
405
408
|
:param problem: the problem
|
|
406
409
|
:param shr_domains_stack: the stack of shared domains
|
|
407
410
|
:param not_entailed_propagators_stack: the stack of not entailed propagators
|
|
408
|
-
:param
|
|
411
|
+
:param dom_update_stack: the stack of domain updates
|
|
412
|
+
:param stacks_top: the index of the top of the stacks as a Numpy array
|
|
409
413
|
:param triggered_propagators: the list of triggered propagators
|
|
410
414
|
"""
|
|
411
415
|
cp_init(
|
|
412
416
|
shr_domains_stack,
|
|
413
417
|
not_entailed_propagators_stack,
|
|
414
418
|
dom_update_stack,
|
|
415
|
-
|
|
419
|
+
stacks_top,
|
|
416
420
|
np.array(problem.shr_domains_lst),
|
|
417
421
|
)
|
|
418
422
|
triggered_propagators.fill(True)
|
|
@@ -433,7 +437,7 @@ def solve_one(
|
|
|
433
437
|
shr_domains_stack: NDArray,
|
|
434
438
|
not_entailed_propagators_stack: NDArray,
|
|
435
439
|
dom_update_stack: NDArray,
|
|
436
|
-
|
|
440
|
+
stacks_top: NDArray,
|
|
437
441
|
triggered_propagators: NDArray,
|
|
438
442
|
consistency_alg_idx: int,
|
|
439
443
|
var_heuristic_idx: int,
|
|
@@ -461,7 +465,8 @@ def solve_one(
|
|
|
461
465
|
the first level correspond to the current shared domains, the rest correspond to the choice points
|
|
462
466
|
:param not_entailed_propagators_stack: a stack not entailed propagators;
|
|
463
467
|
the first level correspond to the propagators currently not entailed, the rest correspond to the choice points
|
|
464
|
-
:param
|
|
468
|
+
:param dom_update_stack: the stack of domain updates
|
|
469
|
+
:param stacks_top: the index of the top of the stacks as a Numpy array
|
|
465
470
|
:param triggered_propagators: the Numpy array of triggered propagators
|
|
466
471
|
:param consistency_alg_idx: the index of the consistency algorithm
|
|
467
472
|
:param var_heuristic_idx: the index of the variable heuristic
|
|
@@ -495,35 +500,33 @@ def solve_one(
|
|
|
495
500
|
shr_domains_stack,
|
|
496
501
|
not_entailed_propagators_stack,
|
|
497
502
|
dom_update_stack,
|
|
498
|
-
|
|
503
|
+
stacks_top,
|
|
499
504
|
triggered_propagators,
|
|
500
505
|
compute_domains_addrs,
|
|
501
506
|
)
|
|
502
507
|
if status == PROBLEM_BOUND:
|
|
503
508
|
statistics[STATS_IDX_SOLVER_SOLUTION_NB] += 1
|
|
504
|
-
return get_solution(shr_domains_stack
|
|
509
|
+
return get_solution(shr_domains_stack, stacks_top, dom_indices_arr, dom_offsets_arr)
|
|
505
510
|
elif status == PROBLEM_UNBOUND:
|
|
506
|
-
dom_idx = var_heuristic_fct(shr_domains_stack
|
|
507
|
-
cp_put(shr_domains_stack, not_entailed_propagators_stack, dom_update_stack,
|
|
508
|
-
|
|
509
|
-
event = dom_heuristic_fct(shr_domains_stack[0, dom_idx], shr_domains_stack[cp_idx, dom_idx])
|
|
510
|
-
dom_update_stack[cp_idx, 1] = event
|
|
511
|
+
dom_idx = var_heuristic_fct(shr_domains_stack, stacks_top)
|
|
512
|
+
cp_put(shr_domains_stack, not_entailed_propagators_stack, dom_update_stack, stacks_top, dom_idx)
|
|
513
|
+
event = dom_heuristic_fct(shr_domains_stack, dom_update_stack, stacks_top, dom_idx)
|
|
511
514
|
add_propagators(
|
|
512
515
|
triggered_propagators,
|
|
513
|
-
not_entailed_propagators_stack
|
|
516
|
+
not_entailed_propagators_stack,
|
|
517
|
+
stacks_top,
|
|
514
518
|
shr_domains_propagators,
|
|
515
519
|
dom_idx,
|
|
516
520
|
event,
|
|
517
521
|
)
|
|
518
522
|
statistics[STATS_IDX_SOLVER_CHOICE_NB] += 1
|
|
519
|
-
if
|
|
520
|
-
statistics[STATS_IDX_SOLVER_CHOICE_DEPTH] =
|
|
523
|
+
if stacks_top[0] > statistics[STATS_IDX_SOLVER_CHOICE_DEPTH]:
|
|
524
|
+
statistics[STATS_IDX_SOLVER_CHOICE_DEPTH] = stacks_top[0]
|
|
521
525
|
elif not backtrack(
|
|
522
526
|
statistics,
|
|
523
|
-
shr_domains_stack,
|
|
524
527
|
not_entailed_propagators_stack,
|
|
525
528
|
dom_update_stack,
|
|
526
|
-
|
|
529
|
+
stacks_top,
|
|
527
530
|
triggered_propagators,
|
|
528
531
|
shr_domains_propagators,
|
|
529
532
|
):
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
#
|
|
11
11
|
# Copyright 2024 - Yan Georget
|
|
12
12
|
###############################################################################
|
|
13
|
-
import numpy as np
|
|
14
13
|
from numba import njit # type: ignore
|
|
15
14
|
from numpy.typing import NDArray
|
|
16
15
|
|
|
@@ -52,7 +51,7 @@ def bound_consistency_algorithm(
|
|
|
52
51
|
shr_domains_stack: NDArray,
|
|
53
52
|
not_entailed_propagators_stack: NDArray,
|
|
54
53
|
dom_update_stack: NDArray,
|
|
55
|
-
|
|
54
|
+
stacks_top: NDArray,
|
|
56
55
|
triggered_propagators: NDArray,
|
|
57
56
|
compute_domains_addrs: NDArray,
|
|
58
57
|
) -> int:
|
|
@@ -74,7 +73,8 @@ def bound_consistency_algorithm(
|
|
|
74
73
|
the first level correspond to the current shared domains, the rest correspond to the choice points
|
|
75
74
|
:param not_entailed_propagators_stack: a stack not entailed propagators;
|
|
76
75
|
the first level correspond to the propagators currently not entailed, the rest correspond to the choice points
|
|
77
|
-
:param
|
|
76
|
+
:param dom_update_stack: the stack of domain updates
|
|
77
|
+
:param stacks_top: the height of the stacks as a Numpy array
|
|
78
78
|
:param triggered_propagators: the Numpy array of triggered propagators
|
|
79
79
|
:param compute_domains_addrs: the addresses of the compute_domains functions
|
|
80
80
|
:return: a status (consistency, inconsistency or entailment) as an integer
|
|
@@ -84,41 +84,41 @@ def bound_consistency_algorithm(
|
|
|
84
84
|
while True:
|
|
85
85
|
prop_idx = pop_propagator(triggered_propagators, prop_idx)
|
|
86
86
|
if prop_idx == -1:
|
|
87
|
-
return PROBLEM_BOUND if is_solved(shr_domains_stack
|
|
87
|
+
return PROBLEM_BOUND if is_solved(shr_domains_stack, stacks_top) else PROBLEM_UNBOUND
|
|
88
88
|
statistics[STATS_IDX_PROPAGATOR_FILTER_NB] += 1
|
|
89
89
|
prop_var_start = var_bounds[prop_idx, START]
|
|
90
90
|
prop_var_end = var_bounds[prop_idx, END]
|
|
91
91
|
prop_indices = props_dom_indices[prop_var_start:prop_var_end]
|
|
92
92
|
prop_offsets = props_dom_offsets[prop_var_start:prop_var_end]
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
np.add(shr_domains_stack[0, prop_indices], prop_offsets, prop_domains)
|
|
96
|
-
compute_domains_function = (
|
|
93
|
+
prop_domains = shr_domains_stack[stacks_top[0], prop_indices] + prop_offsets
|
|
94
|
+
compute_domains_fct = (
|
|
97
95
|
COMPUTE_DOMAINS_FCTS[algorithms[prop_idx]]
|
|
98
96
|
if NUMBA_DISABLE_JIT
|
|
99
97
|
else function_from_address(TYPE_COMPUTE_DOMAINS, compute_domains_addrs[algorithms[prop_idx]])
|
|
100
98
|
)
|
|
101
|
-
status =
|
|
99
|
+
status = compute_domains_fct(
|
|
102
100
|
prop_domains, props_parameters[param_bounds[prop_idx, START] : param_bounds[prop_idx, END]]
|
|
103
101
|
)
|
|
104
102
|
if status == PROP_INCONSISTENCY:
|
|
105
103
|
statistics[STATS_IDX_PROPAGATOR_INCONSISTENCY_NB] += 1
|
|
106
104
|
return PROBLEM_INCONSISTENT
|
|
107
105
|
if status == PROP_ENTAILMENT:
|
|
108
|
-
not_entailed_propagators_stack[0, prop_idx] = False
|
|
106
|
+
not_entailed_propagators_stack[stacks_top[0], prop_idx] = False
|
|
109
107
|
statistics[STATS_IDX_PROPAGATOR_ENTAILMENT_NB] += 1
|
|
110
108
|
shr_domains_changes = False
|
|
111
|
-
|
|
109
|
+
top = stacks_top[0]
|
|
110
|
+
for var_idx in range(prop_var_end - prop_var_start):
|
|
112
111
|
shr_domain_idx = prop_indices[var_idx]
|
|
113
112
|
prop_offset = prop_offsets[var_idx, 0] # because of vertical shape
|
|
114
113
|
for bound in [MIN, MAX]:
|
|
115
114
|
shr_domain_bound = prop_domains[var_idx, bound] - prop_offset
|
|
116
|
-
if shr_domains_stack[
|
|
117
|
-
shr_domains_stack[
|
|
115
|
+
if shr_domains_stack[top, shr_domain_idx, bound] != shr_domain_bound:
|
|
116
|
+
shr_domains_stack[top, shr_domain_idx, bound] = shr_domain_bound
|
|
118
117
|
shr_domains_changes = True
|
|
119
118
|
add_propagators(
|
|
120
119
|
triggered_propagators,
|
|
121
|
-
not_entailed_propagators_stack
|
|
120
|
+
not_entailed_propagators_stack,
|
|
121
|
+
stacks_top,
|
|
122
122
|
shr_domains_propagators,
|
|
123
123
|
shr_domain_idx,
|
|
124
124
|
bound,
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from numba import njit # type: ignore
|
|
4
|
+
from numpy.typing import NDArray
|
|
5
|
+
|
|
6
|
+
from nucs.constants import STATS_IDX_SOLVER_BACKTRACK_NB
|
|
7
|
+
from nucs.propagators.propagators import add_propagators
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@njit(cache=True)
|
|
11
|
+
def cp_init(
|
|
12
|
+
shr_domains_stack: NDArray,
|
|
13
|
+
not_entailed_propagators_stack: NDArray,
|
|
14
|
+
dom_update_stack: NDArray,
|
|
15
|
+
stacks_top: NDArray,
|
|
16
|
+
shr_domains_arr: NDArray,
|
|
17
|
+
) -> None:
|
|
18
|
+
"""
|
|
19
|
+
Inits the choice points.
|
|
20
|
+
:param shr_domains_stack: the stack of shared domains
|
|
21
|
+
:param not_entailed_propagators_stack: the stack of not entailed propagators
|
|
22
|
+
:param stacks_top: the index of the top of the stacks as a Numpy array
|
|
23
|
+
:param shr_domains_arr: the shared domains
|
|
24
|
+
:return: a Numpy array
|
|
25
|
+
"""
|
|
26
|
+
shr_domains_stack[0] = shr_domains_arr
|
|
27
|
+
not_entailed_propagators_stack[0] = True
|
|
28
|
+
dom_update_stack[0, :] = 0
|
|
29
|
+
stacks_top[0] = 0
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@njit(cache=True)
|
|
33
|
+
def cp_put(
|
|
34
|
+
shr_domains_stack: NDArray,
|
|
35
|
+
not_entailed_propagators_stack: NDArray,
|
|
36
|
+
dom_update_stack: NDArray,
|
|
37
|
+
stacks_top: NDArray,
|
|
38
|
+
dom_index: int,
|
|
39
|
+
bound: Optional[int] = 0,
|
|
40
|
+
) -> None:
|
|
41
|
+
"""
|
|
42
|
+
Adds a choice point to the stack of choice points.
|
|
43
|
+
:param shr_domains_stack: the stack of shared domains
|
|
44
|
+
:param not_entailed_propagators_stack: the stack of not entailed propagators
|
|
45
|
+
:param dom_update_stack: the stack of domain updates
|
|
46
|
+
:param stacks_top: the index of the top of the stacks as a Numpy array
|
|
47
|
+
:param dom_index: the index of the shared domain which is changed
|
|
48
|
+
:param bound: the bound (MIN or MAX) which is changed
|
|
49
|
+
"""
|
|
50
|
+
dom_update_stack[stacks_top[0], 0] = dom_index
|
|
51
|
+
dom_update_stack[stacks_top[0], 1] = bound
|
|
52
|
+
shr_domains_stack[stacks_top[0] + 1, :, :] = shr_domains_stack[stacks_top[0], :, :]
|
|
53
|
+
not_entailed_propagators_stack[stacks_top[0] + 1, :] = not_entailed_propagators_stack[stacks_top[0], :]
|
|
54
|
+
stacks_top[0] += 1
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@njit(cache=True)
|
|
58
|
+
def backtrack(
|
|
59
|
+
statistics: NDArray,
|
|
60
|
+
not_entailed_propagators_stack: NDArray,
|
|
61
|
+
dom_update_stack: NDArray,
|
|
62
|
+
stacks_top: NDArray,
|
|
63
|
+
triggered_propagators: NDArray,
|
|
64
|
+
shr_domains_propagators: NDArray,
|
|
65
|
+
) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
Backtracks and updates the problem's domains.
|
|
68
|
+
:param statistics: the statistics array
|
|
69
|
+
:param not_entailed_propagators_stack: the stack of not entailed propagators
|
|
70
|
+
:param dom_update_stack: the stack of domain updates
|
|
71
|
+
:param stacks_top: the index of the top of the stacks as a Numpy array
|
|
72
|
+
:param triggered_propagators: the set propagators that are currently triggered as a Numpy array
|
|
73
|
+
:param shr_domains_propagators: a Numpy array indicating which propagators to trigger for a domain change
|
|
74
|
+
:return: true iff it is possible to backtrack
|
|
75
|
+
"""
|
|
76
|
+
if stacks_top[0] == 0:
|
|
77
|
+
return False
|
|
78
|
+
stacks_top[0] -= 1
|
|
79
|
+
statistics[STATS_IDX_SOLVER_BACKTRACK_NB] += 1
|
|
80
|
+
add_propagators(
|
|
81
|
+
triggered_propagators,
|
|
82
|
+
not_entailed_propagators_stack,
|
|
83
|
+
stacks_top,
|
|
84
|
+
shr_domains_propagators,
|
|
85
|
+
dom_update_stack[stacks_top[0], 0],
|
|
86
|
+
1 - dom_update_stack[stacks_top[0], 1],
|
|
87
|
+
)
|
|
88
|
+
return True
|