Skip to content

Unit tests of the constraints Maximum and Minimum, and fixes of #244 to #251 - #243

Merged
xcsp3team merged 15 commits into
masterfrom
tests-maximum-minimum
Oct 19, 2026
Merged

xcsp3team merged 15 commits into
masterfrom
tests-maximum-minimum

Conversation

@szczepanskiNicolas

@szczepanskiNicolas szczepanskiNicolas commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Unit tests of the constraints Maximum and Minimum of the section Constraints (Global) of the API (https://pycsp.org/documentation/api/constraints-global/#Maximum), written as for the previous global constraints (#66, #130, #135, #141, #148, #156, #169, #213, #226), in a single file: each test is run for Maximum() (its solutions compared with max()) and for Minimum() (compared with min()), and fixes of the issues found (#244 to #251).

Tests

pycsp3/tests/test_constraints_global_maximum_minimum.py: conditions given by operators (==, !=, <, ..., in, not in, with integers, variables, expressions, ranges, sets, empty sets) or by the parameter condition; forms of the terms (lists, generators, several arrays, matrices, arrays with holes); sizes and domains (holes, negative values); expressions (generalized form), integers and components (Sum, Count, NValues, nested extremums) among the terms; single and repeated terms; extremums as terms of expressions; groups, functions, Slide; logical contexts (|, &, ~, imply, iff, xor, If, either, both, ...); meta-constraints; objectives (minimize, maximize, with expressions, integers, components); examples of the documentation; XCSP3 files; invalid arguments; variables among the values of in with all the constraints having a condition, and in intension. The constraints maximum and minimum are not part of the mini-tracks, so there is no test with -mini.

Commit Tests Passed Expected failures Skipped
a993a79 (tests) 1086 896 162 28
2812f37 (fixes) 1340 1232 79 29
117dd3d (Refs #249) 1370 1261 79 30

Whole suite at 117dd3d: 11135 passed, 471 skipped, 622 xfailed, 0 failed. Each fix comes with tests that fail without it. PyCSP3-models (one instance per model): at 2812f37, 412 of 415 XCSP3 files identical to master (414 of 415 identical between 2812f37 and 117dd3d), compilation 236.0 s on master and 230.7 s with the fixes; the 3 differences: AccordionMini (MemoryError on both sides, with a cap of 3 GB), League13 (25 auxiliary variables instead of 80, the zeros of rankings[j] * (x[j] == i) being reduced to one, #248), OPD (Maximum(x[i] * x[j] for ...) now gives the same XCSP3 file as the list form, #251; on a reduced version, ACE and CHOCO find the solutions of brute force with both files).

Issues of PyCSP3

Issue Commit Tests
#244: the parameter condition of Maximum() and Minimum() was written as it is (eq 2), neither built nor checked; Condition.build_condition() now also reports an operator that cannot be applied to its operand (('in', 2)) 4dc2fed 72
#245: a single term with the parameter condition gave a constraint on one term; the condition is now applied to the term 3e9c545 14
#246: no term: an explicit error instead of an assert without message 1ab2831 16
#247: invalid terms: symbolic variables and Booleans refused with explicit messages 9207ae8 12
#248 (question, decision B): the integers are evaluated (a constant for integers only, reduced to the best one otherwise) 5b3ad7a 22
#250 (decision A): each term given several times is kept once 4719e6c 17
#251: the terms following a generator were ignored (also in MaximumArg() and MinimumArg()) 5e4b3b7 14
#249 (decision D): a variable among the values of in/not in with any constraint having a condition (Maximum(), Sum(), Count(), NValues(), MaximumArg(), element, Cumulative(), BinPacking(), Flow(), ...) is reported by an explicit error; intension remains permissive (x[0] in {y, 2}), and x[0] * 2 not in {y, z, 1} no longer gives a sum with variables in its condition 2812f37 99
#249: any value other than an integer (a float, a symbol, None, a tuple) is reported too (Sum(x) in {1.5, 2} gave (in,{1.5,2}), s[i] in {'a', 'b'} gave (in,{a,b}), not accepted by the parser of XCSP3) 117dd3d 25

The last column gives the number of tests of the commit that fail on the code of the previous commit.

Other changes

Commit Change
67625f6 The builds (build-linux.yml, build-macos.yml, build-windows.yml) were run for each commit of a pull request; they are now run at each push on master (so when a pull request is merged), when a tag is pushed, on a pull request only when the label run-tests is added (as the tests), and on demand (workflow_dispatch). The section about the continuous integration of pycsp3/tests/README.md is updated.
870befb The macOS tests failed at the merge of #226 (run 39233333994): test_cardinality_occurrences_given_by_variables[COSOCO-{0: z, 7: z}] gave XPASS(strict), the heap overflow of cosoco (xcsp3team/cosoco#98) being detected by the allocator of glibc on Linux (crash) but not on macOS (right solutions by chance). The case is now skipped for cosoco outside Linux, the strict mark being kept on Linux.
99e36de In the README, the line of the badges of the builds is now before the line of the badges of the tests.

Problems of the solvers

Solver Problem Issue Expected failures
ACE maximum and minimum with (ne,k): ! ERROR (AssertionError: NE is not implemented in ExtremumCst.buildFrom()) xcsp3team/ACE#34 6
cosoco maximum and minimum: (ne,k) posted as (eq,k); (lt|le|gt|ge,y) posted as op(y,max(X)); intervals with swapped bounds and notin posted as in; sets handled as intervals; segmentation fault of maximum with (eq,y) (null sentinel in MaximumVariableEQ) xcsp3team/cosoco#99 51
CHOCO maximum and minimum with (in,a..b) and (notin,a..b): wrong solutions with 4.10.15-beta (embedded in PyCSP3) and 6.0.2, fixed in the develop version (859fdde) not reported 6
ACE, CHOCO a table with an empty set of supports (Maximum(x) in set()) not reported, as decided before 16
ACE maximum on a list made of a single variable given several times (x[0] x[0]) with (eq,k) behaves as (le,k) not reported: no longer generated (#250) 0

Cases not handled by the solvers (skipped)

Case ACE CHOCO cosoco
maximum and minimum on expressions, except with (eq,y) not yet supported (s UNSUPPORTED): 23 tests

The 7 other skipped tests are the parameter condition of MaximumArg(), MinimumArg() and element, which have no such parameter or do not build it yet (to be tested with these constraints).

Found, to be handled with the next constraints

  • The parameter condition of MaximumArg() and MinimumArg() is not built (as The parameter condition of Maximum() and Minimum() is written as it is (neither built nor checked) #244).
  • Knapsack(...) in {1, 3} fails with an assert without message, and m[i][j] in {1, 3} (element on a matrix) with "Problem with an indexing expression".
  • A constraint element without index (member), generated for a variable or a component in a list of four variables or more: ACE gives wrong solutions, CHOCO gives solutions twice, and cosoco does not handle it (s UNSUPPORTED); to be reported with the tests of Element.

Decisions

A. Repeated terms: (a) each term is kept once by PyCSP3, as for NotAllEqual() (#134); the problem of ACE is then not reported (#250).

B. Extremum of integers (#248): (a) evaluated by PyCSP3: Maximum(3, 1) == y gives y == 3, an objective made of integers only is refused (with the message of #221, as for NValues() of a single term, or Sum() and Count() of no term), and, with variables, the integers are reduced to the largest one (the smallest one for Minimum()), replaced by a single auxiliary variable. (The question said "as for Sum()", which was wrong: Sum(3, 5) also gives an auxiliary variable for each integer.)

C. Single term with the parameter condition (#245): (a) the condition applied to the term, as Sum() does.

D. Variable among the values of in (#249): (a) an explicit error, for all the constraints having a condition, the values of a condition in/notin being integers (any other value is refused); lists of variables are refused too; intension remains permissive.

E. ACE and (ne,k): (a) reported (xcsp3team/ACE#34).

F. Issues of the solvers: created (xcsp3team/ACE#34, xcsp3team/cosoco#99).

… the marks of the Maximum and Minimum tests

Tests of #250 (a term given several times is kept once): test_xcsp3_extremum_with_a_repeated_term and
test_xcsp3_extremum_of_a_single_repeated_term.
The condition given by the parameter condition is built by Condition.build_condition(), as for Sum(),
instead of being written as it is (eq 2); an empty set of values gives false with in, and true with notin.
Condition.build_condition() now reports an operator that cannot be applied to its operand, as ('in', 2)
or ('eq', {1, 2}), instead of an assert without message.

Fixes #244
…imum()

As XCSP3-core requires at least two terms, the condition given to Maximum() or Minimum() of a single term is
applied to this term, as for Sum() (Maximum(x[0], condition=('ge', 2)) gives x[0] >= 2).

Fixes #245
Maximum() and Minimum() of no term (as Maximum([]) or Maximum(None)) fail with the message "Maximum() requires
at least one term", instead of an assert without message.

Fixes #246
…nd Minimum()

A Boolean among the terms fails with "Maximum() does not accept Booleans as terms, which is the case of True"
(instead of a message about the values of a domain), and a symbolic variable with "Maximum() requires integer
variables (or expressions), which is not the case of s[0]" (instead of being accepted), as for Sum().

Fixes #247
The extremum of integers only is a constant (Maximum(3, 1) == y gives y == 3, and minimize(Maximum(3, 1)) is
refused, there being nothing to optimize); with other terms, the integers are reduced to the largest one (the
smallest one for Minimum()), replaced by a single auxiliary variable. A constant term (as Sum([]), or x >= x
simplified into 1) is an integer, instead of being written in the list.

Fixes #248
As the maximum and the minimum do not depend on repetitions, each term is kept once (variables compared by
identity, expressions by their text, identical components being replaced by the same auxiliary variable), as for
NotAllEqual() (#134); a single remaining term gives an expression (Maximum(x[0], x[0]) == 2 gives x[0] == 2).
Then ACE no longer finds wrong solutions for maximum on [x[0] x[0]], and cosoco no longer refuses a repeated
variable in minimum.

Fixes #250
…Arg() and MinimumArg()

The terms were given by list(term) when the first argument is a generator, the other terms being ignored
(Maximum((x[i] for i in range(2)), x[2]) gave a maximum of x[0] and x[1]): they are now given by
flatten(term, others), which handles generators, as for Sum().

Fixes #251
The values of a condition in (or notin) of a constraint (maximum, minimum, sum, count, nValues, element,
cumulative, binPacking, flow, ...) must be integers: Condition.build_condition() reports a variable (or an
expression) among them, instead of writing it in the condition (Maximum(x) in {y, 2} gave (in,{y,2}), which is
not XCSP3), also for the parameter condition. A list (or a tuple) of values with a variable, as in
Maximum(x) in [y, 2] or Sum(x) in [y, z], is given to the condition, so as to be reported when the constraint is
posted, instead of failing with an assert without message or of being accepted. In intension, variables remain
accepted among the values (x[0] in {y, 2}); a variable with a coefficient (x[0] * 2 not in {y, z, 1}) gives a sum
of one term (#208) only with integers, and an expression in intension otherwise.

Fixes #249
@szczepanskiNicolas szczepanskiNicolas changed the title Unit tests of the constraints Maximum and Minimum Unit tests of the constraints Maximum and Minimum, and fixes of #244 to #251 Oct 19, 2026
The values of a condition in (or notin) must be integers: Condition.build_condition() now reports any other
value (a float, a symbol, None, a tuple, ...), not only a variable or an expression (Sum(x) in {1.5, 2} gave
(in,{1.5,2}), and s[i] in {'a', 'b'} gave (in,{a,b}), which the parser of XCSP3 does not accept); a list (or a
tuple) of such values with a constraint is given to the condition, instead of failing with an assert without
message (Sum(x) in ['a', 2]).

Refs #249
…and on demand

The builds were run for each commit of a pull request (event pull_request without types). They are now run at each
push on master (so when a pull request is merged) and when a tag is pushed; on a pull request, only when the label
run-tests is added (as for the tests); and on demand from the tab Actions (workflow_dispatch). The section about
the continuous integration of the README of the tests is updated (Windows, label run-tests).
With occurrences={0: z, 7: z}, cosoco writes out of an array on the heap (xcsp3team/cosoco#98): the corruption
is detected by the allocator of glibc on Linux (crash, hence the strict mark), but not on macOS, where cosoco may
give the right solutions (XPASS(strict) in the run of the tests at the merge of #226). The case is skipped for
cosoco outside Linux, the result being undefined there; the strict mark is kept on Linux.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants