git-subtree-dir: software/gecode_on_replay git-subtree-split: 8051d92b9c89e49cccfbd1c201371580d7703ab4
325 lines
9.6 KiB
C++
325 lines
9.6 KiB
C++
/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
|
/*
|
|
* Main authors:
|
|
* Christian Schulte <schulte@gecode.org>
|
|
*
|
|
* Copyright:
|
|
* Christian Schulte, 2011
|
|
*
|
|
* 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/distinct.hh>
|
|
|
|
namespace Gecode { namespace Int { namespace NValues {
|
|
|
|
template<class VY>
|
|
forceinline
|
|
IntBase<VY>::IntBase(Home home, ValSet& vs0, ViewArray<IntView>& x, VY y)
|
|
: MixNaryOnePropagator<IntView,PC_INT_DOM,VY,PC_INT_BND>(home,x,y),
|
|
vs(vs0) {}
|
|
|
|
template<class VY>
|
|
forceinline
|
|
IntBase<VY>::IntBase(Space& home, IntBase<VY>& p)
|
|
: MixNaryOnePropagator<IntView,PC_INT_DOM,VY,PC_INT_BND>(home, p) {
|
|
vs.update(home, p.vs);
|
|
}
|
|
|
|
template<class VY>
|
|
forceinline size_t
|
|
IntBase<VY>::dispose(Space& home) {
|
|
vs.dispose(home);
|
|
(void) MixNaryOnePropagator<IntView,PC_INT_DOM,VY,PC_INT_BND>
|
|
::dispose(home);
|
|
return sizeof(*this);
|
|
}
|
|
|
|
template<class VY>
|
|
PropCost
|
|
IntBase<VY>::cost(const Space&, const ModEventDelta&) const {
|
|
return PropCost::quadratic(PropCost::HI, x.size());
|
|
}
|
|
|
|
template<class VY>
|
|
void
|
|
IntBase<VY>::add(Space& home) {
|
|
int n=x.size();
|
|
for (int i=n; i--; )
|
|
if (x[i].assigned()) {
|
|
vs.add(home, x[i].val());
|
|
x[i] = x[--n];
|
|
}
|
|
x.size(n);
|
|
}
|
|
|
|
template<class VY>
|
|
void
|
|
IntBase<VY>::disjoint(Space& home, Region& r, int*& dis, int& n_dis) {
|
|
// Compute positions of disjoint views
|
|
int n=x.size();
|
|
dis = r.alloc<int>(n); n_dis = 0;
|
|
|
|
int i=0;
|
|
while (i < n)
|
|
switch (vs.compare(x[i])) {
|
|
case Iter::Ranges::CS_SUBSET:
|
|
// All values are already contained in vs, eliminate x[i]
|
|
x[i].cancel(home, *this, PC_INT_DOM);
|
|
x[i] = x[--n];
|
|
break;
|
|
case Iter::Ranges::CS_DISJOINT:
|
|
dis[n_dis++] = i++;
|
|
break;
|
|
case Iter::Ranges::CS_NONE:
|
|
i++;
|
|
break;
|
|
default:
|
|
GECODE_NEVER;
|
|
}
|
|
x.size(n);
|
|
}
|
|
|
|
template<class VY>
|
|
void
|
|
IntBase<VY>::eliminate(Space& home) {
|
|
int n=x.size();
|
|
for (int i=n; i--; )
|
|
if (vs.subset(x[i])) {
|
|
// All values are already contained in vs, eliminate x[i]
|
|
x[i].cancel(home, *this, PC_INT_DOM);
|
|
x[i] = x[--n];
|
|
}
|
|
x.size(n);
|
|
}
|
|
|
|
template<class VY>
|
|
ExecStatus
|
|
IntBase<VY>::all_in_valset(Space& home) {
|
|
for (int i=x.size(); i--; ) {
|
|
ValSet::Ranges vsr(vs);
|
|
GECODE_ME_CHECK(x[i].inter_r(home, vsr, false));
|
|
}
|
|
return home.ES_SUBSUMED(*this);
|
|
}
|
|
|
|
template<class VY>
|
|
ExecStatus
|
|
IntBase<VY>::prune_lower(Space& home, int* dis, int n_dis) {
|
|
assert(n_dis > 0);
|
|
|
|
// At least one more value will be needed
|
|
GECODE_ME_CHECK(y.gq(home,vs.size() + 1));
|
|
|
|
Region r;
|
|
|
|
// Only one additional value is allowed
|
|
if (y.max() == vs.size() + 1) {
|
|
// Compute possible values
|
|
ViewRanges<IntView>* r_dis = r.alloc<ViewRanges<IntView> >(n_dis);
|
|
for (int i=n_dis; i--; )
|
|
r_dis[i] = ViewRanges<IntView>(x[dis[i]]);
|
|
Iter::Ranges::NaryInter iv(r, r_dis, n_dis);
|
|
// Is there a common value at all?
|
|
if (!iv())
|
|
return ES_FAILED;
|
|
ValSet::Ranges vsr(vs);
|
|
Iter::Ranges::NaryUnion pv(r,iv,vsr);
|
|
// Enforce common values
|
|
for (int i=x.size(); i--; ) {
|
|
pv.reset();
|
|
GECODE_ME_CHECK(x[i].inter_r(home, pv, false));
|
|
}
|
|
return ES_OK;
|
|
}
|
|
|
|
// Compute independent set for lower bound
|
|
// ovl is a bit-matrix defining whether two views overlap
|
|
SymBitMatrix ovl(r,x.size());
|
|
// deg[i] is the degree of x[i]
|
|
int* deg = r.alloc<int>(x.size());
|
|
// ovl_i[i] is an array of indices j such that x[j] overlaps with x[i]
|
|
int** ovl_i = r.alloc<int*>(x.size());
|
|
// n_ovl_i[i] defines how many integers are stored for ovl_i[i]
|
|
int* n_ovl_i = r.alloc<int>(x.size());
|
|
{
|
|
#ifndef NDEBUG
|
|
// Initialize all to null pointers so that things crash ;-)
|
|
for (int i=x.size(); i--; )
|
|
ovl_i[i] = nullptr;
|
|
#endif
|
|
// For each i there can be at most n_dis-1 entries in ovl_i[i]
|
|
int* m = r.alloc<int>(n_dis*(n_dis-1));
|
|
for (int i=n_dis; i--; ) {
|
|
deg[dis[i]] = 0;
|
|
ovl_i[dis[i]] = m; m += n_dis-1;
|
|
}
|
|
}
|
|
|
|
// Initialize overlap matrix by analyzing the view ranges
|
|
{
|
|
// Compute how many events are needed
|
|
// One event for the end marker
|
|
int n_re = 1;
|
|
// Two events for each range
|
|
for (int i=n_dis; i--; )
|
|
for (ViewRanges<IntView> rx(x[dis[i]]); rx(); ++rx)
|
|
n_re += 2;
|
|
|
|
// Allocate and initialize events
|
|
RangeEvent* re = r.alloc<RangeEvent>(n_re);
|
|
{
|
|
int j=0;
|
|
for (int i=n_dis; i--; )
|
|
for (ViewRanges<IntView> rx(x[dis[i]]); rx(); ++rx) {
|
|
// Event when a range starts
|
|
re[j].ret=RET_FST; re[j].val=rx.min(); re[j].view=dis[i]; j++;
|
|
// Event when a range ends
|
|
re[j].ret=RET_LST; re[j].val=rx.max(); re[j].view=dis[i]; j++;
|
|
}
|
|
// Make this the last event
|
|
re[j].ret=RET_END; re[j].val=Int::Limits::infinity;
|
|
assert(j+1 == n_re);
|
|
}
|
|
// Sort and process events
|
|
Support::quicksort(re,n_re);
|
|
|
|
// Current views with a range being active
|
|
Support::BitSet<Region> cur(r,static_cast<unsigned int>(x.size()));
|
|
// Process all events
|
|
for (int i=0; true; i++)
|
|
switch (re[i].ret) {
|
|
case RET_FST:
|
|
// Process all overlapping views
|
|
for (Iter::Values::BitSet<Support::BitSet<Region> > j(cur);
|
|
j(); ++j) {
|
|
int di = re[i].view, dj = j.val();
|
|
if (!ovl.get(di,dj)) {
|
|
ovl.set(di,dj);
|
|
ovl_i[di][deg[di]++] = dj;
|
|
ovl_i[dj][deg[dj]++] = di;
|
|
}
|
|
}
|
|
cur.set(static_cast<unsigned int>(re[i].view));
|
|
break;
|
|
case RET_LST:
|
|
cur.clear(static_cast<unsigned int>(re[i].view));
|
|
break;
|
|
case RET_END:
|
|
goto done;
|
|
default:
|
|
GECODE_NEVER;
|
|
}
|
|
done:
|
|
r.free<RangeEvent>(re,n_re);
|
|
}
|
|
|
|
// While deg changes, n_ovl_i remains unchanged and is needed, so copy it
|
|
for (int i=n_dis; i--; ) {
|
|
assert(deg[dis[i]] < n_dis);
|
|
n_ovl_i[dis[i]] = deg[dis[i]];
|
|
}
|
|
|
|
// Views in the independent set
|
|
int* ind = r.alloc<int>(n_dis);
|
|
int n_ind = 0;
|
|
|
|
while (n_dis > 0) {
|
|
int i_min = n_dis-1;
|
|
int d_min = deg[dis[i_min]];
|
|
unsigned int s_min = x[dis[i_min]].size();
|
|
|
|
// Find view with smallest (degree,size)
|
|
for (int i=n_dis-1; i--; )
|
|
if ((d_min > deg[dis[i]]) ||
|
|
((d_min == deg[dis[i]]) && (s_min > x[dis[i]].size()))) {
|
|
i_min = i;
|
|
d_min = deg[dis[i]];
|
|
s_min = x[dis[i]].size();
|
|
}
|
|
|
|
// i_min refers to view with smallest (degree,size)
|
|
ind[n_ind++] = dis[i_min]; dis[i_min] = dis[--n_dis];
|
|
|
|
// Filter out non disjoint views
|
|
for (int i=n_dis; i--; )
|
|
if (ovl.get(dis[i],ind[n_ind-1])) {
|
|
// Update degree information
|
|
for (int j=n_ovl_i[dis[i]]; j--; )
|
|
deg[ovl_i[dis[i]][j]]--;
|
|
// Eliminate view
|
|
dis[i] = dis[--n_dis];
|
|
}
|
|
}
|
|
// Enforce lower bound
|
|
GECODE_ME_CHECK(y.gq(home,vs.size() + n_ind));
|
|
|
|
// Prune, if possible
|
|
if (vs.size() + n_ind == y.max()) {
|
|
// Only values from the indepent set a can be taken
|
|
ViewRanges<IntView>* r_ind = r.alloc<ViewRanges<IntView> >(n_ind);
|
|
for (int i=n_ind; i--; )
|
|
r_ind[i] = ViewRanges<IntView>(x[ind[i]]);
|
|
Iter::Ranges::NaryUnion v_ind(r, r_ind, n_ind);
|
|
ValSet::Ranges vsr(vs);
|
|
v_ind |= vsr;
|
|
for (int i=x.size(); i--; ) {
|
|
v_ind.reset();
|
|
GECODE_ME_CHECK(x[i].inter_r(home,v_ind,false));
|
|
}
|
|
}
|
|
return ES_OK;
|
|
}
|
|
|
|
template<class VY>
|
|
forceinline ExecStatus
|
|
IntBase<VY>::prune_upper(Space& home, Graph& g) {
|
|
if (!g) {
|
|
g.init(home,vs,x);
|
|
} else {
|
|
g.purge();
|
|
g.sync();
|
|
}
|
|
GECODE_ME_CHECK(y.lq(home, g.size()));
|
|
if (y.min() == g.size()) {
|
|
// All values must be taken on
|
|
if (vs.size() + x.size() == g.size()) {
|
|
// This is in fact a distinct, simplify and rewrite
|
|
for (int i=x.size(); i--; ) {
|
|
ValSet::Ranges vsr(vs);
|
|
GECODE_ME_CHECK(x[i].minus_r(home, vsr, false));
|
|
}
|
|
GECODE_REWRITE(*this,Distinct::Dom<IntView>::post(home(*this),x));
|
|
}
|
|
if (g.mark())
|
|
GECODE_ES_CHECK(g.prune(home));
|
|
}
|
|
return ES_OK;
|
|
}
|
|
|
|
}}}
|
|
|
|
// STATISTICS: int-prop
|