NUCS 0.1__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-0.1/LICENSE.md +21 -0
- nucs-0.1/NUCS.egg-info/PKG-INFO +78 -0
- nucs-0.1/NUCS.egg-info/SOURCES.txt +38 -0
- nucs-0.1/NUCS.egg-info/dependency_links.txt +1 -0
- nucs-0.1/NUCS.egg-info/requires.txt +2 -0
- nucs-0.1/NUCS.egg-info/top_level.txt +1 -0
- nucs-0.1/PKG-INFO +78 -0
- nucs-0.1/README.md +63 -0
- nucs-0.1/nucs/__init__.py +0 -0
- nucs-0.1/nucs/heuristics/heuristic.py +24 -0
- nucs-0.1/nucs/heuristics/variable_heuristic.py +97 -0
- nucs-0.1/nucs/memory.py +73 -0
- nucs-0.1/nucs/problems/__init__.py +0 -0
- nucs-0.1/nucs/problems/alpha_problem.py +107 -0
- nucs-0.1/nucs/problems/bibd_problem.py +49 -0
- nucs-0.1/nucs/problems/golomb_problem.py +76 -0
- nucs-0.1/nucs/problems/problem.py +271 -0
- nucs-0.1/nucs/problems/queens_problem.py +22 -0
- nucs-0.1/nucs/problems/schur_lemma_problem.py +29 -0
- nucs-0.1/nucs/problems/sudoku_problem.py +46 -0
- nucs-0.1/nucs/propagators/__init__.py +0 -0
- nucs-0.1/nucs/propagators/affine_eq_propagator.py +55 -0
- nucs-0.1/nucs/propagators/affine_geq_propagator.py +52 -0
- nucs-0.1/nucs/propagators/affine_leq_propagator.py +52 -0
- nucs-0.1/nucs/propagators/alldifferent_propagator.py +180 -0
- nucs-0.1/nucs/propagators/count_eq_propagator.py +47 -0
- nucs-0.1/nucs/propagators/dummy_propagator.py +21 -0
- nucs-0.1/nucs/propagators/exactly_eq_propagator.py +44 -0
- nucs-0.1/nucs/propagators/lexicographic_leq_propagator.py +139 -0
- nucs-0.1/nucs/propagators/max_eq_propagator.py +36 -0
- nucs-0.1/nucs/propagators/max_leq_propagator.py +46 -0
- nucs-0.1/nucs/propagators/min_eq_propagator.py +36 -0
- nucs-0.1/nucs/propagators/min_geq_propagator.py +46 -0
- nucs-0.1/nucs/propagators/propagators.py +135 -0
- nucs-0.1/nucs/solvers/__init__.py +0 -0
- nucs-0.1/nucs/solvers/backtrack_solver.py +105 -0
- nucs-0.1/nucs/solvers/solver.py +41 -0
- nucs-0.1/nucs/statistics.py +44 -0
- nucs-0.1/pyproject.toml +31 -0
- nucs-0.1/setup.cfg +4 -0
nucs-0.1/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Yan Georget
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: NUCS
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: A Numpy and Numba based Python library for solving Constraint Satifaction Problems
|
|
5
|
+
Author-email: Yan Georget <yan.georget@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/yangeorget/nucs
|
|
7
|
+
Project-URL: Issues, https://github.com/yangeorget/nucs
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE.md
|
|
13
|
+
Requires-Dist: numba==0.60.0
|
|
14
|
+
Requires-Dist: numpy==2.0.1
|
|
15
|
+
|
|
16
|
+
# NUCS
|
|
17
|
+
|
|
18
|
+
## TLDR
|
|
19
|
+
NUCS is a Python library for solving Constraint Satisfaction and Optimization Problems.
|
|
20
|
+
|
|
21
|
+
NUCS is powered by Numpy (https://numpy.org/) and Numba (https://numba.pydata.org/).
|
|
22
|
+
|
|
23
|
+
NUCS is fast and easy to use.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## How to use NUCS ?
|
|
27
|
+
It is very simple to get started with NUCS.
|
|
28
|
+
Either clone the Github repository or install the package.
|
|
29
|
+
|
|
30
|
+
### Clone the NUCS Github repository
|
|
31
|
+
Let's install NUCS from the source:
|
|
32
|
+
```
|
|
33
|
+
git clone https://github.com/yangeorget/nucs.git
|
|
34
|
+
pip install -r requirements.txt
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
From there, you can launch some NUCS examples.
|
|
38
|
+
Note that the second run will be much faster since the Python code will have been compiled.
|
|
39
|
+
|
|
40
|
+
#### Run the NUCS tests
|
|
41
|
+
```
|
|
42
|
+
NUMBA_CACHE_DIR=.numba/cache PYTHONPATH=. pytest tests/
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### Run some examples
|
|
46
|
+
Find all solutions to the 12-queens problem:
|
|
47
|
+
```
|
|
48
|
+
NUMBA_CACHE_DIR=.numba/cache PYTHONPATH=. python3 tests/examples/test_queens.py -n 12
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Find the optimal solution to the Golomb ruler problem with 10 marks:
|
|
52
|
+
```
|
|
53
|
+
NUMBA_CACHE_DIR=.numba/cache PYTHONPATH=. python3 tests/examples/test_golomb.py -n 10
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Install the NUCS package
|
|
57
|
+
```
|
|
58
|
+
pip install nucs
|
|
59
|
+
````
|
|
60
|
+
|
|
61
|
+
## Why Python ?
|
|
62
|
+
NUCS is a Python library leveraging Numpy and Numba.
|
|
63
|
+
|
|
64
|
+
Python is a powerful and flexible programing language that allows to express complex problems in a few lines of code.
|
|
65
|
+
|
|
66
|
+
Numpy brings the computational power of languages like C and Fortran to Python, a language much easier to learn and use.
|
|
67
|
+
|
|
68
|
+
Numba translates Python functions to optimized machine code at runtime using the industry-standard LLVM compiler library.
|
|
69
|
+
Numba-compiled numerical algorithms in Python can approach the speeds of C or FORTRAN.
|
|
70
|
+
|
|
71
|
+
## Architecture
|
|
72
|
+
|
|
73
|
+
## Speed matters
|
|
74
|
+
|
|
75
|
+
## Other constraint solvers in Python
|
|
76
|
+
- python-constraint
|
|
77
|
+
- CCPMpy
|
|
78
|
+
- PyCSP
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
LICENSE.md
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
NUCS.egg-info/PKG-INFO
|
|
5
|
+
NUCS.egg-info/SOURCES.txt
|
|
6
|
+
NUCS.egg-info/dependency_links.txt
|
|
7
|
+
NUCS.egg-info/requires.txt
|
|
8
|
+
NUCS.egg-info/top_level.txt
|
|
9
|
+
nucs/__init__.py
|
|
10
|
+
nucs/memory.py
|
|
11
|
+
nucs/statistics.py
|
|
12
|
+
nucs/heuristics/heuristic.py
|
|
13
|
+
nucs/heuristics/variable_heuristic.py
|
|
14
|
+
nucs/problems/__init__.py
|
|
15
|
+
nucs/problems/alpha_problem.py
|
|
16
|
+
nucs/problems/bibd_problem.py
|
|
17
|
+
nucs/problems/golomb_problem.py
|
|
18
|
+
nucs/problems/problem.py
|
|
19
|
+
nucs/problems/queens_problem.py
|
|
20
|
+
nucs/problems/schur_lemma_problem.py
|
|
21
|
+
nucs/problems/sudoku_problem.py
|
|
22
|
+
nucs/propagators/__init__.py
|
|
23
|
+
nucs/propagators/affine_eq_propagator.py
|
|
24
|
+
nucs/propagators/affine_geq_propagator.py
|
|
25
|
+
nucs/propagators/affine_leq_propagator.py
|
|
26
|
+
nucs/propagators/alldifferent_propagator.py
|
|
27
|
+
nucs/propagators/count_eq_propagator.py
|
|
28
|
+
nucs/propagators/dummy_propagator.py
|
|
29
|
+
nucs/propagators/exactly_eq_propagator.py
|
|
30
|
+
nucs/propagators/lexicographic_leq_propagator.py
|
|
31
|
+
nucs/propagators/max_eq_propagator.py
|
|
32
|
+
nucs/propagators/max_leq_propagator.py
|
|
33
|
+
nucs/propagators/min_eq_propagator.py
|
|
34
|
+
nucs/propagators/min_geq_propagator.py
|
|
35
|
+
nucs/propagators/propagators.py
|
|
36
|
+
nucs/solvers/__init__.py
|
|
37
|
+
nucs/solvers/backtrack_solver.py
|
|
38
|
+
nucs/solvers/solver.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nucs
|
nucs-0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: NUCS
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: A Numpy and Numba based Python library for solving Constraint Satifaction Problems
|
|
5
|
+
Author-email: Yan Georget <yan.georget@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/yangeorget/nucs
|
|
7
|
+
Project-URL: Issues, https://github.com/yangeorget/nucs
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE.md
|
|
13
|
+
Requires-Dist: numba==0.60.0
|
|
14
|
+
Requires-Dist: numpy==2.0.1
|
|
15
|
+
|
|
16
|
+
# NUCS
|
|
17
|
+
|
|
18
|
+
## TLDR
|
|
19
|
+
NUCS is a Python library for solving Constraint Satisfaction and Optimization Problems.
|
|
20
|
+
|
|
21
|
+
NUCS is powered by Numpy (https://numpy.org/) and Numba (https://numba.pydata.org/).
|
|
22
|
+
|
|
23
|
+
NUCS is fast and easy to use.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## How to use NUCS ?
|
|
27
|
+
It is very simple to get started with NUCS.
|
|
28
|
+
Either clone the Github repository or install the package.
|
|
29
|
+
|
|
30
|
+
### Clone the NUCS Github repository
|
|
31
|
+
Let's install NUCS from the source:
|
|
32
|
+
```
|
|
33
|
+
git clone https://github.com/yangeorget/nucs.git
|
|
34
|
+
pip install -r requirements.txt
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
From there, you can launch some NUCS examples.
|
|
38
|
+
Note that the second run will be much faster since the Python code will have been compiled.
|
|
39
|
+
|
|
40
|
+
#### Run the NUCS tests
|
|
41
|
+
```
|
|
42
|
+
NUMBA_CACHE_DIR=.numba/cache PYTHONPATH=. pytest tests/
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### Run some examples
|
|
46
|
+
Find all solutions to the 12-queens problem:
|
|
47
|
+
```
|
|
48
|
+
NUMBA_CACHE_DIR=.numba/cache PYTHONPATH=. python3 tests/examples/test_queens.py -n 12
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Find the optimal solution to the Golomb ruler problem with 10 marks:
|
|
52
|
+
```
|
|
53
|
+
NUMBA_CACHE_DIR=.numba/cache PYTHONPATH=. python3 tests/examples/test_golomb.py -n 10
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Install the NUCS package
|
|
57
|
+
```
|
|
58
|
+
pip install nucs
|
|
59
|
+
````
|
|
60
|
+
|
|
61
|
+
## Why Python ?
|
|
62
|
+
NUCS is a Python library leveraging Numpy and Numba.
|
|
63
|
+
|
|
64
|
+
Python is a powerful and flexible programing language that allows to express complex problems in a few lines of code.
|
|
65
|
+
|
|
66
|
+
Numpy brings the computational power of languages like C and Fortran to Python, a language much easier to learn and use.
|
|
67
|
+
|
|
68
|
+
Numba translates Python functions to optimized machine code at runtime using the industry-standard LLVM compiler library.
|
|
69
|
+
Numba-compiled numerical algorithms in Python can approach the speeds of C or FORTRAN.
|
|
70
|
+
|
|
71
|
+
## Architecture
|
|
72
|
+
|
|
73
|
+
## Speed matters
|
|
74
|
+
|
|
75
|
+
## Other constraint solvers in Python
|
|
76
|
+
- python-constraint
|
|
77
|
+
- CCPMpy
|
|
78
|
+
- PyCSP
|
nucs-0.1/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# NUCS
|
|
2
|
+
|
|
3
|
+
## TLDR
|
|
4
|
+
NUCS is a Python library for solving Constraint Satisfaction and Optimization Problems.
|
|
5
|
+
|
|
6
|
+
NUCS is powered by Numpy (https://numpy.org/) and Numba (https://numba.pydata.org/).
|
|
7
|
+
|
|
8
|
+
NUCS is fast and easy to use.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## How to use NUCS ?
|
|
12
|
+
It is very simple to get started with NUCS.
|
|
13
|
+
Either clone the Github repository or install the package.
|
|
14
|
+
|
|
15
|
+
### Clone the NUCS Github repository
|
|
16
|
+
Let's install NUCS from the source:
|
|
17
|
+
```
|
|
18
|
+
git clone https://github.com/yangeorget/nucs.git
|
|
19
|
+
pip install -r requirements.txt
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
From there, you can launch some NUCS examples.
|
|
23
|
+
Note that the second run will be much faster since the Python code will have been compiled.
|
|
24
|
+
|
|
25
|
+
#### Run the NUCS tests
|
|
26
|
+
```
|
|
27
|
+
NUMBA_CACHE_DIR=.numba/cache PYTHONPATH=. pytest tests/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
#### Run some examples
|
|
31
|
+
Find all solutions to the 12-queens problem:
|
|
32
|
+
```
|
|
33
|
+
NUMBA_CACHE_DIR=.numba/cache PYTHONPATH=. python3 tests/examples/test_queens.py -n 12
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Find the optimal solution to the Golomb ruler problem with 10 marks:
|
|
37
|
+
```
|
|
38
|
+
NUMBA_CACHE_DIR=.numba/cache PYTHONPATH=. python3 tests/examples/test_golomb.py -n 10
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Install the NUCS package
|
|
42
|
+
```
|
|
43
|
+
pip install nucs
|
|
44
|
+
````
|
|
45
|
+
|
|
46
|
+
## Why Python ?
|
|
47
|
+
NUCS is a Python library leveraging Numpy and Numba.
|
|
48
|
+
|
|
49
|
+
Python is a powerful and flexible programing language that allows to express complex problems in a few lines of code.
|
|
50
|
+
|
|
51
|
+
Numpy brings the computational power of languages like C and Fortran to Python, a language much easier to learn and use.
|
|
52
|
+
|
|
53
|
+
Numba translates Python functions to optimized machine code at runtime using the industry-standard LLVM compiler library.
|
|
54
|
+
Numba-compiled numerical algorithms in Python can approach the speeds of C or FORTRAN.
|
|
55
|
+
|
|
56
|
+
## Architecture
|
|
57
|
+
|
|
58
|
+
## Speed matters
|
|
59
|
+
|
|
60
|
+
## Other constraint solvers in Python
|
|
61
|
+
- python-constraint
|
|
62
|
+
- CCPMpy
|
|
63
|
+
- PyCSP
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
from numpy.typing import NDArray
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Heuristic:
|
|
8
|
+
"""
|
|
9
|
+
Makes a choice.
|
|
10
|
+
A choice can be more complex than just instantiating a variable.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def choose(
|
|
15
|
+
self, choice_points: List[NDArray], shr_domains: NDArray, shr_domain_changes: NDArray, dom_indices: NDArray
|
|
16
|
+
) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Makes a choice.
|
|
19
|
+
:param choice_points: the choice point stack
|
|
20
|
+
:param shr_domains: the shared domains of the problem
|
|
21
|
+
:param shr_domain_changes: an array of shared domain changes
|
|
22
|
+
:param dom_indices: the domain indices of the problem variables
|
|
23
|
+
"""
|
|
24
|
+
pass
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from typing import Callable, List
|
|
3
|
+
|
|
4
|
+
from numba import njit # type: ignore
|
|
5
|
+
from numpy.typing import NDArray
|
|
6
|
+
|
|
7
|
+
from nucs.heuristics.heuristic import Heuristic
|
|
8
|
+
from nucs.memory import MAX, MIN
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class VariableHeuristic(Heuristic):
|
|
12
|
+
"""
|
|
13
|
+
Chooses a variable and one or several values for this variable.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, variable_heuristic: Callable, domain_heuristic: Callable):
|
|
17
|
+
"""
|
|
18
|
+
Inits the heuristic.
|
|
19
|
+
:param variable_heuristic: a function for choosing the variable
|
|
20
|
+
:param domain_heuristic: a function for choosing a new domain for this variable
|
|
21
|
+
"""
|
|
22
|
+
self.variable_heuristic = variable_heuristic
|
|
23
|
+
self.domain_heuristic = domain_heuristic
|
|
24
|
+
|
|
25
|
+
def choose(
|
|
26
|
+
self, choice_points: List[NDArray], shr_domains: NDArray, shr_domain_changes: NDArray, dom_indices: NDArray
|
|
27
|
+
) -> None:
|
|
28
|
+
var_idx = self.variable_heuristic(shr_domains, dom_indices)
|
|
29
|
+
shr_domains_copy = shr_domains.copy(order="F")
|
|
30
|
+
self.domain_heuristic(shr_domains, shr_domain_changes, shr_domains_copy, dom_indices[var_idx])
|
|
31
|
+
choice_points.append(shr_domains_copy)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@njit("int16(int32[::1, :], uint16[:])", cache=True)
|
|
35
|
+
def first_not_instantiated_var_heuristic(shr_domains: NDArray, dom_indices: NDArray) -> int:
|
|
36
|
+
"""
|
|
37
|
+
Chooses the first non instantiated variable.
|
|
38
|
+
:param shr_domains: the shared domains of the problem
|
|
39
|
+
:param dom_indices: the domain indices of the problem variables
|
|
40
|
+
:return: the index of the variable
|
|
41
|
+
"""
|
|
42
|
+
for var_idx, dom_index in enumerate(dom_indices):
|
|
43
|
+
if shr_domains[dom_index, MIN] < shr_domains[dom_index, MAX]:
|
|
44
|
+
return var_idx
|
|
45
|
+
return -1 # cannot happen
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@njit("int16(int32[::1, :], uint16[:])", cache=True)
|
|
49
|
+
def smallest_domain_var_heuristic(shr_domains: NDArray, dom_indices: NDArray) -> int:
|
|
50
|
+
"""
|
|
51
|
+
Chooses the variable with the smallest domain and which is not instantiated.
|
|
52
|
+
:param shr_domains: the shared domains of the problem
|
|
53
|
+
:param dom_indices: the domain indices of the problem variables
|
|
54
|
+
:return: the index of the variable
|
|
55
|
+
"""
|
|
56
|
+
min_size = sys.maxsize
|
|
57
|
+
min_idx = -1
|
|
58
|
+
for var_idx, dom_index in enumerate(dom_indices):
|
|
59
|
+
size = shr_domains[dom_index, MAX] - shr_domains[dom_index, MIN] # actually this is size - 1
|
|
60
|
+
if 0 < size < min_size:
|
|
61
|
+
min_idx = var_idx
|
|
62
|
+
min_size = size
|
|
63
|
+
return min_idx
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@njit("(int32[::1, :], boolean[::1, :], int32[::1, :], uint16)", cache=True)
|
|
67
|
+
def min_value_dom_heuristic(
|
|
68
|
+
shr_domains: NDArray, shr_domain_changes: NDArray, shr_domains_copy: NDArray, domain_idx: int
|
|
69
|
+
) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Chooses the first value of the domain
|
|
72
|
+
:param shr_domains: the shared domains of the problem
|
|
73
|
+
:param: shr_domain_changes: the changes to the shared domains
|
|
74
|
+
:param: shr_domains_copy: the shared domains to be added to the choice point
|
|
75
|
+
:param domain_idx: the index of the domain
|
|
76
|
+
"""
|
|
77
|
+
value = shr_domains[domain_idx, MIN]
|
|
78
|
+
shr_domains_copy[domain_idx, MIN] = value + 1
|
|
79
|
+
shr_domains[domain_idx, MAX] = value
|
|
80
|
+
shr_domain_changes[domain_idx, MAX] = True
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@njit("(int32[::1, :], boolean[::1, :], int32[::1, :], uint16)", cache=True)
|
|
84
|
+
def split_low_dom_heuristic(
|
|
85
|
+
shr_domains: NDArray, shr_domain_changes: NDArray, shr_domains_copy: NDArray, domain_idx: int
|
|
86
|
+
) -> None:
|
|
87
|
+
"""
|
|
88
|
+
Chooses the first half of the domain
|
|
89
|
+
:param shr_domains: the shared domains of the problem
|
|
90
|
+
:param: shr_domain_changes: the changes to the shared domains
|
|
91
|
+
:param: shr_domains_copy: the shared domains to be added to the choice point
|
|
92
|
+
:param domain_idx: the index of the domain
|
|
93
|
+
"""
|
|
94
|
+
value = (shr_domains[domain_idx, MIN] + shr_domains[domain_idx, MAX]) // 2
|
|
95
|
+
shr_domains_copy[domain_idx, MIN] = value + 1
|
|
96
|
+
shr_domains[domain_idx, MAX] = value
|
|
97
|
+
shr_domain_changes[domain_idx, MAX] = True
|
nucs-0.1/nucs/memory.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from typing import List, Tuple, Union
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from numpy.typing import NDArray
|
|
5
|
+
|
|
6
|
+
START = 0
|
|
7
|
+
END = 1
|
|
8
|
+
|
|
9
|
+
MIN = 0
|
|
10
|
+
MAX = 1
|
|
11
|
+
|
|
12
|
+
PROP_INCONSISTENCY = 0
|
|
13
|
+
PROP_CONSISTENCY = 1
|
|
14
|
+
PROP_ENTAILMENT = 2
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def new_domains_by_values(domains: List[Union[int, Tuple[int, int]]]) -> NDArray:
|
|
18
|
+
return np.array(
|
|
19
|
+
[(domain, domain) if isinstance(domain, int) else domain for domain in domains],
|
|
20
|
+
dtype=np.int32,
|
|
21
|
+
order="F",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def new_indices_by_values(dom_indices: List[int]) -> NDArray:
|
|
26
|
+
return np.array(dom_indices, dtype=np.uint16, order="C")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def new_indices(n: int) -> NDArray:
|
|
30
|
+
return np.empty(n, dtype=np.uint16, order="C")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def new_domain_offsets_by_values(dom_offsets: List[int]) -> NDArray:
|
|
34
|
+
return np.array(dom_offsets, dtype=np.int32, order="C")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def new_offsets(n: int) -> NDArray:
|
|
38
|
+
return np.empty(n, dtype=np.int32, order="C")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def new_data_by_values(data: List[int]) -> NDArray:
|
|
42
|
+
return np.array(data, dtype=np.int32, order="C")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def new_data(n: int) -> NDArray:
|
|
46
|
+
return np.empty(n, dtype=np.int32, order="C")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def new_domain_changes(n: int) -> NDArray:
|
|
50
|
+
return np.ones((n, 2), dtype=bool, order="F")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def new_triggers(n: int, init_value: bool) -> NDArray:
|
|
54
|
+
if init_value:
|
|
55
|
+
return np.ones((n, 2), dtype=bool, order="F")
|
|
56
|
+
else:
|
|
57
|
+
return np.zeros((n, 2), dtype=bool, order="F")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def new_triggered_propagators(n: int) -> NDArray:
|
|
61
|
+
return np.empty(n, dtype=bool, order="C")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def new_entailed_propagators(n: int) -> NDArray:
|
|
65
|
+
return np.zeros(n, dtype=bool, order="C")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def new_algorithms(n: int) -> NDArray:
|
|
69
|
+
return np.empty(n, dtype=np.int8, order="C")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def new_bounds(n: int) -> NDArray:
|
|
73
|
+
return np.empty((n, 2), dtype=np.uint16)
|
|
File without changes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from nucs.problems.problem import Problem
|
|
4
|
+
from nucs.propagators.propagators import ALG_AFFINE_EQ, ALG_ALLDIFFERENT
|
|
5
|
+
|
|
6
|
+
A = 0
|
|
7
|
+
B = 1
|
|
8
|
+
C = 2
|
|
9
|
+
D = 3
|
|
10
|
+
E = 4
|
|
11
|
+
F = 5
|
|
12
|
+
G = 6
|
|
13
|
+
H = 7
|
|
14
|
+
I = 8
|
|
15
|
+
J = 9
|
|
16
|
+
K = 10
|
|
17
|
+
L = 11
|
|
18
|
+
M = 12
|
|
19
|
+
N = 13
|
|
20
|
+
O = 14
|
|
21
|
+
P = 15
|
|
22
|
+
Q = 16
|
|
23
|
+
R = 17
|
|
24
|
+
S = 18
|
|
25
|
+
T = 19
|
|
26
|
+
U = 20
|
|
27
|
+
V = 21
|
|
28
|
+
W = 22
|
|
29
|
+
X = 23
|
|
30
|
+
Y = 24
|
|
31
|
+
Z = 25
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AlphaProblem(Problem):
|
|
35
|
+
"""
|
|
36
|
+
This problem comes from the newsgroup rec.puzzle.
|
|
37
|
+
The numbers from 1 to 26 are assigned to the letters of the alphabet.
|
|
38
|
+
The numbers beside each word are the total of the values assigned to the letters in the word
|
|
39
|
+
(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).
|
|
40
|
+
|
|
41
|
+
Find the value of each letter under the equations:
|
|
42
|
+
BALLET 45 GLEE 66 POLKA 59 SONG 61
|
|
43
|
+
CELLO 43 JAZZ 58 QUARTET 50 SOPRANO 82
|
|
44
|
+
CONCERT 74 LYRE 47 SAXOPHONE 134 THEME 72
|
|
45
|
+
FLUTE 30 OBOE 53 SCALE 51 VIOLIN 100
|
|
46
|
+
FUGUE 50 OPERA 65 SOLO 37 WALTZ 34
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self) -> None:
|
|
50
|
+
super().__init__(shr_domains=[(1, 26)] * 26, dom_indices=list(range(26)), dom_offsets=[0] * 26)
|
|
51
|
+
self.set_propagators(
|
|
52
|
+
[
|
|
53
|
+
([A, B, E, T, L], ALG_AFFINE_EQ, [1, 1, 1, 1, 2, 45]),
|
|
54
|
+
([C, E, O, L], ALG_AFFINE_EQ, [1, 1, 1, 2, 43]),
|
|
55
|
+
([E, O, N, R, T, C], ALG_AFFINE_EQ, [1, 1, 1, 1, 1, 2, 74]),
|
|
56
|
+
([E, F, L, U, T], ALG_AFFINE_EQ, [1, 1, 1, 1, 1, 30]),
|
|
57
|
+
([E, F, G, U], ALG_AFFINE_EQ, [1, 1, 1, 2, 50]),
|
|
58
|
+
([G, L, E], ALG_AFFINE_EQ, [1, 1, 2, 66]),
|
|
59
|
+
([A, J, Z], ALG_AFFINE_EQ, [1, 1, 2, 58]),
|
|
60
|
+
([E, L, R, Y], ALG_AFFINE_EQ, [1, 1, 1, 1, 47]),
|
|
61
|
+
([E, B, O], ALG_AFFINE_EQ, [1, 1, 2, 53]),
|
|
62
|
+
([A, E, P, O, R], ALG_AFFINE_EQ, [1, 1, 1, 1, 1, 65]),
|
|
63
|
+
([A, K, L, O, P], ALG_AFFINE_EQ, [1, 1, 1, 1, 1, 59]),
|
|
64
|
+
([A, E, Q, R, U, T], ALG_AFFINE_EQ, [1, 1, 1, 1, 1, 2, 50]),
|
|
65
|
+
([A, E, H, N, P, S, X, O], ALG_AFFINE_EQ, [1, 1, 1, 1, 1, 1, 1, 2, 134]),
|
|
66
|
+
([A, C, E, L, S], ALG_AFFINE_EQ, [1, 1, 1, 1, 1, 51]),
|
|
67
|
+
([L, S, O], ALG_AFFINE_EQ, [1, 1, 2, 37]),
|
|
68
|
+
([G, N, O, S], ALG_AFFINE_EQ, [1, 1, 1, 1, 61]),
|
|
69
|
+
([A, N, P, R, S, O], ALG_AFFINE_EQ, [1, 1, 1, 1, 1, 2, 82]),
|
|
70
|
+
([H, M, T, E], ALG_AFFINE_EQ, [1, 1, 1, 2, 72]),
|
|
71
|
+
([L, N, O, V, I], ALG_AFFINE_EQ, [1, 1, 1, 1, 2, 100]),
|
|
72
|
+
([A, L, T, W, Z], ALG_AFFINE_EQ, [1, 1, 1, 1, 1, 34]),
|
|
73
|
+
([A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z], ALG_ALLDIFFERENT, []),
|
|
74
|
+
]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def pretty_print_solution(self, solution: List[int]) -> None:
|
|
78
|
+
print(
|
|
79
|
+
{
|
|
80
|
+
"A": solution[A],
|
|
81
|
+
"B": solution[B],
|
|
82
|
+
"C": solution[C],
|
|
83
|
+
"D": solution[D],
|
|
84
|
+
"E": solution[E],
|
|
85
|
+
"F": solution[F],
|
|
86
|
+
"G": solution[G],
|
|
87
|
+
"H": solution[H],
|
|
88
|
+
"I": solution[I],
|
|
89
|
+
"J": solution[J],
|
|
90
|
+
"K": solution[K],
|
|
91
|
+
"L": solution[L],
|
|
92
|
+
"M": solution[M],
|
|
93
|
+
"N": solution[N],
|
|
94
|
+
"O": solution[O],
|
|
95
|
+
"P": solution[P],
|
|
96
|
+
"Q": solution[Q],
|
|
97
|
+
"R": solution[R],
|
|
98
|
+
"S": solution[S],
|
|
99
|
+
"T": solution[T],
|
|
100
|
+
"U": solution[U],
|
|
101
|
+
"V": solution[V],
|
|
102
|
+
"W": solution[W],
|
|
103
|
+
"X": solution[X],
|
|
104
|
+
"Y": solution[Y],
|
|
105
|
+
"Z": solution[Z],
|
|
106
|
+
}
|
|
107
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from nucs.problems.problem import Problem
|
|
2
|
+
from nucs.propagators.propagators import (
|
|
3
|
+
ALG_EXACTLY_EQ,
|
|
4
|
+
ALG_LEXICOGRAPHIC_LEQ,
|
|
5
|
+
ALG_MIN_EQ,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BIBDProblem(Problem):
|
|
10
|
+
"""
|
|
11
|
+
A simple model for the BIBD problem.
|
|
12
|
+
See https://www.csplib.org/Problems/prob028/ for a complete description of the problem.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, v: int, b: int, r: int, k: int, l: int):
|
|
16
|
+
s = (v * (v - 1)) // 2
|
|
17
|
+
n = (v + s) * b
|
|
18
|
+
super().__init__(
|
|
19
|
+
shr_domains=[(0, 1)] * n,
|
|
20
|
+
dom_indices=list(range(n)),
|
|
21
|
+
dom_offsets=[0] * n,
|
|
22
|
+
)
|
|
23
|
+
propagators = []
|
|
24
|
+
# rows: counts
|
|
25
|
+
for i in range(0, v):
|
|
26
|
+
propagators.append((list(range(i * b, (i + 1) * b)), ALG_EXACTLY_EQ, [1, r]))
|
|
27
|
+
# columns: counts
|
|
28
|
+
for j in range(0, b):
|
|
29
|
+
propagators.append((list(range(j, v * b, b)), ALG_EXACTLY_EQ, [1, k]))
|
|
30
|
+
# scalar products: conjunctions and counts
|
|
31
|
+
conj_idx = v * b
|
|
32
|
+
for i1 in range(0, v - 1):
|
|
33
|
+
for i2 in range(i1 + 1, v):
|
|
34
|
+
conj_vars = []
|
|
35
|
+
for j in range(0, b):
|
|
36
|
+
propagators.append(
|
|
37
|
+
([(i1 * b + j), (i2 * b + j), conj_idx], ALG_MIN_EQ, [])
|
|
38
|
+
) # TODO: replace by AND ?
|
|
39
|
+
conj_vars.append(conj_idx)
|
|
40
|
+
conj_idx += 1
|
|
41
|
+
propagators.append((conj_vars, ALG_EXACTLY_EQ, [1, l]))
|
|
42
|
+
# remove symmetries
|
|
43
|
+
# lexleq on rows
|
|
44
|
+
for i in range(0, v - 1):
|
|
45
|
+
propagators.append((list(range(i * b, (i + 2) * b)), ALG_LEXICOGRAPHIC_LEQ, []))
|
|
46
|
+
# lexleq on columns
|
|
47
|
+
for j in range(0, b - 1):
|
|
48
|
+
propagators.append((list(range(j, v * b, b)) + list(range(j + 1, v * b, b)), ALG_LEXICOGRAPHIC_LEQ, []))
|
|
49
|
+
self.set_propagators(propagators)
|