NUCS 0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- NUCS-0.1.dist-info/LICENSE.md +21 -0
- NUCS-0.1.dist-info/METADATA +78 -0
- NUCS-0.1.dist-info/RECORD +35 -0
- NUCS-0.1.dist-info/WHEEL +5 -0
- NUCS-0.1.dist-info/top_level.txt +1 -0
- nucs/__init__.py +0 -0
- nucs/heuristics/heuristic.py +24 -0
- nucs/heuristics/variable_heuristic.py +97 -0
- nucs/memory.py +73 -0
- nucs/problems/__init__.py +0 -0
- nucs/problems/alpha_problem.py +107 -0
- nucs/problems/bibd_problem.py +49 -0
- nucs/problems/golomb_problem.py +76 -0
- nucs/problems/problem.py +271 -0
- nucs/problems/queens_problem.py +22 -0
- nucs/problems/schur_lemma_problem.py +29 -0
- nucs/problems/sudoku_problem.py +46 -0
- nucs/propagators/__init__.py +0 -0
- nucs/propagators/affine_eq_propagator.py +55 -0
- nucs/propagators/affine_geq_propagator.py +52 -0
- nucs/propagators/affine_leq_propagator.py +52 -0
- nucs/propagators/alldifferent_propagator.py +180 -0
- nucs/propagators/count_eq_propagator.py +47 -0
- nucs/propagators/dummy_propagator.py +21 -0
- nucs/propagators/exactly_eq_propagator.py +44 -0
- nucs/propagators/lexicographic_leq_propagator.py +139 -0
- nucs/propagators/max_eq_propagator.py +36 -0
- nucs/propagators/max_leq_propagator.py +46 -0
- nucs/propagators/min_eq_propagator.py +36 -0
- nucs/propagators/min_geq_propagator.py +46 -0
- nucs/propagators/propagators.py +135 -0
- nucs/solvers/__init__.py +0 -0
- nucs/solvers/backtrack_solver.py +105 -0
- nucs/solvers/solver.py +41 -0
- nucs/statistics.py +44 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numba import njit # type: ignore
|
|
3
|
+
from numpy.typing import NDArray
|
|
4
|
+
|
|
5
|
+
from nucs.memory import MAX, MIN, PROP_CONSISTENCY, PROP_INCONSISTENCY, new_triggers
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_triggers(n: int, data: NDArray) -> NDArray:
|
|
9
|
+
"""
|
|
10
|
+
This propagator is triggered whenever there is a change in the domain of a variable.
|
|
11
|
+
:param n: the number of variables
|
|
12
|
+
:return: an array of triggers
|
|
13
|
+
"""
|
|
14
|
+
return new_triggers(n, True)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@njit("(uint16[:], uint16, uint16, uint16)", cache=True)
|
|
18
|
+
def path_set(t: NDArray, start: int, end: int, to: int) -> None:
|
|
19
|
+
while (p := start) != end:
|
|
20
|
+
start = t[p]
|
|
21
|
+
t[p] = to
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@njit("uint16(uint16[:], uint16)", cache=True)
|
|
25
|
+
def path_min(t: NDArray, i: int) -> int:
|
|
26
|
+
while t[i] < i:
|
|
27
|
+
i = t[i]
|
|
28
|
+
return i
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@njit("uint16(uint16[:], uint16)", cache=True)
|
|
32
|
+
def path_max(t: NDArray, i: int) -> int:
|
|
33
|
+
while t[i] > i:
|
|
34
|
+
i = t[i]
|
|
35
|
+
return i
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@njit("uint16(int64, int32[::1, :], uint16[:, :], int64[:], int64[:], int32[:])", cache=True)
|
|
39
|
+
def compute_nb(
|
|
40
|
+
n: int,
|
|
41
|
+
domains: NDArray,
|
|
42
|
+
ranks: NDArray,
|
|
43
|
+
min_sorted_vars: NDArray,
|
|
44
|
+
max_sorted_vars: NDArray,
|
|
45
|
+
bounds: NDArray,
|
|
46
|
+
) -> int:
|
|
47
|
+
min_value = domains[min_sorted_vars[0], MIN]
|
|
48
|
+
max_value = domains[max_sorted_vars[0], MAX] + 1
|
|
49
|
+
last = min_value - 2
|
|
50
|
+
bounds[0] = last
|
|
51
|
+
i = j = nb = 0
|
|
52
|
+
while True:
|
|
53
|
+
if i < n and min_value <= max_value:
|
|
54
|
+
if min_value != last:
|
|
55
|
+
nb += 1
|
|
56
|
+
bounds[nb] = last = min_value
|
|
57
|
+
ranks[min_sorted_vars[i], MIN] = nb
|
|
58
|
+
i += 1
|
|
59
|
+
if i < n:
|
|
60
|
+
min_value = domains[min_sorted_vars[i], MIN]
|
|
61
|
+
else:
|
|
62
|
+
if max_value != last:
|
|
63
|
+
nb += 1
|
|
64
|
+
bounds[nb] = last = max_value
|
|
65
|
+
ranks[max_sorted_vars[j], MAX] = nb
|
|
66
|
+
j += 1
|
|
67
|
+
if j == n:
|
|
68
|
+
break
|
|
69
|
+
max_value = domains[max_sorted_vars[j], MAX] + 1
|
|
70
|
+
bounds[nb + 1] = bounds[nb] + 2
|
|
71
|
+
return nb
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@njit(
|
|
75
|
+
"boolean(int64, int64, uint16[:], int32[:], uint16[:], int32[:], int32[::1, :], uint16[:, :], int64[:])",
|
|
76
|
+
cache=True,
|
|
77
|
+
)
|
|
78
|
+
def filter_lower(
|
|
79
|
+
n: int,
|
|
80
|
+
nb: int,
|
|
81
|
+
t: NDArray,
|
|
82
|
+
d: NDArray,
|
|
83
|
+
h: NDArray,
|
|
84
|
+
bounds: NDArray,
|
|
85
|
+
domains: NDArray,
|
|
86
|
+
ranks: NDArray,
|
|
87
|
+
max_sorted_vars: NDArray,
|
|
88
|
+
) -> bool:
|
|
89
|
+
for i in range(1, nb + 2):
|
|
90
|
+
t[i] = h[i] = i - 1
|
|
91
|
+
d[i] = bounds[i] - bounds[i - 1]
|
|
92
|
+
for i in range(n):
|
|
93
|
+
max_sorted_vars_i = max_sorted_vars[i]
|
|
94
|
+
x = ranks[max_sorted_vars_i, MIN]
|
|
95
|
+
y = ranks[max_sorted_vars_i, MAX]
|
|
96
|
+
z = path_max(t, x + 1)
|
|
97
|
+
j = t[z]
|
|
98
|
+
d[z] -= 1
|
|
99
|
+
if d[z] == 0:
|
|
100
|
+
t[z] = z + 1
|
|
101
|
+
z = path_max(t, t[z])
|
|
102
|
+
t[z] = j
|
|
103
|
+
if d[z] + bounds[y] < bounds[z]: # moved above the path compression which is not the case in the paper
|
|
104
|
+
return False
|
|
105
|
+
path_set(t, x + 1, z, z) # path compression
|
|
106
|
+
if h[x] > x:
|
|
107
|
+
w = path_max(h, h[x])
|
|
108
|
+
domains[max_sorted_vars_i, MIN] = bounds[w]
|
|
109
|
+
path_set(h, x, w, w) # path compression
|
|
110
|
+
if d[z] + bounds[y] == bounds[z]:
|
|
111
|
+
path_set(h, h[y], j - 1, y) # mark hall interval
|
|
112
|
+
h[y] = j - 1 # hall interval[bounds[j], bounds[y]]
|
|
113
|
+
return True
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@njit(
|
|
117
|
+
"boolean(int64, int64, uint16[:], int32[:], uint16[:], int32[:], int32[::1, :], uint16[:, :], int64[:])",
|
|
118
|
+
cache=True,
|
|
119
|
+
)
|
|
120
|
+
def filter_upper(
|
|
121
|
+
n: int,
|
|
122
|
+
nb: int,
|
|
123
|
+
t: NDArray,
|
|
124
|
+
d: NDArray,
|
|
125
|
+
h: NDArray,
|
|
126
|
+
bounds: NDArray,
|
|
127
|
+
domains: NDArray,
|
|
128
|
+
ranks: NDArray,
|
|
129
|
+
min_sorted_vars: NDArray,
|
|
130
|
+
) -> bool:
|
|
131
|
+
for i in range(nb + 1):
|
|
132
|
+
t[i] = h[i] = i + 1
|
|
133
|
+
d[i] = bounds[i + 1] - bounds[i]
|
|
134
|
+
for i in range(n - 1, -1, -1):
|
|
135
|
+
min_sorted_vars_i = min_sorted_vars[i]
|
|
136
|
+
x = ranks[min_sorted_vars_i, MAX]
|
|
137
|
+
y = ranks[min_sorted_vars_i, MIN]
|
|
138
|
+
z = path_min(t, x - 1)
|
|
139
|
+
j = t[z]
|
|
140
|
+
d[z] -= 1
|
|
141
|
+
if d[z] == 0:
|
|
142
|
+
t[z] = z - 1
|
|
143
|
+
z = path_min(t, t[z])
|
|
144
|
+
t[z] = j
|
|
145
|
+
if d[z] + bounds[z] < bounds[y]: # moved above the path compression which is not the case in the paper
|
|
146
|
+
return False
|
|
147
|
+
path_set(t, x - 1, z, z) # path compression
|
|
148
|
+
if h[x] < x:
|
|
149
|
+
w = path_min(h, h[x])
|
|
150
|
+
domains[min_sorted_vars_i, MAX] = bounds[w] - 1
|
|
151
|
+
path_set(h, x, w, w) # path compression
|
|
152
|
+
if d[z] + bounds[z] == bounds[y]:
|
|
153
|
+
path_set(h, h[y], j + 1, y) # mark hall interval
|
|
154
|
+
h[y] = j + 1 # hall interval[bounds[j], bounds[y]]
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@njit("int64(int32[::1,:], int32[:])", cache=True)
|
|
159
|
+
def compute_domains(domains: NDArray, data: NDArray) -> int:
|
|
160
|
+
"""
|
|
161
|
+
Adapted from "A fast and simple algorithm for bounds consistency of the alldifferent constraint".
|
|
162
|
+
:param domains: the domains of the variables
|
|
163
|
+
:param data: unused here
|
|
164
|
+
"""
|
|
165
|
+
n = len(domains)
|
|
166
|
+
ranks = np.zeros((n, 2), dtype=np.uint16)
|
|
167
|
+
bounds_nb = 2 * n + 2
|
|
168
|
+
bounds = np.zeros(bounds_nb, dtype=np.int32)
|
|
169
|
+
min_sorted_vars = np.argsort(domains[:, MIN])
|
|
170
|
+
max_sorted_vars = np.argsort(domains[:, MAX])
|
|
171
|
+
nb = compute_nb(n, domains, ranks, min_sorted_vars, max_sorted_vars, bounds)
|
|
172
|
+
t = np.zeros(bounds_nb, dtype=np.uint16) # critical capacity pointers
|
|
173
|
+
d = np.zeros(bounds_nb, dtype=np.int32) # differences between critical capacities
|
|
174
|
+
h = np.zeros(bounds_nb, dtype=np.uint16) # Hall interval pointers
|
|
175
|
+
return (
|
|
176
|
+
PROP_CONSISTENCY
|
|
177
|
+
if filter_lower(n, nb, t, d, h, bounds, domains, ranks, max_sorted_vars)
|
|
178
|
+
and filter_upper(n, nb, t, d, h, bounds, domains, ranks, min_sorted_vars)
|
|
179
|
+
else PROP_INCONSISTENCY
|
|
180
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numba import njit # type: ignore
|
|
3
|
+
from numpy.typing import NDArray
|
|
4
|
+
|
|
5
|
+
from nucs.memory import (
|
|
6
|
+
MAX,
|
|
7
|
+
MIN,
|
|
8
|
+
PROP_CONSISTENCY,
|
|
9
|
+
PROP_ENTAILMENT,
|
|
10
|
+
PROP_INCONSISTENCY,
|
|
11
|
+
new_triggers,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_triggers(n: int, data: NDArray) -> NDArray:
|
|
16
|
+
"""
|
|
17
|
+
This propagator is triggered whenever there is a change in the domain of a variable.
|
|
18
|
+
:param n: the number of variables
|
|
19
|
+
:return: an array of triggers
|
|
20
|
+
"""
|
|
21
|
+
return new_triggers(n, True)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@njit("int64(int32[::1,:], int32[:])", cache=True)
|
|
25
|
+
def compute_domains(domains: NDArray, data: NDArray) -> int:
|
|
26
|
+
"""
|
|
27
|
+
Implements Sigma_i (x_i == a) = x_{n-1}.
|
|
28
|
+
:param domains: the domains of the variables
|
|
29
|
+
:param data: the parameters of the propagator
|
|
30
|
+
"""
|
|
31
|
+
x = domains[:-1]
|
|
32
|
+
value = data[0]
|
|
33
|
+
ok_count_max = len(x) - np.count_nonzero((x[:, MIN] > value) | (x[:, MAX] < value))
|
|
34
|
+
ok_count_min = np.count_nonzero((x[:, MIN] == value) & (x[:, MAX] == value))
|
|
35
|
+
counter = domains[-1]
|
|
36
|
+
counter[MIN] = max(counter[MIN], ok_count_min)
|
|
37
|
+
counter[MAX] = min(counter[MAX], ok_count_max)
|
|
38
|
+
if counter[MIN] > counter[MAX]:
|
|
39
|
+
return PROP_INCONSISTENCY
|
|
40
|
+
if ok_count_min == ok_count_max:
|
|
41
|
+
return PROP_ENTAILMENT
|
|
42
|
+
if ok_count_min == counter[MAX]: # we cannot have more domains equal to c
|
|
43
|
+
x[(x[:, MIN] == value) & (x[:, MAX] > value), MIN] = value + 1
|
|
44
|
+
x[(x[:, MIN] < value) & (x[:, MAX] == value), MAX] = value - 1
|
|
45
|
+
if ok_count_max == counter[MIN]: # we cannot have more domains different from c
|
|
46
|
+
x[(x[:, MIN] <= value) & (value <= x[:, MAX]), :] = value
|
|
47
|
+
return PROP_CONSISTENCY
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from numba import njit # type: ignore
|
|
2
|
+
from numpy.typing import NDArray
|
|
3
|
+
|
|
4
|
+
from nucs.memory import PROP_CONSISTENCY, new_triggers
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_triggers(n: int, data: NDArray) -> NDArray:
|
|
8
|
+
"""
|
|
9
|
+
This propagator is triggered whenever there is a change in the domain of a variable.
|
|
10
|
+
:param n: the number of variables
|
|
11
|
+
:return: an array of triggers
|
|
12
|
+
"""
|
|
13
|
+
return new_triggers(n, True)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@njit("int64(int32[::1,:], int32[:])", cache=True)
|
|
17
|
+
def compute_domains(domains: NDArray, data: NDArray) -> int:
|
|
18
|
+
"""
|
|
19
|
+
A propagator that does nothing.
|
|
20
|
+
"""
|
|
21
|
+
return PROP_CONSISTENCY
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numba import njit # type: ignore
|
|
3
|
+
from numpy.typing import NDArray
|
|
4
|
+
|
|
5
|
+
from nucs.memory import (
|
|
6
|
+
MAX,
|
|
7
|
+
MIN,
|
|
8
|
+
PROP_CONSISTENCY,
|
|
9
|
+
PROP_ENTAILMENT,
|
|
10
|
+
PROP_INCONSISTENCY,
|
|
11
|
+
new_triggers,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_triggers(n: int, data: NDArray) -> NDArray:
|
|
16
|
+
"""
|
|
17
|
+
This propagator is triggered whenever there is a change in the domain of a variable.
|
|
18
|
+
:param n: the number of variables
|
|
19
|
+
:return: an array of triggers
|
|
20
|
+
"""
|
|
21
|
+
return new_triggers(n, True)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@njit("int64(int32[::1,:], int32[:])", cache=True)
|
|
25
|
+
def compute_domains(x: NDArray, data: NDArray) -> int:
|
|
26
|
+
"""
|
|
27
|
+
Implements Sigma_i (x_i == a_0) = a_1.
|
|
28
|
+
:param domains: the domains of the variables
|
|
29
|
+
:param data: the parameters of the propagator
|
|
30
|
+
"""
|
|
31
|
+
value = data[0]
|
|
32
|
+
ok_count_max = len(x) - np.count_nonzero((x[:, MIN] > value) | (x[:, MAX] < value))
|
|
33
|
+
ok_count_min = np.count_nonzero((x[:, MIN] == value) & (x[:, MAX] == value))
|
|
34
|
+
counter = data[1]
|
|
35
|
+
if ok_count_min > counter or ok_count_max < counter:
|
|
36
|
+
return PROP_INCONSISTENCY
|
|
37
|
+
if ok_count_min == counter and ok_count_max == counter:
|
|
38
|
+
return PROP_ENTAILMENT
|
|
39
|
+
if ok_count_min == counter: # we cannot have more domains equal to c
|
|
40
|
+
x[(x[:, MIN] == value) & (x[:, MAX] > value), MIN] = value + 1
|
|
41
|
+
x[(x[:, MIN] < value) & (x[:, MAX] == value), MAX] = value - 1
|
|
42
|
+
if ok_count_max == counter: # we cannot have more domains different from c
|
|
43
|
+
x[(x[:, MIN] <= value) & (value <= x[:, MAX]), :] = value
|
|
44
|
+
return PROP_CONSISTENCY
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from numba import njit # type: ignore
|
|
2
|
+
from numpy.typing import NDArray
|
|
3
|
+
|
|
4
|
+
from nucs.memory import (
|
|
5
|
+
MAX,
|
|
6
|
+
MIN,
|
|
7
|
+
PROP_CONSISTENCY,
|
|
8
|
+
PROP_ENTAILMENT,
|
|
9
|
+
PROP_INCONSISTENCY,
|
|
10
|
+
new_triggers,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_triggers(n: int, data: NDArray) -> NDArray:
|
|
15
|
+
"""
|
|
16
|
+
This propagator is triggered whenever there is a change in the domain of a variable.
|
|
17
|
+
:param n: the number of variables
|
|
18
|
+
:return: an array of triggers
|
|
19
|
+
"""
|
|
20
|
+
return new_triggers(n, True)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@njit("int64(int32[:,:], int32[:,:], int64, int64, int64, int64, int64)", cache=True)
|
|
24
|
+
def compute_domains_4(x: NDArray, y: NDArray, n: int, i: int, q: int, r: int, s: int) -> int:
|
|
25
|
+
while i < n and x[i, MIN] == y[i, MAX]:
|
|
26
|
+
i += 1
|
|
27
|
+
# s = i
|
|
28
|
+
if i < n and x[i, MIN] > y[i, MAX]:
|
|
29
|
+
# xq < yq
|
|
30
|
+
x[q, MAX] = min(x[q, MAX], y[q, MAX] - 1)
|
|
31
|
+
if x[q, MAX] < x[q, MIN]:
|
|
32
|
+
return PROP_INCONSISTENCY
|
|
33
|
+
y[q, MIN] = max(y[q, MIN], x[q, MIN] + 1)
|
|
34
|
+
if y[q, MAX] < y[q, MIN]:
|
|
35
|
+
return PROP_INCONSISTENCY
|
|
36
|
+
return PROP_ENTAILMENT if x[q, MAX] < y[q, MIN] else PROP_CONSISTENCY
|
|
37
|
+
# u = 4
|
|
38
|
+
return PROP_CONSISTENCY
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@njit("int64(int32[:,:], int32[:,:], int64, int64, int64, int64, int64)", cache=True)
|
|
42
|
+
def compute_domains_3(x: NDArray, y: NDArray, n: int, i: int, q: int, r: int, s: int) -> int:
|
|
43
|
+
while i < n and x[i, MAX] == y[i, MIN]:
|
|
44
|
+
i += 1
|
|
45
|
+
# s = i
|
|
46
|
+
if i == n or x[i, MAX] < y[i, MIN]:
|
|
47
|
+
# xq <= yq
|
|
48
|
+
x[q, MAX] = min(x[q, MAX], y[q, MAX])
|
|
49
|
+
if x[q, MAX] < x[q, MIN]:
|
|
50
|
+
return PROP_INCONSISTENCY
|
|
51
|
+
y[q, MIN] = max(y[q, MIN], x[q, MIN])
|
|
52
|
+
if y[q, MAX] < y[q, MIN]:
|
|
53
|
+
return PROP_INCONSISTENCY
|
|
54
|
+
return PROP_ENTAILMENT if x[q, MAX] <= y[q, MIN] else PROP_CONSISTENCY
|
|
55
|
+
# u = 3
|
|
56
|
+
return PROP_CONSISTENCY
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@njit("int64(int32[:,:], int32[:,:], int64, int64, int64, int64, int64)", cache=True)
|
|
60
|
+
def compute_domains_2(x: NDArray, y: NDArray, n: int, i: int, q: int, r: int, s: int) -> int:
|
|
61
|
+
while i < n and x[i, MIN] == x[i, MAX] == y[i, MIN] == y[i, MAX]:
|
|
62
|
+
i += 1
|
|
63
|
+
r = i
|
|
64
|
+
if i == n or x[i, MAX] < y[i, MIN]:
|
|
65
|
+
# xq <= yq
|
|
66
|
+
x[q, MAX] = min(x[q, MAX], y[q, MAX])
|
|
67
|
+
if x[q, MAX] < x[q, MIN]:
|
|
68
|
+
return PROP_INCONSISTENCY
|
|
69
|
+
y[q, MIN] = max(y[q, MIN], x[q, MIN])
|
|
70
|
+
if y[q, MAX] < y[q, MIN]:
|
|
71
|
+
return PROP_INCONSISTENCY
|
|
72
|
+
return PROP_ENTAILMENT if x[q, MAX] <= y[q, MIN] else PROP_CONSISTENCY
|
|
73
|
+
if x[i, MIN] > y[i, MAX]:
|
|
74
|
+
# xq < yq
|
|
75
|
+
x[q, MAX] = min(x[q, MAX], y[q, MAX] - 1)
|
|
76
|
+
if x[q, MAX] < x[q, MIN]:
|
|
77
|
+
return PROP_INCONSISTENCY
|
|
78
|
+
y[q, MIN] = max(y[q, MIN], x[q, MIN] + 1)
|
|
79
|
+
if y[q, MAX] < y[q, MIN]:
|
|
80
|
+
return PROP_INCONSISTENCY
|
|
81
|
+
return PROP_ENTAILMENT if x[q, MAX] < y[q, MIN] else PROP_CONSISTENCY
|
|
82
|
+
if x[i, MAX] == y[i, MIN] and x[i, MIN] < y[i, MAX]:
|
|
83
|
+
if s > i + 1:
|
|
84
|
+
i = s
|
|
85
|
+
else:
|
|
86
|
+
i += 1
|
|
87
|
+
s = i
|
|
88
|
+
return compute_domains_3(x, y, n, i, q, r, s)
|
|
89
|
+
if x[i, MIN] == y[i, MAX] and x[i, MAX] > y[i, MIN]:
|
|
90
|
+
if s > i + 1:
|
|
91
|
+
i = s
|
|
92
|
+
else:
|
|
93
|
+
i += 1
|
|
94
|
+
s = i
|
|
95
|
+
return compute_domains_4(x, y, n, i, q, r, s)
|
|
96
|
+
# u = 2
|
|
97
|
+
return PROP_CONSISTENCY
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@njit("int64(int32[:,:], int32[:,:], int64, int64, int64, int64, int64)", cache=True)
|
|
101
|
+
def compute_domains_1(x: NDArray, y: NDArray, n: int, i: int, q: int, r: int, s: int) -> int:
|
|
102
|
+
while i < n and x[i, MIN] == y[i, MAX]:
|
|
103
|
+
# enforce xi = yi
|
|
104
|
+
x[i, MAX] = min(x[i, MAX], y[i, MAX])
|
|
105
|
+
if x[i, MAX] < x[i, MIN]:
|
|
106
|
+
return PROP_INCONSISTENCY
|
|
107
|
+
y[i, MIN] = max(y[i, MIN], x[i, MIN])
|
|
108
|
+
if y[i, MAX] < y[i, MIN]:
|
|
109
|
+
return PROP_INCONSISTENCY
|
|
110
|
+
i += 1
|
|
111
|
+
q = i
|
|
112
|
+
if i == n or x[i, MAX] < y[i, MIN]:
|
|
113
|
+
return PROP_ENTAILMENT
|
|
114
|
+
# enforce xq <= yq
|
|
115
|
+
x[i, MAX] = min(x[i, MAX], y[i, MAX])
|
|
116
|
+
if x[i, MAX] < x[i, MIN]:
|
|
117
|
+
return PROP_INCONSISTENCY
|
|
118
|
+
y[i, MIN] = max(y[i, MIN], x[i, MIN])
|
|
119
|
+
if y[i, MAX] < y[i, MIN]:
|
|
120
|
+
return PROP_INCONSISTENCY
|
|
121
|
+
if r > i + 1:
|
|
122
|
+
i = r
|
|
123
|
+
else:
|
|
124
|
+
i += 1
|
|
125
|
+
r = i
|
|
126
|
+
return compute_domains_2(x, y, n, i, q, r, s)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@njit("int64(int32[::1,:], int32[:])", cache=True)
|
|
130
|
+
def compute_domains(domains: NDArray, data: NDArray) -> int:
|
|
131
|
+
"""
|
|
132
|
+
Implements lexicographic leq.
|
|
133
|
+
See https://www.diva-portal.org/smash/record.jsf?pid=diva2:1041533.
|
|
134
|
+
:param domains: the domains of the variables
|
|
135
|
+
:param data: unused here
|
|
136
|
+
"""
|
|
137
|
+
# TODO: make incremental
|
|
138
|
+
n = len(domains) // 2
|
|
139
|
+
return compute_domains_1(domains[:n], domains[n:], n, 0, 0, 0, 0)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numba import njit # type: ignore
|
|
3
|
+
from numpy.typing import NDArray
|
|
4
|
+
|
|
5
|
+
from nucs.memory import MAX, MIN, PROP_CONSISTENCY, PROP_INCONSISTENCY, new_triggers
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_triggers(n: int, data: NDArray) -> NDArray:
|
|
9
|
+
"""
|
|
10
|
+
Returns the triggers for this propagator.
|
|
11
|
+
:param n: the number of variables
|
|
12
|
+
:return: an array of triggers
|
|
13
|
+
"""
|
|
14
|
+
triggers = new_triggers(n, True)
|
|
15
|
+
triggers[-1, MIN] = False
|
|
16
|
+
return triggers
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@njit("int64(int32[::1,:], int32[:])", cache=True)
|
|
20
|
+
def compute_domains(domains: NDArray, data: NDArray) -> int:
|
|
21
|
+
"""
|
|
22
|
+
Implements Max_i x_i = x_{n-1}.
|
|
23
|
+
:param domains: the domains of the variables
|
|
24
|
+
:param data: unused here
|
|
25
|
+
"""
|
|
26
|
+
x = domains[:-1]
|
|
27
|
+
y = domains[-1]
|
|
28
|
+
y[MIN] = max(y[MIN], np.max(x[:, MIN]))
|
|
29
|
+
y[MAX] = min(y[MAX], np.max(x[:, MAX]))
|
|
30
|
+
if y[MIN] > y[MAX]:
|
|
31
|
+
return PROP_INCONSISTENCY
|
|
32
|
+
for i in range(len(x)):
|
|
33
|
+
x[i, MAX] = min(x[i, MAX], y[MAX])
|
|
34
|
+
if x[i, MAX] < x[i, MIN]:
|
|
35
|
+
return PROP_INCONSISTENCY
|
|
36
|
+
return PROP_CONSISTENCY
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numba import njit # type: ignore
|
|
3
|
+
from numpy.typing import NDArray
|
|
4
|
+
|
|
5
|
+
from nucs.memory import (
|
|
6
|
+
MAX,
|
|
7
|
+
MIN,
|
|
8
|
+
PROP_CONSISTENCY,
|
|
9
|
+
PROP_ENTAILMENT,
|
|
10
|
+
PROP_INCONSISTENCY,
|
|
11
|
+
new_triggers,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_triggers(n: int, data: NDArray) -> NDArray:
|
|
16
|
+
"""
|
|
17
|
+
Returns the triggers for this propagator.
|
|
18
|
+
:param n: the number of variables
|
|
19
|
+
:return: an array of triggers
|
|
20
|
+
"""
|
|
21
|
+
triggers = new_triggers(n, False)
|
|
22
|
+
for i in range(n - 1):
|
|
23
|
+
triggers[i, MIN] = True
|
|
24
|
+
triggers[-1, MAX] = True
|
|
25
|
+
return triggers
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@njit("int64(int32[::1,:], int32[:])", cache=True)
|
|
29
|
+
def compute_domains(domains: NDArray, data: NDArray) -> int:
|
|
30
|
+
"""
|
|
31
|
+
Implements Max_i x_i <= x_{n-1}.
|
|
32
|
+
:param domains: the domains of the variables
|
|
33
|
+
:param data: unused here
|
|
34
|
+
"""
|
|
35
|
+
x = domains[:-1]
|
|
36
|
+
y = domains[-1]
|
|
37
|
+
if np.max(x[:, MAX]) <= y[MIN]:
|
|
38
|
+
return PROP_ENTAILMENT
|
|
39
|
+
y[MIN] = max(y[MIN], np.max(x[:, MIN]))
|
|
40
|
+
if y[MIN] > y[MAX]:
|
|
41
|
+
return PROP_INCONSISTENCY
|
|
42
|
+
for i in range(len(x)):
|
|
43
|
+
x[i, MAX] = min(x[i, MAX], y[MAX])
|
|
44
|
+
if x[i, MAX] < x[i, MIN]:
|
|
45
|
+
return PROP_INCONSISTENCY
|
|
46
|
+
return PROP_CONSISTENCY
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numba import njit # type: ignore
|
|
3
|
+
from numpy.typing import NDArray
|
|
4
|
+
|
|
5
|
+
from nucs.memory import MAX, MIN, PROP_CONSISTENCY, PROP_INCONSISTENCY, new_triggers
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_triggers(n: int, data: NDArray) -> NDArray:
|
|
9
|
+
"""
|
|
10
|
+
Returns the triggers for this propagator.
|
|
11
|
+
:param n: the number of variables
|
|
12
|
+
:return: an array of triggers
|
|
13
|
+
"""
|
|
14
|
+
triggers = new_triggers(n, True)
|
|
15
|
+
triggers[-1, MAX] = False
|
|
16
|
+
return triggers
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@njit("int64(int32[::1,:], int32[:])", cache=True)
|
|
20
|
+
def compute_domains(domains: NDArray, data: NDArray) -> int:
|
|
21
|
+
"""
|
|
22
|
+
Implements Min_i x_i = x_{n-1}.
|
|
23
|
+
:param domains: the domains of the variables
|
|
24
|
+
:param data: unused here
|
|
25
|
+
"""
|
|
26
|
+
x = domains[:-1]
|
|
27
|
+
y = domains[-1]
|
|
28
|
+
y[MIN] = max(y[MIN], np.min(x[:, MIN]))
|
|
29
|
+
y[MAX] = min(y[MAX], np.min(x[:, MAX]))
|
|
30
|
+
if y[MIN] > y[MAX]:
|
|
31
|
+
return PROP_INCONSISTENCY
|
|
32
|
+
for i in range(len(x)):
|
|
33
|
+
x[i, MIN] = max(x[i, MIN], y[MIN])
|
|
34
|
+
if x[i, MAX] < x[i, MIN]:
|
|
35
|
+
return PROP_INCONSISTENCY
|
|
36
|
+
return PROP_CONSISTENCY
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numba import njit # type: ignore
|
|
3
|
+
from numpy.typing import NDArray
|
|
4
|
+
|
|
5
|
+
from nucs.memory import (
|
|
6
|
+
MAX,
|
|
7
|
+
MIN,
|
|
8
|
+
PROP_CONSISTENCY,
|
|
9
|
+
PROP_ENTAILMENT,
|
|
10
|
+
PROP_INCONSISTENCY,
|
|
11
|
+
new_triggers,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_triggers(n: int, data: NDArray) -> NDArray:
|
|
16
|
+
"""
|
|
17
|
+
Returns the triggers for this propagator.
|
|
18
|
+
:param n: the number of variables
|
|
19
|
+
:return: an array of triggers
|
|
20
|
+
"""
|
|
21
|
+
triggers = new_triggers(n, False)
|
|
22
|
+
for i in range(n - 1):
|
|
23
|
+
triggers[i, MAX] = True
|
|
24
|
+
triggers[-1, MIN] = True
|
|
25
|
+
return triggers
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@njit("int64(int32[::1,:], int32[:])", cache=True)
|
|
29
|
+
def compute_domains(domains: NDArray, data: NDArray) -> int:
|
|
30
|
+
"""
|
|
31
|
+
Implements Min_i x_i >= x_{n-1}.
|
|
32
|
+
:param domains: the domains of the variables
|
|
33
|
+
:param data: unused here
|
|
34
|
+
"""
|
|
35
|
+
x = domains[:-1]
|
|
36
|
+
y = domains[-1]
|
|
37
|
+
if y[MAX] <= np.min(x[:, MIN]):
|
|
38
|
+
return PROP_ENTAILMENT
|
|
39
|
+
y[MAX] = min(y[MAX], np.min(x[:, MAX]))
|
|
40
|
+
if y[MIN] > y[MAX]:
|
|
41
|
+
return PROP_INCONSISTENCY
|
|
42
|
+
for i in range(len(x)):
|
|
43
|
+
x[i, MIN] = max(x[i, MIN], y[MIN])
|
|
44
|
+
if x[i, MAX] < x[i, MIN]:
|
|
45
|
+
return PROP_INCONSISTENCY
|
|
46
|
+
return PROP_CONSISTENCY
|