From accad1a0c377169d4c94a4ad377b5813aee0c819 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Sat, 3 Oct 2026 12:20:45 +0200 Subject: [PATCH 01/13] Add quadratic inter-distance bounds propagation --- Makefile.in | 6 +- changelog.in | 10 + cmake/GecodeSources.cmake | 2 + gecode/int.hh | 25 ++ gecode/int/inter-distance.cpp | 500 ++++++++++++++++++++++++++++++++++ gecode/int/inter-distance.hh | 87 ++++++ test/int/inter-distance.cpp | 277 +++++++++++++++++++ 7 files changed, 904 insertions(+), 3 deletions(-) create mode 100644 gecode/int/inter-distance.cpp create mode 100644 gecode/int/inter-distance.hh create mode 100644 test/int/inter-distance.cpp diff --git a/Makefile.in b/Makefile.in index 26d8c94e77..8e5d2c625d 100755 --- a/Makefile.in +++ b/Makefile.in @@ -337,7 +337,7 @@ INTSRC0 = \ bin-packing/conflict-graph.cpp \ order.cpp order/propagate.cpp \ unary.cpp cumulative.cpp cumulatives.cpp \ - circuit.cpp no-overlap.cpp nvalues.cpp \ + circuit.cpp no-overlap.cpp inter-distance.cpp nvalues.cpp \ member.cpp min-distance.cpp branch/action.cpp branch/chb.cpp \ arithmetic/mult.cpp \ branch/view-sel.cpp branch/val-sel-commit.cpp \ @@ -428,7 +428,7 @@ INTHDR0 = \ circuit.hh circuit/base.hpp circuit/val.hpp circuit/dom.hpp \ no-overlap.hh no-overlap/dim.hpp no-overlap/box.hpp \ no-overlap/base.hpp no-overlap/man.hpp no-overlap/opt.hpp \ - nvalues.hh nvalues/range-event.hpp \ + inter-distance.hh nvalues.hh nvalues/range-event.hpp \ nvalues/sym-bit-matrix.hpp nvalues/graph.hpp \ nvalues/bool-base.hpp nvalues/bool-eq.hpp \ nvalues/bool-lq.hpp nvalues/bool-gq.hpp \ @@ -1122,7 +1122,7 @@ INTTESTSRC00 = \ rel linear gcc sorted unshare exec sequence \ mm-arithmetic mm-bool mm-lin mm-count mm-rel \ bin-packing order unary cumulative cumulatives circuit \ - no-overlap precede nvalues member min-distance + no-overlap precede inter-distance nvalues member min-distance INTTESTSRC0 = test/int.cpp $(INTTESTSRC00:%=test/int/%.cpp) INTTESTOBJ = $(INTTESTSRC0:%.cpp=%$(OBJSUFFIX)) diff --git a/changelog.in b/changelog.in index 219972aeb1..39cf03f875 100755 --- a/changelog.in +++ b/changelog.in @@ -162,6 +162,16 @@ The propagation algorithms follow: M. Z. Lagerkvist, Propagation Algorithms for the Minimum-Distance Constraint over Selected Points, ModRef 2026. +[ENTRY] +Module: int +What: new +Rank: major +Thanks: Claude-Guy Quimper +[DESCRIPTION] +Add the inter-distance constraint with quadratic bounds filtering, adopting +Claude-Guy Quimper's 2006 submission and the algorithm of Quimper, +Lopez-Ortiz, and Pesant (AAAI 2006). + [RELEASE] Version: 6.4.0 Date: 2026-07-15 diff --git a/cmake/GecodeSources.cmake b/cmake/GecodeSources.cmake index 09b4365587..58a415eb36 100644 --- a/cmake/GecodeSources.cmake +++ b/cmake/GecodeSources.cmake @@ -88,6 +88,7 @@ set(GECODE_INT_SOURCES gecode/int/extensional/tuple-set.cpp gecode/int/gcc.cpp gecode/int/int-set.cpp + gecode/int/inter-distance.cpp gecode/int/ldsb.cpp gecode/int/ldsb/sym-imp.cpp gecode/int/ldsb/sym-obj.cpp @@ -415,6 +416,7 @@ set(GECODE_TEST_SOURCES test/int/exec.cpp test/int/extensional.cpp test/int/gcc.cpp + test/int/inter-distance.cpp test/int/linear.cpp test/int/member.cpp test/int/min-distance.cpp diff --git a/gecode/int.hh b/gecode/int.hh index 7d0e765955..382ac4ef43 100755 --- a/gecode/int.hh +++ b/gecode/int.hh @@ -1748,6 +1748,31 @@ namespace Gecode { //@} + /** + * \defgroup TaskModelIntInterDistance Inter-distance constraint + * \ingroup TaskModelInt + */ + //@{ + /** \brief Post bounds-consistent propagator for \f$|x_i-x_j|\geq p\f$ for all \f$0\leq i\neq j<|x|\f$ + * + * Uses the quadratic algorithm of Quimper, Lopez-Ortiz, and Pesant + * (AAAI 2006). Supports bounds(Z) consistency (all values of \a ipl). + * Domains with holes are treated as intervals between their bounds. + * Each filtering pass uses quadratic time and temporary space. + * + * A zero distance, or fewer than two variables, imposes no restriction. + * A distance of one is propagated as a bounds-consistent distinct constraint. + * + * Throws Int::OutOfLimits if \a p is negative or exceeds Int::Limits::max. + * Throws Int::ArgumentSame if \a p is positive and \a x contains the same + * unassigned variable multiply. + */ + GECODE_INT_EXPORT void + inter_distance(Home home, const IntVarArgs& x, int p, + IntPropLevel ipl=IPL_DEF); + //@} + + /** * \defgroup TaskModelIntDistinct Distinct constraints * \ingroup TaskModelInt diff --git a/gecode/int/inter-distance.cpp b/gecode/int/inter-distance.cpp new file mode 100644 index 0000000000..da4a022a60 --- /dev/null +++ b/gecode/int/inter-distance.cpp @@ -0,0 +1,500 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ +/* + * Main authors: + * Claude-Guy Quimper + * + * Contributing authors: + * Mikael Zayenz Lagerkvist + * + * Copyright: + * Claude-Guy Quimper, 2006 + * 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 +#include + +#include +#include + +namespace Gecode { namespace Int { namespace InterDistance { + + namespace { + typedef long long int Time; + typedef std::ptrdiff_t RegionIndex; + + /// Release time, deadline, and position in release-time order + struct Task { + Time min, max; + int minRank, maxRank; + }; + + /// Forbidden starting times (inclusive) + struct Forbidden { + Time min, max; + }; + + /// Adjustment interval in a doubly linked list sorted by minimum + struct Adjustment { + Time min, max; + RegionIndex previous, next; + }; + + /** + * \brief Scratch data for one direction of bounds filtering + * + * The Garey-Johnson-Simons-Tarjan feasibility pass constructs forbidden + * starting regions. An earliest-completion table and a latest-start + * vector allow the dominance sweep to generate only quadratically many + * adjustment intervals. The linked list and union-find propagate changes + * to bounds without rescanning all variables for each interval. + */ + class Filter { + public: + const int n; + const Time p; + Task* tasks; + Task** minSorted; + Task** maxSorted; + Forbidden* forbidden; + int forbiddenCount; + Time* ectTable; + Time* lstTable; + Adjustment* regions; + RegionIndex regionCount; + RegionIndex* firstRegion; + RegionIndex* nextRegion; + Time* bounds; + int* links; + bool* processed; + int* leader; + /// Allocate scratch storage, with no persistent ownership + Filter(Region& r, int n0, int p0) + : n(n0), p(p0), forbiddenCount(0), regionCount(0) { + const size_t count = static_cast(n); + const size_t limit = std::numeric_limits::max(); + // Region::alloc multiplies unchecked. Reject impossible byte sizes + // before allocating the quadratic tables or interval list. + if ((count > limit / sizeof(Time) / (count+1)) || + (count > (limit / sizeof(Adjustment) - 3) / (count+3))) + throw MemoryExhausted(); + tasks = r.alloc(n); + minSorted = r.alloc(n); + maxSorted = r.alloc(n); + forbidden = r.alloc(n); + const size_t entries = count * (count+1); + ectTable = static_cast(r.ralloc(entries * sizeof(Time))); + lstTable = r.alloc