NUCS 4.0.0__tar.gz → 4.3.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-4.0.0 → nucs-4.3.0/NUCS.egg-info}/PKG-INFO +1 -1
- {nucs-4.0.0 → nucs-4.3.0}/NUCS.egg-info/SOURCES.txt +5 -0
- {nucs-4.0.0/NUCS.egg-info → nucs-4.3.0}/PKG-INFO +1 -1
- {nucs-4.0.0 → nucs-4.3.0}/docs/source/conf.py +2 -2
- {nucs-4.0.0 → nucs-4.3.0}/nucs/constants.py +6 -2
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/alpha/alpha_problem.py +3 -2
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/bibd/__main__.py +4 -2
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/donald/donald_problem.py +4 -3
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/golomb/__main__.py +20 -4
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/golomb/golomb_problem.py +14 -22
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/knapsack/__main__.py +3 -2
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/magic_sequence/__main__.py +2 -1
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/magic_square/__main__.py +2 -2
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/queens/queens_problem.py +2 -1
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/schur_lemma/__main__.py +2 -2
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/sports_tournament_scheduling/sports_tournament_scheduling_problem.py +1 -1
- nucs-4.3.0/nucs/examples/tsp/__main__.py +69 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/tsp/total_cost_propagator.py +17 -6
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/tsp/tsp_problem.py +14 -11
- nucs-4.3.0/nucs/examples/tsp/tsp_var_heuristic.py +52 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/first_not_instantiated_var_heuristic.py +4 -4
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/greatest_domain_var_heuristic.py +11 -11
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/heuristics.py +5 -3
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/max_regret_var_heuristic.py +23 -20
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/max_value_dom_heuristic.py +9 -9
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/min_cost_dom_heuristic.py +5 -5
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/min_value_dom_heuristic.py +9 -9
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/smallest_domain_var_heuristic.py +11 -11
- nucs-4.3.0/nucs/heuristics/split_high_dom_heuristic.py +58 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/split_low_dom_heuristic.py +9 -9
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/value_dom_heuristic.py +15 -15
- {nucs-4.0.0 → nucs-4.3.0}/nucs/numba_helper.py +2 -2
- nucs-4.3.0/nucs/problems/circuit_problem.py +36 -0
- nucs-4.0.0/nucs/problems/circuit_problem.py → nucs-4.3.0/nucs/problems/permutation_problem.py +11 -10
- {nucs-4.0.0 → nucs-4.3.0}/nucs/problems/problem.py +1 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/affine_eq_propagator.py +1 -1
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/affine_geq_propagator.py +1 -1
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/affine_leq_propagator.py +1 -1
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/element_iv_propagator.py +3 -3
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/gcc_propagator.py +9 -9
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/no_sub_cycle_propagator.py +21 -18
- nucs-4.3.0/nucs/propagators/permutation_aux_propagator.py +65 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/propagators.py +11 -4
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/scc_propagator.py +1 -1
- {nucs-4.0.0 → nucs-4.3.0}/nucs/solvers/backtrack_solver.py +101 -86
- {nucs-4.0.0 → nucs-4.3.0}/nucs/solvers/bound_consistency_algorithm.py +3 -3
- {nucs-4.0.0 → nucs-4.3.0}/nucs/solvers/choice_points.py +61 -3
- {nucs-4.0.0 → nucs-4.3.0}/nucs/solvers/multiprocessing_solver.py +12 -9
- {nucs-4.0.0 → nucs-4.3.0}/nucs/solvers/shaving_consistency_algorithm.py +5 -5
- {nucs-4.0.0 → nucs-4.3.0}/nucs/solvers/solver.py +3 -45
- {nucs-4.0.0 → nucs-4.3.0}/pyproject.toml +1 -1
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_bibd.py +3 -3
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_golomb.py +2 -7
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_magic_sequence.py +1 -0
- nucs-4.3.0/tests/examples/test_tsp.py +59 -0
- nucs-4.3.0/tests/problems/test_permutation_problem.py +35 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_no_sub_cycle.py +7 -2
- {nucs-4.0.0 → nucs-4.3.0}/tests/solvers/test_backtrack_solver.py +31 -1
- {nucs-4.0.0 → nucs-4.3.0}/tests/solvers/test_multiprocessing_solver.py +28 -23
- nucs-4.0.0/nucs/examples/tsp/__main__.py +0 -43
- nucs-4.0.0/tests/examples/test_tsp.py +0 -47
- {nucs-4.0.0 → nucs-4.3.0}/LICENSE.md +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/NUCS.egg-info/dependency_links.txt +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/NUCS.egg-info/requires.txt +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/NUCS.egg-info/top_level.txt +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/README.md +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/alpha/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/alpha/__main__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/bibd/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/bibd/bibd_problem.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/donald/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/donald/__main__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/golomb/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/knapsack/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/knapsack/knapsack_problem.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/magic_sequence/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/magic_sequence/magic_sequence_problem.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/magic_square/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/magic_square/magic_square_problem.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/quasigroup/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/quasigroup/__main__.py +1 -1
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/quasigroup/quasigroup_problem.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/queens/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/queens/__main__.py +2 -2
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/schur_lemma/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/schur_lemma/schur_lemma_problem.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/sports_tournament_scheduling/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/sports_tournament_scheduling/__main__.py +1 -1
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/sudoku/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/sudoku/sudoku_problem.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/tsp/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/examples/tsp/tsp_instances.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/heuristics/mid_value_dom_heuristic.py +3 -3
- {nucs-4.0.0 → nucs-4.3.0}/nucs/numpy_helper.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/problems/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/problems/latin_square_problem.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/alldifferent_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/and_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/count_eq_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/dummy_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/element_lic_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/element_liv_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/exactly_eq_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/exactly_true_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/lexicographic_leq_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/max_eq_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/max_leq_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/min_eq_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/min_geq_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/propagators/relation_propagator.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/solvers/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/nucs/solvers/consistency_algorithms.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/setup.cfg +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_alpha.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_donald.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_knapsack.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_latin_square.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_magic_square.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_quasigroup.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_queens.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_schur_lemma.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_sports_tournament_scheduling.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/examples/test_sudokus.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/heuristics/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/heuristics/test_max_value_dom_heuristic.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/heuristics/test_mid_value_dom_heuristic.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/heuristics/test_min_value_dom_heuristic.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/problems/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/problems/test_circuit_problem.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/problems/test_problem.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/__init__.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_affine_eq.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_affine_geq.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_affine_leq.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_alldifferent.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_count_eq.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_element_iv.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_element_lic.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_element_liv.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_exactly_eq.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_gcc.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_lexicographic_leq.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_max_eq.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_max_leq.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_min_eq.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_min_geq.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_relation.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/propagators/test_scc.py +0 -0
- {nucs-4.0.0 → nucs-4.3.0}/tests/solvers/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: NUCS
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.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
|
|
@@ -52,6 +52,7 @@ nucs/examples/tsp/__main__.py
|
|
|
52
52
|
nucs/examples/tsp/total_cost_propagator.py
|
|
53
53
|
nucs/examples/tsp/tsp_instances.py
|
|
54
54
|
nucs/examples/tsp/tsp_problem.py
|
|
55
|
+
nucs/examples/tsp/tsp_var_heuristic.py
|
|
55
56
|
nucs/heuristics/__init__.py
|
|
56
57
|
nucs/heuristics/first_not_instantiated_var_heuristic.py
|
|
57
58
|
nucs/heuristics/greatest_domain_var_heuristic.py
|
|
@@ -62,11 +63,13 @@ nucs/heuristics/mid_value_dom_heuristic.py
|
|
|
62
63
|
nucs/heuristics/min_cost_dom_heuristic.py
|
|
63
64
|
nucs/heuristics/min_value_dom_heuristic.py
|
|
64
65
|
nucs/heuristics/smallest_domain_var_heuristic.py
|
|
66
|
+
nucs/heuristics/split_high_dom_heuristic.py
|
|
65
67
|
nucs/heuristics/split_low_dom_heuristic.py
|
|
66
68
|
nucs/heuristics/value_dom_heuristic.py
|
|
67
69
|
nucs/problems/__init__.py
|
|
68
70
|
nucs/problems/circuit_problem.py
|
|
69
71
|
nucs/problems/latin_square_problem.py
|
|
72
|
+
nucs/problems/permutation_problem.py
|
|
70
73
|
nucs/problems/problem.py
|
|
71
74
|
nucs/propagators/__init__.py
|
|
72
75
|
nucs/propagators/affine_eq_propagator.py
|
|
@@ -88,6 +91,7 @@ nucs/propagators/max_leq_propagator.py
|
|
|
88
91
|
nucs/propagators/min_eq_propagator.py
|
|
89
92
|
nucs/propagators/min_geq_propagator.py
|
|
90
93
|
nucs/propagators/no_sub_cycle_propagator.py
|
|
94
|
+
nucs/propagators/permutation_aux_propagator.py
|
|
91
95
|
nucs/propagators/propagators.py
|
|
92
96
|
nucs/propagators/relation_propagator.py
|
|
93
97
|
nucs/propagators/scc_propagator.py
|
|
@@ -120,6 +124,7 @@ tests/heuristics/test_mid_value_dom_heuristic.py
|
|
|
120
124
|
tests/heuristics/test_min_value_dom_heuristic.py
|
|
121
125
|
tests/problems/__init__.py
|
|
122
126
|
tests/problems/test_circuit_problem.py
|
|
127
|
+
tests/problems/test_permutation_problem.py
|
|
123
128
|
tests/problems/test_problem.py
|
|
124
129
|
tests/propagators/__init__.py
|
|
125
130
|
tests/propagators/test_affine_eq.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: NUCS
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.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
|
|
@@ -14,6 +14,10 @@ import os
|
|
|
14
14
|
|
|
15
15
|
from numba import bool, int32, int64, types, uint8, uint16 # type: ignore
|
|
16
16
|
|
|
17
|
+
OPT_RESET = "RESET"
|
|
18
|
+
OPT_PRUNE = "PRUNE"
|
|
19
|
+
OPT_MODES = [OPT_RESET, OPT_PRUNE]
|
|
20
|
+
|
|
17
21
|
RG_START = 0 # index corresponding the start of a values range
|
|
18
22
|
RG_END = 1 # index corresponding the end of a values range
|
|
19
23
|
|
|
@@ -61,18 +65,18 @@ SIGNATURE_CONSISTENCY_ALG = int64(
|
|
|
61
65
|
uint16[:], # decision_domains
|
|
62
66
|
)
|
|
63
67
|
SIGNATURE_DOM_HEURISTIC = int64(
|
|
64
|
-
int64[:, :], # dom_heuristic_params
|
|
65
68
|
int32[:, :, :], # shr_domains_stack
|
|
66
69
|
bool[:, :], # not_entailed_propagators_stack
|
|
67
70
|
uint16[:, :], # dom_update_stack
|
|
68
71
|
uint8[:], # stacks_top
|
|
69
72
|
int64, # dom_idx
|
|
73
|
+
int64[:, :], # dom_heuristic_params
|
|
70
74
|
)
|
|
71
75
|
SIGNATURE_VAR_HEURISTIC = int64(
|
|
72
|
-
int64[:, :], # var_heuristic_params
|
|
73
76
|
uint16[:], # decision_variables
|
|
74
77
|
int32[:, :, :], # shr_domains_stack
|
|
75
78
|
uint8[:], # stacks_top
|
|
79
|
+
int64[:, :], # var_heuristic_params
|
|
76
80
|
)
|
|
77
81
|
|
|
78
82
|
|
|
@@ -24,7 +24,7 @@ class AlphaProblem(Problem):
|
|
|
24
24
|
"""
|
|
25
25
|
The numbers from 1 to 26 are assigned to the letters of the alphabet.
|
|
26
26
|
The numbers beside each word are the total of the values assigned to the letters in the word
|
|
27
|
-
(
|
|
27
|
+
(eg for LYRE: L,Y,R,E might be to equal 5,9,20 and 13 or any other combination that add up to 47).
|
|
28
28
|
|
|
29
29
|
Find the value of each letter under the equations:
|
|
30
30
|
BALLET 45 GLEE 66 POLKA 59 SONG 61
|
|
@@ -67,7 +67,8 @@ class AlphaProblem(Problem):
|
|
|
67
67
|
]
|
|
68
68
|
)
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
@staticmethod
|
|
71
|
+
def solution_as_dict(solution: NDArray) -> Dict[str, int]:
|
|
71
72
|
"""
|
|
72
73
|
Returns the solution as a dict.
|
|
73
74
|
:param solution: the solution as a list of ints
|
|
@@ -28,10 +28,12 @@ if __name__ == "__main__":
|
|
|
28
28
|
parser.add_argument("-r", type=int)
|
|
29
29
|
parser.add_argument("-k", type=int)
|
|
30
30
|
parser.add_argument("-l", type=int)
|
|
31
|
-
parser.add_argument("--symmetry_breaking", action=argparse.BooleanOptionalAction, default=True)
|
|
32
31
|
parser.add_argument("--log_level", choices=LOG_LEVELS, default=LOG_LEVEL_INFO)
|
|
32
|
+
parser.add_argument("--symmetry_breaking", action=argparse.BooleanOptionalAction, default=True)
|
|
33
33
|
args = parser.parse_args()
|
|
34
34
|
problem = BIBDProblem(args.v, args.b, args.r, args.k, args.l, args.symmetry_breaking)
|
|
35
|
-
solver = BacktrackSolver(
|
|
35
|
+
solver = BacktrackSolver(
|
|
36
|
+
problem, dom_heuristic_idx=DOM_HEURISTIC_MAX_VALUE, log_level=args.log_level, stack_max_height=128
|
|
37
|
+
)
|
|
36
38
|
solver.solve_all()
|
|
37
39
|
print(solver.get_statistics())
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
#
|
|
11
11
|
# Copyright 2024 - Yan Georget
|
|
12
12
|
###############################################################################
|
|
13
|
-
from typing import Dict
|
|
13
|
+
from typing import Any, Dict
|
|
14
14
|
|
|
15
|
-
from numpy.
|
|
15
|
+
from numpy.typing import NDArray
|
|
16
16
|
|
|
17
17
|
from nucs.problems.problem import Problem
|
|
18
18
|
from nucs.propagators.propagators import ALG_AFFINE_EQ, ALG_ALLDIFFERENT
|
|
@@ -40,7 +40,8 @@ class DonaldProblem(Problem):
|
|
|
40
40
|
)
|
|
41
41
|
self.add_propagator(([A, B, D, E, G, L, N, O, R, T], ALG_ALLDIFFERENT, []))
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
@staticmethod
|
|
44
|
+
def solution_as_dict(solution: NDArray) -> Dict[str, Any]:
|
|
44
45
|
"""
|
|
45
46
|
Returns the solution as a dict.
|
|
46
47
|
:param solution: the solution as a list of ints
|
|
@@ -14,23 +14,39 @@ import argparse
|
|
|
14
14
|
|
|
15
15
|
from rich import print
|
|
16
16
|
|
|
17
|
-
from nucs.constants import LOG_LEVEL_INFO, LOG_LEVELS
|
|
17
|
+
from nucs.constants import LOG_LEVEL_INFO, LOG_LEVELS, OPT_MODES, OPT_PRUNE
|
|
18
18
|
from nucs.examples.golomb.golomb_problem import GolombProblem, golomb_consistency_algorithm
|
|
19
19
|
from nucs.solvers.backtrack_solver import BacktrackSolver
|
|
20
20
|
from nucs.solvers.consistency_algorithms import register_consistency_algorithm
|
|
21
|
+
from nucs.solvers.multiprocessing_solver import MultiprocessingSolver
|
|
21
22
|
|
|
22
23
|
# Run with the following command (the second run is much faster because the code has been compiled):
|
|
23
24
|
# NUMBA_CACHE_DIR=.numba/cache python -m nucs.examples.golomb -n 10 --symmetry_breaking
|
|
24
25
|
if __name__ == "__main__":
|
|
25
26
|
parser = argparse.ArgumentParser()
|
|
26
27
|
parser.add_argument("-n", type=int, default=10)
|
|
27
|
-
parser.add_argument("--symmetry_breaking", action=argparse.BooleanOptionalAction, default=True)
|
|
28
28
|
parser.add_argument("--log_level", choices=LOG_LEVELS, default=LOG_LEVEL_INFO)
|
|
29
|
+
parser.add_argument("--opt_mode", choices=OPT_MODES, default=OPT_PRUNE)
|
|
30
|
+
parser.add_argument("--processors", type=int, default=1)
|
|
31
|
+
parser.add_argument("--symmetry_breaking", action=argparse.BooleanOptionalAction, default=True)
|
|
29
32
|
args = parser.parse_args()
|
|
30
33
|
problem = GolombProblem(args.n, args.symmetry_breaking)
|
|
31
34
|
consistency_alg_golomb = register_consistency_algorithm(golomb_consistency_algorithm)
|
|
32
|
-
solver =
|
|
33
|
-
|
|
35
|
+
solver = (
|
|
36
|
+
MultiprocessingSolver(
|
|
37
|
+
[
|
|
38
|
+
BacktrackSolver(
|
|
39
|
+
prob, consistency_alg_idx=consistency_alg_golomb, log_level=args.log_level, stack_max_height=128
|
|
40
|
+
)
|
|
41
|
+
for prob in problem.split(args.processors, 0)
|
|
42
|
+
]
|
|
43
|
+
)
|
|
44
|
+
if args.processors > 1
|
|
45
|
+
else BacktrackSolver(
|
|
46
|
+
problem, consistency_alg_idx=consistency_alg_golomb, log_level=args.log_level, stack_max_height=128
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
solution = solver.minimize(problem.length_idx, mode=args.opt_mode)
|
|
34
50
|
print(solver.get_statistics())
|
|
35
51
|
if solution is not None:
|
|
36
52
|
print(solution[: (args.n - 1)])
|
|
@@ -19,14 +19,14 @@ from numpy.typing import NDArray
|
|
|
19
19
|
from nucs.constants import EVENT_MASK_GROUND, EVENT_MASK_MIN, MAX, MIN
|
|
20
20
|
from nucs.heuristics.heuristics import first_not_instantiated_var_heuristic
|
|
21
21
|
from nucs.problems.problem import Problem
|
|
22
|
-
from nucs.propagators.propagators import ALG_AFFINE_EQ, ALG_AFFINE_LEQ, ALG_ALLDIFFERENT,
|
|
22
|
+
from nucs.propagators.propagators import ALG_AFFINE_EQ, ALG_AFFINE_LEQ, ALG_ALLDIFFERENT, update_propagators
|
|
23
23
|
from nucs.solvers.bound_consistency_algorithm import bound_consistency_algorithm
|
|
24
24
|
|
|
25
25
|
GOLOMB_LENGTHS = np.array([0, 0, 1, 3, 6, 11, 17, 25, 34, 44, 55, 72, 85, 106, 127])
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
@njit(cache=True)
|
|
29
|
-
def sum_first(n:
|
|
29
|
+
def sum_first(n: int) -> int:
|
|
30
30
|
"""
|
|
31
31
|
Returns the sum of the first n integers.
|
|
32
32
|
:param n: an integer
|
|
@@ -36,7 +36,7 @@ def sum_first(n: np.uint32) -> np.uint32:
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
@njit(cache=True)
|
|
39
|
-
def index(mark_nb:
|
|
39
|
+
def index(mark_nb: int, i: int, j: int) -> int:
|
|
40
40
|
"""
|
|
41
41
|
Returns the index of the distance variable between two marks.
|
|
42
42
|
:param mark_nb: the total number of marks
|
|
@@ -47,22 +47,6 @@ def index(mark_nb: np.uint32, i: np.uint32, j: np.uint32) -> np.uint32:
|
|
|
47
47
|
return i * mark_nb - sum_first(i) + j - i - 1
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
@njit(cache=True)
|
|
51
|
-
def init_domains(dist_nb: int, mark_nb: int) -> NDArray:
|
|
52
|
-
"""
|
|
53
|
-
Returns the domains.
|
|
54
|
-
:param dist_nb: the number of distances
|
|
55
|
-
:param mark_nb: the number of marks
|
|
56
|
-
:return: a Numpy array of domains
|
|
57
|
-
"""
|
|
58
|
-
domains = np.empty((dist_nb, 2), dtype=np.int32)
|
|
59
|
-
for i in range(0, mark_nb - 1):
|
|
60
|
-
for j in range(i + 1, mark_nb):
|
|
61
|
-
domains[index(mark_nb, i, j), MIN] = GOLOMB_LENGTHS[j - i + 1] if j - i + 1 < mark_nb else sum_first(j - i)
|
|
62
|
-
domains[:, MAX] = sum_first(dist_nb)
|
|
63
|
-
return domains
|
|
64
|
-
|
|
65
|
-
|
|
66
50
|
class GolombProblem(Problem):
|
|
67
51
|
"""
|
|
68
52
|
This is the famous Golomb ruler problem.
|
|
@@ -77,7 +61,15 @@ class GolombProblem(Problem):
|
|
|
77
61
|
"""
|
|
78
62
|
|
|
79
63
|
def __init__(self, mark_nb: int, symmetry_breaking: bool = True) -> None:
|
|
80
|
-
|
|
64
|
+
dist_nb = sum_first(mark_nb - 1)
|
|
65
|
+
domains = np.empty((dist_nb, 2), dtype=np.int32)
|
|
66
|
+
for i in range(0, mark_nb - 1):
|
|
67
|
+
for j in range(i + 1, mark_nb):
|
|
68
|
+
domains[index(mark_nb, i, j), MIN] = (
|
|
69
|
+
GOLOMB_LENGTHS[j - i + 1] if j - i + 1 < mark_nb else sum_first(j - i)
|
|
70
|
+
)
|
|
71
|
+
domains[:, MAX] = sum_first(dist_nb) - sum_first(dist_nb - mark_nb)
|
|
72
|
+
super().__init__(domains)
|
|
81
73
|
self.length_idx = index(mark_nb, 0, mark_nb - 1) # we want to minimize this
|
|
82
74
|
# dist_ij = mark_j - mark_i for j > i
|
|
83
75
|
# mark_j = dist_0j for j > 0
|
|
@@ -142,7 +134,7 @@ def golomb_consistency_algorithm(
|
|
|
142
134
|
# first prune the search space
|
|
143
135
|
mark_nb = (1 + int(math.sqrt(8 * len(dom_indices_arr) + 1))) // 2
|
|
144
136
|
ni_var_idx = first_not_instantiated_var_heuristic(
|
|
145
|
-
|
|
137
|
+
decision_domains, shr_domains_stack, stacks_top, None
|
|
146
138
|
) # no domains shared between vars
|
|
147
139
|
if 1 < ni_var_idx < mark_nb - 1: # otherwise useless
|
|
148
140
|
used_distance = np.zeros(sum_first(mark_nb - 2) + 1, dtype=np.bool)
|
|
@@ -169,7 +161,7 @@ def golomb_consistency_algorithm(
|
|
|
169
161
|
events = EVENT_MASK_MIN
|
|
170
162
|
if shr_domains_stack[top, dom_idx, MIN] == shr_domains_stack[top, dom_idx, MAX]:
|
|
171
163
|
events |= EVENT_MASK_GROUND
|
|
172
|
-
|
|
164
|
+
update_propagators(
|
|
173
165
|
triggered_propagators,
|
|
174
166
|
not_entailed_propagators_stack[top],
|
|
175
167
|
shr_domains_propagators,
|
|
@@ -14,7 +14,7 @@ import argparse
|
|
|
14
14
|
|
|
15
15
|
from rich import print
|
|
16
16
|
|
|
17
|
-
from nucs.constants import LOG_LEVEL_INFO, LOG_LEVELS
|
|
17
|
+
from nucs.constants import LOG_LEVEL_INFO, LOG_LEVELS, OPT_MODES, OPT_RESET
|
|
18
18
|
from nucs.examples.knapsack.knapsack_problem import KnapsackProblem
|
|
19
19
|
from nucs.heuristics.heuristics import DOM_HEURISTIC_MAX_VALUE, VAR_HEURISTIC_FIRST_NOT_INSTANTIATED
|
|
20
20
|
from nucs.solvers.backtrack_solver import BacktrackSolver
|
|
@@ -24,6 +24,7 @@ from nucs.solvers.backtrack_solver import BacktrackSolver
|
|
|
24
24
|
if __name__ == "__main__":
|
|
25
25
|
parser = argparse.ArgumentParser()
|
|
26
26
|
parser.add_argument("--log_level", choices=LOG_LEVELS, default=LOG_LEVEL_INFO)
|
|
27
|
+
parser.add_argument("--opt_mode", choices=OPT_MODES, default=OPT_RESET)
|
|
27
28
|
args = parser.parse_args()
|
|
28
29
|
problem = KnapsackProblem(
|
|
29
30
|
[40, 40, 38, 38, 36, 36, 34, 34, 32, 32, 30, 30, 28, 28, 26, 26, 24, 24, 22, 22],
|
|
@@ -36,6 +37,6 @@ if __name__ == "__main__":
|
|
|
36
37
|
dom_heuristic_idx=DOM_HEURISTIC_MAX_VALUE,
|
|
37
38
|
log_level=args.log_level,
|
|
38
39
|
)
|
|
39
|
-
solution = solver.maximize(problem.weight)
|
|
40
|
+
solution = solver.maximize(problem.weight, mode=args.opt_mode)
|
|
40
41
|
print(solver.get_statistics())
|
|
41
42
|
print(solution)
|
|
@@ -23,8 +23,8 @@ from nucs.solvers.backtrack_solver import BacktrackSolver
|
|
|
23
23
|
# NUMBA_CACHE_DIR=.numba/cache python -m nucs.examples.magic_sequence -n 100
|
|
24
24
|
if __name__ == "__main__":
|
|
25
25
|
parser = argparse.ArgumentParser()
|
|
26
|
-
parser.add_argument("-n", type=int, default=100)
|
|
27
26
|
parser.add_argument("--log_level", choices=LOG_LEVELS, default=LOG_LEVEL_INFO)
|
|
27
|
+
parser.add_argument("-n", type=int, default=100)
|
|
28
28
|
args = parser.parse_args()
|
|
29
29
|
problem = MagicSequenceProblem(args.n)
|
|
30
30
|
solver = BacktrackSolver(
|
|
@@ -33,6 +33,7 @@ if __name__ == "__main__":
|
|
|
33
33
|
var_heuristic_idx=VAR_HEURISTIC_FIRST_NOT_INSTANTIATED,
|
|
34
34
|
dom_heuristic_idx=DOM_HEURISTIC_MIN_VALUE,
|
|
35
35
|
log_level=args.log_level,
|
|
36
|
+
stack_max_height=128,
|
|
36
37
|
)
|
|
37
38
|
solver.solve_all()
|
|
38
39
|
print(solver.get_statistics())
|
|
@@ -23,9 +23,9 @@ from nucs.solvers.backtrack_solver import BacktrackSolver
|
|
|
23
23
|
# NUMBA_CACHE_DIR=.numba/cache python -m nucs.examples.magic_square -n 4 --symmetry_breaking
|
|
24
24
|
if __name__ == "__main__":
|
|
25
25
|
parser = argparse.ArgumentParser()
|
|
26
|
-
parser.add_argument("-n", type=int)
|
|
27
|
-
parser.add_argument("--symmetry_breaking", action=argparse.BooleanOptionalAction, default=True)
|
|
28
26
|
parser.add_argument("--log_level", choices=LOG_LEVELS, default=LOG_LEVEL_INFO)
|
|
27
|
+
parser.add_argument("-n", type=int, default=4)
|
|
28
|
+
parser.add_argument("--symmetry_breaking", action=argparse.BooleanOptionalAction, default=True)
|
|
29
29
|
args = parser.parse_args()
|
|
30
30
|
problem = MagicSquareProblem(args.n, args.symmetry_breaking)
|
|
31
31
|
solver = BacktrackSolver(
|
|
@@ -37,6 +37,7 @@ class QueensProblem(Problem):
|
|
|
37
37
|
self.add_propagator((list(range(n, 2 * n)), ALG_ALLDIFFERENT, []))
|
|
38
38
|
self.add_propagator((list(range(2 * n, 3 * n)), ALG_ALLDIFFERENT, []))
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
@staticmethod
|
|
41
|
+
def solution_as_matrix(solution: List[int]) -> List[List[str]]:
|
|
41
42
|
n = len(solution)
|
|
42
43
|
return [([" "] * i + ["X"] + [" "] * (n - i - 1)) for i in range(n)]
|
|
@@ -22,11 +22,11 @@ from nucs.solvers.backtrack_solver import BacktrackSolver
|
|
|
22
22
|
# NUMBA_CACHE_DIR=.numba/cache python -m nucs.examples.schur_lemma -n 20 --symmetry_breaking
|
|
23
23
|
if __name__ == "__main__":
|
|
24
24
|
parser = argparse.ArgumentParser()
|
|
25
|
-
parser.add_argument("-n", type=int, default=20)
|
|
26
25
|
parser.add_argument("--log_level", choices=LOG_LEVELS, default=LOG_LEVEL_INFO)
|
|
26
|
+
parser.add_argument("-n", type=int, default=20)
|
|
27
27
|
parser.add_argument("--symmetry_breaking", action=argparse.BooleanOptionalAction, default=True)
|
|
28
28
|
args = parser.parse_args()
|
|
29
29
|
problem = SchurLemmaProblem(args.n, args.symmetry_breaking)
|
|
30
|
-
solver = BacktrackSolver(problem, log_level=args.log_level)
|
|
30
|
+
solver = BacktrackSolver(problem, log_level=args.log_level, stack_max_height=128)
|
|
31
31
|
solver.solve_all()
|
|
32
32
|
print(solver.get_statistics())
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
###############################################################################
|
|
13
13
|
from typing import List
|
|
14
14
|
|
|
15
|
-
from numpy.
|
|
15
|
+
from numpy.typing import NDArray
|
|
16
16
|
|
|
17
17
|
from nucs.problems.problem import Problem
|
|
18
18
|
from nucs.propagators.propagators import ALG_ALLDIFFERENT, ALG_EXACTLY_EQ, ALG_GCC, ALG_RELATION
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
###############################################################################
|
|
2
|
+
# __ _ _____ _____
|
|
3
|
+
# | \ | | / ____| / ____|
|
|
4
|
+
# | \| | _ _ | | | (___
|
|
5
|
+
# | . ` | | | | | | | \___ \
|
|
6
|
+
# | |\ | | |_| | | |____ ____) |
|
|
7
|
+
# |_| \_| \__,_| \_____| |_____/
|
|
8
|
+
#
|
|
9
|
+
# Fast constraint solving in Python - https://github.com/yangeorget/nucs
|
|
10
|
+
#
|
|
11
|
+
# Copyright 2024 - Yan Georget
|
|
12
|
+
###############################################################################
|
|
13
|
+
import argparse
|
|
14
|
+
|
|
15
|
+
from nucs.constants import LOG_LEVEL_INFO, LOG_LEVELS, OPT_MODES, OPT_PRUNE
|
|
16
|
+
from nucs.examples.tsp.tsp_instances import TSP_INSTANCES
|
|
17
|
+
from nucs.examples.tsp.tsp_problem import TSPProblem
|
|
18
|
+
from nucs.examples.tsp.tsp_var_heuristic import tsp_var_heuristic
|
|
19
|
+
from nucs.heuristics.heuristics import DOM_HEURISTIC_MIN_COST, register_var_heuristic
|
|
20
|
+
from nucs.solvers.backtrack_solver import BacktrackSolver
|
|
21
|
+
from nucs.solvers.consistency_algorithms import CONSISTENCY_ALG_BC, CONSISTENCY_ALG_SHAVING
|
|
22
|
+
from nucs.solvers.multiprocessing_solver import MultiprocessingSolver
|
|
23
|
+
|
|
24
|
+
# Run with the following command (the second run is much faster because the code has been compiled):
|
|
25
|
+
# NUMBA_CACHE_DIR=.numba/cache python -m nucs.examples.alpha
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
parser = argparse.ArgumentParser()
|
|
28
|
+
parser.add_argument("--log_level", choices=LOG_LEVELS, default=LOG_LEVEL_INFO)
|
|
29
|
+
parser.add_argument("--name", choices=["GR17", "GR21", "GR24"], default="GR17")
|
|
30
|
+
parser.add_argument("--opt_mode", choices=OPT_MODES, default=OPT_PRUNE)
|
|
31
|
+
parser.add_argument("--processors", type=int, default=1)
|
|
32
|
+
parser.add_argument("--shaving", type=bool, action=argparse.BooleanOptionalAction, default=False)
|
|
33
|
+
args = parser.parse_args()
|
|
34
|
+
tsp_instance = TSP_INSTANCES[args.name]
|
|
35
|
+
n = len(tsp_instance)
|
|
36
|
+
decision_domains = list(range(0, 2 * n))
|
|
37
|
+
problem = TSPProblem(tsp_instance)
|
|
38
|
+
costs = tsp_instance + tsp_instance # symmetry of costs
|
|
39
|
+
tsp_var_heuristic_idx = register_var_heuristic(tsp_var_heuristic)
|
|
40
|
+
solver = (
|
|
41
|
+
MultiprocessingSolver(
|
|
42
|
+
[
|
|
43
|
+
BacktrackSolver(
|
|
44
|
+
prob,
|
|
45
|
+
consistency_alg_idx=CONSISTENCY_ALG_SHAVING if args.shaving else CONSISTENCY_ALG_BC,
|
|
46
|
+
decision_domains=decision_domains,
|
|
47
|
+
var_heuristic_idx=tsp_var_heuristic_idx,
|
|
48
|
+
var_heuristic_params=costs,
|
|
49
|
+
dom_heuristic_idx=DOM_HEURISTIC_MIN_COST,
|
|
50
|
+
dom_heuristic_params=costs,
|
|
51
|
+
log_level=args.log_level,
|
|
52
|
+
)
|
|
53
|
+
for prob in problem.split(args.processors, 0)
|
|
54
|
+
]
|
|
55
|
+
)
|
|
56
|
+
if args.processors > 1
|
|
57
|
+
else BacktrackSolver(
|
|
58
|
+
problem,
|
|
59
|
+
consistency_alg_idx=CONSISTENCY_ALG_SHAVING if args.shaving else CONSISTENCY_ALG_BC,
|
|
60
|
+
decision_domains=decision_domains,
|
|
61
|
+
var_heuristic_idx=tsp_var_heuristic_idx,
|
|
62
|
+
var_heuristic_params=costs,
|
|
63
|
+
dom_heuristic_idx=DOM_HEURISTIC_MIN_COST,
|
|
64
|
+
dom_heuristic_params=costs,
|
|
65
|
+
log_level=args.log_level,
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
solution = solver.minimize(problem.total_cost, mode=args.opt_mode)
|
|
69
|
+
print(solver.get_statistics())
|
|
@@ -26,7 +26,7 @@ def get_complexity_total_cost(n: int, parameters: NDArray) -> float:
|
|
|
26
26
|
:param parameters: the parameters, unused here
|
|
27
27
|
:return: a float
|
|
28
28
|
"""
|
|
29
|
-
return
|
|
29
|
+
return n * n
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def get_triggers_total_cost(n: int, parameters: NDArray) -> NDArray:
|
|
@@ -53,13 +53,24 @@ def compute_domains_total_cost(domains: NDArray, parameters: NDArray) -> int:
|
|
|
53
53
|
for i in range(n):
|
|
54
54
|
if domains[i, MIN] == domains[i, MAX]:
|
|
55
55
|
used[domains[i, MIN]] = True
|
|
56
|
-
global_min =
|
|
56
|
+
global_min = 0
|
|
57
|
+
# for j in range(n):
|
|
58
|
+
# max_regret = 0
|
|
59
|
+
# min_cost = min([costs[i, j] for i in range(n) if costs[i,j] > 0])
|
|
60
|
+
# for i in range(n):
|
|
61
|
+
# if costs[i, j] == min_cost:
|
|
62
|
+
# regret = compute_regret(domains[i], costs[i])
|
|
63
|
+
# if regret > max_regret:
|
|
64
|
+
# max_regret = regret
|
|
65
|
+
# global_min += regret
|
|
66
|
+
# global_min -= max_regret
|
|
67
|
+
global_max = 0
|
|
57
68
|
for i in range(n):
|
|
58
|
-
local_min = sys.maxsize
|
|
59
|
-
local_max = 0
|
|
60
69
|
if domains[i, MIN] == domains[i, MAX]:
|
|
61
70
|
local_min = local_max = parameters[i * n + domains[i, MIN]]
|
|
62
71
|
else:
|
|
72
|
+
local_min = sys.maxsize
|
|
73
|
+
local_max = 0
|
|
63
74
|
for value in range(domains[i, MIN], domains[i, MAX] + 1):
|
|
64
75
|
if not used[value]:
|
|
65
76
|
cost = parameters[i * n + value]
|
|
@@ -68,8 +79,8 @@ def compute_domains_total_cost(domains: NDArray, parameters: NDArray) -> int:
|
|
|
68
79
|
local_min = cost
|
|
69
80
|
if cost > local_max:
|
|
70
81
|
local_max = cost
|
|
71
|
-
if local_min > local_max:
|
|
72
|
-
|
|
82
|
+
# if local_min > local_max:
|
|
83
|
+
# return PROP_INCONSISTENCY
|
|
73
84
|
global_min += local_min
|
|
74
85
|
global_max += local_max
|
|
75
86
|
domains[-1, MIN] = max(domains[-1, MIN], global_min)
|
|
@@ -33,18 +33,21 @@ class TSPProblem(CircuitProblem):
|
|
|
33
33
|
super().__init__(n)
|
|
34
34
|
max_costs = [max(cost_row) for cost_row in cost_rows]
|
|
35
35
|
min_costs = [min([cost for cost in cost_row if cost > 0]) for cost_row in cost_rows]
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
self.next_costs = self.add_variables([(min_costs[i], max_costs[i]) for i in range(n)])
|
|
37
|
+
self.prev_costs = self.add_variables([(min_costs[i], max_costs[i]) for i in range(n)])
|
|
38
|
+
self.total_cost = self.add_variable((sum(min_costs), sum(max_costs))) # the total cost
|
|
38
39
|
for i in range(n):
|
|
39
|
-
self.add_propagator(([i,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
self.add_propagator(([i, self.next_costs + i], ALG_ELEMENT_IV, cost_rows[i]))
|
|
41
|
+
self.add_propagator(([n + i, self.prev_costs + i], ALG_ELEMENT_IV, cost_rows[i]))
|
|
42
|
+
self.add_propagator(
|
|
43
|
+
(list(range(self.next_costs, self.next_costs + n)) + [self.total_cost], ALG_AFFINE_EQ, [1] * n + [-1, 0])
|
|
43
44
|
)
|
|
44
45
|
self.add_propagator(
|
|
45
|
-
(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
)
|
|
46
|
+
(list(range(self.prev_costs, self.prev_costs + n)) + [self.total_cost], ALG_AFFINE_EQ, [1] * n + [-1, 0])
|
|
47
|
+
)
|
|
48
|
+
total_cost_prop_idx = register_propagator(
|
|
49
|
+
get_triggers_total_cost, get_complexity_total_cost, compute_domains_total_cost
|
|
50
50
|
)
|
|
51
|
+
costs = [cost for cost_row in cost_rows for cost in cost_row]
|
|
52
|
+
self.add_propagator((list(range(n)) + [self.total_cost], total_cost_prop_idx, costs))
|
|
53
|
+
self.add_propagator((list(range(n, 2 * n)) + [self.total_cost], total_cost_prop_idx, costs))
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
###############################################################################
|
|
2
|
+
# __ _ _____ _____
|
|
3
|
+
# | \ | | / ____| / ____|
|
|
4
|
+
# | \| | _ _ | | | (___
|
|
5
|
+
# | . ` | | | | | | | \___ \
|
|
6
|
+
# | |\ | | |_| | | |____ ____) |
|
|
7
|
+
# |_| \_| \__,_| \_____| |_____/
|
|
8
|
+
#
|
|
9
|
+
# Fast constraint solving in Python - https://github.com/yangeorget/nucs
|
|
10
|
+
#
|
|
11
|
+
# Copyright 2024 - Yan Georget
|
|
12
|
+
###############################################################################
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
from numba import njit # type: ignore
|
|
16
|
+
from numpy.typing import NDArray
|
|
17
|
+
|
|
18
|
+
from nucs.constants import MAX, MIN
|
|
19
|
+
from nucs.heuristics.max_regret_var_heuristic import regret
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@njit(cache=True)
|
|
23
|
+
def tsp_var_heuristic(
|
|
24
|
+
decision_domains: NDArray, shr_domains_stack: NDArray, stacks_top: NDArray, params: NDArray
|
|
25
|
+
) -> int:
|
|
26
|
+
"""
|
|
27
|
+
:param decision_domains: the indices of a subset of the shared domains
|
|
28
|
+
:param shr_domains_stack: the stack of shared domains
|
|
29
|
+
:param stacks_top: the index of the top of the stacks as a Numpy array
|
|
30
|
+
:param params: a two-dimensional (first dimension correspond to variables, second to values) costs array
|
|
31
|
+
:return: the index of the shared domain
|
|
32
|
+
"""
|
|
33
|
+
best_score = -sys.maxsize
|
|
34
|
+
best_idx = -1
|
|
35
|
+
top = stacks_top[0]
|
|
36
|
+
for dom_idx in decision_domains:
|
|
37
|
+
shr_domain = shr_domains_stack[top, dom_idx]
|
|
38
|
+
if 0 < shr_domain[MAX] - shr_domain[MIN]:
|
|
39
|
+
score = compute_score(shr_domain, dom_idx, params)
|
|
40
|
+
if best_score < score:
|
|
41
|
+
best_idx = dom_idx
|
|
42
|
+
best_score = score
|
|
43
|
+
return best_idx
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@njit(cache=True)
|
|
47
|
+
def compute_score(shr_domain: NDArray, dom_idx: int, params: NDArray) -> int:
|
|
48
|
+
"""
|
|
49
|
+
Minimize [min(5, size(X)), -regret(X)] for lexicographic order.
|
|
50
|
+
"""
|
|
51
|
+
size = min(12, shr_domain[MAX] - shr_domain[MIN] + 1)
|
|
52
|
+
return -size * 1024 + regret(shr_domain, params[dom_idx])
|
|
@@ -18,18 +18,18 @@ from nucs.constants import MAX, MIN
|
|
|
18
18
|
|
|
19
19
|
@njit(cache=True)
|
|
20
20
|
def first_not_instantiated_var_heuristic(
|
|
21
|
-
|
|
21
|
+
decision_domains: NDArray, shr_domains_stack: NDArray, stacks_top: NDArray, params: NDArray
|
|
22
22
|
) -> int:
|
|
23
23
|
"""
|
|
24
24
|
Chooses the first non-instantiated shared domain.
|
|
25
|
-
:param params: a two-dimensional parameters array, unused here
|
|
26
25
|
:param decision_domains: the indices of a subset of the shared domains
|
|
27
26
|
:param shr_domains_stack: the stack of shared domains
|
|
28
27
|
:param stacks_top: the index of the top of the stacks as a Numpy array
|
|
28
|
+
:param params: a two-dimensional parameters array, unused here
|
|
29
29
|
:return: the index of the shared domain
|
|
30
30
|
"""
|
|
31
|
-
|
|
31
|
+
top = stacks_top[0]
|
|
32
32
|
for dom_idx in decision_domains:
|
|
33
|
-
if shr_domains_stack[
|
|
33
|
+
if shr_domains_stack[top, dom_idx, MIN] < shr_domains_stack[top, dom_idx, MAX]:
|
|
34
34
|
return dom_idx
|
|
35
35
|
return -1 # cannot happen
|
|
@@ -18,23 +18,23 @@ from nucs.constants import MAX, MIN
|
|
|
18
18
|
|
|
19
19
|
@njit(cache=True)
|
|
20
20
|
def greatest_domain_var_heuristic(
|
|
21
|
-
|
|
21
|
+
decision_domains: NDArray, shr_domains_stack: NDArray, stacks_top: NDArray, params: NDArray
|
|
22
22
|
) -> int:
|
|
23
23
|
"""
|
|
24
24
|
Chooses the greatest shared domain and which is not instantiated.
|
|
25
|
-
:param params: a two-dimensional parameters array, unused here
|
|
26
25
|
:param decision_domains: the indices of a subset of the shared domains
|
|
27
26
|
:param shr_domains_stack: the stack of shared domains
|
|
28
27
|
:param stacks_top: the index of the top of the stacks as a Numpy array
|
|
28
|
+
:param params: a two-dimensional parameters array, unused here
|
|
29
29
|
:return: the index of the shared domain
|
|
30
30
|
"""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
best_score = 0
|
|
32
|
+
best_idx = -1
|
|
33
|
+
top = stacks_top[0]
|
|
34
34
|
for dom_idx in decision_domains:
|
|
35
|
-
shr_domain = shr_domains_stack[
|
|
36
|
-
|
|
37
|
-
if
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return
|
|
35
|
+
shr_domain = shr_domains_stack[top, dom_idx]
|
|
36
|
+
score = shr_domain[MAX] - shr_domain[MIN] # this is size - 1
|
|
37
|
+
if best_score < score:
|
|
38
|
+
best_idx = dom_idx
|
|
39
|
+
best_score = score
|
|
40
|
+
return best_idx
|