1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker 3e72b0e857 Squashed 'software/gecode_on_record/' content from commit 37ed9bda4
git-subtree-dir: software/gecode_on_record
git-subtree-split: 37ed9bda495ea87e63217c19a374b5a93bb0078e
2021-06-16 14:03:52 +10:00

660 lines
19 KiB
C++
Executable File

/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Main authors:
* Patrick Pekczynski <pekczynski@ps.uni-sb.de>
*
* Copyright:
* Patrick Pekczynski, 2004
*
* This file is part of Gecode, the generic constraint
* development environment:
* http://www.gecode.org
*
* 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 <gecode/int/rel.hh>
#include <gecode/int/distinct.hh>
namespace Gecode { namespace Int { namespace Sorted {
/*
* Summary of the propagation algorithm as implemented in the
* propagate method below:
*
* STEP 1: Normalize the domains of the y variables
* STEP 2: Sort the domains of the x variables according to their lower
* and upper endpoints
* STEP 3: Compute the matchings phi and phiprime with
* Glover's matching algorithm
* STEP 4: Compute the strongly connected components in
* the oriented intersection graph
* STEP 5: Narrow the domains of the variables
*
*/
/**
* \brief Perform bounds consistent sortedness propagation
*
* Implements the propagation algorithm for Sorted::Sorted
* and is provided as seperate function, because a second pass of
* the propagation algorithm is needed in order to achieve idempotency
* in case explicit permutation variables are provided.
*
* If \a Perm is true, permutation variables form the
* third argument which implies additional inferences,
* consistency check on the permutation variables and eventually a
* second pass of the propagation algorithm.
* Otherwise, the algorithm does not take care of the permutation
* variables resulting in a better performance.
*/
template<class View, bool Perm>
ExecStatus
bounds_propagation(Space& home, Propagator& p,
ViewArray<View>& x,
ViewArray<View>& y,
ViewArray<View>& z,
bool& repairpass,
bool& nofix,
bool& match_fixed){
int n = x.size();
Region r;
int* tau = r.alloc<int>(n);
int* phi = r.alloc<int>(n);
int* phiprime = r.alloc<int>(n);
OfflineMinItem* sequence = r.alloc<OfflineMinItem>(n);
int* vertices = r.alloc<int>(n);
if (match_fixed) {
// sorting is determined, sigma and tau coincide
for (int i=0; i<n; i++)
tau[z[i].val()] = i;
} else {
for (int i=0; i<n; i++)
tau[i] = i;
}
if (Perm) {
// normalized and sorted
// collect all bounds
Rank* allbnd = r.alloc<Rank>(x.size());
#ifndef NDEBUG
for (int i=n; i--;)
allbnd[i].min = allbnd[i].max = -1;
#endif
for (int i=n; i--;) {
int min = x[i].min();
int max = x[i].max();
for (int j=0; j<n; j++) {
if ( (y[j].min() > min) ||
(y[j].min() <= min && min <= y[j].max()) ) {
allbnd[i].min = j;
break;
}
}
for (int j=n; j--;) {
if (y[j].min() > max) {
allbnd[i].max = j-1;
break;
} else if (y[j].min() <= max && min <= y[j].max()) {
allbnd[i].max = j;
break;
}
}
}
for (int i=0; i<n; i++) {
// minimum reachable y-variable
int minr = allbnd[i].min;
assert(minr != -1);
int maxr = allbnd[i].max;
assert(maxr != -1);
ModEvent me = x[i].gq(home, y[minr].min());
if (me_failed(me))
return ES_FAILED;
nofix |= (me_modified(me) && (x[i].min() != y[minr].min()));
me = x[i].lq(home, y[maxr].max());
if (me_failed(me))
return ES_FAILED;
nofix |= (me_modified(me) && (x[i].min() != y[maxr].max()));
me = z[i].gq(home, minr);
if (me_failed(me))
return ES_FAILED;
nofix |= (me_modified(me) && (z[i].min() != minr));
me = z[i].lq(home, maxr);
if (me_failed(me))
return ES_FAILED;
nofix |= (me_modified(me) && (z[i].max() != maxr));
}
// channel information from x to y through permutation variables in z
if (!channel(home,x,y,z,nofix))
return ES_FAILED;
if (nofix)
return ES_NOFIX;
}
/*
* STEP 1:
* normalization is implemented in "order.hpp"
* o setting the lower bounds of the y_i domains (\lb E_i)
* to max(\lb E_{i-1},\lb E_i)
* o setting the upper bounds of the y_i domains (\ub E_i)
* to min(\ub E_i,\ub E_{i+1})
*/
if (!normalize(home, y, x, nofix))
return ES_FAILED;
if (Perm) {
// check consistency of channeling after normalization
if (!channel(home,x,y,z,nofix))
return ES_FAILED;
if (nofix)
return ES_NOFIX;
}
// if bounds have changed we have to recreate sigma to restore
// optimized dropping of variables
sort_sigma<View,Perm>(x,z);
bool subsumed = true;
bool array_subs = false;
int dropfst = 0;
bool noperm_bc = false;
if (!check_subsumption<View,Perm>(x,y,z,subsumed,dropfst) ||
!array_assigned<View,Perm>(home,x,y,z,array_subs,match_fixed,nofix,noperm_bc))
return ES_FAILED;
if (subsumed || array_subs)
return home.ES_SUBSUMED(p);
/*
* STEP 2: creating tau
* Sort the domains of the x variables according
* to their lower bounds, where we use an
* intermediate array of integers for sorting
*/
sort_tau<View,Perm>(x,z,tau);
/*
* STEP 3:
* Compute the matchings \phi and \phi' between
* the x and the y variables
* with Glover's matching algorithm.
* o phi is computed with the glover function
* o phiprime is computed with the revglover function
* glover and revglover are implemented in "matching.hpp"
*/
if (!match_fixed) {
if (!glover(x,y,tau,phi,sequence,vertices))
return ES_FAILED;
} else {
for (int i=0; i<x.size(); i++) {
phi[i] = z[i].val();
phiprime[i] = phi[i];
}
}
for (int i = n; i--; )
if (!y[i].assigned()) {
// phiprime is not needed to narrow the domains of the x-variables
if (!match_fixed &&
!revglover(x,y,tau,phiprime,sequence,vertices))
return ES_FAILED;
if (!narrow_domy(home,x,y,phi,phiprime,nofix))
return ES_FAILED;
if (nofix && !match_fixed) {
// data structures (matching) destroyed by domains with holes
for (int j = y.size(); j--; )
phi[j]=phiprime[j]=0;
if (!glover(x,y,tau,phi,sequence,vertices))
return ES_FAILED;
if (!revglover(x,y,tau,phiprime,sequence,vertices))
return ES_FAILED;
if (!narrow_domy(home,x,y,phi,phiprime,nofix))
return ES_FAILED;
}
break;
}
if (Perm) {
// check consistency of channeling after normalization
if (!channel(home,x,y,z,nofix))
return ES_FAILED;
if (nofix)
return ES_NOFIX;
}
/*
* STEP 4:
* Compute the strongly connected components in
* the oriented intersection graph
* the computation of the sccs is implemented in
* "narrowing.hpp" in the function narrow_domx
*/
int* scclist = r.alloc<int>(n);
SccComponent* sinfo = r.alloc<SccComponent>(n);
for(int i = n; i--; )
sinfo[i].left=sinfo[i].right=sinfo[i].rightmost=sinfo[i].leftmost= i;
computesccs(x,y,phi,sinfo,scclist);
/*
* STEP 5:
* Narrow the domains of the variables
* Also implemented in "narrowing.hpp"
* in the functions narrow_domx and narrow_domy
*/
if (!narrow_domx<View,Perm>(home,x,y,z,tau,phi,scclist,sinfo,nofix))
return ES_FAILED;
if (Perm) {
if (!noperm_bc &&
!perm_bc<View>
(home, tau, sinfo, scclist, x,z, repairpass, nofix))
return ES_FAILED;
// channeling also needed after normal propagation steps
// in order to ensure consistency after possible modification in perm_bc
if (!channel(home,x,y,z,nofix))
return ES_FAILED;
if (nofix)
return ES_NOFIX;
}
sort_tau<View,Perm>(x,z,tau);
if (Perm) {
// special case of sccs of size 2 denoted by permutation variables
// used to enforce consistency from x to y
// case of the upper bound ordering tau
for (int i = x.size() - 1; i--; ) {
// two x variables are in the same scc of size 2
if (z[tau[i]].min() == z[tau[i+1]].min() &&
z[tau[i]].max() == z[tau[i+1]].max() &&
z[tau[i]].size() == 2 && z[tau[i]].range()) {
// if bounds are strictly smaller
if (x[tau[i]].max() < x[tau[i+1]].max()) {
ModEvent me = y[z[tau[i]].min()].lq(home, x[tau[i]].max());
if (me_failed(me))
return ES_FAILED;
nofix |= (me_modified(me) &&
y[z[tau[i]].min()].max() != x[tau[i]].max());
me = y[z[tau[i+1]].max()].lq(home, x[tau[i+1]].max());
if (me_failed(me))
return ES_FAILED;
nofix |= (me_modified(me) &&
y[z[tau[i+1]].max()].max() != x[tau[i+1]].max());
}
}
}
}
return nofix ? ES_NOFIX : ES_FIX;
}
template<class View, bool Perm>
forceinline Sorted<View,Perm>::
Sorted(Space& home, Sorted<View,Perm>& p):
Propagator(home, p),
reachable(p.reachable) {
x.update(home, p.x);
y.update(home, p.y);
z.update(home, p.z);
w.update(home, p.w);
}
template<class View, bool Perm>
Sorted<View,Perm>::
Sorted(Home home,
ViewArray<View>& x0, ViewArray<View>& y0, ViewArray<View>& z0) :
Propagator(home), x(x0), y(y0), z(z0), w(home,y0), reachable(-1) {
x.subscribe(home, *this, PC_INT_BND);
y.subscribe(home, *this, PC_INT_BND);
if (Perm)
z.subscribe(home, *this, PC_INT_BND);
}
template<class View, bool Perm>
forceinline size_t
Sorted<View,Perm>::dispose(Space& home) {
x.cancel(home,*this, PC_INT_BND);
y.cancel(home,*this, PC_INT_BND);
if (Perm)
z.cancel(home,*this, PC_INT_BND);
(void) Propagator::dispose(home);
return sizeof(*this);
}
template<class View, bool Perm>
Actor* Sorted<View,Perm>::copy(Space& home) {
return new (home) Sorted<View,Perm>(home, *this);
}
template<class View, bool Perm>
PropCost Sorted<View,Perm>::cost(const Space&, const ModEventDelta&) const {
return PropCost::linear(PropCost::LO, x.size());
}
template<class View, bool Perm>
void
Sorted<View,Perm>::reschedule(Space& home) {
x.reschedule(home, *this, PC_INT_BND);
y.reschedule(home, *this, PC_INT_BND);
if (Perm)
z.reschedule(home, *this, PC_INT_BND);
}
template<class View, bool Perm>
ExecStatus
Sorted<View,Perm>::propagate(Space& home, const ModEventDelta&) {
int n = x.size();
bool secondpass = false;
bool nofix = false;
int dropfst = 0;
bool subsumed = false;
bool array_subs = false;
bool match_fixed = false;
// normalization of x and y
if (!normalize(home, y, x, nofix))
return ES_FAILED;
// create sigma sorting
sort_sigma<View,Perm>(x,z);
bool noperm_bc = false;
if (!array_assigned<View,Perm>
(home, x, y, z, array_subs, match_fixed, nofix, noperm_bc))
return ES_FAILED;
if (array_subs)
return home.ES_SUBSUMED(*this);
sort_sigma<View,Perm>(x,z);
// in this case check_subsumptions is guaranteed to find
// the xs ordered by sigma
if (!check_subsumption<View,Perm>(x,y,z,subsumed,dropfst))
return ES_FAILED;
if (subsumed)
return home.ES_SUBSUMED(*this);
if (Perm) {
// dropping possibly yields inconsistent indices on permutation variables
if (dropfst) {
reachable = w[dropfst - 1].max();
bool unreachable = true;
for (int i = x.size(); unreachable && i-- ; ) {
unreachable &= (reachable < x[i].min());
}
if (unreachable) {
x.drop_fst(dropfst, home, *this, PC_INT_BND);
y.drop_fst(dropfst, home, *this, PC_INT_BND);
z.drop_fst(dropfst, home, *this, PC_INT_BND);
} else {
dropfst = 0;
}
}
n = x.size();
if (n < 2) {
if (x[0].max() < y[0].min() || y[0].max() < x[0].min())
return ES_FAILED;
if (Perm) {
GECODE_ME_CHECK(z[0].eq(home, w.size() - 1));
}
GECODE_REWRITE(*this,(Rel::EqBnd<View,View>::post(home(*this), x[0], y[0])));
}
// check whether shifting the permutation variables
// is necessary after dropping x and y vars
// highest reachable index
int valid = n - 1;
int index = 0;
int shift = 0;
for (int i = n; i--; ){
if (z[i].max() > index)
index = z[i].max();
if (index > valid)
shift = index - valid;
}
if (shift) {
ViewArray<OffsetView> ox(home,n), oy(home,n), oz(home,n);
for (int i = n; i--; ) {
GECODE_ME_CHECK(z[i].gq(home, shift));
oz[i] = OffsetView(z[i], -shift);
ox[i] = OffsetView(x[i], 0);
oy[i] = OffsetView(y[i], 0);
}
GECODE_ES_CHECK((bounds_propagation<OffsetView,Perm>
(home,*this,ox,oy,oz,secondpass,nofix,match_fixed)));
if (secondpass) {
GECODE_ES_CHECK((bounds_propagation<OffsetView,Perm>
(home,*this,ox,oy,oz,secondpass,nofix,match_fixed)));
}
} else {
GECODE_ES_CHECK((bounds_propagation<View,Perm>
(home,*this,x,y,z,secondpass,nofix,match_fixed)));
if (secondpass) {
GECODE_ES_CHECK((bounds_propagation<View,Perm>
(home,*this,x,y,z,secondpass,nofix,match_fixed)));
}
}
} else {
// dropping has no consequences
if (dropfst) {
x.drop_fst(dropfst, home, *this, PC_INT_BND);
y.drop_fst(dropfst, home, *this, PC_INT_BND);
}
n = x.size();
if (n < 2) {
if (x[0].max() < y[0].min() || y[0].max() < x[0].min())
return ES_FAILED;
GECODE_REWRITE(*this,(Rel::EqBnd<View,View>::post(home(*this), x[0], y[0])));
}
GECODE_ES_CHECK((bounds_propagation<View,Perm>
(home, *this, x, y, z,secondpass, nofix, match_fixed)));
// no second pass possible if there are no permvars
}
if (!normalize(home, y, x, nofix))
return ES_FAILED;
Region r;
int* tau = r.alloc<int>(n);
if (match_fixed) {
// sorting is determined
// sigma and tau coincide
for (int i = x.size(); i--; ) {
int pi = z[i].val();
tau[pi] = i;
}
} else {
for (int i = n; i--; ) {
tau[i] = i;
}
}
sort_tau<View,Perm>(x,z,tau);
// recreate consistency for already assigned subparts
// in order of the upper bounds starting at the end of the array
bool xbassigned = true;
for (int i = x.size(); i--; ) {
if (x[tau[i]].assigned() && xbassigned) {
GECODE_ME_CHECK(y[i].eq(home, x[tau[i]].val()));
} else {
xbassigned = false;
}
}
subsumed = true;
array_subs = false;
noperm_bc = false;
// creating sorting anew
sort_sigma<View,Perm>(x,z);
if (Perm) {
for (int i = 0; i < x.size() - 1; i++) {
// special case of subsccs of size2 for the lower bounds
// two x variables are in the same scc of size 2
if (z[i].min() == z[i+1].min() &&
z[i].max() == z[i+1].max() &&
z[i].size() == 2 && z[i].range()) {
if (x[i].min() < x[i+1].min()) {
ModEvent me = y[z[i].min()].gq(home, x[i].min());
GECODE_ME_CHECK(me);
nofix |= (me_modified(me) &&
y[z[i].min()].min() != x[i].min());
me = y[z[i+1].max()].gq(home, x[i+1].min());
GECODE_ME_CHECK(me);
nofix |= (me_modified(me) &&
y[z[i+1].max()].min() != x[i+1].min());
}
}
}
}
// check assigned
// should be sorted
bool xassigned = true;
for (int i = 0; i < x.size(); i++) {
if (x[i].assigned() && xassigned) {
GECODE_ME_CHECK(y[i].eq(home,x[i].val()));
} else {
xassigned = false;
}
}
// sorted check bounds
// final check that variables are consitent with least and greatest possible
// values
int tlb = std::min(x[0].min(), y[0].min());
int tub = std::max(x[x.size() - 1].max(), y[y.size() - 1].max());
for (int i = x.size(); i--; ) {
ModEvent me = y[i].lq(home, tub);
GECODE_ME_CHECK(me);
nofix |= me_modified(me) && (y[i].max() != tub);
me = y[i].gq(home, tlb);
GECODE_ME_CHECK(me);
nofix |= me_modified(me) && (y[i].min() != tlb);
me = x[i].lq(home, tub);
GECODE_ME_CHECK(me);
nofix |= me_modified(me) && (x[i].max() != tub);
me = x[i].gq(home, tlb);
GECODE_ME_CHECK(me);
nofix |= me_modified(me) && (x[i].min() != tlb);
}
if (!array_assigned<View,Perm>
(home, x, y, z, array_subs, match_fixed, nofix, noperm_bc))
return ES_FAILED;
if (array_subs)
return home.ES_SUBSUMED(*this);
if (!check_subsumption<View,Perm>(x,y,z,subsumed,dropfst))
return ES_FAILED;
if (subsumed)
return home.ES_SUBSUMED(*this);
return nofix ? ES_NOFIX : ES_FIX;
}
template<class View, bool Perm>
ExecStatus
Sorted<View,Perm>::
post(Home home,
ViewArray<View>& x0, ViewArray<View>& y0, ViewArray<View>& z0) {
int n = x0.size();
if (n < 2) {
if ((x0[0].max() < y0[0].min()) || (y0[0].max() < x0[0].min()))
return ES_FAILED;
GECODE_ES_CHECK((Rel::EqBnd<View,View>::post(home,x0[0],y0[0])));
if (Perm) {
GECODE_ME_CHECK(z0[0].eq(home,0));
}
} else {
if (Perm) {
ViewArray<View> z(home,n);
for (int i=n; i--; ) {
z[i]=z0[i];
GECODE_ME_CHECK(z[i].gq(home,0));
GECODE_ME_CHECK(z[i].lq(home,n-1));
}
GECODE_ES_CHECK(Distinct::Bnd<View>::post(home,z));
}
new (home) Sorted<View,Perm>(home,x0,y0,z0);
}
return ES_OK;
}
}}}
// STATISTICS: int-prop