From 4899ea7f328d01684594c6a23a4a1c7609711126 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 18:18:36 +0200 Subject: [PATCH 01/22] Expose missing native propagators through MiniZinc and FlatZinc --- CMakeLists.txt | 11 + Makefile.in | 1 + cmake/GecodeSources.cmake | 1 + configure.ac | 2 +- docs/minizinc-constraint-audit.md | 96 ++++ gecode/flatzinc/flatzinc.cpp | 31 +- .../mznlib/fzn_all_different_int_opt.mzn | 54 ++ .../mznlib/fzn_all_equal_int_reif.mzn | 38 ++ .../mznlib/fzn_alldifferent_except.mzn | 57 ++ .../mznlib/fzn_alldifferent_except_0.mzn | 54 +- .../mznlib/fzn_array_set_intersect.mzn | 39 ++ gecode/flatzinc/mznlib/fzn_count_geq.mzn | 38 ++ gecode/flatzinc/mznlib/fzn_count_gt.mzn | 38 ++ gecode/flatzinc/mznlib/fzn_count_leq.mzn | 38 ++ gecode/flatzinc/mznlib/fzn_count_lt.mzn | 38 ++ gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn | 30 +- .../mznlib/fzn_disjunctive_strict_opt.mzn | 15 +- .../flatzinc/mznlib/fzn_if_then_else_bool.mzn | 49 ++ .../mznlib/fzn_if_then_else_float.mzn | 49 ++ .../flatzinc/mznlib/fzn_if_then_else_int.mzn | 49 ++ .../flatzinc/mznlib/fzn_if_then_else_set.mzn | 49 ++ .../mznlib/fzn_if_then_else_var_bool.mzn | 49 ++ .../mznlib/fzn_if_then_else_var_float.mzn | 49 ++ .../mznlib/fzn_if_then_else_var_int.mzn | 49 ++ .../mznlib/fzn_if_then_else_var_set.mzn | 49 ++ gecode/flatzinc/mznlib/gecode.mzn | 273 ++++++++- gecode/flatzinc/mznlib/gecode_fzn.mzn | 216 ++++++++ gecode/flatzinc/mznlib/redefinitions-2.0.mzn | 40 +- .../flatzinc/mznlib/redefinitions-2.1.1.mzn | 103 ++++ .../flatzinc/mznlib/redefinitions-2.5.2.mzn | 15 +- gecode/flatzinc/mznlib/redefinitions.mzn | 62 +++ .../mznlib/solver-native-overloads.mzn | 107 ++++ gecode/flatzinc/registry.cpp | 522 +++++++++++++++++- gecode/float/transcendental/exp-log.hpp | 27 +- test/flatzinc/native-registry.cpp | 110 ++++ test/float/transcendental.cpp | 72 ++- 36 files changed, 2436 insertions(+), 84 deletions(-) create mode 100644 docs/minizinc-constraint-audit.md create mode 100644 gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_all_equal_int_reif.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_count_geq.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_count_gt.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_count_leq.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_count_lt.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn create mode 100644 gecode/flatzinc/mznlib/gecode_fzn.mzn create mode 100644 gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn create mode 100644 gecode/flatzinc/mznlib/solver-native-overloads.mzn create mode 100644 test/flatzinc/native-registry.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 01b980c7be..3a95d92708 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1263,6 +1263,17 @@ if(GECODE_ENABLE_SET_VARS) endif() if(GECODE_ENABLE_FLOAT_VARS) gecode_link_component(float int kernel) + # FloatVal arithmetic changes the rounding mode, including in public headers. + if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$") + check_cxx_compiler_flag(-frounding-math HAVE_ROUNDING_MATH_FLAG) + if(HAVE_ROUNDING_MATH_FLAG) + foreach(kind shared static) + if(TARGET gecodefloat_${kind}) + target_compile_options(gecodefloat_${kind} PUBLIC -frounding-math) + endif() + endforeach() + endif() + endif() if(GECODE_ENABLE_MPFR AND MPFR_FOUND) foreach(kind shared static) if(TARGET gecodefloat_${kind}) diff --git a/Makefile.in b/Makefile.in index 8e5d2c625d..aa182f5cde 100755 --- a/Makefile.in +++ b/Makefile.in @@ -1157,6 +1157,7 @@ FLATZINCTESTSRC0 = \ test/flatzinc/bugfix_r7854.cpp \ test/flatzinc/empty_domain_1.cpp \ test/flatzinc/empty_domain_2.cpp \ + test/flatzinc/native-registry.cpp \ test/flatzinc/int_set_as_type1.cpp \ test/flatzinc/int_set_as_type2.cpp \ test/flatzinc/issue166.cpp \ diff --git a/cmake/GecodeSources.cmake b/cmake/GecodeSources.cmake index 58a415eb36..e762f19034 100644 --- a/cmake/GecodeSources.cmake +++ b/cmake/GecodeSources.cmake @@ -316,6 +316,7 @@ set(GECODE_TEST_SOURCES test/flatzinc/eq20.cpp test/flatzinc/factory_planning_instance.cpp test/flatzinc/golomb.cpp + test/flatzinc/native-registry.cpp test/flatzinc/int_set_as_type1.cpp test/flatzinc/int_set_as_type2.cpp test/flatzinc/issue166.cpp diff --git a/configure.ac b/configure.ac index b2c3ef0d58..f83bf81ef6 100644 --- a/configure.ac +++ b/configure.ac @@ -177,7 +177,7 @@ gnu) dnl but -funsafe-math-optimizations break IEEE float compatibility, so we have to avoid it AC_GECODE_CHECK_COMPILERFLAG([-fno-math-errno]) AC_GECODE_CHECK_COMPILERFLAG([-ffinite-math-only]) - AC_GECODE_CHECK_COMPILERFLAG([-fno-rounding-math]) + AC_GECODE_CHECK_COMPILERFLAG([-frounding-math]) AC_GECODE_CHECK_COMPILERFLAG([-fno-signaling-nans]) AC_GECODE_CHECK_COMPILERFLAG([-fcx-limited-range]) AC_GECODE_CHECK_COMPILERFLAG([-mthreads]) diff --git a/docs/minizinc-constraint-audit.md b/docs/minizinc-constraint-audit.md new file mode 100644 index 0000000000..2f73906725 --- /dev/null +++ b/docs/minizinc-constraint-audit.md @@ -0,0 +1,96 @@ +# MiniZinc native propagator audit + +The audit asks whether a standard MiniZinc expression already reaches a native Gecode propagator, including compiler rewrites and solver-library definitions. When a standard solver hook can select a missing actor, the implementation upgrades that hook. Additional public `gecode_*` predicates cover the remaining native capabilities. A different decomposition alone does not establish that a new name is needed. + +Verification uses MiniZinc 2.10.1 (build 33348285743) and this checkout's FlatZinc solver. Standard-library filenames below refer to that installation. A local MiniZinc 2.9.3 compiler checkout supplied additional source evidence; the installed compiler's generated FlatZinc was checked independently. Native declarations and posting code are in [int.hh](../gecode/int.hh), [set.hh](../gecode/set.hh), [float.hh](../gecode/float.hh), and their implementation directories. + +[gecode.mzn](../gecode/flatzinc/mznlib/gecode.mzn) supplies the public overloads and semantic relation/task enums. [gecode_fzn.mzn](../gecode/flatzinc/mznlib/gecode_fzn.mzn) contains typed primitive declarations, including internal leaves used by standard translations. [registry.cpp](../gecode/flatzinc/registry.cpp) posts those primitives. Existing registry aliases are reused rather than registered twice. Both build systems install the entire solver-library directory. + +## Automatic standard routes + +These capabilities receive no additional public modeling names. The solver library selects the native actor from standard syntax, preserving the standard relation and its boundary behavior. + +| Standard expression or global | Solver route | +| --- | --- | +| Optional all-different, single-exception all-different | `fzn_all_different_int_opt.mzn`, `fzn_alldifferent_except.mzn`, and `fzn_alldifferent_except_0.mzn` use native optional/single-exception distinct. Multiple exceptions and reified cases retain suitable decomposition. | +| Scalar count comparisons | Four `fzn_count_*` inequality overrides call the native count relation. MiniZinc rewrites comparisons on scalar `count` and corresponding equality comprehensions to these hooks. Standard `count_leq` means `n <= count`, so its native relation is greater-or-equal. | +| Binary conditionals, all four variable types | Eight `fzn_if_then_else[_var]_{int,bool,float,set}.mzn` overrides select native ITE for two branches ending in a known true condition. Longer and partial condition lists retain standard guarded-equality behavior. | +| Integer/Boolean array `!=` | `solver-native-overloads.mzn` preserves standard matching-index checks, then uses native array disequality in positive root contexts. Other contexts retain existential scalar disequality. Integer enums are converted explicitly. | +| `not all_equal(x)` | The integer reification hook and Boolean overload select native not-all-equal when the result is fixed false. General reification keeps the standard linear-size comparison translation. | +| Boolean `nvalue` equality | A typed Boolean overload selects native Boolean nvalues. Integer equality already had a native route. | +| Boolean `regular` | Typed standard overloads normalize Boolean transitions and select the native Boolean DFA. Guarded/reified expressions retain the standard integer translation. | +| Optional cumulative, optional strict disjunctive | Native flexible optional tasks support variable durations, and optional cumulative now accepts variable capacity. Standard cumulative filters zero-duration tasks; strict disjunctive retains zero-duration ordering. | +| Array intersection of sets | `fzn_array_set_intersect.mzn` selects native nary intersection for nonempty arrays. Empty standard intersection remains false. | +| Two-dimensional set lookup | `redefinitions-2.5.2.mzn` selects native row/column set element, shifting standard indexes. Empty lookup remains undefined. | +| Set `min`/`max` | The documented `redefinitions-2.1.1.mzn` function hooks select native extrema. Partial contexts guard native implication with nonemptiness, without an extra surrogate set. Integer and enum sets preserve standard undefinedness. | +| Float array `min`/`max` | `redefinitions-2.0.mzn` selects native nary extrema rather than a binary chain. Empty expressions retain their standard errors. | +| Float power | The standard `float_pow` hook selects native fixed nonnegative integer exponent or fixed positive base. General varying-base/varying-exponent power was unsupported before and remains unsupported, with a clear compile error. | +| Scalar float implication | Existing scalar and normalized linear implication hooks select native scalar implication. General linear forms retain equivalence plus implication. | +| Set equality/disequality/subset/superset implication | Standard half-reification hooks select native implication. | +| Negated integer/Boolean tables | Already exposed: a positive reified table with result false selects native negative actors in the compact, compressed, and sparse posting implementations. No negative-table names or posters are added. | + +The additional typed standard overloads are included automatically from `redefinitions.mzn`. Models do not need to include `gecode.mzn` to receive these routes. + +## Remaining public families + +The following names cover capabilities without a matching standard automatic route. A family may also accept cases already exposed; those cases reuse existing routes rather than adding another actor. + +| Public names | Why the remaining native variant is needed | +| --- | --- | +| `gecode_inter_distance` | Native pairwise minimum absolute distance, including variable distance and basic/advanced algorithms; pairwise arithmetic has no matching global rewrite. | +| `gecode_all_different` | Offset form only. Standard arithmetic plus all-different introduces auxiliary variables instead of native offset views. The registry alias already existed. | +| `gecode_channel` | Native one-hot Boolean-array/index and direct Boolean/float channels. `bool2float` has an existing function body through integer conversion, without a solver override hook. | +| `gecode_sort_permutation` | Native input-to-output permutation permits arbitrary tie order. Standard `arg_sort` returns a stable inverse mapping. | +| `gecode_count` | Set-target inequalities and position-dependent target arrays have no compiler comparison rewrite. Scalar count is automatic; set equality/disequality reuse among. | +| `gecode_global_cardinality_sets` | Closed cardinality with arbitrary allowed count sets, including holes; standard GCC exposes variable counts or interval bounds instead. | +| `gecode_nvalues` | Native inequality propagation has no standard nvalue comparison rewrite. Equality/disequality reuse the existing route or the native Boolean equality actor. | +| `gecode_divmod` | Joint truncating division and remainder. Standard `int_div` and `int_mod` are separate hooks. | +| `gecode_nroot` | Native integer roots and float roots of general positive degree. Float square roots reuse the standard route. | +| `gecode_path` | Hamiltonian successor representation, terminal sentinel, and total/per-edge costs. Standard graph path uses selected graph nodes and edges. | +| `gecode_order` | Native two-task ordering Boolean has no joint standard ordering hook. | +| `gecode_unary_typed`, `gecode_cumulative_typed` | Native fixed-start/fixed-end task types, including optional tasks, are absent from standard scheduling globals. | +| `gecode_nooverlap` | Native optional rectangles with fixed or variable dimensions and explicit ends. Standard diffn covers mandatory rectangles. | +| `gecode_convex`, `gecode_set_sequence` | Native convex hull and ordered set sequence/sequence union lack matching standard globals. One-set convexity and sequence aliases already existed in the registry. | +| `gecode_set_atmost_one` | Native equal fixed cardinality plus pairwise intersection size at most one. Standard at_most1 does not carry this cardinality parameter. | +| `gecode_set_channel_sorted` | Native set/strictly-increasing-enumeration channel lacks a standard global. | +| `gecode_set_operation` | Combined operation/relation propagation, including native intersection/subset and union/superset actors. Ordinary operation equality reuses standard set operations. | +| `gecode_array_int_set_operation`, `gecode_array_int_set_element` | Singleton-view array operations with an initial set, and selected intersection/disjoint union. Selected union already has a native range route and reuses it. | +| `gecode_min_reif`, `gecode_max_reif` | Specialized native set-extremum equivalence actors are not selected by the standard function hooks. A false result selects native NotMin/NotMax; separate negated names are unnecessary. | +| `gecode_set_singleton_rel`, `_reif`, `_imp` | Native singleton views for relations beyond membership, without an intermediate variable set. Superset/membership uses the standard route. | +| `gecode_rel_imp` | Set disjointness, full-universe complement, and Gecode characteristic-function ordering implication. Equality/disequality/subset/superset reuse standard hooks. | +| `gecode_log` | Arbitrary fixed-base logarithm with variable argument. Standard two-argument log is parameter-only; bases 2 and 10 reuse existing native routes. | +| `gecode_arg_min`, `gecode_arg_max` | Native arbitrary tie choice has different solutions from standard smallest-index tie breaking. Standard tie breaking is reused when requested. | + +Gecode set ordering compares characteristic functions starting at the smallest element. MiniZinc documents sorted-list lexicographic ordering. The inherited Gecode standard ordering posters already use the native order; this change does not add automatic ordering implication hooks or modify those existing posters. The explicit Gecode relation enum documents its own order. + +## Other candidates excluded + +| Candidate | Existing translation or native implementation | +| --- | --- | +| Integer/float squares | Integer power of degree two and multiplication of a variable by itself select native square propagation. | +| Variable-right-hand-side float linear constraints | Native posting appends the right-hand-side variable with coefficient -1, matching existing arithmetic normalization. | +| Scalar relations and scalar loops over arrays | Existing primitive posters already reach the native relations; the array helpers merely repeat them. | +| Mandatory flexible scheduling and mandatory rectangles | Existing cumulative/disjunctive and diffn routes already reach their actors. | +| Integer/set precedence chains | Native posting repeats the existing adjacent-pair precedence propagators. | +| Reified set cardinality | Native posting combines cardinality and an existing reified integer relation. | +| Set-wide integer relations | Native posting uses extrema plus scalar relations, nonempty cardinality plus nonmembership, or the singleton relation. Those component routes are reachable. | +| Multidimensional bin packing | The helper decomposes into packing constraints and clique all-different constraints; no additional actor is missing. | +| Ordinary set complement equality | Full-universe set difference selects the native complement-view equality actor. | + +## Boundaries and verification + +Optional distinct preserves absent values. If native distinct cannot allocate temporary values outside a full input domain, the registry uses guarded pairwise relations. Irrelevant out-of-range exceptions are eliminated before FlatZinc integer-limit checks. Array shape/index and semantic enum-code checks cover the new bindings; existing unsharing is retained where native actors reject repeated unassigned variables. + +Compiler checks cover every remaining typed leaf and public overloads, inferred reification, shifted indexes, and consistency annotations. Standard-call checks compare solution sets/counts against installed MiniZinc/Gecode and exercise root, reified, implication, negated, empty, and enum cases. Generated FlatZinc confirms automatic native dispatch without an explicit Gecode library include. These checks establish semantics and routing, not performance. + +Fixed-base float testing exposed a rounded scalar logarithm and an invalid shared-variable shortcut in [exp-log.hpp](../gecode/float/transcendental/exp-log.hpp). The implementation uses interval logarithms, handles base-one power, and retains propagation after its own updates. Both build systems enable rounding-aware float compilation. [Float regressions](../test/float/transcendental.cpp) cover exact powers, decreasing bases, base one, and shared variables. + +Propagation algorithms are selected with annotations, never predicate arguments. `gecode_basic_propagation`, `gecode_advanced_propagation`, and `gecode_full_propagation` map to `IPL_BASIC`, `IPL_ADVANCED`, and their combination. They combine with existing domain/bounds/value propagation annotations. Unsupported algorithm choices retain the native constraint's normal behavior. [Registry regressions](../test/flatzinc/native-registry.cpp) check semantic boundaries and demonstrate different native inter-distance filtering under basic and advanced algorithms. + +```minizinc +include "gecode.mzn"; +constraint gecode_inter_distance(x, distance) :: gecode_basic_propagation; +constraint gecode_inter_distance(y, distance) + :: bounds_propagation :: gecode_advanced_propagation; +constraint gecode_count(x, targets, GecodeLe, limit) :: domain_propagation; +``` diff --git a/gecode/flatzinc/flatzinc.cpp b/gecode/flatzinc/flatzinc.cpp index e35bd3638c..c378bcf0e9 100644 --- a/gecode/flatzinc/flatzinc.cpp +++ b/gecode/flatzinc/flatzinc.cpp @@ -2647,19 +2647,28 @@ namespace Gecode { namespace FlatZinc { #endif IntPropLevel FlatZincSpace::ann2ipl(AST::Node* ann) { + int ipl = IPL_DEF; if (ann) { if (ann->hasAtom("val") || ann->hasAtom("value_propagation")) - return IPL_VAL; - if (ann->hasAtom("domain") || ann->hasAtom("domain_propagation")) - return IPL_DOM; - if (ann->hasAtom("bounds") || - ann->hasAtom("bounds_propagation") || - ann->hasAtom("boundsR") || - ann->hasAtom("boundsD") || - ann->hasAtom("boundsZ")) - return IPL_BND; - } - return IPL_DEF; + ipl = IPL_VAL; + else if (ann->hasAtom("domain") || ann->hasAtom("domain_propagation")) + ipl = IPL_DOM; + else if (ann->hasAtom("bounds") || + ann->hasAtom("bounds_propagation") || + ann->hasAtom("boundsR") || + ann->hasAtom("boundsD") || + ann->hasAtom("boundsZ")) + ipl = IPL_BND; + if (ann->hasAtom("gecode_full_propagation")) + ipl |= IPL_FULL; + else { + if (ann->hasAtom("gecode_basic_propagation")) + ipl |= IPL_BASIC; + if (ann->hasAtom("gecode_advanced_propagation")) + ipl |= IPL_ADVANCED; + } + } + return static_cast(ipl); } DFA diff --git a/gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn b/gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn new file mode 100644 index 0000000000..c52a7e53b1 --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn @@ -0,0 +1,54 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_all_different_optional(array[int] of var int: x, + array[int] of var bool: present); + +predicate fzn_all_different_int_opt(array[int] of var opt int: xs) = + if length(xs) = 0 then + true + else + % Fixed absent values have no deopt value; their integer is ignored. + gecode_all_different_optional( + [if is_fixed(x) /\ absent(fix(x)) then 0 else deopt(x) endif | x in xs], + [occurs(x) | x in xs]) + endif; + +% Reification has no native optional-distinct propagator. +predicate fzn_all_different_int_opt_reif(array[int] of var opt int: xs, + var bool: b) = + let { + array[index_set(xs)] of var int: values = + array1d(index_set(xs), + [if is_fixed(x) /\ absent(fix(x)) then 0 else deopt(x) endif | x in xs]); + } in b <-> forall (i, j in index_set(xs) where i < j) ( + (occurs(xs[i]) /\ occurs(xs[j])) -> values[i] != values[j]); diff --git a/gecode/flatzinc/mznlib/fzn_all_equal_int_reif.mzn b/gecode/flatzinc/mznlib/fzn_all_equal_int_reif.mzn new file mode 100644 index 0000000000..69e77ec91e --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_all_equal_int_reif.mzn @@ -0,0 +1,38 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_not_all_equal_int(array[int] of var int: xs); + +predicate fzn_all_equal_int_reif(array[int] of var int: xs, var bool: b) = + if is_fixed(b) /\ not fix(b) then gecode_not_all_equal_int(xs) + else b <-> (if length(xs) = 0 then true else + forall(i in index_set(xs))(xs[i] = xs[min(index_set(xs))]) endif) endif; diff --git a/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn b/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn new file mode 100644 index 0000000000..70127eff08 --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn @@ -0,0 +1,57 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +include "global_cardinality_low_up.mzn"; +include "fzn_all_different_int.mzn"; + +predicate gecode_all_different_except(array[int] of var int: x, int: except_value); + +predicate fzn_alldifferent_except(array[int] of var int: xs, set of int: except) = + if length(xs) = 0 then + true + elseif card(except) = 1 then + if min(except) < lb_array(xs) \/ min(except) > ub_array(xs) then + % Drop irrelevant exceptions before FlatZinc checks integer limits. + fzn_all_different_int(xs) + else + gecode_all_different_except(xs, min(except)) + endif + elseif forall (i in index_set(xs)) (has_bounds(xs[i])) then + % Native distinct supports one exception; retain the standard translation + % for larger exception sets. + let { + set of int: values = dom_array(xs) diff except; + } in global_cardinality(xs, set2array(values), [0 | i in values], [1 | i in values]) + else + forall (i, j in index_set(xs) where i < j) ( + (xs[i] in except /\ xs[j] in except) \/ xs[i] != xs[j]) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_alldifferent_except_0.mzn b/gecode/flatzinc/mznlib/fzn_alldifferent_except_0.mzn index 967f001da9..d99fb634e0 100644 --- a/gecode/flatzinc/mznlib/fzn_alldifferent_except_0.mzn +++ b/gecode/flatzinc/mznlib/fzn_alldifferent_except_0.mzn @@ -1,24 +1,40 @@ -include "global_cardinality_low_up_closed.mzn"; +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% -/** @group globals.alldifferent +include "fzn_alldifferent_except.mzn"; + +/** @group globals.alldifferent Constrain the array of integers \a vs to be all different except those elements that are assigned the value 0. */ predicate fzn_alldifferent_except_0(array[int] of var int: vs) = - if length(vs)==0 then true - else - let { - int: l = lb_array(vs); - int: u = ub_array(vs); - } in - if l != -infinity /\ u != infinity then - global_cardinality_low_up_closed(vs, [i | i in l..u], - [0 | i in l..u], - [if i==0 then length(vs) else 1 endif | i in l..u] - ) - else - forall(i, j in index_set(vs) where i < j) ( - (vs[i] != 0 /\ vs[j] != 0) -> vs[i] != vs[j] - ) - endif - endif; + fzn_alldifferent_except(vs, {0}); diff --git a/gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn b/gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn new file mode 100644 index 0000000000..5c5de79cfc --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn @@ -0,0 +1,39 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_array_set_intersect(array[int] of var set of int: x, + var set of int: y); + +% An empty array has no bounded intersection, matching the standard library. +predicate fzn_array_set_intersect(array[int] of var set of int: x, + var set of int: y) = + if length(x) = 0 then false else gecode_array_set_intersect(x, y) endif; diff --git a/gecode/flatzinc/mznlib/fzn_count_geq.mzn b/gecode/flatzinc/mznlib/fzn_count_geq.mzn new file mode 100644 index 0000000000..2bd5e7af63 --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_count_geq.mzn @@ -0,0 +1,38 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, + int: relation, var int: n); + +% Standard count_geq compares n against the count, not vice versa. +predicate fzn_count_geq(array[int] of var int: xs, var int: value, var int: n) = + gecode_count_int_fzn(xs, value, 3, n); diff --git a/gecode/flatzinc/mznlib/fzn_count_gt.mzn b/gecode/flatzinc/mznlib/fzn_count_gt.mzn new file mode 100644 index 0000000000..2972d7f9ae --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_count_gt.mzn @@ -0,0 +1,38 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, + int: relation, var int: n); + +% Standard count_gt compares n against the count, not vice versa. +predicate fzn_count_gt(array[int] of var int: xs, var int: value, var int: n) = + gecode_count_int_fzn(xs, value, 4, n); diff --git a/gecode/flatzinc/mznlib/fzn_count_leq.mzn b/gecode/flatzinc/mznlib/fzn_count_leq.mzn new file mode 100644 index 0000000000..1237bb0cce --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_count_leq.mzn @@ -0,0 +1,38 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, + int: relation, var int: n); + +% Standard count_leq compares n against the count, not vice versa. +predicate fzn_count_leq(array[int] of var int: xs, var int: value, var int: n) = + gecode_count_int_fzn(xs, value, 5, n); diff --git a/gecode/flatzinc/mznlib/fzn_count_lt.mzn b/gecode/flatzinc/mznlib/fzn_count_lt.mzn new file mode 100644 index 0000000000..a440de3290 --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_count_lt.mzn @@ -0,0 +1,38 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, + int: relation, var int: n); + +% Standard count_lt compares n against the count, not vice versa. +predicate fzn_count_lt(array[int] of var int: xs, var int: value, var int: n) = + gecode_count_int_fzn(xs, value, 6, n); diff --git a/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn b/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn index ecea229ea5..c795ace0b4 100644 --- a/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn +++ b/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn @@ -36,23 +36,41 @@ include "fzn_cumulative_opt_decomp.mzn"; predicate gecode_schedule_cumulative_optional(array[int] of var int: start, array[int] of int: duration, array[int] of int: usage, - array[int] of var bool: m, int: capacity); + array[int] of var bool: m, var int: capacity); + +predicate gecode_cumulative_flex_optional(array[int] of var int: start, + array[int] of var int: duration, array[int] of var int: end, + array[int] of int: usage, var int: capacity, array[int] of var bool: present); predicate fzn_cumulative_opt(array[int] of var opt int: s, array[int] of var int: d, array[int] of var int: r, var int: b) = - if + if ub(b) < 0 then + false + elseif forall (i in index_set(d)) (is_fixed(d[i])) /\ - forall (i in index_set(r)) (is_fixed(r[i])) /\ - is_fixed(b) + forall (i in index_set(r)) (is_fixed(r[i])) then + % Zero-duration tasks consume no capacity in MiniZinc. gecode_schedule_cumulative_optional( [ if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s ], [ fix(di) | di in d ], [ fix(ri) | ri in r ], - [ occurs(si) | si in s ], - fix(b) + [ occurs(s[i]) /\ fix(d[i]) > 0 | i in index_set(s) ], + b ) + elseif forall (i in index_set(r)) (is_fixed(r[i])) then + let { + array[int] of var int: start = + [if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s]; + array[int] of var int: duration = array1d(d); + % Zero-duration tasks consume no capacity in MiniZinc. + array[int] of var bool: present = + [occurs(array1d(s)[i]) /\ duration[i] > 0 | i in index_set(duration)]; + array[int] of var int: end = + [if present[i] then start[i] + duration[i] else 0 endif | i in index_set(start)]; + } in gecode_cumulative_flex_optional(start, duration, end, + [fix(ri) | ri in r], b, present) else fzn_cumulative_opt_decomp(s,d,r,b) endif; diff --git a/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn b/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn index d3aa941c90..1f083f2e18 100644 --- a/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn +++ b/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn @@ -35,7 +35,9 @@ predicate gecode_schedule_unary_optional(array[int] of var int: x, array[int] of int: p, array[int] of var bool: m); -include "fzn_disjunctive_strict_opt_decomp.mzn"; +predicate gecode_unary_flex_optional(array[int] of var int: start, + array[int] of var int: duration, array[int] of var int: end, + array[int] of var bool: present); predicate fzn_disjunctive_strict_opt(array[int] of var opt int: s, array[int] of var int: d) = @@ -46,4 +48,13 @@ predicate fzn_disjunctive_strict_opt(array[int] of var opt int: s, [ fix(di) | di in d], [ occurs(si) | si in s ] ) - else fzn_disjunctive_strict_opt_decomp(s,d) endif; + else + let { + array[int] of var int: start = + [if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s]; + array[int] of var bool: present = [occurs(si) | si in s]; + array[int] of var int: duration = array1d(d); + array[int] of var int: end = + [if present[i] then start[i] + duration[i] else 0 endif | i in index_set(start)]; + } in gecode_unary_flex_optional(start, duration, end, present) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn new file mode 100644 index 0000000000..4dadafaa32 --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn @@ -0,0 +1,49 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_bool_ite(var bool: b, var bool: x, + var bool: y, var bool: z); + +% The compiler appends a true condition for the else branch. Use the native +% binary conditional for that shape, keeping the standard general translation. +predicate fzn_if_then_else_bool(array[int] of var bool: c, + array[int] of bool: x, var bool: y) = + if length(c) = 2 /\ index_sets_agree(c,x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) + then + gecode_bool_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) + else + let {array[index_set(c)] of var bool: d;} in + forall(i in index_set(c))( + if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ + forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn new file mode 100644 index 0000000000..176e0ff9d5 --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn @@ -0,0 +1,49 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_float_ite(var bool: b, var float: x, + var float: y, var float: z); + +% The compiler appends a true condition for the else branch. Use the native +% binary conditional for that shape, keeping the standard general translation. +predicate fzn_if_then_else_float(array[int] of var bool: c, + array[int] of float: x, var float: y) = + if length(c) = 2 /\ index_sets_agree(c,x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) + then + gecode_float_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) + else + let {array[index_set(c)] of var bool: d;} in + forall(i in index_set(c))( + if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ + forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn new file mode 100644 index 0000000000..a0658ede60 --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn @@ -0,0 +1,49 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_int_ite(var bool: b, var int: x, + var int: y, var int: z); + +% The compiler appends a true condition for the else branch. Use the native +% binary conditional for that shape, keeping the standard general translation. +predicate fzn_if_then_else_int(array[int] of var bool: c, + array[int] of int: x, var int: y) = + if length(c) = 2 /\ index_sets_agree(c,x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) + then + gecode_int_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) + else + let {array[index_set(c)] of var bool: d;} in + forall(i in index_set(c))( + if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ + forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn new file mode 100644 index 0000000000..041577efc7 --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn @@ -0,0 +1,49 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_set_ite(var bool: b, var set of int: x, + var set of int: y, var set of int: z); + +% The compiler appends a true condition for the else branch. Use the native +% binary conditional for that shape, keeping the standard general translation. +predicate fzn_if_then_else_set(array[int] of var bool: c, + array[int] of set of int: x, var set of int: y) = + if length(c) = 2 /\ index_sets_agree(c,x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) + then + gecode_set_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) + else + let {array[index_set(c)] of var bool: d;} in + forall(i in index_set(c))( + if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ + forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn new file mode 100644 index 0000000000..c582890ddc --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn @@ -0,0 +1,49 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_bool_ite(var bool: b, var bool: x, + var bool: y, var bool: z); + +% The compiler appends a true condition for the else branch. Use the native +% binary conditional for that shape, keeping the standard general translation. +predicate fzn_if_then_else_var_bool(array[int] of var bool: c, + array[int] of var bool: x, var bool: y) = + if length(c) = 2 /\ index_sets_agree(c,x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) + then + gecode_bool_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) + else + let {array[index_set(c)] of var bool: d;} in + forall(i in index_set(c))( + if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ + forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn new file mode 100644 index 0000000000..a6645031fd --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn @@ -0,0 +1,49 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_float_ite(var bool: b, var float: x, + var float: y, var float: z); + +% The compiler appends a true condition for the else branch. Use the native +% binary conditional for that shape, keeping the standard general translation. +predicate fzn_if_then_else_var_float(array[int] of var bool: c, + array[int] of var float: x, var float: y) = + if length(c) = 2 /\ index_sets_agree(c,x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) + then + gecode_float_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) + else + let {array[index_set(c)] of var bool: d;} in + forall(i in index_set(c))( + if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ + forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn new file mode 100644 index 0000000000..331e6b6e4c --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn @@ -0,0 +1,49 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_int_ite(var bool: b, var int: x, + var int: y, var int: z); + +% The compiler appends a true condition for the else branch. Use the native +% binary conditional for that shape, keeping the standard general translation. +predicate fzn_if_then_else_var_int(array[int] of var bool: c, + array[int] of var int: x, var int: y) = + if length(c) = 2 /\ index_sets_agree(c,x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) + then + gecode_int_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) + else + let {array[index_set(c)] of var bool: d;} in + forall(i in index_set(c))( + if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ + forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn new file mode 100644 index 0000000000..a471e4de2a --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn @@ -0,0 +1,49 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_set_ite(var bool: b, var set of int: x, + var set of int: y, var set of int: z); + +% The compiler appends a true condition for the else branch. Use the native +% binary conditional for that shape, keeping the standard general translation. +predicate fzn_if_then_else_var_set(array[int] of var bool: c, + array[int] of var set of int: x, var set of int: y) = + if length(c) = 2 /\ index_sets_agree(c,x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) + then + gecode_set_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) + else + let {array[index_set(c)] of var bool: d;} in + forall(i in index_set(c))( + if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ + forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + endif; diff --git a/gecode/flatzinc/mznlib/gecode.mzn b/gecode/flatzinc/mznlib/gecode.mzn index ca4b5190de..5b66ab507a 100644 --- a/gecode/flatzinc/mznlib/gecode.mzn +++ b/gecode/flatzinc/mznlib/gecode.mzn @@ -38,7 +38,7 @@ */ /*** - @groupdef gecode.annotations Additional Gecode search annotations + @groupdef gecode.annotations Additional Gecode annotations */ /** @group gecode.annotations Select variable with smallest accumulated failure count */ @@ -60,6 +60,15 @@ annotation action_size_max; /** @group gecode.annotations Select random variable */ annotation random; +/** @group gecode.annotations Use the basic algorithm where supported. */ +annotation gecode_basic_propagation; +/** @group gecode.annotations Use the advanced algorithm where supported. */ +annotation gecode_advanced_propagation; +/** @group gecode.annotations Use both basic and advanced algorithms where supported. + These may be combined with domain_propagation or bounds_propagation; + constraints without algorithm choices ignore them. */ +annotation gecode_full_propagation; + annotation int_assign(array[int] of var int: x, ann:a); /** @group gecode.annotations Specify default search strategy for integer variables to use variable selection @@ -163,4 +172,264 @@ predicate gecode_schedule_unary_optional(array[int] of var int: x, predicate gecode_schedule_cumulative_optional(array[int] of var int: start, array[int] of int: duration, array[int] of int: usage, - array[int] of var bool: m, int: capacity); + array[int] of var bool: m, var int: capacity); + + +include "gecode_fzn.mzn"; +include "among.mzn"; +include "nvalue.mzn"; +include "range.mzn"; +include "arg_min.mzn"; +include "arg_max.mzn"; + +% Arrays correspond by position unless an index agreement is stated. +% Enum order is part of the typed FlatZinc interface. +enum GecodeIntRelation = { + GecodeEq, GecodeNe, GecodeLe, GecodeLt, GecodeGe, GecodeGt +}; +/** @group gecode.constraints Set comparison. Complement uses the full Gecode + set universe. Ordering compares characteristic functions from the smallest + element, rather than the lexicographic order of sorted element lists. */ +enum GecodeSetRelation = { + GecodeSetEq, GecodeSetNe, GecodeSubset, GecodeSuperset, GecodeDisjoint, + GecodeComplement, GecodeSetLe, GecodeSetLt, GecodeSetGe, GecodeSetGt +}; +enum GecodeSetOperation = { + GecodeUnion, GecodeDisjointUnion, GecodeIntersection, GecodeDifference +}; +/** @group gecode.constraints FixedDuration: flex is start, fix is duration. + FixedStart: flex is end, fix is start. FixedEnd: flex is start, fix is end. */ +enum GecodeTaskType = { + GecodeFixedDuration, GecodeFixedStart, GecodeFixedEnd +}; + +/* @group gecode.constraints Pairwise absolute differences are at least the + nonnegative distance. Empty and singleton arrays only constrain distance. + Native bounds propagation offers basic and advanced algorithms, selected + with gecode_basic_propagation, gecode_advanced_propagation, or + gecode_full_propagation. */ +% gecode_inter_distance(x, distance) is declared in gecode_fzn.mzn. + +/** @group gecode.constraints All x[i]+offset[i] values are distinct. + Arrays must have equal lengths. Uses the existing native offset route. */ +predicate gecode_all_different(array[int] of int: offset, array[int] of var int: x) = + gecode_all_different_offset(offset, x); + +/** @group gecode.constraints Sort x into y with x[i]=y[permutation[i]]. Ties + may be permuted; standard arg_sort instead returns a stable inverse mapping. + The explicit-offset typed form labels output positions starting at offset. */ +predicate gecode_sort_permutation(array[int] of var int: x, + array[int] of var int: y, array[int] of var int: permutation) = + assert(index_set(x) = index_set(permutation) /\ length(x) = length(y), + "gecode_sort_permutation: incompatible array indexes or lengths", + if length(y) = 0 then true else + gecode_sort_permutation(x, y, permutation, min(index_set(y))) endif); + +/** @group gecode.constraints Compare an occurrence count with n. The second + argument selects a value set, or one target per array position. + Existing equality/disequality translations are reused where available. */ +predicate gecode_count(array[int] of var int: x, set of int: values, + GecodeIntRelation: relation, var int: n) = + if relation = GecodeEq then among(n, x, values) + elseif relation = GecodeNe then + let {var 0..length(x): c; constraint among(c, x, values);} in c != n + else gecode_count_in_fzn(x, values, enum2int(relation), n) endif; +predicate gecode_count(array[int] of var int: x, array[int] of int: values, + GecodeIntRelation: relation, var int: n) = + assert(length(x) = length(values), "gecode_count: unequal array lengths", + gecode_count_matches_fzn(x, values, enum2int(relation), n)); + +/* @group gecode.constraints Closed cardinality with arbitrary allowed count + sets. Every x[i] belongs to cover, cover values are distinct, and cover and + counts have equal lengths. See gecode_global_cardinality_sets in the typed + declarations for its signature. */ + +/** @group gecode.constraints Compare the number of distinct values with n. + The empty array has zero values. Equality and disequality on integers reuse + nvalue; Boolean arrays use the native Boolean propagator. */ +predicate gecode_nvalues(array[int] of var int: x, + GecodeIntRelation: relation, var int: n) = + if relation = GecodeEq then nvalue(n, x) + elseif relation = GecodeNe then + let {var 0..length(x): c; constraint nvalue(c, x);} in c != n + else gecode_nvalues_int_fzn(x, enum2int(relation), n) endif; +predicate gecode_nvalues(array[int] of var bool: x, + GecodeIntRelation: relation, var int: n) = + if relation = GecodeNe then + let {var 0..2: c; + constraint gecode_nvalues_bool_fzn(x, enum2int(GecodeEq), c);} in c != n + else gecode_nvalues_bool_fzn(x, enum2int(relation), n) endif; + +/** @group gecode.constraints Joint truncating division and remainder; y is + nonzero. */ +predicate gecode_divmod(var int: x, var int: y, var int: quotient, var int: remainder) = + gecode_int_divmod(x,y,quotient,remainder); +/** @group gecode.constraints Integer root rounds down; negative x is allowed + for odd degrees. Float roots use interval propagation. Degree is positive. */ +predicate gecode_nroot(var int: x, int: degree, var int: y) = + assert(degree > 0, "gecode_nroot: degree must be positive", gecode_int_nroot(x,degree,y)); +predicate gecode_nroot(var float: x, int: degree, var float: y) = + assert(degree > 0, "gecode_nroot: degree must be positive", + if degree = 2 then float_sqrt(x,y) else gecode_float_nroot(x,degree,y) endif); + +/** @group gecode.constraints Hamiltonian successor path visits every node. + x[end]=max(index_set(x))+1 is the terminal sentinel; x is nonempty. This + differs from standard graph path. Costs are a row-major square matrix; + the final sentinel edge costs zero. Typed forms accept an explicit + nonnegative node offset and flat costs. */ +predicate gecode_path(array[int] of var int: x, var int: start, var int: end) = + assert(length(x) > 0, "gecode_path: empty node array", + gecode_path(0, [v-min(index_set(x)) | v in x], + start-min(index_set(x)), end-min(index_set(x)))); +predicate gecode_path(array[int,int] of int: costs, array[int] of var int: x, + var int: start, var int: end, var int: cost) = + assert(length(x) > 0 /\ index_set_1of2(costs) = index_set(x) /\ + index_set_2of2(costs) = index_set(x), "gecode_path: incompatible cost matrix", + gecode_path_cost(array1d(costs), 0, [v-min(index_set(x)) | v in x], + start-min(index_set(x)), end-min(index_set(x)), cost)); +predicate gecode_path(array[int,int] of int: costs, array[int] of var int: x, + var int: start, var int: end, array[int] of var int: edge_costs, var int: cost) = + assert(length(x) > 0 /\ index_set_1of2(costs) = index_set(x) /\ + index_set_2of2(costs) = index_set(x) /\ index_set(edge_costs) = index_set(x), + "gecode_path: incompatible cost matrix or edge costs", + gecode_path_cost_array(array1d(costs), 0, [v-min(index_set(x)) | v in x], + start-min(index_set(x)), end-min(index_set(x)), edge_costs, cost)); + +/** @group gecode.constraints Native typed tasks may fix duration, start, + or end. These task types are not part of standard disjunctive/cumulative. */ +predicate gecode_unary_typed(array[int] of GecodeTaskType: types, + array[int] of var int: flex, array[int] of int: fix) = + gecode_unary_typed_fzn(enum2int(types),flex,fix); +predicate gecode_unary_typed(array[int] of GecodeTaskType: types, + array[int] of var int: flex, array[int] of int: fix, array[int] of var bool: present) = + gecode_unary_typed_optional_fzn(enum2int(types),flex,fix,present); + +predicate gecode_cumulative_typed(array[int] of GecodeTaskType: types, + array[int] of var int: flex, array[int] of int: fix, + array[int] of int: usage, var int: capacity) = + gecode_cumulative_typed_fzn(enum2int(types),flex,fix,usage,capacity); +predicate gecode_cumulative_typed(array[int] of GecodeTaskType: types, + array[int] of var int: flex, array[int] of int: fix, + array[int] of int: usage, var int: capacity, array[int] of var bool: present) = + gecode_cumulative_typed_optional_fzn(enum2int(types),flex,fix,usage,capacity,present); + +/** @group gecode.constraints Optional nonoverlapping rectangles. Fixed sizes + are nonnegative. Variable-size forms enforce x+width=right and y+height=top + when present. All arrays share their index sets. */ +predicate gecode_nooverlap(array[int] of var int: x, array[int] of int: width, + array[int] of var int: y, array[int] of int: height, array[int] of var bool: present) = + gecode_nooverlap_optional(x,width,y,height,present); +predicate gecode_nooverlap(array[int] of var int: x, array[int] of var int: width, + array[int] of var int: right, array[int] of var int: y, + array[int] of var int: height, array[int] of var int: top, + array[int] of var bool: present) = + assert(index_set(x) = index_set(width) /\ index_set(x) = index_set(right) /\ + index_set(x) = index_set(y) /\ index_set(x) = index_set(height) /\ + index_set(x) = index_set(top) /\ index_set(x) = index_set(present), + "gecode_nooverlap: incompatible indexes", + forall(i in index_set(x))(present[i] -> + (x[i]+width[i]=right[i] /\ y[i]+height[i]=top[i])) /\ + gecode_nooverlap_flex_optional(x,width,right,y,height,top,present)); + +/** @group gecode.constraints The set is empty or an interval; the two-argument + form returns its convex hull (empty for an empty input). */ +predicate gecode_convex(var set of int: x) = gecode_set_convex(x); +predicate gecode_convex(var set of int: x, var set of int: hull) = gecode_set_convex_hull(x,hull); +/** @group gecode.constraints Ordered disjoint sets: every element precedes + every element of subsequent nonempty sets. Optional second result is union. */ +predicate gecode_set_sequence(array[int] of var set of int: x) = gecode_array_set_seq(x); +predicate gecode_set_sequence(array[int] of var set of int: x, var set of int: y) = + gecode_array_set_seq_union(x,y); +/* @group gecode.constraints gecode_set_atmost_one(x, cardinality) constrains + every set to that cardinality and pairwise intersections to at most one. + gecode_set_channel_sorted(x,s) makes x a strictly increasing enumeration of + s. Both native primitive signatures are in gecode_fzn.mzn. */ + +/** @group gecode.constraints Native combined (x operation y) relation z. + Equality for ordinary union, intersection, and difference reuses existing + MiniZinc operations. Complement is relative to Gecode's full set universe. */ +predicate gecode_set_operation(var set of int: x, GecodeSetOperation: operation, + var set of int: y, GecodeSetRelation: relation, var set of int: z) = + if relation = GecodeSetEq /\ operation = GecodeUnion then z = x union y + elseif relation = GecodeSetEq /\ operation = GecodeIntersection then z = x intersect y + elseif relation = GecodeSetEq /\ operation = GecodeDifference then z = x diff y + else gecode_set_operation_fzn(x,enum2int(operation),y,enum2int(relation),z) endif; +/** @group gecode.constraints Intersect singleton sets {x[i]} and initial, or + form their union/disjoint union. Difference is unsupported. */ +predicate gecode_array_int_set_operation(array[int] of var int: x, + GecodeSetOperation: operation, set of int: initial, var set of int: y) = + assert(operation != GecodeDifference, "gecode_array_int_set_operation: difference is unsupported", + gecode_array_int_set_operation_fzn(x,enum2int(operation),initial,y)); +/** @group gecode.constraints Selected zero-based positions of x are combined + as singleton sets. Empty intersection is universe; empty union is empty. + Union already has a standard range route; intersection and disjoint union + select additional native variants. */ +predicate gecode_array_int_set_element(array[int] of var int: x, + GecodeSetOperation: operation, var set of int: selected, + var set of int: value, set of int: universe) = + assert(operation != GecodeDifference, "gecode_array_int_set_element: difference is unsupported", + if operation = GecodeUnion then gecode_range(x,0,selected,value) + else gecode_array_int_set_element_fzn(x,enum2int(operation),selected,value,universe) endif); +/** @group gecode.constraints Reified set extrema. False for an empty set. + Unlike standard min/max expressions, these post the specialized native + equivalence propagators. Negate by passing false as the final argument. */ +predicate gecode_min_reif(var set of int: s, var int: x, var bool: b) = + gecode_set_min_reif(s,x,b); +predicate gecode_max_reif(var set of int: s, var int: x, var bool: b) = + gecode_set_max_reif(s,x,b); + +/** @group gecode.constraints Compare s with singleton {x}. Superset is + membership and reuses the existing membership translation. Other comparisons + use native singleton views instead of introducing a set variable. */ +predicate gecode_set_singleton_rel(var set of int: s, GecodeSetRelation: relation, var int: x) = + if relation = GecodeSuperset then x in s else + gecode_set_singleton_rel_fzn(s,enum2int(relation),x) endif; +predicate gecode_set_singleton_rel_reif(var set of int: s, GecodeSetRelation: relation, + var int: x, var bool: b) = + if relation = GecodeSuperset then b <-> x in s else + gecode_set_singleton_rel_fzn_reif(s,enum2int(relation),x,b) endif; +predicate gecode_set_singleton_rel_imp(var set of int: s, GecodeSetRelation: relation, + var int: x, var bool: b) = + if relation = GecodeSuperset then b -> x in s else + gecode_set_singleton_rel_fzn_imp(s,enum2int(relation),x,b) endif; +/** @group gecode.constraints Native set implication. Equality, disequality, + subset, and superset use standard implication routes. Disjointness, + complement, and characteristic-function ordering use native relations. */ +predicate gecode_rel_imp(var set of int: x, GecodeSetRelation: relation, + var set of int: y, var bool: b) = + if relation = GecodeSetEq then b -> x = y + elseif relation = GecodeSetNe then b -> x != y + elseif relation = GecodeSubset then b -> x subset y + elseif relation = GecodeSuperset then b -> x superset y + else gecode_set_rel_imp_fzn(x,enum2int(relation),y,b) endif; + +/** @group gecode.constraints Fixed-base logarithm requires MPFR; base must + be positive and differ from one. Bases 2 and 10 reuse standard routes. */ +predicate gecode_log(float: base, var float: x, var float: y) = + assert(base > 0.0 /\ base != 1.0, "gecode_log: base must be positive and different from one", + if base = 2.0 then float_log2(x,y) + elseif base = 10.0 then float_log10(x,y) + else gecode_float_log_base(base,x,y) endif); + +/** @group gecode.constraints Index of a minimum/maximum. break_ties=true + reuses standard arg_min/arg_max and selects the smallest qualifying index. + Otherwise any qualifying index is permitted. Arrays must be nonempty. */ +predicate gecode_arg_min(array[int] of var int: x, var int: index, bool: break_ties) = + assert(length(x)>0, "gecode_arg_min: empty array", if break_ties then arg_min(array1d(x))+min(index_set(x))-1=index + else gecode_arg_min_int(x,0,index-min(index_set(x)),false) endif); +predicate gecode_arg_min(array[int] of var bool: x, var int: index, bool: break_ties) = + assert(length(x)>0, "gecode_arg_min: empty array", if break_ties then arg_min(array1d(x))+min(index_set(x))-1=index + else gecode_arg_min_bool(x,0,index-min(index_set(x)),false) endif); +predicate gecode_arg_max(array[int] of var int: x, var int: index, bool: break_ties) = + assert(length(x)>0, "gecode_arg_max: empty array", if break_ties then arg_max(array1d(x))+min(index_set(x))-1=index + else gecode_arg_max_int(x,0,index-min(index_set(x)),false) endif); +predicate gecode_arg_max(array[int] of var bool: x, var int: index, bool: break_ties) = + assert(length(x)>0, "gecode_arg_max: empty array", if break_ties then arg_max(array1d(x))+min(index_set(x))-1=index + else gecode_arg_max_bool(x,0,index-min(index_set(x)),false) endif); + +/** @group gecode.constraints Exactly one Boolean array entry is true and its + position plus offset is value, or a float equals 1.0/0.0 for true/false. + The Boolean/float overload uses the native channel directly. */ +predicate gecode_channel(array[int] of var bool: x, var int: value, int: offset) = + gecode_bool_int_channel(x,value,offset); +predicate gecode_channel(var bool: b, var float: x) = gecode_bool_float_channel(b,x); diff --git a/gecode/flatzinc/mznlib/gecode_fzn.mzn b/gecode/flatzinc/mznlib/gecode_fzn.mzn new file mode 100644 index 0000000000..4019392a2c --- /dev/null +++ b/gecode/flatzinc/mznlib/gecode_fzn.mzn @@ -0,0 +1,216 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +% Typed FlatZinc declarations for the native Gecode library. +% Model code should include gecode.mzn for overloaded predicates. +% Relation and task codes follow the enums declared there. + +predicate gecode_inter_distance(array[int] of var int: x, var int: distance); + +predicate gecode_all_different_optional(array[int] of var int: x, + array[int] of var bool: present); + +predicate gecode_all_different_except(array[int] of var int: x, int: except_value); + +predicate gecode_all_different_offset(array[int] of int: offset, + array[int] of var int: x); + +predicate gecode_bool_int_channel(array[int] of var bool: x, + var int: value, int: offset); + +predicate gecode_sort_permutation(array[int] of var int: x, + array[int] of var int: y, array[int] of var int: permutation, int: offset); + +predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, + int: relation, var int: count); + +predicate gecode_count_in_fzn(array[int] of var int: x, set of int: values, + int: relation, var int: count); + +predicate gecode_count_matches_fzn(array[int] of var int: x, array[int] of int: values, + int: relation, var int: count); + +predicate gecode_global_cardinality_sets(array[int] of var int: x, + array[int] of int: cover, array[int] of set of int: counts); + +predicate gecode_nvalues_int_fzn(array[int] of var int: x, + int: relation, var int: count); + +predicate gecode_nvalues_bool_fzn(array[int] of var bool: x, + int: relation, var int: count); + +predicate gecode_array_int_ne(array[int] of var int: x, array[int] of var int: y); + +predicate gecode_array_bool_ne(array[int] of var bool: x, array[int] of var bool: y); + +predicate gecode_not_all_equal_int(array[int] of var int: x); + +predicate gecode_not_all_equal_bool(array[int] of var bool: x); + +predicate gecode_int_ite(var bool: b, var int: x, var int: y, var int: z); + +predicate gecode_bool_ite(var bool: b, var bool: x, var bool: y, var bool: z); + +predicate gecode_set_ite(var bool: b, var set of int: x, + var set of int: y, var set of int: z); + +predicate gecode_float_ite(var bool: b, var float: x, var float: y, var float: z); + +predicate gecode_int_divmod(var int: x, var int: y, + var int: quotient, var int: remainder); + +predicate gecode_int_nroot(var int: x, int: n, var int: y); + +predicate gecode_regular_bool(array[int] of var bool: x, int: q, + array[int] of int: transitions, int: initial, set of int: final); + +predicate gecode_path(int: offset, array[int] of var int: x, + var int: start, var int: end); + +predicate gecode_path_cost(array[int] of int: costs, int: offset, + array[int] of var int: x, var int: start, var int: end, var int: cost); + +predicate gecode_path_cost_array(array[int] of int: costs, int: offset, + array[int] of var int: x, var int: start, var int: end, + array[int] of var int: edge_costs, var int: cost); + +predicate gecode_order(var int: start0, int: duration0, + var int: start1, int: duration1, var bool: order); + +predicate gecode_unary_flex_optional(array[int] of var int: start, + array[int] of var int: duration, array[int] of var int: end, + array[int] of var bool: present); + +predicate gecode_cumulative_flex_optional(array[int] of var int: start, + array[int] of var int: duration, array[int] of var int: end, + array[int] of int: usage, var int: capacity, array[int] of var bool: present); + +predicate gecode_unary_typed_fzn(array[int] of int: task_type, + array[int] of var int: flex, array[int] of int: fix); + +predicate gecode_unary_typed_optional_fzn(array[int] of int: task_type, + array[int] of var int: flex, array[int] of int: fix, + array[int] of var bool: present); + +predicate gecode_cumulative_typed_fzn(array[int] of int: task_type, + array[int] of var int: flex, array[int] of int: fix, + array[int] of int: usage, var int: capacity); + +predicate gecode_cumulative_typed_optional_fzn(array[int] of int: task_type, + array[int] of var int: flex, array[int] of int: fix, + array[int] of int: usage, var int: capacity, array[int] of var bool: present); + +predicate gecode_nooverlap_optional(array[int] of var int: x, + array[int] of int: width, array[int] of var int: y, + array[int] of int: height, array[int] of var bool: present); + +predicate gecode_nooverlap_flex_optional(array[int] of var int: x, + array[int] of var int: width, array[int] of var int: right, + array[int] of var int: y, array[int] of var int: height, + array[int] of var int: top, array[int] of var bool: present); + +predicate gecode_set_convex_hull(var set of int: x, var set of int: hull); + +predicate gecode_set_convex(var set of int: x); + +predicate gecode_set_atmost_one(array[int] of var set of int: x, int: cardinality); + +predicate gecode_set_channel_sorted(array[int] of var int: x, var set of int: y); + +predicate gecode_set_operation_fzn(var set of int: x, int: operation, + var set of int: y, int: relation, var set of int: z); + +predicate gecode_array_set_intersect(array[int] of var set of int: x, + var set of int: y); + +predicate gecode_array_int_set_operation_fzn(array[int] of var int: x, + int: operation, set of int: initial, var set of int: y); + +predicate gecode_set_element2d(array[int] of var set of int: x, + var int: column, int: width, var int: row, int: height, var set of int: value); + +predicate gecode_array_int_set_element_fzn(array[int] of var int: x, + int: operation, var set of int: selected, + var set of int: value, set of int: universe); + +predicate gecode_float_pow(var float: x, int: n, var float: y); + +predicate gecode_float_nroot(var float: x, int: n, var float: y); + +predicate gecode_float_array_min(array[int] of var float: x, var float: y); + +predicate gecode_float_array_max(array[int] of var float: x, var float: y); + +predicate gecode_float_pow_base(float: base, var float: x, var float: y); + +predicate gecode_float_log_base(float: base, var float: x, var float: y); + +predicate gecode_arg_min_int(array[int] of var int: x, int: offset, + var int: index, bool: break_ties); + +predicate gecode_arg_max_int(array[int] of var int: x, int: offset, + var int: index, bool: break_ties); + +predicate gecode_arg_min_bool(array[int] of var bool: x, int: offset, + var int: index, bool: break_ties); + +predicate gecode_arg_max_bool(array[int] of var bool: x, int: offset, + var int: index, bool: break_ties); + +predicate gecode_set_min(var set of int: s, var int: x); + +predicate gecode_set_min_reif(var set of int: s, var int: x, var bool: b); + +predicate gecode_set_min_imp(var set of int: s, var int: x, var bool: b); + +predicate gecode_set_max(var set of int: s, var int: x); + +predicate gecode_set_max_reif(var set of int: s, var int: x, var bool: b); + +predicate gecode_set_max_imp(var set of int: s, var int: x, var bool: b); + +predicate gecode_set_singleton_rel_fzn(var set of int: s, int: relation, + var int: x); + +predicate gecode_set_singleton_rel_fzn_reif(var set of int: s, int: relation, + var int: x, var bool: b); + +predicate gecode_set_singleton_rel_fzn_imp(var set of int: s, int: relation, + var int: x, var bool: b); + +predicate gecode_array_set_seq(array[int] of var set of int: x); +predicate gecode_array_set_seq_union(array[int] of var set of int: x, var set of int: y); + +predicate gecode_float_rel_imp_fzn(var float: x, int: relation, var float: y, var bool: b); +predicate gecode_set_rel_imp_fzn(var set of int: x, int: relation, var set of int: y, var bool: b); + +predicate gecode_bool_float_channel(var bool: b, var float: x); diff --git a/gecode/flatzinc/mznlib/redefinitions-2.0.mzn b/gecode/flatzinc/mznlib/redefinitions-2.0.mzn index 2bc9c419de..a7aff30686 100644 --- a/gecode/flatzinc/mznlib/redefinitions-2.0.mzn +++ b/gecode/flatzinc/mznlib/redefinitions-2.0.mzn @@ -7,24 +7,32 @@ predicate bool_clause_reif(array[int] of var bool: as, predicate array_int_maximum(var int: m, array[int] of var int: x); +predicate gecode_float_array_max(array[int] of var float: x, var float: m); + predicate array_float_maximum(var float: m, array[int] of var float: x) = - let { int: l = min(index_set(x)), - int: u = max(index_set(x)), - float: ly = lb_array(x), - float: uy = ub_array(x), - array[l..u] of var ly..uy: y } in - y[l] = x[l] /\ - m = y[u] /\ - forall (i in l+1 .. u) ( y[i] == max(x[i],y[i-1]) ); + if length(x) = 0 then false else gecode_float_array_max(x, m) endif; predicate array_int_minimum(var int: m, array[int] of var int: x); +predicate gecode_float_array_min(array[int] of var float: x, var float: m); + predicate array_float_minimum(var float: m, array[int] of var float: x) = - let { int: l = min(index_set(x)), - int: u = max(index_set(x)), - float: ly = lb_array(x), - float: uy = ub_array(x), - array[l..u] of var ly..uy: y } in - y[l] = x[l] /\ - m = y[u] /\ - forall (i in l+1 .. u) ( y[i] == min(x[i],y[i-1]) ); + if length(x) = 0 then false else gecode_float_array_min(x, m) endif; + +predicate gecode_float_pow(var float: x, int: n, var float: y); +predicate gecode_float_pow_base(float: base, var float: x, var float: y); + +% Integer powers work for negative and zero bases as well as positive bases. +predicate float_pow(var float: x, var float: exponent, var float: y) = + let { + float: n = if is_fixed(exponent) then fix(exponent) else -1.0 endif; + bool: integer_exponent = if n >= 0.0 /\ n <= 2147483646.0 then + n = int2float(floor(n)) else false endif; + } in + if integer_exponent then + gecode_float_pow(x, floor(n), y) + elseif is_fixed(x) /\ fix(x) > 0.0 then + gecode_float_pow_base(fix(x), exponent, y) + else + assert(false, "Gecode float_pow requires a nonnegative integer exponent or a fixed positive base") + endif; diff --git a/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn b/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn new file mode 100644 index 0000000000..6e3426ce7b --- /dev/null +++ b/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn @@ -0,0 +1,103 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +% Solver overrides for variable-set extrema, following the standard +% redefinitions-2.1.1.mzn template. Partial contexts permit empty sets when +% the enclosing expression is inactive. +predicate gecode_set_min(var set of int: s, var int: m); +predicate gecode_set_min_reif(var set of int: s, var int: m, var bool: b); +predicate gecode_set_min_imp(var set of int: s, var int: m, var bool: b); +predicate gecode_set_max(var set of int: s, var int: m); +predicate gecode_set_max_reif(var set of int: s, var int: m, var bool: b); +predicate gecode_set_max_imp(var set of int: s, var int: m, var bool: b); + +function var $$E: min(var set of $$E: s) = + if card(ub(enum2int(s))) = 0 then + % Preserve the standard error for a statically empty set bound. + let { + var set of int: si = enum2int(s); + var min(ub(si))..max(ub(si)): m = + min([e + (max(ub(si)) + 1 - e) * (1 - (e in si)) | e in ub(si)]); + } in to_enum_internal(enum_of(s), m) + else + to_enum_internal(enum_of(s), + if mzn_in_root_context() then gecode_set_min_value(enum2int(s)) else + let { + constraint card(s) > 0; + } in gecode_set_min_partial(enum2int(s)) + endif) + endif; + +function var int: gecode_set_min_value(var set of int: s) :: promise_total = + let { + var min(ub(s) union {1})..max(ub(s) union {1}): m :: is_defined_var; + constraint card(s) > 0; + constraint gecode_set_min(s, m) :: defines_var(m); + } in m; + +function var int: gecode_set_min_partial(var set of int: s) :: promise_total = + let { + var min(ub(s) union {1})..max(ub(s) union {1}): m :: is_defined_var; + var bool: present = card(s) > 0; + constraint gecode_set_min_imp(s, m, present) :: defines_var(m); + } in m; + +function var $$E: max(var set of $$E: s) = + if card(ub(enum2int(s))) = 0 then + % Preserve the standard error for a statically empty set bound. + let { + var set of int: si = enum2int(s); + var min(ub(si))..max(ub(si)): m = + max([e + (min(ub(si)) - 1 - e) * (1 - (e in si)) | e in ub(si)]); + } in to_enum_internal(enum_of(s), m) + else + to_enum_internal(enum_of(s), + if mzn_in_root_context() then gecode_set_max_value(enum2int(s)) else + let { + constraint card(s) > 0; + } in gecode_set_max_partial(enum2int(s)) + endif) + endif; + +function var int: gecode_set_max_value(var set of int: s) :: promise_total = + let { + var min(ub(s) union {1})..max(ub(s) union {1}): m :: is_defined_var; + constraint card(s) > 0; + constraint gecode_set_max(s, m) :: defines_var(m); + } in m; + +function var int: gecode_set_max_partial(var set of int: s) :: promise_total = + let { + var min(ub(s) union {1})..max(ub(s) union {1}): m :: is_defined_var; + var bool: present = card(s) > 0; + constraint gecode_set_max_imp(s, m, present) :: defines_var(m); + } in m; diff --git a/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn b/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn index 45f8c949db..4c110bf701 100644 --- a/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn +++ b/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn @@ -14,8 +14,13 @@ predicate array_var_float_element2d_nonshifted(var int: idx1, var int: idx2, arr int: min_flat = min(index_set_1of2(x))*dim+min(index_set_2of2(x))-1; } in array_var_float_element_nonshifted((idx1*dim+idx2-min_flat)::domain, array1d(x), c); -predicate array_var_set_element2d_nonshifted(var int: idx1, var int: idx2, array[int,int] of var set of int: x, var set of int: c) = - let { - int: dim = card(index_set_2of2(x)); - int: min_flat = min(index_set_1of2(x))*dim+min(index_set_2of2(x))-1; - } in array_var_set_element_nonshifted((idx1*dim+idx2-min_flat)::domain, array1d(x), c); +predicate gecode_set_element2d(array[int] of var set of int: x, + var int: column, int: width, var int: row, int: height, var set of int: value); + +predicate array_var_set_element2d_nonshifted(var int: idx1, var int: idx2, + array[int,int] of var set of int: x, var set of int: c) = + if length(x) = 0 then false else + gecode_set_element2d(array1d(x), idx2-min(index_set_2of2(x)), + card(index_set_2of2(x)), idx1-min(index_set_1of2(x)), + card(index_set_1of2(x)), c) + endif; diff --git a/gecode/flatzinc/mznlib/redefinitions.mzn b/gecode/flatzinc/mznlib/redefinitions.mzn index e1e388009a..84037e1eb5 100644 --- a/gecode/flatzinc/mznlib/redefinitions.mzn +++ b/gecode/flatzinc/mznlib/redefinitions.mzn @@ -30,6 +30,8 @@ % % +include "solver-native-overloads.mzn"; + % Sums over Boolean variables predicate bool_lin_eq(array[int] of int: a, array[int] of var bool: x, var int: c); @@ -136,3 +138,63 @@ predicate int_lin_ne_imp(array [int] of int: as, array [int] of var int: bs, int: c, var bool: r); predicate set_in_imp(var int: x, set of int: S, var bool: r); + +% Half-reified scalar relations use the native implication propagators. +predicate gecode_float_rel_imp_fzn(var float: x, int: relation, + var float: y, var bool: b); +predicate float_eq_imp(var float: x, var float: y, var bool: b) = + gecode_float_rel_imp_fzn(x, 1, y, b); +predicate float_ne_imp(var float: x, var float: y, var bool: b) = + gecode_float_rel_imp_fzn(x, 2, y, b); +predicate float_le_imp(var float: x, var float: y, var bool: b) = + gecode_float_rel_imp_fzn(x, 3, y, b); +predicate float_lt_imp(var float: x, var float: y, var bool: b) = + gecode_float_rel_imp_fzn(x, 4, y, b); + +% MiniZinc normalizes scalar inequalities to one- or two-term linear forms. +% Recover those scalar forms; other linear expressions keep full reification. +predicate gecode_float_lin_scalar_imp(array[int] of float: a, + array[int] of var float: x, float: y, int: relation, var bool: b) = + let { + array[int] of float: aa = array1d(a); + array[int] of var float: xx = array1d(x); + } in + if length(aa) = 1 /\ length(xx) = 1 /\ aa[1] = 1.0 then + gecode_float_rel_imp_fzn(xx[1], relation, y, b) + elseif length(aa) = 2 /\ length(xx) = 2 /\ y = 0.0 /\ + aa[1] = 1.0 /\ aa[2] = -1.0 then + gecode_float_rel_imp_fzn(xx[1], relation, xx[2], b) + elseif length(aa) = 2 /\ length(xx) = 2 /\ y = 0.0 /\ + aa[1] = -1.0 /\ aa[2] = 1.0 then + gecode_float_rel_imp_fzn(xx[2], relation, xx[1], b) + else + let { var bool: r; } in + (if relation = 1 then float_lin_eq_reif(a, x, y, r) + elseif relation = 2 then float_lin_eq_reif(a, x, y, not r) + elseif relation = 3 then float_lin_le_reif(a, x, y, r) + else float_lin_lt_reif(a, x, y, r) endif) /\ bool_clause([r], [b]) + endif; + +predicate float_lin_eq_imp(array[int] of float: a, + array[int] of var float: x, float: y, var bool: b) = + gecode_float_lin_scalar_imp(a, x, y, 1, b); +predicate float_lin_ne_imp(array[int] of float: a, + array[int] of var float: x, float: y, var bool: b) = + gecode_float_lin_scalar_imp(a, x, y, 2, b); +predicate float_lin_le_imp(array[int] of float: a, + array[int] of var float: x, float: y, var bool: b) = + gecode_float_lin_scalar_imp(a, x, y, 3, b); +predicate float_lin_lt_imp(array[int] of float: a, + array[int] of var float: x, float: y, var bool: b) = + gecode_float_lin_scalar_imp(a, x, y, 4, b); + +predicate gecode_set_rel_imp_fzn(var set of int: x, int: relation, + var set of int: y, var bool: b); +predicate set_eq_imp(var set of int: x, var set of int: y, var bool: b) = + gecode_set_rel_imp_fzn(x, 1, y, b); +predicate set_ne_imp(var set of int: x, var set of int: y, var bool: b) = + gecode_set_rel_imp_fzn(x, 2, y, b); +predicate set_subset_imp(var set of int: x, var set of int: y, var bool: b) = + gecode_set_rel_imp_fzn(x, 3, y, b); +predicate set_superset_imp(var set of int: x, var set of int: y, var bool: b) = + gecode_set_rel_imp_fzn(x, 4, y, b); diff --git a/gecode/flatzinc/mznlib/solver-native-overloads.mzn b/gecode/flatzinc/mznlib/solver-native-overloads.mzn new file mode 100644 index 0000000000..80fdacf67b --- /dev/null +++ b/gecode/flatzinc/mznlib/solver-native-overloads.mzn @@ -0,0 +1,107 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +include "all_equal.mzn"; +include "regular.mzn"; +include "nvalue_fn.mzn"; +include "gecode_fzn.mzn"; + +% Typed standard operators preserve MiniZinc's index-set checks and use the +% non-reified native actor only in a positive root context. +function var bool: '!='(array[$U] of var $$E: x, + array[$U] of var $$E: y) :: promise_commutative = + matching_index_sets(x,y) /\ + if mzn_in_root_context() then + gecode_array_int_ne(enum2int(array1d(x)),enum2int(array1d(y))) + else + let {any: xx = enum2int(array1d(x)); any: yy = enum2int(array1d(y));} in + exists(i in index_set(xx))(xx[i] != yy[i]) + endif; + +function var bool: '!='(array[$U] of var bool: x, + array[$U] of var bool: y) :: promise_commutative = + matching_index_sets(x,y) /\ + if mzn_in_root_context() then + gecode_array_bool_ne(array1d(x),array1d(y)) + else + let {any: xx = array1d(x); any: yy = array1d(y);} in + exists(i in index_set(xx))(xx[i] != yy[i]) + endif; + +predicate all_equal(array[$X] of var bool: xs) = + if length(xs) <= 1 then true else fzn_all_equal_bool(array1d(xs)) endif; + +predicate fzn_all_equal_bool(array[int] of var bool: xs) = + (if length(xs) = 0 then true else + forall(i in index_set(xs))(xs[i] = xs[min(index_set(xs))]) endif); + +predicate fzn_all_equal_bool_reif(array[int] of var bool: xs, var bool: b) = + if is_fixed(b) /\ not fix(b) then gecode_not_all_equal_bool(xs) + else b <-> (if length(xs) = 0 then true else + forall(i in index_set(xs))(xs[i] = xs[min(index_set(xs))]) endif) endif; + +predicate nvalue(var int: n, array[$X] of var bool: xs) = + if mzn_in_root_context() then + gecode_nvalues_bool_fzn(array1d(xs),1,n) + else nvalue(n,bool2int(xs)) endif; + +function var int: nvalue(array[$X] of var bool: xs) :: promise_total = + let { + var bool2int(length(xs) > 0)..min(2,length(xs)): n :: is_defined_var; + constraint gecode_nvalues_bool_fzn(array1d(xs),1,n) :: defines_var(n); + } in n; + +% Boolean regular uses symbols 0 and 1. Symbols missing from the standard +% alphabet have dead transitions. The normalized alphabet also lets the +% standard reified translation represent an out-of-alphabet Boolean as false. +predicate regular(array[int] of var bool: xs, int: Q, set of int: S, + array[int,int] of int: d, int: q0, set of int: F) = + assert(Q > 0, "regular: 'Q' must be greater than zero") /\ + assert(card(S) > 0, "regular: 'S' must be non empty") /\ + assert(index_set_1of2(d) = 1..Q /\ index_set_2of2(d) = S, + "regular: the transition function 'd' must be [1..Q,S]") /\ + assert(forall(v in d)(v in 0..Q), + "regular: transition function 'd' points to states outside 0..Q") /\ + assert(q0 in 1..Q, "regular: start state 'q0' not in 1..Q") /\ + assert(F subset 1..Q, "regular: final states in 'F' contain states outside 1..Q") /\ + let { + array[1..Q,0..1] of int: dd = array2d(1..Q,0..1, + [if symbol in S then d[state,symbol] else 0 endif + | state in 1..Q, symbol in 0..1]); + } in + if mzn_in_root_context() then gecode_regular_bool(xs,Q,array1d(dd),q0,F) + else regular(bool2int(xs),Q,0..1,dd,q0,F) endif; + +predicate regular(array[int] of var bool: xs, int: Q, int: S, + array[int,int] of int: d, int: q0, set of int: F) = + assert(S > 0, "regular: 'S' must be greater than zero") /\ + regular(xs,Q,1..S,d,q0,F); diff --git a/gecode/flatzinc/registry.cpp b/gecode/flatzinc/registry.cpp index 8df3f19214..f6e38dc904 100755 --- a/gecode/flatzinc/registry.cpp +++ b/gecode/flatzinc/registry.cpp @@ -1154,7 +1154,8 @@ namespace Gecode { namespace FlatZinc { argmax(s, bv, offset, s.arg2IntVar(ce[2]), true, s.ann2ipl(ann)); } - void p_regular(FlatZincSpace& s, IntVarArgs iv, int q, int symbols_min, + template + void p_regular(FlatZincSpace& s, VarArgs iv, int q, int symbols_min, int symbols_max, IntArgs d, int q0, AST::SetLit* finals, AST::Node* ann) { int symbols = symbols_max - symbols_min + 1; @@ -1213,6 +1214,21 @@ namespace Gecode { namespace FlatZinc { ce[6]->getSet(), ann); } + void p_regular_bool(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + int q = ce[1]->getInt(); + IntArgs transitions = s.arg2intargs(ce[2]); + IntSet final = s.arg2intset(ce[4]); + if ((q <= 0) || (static_cast(q)*2 != transitions.size()) || + (ce[3]->getInt() < 1) || (ce[3]->getInt() > q) || + ((final.size() != 0) && ((final.min() < 1) || (final.max() > q)))) + throw FlatZinc::Error("Registry", "invalid Boolean DFA dimensions or states"); + for (int i=0; i q)) + throw FlatZinc::Error("Registry", "invalid Boolean DFA transition"); + p_regular(s, s.arg2boolvarargs(ce[0]), q, 0, 1, transitions, + ce[3]->getInt(), ce[4]->getSet(), ann); + } + void p_sort(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { IntVarArgs x = s.arg2intvarargs(ce[0]); @@ -1325,7 +1341,7 @@ namespace Gecode { namespace FlatZinc { IntArgs duration = s.arg2intargs(ce[1]); IntArgs height = s.arg2intargs(ce[2]); BoolVarArgs opt = s.arg2boolvarargs(ce[3]); - int bound = ce[4]->getInt(); + IntVar bound = s.arg2IntVar(ce[4]); unshare(s,start); cumulative(s,bound,start,duration,height,opt,s.ann2ipl(ann)); } @@ -1769,6 +1785,287 @@ fvar, mode, target, args, reason); } + + // The codes follow the declaration order of GecodeIntRelation in gecode.mzn. + IntRelType int_relation(AST::Node* arg) { + switch (arg->getInt()) { + case 1: return IRT_EQ; + case 2: return IRT_NQ; + case 3: return IRT_LQ; + case 4: return IRT_LE; + case 5: return IRT_GQ; + case 6: return IRT_GR; + default: throw FlatZinc::Error("Registry", "invalid integer relation"); + } + } + + void p_inter_distance(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs x = s.arg2intvarargs(ce[0]); + if (ce[1]->isIntVar()) { + // Copy shared occurrences, including the separation variable. + x << s.arg2IntVar(ce[1]); + unshare(s, x); + inter_distance(s, x.slice(0,1,x.size()-1), x[x.size()-1], + s.ann2ipl(ann)); + } else { + unshare(s, x); + inter_distance(s, x, ce[1]->getInt(), s.ann2ipl(ann)); + } + } + + void p_distinct_optional(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs x = s.arg2intvarargs(ce[0]); + BoolVarArgs present = s.arg2boolvarargs(ce[1]); + if (x.size() != present.size()) + throw FlatZinc::Error("Registry", "optional distinct arrays must have equal lengths"); + int lo = Int::Limits::max, hi = Int::Limits::min, n = 0; + bool mandatory = true; + for (int i=0; i Int::Limits::min+n)) { + unshare(s, x); + distinct(s, present, x, s.ann2ipl(ann)); + } else if (mandatory) { + IntVarArgs active; + for (int i=0; igetInt(); + // FlatZinc constants can exceed Gecode's variable bounds. + if ((c < Int::Limits::min) || (c > Int::Limits::max)) { + unshare(s, x); + distinct(s, x, s.ann2ipl(ann)); + return; + } + int lo = Int::Limits::max, hi = Int::Limits::min, n = 0; + for (int i=0; i Int::Limits::min+n)) { + unshare(s, x); + distinct(s, x, c, s.ann2ipl(ann)); + } else { + for (int i=0; igetInt(), s.ann2ipl(ann)); + } + void p_sort_permutation(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs x = s.arg2intvarargs(ce[0]); + IntVarArgs y = s.arg2intvarargs(ce[1]); + IntVarArgs z = s.arg2intvarargs(ce[2]); + if ((x.size() != y.size()) || (x.size() != z.size())) + throw FlatZinc::Error("Registry", "sort arrays must have equal lengths"); + int offset = ce[3]->getInt(); + for (int i=0; igetInt(), s.arg2IntVar(ce[2]), + s.ann2ipl(ann)); + } + + void p_path(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs x = s.arg2intvarargs(ce[1]); + unshare(s, x); + path(s, ce[0]->getInt(), x, s.arg2IntVar(ce[2]), s.arg2IntVar(ce[3]), + s.ann2ipl(ann)); + } + void p_path_cost(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs x = s.arg2intvarargs(ce[2]); + unshare(s, x); + path(s, s.arg2intargs(ce[0]), ce[1]->getInt(), x, + s.arg2IntVar(ce[3]), s.arg2IntVar(ce[4]), s.arg2IntVar(ce[5]), + s.ann2ipl(ann)); + } + void p_path_cost_array(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs x = s.arg2intvarargs(ce[2]); + unshare(s, x); + path(s, s.arg2intargs(ce[0]), ce[1]->getInt(), x, + s.arg2IntVar(ce[3]), s.arg2IntVar(ce[4]), s.arg2intvarargs(ce[5]), + s.arg2IntVar(ce[6]), s.ann2ipl(ann)); + } + void p_order(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + order(s, s.arg2IntVar(ce[0]), ce[1]->getInt(), s.arg2IntVar(ce[2]), + ce[3]->getInt(), s.arg2BoolVar(ce[4]), s.ann2ipl(ann)); + } + TaskTypeArgs task_types(FlatZincSpace& s, AST::Node* arg) { + IntArgs codes = s.arg2intargs(arg); + TaskTypeArgs types(codes.size()); + for (int i=0; ia.size() == 4) + unary(s, task_types(s, ce[0]), flex, s.arg2intargs(ce[2]), + s.arg2boolvarargs(ce[3]), s.ann2ipl(ann)); + else + unary(s, task_types(s, ce[0]), flex, s.arg2intargs(ce[2]), s.ann2ipl(ann)); + } + void p_cumulative_typed(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs flex = s.arg2intvarargs(ce[1]); + unshare(s, flex); + if (ce.args->a.size() == 6) + cumulative(s, s.arg2IntVar(ce[4]), task_types(s, ce[0]), flex, + s.arg2intargs(ce[2]), s.arg2intargs(ce[3]), + s.arg2boolvarargs(ce[5]), s.ann2ipl(ann)); + else + cumulative(s, s.arg2IntVar(ce[4]), task_types(s, ce[0]), flex, + s.arg2intargs(ce[2]), s.arg2intargs(ce[3]), s.ann2ipl(ann)); + } + void p_nooverlap_optional(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + nooverlap(s, s.arg2intvarargs(ce[0]), s.arg2intargs(ce[1]), + s.arg2intvarargs(ce[2]), s.arg2intargs(ce[3]), + s.arg2boolvarargs(ce[4]), s.ann2ipl(ann)); + } + void p_nooverlap_flex(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + nooverlap(s, s.arg2intvarargs(ce[0]), s.arg2intvarargs(ce[1]), + s.arg2intvarargs(ce[2]), s.arg2intvarargs(ce[3]), + s.arg2intvarargs(ce[4]), s.arg2intvarargs(ce[5]), + s.arg2boolvarargs(ce[6]), s.ann2ipl(ann)); + } + + void p_arg_min_int(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs xy = s.arg2intvarargs(ce[0]); + xy << s.arg2IntVar(ce[2]); + unshare(s, xy); + argmin(s, xy.slice(0,1,xy.size()-1), ce[1]->getInt(), + xy[xy.size()-1], ce[3]->getBool(), s.ann2ipl(ann)); + } + + void p_arg_max_int(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs xy = s.arg2intvarargs(ce[0]); + xy << s.arg2IntVar(ce[2]); + unshare(s, xy); + argmax(s, xy.slice(0,1,xy.size()-1), ce[1]->getInt(), + xy[xy.size()-1], ce[3]->getBool(), s.ann2ipl(ann)); + } + + void p_arg_min_bool(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + BoolVarArgs x = s.arg2boolvarargs(ce[0]); + unshare(s, x); + argmin(s, x, ce[1]->getInt(), s.arg2IntVar(ce[2]), + ce[3]->getBool(), s.ann2ipl(ann)); + } + + void p_arg_max_bool(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + BoolVarArgs x = s.arg2boolvarargs(ce[0]); + unshare(s, x); + argmax(s, x, ce[1]->getInt(), s.arg2IntVar(ce[2]), + ce[3]->getBool(), s.ann2ipl(ann)); + } + + void p_unary_flex_optional(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs start = s.arg2intvarargs(ce[0]); + unshare(s, start); + unary(s, start, s.arg2intvarargs(ce[1]), s.arg2intvarargs(ce[2]), + s.arg2boolvarargs(ce[3]), s.ann2ipl(ann)); + } + + + void p_cumulative_flex_optional(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs start = s.arg2intvarargs(ce[0]); + unshare(s, start); + cumulative(s, s.arg2IntVar(ce[4]), start, s.arg2intvarargs(ce[1]), + s.arg2intvarargs(ce[2]), s.arg2intargs(ce[3]), + s.arg2boolvarargs(ce[5]), s.ann2ipl(ann)); + } + class IntPoster { public: IntPoster(void) { @@ -1958,6 +2255,43 @@ fvar, registry().add("member_bool",&p_member_bool); registry().add("gecode_member_bool_reif",&p_member_bool_reif); + registry().add("gecode_unary_flex_optional", &p_unary_flex_optional); + registry().add("gecode_cumulative_flex_optional", &p_cumulative_flex_optional); + registry().add("gecode_array_int_ne", &p_array_int_ne); + registry().add("gecode_not_all_equal_int", &p_not_all_equal_int); + registry().add("gecode_array_bool_ne", &p_array_bool_ne); + registry().add("gecode_not_all_equal_bool", &p_not_all_equal_bool); + registry().add("gecode_inter_distance", &p_inter_distance); + registry().add("gecode_bool_int_channel", &p_bool_int_channel); + registry().add("gecode_sort_permutation", &p_sort_permutation); + registry().add("gecode_count_int_fzn", &p_count_int); + registry().add("gecode_count_in_fzn", &p_count_in); + registry().add("gecode_count_matches_fzn", &p_count_matches); + registry().add("gecode_global_cardinality_sets", &p_global_cardinality_sets); + registry().add("gecode_nvalues_int_fzn", &p_nvalues_int); + registry().add("gecode_nvalues_bool_fzn", &p_nvalues_bool); + registry().add("gecode_int_ite", &p_int_ite); + registry().add("gecode_bool_ite", &p_bool_ite); + registry().add("gecode_int_divmod", &p_int_divmod); + registry().add("gecode_int_nroot", &p_int_nroot); + registry().add("gecode_path", &p_path); + registry().add("gecode_path_cost", &p_path_cost); + registry().add("gecode_path_cost_array", &p_path_cost_array); + registry().add("gecode_order", &p_order); + registry().add("gecode_nooverlap_optional", &p_nooverlap_optional); + registry().add("gecode_all_different_optional", &p_distinct_optional); + registry().add("gecode_all_different_except", &p_distinct_except); + registry().add("gecode_regular_bool", &p_regular_bool); + registry().add("gecode_unary_typed_fzn", &p_unary_typed); + registry().add("gecode_unary_typed_optional_fzn", &p_unary_typed); + registry().add("gecode_cumulative_typed_fzn", &p_cumulative_typed); + registry().add("gecode_cumulative_typed_optional_fzn", &p_cumulative_typed); + registry().add("gecode_nooverlap_flex_optional", &p_nooverlap_flex); + registry().add("gecode_arg_min_int", &p_arg_min_int); + registry().add("gecode_arg_max_int", &p_arg_max_int); + registry().add("gecode_arg_min_bool", &p_arg_min_bool); + registry().add("gecode_arg_max_bool", &p_arg_max_bool); + registry().add("gecode_blackbox", &p_blackbox); registry().add("gecode_blackbox_bounds", &p_blackbox_bounds); } @@ -2259,6 +2593,113 @@ fvar, precede(s,x,p_s,p_t); } + + SetRelType set_relation(AST::Node* arg) { + switch (arg->getInt()) { + case 1: return SRT_EQ; + case 2: return SRT_NQ; + case 3: return SRT_SUB; + case 4: return SRT_SUP; + case 5: return SRT_DISJ; + case 6: return SRT_CMPL; + case 7: return SRT_LQ; + case 8: return SRT_LE; + case 9: return SRT_GQ; + case 10: return SRT_GR; + default: throw FlatZinc::Error("Registry", "invalid set relation"); + } + } + SetOpType set_operation(AST::Node* arg) { + switch (arg->getInt()) { + case 1: return SOT_UNION; + case 2: return SOT_DUNION; + case 3: return SOT_INTER; + case 4: return SOT_MINUS; + default: throw FlatZinc::Error("Registry", "invalid set operation"); + } + } + void p_set_ite(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + ite(s, s.arg2BoolVar(ce[0]), s.arg2SetVar(ce[1]), s.arg2SetVar(ce[2]), + s.arg2SetVar(ce[3])); + } + void p_set_convex_hull(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + convex(s, s.arg2SetVar(ce[0]), s.arg2SetVar(ce[1])); + } + void p_set_atmost_one(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + int cardinality = ce[1]->getInt(); + if (cardinality < 0) + throw FlatZinc::Error("Registry", "set cardinality must be nonnegative"); + SetVarArgs x = s.arg2setvarargs(ce[0]); + if ((x.size() < 2) || (cardinality <= 1)) { + // Pairwise intersections are automatically at most one in this case. + // The native atmostOne implementation assumes at least two elements. + for (int i=0; i(cardinality)); + } + } + void p_set_channel_sorted(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + channelSorted(s, s.arg2intvarargs(ce[0]), s.arg2SetVar(ce[1])); + } + void p_set_operation(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + rel(s, s.arg2SetVar(ce[0]), set_operation(ce[1]), s.arg2SetVar(ce[2]), + set_relation(ce[3]), s.arg2SetVar(ce[4])); + } + void p_array_set_intersect(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + rel(s, SOT_INTER, s.arg2setvarargs(ce[0]), s.arg2SetVar(ce[1])); + } + void p_array_int_set_operation(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + rel(s, set_operation(ce[1]), s.arg2intvarargs(ce[0]), + s.arg2intset(ce[2]), s.arg2SetVar(ce[3])); + } + void p_set_element2d(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + SetVarArgs x = s.arg2setvarargs(ce[0]); + int width = ce[2]->getInt(), height = ce[4]->getInt(); + if ((width <= 0) || (height <= 0) || + (static_cast(width)*height != x.size())) + throw FlatZinc::Error("Registry", "invalid set matrix dimensions"); + element(s, x, s.arg2IntVar(ce[1]), width, + s.arg2IntVar(ce[3]), height, s.arg2SetVar(ce[5])); + } + void p_array_int_set_element(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + element(s, set_operation(ce[1]), s.arg2intvarargs(ce[0]), + s.arg2SetVar(ce[2]), s.arg2SetVar(ce[3]), s.arg2intset(ce[4])); + } + + void p_set_min(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + min(s, s.arg2SetVar(ce[0]), s.arg2IntVar(ce[1])); + } + template + void p_set_min_reif(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + min(s, s.arg2SetVar(ce[0]), s.arg2IntVar(ce[1]), + Reify(s.arg2BoolVar(ce[2]), mode)); + } + + void p_set_max(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + max(s, s.arg2SetVar(ce[0]), s.arg2IntVar(ce[1])); + } + template + void p_set_max_reif(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + max(s, s.arg2SetVar(ce[0]), s.arg2IntVar(ce[1]), + Reify(s.arg2BoolVar(ce[2]), mode)); + } + + + void p_set_singleton_rel(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + rel(s, s.arg2SetVar(ce[0]), set_relation(ce[1]), s.arg2IntVar(ce[2])); + } + template + void p_set_singleton_rel_reif(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + rel(s, s.arg2SetVar(ce[0]), set_relation(ce[1]), s.arg2IntVar(ce[2]), + Reify(s.arg2BoolVar(ce[3]), mode)); + } + + void p_set_rel_imp(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + rel(s, s.arg2SetVar(ce[0]), set_relation(ce[1]), s.arg2SetVar(ce[2]), + Reify(s.arg2BoolVar(ce[3]), RM_IMP)); + } + class SetPoster { public: SetPoster(void) { @@ -2311,6 +2752,26 @@ fvar, &p_weights); registry().add("gecode_inverse_set", &p_inverse_set); registry().add("gecode_precede_set", &p_precede_set); + registry().add("gecode_set_rel_imp_fzn", &p_set_rel_imp); + registry().add("gecode_set_ite", &p_set_ite); + registry().add("gecode_set_convex_hull", &p_set_convex_hull); + registry().add("gecode_set_atmost_one", &p_set_atmost_one); + registry().add("gecode_set_channel_sorted", &p_set_channel_sorted); + registry().add("gecode_set_operation_fzn", &p_set_operation); + registry().add("gecode_array_set_intersect", &p_array_set_intersect); + registry().add("gecode_array_int_set_operation_fzn", &p_array_int_set_operation); + registry().add("gecode_set_element2d", &p_set_element2d); + registry().add("gecode_array_int_set_element_fzn", &p_array_int_set_element); + registry().add("gecode_set_min", &p_set_min); + registry().add("gecode_set_min_reif", &p_set_min_reif); + registry().add("gecode_set_min_imp", &p_set_min_reif); + registry().add("gecode_set_max", &p_set_max); + registry().add("gecode_set_max_reif", &p_set_max_reif); + registry().add("gecode_set_max_imp", &p_set_max_reif); + registry().add("gecode_set_singleton_rel_fzn", &p_set_singleton_rel); + registry().add("gecode_set_singleton_rel_fzn_reif", &p_set_singleton_rel_reif); + registry().add("gecode_set_singleton_rel_fzn_imp", &p_set_singleton_rel_reif); + } }; SetPoster __set_poster; @@ -2486,10 +2947,65 @@ fvar, #endif + + void p_float_ite(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + ite(s, s.arg2BoolVar(ce[0]), s.arg2FloatVar(ce[1]), s.arg2FloatVar(ce[2]), + s.arg2FloatVar(ce[3])); + } + void p_float_pow(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + pow(s, s.arg2FloatVar(ce[0]), ce[1]->getInt(), s.arg2FloatVar(ce[2])); + } + void p_float_nroot(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + if (ce[1]->getInt() <= 0) + throw FlatZinc::Error("Registry", "float root degree must be positive"); + nroot(s, s.arg2FloatVar(ce[0]), ce[1]->getInt(), s.arg2FloatVar(ce[2])); + } + void p_float_array_min(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + min(s, s.arg2floatvarargs(ce[0]), s.arg2FloatVar(ce[1])); + } + void p_float_array_max(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + max(s, s.arg2floatvarargs(ce[0]), s.arg2FloatVar(ce[1])); + } + void p_bool_float_channel(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + channel(s, s.arg2BoolVar(ce[0]), s.arg2FloatVar(ce[1])); + } +#ifdef GECODE_HAS_MPFR + void p_float_pow_base(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + pow(s, ce[0]->getFloat(), s.arg2FloatVar(ce[1]), s.arg2FloatVar(ce[2])); + } + void p_float_log_base(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + log(s, ce[0]->getFloat(), s.arg2FloatVar(ce[1]), s.arg2FloatVar(ce[2])); + } +#endif + + void p_float_rel_imp(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { + FloatRelType r; + switch (ce[1]->getInt()) { + case 1: r = FRT_EQ; break; + case 2: r = FRT_NQ; break; + case 3: r = FRT_LQ; break; + case 4: r = FRT_LE; break; + case 5: r = FRT_GQ; break; + case 6: r = FRT_GR; break; + default: throw FlatZinc::Error("Registry", "invalid float relation"); + } + rel(s, s.arg2FloatVar(ce[0]), r, s.arg2FloatVar(ce[2]), + Reify(s.arg2BoolVar(ce[3]), RM_IMP)); + } + class FloatPoster { public: FloatPoster(void) { registry().add("int2float",&p_int2float); + + registry().add("gecode_float_rel_imp_fzn", &p_float_rel_imp); + registry().add("gecode_float_ite", &p_float_ite); + registry().add("gecode_float_pow", &p_float_pow); + registry().add("gecode_float_nroot", &p_float_nroot); + registry().add("gecode_float_array_min", &p_float_array_min); + registry().add("gecode_float_array_max", &p_float_array_max); + registry().add("gecode_bool_float_channel", &p_bool_float_channel); + registry().add("float_abs",&p_float_abs); registry().add("float_sqrt",&p_float_sqrt); registry().add("float_eq",&p_float_eq); @@ -2513,6 +3029,8 @@ fvar, registry().add("float_lin_lt_reif",&p_float_lin_lt_reif); #ifdef GECODE_HAS_MPFR + registry().add("gecode_float_pow_base", &p_float_pow_base); + registry().add("gecode_float_log_base", &p_float_log_base); registry().add("float_acos",&p_float_acos); registry().add("float_asin",&p_float_asin); registry().add("float_atan",&p_float_atan); diff --git a/gecode/float/transcendental/exp-log.hpp b/gecode/float/transcendental/exp-log.hpp index 1ff43f0460..c9fc4234af 100755 --- a/gecode/float/transcendental/exp-log.hpp +++ b/gecode/float/transcendental/exp-log.hpp @@ -97,16 +97,18 @@ namespace Gecode { namespace Float { namespace Transcendental { ExecStatus Pow::post(Home home, FloatNum base, A x0, B x1) { if (base <= 0) return ES_FAILED; - if (x0 == x1) { - GECODE_ME_CHECK(x0.eq(home,0.0)); - } else { - GECODE_ME_CHECK(x1.gq(home,0.0)); - if (x1.max() == 0.0) - return ES_FAILED; - GECODE_ME_CHECK(x0.eq(home,log(x1.domain())/log(base))); - GECODE_ME_CHECK(x1.eq(home,exp(x0.domain()*log(base)))); - (void) new (home) Pow(home,base,x0,x1); + if (base == 1.0) { + GECODE_ME_CHECK(x1.eq(home,1.0)); + return ES_OK; } + GECODE_ME_CHECK(x1.gq(home,0.0)); + if (x1.max() == 0.0) + return ES_FAILED; + // Enclose log(base) in both directions to retain exact power solutions. + FloatVal lb = log(FloatVal(base)); + GECODE_ME_CHECK(x0.eq(home,log(x1.domain())/lb)); + GECODE_ME_CHECK(x1.eq(home,exp(x0.domain()*lb))); + (void) new (home) Pow(home,base,x0,x1); return ES_OK; } @@ -127,9 +129,10 @@ namespace Gecode { namespace Float { namespace Transcendental { Pow::propagate(Space& home, const ModEventDelta&) { if (x1.max() == 0.0) return ES_FAILED; - GECODE_ME_CHECK(x0.eq(home,log(x1.domain())/log(base))); - GECODE_ME_CHECK(x1.eq(home,exp(x0.domain()*log(base)))); - return x0.assigned() ? home.ES_SUBSUMED(*this) : ES_FIX; + FloatVal lb = log(FloatVal(base)); + GECODE_ME_CHECK(x0.eq(home,log(x1.domain())/lb)); + GECODE_ME_CHECK(x1.eq(home,exp(x0.domain()*lb))); + return x0.assigned() ? home.ES_SUBSUMED(*this) : ES_NOFIX; } }}} diff --git a/test/flatzinc/native-registry.cpp b/test/flatzinc/native-registry.cpp new file mode 100644 index 0000000000..95f5b198af --- /dev/null +++ b/test/flatzinc/native-registry.cpp @@ -0,0 +1,110 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ +/* + * Main authors: + * Mikael Zayenz Lagerkvist + * + * Copyright: + * Mikael Zayenz Lagerkvist, 2026 + * + * This file is part of Gecode, the generic constraint + * development environment: + * http://www.gecode.dev + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "test/flatzinc.hh" +#include + +namespace Test { namespace FlatZinc { + namespace { + // The algorithm annotation must select the native filtering stage. + class AlgorithmAnnotations : public Base { + public: + AlgorithmAnnotations(void) : Base("FlatZinc::NativeRegistry::AlgorithmAnnotations") {} + virtual bool run(void) { + const char* names[] = {"gecode_basic_propagation", + "gecode_advanced_propagation", "gecode_full_propagation"}; + for (int i=0; i<3; i++) { + Gecode::FlatZinc::FlatZincSpace space; + Gecode::FlatZinc::AST::Array ann; + ann.a.push_back(new Gecode::FlatZinc::AST::Atom(names[i])); + ann.a.push_back(new Gecode::FlatZinc::AST::Atom("domain")); + Gecode::IntVarArgs x(3); + x[0] = Gecode::IntVar(space,2,6); + x[1] = Gecode::IntVar(space,10,14); + x[2] = Gecode::IntVar(space,4,15); + Gecode::IntVar distance(space,6,8); + Gecode::inter_distance(space,x,distance,space.ann2ipl(&ann)); + if ((space.status() == Gecode::SS_FAILED) || !distance.assigned() || + (distance.val() != 6)) + return false; + if (i == 0) { + if ((x[0].min() != 2) || (x[0].max() != 6)) return false; + } else if (!x.assigned() || (x[0].val() != 2) || + (x[1].val() != 14) || (x[2].val() != 8)) { + return false; + } + } + return true; + } + } algorithm_annotations; + + class Create { + public: + Create(void) { + const std::string sat = "----------\n"; + const std::string unsat = "=====UNSATISFIABLE=====\n"; + (void) new FlatZincTest("NativeRegistry::DistanceShared", + "var 0..2:x; constraint gecode_inter_distance([x,x],1); solve satisfy;", unsat); + (void) new FlatZincTest("NativeRegistry::NotAllEqual", + "constraint gecode_not_all_equal_int([1,1,2]); solve satisfy;", sat); + (void) new FlatZincTest("NativeRegistry::CardinalityHoles", + "constraint gecode_global_cardinality_sets([1,1],[1],[{0,3}]); solve satisfy;", unsat); + (void) new FlatZincTest("NativeRegistry::SortOrientation", + "constraint gecode_sort_permutation([3,1,2],[1,2,3],[2,3,1],1); solve satisfy;", unsat); + (void) new FlatZincTest("NativeRegistry::ArgMinShared", + "var 1..2:x; constraint gecode_arg_min_int([x,x],1,x,false); solve satisfy;", sat); + (void) new FlatZincTest("NativeRegistry::DivmodNegative", + "constraint gecode_int_divmod(-7,4,-1,-3); solve satisfy;", sat); + (void) new FlatZincTest("NativeRegistry::PathCostSentinel", + "constraint gecode_path_cost_array([0,4,9,1,0,5,7,2,0],0,[1,2,3],0,2,[4,5,0],9); solve satisfy;", sat); + (void) new FlatZincErrorTest("NativeRegistry::SortSize", + "constraint gecode_sort_permutation([1,2],[1],[1,2],1); solve satisfy;", + {}, "sort arrays must have equal lengths"); +#ifdef GECODE_HAS_SET_VARS + (void) new FlatZincTest("NativeRegistry::AtmostOneSingletonCardinality", + "constraint gecode_set_atmost_one([{1}],2); solve satisfy;", unsat); + (void) new FlatZincTest("NativeRegistry::AtmostOneUnitCardinality", + "constraint gecode_set_atmost_one([{1},{1}],1); solve satisfy;", sat); + (void) new FlatZincErrorTest("NativeRegistry::SetMatrixSize", + "constraint gecode_set_element2d([{1}],0,0,0,1,{1}); solve satisfy;", + {}, "invalid set matrix dimensions"); + (void) new FlatZincTest("NativeRegistry::EmptySetMinReification", + "constraint gecode_set_min_reif({},1,false); solve satisfy;", sat); +#endif + } + }; + Create c; + } +}} + +// STATISTICS: test-flatzinc diff --git a/test/float/transcendental.cpp b/test/float/transcendental.cpp index 0445017139..1864f84dfc 100644 --- a/test/float/transcendental.cpp +++ b/test/float/transcendental.cpp @@ -46,6 +46,62 @@ namespace Test { namespace Float { /// %Tests for transcendental constraints namespace Transcendental { + /// Exact fixed-base powers, inverse logarithms, and shared variables + class PowBaseConsistency : public Base { + class TestSpace : public Gecode::Space { + public: + Gecode::FloatVar x, y; + TestSpace(Gecode::FloatNum base, Gecode::FloatNum a, + Gecode::FloatNum b, bool logarithm, bool shared, + bool delayed) + : x(*this,delayed ? -4.0 : a,delayed ? 8.0 : a), + y(*this,delayed ? 0.0 : b,delayed ? 8.0 : b) { + if (shared) + y = x; + if (logarithm) + Gecode::log(*this,base,y,x); + else + Gecode::pow(*this,base,x,y); + } + TestSpace(TestSpace& s) : Gecode::Space(s) { + x.update(*this,s.x); + y.update(*this,s.y); + } + virtual Gecode::Space* copy(void) { + return new TestSpace(*this); + } + }; + + bool check(Gecode::FloatNum base, Gecode::FloatNum a, + Gecode::FloatNum b, bool logarithm, bool shared, + bool delayed, bool expected) { + TestSpace s(base,a,b,logarithm,shared,delayed); + if (delayed && (s.status() != Gecode::SS_FAILED)) { + Gecode::rel(s,s.x,Gecode::FRT_EQ,a); + Gecode::rel(s,s.y,Gecode::FRT_EQ,b); + } + return (s.status() != Gecode::SS_FAILED) == expected; + } + public: + PowBaseConsistency(void) + : Base("Float::Transcendental::PowBaseConsistency") {} + virtual bool run(void) { + for (int delayed=0; delayed<2; delayed++) { + for (int logarithm=0; logarithm<2; logarithm++) { + // Exact binary values, independent of exp/log test oracles. + if (!check(2.0,3.0,8.0,logarithm,false,delayed,true) || + !check(2.0,3.0,7.0,logarithm,false,delayed,false) || + !check(0.5,3.0,0.125,logarithm,false,delayed,true) || + !check(1.0,3.0,1.0,logarithm,false,delayed,true) || + !check(2.0,0.0,0.0,logarithm,true,delayed,false) || + !check(0.25,0.5,0.5,logarithm,true,delayed,true)) + return false; + } + } + return true; + } + } pow_base_consistency; + /// %Test for exponent constraint class ExpXY : public Test { public: @@ -190,7 +246,7 @@ namespace Test { namespace Float { virtual MaybeType solution(const Assignment& x) const { if ((x[0].max() <= 0.0) || (base <= 0.0)) return MT_FALSE; - return eq(log(x[0]) / log(base), x[1]); + return eq(log(x[0]) / log(Gecode::FloatVal(base)), x[1]); } /// Post constraint on \a x virtual void post(Gecode::Space& home, Gecode::FloatVarArray& x) { @@ -209,13 +265,13 @@ namespace Test { namespace Float { virtual MaybeType solution(const Assignment& x) const { if ((x[0].max() <= 0.0) || (base <= 0.0)) return MT_FALSE; - return eq(log(x[0]) / log(base), x[1]); + return eq(log(x[0]) / log(Gecode::FloatVal(base)), x[1]); } /// Extend assignment \a x virtual bool extendAssignment(Assignment& x) const { if ((x[0].max() <= 0.0) || (base <= 0.0)) return false; - Gecode::FloatVal d = log(x[0])/log(base); + Gecode::FloatVal d = log(x[0])/log(Gecode::FloatVal(base)); if (Gecode::Float::subset(d, dom)) { x.set(1, d); return true; @@ -240,7 +296,7 @@ namespace Test { namespace Float { virtual MaybeType solution(const Assignment& x) const { if ((x[0].max() <= 0.0) || (base <= 0.0)) return MT_FALSE; - return eq(log(x[0]) / log(base), x[0]); + return eq(log(x[0]) / log(Gecode::FloatVal(base)), x[0]); } /// Post constraint on \a x virtual void post(Gecode::Space& home, Gecode::FloatVarArray& x) { @@ -259,7 +315,7 @@ namespace Test { namespace Float { virtual MaybeType solution(const Assignment& x) const { if (base <= 0.0) return MT_FALSE; - return eq(exp(x[0] * log(base)), x[1]); + return eq(exp(x[0] * log(Gecode::FloatVal(base))), x[1]); } /// Post constraint on \a x virtual void post(Gecode::Space& home, Gecode::FloatVarArray& x) { @@ -278,12 +334,12 @@ namespace Test { namespace Float { virtual MaybeType solution(const Assignment& x) const { if (base <= 0.0) return MT_FALSE; - return eq(exp(x[0] * log(base)), x[1]); + return eq(exp(x[0] * log(Gecode::FloatVal(base))), x[1]); } /// Extend assignment \a x virtual bool extendAssignment(Assignment& x) const { if (base <= 0.0) return false; - Gecode::FloatVal d = exp(x[0]*log(base)); + Gecode::FloatVal d = exp(x[0]*log(Gecode::FloatVal(base))); if (Gecode::Float::subset(d, dom)) { x.set(1, d); return true; @@ -312,7 +368,7 @@ namespace Test { namespace Float { virtual MaybeType solution(const Assignment& x) const { if ((x[0].max() <= 0.0) || (base <= 0.0)) return MT_FALSE; - return eq(exp(x[0] * log(base)), x[0]); + return eq(exp(x[0] * log(Gecode::FloatVal(base))), x[0]); } /// Post constraint on \a x virtual void post(Gecode::Space& home, Gecode::FloatVarArray& x) { From 55ac4f20ae209932e1c553d893589084a2fc66ab Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 18:21:35 +0200 Subject: [PATCH 02/22] Align nooverlap index validation and document public native bindings --- docs/minizinc-constraint-audit.md | 22 ++++++++++++++++- gecode/flatzinc/mznlib/gecode.mzn | 34 ++++++++++----------------- gecode/flatzinc/mznlib/gecode_fzn.mzn | 14 +++++++++++ 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/docs/minizinc-constraint-audit.md b/docs/minizinc-constraint-audit.md index 2f73906725..2ed8d2ba77 100644 --- a/docs/minizinc-constraint-audit.md +++ b/docs/minizinc-constraint-audit.md @@ -4,7 +4,7 @@ The audit asks whether a standard MiniZinc expression already reaches a native G Verification uses MiniZinc 2.10.1 (build 33348285743) and this checkout's FlatZinc solver. Standard-library filenames below refer to that installation. A local MiniZinc 2.9.3 compiler checkout supplied additional source evidence; the installed compiler's generated FlatZinc was checked independently. Native declarations and posting code are in [int.hh](../gecode/int.hh), [set.hh](../gecode/set.hh), [float.hh](../gecode/float.hh), and their implementation directories. -[gecode.mzn](../gecode/flatzinc/mznlib/gecode.mzn) supplies the public overloads and semantic relation/task enums. [gecode_fzn.mzn](../gecode/flatzinc/mznlib/gecode_fzn.mzn) contains typed primitive declarations, including internal leaves used by standard translations. [registry.cpp](../gecode/flatzinc/registry.cpp) posts those primitives. Existing registry aliases are reused rather than registered twice. Both build systems install the entire solver-library directory. +[gecode.mzn](../gecode/flatzinc/mznlib/gecode.mzn) supplies the public overloads and semantic relation/task enums. [gecode_fzn.mzn](../gecode/flatzinc/mznlib/gecode_fzn.mzn) contains typed primitive declarations, including internal leaves used by standard translations. Public predicates that already have a single typed signature are documented at their declaration there, without a redundant forwarding wrapper. [registry.cpp](../gecode/flatzinc/registry.cpp) posts those primitives. Existing registry aliases are reused rather than registered twice. Both build systems install the entire solver-library directory. ## Automatic standard routes @@ -94,3 +94,23 @@ constraint gecode_inter_distance(y, distance) :: bounds_propagation :: gecode_advanced_propagation; constraint gecode_count(x, targets, GecodeLe, limit) :: domain_propagation; ``` + +To run the retained native regressions from an existing CMake build: + +```sh +cmake --build build/minizinc-registry --target fzn-gecode gecode-test -j6 +build/minizinc-registry/bin/gecode-test -test '^FlatZinc::' -iter 1 -threads 6 +build/minizinc-registry/bin/gecode-test -test '^Float::Transcendental::' -iter 1 -threads 6 +``` + +For a MiniZinc routing check, select this checkout as the solver library with +`-G`; an include directory alone does not replace the installed solver overrides: + +```sh +minizinc --compile --solver gecode -G "$PWD/gecode/flatzinc/mznlib" \ + --output-fzn-to-file /tmp/native-check.fzn model.mzn +build/minizinc-registry/bin/fzn-gecode -p 6 /tmp/native-check.fzn +``` + +The compiler and solution-comparison checks described above were run during the +audit; they are not an additional checked-in test suite. diff --git a/gecode/flatzinc/mznlib/gecode.mzn b/gecode/flatzinc/mznlib/gecode.mzn index 5b66ab507a..55f7a14546 100644 --- a/gecode/flatzinc/mznlib/gecode.mzn +++ b/gecode/flatzinc/mznlib/gecode.mzn @@ -203,13 +203,6 @@ enum GecodeTaskType = { GecodeFixedDuration, GecodeFixedStart, GecodeFixedEnd }; -/* @group gecode.constraints Pairwise absolute differences are at least the - nonnegative distance. Empty and singleton arrays only constrain distance. - Native bounds propagation offers basic and advanced algorithms, selected - with gecode_basic_propagation, gecode_advanced_propagation, or - gecode_full_propagation. */ -% gecode_inter_distance(x, distance) is declared in gecode_fzn.mzn. - /** @group gecode.constraints All x[i]+offset[i] values are distinct. Arrays must have equal lengths. Uses the existing native offset route. */ predicate gecode_all_different(array[int] of int: offset, array[int] of var int: x) = @@ -239,11 +232,6 @@ predicate gecode_count(array[int] of var int: x, array[int] of int: values, assert(length(x) = length(values), "gecode_count: unequal array lengths", gecode_count_matches_fzn(x, values, enum2int(relation), n)); -/* @group gecode.constraints Closed cardinality with arbitrary allowed count - sets. Every x[i] belongs to cover, cover values are distinct, and cover and - counts have equal lengths. See gecode_global_cardinality_sets in the typed - declarations for its signature. */ - /** @group gecode.constraints Compare the number of distinct values with n. The empty array has zero values. Equality and disequality on integers reuse nvalue; Boolean arrays use the native Boolean propagator. */ @@ -318,7 +306,10 @@ predicate gecode_cumulative_typed(array[int] of GecodeTaskType: types, when present. All arrays share their index sets. */ predicate gecode_nooverlap(array[int] of var int: x, array[int] of int: width, array[int] of var int: y, array[int] of int: height, array[int] of var bool: present) = - gecode_nooverlap_optional(x,width,y,height,present); + assert(index_set(x) = index_set(width) /\ index_set(x) = index_set(y) /\ + index_set(x) = index_set(height) /\ index_set(x) = index_set(present), + "gecode_nooverlap: incompatible indexes", + gecode_nooverlap_optional(x,width,y,height,present)); predicate gecode_nooverlap(array[int] of var int: x, array[int] of var int: width, array[int] of var int: right, array[int] of var int: y, array[int] of var int: height, array[int] of var int: top, @@ -340,11 +331,6 @@ predicate gecode_convex(var set of int: x, var set of int: hull) = gecode_set_co predicate gecode_set_sequence(array[int] of var set of int: x) = gecode_array_set_seq(x); predicate gecode_set_sequence(array[int] of var set of int: x, var set of int: y) = gecode_array_set_seq_union(x,y); -/* @group gecode.constraints gecode_set_atmost_one(x, cardinality) constrains - every set to that cardinality and pairwise intersections to at most one. - gecode_set_channel_sorted(x,s) makes x a strictly increasing enumeration of - s. Both native primitive signatures are in gecode_fzn.mzn. */ - /** @group gecode.constraints Native combined (x operation y) relation z. Equality for ordinary union, intersection, and difference reuses existing MiniZinc operations. Complement is relative to Gecode's full set universe. */ @@ -415,16 +401,20 @@ predicate gecode_log(float: base, var float: x, var float: y) = reuses standard arg_min/arg_max and selects the smallest qualifying index. Otherwise any qualifying index is permitted. Arrays must be nonempty. */ predicate gecode_arg_min(array[int] of var int: x, var int: index, bool: break_ties) = - assert(length(x)>0, "gecode_arg_min: empty array", if break_ties then arg_min(array1d(x))+min(index_set(x))-1=index + assert(length(x) > 0, "gecode_arg_min: empty array", + if break_ties then arg_min(array1d(x)) + min(index_set(x)) - 1 = index else gecode_arg_min_int(x,0,index-min(index_set(x)),false) endif); predicate gecode_arg_min(array[int] of var bool: x, var int: index, bool: break_ties) = - assert(length(x)>0, "gecode_arg_min: empty array", if break_ties then arg_min(array1d(x))+min(index_set(x))-1=index + assert(length(x) > 0, "gecode_arg_min: empty array", + if break_ties then arg_min(array1d(x)) + min(index_set(x)) - 1 = index else gecode_arg_min_bool(x,0,index-min(index_set(x)),false) endif); predicate gecode_arg_max(array[int] of var int: x, var int: index, bool: break_ties) = - assert(length(x)>0, "gecode_arg_max: empty array", if break_ties then arg_max(array1d(x))+min(index_set(x))-1=index + assert(length(x) > 0, "gecode_arg_max: empty array", + if break_ties then arg_max(array1d(x)) + min(index_set(x)) - 1 = index else gecode_arg_max_int(x,0,index-min(index_set(x)),false) endif); predicate gecode_arg_max(array[int] of var bool: x, var int: index, bool: break_ties) = - assert(length(x)>0, "gecode_arg_max: empty array", if break_ties then arg_max(array1d(x))+min(index_set(x))-1=index + assert(length(x) > 0, "gecode_arg_max: empty array", + if break_ties then arg_max(array1d(x)) + min(index_set(x)) - 1 = index else gecode_arg_max_bool(x,0,index-min(index_set(x)),false) endif); /** @group gecode.constraints Exactly one Boolean array entry is true and its diff --git a/gecode/flatzinc/mznlib/gecode_fzn.mzn b/gecode/flatzinc/mznlib/gecode_fzn.mzn index 4019392a2c..5a3d8610ee 100644 --- a/gecode/flatzinc/mznlib/gecode_fzn.mzn +++ b/gecode/flatzinc/mznlib/gecode_fzn.mzn @@ -34,6 +34,11 @@ % Model code should include gecode.mzn for overloaded predicates. % Relation and task codes follow the enums declared there. +/** @group gecode.constraints Pairwise absolute differences are at least the + nonnegative distance. Empty and singleton arrays only constrain distance. + Native bounds propagation offers basic and advanced algorithms, selected + with gecode_basic_propagation, gecode_advanced_propagation, or + gecode_full_propagation. */ predicate gecode_inter_distance(array[int] of var int: x, var int: distance); predicate gecode_all_different_optional(array[int] of var int: x, @@ -59,6 +64,9 @@ predicate gecode_count_in_fzn(array[int] of var int: x, set of int: values, predicate gecode_count_matches_fzn(array[int] of var int: x, array[int] of int: values, int: relation, var int: count); +/** @group gecode.constraints Closed cardinality with arbitrary allowed count + sets. Every x[i] belongs to cover, cover values are distinct, and cover and + counts have equal lengths. Arrays correspond by position. */ predicate gecode_global_cardinality_sets(array[int] of var int: x, array[int] of int: cover, array[int] of set of int: counts); @@ -103,6 +111,9 @@ predicate gecode_path_cost_array(array[int] of int: costs, int: offset, array[int] of var int: x, var int: start, var int: end, array[int] of var int: edge_costs, var int: cost); +/** @group gecode.constraints Order two tasks without overlap. When order is + false, start0+duration0 <= start1; when true, start1+duration1 <= start0. + Durations are nonnegative. */ predicate gecode_order(var int: start0, int: duration0, var int: start1, int: duration1, var bool: order); @@ -142,8 +153,11 @@ predicate gecode_set_convex_hull(var set of int: x, var set of int: hull); predicate gecode_set_convex(var set of int: x); +/** @group gecode.constraints Every set has the given nonnegative cardinality, + and every pair of sets intersects in at most one element. */ predicate gecode_set_atmost_one(array[int] of var set of int: x, int: cardinality); +/** @group gecode.constraints x is a strictly increasing enumeration of y. */ predicate gecode_set_channel_sorted(array[int] of var int: x, var set of int: y); predicate gecode_set_operation_fzn(var set of int: x, int: operation, From 374dabe355c8839483cafca892a03fc1ee4e7276 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 18:32:33 +0200 Subject: [PATCH 03/22] Complete native API coverage on the updated inter-distance base --- changelog.in | 21 ++++ docs/minizinc-constraint-audit.md | 11 +- gecode/flatzinc/mznlib/fzn_regular_nfa.mzn | 41 +++++++ .../flatzinc/mznlib/fzn_regular_nfa_set.mzn | 42 +++++++ gecode/flatzinc/mznlib/gecode.mzn | 58 +++++++++ gecode/flatzinc/mznlib/gecode_fzn.mzn | 27 ++++ .../mznlib/solver-native-overloads.mzn | 35 ++++++ gecode/flatzinc/registry.cpp | 115 ++++++++++++++++++ test/flatzinc/native-registry.cpp | 6 + 9 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 gecode/flatzinc/mznlib/fzn_regular_nfa.mzn create mode 100644 gecode/flatzinc/mznlib/fzn_regular_nfa_set.mzn diff --git a/changelog.in b/changelog.in index 394b936a5d..bd0c2a1888 100755 --- a/changelog.in +++ b/changelog.in @@ -73,6 +73,27 @@ Date: unreleased [DESCRIPTION] This is the development changelog for the next Gecode release. +[ENTRY] +Module: flatzinc +What: new +Rank: major +[DESCRIPTION] +Expose missing native integer, set, float, and scheduling propagators through +an overloaded Gecode MiniZinc library and typed FlatZinc registry bindings. +Upgrade standard solver-library translations where available, including +conditionals, count inequalities, n-ary products, nondeterministic regular, +optional scheduling, and extrema. Add annotations for basic, advanced, and +combined propagation algorithms, and minimum-distance actor organization. + +[ENTRY] +Module: float +What: bug +Rank: minor +[DESCRIPTION] +Keep exact fixed-base power and logarithm solutions using interval logarithms, +handle base-one powers, and remove an invalid shared-variable shortcut. +Enable rounding-aware float compilation in both build systems. + [ENTRY] Module: int diff --git a/docs/minizinc-constraint-audit.md b/docs/minizinc-constraint-audit.md index 2ed8d2ba77..dc108016d2 100644 --- a/docs/minizinc-constraint-audit.md +++ b/docs/minizinc-constraint-audit.md @@ -19,6 +19,8 @@ These capabilities receive no additional public modeling names. The solver libra | `not all_equal(x)` | The integer reification hook and Boolean overload select native not-all-equal when the result is fixed false. General reification keeps the standard linear-size comparison translation. | | Boolean `nvalue` equality | A typed Boolean overload selects native Boolean nvalues. Integer equality already had a native route. | | Boolean `regular` | Typed standard overloads normalize Boolean transitions and select the native Boolean DFA. Guarded/reified expressions retain the standard integer translation. | +| `regular_nfa`, integer and Boolean words | Existing `fzn_regular_nfa` hooks select native `DFA::nfa` construction without determinization. Integer-size and range alphabets retain their standard validation, and empty words test initial-state acceptance. Reification remains unsupported as in the standard library. | +| Integer `product` | A more specific enum/integer index overload replaces the recursive binary-multiplication translation with native nary product. Parameter products retain the builtin route. Specialized native equivalence/implication actors are exposed separately. | | Optional cumulative, optional strict disjunctive | Native flexible optional tasks support variable durations, and optional cumulative now accepts variable capacity. Standard cumulative filters zero-duration tasks; strict disjunctive retains zero-duration ordering. | | Array intersection of sets | `fzn_array_set_intersect.mzn` selects native nary intersection for nonempty arrays. Empty standard intersection remains false. | | Two-dimensional set lookup | `redefinitions-2.5.2.mzn` selects native row/column set element, shifting standard indexes. Empty lookup remains undefined. | @@ -44,6 +46,10 @@ The following names cover capabilities without a matching standard automatic rou | `gecode_count` | Set-target inequalities and position-dependent target arrays have no compiler comparison rewrite. Scalar count is automatic; set equality/disequality reuse among. | | `gecode_global_cardinality_sets` | Closed cardinality with arbitrary allowed count sets, including holes; standard GCC exposes variable counts or interval bounds instead. | | `gecode_nvalues` | Native inequality propagation has no standard nvalue comparison rewrite. Equality/disequality reuse the existing route or the native Boolean equality actor. | +| `gecode_gcd`, `gecode_divides` | Native signed-operand GCD and zero-aware divisibility have no standard variable hooks. Their typed equivalence/implication leaves support inferred reification of the public predicates. | +| `gecode_product_reif`, `gecode_product_imp` | Specialized native nary product equivalence/implication differs from an equality against a separately computed standard product. Ordinary product already has an automatic route. | +| `gecode_product_mod` | Native canonical Euclidean residue of a nary product, including a variable modulus. There is no matching standard global; truncating mod of a recursively computed product differs for negative products. Known positive moduli select the fixed actor. Modulus positivity is part of the reified proposition, so constant folding never turns a false relation into a precondition error. | +| `gecode_minimum_distance` | Native table-distance global, optional pair requirements, and single/decomposed actors lack a standard route. Site values use the matrix's indexes; repeated sites are allowed. | | `gecode_divmod` | Joint truncating division and remainder. Standard `int_div` and `int_mod` are separate hooks. | | `gecode_nroot` | Native integer roots and float roots of general positive degree. Float square roots reuse the standard route. | | `gecode_path` | Hamiltonian successor representation, terminal sentinel, and total/per-edge costs. Standard graph path uses selected graph nodes and edges. | @@ -71,6 +77,7 @@ Gecode set ordering compares characteristic functions starting at the smallest e | Variable-right-hand-side float linear constraints | Native posting appends the right-hand-side variable with coefficient -1, matching existing arithmetic normalization. | | Scalar relations and scalar loops over arrays | Existing primitive posters already reach the native relations; the array helpers merely repeat them. | | Mandatory flexible scheduling and mandatory rectangles | Existing cumulative/disjunctive and diffn routes already reach their actors. | +| Knapsack-augmented cumulative overload checking | The native advanced algorithm is selected through the existing cumulative routes and new algorithm annotations. No additional constraint name is needed. | | Integer/set precedence chains | Native posting repeats the existing adjacent-pair precedence propagators. | | Reified set cardinality | Native posting combines cardinality and an existing reified integer relation. | | Set-wide integer relations | Native posting uses extrema plus scalar relations, nonempty cardinality plus nonmembership, or the singleton relation. Those component routes are reachable. | @@ -85,7 +92,9 @@ Compiler checks cover every remaining typed leaf and public overloads, inferred Fixed-base float testing exposed a rounded scalar logarithm and an invalid shared-variable shortcut in [exp-log.hpp](../gecode/float/transcendental/exp-log.hpp). The implementation uses interval logarithms, handles base-one power, and retains propagation after its own updates. Both build systems enable rounding-aware float compilation. [Float regressions](../test/float/transcendental.cpp) cover exact powers, decreasing bases, base one, and shared variables. -Propagation algorithms are selected with annotations, never predicate arguments. `gecode_basic_propagation`, `gecode_advanced_propagation`, and `gecode_full_propagation` map to `IPL_BASIC`, `IPL_ADVANCED`, and their combination. They combine with existing domain/bounds/value propagation annotations. Unsupported algorithm choices retain the native constraint's normal behavior. [Registry regressions](../test/flatzinc/native-registry.cpp) check semantic boundaries and demonstrate different native inter-distance filtering under basic and advanced algorithms. +The branch was rebased onto the current inter-distance parent before publication. Additional arithmetic, minimum-distance, and NFA bindings were checked with 35 exhaustive/routing cases, including signed and zero inputs, false guards, invalid variable moduli, shifted matrices, nondeterministic words, and parameter/enum products. + +Propagation algorithms are selected with annotations, never predicate arguments. `gecode_basic_propagation`, `gecode_advanced_propagation`, and `gecode_full_propagation` map to `IPL_BASIC`, `IPL_ADVANCED`, and their combination. They combine with existing domain/bounds/value propagation annotations. `gecode_decomposed_propagation` selects separate pair actors for minimum distance; the default is a single actor with the same filtering. Unsupported algorithm choices retain the native constraint's normal behavior. [Registry regressions](../test/flatzinc/native-registry.cpp) check semantic boundaries and demonstrate different native inter-distance filtering under basic and advanced algorithms. ```minizinc include "gecode.mzn"; diff --git a/gecode/flatzinc/mznlib/fzn_regular_nfa.mzn b/gecode/flatzinc/mznlib/fzn_regular_nfa.mzn new file mode 100644 index 0000000000..47fa786d1a --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_regular_nfa.mzn @@ -0,0 +1,41 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_regular_nfa_int_fzn(array[int] of var int: x, int: q, + int: first_symbol, int: symbols, array[int] of set of int: transitions, + int: initial, set of int: final); + +predicate fzn_regular_nfa(array[int] of var int: xs, int: Q, int: S, + array[int,int] of set of int: d, int: q0, set of int: F) = + if length(xs) = 0 then q0 in F else + gecode_regular_nfa_int_fzn(xs,Q,1,S,array1d(d),q0,F) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_regular_nfa_set.mzn b/gecode/flatzinc/mznlib/fzn_regular_nfa_set.mzn new file mode 100644 index 0000000000..35e89b5c77 --- /dev/null +++ b/gecode/flatzinc/mznlib/fzn_regular_nfa_set.mzn @@ -0,0 +1,42 @@ +% +% Main authors: +% Mikael Zayenz Lagerkvist +% +% Copyright: +% Mikael Zayenz Lagerkvist, 2026 +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.dev +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_regular_nfa_int_fzn(array[int] of var int: x, int: q, + int: first_symbol, int: symbols, array[int] of set of int: transitions, + int: initial, set of int: final); + +predicate fzn_regular_nfa(array[int] of var int: xs, int: Q, set of int: S, + array[int,int] of set of int: d, int: q0, set of int: F) = + if length(xs) = 0 then q0 in F else + gecode_regular_nfa_int_fzn(xs,Q,min(S),card(S), + array1d(d),q0,F) + endif; diff --git a/gecode/flatzinc/mznlib/gecode.mzn b/gecode/flatzinc/mznlib/gecode.mzn index 55f7a14546..7e064340bb 100644 --- a/gecode/flatzinc/mznlib/gecode.mzn +++ b/gecode/flatzinc/mznlib/gecode.mzn @@ -68,6 +68,10 @@ annotation gecode_advanced_propagation; These may be combined with domain_propagation or bounds_propagation; constraints without algorithm choices ignore them. */ annotation gecode_full_propagation; +/** @group gecode.annotations Use separate pair propagators for minimum + distance. The default uses one propagator; both implement the same filtering. + Combine with basic/advanced/full annotations to select matching propagation. */ +annotation gecode_decomposed_propagation; annotation int_assign(array[int] of var int: x, ann:a); @@ -248,6 +252,60 @@ predicate gecode_nvalues(array[int] of var bool: x, constraint gecode_nvalues_bool_fzn(x, enum2int(GecodeEq), c);} in c != n else gecode_nvalues_bool_fzn(x, enum2int(relation), n) endif; +/** @group gecode.constraints Nonnegative greatest common divisor of x and y. + Negative operands use their absolute value; gcd(0,0)=0. Reification and + implication of this predicate select the corresponding native actors. */ +predicate gecode_gcd(var int: x, var int: y, var int: result) = + gecode_gcd_int(x,y,result); + +/** @group gecode.constraints Divisor divides dividend, including 0 divides 0. + Zero does not divide nonzero values. Supports reification and implication. */ +predicate gecode_divides(var int: divisor, var int: dividend) = + gecode_divides_int(divisor,dividend); + +/** @group gecode.constraints Reify or imply the exact nary integer product. + Empty product is one. These specialized actors differ from reifying an + equality against a separately computed standard product. */ +predicate gecode_product_reif(array[int] of var int: x, var int: result, var bool: b) = + gecode_product_int_reif(x,result,b); +predicate gecode_product_imp(array[int] of var int: x, var int: result, var bool: b) = + gecode_product_int_imp(x,result,b); + +/** @group gecode.constraints Nary product with canonical Euclidean residue + 0 <= result < modulus, including for negative products. Empty product is one. + Modulus positivity/range is part of the reified proposition. An inactive + implication imposes no restriction on modulus or result. Known positive + moduli select the fixed-modulus native actor. */ +predicate gecode_product_mod(array[int] of var int: x, var int: modulus, var int: result) = + if is_fixed(modulus) then + if fix(modulus) > 0 then gecode_product_mod_fixed_fzn(x,fix(modulus),result) + else gecode_product_mod_var_fzn(x,modulus,result) endif + else gecode_product_mod_var_fzn(x,modulus,result) endif; + +/** @group gecode.constraints Minimum table distance among selected sites, + with at least two positions. Matrix axes are equal site indexes; entries + are symmetric/nonnegative with zero diagonal. Repeated sites are allowed. + Optional requirements matrix uses the positions' index set and imposes + inclusive pairwise lower bounds. Select native algorithm/organization + through propagation annotations. */ +predicate gecode_minimum_distance(array[int] of var int: sites, + array[int,int] of int: distances, var int: result) = + assert(length(sites) >= 2 /\ length(distances) > 0 /\ + index_set_1of2(distances) = index_set_2of2(distances), + "gecode_minimum_distance: need at least two positions and a square nonempty distance matrix", + gecode_minimum_distance_fzn([v-min(index_set_1of2(distances)) | v in sites], + card(index_set_1of2(distances)),array1d(distances),result)); +predicate gecode_minimum_distance(array[int] of var int: sites, + array[int,int] of int: distances, var int: result, + array[int,int] of int: requirements) = + assert(length(sites) >= 2 /\ length(distances) > 0 /\ + index_set_1of2(distances) = index_set_2of2(distances) /\ + index_set_1of2(requirements) = index_set(sites) /\ + index_set_2of2(requirements) = index_set(sites), + "gecode_minimum_distance: incompatible distance/requirements matrix or too few positions", + gecode_minimum_distance_required_fzn([v-min(index_set_1of2(distances)) | v in sites], + card(index_set_1of2(distances)),array1d(distances),result,array1d(requirements))); + /** @group gecode.constraints Joint truncating division and remainder; y is nonzero. */ predicate gecode_divmod(var int: x, var int: y, var int: quotient, var int: remainder) = diff --git a/gecode/flatzinc/mznlib/gecode_fzn.mzn b/gecode/flatzinc/mznlib/gecode_fzn.mzn index 5a3d8610ee..2f6ee4e380 100644 --- a/gecode/flatzinc/mznlib/gecode_fzn.mzn +++ b/gecode/flatzinc/mznlib/gecode_fzn.mzn @@ -93,6 +93,26 @@ predicate gecode_set_ite(var bool: b, var set of int: x, predicate gecode_float_ite(var bool: b, var float: x, var float: y, var float: z); +predicate gecode_gcd_int(var int: x, var int: y, var int: result); +predicate gecode_gcd_int_reif(var int: x, var int: y, var int: result, var bool: b); +predicate gecode_gcd_int_imp(var int: x, var int: y, var int: result, var bool: b); +predicate gecode_divides_int(var int: divisor, var int: dividend); +predicate gecode_divides_int_reif(var int: divisor, var int: dividend, var bool: b); +predicate gecode_divides_int_imp(var int: divisor, var int: dividend, var bool: b); +predicate gecode_product_int(array[int] of var int: x, var int: result); +predicate gecode_product_int_reif(array[int] of var int: x, var int: result, var bool: b); +predicate gecode_product_int_imp(array[int] of var int: x, var int: result, var bool: b); +predicate gecode_product_mod_fixed_fzn(array[int] of var int: x, int: modulus, var int: result); +predicate gecode_product_mod_fixed_fzn_reif(array[int] of var int: x, int: modulus, var int: result, var bool: b); +predicate gecode_product_mod_fixed_fzn_imp(array[int] of var int: x, int: modulus, var int: result, var bool: b); +predicate gecode_product_mod_var_fzn(array[int] of var int: x, var int: modulus, var int: result); +predicate gecode_product_mod_var_fzn_reif(array[int] of var int: x, var int: modulus, var int: result, var bool: b); +predicate gecode_product_mod_var_fzn_imp(array[int] of var int: x, var int: modulus, var int: result, var bool: b); +predicate gecode_minimum_distance_fzn(array[int] of var int: sites, + int: size, array[int] of int: distances, var int: result); +predicate gecode_minimum_distance_required_fzn(array[int] of var int: sites, + int: size, array[int] of int: distances, var int: result, array[int] of int: requirements); + predicate gecode_int_divmod(var int: x, var int: y, var int: quotient, var int: remainder); @@ -101,6 +121,13 @@ predicate gecode_int_nroot(var int: x, int: n, var int: y); predicate gecode_regular_bool(array[int] of var bool: x, int: q, array[int] of int: transitions, int: initial, set of int: final); +predicate gecode_regular_nfa_int_fzn(array[int] of var int: x, int: q, + int: first_symbol, int: symbols, array[int] of set of int: transitions, + int: initial, set of int: final); +predicate gecode_regular_nfa_bool_fzn(array[int] of var bool: x, int: q, + int: first_symbol, int: symbols, array[int] of set of int: transitions, + int: initial, set of int: final); + predicate gecode_path(int: offset, array[int] of var int: x, var int: start, var int: end); diff --git a/gecode/flatzinc/mznlib/solver-native-overloads.mzn b/gecode/flatzinc/mznlib/solver-native-overloads.mzn index 80fdacf67b..4d9de1a6ca 100644 --- a/gecode/flatzinc/mznlib/solver-native-overloads.mzn +++ b/gecode/flatzinc/mznlib/solver-native-overloads.mzn @@ -32,9 +32,18 @@ include "all_equal.mzn"; include "regular.mzn"; +include "regular_nfa.mzn"; include "nvalue_fn.mzn"; include "gecode_fzn.mzn"; +% A more specific index type overrides the standard recursive product for +% decision variables while leaving parameter products on the builtin route. +function var int: product(array[$$X] of var int: x) :: promise_commutative = + let { + var int: result :: is_defined_var; + constraint gecode_product_int(array1d(x),result) :: defines_var(result); + } in result; + % Typed standard operators preserve MiniZinc's index-set checks and use the % non-reified native actor only in a positive root context. function var bool: '!='(array[$U] of var $$E: x, @@ -105,3 +114,29 @@ predicate regular(array[int] of var bool: xs, int: Q, int: S, array[int,int] of int: d, int: q0, set of int: F) = assert(S > 0, "regular: 'S' must be greater than zero") /\ regular(xs,Q,1..S,d,q0,F); + +% Boolean NFA symbols missing from the standard alphabet have no transitions. +% Standard regular_nfa does not support reification; retain that restriction. +predicate regular_nfa(array[int] of var bool: x, int: q, set of int: symbols, + array[int,int] of set of int: transitions, int: initial, set of int: final) = + assert(q > 0 /\ card(symbols) > 0 /\ symbols = min(symbols)..max(symbols), + "regular_nfa: states must be positive and alphabet a nonempty range") /\ + assert(index_set_1of2(transitions) = 1..q /\ index_set_2of2(transitions) = symbols, + "regular_nfa: transition indexes must be [1..q,alphabet]") /\ + assert(forall(targets in transitions)(targets subset 1..q) /\ + initial in 1..q /\ final subset 1..q, "regular_nfa: invalid transition or initial/final state") /\ + if length(x) = 0 then initial in final else + let { + array[1..q,0..1] of set of int: normalized = array2d(1..q,0..1, + [if symbol in symbols then transitions[state,symbol] else {} endif + | state in 1..q, symbol in 0..1]); + } in + if mzn_in_root_context() then + gecode_regular_nfa_bool_fzn(x,q,0,2,array1d(normalized),initial,final) + else regular_nfa(bool2int(x),q,0..1,normalized,initial,final) endif + endif; + +predicate regular_nfa(array[int] of var bool: x, int: q, int: symbols, + array[int,int] of set of int: transitions, int: initial, set of int: final) = + assert(symbols > 0, "regular_nfa: alphabet size must be positive") /\ + regular_nfa(x,q,1..symbols,transitions,initial,final); diff --git a/gecode/flatzinc/registry.cpp b/gecode/flatzinc/registry.cpp index f6e38dc904..07b2017eab 100755 --- a/gecode/flatzinc/registry.cpp +++ b/gecode/flatzinc/registry.cpp @@ -1229,6 +1229,45 @@ namespace Gecode { namespace FlatZinc { ce[3]->getInt(), ce[4]->getSet(), ann); } + template + void p_regular_nfa(FlatZincSpace& s, VarArgs x, const ConExpr& ce, + AST::Node* ann) { + int q = ce[1]->getInt(), first_symbol = ce[2]->getInt(); + int symbols = ce[3]->getInt(), initial = ce[5]->getInt(); + IntSetArgs table = s.arg2intsetargs(ce[4]); + IntSet finals = s.arg2intset(ce[6]); + if ((q <= 0) || (symbols <= 0) || + (static_cast(q)*symbols != table.size()) || + (initial < 1) || (initial > q) || + ((finals.size() != 0) && ((finals.min() < 1) || (finals.max() > q))) || + (first_symbol < Int::Limits::min) || + (static_cast(first_symbol)+symbols-1 > Int::Limits::max)) + throw FlatZinc::Error("Registry", "invalid NFA dimensions or states"); + std::vector transitions; + for (int state=0; state q))) + throw FlatZinc::Error("Registry", "invalid NFA transition"); + for (IntSetValues target(targets); target(); ++target) + transitions.push_back(DFA::Transition(state+1,first_symbol+symbol,target.val())); + } + transitions.push_back(DFA::Transition(-1,0,0)); + std::vector accepting; + for (IntSetValues final(finals); final(); ++final) + accepting.push_back(final.val()); + accepting.push_back(-1); + DFA automaton = DFA::nfa(initial,transitions.data(),accepting.data()); + unshare(s,x); + extensional(s,x,s.getSharedDFA(automaton),s.ann2ipl(ann)); + } + void p_regular_nfa_int(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + p_regular_nfa(s,s.arg2intvarargs(ce[0]),ce,ann); + } + void p_regular_nfa_bool(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + p_regular_nfa(s,s.arg2boolvarargs(ce[0]),ce,ann); + } + void p_sort(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { IntVarArgs x = s.arg2intvarargs(ce[0]); @@ -1942,6 +1981,63 @@ fvar, ite(s, s.arg2BoolVar(ce[0]), s.arg2BoolVar(ce[1]), s.arg2BoolVar(ce[2]), s.arg2BoolVar(ce[3]), s.ann2ipl(ann)); } + void p_gcd(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + gcd(s, s.arg2IntVar(ce[0]), s.arg2IntVar(ce[1]), s.arg2IntVar(ce[2]), + s.ann2ipl(ann)); + } + template + void p_gcd_reif(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + gcd(s, s.arg2IntVar(ce[0]), s.arg2IntVar(ce[1]), s.arg2IntVar(ce[2]), + Reify(s.arg2BoolVar(ce[3]), mode), s.ann2ipl(ann)); + } + void p_divides(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + divides(s, s.arg2IntVar(ce[0]), s.arg2IntVar(ce[1]), + Reify(BoolVar(s,1,1),RM_EQV), s.ann2ipl(ann)); + } + template + void p_divides_reif(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + divides(s, s.arg2IntVar(ce[0]), s.arg2IntVar(ce[1]), + Reify(s.arg2BoolVar(ce[2]),mode), s.ann2ipl(ann)); + } + void p_product(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + product(s, s.arg2intvarargs(ce[0]), s.arg2IntVar(ce[1]), s.ann2ipl(ann)); + } + template + void p_product_reif(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + product(s, s.arg2intvarargs(ce[0]), s.arg2IntVar(ce[1]), + Reify(s.arg2BoolVar(ce[2]),mode), s.ann2ipl(ann)); + } + template + void p_product_mod(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs x = s.arg2intvarargs(ce[0]); + IntVar y = s.arg2IntVar(ce[2]); + if (fixed) + product_mod(s,x,ce[1]->getInt(),y,s.ann2ipl(ann)); + else + product_mod(s,x,s.arg2IntVar(ce[1]),y,s.ann2ipl(ann)); + } + template + void p_product_mod_reif(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs x = s.arg2intvarargs(ce[0]); + IntVar y = s.arg2IntVar(ce[2]); + Reify r(s.arg2BoolVar(ce[3]),mode); + if (fixed) + product_mod(s,x,ce[1]->getInt(),y,r,s.ann2ipl(ann)); + else + product_mod(s,x,s.arg2IntVar(ce[1]),y,r,s.ann2ipl(ann)); + } + void p_minimum_distance(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { + IntVarArgs x = s.arg2intvarargs(ce[0]); + IntDistance d(ce[1]->getInt(),s.arg2intargs(ce[2])); + MinDistancePropKind kind = ann && ann->hasAtom("gecode_decomposed_propagation") + ? MDP_DECOMPOSED : MDP_SINGLE; + if (ce.size() == 5) + minimum_distance(s,x,s.arg2IntVar(ce[3]),d, + s.arg2intargs(ce[4]),s.ann2ipl(ann),kind); + else + minimum_distance(s,x,s.arg2IntVar(ce[3]),d,s.ann2ipl(ann),kind); + } + void p_int_divmod(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { divmod(s, s.arg2IntVar(ce[0]), s.arg2IntVar(ce[1]), s.arg2IntVar(ce[2]), s.arg2IntVar(ce[3]), s.ann2ipl(ann)); @@ -2272,6 +2368,23 @@ fvar, registry().add("gecode_nvalues_bool_fzn", &p_nvalues_bool); registry().add("gecode_int_ite", &p_int_ite); registry().add("gecode_bool_ite", &p_bool_ite); + registry().add("gecode_gcd_int", &p_gcd); + registry().add("gecode_gcd_int_reif", &p_gcd_reif); + registry().add("gecode_gcd_int_imp", &p_gcd_reif); + registry().add("gecode_divides_int", &p_divides); + registry().add("gecode_divides_int_reif", &p_divides_reif); + registry().add("gecode_divides_int_imp", &p_divides_reif); + registry().add("gecode_product_int", &p_product); + registry().add("gecode_product_int_reif", &p_product_reif); + registry().add("gecode_product_int_imp", &p_product_reif); + registry().add("gecode_product_mod_fixed_fzn", &p_product_mod); + registry().add("gecode_product_mod_fixed_fzn_reif", &p_product_mod_reif); + registry().add("gecode_product_mod_fixed_fzn_imp", &p_product_mod_reif); + registry().add("gecode_product_mod_var_fzn", &p_product_mod); + registry().add("gecode_product_mod_var_fzn_reif", &p_product_mod_reif); + registry().add("gecode_product_mod_var_fzn_imp", &p_product_mod_reif); + registry().add("gecode_minimum_distance_fzn", &p_minimum_distance); + registry().add("gecode_minimum_distance_required_fzn", &p_minimum_distance); registry().add("gecode_int_divmod", &p_int_divmod); registry().add("gecode_int_nroot", &p_int_nroot); registry().add("gecode_path", &p_path); @@ -2282,6 +2395,8 @@ fvar, registry().add("gecode_all_different_optional", &p_distinct_optional); registry().add("gecode_all_different_except", &p_distinct_except); registry().add("gecode_regular_bool", &p_regular_bool); + registry().add("gecode_regular_nfa_int_fzn", &p_regular_nfa_int); + registry().add("gecode_regular_nfa_bool_fzn", &p_regular_nfa_bool); registry().add("gecode_unary_typed_fzn", &p_unary_typed); registry().add("gecode_unary_typed_optional_fzn", &p_unary_typed); registry().add("gecode_cumulative_typed_fzn", &p_cumulative_typed); diff --git a/test/flatzinc/native-registry.cpp b/test/flatzinc/native-registry.cpp index 95f5b198af..411be4b5a8 100644 --- a/test/flatzinc/native-registry.cpp +++ b/test/flatzinc/native-registry.cpp @@ -83,6 +83,12 @@ namespace Test { namespace FlatZinc { "constraint gecode_sort_permutation([3,1,2],[1,2,3],[2,3,1],1); solve satisfy;", unsat); (void) new FlatZincTest("NativeRegistry::ArgMinShared", "var 1..2:x; constraint gecode_arg_min_int([x,x],1,x,false); solve satisfy;", sat); + (void) new FlatZincTest("NativeRegistry::InvalidModulusReification", + "constraint gecode_product_mod_var_fzn_reif([3],0,3,false); solve satisfy;", sat); + (void) new FlatZincTest("NativeRegistry::MinimumDistanceRepeatedSite", + "constraint gecode_minimum_distance_fzn([0,0,2],3,[0,2,4,2,0,3,4,3,0],1); solve satisfy;", unsat); + (void) new FlatZincTest("NativeRegistry::NondeterministicBooleanPrefix", + "constraint gecode_regular_nfa_bool_fzn([false,true],3,0,2,[{1,2},{},{},{3},{},{}],1,{3}); solve satisfy;", sat); (void) new FlatZincTest("NativeRegistry::DivmodNegative", "constraint gecode_int_divmod(-7,4,-1,-3); solve satisfy;", sat); (void) new FlatZincTest("NativeRegistry::PathCostSentinel", From 85dd5e212e62d0c5bb1fffbd4b33e8a8fd97b4c2 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 18:35:41 +0200 Subject: [PATCH 04/22] Preserve standard product behavior in non-root contexts --- docs/minizinc-constraint-audit.md | 4 ++-- gecode/flatzinc/mznlib/gecode.mzn | 9 +++++---- gecode/flatzinc/mznlib/solver-native-overloads.mzn | 13 +++++++++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/minizinc-constraint-audit.md b/docs/minizinc-constraint-audit.md index dc108016d2..452c2b7618 100644 --- a/docs/minizinc-constraint-audit.md +++ b/docs/minizinc-constraint-audit.md @@ -20,7 +20,7 @@ These capabilities receive no additional public modeling names. The solver libra | Boolean `nvalue` equality | A typed Boolean overload selects native Boolean nvalues. Integer equality already had a native route. | | Boolean `regular` | Typed standard overloads normalize Boolean transitions and select the native Boolean DFA. Guarded/reified expressions retain the standard integer translation. | | `regular_nfa`, integer and Boolean words | Existing `fzn_regular_nfa` hooks select native `DFA::nfa` construction without determinization. Integer-size and range alphabets retain their standard validation, and empty words test initial-state acceptance. Reification remains unsupported as in the standard library. | -| Integer `product` | A more specific enum/integer index overload replaces the recursive binary-multiplication translation with native nary product. Parameter products retain the builtin route. Specialized native equivalence/implication actors are exposed separately. | +| Integer `product` | A more specific one-dimensional enum/integer index overload selects native nary product in positive root contexts, including generator syntax and the standard optional-value translation. Other contexts and direct multidimensional arrays retain recursive binary multiplication. Parameter products retain the builtin route. Specialized native equivalence/implication actors are exposed separately. | | Optional cumulative, optional strict disjunctive | Native flexible optional tasks support variable durations, and optional cumulative now accepts variable capacity. Standard cumulative filters zero-duration tasks; strict disjunctive retains zero-duration ordering. | | Array intersection of sets | `fzn_array_set_intersect.mzn` selects native nary intersection for nonempty arrays. Empty standard intersection remains false. | | Two-dimensional set lookup | `redefinitions-2.5.2.mzn` selects native row/column set element, shifting standard indexes. Empty lookup remains undefined. | @@ -92,7 +92,7 @@ Compiler checks cover every remaining typed leaf and public overloads, inferred Fixed-base float testing exposed a rounded scalar logarithm and an invalid shared-variable shortcut in [exp-log.hpp](../gecode/float/transcendental/exp-log.hpp). The implementation uses interval logarithms, handles base-one power, and retains propagation after its own updates. Both build systems enable rounding-aware float compilation. [Float regressions](../test/float/transcendental.cpp) cover exact powers, decreasing bases, base one, and shared variables. -The branch was rebased onto the current inter-distance parent before publication. Additional arithmetic, minimum-distance, and NFA bindings were checked with 35 exhaustive/routing cases, including signed and zero inputs, false guards, invalid variable moduli, shifted matrices, nondeterministic words, and parameter/enum products. +Arithmetic, minimum-distance, and NFA bindings were checked with 35 exhaustive/routing cases, including signed and zero inputs, false guards, invalid variable moduli, shifted matrices, nondeterministic words, and parameter/enum products. Propagation algorithms are selected with annotations, never predicate arguments. `gecode_basic_propagation`, `gecode_advanced_propagation`, and `gecode_full_propagation` map to `IPL_BASIC`, `IPL_ADVANCED`, and their combination. They combine with existing domain/bounds/value propagation annotations. `gecode_decomposed_propagation` selects separate pair actors for minimum distance; the default is a single actor with the same filtering. Unsupported algorithm choices retain the native constraint's normal behavior. [Registry regressions](../test/flatzinc/native-registry.cpp) check semantic boundaries and demonstrate different native inter-distance filtering under basic and advanced algorithms. diff --git a/gecode/flatzinc/mznlib/gecode.mzn b/gecode/flatzinc/mznlib/gecode.mzn index 7e064340bb..1c45d5982b 100644 --- a/gecode/flatzinc/mznlib/gecode.mzn +++ b/gecode/flatzinc/mznlib/gecode.mzn @@ -272,7 +272,8 @@ predicate gecode_product_imp(array[int] of var int: x, var int: result, var bool gecode_product_int_imp(x,result,b); /** @group gecode.constraints Nary product with canonical Euclidean residue - 0 <= result < modulus, including for negative products. Empty product is one. + 0 <= result < modulus, including for negative products. The empty array + produces 1 modulo modulus (zero when modulus is one). Modulus positivity/range is part of the reified proposition. An inactive implication imposes no restriction on modulus or result. Known positive moduli select the fixed-modulus native actor. */ @@ -285,9 +286,9 @@ predicate gecode_product_mod(array[int] of var int: x, var int: modulus, var int /** @group gecode.constraints Minimum table distance among selected sites, with at least two positions. Matrix axes are equal site indexes; entries are symmetric/nonnegative with zero diagonal. Repeated sites are allowed. - Optional requirements matrix uses the positions' index set and imposes - inclusive pairwise lower bounds. Select native algorithm/organization - through propagation annotations. */ + Optional requirements matrix uses the positions' index set, is symmetric + and nonnegative with zero diagonal, and imposes inclusive pairwise lower + bounds. Select native algorithm/organization through propagation annotations. */ predicate gecode_minimum_distance(array[int] of var int: sites, array[int,int] of int: distances, var int: result) = assert(length(sites) >= 2 /\ length(distances) > 0 /\ diff --git a/gecode/flatzinc/mznlib/solver-native-overloads.mzn b/gecode/flatzinc/mznlib/solver-native-overloads.mzn index 4d9de1a6ca..b4eef04b48 100644 --- a/gecode/flatzinc/mznlib/solver-native-overloads.mzn +++ b/gecode/flatzinc/mznlib/solver-native-overloads.mzn @@ -39,10 +39,15 @@ include "gecode_fzn.mzn"; % A more specific index type overrides the standard recursive product for % decision variables while leaving parameter products on the builtin route. function var int: product(array[$$X] of var int: x) :: promise_commutative = - let { - var int: result :: is_defined_var; - constraint gecode_product_int(array1d(x),result) :: defines_var(result); - } in result; + if mzn_in_root_context() then + let { + var int: result :: is_defined_var; + constraint gecode_product_int(array1d(x),result) :: defines_var(result); + } in result + else + % Retain the standard translation where a free result cannot be introduced. + product_rec(array1d(x)) + endif; % Typed standard operators preserve MiniZinc's index-set checks and use the % non-reified native actor only in a positive root context. From c1e34cf0945d183a04e067e17633c23e132958b3 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 19:14:26 +0200 Subject: [PATCH 05/22] Remove change-specific MiniZinc audit document --- docs/minizinc-constraint-audit.md | 125 ------------------------------ 1 file changed, 125 deletions(-) delete mode 100644 docs/minizinc-constraint-audit.md diff --git a/docs/minizinc-constraint-audit.md b/docs/minizinc-constraint-audit.md deleted file mode 100644 index 452c2b7618..0000000000 --- a/docs/minizinc-constraint-audit.md +++ /dev/null @@ -1,125 +0,0 @@ -# MiniZinc native propagator audit - -The audit asks whether a standard MiniZinc expression already reaches a native Gecode propagator, including compiler rewrites and solver-library definitions. When a standard solver hook can select a missing actor, the implementation upgrades that hook. Additional public `gecode_*` predicates cover the remaining native capabilities. A different decomposition alone does not establish that a new name is needed. - -Verification uses MiniZinc 2.10.1 (build 33348285743) and this checkout's FlatZinc solver. Standard-library filenames below refer to that installation. A local MiniZinc 2.9.3 compiler checkout supplied additional source evidence; the installed compiler's generated FlatZinc was checked independently. Native declarations and posting code are in [int.hh](../gecode/int.hh), [set.hh](../gecode/set.hh), [float.hh](../gecode/float.hh), and their implementation directories. - -[gecode.mzn](../gecode/flatzinc/mznlib/gecode.mzn) supplies the public overloads and semantic relation/task enums. [gecode_fzn.mzn](../gecode/flatzinc/mznlib/gecode_fzn.mzn) contains typed primitive declarations, including internal leaves used by standard translations. Public predicates that already have a single typed signature are documented at their declaration there, without a redundant forwarding wrapper. [registry.cpp](../gecode/flatzinc/registry.cpp) posts those primitives. Existing registry aliases are reused rather than registered twice. Both build systems install the entire solver-library directory. - -## Automatic standard routes - -These capabilities receive no additional public modeling names. The solver library selects the native actor from standard syntax, preserving the standard relation and its boundary behavior. - -| Standard expression or global | Solver route | -| --- | --- | -| Optional all-different, single-exception all-different | `fzn_all_different_int_opt.mzn`, `fzn_alldifferent_except.mzn`, and `fzn_alldifferent_except_0.mzn` use native optional/single-exception distinct. Multiple exceptions and reified cases retain suitable decomposition. | -| Scalar count comparisons | Four `fzn_count_*` inequality overrides call the native count relation. MiniZinc rewrites comparisons on scalar `count` and corresponding equality comprehensions to these hooks. Standard `count_leq` means `n <= count`, so its native relation is greater-or-equal. | -| Binary conditionals, all four variable types | Eight `fzn_if_then_else[_var]_{int,bool,float,set}.mzn` overrides select native ITE for two branches ending in a known true condition. Longer and partial condition lists retain standard guarded-equality behavior. | -| Integer/Boolean array `!=` | `solver-native-overloads.mzn` preserves standard matching-index checks, then uses native array disequality in positive root contexts. Other contexts retain existential scalar disequality. Integer enums are converted explicitly. | -| `not all_equal(x)` | The integer reification hook and Boolean overload select native not-all-equal when the result is fixed false. General reification keeps the standard linear-size comparison translation. | -| Boolean `nvalue` equality | A typed Boolean overload selects native Boolean nvalues. Integer equality already had a native route. | -| Boolean `regular` | Typed standard overloads normalize Boolean transitions and select the native Boolean DFA. Guarded/reified expressions retain the standard integer translation. | -| `regular_nfa`, integer and Boolean words | Existing `fzn_regular_nfa` hooks select native `DFA::nfa` construction without determinization. Integer-size and range alphabets retain their standard validation, and empty words test initial-state acceptance. Reification remains unsupported as in the standard library. | -| Integer `product` | A more specific one-dimensional enum/integer index overload selects native nary product in positive root contexts, including generator syntax and the standard optional-value translation. Other contexts and direct multidimensional arrays retain recursive binary multiplication. Parameter products retain the builtin route. Specialized native equivalence/implication actors are exposed separately. | -| Optional cumulative, optional strict disjunctive | Native flexible optional tasks support variable durations, and optional cumulative now accepts variable capacity. Standard cumulative filters zero-duration tasks; strict disjunctive retains zero-duration ordering. | -| Array intersection of sets | `fzn_array_set_intersect.mzn` selects native nary intersection for nonempty arrays. Empty standard intersection remains false. | -| Two-dimensional set lookup | `redefinitions-2.5.2.mzn` selects native row/column set element, shifting standard indexes. Empty lookup remains undefined. | -| Set `min`/`max` | The documented `redefinitions-2.1.1.mzn` function hooks select native extrema. Partial contexts guard native implication with nonemptiness, without an extra surrogate set. Integer and enum sets preserve standard undefinedness. | -| Float array `min`/`max` | `redefinitions-2.0.mzn` selects native nary extrema rather than a binary chain. Empty expressions retain their standard errors. | -| Float power | The standard `float_pow` hook selects native fixed nonnegative integer exponent or fixed positive base. General varying-base/varying-exponent power was unsupported before and remains unsupported, with a clear compile error. | -| Scalar float implication | Existing scalar and normalized linear implication hooks select native scalar implication. General linear forms retain equivalence plus implication. | -| Set equality/disequality/subset/superset implication | Standard half-reification hooks select native implication. | -| Negated integer/Boolean tables | Already exposed: a positive reified table with result false selects native negative actors in the compact, compressed, and sparse posting implementations. No negative-table names or posters are added. | - -The additional typed standard overloads are included automatically from `redefinitions.mzn`. Models do not need to include `gecode.mzn` to receive these routes. - -## Remaining public families - -The following names cover capabilities without a matching standard automatic route. A family may also accept cases already exposed; those cases reuse existing routes rather than adding another actor. - -| Public names | Why the remaining native variant is needed | -| --- | --- | -| `gecode_inter_distance` | Native pairwise minimum absolute distance, including variable distance and basic/advanced algorithms; pairwise arithmetic has no matching global rewrite. | -| `gecode_all_different` | Offset form only. Standard arithmetic plus all-different introduces auxiliary variables instead of native offset views. The registry alias already existed. | -| `gecode_channel` | Native one-hot Boolean-array/index and direct Boolean/float channels. `bool2float` has an existing function body through integer conversion, without a solver override hook. | -| `gecode_sort_permutation` | Native input-to-output permutation permits arbitrary tie order. Standard `arg_sort` returns a stable inverse mapping. | -| `gecode_count` | Set-target inequalities and position-dependent target arrays have no compiler comparison rewrite. Scalar count is automatic; set equality/disequality reuse among. | -| `gecode_global_cardinality_sets` | Closed cardinality with arbitrary allowed count sets, including holes; standard GCC exposes variable counts or interval bounds instead. | -| `gecode_nvalues` | Native inequality propagation has no standard nvalue comparison rewrite. Equality/disequality reuse the existing route or the native Boolean equality actor. | -| `gecode_gcd`, `gecode_divides` | Native signed-operand GCD and zero-aware divisibility have no standard variable hooks. Their typed equivalence/implication leaves support inferred reification of the public predicates. | -| `gecode_product_reif`, `gecode_product_imp` | Specialized native nary product equivalence/implication differs from an equality against a separately computed standard product. Ordinary product already has an automatic route. | -| `gecode_product_mod` | Native canonical Euclidean residue of a nary product, including a variable modulus. There is no matching standard global; truncating mod of a recursively computed product differs for negative products. Known positive moduli select the fixed actor. Modulus positivity is part of the reified proposition, so constant folding never turns a false relation into a precondition error. | -| `gecode_minimum_distance` | Native table-distance global, optional pair requirements, and single/decomposed actors lack a standard route. Site values use the matrix's indexes; repeated sites are allowed. | -| `gecode_divmod` | Joint truncating division and remainder. Standard `int_div` and `int_mod` are separate hooks. | -| `gecode_nroot` | Native integer roots and float roots of general positive degree. Float square roots reuse the standard route. | -| `gecode_path` | Hamiltonian successor representation, terminal sentinel, and total/per-edge costs. Standard graph path uses selected graph nodes and edges. | -| `gecode_order` | Native two-task ordering Boolean has no joint standard ordering hook. | -| `gecode_unary_typed`, `gecode_cumulative_typed` | Native fixed-start/fixed-end task types, including optional tasks, are absent from standard scheduling globals. | -| `gecode_nooverlap` | Native optional rectangles with fixed or variable dimensions and explicit ends. Standard diffn covers mandatory rectangles. | -| `gecode_convex`, `gecode_set_sequence` | Native convex hull and ordered set sequence/sequence union lack matching standard globals. One-set convexity and sequence aliases already existed in the registry. | -| `gecode_set_atmost_one` | Native equal fixed cardinality plus pairwise intersection size at most one. Standard at_most1 does not carry this cardinality parameter. | -| `gecode_set_channel_sorted` | Native set/strictly-increasing-enumeration channel lacks a standard global. | -| `gecode_set_operation` | Combined operation/relation propagation, including native intersection/subset and union/superset actors. Ordinary operation equality reuses standard set operations. | -| `gecode_array_int_set_operation`, `gecode_array_int_set_element` | Singleton-view array operations with an initial set, and selected intersection/disjoint union. Selected union already has a native range route and reuses it. | -| `gecode_min_reif`, `gecode_max_reif` | Specialized native set-extremum equivalence actors are not selected by the standard function hooks. A false result selects native NotMin/NotMax; separate negated names are unnecessary. | -| `gecode_set_singleton_rel`, `_reif`, `_imp` | Native singleton views for relations beyond membership, without an intermediate variable set. Superset/membership uses the standard route. | -| `gecode_rel_imp` | Set disjointness, full-universe complement, and Gecode characteristic-function ordering implication. Equality/disequality/subset/superset reuse standard hooks. | -| `gecode_log` | Arbitrary fixed-base logarithm with variable argument. Standard two-argument log is parameter-only; bases 2 and 10 reuse existing native routes. | -| `gecode_arg_min`, `gecode_arg_max` | Native arbitrary tie choice has different solutions from standard smallest-index tie breaking. Standard tie breaking is reused when requested. | - -Gecode set ordering compares characteristic functions starting at the smallest element. MiniZinc documents sorted-list lexicographic ordering. The inherited Gecode standard ordering posters already use the native order; this change does not add automatic ordering implication hooks or modify those existing posters. The explicit Gecode relation enum documents its own order. - -## Other candidates excluded - -| Candidate | Existing translation or native implementation | -| --- | --- | -| Integer/float squares | Integer power of degree two and multiplication of a variable by itself select native square propagation. | -| Variable-right-hand-side float linear constraints | Native posting appends the right-hand-side variable with coefficient -1, matching existing arithmetic normalization. | -| Scalar relations and scalar loops over arrays | Existing primitive posters already reach the native relations; the array helpers merely repeat them. | -| Mandatory flexible scheduling and mandatory rectangles | Existing cumulative/disjunctive and diffn routes already reach their actors. | -| Knapsack-augmented cumulative overload checking | The native advanced algorithm is selected through the existing cumulative routes and new algorithm annotations. No additional constraint name is needed. | -| Integer/set precedence chains | Native posting repeats the existing adjacent-pair precedence propagators. | -| Reified set cardinality | Native posting combines cardinality and an existing reified integer relation. | -| Set-wide integer relations | Native posting uses extrema plus scalar relations, nonempty cardinality plus nonmembership, or the singleton relation. Those component routes are reachable. | -| Multidimensional bin packing | The helper decomposes into packing constraints and clique all-different constraints; no additional actor is missing. | -| Ordinary set complement equality | Full-universe set difference selects the native complement-view equality actor. | - -## Boundaries and verification - -Optional distinct preserves absent values. If native distinct cannot allocate temporary values outside a full input domain, the registry uses guarded pairwise relations. Irrelevant out-of-range exceptions are eliminated before FlatZinc integer-limit checks. Array shape/index and semantic enum-code checks cover the new bindings; existing unsharing is retained where native actors reject repeated unassigned variables. - -Compiler checks cover every remaining typed leaf and public overloads, inferred reification, shifted indexes, and consistency annotations. Standard-call checks compare solution sets/counts against installed MiniZinc/Gecode and exercise root, reified, implication, negated, empty, and enum cases. Generated FlatZinc confirms automatic native dispatch without an explicit Gecode library include. These checks establish semantics and routing, not performance. - -Fixed-base float testing exposed a rounded scalar logarithm and an invalid shared-variable shortcut in [exp-log.hpp](../gecode/float/transcendental/exp-log.hpp). The implementation uses interval logarithms, handles base-one power, and retains propagation after its own updates. Both build systems enable rounding-aware float compilation. [Float regressions](../test/float/transcendental.cpp) cover exact powers, decreasing bases, base one, and shared variables. - -Arithmetic, minimum-distance, and NFA bindings were checked with 35 exhaustive/routing cases, including signed and zero inputs, false guards, invalid variable moduli, shifted matrices, nondeterministic words, and parameter/enum products. - -Propagation algorithms are selected with annotations, never predicate arguments. `gecode_basic_propagation`, `gecode_advanced_propagation`, and `gecode_full_propagation` map to `IPL_BASIC`, `IPL_ADVANCED`, and their combination. They combine with existing domain/bounds/value propagation annotations. `gecode_decomposed_propagation` selects separate pair actors for minimum distance; the default is a single actor with the same filtering. Unsupported algorithm choices retain the native constraint's normal behavior. [Registry regressions](../test/flatzinc/native-registry.cpp) check semantic boundaries and demonstrate different native inter-distance filtering under basic and advanced algorithms. - -```minizinc -include "gecode.mzn"; -constraint gecode_inter_distance(x, distance) :: gecode_basic_propagation; -constraint gecode_inter_distance(y, distance) - :: bounds_propagation :: gecode_advanced_propagation; -constraint gecode_count(x, targets, GecodeLe, limit) :: domain_propagation; -``` - -To run the retained native regressions from an existing CMake build: - -```sh -cmake --build build/minizinc-registry --target fzn-gecode gecode-test -j6 -build/minizinc-registry/bin/gecode-test -test '^FlatZinc::' -iter 1 -threads 6 -build/minizinc-registry/bin/gecode-test -test '^Float::Transcendental::' -iter 1 -threads 6 -``` - -For a MiniZinc routing check, select this checkout as the solver library with -`-G`; an include directory alone does not replace the installed solver overrides: - -```sh -minizinc --compile --solver gecode -G "$PWD/gecode/flatzinc/mznlib" \ - --output-fzn-to-file /tmp/native-check.fzn model.mzn -build/minizinc-registry/bin/fzn-gecode -p 6 /tmp/native-check.fzn -``` - -The compiler and solution-comparison checks described above were run during the -audit; they are not an additional checked-in test suite. From ed4223daf4a7396ef86cad213dd189782e49af03 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 19:52:33 +0200 Subject: [PATCH 06/22] Bound FlatZinc divmod results before native posting --- gecode/flatzinc/registry.cpp | 12 ++++++++++-- test/flatzinc/native-registry.cpp | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/gecode/flatzinc/registry.cpp b/gecode/flatzinc/registry.cpp index 07b2017eab..d0b0e19546 100755 --- a/gecode/flatzinc/registry.cpp +++ b/gecode/flatzinc/registry.cpp @@ -2039,8 +2039,16 @@ fvar, } void p_int_divmod(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - divmod(s, s.arg2IntVar(ce[0]), s.arg2IntVar(ce[1]), s.arg2IntVar(ce[2]), - s.arg2IntVar(ce[3]), s.ann2ipl(ann)); + IntVar x = s.arg2IntVar(ce[0]), y = s.arg2IntVar(ce[1]); + IntVar q = s.arg2IntVar(ce[2]), r = s.arg2IntVar(ce[3]); + // Bound unrestricted results before the native linear decomposition + // estimates its intermediate domains. Truncating division gives + // |q| <= |x|, and r lies between zero and x, for every nonzero y. + int magnitude = std::max(-x.min(),x.max()); + dom(s,q,-magnitude,magnitude); + dom(s,r,std::min(0,x.min()),std::max(0,x.max())); + if (!s.failed()) + divmod(s,x,y,q,r,s.ann2ipl(ann)); } void p_int_nroot(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { nroot(s, s.arg2IntVar(ce[0]), ce[1]->getInt(), s.arg2IntVar(ce[2]), diff --git a/test/flatzinc/native-registry.cpp b/test/flatzinc/native-registry.cpp index 411be4b5a8..66f0ad2cb4 100644 --- a/test/flatzinc/native-registry.cpp +++ b/test/flatzinc/native-registry.cpp @@ -91,6 +91,14 @@ namespace Test { namespace FlatZinc { "constraint gecode_regular_nfa_bool_fzn([false,true],3,0,2,[{1,2},{},{},{3},{},{}],1,{3}); solve satisfy;", sat); (void) new FlatZincTest("NativeRegistry::DivmodNegative", "constraint gecode_int_divmod(-7,4,-1,-3); solve satisfy;", sat); + (void) new FlatZincTest("NativeRegistry::DivmodUnboundedResults", + "var 0..17:x; var int:q; var int:r; " + "constraint gecode_int_divmod(x,5,q,r); " + "constraint int_eq(x,17); constraint int_ne(q,3); solve satisfy;", unsat); + (void) new FlatZincTest("NativeRegistry::DivmodUnboundedNegativeResults", + "var -17..0:x; var int:q; var int:r; " + "constraint gecode_int_divmod(x,-5,q,r); " + "constraint int_eq(x,-17); constraint int_ne(r,-2); solve satisfy;", unsat); (void) new FlatZincTest("NativeRegistry::PathCostSentinel", "constraint gecode_path_cost_array([0,4,9,1,0,5,7,2,0],0,[1,2,3],0,2,[4,5,0],9); solve satisfy;", sat); (void) new FlatZincErrorTest("NativeRegistry::SortSize", From fdec18d1696212a5dea1bccb9b9a170b332631aa Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 19:55:55 +0200 Subject: [PATCH 07/22] Infer MiniZinc divmod result domains before flattening --- gecode/flatzinc/mznlib/gecode.mzn | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gecode/flatzinc/mznlib/gecode.mzn b/gecode/flatzinc/mznlib/gecode.mzn index 1c45d5982b..709ad52129 100644 --- a/gecode/flatzinc/mznlib/gecode.mzn +++ b/gecode/flatzinc/mznlib/gecode.mzn @@ -310,7 +310,11 @@ predicate gecode_minimum_distance(array[int] of var int: sites, /** @group gecode.constraints Joint truncating division and remainder; y is nonzero. */ predicate gecode_divmod(var int: x, var int: y, var int: quotient, var int: remainder) = - gecode_int_divmod(x,y,quotient,remainder); + % Infer result domains before FlatZinc posts dependent linear constraints. + let { + var compute_div_bounds(x,y): q = quotient; + var compute_mod_bounds(x,y): r = remainder; + } in gecode_int_divmod(x,y,q,r); /** @group gecode.constraints Integer root rounds down; negative x is allowed for odd degrees. Float roots use interval propagation. Degree is positive. */ predicate gecode_nroot(var int: x, int: degree, var int: y) = From 1521f4725f7419117ee8431567a818c709efc8fa Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 20:02:32 +0200 Subject: [PATCH 08/22] Regenerate configure for rounding-aware float compilation --- configure | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/configure b/configure index 0a59cb0548..05371f9380 100755 --- a/configure +++ b/configure @@ -9532,9 +9532,9 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi if test "${ac_cv_cxx_compiler_vendor}" = "microsoft"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CXX} accepts -fno-rounding-math" >&5 -printf %s "checking whether ${CXX} accepts -fno-rounding-math... " >&6; } - if ${CXX} -fno-rounding-math 2>&1 | grep "ignoring unknown option" -q; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CXX} accepts -frounding-math" >&5 +printf %s "checking whether ${CXX} accepts -frounding-math... " >&6; } + if ${CXX} -frounding-math 2>&1 | grep "ignoring unknown option" -q; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } : @@ -9542,14 +9542,14 @@ printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - CXXFLAGS="-fno-rounding-math${CXXFLAGS:+ }${CXXFLAGS}" - CFLAGS="-fno-rounding-math${CFLAGS:+ }${CFLAGS}" + CXXFLAGS="-frounding-math${CXXFLAGS:+ }${CXXFLAGS}" + CFLAGS="-frounding-math${CFLAGS:+ }${CFLAGS}" fi else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CXX} accepts -fno-rounding-math" >&5 -printf %s "checking whether ${CXX} accepts -fno-rounding-math... " >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CXX} accepts -frounding-math" >&5 +printf %s "checking whether ${CXX} accepts -frounding-math... " >&6; } ac_gecode_save_CXXFLAGS="${CXXFLAGS}" - CXXFLAGS="-fno-rounding-math${CXXFLAGS:+ }${CXXFLAGS} -Werror" + CXXFLAGS="-frounding-math${CXXFLAGS:+ }${CXXFLAGS} -Werror" ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -9579,7 +9579,7 @@ printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - CXXFLAGS="-fno-rounding-math${CXXFLAGS:+ }${CXXFLAGS}" + CXXFLAGS="-frounding-math${CXXFLAGS:+ }${CXXFLAGS}" fi else case e in #( @@ -9597,10 +9597,10 @@ ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ex ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CC} accepts -fno-rounding-math" >&5 -printf %s "checking whether ${CC} accepts -fno-rounding-math... " >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CC} accepts -frounding-math" >&5 +printf %s "checking whether ${CC} accepts -frounding-math... " >&6; } ac_gecode_save_CFLAGS="${CFLAGS}" - CFLAGS="-fno-rounding-math${CFLAGS:+ }${CFLAGS} -Werror" + CFLAGS="-frounding-math${CFLAGS:+ }${CFLAGS} -Werror" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -9630,7 +9630,7 @@ printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - CFLAGS="-fno-rounding-math${CFLAGS:+ }${CFLAGS}" + CFLAGS="-frounding-math${CFLAGS:+ }${CFLAGS}" fi else case e in #( From 4234d6de591ea782ac9e099f0028cdff5944a491 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 20:28:51 +0200 Subject: [PATCH 09/22] Keep compiler rounding policy unchanged in registry update --- CMakeLists.txt | 11 ----------- changelog.in | 1 - configure | 26 +++++++++++++------------- configure.ac | 2 +- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a95d92708..01b980c7be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1263,17 +1263,6 @@ if(GECODE_ENABLE_SET_VARS) endif() if(GECODE_ENABLE_FLOAT_VARS) gecode_link_component(float int kernel) - # FloatVal arithmetic changes the rounding mode, including in public headers. - if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$") - check_cxx_compiler_flag(-frounding-math HAVE_ROUNDING_MATH_FLAG) - if(HAVE_ROUNDING_MATH_FLAG) - foreach(kind shared static) - if(TARGET gecodefloat_${kind}) - target_compile_options(gecodefloat_${kind} PUBLIC -frounding-math) - endif() - endforeach() - endif() - endif() if(GECODE_ENABLE_MPFR AND MPFR_FOUND) foreach(kind shared static) if(TARGET gecodefloat_${kind}) diff --git a/changelog.in b/changelog.in index bd0c2a1888..59341528d6 100755 --- a/changelog.in +++ b/changelog.in @@ -92,7 +92,6 @@ Rank: minor [DESCRIPTION] Keep exact fixed-base power and logarithm solutions using interval logarithms, handle base-one powers, and remove an invalid shared-variable shortcut. -Enable rounding-aware float compilation in both build systems. [ENTRY] diff --git a/configure b/configure index 05371f9380..0a59cb0548 100755 --- a/configure +++ b/configure @@ -9532,9 +9532,9 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi if test "${ac_cv_cxx_compiler_vendor}" = "microsoft"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CXX} accepts -frounding-math" >&5 -printf %s "checking whether ${CXX} accepts -frounding-math... " >&6; } - if ${CXX} -frounding-math 2>&1 | grep "ignoring unknown option" -q; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CXX} accepts -fno-rounding-math" >&5 +printf %s "checking whether ${CXX} accepts -fno-rounding-math... " >&6; } + if ${CXX} -fno-rounding-math 2>&1 | grep "ignoring unknown option" -q; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } : @@ -9542,14 +9542,14 @@ printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - CXXFLAGS="-frounding-math${CXXFLAGS:+ }${CXXFLAGS}" - CFLAGS="-frounding-math${CFLAGS:+ }${CFLAGS}" + CXXFLAGS="-fno-rounding-math${CXXFLAGS:+ }${CXXFLAGS}" + CFLAGS="-fno-rounding-math${CFLAGS:+ }${CFLAGS}" fi else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CXX} accepts -frounding-math" >&5 -printf %s "checking whether ${CXX} accepts -frounding-math... " >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CXX} accepts -fno-rounding-math" >&5 +printf %s "checking whether ${CXX} accepts -fno-rounding-math... " >&6; } ac_gecode_save_CXXFLAGS="${CXXFLAGS}" - CXXFLAGS="-frounding-math${CXXFLAGS:+ }${CXXFLAGS} -Werror" + CXXFLAGS="-fno-rounding-math${CXXFLAGS:+ }${CXXFLAGS} -Werror" ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -9579,7 +9579,7 @@ printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - CXXFLAGS="-frounding-math${CXXFLAGS:+ }${CXXFLAGS}" + CXXFLAGS="-fno-rounding-math${CXXFLAGS:+ }${CXXFLAGS}" fi else case e in #( @@ -9597,10 +9597,10 @@ ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ex ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CC} accepts -frounding-math" >&5 -printf %s "checking whether ${CC} accepts -frounding-math... " >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${CC} accepts -fno-rounding-math" >&5 +printf %s "checking whether ${CC} accepts -fno-rounding-math... " >&6; } ac_gecode_save_CFLAGS="${CFLAGS}" - CFLAGS="-frounding-math${CFLAGS:+ }${CFLAGS} -Werror" + CFLAGS="-fno-rounding-math${CFLAGS:+ }${CFLAGS} -Werror" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -9630,7 +9630,7 @@ printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - CFLAGS="-frounding-math${CFLAGS:+ }${CFLAGS}" + CFLAGS="-fno-rounding-math${CFLAGS:+ }${CFLAGS}" fi else case e in #( diff --git a/configure.ac b/configure.ac index f83bf81ef6..b2c3ef0d58 100644 --- a/configure.ac +++ b/configure.ac @@ -177,7 +177,7 @@ gnu) dnl but -funsafe-math-optimizations break IEEE float compatibility, so we have to avoid it AC_GECODE_CHECK_COMPILERFLAG([-fno-math-errno]) AC_GECODE_CHECK_COMPILERFLAG([-ffinite-math-only]) - AC_GECODE_CHECK_COMPILERFLAG([-frounding-math]) + AC_GECODE_CHECK_COMPILERFLAG([-fno-rounding-math]) AC_GECODE_CHECK_COMPILERFLAG([-fno-signaling-nans]) AC_GECODE_CHECK_COMPILERFLAG([-fcx-limited-range]) AC_GECODE_CHECK_COMPILERFLAG([-mthreads]) From 2159bc903a7f89a00cc56009c477cc45c38bdfd6 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 20:38:38 +0200 Subject: [PATCH 10/22] Round fixed-base float power bounds explicitly with MPFR --- changelog.in | 5 +- gecode/float/transcendental/exp-log.hpp | 65 ++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/changelog.in b/changelog.in index 59341528d6..e48adf5059 100755 --- a/changelog.in +++ b/changelog.in @@ -90,8 +90,9 @@ Module: float What: bug Rank: minor [DESCRIPTION] -Keep exact fixed-base power and logarithm solutions using interval logarithms, -handle base-one powers, and remove an invalid shared-variable shortcut. +Keep exact fixed-base power and logarithm solutions using explicitly rounded +MPFR bounds, handle base-one powers, and remove an invalid shared-variable +shortcut. [ENTRY] diff --git a/gecode/float/transcendental/exp-log.hpp b/gecode/float/transcendental/exp-log.hpp index c9fc4234af..061d8c548f 100755 --- a/gecode/float/transcendental/exp-log.hpp +++ b/gecode/float/transcendental/exp-log.hpp @@ -31,8 +31,62 @@ * */ +#include + namespace Gecode { namespace Float { namespace Transcendental { + // Use explicit MPFR rounding here: compiler assumptions about the hardware + // rounding mode must not collapse the bounds of a fixed-base power. + forceinline FloatVal + fixed_base_pow(FloatNum base, const FloatVal& exponent) { + mpfr_t b, x, y; + mpfr_init2(b, std::numeric_limits::digits); + mpfr_init2(x, std::numeric_limits::digits); + mpfr_init2(y, std::numeric_limits::digits); + mpfr_set_d(b, base, GMP_RNDN); + mpfr_set_d(x, base > 1.0 ? exponent.min() : exponent.max(), GMP_RNDN); + mpfr_pow(y, b, x, GMP_RNDD); + FloatNum lower = mpfr_get_d(y, GMP_RNDD); + mpfr_set_d(x, base > 1.0 ? exponent.max() : exponent.min(), GMP_RNDN); + mpfr_pow(y, b, x, GMP_RNDU); + FloatNum upper = mpfr_get_d(y, GMP_RNDU); + mpfr_clear(b); + mpfr_clear(x); + mpfr_clear(y); + return FloatVal(lower, upper); + } + + forceinline FloatVal + fixed_base_log(FloatNum base, const FloatVal& value) { + FloatVal numerator = log(value); + FloatVal denominator = log(FloatVal(base)); + const FloatNum n[] = {numerator.min(), numerator.max()}; + const FloatNum d[] = {denominator.min(), denominator.max()}; + FloatNum lower = std::numeric_limits::infinity(); + FloatNum upper = -lower; + mpfr_t x, y, q; + mpfr_init2(x, std::numeric_limits::digits); + mpfr_init2(y, std::numeric_limits::digits); + mpfr_init2(q, std::numeric_limits::digits); + // The base is positive and different from one, so log(base) has a fixed + // nonzero sign. All endpoint quotients enclose either increasing or + // decreasing logarithms without hardware-rounded interval division. + for (int i=0; i<2; i++) { + mpfr_set_d(x, n[i], GMP_RNDN); + for (int j=0; j<2; j++) { + mpfr_set_d(y, d[j], GMP_RNDN); + mpfr_div(q, x, y, GMP_RNDD); + lower = std::min(lower, mpfr_get_d(q, GMP_RNDD)); + mpfr_div(q, x, y, GMP_RNDU); + upper = std::max(upper, mpfr_get_d(q, GMP_RNDU)); + } + } + mpfr_clear(x); + mpfr_clear(y); + mpfr_clear(q); + return FloatVal(lower, upper); + } + /* * Bounds consistent exponential operator * @@ -104,10 +158,8 @@ namespace Gecode { namespace Float { namespace Transcendental { GECODE_ME_CHECK(x1.gq(home,0.0)); if (x1.max() == 0.0) return ES_FAILED; - // Enclose log(base) in both directions to retain exact power solutions. - FloatVal lb = log(FloatVal(base)); - GECODE_ME_CHECK(x0.eq(home,log(x1.domain())/lb)); - GECODE_ME_CHECK(x1.eq(home,exp(x0.domain()*lb))); + GECODE_ME_CHECK(x0.eq(home,fixed_base_log(base,x1.domain()))); + GECODE_ME_CHECK(x1.eq(home,fixed_base_pow(base,x0.domain()))); (void) new (home) Pow(home,base,x0,x1); return ES_OK; } @@ -129,9 +181,8 @@ namespace Gecode { namespace Float { namespace Transcendental { Pow::propagate(Space& home, const ModEventDelta&) { if (x1.max() == 0.0) return ES_FAILED; - FloatVal lb = log(FloatVal(base)); - GECODE_ME_CHECK(x0.eq(home,log(x1.domain())/lb)); - GECODE_ME_CHECK(x1.eq(home,exp(x0.domain()*lb))); + GECODE_ME_CHECK(x0.eq(home,fixed_base_log(base,x1.domain()))); + GECODE_ME_CHECK(x1.eq(home,fixed_base_pow(base,x0.domain()))); return x0.assigned() ? home.ES_SUBSUMED(*this) : ES_NOFIX; } From e30fd4c8aa031d8f18311671d077d3cd6a88f9c1 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 22:03:36 +0200 Subject: [PATCH 11/22] Document Gecode MiniZinc predicate contracts --- gecode/flatzinc/mznlib/gecode.mzn | 345 +++++++++++++++++++------- gecode/flatzinc/mznlib/gecode_fzn.mzn | 146 +++++++++-- 2 files changed, 387 insertions(+), 104 deletions(-) diff --git a/gecode/flatzinc/mznlib/gecode.mzn b/gecode/flatzinc/mznlib/gecode.mzn index 709ad52129..da990a0b56 100644 --- a/gecode/flatzinc/mznlib/gecode.mzn +++ b/gecode/flatzinc/mznlib/gecode.mzn @@ -68,9 +68,11 @@ annotation gecode_advanced_propagation; These may be combined with domain_propagation or bounds_propagation; constraints without algorithm choices ignore them. */ annotation gecode_full_propagation; -/** @group gecode.annotations Use separate pair propagators for minimum - distance. The default uses one propagator; both implement the same filtering. - Combine with basic/advanced/full annotations to select matching propagation. */ +/** @group gecode.annotations + Use a separate propagator for each pair in minimum-distance constraints. + The default is a single propagator. The basic, advanced, and full propagation + annotations select the algorithm in either case. +*/ annotation gecode_decomposed_propagation; annotation int_assign(array[int] of var int: x, ann:a); @@ -186,35 +188,53 @@ include "range.mzn"; include "arg_min.mzn"; include "arg_max.mzn"; -% Arrays correspond by position unless an index agreement is stated. -% Enum order is part of the typed FlatZinc interface. +/** @group gecode.constraints + Comparison operators for integer values and counts. +*/ enum GecodeIntRelation = { GecodeEq, GecodeNe, GecodeLe, GecodeLt, GecodeGe, GecodeGt }; -/** @group gecode.constraints Set comparison. Complement uses the full Gecode - set universe. Ordering compares characteristic functions from the smallest - element, rather than the lexicographic order of sorted element lists. */ + +/** @group gecode.constraints + Comparison operators for sets. Complement uses Gecode's full set + universe. Ordering compares membership from the smallest element, rather + than comparing sorted lists of elements. +*/ enum GecodeSetRelation = { GecodeSetEq, GecodeSetNe, GecodeSubset, GecodeSuperset, GecodeDisjoint, GecodeComplement, GecodeSetLe, GecodeSetLt, GecodeSetGe, GecodeSetGt }; +/** @group gecode.constraints + Union, disjoint union, intersection, and difference of sets. +*/ enum GecodeSetOperation = { GecodeUnion, GecodeDisjointUnion, GecodeIntersection, GecodeDifference }; -/** @group gecode.constraints FixedDuration: flex is start, fix is duration. - FixedStart: flex is end, fix is start. FixedEnd: flex is start, fix is end. */ + +/** @group gecode.constraints + Describes the fixed part of a task. + GecodeFixedDuration uses flex as start and fix as duration. + GecodeFixedStart uses flex as end and fix as start. + GecodeFixedEnd uses flex as start and fix as end. + Fixed durations must be nonnegative. +*/ enum GecodeTaskType = { GecodeFixedDuration, GecodeFixedStart, GecodeFixedEnd }; -/** @group gecode.constraints All x[i]+offset[i] values are distinct. - Arrays must have equal lengths. Uses the existing native offset route. */ +/** @group gecode.constraints + Requires all values \a x[i] + \a offset[i] to be distinct. + The arrays must have equal lengths and correspond by position. +*/ predicate gecode_all_different(array[int] of int: offset, array[int] of var int: x) = gecode_all_different_offset(offset, x); -/** @group gecode.constraints Sort x into y with x[i]=y[permutation[i]]. Ties - may be permuted; standard arg_sort instead returns a stable inverse mapping. - The explicit-offset typed form labels output positions starting at offset. */ +/** @group gecode.constraints + Constrains \a y to be the sorted values of \a x, with + \a x[i] = \a y[\a permutation[i]]. Equal values may appear in either order. + The index sets of \a x and \a permutation must agree, and all arrays must + have equal lengths. +*/ predicate gecode_sort_permutation(array[int] of var int: x, array[int] of var int: y, array[int] of var int: permutation) = assert(index_set(x) = index_set(permutation) /\ length(x) = length(y), @@ -222,29 +242,40 @@ predicate gecode_sort_permutation(array[int] of var int: x, if length(y) = 0 then true else gecode_sort_permutation(x, y, permutation, min(index_set(y))) endif); -/** @group gecode.constraints Compare an occurrence count with n. The second - argument selects a value set, or one target per array position. - Existing equality/disequality translations are reused where available. */ +/** @group gecode.constraints + Compares the number of elements of \a x that belong to \a values + with \a n, using \a relation. +*/ predicate gecode_count(array[int] of var int: x, set of int: values, GecodeIntRelation: relation, var int: n) = if relation = GecodeEq then among(n, x, values) elseif relation = GecodeNe then let {var 0..length(x): c; constraint among(c, x, values);} in c != n else gecode_count_in_fzn(x, values, enum2int(relation), n) endif; +/** @group gecode.constraints + Compares the number of positions where \a x matches \a values + with \a n, using \a relation. The arrays must have equal lengths and + correspond by position. +*/ predicate gecode_count(array[int] of var int: x, array[int] of int: values, GecodeIntRelation: relation, var int: n) = assert(length(x) = length(values), "gecode_count: unequal array lengths", gecode_count_matches_fzn(x, values, enum2int(relation), n)); -/** @group gecode.constraints Compare the number of distinct values with n. - The empty array has zero values. Equality and disequality on integers reuse - nvalue; Boolean arrays use the native Boolean propagator. */ +/** @group gecode.constraints + Compares the number of distinct values in \a x with \a n, using + \a relation. An empty array has zero distinct values. +*/ predicate gecode_nvalues(array[int] of var int: x, GecodeIntRelation: relation, var int: n) = if relation = GecodeEq then nvalue(n, x) elseif relation = GecodeNe then let {var 0..length(x): c; constraint nvalue(c, x);} in c != n else gecode_nvalues_int_fzn(x, enum2int(relation), n) endif; +/** @group gecode.constraints + Compares the number of distinct values in \a x with \a n, using + \a relation. An empty array has zero distinct values. +*/ predicate gecode_nvalues(array[int] of var bool: x, GecodeIntRelation: relation, var int: n) = if relation = GecodeNe then @@ -252,43 +283,53 @@ predicate gecode_nvalues(array[int] of var bool: x, constraint gecode_nvalues_bool_fzn(x, enum2int(GecodeEq), c);} in c != n else gecode_nvalues_bool_fzn(x, enum2int(relation), n) endif; -/** @group gecode.constraints Nonnegative greatest common divisor of x and y. - Negative operands use their absolute value; gcd(0,0)=0. Reification and - implication of this predicate select the corresponding native actors. */ +/** @group gecode.constraints + Constrains \a result to be the nonnegative greatest common divisor + of \a x and \a y. Negative operands use their absolute values, and + gcd(0, 0) = 0. +*/ predicate gecode_gcd(var int: x, var int: y, var int: result) = gecode_gcd_int(x,y,result); -/** @group gecode.constraints Divisor divides dividend, including 0 divides 0. - Zero does not divide nonzero values. Supports reification and implication. */ +/** @group gecode.constraints + Requires \a dividend to be an integer multiple of \a divisor. + A zero divisor is permitted only when \a dividend is zero. +*/ predicate gecode_divides(var int: divisor, var int: dividend) = gecode_divides_int(divisor,dividend); -/** @group gecode.constraints Reify or imply the exact nary integer product. - Empty product is one. These specialized actors differ from reifying an - equality against a separately computed standard product. */ +/** @group gecode.constraints + Constrains \a b to be true if and only if the product of \a x + equals \a result. The product of an empty array is one. +*/ predicate gecode_product_reif(array[int] of var int: x, var int: result, var bool: b) = gecode_product_int_reif(x,result,b); +/** @group gecode.constraints + Requires the product of \a x to equal \a result when \a b is true. + The product of an empty array is one. +*/ predicate gecode_product_imp(array[int] of var int: x, var int: result, var bool: b) = gecode_product_int_imp(x,result,b); -/** @group gecode.constraints Nary product with canonical Euclidean residue - 0 <= result < modulus, including for negative products. The empty array - produces 1 modulo modulus (zero when modulus is one). - Modulus positivity/range is part of the reified proposition. An inactive - implication imposes no restriction on modulus or result. Known positive - moduli select the fixed-modulus native actor. */ +/** @group gecode.constraints + Constrains \a result to be the Euclidean remainder of the product + of \a x modulo \a modulus, with 0 <= \a result < \a modulus. + A nonpositive modulus makes the proposition false. + The product of an empty array is one. +*/ predicate gecode_product_mod(array[int] of var int: x, var int: modulus, var int: result) = if is_fixed(modulus) then if fix(modulus) > 0 then gecode_product_mod_fixed_fzn(x,fix(modulus),result) else gecode_product_mod_var_fzn(x,modulus,result) endif else gecode_product_mod_var_fzn(x,modulus,result) endif; -/** @group gecode.constraints Minimum table distance among selected sites, - with at least two positions. Matrix axes are equal site indexes; entries - are symmetric/nonnegative with zero diagonal. Repeated sites are allowed. - Optional requirements matrix uses the positions' index set, is symmetric - and nonnegative with zero diagonal, and imposes inclusive pairwise lower - bounds. Select native algorithm/organization through propagation annotations. */ +/** @group gecode.constraints + Constrains \a result to be the smallest distance between any two + positions in \a sites, using \a distances[\a sites[i], \a sites[j]]. + There must be at least two positions. Both matrix axes use the same site + indexes, and the matrix must be symmetric and nonnegative with a zero + diagonal. Repeated sites are permitted. +*/ predicate gecode_minimum_distance(array[int] of var int: sites, array[int,int] of int: distances, var int: result) = assert(length(sites) >= 2 /\ length(distances) > 0 /\ @@ -296,6 +337,14 @@ predicate gecode_minimum_distance(array[int] of var int: sites, "gecode_minimum_distance: need at least two positions and a square nonempty distance matrix", gecode_minimum_distance_fzn([v-min(index_set_1of2(distances)) | v in sites], card(index_set_1of2(distances)),array1d(distances),result)); +/** @group gecode.constraints + Constrains \a result to be the smallest distance between any two + positions in \a sites. Each pair must also meet the lower bound in + \a requirements[i,j]. There must be at least two positions. + Both axes of \a distances use the same site indexes, and both axes of + \a requirements use the index set of \a sites. Both matrices must be + symmetric and nonnegative with a zero diagonal. Repeated sites are permitted. +*/ predicate gecode_minimum_distance(array[int] of var int: sites, array[int,int] of int: distances, var int: result, array[int,int] of int: requirements) = @@ -307,37 +356,64 @@ predicate gecode_minimum_distance(array[int] of var int: sites, gecode_minimum_distance_required_fzn([v-min(index_set_1of2(distances)) | v in sites], card(index_set_1of2(distances)),array1d(distances),result,array1d(requirements))); -/** @group gecode.constraints Joint truncating division and remainder; y is - nonzero. */ +/** @group gecode.constraints + Constrains \a quotient = \a x div \a y and + \a remainder = \a x mod \a y. The divisor \a y must be nonzero. + Division truncates towards zero; a nonzero remainder has the sign of \a x. +*/ predicate gecode_divmod(var int: x, var int: y, var int: quotient, var int: remainder) = - % Infer result domains before FlatZinc posts dependent linear constraints. + % Bound the results before dependent linear constraints are posted. let { var compute_div_bounds(x,y): q = quotient; var compute_mod_bounds(x,y): r = remainder; } in gecode_int_divmod(x,y,q,r); -/** @group gecode.constraints Integer root rounds down; negative x is allowed - for odd degrees. Float roots use interval propagation. Degree is positive. */ + +/** @group gecode.constraints + Constrains \a y to be the integer root of degree \a degree of \a x, + rounded down. The degree must be positive, and negative \a x is permitted + only for odd degrees. +*/ predicate gecode_nroot(var int: x, int: degree, var int: y) = assert(degree > 0, "gecode_nroot: degree must be positive", gecode_int_nroot(x,degree,y)); +/** @group gecode.constraints + Constrains \a y to be the nonnegative root of degree \a degree of \a x. + The degree must be positive, and \a x must be nonnegative. +*/ predicate gecode_nroot(var float: x, int: degree, var float: y) = assert(degree > 0, "gecode_nroot: degree must be positive", if degree = 2 then float_sqrt(x,y) else gecode_float_nroot(x,degree,y) endif); -/** @group gecode.constraints Hamiltonian successor path visits every node. - x[end]=max(index_set(x))+1 is the terminal sentinel; x is nonempty. This - differs from standard graph path. Costs are a row-major square matrix; - the final sentinel edge costs zero. Typed forms accept an explicit - nonnegative node offset and flat costs. */ +/** @group gecode.constraints + Requires \a x to describe a path from \a start to \a end that + visits every node once, where \a x[i] is the successor of node i. + The node array must be nonempty. Its indexes identify the nodes, and + \a x[\a end] = max(index_set(\a x)) + 1 marks the end of the path. +*/ predicate gecode_path(array[int] of var int: x, var int: start, var int: end) = assert(length(x) > 0, "gecode_path: empty node array", gecode_path(0, [v-min(index_set(x)) | v in x], start-min(index_set(x)), end-min(index_set(x)))); +/** @group gecode.constraints + Requires \a x to describe a path from \a start to \a end that + visits every node once, with total edge cost \a cost given by \a costs. + Both matrix axes must use the index set of the nonempty node array. + The successor \a x[\a end] = max(index_set(\a x)) + 1 marks the end, + and its edge costs zero. +*/ predicate gecode_path(array[int,int] of int: costs, array[int] of var int: x, var int: start, var int: end, var int: cost) = assert(length(x) > 0 /\ index_set_1of2(costs) = index_set(x) /\ index_set_2of2(costs) = index_set(x), "gecode_path: incompatible cost matrix", gecode_path_cost(array1d(costs), 0, [v-min(index_set(x)) | v in x], start-min(index_set(x)), end-min(index_set(x)), cost)); +/** @group gecode.constraints + Requires \a x to describe a path from \a start to \a end that + visits every node once, with total cost \a cost. Each \a edge_costs[i] + is the cost of the edge from i to \a x[i], as given by \a costs. + Both matrix axes and \a edge_costs must use the index set of the nonempty + node array. The successor \a x[\a end] = max(index_set(\a x)) + 1 marks + the end, and its edge costs zero. +*/ predicate gecode_path(array[int,int] of int: costs, array[int] of var int: x, var int: start, var int: end, array[int] of var int: edge_costs, var int: cost) = assert(length(x) > 0 /\ index_set_1of2(costs) = index_set(x) /\ @@ -346,33 +422,66 @@ predicate gecode_path(array[int,int] of int: costs, array[int] of var int: x, gecode_path_cost_array(array1d(costs), 0, [v-min(index_set(x)) | v in x], start-min(index_set(x)), end-min(index_set(x)), edge_costs, cost)); -/** @group gecode.constraints Native typed tasks may fix duration, start, - or end. These task types are not part of standard disjunctive/cumulative. */ +/** @group gecode.constraints + Requires the tasks described by \a types, \a flex, and \a fix + not to overlap. GecodeTaskType specifies which start, end, or duration is + fixed. The arrays must have equal lengths and correspond by position. +*/ predicate gecode_unary_typed(array[int] of GecodeTaskType: types, array[int] of var int: flex, array[int] of int: fix) = gecode_unary_typed_fzn(enum2int(types),flex,fix); +/** @group gecode.constraints + Requires the present tasks described by \a types, \a flex, and + \a fix not to overlap. A task is present when its entry in \a present + is true. GecodeTaskType specifies which start, end, or duration is fixed. + The arrays must have equal lengths and correspond by position. +*/ predicate gecode_unary_typed(array[int] of GecodeTaskType: types, array[int] of var int: flex, array[int] of int: fix, array[int] of var bool: present) = gecode_unary_typed_optional_fzn(enum2int(types),flex,fix,present); +/** @group gecode.constraints + Requires the total \a usage of tasks executing at any time not + to exceed \a capacity. GecodeTaskType specifies how \a types, \a flex, + and \a fix describe each task. Usage and capacity must be nonnegative. + The arrays must have equal lengths and correspond by position. +*/ predicate gecode_cumulative_typed(array[int] of GecodeTaskType: types, array[int] of var int: flex, array[int] of int: fix, array[int] of int: usage, var int: capacity) = gecode_cumulative_typed_fzn(enum2int(types),flex,fix,usage,capacity); +/** @group gecode.constraints + Requires the total \a usage of present tasks executing at any + time not to exceed \a capacity. A task is present when its entry in + \a present is true. GecodeTaskType specifies how \a types, \a flex, + and \a fix describe each task. Usage and capacity must be nonnegative. + The arrays must have equal lengths and correspond by position. +*/ predicate gecode_cumulative_typed(array[int] of GecodeTaskType: types, array[int] of var int: flex, array[int] of int: fix, array[int] of int: usage, var int: capacity, array[int] of var bool: present) = gecode_cumulative_typed_optional_fzn(enum2int(types),flex,fix,usage,capacity,present); -/** @group gecode.constraints Optional nonoverlapping rectangles. Fixed sizes - are nonnegative. Variable-size forms enforce x+width=right and y+height=top - when present. All arrays share their index sets. */ +/** @group gecode.constraints + Requires the present rectangles with origins (\a x[i], \a y[i]) + and fixed sizes (\a width[i], \a height[i]) not to overlap. + A rectangle is present when \a present[i] is true. + Sizes must be nonnegative. All arrays must have identical index sets. +*/ predicate gecode_nooverlap(array[int] of var int: x, array[int] of int: width, array[int] of var int: y, array[int] of int: height, array[int] of var bool: present) = assert(index_set(x) = index_set(width) /\ index_set(x) = index_set(y) /\ index_set(x) = index_set(height) /\ index_set(x) = index_set(present), "gecode_nooverlap: incompatible indexes", gecode_nooverlap_optional(x,width,y,height,present)); +/** @group gecode.constraints + Requires the present rectangles not to overlap, with horizontal + bounds \a x and \a right and vertical bounds \a y and \a top. + A rectangle is present when \a present[i] is true. + For each present rectangle, \a x + \a width = \a right and + \a y + \a height = \a top. Sizes must be nonnegative. + All arrays must have identical index sets. +*/ predicate gecode_nooverlap(array[int] of var int: x, array[int] of var int: width, array[int] of var int: right, array[int] of var int: y, array[int] of var int: height, array[int] of var int: top, @@ -385,65 +494,106 @@ predicate gecode_nooverlap(array[int] of var int: x, array[int] of var int: widt (x[i]+width[i]=right[i] /\ y[i]+height[i]=top[i])) /\ gecode_nooverlap_flex_optional(x,width,right,y,height,top,present)); -/** @group gecode.constraints The set is empty or an interval; the two-argument - form returns its convex hull (empty for an empty input). */ +/** @group gecode.constraints + Requires \a x to be empty or a consecutive interval of integers. +*/ predicate gecode_convex(var set of int: x) = gecode_set_convex(x); +/** @group gecode.constraints + Constrains \a hull to be the smallest consecutive interval + containing \a x. The hull of an empty set is empty. +*/ predicate gecode_convex(var set of int: x, var set of int: hull) = gecode_set_convex_hull(x,hull); -/** @group gecode.constraints Ordered disjoint sets: every element precedes - every element of subsequent nonempty sets. Optional second result is union. */ + +/** @group gecode.constraints + Requires every element of each set in \a x to be smaller than + every element of each later set. Empty sets are permitted. +*/ predicate gecode_set_sequence(array[int] of var set of int: x) = gecode_array_set_seq(x); +/** @group gecode.constraints + Requires every element of each set in \a x to be smaller than + every element of each later set, and constrains \a y to their union. + Empty sets are permitted. +*/ predicate gecode_set_sequence(array[int] of var set of int: x, var set of int: y) = gecode_array_set_seq_union(x,y); -/** @group gecode.constraints Native combined (x operation y) relation z. - Equality for ordinary union, intersection, and difference reuses existing - MiniZinc operations. Complement is relative to Gecode's full set universe. */ + +/** @group gecode.constraints + Compares the result of applying \a operation to \a x and \a y + with \a z, using \a relation. Complement and set ordering follow + GecodeSetRelation. +*/ predicate gecode_set_operation(var set of int: x, GecodeSetOperation: operation, var set of int: y, GecodeSetRelation: relation, var set of int: z) = if relation = GecodeSetEq /\ operation = GecodeUnion then z = x union y elseif relation = GecodeSetEq /\ operation = GecodeIntersection then z = x intersect y elseif relation = GecodeSetEq /\ operation = GecodeDifference then z = x diff y else gecode_set_operation_fzn(x,enum2int(operation),y,enum2int(relation),z) endif; -/** @group gecode.constraints Intersect singleton sets {x[i]} and initial, or - form their union/disjoint union. Difference is unsupported. */ + +/** @group gecode.constraints + Combines \a initial and the singleton sets {\a x[i]} using + \a operation, and constrains \a y to the result. Union, disjoint union, + and intersection are supported. +*/ predicate gecode_array_int_set_operation(array[int] of var int: x, GecodeSetOperation: operation, set of int: initial, var set of int: y) = assert(operation != GecodeDifference, "gecode_array_int_set_operation: difference is unsupported", gecode_array_int_set_operation_fzn(x,enum2int(operation),initial,y)); -/** @group gecode.constraints Selected zero-based positions of x are combined - as singleton sets. Empty intersection is universe; empty union is empty. - Union already has a standard range route; intersection and disjoint union - select additional native variants. */ + +/** @group gecode.constraints + Combines the singleton sets {\a x[i]} at the zero-based positions + in \a selected, using \a operation, and constrains \a value to the result. + Union, disjoint union, and intersection are supported. An empty selection + gives \a universe for intersection and the empty set for either union. +*/ predicate gecode_array_int_set_element(array[int] of var int: x, GecodeSetOperation: operation, var set of int: selected, var set of int: value, set of int: universe) = assert(operation != GecodeDifference, "gecode_array_int_set_element: difference is unsupported", if operation = GecodeUnion then gecode_range(x,0,selected,value) else gecode_array_int_set_element_fzn(x,enum2int(operation),selected,value,universe) endif); -/** @group gecode.constraints Reified set extrema. False for an empty set. - Unlike standard min/max expressions, these post the specialized native - equivalence propagators. Negate by passing false as the final argument. */ + +/** @group gecode.constraints + Constrains \a b to be true if and only if \a s is nonempty and + \a x is its smallest element. +*/ predicate gecode_min_reif(var set of int: s, var int: x, var bool: b) = gecode_set_min_reif(s,x,b); +/** @group gecode.constraints + Constrains \a b to be true if and only if \a s is nonempty and + \a x is its largest element. +*/ predicate gecode_max_reif(var set of int: s, var int: x, var bool: b) = gecode_set_max_reif(s,x,b); -/** @group gecode.constraints Compare s with singleton {x}. Superset is - membership and reuses the existing membership translation. Other comparisons - use native singleton views instead of introducing a set variable. */ +/** @group gecode.constraints + Compares \a s with the singleton set {\a x}, using \a relation. + Complement and set ordering follow GecodeSetRelation. +*/ predicate gecode_set_singleton_rel(var set of int: s, GecodeSetRelation: relation, var int: x) = if relation = GecodeSuperset then x in s else gecode_set_singleton_rel_fzn(s,enum2int(relation),x) endif; +/** @group gecode.constraints + Constrains \a b to be true if and only if \a s has \a relation + to the singleton set {\a x}. Complement and set ordering follow + GecodeSetRelation. +*/ predicate gecode_set_singleton_rel_reif(var set of int: s, GecodeSetRelation: relation, var int: x, var bool: b) = if relation = GecodeSuperset then b <-> x in s else gecode_set_singleton_rel_fzn_reif(s,enum2int(relation),x,b) endif; +/** @group gecode.constraints + Requires \a s to have \a relation to the singleton set {\a x} + when \a b is true. Complement and set ordering follow GecodeSetRelation. +*/ predicate gecode_set_singleton_rel_imp(var set of int: s, GecodeSetRelation: relation, var int: x, var bool: b) = if relation = GecodeSuperset then b -> x in s else gecode_set_singleton_rel_fzn_imp(s,enum2int(relation),x,b) endif; -/** @group gecode.constraints Native set implication. Equality, disequality, - subset, and superset use standard implication routes. Disjointness, - complement, and characteristic-function ordering use native relations. */ + +/** @group gecode.constraints + Requires \a x to have \a relation to \a y when \a b is true. + Complement and set ordering follow GecodeSetRelation. +*/ predicate gecode_rel_imp(var set of int: x, GecodeSetRelation: relation, var set of int: y, var bool: b) = if relation = GecodeSetEq then b -> x = y @@ -452,37 +602,60 @@ predicate gecode_rel_imp(var set of int: x, GecodeSetRelation: relation, elseif relation = GecodeSuperset then b -> x superset y else gecode_set_rel_imp_fzn(x,enum2int(relation),y,b) endif; -/** @group gecode.constraints Fixed-base logarithm requires MPFR; base must - be positive and differ from one. Bases 2 and 10 reuse standard routes. */ +/** @group gecode.constraints + Constrains \a y to be the logarithm of \a x to base \a base. + The base must be positive and different from one. Requires MPFR support. +*/ predicate gecode_log(float: base, var float: x, var float: y) = assert(base > 0.0 /\ base != 1.0, "gecode_log: base must be positive and different from one", if base = 2.0 then float_log2(x,y) elseif base = 10.0 then float_log10(x,y) else gecode_float_log_base(base,x,y) endif); -/** @group gecode.constraints Index of a minimum/maximum. break_ties=true - reuses standard arg_min/arg_max and selects the smallest qualifying index. - Otherwise any qualifying index is permitted. Arrays must be nonempty. */ +/** @group gecode.constraints + Constrains \a index to be the index of a smallest value in \a x. + If \a break_ties is true, the smallest qualifying index is required; + otherwise any qualifying index is permitted. The array must be nonempty. +*/ predicate gecode_arg_min(array[int] of var int: x, var int: index, bool: break_ties) = assert(length(x) > 0, "gecode_arg_min: empty array", if break_ties then arg_min(array1d(x)) + min(index_set(x)) - 1 = index else gecode_arg_min_int(x,0,index-min(index_set(x)),false) endif); +/** @group gecode.constraints + Constrains \a index to be the index of a smallest value in \a x. + If \a break_ties is true, the smallest qualifying index is required; + otherwise any qualifying index is permitted. The array must be nonempty. +*/ predicate gecode_arg_min(array[int] of var bool: x, var int: index, bool: break_ties) = assert(length(x) > 0, "gecode_arg_min: empty array", if break_ties then arg_min(array1d(x)) + min(index_set(x)) - 1 = index else gecode_arg_min_bool(x,0,index-min(index_set(x)),false) endif); +/** @group gecode.constraints + Constrains \a index to be the index of a largest value in \a x. + If \a break_ties is true, the smallest qualifying index is required; + otherwise any qualifying index is permitted. The array must be nonempty. +*/ predicate gecode_arg_max(array[int] of var int: x, var int: index, bool: break_ties) = assert(length(x) > 0, "gecode_arg_max: empty array", if break_ties then arg_max(array1d(x)) + min(index_set(x)) - 1 = index else gecode_arg_max_int(x,0,index-min(index_set(x)),false) endif); +/** @group gecode.constraints + Constrains \a index to be the index of a largest value in \a x. + If \a break_ties is true, the smallest qualifying index is required; + otherwise any qualifying index is permitted. The array must be nonempty. +*/ predicate gecode_arg_max(array[int] of var bool: x, var int: index, bool: break_ties) = assert(length(x) > 0, "gecode_arg_max: empty array", if break_ties then arg_max(array1d(x)) + min(index_set(x)) - 1 = index else gecode_arg_max_bool(x,0,index-min(index_set(x)),false) endif); -/** @group gecode.constraints Exactly one Boolean array entry is true and its - position plus offset is value, or a float equals 1.0/0.0 for true/false. - The Boolean/float overload uses the native channel directly. */ +/** @group gecode.constraints + Requires exactly one element of \a x to be true, at zero-based + position \a value - \a offset. +*/ predicate gecode_channel(array[int] of var bool: x, var int: value, int: offset) = gecode_bool_int_channel(x,value,offset); +/** @group gecode.constraints + Constrains \a x to be 1.0 when \a b is true and 0.0 otherwise. +*/ predicate gecode_channel(var bool: b, var float: x) = gecode_bool_float_channel(b,x); diff --git a/gecode/flatzinc/mznlib/gecode_fzn.mzn b/gecode/flatzinc/mznlib/gecode_fzn.mzn index 2f6ee4e380..54d909cdeb 100644 --- a/gecode/flatzinc/mznlib/gecode_fzn.mzn +++ b/gecode/flatzinc/mznlib/gecode_fzn.mzn @@ -30,60 +30,83 @@ % % -% Typed FlatZinc declarations for the native Gecode library. -% Model code should include gecode.mzn for overloaded predicates. -% Relation and task codes follow the enums declared there. - -/** @group gecode.constraints Pairwise absolute differences are at least the - nonnegative distance. Empty and singleton arrays only constrain distance. - Native bounds propagation offers basic and advanced algorithms, selected - with gecode_basic_propagation, gecode_advanced_propagation, or - gecode_full_propagation. */ +% Integer relation codes: 1 =, 2 !=, 3 <=, 4 <, 5 >=, 6 >. +% Set relation, operation, and task codes follow the enums in gecode.mzn. +% Arrays correspond by position unless stated otherwise. +% _reif predicates equate b with the relation; _imp require it when b is true. + +/** @group gecode.constraints + Constrain every pair of values in \a x to differ by at least \a distance. + The distance is nonnegative, including for empty and singleton arrays. +*/ predicate gecode_inter_distance(array[int] of var int: x, var int: distance); +/** @group gecode.constraints + Constrain values in \a x whose corresponding \a present entry is true to + be pairwise distinct. The arrays have equal lengths. +*/ predicate gecode_all_different_optional(array[int] of var int: x, array[int] of var bool: present); +/** @group gecode.constraints + Constrain the values in \a x to be pairwise distinct, except that + \a except_value may occur more than once. +*/ predicate gecode_all_different_except(array[int] of var int: x, int: except_value); +/** @group gecode.constraints + Constrain the sums of corresponding entries in \a x and \a offset to be + pairwise distinct. The arrays must have equal lengths. +*/ predicate gecode_all_different_offset(array[int] of int: offset, array[int] of var int: x); +% Exactly one entry is true; its zero-based position plus offset equals value. predicate gecode_bool_int_channel(array[int] of var bool: x, var int: value, int: offset); +% Sort x into y; permutation[i]-offset is the zero-based position of x[i] in y. predicate gecode_sort_permutation(array[int] of var int: x, array[int] of var int: y, array[int] of var int: permutation, int: offset); +% Compare the number of occurrences of value in x with count. predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, int: relation, var int: count); +% Compare the number of entries of x that belong to values with count. predicate gecode_count_in_fzn(array[int] of var int: x, set of int: values, int: relation, var int: count); +% Compare the number of position-wise matches between x and values with count. predicate gecode_count_matches_fzn(array[int] of var int: x, array[int] of int: values, int: relation, var int: count); -/** @group gecode.constraints Closed cardinality with arbitrary allowed count - sets. Every x[i] belongs to cover, cover values are distinct, and cover and - counts have equal lengths. Arrays correspond by position. */ +/** @group gecode.constraints + Constrain the number of occurrences of each value in \a cover to belong to + the corresponding set in \a counts. Every value in \a x must occur in + \a cover. Cover values are distinct; \a cover and \a counts have equal lengths. +*/ predicate gecode_global_cardinality_sets(array[int] of var int: x, array[int] of int: cover, array[int] of set of int: counts); +% Compare the number of distinct values in x with count; an empty array has none. predicate gecode_nvalues_int_fzn(array[int] of var int: x, int: relation, var int: count); predicate gecode_nvalues_bool_fzn(array[int] of var bool: x, int: relation, var int: count); +% Arrays have unequal lengths or at least one pair of corresponding entries differs. predicate gecode_array_int_ne(array[int] of var int: x, array[int] of var int: y); predicate gecode_array_bool_ne(array[int] of var bool: x, array[int] of var bool: y); +% At least two entries differ; empty and singleton arrays cannot satisfy this. predicate gecode_not_all_equal_int(array[int] of var int: x); predicate gecode_not_all_equal_bool(array[int] of var bool: x); +% z equals x when b is true, and y otherwise. predicate gecode_int_ite(var bool: b, var int: x, var int: y, var int: z); predicate gecode_bool_ite(var bool: b, var bool: x, var bool: y, var bool: z); @@ -93,34 +116,56 @@ predicate gecode_set_ite(var bool: b, var set of int: x, predicate gecode_float_ite(var bool: b, var float: x, var float: y, var float: z); +% result is the nonnegative gcd of x and y; gcd(0,0) is zero. predicate gecode_gcd_int(var int: x, var int: y, var int: result); predicate gecode_gcd_int_reif(var int: x, var int: y, var int: result, var bool: b); predicate gecode_gcd_int_imp(var int: x, var int: y, var int: result, var bool: b); + +% divisor divides dividend; zero divides only zero. predicate gecode_divides_int(var int: divisor, var int: dividend); predicate gecode_divides_int_reif(var int: divisor, var int: dividend, var bool: b); predicate gecode_divides_int_imp(var int: divisor, var int: dividend, var bool: b); + +% result is the product of x; an empty product is one. predicate gecode_product_int(array[int] of var int: x, var int: result); predicate gecode_product_int_reif(array[int] of var int: x, var int: result, var bool: b); predicate gecode_product_int_imp(array[int] of var int: x, var int: result, var bool: b); + +% The product has Euclidean residue result: modulus > 0 and 0 <= result < modulus. predicate gecode_product_mod_fixed_fzn(array[int] of var int: x, int: modulus, var int: result); predicate gecode_product_mod_fixed_fzn_reif(array[int] of var int: x, int: modulus, var int: result, var bool: b); predicate gecode_product_mod_fixed_fzn_imp(array[int] of var int: x, int: modulus, var int: result, var bool: b); predicate gecode_product_mod_var_fzn(array[int] of var int: x, var int: modulus, var int: result); predicate gecode_product_mod_var_fzn_reif(array[int] of var int: x, var int: modulus, var int: result, var bool: b); predicate gecode_product_mod_var_fzn_imp(array[int] of var int: x, var int: modulus, var int: result, var bool: b); + +% sites use zero-based labels; distances is a row-major size-by-size matrix. predicate gecode_minimum_distance_fzn(array[int] of var int: sites, int: size, array[int] of int: distances, var int: result); + +% requirements is a row-major square matrix of pairwise lower bounds, +% with one row per entry in sites. predicate gecode_minimum_distance_required_fzn(array[int] of var int: sites, int: size, array[int] of int: distances, var int: result, array[int] of int: requirements); +% x = y*quotient+remainder, with quotient truncated toward zero and y != 0. predicate gecode_int_divmod(var int: x, var int: y, var int: quotient, var int: remainder); +% y is floor(root(x,n)); n is positive, and negative x requires odd n. predicate gecode_int_nroot(var int: x, int: n, var int: y); +/** @group gecode.constraints + Constrain \a x to be accepted by a deterministic automaton with states 1..\a q. + The row-major \a transitions array has \a q rows and two columns, for false + and true. A zero transition rejects; \a initial and \a final specify the + initial state and accepting states. +*/ predicate gecode_regular_bool(array[int] of var bool: x, int: q, array[int] of int: transitions, int: initial, set of int: final); +% NFA rows are states 1..q, columns are first_symbol..first_symbol+symbols-1. +% Each entry gives the set of target states; an empty set rejects. predicate gecode_regular_nfa_int_fzn(array[int] of var int: x, int: q, int: first_symbol, int: symbols, array[int] of set of int: transitions, int: initial, set of int: final); @@ -128,9 +173,11 @@ predicate gecode_regular_nfa_bool_fzn(array[int] of var bool: x, int: q, int: first_symbol, int: symbols, array[int] of set of int: transitions, int: initial, set of int: final); +% Node labels start at offset; the final successor is offset+length(x). predicate gecode_path(int: offset, array[int] of var int: x, var int: start, var int: end); +% costs is a row-major square matrix; the final successor edge costs zero. predicate gecode_path_cost(array[int] of int: costs, int: offset, array[int] of var int: x, var int: start, var int: end, var int: cost); @@ -138,20 +185,36 @@ predicate gecode_path_cost_array(array[int] of int: costs, int: offset, array[int] of var int: x, var int: start, var int: end, array[int] of var int: edge_costs, var int: cost); -/** @group gecode.constraints Order two tasks without overlap. When order is - false, start0+duration0 <= start1; when true, start1+duration1 <= start0. - Durations are nonnegative. */ +/** @group gecode.constraints + Constrain two tasks not to overlap. If \a order is false, + \a start0 + \a duration0 <= \a start1; otherwise, + \a start1 + \a duration1 <= \a start0. + Durations are nonnegative. +*/ predicate gecode_order(var int: start0, int: duration0, var int: start1, int: duration1, var bool: order); +/** @group gecode.constraints + Constrain tasks whose \a present entry is true not to overlap. Durations are + nonnegative. The arrays have equal lengths. The caller must also constrain + \a start[i] + \a duration[i] = \a end[i] for each present task. +*/ predicate gecode_unary_flex_optional(array[int] of var int: start, array[int] of var int: duration, array[int] of var int: end, array[int] of var bool: present); +/** @group gecode.constraints + Constrain the total \a usage of tasks whose \a present entry is true not to + exceed \a capacity. Durations, usage, and capacity are nonnegative. + The arrays have equal lengths. The caller must also constrain + \a start[i] + \a duration[i] = \a end[i] for each present task. +*/ predicate gecode_cumulative_flex_optional(array[int] of var int: start, array[int] of var int: duration, array[int] of var int: end, array[int] of int: usage, var int: capacity, array[int] of var bool: present); +% Task codes: 1 has fixed duration, 2 fixed start, 3 fixed end. +% flex/fix mean start/duration, end/start, and start/end, respectively. predicate gecode_unary_typed_fzn(array[int] of int: task_type, array[int] of var int: flex, array[int] of int: fix); @@ -167,54 +230,96 @@ predicate gecode_cumulative_typed_optional_fzn(array[int] of int: task_type, array[int] of var int: flex, array[int] of int: fix, array[int] of int: usage, var int: capacity, array[int] of var bool: present); +% Present rectangles have nonnegative fixed widths and heights and cannot overlap. predicate gecode_nooverlap_optional(array[int] of var int: x, array[int] of int: width, array[int] of var int: y, array[int] of int: height, array[int] of var bool: present); +% Present rectangles cannot overlap. The caller must also constrain +% x[i]+width[i]=right[i] and y[i]+height[i]=top[i] for present rectangles. predicate gecode_nooverlap_flex_optional(array[int] of var int: x, array[int] of var int: width, array[int] of var int: right, array[int] of var int: y, array[int] of var int: height, array[int] of var int: top, array[int] of var bool: present); +% hull is empty when x is empty, and min(x)..max(x) otherwise. predicate gecode_set_convex_hull(var set of int: x, var set of int: hull); +% x is empty or contains every integer between its minimum and maximum. predicate gecode_set_convex(var set of int: x); -/** @group gecode.constraints Every set has the given nonnegative cardinality, - and every pair of sets intersects in at most one element. */ +/** @group gecode.constraints + Constrain each set in \a x to have \a cardinality elements and each pair of + sets to intersect in at most one element. The cardinality is nonnegative. +*/ predicate gecode_set_atmost_one(array[int] of var set of int: x, int: cardinality); -/** @group gecode.constraints x is a strictly increasing enumeration of y. */ +/** @group gecode.constraints + Constrain \a x to list the elements of \a y in strictly increasing order. +*/ predicate gecode_set_channel_sorted(array[int] of var int: x, var set of int: y); predicate gecode_set_operation_fzn(var set of int: x, int: operation, var set of int: y, int: relation, var set of int: z); +/** @group gecode.constraints + Constrain \a y to be the intersection of all sets in \a x. + The intersection of an empty array is the full Gecode set universe. +*/ predicate gecode_array_set_intersect(array[int] of var set of int: x, var set of int: y); predicate gecode_array_int_set_operation_fzn(array[int] of var int: x, int: operation, set of int: initial, var set of int: y); +/** @group gecode.constraints + Constrain \a value to be the set at (\a column, \a row) in the row-major + \a x matrix. Coordinates are zero-based; \a width and \a height are positive + and their product equals the length of \a x. +*/ predicate gecode_set_element2d(array[int] of var set of int: x, var int: column, int: width, var int: row, int: height, var set of int: value); +% selected contains zero-based positions; combine the singleton sets {x[i]}. +% An empty intersection equals universe; an empty union is empty. predicate gecode_array_int_set_element_fzn(array[int] of var int: x, int: operation, var set of int: selected, var set of int: value, set of int: universe); +/** @group gecode.constraints + Constrain \a y to be \a x raised to the nonnegative integer \a n. +*/ predicate gecode_float_pow(var float: x, int: n, var float: y); +/** @group gecode.constraints + Constrain \a y to be the n-th root of \a x. The degree \a n is positive. + Both \a x and \a y are nonnegative, including for odd degrees. +*/ predicate gecode_float_nroot(var float: x, int: n, var float: y); +/** @group gecode.constraints + Constrain \a y to be the minimum of the nonempty array \a x. +*/ predicate gecode_float_array_min(array[int] of var float: x, var float: y); +/** @group gecode.constraints + Constrain \a y to be the maximum of the nonempty array \a x. +*/ predicate gecode_float_array_max(array[int] of var float: x, var float: y); +/** @group gecode.constraints + Constrain \a y to be the positive fixed \a base raised to \a x. Requires MPFR. +*/ predicate gecode_float_pow_base(float: base, var float: x, var float: y); +/** @group gecode.constraints + Constrain \a y to be the logarithm of \a x to the fixed \a base. + The base is positive and differs from one; \a x is positive. Requires MPFR. +*/ predicate gecode_float_log_base(float: base, var float: x, var float: y); +% index is the zero-based position plus offset of an extremum. +% If break_ties is true, choose the first such position. Arrays are nonempty. predicate gecode_arg_min_int(array[int] of var int: x, int: offset, var int: index, bool: break_ties); @@ -227,18 +332,21 @@ predicate gecode_arg_min_bool(array[int] of var bool: x, int: offset, predicate gecode_arg_max_bool(array[int] of var bool: x, int: offset, var int: index, bool: break_ties); +% x is the minimum of the nonempty set s; reified forms are false for empty s. predicate gecode_set_min(var set of int: s, var int: x); predicate gecode_set_min_reif(var set of int: s, var int: x, var bool: b); predicate gecode_set_min_imp(var set of int: s, var int: x, var bool: b); +% x is the maximum of the nonempty set s; reified forms are false for empty s. predicate gecode_set_max(var set of int: s, var int: x); predicate gecode_set_max_reif(var set of int: s, var int: x, var bool: b); predicate gecode_set_max_imp(var set of int: s, var int: x, var bool: b); +% Compare s with the singleton set {x}. predicate gecode_set_singleton_rel_fzn(var set of int: s, int: relation, var int: x); @@ -248,10 +356,12 @@ predicate gecode_set_singleton_rel_fzn_reif(var set of int: s, int: relation, predicate gecode_set_singleton_rel_fzn_imp(var set of int: s, int: relation, var int: x, var bool: b); +% Every element precedes all elements in later nonempty sets; sets are disjoint. predicate gecode_array_set_seq(array[int] of var set of int: x); predicate gecode_array_set_seq_union(array[int] of var set of int: x, var set of int: y); predicate gecode_float_rel_imp_fzn(var float: x, int: relation, var float: y, var bool: b); predicate gecode_set_rel_imp_fzn(var set of int: x, int: relation, var set of int: y, var bool: b); +% x equals 1.0 when b is true, and 0.0 otherwise. predicate gecode_bool_float_channel(var bool: b, var float: x); From 27b84bb6a065ce385afef3940f794257bf03733a Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 22:04:10 +0200 Subject: [PATCH 12/22] Keep constraint implementation comments focused on behavior --- .../flatzinc/mznlib/fzn_array_set_intersect.mzn | 2 +- gecode/flatzinc/mznlib/fzn_count_geq.mzn | 2 +- gecode/flatzinc/mznlib/fzn_count_gt.mzn | 2 +- gecode/flatzinc/mznlib/fzn_count_leq.mzn | 2 +- gecode/flatzinc/mznlib/fzn_count_lt.mzn | 2 +- gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn | 3 +-- .../flatzinc/mznlib/fzn_if_then_else_float.mzn | 3 +-- gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn | 3 +-- gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn | 3 +-- .../mznlib/fzn_if_then_else_var_bool.mzn | 3 +-- .../mznlib/fzn_if_then_else_var_float.mzn | 3 +-- .../flatzinc/mznlib/fzn_if_then_else_var_int.mzn | 3 +-- .../flatzinc/mznlib/fzn_if_then_else_var_set.mzn | 3 +-- gecode/flatzinc/mznlib/redefinitions-2.0.mzn | 2 +- gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn | 9 ++++----- gecode/flatzinc/mznlib/redefinitions.mzn | 6 +++--- .../flatzinc/mznlib/solver-native-overloads.mzn | 16 ++++++---------- gecode/flatzinc/registry.cpp | 15 ++++++++------- gecode/float/transcendental/exp-log.hpp | 9 ++++----- test/flatzinc/native-registry.cpp | 2 +- test/float/transcendental.cpp | 2 +- 21 files changed, 41 insertions(+), 54 deletions(-) diff --git a/gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn b/gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn index 5c5de79cfc..3ea2b574b3 100644 --- a/gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn +++ b/gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn @@ -33,7 +33,7 @@ predicate gecode_array_set_intersect(array[int] of var set of int: x, var set of int: y); -% An empty array has no bounded intersection, matching the standard library. +% The intersection of an empty array is undefined in the standard library. predicate fzn_array_set_intersect(array[int] of var set of int: x, var set of int: y) = if length(x) = 0 then false else gecode_array_set_intersect(x, y) endif; diff --git a/gecode/flatzinc/mznlib/fzn_count_geq.mzn b/gecode/flatzinc/mznlib/fzn_count_geq.mzn index 2bd5e7af63..1c1aa28fb2 100644 --- a/gecode/flatzinc/mznlib/fzn_count_geq.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_geq.mzn @@ -33,6 +33,6 @@ predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, int: relation, var int: n); -% Standard count_geq compares n against the count, not vice versa. +% Requires n >= the number of occurrences of value in xs. predicate fzn_count_geq(array[int] of var int: xs, var int: value, var int: n) = gecode_count_int_fzn(xs, value, 3, n); diff --git a/gecode/flatzinc/mznlib/fzn_count_gt.mzn b/gecode/flatzinc/mznlib/fzn_count_gt.mzn index 2972d7f9ae..bbbf17d95c 100644 --- a/gecode/flatzinc/mznlib/fzn_count_gt.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_gt.mzn @@ -33,6 +33,6 @@ predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, int: relation, var int: n); -% Standard count_gt compares n against the count, not vice versa. +% Requires n > the number of occurrences of value in xs. predicate fzn_count_gt(array[int] of var int: xs, var int: value, var int: n) = gecode_count_int_fzn(xs, value, 4, n); diff --git a/gecode/flatzinc/mznlib/fzn_count_leq.mzn b/gecode/flatzinc/mznlib/fzn_count_leq.mzn index 1237bb0cce..cfec6f2e49 100644 --- a/gecode/flatzinc/mznlib/fzn_count_leq.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_leq.mzn @@ -33,6 +33,6 @@ predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, int: relation, var int: n); -% Standard count_leq compares n against the count, not vice versa. +% Requires n <= the number of occurrences of value in xs. predicate fzn_count_leq(array[int] of var int: xs, var int: value, var int: n) = gecode_count_int_fzn(xs, value, 5, n); diff --git a/gecode/flatzinc/mznlib/fzn_count_lt.mzn b/gecode/flatzinc/mznlib/fzn_count_lt.mzn index a440de3290..0cfe426145 100644 --- a/gecode/flatzinc/mznlib/fzn_count_lt.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_lt.mzn @@ -33,6 +33,6 @@ predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, int: relation, var int: n); -% Standard count_lt compares n against the count, not vice versa. +% Requires n < the number of occurrences of value in xs. predicate fzn_count_lt(array[int] of var int: xs, var int: value, var int: n) = gecode_count_int_fzn(xs, value, 6, n); diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn index 4dadafaa32..b175fdd082 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn @@ -33,8 +33,7 @@ predicate gecode_bool_ite(var bool: b, var bool: x, var bool: y, var bool: z); -% The compiler appends a true condition for the else branch. Use the native -% binary conditional for that shape, keeping the standard general translation. +% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_bool(array[int] of var bool: c, array[int] of bool: x, var bool: y) = if length(c) = 2 /\ index_sets_agree(c,x) /\ diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn index 176e0ff9d5..8ac500e1fd 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn @@ -33,8 +33,7 @@ predicate gecode_float_ite(var bool: b, var float: x, var float: y, var float: z); -% The compiler appends a true condition for the else branch. Use the native -% binary conditional for that shape, keeping the standard general translation. +% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_float(array[int] of var bool: c, array[int] of float: x, var float: y) = if length(c) = 2 /\ index_sets_agree(c,x) /\ diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn index a0658ede60..7a2f3a59d1 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn @@ -33,8 +33,7 @@ predicate gecode_int_ite(var bool: b, var int: x, var int: y, var int: z); -% The compiler appends a true condition for the else branch. Use the native -% binary conditional for that shape, keeping the standard general translation. +% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_int(array[int] of var bool: c, array[int] of int: x, var int: y) = if length(c) = 2 /\ index_sets_agree(c,x) /\ diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn index 041577efc7..7fa63172c1 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn @@ -33,8 +33,7 @@ predicate gecode_set_ite(var bool: b, var set of int: x, var set of int: y, var set of int: z); -% The compiler appends a true condition for the else branch. Use the native -% binary conditional for that shape, keeping the standard general translation. +% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_set(array[int] of var bool: c, array[int] of set of int: x, var set of int: y) = if length(c) = 2 /\ index_sets_agree(c,x) /\ diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn index c582890ddc..cf4764b2fa 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn @@ -33,8 +33,7 @@ predicate gecode_bool_ite(var bool: b, var bool: x, var bool: y, var bool: z); -% The compiler appends a true condition for the else branch. Use the native -% binary conditional for that shape, keeping the standard general translation. +% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_var_bool(array[int] of var bool: c, array[int] of var bool: x, var bool: y) = if length(c) = 2 /\ index_sets_agree(c,x) /\ diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn index a6645031fd..7de894b8f0 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn @@ -33,8 +33,7 @@ predicate gecode_float_ite(var bool: b, var float: x, var float: y, var float: z); -% The compiler appends a true condition for the else branch. Use the native -% binary conditional for that shape, keeping the standard general translation. +% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_var_float(array[int] of var bool: c, array[int] of var float: x, var float: y) = if length(c) = 2 /\ index_sets_agree(c,x) /\ diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn index 331e6b6e4c..d1fc8e6a4a 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn @@ -33,8 +33,7 @@ predicate gecode_int_ite(var bool: b, var int: x, var int: y, var int: z); -% The compiler appends a true condition for the else branch. Use the native -% binary conditional for that shape, keeping the standard general translation. +% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_var_int(array[int] of var bool: c, array[int] of var int: x, var int: y) = if length(c) = 2 /\ index_sets_agree(c,x) /\ diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn index a471e4de2a..5797e4e4b9 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn @@ -33,8 +33,7 @@ predicate gecode_set_ite(var bool: b, var set of int: x, var set of int: y, var set of int: z); -% The compiler appends a true condition for the else branch. Use the native -% binary conditional for that shape, keeping the standard general translation. +% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_var_set(array[int] of var bool: c, array[int] of var set of int: x, var set of int: y) = if length(c) = 2 /\ index_sets_agree(c,x) /\ diff --git a/gecode/flatzinc/mznlib/redefinitions-2.0.mzn b/gecode/flatzinc/mznlib/redefinitions-2.0.mzn index a7aff30686..4159aa45a4 100644 --- a/gecode/flatzinc/mznlib/redefinitions-2.0.mzn +++ b/gecode/flatzinc/mznlib/redefinitions-2.0.mzn @@ -22,7 +22,7 @@ predicate array_float_minimum(var float: m, array[int] of var float: x) = predicate gecode_float_pow(var float: x, int: n, var float: y); predicate gecode_float_pow_base(float: base, var float: x, var float: y); -% Integer powers work for negative and zero bases as well as positive bases. +% Nonnegative integer exponents permit zero or negative bases. predicate float_pow(var float: x, var float: exponent, var float: y) = let { float: n = if is_fixed(exponent) then fix(exponent) else -1.0 endif; diff --git a/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn b/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn index 6e3426ce7b..17030a1106 100644 --- a/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn +++ b/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn @@ -30,9 +30,8 @@ % % -% Solver overrides for variable-set extrema, following the standard -% redefinitions-2.1.1.mzn template. Partial contexts permit empty sets when -% the enclosing expression is inactive. +% Set extrema are undefined for empty sets. The partial functions guard the +% extremum constraint with set nonemptiness, for use in inactive expressions. predicate gecode_set_min(var set of int: s, var int: m); predicate gecode_set_min_reif(var set of int: s, var int: m, var bool: b); predicate gecode_set_min_imp(var set of int: s, var int: m, var bool: b); @@ -42,7 +41,7 @@ predicate gecode_set_max_imp(var set of int: s, var int: m, var bool: b); function var $$E: min(var set of $$E: s) = if card(ub(enum2int(s))) = 0 then - % Preserve the standard error for a statically empty set bound. + % An empty upper bound makes the extremum undefined at compilation. let { var set of int: si = enum2int(s); var min(ub(si))..max(ub(si)): m = @@ -73,7 +72,7 @@ function var int: gecode_set_min_partial(var set of int: s) :: promise_total = function var $$E: max(var set of $$E: s) = if card(ub(enum2int(s))) = 0 then - % Preserve the standard error for a statically empty set bound. + % An empty upper bound makes the extremum undefined at compilation. let { var set of int: si = enum2int(s); var min(ub(si))..max(ub(si)): m = diff --git a/gecode/flatzinc/mznlib/redefinitions.mzn b/gecode/flatzinc/mznlib/redefinitions.mzn index 84037e1eb5..792f5d9999 100644 --- a/gecode/flatzinc/mznlib/redefinitions.mzn +++ b/gecode/flatzinc/mznlib/redefinitions.mzn @@ -139,7 +139,7 @@ predicate int_lin_ne_imp(array [int] of int: as, array [int] of var int: bs, predicate set_in_imp(var int: x, set of int: S, var bool: r); -% Half-reified scalar relations use the native implication propagators. +% b implies the scalar float relation; a false b leaves x and y unconstrained. predicate gecode_float_rel_imp_fzn(var float: x, int: relation, var float: y, var bool: b); predicate float_eq_imp(var float: x, var float: y, var bool: b) = @@ -151,8 +151,8 @@ predicate float_le_imp(var float: x, var float: y, var bool: b) = predicate float_lt_imp(var float: x, var float: y, var bool: b) = gecode_float_rel_imp_fzn(x, 4, y, b); -% MiniZinc normalizes scalar inequalities to one- or two-term linear forms. -% Recover those scalar forms; other linear expressions keep full reification. +% MiniZinc represents scalar comparisons as one- or two-term linear forms. +% Other linear expressions use full reification followed by implication. predicate gecode_float_lin_scalar_imp(array[int] of float: a, array[int] of var float: x, float: y, int: relation, var bool: b) = let { diff --git a/gecode/flatzinc/mznlib/solver-native-overloads.mzn b/gecode/flatzinc/mznlib/solver-native-overloads.mzn index b4eef04b48..606ee5a6a2 100644 --- a/gecode/flatzinc/mznlib/solver-native-overloads.mzn +++ b/gecode/flatzinc/mznlib/solver-native-overloads.mzn @@ -36,8 +36,8 @@ include "regular_nfa.mzn"; include "nvalue_fn.mzn"; include "gecode_fzn.mzn"; -% A more specific index type overrides the standard recursive product for -% decision variables while leaving parameter products on the builtin route. +% This index-type overload leaves parameter arrays on the builtin product. +% Root constraints use a result variable for the native product. function var int: product(array[$$X] of var int: x) :: promise_commutative = if mzn_in_root_context() then let { @@ -45,12 +45,10 @@ function var int: product(array[$$X] of var int: x) :: promise_commutative = constraint gecode_product_int(array1d(x),result) :: defines_var(result); } in result else - % Retain the standard translation where a free result cannot be introduced. product_rec(array1d(x)) endif; -% Typed standard operators preserve MiniZinc's index-set checks and use the -% non-reified native actor only in a positive root context. +% The array disequality propagator supports unreified constraints only. function var bool: '!='(array[$U] of var $$E: x, array[$U] of var $$E: y) :: promise_commutative = matching_index_sets(x,y) /\ @@ -94,9 +92,8 @@ function var int: nvalue(array[$X] of var bool: xs) :: promise_total = constraint gecode_nvalues_bool_fzn(array1d(xs),1,n) :: defines_var(n); } in n; -% Boolean regular uses symbols 0 and 1. Symbols missing from the standard -% alphabet have dead transitions. The normalized alphabet also lets the -% standard reified translation represent an out-of-alphabet Boolean as false. +% Boolean symbols are 0 and 1. Missing symbols lead to state 0, so they +% reject the word in both unreified and reified constraints. predicate regular(array[int] of var bool: xs, int: Q, set of int: S, array[int,int] of int: d, int: q0, set of int: F) = assert(Q > 0, "regular: 'Q' must be greater than zero") /\ @@ -120,8 +117,7 @@ predicate regular(array[int] of var bool: xs, int: Q, int: S, assert(S > 0, "regular: 'S' must be greater than zero") /\ regular(xs,Q,1..S,d,q0,F); -% Boolean NFA symbols missing from the standard alphabet have no transitions. -% Standard regular_nfa does not support reification; retain that restriction. +% Missing Boolean symbols have no transitions. Reification is unsupported. predicate regular_nfa(array[int] of var bool: x, int: q, set of int: symbols, array[int,int] of set of int: transitions, int: initial, set of int: final) = assert(q > 0 /\ card(symbols) > 0 /\ symbols = min(symbols)..max(symbols), diff --git a/gecode/flatzinc/registry.cpp b/gecode/flatzinc/registry.cpp index d0b0e19546..eaae2f9dbe 100755 --- a/gecode/flatzinc/registry.cpp +++ b/gecode/flatzinc/registry.cpp @@ -1841,7 +1841,7 @@ fvar, void p_inter_distance(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { IntVarArgs x = s.arg2intvarargs(ce[0]); if (ce[1]->isIntVar()) { - // Copy shared occurrences, including the separation variable. + // The native propagator rejects aliases between x and the distance. x << s.arg2IntVar(ce[1]); unshare(s, x); inter_distance(s, x.slice(0,1,x.size()-1), x[x.size()-1], @@ -1868,6 +1868,7 @@ fvar, } if (n < 2) return; + // Native optional distinct needs unused values outside the active domains. if ((hi < Int::Limits::max-n) || (lo > Int::Limits::min+n)) { unshare(s, x); distinct(s, present, x, s.ann2ipl(ann)); @@ -1887,7 +1888,7 @@ fvar, void p_distinct_except(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { IntVarArgs x = s.arg2intvarargs(ce[0]); int c = ce[1]->getInt(); - // FlatZinc constants can exceed Gecode's variable bounds. + // An excluded value outside the variable limits cannot occur in x. if ((c < Int::Limits::min) || (c > Int::Limits::max)) { unshare(s, x); distinct(s, x, s.ann2ipl(ann)); @@ -1902,6 +1903,7 @@ fvar, } if (n < 2) return; + // The native propagator needs unused values outside the active domains. if ((hi < Int::Limits::max-n) || (lo > Int::Limits::min+n)) { unshare(s, x); distinct(s, x, c, s.ann2ipl(ann)); @@ -2041,9 +2043,8 @@ fvar, void p_int_divmod(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { IntVar x = s.arg2IntVar(ce[0]), y = s.arg2IntVar(ce[1]); IntVar q = s.arg2IntVar(ce[2]), r = s.arg2IntVar(ce[3]); - // Bound unrestricted results before the native linear decomposition - // estimates its intermediate domains. Truncating division gives - // |q| <= |x|, and r lies between zero and x, for every nonzero y. + // Bound the results before the linear decomposition estimates its ranges. + // For nonzero y, truncating division gives |q| <= |x| and r between 0 and x. int magnitude = std::max(-x.min(),x.max()); dom(s,q,-magnitude,magnitude); dom(s,r,std::min(0,x.min()),std::max(0,x.max())); @@ -2754,8 +2755,8 @@ fvar, throw FlatZinc::Error("Registry", "set cardinality must be nonnegative"); SetVarArgs x = s.arg2setvarargs(ce[0]); if ((x.size() < 2) || (cardinality <= 1)) { - // Pairwise intersections are automatically at most one in this case. - // The native atmostOne implementation assumes at least two elements. + // The intersection bound is trivial here. atmostOne skips cardinality + // for fewer than two sets, and its filter divides by cardinality - 1. for (int i=0; i::digits); mpfr_init2(y, std::numeric_limits::digits); mpfr_init2(q, std::numeric_limits::digits); - // The base is positive and different from one, so log(base) has a fixed - // nonzero sign. All endpoint quotients enclose either increasing or - // decreasing logarithms without hardware-rounded interval division. + // log(base) does not contain zero. Divide all four endpoint pairs with + // outward rounding, including when 0 < base < 1. for (int i=0; i<2; i++) { mpfr_set_d(x, n[i], GMP_RNDN); for (int j=0; j<2; j++) { diff --git a/test/flatzinc/native-registry.cpp b/test/flatzinc/native-registry.cpp index 66f0ad2cb4..f245737cc9 100644 --- a/test/flatzinc/native-registry.cpp +++ b/test/flatzinc/native-registry.cpp @@ -36,7 +36,7 @@ namespace Test { namespace FlatZinc { namespace { - // The algorithm annotation must select the native filtering stage. + // Basic and advanced annotations must select different filtering stages. class AlgorithmAnnotations : public Base { public: AlgorithmAnnotations(void) : Base("FlatZinc::NativeRegistry::AlgorithmAnnotations") {} diff --git a/test/float/transcendental.cpp b/test/float/transcendental.cpp index 1864f84dfc..5c14917af8 100644 --- a/test/float/transcendental.cpp +++ b/test/float/transcendental.cpp @@ -88,7 +88,7 @@ namespace Test { namespace Float { virtual bool run(void) { for (int delayed=0; delayed<2; delayed++) { for (int logarithm=0; logarithm<2; logarithm++) { - // Exact binary values, independent of exp/log test oracles. + // Exact powers avoid checking exp/log against itself. if (!check(2.0,3.0,8.0,logarithm,false,delayed,true) || !check(2.0,3.0,7.0,logarithm,false,delayed,false) || !check(0.5,3.0,0.125,logarithm,false,delayed,true) || From c55d360f963016b908f3737b7fc904db0af5b884 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 22:21:50 +0200 Subject: [PATCH 13/22] Format MiniZinc registry declarations and predicate bodies --- .../mznlib/fzn_all_different_int_opt.mzn | 28 +- .../mznlib/fzn_all_equal_int_reif.mzn | 14 +- .../mznlib/fzn_alldifferent_except.mzn | 12 +- .../mznlib/fzn_array_set_intersect.mzn | 18 +- gecode/flatzinc/mznlib/fzn_count_geq.mzn | 8 +- gecode/flatzinc/mznlib/fzn_count_gt.mzn | 8 +- gecode/flatzinc/mznlib/fzn_count_leq.mzn | 8 +- gecode/flatzinc/mznlib/fzn_count_lt.mzn | 8 +- gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn | 98 +-- .../mznlib/fzn_disjunctive_strict_opt.mzn | 59 +- .../flatzinc/mznlib/fzn_if_then_else_bool.mzn | 39 +- .../mznlib/fzn_if_then_else_float.mzn | 39 +- .../flatzinc/mznlib/fzn_if_then_else_int.mzn | 39 +- .../flatzinc/mznlib/fzn_if_then_else_set.mzn | 39 +- .../mznlib/fzn_if_then_else_var_bool.mzn | 39 +- .../mznlib/fzn_if_then_else_var_float.mzn | 39 +- .../mznlib/fzn_if_then_else_var_int.mzn | 39 +- .../mznlib/fzn_if_then_else_var_set.mzn | 39 +- gecode/flatzinc/mznlib/fzn_regular_nfa.mzn | 28 +- .../flatzinc/mznlib/fzn_regular_nfa_set.mzn | 37 +- gecode/flatzinc/mznlib/gecode.mzn | 636 +++++++++++++----- gecode/flatzinc/mznlib/gecode_fzn.mzn | 377 ++++++++--- gecode/flatzinc/mznlib/redefinitions-2.0.mzn | 35 +- .../flatzinc/mznlib/redefinitions-2.1.1.mzn | 44 +- .../flatzinc/mznlib/redefinitions-2.5.2.mzn | 33 +- gecode/flatzinc/mznlib/redefinitions.mzn | 92 ++- .../mznlib/solver-native-overloads.mzn | 217 ++++-- 27 files changed, 1474 insertions(+), 598 deletions(-) diff --git a/gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn b/gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn index c52a7e53b1..105555a209 100644 --- a/gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn +++ b/gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn @@ -30,8 +30,10 @@ % % -predicate gecode_all_different_optional(array[int] of var int: x, - array[int] of var bool: present); +predicate gecode_all_different_optional( + array[int] of var int: x, + array[int] of var bool: present, +); predicate fzn_all_different_int_opt(array[int] of var opt int: xs) = if length(xs) = 0 then @@ -40,15 +42,21 @@ predicate fzn_all_different_int_opt(array[int] of var opt int: xs) = % Fixed absent values have no deopt value; their integer is ignored. gecode_all_different_optional( [if is_fixed(x) /\ absent(fix(x)) then 0 else deopt(x) endif | x in xs], - [occurs(x) | x in xs]) + [occurs(x) | x in xs], + ) endif; % Reification has no native optional-distinct propagator. -predicate fzn_all_different_int_opt_reif(array[int] of var opt int: xs, - var bool: b) = +predicate fzn_all_different_int_opt_reif( + array[int] of var opt int: xs, + var bool: b, +) = let { - array[index_set(xs)] of var int: values = - array1d(index_set(xs), - [if is_fixed(x) /\ absent(fix(x)) then 0 else deopt(x) endif | x in xs]); - } in b <-> forall (i, j in index_set(xs) where i < j) ( - (occurs(xs[i]) /\ occurs(xs[j])) -> values[i] != values[j]); + array[index_set(xs)] of var int: values = array1d( + index_set(xs), + [if is_fixed(x) /\ absent(fix(x)) then 0 else deopt(x) endif | x in xs], + ); + } in + b <-> forall (i, j in index_set(xs) where i < j) ( + (occurs(xs[i]) /\ occurs(xs[j])) -> values[i] != values[j] + ); diff --git a/gecode/flatzinc/mznlib/fzn_all_equal_int_reif.mzn b/gecode/flatzinc/mznlib/fzn_all_equal_int_reif.mzn index 69e77ec91e..714301f48b 100644 --- a/gecode/flatzinc/mznlib/fzn_all_equal_int_reif.mzn +++ b/gecode/flatzinc/mznlib/fzn_all_equal_int_reif.mzn @@ -33,6 +33,14 @@ predicate gecode_not_all_equal_int(array[int] of var int: xs); predicate fzn_all_equal_int_reif(array[int] of var int: xs, var bool: b) = - if is_fixed(b) /\ not fix(b) then gecode_not_all_equal_int(xs) - else b <-> (if length(xs) = 0 then true else - forall(i in index_set(xs))(xs[i] = xs[min(index_set(xs))]) endif) endif; + if is_fixed(b) /\ not fix(b) then + gecode_not_all_equal_int(xs) + else + b <-> (if length(xs) = 0 then + true + else + forall (i in index_set(xs)) ( + xs[i] = xs[min(index_set(xs))] + ) + endif) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn b/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn index 70127eff08..26b0355c29 100644 --- a/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn +++ b/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn @@ -30,8 +30,8 @@ % % -include "global_cardinality_low_up.mzn"; include "fzn_all_different_int.mzn"; +include "global_cardinality_low_up.mzn"; predicate gecode_all_different_except(array[int] of var int: x, int: except_value); @@ -45,13 +45,17 @@ predicate fzn_alldifferent_except(array[int] of var int: xs, set of int: except) else gecode_all_different_except(xs, min(except)) endif - elseif forall (i in index_set(xs)) (has_bounds(xs[i])) then + elseif forall (i in index_set(xs)) ( + has_bounds(xs[i]) + ) then % Native distinct supports one exception; retain the standard translation % for larger exception sets. let { set of int: values = dom_array(xs) diff except; - } in global_cardinality(xs, set2array(values), [0 | i in values], [1 | i in values]) + } in + global_cardinality(xs, set2array(values), [0 | i in values], [1 | i in values]) else forall (i, j in index_set(xs) where i < j) ( - (xs[i] in except /\ xs[j] in except) \/ xs[i] != xs[j]) + (xs[i] in except /\ xs[j] in except) \/ xs[i] != xs[j] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn b/gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn index 3ea2b574b3..441c0b52cd 100644 --- a/gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn +++ b/gecode/flatzinc/mznlib/fzn_array_set_intersect.mzn @@ -30,10 +30,18 @@ % % -predicate gecode_array_set_intersect(array[int] of var set of int: x, - var set of int: y); +predicate gecode_array_set_intersect( + array[int] of var set of int: x, + var set of int: y, +); % The intersection of an empty array is undefined in the standard library. -predicate fzn_array_set_intersect(array[int] of var set of int: x, - var set of int: y) = - if length(x) = 0 then false else gecode_array_set_intersect(x, y) endif; +predicate fzn_array_set_intersect( + array[int] of var set of int: x, + var set of int: y, +) = + if length(x) = 0 then + false + else + gecode_array_set_intersect(x, y) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_count_geq.mzn b/gecode/flatzinc/mznlib/fzn_count_geq.mzn index 1c1aa28fb2..a524b0055c 100644 --- a/gecode/flatzinc/mznlib/fzn_count_geq.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_geq.mzn @@ -30,8 +30,12 @@ % % -predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, - int: relation, var int: n); +predicate gecode_count_int_fzn( + array[int] of var int: x, + var int: value, + int: relation, + var int: n, +); % Requires n >= the number of occurrences of value in xs. predicate fzn_count_geq(array[int] of var int: xs, var int: value, var int: n) = diff --git a/gecode/flatzinc/mznlib/fzn_count_gt.mzn b/gecode/flatzinc/mznlib/fzn_count_gt.mzn index bbbf17d95c..aa03bf5b8b 100644 --- a/gecode/flatzinc/mznlib/fzn_count_gt.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_gt.mzn @@ -30,8 +30,12 @@ % % -predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, - int: relation, var int: n); +predicate gecode_count_int_fzn( + array[int] of var int: x, + var int: value, + int: relation, + var int: n, +); % Requires n > the number of occurrences of value in xs. predicate fzn_count_gt(array[int] of var int: xs, var int: value, var int: n) = diff --git a/gecode/flatzinc/mznlib/fzn_count_leq.mzn b/gecode/flatzinc/mznlib/fzn_count_leq.mzn index cfec6f2e49..6268941c11 100644 --- a/gecode/flatzinc/mznlib/fzn_count_leq.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_leq.mzn @@ -30,8 +30,12 @@ % % -predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, - int: relation, var int: n); +predicate gecode_count_int_fzn( + array[int] of var int: x, + var int: value, + int: relation, + var int: n, +); % Requires n <= the number of occurrences of value in xs. predicate fzn_count_leq(array[int] of var int: xs, var int: value, var int: n) = diff --git a/gecode/flatzinc/mznlib/fzn_count_lt.mzn b/gecode/flatzinc/mznlib/fzn_count_lt.mzn index 0cfe426145..fcbfcd2327 100644 --- a/gecode/flatzinc/mznlib/fzn_count_lt.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_lt.mzn @@ -30,8 +30,12 @@ % % -predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, - int: relation, var int: n); +predicate gecode_count_int_fzn( + array[int] of var int: x, + var int: value, + int: relation, + var int: n, +); % Requires n < the number of occurrences of value in xs. predicate fzn_count_lt(array[int] of var int: xs, var int: value, var int: n) = diff --git a/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn b/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn index c795ace0b4..43f0b01bd0 100644 --- a/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn +++ b/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn @@ -34,43 +34,65 @@ include "fzn_cumulative_opt_decomp.mzn"; -predicate gecode_schedule_cumulative_optional(array[int] of var int: start, - array[int] of int: duration, array[int] of int: usage, - array[int] of var bool: m, var int: capacity); +predicate gecode_schedule_cumulative_optional( + array[int] of var int: start, + array[int] of int: duration, + array[int] of int: usage, + array[int] of var bool: m, + var int: capacity, +); -predicate gecode_cumulative_flex_optional(array[int] of var int: start, - array[int] of var int: duration, array[int] of var int: end, - array[int] of int: usage, var int: capacity, array[int] of var bool: present); +predicate gecode_cumulative_flex_optional( + array[int] of var int: start, + array[int] of var int: duration, + array[int] of var int: end, + array[int] of int: usage, + var int: capacity, + array[int] of var bool: present, +); -predicate fzn_cumulative_opt(array[int] of var opt int: s, - array[int] of var int: d, - array[int] of var int: r, var int: b) = - if ub(b) < 0 then - false - elseif - forall (i in index_set(d)) (is_fixed(d[i])) /\ - forall (i in index_set(r)) (is_fixed(r[i])) - then - % Zero-duration tasks consume no capacity in MiniZinc. - gecode_schedule_cumulative_optional( - [ if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s ], - [ fix(di) | di in d ], - [ fix(ri) | ri in r ], - [ occurs(s[i]) /\ fix(d[i]) > 0 | i in index_set(s) ], - b - ) - elseif forall (i in index_set(r)) (is_fixed(r[i])) then - let { - array[int] of var int: start = - [if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s]; - array[int] of var int: duration = array1d(d); - % Zero-duration tasks consume no capacity in MiniZinc. - array[int] of var bool: present = - [occurs(array1d(s)[i]) /\ duration[i] > 0 | i in index_set(duration)]; - array[int] of var int: end = - [if present[i] then start[i] + duration[i] else 0 endif | i in index_set(start)]; - } in gecode_cumulative_flex_optional(start, duration, end, - [fix(ri) | ri in r], b, present) - else - fzn_cumulative_opt_decomp(s,d,r,b) - endif; +predicate fzn_cumulative_opt( + array[int] of var opt int: s, + array[int] of var int: d, + array[int] of var int: r, + var int: b, +) = + if ub(b) < 0 then + false + elseif + forall (i in index_set(d)) ( + is_fixed(d[i]) + ) /\ + forall (i in index_set(r)) ( + is_fixed(r[i]) + ) + then + % Zero-duration tasks consume no capacity in MiniZinc. + gecode_schedule_cumulative_optional( + [if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s], + [fix(di) | di in d], + [fix(ri) | ri in r], + [occurs(s[i]) /\ fix(d[i]) > 0 | i in index_set(s)], + b, + ) + elseif forall (i in index_set(r)) ( + is_fixed(r[i]) + ) then + let { + array[int] of var int: start = [if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s]; + array[int] of var int: duration = array1d(d); + % Zero-duration tasks consume no capacity in MiniZinc. + array[int] of var bool: present = [occurs(array1d(s)[i]) /\ duration[i] > 0 | i in index_set(duration)]; + array[int] of var int: end = [if present[i] then start[i] + duration[i] else 0 endif | i in index_set(start)]; + } in + gecode_cumulative_flex_optional( + start, + duration, + end, + [fix(ri) | ri in r], + b, + present, + ) + else + fzn_cumulative_opt_decomp(s, d, r, b) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn b/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn index 1f083f2e18..2eef516a1e 100644 --- a/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn +++ b/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn @@ -32,29 +32,40 @@ % % -predicate gecode_schedule_unary_optional(array[int] of var int: x, - array[int] of int: p, array[int] of var bool: m); +predicate gecode_schedule_unary_optional( + array[int] of var int: x, + array[int] of int: p, + array[int] of var bool: m, +); -predicate gecode_unary_flex_optional(array[int] of var int: start, - array[int] of var int: duration, array[int] of var int: end, - array[int] of var bool: present); +predicate gecode_unary_flex_optional( + array[int] of var int: start, + array[int] of var int: duration, + array[int] of var int: end, + array[int] of var bool: present, +); -predicate fzn_disjunctive_strict_opt(array[int] of var opt int: s, - array[int] of var int: d) = - forall (i in index_set(d)) (d[i] >= 0) /\ - if forall (i in index_set(d)) (is_fixed(d[i])) then - gecode_schedule_unary_optional( - [ if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s], - [ fix(di) | di in d], - [ occurs(si) | si in s ] - ) - else - let { - array[int] of var int: start = - [if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s]; - array[int] of var bool: present = [occurs(si) | si in s]; - array[int] of var int: duration = array1d(d); - array[int] of var int: end = - [if present[i] then start[i] + duration[i] else 0 endif | i in index_set(start)]; - } in gecode_unary_flex_optional(start, duration, end, present) - endif; +predicate fzn_disjunctive_strict_opt( + array[int] of var opt int: s, + array[int] of var int: d, +) = + forall (i in index_set(d)) ( + d[i] >= 0 + ) /\ + if forall (i in index_set(d)) ( + is_fixed(d[i]) + ) then + gecode_schedule_unary_optional( + [if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s], + [fix(di) | di in d], + [occurs(si) | si in s], + ) + else + let { + array[int] of var int: start = [if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s]; + array[int] of var bool: present = [occurs(si) | si in s]; + array[int] of var int: duration = array1d(d); + array[int] of var int: end = [if present[i] then start[i] + duration[i] else 0 endif | i in index_set(start)]; + } in + gecode_unary_flex_optional(start, duration, end, present) + endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn index b175fdd082..4913e693d1 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn @@ -30,19 +30,34 @@ % % -predicate gecode_bool_ite(var bool: b, var bool: x, - var bool: y, var bool: z); +predicate gecode_bool_ite( + var bool: b, + var bool: x, + var bool: y, + var bool: z, +); % A two-branch conditional ends with a fixed true condition for else. -predicate fzn_if_then_else_bool(array[int] of var bool: c, - array[int] of bool: x, var bool: y) = - if length(c) = 2 /\ index_sets_agree(c,x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) - then - gecode_bool_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) +predicate fzn_if_then_else_bool( + array[int] of var bool: c, + array[int] of bool: x, + var bool: y, +) = + if length(c) = 2 /\ index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + gecode_bool_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else - let {array[index_set(c)] of var bool: d;} in - forall(i in index_set(c))( - if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ - forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + let { + array[index_set(c)] of var bool: d; + } in + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn index 8ac500e1fd..b92d0484ec 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn @@ -30,19 +30,34 @@ % % -predicate gecode_float_ite(var bool: b, var float: x, - var float: y, var float: z); +predicate gecode_float_ite( + var bool: b, + var float: x, + var float: y, + var float: z, +); % A two-branch conditional ends with a fixed true condition for else. -predicate fzn_if_then_else_float(array[int] of var bool: c, - array[int] of float: x, var float: y) = - if length(c) = 2 /\ index_sets_agree(c,x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) - then - gecode_float_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) +predicate fzn_if_then_else_float( + array[int] of var bool: c, + array[int] of float: x, + var float: y, +) = + if length(c) = 2 /\ index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + gecode_float_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else - let {array[index_set(c)] of var bool: d;} in - forall(i in index_set(c))( - if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ - forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + let { + array[index_set(c)] of var bool: d; + } in + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn index 7a2f3a59d1..db9c5da426 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn @@ -30,19 +30,34 @@ % % -predicate gecode_int_ite(var bool: b, var int: x, - var int: y, var int: z); +predicate gecode_int_ite( + var bool: b, + var int: x, + var int: y, + var int: z, +); % A two-branch conditional ends with a fixed true condition for else. -predicate fzn_if_then_else_int(array[int] of var bool: c, - array[int] of int: x, var int: y) = - if length(c) = 2 /\ index_sets_agree(c,x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) - then - gecode_int_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) +predicate fzn_if_then_else_int( + array[int] of var bool: c, + array[int] of int: x, + var int: y, +) = + if length(c) = 2 /\ index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + gecode_int_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else - let {array[index_set(c)] of var bool: d;} in - forall(i in index_set(c))( - if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ - forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + let { + array[index_set(c)] of var bool: d; + } in + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn index 7fa63172c1..622c77a647 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn @@ -30,19 +30,34 @@ % % -predicate gecode_set_ite(var bool: b, var set of int: x, - var set of int: y, var set of int: z); +predicate gecode_set_ite( + var bool: b, + var set of int: x, + var set of int: y, + var set of int: z, +); % A two-branch conditional ends with a fixed true condition for else. -predicate fzn_if_then_else_set(array[int] of var bool: c, - array[int] of set of int: x, var set of int: y) = - if length(c) = 2 /\ index_sets_agree(c,x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) - then - gecode_set_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) +predicate fzn_if_then_else_set( + array[int] of var bool: c, + array[int] of set of int: x, + var set of int: y, +) = + if length(c) = 2 /\ index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + gecode_set_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else - let {array[index_set(c)] of var bool: d;} in - forall(i in index_set(c))( - if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ - forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + let { + array[index_set(c)] of var bool: d; + } in + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn index cf4764b2fa..19f7bed8a0 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn @@ -30,19 +30,34 @@ % % -predicate gecode_bool_ite(var bool: b, var bool: x, - var bool: y, var bool: z); +predicate gecode_bool_ite( + var bool: b, + var bool: x, + var bool: y, + var bool: z, +); % A two-branch conditional ends with a fixed true condition for else. -predicate fzn_if_then_else_var_bool(array[int] of var bool: c, - array[int] of var bool: x, var bool: y) = - if length(c) = 2 /\ index_sets_agree(c,x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) - then - gecode_bool_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) +predicate fzn_if_then_else_var_bool( + array[int] of var bool: c, + array[int] of var bool: x, + var bool: y, +) = + if length(c) = 2 /\ index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + gecode_bool_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else - let {array[index_set(c)] of var bool: d;} in - forall(i in index_set(c))( - if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ - forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + let { + array[index_set(c)] of var bool: d; + } in + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn index 7de894b8f0..d9c5677933 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn @@ -30,19 +30,34 @@ % % -predicate gecode_float_ite(var bool: b, var float: x, - var float: y, var float: z); +predicate gecode_float_ite( + var bool: b, + var float: x, + var float: y, + var float: z, +); % A two-branch conditional ends with a fixed true condition for else. -predicate fzn_if_then_else_var_float(array[int] of var bool: c, - array[int] of var float: x, var float: y) = - if length(c) = 2 /\ index_sets_agree(c,x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) - then - gecode_float_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) +predicate fzn_if_then_else_var_float( + array[int] of var bool: c, + array[int] of var float: x, + var float: y, +) = + if length(c) = 2 /\ index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + gecode_float_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else - let {array[index_set(c)] of var bool: d;} in - forall(i in index_set(c))( - if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ - forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + let { + array[index_set(c)] of var bool: d; + } in + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn index d1fc8e6a4a..3369aa7aa9 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn @@ -30,19 +30,34 @@ % % -predicate gecode_int_ite(var bool: b, var int: x, - var int: y, var int: z); +predicate gecode_int_ite( + var bool: b, + var int: x, + var int: y, + var int: z, +); % A two-branch conditional ends with a fixed true condition for else. -predicate fzn_if_then_else_var_int(array[int] of var bool: c, - array[int] of var int: x, var int: y) = - if length(c) = 2 /\ index_sets_agree(c,x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) - then - gecode_int_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) +predicate fzn_if_then_else_var_int( + array[int] of var bool: c, + array[int] of var int: x, + var int: y, +) = + if length(c) = 2 /\ index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + gecode_int_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else - let {array[index_set(c)] of var bool: d;} in - forall(i in index_set(c))( - if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ - forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + let { + array[index_set(c)] of var bool: d; + } in + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn index 5797e4e4b9..ab4151ab8f 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn @@ -30,19 +30,34 @@ % % -predicate gecode_set_ite(var bool: b, var set of int: x, - var set of int: y, var set of int: z); +predicate gecode_set_ite( + var bool: b, + var set of int: x, + var set of int: y, + var set of int: z, +); % A two-branch conditional ends with a fixed true condition for else. -predicate fzn_if_then_else_var_set(array[int] of var bool: c, - array[int] of var set of int: x, var set of int: y) = - if length(c) = 2 /\ index_sets_agree(c,x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) - then - gecode_set_ite(c[min(index_set(c))],x[min(index_set(c))],x[max(index_set(c))],y) +predicate fzn_if_then_else_var_set( + array[int] of var bool: c, + array[int] of var set of int: x, + var set of int: y, +) = + if length(c) = 2 /\ index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + gecode_set_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else - let {array[index_set(c)] of var bool: d;} in - forall(i in index_set(c))( - if i > min(index_set(c)) then d[i] = (not c[i-1] /\ d[i-1]) else d[i] = true endif) /\ - forall(i in index_set(c))(c[i] /\ d[i] -> y = x[i]) + let { + array[index_set(c)] of var bool: d; + } in + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_regular_nfa.mzn b/gecode/flatzinc/mznlib/fzn_regular_nfa.mzn index 47fa786d1a..3da70bb2e9 100644 --- a/gecode/flatzinc/mznlib/fzn_regular_nfa.mzn +++ b/gecode/flatzinc/mznlib/fzn_regular_nfa.mzn @@ -30,12 +30,26 @@ % % -predicate gecode_regular_nfa_int_fzn(array[int] of var int: x, int: q, - int: first_symbol, int: symbols, array[int] of set of int: transitions, - int: initial, set of int: final); +predicate gecode_regular_nfa_int_fzn( + array[int] of var int: x, + int: q, + int: first_symbol, + int: symbols, + array[int] of set of int: transitions, + int: initial, + set of int: final, +); -predicate fzn_regular_nfa(array[int] of var int: xs, int: Q, int: S, - array[int,int] of set of int: d, int: q0, set of int: F) = - if length(xs) = 0 then q0 in F else - gecode_regular_nfa_int_fzn(xs,Q,1,S,array1d(d),q0,F) +predicate fzn_regular_nfa( + array[int] of var int: xs, + int: Q, + int: S, + array[int, int] of set of int: d, + int: q0, + set of int: F, +) = + if length(xs) = 0 then + q0 in F + else + gecode_regular_nfa_int_fzn(xs, Q, 1, S, array1d(d), q0, F) endif; diff --git a/gecode/flatzinc/mznlib/fzn_regular_nfa_set.mzn b/gecode/flatzinc/mznlib/fzn_regular_nfa_set.mzn index 35e89b5c77..ef192e2ae7 100644 --- a/gecode/flatzinc/mznlib/fzn_regular_nfa_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_regular_nfa_set.mzn @@ -30,13 +30,34 @@ % % -predicate gecode_regular_nfa_int_fzn(array[int] of var int: x, int: q, - int: first_symbol, int: symbols, array[int] of set of int: transitions, - int: initial, set of int: final); +predicate gecode_regular_nfa_int_fzn( + array[int] of var int: x, + int: q, + int: first_symbol, + int: symbols, + array[int] of set of int: transitions, + int: initial, + set of int: final, +); -predicate fzn_regular_nfa(array[int] of var int: xs, int: Q, set of int: S, - array[int,int] of set of int: d, int: q0, set of int: F) = - if length(xs) = 0 then q0 in F else - gecode_regular_nfa_int_fzn(xs,Q,min(S),card(S), - array1d(d),q0,F) +predicate fzn_regular_nfa( + array[int] of var int: xs, + int: Q, + set of int: S, + array[int, int] of set of int: d, + int: q0, + set of int: F, +) = + if length(xs) = 0 then + q0 in F + else + gecode_regular_nfa_int_fzn( + xs, + Q, + min(S), + card(S), + array1d(d), + q0, + F, + ) endif; diff --git a/gecode/flatzinc/mznlib/gecode.mzn b/gecode/flatzinc/mznlib/gecode.mzn index da990a0b56..30edda0308 100644 --- a/gecode/flatzinc/mznlib/gecode.mzn +++ b/gecode/flatzinc/mznlib/gecode.mzn @@ -192,7 +192,12 @@ include "arg_max.mzn"; Comparison operators for integer values and counts. */ enum GecodeIntRelation = { - GecodeEq, GecodeNe, GecodeLe, GecodeLt, GecodeGe, GecodeGt + GecodeEq, + GecodeNe, + GecodeLe, + GecodeLt, + GecodeGe, + GecodeGt, }; /** @group gecode.constraints @@ -201,14 +206,25 @@ enum GecodeIntRelation = { than comparing sorted lists of elements. */ enum GecodeSetRelation = { - GecodeSetEq, GecodeSetNe, GecodeSubset, GecodeSuperset, GecodeDisjoint, - GecodeComplement, GecodeSetLe, GecodeSetLt, GecodeSetGe, GecodeSetGt + GecodeSetEq, + GecodeSetNe, + GecodeSubset, + GecodeSuperset, + GecodeDisjoint, + GecodeComplement, + GecodeSetLe, + GecodeSetLt, + GecodeSetGe, + GecodeSetGt, }; /** @group gecode.constraints Union, disjoint union, intersection, and difference of sets. */ enum GecodeSetOperation = { - GecodeUnion, GecodeDisjointUnion, GecodeIntersection, GecodeDifference + GecodeUnion, + GecodeDisjointUnion, + GecodeIntersection, + GecodeDifference, }; /** @group gecode.constraints @@ -219,14 +235,19 @@ enum GecodeSetOperation = { Fixed durations must be nonnegative. */ enum GecodeTaskType = { - GecodeFixedDuration, GecodeFixedStart, GecodeFixedEnd + GecodeFixedDuration, + GecodeFixedStart, + GecodeFixedEnd, }; /** @group gecode.constraints Requires all values \a x[i] + \a offset[i] to be distinct. The arrays must have equal lengths and correspond by position. */ -predicate gecode_all_different(array[int] of int: offset, array[int] of var int: x) = +predicate gecode_all_different( + array[int] of int: offset, + array[int] of var int: x, +) = gecode_all_different_offset(offset, x); /** @group gecode.constraints @@ -235,53 +256,97 @@ predicate gecode_all_different(array[int] of int: offset, array[int] of var int: The index sets of \a x and \a permutation must agree, and all arrays must have equal lengths. */ -predicate gecode_sort_permutation(array[int] of var int: x, - array[int] of var int: y, array[int] of var int: permutation) = - assert(index_set(x) = index_set(permutation) /\ length(x) = length(y), +predicate gecode_sort_permutation( + array[int] of var int: x, + array[int] of var int: y, + array[int] of var int: permutation, +) = + assert( + index_set(x) = index_set(permutation) /\ length(x) = length(y), "gecode_sort_permutation: incompatible array indexes or lengths", - if length(y) = 0 then true else - gecode_sort_permutation(x, y, permutation, min(index_set(y))) endif); + if length(y) = 0 then + true + else + gecode_sort_permutation(x, y, permutation, min(index_set(y))) + endif, + ); /** @group gecode.constraints Compares the number of elements of \a x that belong to \a values with \a n, using \a relation. */ -predicate gecode_count(array[int] of var int: x, set of int: values, - GecodeIntRelation: relation, var int: n) = - if relation = GecodeEq then among(n, x, values) +predicate gecode_count( + array[int] of var int: x, + set of int: values, + GecodeIntRelation: relation, + var int: n, +) = + if relation = GecodeEq then + among(n, x, values) elseif relation = GecodeNe then - let {var 0..length(x): c; constraint among(c, x, values);} in c != n - else gecode_count_in_fzn(x, values, enum2int(relation), n) endif; + let { + var 0 .. length(x): c; + constraint among(c, x, values); + } in + c != n + else + gecode_count_in_fzn(x, values, enum2int(relation), n) + endif; /** @group gecode.constraints Compares the number of positions where \a x matches \a values with \a n, using \a relation. The arrays must have equal lengths and correspond by position. */ -predicate gecode_count(array[int] of var int: x, array[int] of int: values, - GecodeIntRelation: relation, var int: n) = - assert(length(x) = length(values), "gecode_count: unequal array lengths", - gecode_count_matches_fzn(x, values, enum2int(relation), n)); +predicate gecode_count( + array[int] of var int: x, + array[int] of int: values, + GecodeIntRelation: relation, + var int: n, +) = + assert( + length(x) = length(values), + "gecode_count: unequal array lengths", + gecode_count_matches_fzn(x, values, enum2int(relation), n), + ); /** @group gecode.constraints Compares the number of distinct values in \a x with \a n, using \a relation. An empty array has zero distinct values. */ -predicate gecode_nvalues(array[int] of var int: x, - GecodeIntRelation: relation, var int: n) = - if relation = GecodeEq then nvalue(n, x) +predicate gecode_nvalues( + array[int] of var int: x, + GecodeIntRelation: relation, + var int: n, +) = + if relation = GecodeEq then + nvalue(n, x) elseif relation = GecodeNe then - let {var 0..length(x): c; constraint nvalue(c, x);} in c != n - else gecode_nvalues_int_fzn(x, enum2int(relation), n) endif; + let { + var 0 .. length(x): c; + constraint nvalue(c, x); + } in + c != n + else + gecode_nvalues_int_fzn(x, enum2int(relation), n) + endif; /** @group gecode.constraints Compares the number of distinct values in \a x with \a n, using \a relation. An empty array has zero distinct values. */ -predicate gecode_nvalues(array[int] of var bool: x, - GecodeIntRelation: relation, var int: n) = +predicate gecode_nvalues( + array[int] of var bool: x, + GecodeIntRelation: relation, + var int: n, +) = if relation = GecodeNe then - let {var 0..2: c; - constraint gecode_nvalues_bool_fzn(x, enum2int(GecodeEq), c);} in c != n - else gecode_nvalues_bool_fzn(x, enum2int(relation), n) endif; + let { + var 0 .. 2: c; + constraint gecode_nvalues_bool_fzn(x, enum2int(GecodeEq), c); + } in + c != n + else + gecode_nvalues_bool_fzn(x, enum2int(relation), n) + endif; /** @group gecode.constraints Constrains \a result to be the nonnegative greatest common divisor @@ -289,27 +354,35 @@ predicate gecode_nvalues(array[int] of var bool: x, gcd(0, 0) = 0. */ predicate gecode_gcd(var int: x, var int: y, var int: result) = - gecode_gcd_int(x,y,result); + gecode_gcd_int(x, y, result); /** @group gecode.constraints Requires \a dividend to be an integer multiple of \a divisor. A zero divisor is permitted only when \a dividend is zero. */ predicate gecode_divides(var int: divisor, var int: dividend) = - gecode_divides_int(divisor,dividend); + gecode_divides_int(divisor, dividend); /** @group gecode.constraints Constrains \a b to be true if and only if the product of \a x equals \a result. The product of an empty array is one. */ -predicate gecode_product_reif(array[int] of var int: x, var int: result, var bool: b) = - gecode_product_int_reif(x,result,b); +predicate gecode_product_reif( + array[int] of var int: x, + var int: result, + var bool: b, +) = + gecode_product_int_reif(x, result, b); /** @group gecode.constraints Requires the product of \a x to equal \a result when \a b is true. The product of an empty array is one. */ -predicate gecode_product_imp(array[int] of var int: x, var int: result, var bool: b) = - gecode_product_int_imp(x,result,b); +predicate gecode_product_imp( + array[int] of var int: x, + var int: result, + var bool: b, +) = + gecode_product_int_imp(x, result, b); /** @group gecode.constraints Constrains \a result to be the Euclidean remainder of the product @@ -317,11 +390,20 @@ predicate gecode_product_imp(array[int] of var int: x, var int: result, var bool A nonpositive modulus makes the proposition false. The product of an empty array is one. */ -predicate gecode_product_mod(array[int] of var int: x, var int: modulus, var int: result) = +predicate gecode_product_mod( + array[int] of var int: x, + var int: modulus, + var int: result, +) = if is_fixed(modulus) then - if fix(modulus) > 0 then gecode_product_mod_fixed_fzn(x,fix(modulus),result) - else gecode_product_mod_var_fzn(x,modulus,result) endif - else gecode_product_mod_var_fzn(x,modulus,result) endif; + if fix(modulus) > 0 then + gecode_product_mod_fixed_fzn(x, fix(modulus), result) + else + gecode_product_mod_var_fzn(x, modulus, result) + endif + else + gecode_product_mod_var_fzn(x, modulus, result) + endif; /** @group gecode.constraints Constrains \a result to be the smallest distance between any two @@ -330,13 +412,23 @@ predicate gecode_product_mod(array[int] of var int: x, var int: modulus, var int indexes, and the matrix must be symmetric and nonnegative with a zero diagonal. Repeated sites are permitted. */ -predicate gecode_minimum_distance(array[int] of var int: sites, - array[int,int] of int: distances, var int: result) = - assert(length(sites) >= 2 /\ length(distances) > 0 /\ - index_set_1of2(distances) = index_set_2of2(distances), +predicate gecode_minimum_distance( + array[int] of var int: sites, + array[int, int] of int: distances, + var int: result, +) = + assert( + length(sites) >= 2 /\ + length(distances) > 0 /\ + index_set_1of2(distances) = index_set_2of2(distances), "gecode_minimum_distance: need at least two positions and a square nonempty distance matrix", - gecode_minimum_distance_fzn([v-min(index_set_1of2(distances)) | v in sites], - card(index_set_1of2(distances)),array1d(distances),result)); + gecode_minimum_distance_fzn( + [v - min(index_set_1of2(distances)) | v in sites], + card(index_set_1of2(distances)), + array1d(distances), + result, + ), + ); /** @group gecode.constraints Constrains \a result to be the smallest distance between any two positions in \a sites. Each pair must also meet the lower bound in @@ -345,28 +437,45 @@ predicate gecode_minimum_distance(array[int] of var int: sites, \a requirements use the index set of \a sites. Both matrices must be symmetric and nonnegative with a zero diagonal. Repeated sites are permitted. */ -predicate gecode_minimum_distance(array[int] of var int: sites, - array[int,int] of int: distances, var int: result, - array[int,int] of int: requirements) = - assert(length(sites) >= 2 /\ length(distances) > 0 /\ - index_set_1of2(distances) = index_set_2of2(distances) /\ - index_set_1of2(requirements) = index_set(sites) /\ - index_set_2of2(requirements) = index_set(sites), +predicate gecode_minimum_distance( + array[int] of var int: sites, + array[int, int] of int: distances, + var int: result, + array[int, int] of int: requirements, +) = + assert( + length(sites) >= 2 /\ + length(distances) > 0 /\ + index_set_1of2(distances) = index_set_2of2(distances) /\ + index_set_1of2(requirements) = index_set(sites) /\ + index_set_2of2(requirements) = index_set(sites), "gecode_minimum_distance: incompatible distance/requirements matrix or too few positions", - gecode_minimum_distance_required_fzn([v-min(index_set_1of2(distances)) | v in sites], - card(index_set_1of2(distances)),array1d(distances),result,array1d(requirements))); + gecode_minimum_distance_required_fzn( + [v - min(index_set_1of2(distances)) | v in sites], + card(index_set_1of2(distances)), + array1d(distances), + result, + array1d(requirements), + ), + ); /** @group gecode.constraints Constrains \a quotient = \a x div \a y and \a remainder = \a x mod \a y. The divisor \a y must be nonzero. Division truncates towards zero; a nonzero remainder has the sign of \a x. */ -predicate gecode_divmod(var int: x, var int: y, var int: quotient, var int: remainder) = +predicate gecode_divmod( + var int: x, + var int: y, + var int: quotient, + var int: remainder, +) = % Bound the results before dependent linear constraints are posted. let { - var compute_div_bounds(x,y): q = quotient; - var compute_mod_bounds(x,y): r = remainder; - } in gecode_int_divmod(x,y,q,r); + var compute_div_bounds(x, y): q = quotient; + var compute_mod_bounds(x, y): r = remainder; + } in + gecode_int_divmod(x, y, q, r); /** @group gecode.constraints Constrains \a y to be the integer root of degree \a degree of \a x, @@ -374,14 +483,21 @@ predicate gecode_divmod(var int: x, var int: y, var int: quotient, var int: rema only for odd degrees. */ predicate gecode_nroot(var int: x, int: degree, var int: y) = - assert(degree > 0, "gecode_nroot: degree must be positive", gecode_int_nroot(x,degree,y)); + assert(degree > 0, "gecode_nroot: degree must be positive", gecode_int_nroot(x, degree, y)); /** @group gecode.constraints Constrains \a y to be the nonnegative root of degree \a degree of \a x. The degree must be positive, and \a x must be nonnegative. */ predicate gecode_nroot(var float: x, int: degree, var float: y) = - assert(degree > 0, "gecode_nroot: degree must be positive", - if degree = 2 then float_sqrt(x,y) else gecode_float_nroot(x,degree,y) endif); + assert( + degree > 0, + "gecode_nroot: degree must be positive", + if degree = 2 then + float_sqrt(x, y) + else + gecode_float_nroot(x, degree, y) + endif, + ); /** @group gecode.constraints Requires \a x to describe a path from \a start to \a end that @@ -390,9 +506,16 @@ predicate gecode_nroot(var float: x, int: degree, var float: y) = \a x[\a end] = max(index_set(\a x)) + 1 marks the end of the path. */ predicate gecode_path(array[int] of var int: x, var int: start, var int: end) = - assert(length(x) > 0, "gecode_path: empty node array", - gecode_path(0, [v-min(index_set(x)) | v in x], - start-min(index_set(x)), end-min(index_set(x)))); + assert( + length(x) > 0, + "gecode_path: empty node array", + gecode_path( + 0, + [v - min(index_set(x)) | v in x], + start - min(index_set(x)), + end - min(index_set(x)), + ), + ); /** @group gecode.constraints Requires \a x to describe a path from \a start to \a end that visits every node once, with total edge cost \a cost given by \a costs. @@ -400,12 +523,27 @@ predicate gecode_path(array[int] of var int: x, var int: start, var int: end) = The successor \a x[\a end] = max(index_set(\a x)) + 1 marks the end, and its edge costs zero. */ -predicate gecode_path(array[int,int] of int: costs, array[int] of var int: x, - var int: start, var int: end, var int: cost) = - assert(length(x) > 0 /\ index_set_1of2(costs) = index_set(x) /\ - index_set_2of2(costs) = index_set(x), "gecode_path: incompatible cost matrix", - gecode_path_cost(array1d(costs), 0, [v-min(index_set(x)) | v in x], - start-min(index_set(x)), end-min(index_set(x)), cost)); +predicate gecode_path( + array[int, int] of int: costs, + array[int] of var int: x, + var int: start, + var int: end, + var int: cost, +) = + assert( + length(x) > 0 /\ + index_set_1of2(costs) = index_set(x) /\ + index_set_2of2(costs) = index_set(x), + "gecode_path: incompatible cost matrix", + gecode_path_cost( + array1d(costs), + 0, + [v - min(index_set(x)) | v in x], + start - min(index_set(x)), + end - min(index_set(x)), + cost, + ), + ); /** @group gecode.constraints Requires \a x to describe a path from \a start to \a end that visits every node once, with total cost \a cost. Each \a edge_costs[i] @@ -414,31 +552,55 @@ predicate gecode_path(array[int,int] of int: costs, array[int] of var int: x, node array. The successor \a x[\a end] = max(index_set(\a x)) + 1 marks the end, and its edge costs zero. */ -predicate gecode_path(array[int,int] of int: costs, array[int] of var int: x, - var int: start, var int: end, array[int] of var int: edge_costs, var int: cost) = - assert(length(x) > 0 /\ index_set_1of2(costs) = index_set(x) /\ - index_set_2of2(costs) = index_set(x) /\ index_set(edge_costs) = index_set(x), +predicate gecode_path( + array[int, int] of int: costs, + array[int] of var int: x, + var int: start, + var int: end, + array[int] of var int: edge_costs, + var int: cost, +) = + assert( + length(x) > 0 /\ + index_set_1of2(costs) = index_set(x) /\ + index_set_2of2(costs) = index_set(x) /\ + index_set(edge_costs) = index_set(x), "gecode_path: incompatible cost matrix or edge costs", - gecode_path_cost_array(array1d(costs), 0, [v-min(index_set(x)) | v in x], - start-min(index_set(x)), end-min(index_set(x)), edge_costs, cost)); + gecode_path_cost_array( + array1d(costs), + 0, + [v - min(index_set(x)) | v in x], + start - min(index_set(x)), + end - min(index_set(x)), + edge_costs, + cost, + ), + ); /** @group gecode.constraints Requires the tasks described by \a types, \a flex, and \a fix not to overlap. GecodeTaskType specifies which start, end, or duration is fixed. The arrays must have equal lengths and correspond by position. */ -predicate gecode_unary_typed(array[int] of GecodeTaskType: types, - array[int] of var int: flex, array[int] of int: fix) = - gecode_unary_typed_fzn(enum2int(types),flex,fix); +predicate gecode_unary_typed( + array[int] of GecodeTaskType: types, + array[int] of var int: flex, + array[int] of int: fix, +) = + gecode_unary_typed_fzn(enum2int(types), flex, fix); /** @group gecode.constraints Requires the present tasks described by \a types, \a flex, and \a fix not to overlap. A task is present when its entry in \a present is true. GecodeTaskType specifies which start, end, or duration is fixed. The arrays must have equal lengths and correspond by position. */ -predicate gecode_unary_typed(array[int] of GecodeTaskType: types, - array[int] of var int: flex, array[int] of int: fix, array[int] of var bool: present) = - gecode_unary_typed_optional_fzn(enum2int(types),flex,fix,present); +predicate gecode_unary_typed( + array[int] of GecodeTaskType: types, + array[int] of var int: flex, + array[int] of int: fix, + array[int] of var bool: present, +) = + gecode_unary_typed_optional_fzn(enum2int(types), flex, fix, present); /** @group gecode.constraints Requires the total \a usage of tasks executing at any time not @@ -446,10 +608,14 @@ predicate gecode_unary_typed(array[int] of GecodeTaskType: types, and \a fix describe each task. Usage and capacity must be nonnegative. The arrays must have equal lengths and correspond by position. */ -predicate gecode_cumulative_typed(array[int] of GecodeTaskType: types, - array[int] of var int: flex, array[int] of int: fix, - array[int] of int: usage, var int: capacity) = - gecode_cumulative_typed_fzn(enum2int(types),flex,fix,usage,capacity); +predicate gecode_cumulative_typed( + array[int] of GecodeTaskType: types, + array[int] of var int: flex, + array[int] of int: fix, + array[int] of int: usage, + var int: capacity, +) = + gecode_cumulative_typed_fzn(enum2int(types), flex, fix, usage, capacity); /** @group gecode.constraints Requires the total \a usage of present tasks executing at any time not to exceed \a capacity. A task is present when its entry in @@ -457,10 +623,15 @@ predicate gecode_cumulative_typed(array[int] of GecodeTaskType: types, and \a fix describe each task. Usage and capacity must be nonnegative. The arrays must have equal lengths and correspond by position. */ -predicate gecode_cumulative_typed(array[int] of GecodeTaskType: types, - array[int] of var int: flex, array[int] of int: fix, - array[int] of int: usage, var int: capacity, array[int] of var bool: present) = - gecode_cumulative_typed_optional_fzn(enum2int(types),flex,fix,usage,capacity,present); +predicate gecode_cumulative_typed( + array[int] of GecodeTaskType: types, + array[int] of var int: flex, + array[int] of int: fix, + array[int] of int: usage, + var int: capacity, + array[int] of var bool: present, +) = + gecode_cumulative_typed_optional_fzn(enum2int(types), flex, fix, usage, capacity, present); /** @group gecode.constraints Requires the present rectangles with origins (\a x[i], \a y[i]) @@ -468,12 +639,21 @@ predicate gecode_cumulative_typed(array[int] of GecodeTaskType: types, A rectangle is present when \a present[i] is true. Sizes must be nonnegative. All arrays must have identical index sets. */ -predicate gecode_nooverlap(array[int] of var int: x, array[int] of int: width, - array[int] of var int: y, array[int] of int: height, array[int] of var bool: present) = - assert(index_set(x) = index_set(width) /\ index_set(x) = index_set(y) /\ - index_set(x) = index_set(height) /\ index_set(x) = index_set(present), +predicate gecode_nooverlap( + array[int] of var int: x, + array[int] of int: width, + array[int] of var int: y, + array[int] of int: height, + array[int] of var bool: present, +) = + assert( + index_set(x) = index_set(width) /\ + index_set(x) = index_set(y) /\ + index_set(x) = index_set(height) /\ + index_set(x) = index_set(present), "gecode_nooverlap: incompatible indexes", - gecode_nooverlap_optional(x,width,y,height,present)); + gecode_nooverlap_optional(x, width, y, height, present), + ); /** @group gecode.constraints Requires the present rectangles not to overlap, with horizontal bounds \a x and \a right and vertical bounds \a y and \a top. @@ -482,62 +662,93 @@ predicate gecode_nooverlap(array[int] of var int: x, array[int] of int: width, \a y + \a height = \a top. Sizes must be nonnegative. All arrays must have identical index sets. */ -predicate gecode_nooverlap(array[int] of var int: x, array[int] of var int: width, - array[int] of var int: right, array[int] of var int: y, - array[int] of var int: height, array[int] of var int: top, - array[int] of var bool: present) = - assert(index_set(x) = index_set(width) /\ index_set(x) = index_set(right) /\ - index_set(x) = index_set(y) /\ index_set(x) = index_set(height) /\ - index_set(x) = index_set(top) /\ index_set(x) = index_set(present), +predicate gecode_nooverlap( + array[int] of var int: x, + array[int] of var int: width, + array[int] of var int: right, + array[int] of var int: y, + array[int] of var int: height, + array[int] of var int: top, + array[int] of var bool: present, +) = + assert( + index_set(x) = index_set(width) /\ + index_set(x) = index_set(right) /\ + index_set(x) = index_set(y) /\ + index_set(x) = index_set(height) /\ + index_set(x) = index_set(top) /\ + index_set(x) = index_set(present), "gecode_nooverlap: incompatible indexes", - forall(i in index_set(x))(present[i] -> - (x[i]+width[i]=right[i] /\ y[i]+height[i]=top[i])) /\ - gecode_nooverlap_flex_optional(x,width,right,y,height,top,present)); + forall (i in index_set(x)) ( + present[i] -> (x[i] + width[i] = right[i] /\ y[i] + height[i] = top[i]) + ) /\ + gecode_nooverlap_flex_optional(x, width, right, y, height, top, present), + ); /** @group gecode.constraints Requires \a x to be empty or a consecutive interval of integers. */ -predicate gecode_convex(var set of int: x) = gecode_set_convex(x); +predicate gecode_convex(var set of int: x) = + gecode_set_convex(x); /** @group gecode.constraints Constrains \a hull to be the smallest consecutive interval containing \a x. The hull of an empty set is empty. */ -predicate gecode_convex(var set of int: x, var set of int: hull) = gecode_set_convex_hull(x,hull); +predicate gecode_convex(var set of int: x, var set of int: hull) = + gecode_set_convex_hull(x, hull); /** @group gecode.constraints Requires every element of each set in \a x to be smaller than every element of each later set. Empty sets are permitted. */ -predicate gecode_set_sequence(array[int] of var set of int: x) = gecode_array_set_seq(x); +predicate gecode_set_sequence(array[int] of var set of int: x) = + gecode_array_set_seq(x); /** @group gecode.constraints Requires every element of each set in \a x to be smaller than every element of each later set, and constrains \a y to their union. Empty sets are permitted. */ predicate gecode_set_sequence(array[int] of var set of int: x, var set of int: y) = - gecode_array_set_seq_union(x,y); + gecode_array_set_seq_union(x, y); /** @group gecode.constraints Compares the result of applying \a operation to \a x and \a y with \a z, using \a relation. Complement and set ordering follow GecodeSetRelation. */ -predicate gecode_set_operation(var set of int: x, GecodeSetOperation: operation, - var set of int: y, GecodeSetRelation: relation, var set of int: z) = - if relation = GecodeSetEq /\ operation = GecodeUnion then z = x union y - elseif relation = GecodeSetEq /\ operation = GecodeIntersection then z = x intersect y - elseif relation = GecodeSetEq /\ operation = GecodeDifference then z = x diff y - else gecode_set_operation_fzn(x,enum2int(operation),y,enum2int(relation),z) endif; +predicate gecode_set_operation( + var set of int: x, + GecodeSetOperation: operation, + var set of int: y, + GecodeSetRelation: relation, + var set of int: z, +) = + if relation = GecodeSetEq /\ operation = GecodeUnion then + z = x union y + elseif relation = GecodeSetEq /\ operation = GecodeIntersection then + z = x intersect y + elseif relation = GecodeSetEq /\ operation = GecodeDifference then + z = x diff y + else + gecode_set_operation_fzn(x, enum2int(operation), y, enum2int(relation), z) + endif; /** @group gecode.constraints Combines \a initial and the singleton sets {\a x[i]} using \a operation, and constrains \a y to the result. Union, disjoint union, and intersection are supported. */ -predicate gecode_array_int_set_operation(array[int] of var int: x, - GecodeSetOperation: operation, set of int: initial, var set of int: y) = - assert(operation != GecodeDifference, "gecode_array_int_set_operation: difference is unsupported", - gecode_array_int_set_operation_fzn(x,enum2int(operation),initial,y)); +predicate gecode_array_int_set_operation( + array[int] of var int: x, + GecodeSetOperation: operation, + set of int: initial, + var set of int: y, +) = + assert( + operation != GecodeDifference, + "gecode_array_int_set_operation: difference is unsupported", + gecode_array_int_set_operation_fzn(x, enum2int(operation), initial, y), + ); /** @group gecode.constraints Combines the singleton sets {\a x[i]} at the zero-based positions @@ -545,117 +756,206 @@ predicate gecode_array_int_set_operation(array[int] of var int: x, Union, disjoint union, and intersection are supported. An empty selection gives \a universe for intersection and the empty set for either union. */ -predicate gecode_array_int_set_element(array[int] of var int: x, - GecodeSetOperation: operation, var set of int: selected, - var set of int: value, set of int: universe) = - assert(operation != GecodeDifference, "gecode_array_int_set_element: difference is unsupported", - if operation = GecodeUnion then gecode_range(x,0,selected,value) - else gecode_array_int_set_element_fzn(x,enum2int(operation),selected,value,universe) endif); +predicate gecode_array_int_set_element( + array[int] of var int: x, + GecodeSetOperation: operation, + var set of int: selected, + var set of int: value, + set of int: universe, +) = + assert( + operation != GecodeDifference, + "gecode_array_int_set_element: difference is unsupported", + if operation = GecodeUnion then + gecode_range(x, 0, selected, value) + else + gecode_array_int_set_element_fzn(x, enum2int(operation), selected, value, universe) + endif, + ); /** @group gecode.constraints Constrains \a b to be true if and only if \a s is nonempty and \a x is its smallest element. */ predicate gecode_min_reif(var set of int: s, var int: x, var bool: b) = - gecode_set_min_reif(s,x,b); + gecode_set_min_reif(s, x, b); /** @group gecode.constraints Constrains \a b to be true if and only if \a s is nonempty and \a x is its largest element. */ predicate gecode_max_reif(var set of int: s, var int: x, var bool: b) = - gecode_set_max_reif(s,x,b); + gecode_set_max_reif(s, x, b); /** @group gecode.constraints Compares \a s with the singleton set {\a x}, using \a relation. Complement and set ordering follow GecodeSetRelation. */ -predicate gecode_set_singleton_rel(var set of int: s, GecodeSetRelation: relation, var int: x) = - if relation = GecodeSuperset then x in s else - gecode_set_singleton_rel_fzn(s,enum2int(relation),x) endif; +predicate gecode_set_singleton_rel( + var set of int: s, + GecodeSetRelation: relation, + var int: x, +) = + if relation = GecodeSuperset then + x in s + else + gecode_set_singleton_rel_fzn(s, enum2int(relation), x) + endif; /** @group gecode.constraints Constrains \a b to be true if and only if \a s has \a relation to the singleton set {\a x}. Complement and set ordering follow GecodeSetRelation. */ -predicate gecode_set_singleton_rel_reif(var set of int: s, GecodeSetRelation: relation, - var int: x, var bool: b) = - if relation = GecodeSuperset then b <-> x in s else - gecode_set_singleton_rel_fzn_reif(s,enum2int(relation),x,b) endif; +predicate gecode_set_singleton_rel_reif( + var set of int: s, + GecodeSetRelation: relation, + var int: x, + var bool: b, +) = + if relation = GecodeSuperset then + b <-> x in s + else + gecode_set_singleton_rel_fzn_reif(s, enum2int(relation), x, b) + endif; /** @group gecode.constraints Requires \a s to have \a relation to the singleton set {\a x} when \a b is true. Complement and set ordering follow GecodeSetRelation. */ -predicate gecode_set_singleton_rel_imp(var set of int: s, GecodeSetRelation: relation, - var int: x, var bool: b) = - if relation = GecodeSuperset then b -> x in s else - gecode_set_singleton_rel_fzn_imp(s,enum2int(relation),x,b) endif; +predicate gecode_set_singleton_rel_imp( + var set of int: s, + GecodeSetRelation: relation, + var int: x, + var bool: b, +) = + if relation = GecodeSuperset then + b -> x in s + else + gecode_set_singleton_rel_fzn_imp(s, enum2int(relation), x, b) + endif; /** @group gecode.constraints Requires \a x to have \a relation to \a y when \a b is true. Complement and set ordering follow GecodeSetRelation. */ -predicate gecode_rel_imp(var set of int: x, GecodeSetRelation: relation, - var set of int: y, var bool: b) = - if relation = GecodeSetEq then b -> x = y - elseif relation = GecodeSetNe then b -> x != y - elseif relation = GecodeSubset then b -> x subset y - elseif relation = GecodeSuperset then b -> x superset y - else gecode_set_rel_imp_fzn(x,enum2int(relation),y,b) endif; +predicate gecode_rel_imp( + var set of int: x, + GecodeSetRelation: relation, + var set of int: y, + var bool: b, +) = + if relation = GecodeSetEq then + b -> x = y + elseif relation = GecodeSetNe then + b -> x != y + elseif relation = GecodeSubset then + b -> x subset y + elseif relation = GecodeSuperset then + b -> x superset y + else + gecode_set_rel_imp_fzn(x, enum2int(relation), y, b) + endif; /** @group gecode.constraints Constrains \a y to be the logarithm of \a x to base \a base. The base must be positive and different from one. Requires MPFR support. */ predicate gecode_log(float: base, var float: x, var float: y) = - assert(base > 0.0 /\ base != 1.0, "gecode_log: base must be positive and different from one", - if base = 2.0 then float_log2(x,y) - elseif base = 10.0 then float_log10(x,y) - else gecode_float_log_base(base,x,y) endif); + assert( + base > 0.0 /\ base != 1.0, + "gecode_log: base must be positive and different from one", + if base = 2.0 then + float_log2(x, y) + elseif base = 10.0 then + float_log10(x, y) + else + gecode_float_log_base(base, x, y) + endif, + ); /** @group gecode.constraints Constrains \a index to be the index of a smallest value in \a x. If \a break_ties is true, the smallest qualifying index is required; otherwise any qualifying index is permitted. The array must be nonempty. */ -predicate gecode_arg_min(array[int] of var int: x, var int: index, bool: break_ties) = - assert(length(x) > 0, "gecode_arg_min: empty array", - if break_ties then arg_min(array1d(x)) + min(index_set(x)) - 1 = index - else gecode_arg_min_int(x,0,index-min(index_set(x)),false) endif); +predicate gecode_arg_min( + array[int] of var int: x, + var int: index, + bool: break_ties, +) = + assert( + length(x) > 0, + "gecode_arg_min: empty array", + if break_ties then + arg_min(array1d(x)) + min(index_set(x)) - 1 = index + else + gecode_arg_min_int(x, 0, index - min(index_set(x)), false) + endif, + ); /** @group gecode.constraints Constrains \a index to be the index of a smallest value in \a x. If \a break_ties is true, the smallest qualifying index is required; otherwise any qualifying index is permitted. The array must be nonempty. */ -predicate gecode_arg_min(array[int] of var bool: x, var int: index, bool: break_ties) = - assert(length(x) > 0, "gecode_arg_min: empty array", - if break_ties then arg_min(array1d(x)) + min(index_set(x)) - 1 = index - else gecode_arg_min_bool(x,0,index-min(index_set(x)),false) endif); +predicate gecode_arg_min( + array[int] of var bool: x, + var int: index, + bool: break_ties, +) = + assert( + length(x) > 0, + "gecode_arg_min: empty array", + if break_ties then + arg_min(array1d(x)) + min(index_set(x)) - 1 = index + else + gecode_arg_min_bool(x, 0, index - min(index_set(x)), false) + endif, + ); /** @group gecode.constraints Constrains \a index to be the index of a largest value in \a x. If \a break_ties is true, the smallest qualifying index is required; otherwise any qualifying index is permitted. The array must be nonempty. */ -predicate gecode_arg_max(array[int] of var int: x, var int: index, bool: break_ties) = - assert(length(x) > 0, "gecode_arg_max: empty array", - if break_ties then arg_max(array1d(x)) + min(index_set(x)) - 1 = index - else gecode_arg_max_int(x,0,index-min(index_set(x)),false) endif); +predicate gecode_arg_max( + array[int] of var int: x, + var int: index, + bool: break_ties, +) = + assert( + length(x) > 0, + "gecode_arg_max: empty array", + if break_ties then + arg_max(array1d(x)) + min(index_set(x)) - 1 = index + else + gecode_arg_max_int(x, 0, index - min(index_set(x)), false) + endif, + ); /** @group gecode.constraints Constrains \a index to be the index of a largest value in \a x. If \a break_ties is true, the smallest qualifying index is required; otherwise any qualifying index is permitted. The array must be nonempty. */ -predicate gecode_arg_max(array[int] of var bool: x, var int: index, bool: break_ties) = - assert(length(x) > 0, "gecode_arg_max: empty array", - if break_ties then arg_max(array1d(x)) + min(index_set(x)) - 1 = index - else gecode_arg_max_bool(x,0,index-min(index_set(x)),false) endif); +predicate gecode_arg_max( + array[int] of var bool: x, + var int: index, + bool: break_ties, +) = + assert( + length(x) > 0, + "gecode_arg_max: empty array", + if break_ties then + arg_max(array1d(x)) + min(index_set(x)) - 1 = index + else + gecode_arg_max_bool(x, 0, index - min(index_set(x)), false) + endif, + ); /** @group gecode.constraints Requires exactly one element of \a x to be true, at zero-based position \a value - \a offset. */ predicate gecode_channel(array[int] of var bool: x, var int: value, int: offset) = - gecode_bool_int_channel(x,value,offset); + gecode_bool_int_channel(x, value, offset); /** @group gecode.constraints Constrains \a x to be 1.0 when \a b is true and 0.0 otherwise. */ -predicate gecode_channel(var bool: b, var float: x) = gecode_bool_float_channel(b,x); +predicate gecode_channel(var bool: b, var float: x) = + gecode_bool_float_channel(b, x); diff --git a/gecode/flatzinc/mznlib/gecode_fzn.mzn b/gecode/flatzinc/mznlib/gecode_fzn.mzn index 54d909cdeb..f1b5b0c7da 100644 --- a/gecode/flatzinc/mznlib/gecode_fzn.mzn +++ b/gecode/flatzinc/mznlib/gecode_fzn.mzn @@ -45,8 +45,10 @@ predicate gecode_inter_distance(array[int] of var int: x, var int: distance); Constrain values in \a x whose corresponding \a present entry is true to be pairwise distinct. The arrays have equal lengths. */ -predicate gecode_all_different_optional(array[int] of var int: x, - array[int] of var bool: present); +predicate gecode_all_different_optional( + array[int] of var int: x, + array[int] of var bool: present, +); /** @group gecode.constraints Constrain the values in \a x to be pairwise distinct, except that @@ -58,43 +60,73 @@ predicate gecode_all_different_except(array[int] of var int: x, int: except_valu Constrain the sums of corresponding entries in \a x and \a offset to be pairwise distinct. The arrays must have equal lengths. */ -predicate gecode_all_different_offset(array[int] of int: offset, - array[int] of var int: x); +predicate gecode_all_different_offset( + array[int] of int: offset, + array[int] of var int: x, +); % Exactly one entry is true; its zero-based position plus offset equals value. -predicate gecode_bool_int_channel(array[int] of var bool: x, - var int: value, int: offset); +predicate gecode_bool_int_channel( + array[int] of var bool: x, + var int: value, + int: offset, +); % Sort x into y; permutation[i]-offset is the zero-based position of x[i] in y. -predicate gecode_sort_permutation(array[int] of var int: x, - array[int] of var int: y, array[int] of var int: permutation, int: offset); +predicate gecode_sort_permutation( + array[int] of var int: x, + array[int] of var int: y, + array[int] of var int: permutation, + int: offset, +); % Compare the number of occurrences of value in x with count. -predicate gecode_count_int_fzn(array[int] of var int: x, var int: value, - int: relation, var int: count); +predicate gecode_count_int_fzn( + array[int] of var int: x, + var int: value, + int: relation, + var int: count, +); % Compare the number of entries of x that belong to values with count. -predicate gecode_count_in_fzn(array[int] of var int: x, set of int: values, - int: relation, var int: count); +predicate gecode_count_in_fzn( + array[int] of var int: x, + set of int: values, + int: relation, + var int: count, +); % Compare the number of position-wise matches between x and values with count. -predicate gecode_count_matches_fzn(array[int] of var int: x, array[int] of int: values, - int: relation, var int: count); +predicate gecode_count_matches_fzn( + array[int] of var int: x, + array[int] of int: values, + int: relation, + var int: count, +); /** @group gecode.constraints Constrain the number of occurrences of each value in \a cover to belong to the corresponding set in \a counts. Every value in \a x must occur in \a cover. Cover values are distinct; \a cover and \a counts have equal lengths. */ -predicate gecode_global_cardinality_sets(array[int] of var int: x, - array[int] of int: cover, array[int] of set of int: counts); +predicate gecode_global_cardinality_sets( + array[int] of var int: x, + array[int] of int: cover, + array[int] of set of int: counts, +); % Compare the number of distinct values in x with count; an empty array has none. -predicate gecode_nvalues_int_fzn(array[int] of var int: x, - int: relation, var int: count); - -predicate gecode_nvalues_bool_fzn(array[int] of var bool: x, - int: relation, var int: count); +predicate gecode_nvalues_int_fzn( + array[int] of var int: x, + int: relation, + var int: count, +); + +predicate gecode_nvalues_bool_fzn( + array[int] of var bool: x, + int: relation, + var int: count, +); % Arrays have unequal lengths or at least one pair of corresponding entries differs. predicate gecode_array_int_ne(array[int] of var int: x, array[int] of var int: y); @@ -111,8 +143,12 @@ predicate gecode_int_ite(var bool: b, var int: x, var int: y, var int: z); predicate gecode_bool_ite(var bool: b, var bool: x, var bool: y, var bool: z); -predicate gecode_set_ite(var bool: b, var set of int: x, - var set of int: y, var set of int: z); +predicate gecode_set_ite( + var bool: b, + var set of int: x, + var set of int: y, + var set of int: z, +); predicate gecode_float_ite(var bool: b, var float: x, var float: y, var float: z); @@ -140,17 +176,30 @@ predicate gecode_product_mod_var_fzn_reif(array[int] of var int: x, var int: mod predicate gecode_product_mod_var_fzn_imp(array[int] of var int: x, var int: modulus, var int: result, var bool: b); % sites use zero-based labels; distances is a row-major size-by-size matrix. -predicate gecode_minimum_distance_fzn(array[int] of var int: sites, - int: size, array[int] of int: distances, var int: result); +predicate gecode_minimum_distance_fzn( + array[int] of var int: sites, + int: size, + array[int] of int: distances, + var int: result, +); % requirements is a row-major square matrix of pairwise lower bounds, % with one row per entry in sites. -predicate gecode_minimum_distance_required_fzn(array[int] of var int: sites, - int: size, array[int] of int: distances, var int: result, array[int] of int: requirements); +predicate gecode_minimum_distance_required_fzn( + array[int] of var int: sites, + int: size, + array[int] of int: distances, + var int: result, + array[int] of int: requirements, +); % x = y*quotient+remainder, with quotient truncated toward zero and y != 0. -predicate gecode_int_divmod(var int: x, var int: y, - var int: quotient, var int: remainder); +predicate gecode_int_divmod( + var int: x, + var int: y, + var int: quotient, + var int: remainder, +); % y is floor(root(x,n)); n is positive, and negative x requires odd n. predicate gecode_int_nroot(var int: x, int: n, var int: y); @@ -161,29 +210,62 @@ predicate gecode_int_nroot(var int: x, int: n, var int: y); and true. A zero transition rejects; \a initial and \a final specify the initial state and accepting states. */ -predicate gecode_regular_bool(array[int] of var bool: x, int: q, - array[int] of int: transitions, int: initial, set of int: final); +predicate gecode_regular_bool( + array[int] of var bool: x, + int: q, + array[int] of int: transitions, + int: initial, + set of int: final, +); % NFA rows are states 1..q, columns are first_symbol..first_symbol+symbols-1. % Each entry gives the set of target states; an empty set rejects. -predicate gecode_regular_nfa_int_fzn(array[int] of var int: x, int: q, - int: first_symbol, int: symbols, array[int] of set of int: transitions, - int: initial, set of int: final); -predicate gecode_regular_nfa_bool_fzn(array[int] of var bool: x, int: q, - int: first_symbol, int: symbols, array[int] of set of int: transitions, - int: initial, set of int: final); +predicate gecode_regular_nfa_int_fzn( + array[int] of var int: x, + int: q, + int: first_symbol, + int: symbols, + array[int] of set of int: transitions, + int: initial, + set of int: final, +); +predicate gecode_regular_nfa_bool_fzn( + array[int] of var bool: x, + int: q, + int: first_symbol, + int: symbols, + array[int] of set of int: transitions, + int: initial, + set of int: final, +); % Node labels start at offset; the final successor is offset+length(x). -predicate gecode_path(int: offset, array[int] of var int: x, - var int: start, var int: end); +predicate gecode_path( + int: offset, + array[int] of var int: x, + var int: start, + var int: end, +); % costs is a row-major square matrix; the final successor edge costs zero. -predicate gecode_path_cost(array[int] of int: costs, int: offset, - array[int] of var int: x, var int: start, var int: end, var int: cost); - -predicate gecode_path_cost_array(array[int] of int: costs, int: offset, - array[int] of var int: x, var int: start, var int: end, - array[int] of var int: edge_costs, var int: cost); +predicate gecode_path_cost( + array[int] of int: costs, + int: offset, + array[int] of var int: x, + var int: start, + var int: end, + var int: cost, +); + +predicate gecode_path_cost_array( + array[int] of int: costs, + int: offset, + array[int] of var int: x, + var int: start, + var int: end, + array[int] of var int: edge_costs, + var int: cost, +); /** @group gecode.constraints Constrain two tasks not to overlap. If \a order is false, @@ -191,17 +273,25 @@ predicate gecode_path_cost_array(array[int] of int: costs, int: offset, \a start1 + \a duration1 <= \a start0. Durations are nonnegative. */ -predicate gecode_order(var int: start0, int: duration0, - var int: start1, int: duration1, var bool: order); +predicate gecode_order( + var int: start0, + int: duration0, + var int: start1, + int: duration1, + var bool: order, +); /** @group gecode.constraints Constrain tasks whose \a present entry is true not to overlap. Durations are nonnegative. The arrays have equal lengths. The caller must also constrain \a start[i] + \a duration[i] = \a end[i] for each present task. */ -predicate gecode_unary_flex_optional(array[int] of var int: start, - array[int] of var int: duration, array[int] of var int: end, - array[int] of var bool: present); +predicate gecode_unary_flex_optional( + array[int] of var int: start, + array[int] of var int: duration, + array[int] of var int: end, + array[int] of var bool: present, +); /** @group gecode.constraints Constrain the total \a usage of tasks whose \a present entry is true not to @@ -209,38 +299,67 @@ predicate gecode_unary_flex_optional(array[int] of var int: start, The arrays have equal lengths. The caller must also constrain \a start[i] + \a duration[i] = \a end[i] for each present task. */ -predicate gecode_cumulative_flex_optional(array[int] of var int: start, - array[int] of var int: duration, array[int] of var int: end, - array[int] of int: usage, var int: capacity, array[int] of var bool: present); +predicate gecode_cumulative_flex_optional( + array[int] of var int: start, + array[int] of var int: duration, + array[int] of var int: end, + array[int] of int: usage, + var int: capacity, + array[int] of var bool: present, +); % Task codes: 1 has fixed duration, 2 fixed start, 3 fixed end. % flex/fix mean start/duration, end/start, and start/end, respectively. -predicate gecode_unary_typed_fzn(array[int] of int: task_type, - array[int] of var int: flex, array[int] of int: fix); - -predicate gecode_unary_typed_optional_fzn(array[int] of int: task_type, - array[int] of var int: flex, array[int] of int: fix, - array[int] of var bool: present); - -predicate gecode_cumulative_typed_fzn(array[int] of int: task_type, - array[int] of var int: flex, array[int] of int: fix, - array[int] of int: usage, var int: capacity); - -predicate gecode_cumulative_typed_optional_fzn(array[int] of int: task_type, - array[int] of var int: flex, array[int] of int: fix, - array[int] of int: usage, var int: capacity, array[int] of var bool: present); +predicate gecode_unary_typed_fzn( + array[int] of int: task_type, + array[int] of var int: flex, + array[int] of int: fix, +); + +predicate gecode_unary_typed_optional_fzn( + array[int] of int: task_type, + array[int] of var int: flex, + array[int] of int: fix, + array[int] of var bool: present, +); + +predicate gecode_cumulative_typed_fzn( + array[int] of int: task_type, + array[int] of var int: flex, + array[int] of int: fix, + array[int] of int: usage, + var int: capacity, +); + +predicate gecode_cumulative_typed_optional_fzn( + array[int] of int: task_type, + array[int] of var int: flex, + array[int] of int: fix, + array[int] of int: usage, + var int: capacity, + array[int] of var bool: present, +); % Present rectangles have nonnegative fixed widths and heights and cannot overlap. -predicate gecode_nooverlap_optional(array[int] of var int: x, - array[int] of int: width, array[int] of var int: y, - array[int] of int: height, array[int] of var bool: present); +predicate gecode_nooverlap_optional( + array[int] of var int: x, + array[int] of int: width, + array[int] of var int: y, + array[int] of int: height, + array[int] of var bool: present, +); % Present rectangles cannot overlap. The caller must also constrain % x[i]+width[i]=right[i] and y[i]+height[i]=top[i] for present rectangles. -predicate gecode_nooverlap_flex_optional(array[int] of var int: x, - array[int] of var int: width, array[int] of var int: right, - array[int] of var int: y, array[int] of var int: height, - array[int] of var int: top, array[int] of var bool: present); +predicate gecode_nooverlap_flex_optional( + array[int] of var int: x, + array[int] of var int: width, + array[int] of var int: right, + array[int] of var int: y, + array[int] of var int: height, + array[int] of var int: top, + array[int] of var bool: present, +); % hull is empty when x is empty, and min(x)..max(x) otherwise. predicate gecode_set_convex_hull(var set of int: x, var set of int: hull); @@ -259,32 +378,53 @@ predicate gecode_set_atmost_one(array[int] of var set of int: x, int: cardinalit */ predicate gecode_set_channel_sorted(array[int] of var int: x, var set of int: y); -predicate gecode_set_operation_fzn(var set of int: x, int: operation, - var set of int: y, int: relation, var set of int: z); +predicate gecode_set_operation_fzn( + var set of int: x, + int: operation, + var set of int: y, + int: relation, + var set of int: z, +); /** @group gecode.constraints Constrain \a y to be the intersection of all sets in \a x. The intersection of an empty array is the full Gecode set universe. */ -predicate gecode_array_set_intersect(array[int] of var set of int: x, - var set of int: y); - -predicate gecode_array_int_set_operation_fzn(array[int] of var int: x, - int: operation, set of int: initial, var set of int: y); +predicate gecode_array_set_intersect( + array[int] of var set of int: x, + var set of int: y, +); + +predicate gecode_array_int_set_operation_fzn( + array[int] of var int: x, + int: operation, + set of int: initial, + var set of int: y, +); /** @group gecode.constraints Constrain \a value to be the set at (\a column, \a row) in the row-major \a x matrix. Coordinates are zero-based; \a width and \a height are positive and their product equals the length of \a x. */ -predicate gecode_set_element2d(array[int] of var set of int: x, - var int: column, int: width, var int: row, int: height, var set of int: value); +predicate gecode_set_element2d( + array[int] of var set of int: x, + var int: column, + int: width, + var int: row, + int: height, + var set of int: value, +); % selected contains zero-based positions; combine the singleton sets {x[i]}. % An empty intersection equals universe; an empty union is empty. -predicate gecode_array_int_set_element_fzn(array[int] of var int: x, - int: operation, var set of int: selected, - var set of int: value, set of int: universe); +predicate gecode_array_int_set_element_fzn( + array[int] of var int: x, + int: operation, + var set of int: selected, + var set of int: value, + set of int: universe, +); /** @group gecode.constraints Constrain \a y to be \a x raised to the nonnegative integer \a n. @@ -320,17 +460,33 @@ predicate gecode_float_log_base(float: base, var float: x, var float: y); % index is the zero-based position plus offset of an extremum. % If break_ties is true, choose the first such position. Arrays are nonempty. -predicate gecode_arg_min_int(array[int] of var int: x, int: offset, - var int: index, bool: break_ties); - -predicate gecode_arg_max_int(array[int] of var int: x, int: offset, - var int: index, bool: break_ties); - -predicate gecode_arg_min_bool(array[int] of var bool: x, int: offset, - var int: index, bool: break_ties); - -predicate gecode_arg_max_bool(array[int] of var bool: x, int: offset, - var int: index, bool: break_ties); +predicate gecode_arg_min_int( + array[int] of var int: x, + int: offset, + var int: index, + bool: break_ties, +); + +predicate gecode_arg_max_int( + array[int] of var int: x, + int: offset, + var int: index, + bool: break_ties, +); + +predicate gecode_arg_min_bool( + array[int] of var bool: x, + int: offset, + var int: index, + bool: break_ties, +); + +predicate gecode_arg_max_bool( + array[int] of var bool: x, + int: offset, + var int: index, + bool: break_ties, +); % x is the minimum of the nonempty set s; reified forms are false for empty s. predicate gecode_set_min(var set of int: s, var int: x); @@ -347,14 +503,25 @@ predicate gecode_set_max_reif(var set of int: s, var int: x, var bool: b); predicate gecode_set_max_imp(var set of int: s, var int: x, var bool: b); % Compare s with the singleton set {x}. -predicate gecode_set_singleton_rel_fzn(var set of int: s, int: relation, - var int: x); - -predicate gecode_set_singleton_rel_fzn_reif(var set of int: s, int: relation, - var int: x, var bool: b); - -predicate gecode_set_singleton_rel_fzn_imp(var set of int: s, int: relation, - var int: x, var bool: b); +predicate gecode_set_singleton_rel_fzn( + var set of int: s, + int: relation, + var int: x, +); + +predicate gecode_set_singleton_rel_fzn_reif( + var set of int: s, + int: relation, + var int: x, + var bool: b, +); + +predicate gecode_set_singleton_rel_fzn_imp( + var set of int: s, + int: relation, + var int: x, + var bool: b, +); % Every element precedes all elements in later nonempty sets; sets are disjoint. predicate gecode_array_set_seq(array[int] of var set of int: x); diff --git a/gecode/flatzinc/mznlib/redefinitions-2.0.mzn b/gecode/flatzinc/mznlib/redefinitions-2.0.mzn index 4159aa45a4..8265bb453d 100644 --- a/gecode/flatzinc/mznlib/redefinitions-2.0.mzn +++ b/gecode/flatzinc/mznlib/redefinitions-2.0.mzn @@ -10,29 +10,44 @@ predicate array_int_maximum(var int: m, array[int] of var int: x); predicate gecode_float_array_max(array[int] of var float: x, var float: m); predicate array_float_maximum(var float: m, array[int] of var float: x) = - if length(x) = 0 then false else gecode_float_array_max(x, m) endif; + if length(x) = 0 then + false + else + gecode_float_array_max(x, m) + endif; predicate array_int_minimum(var int: m, array[int] of var int: x); predicate gecode_float_array_min(array[int] of var float: x, var float: m); predicate array_float_minimum(var float: m, array[int] of var float: x) = - if length(x) = 0 then false else gecode_float_array_min(x, m) endif; + if length(x) = 0 then + false + else + gecode_float_array_min(x, m) + endif; predicate gecode_float_pow(var float: x, int: n, var float: y); predicate gecode_float_pow_base(float: base, var float: x, var float: y); % Nonnegative integer exponents permit zero or negative bases. predicate float_pow(var float: x, var float: exponent, var float: y) = - let { - float: n = if is_fixed(exponent) then fix(exponent) else -1.0 endif; - bool: integer_exponent = if n >= 0.0 /\ n <= 2147483646.0 then - n = int2float(floor(n)) else false endif; - } in + let { + float: n = if is_fixed(exponent) then fix(exponent) else -1.0 endif; + bool: integer_exponent = + if n >= 0.0 /\ n <= 2147483646.0 then + n = int2float(floor(n)) + else + false + endif; + } in if integer_exponent then - gecode_float_pow(x, floor(n), y) + gecode_float_pow(x, floor(n), y) elseif is_fixed(x) /\ fix(x) > 0.0 then - gecode_float_pow_base(fix(x), exponent, y) + gecode_float_pow_base(fix(x), exponent, y) else - assert(false, "Gecode float_pow requires a nonnegative integer exponent or a fixed positive base") + assert( + false, + "Gecode float_pow requires a nonnegative integer exponent or a fixed positive base" + ) endif; diff --git a/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn b/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn index 17030a1106..ea3f597e5d 100644 --- a/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn +++ b/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn @@ -44,28 +44,34 @@ function var $$E: min(var set of $$E: s) = % An empty upper bound makes the extremum undefined at compilation. let { var set of int: si = enum2int(s); - var min(ub(si))..max(ub(si)): m = + var min(ub(si)) .. max(ub(si)): m = min([e + (max(ub(si)) + 1 - e) * (1 - (e in si)) | e in ub(si)]); - } in to_enum_internal(enum_of(s), m) + } in + to_enum_internal(enum_of(s), m) else - to_enum_internal(enum_of(s), - if mzn_in_root_context() then gecode_set_min_value(enum2int(s)) else + to_enum_internal( + enum_of(s), + if mzn_in_root_context() then + gecode_set_min_value(enum2int(s)) + else let { constraint card(s) > 0; - } in gecode_set_min_partial(enum2int(s)) - endif) + } in + gecode_set_min_partial(enum2int(s)) + endif + ) endif; function var int: gecode_set_min_value(var set of int: s) :: promise_total = let { - var min(ub(s) union {1})..max(ub(s) union {1}): m :: is_defined_var; + var min(ub(s) union {1}) .. max(ub(s) union {1}): m :: is_defined_var; constraint card(s) > 0; constraint gecode_set_min(s, m) :: defines_var(m); } in m; function var int: gecode_set_min_partial(var set of int: s) :: promise_total = let { - var min(ub(s) union {1})..max(ub(s) union {1}): m :: is_defined_var; + var min(ub(s) union {1}) .. max(ub(s) union {1}): m :: is_defined_var; var bool: present = card(s) > 0; constraint gecode_set_min_imp(s, m, present) :: defines_var(m); } in m; @@ -75,28 +81,34 @@ function var $$E: max(var set of $$E: s) = % An empty upper bound makes the extremum undefined at compilation. let { var set of int: si = enum2int(s); - var min(ub(si))..max(ub(si)): m = + var min(ub(si)) .. max(ub(si)): m = max([e + (min(ub(si)) - 1 - e) * (1 - (e in si)) | e in ub(si)]); - } in to_enum_internal(enum_of(s), m) + } in + to_enum_internal(enum_of(s), m) else - to_enum_internal(enum_of(s), - if mzn_in_root_context() then gecode_set_max_value(enum2int(s)) else + to_enum_internal( + enum_of(s), + if mzn_in_root_context() then + gecode_set_max_value(enum2int(s)) + else let { constraint card(s) > 0; - } in gecode_set_max_partial(enum2int(s)) - endif) + } in + gecode_set_max_partial(enum2int(s)) + endif + ) endif; function var int: gecode_set_max_value(var set of int: s) :: promise_total = let { - var min(ub(s) union {1})..max(ub(s) union {1}): m :: is_defined_var; + var min(ub(s) union {1}) .. max(ub(s) union {1}): m :: is_defined_var; constraint card(s) > 0; constraint gecode_set_max(s, m) :: defines_var(m); } in m; function var int: gecode_set_max_partial(var set of int: s) :: promise_total = let { - var min(ub(s) union {1})..max(ub(s) union {1}): m :: is_defined_var; + var min(ub(s) union {1}) .. max(ub(s) union {1}): m :: is_defined_var; var bool: present = card(s) > 0; constraint gecode_set_max_imp(s, m, present) :: defines_var(m); } in m; diff --git a/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn b/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn index 4c110bf701..8aa671f269 100644 --- a/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn +++ b/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn @@ -14,13 +14,30 @@ predicate array_var_float_element2d_nonshifted(var int: idx1, var int: idx2, arr int: min_flat = min(index_set_1of2(x))*dim+min(index_set_2of2(x))-1; } in array_var_float_element_nonshifted((idx1*dim+idx2-min_flat)::domain, array1d(x), c); -predicate gecode_set_element2d(array[int] of var set of int: x, - var int: column, int: width, var int: row, int: height, var set of int: value); +predicate gecode_set_element2d( + array[int] of var set of int: x, + var int: column, + int: width, + var int: row, + int: height, + var set of int: value +); -predicate array_var_set_element2d_nonshifted(var int: idx1, var int: idx2, - array[int,int] of var set of int: x, var set of int: c) = - if length(x) = 0 then false else - gecode_set_element2d(array1d(x), idx2-min(index_set_2of2(x)), - card(index_set_2of2(x)), idx1-min(index_set_1of2(x)), - card(index_set_1of2(x)), c) +predicate array_var_set_element2d_nonshifted( + var int: idx1, + var int: idx2, + array[int, int] of var set of int: x, + var set of int: c +) = + if length(x) = 0 then + false + else + gecode_set_element2d( + array1d(x), + idx2 - min(index_set_2of2(x)), + card(index_set_2of2(x)), + idx1 - min(index_set_1of2(x)), + card(index_set_1of2(x)), + c + ) endif; diff --git a/gecode/flatzinc/mznlib/redefinitions.mzn b/gecode/flatzinc/mznlib/redefinitions.mzn index 792f5d9999..eef909e74e 100644 --- a/gecode/flatzinc/mznlib/redefinitions.mzn +++ b/gecode/flatzinc/mznlib/redefinitions.mzn @@ -140,8 +140,12 @@ predicate int_lin_ne_imp(array [int] of int: as, array [int] of var int: bs, predicate set_in_imp(var int: x, set of int: S, var bool: r); % b implies the scalar float relation; a false b leaves x and y unconstrained. -predicate gecode_float_rel_imp_fzn(var float: x, int: relation, - var float: y, var bool: b); +predicate gecode_float_rel_imp_fzn( + var float: x, + int: relation, + var float: y, + var bool: b +); predicate float_eq_imp(var float: x, var float: y, var bool: b) = gecode_float_rel_imp_fzn(x, 1, y, b); predicate float_ne_imp(var float: x, var float: y, var bool: b) = @@ -153,43 +157,71 @@ predicate float_lt_imp(var float: x, var float: y, var bool: b) = % MiniZinc represents scalar comparisons as one- or two-term linear forms. % Other linear expressions use full reification followed by implication. -predicate gecode_float_lin_scalar_imp(array[int] of float: a, - array[int] of var float: x, float: y, int: relation, var bool: b) = +predicate gecode_float_lin_scalar_imp( + array[int] of float: a, + array[int] of var float: x, + float: y, + int: relation, + var bool: b +) = let { array[int] of float: aa = array1d(a); array[int] of var float: xx = array1d(x); } in - if length(aa) = 1 /\ length(xx) = 1 /\ aa[1] = 1.0 then - gecode_float_rel_imp_fzn(xx[1], relation, y, b) - elseif length(aa) = 2 /\ length(xx) = 2 /\ y = 0.0 /\ - aa[1] = 1.0 /\ aa[2] = -1.0 then - gecode_float_rel_imp_fzn(xx[1], relation, xx[2], b) - elseif length(aa) = 2 /\ length(xx) = 2 /\ y = 0.0 /\ - aa[1] = -1.0 /\ aa[2] = 1.0 then - gecode_float_rel_imp_fzn(xx[2], relation, xx[1], b) - else - let { var bool: r; } in - (if relation = 1 then float_lin_eq_reif(a, x, y, r) - elseif relation = 2 then float_lin_eq_reif(a, x, y, not r) - elseif relation = 3 then float_lin_le_reif(a, x, y, r) - else float_lin_lt_reif(a, x, y, r) endif) /\ bool_clause([r], [b]) - endif; - -predicate float_lin_eq_imp(array[int] of float: a, - array[int] of var float: x, float: y, var bool: b) = + if length(aa) = 1 /\ length(xx) = 1 /\ aa[1] = 1.0 then + gecode_float_rel_imp_fzn(xx[1], relation, y, b) + elseif length(aa) = 2 /\ length(xx) = 2 /\ y = 0.0 /\ aa[1] = 1.0 /\ aa[2] = -1.0 then + gecode_float_rel_imp_fzn(xx[1], relation, xx[2], b) + elseif length(aa) = 2 /\ length(xx) = 2 /\ y = 0.0 /\ aa[1] = -1.0 /\ aa[2] = 1.0 then + gecode_float_rel_imp_fzn(xx[2], relation, xx[1], b) + else + let { var bool: r; } in + (if relation = 1 then + float_lin_eq_reif(a, x, y, r) + elseif relation = 2 then + float_lin_eq_reif(a, x, y, not r) + elseif relation = 3 then + float_lin_le_reif(a, x, y, r) + else + float_lin_lt_reif(a, x, y, r) + endif) /\ bool_clause([r], [b]) + endif; + +predicate float_lin_eq_imp( + array[int] of float: a, + array[int] of var float: x, + float: y, + var bool: b +) = gecode_float_lin_scalar_imp(a, x, y, 1, b); -predicate float_lin_ne_imp(array[int] of float: a, - array[int] of var float: x, float: y, var bool: b) = +predicate float_lin_ne_imp( + array[int] of float: a, + array[int] of var float: x, + float: y, + var bool: b +) = gecode_float_lin_scalar_imp(a, x, y, 2, b); -predicate float_lin_le_imp(array[int] of float: a, - array[int] of var float: x, float: y, var bool: b) = +predicate float_lin_le_imp( + array[int] of float: a, + array[int] of var float: x, + float: y, + var bool: b +) = gecode_float_lin_scalar_imp(a, x, y, 3, b); -predicate float_lin_lt_imp(array[int] of float: a, - array[int] of var float: x, float: y, var bool: b) = +predicate float_lin_lt_imp( + array[int] of float: a, + array[int] of var float: x, + float: y, + var bool: b +) = gecode_float_lin_scalar_imp(a, x, y, 4, b); -predicate gecode_set_rel_imp_fzn(var set of int: x, int: relation, - var set of int: y, var bool: b); +predicate gecode_set_rel_imp_fzn( + var set of int: x, + int: relation, + var set of int: y, + var bool: b +); predicate set_eq_imp(var set of int: x, var set of int: y, var bool: b) = gecode_set_rel_imp_fzn(x, 1, y, b); predicate set_ne_imp(var set of int: x, var set of int: y, var bool: b) = diff --git a/gecode/flatzinc/mznlib/solver-native-overloads.mzn b/gecode/flatzinc/mznlib/solver-native-overloads.mzn index 606ee5a6a2..2cd2a7be0c 100644 --- a/gecode/flatzinc/mznlib/solver-native-overloads.mzn +++ b/gecode/flatzinc/mznlib/solver-native-overloads.mzn @@ -42,102 +42,203 @@ function var int: product(array[$$X] of var int: x) :: promise_commutative = if mzn_in_root_context() then let { var int: result :: is_defined_var; - constraint gecode_product_int(array1d(x),result) :: defines_var(result); - } in result + constraint gecode_product_int(array1d(x), result) :: defines_var(result); + } in + result else product_rec(array1d(x)) endif; % The array disequality propagator supports unreified constraints only. -function var bool: '!='(array[$U] of var $$E: x, - array[$U] of var $$E: y) :: promise_commutative = - matching_index_sets(x,y) /\ +function var bool: '!='( + array[$U] of var $$E: x, + array[$U] of var $$E: y, +) :: promise_commutative = + matching_index_sets(x, y) /\ if mzn_in_root_context() then - gecode_array_int_ne(enum2int(array1d(x)),enum2int(array1d(y))) + gecode_array_int_ne(enum2int(array1d(x)), enum2int(array1d(y))) else - let {any: xx = enum2int(array1d(x)); any: yy = enum2int(array1d(y));} in - exists(i in index_set(xx))(xx[i] != yy[i]) + let { + any: xx = enum2int(array1d(x)); + any: yy = enum2int(array1d(y)); + } in + exists (i in index_set(xx)) (xx[i] != yy[i]) endif; -function var bool: '!='(array[$U] of var bool: x, - array[$U] of var bool: y) :: promise_commutative = - matching_index_sets(x,y) /\ +function var bool: '!='( + array[$U] of var bool: x, + array[$U] of var bool: y, +) :: promise_commutative = + matching_index_sets(x, y) /\ if mzn_in_root_context() then - gecode_array_bool_ne(array1d(x),array1d(y)) + gecode_array_bool_ne(array1d(x), array1d(y)) else - let {any: xx = array1d(x); any: yy = array1d(y);} in - exists(i in index_set(xx))(xx[i] != yy[i]) + let { + any: xx = array1d(x); + any: yy = array1d(y); + } in + exists (i in index_set(xx)) (xx[i] != yy[i]) endif; predicate all_equal(array[$X] of var bool: xs) = - if length(xs) <= 1 then true else fzn_all_equal_bool(array1d(xs)) endif; + if length(xs) <= 1 then + true + else + fzn_all_equal_bool(array1d(xs)) + endif; predicate fzn_all_equal_bool(array[int] of var bool: xs) = - (if length(xs) = 0 then true else - forall(i in index_set(xs))(xs[i] = xs[min(index_set(xs))]) endif); + (if length(xs) = 0 then + true + else + forall (i in index_set(xs)) ( + xs[i] = xs[min(index_set(xs))] + ) + endif); predicate fzn_all_equal_bool_reif(array[int] of var bool: xs, var bool: b) = - if is_fixed(b) /\ not fix(b) then gecode_not_all_equal_bool(xs) - else b <-> (if length(xs) = 0 then true else - forall(i in index_set(xs))(xs[i] = xs[min(index_set(xs))]) endif) endif; + if is_fixed(b) /\ not fix(b) then + gecode_not_all_equal_bool(xs) + else + b <-> (if length(xs) = 0 then + true + else + forall (i in index_set(xs)) ( + xs[i] = xs[min(index_set(xs))] + ) + endif) + endif; predicate nvalue(var int: n, array[$X] of var bool: xs) = if mzn_in_root_context() then - gecode_nvalues_bool_fzn(array1d(xs),1,n) - else nvalue(n,bool2int(xs)) endif; + gecode_nvalues_bool_fzn(array1d(xs), 1, n) + else + nvalue(n, bool2int(xs)) + endif; function var int: nvalue(array[$X] of var bool: xs) :: promise_total = let { - var bool2int(length(xs) > 0)..min(2,length(xs)): n :: is_defined_var; - constraint gecode_nvalues_bool_fzn(array1d(xs),1,n) :: defines_var(n); - } in n; + var bool2int(length(xs) > 0) .. min(2, length(xs)): n :: is_defined_var; + constraint gecode_nvalues_bool_fzn(array1d(xs), 1, n) :: defines_var(n); + } in + n; % Boolean symbols are 0 and 1. Missing symbols lead to state 0, so they % reject the word in both unreified and reified constraints. -predicate regular(array[int] of var bool: xs, int: Q, set of int: S, - array[int,int] of int: d, int: q0, set of int: F) = +predicate regular( + array[int] of var bool: xs, + int: Q, + set of int: S, + array[int, int] of int: d, + int: q0, + set of int: F, +) = assert(Q > 0, "regular: 'Q' must be greater than zero") /\ assert(card(S) > 0, "regular: 'S' must be non empty") /\ - assert(index_set_1of2(d) = 1..Q /\ index_set_2of2(d) = S, - "regular: the transition function 'd' must be [1..Q,S]") /\ - assert(forall(v in d)(v in 0..Q), - "regular: transition function 'd' points to states outside 0..Q") /\ - assert(q0 in 1..Q, "regular: start state 'q0' not in 1..Q") /\ - assert(F subset 1..Q, "regular: final states in 'F' contain states outside 1..Q") /\ + assert( + index_set_1of2(d) = 1 .. Q /\ index_set_2of2(d) = S, + "regular: the transition function 'd' must be [1..Q,S]", + ) /\ + assert( + forall (v in d) ( + v in 0 .. Q + ), + "regular: transition function 'd' points to states outside 0..Q", + ) /\ + assert(q0 in 1 .. Q, "regular: start state 'q0' not in 1..Q") /\ + assert( + F subset 1 .. Q, + "regular: final states in 'F' contain states outside 1..Q", + ) /\ let { - array[1..Q,0..1] of int: dd = array2d(1..Q,0..1, - [if symbol in S then d[state,symbol] else 0 endif - | state in 1..Q, symbol in 0..1]); + array[1 .. Q, 0 .. 1] of int: dd = array2d( + 1 .. Q, + 0 .. 1, + [ + if symbol in S then + d[state, symbol] + else + 0 + endif | + state in 1 .. Q, + symbol in 0 .. 1, + ], + ); } in - if mzn_in_root_context() then gecode_regular_bool(xs,Q,array1d(dd),q0,F) - else regular(bool2int(xs),Q,0..1,dd,q0,F) endif; + if mzn_in_root_context() then + gecode_regular_bool(xs, Q, array1d(dd), q0, F) + else + regular(bool2int(xs), Q, 0 .. 1, dd, q0, F) + endif; -predicate regular(array[int] of var bool: xs, int: Q, int: S, - array[int,int] of int: d, int: q0, set of int: F) = +predicate regular( + array[int] of var bool: xs, + int: Q, + int: S, + array[int, int] of int: d, + int: q0, + set of int: F, +) = assert(S > 0, "regular: 'S' must be greater than zero") /\ - regular(xs,Q,1..S,d,q0,F); + regular(xs, Q, 1 .. S, d, q0, F); % Missing Boolean symbols have no transitions. Reification is unsupported. -predicate regular_nfa(array[int] of var bool: x, int: q, set of int: symbols, - array[int,int] of set of int: transitions, int: initial, set of int: final) = - assert(q > 0 /\ card(symbols) > 0 /\ symbols = min(symbols)..max(symbols), - "regular_nfa: states must be positive and alphabet a nonempty range") /\ - assert(index_set_1of2(transitions) = 1..q /\ index_set_2of2(transitions) = symbols, - "regular_nfa: transition indexes must be [1..q,alphabet]") /\ - assert(forall(targets in transitions)(targets subset 1..q) /\ - initial in 1..q /\ final subset 1..q, "regular_nfa: invalid transition or initial/final state") /\ - if length(x) = 0 then initial in final else +predicate regular_nfa( + array[int] of var bool: x, + int: q, + set of int: symbols, + array[int, int] of set of int: transitions, + int: initial, + set of int: final, +) = + assert( + q > 0 /\ card(symbols) > 0 /\ symbols = min(symbols) .. max(symbols), + "regular_nfa: states must be positive and alphabet a nonempty range", + ) /\ + assert( + index_set_1of2(transitions) = 1 .. q /\ index_set_2of2(transitions) = symbols, + "regular_nfa: transition indexes must be [1..q,alphabet]", + ) /\ + assert( + forall (targets in transitions) ( + targets subset 1 .. q + ) /\ + initial in 1 .. q /\ + final subset 1 .. q, + "regular_nfa: invalid transition or initial/final state", + ) /\ + if length(x) = 0 then + initial in final + else let { - array[1..q,0..1] of set of int: normalized = array2d(1..q,0..1, - [if symbol in symbols then transitions[state,symbol] else {} endif - | state in 1..q, symbol in 0..1]); + array[1 .. q, 0 .. 1] of set of int: normalized = array2d( + 1 .. q, + 0 .. 1, + [ + if symbol in symbols then + transitions[state, symbol] + else + {} + endif | + state in 1 .. q, + symbol in 0 .. 1, + ], + ); } in if mzn_in_root_context() then - gecode_regular_nfa_bool_fzn(x,q,0,2,array1d(normalized),initial,final) - else regular_nfa(bool2int(x),q,0..1,normalized,initial,final) endif + gecode_regular_nfa_bool_fzn(x, q, 0, 2, array1d(normalized), initial, final) + else + regular_nfa(bool2int(x), q, 0 .. 1, normalized, initial, final) + endif endif; -predicate regular_nfa(array[int] of var bool: x, int: q, int: symbols, - array[int,int] of set of int: transitions, int: initial, set of int: final) = +predicate regular_nfa( + array[int] of var bool: x, + int: q, + int: symbols, + array[int, int] of set of int: transitions, + int: initial, + set of int: final, +) = assert(symbols > 0, "regular_nfa: alphabet size must be positive") /\ - regular_nfa(x,q,1..symbols,transitions,initial,final); + regular_nfa(x, q, 1 .. symbols, transitions, initial, final); From ce7f2105db016f3a99a14bfcef1eb3e44e759f90 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 10 Oct 2026 22:22:26 +0200 Subject: [PATCH 14/22] Preserve include order in the formatted solver hook --- gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn b/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn index 26b0355c29..8c7b253c7a 100644 --- a/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn +++ b/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn @@ -30,8 +30,8 @@ % % -include "fzn_all_different_int.mzn"; include "global_cardinality_low_up.mzn"; +include "fzn_all_different_int.mzn"; predicate gecode_all_different_except(array[int] of var int: x, int: except_value); From 26fd648f033734045ec74ec4c9ac704096fbf753 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sun, 11 Oct 2026 11:31:20 +0200 Subject: [PATCH 15/22] Keep standard MiniZinc globals in the standard library --- gecode/flatzinc/mznlib/redefinitions.mzn | 2 - .../mznlib/solver-native-overloads.mzn | 244 ------------------ 2 files changed, 246 deletions(-) delete mode 100644 gecode/flatzinc/mznlib/solver-native-overloads.mzn diff --git a/gecode/flatzinc/mznlib/redefinitions.mzn b/gecode/flatzinc/mznlib/redefinitions.mzn index eef909e74e..58a26e970b 100644 --- a/gecode/flatzinc/mznlib/redefinitions.mzn +++ b/gecode/flatzinc/mznlib/redefinitions.mzn @@ -30,8 +30,6 @@ % % -include "solver-native-overloads.mzn"; - % Sums over Boolean variables predicate bool_lin_eq(array[int] of int: a, array[int] of var bool: x, var int: c); diff --git a/gecode/flatzinc/mznlib/solver-native-overloads.mzn b/gecode/flatzinc/mznlib/solver-native-overloads.mzn deleted file mode 100644 index 2cd2a7be0c..0000000000 --- a/gecode/flatzinc/mznlib/solver-native-overloads.mzn +++ /dev/null @@ -1,244 +0,0 @@ -% -% Main authors: -% Mikael Zayenz Lagerkvist -% -% Copyright: -% Mikael Zayenz Lagerkvist, 2026 -% -% This file is part of Gecode, the generic constraint -% development environment: -% http://www.gecode.dev -% -% Permission is hereby granted, free of charge, to any person obtaining -% a copy of this software and associated documentation files (the -% "Software"), to deal in the Software without restriction, including -% without limitation the rights to use, copy, modify, merge, publish, -% distribute, sublicense, and/or sell copies of the Software, and to -% permit persons to whom the Software is furnished to do so, subject to -% the following conditions: -% -% The above copyright notice and this permission notice shall be -% included in all copies or substantial portions of the Software. -% -% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -% -% - -include "all_equal.mzn"; -include "regular.mzn"; -include "regular_nfa.mzn"; -include "nvalue_fn.mzn"; -include "gecode_fzn.mzn"; - -% This index-type overload leaves parameter arrays on the builtin product. -% Root constraints use a result variable for the native product. -function var int: product(array[$$X] of var int: x) :: promise_commutative = - if mzn_in_root_context() then - let { - var int: result :: is_defined_var; - constraint gecode_product_int(array1d(x), result) :: defines_var(result); - } in - result - else - product_rec(array1d(x)) - endif; - -% The array disequality propagator supports unreified constraints only. -function var bool: '!='( - array[$U] of var $$E: x, - array[$U] of var $$E: y, -) :: promise_commutative = - matching_index_sets(x, y) /\ - if mzn_in_root_context() then - gecode_array_int_ne(enum2int(array1d(x)), enum2int(array1d(y))) - else - let { - any: xx = enum2int(array1d(x)); - any: yy = enum2int(array1d(y)); - } in - exists (i in index_set(xx)) (xx[i] != yy[i]) - endif; - -function var bool: '!='( - array[$U] of var bool: x, - array[$U] of var bool: y, -) :: promise_commutative = - matching_index_sets(x, y) /\ - if mzn_in_root_context() then - gecode_array_bool_ne(array1d(x), array1d(y)) - else - let { - any: xx = array1d(x); - any: yy = array1d(y); - } in - exists (i in index_set(xx)) (xx[i] != yy[i]) - endif; - -predicate all_equal(array[$X] of var bool: xs) = - if length(xs) <= 1 then - true - else - fzn_all_equal_bool(array1d(xs)) - endif; - -predicate fzn_all_equal_bool(array[int] of var bool: xs) = - (if length(xs) = 0 then - true - else - forall (i in index_set(xs)) ( - xs[i] = xs[min(index_set(xs))] - ) - endif); - -predicate fzn_all_equal_bool_reif(array[int] of var bool: xs, var bool: b) = - if is_fixed(b) /\ not fix(b) then - gecode_not_all_equal_bool(xs) - else - b <-> (if length(xs) = 0 then - true - else - forall (i in index_set(xs)) ( - xs[i] = xs[min(index_set(xs))] - ) - endif) - endif; - -predicate nvalue(var int: n, array[$X] of var bool: xs) = - if mzn_in_root_context() then - gecode_nvalues_bool_fzn(array1d(xs), 1, n) - else - nvalue(n, bool2int(xs)) - endif; - -function var int: nvalue(array[$X] of var bool: xs) :: promise_total = - let { - var bool2int(length(xs) > 0) .. min(2, length(xs)): n :: is_defined_var; - constraint gecode_nvalues_bool_fzn(array1d(xs), 1, n) :: defines_var(n); - } in - n; - -% Boolean symbols are 0 and 1. Missing symbols lead to state 0, so they -% reject the word in both unreified and reified constraints. -predicate regular( - array[int] of var bool: xs, - int: Q, - set of int: S, - array[int, int] of int: d, - int: q0, - set of int: F, -) = - assert(Q > 0, "regular: 'Q' must be greater than zero") /\ - assert(card(S) > 0, "regular: 'S' must be non empty") /\ - assert( - index_set_1of2(d) = 1 .. Q /\ index_set_2of2(d) = S, - "regular: the transition function 'd' must be [1..Q,S]", - ) /\ - assert( - forall (v in d) ( - v in 0 .. Q - ), - "regular: transition function 'd' points to states outside 0..Q", - ) /\ - assert(q0 in 1 .. Q, "regular: start state 'q0' not in 1..Q") /\ - assert( - F subset 1 .. Q, - "regular: final states in 'F' contain states outside 1..Q", - ) /\ - let { - array[1 .. Q, 0 .. 1] of int: dd = array2d( - 1 .. Q, - 0 .. 1, - [ - if symbol in S then - d[state, symbol] - else - 0 - endif | - state in 1 .. Q, - symbol in 0 .. 1, - ], - ); - } in - if mzn_in_root_context() then - gecode_regular_bool(xs, Q, array1d(dd), q0, F) - else - regular(bool2int(xs), Q, 0 .. 1, dd, q0, F) - endif; - -predicate regular( - array[int] of var bool: xs, - int: Q, - int: S, - array[int, int] of int: d, - int: q0, - set of int: F, -) = - assert(S > 0, "regular: 'S' must be greater than zero") /\ - regular(xs, Q, 1 .. S, d, q0, F); - -% Missing Boolean symbols have no transitions. Reification is unsupported. -predicate regular_nfa( - array[int] of var bool: x, - int: q, - set of int: symbols, - array[int, int] of set of int: transitions, - int: initial, - set of int: final, -) = - assert( - q > 0 /\ card(symbols) > 0 /\ symbols = min(symbols) .. max(symbols), - "regular_nfa: states must be positive and alphabet a nonempty range", - ) /\ - assert( - index_set_1of2(transitions) = 1 .. q /\ index_set_2of2(transitions) = symbols, - "regular_nfa: transition indexes must be [1..q,alphabet]", - ) /\ - assert( - forall (targets in transitions) ( - targets subset 1 .. q - ) /\ - initial in 1 .. q /\ - final subset 1 .. q, - "regular_nfa: invalid transition or initial/final state", - ) /\ - if length(x) = 0 then - initial in final - else - let { - array[1 .. q, 0 .. 1] of set of int: normalized = array2d( - 1 .. q, - 0 .. 1, - [ - if symbol in symbols then - transitions[state, symbol] - else - {} - endif | - state in 1 .. q, - symbol in 0 .. 1, - ], - ); - } in - if mzn_in_root_context() then - gecode_regular_nfa_bool_fzn(x, q, 0, 2, array1d(normalized), initial, final) - else - regular_nfa(bool2int(x), q, 0 .. 1, normalized, initial, final) - endif - endif; - -predicate regular_nfa( - array[int] of var bool: x, - int: q, - int: symbols, - array[int, int] of set of int: transitions, - int: initial, - set of int: final, -) = - assert(symbols > 0, "regular_nfa: alphabet size must be positive") /\ - regular_nfa(x, q, 1 .. symbols, transitions, initial, final); From 0ed8936720a7f841186a0661565c43741c35b312 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sun, 11 Oct 2026 11:42:30 +0200 Subject: [PATCH 16/22] Give native MiniZinc extensions modelling interfaces --- .../mznlib/fzn_all_different_int_opt.mzn | 6 +- .../mznlib/fzn_alldifferent_except.mzn | 6 +- .../mznlib/fzn_alldifferent_except_0.mzn | 3 +- gecode/flatzinc/mznlib/fzn_count_geq.mzn | 3 +- gecode/flatzinc/mznlib/fzn_count_gt.mzn | 3 +- gecode/flatzinc/mznlib/fzn_count_leq.mzn | 3 +- gecode/flatzinc/mznlib/fzn_count_lt.mzn | 3 +- gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn | 29 +- gecode/flatzinc/mznlib/fzn_diffn.mzn | 19 +- .../mznlib/fzn_disjunctive_strict_opt.mzn | 6 +- .../flatzinc/mznlib/fzn_if_then_else_bool.mzn | 29 +- .../mznlib/fzn_if_then_else_float.mzn | 29 +- .../flatzinc/mznlib/fzn_if_then_else_int.mzn | 29 +- .../flatzinc/mznlib/fzn_if_then_else_set.mzn | 29 +- .../mznlib/fzn_if_then_else_var_bool.mzn | 29 +- .../mznlib/fzn_if_then_else_var_float.mzn | 29 +- .../mznlib/fzn_if_then_else_var_int.mzn | 29 +- .../mznlib/fzn_if_then_else_var_set.mzn | 29 +- gecode/flatzinc/mznlib/gecode.mzn | 878 ++++++++---------- gecode/flatzinc/mznlib/gecode_fzn.mzn | 231 +---- gecode/flatzinc/mznlib/redefinitions-2.0.mzn | 35 +- .../flatzinc/mznlib/redefinitions-2.1.1.mzn | 30 +- .../flatzinc/mznlib/redefinitions-2.5.2.mzn | 6 +- gecode/flatzinc/mznlib/redefinitions.mzn | 87 +- gecode/flatzinc/registry.cpp | 118 +-- test/flatzinc/native-registry.cpp | 10 +- 26 files changed, 666 insertions(+), 1042 deletions(-) diff --git a/gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn b/gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn index 105555a209..54e55b7284 100644 --- a/gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn +++ b/gecode/flatzinc/mznlib/fzn_all_different_int_opt.mzn @@ -57,6 +57,6 @@ predicate fzn_all_different_int_opt_reif( [if is_fixed(x) /\ absent(fix(x)) then 0 else deopt(x) endif | x in xs], ); } in - b <-> forall (i, j in index_set(xs) where i < j) ( - (occurs(xs[i]) /\ occurs(xs[j])) -> values[i] != values[j] - ); + b <-> forall (i, j in index_set(xs) where i < j) ( + (occurs(xs[i]) /\ occurs(xs[j])) -> values[i] != values[j] + ); diff --git a/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn b/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn index 8c7b253c7a..9f66256810 100644 --- a/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn +++ b/gecode/flatzinc/mznlib/fzn_alldifferent_except.mzn @@ -45,15 +45,13 @@ predicate fzn_alldifferent_except(array[int] of var int: xs, set of int: except) else gecode_all_different_except(xs, min(except)) endif - elseif forall (i in index_set(xs)) ( - has_bounds(xs[i]) - ) then + elseif forall (i in index_set(xs)) (has_bounds(xs[i])) then % Native distinct supports one exception; retain the standard translation % for larger exception sets. let { set of int: values = dom_array(xs) diff except; } in - global_cardinality(xs, set2array(values), [0 | i in values], [1 | i in values]) + global_cardinality(xs, set2array(values), [0 | i in values], [1 | i in values]) else forall (i, j in index_set(xs) where i < j) ( (xs[i] in except /\ xs[j] in except) \/ xs[i] != xs[j] diff --git a/gecode/flatzinc/mznlib/fzn_alldifferent_except_0.mzn b/gecode/flatzinc/mznlib/fzn_alldifferent_except_0.mzn index d99fb634e0..dede4ad0d0 100644 --- a/gecode/flatzinc/mznlib/fzn_alldifferent_except_0.mzn +++ b/gecode/flatzinc/mznlib/fzn_alldifferent_except_0.mzn @@ -36,5 +36,4 @@ include "fzn_alldifferent_except.mzn"; Constrain the array of integers \a vs to be all different except those elements that are assigned the value 0. */ -predicate fzn_alldifferent_except_0(array[int] of var int: vs) = - fzn_alldifferent_except(vs, {0}); +predicate fzn_alldifferent_except_0(array[int] of var int: vs) = fzn_alldifferent_except(vs, {0}); diff --git a/gecode/flatzinc/mznlib/fzn_count_geq.mzn b/gecode/flatzinc/mznlib/fzn_count_geq.mzn index a524b0055c..bc0c00f492 100644 --- a/gecode/flatzinc/mznlib/fzn_count_geq.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_geq.mzn @@ -38,5 +38,4 @@ predicate gecode_count_int_fzn( ); % Requires n >= the number of occurrences of value in xs. -predicate fzn_count_geq(array[int] of var int: xs, var int: value, var int: n) = - gecode_count_int_fzn(xs, value, 3, n); +predicate fzn_count_geq(array[int] of var int: xs, var int: value, var int: n) = gecode_count_int_fzn(xs, value, 3, n); diff --git a/gecode/flatzinc/mznlib/fzn_count_gt.mzn b/gecode/flatzinc/mznlib/fzn_count_gt.mzn index aa03bf5b8b..8cda47ffbb 100644 --- a/gecode/flatzinc/mznlib/fzn_count_gt.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_gt.mzn @@ -38,5 +38,4 @@ predicate gecode_count_int_fzn( ); % Requires n > the number of occurrences of value in xs. -predicate fzn_count_gt(array[int] of var int: xs, var int: value, var int: n) = - gecode_count_int_fzn(xs, value, 4, n); +predicate fzn_count_gt(array[int] of var int: xs, var int: value, var int: n) = gecode_count_int_fzn(xs, value, 4, n); diff --git a/gecode/flatzinc/mznlib/fzn_count_leq.mzn b/gecode/flatzinc/mznlib/fzn_count_leq.mzn index 6268941c11..6ffb4b953c 100644 --- a/gecode/flatzinc/mznlib/fzn_count_leq.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_leq.mzn @@ -38,5 +38,4 @@ predicate gecode_count_int_fzn( ); % Requires n <= the number of occurrences of value in xs. -predicate fzn_count_leq(array[int] of var int: xs, var int: value, var int: n) = - gecode_count_int_fzn(xs, value, 5, n); +predicate fzn_count_leq(array[int] of var int: xs, var int: value, var int: n) = gecode_count_int_fzn(xs, value, 5, n); diff --git a/gecode/flatzinc/mznlib/fzn_count_lt.mzn b/gecode/flatzinc/mznlib/fzn_count_lt.mzn index fcbfcd2327..6c82182929 100644 --- a/gecode/flatzinc/mznlib/fzn_count_lt.mzn +++ b/gecode/flatzinc/mznlib/fzn_count_lt.mzn @@ -38,5 +38,4 @@ predicate gecode_count_int_fzn( ); % Requires n < the number of occurrences of value in xs. -predicate fzn_count_lt(array[int] of var int: xs, var int: value, var int: n) = - gecode_count_int_fzn(xs, value, 6, n); +predicate fzn_count_lt(array[int] of var int: xs, var int: value, var int: n) = gecode_count_int_fzn(xs, value, 6, n); diff --git a/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn b/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn index 43f0b01bd0..43097f37f4 100644 --- a/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn +++ b/gecode/flatzinc/mznlib/fzn_cumulative_opt.mzn @@ -59,14 +59,7 @@ predicate fzn_cumulative_opt( ) = if ub(b) < 0 then false - elseif - forall (i in index_set(d)) ( - is_fixed(d[i]) - ) /\ - forall (i in index_set(r)) ( - is_fixed(r[i]) - ) - then + elseif forall (i in index_set(d)) (is_fixed(d[i])) /\ forall (i in index_set(r)) (is_fixed(r[i])) then % Zero-duration tasks consume no capacity in MiniZinc. gecode_schedule_cumulative_optional( [if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s], @@ -75,9 +68,7 @@ predicate fzn_cumulative_opt( [occurs(s[i]) /\ fix(d[i]) > 0 | i in index_set(s)], b, ) - elseif forall (i in index_set(r)) ( - is_fixed(r[i]) - ) then + elseif forall (i in index_set(r)) (is_fixed(r[i])) then let { array[int] of var int: start = [if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s]; array[int] of var int: duration = array1d(d); @@ -85,14 +76,14 @@ predicate fzn_cumulative_opt( array[int] of var bool: present = [occurs(array1d(s)[i]) /\ duration[i] > 0 | i in index_set(duration)]; array[int] of var int: end = [if present[i] then start[i] + duration[i] else 0 endif | i in index_set(start)]; } in - gecode_cumulative_flex_optional( - start, - duration, - end, - [fix(ri) | ri in r], - b, - present, - ) + gecode_cumulative_flex_optional( + start, + duration, + end, + [fix(ri) | ri in r], + b, + present, + ) else fzn_cumulative_opt_decomp(s, d, r, b) endif; diff --git a/gecode/flatzinc/mznlib/fzn_diffn.mzn b/gecode/flatzinc/mznlib/fzn_diffn.mzn index 070402e9ef..3c1abcc248 100644 --- a/gecode/flatzinc/mznlib/fzn_diffn.mzn +++ b/gecode/flatzinc/mznlib/fzn_diffn.mzn @@ -30,11 +30,16 @@ % % -predicate gecode_nooverlap(array[int] of var int: x, array[int] of var int: w, - array[int] of var int: y, array[int] of var int: h); +predicate gecode_nooverlap_fzn( + array[int] of var int: x, + array[int] of var int: w, + array[int] of var int: y, + array[int] of var int: h, +); -predicate fzn_diffn(array[int] of var int: x, - array[int] of var int: y, - array[int] of var int: dx, - array[int] of var int: dy) = - gecode_nooverlap(x,dx,y,dy); +predicate fzn_diffn( + array[int] of var int: x, + array[int] of var int: y, + array[int] of var int: dx, + array[int] of var int: dy, +) = gecode_nooverlap_fzn(x, dx, y, dy); diff --git a/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn b/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn index 2eef516a1e..d877409249 100644 --- a/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn +++ b/gecode/flatzinc/mznlib/fzn_disjunctive_strict_opt.mzn @@ -52,9 +52,7 @@ predicate fzn_disjunctive_strict_opt( forall (i in index_set(d)) ( d[i] >= 0 ) /\ - if forall (i in index_set(d)) ( - is_fixed(d[i]) - ) then + if forall (i in index_set(d)) (is_fixed(d[i])) then gecode_schedule_unary_optional( [if is_fixed(si) /\ absent(fix(si)) then 0 else deopt(si) endif | si in s], [fix(di) | di in d], @@ -67,5 +65,5 @@ predicate fzn_disjunctive_strict_opt( array[int] of var int: duration = array1d(d); array[int] of var int: end = [if present[i] then start[i] + duration[i] else 0 endif | i in index_set(start)]; } in - gecode_unary_flex_optional(start, duration, end, present) + gecode_unary_flex_optional(start, duration, end, present) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn index 4913e693d1..f0df281203 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn @@ -43,21 +43,26 @@ predicate fzn_if_then_else_bool( array[int] of bool: x, var bool: y, ) = - if length(c) = 2 /\ index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_bool_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { array[index_set(c)] of var bool: d; } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn index b92d0484ec..403eebe5ff 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn @@ -43,21 +43,26 @@ predicate fzn_if_then_else_float( array[int] of float: x, var float: y, ) = - if length(c) = 2 /\ index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_float_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { array[index_set(c)] of var bool: d; } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn index db9c5da426..05150f8807 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn @@ -43,21 +43,26 @@ predicate fzn_if_then_else_int( array[int] of int: x, var int: y, ) = - if length(c) = 2 /\ index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_int_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { array[index_set(c)] of var bool: d; } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn index 622c77a647..38b42136a3 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn @@ -43,21 +43,26 @@ predicate fzn_if_then_else_set( array[int] of set of int: x, var set of int: y, ) = - if length(c) = 2 /\ index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_set_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { array[index_set(c)] of var bool: d; } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn index 19f7bed8a0..19361e62ed 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn @@ -43,21 +43,26 @@ predicate fzn_if_then_else_var_bool( array[int] of var bool: x, var bool: y, ) = - if length(c) = 2 /\ index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_bool_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { array[index_set(c)] of var bool: d; } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn index d9c5677933..0fe194b7fa 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn @@ -43,21 +43,26 @@ predicate fzn_if_then_else_var_float( array[int] of var float: x, var float: y, ) = - if length(c) = 2 /\ index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_float_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { array[index_set(c)] of var bool: d; } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn index 3369aa7aa9..c71606ac6d 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn @@ -43,21 +43,26 @@ predicate fzn_if_then_else_var_int( array[int] of var int: x, var int: y, ) = - if length(c) = 2 /\ index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_int_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { array[index_set(c)] of var bool: d; } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn index ab4151ab8f..248e88277c 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn @@ -43,21 +43,26 @@ predicate fzn_if_then_else_var_set( array[int] of var set of int: x, var set of int: y, ) = - if length(c) = 2 /\ index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then fix(c[max(index_set(c))]) else false endif) then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_set_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { array[index_set(c)] of var bool: d; } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) + forall (i in index_set(c)) ( + if i > min(index_set(c)) then + d[i] = (not c[i - 1] /\ d[i - 1]) + else + d[i] = true + endif + ) /\ + forall (i in index_set(c)) ( + c[i] /\ d[i] -> y = x[i] + ) endif; diff --git a/gecode/flatzinc/mznlib/gecode.mzn b/gecode/flatzinc/mznlib/gecode.mzn index 30edda0308..fa5f2eb441 100644 --- a/gecode/flatzinc/mznlib/gecode.mzn +++ b/gecode/flatzinc/mznlib/gecode.mzn @@ -181,85 +181,54 @@ predicate gecode_schedule_cumulative_optional(array[int] of var int: start, array[int] of var bool: m, var int: capacity); +include "all_different.mzn"; +include "among_fn.mzn"; include "gecode_fzn.mzn"; -include "among.mzn"; -include "nvalue.mzn"; -include "range.mzn"; -include "arg_min.mzn"; -include "arg_max.mzn"; - -/** @group gecode.constraints - Comparison operators for integer values and counts. -*/ -enum GecodeIntRelation = { - GecodeEq, - GecodeNe, - GecodeLe, - GecodeLt, - GecodeGe, - GecodeGt, -}; - -/** @group gecode.constraints - Comparison operators for sets. Complement uses Gecode's full set - universe. Ordering compares membership from the smallest element, rather - than comparing sorted lists of elements. -*/ -enum GecodeSetRelation = { - GecodeSetEq, - GecodeSetNe, - GecodeSubset, - GecodeSuperset, - GecodeDisjoint, - GecodeComplement, - GecodeSetLe, - GecodeSetLt, - GecodeSetGe, - GecodeSetGt, -}; -/** @group gecode.constraints - Union, disjoint union, intersection, and difference of sets. -*/ -enum GecodeSetOperation = { - GecodeUnion, - GecodeDisjointUnion, - GecodeIntersection, - GecodeDifference, -}; - -/** @group gecode.constraints - Describes the fixed part of a task. - GecodeFixedDuration uses flex as start and fix as duration. - GecodeFixedStart uses flex as end and fix as start. - GecodeFixedEnd uses flex as start and fix as end. - Fixed durations must be nonnegative. -*/ -enum GecodeTaskType = { - GecodeFixedDuration, - GecodeFixedStart, - GecodeFixedEnd, -}; - -/** @group gecode.constraints - Requires all values \a x[i] + \a offset[i] to be distinct. - The arrays must have equal lengths and correspond by position. -*/ -predicate gecode_all_different( - array[int] of int: offset, - array[int] of var int: x, +include "nvalue_fn.mzn"; + +/** @group gecode.constraints + Requires every pair of values in \a x to differ by at least \a distance. + The distance must be nonnegative, including for empty and singleton arrays. +*/ +predicate gecode_inter_distance(array[$$X] of var int: x, var int: distance) = + if mzn_in_root_context() then + gecode_inter_distance_fzn(array1d(x), distance) + else + distance >= 0 /\ + forall (i, j in index_set(x) where i < j) ( + abs(x[i] - x[j]) >= distance + ) + endif; + +/** @group gecode.constraints + Requires the sums \a x[i] + \a offset[i] to be pairwise distinct. + The arrays must have identical index sets. +*/ +predicate gecode_all_different_offset( + array[$$X] of int: offset, + array[$$X] of var int: x, ) = - gecode_all_different_offset(offset, x); + assert( + index_set(offset) = index_set(x), + "gecode_all_different_offset: index set mismatch", + if mzn_in_root_context() then + gecode_all_different_offset_fzn(array1d(offset), array1d(x)) + else + all_different([x[i] + offset[i] | i in index_set(x)]) + endif, + ); /** @group gecode.constraints Constrains \a y to be the sorted values of \a x, with \a x[i] = \a y[\a permutation[i]]. Equal values may appear in either order. - The index sets of \a x and \a permutation must agree, and all arrays must - have equal lengths. + The index sets of \a x and \a permutation must agree, and all arrays + must have equal lengths. + Reification is not supported. */ predicate gecode_sort_permutation( - array[int] of var int: x, - array[int] of var int: y, - array[int] of var int: permutation, + array[$$X] of var $$V: x, + array[$$Y] of var $$V: y, + array[$$X] of var $$Y: permutation, ) = assert( index_set(x) = index_set(permutation) /\ length(x) = length(y), @@ -267,122 +236,176 @@ predicate gecode_sort_permutation( if length(y) = 0 then true else - gecode_sort_permutation(x, y, permutation, min(index_set(y))) + gecode_sort_permutation( + enum2int(array1d(x)), + enum2int(array1d(y)), + enum2int(array1d(permutation)), + min(enum2int(index_set(y))), + ) endif, ); /** @group gecode.constraints - Compares the number of elements of \a x that belong to \a values - with \a n, using \a relation. + Requires at least one corresponding pair of elements in \a x and \a y + to differ. The arrays must have identical index sets. */ -predicate gecode_count( - array[int] of var int: x, - set of int: values, - GecodeIntRelation: relation, - var int: n, -) = - if relation = GecodeEq then - among(n, x, values) - elseif relation = GecodeNe then - let { - var 0 .. length(x): c; - constraint among(c, x, values); - } in - c != n - else - gecode_count_in_fzn(x, values, enum2int(relation), n) - endif; +predicate gecode_array_ne(array[$$X] of var $$E: x, array[$$X] of var $$E: y) = + assert( + index_set(x) = index_set(y), + "gecode_array_ne: index set mismatch", + if mzn_in_root_context() then + gecode_array_int_ne(enum2int(array1d(x)), enum2int(array1d(y))) + else + exists (i in index_set(x)) (x[i] != y[i]) + endif, + ); + /** @group gecode.constraints - Compares the number of positions where \a x matches \a values - with \a n, using \a relation. The arrays must have equal lengths and - correspond by position. + Requires at least one corresponding pair of Boolean elements in \a x + and \a y to differ. The arrays must have identical index sets. */ -predicate gecode_count( - array[int] of var int: x, - array[int] of int: values, - GecodeIntRelation: relation, - var int: n, -) = +predicate gecode_array_ne(array[$$X] of var bool: x, array[$$X] of var bool: y) = assert( - length(x) = length(values), - "gecode_count: unequal array lengths", - gecode_count_matches_fzn(x, values, enum2int(relation), n), + index_set(x) = index_set(y), + "gecode_array_ne: index set mismatch", + if mzn_in_root_context() then + gecode_array_bool_ne(array1d(x), array1d(y)) + else + exists (i in index_set(x)) (x[i] != y[i]) + endif, ); /** @group gecode.constraints - Compares the number of distinct values in \a x with \a n, using - \a relation. An empty array has zero distinct values. + Requires the number of distinct values in \a x to be at most \a n. + An empty array has zero distinct values. */ -predicate gecode_nvalues( - array[int] of var int: x, - GecodeIntRelation: relation, - var int: n, -) = - if relation = GecodeEq then - nvalue(n, x) - elseif relation = GecodeNe then - let { - var 0 .. length(x): c; - constraint nvalue(c, x); - } in - c != n +predicate gecode_nvalue_at_most(var int: n, array[$$X] of var $$E: x) = + if mzn_in_root_context() then + gecode_nvalues_int_fzn(enum2int(array1d(x)), 3, n) else - gecode_nvalues_int_fzn(x, enum2int(relation), n) + nvalue(x) <= n endif; + /** @group gecode.constraints - Compares the number of distinct values in \a x with \a n, using - \a relation. An empty array has zero distinct values. + Requires the number of distinct values in \a x to be at most \a n. + An empty array has zero distinct values. */ -predicate gecode_nvalues( - array[int] of var bool: x, - GecodeIntRelation: relation, - var int: n, -) = - if relation = GecodeNe then - let { - var 0 .. 2: c; - constraint gecode_nvalues_bool_fzn(x, enum2int(GecodeEq), c); - } in - c != n +predicate gecode_nvalue_at_most(var int: n, array[$$X] of var bool: x) = + if mzn_in_root_context() then + gecode_nvalues_bool_fzn(array1d(x), 3, n) + else + nvalue(x) <= n + endif; + +/** @group gecode.constraints + Requires the number of distinct values in \a x to be at least \a n. + An empty array has zero distinct values. +*/ +predicate gecode_nvalue_at_least(var int: n, array[$$X] of var $$E: x) = + if mzn_in_root_context() then + gecode_nvalues_int_fzn(enum2int(array1d(x)), 5, n) + else + nvalue(x) >= n + endif; + +/** @group gecode.constraints + Requires the number of distinct values in \a x to be at least \a n. + An empty array has zero distinct values. +*/ +predicate gecode_nvalue_at_least(var int: n, array[$$X] of var bool: x) = + if mzn_in_root_context() then + gecode_nvalues_bool_fzn(array1d(x), 5, n) + else + nvalue(x) >= n + endif; + +/** @group gecode.constraints + Requires at most \a n elements of \a x to belong to \a values. +*/ +predicate gecode_among_at_most(var int: n, array[$$X] of var $$E: x, set of $$E: values) = + if mzn_in_root_context() then + gecode_count_in_fzn(enum2int(array1d(x)), enum2int(values), 3, n) + else + among(x, values) <= n + endif; + +/** @group gecode.constraints + Requires at least \a n elements of \a x to belong to \a values. +*/ +predicate gecode_among_at_least(var int: n, array[$$X] of var $$E: x, set of $$E: values) = + if mzn_in_root_context() then + gecode_count_in_fzn(enum2int(array1d(x)), enum2int(values), 5, n) else - gecode_nvalues_bool_fzn(x, enum2int(relation), n) + among(x, values) >= n endif; +/** @group gecode.constraints + Requires at most \a n positions where \a x matches \a values. + The arrays must have identical index sets. +*/ +predicate gecode_count_matches_at_most(var int: n, array[$$X] of var $$E: x, array[$$X] of $$E: values) = + assert( + index_set(x) = index_set(values), + "gecode_count_matches_at_most: index set mismatch", + if mzn_in_root_context() then + gecode_count_matches_fzn(enum2int(array1d(x)), enum2int(array1d(values)), 3, n) + else + sum (i in index_set(x)) (bool2int(x[i] = values[i])) <= n + endif, + ); + +/** @group gecode.constraints + Requires at least \a n positions where \a x matches \a values. + The arrays must have identical index sets. +*/ +predicate gecode_count_matches_at_least(var int: n, array[$$X] of var $$E: x, array[$$X] of $$E: values) = + assert( + index_set(x) = index_set(values), + "gecode_count_matches_at_least: index set mismatch", + if mzn_in_root_context() then + gecode_count_matches_fzn(enum2int(array1d(x)), enum2int(array1d(values)), 5, n) + else + sum (i in index_set(x)) (bool2int(x[i] = values[i])) >= n + endif, + ); + +/** @group gecode.constraints + Constrains \a n to be the number of positions where \a x matches + \a values. The arrays must have identical index sets. +*/ +predicate gecode_count_matches( + var int: n, + array[$$X] of var $$E: x, + array[$$X] of $$E: values, +) = + assert( + index_set(x) = index_set(values), + "gecode_count_matches: index set mismatch", + if mzn_in_root_context() then + gecode_count_matches_fzn(enum2int(array1d(x)), enum2int(array1d(values)), 1, n) + else + sum (i in index_set(x)) (bool2int(x[i] = values[i])) = n + endif, + ); + /** @group gecode.constraints Constrains \a result to be the nonnegative greatest common divisor of \a x and \a y. Negative operands use their absolute values, and gcd(0, 0) = 0. */ -predicate gecode_gcd(var int: x, var int: y, var int: result) = - gecode_gcd_int(x, y, result); +predicate gecode_gcd(var int: x, var int: y, var int: result) = gecode_gcd_int(x, y, result); /** @group gecode.constraints Requires \a dividend to be an integer multiple of \a divisor. A zero divisor is permitted only when \a dividend is zero. */ -predicate gecode_divides(var int: divisor, var int: dividend) = - gecode_divides_int(divisor, dividend); +predicate gecode_divides(var int: divisor, var int: dividend) = gecode_divides_int(divisor, dividend); /** @group gecode.constraints - Constrains \a b to be true if and only if the product of \a x - equals \a result. The product of an empty array is one. -*/ -predicate gecode_product_reif( - array[int] of var int: x, - var int: result, - var bool: b, -) = - gecode_product_int_reif(x, result, b); -/** @group gecode.constraints - Requires the product of \a x to equal \a result when \a b is true. + Constrains \a result to be the product of \a x. The product of an empty array is one. */ -predicate gecode_product_imp( - array[int] of var int: x, - var int: result, - var bool: b, -) = - gecode_product_int_imp(x, result, b); +predicate gecode_product(array[$$X] of var int: x, var int: result) = gecode_product_int(array1d(x), result); /** @group gecode.constraints Constrains \a result to be the Euclidean remainder of the product @@ -391,18 +414,18 @@ predicate gecode_product_imp( The product of an empty array is one. */ predicate gecode_product_mod( - array[int] of var int: x, + array[$$X] of var int: x, var int: modulus, var int: result, ) = if is_fixed(modulus) then if fix(modulus) > 0 then - gecode_product_mod_fixed_fzn(x, fix(modulus), result) + gecode_product_mod_fixed_fzn(array1d(x), fix(modulus), result) else - gecode_product_mod_var_fzn(x, modulus, result) + gecode_product_mod_var_fzn(array1d(x), modulus, result) endif else - gecode_product_mod_var_fzn(x, modulus, result) + gecode_product_mod_var_fzn(array1d(x), modulus, result) endif; /** @group gecode.constraints @@ -411,10 +434,11 @@ predicate gecode_product_mod( There must be at least two positions. Both matrix axes use the same site indexes, and the matrix must be symmetric and nonnegative with a zero diagonal. Repeated sites are permitted. + Reification is not supported. */ predicate gecode_minimum_distance( - array[int] of var int: sites, - array[int, int] of int: distances, + array[$$Position] of var $$Site: sites, + array[$$Site, $$Site] of int: distances, var int: result, ) = assert( @@ -423,12 +447,13 @@ predicate gecode_minimum_distance( index_set_1of2(distances) = index_set_2of2(distances), "gecode_minimum_distance: need at least two positions and a square nonempty distance matrix", gecode_minimum_distance_fzn( - [v - min(index_set_1of2(distances)) | v in sites], + [enum2int(v) - min(enum2int(index_set_1of2(distances))) | v in sites], card(index_set_1of2(distances)), array1d(distances), result, ), ); + /** @group gecode.constraints Constrains \a result to be the smallest distance between any two positions in \a sites. Each pair must also meet the lower bound in @@ -436,12 +461,13 @@ predicate gecode_minimum_distance( Both axes of \a distances use the same site indexes, and both axes of \a requirements use the index set of \a sites. Both matrices must be symmetric and nonnegative with a zero diagonal. Repeated sites are permitted. + Reification is not supported. */ predicate gecode_minimum_distance( - array[int] of var int: sites, - array[int, int] of int: distances, + array[$$Position] of var $$Site: sites, + array[$$Site, $$Site] of int: distances, var int: result, - array[int, int] of int: requirements, + array[$$Position, $$Position] of int: requirements, ) = assert( length(sites) >= 2 /\ @@ -451,7 +477,7 @@ predicate gecode_minimum_distance( index_set_2of2(requirements) = index_set(sites), "gecode_minimum_distance: incompatible distance/requirements matrix or too few positions", gecode_minimum_distance_required_fzn( - [v - min(index_set_1of2(distances)) | v in sites], + [enum2int(v) - min(enum2int(index_set_1of2(distances))) | v in sites], card(index_set_1of2(distances)), array1d(distances), result, @@ -463,6 +489,7 @@ predicate gecode_minimum_distance( Constrains \a quotient = \a x div \a y and \a remainder = \a x mod \a y. The divisor \a y must be nonzero. Division truncates towards zero; a nonzero remainder has the sign of \a x. + Reification is not supported. */ predicate gecode_divmod( var int: x, @@ -475,18 +502,21 @@ predicate gecode_divmod( var compute_div_bounds(x, y): q = quotient; var compute_mod_bounds(x, y): r = remainder; } in - gecode_int_divmod(x, y, q, r); + gecode_int_divmod(x, y, q, r); /** @group gecode.constraints Constrains \a y to be the integer root of degree \a degree of \a x, rounded down. The degree must be positive, and negative \a x is permitted only for odd degrees. + Reification is not supported. */ predicate gecode_nroot(var int: x, int: degree, var int: y) = assert(degree > 0, "gecode_nroot: degree must be positive", gecode_int_nroot(x, degree, y)); + /** @group gecode.constraints Constrains \a y to be the nonnegative root of degree \a degree of \a x. The degree must be positive, and \a x must be nonnegative. + Reification is not supported. */ predicate gecode_nroot(var float: x, int: degree, var float: y) = assert( @@ -500,363 +530,206 @@ predicate gecode_nroot(var float: x, int: degree, var float: y) = ); /** @group gecode.constraints - Requires \a x to describe a path from \a start to \a end that - visits every node once, where \a x[i] is the successor of node i. - The node array must be nonempty. Its indexes identify the nodes, and - \a x[\a end] = max(index_set(\a x)) + 1 marks the end of the path. + Requires \a successor to describe a path from \a start to \a end + that visits every node once. The array indexes identify the nodes. + Only \a successor[\a end] is absent. The node array must be nonempty. + Reification is not supported. */ -predicate gecode_path(array[int] of var int: x, var int: start, var int: end) = +predicate gecode_path( + array[$$Node] of var opt $$Node: successor, + var $$Node: start, + var $$Node: end, +) = assert( - length(x) > 0, + length(successor) > 0, "gecode_path: empty node array", gecode_path( 0, - [v - min(index_set(x)) | v in x], - start - min(index_set(x)), - end - min(index_set(x)), + gecode_path_successors(successor), + enum2int(start) - min(enum2int(index_set(successor))), + enum2int(end) - min(enum2int(index_set(successor))), ), ); + /** @group gecode.constraints - Requires \a x to describe a path from \a start to \a end that - visits every node once, with total edge cost \a cost given by \a costs. + Requires \a successor to describe a path from \a start to \a end + that visits every node once, with total edge cost \a cost. + Only \a successor[\a end] is absent, and its edge costs zero. + The edge from i to j has cost \a costs[i,j]. Both matrix axes must use the index set of the nonempty node array. - The successor \a x[\a end] = max(index_set(\a x)) + 1 marks the end, - and its edge costs zero. + Reification is not supported. */ predicate gecode_path( - array[int, int] of int: costs, - array[int] of var int: x, - var int: start, - var int: end, + array[$$Node, $$Node] of int: costs, + array[$$Node] of var opt $$Node: successor, + var $$Node: start, + var $$Node: end, var int: cost, ) = assert( - length(x) > 0 /\ - index_set_1of2(costs) = index_set(x) /\ - index_set_2of2(costs) = index_set(x), + length(successor) > 0 /\ + index_set_1of2(costs) = index_set(successor) /\ + index_set_2of2(costs) = index_set(successor), "gecode_path: incompatible cost matrix", gecode_path_cost( array1d(costs), 0, - [v - min(index_set(x)) | v in x], - start - min(index_set(x)), - end - min(index_set(x)), + gecode_path_successors(successor), + enum2int(start) - min(enum2int(index_set(successor))), + enum2int(end) - min(enum2int(index_set(successor))), cost, ), ); + /** @group gecode.constraints - Requires \a x to describe a path from \a start to \a end that - visits every node once, with total cost \a cost. Each \a edge_costs[i] - is the cost of the edge from i to \a x[i], as given by \a costs. - Both matrix axes and \a edge_costs must use the index set of the nonempty - node array. The successor \a x[\a end] = max(index_set(\a x)) + 1 marks - the end, and its edge costs zero. + Requires \a successor to describe a path from \a start to \a end + that visits every node once. Each \a edge_costs[i] is the cost of + the edge from i to \a successor[i], and their sum is \a cost. + Only \a successor[\a end] is absent, and its edge costs zero. + The edge from i to j has cost \a costs[i,j]. + All array axes must use the index set of the nonempty node array. + Reification is not supported. */ predicate gecode_path( - array[int, int] of int: costs, - array[int] of var int: x, - var int: start, - var int: end, - array[int] of var int: edge_costs, + array[$$Node, $$Node] of int: costs, + array[$$Node] of var opt $$Node: successor, + var $$Node: start, + var $$Node: end, + array[$$Node] of var int: edge_costs, var int: cost, ) = assert( - length(x) > 0 /\ - index_set_1of2(costs) = index_set(x) /\ - index_set_2of2(costs) = index_set(x) /\ - index_set(edge_costs) = index_set(x), + length(successor) > 0 /\ + index_set_1of2(costs) = index_set(successor) /\ + index_set_2of2(costs) = index_set(successor) /\ + index_set(edge_costs) = index_set(successor), "gecode_path: incompatible cost matrix or edge costs", gecode_path_cost_array( array1d(costs), 0, - [v - min(index_set(x)) | v in x], - start - min(index_set(x)), - end - min(index_set(x)), - edge_costs, + gecode_path_successors(successor), + enum2int(start) - min(enum2int(index_set(successor))), + enum2int(end) - min(enum2int(index_set(successor))), + array1d(edge_costs), cost, ), ); -/** @group gecode.constraints - Requires the tasks described by \a types, \a flex, and \a fix - not to overlap. GecodeTaskType specifies which start, end, or duration is - fixed. The arrays must have equal lengths and correspond by position. -*/ -predicate gecode_unary_typed( - array[int] of GecodeTaskType: types, - array[int] of var int: flex, - array[int] of int: fix, +% The native successor representation uses an extra node to mark the end. +function array[int] of var int: gecode_path_successors( + array[$$Node] of var opt $$Node: successor, ) = - gecode_unary_typed_fzn(enum2int(types), flex, fix); -/** @group gecode.constraints - Requires the present tasks described by \a types, \a flex, and - \a fix not to overlap. A task is present when its entry in \a present - is true. GecodeTaskType specifies which start, end, or duration is fixed. - The arrays must have equal lengths and correspond by position. -*/ -predicate gecode_unary_typed( - array[int] of GecodeTaskType: types, - array[int] of var int: flex, - array[int] of int: fix, - array[int] of var bool: present, -) = - gecode_unary_typed_optional_fzn(enum2int(types), flex, fix, present); - -/** @group gecode.constraints - Requires the total \a usage of tasks executing at any time not - to exceed \a capacity. GecodeTaskType specifies how \a types, \a flex, - and \a fix describe each task. Usage and capacity must be nonnegative. - The arrays must have equal lengths and correspond by position. -*/ -predicate gecode_cumulative_typed( - array[int] of GecodeTaskType: types, - array[int] of var int: flex, - array[int] of int: fix, - array[int] of int: usage, - var int: capacity, -) = - gecode_cumulative_typed_fzn(enum2int(types), flex, fix, usage, capacity); -/** @group gecode.constraints - Requires the total \a usage of present tasks executing at any - time not to exceed \a capacity. A task is present when its entry in - \a present is true. GecodeTaskType specifies how \a types, \a flex, - and \a fix describe each task. Usage and capacity must be nonnegative. - The arrays must have equal lengths and correspond by position. -*/ -predicate gecode_cumulative_typed( - array[int] of GecodeTaskType: types, - array[int] of var int: flex, - array[int] of int: fix, - array[int] of int: usage, - var int: capacity, - array[int] of var bool: present, -) = - gecode_cumulative_typed_optional_fzn(enum2int(types), flex, fix, usage, capacity, present); + let { + constraint forall (next in successor) ( + occurs(next) -> deopt(next) in index_set(successor) + ); + } in + [if occurs(next) then + enum2int(deopt(next)) - min(enum2int(index_set(successor))) + else + length(successor) + endif | next in successor]; /** @group gecode.constraints Requires the present rectangles with origins (\a x[i], \a y[i]) - and fixed sizes (\a width[i], \a height[i]) not to overlap. - A rectangle is present when \a present[i] is true. - Sizes must be nonnegative. All arrays must have identical index sets. -*/ -predicate gecode_nooverlap( - array[int] of var int: x, - array[int] of int: width, - array[int] of var int: y, - array[int] of int: height, - array[int] of var bool: present, -) = - assert( - index_set(x) = index_set(width) /\ - index_set(x) = index_set(y) /\ - index_set(x) = index_set(height) /\ - index_set(x) = index_set(present), - "gecode_nooverlap: incompatible indexes", - gecode_nooverlap_optional(x, width, y, height, present), - ); -/** @group gecode.constraints - Requires the present rectangles not to overlap, with horizontal - bounds \a x and \a right and vertical bounds \a y and \a top. - A rectangle is present when \a present[i] is true. - For each present rectangle, \a x + \a width = \a right and - \a y + \a height = \a top. Sizes must be nonnegative. - All arrays must have identical index sets. + and sizes (\a width[i], \a height[i]) not to overlap. + The two origins of a rectangle must be absent together. + Zero-sized rectangles still participate in non-overlap. + Sizes must be nonnegative, and all arrays must have identical index sets. + Reification is not supported. */ predicate gecode_nooverlap( - array[int] of var int: x, - array[int] of var int: width, - array[int] of var int: right, - array[int] of var int: y, - array[int] of var int: height, - array[int] of var int: top, - array[int] of var bool: present, + array[$$X] of var opt int: x, + array[$$X] of var int: width, + array[$$X] of var opt int: y, + array[$$X] of var int: height, ) = assert( index_set(x) = index_set(width) /\ - index_set(x) = index_set(right) /\ index_set(x) = index_set(y) /\ - index_set(x) = index_set(height) /\ - index_set(x) = index_set(top) /\ - index_set(x) = index_set(present), - "gecode_nooverlap: incompatible indexes", + index_set(x) = index_set(height), + "gecode_nooverlap: index set mismatch", forall (i in index_set(x)) ( - present[i] -> (x[i] + width[i] = right[i] /\ y[i] + height[i] = top[i]) + occurs(x[i]) = occurs(y[i]) /\ width[i] >= 0 /\ height[i] >= 0 ) /\ - gecode_nooverlap_flex_optional(x, width, right, y, height, top, present), + let { + array[int] of var bool: present = [occurs(xi) | xi in x]; + array[int] of var int: xx = [xi default 0 | xi in x]; + array[int] of var int: yy = [yi default 0 | yi in y]; + } in + if forall (w in width) (is_fixed(w)) /\ forall (h in height) (is_fixed(h)) then + gecode_nooverlap_optional( + xx, + [fix(w) | w in width], + yy, + [fix(h) | h in height], + present, + ) + else + let { + array[int] of var int: w = array1d(width); + array[int] of var int: h = array1d(height); + array[int] of var int: right = [if present[i] then xx[i] + w[i] else 0 endif | i in index_set(xx)]; + array[int] of var int: top = [if present[i] then yy[i] + h[i] else 0 endif | i in index_set(yy)]; + } in + gecode_nooverlap_flex_optional(xx, w, right, yy, h, top, present) + endif, ); /** @group gecode.constraints - Requires \a x to be empty or a consecutive interval of integers. + Requires \a x to be empty or a consecutive interval of values. + Enum values follow their declaration order. + Reification is not supported. */ -predicate gecode_convex(var set of int: x) = - gecode_set_convex(x); +predicate gecode_convex(var set of $$E: x) = gecode_set_convex(enum2int(x)); + /** @group gecode.constraints Constrains \a hull to be the smallest consecutive interval containing \a x. The hull of an empty set is empty. + Enum values follow their declaration order. + Reification is not supported. */ -predicate gecode_convex(var set of int: x, var set of int: hull) = - gecode_set_convex_hull(x, hull); +predicate gecode_convex(var set of $$E: x, var set of $$E: hull) = gecode_set_convex_hull(enum2int(x), enum2int(hull)); /** @group gecode.constraints Requires every element of each set in \a x to be smaller than every element of each later set. Empty sets are permitted. + Reification is not supported. */ -predicate gecode_set_sequence(array[int] of var set of int: x) = - gecode_array_set_seq(x); +predicate gecode_set_sequence(array[$$X] of var set of $$E: x) = gecode_array_set_seq(enum2int(array1d(x))); + /** @group gecode.constraints Requires every element of each set in \a x to be smaller than every element of each later set, and constrains \a y to their union. Empty sets are permitted. + Reification is not supported. */ -predicate gecode_set_sequence(array[int] of var set of int: x, var set of int: y) = - gecode_array_set_seq_union(x, y); +predicate gecode_set_sequence(array[$$X] of var set of $$E: x, var set of $$E: y) = + gecode_array_set_seq_union(enum2int(array1d(x)), enum2int(y)); /** @group gecode.constraints - Compares the result of applying \a operation to \a x and \a y - with \a z, using \a relation. Complement and set ordering follow - GecodeSetRelation. + Requires each pair of sets in \a x to share at most one element, + and every set to have cardinality \a size. + Reification is not supported. */ -predicate gecode_set_operation( - var set of int: x, - GecodeSetOperation: operation, - var set of int: y, - GecodeSetRelation: relation, - var set of int: z, -) = - if relation = GecodeSetEq /\ operation = GecodeUnion then - z = x union y - elseif relation = GecodeSetEq /\ operation = GecodeIntersection then - z = x intersect y - elseif relation = GecodeSetEq /\ operation = GecodeDifference then - z = x diff y - else - gecode_set_operation_fzn(x, enum2int(operation), y, enum2int(relation), z) - endif; - -/** @group gecode.constraints - Combines \a initial and the singleton sets {\a x[i]} using - \a operation, and constrains \a y to the result. Union, disjoint union, - and intersection are supported. -*/ -predicate gecode_array_int_set_operation( - array[int] of var int: x, - GecodeSetOperation: operation, - set of int: initial, - var set of int: y, -) = +predicate gecode_at_most_one_intersection(array[$$X] of var set of $$E: x, int: size) = assert( - operation != GecodeDifference, - "gecode_array_int_set_operation: difference is unsupported", - gecode_array_int_set_operation_fzn(x, enum2int(operation), initial, y), + size >= 0, + "gecode_at_most_one_intersection: negative cardinality", + gecode_set_atmost_one(enum2int(array1d(x)), size), ); /** @group gecode.constraints - Combines the singleton sets {\a x[i]} at the zero-based positions - in \a selected, using \a operation, and constrains \a value to the result. - Union, disjoint union, and intersection are supported. An empty selection - gives \a universe for intersection and the empty set for either union. + Constrains \a x to be the elements of \a values in strictly increasing order. + Reification is not supported. */ -predicate gecode_array_int_set_element( - array[int] of var int: x, - GecodeSetOperation: operation, - var set of int: selected, - var set of int: value, - set of int: universe, -) = - assert( - operation != GecodeDifference, - "gecode_array_int_set_element: difference is unsupported", - if operation = GecodeUnion then - gecode_range(x, 0, selected, value) - else - gecode_array_int_set_element_fzn(x, enum2int(operation), selected, value, universe) - endif, - ); - -/** @group gecode.constraints - Constrains \a b to be true if and only if \a s is nonempty and - \a x is its smallest element. -*/ -predicate gecode_min_reif(var set of int: s, var int: x, var bool: b) = - gecode_set_min_reif(s, x, b); -/** @group gecode.constraints - Constrains \a b to be true if and only if \a s is nonempty and - \a x is its largest element. -*/ -predicate gecode_max_reif(var set of int: s, var int: x, var bool: b) = - gecode_set_max_reif(s, x, b); - -/** @group gecode.constraints - Compares \a s with the singleton set {\a x}, using \a relation. - Complement and set ordering follow GecodeSetRelation. -*/ -predicate gecode_set_singleton_rel( - var set of int: s, - GecodeSetRelation: relation, - var int: x, -) = - if relation = GecodeSuperset then - x in s - else - gecode_set_singleton_rel_fzn(s, enum2int(relation), x) - endif; -/** @group gecode.constraints - Constrains \a b to be true if and only if \a s has \a relation - to the singleton set {\a x}. Complement and set ordering follow - GecodeSetRelation. -*/ -predicate gecode_set_singleton_rel_reif( - var set of int: s, - GecodeSetRelation: relation, - var int: x, - var bool: b, -) = - if relation = GecodeSuperset then - b <-> x in s - else - gecode_set_singleton_rel_fzn_reif(s, enum2int(relation), x, b) - endif; -/** @group gecode.constraints - Requires \a s to have \a relation to the singleton set {\a x} - when \a b is true. Complement and set ordering follow GecodeSetRelation. -*/ -predicate gecode_set_singleton_rel_imp( - var set of int: s, - GecodeSetRelation: relation, - var int: x, - var bool: b, -) = - if relation = GecodeSuperset then - b -> x in s - else - gecode_set_singleton_rel_fzn_imp(s, enum2int(relation), x, b) - endif; - -/** @group gecode.constraints - Requires \a x to have \a relation to \a y when \a b is true. - Complement and set ordering follow GecodeSetRelation. -*/ -predicate gecode_rel_imp( - var set of int: x, - GecodeSetRelation: relation, - var set of int: y, - var bool: b, -) = - if relation = GecodeSetEq then - b -> x = y - elseif relation = GecodeSetNe then - b -> x != y - elseif relation = GecodeSubset then - b -> x subset y - elseif relation = GecodeSuperset then - b -> x superset y - else - gecode_set_rel_imp_fzn(x, enum2int(relation), y, b) - endif; +predicate gecode_sorted_set(array[$$X] of var $$E: x, var set of $$E: values) = + gecode_set_channel_sorted(enum2int(array1d(x)), enum2int(values)); /** @group gecode.constraints Constrains \a y to be the logarithm of \a x to base \a base. The base must be positive and different from one. Requires MPFR support. + Reification is not supported. */ predicate gecode_log(float: base, var float: x, var float: y) = assert( @@ -873,89 +746,80 @@ predicate gecode_log(float: base, var float: x, var float: y) = /** @group gecode.constraints Constrains \a index to be the index of a smallest value in \a x. - If \a break_ties is true, the smallest qualifying index is required; - otherwise any qualifying index is permitted. The array must be nonempty. + Any qualifying index is permitted. The array must be nonempty. + Reification is not supported. */ -predicate gecode_arg_min( - array[int] of var int: x, - var int: index, - bool: break_ties, -) = +predicate gecode_arg_min_any(array[$$X] of var $$E: x, var $$X: index) = assert( length(x) > 0, - "gecode_arg_min: empty array", - if break_ties then - arg_min(array1d(x)) + min(index_set(x)) - 1 = index - else - gecode_arg_min_int(x, 0, index - min(index_set(x)), false) - endif, + "gecode_arg_min_any: empty array", + gecode_arg_min_int( + enum2int(array1d(x)), + 0, + enum2int(index) - min(enum2int(index_set(x))), + false, + ), ); + /** @group gecode.constraints Constrains \a index to be the index of a smallest value in \a x. - If \a break_ties is true, the smallest qualifying index is required; - otherwise any qualifying index is permitted. The array must be nonempty. + Any qualifying index is permitted. The array must be nonempty. + Reification is not supported. */ -predicate gecode_arg_min( - array[int] of var bool: x, - var int: index, - bool: break_ties, -) = +predicate gecode_arg_min_any(array[$$X] of var bool: x, var $$X: index) = assert( length(x) > 0, - "gecode_arg_min: empty array", - if break_ties then - arg_min(array1d(x)) + min(index_set(x)) - 1 = index - else - gecode_arg_min_bool(x, 0, index - min(index_set(x)), false) - endif, + "gecode_arg_min_any: empty array", + gecode_arg_min_bool( + array1d(x), + 0, + enum2int(index) - min(enum2int(index_set(x))), + false, + ), ); + /** @group gecode.constraints Constrains \a index to be the index of a largest value in \a x. - If \a break_ties is true, the smallest qualifying index is required; - otherwise any qualifying index is permitted. The array must be nonempty. + Any qualifying index is permitted. The array must be nonempty. + Reification is not supported. */ -predicate gecode_arg_max( - array[int] of var int: x, - var int: index, - bool: break_ties, -) = +predicate gecode_arg_max_any(array[$$X] of var $$E: x, var $$X: index) = assert( length(x) > 0, - "gecode_arg_max: empty array", - if break_ties then - arg_max(array1d(x)) + min(index_set(x)) - 1 = index - else - gecode_arg_max_int(x, 0, index - min(index_set(x)), false) - endif, + "gecode_arg_max_any: empty array", + gecode_arg_max_int( + enum2int(array1d(x)), + 0, + enum2int(index) - min(enum2int(index_set(x))), + false, + ), ); + /** @group gecode.constraints Constrains \a index to be the index of a largest value in \a x. - If \a break_ties is true, the smallest qualifying index is required; - otherwise any qualifying index is permitted. The array must be nonempty. + Any qualifying index is permitted. The array must be nonempty. + Reification is not supported. */ -predicate gecode_arg_max( - array[int] of var bool: x, - var int: index, - bool: break_ties, -) = +predicate gecode_arg_max_any(array[$$X] of var bool: x, var $$X: index) = assert( length(x) > 0, - "gecode_arg_max: empty array", - if break_ties then - arg_max(array1d(x)) + min(index_set(x)) - 1 = index - else - gecode_arg_max_bool(x, 0, index - min(index_set(x)), false) - endif, + "gecode_arg_max_any: empty array", + gecode_arg_max_bool( + array1d(x), + 0, + enum2int(index) - min(enum2int(index_set(x))), + false, + ), ); /** @group gecode.constraints - Requires exactly one element of \a x to be true, at zero-based - position \a value - \a offset. + Requires exactly one element of \a x to be true, with \a value + equal to its array index. The array must be nonempty. + Reification is not supported. */ -predicate gecode_channel(array[int] of var bool: x, var int: value, int: offset) = - gecode_bool_int_channel(x, value, offset); -/** @group gecode.constraints - Constrains \a x to be 1.0 when \a b is true and 0.0 otherwise. -*/ -predicate gecode_channel(var bool: b, var float: x) = - gecode_bool_float_channel(b, x); +predicate gecode_one_hot(array[$$X] of var bool: x, var $$X: value) = + assert( + length(x) > 0, + "gecode_one_hot: empty array", + gecode_bool_int_channel(array1d(x), enum2int(value), min(enum2int(index_set(x)))), + ); diff --git a/gecode/flatzinc/mznlib/gecode_fzn.mzn b/gecode/flatzinc/mznlib/gecode_fzn.mzn index f1b5b0c7da..07ee28b9a8 100644 --- a/gecode/flatzinc/mznlib/gecode_fzn.mzn +++ b/gecode/flatzinc/mznlib/gecode_fzn.mzn @@ -31,36 +31,28 @@ % % Integer relation codes: 1 =, 2 !=, 3 <=, 4 <, 5 >=, 6 >. -% Set relation, operation, and task codes follow the enums in gecode.mzn. +% Set relation codes: 1 =, 2 !=, 3 subset, 4 superset. % Arrays correspond by position unless stated otherwise. % _reif predicates equate b with the relation; _imp require it when b is true. -/** @group gecode.constraints - Constrain every pair of values in \a x to differ by at least \a distance. - The distance is nonnegative, including for empty and singleton arrays. -*/ -predicate gecode_inter_distance(array[int] of var int: x, var int: distance); +% Constrain every pair of values in x to differ by at least distance. +% The distance is nonnegative, including for empty and singleton arrays. +predicate gecode_inter_distance_fzn(array[int] of var int: x, var int: distance); -/** @group gecode.constraints - Constrain values in \a x whose corresponding \a present entry is true to - be pairwise distinct. The arrays have equal lengths. -*/ +% Constrain values in x whose corresponding present entry is true to +% be pairwise distinct. The arrays have equal lengths. predicate gecode_all_different_optional( array[int] of var int: x, array[int] of var bool: present, ); -/** @group gecode.constraints - Constrain the values in \a x to be pairwise distinct, except that - \a except_value may occur more than once. -*/ +% Constrain the values in x to be pairwise distinct, except that +% except_value may occur more than once. predicate gecode_all_different_except(array[int] of var int: x, int: except_value); -/** @group gecode.constraints - Constrain the sums of corresponding entries in \a x and \a offset to be - pairwise distinct. The arrays must have equal lengths. -*/ -predicate gecode_all_different_offset( +% Constrain the sums of corresponding entries in x and offset to be +% pairwise distinct. The arrays must have equal lengths. +predicate gecode_all_different_offset_fzn( array[int] of int: offset, array[int] of var int: x, ); @@ -104,17 +96,6 @@ predicate gecode_count_matches_fzn( var int: count, ); -/** @group gecode.constraints - Constrain the number of occurrences of each value in \a cover to belong to - the corresponding set in \a counts. Every value in \a x must occur in - \a cover. Cover values are distinct; \a cover and \a counts have equal lengths. -*/ -predicate gecode_global_cardinality_sets( - array[int] of var int: x, - array[int] of int: cover, - array[int] of set of int: counts, -); - % Compare the number of distinct values in x with count; an empty array has none. predicate gecode_nvalues_int_fzn( array[int] of var int: x, @@ -136,8 +117,6 @@ predicate gecode_array_bool_ne(array[int] of var bool: x, array[int] of var bool % At least two entries differ; empty and singleton arrays cannot satisfy this. predicate gecode_not_all_equal_int(array[int] of var int: x); -predicate gecode_not_all_equal_bool(array[int] of var bool: x); - % z equals x when b is true, and y otherwise. predicate gecode_int_ite(var bool: b, var int: x, var int: y, var int: z); @@ -204,20 +183,6 @@ predicate gecode_int_divmod( % y is floor(root(x,n)); n is positive, and negative x requires odd n. predicate gecode_int_nroot(var int: x, int: n, var int: y); -/** @group gecode.constraints - Constrain \a x to be accepted by a deterministic automaton with states 1..\a q. - The row-major \a transitions array has \a q rows and two columns, for false - and true. A zero transition rejects; \a initial and \a final specify the - initial state and accepting states. -*/ -predicate gecode_regular_bool( - array[int] of var bool: x, - int: q, - array[int] of int: transitions, - int: initial, - set of int: final, -); - % NFA rows are states 1..q, columns are first_symbol..first_symbol+symbols-1. % Each entry gives the set of target states; an empty set rejects. predicate gecode_regular_nfa_int_fzn( @@ -229,15 +194,6 @@ predicate gecode_regular_nfa_int_fzn( int: initial, set of int: final, ); -predicate gecode_regular_nfa_bool_fzn( - array[int] of var bool: x, - int: q, - int: first_symbol, - int: symbols, - array[int] of set of int: transitions, - int: initial, - set of int: final, -); % Node labels start at offset; the final successor is offset+length(x). predicate gecode_path( @@ -267,25 +223,9 @@ predicate gecode_path_cost_array( var int: cost, ); -/** @group gecode.constraints - Constrain two tasks not to overlap. If \a order is false, - \a start0 + \a duration0 <= \a start1; otherwise, - \a start1 + \a duration1 <= \a start0. - Durations are nonnegative. -*/ -predicate gecode_order( - var int: start0, - int: duration0, - var int: start1, - int: duration1, - var bool: order, -); - -/** @group gecode.constraints - Constrain tasks whose \a present entry is true not to overlap. Durations are - nonnegative. The arrays have equal lengths. The caller must also constrain - \a start[i] + \a duration[i] = \a end[i] for each present task. -*/ +% Constrain tasks whose present entry is true not to overlap. Durations are +% nonnegative. The arrays have equal lengths. The caller must also constrain +% start[i] + duration[i] = end[i] for each present task. predicate gecode_unary_flex_optional( array[int] of var int: start, array[int] of var int: duration, @@ -293,12 +233,10 @@ predicate gecode_unary_flex_optional( array[int] of var bool: present, ); -/** @group gecode.constraints - Constrain the total \a usage of tasks whose \a present entry is true not to - exceed \a capacity. Durations, usage, and capacity are nonnegative. - The arrays have equal lengths. The caller must also constrain - \a start[i] + \a duration[i] = \a end[i] for each present task. -*/ +% Constrain the total usage of tasks whose present entry is true not to +% exceed capacity. Durations, usage, and capacity are nonnegative. +% The arrays have equal lengths. The caller must also constrain +% start[i] + duration[i] = end[i] for each present task. predicate gecode_cumulative_flex_optional( array[int] of var int: start, array[int] of var int: duration, @@ -308,38 +246,6 @@ predicate gecode_cumulative_flex_optional( array[int] of var bool: present, ); -% Task codes: 1 has fixed duration, 2 fixed start, 3 fixed end. -% flex/fix mean start/duration, end/start, and start/end, respectively. -predicate gecode_unary_typed_fzn( - array[int] of int: task_type, - array[int] of var int: flex, - array[int] of int: fix, -); - -predicate gecode_unary_typed_optional_fzn( - array[int] of int: task_type, - array[int] of var int: flex, - array[int] of int: fix, - array[int] of var bool: present, -); - -predicate gecode_cumulative_typed_fzn( - array[int] of int: task_type, - array[int] of var int: flex, - array[int] of int: fix, - array[int] of int: usage, - var int: capacity, -); - -predicate gecode_cumulative_typed_optional_fzn( - array[int] of int: task_type, - array[int] of var int: flex, - array[int] of int: fix, - array[int] of int: usage, - var int: capacity, - array[int] of var bool: present, -); - % Present rectangles have nonnegative fixed widths and heights and cannot overlap. predicate gecode_nooverlap_optional( array[int] of var int: x, @@ -367,46 +273,23 @@ predicate gecode_set_convex_hull(var set of int: x, var set of int: hull); % x is empty or contains every integer between its minimum and maximum. predicate gecode_set_convex(var set of int: x); -/** @group gecode.constraints - Constrain each set in \a x to have \a cardinality elements and each pair of - sets to intersect in at most one element. The cardinality is nonnegative. -*/ +% Constrain each set in x to have cardinality elements and each pair of +% sets to intersect in at most one element. The cardinality is nonnegative. predicate gecode_set_atmost_one(array[int] of var set of int: x, int: cardinality); -/** @group gecode.constraints - Constrain \a x to list the elements of \a y in strictly increasing order. -*/ +% Constrain x to list the elements of y in strictly increasing order. predicate gecode_set_channel_sorted(array[int] of var int: x, var set of int: y); -predicate gecode_set_operation_fzn( - var set of int: x, - int: operation, - var set of int: y, - int: relation, - var set of int: z, -); - -/** @group gecode.constraints - Constrain \a y to be the intersection of all sets in \a x. - The intersection of an empty array is the full Gecode set universe. -*/ +% Constrain y to be the intersection of all sets in x. +% The intersection of an empty array is the full Gecode set universe. predicate gecode_array_set_intersect( array[int] of var set of int: x, var set of int: y, ); -predicate gecode_array_int_set_operation_fzn( - array[int] of var int: x, - int: operation, - set of int: initial, - var set of int: y, -); - -/** @group gecode.constraints - Constrain \a value to be the set at (\a column, \a row) in the row-major - \a x matrix. Coordinates are zero-based; \a width and \a height are positive - and their product equals the length of \a x. -*/ +% Constrain value to be the set at (column, row) in the row-major +% x matrix. Coordinates are zero-based; width and height are positive +% and their product equals the length of x. predicate gecode_set_element2d( array[int] of var set of int: x, var int: column, @@ -416,46 +299,24 @@ predicate gecode_set_element2d( var set of int: value, ); -% selected contains zero-based positions; combine the singleton sets {x[i]}. -% An empty intersection equals universe; an empty union is empty. -predicate gecode_array_int_set_element_fzn( - array[int] of var int: x, - int: operation, - var set of int: selected, - var set of int: value, - set of int: universe, -); - -/** @group gecode.constraints - Constrain \a y to be \a x raised to the nonnegative integer \a n. -*/ +% Constrain y to be x raised to the nonnegative integer n. predicate gecode_float_pow(var float: x, int: n, var float: y); -/** @group gecode.constraints - Constrain \a y to be the n-th root of \a x. The degree \a n is positive. - Both \a x and \a y are nonnegative, including for odd degrees. -*/ +% Constrain y to be the n-th root of x. The degree n is positive. +% Both x and y are nonnegative, including for odd degrees. predicate gecode_float_nroot(var float: x, int: n, var float: y); -/** @group gecode.constraints - Constrain \a y to be the minimum of the nonempty array \a x. -*/ +% Constrain y to be the minimum of the nonempty array x. predicate gecode_float_array_min(array[int] of var float: x, var float: y); -/** @group gecode.constraints - Constrain \a y to be the maximum of the nonempty array \a x. -*/ +% Constrain y to be the maximum of the nonempty array x. predicate gecode_float_array_max(array[int] of var float: x, var float: y); -/** @group gecode.constraints - Constrain \a y to be the positive fixed \a base raised to \a x. Requires MPFR. -*/ +% Constrain y to be the positive fixed base raised to x. Requires MPFR. predicate gecode_float_pow_base(float: base, var float: x, var float: y); -/** @group gecode.constraints - Constrain \a y to be the logarithm of \a x to the fixed \a base. - The base is positive and differs from one; \a x is positive. Requires MPFR. -*/ +% Constrain y to be the logarithm of x to the fixed base. +% The base is positive and differs from one; x is positive. Requires MPFR. predicate gecode_float_log_base(float: base, var float: x, var float: y); % index is the zero-based position plus offset of an extremum. @@ -502,33 +363,9 @@ predicate gecode_set_max_reif(var set of int: s, var int: x, var bool: b); predicate gecode_set_max_imp(var set of int: s, var int: x, var bool: b); -% Compare s with the singleton set {x}. -predicate gecode_set_singleton_rel_fzn( - var set of int: s, - int: relation, - var int: x, -); - -predicate gecode_set_singleton_rel_fzn_reif( - var set of int: s, - int: relation, - var int: x, - var bool: b, -); - -predicate gecode_set_singleton_rel_fzn_imp( - var set of int: s, - int: relation, - var int: x, - var bool: b, -); - % Every element precedes all elements in later nonempty sets; sets are disjoint. predicate gecode_array_set_seq(array[int] of var set of int: x); predicate gecode_array_set_seq_union(array[int] of var set of int: x, var set of int: y); predicate gecode_float_rel_imp_fzn(var float: x, int: relation, var float: y, var bool: b); predicate gecode_set_rel_imp_fzn(var set of int: x, int: relation, var set of int: y, var bool: b); - -% x equals 1.0 when b is true, and 0.0 otherwise. -predicate gecode_bool_float_channel(var bool: b, var float: x); diff --git a/gecode/flatzinc/mznlib/redefinitions-2.0.mzn b/gecode/flatzinc/mznlib/redefinitions-2.0.mzn index 8265bb453d..591486424e 100644 --- a/gecode/flatzinc/mznlib/redefinitions-2.0.mzn +++ b/gecode/flatzinc/mznlib/redefinitions-2.0.mzn @@ -33,21 +33,24 @@ predicate gecode_float_pow_base(float: base, var float: x, var float: y); % Nonnegative integer exponents permit zero or negative bases. predicate float_pow(var float: x, var float: exponent, var float: y) = let { - float: n = if is_fixed(exponent) then fix(exponent) else -1.0 endif; - bool: integer_exponent = - if n >= 0.0 /\ n <= 2147483646.0 then - n = int2float(floor(n)) - else - false - endif; - } in - if integer_exponent then - gecode_float_pow(x, floor(n), y) - elseif is_fixed(x) /\ fix(x) > 0.0 then - gecode_float_pow_base(fix(x), exponent, y) + float: n = if is_fixed(exponent) then + fix(exponent) + else + -1.0 + endif; + bool: integer_exponent = if n >= 0.0 /\ n <= 2147483646.0 then + n = int2float(floor(n)) else - assert( - false, - "Gecode float_pow requires a nonnegative integer exponent or a fixed positive base" - ) + false endif; + } in + if integer_exponent then + gecode_float_pow(x, floor(n), y) + elseif is_fixed(x) /\ fix(x) > 0.0 then + gecode_float_pow_base(fix(x), exponent, y) + else + assert( + false, + "Gecode float_pow requires a nonnegative integer exponent or a fixed positive base", + ) + endif; diff --git a/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn b/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn index ea3f597e5d..fa1bd421c8 100644 --- a/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn +++ b/gecode/flatzinc/mznlib/redefinitions-2.1.1.mzn @@ -44,10 +44,9 @@ function var $$E: min(var set of $$E: s) = % An empty upper bound makes the extremum undefined at compilation. let { var set of int: si = enum2int(s); - var min(ub(si)) .. max(ub(si)): m = - min([e + (max(ub(si)) + 1 - e) * (1 - (e in si)) | e in ub(si)]); + var min(ub(si)) .. max(ub(si)): m = min([e + (max(ub(si)) + 1 - e) * (1 - (e in si)) | e in ub(si)]); } in - to_enum_internal(enum_of(s), m) + to_enum_internal(enum_of(s), m) else to_enum_internal( enum_of(s), @@ -57,8 +56,8 @@ function var $$E: min(var set of $$E: s) = let { constraint card(s) > 0; } in - gecode_set_min_partial(enum2int(s)) - endif + gecode_set_min_partial(enum2int(s)) + endif, ) endif; @@ -67,24 +66,25 @@ function var int: gecode_set_min_value(var set of int: s) :: promise_total = var min(ub(s) union {1}) .. max(ub(s) union {1}): m :: is_defined_var; constraint card(s) > 0; constraint gecode_set_min(s, m) :: defines_var(m); - } in m; + } in + m; function var int: gecode_set_min_partial(var set of int: s) :: promise_total = let { var min(ub(s) union {1}) .. max(ub(s) union {1}): m :: is_defined_var; var bool: present = card(s) > 0; constraint gecode_set_min_imp(s, m, present) :: defines_var(m); - } in m; + } in + m; function var $$E: max(var set of $$E: s) = if card(ub(enum2int(s))) = 0 then % An empty upper bound makes the extremum undefined at compilation. let { var set of int: si = enum2int(s); - var min(ub(si)) .. max(ub(si)): m = - max([e + (min(ub(si)) - 1 - e) * (1 - (e in si)) | e in ub(si)]); + var min(ub(si)) .. max(ub(si)): m = max([e + (min(ub(si)) - 1 - e) * (1 - (e in si)) | e in ub(si)]); } in - to_enum_internal(enum_of(s), m) + to_enum_internal(enum_of(s), m) else to_enum_internal( enum_of(s), @@ -94,8 +94,8 @@ function var $$E: max(var set of $$E: s) = let { constraint card(s) > 0; } in - gecode_set_max_partial(enum2int(s)) - endif + gecode_set_max_partial(enum2int(s)) + endif, ) endif; @@ -104,11 +104,13 @@ function var int: gecode_set_max_value(var set of int: s) :: promise_total = var min(ub(s) union {1}) .. max(ub(s) union {1}): m :: is_defined_var; constraint card(s) > 0; constraint gecode_set_max(s, m) :: defines_var(m); - } in m; + } in + m; function var int: gecode_set_max_partial(var set of int: s) :: promise_total = let { var min(ub(s) union {1}) .. max(ub(s) union {1}): m :: is_defined_var; var bool: present = card(s) > 0; constraint gecode_set_max_imp(s, m, present) :: defines_var(m); - } in m; + } in + m; diff --git a/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn b/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn index 8aa671f269..451382ee54 100644 --- a/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn +++ b/gecode/flatzinc/mznlib/redefinitions-2.5.2.mzn @@ -20,14 +20,14 @@ predicate gecode_set_element2d( int: width, var int: row, int: height, - var set of int: value + var set of int: value, ); predicate array_var_set_element2d_nonshifted( var int: idx1, var int: idx2, array[int, int] of var set of int: x, - var set of int: c + var set of int: c, ) = if length(x) = 0 then false @@ -38,6 +38,6 @@ predicate array_var_set_element2d_nonshifted( card(index_set_2of2(x)), idx1 - min(index_set_1of2(x)), card(index_set_1of2(x)), - c + c, ) endif; diff --git a/gecode/flatzinc/mznlib/redefinitions.mzn b/gecode/flatzinc/mznlib/redefinitions.mzn index 58a26e970b..3629dbfb84 100644 --- a/gecode/flatzinc/mznlib/redefinitions.mzn +++ b/gecode/flatzinc/mznlib/redefinitions.mzn @@ -142,16 +142,12 @@ predicate gecode_float_rel_imp_fzn( var float: x, int: relation, var float: y, - var bool: b + var bool: b, ); -predicate float_eq_imp(var float: x, var float: y, var bool: b) = - gecode_float_rel_imp_fzn(x, 1, y, b); -predicate float_ne_imp(var float: x, var float: y, var bool: b) = - gecode_float_rel_imp_fzn(x, 2, y, b); -predicate float_le_imp(var float: x, var float: y, var bool: b) = - gecode_float_rel_imp_fzn(x, 3, y, b); -predicate float_lt_imp(var float: x, var float: y, var bool: b) = - gecode_float_rel_imp_fzn(x, 4, y, b); +predicate float_eq_imp(var float: x, var float: y, var bool: b) = gecode_float_rel_imp_fzn(x, 1, y, b); +predicate float_ne_imp(var float: x, var float: y, var bool: b) = gecode_float_rel_imp_fzn(x, 2, y, b); +predicate float_le_imp(var float: x, var float: y, var bool: b) = gecode_float_rel_imp_fzn(x, 3, y, b); +predicate float_lt_imp(var float: x, var float: y, var bool: b) = gecode_float_rel_imp_fzn(x, 4, y, b); % MiniZinc represents scalar comparisons as one- or two-term linear forms. % Other linear expressions use full reification followed by implication. @@ -160,71 +156,66 @@ predicate gecode_float_lin_scalar_imp( array[int] of var float: x, float: y, int: relation, - var bool: b + var bool: b, ) = let { array[int] of float: aa = array1d(a); array[int] of var float: xx = array1d(x); } in - if length(aa) = 1 /\ length(xx) = 1 /\ aa[1] = 1.0 then - gecode_float_rel_imp_fzn(xx[1], relation, y, b) - elseif length(aa) = 2 /\ length(xx) = 2 /\ y = 0.0 /\ aa[1] = 1.0 /\ aa[2] = -1.0 then - gecode_float_rel_imp_fzn(xx[1], relation, xx[2], b) - elseif length(aa) = 2 /\ length(xx) = 2 /\ y = 0.0 /\ aa[1] = -1.0 /\ aa[2] = 1.0 then - gecode_float_rel_imp_fzn(xx[2], relation, xx[1], b) + if length(aa) = 1 /\ length(xx) = 1 /\ aa[1] = 1.0 then + gecode_float_rel_imp_fzn(xx[1], relation, y, b) + elseif length(aa) = 2 /\ length(xx) = 2 /\ y = 0.0 /\ aa[1] = 1.0 /\ aa[2] = -1.0 then + gecode_float_rel_imp_fzn(xx[1], relation, xx[2], b) + elseif length(aa) = 2 /\ length(xx) = 2 /\ y = 0.0 /\ aa[1] = -1.0 /\ aa[2] = 1.0 then + gecode_float_rel_imp_fzn(xx[2], relation, xx[1], b) + else + let { + var bool: r; + } in + (if relation = 1 then + float_lin_eq_reif(a, x, y, r) + elseif relation = 2 then + float_lin_eq_reif(a, x, y, not r) + elseif relation = 3 then + float_lin_le_reif(a, x, y, r) else - let { var bool: r; } in - (if relation = 1 then - float_lin_eq_reif(a, x, y, r) - elseif relation = 2 then - float_lin_eq_reif(a, x, y, not r) - elseif relation = 3 then - float_lin_le_reif(a, x, y, r) - else - float_lin_lt_reif(a, x, y, r) - endif) /\ bool_clause([r], [b]) - endif; + float_lin_lt_reif(a, x, y, r) + endif) /\ + bool_clause([r], [b]) + endif; predicate float_lin_eq_imp( array[int] of float: a, array[int] of var float: x, float: y, - var bool: b -) = - gecode_float_lin_scalar_imp(a, x, y, 1, b); + var bool: b, +) = gecode_float_lin_scalar_imp(a, x, y, 1, b); predicate float_lin_ne_imp( array[int] of float: a, array[int] of var float: x, float: y, - var bool: b -) = - gecode_float_lin_scalar_imp(a, x, y, 2, b); + var bool: b, +) = gecode_float_lin_scalar_imp(a, x, y, 2, b); predicate float_lin_le_imp( array[int] of float: a, array[int] of var float: x, float: y, - var bool: b -) = - gecode_float_lin_scalar_imp(a, x, y, 3, b); + var bool: b, +) = gecode_float_lin_scalar_imp(a, x, y, 3, b); predicate float_lin_lt_imp( array[int] of float: a, array[int] of var float: x, float: y, - var bool: b -) = - gecode_float_lin_scalar_imp(a, x, y, 4, b); + var bool: b, +) = gecode_float_lin_scalar_imp(a, x, y, 4, b); predicate gecode_set_rel_imp_fzn( var set of int: x, int: relation, var set of int: y, - var bool: b + var bool: b, ); -predicate set_eq_imp(var set of int: x, var set of int: y, var bool: b) = - gecode_set_rel_imp_fzn(x, 1, y, b); -predicate set_ne_imp(var set of int: x, var set of int: y, var bool: b) = - gecode_set_rel_imp_fzn(x, 2, y, b); -predicate set_subset_imp(var set of int: x, var set of int: y, var bool: b) = - gecode_set_rel_imp_fzn(x, 3, y, b); -predicate set_superset_imp(var set of int: x, var set of int: y, var bool: b) = - gecode_set_rel_imp_fzn(x, 4, y, b); +predicate set_eq_imp(var set of int: x, var set of int: y, var bool: b) = gecode_set_rel_imp_fzn(x, 1, y, b); +predicate set_ne_imp(var set of int: x, var set of int: y, var bool: b) = gecode_set_rel_imp_fzn(x, 2, y, b); +predicate set_subset_imp(var set of int: x, var set of int: y, var bool: b) = gecode_set_rel_imp_fzn(x, 3, y, b); +predicate set_superset_imp(var set of int: x, var set of int: y, var bool: b) = gecode_set_rel_imp_fzn(x, 4, y, b); diff --git a/gecode/flatzinc/registry.cpp b/gecode/flatzinc/registry.cpp index eaae2f9dbe..a314c98266 100755 --- a/gecode/flatzinc/registry.cpp +++ b/gecode/flatzinc/registry.cpp @@ -1214,20 +1214,6 @@ namespace Gecode { namespace FlatZinc { ce[6]->getSet(), ann); } - void p_regular_bool(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - int q = ce[1]->getInt(); - IntArgs transitions = s.arg2intargs(ce[2]); - IntSet final = s.arg2intset(ce[4]); - if ((q <= 0) || (static_cast(q)*2 != transitions.size()) || - (ce[3]->getInt() < 1) || (ce[3]->getInt() > q) || - ((final.size() != 0) && ((final.min() < 1) || (final.max() > q)))) - throw FlatZinc::Error("Registry", "invalid Boolean DFA dimensions or states"); - for (int i=0; i q)) - throw FlatZinc::Error("Registry", "invalid Boolean DFA transition"); - p_regular(s, s.arg2boolvarargs(ce[0]), q, 0, 1, transitions, - ce[3]->getInt(), ce[4]->getSet(), ann); - } template void p_regular_nfa(FlatZincSpace& s, VarArgs x, const ConExpr& ce, @@ -1264,9 +1250,6 @@ namespace Gecode { namespace FlatZinc { void p_regular_nfa_int(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { p_regular_nfa(s,s.arg2intvarargs(ce[0]),ce,ann); } - void p_regular_nfa_bool(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - p_regular_nfa(s,s.arg2boolvarargs(ce[0]),ce,ann); - } void p_sort(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { @@ -1847,6 +1830,10 @@ fvar, inter_distance(s, x.slice(0,1,x.size()-1), x[x.size()-1], s.ann2ipl(ann)); } else { + if (ce[1]->getInt() < 0) { + s.fail(); + return; + } unshare(s, x); inter_distance(s, x, ce[1]->getInt(), s.ann2ipl(ann)); } @@ -1947,12 +1934,6 @@ fvar, count(s, s.arg2intvarargs(ce[0]), s.arg2intargs(ce[1]), int_relation(ce[2]), s.arg2IntVar(ce[3]), s.ann2ipl(ann)); } - void p_global_cardinality_sets(FlatZincSpace& s, const ConExpr& ce, - AST::Node* ann) { - IntVarArgs x = s.arg2intvarargs(ce[0]); - unshare(s, x); - count(s, x, s.arg2intsetargs(ce[2]), s.arg2intargs(ce[1]), s.ann2ipl(ann)); - } void p_nvalues_int(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { nvalues(s, s.arg2intvarargs(ce[0]), int_relation(ce[1]), s.arg2IntVar(ce[2]), s.ann2ipl(ann)); @@ -1972,9 +1953,6 @@ fvar, void p_not_all_equal_int(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { rel(s, s.arg2intvarargs(ce[0]), IRT_NQ, s.ann2ipl(ann)); } - void p_not_all_equal_bool(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - rel(s, s.arg2boolvarargs(ce[0]), IRT_NQ, s.ann2ipl(ann)); - } void p_int_ite(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { ite(s, s.arg2BoolVar(ce[0]), s.arg2IntVar(ce[1]), s.arg2IntVar(ce[2]), s.arg2IntVar(ce[3]), s.ann2ipl(ann)); @@ -2076,43 +2054,6 @@ fvar, s.arg2IntVar(ce[3]), s.arg2IntVar(ce[4]), s.arg2intvarargs(ce[5]), s.arg2IntVar(ce[6]), s.ann2ipl(ann)); } - void p_order(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - order(s, s.arg2IntVar(ce[0]), ce[1]->getInt(), s.arg2IntVar(ce[2]), - ce[3]->getInt(), s.arg2BoolVar(ce[4]), s.ann2ipl(ann)); - } - TaskTypeArgs task_types(FlatZincSpace& s, AST::Node* arg) { - IntArgs codes = s.arg2intargs(arg); - TaskTypeArgs types(codes.size()); - for (int i=0; ia.size() == 4) - unary(s, task_types(s, ce[0]), flex, s.arg2intargs(ce[2]), - s.arg2boolvarargs(ce[3]), s.ann2ipl(ann)); - else - unary(s, task_types(s, ce[0]), flex, s.arg2intargs(ce[2]), s.ann2ipl(ann)); - } - void p_cumulative_typed(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - IntVarArgs flex = s.arg2intvarargs(ce[1]); - unshare(s, flex); - if (ce.args->a.size() == 6) - cumulative(s, s.arg2IntVar(ce[4]), task_types(s, ce[0]), flex, - s.arg2intargs(ce[2]), s.arg2intargs(ce[3]), - s.arg2boolvarargs(ce[5]), s.ann2ipl(ann)); - else - cumulative(s, s.arg2IntVar(ce[4]), task_types(s, ce[0]), flex, - s.arg2intargs(ce[2]), s.arg2intargs(ce[3]), s.ann2ipl(ann)); - } void p_nooverlap_optional(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { nooverlap(s, s.arg2intvarargs(ce[0]), s.arg2intargs(ce[1]), s.arg2intvarargs(ce[2]), s.arg2intargs(ce[3]), @@ -2176,6 +2117,7 @@ fvar, IntPoster(void) { registry().add("all_different_int", &p_distinct); registry().add("all_different_offset", &p_distinctOffset); + registry().add("gecode_all_different_offset_fzn", &p_distinctOffset); registry().add("all_equal_int", &p_all_equal); registry().add("int_eq", &p_int_eq); registry().add("int_ne", &p_int_ne); @@ -2352,6 +2294,7 @@ fvar, registry().add("gecode_circuit_cost_array", &p_circuit_cost_array); registry().add("gecode_circuit_cost", &p_circuit_cost); registry().add("gecode_nooverlap", &p_nooverlap); + registry().add("gecode_nooverlap_fzn", &p_nooverlap); registry().add("gecode_precede", &p_precede); registry().add("nvalue",&p_nvalue); registry().add("among",&p_among); @@ -2365,14 +2308,13 @@ fvar, registry().add("gecode_array_int_ne", &p_array_int_ne); registry().add("gecode_not_all_equal_int", &p_not_all_equal_int); registry().add("gecode_array_bool_ne", &p_array_bool_ne); - registry().add("gecode_not_all_equal_bool", &p_not_all_equal_bool); registry().add("gecode_inter_distance", &p_inter_distance); + registry().add("gecode_inter_distance_fzn", &p_inter_distance); registry().add("gecode_bool_int_channel", &p_bool_int_channel); registry().add("gecode_sort_permutation", &p_sort_permutation); registry().add("gecode_count_int_fzn", &p_count_int); registry().add("gecode_count_in_fzn", &p_count_in); registry().add("gecode_count_matches_fzn", &p_count_matches); - registry().add("gecode_global_cardinality_sets", &p_global_cardinality_sets); registry().add("gecode_nvalues_int_fzn", &p_nvalues_int); registry().add("gecode_nvalues_bool_fzn", &p_nvalues_bool); registry().add("gecode_int_ite", &p_int_ite); @@ -2399,17 +2341,10 @@ fvar, registry().add("gecode_path", &p_path); registry().add("gecode_path_cost", &p_path_cost); registry().add("gecode_path_cost_array", &p_path_cost_array); - registry().add("gecode_order", &p_order); registry().add("gecode_nooverlap_optional", &p_nooverlap_optional); registry().add("gecode_all_different_optional", &p_distinct_optional); registry().add("gecode_all_different_except", &p_distinct_except); - registry().add("gecode_regular_bool", &p_regular_bool); registry().add("gecode_regular_nfa_int_fzn", &p_regular_nfa_int); - registry().add("gecode_regular_nfa_bool_fzn", &p_regular_nfa_bool); - registry().add("gecode_unary_typed_fzn", &p_unary_typed); - registry().add("gecode_unary_typed_optional_fzn", &p_unary_typed); - registry().add("gecode_cumulative_typed_fzn", &p_cumulative_typed); - registry().add("gecode_cumulative_typed_optional_fzn", &p_cumulative_typed); registry().add("gecode_nooverlap_flex_optional", &p_nooverlap_flex); registry().add("gecode_arg_min_int", &p_arg_min_int); registry().add("gecode_arg_max_int", &p_arg_max_int); @@ -2733,15 +2668,6 @@ fvar, default: throw FlatZinc::Error("Registry", "invalid set relation"); } } - SetOpType set_operation(AST::Node* arg) { - switch (arg->getInt()) { - case 1: return SOT_UNION; - case 2: return SOT_DUNION; - case 3: return SOT_INTER; - case 4: return SOT_MINUS; - default: throw FlatZinc::Error("Registry", "invalid set operation"); - } - } void p_set_ite(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { ite(s, s.arg2BoolVar(ce[0]), s.arg2SetVar(ce[1]), s.arg2SetVar(ce[2]), s.arg2SetVar(ce[3])); @@ -2766,17 +2692,9 @@ fvar, void p_set_channel_sorted(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { channelSorted(s, s.arg2intvarargs(ce[0]), s.arg2SetVar(ce[1])); } - void p_set_operation(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { - rel(s, s.arg2SetVar(ce[0]), set_operation(ce[1]), s.arg2SetVar(ce[2]), - set_relation(ce[3]), s.arg2SetVar(ce[4])); - } void p_array_set_intersect(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { rel(s, SOT_INTER, s.arg2setvarargs(ce[0]), s.arg2SetVar(ce[1])); } - void p_array_int_set_operation(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { - rel(s, set_operation(ce[1]), s.arg2intvarargs(ce[0]), - s.arg2intset(ce[2]), s.arg2SetVar(ce[3])); - } void p_set_element2d(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { SetVarArgs x = s.arg2setvarargs(ce[0]); int width = ce[2]->getInt(), height = ce[4]->getInt(); @@ -2786,10 +2704,6 @@ fvar, element(s, x, s.arg2IntVar(ce[1]), width, s.arg2IntVar(ce[3]), height, s.arg2SetVar(ce[5])); } - void p_array_int_set_element(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { - element(s, set_operation(ce[1]), s.arg2intvarargs(ce[0]), - s.arg2SetVar(ce[2]), s.arg2SetVar(ce[3]), s.arg2intset(ce[4])); - } void p_set_min(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { min(s, s.arg2SetVar(ce[0]), s.arg2IntVar(ce[1])); @@ -2810,14 +2724,6 @@ fvar, } - void p_set_singleton_rel(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { - rel(s, s.arg2SetVar(ce[0]), set_relation(ce[1]), s.arg2IntVar(ce[2])); - } - template - void p_set_singleton_rel_reif(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { - rel(s, s.arg2SetVar(ce[0]), set_relation(ce[1]), s.arg2IntVar(ce[2]), - Reify(s.arg2BoolVar(ce[3]), mode)); - } void p_set_rel_imp(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { rel(s, s.arg2SetVar(ce[0]), set_relation(ce[1]), s.arg2SetVar(ce[2]), @@ -2881,20 +2787,14 @@ fvar, registry().add("gecode_set_convex_hull", &p_set_convex_hull); registry().add("gecode_set_atmost_one", &p_set_atmost_one); registry().add("gecode_set_channel_sorted", &p_set_channel_sorted); - registry().add("gecode_set_operation_fzn", &p_set_operation); registry().add("gecode_array_set_intersect", &p_array_set_intersect); - registry().add("gecode_array_int_set_operation_fzn", &p_array_int_set_operation); registry().add("gecode_set_element2d", &p_set_element2d); - registry().add("gecode_array_int_set_element_fzn", &p_array_int_set_element); registry().add("gecode_set_min", &p_set_min); registry().add("gecode_set_min_reif", &p_set_min_reif); registry().add("gecode_set_min_imp", &p_set_min_reif); registry().add("gecode_set_max", &p_set_max); registry().add("gecode_set_max_reif", &p_set_max_reif); registry().add("gecode_set_max_imp", &p_set_max_reif); - registry().add("gecode_set_singleton_rel_fzn", &p_set_singleton_rel); - registry().add("gecode_set_singleton_rel_fzn_reif", &p_set_singleton_rel_reif); - registry().add("gecode_set_singleton_rel_fzn_imp", &p_set_singleton_rel_reif); } }; @@ -3090,9 +2990,6 @@ fvar, void p_float_array_max(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { max(s, s.arg2floatvarargs(ce[0]), s.arg2FloatVar(ce[1])); } - void p_bool_float_channel(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { - channel(s, s.arg2BoolVar(ce[0]), s.arg2FloatVar(ce[1])); - } #ifdef GECODE_HAS_MPFR void p_float_pow_base(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { pow(s, ce[0]->getFloat(), s.arg2FloatVar(ce[1]), s.arg2FloatVar(ce[2])); @@ -3128,7 +3025,6 @@ fvar, registry().add("gecode_float_nroot", &p_float_nroot); registry().add("gecode_float_array_min", &p_float_array_min); registry().add("gecode_float_array_max", &p_float_array_max); - registry().add("gecode_bool_float_channel", &p_bool_float_channel); registry().add("float_abs",&p_float_abs); registry().add("float_sqrt",&p_float_sqrt); diff --git a/test/flatzinc/native-registry.cpp b/test/flatzinc/native-registry.cpp index f245737cc9..fdf1ee7748 100644 --- a/test/flatzinc/native-registry.cpp +++ b/test/flatzinc/native-registry.cpp @@ -75,10 +75,14 @@ namespace Test { namespace FlatZinc { const std::string unsat = "=====UNSATISFIABLE=====\n"; (void) new FlatZincTest("NativeRegistry::DistanceShared", "var 0..2:x; constraint gecode_inter_distance([x,x],1); solve satisfy;", unsat); + (void) new FlatZincTest("NativeRegistry::DistanceNegativeEmpty", + "constraint gecode_inter_distance_fzn([],-1); solve satisfy;", unsat); + (void) new FlatZincTest("NativeRegistry::DistanceNegativeSingleton", + "constraint gecode_inter_distance_fzn([0],-1); solve satisfy;", unsat); (void) new FlatZincTest("NativeRegistry::NotAllEqual", "constraint gecode_not_all_equal_int([1,1,2]); solve satisfy;", sat); (void) new FlatZincTest("NativeRegistry::CardinalityHoles", - "constraint gecode_global_cardinality_sets([1,1],[1],[{0,3}]); solve satisfy;", unsat); + "var {0,3}: count; constraint gecode_global_cardinality_closed([1,1],[1],[count]); solve satisfy;", unsat); (void) new FlatZincTest("NativeRegistry::SortOrientation", "constraint gecode_sort_permutation([3,1,2],[1,2,3],[2,3,1],1); solve satisfy;", unsat); (void) new FlatZincTest("NativeRegistry::ArgMinShared", @@ -87,8 +91,8 @@ namespace Test { namespace FlatZinc { "constraint gecode_product_mod_var_fzn_reif([3],0,3,false); solve satisfy;", sat); (void) new FlatZincTest("NativeRegistry::MinimumDistanceRepeatedSite", "constraint gecode_minimum_distance_fzn([0,0,2],3,[0,2,4,2,0,3,4,3,0],1); solve satisfy;", unsat); - (void) new FlatZincTest("NativeRegistry::NondeterministicBooleanPrefix", - "constraint gecode_regular_nfa_bool_fzn([false,true],3,0,2,[{1,2},{},{},{3},{},{}],1,{3}); solve satisfy;", sat); + (void) new FlatZincTest("NativeRegistry::NondeterministicPrefix", + "constraint gecode_regular_nfa_int_fzn([0,1],3,0,2,[{1,2},{},{},{3},{},{}],1,{3}); solve satisfy;", sat); (void) new FlatZincTest("NativeRegistry::DivmodNegative", "constraint gecode_int_divmod(-7,4,-1,-3); solve satisfy;", sat); (void) new FlatZincTest("NativeRegistry::DivmodUnboundedResults", From 58378d1efde449696c078dc08cc8d8d4eedc58f4 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sun, 11 Oct 2026 11:43:23 +0200 Subject: [PATCH 17/22] Describe the revised MiniZinc library scope --- changelog.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/changelog.in b/changelog.in index e48adf5059..3c98aad864 100755 --- a/changelog.in +++ b/changelog.in @@ -78,12 +78,12 @@ Module: flatzinc What: new Rank: major [DESCRIPTION] -Expose missing native integer, set, float, and scheduling propagators through -an overloaded Gecode MiniZinc library and typed FlatZinc registry bindings. -Upgrade standard solver-library translations where available, including -conditionals, count inequalities, n-ary products, nondeterministic regular, -optional scheduling, and extrema. Add annotations for basic, advanced, and -combined propagation algorithms, and minimum-distance actor organization. +Use standard MiniZinc FlatZinc hooks for native conditionals, count inequalities, +nondeterministic regular, and optional distinctness and scheduling. Expose +additional arithmetic, distance, path, rectangle, and set constraints through +documented modelling predicates with typed FlatZinc bindings. Add annotations +for basic, advanced, and combined propagation algorithms, and minimum-distance +actor organization. [ENTRY] Module: float From f8a3b7c95e1d7d67250a0d0b5f4be22ec50f0734 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sun, 11 Oct 2026 12:04:34 +0200 Subject: [PATCH 18/22] Indent multiline MiniZinc conditional headers --- gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn | 16 +++++++++------- .../flatzinc/mznlib/fzn_if_then_else_float.mzn | 16 +++++++++------- gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn | 16 +++++++++------- gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn | 16 +++++++++------- .../mznlib/fzn_if_then_else_var_bool.mzn | 16 +++++++++------- .../mznlib/fzn_if_then_else_var_float.mzn | 16 +++++++++------- .../flatzinc/mznlib/fzn_if_then_else_var_int.mzn | 16 +++++++++------- .../flatzinc/mznlib/fzn_if_then_else_var_set.mzn | 16 +++++++++------- 8 files changed, 72 insertions(+), 56 deletions(-) diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn index f0df281203..d3fd3c64a3 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn @@ -43,13 +43,15 @@ predicate fzn_if_then_else_bool( array[int] of bool: x, var bool: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then + if + length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) + then gecode_bool_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn index 403eebe5ff..50f85b2363 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn @@ -43,13 +43,15 @@ predicate fzn_if_then_else_float( array[int] of float: x, var float: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then + if + length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) + then gecode_float_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn index 05150f8807..49c7c33760 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn @@ -43,13 +43,15 @@ predicate fzn_if_then_else_int( array[int] of int: x, var int: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then + if + length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) + then gecode_int_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn index 38b42136a3..7755c47191 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn @@ -43,13 +43,15 @@ predicate fzn_if_then_else_set( array[int] of set of int: x, var set of int: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then + if + length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) + then gecode_set_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn index 19361e62ed..add3c6bf4e 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn @@ -43,13 +43,15 @@ predicate fzn_if_then_else_var_bool( array[int] of var bool: x, var bool: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then + if + length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) + then gecode_bool_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn index 0fe194b7fa..32f8b18143 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn @@ -43,13 +43,15 @@ predicate fzn_if_then_else_var_float( array[int] of var float: x, var float: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then + if + length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) + then gecode_float_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn index c71606ac6d..21bbd97364 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn @@ -43,13 +43,15 @@ predicate fzn_if_then_else_var_int( array[int] of var int: x, var int: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then + if + length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) + then gecode_int_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn index 248e88277c..2de46f0444 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn @@ -43,13 +43,15 @@ predicate fzn_if_then_else_var_set( array[int] of var set of int: x, var set of int: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then + if + length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) + then gecode_set_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { From 1937782dd53b16597cf35a2769c0934629744173 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sun, 11 Oct 2026 12:06:10 +0200 Subject: [PATCH 19/22] Align split conditions after the MiniZinc if keyword --- gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn | 16 +++++++--------- .../flatzinc/mznlib/fzn_if_then_else_float.mzn | 16 +++++++--------- gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn | 16 +++++++--------- gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn | 16 +++++++--------- .../mznlib/fzn_if_then_else_var_bool.mzn | 16 +++++++--------- .../mznlib/fzn_if_then_else_var_float.mzn | 16 +++++++--------- .../flatzinc/mznlib/fzn_if_then_else_var_int.mzn | 16 +++++++--------- .../flatzinc/mznlib/fzn_if_then_else_var_set.mzn | 16 +++++++--------- 8 files changed, 56 insertions(+), 72 deletions(-) diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn index d3fd3c64a3..df9b2fbabe 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn @@ -43,15 +43,13 @@ predicate fzn_if_then_else_bool( array[int] of bool: x, var bool: y, ) = - if - length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) - then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_bool_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn index 50f85b2363..a7f7658d97 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn @@ -43,15 +43,13 @@ predicate fzn_if_then_else_float( array[int] of float: x, var float: y, ) = - if - length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) - then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_float_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn index 49c7c33760..4881a22aef 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn @@ -43,15 +43,13 @@ predicate fzn_if_then_else_int( array[int] of int: x, var int: y, ) = - if - length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) - then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_int_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn index 7755c47191..dce1214d61 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn @@ -43,15 +43,13 @@ predicate fzn_if_then_else_set( array[int] of set of int: x, var set of int: y, ) = - if - length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) - then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_set_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn index add3c6bf4e..7736eabe3a 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn @@ -43,15 +43,13 @@ predicate fzn_if_then_else_var_bool( array[int] of var bool: x, var bool: y, ) = - if - length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) - then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_bool_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn index 32f8b18143..98a4ec6265 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn @@ -43,15 +43,13 @@ predicate fzn_if_then_else_var_float( array[int] of var float: x, var float: y, ) = - if - length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) - then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_float_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn index 21bbd97364..d88c390517 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn @@ -43,15 +43,13 @@ predicate fzn_if_then_else_var_int( array[int] of var int: x, var int: y, ) = - if - length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) - then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_int_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn index 2de46f0444..7d63c9beef 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn @@ -43,15 +43,13 @@ predicate fzn_if_then_else_var_set( array[int] of var set of int: x, var set of int: y, ) = - if - length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) - then + if length(c) = 2 /\ + index_sets_agree(c, x) /\ + (if is_fixed(c[max(index_set(c))]) then + fix(c[max(index_set(c))]) + else + false + endif) then gecode_set_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { From c7a9235cfdf037e8ea4dc6d60829cd24073be83e Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Mon, 12 Oct 2026 11:16:57 +0200 Subject: [PATCH 20/22] Trim redundant MiniZinc extensions and handle empty set sequences --- changelog.in | 2 +- gecode/flatzinc/mznlib/gecode.mzn | 228 +------------------------- gecode/flatzinc/mznlib/gecode_fzn.mzn | 65 -------- gecode/flatzinc/registry.cpp | 63 +------ test/flatzinc/native-registry.cpp | 16 +- 5 files changed, 13 insertions(+), 361 deletions(-) diff --git a/changelog.in b/changelog.in index 3c98aad864..2f7c886add 100755 --- a/changelog.in +++ b/changelog.in @@ -80,7 +80,7 @@ Rank: major [DESCRIPTION] Use standard MiniZinc FlatZinc hooks for native conditionals, count inequalities, nondeterministic regular, and optional distinctness and scheduling. Expose -additional arithmetic, distance, path, rectangle, and set constraints through +additional arithmetic, distance, rectangle, and set constraints through documented modelling predicates with typed FlatZinc bindings. Add annotations for basic, advanced, and combined propagation algorithms, and minimum-distance actor organization. diff --git a/gecode/flatzinc/mznlib/gecode.mzn b/gecode/flatzinc/mznlib/gecode.mzn index fa5f2eb441..c3d598148e 100644 --- a/gecode/flatzinc/mznlib/gecode.mzn +++ b/gecode/flatzinc/mznlib/gecode.mzn @@ -182,9 +182,7 @@ predicate gecode_schedule_cumulative_optional(array[int] of var int: start, include "all_different.mzn"; -include "among_fn.mzn"; include "gecode_fzn.mzn"; -include "nvalue_fn.mzn"; /** @group gecode.constraints Requires every pair of values in \a x to differ by at least \a distance. @@ -203,6 +201,7 @@ predicate gecode_inter_distance(array[$$X] of var int: x, var int: distance) = /** @group gecode.constraints Requires the sums \a x[i] + \a offset[i] to be pairwise distinct. The arrays must have identical index sets. + The offsets do not require additional variables for the sums. */ predicate gecode_all_different_offset( array[$$X] of int: offset, @@ -275,73 +274,10 @@ predicate gecode_array_ne(array[$$X] of var bool: x, array[$$X] of var bool: y) endif, ); -/** @group gecode.constraints - Requires the number of distinct values in \a x to be at most \a n. - An empty array has zero distinct values. -*/ -predicate gecode_nvalue_at_most(var int: n, array[$$X] of var $$E: x) = - if mzn_in_root_context() then - gecode_nvalues_int_fzn(enum2int(array1d(x)), 3, n) - else - nvalue(x) <= n - endif; - -/** @group gecode.constraints - Requires the number of distinct values in \a x to be at most \a n. - An empty array has zero distinct values. -*/ -predicate gecode_nvalue_at_most(var int: n, array[$$X] of var bool: x) = - if mzn_in_root_context() then - gecode_nvalues_bool_fzn(array1d(x), 3, n) - else - nvalue(x) <= n - endif; - -/** @group gecode.constraints - Requires the number of distinct values in \a x to be at least \a n. - An empty array has zero distinct values. -*/ -predicate gecode_nvalue_at_least(var int: n, array[$$X] of var $$E: x) = - if mzn_in_root_context() then - gecode_nvalues_int_fzn(enum2int(array1d(x)), 5, n) - else - nvalue(x) >= n - endif; - -/** @group gecode.constraints - Requires the number of distinct values in \a x to be at least \a n. - An empty array has zero distinct values. -*/ -predicate gecode_nvalue_at_least(var int: n, array[$$X] of var bool: x) = - if mzn_in_root_context() then - gecode_nvalues_bool_fzn(array1d(x), 5, n) - else - nvalue(x) >= n - endif; - -/** @group gecode.constraints - Requires at most \a n elements of \a x to belong to \a values. -*/ -predicate gecode_among_at_most(var int: n, array[$$X] of var $$E: x, set of $$E: values) = - if mzn_in_root_context() then - gecode_count_in_fzn(enum2int(array1d(x)), enum2int(values), 3, n) - else - among(x, values) <= n - endif; - -/** @group gecode.constraints - Requires at least \a n elements of \a x to belong to \a values. -*/ -predicate gecode_among_at_least(var int: n, array[$$X] of var $$E: x, set of $$E: values) = - if mzn_in_root_context() then - gecode_count_in_fzn(enum2int(array1d(x)), enum2int(values), 5, n) - else - among(x, values) >= n - endif; - /** @group gecode.constraints Requires at most \a n positions where \a x matches \a values. The arrays must have identical index sets. + The fixed comparison values do not require additional difference variables. */ predicate gecode_count_matches_at_most(var int: n, array[$$X] of var $$E: x, array[$$X] of $$E: values) = assert( @@ -357,6 +293,7 @@ predicate gecode_count_matches_at_most(var int: n, array[$$X] of var $$E: x, arr /** @group gecode.constraints Requires at least \a n positions where \a x matches \a values. The arrays must have identical index sets. + The fixed comparison values do not require additional difference variables. */ predicate gecode_count_matches_at_least(var int: n, array[$$X] of var $$E: x, array[$$X] of $$E: values) = assert( @@ -372,6 +309,7 @@ predicate gecode_count_matches_at_least(var int: n, array[$$X] of var $$E: x, ar /** @group gecode.constraints Constrains \a n to be the number of positions where \a x matches \a values. The arrays must have identical index sets. + The fixed comparison values do not require additional difference variables. */ predicate gecode_count_matches( var int: n, @@ -485,25 +423,6 @@ predicate gecode_minimum_distance( ), ); -/** @group gecode.constraints - Constrains \a quotient = \a x div \a y and - \a remainder = \a x mod \a y. The divisor \a y must be nonzero. - Division truncates towards zero; a nonzero remainder has the sign of \a x. - Reification is not supported. -*/ -predicate gecode_divmod( - var int: x, - var int: y, - var int: quotient, - var int: remainder, -) = - % Bound the results before dependent linear constraints are posted. - let { - var compute_div_bounds(x, y): q = quotient; - var compute_mod_bounds(x, y): r = remainder; - } in - gecode_int_divmod(x, y, q, r); - /** @group gecode.constraints Constrains \a y to be the integer root of degree \a degree of \a x, rounded down. The degree must be positive, and negative \a x is permitted @@ -513,123 +432,6 @@ predicate gecode_divmod( predicate gecode_nroot(var int: x, int: degree, var int: y) = assert(degree > 0, "gecode_nroot: degree must be positive", gecode_int_nroot(x, degree, y)); -/** @group gecode.constraints - Constrains \a y to be the nonnegative root of degree \a degree of \a x. - The degree must be positive, and \a x must be nonnegative. - Reification is not supported. -*/ -predicate gecode_nroot(var float: x, int: degree, var float: y) = - assert( - degree > 0, - "gecode_nroot: degree must be positive", - if degree = 2 then - float_sqrt(x, y) - else - gecode_float_nroot(x, degree, y) - endif, - ); - -/** @group gecode.constraints - Requires \a successor to describe a path from \a start to \a end - that visits every node once. The array indexes identify the nodes. - Only \a successor[\a end] is absent. The node array must be nonempty. - Reification is not supported. -*/ -predicate gecode_path( - array[$$Node] of var opt $$Node: successor, - var $$Node: start, - var $$Node: end, -) = - assert( - length(successor) > 0, - "gecode_path: empty node array", - gecode_path( - 0, - gecode_path_successors(successor), - enum2int(start) - min(enum2int(index_set(successor))), - enum2int(end) - min(enum2int(index_set(successor))), - ), - ); - -/** @group gecode.constraints - Requires \a successor to describe a path from \a start to \a end - that visits every node once, with total edge cost \a cost. - Only \a successor[\a end] is absent, and its edge costs zero. - The edge from i to j has cost \a costs[i,j]. - Both matrix axes must use the index set of the nonempty node array. - Reification is not supported. -*/ -predicate gecode_path( - array[$$Node, $$Node] of int: costs, - array[$$Node] of var opt $$Node: successor, - var $$Node: start, - var $$Node: end, - var int: cost, -) = - assert( - length(successor) > 0 /\ - index_set_1of2(costs) = index_set(successor) /\ - index_set_2of2(costs) = index_set(successor), - "gecode_path: incompatible cost matrix", - gecode_path_cost( - array1d(costs), - 0, - gecode_path_successors(successor), - enum2int(start) - min(enum2int(index_set(successor))), - enum2int(end) - min(enum2int(index_set(successor))), - cost, - ), - ); - -/** @group gecode.constraints - Requires \a successor to describe a path from \a start to \a end - that visits every node once. Each \a edge_costs[i] is the cost of - the edge from i to \a successor[i], and their sum is \a cost. - Only \a successor[\a end] is absent, and its edge costs zero. - The edge from i to j has cost \a costs[i,j]. - All array axes must use the index set of the nonempty node array. - Reification is not supported. -*/ -predicate gecode_path( - array[$$Node, $$Node] of int: costs, - array[$$Node] of var opt $$Node: successor, - var $$Node: start, - var $$Node: end, - array[$$Node] of var int: edge_costs, - var int: cost, -) = - assert( - length(successor) > 0 /\ - index_set_1of2(costs) = index_set(successor) /\ - index_set_2of2(costs) = index_set(successor) /\ - index_set(edge_costs) = index_set(successor), - "gecode_path: incompatible cost matrix or edge costs", - gecode_path_cost_array( - array1d(costs), - 0, - gecode_path_successors(successor), - enum2int(start) - min(enum2int(index_set(successor))), - enum2int(end) - min(enum2int(index_set(successor))), - array1d(edge_costs), - cost, - ), - ); - -% The native successor representation uses an extra node to mark the end. -function array[int] of var int: gecode_path_successors( - array[$$Node] of var opt $$Node: successor, -) = - let { - constraint forall (next in successor) ( - occurs(next) -> deopt(next) in index_set(successor) - ); - } in - [if occurs(next) then - enum2int(deopt(next)) - min(enum2int(index_set(successor))) - else - length(successor) - endif | next in successor]; - /** @group gecode.constraints Requires the present rectangles with origins (\a x[i], \a y[i]) and sizes (\a width[i], \a height[i]) not to overlap. @@ -693,7 +495,7 @@ predicate gecode_convex(var set of $$E: x, var set of $$E: hull) = gecode_set_co /** @group gecode.constraints Requires every element of each set in \a x to be smaller than - every element of each later set. Empty sets are permitted. + every element of each later set. Empty sets and arrays are permitted. Reification is not supported. */ predicate gecode_set_sequence(array[$$X] of var set of $$E: x) = gecode_array_set_seq(enum2int(array1d(x))); @@ -701,7 +503,7 @@ predicate gecode_set_sequence(array[$$X] of var set of $$E: x) = gecode_array_se /** @group gecode.constraints Requires every element of each set in \a x to be smaller than every element of each later set, and constrains \a y to their union. - Empty sets are permitted. + Empty sets and arrays are permitted. An empty array has an empty union. Reification is not supported. */ predicate gecode_set_sequence(array[$$X] of var set of $$E: x, var set of $$E: y) = @@ -726,24 +528,6 @@ predicate gecode_at_most_one_intersection(array[$$X] of var set of $$E: x, int: predicate gecode_sorted_set(array[$$X] of var $$E: x, var set of $$E: values) = gecode_set_channel_sorted(enum2int(array1d(x)), enum2int(values)); -/** @group gecode.constraints - Constrains \a y to be the logarithm of \a x to base \a base. - The base must be positive and different from one. Requires MPFR support. - Reification is not supported. -*/ -predicate gecode_log(float: base, var float: x, var float: y) = - assert( - base > 0.0 /\ base != 1.0, - "gecode_log: base must be positive and different from one", - if base = 2.0 then - float_log2(x, y) - elseif base = 10.0 then - float_log10(x, y) - else - gecode_float_log_base(base, x, y) - endif, - ); - /** @group gecode.constraints Constrains \a index to be the index of a smallest value in \a x. Any qualifying index is permitted. The array must be nonempty. diff --git a/gecode/flatzinc/mznlib/gecode_fzn.mzn b/gecode/flatzinc/mznlib/gecode_fzn.mzn index 07ee28b9a8..2ec406d24d 100644 --- a/gecode/flatzinc/mznlib/gecode_fzn.mzn +++ b/gecode/flatzinc/mznlib/gecode_fzn.mzn @@ -80,14 +80,6 @@ predicate gecode_count_int_fzn( var int: count, ); -% Compare the number of entries of x that belong to values with count. -predicate gecode_count_in_fzn( - array[int] of var int: x, - set of int: values, - int: relation, - var int: count, -); - % Compare the number of position-wise matches between x and values with count. predicate gecode_count_matches_fzn( array[int] of var int: x, @@ -96,19 +88,6 @@ predicate gecode_count_matches_fzn( var int: count, ); -% Compare the number of distinct values in x with count; an empty array has none. -predicate gecode_nvalues_int_fzn( - array[int] of var int: x, - int: relation, - var int: count, -); - -predicate gecode_nvalues_bool_fzn( - array[int] of var bool: x, - int: relation, - var int: count, -); - % Arrays have unequal lengths or at least one pair of corresponding entries differs. predicate gecode_array_int_ne(array[int] of var int: x, array[int] of var int: y); @@ -172,14 +151,6 @@ predicate gecode_minimum_distance_required_fzn( array[int] of int: requirements, ); -% x = y*quotient+remainder, with quotient truncated toward zero and y != 0. -predicate gecode_int_divmod( - var int: x, - var int: y, - var int: quotient, - var int: remainder, -); - % y is floor(root(x,n)); n is positive, and negative x requires odd n. predicate gecode_int_nroot(var int: x, int: n, var int: y); @@ -195,34 +166,6 @@ predicate gecode_regular_nfa_int_fzn( set of int: final, ); -% Node labels start at offset; the final successor is offset+length(x). -predicate gecode_path( - int: offset, - array[int] of var int: x, - var int: start, - var int: end, -); - -% costs is a row-major square matrix; the final successor edge costs zero. -predicate gecode_path_cost( - array[int] of int: costs, - int: offset, - array[int] of var int: x, - var int: start, - var int: end, - var int: cost, -); - -predicate gecode_path_cost_array( - array[int] of int: costs, - int: offset, - array[int] of var int: x, - var int: start, - var int: end, - array[int] of var int: edge_costs, - var int: cost, -); - % Constrain tasks whose present entry is true not to overlap. Durations are % nonnegative. The arrays have equal lengths. The caller must also constrain % start[i] + duration[i] = end[i] for each present task. @@ -302,10 +245,6 @@ predicate gecode_set_element2d( % Constrain y to be x raised to the nonnegative integer n. predicate gecode_float_pow(var float: x, int: n, var float: y); -% Constrain y to be the n-th root of x. The degree n is positive. -% Both x and y are nonnegative, including for odd degrees. -predicate gecode_float_nroot(var float: x, int: n, var float: y); - % Constrain y to be the minimum of the nonempty array x. predicate gecode_float_array_min(array[int] of var float: x, var float: y); @@ -315,10 +254,6 @@ predicate gecode_float_array_max(array[int] of var float: x, var float: y); % Constrain y to be the positive fixed base raised to x. Requires MPFR. predicate gecode_float_pow_base(float: base, var float: x, var float: y); -% Constrain y to be the logarithm of x to the fixed base. -% The base is positive and differs from one; x is positive. Requires MPFR. -predicate gecode_float_log_base(float: base, var float: x, var float: y); - % index is the zero-based position plus offset of an extremum. % If break_ties is true, choose the first such position. Arrays are nonempty. predicate gecode_arg_min_int( diff --git a/gecode/flatzinc/registry.cpp b/gecode/flatzinc/registry.cpp index a314c98266..f3d579e2bf 100755 --- a/gecode/flatzinc/registry.cpp +++ b/gecode/flatzinc/registry.cpp @@ -1926,22 +1926,10 @@ fvar, count(s, s.arg2intvarargs(ce[0]), s.arg2IntVar(ce[1]), int_relation(ce[2]), s.arg2IntVar(ce[3]), s.ann2ipl(ann)); } - void p_count_in(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - count(s, s.arg2intvarargs(ce[0]), s.arg2intset(ce[1]), - int_relation(ce[2]), s.arg2IntVar(ce[3]), s.ann2ipl(ann)); - } void p_count_matches(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { count(s, s.arg2intvarargs(ce[0]), s.arg2intargs(ce[1]), int_relation(ce[2]), s.arg2IntVar(ce[3]), s.ann2ipl(ann)); } - void p_nvalues_int(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - nvalues(s, s.arg2intvarargs(ce[0]), int_relation(ce[1]), - s.arg2IntVar(ce[2]), s.ann2ipl(ann)); - } - void p_nvalues_bool(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - nvalues(s, s.arg2boolvarargs(ce[0]), int_relation(ce[1]), - s.arg2IntVar(ce[2]), s.ann2ipl(ann)); - } void p_array_int_ne(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { rel(s, s.arg2intvarargs(ce[0]), IRT_NQ, s.arg2intvarargs(ce[1]), s.ann2ipl(ann)); @@ -2018,42 +2006,11 @@ fvar, minimum_distance(s,x,s.arg2IntVar(ce[3]),d,s.ann2ipl(ann),kind); } - void p_int_divmod(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - IntVar x = s.arg2IntVar(ce[0]), y = s.arg2IntVar(ce[1]); - IntVar q = s.arg2IntVar(ce[2]), r = s.arg2IntVar(ce[3]); - // Bound the results before the linear decomposition estimates its ranges. - // For nonzero y, truncating division gives |q| <= |x| and r between 0 and x. - int magnitude = std::max(-x.min(),x.max()); - dom(s,q,-magnitude,magnitude); - dom(s,r,std::min(0,x.min()),std::max(0,x.max())); - if (!s.failed()) - divmod(s,x,y,q,r,s.ann2ipl(ann)); - } void p_int_nroot(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { nroot(s, s.arg2IntVar(ce[0]), ce[1]->getInt(), s.arg2IntVar(ce[2]), s.ann2ipl(ann)); } - void p_path(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - IntVarArgs x = s.arg2intvarargs(ce[1]); - unshare(s, x); - path(s, ce[0]->getInt(), x, s.arg2IntVar(ce[2]), s.arg2IntVar(ce[3]), - s.ann2ipl(ann)); - } - void p_path_cost(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - IntVarArgs x = s.arg2intvarargs(ce[2]); - unshare(s, x); - path(s, s.arg2intargs(ce[0]), ce[1]->getInt(), x, - s.arg2IntVar(ce[3]), s.arg2IntVar(ce[4]), s.arg2IntVar(ce[5]), - s.ann2ipl(ann)); - } - void p_path_cost_array(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - IntVarArgs x = s.arg2intvarargs(ce[2]); - unshare(s, x); - path(s, s.arg2intargs(ce[0]), ce[1]->getInt(), x, - s.arg2IntVar(ce[3]), s.arg2IntVar(ce[4]), s.arg2intvarargs(ce[5]), - s.arg2IntVar(ce[6]), s.ann2ipl(ann)); - } void p_nooverlap_optional(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { nooverlap(s, s.arg2intvarargs(ce[0]), s.arg2intargs(ce[1]), s.arg2intvarargs(ce[2]), s.arg2intargs(ce[3]), @@ -2313,10 +2270,7 @@ fvar, registry().add("gecode_bool_int_channel", &p_bool_int_channel); registry().add("gecode_sort_permutation", &p_sort_permutation); registry().add("gecode_count_int_fzn", &p_count_int); - registry().add("gecode_count_in_fzn", &p_count_in); registry().add("gecode_count_matches_fzn", &p_count_matches); - registry().add("gecode_nvalues_int_fzn", &p_nvalues_int); - registry().add("gecode_nvalues_bool_fzn", &p_nvalues_bool); registry().add("gecode_int_ite", &p_int_ite); registry().add("gecode_bool_ite", &p_bool_ite); registry().add("gecode_gcd_int", &p_gcd); @@ -2336,11 +2290,7 @@ fvar, registry().add("gecode_product_mod_var_fzn_imp", &p_product_mod_reif); registry().add("gecode_minimum_distance_fzn", &p_minimum_distance); registry().add("gecode_minimum_distance_required_fzn", &p_minimum_distance); - registry().add("gecode_int_divmod", &p_int_divmod); registry().add("gecode_int_nroot", &p_int_nroot); - registry().add("gecode_path", &p_path); - registry().add("gecode_path_cost", &p_path_cost); - registry().add("gecode_path_cost_array", &p_path_cost_array); registry().add("gecode_nooverlap_optional", &p_nooverlap_optional); registry().add("gecode_all_different_optional", &p_distinct_optional); registry().add("gecode_all_different_except", &p_distinct_except); @@ -2594,7 +2544,8 @@ fvar, void p_array_set_seq(FlatZincSpace& s, const ConExpr& ce, AST::Node *) { SetVarArgs sv = s.arg2setvarargs(ce[0]); - sequence(s, sv); + if (sv.size() > 0) + sequence(s, sv); } void p_array_set_seq_union(FlatZincSpace& s, const ConExpr& ce, @@ -2979,11 +2930,6 @@ fvar, void p_float_pow(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { pow(s, s.arg2FloatVar(ce[0]), ce[1]->getInt(), s.arg2FloatVar(ce[2])); } - void p_float_nroot(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { - if (ce[1]->getInt() <= 0) - throw FlatZinc::Error("Registry", "float root degree must be positive"); - nroot(s, s.arg2FloatVar(ce[0]), ce[1]->getInt(), s.arg2FloatVar(ce[2])); - } void p_float_array_min(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { min(s, s.arg2floatvarargs(ce[0]), s.arg2FloatVar(ce[1])); } @@ -2994,9 +2940,6 @@ fvar, void p_float_pow_base(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { pow(s, ce[0]->getFloat(), s.arg2FloatVar(ce[1]), s.arg2FloatVar(ce[2])); } - void p_float_log_base(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { - log(s, ce[0]->getFloat(), s.arg2FloatVar(ce[1]), s.arg2FloatVar(ce[2])); - } #endif void p_float_rel_imp(FlatZincSpace& s, const ConExpr& ce, AST::Node*) { @@ -3022,7 +2965,6 @@ fvar, registry().add("gecode_float_rel_imp_fzn", &p_float_rel_imp); registry().add("gecode_float_ite", &p_float_ite); registry().add("gecode_float_pow", &p_float_pow); - registry().add("gecode_float_nroot", &p_float_nroot); registry().add("gecode_float_array_min", &p_float_array_min); registry().add("gecode_float_array_max", &p_float_array_max); @@ -3050,7 +2992,6 @@ fvar, #ifdef GECODE_HAS_MPFR registry().add("gecode_float_pow_base", &p_float_pow_base); - registry().add("gecode_float_log_base", &p_float_log_base); registry().add("float_acos",&p_float_acos); registry().add("float_asin",&p_float_asin); registry().add("float_atan",&p_float_atan); diff --git a/test/flatzinc/native-registry.cpp b/test/flatzinc/native-registry.cpp index fdf1ee7748..90e14fa4ac 100644 --- a/test/flatzinc/native-registry.cpp +++ b/test/flatzinc/native-registry.cpp @@ -93,22 +93,14 @@ namespace Test { namespace FlatZinc { "constraint gecode_minimum_distance_fzn([0,0,2],3,[0,2,4,2,0,3,4,3,0],1); solve satisfy;", unsat); (void) new FlatZincTest("NativeRegistry::NondeterministicPrefix", "constraint gecode_regular_nfa_int_fzn([0,1],3,0,2,[{1,2},{},{},{3},{},{}],1,{3}); solve satisfy;", sat); - (void) new FlatZincTest("NativeRegistry::DivmodNegative", - "constraint gecode_int_divmod(-7,4,-1,-3); solve satisfy;", sat); - (void) new FlatZincTest("NativeRegistry::DivmodUnboundedResults", - "var 0..17:x; var int:q; var int:r; " - "constraint gecode_int_divmod(x,5,q,r); " - "constraint int_eq(x,17); constraint int_ne(q,3); solve satisfy;", unsat); - (void) new FlatZincTest("NativeRegistry::DivmodUnboundedNegativeResults", - "var -17..0:x; var int:q; var int:r; " - "constraint gecode_int_divmod(x,-5,q,r); " - "constraint int_eq(x,-17); constraint int_ne(r,-2); solve satisfy;", unsat); - (void) new FlatZincTest("NativeRegistry::PathCostSentinel", - "constraint gecode_path_cost_array([0,4,9,1,0,5,7,2,0],0,[1,2,3],0,2,[4,5,0],9); solve satisfy;", sat); (void) new FlatZincErrorTest("NativeRegistry::SortSize", "constraint gecode_sort_permutation([1,2],[1],[1,2],1); solve satisfy;", {}, "sort arrays must have equal lengths"); #ifdef GECODE_HAS_SET_VARS + (void) new FlatZincTest("NativeRegistry::SetSequenceEmpty", + "constraint gecode_array_set_seq([]); solve satisfy;", sat); + (void) new FlatZincTest("NativeRegistry::SetSequenceEmptyUnion", + "constraint gecode_array_set_seq_union([],{1}); solve satisfy;", unsat); (void) new FlatZincTest("NativeRegistry::AtmostOneSingletonCardinality", "constraint gecode_set_atmost_one([{1}],2); solve satisfy;", unsat); (void) new FlatZincTest("NativeRegistry::AtmostOneUnitCardinality", From 9bcef8a8806c6e9b25b800cfead4eb40fb529f58 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Mon, 12 Oct 2026 20:59:00 +0200 Subject: [PATCH 21/22] Use the float harness for exact fixed-base power examples --- test/float/transcendental.cpp | 102 +++++++++++++++++----------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/test/float/transcendental.cpp b/test/float/transcendental.cpp index 5c14917af8..70ee8703a5 100644 --- a/test/float/transcendental.cpp +++ b/test/float/transcendental.cpp @@ -47,60 +47,62 @@ namespace Test { namespace Float { namespace Transcendental { /// Exact fixed-base powers, inverse logarithms, and shared variables - class PowBaseConsistency : public Base { - class TestSpace : public Gecode::Space { - public: - Gecode::FloatVar x, y; - TestSpace(Gecode::FloatNum base, Gecode::FloatNum a, - Gecode::FloatNum b, bool logarithm, bool shared, - bool delayed) - : x(*this,delayed ? -4.0 : a,delayed ? 8.0 : a), - y(*this,delayed ? 0.0 : b,delayed ? 8.0 : b) { - if (shared) - y = x; - if (logarithm) - Gecode::log(*this,base,y,x); - else - Gecode::pow(*this,base,x,y); - } - TestSpace(TestSpace& s) : Gecode::Space(s) { - x.update(*this,s.x); - y.update(*this,s.y); - } - virtual Gecode::Space* copy(void) { - return new TestSpace(*this); - } - }; - - bool check(Gecode::FloatNum base, Gecode::FloatNum a, - Gecode::FloatNum b, bool logarithm, bool shared, - bool delayed, bool expected) { - TestSpace s(base,a,b,logarithm,shared,delayed); - if (delayed && (s.status() != Gecode::SS_FAILED)) { - Gecode::rel(s,s.x,Gecode::FRT_EQ,a); - Gecode::rel(s,s.y,Gecode::FRT_EQ,b); - } - return (s.status() != Gecode::SS_FAILED) == expected; + class PowBaseConsistency : public Test { + protected: + Gecode::FloatNum base; + Gecode::FloatNum input; + Gecode::FloatNum output; + bool logarithm; + bool shared; + bool expected; + public: + PowBaseConsistency(Gecode::FloatNum b, Gecode::FloatNum x, + Gecode::FloatNum y, bool log, bool share, bool valid) + : Test("Transcendental::PowBaseConsistency::"+str(b)+"::"+ + str(x)+"::"+str(y)+(log ? "::Log" : "::Pow")+ + (share ? "::Shared" : "::Separate"), + share ? 1 : 2,-4.0,8.0,1.0,CPLT_ASSIGNMENT,false), + base(b), input(x), output(y), logarithm(log), shared(share), + expected(valid) { + testsearch = false; + } + /// Check one exact example through the standard posting/assignment tests + virtual Assignment* assignment(void) const { + Assignment* a = new RandomAssignment(arity,dom,1,_rand); + a->set(0,input); + if (!shared) + a->set(1,output); + return a; } + /// Exact expected results avoid checking exp/log against itself + virtual MaybeType solution(const Assignment&) const { + return expected ? MT_TRUE : MT_FALSE; + } + virtual void post(Gecode::Space& home, Gecode::FloatVarArray& x) { + Gecode::FloatVar y = shared ? x[0] : x[1]; + if (!shared) + Gecode::rel(home,y,Gecode::FRT_GQ,0.0); + if (logarithm) + Gecode::log(home,base,y,x[0]); + else + Gecode::pow(home,base,x[0],y); + } + }; + + /// Register the exact powers and their logarithmic forms + class PowBaseExamples { public: - PowBaseConsistency(void) - : Base("Float::Transcendental::PowBaseConsistency") {} - virtual bool run(void) { - for (int delayed=0; delayed<2; delayed++) { - for (int logarithm=0; logarithm<2; logarithm++) { - // Exact powers avoid checking exp/log against itself. - if (!check(2.0,3.0,8.0,logarithm,false,delayed,true) || - !check(2.0,3.0,7.0,logarithm,false,delayed,false) || - !check(0.5,3.0,0.125,logarithm,false,delayed,true) || - !check(1.0,3.0,1.0,logarithm,false,delayed,true) || - !check(2.0,0.0,0.0,logarithm,true,delayed,false) || - !check(0.25,0.5,0.5,logarithm,true,delayed,true)) - return false; - } + PowBaseExamples(void) { + for (int logarithm=0; logarithm<2; logarithm++) { + (void) new PowBaseConsistency(2.0,3.0,8.0,logarithm,false,true); + (void) new PowBaseConsistency(2.0,3.0,7.0,logarithm,false,false); + (void) new PowBaseConsistency(0.5,3.0,0.125,logarithm,false,true); + (void) new PowBaseConsistency(1.0,3.0,1.0,logarithm,false,true); + (void) new PowBaseConsistency(2.0,0.0,0.0,logarithm,true,false); + (void) new PowBaseConsistency(0.25,0.5,0.5,logarithm,true,true); } - return true; } - } pow_base_consistency; + } pow_base_examples; /// %Test for exponent constraint class ExpXY : public Test { From f70f5f9fe3c35ca433952e42339a053153734413 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Mon, 12 Oct 2026 21:26:02 +0200 Subject: [PATCH 22/22] Simplify MiniZinc hooks and native registry posters --- .../flatzinc/mznlib/fzn_if_then_else_bool.mzn | 36 +++---------------- .../mznlib/fzn_if_then_else_float.mzn | 36 +++---------------- .../flatzinc/mznlib/fzn_if_then_else_int.mzn | 36 +++---------------- .../flatzinc/mznlib/fzn_if_then_else_set.mzn | 36 +++---------------- .../mznlib/fzn_if_then_else_var_bool.mzn | 6 ++-- .../mznlib/fzn_if_then_else_var_float.mzn | 6 ++-- .../mznlib/fzn_if_then_else_var_int.mzn | 6 ++-- .../mznlib/fzn_if_then_else_var_set.mzn | 6 ++-- gecode/flatzinc/registry.cpp | 14 +++----- test/flatzinc/native-registry.cpp | 23 ++++-------- 10 files changed, 39 insertions(+), 166 deletions(-) diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn index df9b2fbabe..27d3860c91 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_bool.mzn @@ -30,39 +30,11 @@ % % -predicate gecode_bool_ite( - var bool: b, - var bool: x, - var bool: y, - var bool: z, -); +include "fzn_if_then_else_var_bool.mzn"; -% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_bool( - array[int] of var bool: c, - array[int] of bool: x, + array [int] of var bool: c, + array [int] of bool: x, var bool: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then - gecode_bool_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) - else - let { - array[index_set(c)] of var bool: d; - } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) - endif; + fzn_if_then_else_var_bool(c, x, y); diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn index a7f7658d97..275da6bbc8 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_float.mzn @@ -30,39 +30,11 @@ % % -predicate gecode_float_ite( - var bool: b, - var float: x, - var float: y, - var float: z, -); +include "fzn_if_then_else_var_float.mzn"; -% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_float( - array[int] of var bool: c, - array[int] of float: x, + array [int] of var bool: c, + array [int] of float: x, var float: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then - gecode_float_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) - else - let { - array[index_set(c)] of var bool: d; - } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) - endif; + fzn_if_then_else_var_float(c, x, y); diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn index 4881a22aef..32c94a26b6 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_int.mzn @@ -30,39 +30,11 @@ % % -predicate gecode_int_ite( - var bool: b, - var int: x, - var int: y, - var int: z, -); +include "fzn_if_then_else_var_int.mzn"; -% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_int( - array[int] of var bool: c, - array[int] of int: x, + array [int] of var bool: c, + array [int] of int: x, var int: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then - gecode_int_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) - else - let { - array[index_set(c)] of var bool: d; - } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) - endif; + fzn_if_then_else_var_int(c, x, y); diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn index dce1214d61..2422d8ec6a 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_set.mzn @@ -30,39 +30,11 @@ % % -predicate gecode_set_ite( - var bool: b, - var set of int: x, - var set of int: y, - var set of int: z, -); +include "fzn_if_then_else_var_set.mzn"; -% A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_set( - array[int] of var bool: c, - array[int] of set of int: x, + array [int] of var bool: c, + array [int] of set of int: x, var set of int: y, ) = - if length(c) = 2 /\ - index_sets_agree(c, x) /\ - (if is_fixed(c[max(index_set(c))]) then - fix(c[max(index_set(c))]) - else - false - endif) then - gecode_set_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) - else - let { - array[index_set(c)] of var bool: d; - } in - forall (i in index_set(c)) ( - if i > min(index_set(c)) then - d[i] = (not c[i - 1] /\ d[i - 1]) - else - d[i] = true - endif - ) /\ - forall (i in index_set(c)) ( - c[i] /\ d[i] -> y = x[i] - ) - endif; + fzn_if_then_else_var_set(c, x, y); diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn index 7736eabe3a..814606b32b 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_bool.mzn @@ -39,8 +39,8 @@ predicate gecode_bool_ite( % A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_var_bool( - array[int] of var bool: c, - array[int] of var bool: x, + array [int] of var bool: c, + array [int] of var bool: x, var bool: y, ) = if length(c) = 2 /\ @@ -53,7 +53,7 @@ predicate fzn_if_then_else_var_bool( gecode_bool_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { - array[index_set(c)] of var bool: d; + array [index_set(c)] of var bool: d; } in forall (i in index_set(c)) ( if i > min(index_set(c)) then diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn index 98a4ec6265..e16f978fad 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_float.mzn @@ -39,8 +39,8 @@ predicate gecode_float_ite( % A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_var_float( - array[int] of var bool: c, - array[int] of var float: x, + array [int] of var bool: c, + array [int] of var float: x, var float: y, ) = if length(c) = 2 /\ @@ -53,7 +53,7 @@ predicate fzn_if_then_else_var_float( gecode_float_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { - array[index_set(c)] of var bool: d; + array [index_set(c)] of var bool: d; } in forall (i in index_set(c)) ( if i > min(index_set(c)) then diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn index d88c390517..d9bd87334e 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_int.mzn @@ -39,8 +39,8 @@ predicate gecode_int_ite( % A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_var_int( - array[int] of var bool: c, - array[int] of var int: x, + array [int] of var bool: c, + array [int] of var int: x, var int: y, ) = if length(c) = 2 /\ @@ -53,7 +53,7 @@ predicate fzn_if_then_else_var_int( gecode_int_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { - array[index_set(c)] of var bool: d; + array [index_set(c)] of var bool: d; } in forall (i in index_set(c)) ( if i > min(index_set(c)) then diff --git a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn index 7d63c9beef..bfb2d0d85a 100644 --- a/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn +++ b/gecode/flatzinc/mznlib/fzn_if_then_else_var_set.mzn @@ -39,8 +39,8 @@ predicate gecode_set_ite( % A two-branch conditional ends with a fixed true condition for else. predicate fzn_if_then_else_var_set( - array[int] of var bool: c, - array[int] of var set of int: x, + array [int] of var bool: c, + array [int] of var set of int: x, var set of int: y, ) = if length(c) = 2 /\ @@ -53,7 +53,7 @@ predicate fzn_if_then_else_var_set( gecode_set_ite(c[min(index_set(c))], x[min(index_set(c))], x[max(index_set(c))], y) else let { - array[index_set(c)] of var bool: d; + array [index_set(c)] of var bool: d; } in forall (i in index_set(c)) ( if i > min(index_set(c)) then diff --git a/gecode/flatzinc/registry.cpp b/gecode/flatzinc/registry.cpp index f3d579e2bf..4c05a3dd66 100755 --- a/gecode/flatzinc/registry.cpp +++ b/gecode/flatzinc/registry.cpp @@ -1154,8 +1154,7 @@ namespace Gecode { namespace FlatZinc { argmax(s, bv, offset, s.arg2IntVar(ce[2]), true, s.ann2ipl(ann)); } - template - void p_regular(FlatZincSpace& s, VarArgs iv, int q, int symbols_min, + void p_regular(FlatZincSpace& s, IntVarArgs iv, int q, int symbols_min, int symbols_max, IntArgs d, int q0, AST::SetLit* finals, AST::Node* ann) { int symbols = symbols_max - symbols_min + 1; @@ -1215,9 +1214,9 @@ namespace Gecode { namespace FlatZinc { } - template - void p_regular_nfa(FlatZincSpace& s, VarArgs x, const ConExpr& ce, - AST::Node* ann) { + void p_regular_nfa_int(FlatZincSpace& s, const ConExpr& ce, + AST::Node* ann) { + IntVarArgs x = s.arg2intvarargs(ce[0]); int q = ce[1]->getInt(), first_symbol = ce[2]->getInt(); int symbols = ce[3]->getInt(), initial = ce[5]->getInt(); IntSetArgs table = s.arg2intsetargs(ce[4]); @@ -1247,9 +1246,6 @@ namespace Gecode { namespace FlatZinc { unshare(s,x); extensional(s,x,s.getSharedDFA(automaton),s.ann2ipl(ann)); } - void p_regular_nfa_int(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { - p_regular_nfa(s,s.arg2intvarargs(ce[0]),ce,ann); - } void p_sort(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { @@ -2041,14 +2037,12 @@ fvar, void p_arg_min_bool(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { BoolVarArgs x = s.arg2boolvarargs(ce[0]); - unshare(s, x); argmin(s, x, ce[1]->getInt(), s.arg2IntVar(ce[2]), ce[3]->getBool(), s.ann2ipl(ann)); } void p_arg_max_bool(FlatZincSpace& s, const ConExpr& ce, AST::Node* ann) { BoolVarArgs x = s.arg2boolvarargs(ce[0]); - unshare(s, x); argmax(s, x, ce[1]->getInt(), s.arg2IntVar(ce[2]), ce[3]->getBool(), s.ann2ipl(ann)); } diff --git a/test/flatzinc/native-registry.cpp b/test/flatzinc/native-registry.cpp index 90e14fa4ac..3b2599ca58 100644 --- a/test/flatzinc/native-registry.cpp +++ b/test/flatzinc/native-registry.cpp @@ -36,33 +36,24 @@ namespace Test { namespace FlatZinc { namespace { - // Basic and advanced annotations must select different filtering stages. + // Annotations preserve both consistency and algorithm flags. class AlgorithmAnnotations : public Base { public: - AlgorithmAnnotations(void) : Base("FlatZinc::NativeRegistry::AlgorithmAnnotations") {} + AlgorithmAnnotations(void) + : Base("FlatZinc::NativeRegistry::AlgorithmAnnotations") {} virtual bool run(void) { const char* names[] = {"gecode_basic_propagation", "gecode_advanced_propagation", "gecode_full_propagation"}; + const Gecode::IntPropLevel levels[] = { + Gecode::IPL_BASIC, Gecode::IPL_ADVANCED, Gecode::IPL_FULL + }; for (int i=0; i<3; i++) { Gecode::FlatZinc::FlatZincSpace space; Gecode::FlatZinc::AST::Array ann; ann.a.push_back(new Gecode::FlatZinc::AST::Atom(names[i])); ann.a.push_back(new Gecode::FlatZinc::AST::Atom("domain")); - Gecode::IntVarArgs x(3); - x[0] = Gecode::IntVar(space,2,6); - x[1] = Gecode::IntVar(space,10,14); - x[2] = Gecode::IntVar(space,4,15); - Gecode::IntVar distance(space,6,8); - Gecode::inter_distance(space,x,distance,space.ann2ipl(&ann)); - if ((space.status() == Gecode::SS_FAILED) || !distance.assigned() || - (distance.val() != 6)) + if (space.ann2ipl(&ann) != (Gecode::IPL_DOM | levels[i])) return false; - if (i == 0) { - if ((x[0].min() != 2) || (x[0].max() != 6)) return false; - } else if (!x.assigned() || (x[0].val() != 2) || - (x[1].val() != 14) || (x[2].val() != 8)) { - return false; - } } return true; }