git-subtree-dir: software/gecode_on_record git-subtree-split: 37ed9bda495ea87e63217c19a374b5a93bb0078e
1061 lines
29 KiB
C++
1061 lines
29 KiB
C++
/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
|
/*
|
|
* Main authors:
|
|
* Christian Schulte <schulte@gecode.org>
|
|
*
|
|
* Contributing authors:
|
|
* Samuel Gagnon <samuel.gagnon92@gmail.com>
|
|
*
|
|
* Copyright:
|
|
* Christian Schulte, 2002
|
|
* Samuel Gagnon, 2018
|
|
*
|
|
* 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/kernel.hh>
|
|
|
|
namespace Gecode {
|
|
|
|
/*
|
|
* Variable type disposer
|
|
*
|
|
*/
|
|
void
|
|
VarImpDisposerBase::dispose(Space&,VarImpBase*) {}
|
|
|
|
VarImpDisposerBase::~VarImpDisposerBase(void) {}
|
|
|
|
|
|
|
|
/*
|
|
* Actor
|
|
*
|
|
*/
|
|
Actor* Actor::sentinel;
|
|
|
|
Actor::~Actor(void) {}
|
|
|
|
|
|
/*
|
|
* Propagator
|
|
*
|
|
*/
|
|
ExecStatus
|
|
Propagator::advise(Space&, Advisor&, const Delta&) {
|
|
GECODE_NEVER;
|
|
return ES_FAILED;
|
|
}
|
|
void
|
|
Propagator::advise(Space&, Advisor&) {
|
|
GECODE_NEVER;
|
|
}
|
|
|
|
|
|
/*
|
|
* No-goods
|
|
*
|
|
*/
|
|
void
|
|
NoGoods::post(Space&) const {
|
|
}
|
|
|
|
NoGoods NoGoods::eng;
|
|
|
|
/*
|
|
* Brancher
|
|
*
|
|
*/
|
|
NGL*
|
|
Brancher::ngl(Space&, const Choice&, unsigned int) const {
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
Brancher::print(const Space&, const Choice&, unsigned int,
|
|
std::ostream&) const {
|
|
}
|
|
|
|
|
|
/*
|
|
* Space: Misc
|
|
*
|
|
*/
|
|
|
|
StatusStatistics Space::unused_status;
|
|
CloneStatistics Space::unused_clone;
|
|
CommitStatistics Space::unused_commit;
|
|
|
|
#ifdef GECODE_HAS_VAR_DISPOSE
|
|
VarImpDisposerBase* Space::vd[AllVarConf::idx_d];
|
|
#endif
|
|
|
|
Space::Space(void) : mm(ssd.data().sm) {
|
|
#ifdef GECODE_HAS_CBS
|
|
var_id_counter = 0;
|
|
#endif
|
|
#ifdef GECODE_HAS_VAR_DISPOSE
|
|
for (int i=0; i<AllVarConf::idx_d; i++)
|
|
_vars_d[i] = nullptr;
|
|
#endif
|
|
// Initialize propagator and brancher links
|
|
pl.init();
|
|
bl.init();
|
|
b_status = b_commit = Brancher::cast(&bl);
|
|
// Initialize array for forced deletion to be empty
|
|
d_fst = d_cur = d_lst = nullptr;
|
|
// Initialize space as stable but not failed
|
|
pc.p.active = &pc.p.queue[0]-1;
|
|
// Initialize propagator queues
|
|
for (int i=0; i<=PropCost::AC_MAX; i++)
|
|
pc.p.queue[i].init();
|
|
pc.p.bid_sc = (reserved_bid+1) << sc_bits;
|
|
pc.p.n_sub = 0;
|
|
pc.p.vti.other();
|
|
}
|
|
|
|
void
|
|
Space::ap_notice_dispose(Actor* a, bool duplicate) {
|
|
// Note that a might be a marked pointer!
|
|
if (duplicate && (d_fst != nullptr)) {
|
|
for (Actor** f = d_fst; f < d_cur; f++)
|
|
if (a == *f)
|
|
return;
|
|
}
|
|
if (d_cur == d_lst) {
|
|
// Resize
|
|
if (d_fst == nullptr) {
|
|
// Create new array
|
|
d_fst = alloc<Actor*>(4);
|
|
d_cur = d_fst;
|
|
d_lst = d_fst+4;
|
|
} else {
|
|
// Resize existing array
|
|
unsigned int n = static_cast<unsigned int>(d_lst - d_fst);
|
|
assert(n != 0);
|
|
d_fst = realloc<Actor*>(d_fst,n,2*n);
|
|
d_cur = d_fst+n;
|
|
d_lst = d_fst+2*n;
|
|
}
|
|
}
|
|
*(d_cur++) = a;
|
|
}
|
|
|
|
void
|
|
Space::ap_ignore_dispose(Actor* a, bool duplicate) {
|
|
// Note that a might be a marked pointer!
|
|
assert(d_fst != nullptr);
|
|
Actor** f = d_fst;
|
|
if (duplicate) {
|
|
while (f < d_cur)
|
|
if (a == *f)
|
|
break;
|
|
else
|
|
f++;
|
|
if (f == d_cur)
|
|
return;
|
|
} else {
|
|
while (a != *f)
|
|
f++;
|
|
}
|
|
*f = *(--d_cur);
|
|
}
|
|
|
|
Space::~Space(void) {
|
|
// Mark space as failed
|
|
fail();
|
|
// Delete actors that must be deleted
|
|
{
|
|
Actor** a = d_fst;
|
|
Actor** e = d_cur;
|
|
// So that d_unforce knows that deletion is in progress
|
|
d_fst = nullptr;
|
|
while (a < e) {
|
|
// Ignore entries for tracers
|
|
if (!Support::marked(*a))
|
|
(void) (*a)->dispose(*this);
|
|
a++;
|
|
}
|
|
}
|
|
#ifdef GECODE_HAS_VAR_DISPOSE
|
|
// Delete variables that were registered for disposal
|
|
for (int i=0; i<AllVarConf::idx_d; i++)
|
|
if (_vars_d[i] != nullptr)
|
|
vd[i]->dispose(*this, _vars_d[i]);
|
|
#endif
|
|
// Release memory from memory manager
|
|
mm.release(ssd.data().sm);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Space: propagation
|
|
*
|
|
*/
|
|
|
|
TraceRecorder*
|
|
Space::findtracerecorder(void) {
|
|
for (Actor** a=d_fst; a<d_cur; a++) {
|
|
Propagator* p = Propagator::cast(*a);
|
|
if (!p->disabled())
|
|
if (TraceRecorder* tr = dynamic_cast<TraceRecorder*>(p)) {
|
|
std::swap(*d_fst,*a);
|
|
return tr;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
Space::post(const PostInfo& pi) {
|
|
assert(pc.p.bid_sc & sc_trace);
|
|
TraceRecorder* tr = findtracerecorder();
|
|
if ((tr != nullptr) && (tr->events() & TE_POST)) {
|
|
GECODE_ASSUME(ssd.data().gpi.pid() >= pi.pid);
|
|
unsigned int n = ssd.data().gpi.pid() - pi.pid;
|
|
PostTraceInfo::Status s;
|
|
if (failed())
|
|
s = PostTraceInfo::FAILED;
|
|
else if (n == 0)
|
|
s = PostTraceInfo::SUBSUMED;
|
|
else
|
|
s = PostTraceInfo::POSTED;
|
|
PostTraceInfo pti(pi.pg,s,n);
|
|
tr->tracer()._post(*this,pti);
|
|
}
|
|
}
|
|
|
|
SpaceStatus
|
|
Space::status(StatusStatistics& stat) {
|
|
// Check whether space is failed
|
|
if (failed())
|
|
return SS_FAILED;
|
|
assert(pc.p.active <= &pc.p.queue[PropCost::AC_MAX+1]);
|
|
Propagator* p;
|
|
// Check whether space is stable but not failed
|
|
if (pc.p.active >= &pc.p.queue[0]) {
|
|
ModEventDelta med_o;
|
|
if ((pc.p.bid_sc & ((1 << sc_bits) - 1)) == 0) {
|
|
// No support for disabled propagators and tracing
|
|
// Check whether space is stable but not failed
|
|
goto f_unstable;
|
|
f_execute:
|
|
stat.propagate++;
|
|
// Keep old modification event delta
|
|
med_o = p->u.med;
|
|
// Clear med but leave propagator in queue
|
|
p->u.med = 0;
|
|
switch (p->propagate(*this,med_o)) {
|
|
case ES_FAILED:
|
|
goto failed;
|
|
case ES_NOFIX:
|
|
// Find next, if possible
|
|
if (p->u.med != 0) {
|
|
f_unstable:
|
|
// There is at least one propagator in a queue
|
|
do {
|
|
assert(pc.p.active >= &pc.p.queue[0]);
|
|
// First propagator or link back to queue
|
|
ActorLink* fst = pc.p.active->next();
|
|
if (pc.p.active != fst) {
|
|
p = Propagator::cast(fst);
|
|
goto f_execute;
|
|
}
|
|
pc.p.active--;
|
|
} while (true);
|
|
GECODE_NEVER;
|
|
}
|
|
// Fall through
|
|
case ES_FIX:
|
|
// Clear med
|
|
p->u.med = 0;
|
|
// Put into idle queue
|
|
p->unlink(); pl.head(p);
|
|
f_stable_or_unstable:
|
|
// There might be a propagator in the queue
|
|
do {
|
|
assert(pc.p.active >= &pc.p.queue[0]);
|
|
// First propagator or link back to queue
|
|
ActorLink* fst = pc.p.active->next();
|
|
if (pc.p.active != fst) {
|
|
p = Propagator::cast(fst);
|
|
goto f_execute;
|
|
}
|
|
} while (--pc.p.active >= &pc.p.queue[0]);
|
|
assert(pc.p.active < &pc.p.queue[0]);
|
|
goto f_stable;
|
|
case ES_SUBSUMED_:
|
|
p->unlink(); rfree(p,p->u.size);
|
|
goto f_stable_or_unstable;
|
|
case ES_PARTIAL_:
|
|
// Schedule propagator with specified propagator events
|
|
assert(p->u.med != 0);
|
|
enqueue(p);
|
|
goto f_unstable;
|
|
default:
|
|
GECODE_NEVER;
|
|
}
|
|
f_stable: ;
|
|
} else if ((pc.p.bid_sc & ((1 << sc_bits) - 1)) == sc_disabled) {
|
|
// Support for disabled propagators
|
|
goto d_unstable;
|
|
d_execute:
|
|
stat.propagate++;
|
|
if (p->disabled())
|
|
goto d_put_into_idle;
|
|
// Keep old modification event delta
|
|
med_o = p->u.med;
|
|
// Clear med but leave propagator in queue
|
|
p->u.med = 0;
|
|
switch (p->propagate(*this,med_o)) {
|
|
case ES_FAILED:
|
|
goto failed;
|
|
case ES_NOFIX:
|
|
// Find next, if possible
|
|
if (p->u.med != 0) {
|
|
d_unstable:
|
|
// There is at least one propagator in a queue
|
|
do {
|
|
assert(pc.p.active >= &pc.p.queue[0]);
|
|
// First propagator or link back to queue
|
|
ActorLink* fst = pc.p.active->next();
|
|
if (pc.p.active != fst) {
|
|
p = Propagator::cast(fst);
|
|
goto d_execute;
|
|
}
|
|
pc.p.active--;
|
|
} while (true);
|
|
GECODE_NEVER;
|
|
}
|
|
// Fall through
|
|
case ES_FIX:
|
|
d_put_into_idle:
|
|
// Clear med
|
|
p->u.med = 0;
|
|
// Put into idle queue
|
|
p->unlink(); pl.head(p);
|
|
d_stable_or_unstable:
|
|
// There might be a propagator in the queue
|
|
do {
|
|
assert(pc.p.active >= &pc.p.queue[0]);
|
|
// First propagator or link back to queue
|
|
ActorLink* fst = pc.p.active->next();
|
|
if (pc.p.active != fst) {
|
|
p = Propagator::cast(fst);
|
|
goto d_execute;
|
|
}
|
|
} while (--pc.p.active >= &pc.p.queue[0]);
|
|
assert(pc.p.active < &pc.p.queue[0]);
|
|
goto d_stable;
|
|
case ES_SUBSUMED_:
|
|
p->unlink(); rfree(p,p->u.size);
|
|
goto d_stable_or_unstable;
|
|
case ES_PARTIAL_:
|
|
// Schedule propagator with specified propagator events
|
|
assert(p->u.med != 0);
|
|
enqueue(p);
|
|
goto d_unstable;
|
|
default:
|
|
GECODE_NEVER;
|
|
}
|
|
d_stable: ;
|
|
} else {
|
|
// Support disabled propagators and tracing
|
|
|
|
#define GECODE_STATUS_TRACE(q,s) \
|
|
if ((tr != nullptr) && (tr->events() & TE_PROPAGATE) && \
|
|
(tr->filter()(p->group()))) { \
|
|
PropagateTraceInfo pti(p->id(),p->group(),q, \
|
|
PropagateTraceInfo::s); \
|
|
tr->tracer()._propagate(*this,pti); \
|
|
}
|
|
|
|
// Find a non-disabled tracer recorder (possibly null)
|
|
TraceRecorder* tr = findtracerecorder();
|
|
// Remember post information
|
|
ViewTraceInfo vti(pc.p.vti);
|
|
goto t_unstable;
|
|
|
|
t_execute:
|
|
stat.propagate++;
|
|
if (p->disabled())
|
|
goto t_put_into_idle;
|
|
pc.p.vti.propagator(*p);
|
|
// Keep old modification event delta
|
|
med_o = p->u.med;
|
|
// Clear med but leave propagator in queue
|
|
p->u.med = 0;
|
|
switch (p->propagate(*this,med_o)) {
|
|
case ES_FAILED:
|
|
GECODE_STATUS_TRACE(p,FAILED);
|
|
goto failed;
|
|
case ES_NOFIX:
|
|
// Find next, if possible
|
|
if (p->u.med != 0) {
|
|
GECODE_STATUS_TRACE(p,NOFIX);
|
|
t_unstable:
|
|
// There is at least one propagator in a queue
|
|
do {
|
|
assert(pc.p.active >= &pc.p.queue[0]);
|
|
// First propagator or link back to queue
|
|
ActorLink* fst = pc.p.active->next();
|
|
if (pc.p.active != fst) {
|
|
p = Propagator::cast(fst);
|
|
goto t_execute;
|
|
}
|
|
pc.p.active--;
|
|
} while (true);
|
|
GECODE_NEVER;
|
|
}
|
|
// Fall through
|
|
case ES_FIX:
|
|
GECODE_STATUS_TRACE(p,FIX);
|
|
t_put_into_idle:
|
|
// Clear med
|
|
p->u.med = 0;
|
|
// Put into idle queue
|
|
p->unlink(); pl.head(p);
|
|
t_stable_or_unstable:
|
|
// There might be a propagator in the queue
|
|
do {
|
|
assert(pc.p.active >= &pc.p.queue[0]);
|
|
// First propagator or link back to queue
|
|
ActorLink* fst = pc.p.active->next();
|
|
if (pc.p.active != fst) {
|
|
p = Propagator::cast(fst);
|
|
goto t_execute;
|
|
}
|
|
} while (--pc.p.active >= &pc.p.queue[0]);
|
|
assert(pc.p.active < &pc.p.queue[0]);
|
|
goto t_stable;
|
|
case ES_SUBSUMED_:
|
|
GECODE_STATUS_TRACE(nullptr,SUBSUMED);
|
|
p->unlink(); rfree(p,p->u.size);
|
|
goto t_stable_or_unstable;
|
|
case ES_PARTIAL_:
|
|
GECODE_STATUS_TRACE(p,NOFIX);
|
|
// Schedule propagator with specified propagator events
|
|
assert(p->u.med != 0);
|
|
enqueue(p);
|
|
goto t_unstable;
|
|
default:
|
|
GECODE_NEVER;
|
|
}
|
|
t_stable:
|
|
// Restore post information
|
|
pc.p.vti = vti;
|
|
|
|
#undef GECODE_STATUS_TRACE
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find the next brancher that has still alternatives left
|
|
*
|
|
* It is important to note that branchers reporting to have no more
|
|
* alternatives left cannot be deleted. They cannot be deleted
|
|
* as there might be choices to be used in commit
|
|
* that refer to one of these branchers. This e.g. happens when
|
|
* we combine branch-and-bound search with adaptive recomputation: during
|
|
* recomputation, a copy is constrained to be better than the currently
|
|
* best solution, then the first half of the choices are posted,
|
|
* and a fixpoint computed (for storing in the middle of the path). Then
|
|
* the remaining choices are posted, and because of the additional
|
|
* constraints that the space must be better than the previous solution,
|
|
* the corresponding Branchers may already have no alternatives left.
|
|
*
|
|
* The same situation may arise due to weakly monotonic propagators.
|
|
*
|
|
* A brancher reporting that no more alternatives exist is exhausted.
|
|
* All exhausted branchers will be left of the current pointer b_status.
|
|
* Only when it is known that no more choices
|
|
* can be used for commit an exhausted brancher can actually be deleted.
|
|
* This becomes known when choice is called.
|
|
*/
|
|
while (b_status != Brancher::cast(&bl))
|
|
if (b_status->status(*this)) {
|
|
// Brancher still has choices to generate
|
|
return SS_BRANCH;
|
|
} else {
|
|
// Brancher is exhausted
|
|
b_status = Brancher::cast(b_status->next());
|
|
}
|
|
// No brancher with alternatives left, space is solved
|
|
return SS_SOLVED;
|
|
|
|
// Process failure
|
|
failed:
|
|
// Count failure
|
|
ssd.data().gpi.fail(p->gpi());
|
|
// Mark as failed
|
|
fail();
|
|
// Propagate top priority propagators
|
|
ActorLink* e = &pc.p.queue[PropCost::AC_RECORD];
|
|
for (ActorLink* a = e->next(); a != e; a = a->next()) {
|
|
Propagator* top = Propagator::cast(a);
|
|
// Keep old modification event delta
|
|
ModEventDelta top_med_o = top->u.med;
|
|
// Clear med but leave propagator in queue
|
|
top->u.med = 0;
|
|
switch (top->propagate(*this,top_med_o)) {
|
|
case ES_FIX:
|
|
break;
|
|
case ES_SUBSUMED_:
|
|
break;
|
|
default:
|
|
GECODE_NEVER;
|
|
}
|
|
}
|
|
return SS_FAILED;
|
|
}
|
|
|
|
|
|
const Choice*
|
|
Space::choice(void) {
|
|
if (!stable())
|
|
throw SpaceNotStable("Space::choice");
|
|
if (failed() || (b_status == Brancher::cast(&bl))) {
|
|
// There are no more choices to be generated
|
|
// Delete all branchers
|
|
Brancher* b = Brancher::cast(bl.next());
|
|
while (b != Brancher::cast(&bl)) {
|
|
Brancher* d = b;
|
|
b = Brancher::cast(b->next());
|
|
rfree(d,d->dispose(*this));
|
|
}
|
|
bl.init();
|
|
b_status = b_commit = Brancher::cast(&bl);
|
|
return nullptr;
|
|
}
|
|
/*
|
|
* The call to choice() says that no older choices
|
|
* can be used. Hence, all branchers that are exhausted can be deleted.
|
|
*/
|
|
Brancher* b = Brancher::cast(bl.next());
|
|
while (b != b_status) {
|
|
Brancher* d = b;
|
|
b = Brancher::cast(b->next());
|
|
d->unlink();
|
|
rfree(d,d->dispose(*this));
|
|
}
|
|
// Make sure that b_commit does not point to a deleted brancher!
|
|
b_commit = b_status;
|
|
return b_status->choice(*this);
|
|
}
|
|
|
|
const Choice*
|
|
Space::choice(Archive& e) const {
|
|
unsigned int id; e >> id;
|
|
Brancher* b_cur = Brancher::cast(bl.next());
|
|
while (b_cur != Brancher::cast(&bl)) {
|
|
if (id == b_cur->id())
|
|
return b_cur->choice(*this,e);
|
|
b_cur = Brancher::cast(b_cur->next());
|
|
}
|
|
throw SpaceNoBrancher("Space::choice");
|
|
}
|
|
|
|
void
|
|
Space::_commit(const Choice& c, unsigned int a) {
|
|
if (a >= c.alternatives())
|
|
throw SpaceIllegalAlternative("Space::commit");
|
|
if (failed())
|
|
return;
|
|
if (Brancher* b = brancher(c.bid)) {
|
|
// There is a matching brancher
|
|
if (pc.p.bid_sc & sc_trace) {
|
|
TraceRecorder* tr = findtracerecorder();
|
|
if ((tr != nullptr) && (tr->events() & TE_COMMIT) &&
|
|
tr->filter()(b->group())) {
|
|
CommitTraceInfo cti(*b,c,a);
|
|
tr->tracer()._commit(*this,cti);
|
|
}
|
|
ViewTraceInfo vti = pc.p.vti;
|
|
pc.p.vti.brancher(*b);
|
|
ExecStatus es = b->commit(*this,c,a);
|
|
pc.p.vti = vti;
|
|
if (es == ES_FAILED)
|
|
fail();
|
|
} else {
|
|
if (b->commit(*this,c,a) == ES_FAILED)
|
|
fail();
|
|
}
|
|
} else {
|
|
// There is no matching brancher!
|
|
throw SpaceNoBrancher("Space::commit");
|
|
}
|
|
}
|
|
|
|
void
|
|
Space::_trycommit(const Choice& c, unsigned int a) {
|
|
if (a >= c.alternatives())
|
|
throw SpaceIllegalAlternative("Space::commit");
|
|
if (failed())
|
|
return;
|
|
if (Brancher* b = brancher(c.bid)) {
|
|
// There is a matching brancher
|
|
if (pc.p.bid_sc & sc_trace) {
|
|
TraceRecorder* tr = findtracerecorder();
|
|
if ((tr != nullptr) && (tr->events() & TE_COMMIT) &&
|
|
tr->filter()(b->group())) {
|
|
CommitTraceInfo cti(*b,c,a);
|
|
tr->tracer()._commit(*this,cti);
|
|
}
|
|
ViewTraceInfo vti = pc.p.vti;
|
|
pc.p.vti.brancher(*b);
|
|
ExecStatus es = b->commit(*this,c,a);
|
|
pc.p.vti = vti;
|
|
if (es == ES_FAILED)
|
|
fail();
|
|
} else {
|
|
if (b->commit(*this,c,a) == ES_FAILED)
|
|
fail();
|
|
}
|
|
}
|
|
}
|
|
|
|
NGL*
|
|
Space::ngl(const Choice& c, unsigned int a) {
|
|
if (a >= c.alternatives())
|
|
throw SpaceIllegalAlternative("Space::ngl");
|
|
if (failed())
|
|
return nullptr;
|
|
if (Brancher* b = brancher(c.bid)) {
|
|
// There is a matching brancher
|
|
return b->ngl(*this,c,a);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
Space::print(const Choice& c, unsigned int a, std::ostream& o) const {
|
|
if (a >= c.alternatives())
|
|
throw SpaceIllegalAlternative("Space::print");
|
|
if (failed())
|
|
return;
|
|
if (Brancher* b = const_cast<Space&>(*this).brancher(c.bid)) {
|
|
// There is a matching brancher
|
|
b->print(*this,c,a,o);
|
|
} else {
|
|
// There is no matching brancher!
|
|
throw SpaceNoBrancher("Space::print");
|
|
}
|
|
}
|
|
|
|
void
|
|
Space::kill_brancher(unsigned int id) {
|
|
if (failed())
|
|
return;
|
|
for (Brancher* b = Brancher::cast(bl.next());
|
|
b != Brancher::cast(&bl); b = Brancher::cast(b->next()))
|
|
if (b->id() == id) {
|
|
kill(*b);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Space cloning
|
|
*
|
|
* Cloning is performed in two steps:
|
|
* - The space itself is copied by the copy constructor. This
|
|
* also copies all propagators, branchers, and variables.
|
|
* The copied variables are recorded.
|
|
* - In the second step the dependency information of the recorded
|
|
* variables is updated and their forwarding information is reset.
|
|
*
|
|
*/
|
|
Space::Space(Space& s)
|
|
: ssd(s.ssd),
|
|
mm(ssd.data().sm,s.mm,s.pc.p.n_sub*sizeof(Propagator**)),
|
|
#ifdef GECODE_HAS_CBS
|
|
var_id_counter(s.var_id_counter),
|
|
#endif
|
|
d_fst(&Actor::sentinel) {
|
|
#ifdef GECODE_HAS_VAR_DISPOSE
|
|
for (int i=0; i<AllVarConf::idx_d; i++)
|
|
_vars_d[i] = nullptr;
|
|
#endif
|
|
for (int i=0; i<AllVarConf::idx_c; i++)
|
|
pc.c.vars_u[i] = nullptr;
|
|
pc.c.vars_noidx = nullptr;
|
|
pc.c.local = nullptr;
|
|
// Copy all propagators
|
|
{
|
|
ActorLink* p = &pl;
|
|
ActorLink* e = &s.pl;
|
|
for (ActorLink* a = e->next(); a != e; a = a->next()) {
|
|
Actor* c = Actor::cast(a)->copy(*this);
|
|
// Link copied actor
|
|
p->next(ActorLink::cast(c)); ActorLink::cast(c)->prev(p);
|
|
// Note that forwarding is done in the constructors
|
|
p = c;
|
|
}
|
|
// Link last actor
|
|
p->next(&pl); pl.prev(p);
|
|
}
|
|
// Copy all branchers
|
|
{
|
|
ActorLink* p = &bl;
|
|
ActorLink* e = &s.bl;
|
|
for (ActorLink* a = e->next(); a != e; a = a->next()) {
|
|
Actor* c = Actor::cast(a)->copy(*this);
|
|
// Link copied actor
|
|
p->next(ActorLink::cast(c)); ActorLink::cast(c)->prev(p);
|
|
// Note that forwarding is done in the constructors
|
|
p = c;
|
|
}
|
|
// Link last actor
|
|
p->next(&bl); bl.prev(p);
|
|
}
|
|
// Setup brancher pointers
|
|
if (s.b_status == &s.bl) {
|
|
b_status = Brancher::cast(&bl);
|
|
} else {
|
|
b_status = Brancher::cast(s.b_status->prev());
|
|
}
|
|
if (s.b_commit == &s.bl) {
|
|
b_commit = Brancher::cast(&bl);
|
|
} else {
|
|
b_commit = Brancher::cast(s.b_commit->prev());
|
|
}
|
|
}
|
|
|
|
Space*
|
|
Space::_clone(void) {
|
|
if (failed())
|
|
throw SpaceFailed("Space::clone");
|
|
if (!stable())
|
|
throw SpaceNotStable("Space::clone");
|
|
|
|
// Copy all data structures (which in turn will invoke the constructor)
|
|
Space* c = copy();
|
|
|
|
if (c->d_fst != &Actor::sentinel)
|
|
throw SpaceNotCloned("Space::clone");
|
|
|
|
// Setup array for actor disposal in c
|
|
{
|
|
unsigned int n = static_cast<unsigned int>(d_cur - d_fst);
|
|
if (n == 0) {
|
|
// No actors
|
|
c->d_fst = c->d_cur = c->d_lst = nullptr;
|
|
} else {
|
|
// Leave one entry free
|
|
c->d_fst = c->alloc<Actor*>(n+1);
|
|
c->d_cur = c->d_fst;
|
|
c->d_lst = c->d_fst+n+1;
|
|
for (Actor** d_fst_iter = d_fst; d_fst_iter != d_cur; d_fst_iter++) {
|
|
ptrdiff_t m;
|
|
Actor* a = static_cast<Actor*>(Support::ptrsplit(*d_fst_iter,m));
|
|
if (a->prev())
|
|
*(c->d_cur++) = Actor::cast(static_cast<ActorLink*>
|
|
(Support::ptrjoin(a->prev(),m)));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update variables without indexing structure
|
|
VarImp<NoIdxVarImpConf>* x =
|
|
static_cast<VarImp<NoIdxVarImpConf>*>(c->pc.c.vars_noidx);
|
|
while (x != nullptr) {
|
|
VarImp<NoIdxVarImpConf>* n = x->next();
|
|
x->b.base = nullptr; x->u.idx[0] = 0;
|
|
if (sizeof(ActorLink**) > sizeof(unsigned int))
|
|
*(1+&x->u.idx[0]) = 0;
|
|
x = n;
|
|
}
|
|
// Update variables with indexing structure
|
|
c->update(static_cast<ActorLink**>(c->mm.subscriptions()));
|
|
|
|
// Re-establish prev links (reset forwarding information)
|
|
{
|
|
ActorLink* p_a = &pl;
|
|
ActorLink* c_a = p_a->next();
|
|
// First update propagators and advisors
|
|
while (c_a != &pl) {
|
|
Propagator* p = Propagator::cast(c_a);
|
|
if (p->u.advisors != nullptr) {
|
|
ActorLink* a = p->u.advisors;
|
|
p->u.advisors = nullptr;
|
|
do {
|
|
a->prev(p); a = a->next();
|
|
} while (a != nullptr);
|
|
}
|
|
c_a->prev(p_a); p_a = c_a; c_a = c_a->next();
|
|
}
|
|
}
|
|
{
|
|
ActorLink* p_a = &bl;
|
|
ActorLink* c_a = p_a->next();
|
|
// Update branchers
|
|
while (c_a != &bl) {
|
|
c_a->prev(p_a); p_a = c_a; c_a = c_a->next();
|
|
}
|
|
}
|
|
|
|
// Reset links for local objects
|
|
for (ActorLink* l = c->pc.c.local; l != nullptr; l = l->next())
|
|
l->prev(nullptr);
|
|
|
|
// Initialize propagator queue
|
|
c->pc.p.active = &c->pc.p.queue[0]-1;
|
|
for (int i=0; i<=PropCost::AC_MAX; i++)
|
|
c->pc.p.queue[i].init();
|
|
// Copy propagation only data
|
|
c->pc.p.n_sub = pc.p.n_sub;
|
|
c->pc.p.bid_sc = pc.p.bid_sc;
|
|
|
|
// Reset execution information
|
|
c->pc.p.vti.other(); pc.p.vti.other();
|
|
|
|
return c;
|
|
}
|
|
|
|
void
|
|
Space::constrain(const Space&) {
|
|
}
|
|
|
|
bool
|
|
Space::master(const MetaInfo& mi) {
|
|
switch (mi.type()) {
|
|
case MetaInfo::RESTART:
|
|
if (mi.last() != nullptr)
|
|
constrain(*mi.last());
|
|
mi.nogoods().post(*this);
|
|
// Perform a restart even if a solution has been found
|
|
return true;
|
|
case MetaInfo::PORTFOLIO:
|
|
// Kill all branchers
|
|
BrancherGroup::all.kill(*this);
|
|
return true;
|
|
default: GECODE_NEVER;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
Space::slave(const MetaInfo&) {
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
Space::afc_unshare(void) {
|
|
if (ssd.data().gpi.unshare()) {
|
|
for (Propagators ps(*this); ps(); ++ps) {
|
|
Propagator& p = ps.propagator();
|
|
Kernel::GPI::Info* gpi
|
|
= ssd.data().gpi.allocate(p.gpi().pid,p.gpi().gid);
|
|
if (p.disabled())
|
|
p.gpi_disabled = Support::mark(gpi);
|
|
else
|
|
p.gpi_disabled = gpi;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LocalObject::fwdcopy(Space& home) {
|
|
ActorLink::cast(this)->prev(copy(home));
|
|
next(home.pc.c.local);
|
|
home.pc.c.local = this;
|
|
}
|
|
|
|
void
|
|
Choice::archive(Archive& e) const {
|
|
e << id();
|
|
}
|
|
|
|
bool
|
|
NGL::notice(void) const {
|
|
return false;
|
|
}
|
|
|
|
NGL::~NGL(void) {
|
|
}
|
|
|
|
|
|
/*
|
|
* Groups
|
|
*/
|
|
|
|
Group Group::all(GROUPID_ALL);
|
|
Group Group::def(GROUPID_DEF);
|
|
|
|
PropagatorGroup PropagatorGroup::all(GROUPID_ALL);
|
|
PropagatorGroup PropagatorGroup::def(GROUPID_DEF);
|
|
|
|
BrancherGroup BrancherGroup::all(GROUPID_ALL);
|
|
BrancherGroup BrancherGroup::def(GROUPID_DEF);
|
|
|
|
unsigned int Group::next = GROUPID_DEF+1;
|
|
Support::Mutex Group::m;
|
|
|
|
|
|
Group::Group(void) {
|
|
{
|
|
Support::Lock l(m);
|
|
gid = next++;
|
|
}
|
|
if (gid == GROUPID_MAX)
|
|
throw TooManyGroups("Group::Group");
|
|
}
|
|
|
|
|
|
PropagatorGroup&
|
|
PropagatorGroup::move(Space& home, PropagatorGroup g) {
|
|
if ((id() != GROUPID_ALL) && (id() != g.id()))
|
|
for (Space::Propagators ps(home); ps(); ++ps)
|
|
if (g.in(ps.propagator().group()))
|
|
ps.propagator().group(*this);
|
|
return *this;
|
|
}
|
|
|
|
PropagatorGroup&
|
|
PropagatorGroup::move(Space& home, unsigned int pid) {
|
|
if (id() == GROUPID_ALL)
|
|
return *this;
|
|
for (Space::Propagators ps(home); ps(); ++ps)
|
|
if (ps.propagator().id() == pid) {
|
|
ps.propagator().group(*this);
|
|
return *this;
|
|
}
|
|
throw UnknownPropagator("PropagatorGroup::move");
|
|
GECODE_NEVER;
|
|
return *this;
|
|
}
|
|
|
|
unsigned int
|
|
PropagatorGroup::size(Space& home) const {
|
|
if (home.failed())
|
|
return 0;
|
|
unsigned int n=0;
|
|
for (Space::Propagators ps(home); ps(); ++ps)
|
|
if (in(ps.propagator().group()))
|
|
n++;
|
|
return n;
|
|
}
|
|
|
|
void
|
|
PropagatorGroup::kill(Space& home) {
|
|
if (home.failed())
|
|
return;
|
|
Space::Propagators ps(home);
|
|
while (ps()) {
|
|
Propagator& p = ps.propagator();
|
|
++ps;
|
|
if (in(p.group()))
|
|
home.kill(p);
|
|
}
|
|
}
|
|
|
|
void
|
|
PropagatorGroup::disable(Space& home) {
|
|
if (home.failed())
|
|
return;
|
|
for (Space::Propagators ps(home); ps(); ++ps)
|
|
if (in(ps.propagator().group()))
|
|
ps.propagator().disable(home);
|
|
}
|
|
|
|
void
|
|
PropagatorGroup::enable(Space& home, bool s) {
|
|
if (home.failed())
|
|
return;
|
|
if (s) {
|
|
Space::Propagators ps(home);
|
|
while (ps()) {
|
|
Propagator& p = ps.propagator();
|
|
++ps;
|
|
if (in(p.group())) {
|
|
p.enable(home);
|
|
p.reschedule(home);
|
|
}
|
|
}
|
|
} else {
|
|
for (Space::Propagators ps(home); ps(); ++ps)
|
|
if (in(ps.propagator().group()))
|
|
ps.propagator().enable(home);
|
|
}
|
|
}
|
|
|
|
|
|
BrancherGroup&
|
|
BrancherGroup::move(Space& home, BrancherGroup g) {
|
|
if ((id() != GROUPID_ALL) && (id() != g.id()))
|
|
for (Space::Branchers bs(home); bs(); ++bs)
|
|
if (g.in(bs.brancher().group()))
|
|
bs.brancher().group(*this);
|
|
return *this;
|
|
}
|
|
|
|
BrancherGroup&
|
|
BrancherGroup::move(Space& home, unsigned int bid) {
|
|
if (id() == GROUPID_ALL)
|
|
return *this;
|
|
for (Space::Branchers bs(home); bs(); ++bs)
|
|
if (bs.brancher().id() == bid) {
|
|
bs.brancher().group(*this);
|
|
return *this;
|
|
}
|
|
throw UnknownBrancher("BrancherGroup::move");
|
|
GECODE_NEVER;
|
|
return *this;
|
|
}
|
|
|
|
unsigned int
|
|
BrancherGroup::size(Space& home) const {
|
|
if (home.failed())
|
|
return 0;
|
|
unsigned int n=0;
|
|
for (Space::Branchers bs(home); bs(); ++bs)
|
|
if (in(bs.brancher().group()))
|
|
n++;
|
|
return n;
|
|
}
|
|
|
|
void
|
|
BrancherGroup::kill(Space& home) {
|
|
if (home.failed())
|
|
return;
|
|
Space::Branchers bs(home);
|
|
while (bs()) {
|
|
Brancher& b = bs.brancher();
|
|
++bs;
|
|
if (in(b.group()))
|
|
home.kill(b);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// STATISTICS: kernel-core
|