NUCS 4.2.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.
Files changed (155) hide show
  1. {nucs-4.2.0 → nucs-4.3.0/NUCS.egg-info}/PKG-INFO +1 -1
  2. {nucs-4.2.0 → nucs-4.3.0}/NUCS.egg-info/SOURCES.txt +4 -2
  3. {nucs-4.2.0/NUCS.egg-info → nucs-4.3.0}/PKG-INFO +1 -1
  4. {nucs-4.2.0 → nucs-4.3.0}/docs/source/conf.py +2 -2
  5. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/alpha/alpha_problem.py +3 -2
  6. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/donald/donald_problem.py +4 -3
  7. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/golomb/golomb_problem.py +2 -2
  8. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/magic_square/__main__.py +1 -1
  9. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/queens/queens_problem.py +2 -1
  10. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/sports_tournament_scheduling/sports_tournament_scheduling_problem.py +1 -1
  11. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/tsp/__main__.py +18 -13
  12. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/tsp/total_cost_propagator.py +15 -4
  13. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/tsp/tsp_problem.py +15 -9
  14. nucs-4.3.0/nucs/examples/tsp/tsp_var_heuristic.py +52 -0
  15. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/greatest_domain_var_heuristic.py +7 -7
  16. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/max_regret_var_heuristic.py +18 -14
  17. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/smallest_domain_var_heuristic.py +7 -7
  18. {nucs-4.2.0 → nucs-4.3.0}/nucs/numba_helper.py +2 -2
  19. nucs-4.3.0/nucs/problems/circuit_problem.py +36 -0
  20. nucs-4.2.0/nucs/problems/circuit_problem.py → nucs-4.3.0/nucs/problems/permutation_problem.py +11 -10
  21. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/gcc_propagator.py +9 -9
  22. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/no_sub_cycle_propagator.py +21 -18
  23. nucs-4.3.0/nucs/propagators/permutation_aux_propagator.py +65 -0
  24. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/propagators.py +11 -4
  25. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/scc_propagator.py +1 -1
  26. {nucs-4.2.0 → nucs-4.3.0}/nucs/solvers/backtrack_solver.py +4 -4
  27. {nucs-4.2.0 → nucs-4.3.0}/nucs/solvers/bound_consistency_algorithm.py +3 -3
  28. {nucs-4.2.0 → nucs-4.3.0}/nucs/solvers/choice_points.py +2 -2
  29. {nucs-4.2.0 → nucs-4.3.0}/nucs/solvers/multiprocessing_solver.py +2 -2
  30. {nucs-4.2.0 → nucs-4.3.0}/nucs/solvers/shaving_consistency_algorithm.py +2 -2
  31. {nucs-4.2.0 → nucs-4.3.0}/pyproject.toml +1 -1
  32. nucs-4.3.0/tests/examples/test_tsp.py +59 -0
  33. nucs-4.3.0/tests/problems/test_permutation_problem.py +35 -0
  34. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_no_sub_cycle.py +7 -2
  35. nucs-4.2.0/nucs/examples/tsp/mcp_dom_heuristic.py +0 -59
  36. nucs-4.2.0/nucs/examples/tsp/mrp_var_heuristic.py +0 -60
  37. nucs-4.2.0/tests/examples/test_tsp.py +0 -47
  38. {nucs-4.2.0 → nucs-4.3.0}/LICENSE.md +0 -0
  39. {nucs-4.2.0 → nucs-4.3.0}/NUCS.egg-info/dependency_links.txt +0 -0
  40. {nucs-4.2.0 → nucs-4.3.0}/NUCS.egg-info/requires.txt +0 -0
  41. {nucs-4.2.0 → nucs-4.3.0}/NUCS.egg-info/top_level.txt +0 -0
  42. {nucs-4.2.0 → nucs-4.3.0}/README.md +0 -0
  43. {nucs-4.2.0 → nucs-4.3.0}/nucs/__init__.py +0 -0
  44. {nucs-4.2.0 → nucs-4.3.0}/nucs/constants.py +0 -0
  45. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/__init__.py +0 -0
  46. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/alpha/__init__.py +0 -0
  47. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/alpha/__main__.py +0 -0
  48. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/bibd/__init__.py +0 -0
  49. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/bibd/__main__.py +0 -0
  50. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/bibd/bibd_problem.py +0 -0
  51. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/donald/__init__.py +0 -0
  52. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/donald/__main__.py +0 -0
  53. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/golomb/__init__.py +0 -0
  54. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/golomb/__main__.py +0 -0
  55. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/knapsack/__init__.py +0 -0
  56. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/knapsack/__main__.py +0 -0
  57. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/knapsack/knapsack_problem.py +0 -0
  58. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/magic_sequence/__init__.py +0 -0
  59. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/magic_sequence/__main__.py +0 -0
  60. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/magic_sequence/magic_sequence_problem.py +0 -0
  61. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/magic_square/__init__.py +0 -0
  62. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/magic_square/magic_square_problem.py +0 -0
  63. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/quasigroup/__init__.py +0 -0
  64. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/quasigroup/__main__.py +0 -0
  65. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/quasigroup/quasigroup_problem.py +0 -0
  66. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/queens/__init__.py +0 -0
  67. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/queens/__main__.py +0 -0
  68. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/schur_lemma/__init__.py +0 -0
  69. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/schur_lemma/__main__.py +0 -0
  70. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/schur_lemma/schur_lemma_problem.py +0 -0
  71. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/sports_tournament_scheduling/__init__.py +0 -0
  72. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/sports_tournament_scheduling/__main__.py +0 -0
  73. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/sudoku/__init__.py +0 -0
  74. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/sudoku/sudoku_problem.py +0 -0
  75. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/tsp/__init__.py +0 -0
  76. {nucs-4.2.0 → nucs-4.3.0}/nucs/examples/tsp/tsp_instances.py +0 -0
  77. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/__init__.py +0 -0
  78. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/first_not_instantiated_var_heuristic.py +0 -0
  79. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/heuristics.py +0 -0
  80. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/max_value_dom_heuristic.py +0 -0
  81. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/mid_value_dom_heuristic.py +0 -0
  82. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/min_cost_dom_heuristic.py +0 -0
  83. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/min_value_dom_heuristic.py +0 -0
  84. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/split_high_dom_heuristic.py +0 -0
  85. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/split_low_dom_heuristic.py +0 -0
  86. {nucs-4.2.0 → nucs-4.3.0}/nucs/heuristics/value_dom_heuristic.py +0 -0
  87. {nucs-4.2.0 → nucs-4.3.0}/nucs/numpy_helper.py +0 -0
  88. {nucs-4.2.0 → nucs-4.3.0}/nucs/problems/__init__.py +0 -0
  89. {nucs-4.2.0 → nucs-4.3.0}/nucs/problems/latin_square_problem.py +0 -0
  90. {nucs-4.2.0 → nucs-4.3.0}/nucs/problems/problem.py +0 -0
  91. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/__init__.py +0 -0
  92. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/affine_eq_propagator.py +0 -0
  93. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/affine_geq_propagator.py +0 -0
  94. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/affine_leq_propagator.py +0 -0
  95. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/alldifferent_propagator.py +0 -0
  96. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/and_propagator.py +0 -0
  97. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/count_eq_propagator.py +0 -0
  98. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/dummy_propagator.py +0 -0
  99. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/element_iv_propagator.py +0 -0
  100. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/element_lic_propagator.py +0 -0
  101. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/element_liv_propagator.py +0 -0
  102. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/exactly_eq_propagator.py +0 -0
  103. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/exactly_true_propagator.py +0 -0
  104. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/lexicographic_leq_propagator.py +0 -0
  105. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/max_eq_propagator.py +0 -0
  106. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/max_leq_propagator.py +0 -0
  107. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/min_eq_propagator.py +0 -0
  108. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/min_geq_propagator.py +0 -0
  109. {nucs-4.2.0 → nucs-4.3.0}/nucs/propagators/relation_propagator.py +0 -0
  110. {nucs-4.2.0 → nucs-4.3.0}/nucs/solvers/__init__.py +0 -0
  111. {nucs-4.2.0 → nucs-4.3.0}/nucs/solvers/consistency_algorithms.py +0 -0
  112. {nucs-4.2.0 → nucs-4.3.0}/nucs/solvers/solver.py +0 -0
  113. {nucs-4.2.0 → nucs-4.3.0}/setup.cfg +0 -0
  114. {nucs-4.2.0 → nucs-4.3.0}/tests/__init__.py +0 -0
  115. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_alpha.py +0 -0
  116. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_bibd.py +0 -0
  117. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_donald.py +0 -0
  118. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_golomb.py +0 -0
  119. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_knapsack.py +0 -0
  120. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_latin_square.py +0 -0
  121. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_magic_sequence.py +0 -0
  122. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_magic_square.py +0 -0
  123. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_quasigroup.py +0 -0
  124. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_queens.py +0 -0
  125. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_schur_lemma.py +0 -0
  126. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_sports_tournament_scheduling.py +0 -0
  127. {nucs-4.2.0 → nucs-4.3.0}/tests/examples/test_sudokus.py +0 -0
  128. {nucs-4.2.0 → nucs-4.3.0}/tests/heuristics/__init__.py +0 -0
  129. {nucs-4.2.0 → nucs-4.3.0}/tests/heuristics/test_max_value_dom_heuristic.py +0 -0
  130. {nucs-4.2.0 → nucs-4.3.0}/tests/heuristics/test_mid_value_dom_heuristic.py +0 -0
  131. {nucs-4.2.0 → nucs-4.3.0}/tests/heuristics/test_min_value_dom_heuristic.py +0 -0
  132. {nucs-4.2.0 → nucs-4.3.0}/tests/problems/__init__.py +0 -0
  133. {nucs-4.2.0 → nucs-4.3.0}/tests/problems/test_circuit_problem.py +0 -0
  134. {nucs-4.2.0 → nucs-4.3.0}/tests/problems/test_problem.py +0 -0
  135. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/__init__.py +0 -0
  136. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_affine_eq.py +0 -0
  137. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_affine_geq.py +0 -0
  138. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_affine_leq.py +0 -0
  139. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_alldifferent.py +0 -0
  140. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_count_eq.py +0 -0
  141. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_element_iv.py +0 -0
  142. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_element_lic.py +0 -0
  143. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_element_liv.py +0 -0
  144. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_exactly_eq.py +0 -0
  145. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_gcc.py +0 -0
  146. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_lexicographic_leq.py +0 -0
  147. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_max_eq.py +0 -0
  148. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_max_leq.py +0 -0
  149. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_min_eq.py +0 -0
  150. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_min_geq.py +0 -0
  151. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_relation.py +0 -0
  152. {nucs-4.2.0 → nucs-4.3.0}/tests/propagators/test_scc.py +0 -0
  153. {nucs-4.2.0 → nucs-4.3.0}/tests/solvers/__init__.py +0 -0
  154. {nucs-4.2.0 → nucs-4.3.0}/tests/solvers/test_backtrack_solver.py +0 -0
  155. {nucs-4.2.0 → nucs-4.3.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: 4.2.0
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
@@ -49,11 +49,10 @@ nucs/examples/sudoku/__init__.py
49
49
  nucs/examples/sudoku/sudoku_problem.py
50
50
  nucs/examples/tsp/__init__.py
51
51
  nucs/examples/tsp/__main__.py
52
- nucs/examples/tsp/mcp_dom_heuristic.py
53
- nucs/examples/tsp/mrp_var_heuristic.py
54
52
  nucs/examples/tsp/total_cost_propagator.py
55
53
  nucs/examples/tsp/tsp_instances.py
56
54
  nucs/examples/tsp/tsp_problem.py
55
+ nucs/examples/tsp/tsp_var_heuristic.py
57
56
  nucs/heuristics/__init__.py
58
57
  nucs/heuristics/first_not_instantiated_var_heuristic.py
59
58
  nucs/heuristics/greatest_domain_var_heuristic.py
@@ -70,6 +69,7 @@ nucs/heuristics/value_dom_heuristic.py
70
69
  nucs/problems/__init__.py
71
70
  nucs/problems/circuit_problem.py
72
71
  nucs/problems/latin_square_problem.py
72
+ nucs/problems/permutation_problem.py
73
73
  nucs/problems/problem.py
74
74
  nucs/propagators/__init__.py
75
75
  nucs/propagators/affine_eq_propagator.py
@@ -91,6 +91,7 @@ nucs/propagators/max_leq_propagator.py
91
91
  nucs/propagators/min_eq_propagator.py
92
92
  nucs/propagators/min_geq_propagator.py
93
93
  nucs/propagators/no_sub_cycle_propagator.py
94
+ nucs/propagators/permutation_aux_propagator.py
94
95
  nucs/propagators/propagators.py
95
96
  nucs/propagators/relation_propagator.py
96
97
  nucs/propagators/scc_propagator.py
@@ -123,6 +124,7 @@ tests/heuristics/test_mid_value_dom_heuristic.py
123
124
  tests/heuristics/test_min_value_dom_heuristic.py
124
125
  tests/problems/__init__.py
125
126
  tests/problems/test_circuit_problem.py
127
+ tests/problems/test_permutation_problem.py
126
128
  tests/problems/test_problem.py
127
129
  tests/propagators/__init__.py
128
130
  tests/propagators/test_affine_eq.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: NUCS
3
- Version: 4.2.0
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
@@ -6,8 +6,8 @@ project = 'NUCS'
6
6
  copyright = '2024, Yan Georget'
7
7
  author = 'Yan Georget'
8
8
 
9
- release = '4.2.0'
10
- version = '4.2.0'
9
+ release = '4.3.0'
10
+ version = '4.3.0'
11
11
 
12
12
  # -- General configuration
13
13
 
@@ -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
- (e.g for LYRE: L,Y,R,E might be to equal 5,9,20 and 13 or any other combination that add up to 47).
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
- def solution_as_dict(self, solution: NDArray) -> Dict[str, int]:
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
@@ -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._typing import NDArray
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
- def solution_as_dict(self, solution: NDArray) -> Dict[str, int]:
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
@@ -19,7 +19,7 @@ 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, add_propagators
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])
@@ -161,7 +161,7 @@ def golomb_consistency_algorithm(
161
161
  events = EVENT_MASK_MIN
162
162
  if shr_domains_stack[top, dom_idx, MIN] == shr_domains_stack[top, dom_idx, MAX]:
163
163
  events |= EVENT_MASK_GROUND
164
- add_propagators(
164
+ update_propagators(
165
165
  triggered_propagators,
166
166
  not_entailed_propagators_stack[top],
167
167
  shr_domains_propagators,
@@ -24,7 +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("-n", type=int)
27
+ parser.add_argument("-n", type=int, default=4)
28
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)
@@ -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
- def solution_as_matrix(self, solution: List[int]) -> List[List[str]]:
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)]
@@ -12,7 +12,7 @@
12
12
  ###############################################################################
13
13
  from typing import List
14
14
 
15
- from numpy._typing import NDArray
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
@@ -15,7 +15,8 @@ import argparse
15
15
  from nucs.constants import LOG_LEVEL_INFO, LOG_LEVELS, OPT_MODES, OPT_PRUNE
16
16
  from nucs.examples.tsp.tsp_instances import TSP_INSTANCES
17
17
  from nucs.examples.tsp.tsp_problem import TSPProblem
18
- from nucs.heuristics.heuristics import DOM_HEURISTIC_MIN_COST, VAR_HEURISTIC_MAX_REGRET
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
19
20
  from nucs.solvers.backtrack_solver import BacktrackSolver
20
21
  from nucs.solvers.consistency_algorithms import CONSISTENCY_ALG_BC, CONSISTENCY_ALG_SHAVING
21
22
  from nucs.solvers.multiprocessing_solver import MultiprocessingSolver
@@ -28,21 +29,25 @@ if __name__ == "__main__":
28
29
  parser.add_argument("--name", choices=["GR17", "GR21", "GR24"], default="GR17")
29
30
  parser.add_argument("--opt_mode", choices=OPT_MODES, default=OPT_PRUNE)
30
31
  parser.add_argument("--processors", type=int, default=1)
31
- parser.add_argument("--shaving", type=bool, action=argparse.BooleanOptionalAction, default=True)
32
+ parser.add_argument("--shaving", type=bool, action=argparse.BooleanOptionalAction, default=False)
32
33
  args = parser.parse_args()
33
34
  tsp_instance = TSP_INSTANCES[args.name]
35
+ n = len(tsp_instance)
36
+ decision_domains = list(range(0, 2 * n))
34
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)
35
40
  solver = (
36
41
  MultiprocessingSolver(
37
42
  [
38
43
  BacktrackSolver(
39
44
  prob,
40
45
  consistency_alg_idx=CONSISTENCY_ALG_SHAVING if args.shaving else CONSISTENCY_ALG_BC,
41
- decision_domains=list(range(len(tsp_instance))),
42
- var_heuristic_idx=VAR_HEURISTIC_MAX_REGRET, # register_var_heuristic(mrp_var_heuristic),
43
- var_heuristic_params=tsp_instance,
44
- dom_heuristic_idx=DOM_HEURISTIC_MIN_COST, # register_dom_heuristic(mcp_dom_heuristic),
45
- dom_heuristic_params=tsp_instance,
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,
46
51
  log_level=args.log_level,
47
52
  )
48
53
  for prob in problem.split(args.processors, 0)
@@ -52,13 +57,13 @@ if __name__ == "__main__":
52
57
  else BacktrackSolver(
53
58
  problem,
54
59
  consistency_alg_idx=CONSISTENCY_ALG_SHAVING if args.shaving else CONSISTENCY_ALG_BC,
55
- decision_domains=list(range(len(tsp_instance))),
56
- var_heuristic_idx=VAR_HEURISTIC_MAX_REGRET, # register_var_heuristic(mrp_var_heuristic),
57
- var_heuristic_params=tsp_instance,
58
- dom_heuristic_idx=DOM_HEURISTIC_MIN_COST, # register_dom_heuristic(mcp_dom_heuristic),
59
- dom_heuristic_params=tsp_instance,
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,
60
65
  log_level=args.log_level,
61
66
  )
62
67
  )
63
- solution = solver.minimize(problem.shr_domain_nb - 1, mode=args.opt_mode)
68
+ solution = solver.minimize(problem.total_cost, mode=args.opt_mode)
64
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 2 * n
29
+ return n * n
30
30
 
31
31
 
32
32
  def get_triggers_total_cost(n: int, parameters: NDArray) -> NDArray:
@@ -53,7 +53,18 @@ 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 = global_max = 0
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
69
  if domains[i, MIN] == domains[i, MAX]:
59
70
  local_min = local_max = parameters[i * n + domains[i, MIN]]
@@ -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
- return PROP_INCONSISTENCY
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,15 +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
- cost_vars = self.add_variables([(min_costs[i], max_costs[i]) for i in range(n)]) # the costs
37
- total_cost_var = self.add_variable((sum(min_costs), sum(max_costs))) # the total cost
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, cost_vars + i], ALG_ELEMENT_IV, cost_rows[i]))
40
- self.add_propagator((list(range(cost_vars, cost_vars + n + 1)), ALG_AFFINE_EQ, [1] * n + [-1, 0]))
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]))
41
42
  self.add_propagator(
42
- (
43
- list(range(0, n)) + [total_cost_var],
44
- register_propagator(get_triggers_total_cost, get_complexity_total_cost, compute_domains_total_cost),
45
- [cost for cost_row in cost_rows for cost in cost_row],
46
- )
43
+ (list(range(self.next_costs, self.next_costs + n)) + [self.total_cost], ALG_AFFINE_EQ, [1] * n + [-1, 0])
47
44
  )
45
+ self.add_propagator(
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
+ )
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])
@@ -28,13 +28,13 @@ def greatest_domain_var_heuristic(
28
28
  :param params: a two-dimensional parameters array, unused here
29
29
  :return: the index of the shared domain
30
30
  """
31
- max_size = 0
32
- max_idx = -1
31
+ best_score = 0
32
+ best_idx = -1
33
33
  top = stacks_top[0]
34
34
  for dom_idx in decision_domains:
35
35
  shr_domain = shr_domains_stack[top, dom_idx]
36
- size = shr_domain[MAX] - shr_domain[MIN] # actually this is size - 1
37
- if max_size < size:
38
- max_idx = dom_idx
39
- max_size = size
40
- return max_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
@@ -30,24 +30,28 @@ def max_regret_var_heuristic(
30
30
  :param params: a two-dimensional (first dimension correspond to variables, second to values) costs array
31
31
  :return: the index of the shared domain
32
32
  """
33
- max_regret = 0
33
+ best_score = 0
34
34
  best_idx = -1
35
35
  top = stacks_top[0]
36
36
  for dom_idx in decision_domains:
37
37
  shr_domain = shr_domains_stack[top, dom_idx]
38
38
  if 0 < shr_domain[MAX] - shr_domain[MIN]:
39
- best_cost = sys.maxsize
40
- second_cost = sys.maxsize
41
- for value in range(shr_domain[MIN], shr_domain[MAX] + 1):
42
- cost = params[dom_idx][value]
43
- if cost > 0:
44
- if cost < best_cost:
45
- second_cost = best_cost
46
- best_cost = cost
47
- elif cost < second_cost:
48
- second_cost = cost
49
- regret = second_cost - best_cost
50
- if max_regret < regret:
39
+ score = regret(shr_domain, params[dom_idx])
40
+ if best_score < score:
51
41
  best_idx = dom_idx
52
- max_regret = regret
42
+ best_score = score
53
43
  return best_idx
44
+
45
+
46
+ @njit(cache=True)
47
+ def regret(domain: NDArray, costs: NDArray) -> int:
48
+ best_cost = sys.maxsize
49
+ second_cost = sys.maxsize
50
+ for value in range(domain[MIN], domain[MAX] + 1):
51
+ cost = costs[value]
52
+ if 0 < cost < best_cost:
53
+ second_cost = best_cost
54
+ best_cost = cost
55
+ elif 0 < cost < second_cost:
56
+ second_cost = cost
57
+ return second_cost - best_cost
@@ -30,13 +30,13 @@ def smallest_domain_var_heuristic(
30
30
  :param params: a two-dimensional parameters array, unused here
31
31
  :return: the index of the shared domain
32
32
  """
33
- min_size = sys.maxsize
34
- min_idx = -1
33
+ best_score = -sys.maxsize
34
+ best_idx = -1
35
35
  top = stacks_top[0]
36
36
  for dom_idx in decision_domains:
37
37
  shr_domain = shr_domains_stack[top, dom_idx]
38
- size = shr_domain[MAX] - shr_domain[MIN] # actually this is size - 1
39
- if 0 < size < min_size:
40
- min_idx = dom_idx
41
- min_size = size
42
- return min_idx
38
+ score = shr_domain[MIN] - shr_domain[MAX]
39
+ if best_score < score < 0:
40
+ best_idx = dom_idx
41
+ best_score = score
42
+ return best_idx
@@ -26,9 +26,9 @@ def function_from_address(typingctx, func_type_ref: types.FunctionType, addr: in
26
26
  func_type = func_type_ref.instance_type
27
27
 
28
28
  def codegen(context, builder, sig, args): # type: ignore
29
- _, addr = args
29
+ _, address = args
30
30
  sfunc = cgutils.create_struct_proxy(func_type)(context, builder)
31
- sfunc.addr = builder.inttoptr(addr, context.get_value_type(types.voidptr))
31
+ sfunc.addr = builder.inttoptr(address, context.get_value_type(types.voidptr))
32
32
  return sfunc._getvalue()
33
33
 
34
34
  return func_type(func_type_ref, addr), codegen
@@ -0,0 +1,36 @@
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
+ from nucs.problems.permutation_problem import PermutationProblem
14
+ from nucs.propagators.propagators import ALG_NO_SUB_CYCLE
15
+
16
+
17
+ class CircuitProblem(PermutationProblem):
18
+ """
19
+ A model for circuit.
20
+ """
21
+
22
+ def __init__(self, n: int):
23
+ """
24
+ Inits the circuit problem.
25
+ :param n: the number of vertices
26
+ """
27
+ self.n = n
28
+ super().__init__(n)
29
+ self.shr_domains_lst[0] = [1, n - 1]
30
+ self.shr_domains_lst[n - 1] = [0, n - 2]
31
+ self.shr_domains_lst[n] = [1, n - 1]
32
+ self.shr_domains_lst[2 * n - 1] = [0, n - 2]
33
+ self.add_propagator((list(range(n)), ALG_NO_SUB_CYCLE, []))
34
+ self.add_propagator((list(range(n, 2 * n)), ALG_NO_SUB_CYCLE, []))
35
+ # self.add_propagator((list(range(n)), ALG_SCC, [])) # not worth the cost
36
+ # self.add_propagator((list(range(n, 2*n)), ALG_SCC, [])) # not worth the cost
@@ -11,23 +11,24 @@
11
11
  # Copyright 2024 - Yan Georget
12
12
  ###############################################################################
13
13
  from nucs.problems.problem import Problem
14
- from nucs.propagators.propagators import ALG_ALLDIFFERENT, ALG_NO_SUB_CYCLE
14
+ from nucs.propagators.propagators import ALG_ALLDIFFERENT, ALG_PERMUTATION_AUX
15
15
 
16
16
 
17
- class CircuitProblem(Problem):
17
+ class PermutationProblem(Problem):
18
18
  """
19
- A model for circuit.
19
+ A model for permutation.
20
20
  """
21
21
 
22
22
  def __init__(self, n: int):
23
23
  """
24
- Inits the circuit problem.
25
- :param n: the number of vertices
24
+ Inits the permutation problem.
25
+ :param n: the number variables/values
26
26
  """
27
27
  self.n = n
28
- shr_domains = [(1, n - 1)] + [(0, n - 1)] * (n - 2) + [(0, n - 2)]
28
+ shr_domains = [(0, n - 1)] * 2 * n
29
29
  super().__init__(shr_domains)
30
- s_indices = list(range(n))
31
- self.add_propagator((s_indices, ALG_ALLDIFFERENT, []))
32
- self.add_propagator((s_indices, ALG_NO_SUB_CYCLE, []))
33
- # self.add_propagator((s_indices, ALG_SCC, [])) # not worth the cost
30
+ for i in range(n):
31
+ self.add_propagator((list(range(n)) + [n + i], ALG_PERMUTATION_AUX, [i]))
32
+ self.add_propagator((list(range(n, 2 * n)) + [i], ALG_PERMUTATION_AUX, [i]))
33
+ self.add_propagator((list(range(n)), ALG_ALLDIFFERENT, []))
34
+ self.add_propagator((list(range(n, 2 * n)), ALG_ALLDIFFERENT, []))
@@ -44,7 +44,7 @@ def init_partial_sum(first_value: int, m: int, values: NDArray) -> NDArray:
44
44
  """
45
45
  Inits the partial_sum data structure:
46
46
  ---------------------
47
- | sum | first_value |
47
+ | sm | first_value |
48
48
  ---------------------
49
49
  | ds | last_value |
50
50
  ---------------------
@@ -52,19 +52,19 @@ def init_partial_sum(first_value: int, m: int, values: NDArray) -> NDArray:
52
52
  partial_sum = np.zeros((2, m + 6), dtype=np.int32)
53
53
  partial_sum[0, -1] = first_value - 3
54
54
  partial_sum[1, -1] = first_value + m + 1
55
- sum = partial_sum[0, :-1]
56
- sum[0] = 0
57
- sum[1] = 1
58
- sum[2] = 2
55
+ sm = partial_sum[0, :-1]
56
+ sm[0] = 0
57
+ sm[1] = 1
58
+ sm[2] = 2
59
59
  for i in range(2, m + 2):
60
- sum[i + 1] = sum[i] + values[i - 2]
61
- sum[m + 3] = sum[m + 2] + 1
62
- sum[m + 4] = sum[m + 3] + 1
60
+ sm[i + 1] = sm[i] + values[i - 2]
61
+ sm[m + 3] = sm[m + 2] + 1
62
+ sm[m + 4] = sm[m + 3] + 1
63
63
  ds = partial_sum[1, :-1]
64
64
  i = m + 3
65
65
  j = m + 4
66
66
  while i > 0:
67
- while sum[i] == sum[i - 1]:
67
+ while sm[i] == sm[i - 1]:
68
68
  ds[i] = j
69
69
  i -= 1
70
70
  ds[j] = i
@@ -56,23 +56,26 @@ def compute_domains_no_sub_cycle(domains: NDArray, parameters: NDArray) -> int:
56
56
  while loop:
57
57
  loop = False
58
58
  for i in range(n):
59
- if domains[i, MIN] == domains[i, MAX] and paths[i, PATH_END] == i:
59
+ if domains[i, MIN] == domains[i, MAX]:
60
60
  j = domains[i, MIN]
61
- end = paths[i, PATH_END] = paths[j, PATH_END]
62
- start = paths[j, PATH_START] = paths[i, PATH_START]
63
- paths[start, PATH_END] = end
64
- paths[end, PATH_START] = start
65
- length = paths[i, PATH_LENGTH] + 1 + paths[j, PATH_LENGTH]
66
- paths[i, PATH_LENGTH] = paths[j, PATH_LENGTH] = paths[start, PATH_LENGTH] = paths[end, PATH_LENGTH] = (
67
- length
68
- )
69
- if length < n - 1:
70
- if domains[end, MIN] == start:
71
- domains[end, MIN] = start + 1
72
- if domains[end, MAX] == start:
73
- domains[end, MAX] = start - 1
74
- if domains[end, MIN] > domains[end, MAX]:
75
- return PROP_INCONSISTENCY
76
- if end < i:
77
- loop = True
61
+ if i == j:
62
+ return PROP_INCONSISTENCY
63
+ if paths[i, PATH_END] == i:
64
+ end = paths[i, PATH_END] = paths[j, PATH_END]
65
+ start = paths[j, PATH_START] = paths[i, PATH_START]
66
+ paths[start, PATH_END] = end
67
+ paths[end, PATH_START] = start
68
+ length = paths[i, PATH_LENGTH] + 1 + paths[j, PATH_LENGTH]
69
+ paths[i, PATH_LENGTH] = paths[j, PATH_LENGTH] = paths[start, PATH_LENGTH] = paths[
70
+ end, PATH_LENGTH
71
+ ] = length
72
+ if length < n - 1:
73
+ if domains[end, MIN] == start:
74
+ domains[end, MIN] = start + 1
75
+ if domains[end, MAX] == start:
76
+ domains[end, MAX] = start - 1
77
+ if domains[end, MIN] > domains[end, MAX]:
78
+ return PROP_INCONSISTENCY
79
+ if end < i:
80
+ loop = True
78
81
  return PROP_CONSISTENCY