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.
@@ -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