Skip to content

Commit 4498673

Browse files
committed
introduct leq propagator (faster than affine_leq)
1 parent ee9fffd commit 4498673

File tree

7 files changed

+123
-16
lines changed

7 files changed

+123
-16
lines changed

docs/source/reference/reference_examples.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ This problem can be run with the command:
3232
This problem leverages the propagators:
3333

3434
* :mod:`nucs.propagators.abs_eq_propagator`,
35-
* :mod:`nucs.propagators.affine_leq_propagator`,
3635
* :mod:`nucs.propagators.alldifferent_propagator`,
36+
* :mod:`nucs.propagators.leq_propagator`,
3737
* :mod:`nucs.propagators.sum_eq_propagator`.
3838

3939

@@ -114,8 +114,8 @@ This problem can be run with the command:
114114
115115
This problem leverages the propagators:
116116

117-
* :mod:`nucs.propagators.affine_leq_propagator`,
118117
* :mod:`nucs.propagators.alldifferent_propagator`,
118+
* :mod:`nucs.propagators.leq_propagator`,
119119
* :mod:`nucs.propagators.sum_eq_propagator`.
120120

121121

@@ -167,8 +167,8 @@ This problem can be run with the command:
167167
168168
This problem leverages the propagators:
169169

170-
* :mod:`nucs.propagators.affine_leq_propagator`,
171170
* :mod:`nucs.propagators.alldifferent_propagator`,
171+
* :mod:`nucs.propagators.leq_propagator`,
172172
* :mod:`nucs.propagators.sum_eq_propagator`.
173173

174174

nucs/examples/all_interval_series/all_interval_series_problem.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from numpy.typing import NDArray
1616

1717
from nucs.problems.problem import Problem
18-
from nucs.propagators.propagators import ALG_ABS_EQ, ALG_AFFINE_LEQ, ALG_ALLDIFFERENT, ALG_SUM_EQ
18+
from nucs.propagators.propagators import ALG_ABS_EQ, ALG_ALLDIFFERENT, ALG_LEQ, ALG_SUM_EQ
1919

2020

2121
class AllIntervalSeriesProblem(Problem):
@@ -37,8 +37,8 @@ def __init__(self, n: int, symmetry_breaking: bool):
3737
self.add_propagator((list(range(n)), ALG_ALLDIFFERENT, []))
3838
self.add_propagator((list(range(2 * n - 1, 3 * n - 2)), ALG_ALLDIFFERENT, []))
3939
if symmetry_breaking:
40-
self.add_propagator(([0, 1], ALG_AFFINE_LEQ, [1, -1, -1]))
41-
self.add_propagator(([3 * n - 3, 2 * n - 1], ALG_AFFINE_LEQ, [1, -1, -1]))
40+
self.add_propagator(([0, 1], ALG_LEQ, [-1]))
41+
self.add_propagator(([3 * n - 3, 2 * n - 1], ALG_LEQ, [-1]))
4242

4343
def solution_as_printable(self, solution: NDArray) -> Any:
4444
return solution[: self.n].tolist()

nucs/examples/golomb/golomb_problem.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from nucs.constants import EVENT_MASK_GROUND, EVENT_MASK_MIN, MAX, MIN
2121
from nucs.heuristics.heuristics import first_not_instantiated_var_heuristic
2222
from nucs.problems.problem import Problem
23-
from nucs.propagators.propagators import ALG_AFFINE_LEQ, ALG_ALLDIFFERENT, ALG_SUM_EQ, update_propagators
23+
from nucs.propagators.propagators import ALG_ALLDIFFERENT, ALG_LEQ, ALG_SUM_EQ, update_propagators
2424
from nucs.solvers.bound_consistency_algorithm import bound_consistency_algorithm
2525

2626
GOLOMB_LENGTHS = np.array([0, 0, 1, 3, 6, 11, 17, 25, 34, 44, 55, 72, 85, 106, 127])
@@ -92,16 +92,16 @@ def __init__(self, mark_nb: int, symmetry_breaking: bool = True) -> None:
9292
self.add_propagator(
9393
(
9494
[index(mark_nb, i, j), index(mark_nb, 0, mark_nb - 1)],
95-
ALG_AFFINE_LEQ,
96-
[1, -1, -sum_first(mark_nb - 1 - (j - i))],
95+
ALG_LEQ,
96+
[-sum_first(mark_nb - 1 - (j - i))],
9797
)
9898
)
9999
if symmetry_breaking:
100100
self.add_propagator(
101101
(
102102
[index(mark_nb, 0, 1), index(mark_nb, mark_nb - 2, mark_nb - 1)],
103-
ALG_AFFINE_LEQ,
104-
[1, -1, -1],
103+
ALG_LEQ,
104+
[-1],
105105
)
106106
)
107107

nucs/examples/magic_square/magic_square_problem.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from typing import List
1414

1515
from nucs.problems.problem import Problem
16-
from nucs.propagators.propagators import ALG_AFFINE_LEQ, ALG_ALLDIFFERENT, ALG_SUM_EQ_C
16+
from nucs.propagators.propagators import ALG_ALLDIFFERENT, ALG_LEQ, ALG_SUM_EQ_C
1717

1818

1919
class MagicSquareProblem(Problem):
@@ -43,10 +43,10 @@ def __init__(self, n: int, symmetry_breaking: bool = True):
4343
bottom_right = self.first_diag()[-1]
4444
top_right = self.second_diag()[0]
4545
bottom_left = self.second_diag()[-1]
46-
self.add_propagator(([top_left, top_right], ALG_AFFINE_LEQ, [1, -1, -1]))
47-
self.add_propagator(([top_left, bottom_left], ALG_AFFINE_LEQ, [1, -1, -1]))
48-
self.add_propagator(([top_left, bottom_right], ALG_AFFINE_LEQ, [1, -1, -1]))
49-
self.add_propagator(([top_right, bottom_left], ALG_AFFINE_LEQ, [1, -1, -1]))
46+
self.add_propagator(([top_left, top_right], ALG_LEQ, [-1]))
47+
self.add_propagator(([top_left, bottom_left], ALG_LEQ, [-1]))
48+
self.add_propagator(([top_left, bottom_right], ALG_LEQ, [-1]))
49+
self.add_propagator(([top_right, bottom_left], ALG_LEQ, [-1]))
5050

5151
def row(self, i: int) -> List[int]:
5252
return list(range(0 + i * self.n, self.n + i * self.n))

nucs/propagators/leq_propagator.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
###############################################################################
2+
# __ _ _____ _____
3+
# | \ | | / ____| / ____|
4+
# | \| | _ _ | | | (___
5+
# | . ` | | | | | | | \___ \
6+
# | |\ | | |_| | | |____ ____) |
7+
# |_| \_| \__,_| \_____| |_____/
8+
#
9+
# Fast constraint solving in Python - https://github.com/yangeorget/nucs
10+
#
11+
# Copyright 2024-2025 - Yan Georget
12+
###############################################################################
13+
from numba import njit # type: ignore
14+
from numpy.typing import NDArray
15+
16+
from nucs.constants import (
17+
EVENT_MASK_MAX,
18+
EVENT_MASK_MIN,
19+
MAX,
20+
MIN,
21+
PROP_CONSISTENCY,
22+
PROP_ENTAILMENT,
23+
PROP_INCONSISTENCY,
24+
)
25+
26+
27+
def get_complexity_leq(n: int, parameters: NDArray) -> float:
28+
"""
29+
Returns the time complexity of the propagator as a float.
30+
:param n: the number of variables
31+
:param parameters: the parameters, unused here
32+
:return: a float
33+
"""
34+
return 1
35+
36+
37+
@njit(cache=True)
38+
def get_triggers_leq(n: int, dom_idx: int, parameters: NDArray) -> int:
39+
"""
40+
Returns the triggers for this propagator.
41+
:param parameters: the parameters
42+
:return: an array of triggers
43+
"""
44+
return EVENT_MASK_MIN if dom_idx == 0 else EVENT_MASK_MAX
45+
46+
47+
@njit(cache=True)
48+
def compute_domains_leq(domains: NDArray, parameters: NDArray) -> int:
49+
"""
50+
Implements x <= y + c.
51+
:param domains: the domains of the variables, x is the first domain, y is the second domain
52+
:param parameters: the parameters of the propagator, c is the first parameter
53+
:return: the status of the propagation (consistency, inconsistency or entailment) as an int
54+
"""
55+
x = domains[0]
56+
y = domains[1]
57+
c = parameters[0]
58+
if x[MAX] <= y[MIN] + c:
59+
return PROP_ENTAILMENT
60+
x[MAX] = min(x[MAX], y[MAX] + c)
61+
if x[MIN] > x[MAX]:
62+
return PROP_INCONSISTENCY
63+
y[MIN] = max(y[MIN], x[MIN] - c)
64+
if y[MIN] > y[MAX]:
65+
return PROP_INCONSISTENCY
66+
return PROP_CONSISTENCY

nucs/propagators/propagators.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
get_triggers_equiv_eq,
9191
)
9292
from nucs.propagators.gcc_propagator import compute_domains_gcc, get_complexity_gcc, get_triggers_gcc
93+
from nucs.propagators.leq_propagator import compute_domains_leq, get_complexity_leq, get_triggers_leq
9394
from nucs.propagators.lexicographic_leq_propagator import (
9495
compute_domains_lexicographic_leq,
9596
get_complexity_lexicographic_leq,
@@ -183,6 +184,7 @@ def register_propagator(get_triggers_fct: Callable, get_complexity_fct: Callable
183184
)
184185
ALG_EQUIV_EQ = register_propagator(get_triggers_equiv_eq, get_complexity_equiv_eq, compute_domains_equiv_eq)
185186
ALG_GCC = register_propagator(get_triggers_gcc, get_complexity_gcc, compute_domains_gcc)
187+
ALG_LEQ = register_propagator(get_triggers_leq, get_complexity_leq, compute_domains_leq)
186188
ALG_LEXICOGRAPHIC_LEQ = register_propagator(
187189
get_triggers_lexicographic_leq, get_complexity_lexicographic_leq, compute_domains_lexicographic_leq
188190
)

tests/propagators/test_leq.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
###############################################################################
2+
# __ _ _____ _____
3+
# | \ | | / ____| / ____|
4+
# | \| | _ _ | | | (___
5+
# | . ` | | | | | | | \___ \
6+
# | |\ | | |_| | | |____ ____) |
7+
# |_| \_| \__,_| \_____| |_____/
8+
#
9+
# Fast constraint solving in Python - https://github.com/yangeorget/nucs
10+
#
11+
# Copyright 2024-2025 - Yan Georget
12+
###############################################################################
13+
from typing import List, Optional, Tuple, Union
14+
15+
import pytest
16+
17+
from nucs.constants import PROP_CONSISTENCY, PROP_ENTAILMENT, PROP_INCONSISTENCY
18+
from nucs.propagators.leq_propagator import compute_domains_leq
19+
from tests.propagators.propagator_test import PropagatorTest
20+
21+
22+
class TestLeq(PropagatorTest):
23+
@pytest.mark.parametrize(
24+
"domains,parameters,consistency_result,expected_domains",
25+
[
26+
([(1, 10), (1, 10)], [0], PROP_CONSISTENCY, [[1, 10], [1, 10]]),
27+
([(1, 10), (1, 10)], [-1], PROP_CONSISTENCY, [[1, 9], [2, 10]]),
28+
([(2, 3), (4, 5)], [-1], PROP_ENTAILMENT, [[2, 3], [4, 5]]),
29+
([(4, 5), (2, 3)], [0], PROP_INCONSISTENCY, None),
30+
],
31+
)
32+
def test_compute_domains(
33+
self,
34+
domains: List[Union[int, Tuple[int, int]]],
35+
parameters: List[int],
36+
consistency_result: int,
37+
expected_domains: Optional[List[List[int]]],
38+
) -> None:
39+
self.assert_compute_domains(compute_domains_leq, domains, parameters, consistency_result, expected_domains)

0 commit comments

Comments
 (0)