Альтернативные стратегии ветвления в Gecode

Я пишу здесь, чтобы спросить, есть ли способ чередовать разные стратегии ветвления. Позвольте мне объяснить, у меня есть эффективная стратегия ветвления, которую мы назовем стратегией . Самая большая проблема заключается в том, что стратегия не может использоваться так часто. Поэтому, когда я не могу использовать стратегию , Я использую другую стратегию, которую я назову стратегией В, который менее эффективен.

В документации сказано, что:

Отраслевой заказ. Создание ветки регистрирует его в своем домашнем пространстве. Пространство поддерживает очередь своих ответвлений в том смысле, что ответвитель, который зарегистрирован первым, также используется первым для
ветвления. Первый филиал в очереди филиалов упоминается как текущий филиал.

Итак, я предположил, что если я отправлю ответвление А, то ответвление В, филиал будет иметь приоритет и каждый раз status из говорит, что ветвления нет, ответвление В будет использоваться. Похоже, я был неправ, потому что когда status возврата филиала falseбольше никогда не вызывается.
Вот «минимальный пример«:

#include <gecode/minimodel.hh>
#include <iostream>

using namespace Gecode;
using namespace std;

class MyChoice : public Choice {
public:
int pos; // Position of the variable
int val; // Value of to assign

MyChoice(const Brancher& b, int pos0, int val0)
: Choice(b,2), pos(pos0), val(val0) {}

// Report size occupied
virtual size_t size(void) const {
return sizeof(*this);
}

// Archive into e
virtual void archive(Archive& e) const {
Choice::archive(e);
e << pos << val;
}
};

class BranchA : public Brancher {
protected:
ViewArray<Int::IntView> x;
public:
BranchA(Home home, ViewArray<Int::IntView>& x0)
: Brancher(home), x(x0) {}

static void post(Home home, ViewArray<Int::IntView>& x) {
(void) new (home) BranchA(home,x);
}

virtual size_t dispose(Space& home) {
(void) Brancher::dispose(home);
return sizeof(*this);
}
BranchA(Space& home, bool share, BranchA& b)
: Brancher(home,share,b) {
x.update(home,share,b.x);
}
virtual Brancher* copy(Space& home, bool share) {
return new (home) BranchA(home,share,*this);
}
// status
virtual bool status(const Space& home) const {
for (int i=0; i<x.size(); i++)
if (!x[i].assigned())
return !i%2 && x[i].in(1);
return false;
}
// choice
virtual Choice* choice(Space& home) {
for (int i=0; true; i++)
if (!x[i].assigned())
return new MyChoice(*this,i,1);
GECODE_NEVER;
return NULL;
}
virtual Choice* choice(const Space&, Archive& e) {
int pos, val;
e >> pos >> val;
return new MyChoice(*this, pos, val);
}
// commit
virtual ExecStatus commit(Space& home,
const Choice& c,
unsigned int a) {
const MyChoice& pv = static_cast<const MyChoice&>(c);
int pos=pv.pos, val=pv.val;
if (a == 0)
return me_failed(x[pos].eq(home,val)) ? ES_FAILED : ES_OK;
else
return me_failed(x[pos].nq(home,val)) ? ES_FAILED : ES_OK;
}
};
void branchA(Home home, const IntVarArgs& x) {
if (home.failed()) return;
ViewArray<Int::IntView> y(home,x);
BranchA::post(home,y);
}
// BranchB //////////////////////////////////////////////////////

class BranchB : public Brancher {
protected:
ViewArray<Int::IntView> x;
public:
BranchB(Home home, ViewArray<Int::IntView>& x0)
: Brancher(home), x(x0) {}
static void post(Home home, ViewArray<Int::IntView>& x) {
(void) new (home) BranchB(home,x);
}
virtual size_t dispose(Space& home) {
(void) Brancher::dispose(home);
return sizeof(*this);
}
BranchB(Space& home, bool share, BranchB& b)
: Brancher(home,share,b) {
x.update(home,share,b.x);
}
virtual Brancher* copy(Space& home, bool share) {
return new (home) BranchB(home,share,*this);
}
// status
virtual bool status(const Space& home) const {
for (int i=0; i<x.size(); i++)
if (!x[i].assigned())
return i%2 && x[i].in(2);
return false;
}
// choice
virtual Choice* choice(Space& home) {
for (int i=0; true; i++)
if (!x[i].assigned())
return new MyChoice(*this,i,2);
GECODE_NEVER;
return NULL;
}
virtual Choice* choice(const Space&, Archive& e) {
int pos, val;
e >> pos >> val;
return new MyChoice(*this, pos, val);
}
// commit
virtual ExecStatus commit(Space& home,
const Choice& c,
unsigned int a) {
const MyChoice& pv = static_cast<const MyChoice&>(c);
int pos=pv.pos, val=pv.val;
if (a == 0)
return me_failed(x[pos].eq(home,val)) ? ES_FAILED : ES_OK;
else
return me_failed(x[pos].nq(home,val)) ? ES_FAILED : ES_OK;
}
};
void branchB(Home home, const IntVarArgs& x) {
if (home.failed()) return;
ViewArray<Int::IntView> y(home,x);
BranchB::post(home,y);
}

// Minimal Space ///////////////////////////////////////class TestSpace : public Space {
protected:
IntVarArray x;
public:
TestSpace(int size)
: x(*this, size, 0, 10) {
branchA(*this, x);
branchB(*this, x);
}

TestSpace (bool share, TestSpace& s)
: Space(share, s) {
x.update(*this, share, s.x);
}

virtual Space* copy (bool share) {
return new TestSpace(share, *this);
}
void print(std::ostream& os) {
os << "x= " << x << endl;
}
};

// Minimal Main //////////////////////:

int main (int, char**) {
// create model and search engine
TestSpace* m = new TestSpace(10);
DFS<TestSpace> e(m);
delete m;
// search and print all solutions
while (TestSpace* s = e.next()) {
s->print(cout); delete s;
}
return 0;
}

В этом примере status ветвь вернуть true если следующая переменная для назначения находится на четное индекс, и если переменная может принимать значение 1 (false еще). И ответвление В status вернуть true если следующая переменная для назначения находится на странный индекс, и если переменная может принимать значение 2 (false еще).
С этим кодом я ожидал получить решения [1, 2, 1, 2, ...] а также [!1, !2, !1, !2, ...] (и другие комбинации, такие как [!1, 2, 1, !2, ...]) но так как филиалы располагаются, когда их status вернуть false, только две первые переменные были назначены.

Есть ли хороший способ сделать так, чтобы ветвь не утилизировалась после status вернуть false (или чередовать две разные стратегии ветвления) или я должен объединить два ответвления в один?

1

Решение

Если это может кому-то помочь, вот решение, которое я использовал.
По совету Патрика Трентена, я объединил контроль, создав третий разветвитель, который является вектором разветвителей. Вот реализация, которую я использовал:

Заголовок branchAllInOne.h:

#include <gecode/minimodel.hh>

using namespace Gecode;
using namespace std;

class BranchAllInOne : public Brancher {
protected:
// Queue of brancher (may be better with ActorLink)
vector<Actor *> queue;

// Every brancher are in the brancher
BrancherGroup group;

mutable int toChoose;

class ChoiceAndID : public Choice {
public:
// Choice of the brancher used
Choice* c;
/// ID of brancher used
unsigned int id;

ChoiceAndID(const Brancher& b, Choice * c, unsigned int id);
virtual ~ChoiceAndID();
virtual size_t size(void) const ;
virtual void archive(Archive& e) const ;
};

public:
BranchAllInOne(Home home);
virtual size_t dispose(Space& home);
BranchAllInOne(Home home, bool share, BranchAllInOne& b);
virtual ~BranchAllInOne();
/**
* Check status of brancher, set toChoose value to the ID of the first
* brancher with alternative left
**/
virtual bool status(const Space&) const ;

/**
* Let the brancher of ID toChoose make the choice
*/
virtual Choice* choice(Space&);
virtual Choice* choice(const Space&, Archive& e);

/**
* Let the brancher of ID toChoose commit his choice
*/
virtual ExecStatus commit(Space& home, const Choice& _c, unsigned int a);

/// Copy brancher
virtual Actor* copy(Space& home, bool share);

/// Post brancher
static BranchAllInOne * post(Home home);

virtual void print(const Space& home,
const Choice& c,
unsigned int a,
ostream& o) const ;
void pushBrancher(Space& home, Brancher *b);
};

BranchAllInOne * branchAllInOne(Home home);

Реализация branchAllInOne.cpp:

#include "branchAllInOne.h"
static Brancher * ActorToBrancher(Actor *a);
// Choice implementation
BranchAllInOne::ChoiceAndID::ChoiceAndID(const Brancher& b, Choice * c0, unsigned int id0)
: Choice(b, c0->alternatives()),
c(c0),
id(id0){}

BranchAllInOne::ChoiceAndID::~ChoiceAndID() {
delete c;
}

size_t BranchAllInOne::ChoiceAndID::size(void) const {
return sizeof(*this) + c->size();
}

void BranchAllInOne::ChoiceAndID::archive(Archive& e) const {
Choice::archive(e);
c->archive(e);
}

BranchAllInOne::BranchAllInOne(Home home)
: Brancher(home),
toChoose(-1) {
home.notice(*this,AP_DISPOSE);
}

// brancher
BranchAllInOne * BranchAllInOne::post(Home home) {
return new (home) BranchAllInOne(home);
}size_t BranchAllInOne::dispose(Space& home) {
home.ignore(*this, AP_DISPOSE);
size_t size = queue.size() * sizeof(Actor*);
for (unsigned int i = queue.size() ; i--;) {
size += ActorToBrancher(queue[i])->dispose(home);
}
queue.~vector();

// Making sure to kill each brancher inserted in the queue (may be useless)
group.kill(home);

(void) Brancher::dispose(home);
return sizeof(*this) + size;
}

BranchAllInOne::BranchAllInOne(Home home, bool share, BranchAllInOne& b)
: Brancher(home, share, b),
queue(b.queue.size()),
toChoose(b.toChoose){
for (unsigned int i = 0 ; i < queue.size() ; i++)
queue[i] = b.queue[i]->copy(home, share);
}

BranchAllInOne::~BranchAllInOne() {
for (unsigned int i = 0 ; i < queue.size() ; i++) {
delete queue[i];
}
queue.~vector();
}

Actor* BranchAllInOne::copy(Space& home, bool share){
return new (home) BranchAllInOne(home, share, *this);
}

// status
bool BranchAllInOne::status(const Space& s) const {
for (unsigned int i = 0 ; i < queue.size() ; i++) {
if (ActorToBrancher(queue[i])->status(s)) {
toChoose = i;
return true;
}
}
std::cout << std::endl;
return false;
}

// choice
Choice* BranchAllInOne::choice(Space& s) {
ChoiceAndID* res = new ChoiceAndID(*this,
const_cast<Choice *>(ActorToBrancher(queue[toChoose])->choice(s)),
toChoose);
toChoose = -1;
return res;
}

Choice* BranchAllInOne::choice(const Space& s, Archive& e) {
return new ChoiceAndID(*this,
const_cast<Choice *>(ActorToBrancher(queue[toChoose])->choice(s, e)),
toChoose);
}

// Perform commit for choice \a _c and alternative \a a
ExecStatus BranchAllInOne::commit(Space& home, const Choice& c, unsigned int a) {
const BranchAllInOne::ChoiceAndID& ch =  static_cast<const BranchAllInOne::ChoiceAndID&>(c);
return ActorToBrancher(queue[ch.id])->commit(home, const_cast<Choice&>(*ch.c), a);

}void BranchAllInOne::print(const Space& home,
const Choice& c,
unsigned int a,
ostream& o) const {
const BranchAllInOne::ChoiceAndID& ch =  static_cast<const BranchAllInOne::ChoiceAndID&>(c);
o << ch.id << ": ";
ActorToBrancher(queue[ch.id])->print(home, *(ch.c), a, o);
}

void BranchAllInOne::pushBrancher(Space &home, Brancher *b) {
queue.push_back(b);
group.move(home, *b);
}

static Brancher * ActorToBrancher(Actor *a) {
return dynamic_cast<Brancher *>(a);
}

// end of BranchAllInOne implementation

BranchAllInOne* branchAllInOne(Home home) {
if (home.failed()) return NULL;
return BranchAllInOne::post(home);
}

Я сделал несколько модификаций, чтобы получить указатель на ветки, которые я хочу поместить в вектор (включая функцию post каждого ветки):
brancherA пример:

BranchA * BranchA::post(Home home, ViewArray<Int::IntView>& x) {
return new (home) BranchA(home,x);
}BranchA * branchA(Home home, const IntVarArgs& x) {
if (home.failed()) return NULL;
ViewArray<Int::IntView> y(home,x);
return BranchA::post(home,y);
}

Пространство также было изменено:

  TestSpace::TestSpace(int size)
: x(*this, size, 0, 10) {
BranchAllInOne * b = branchAllInOne(*this);
b->pushBrancher(*this, branchA(*this, x));
b->pushBrancher(*this, branchB(*this, x));
}

Я протестировал его с Gist и без него и получил только утечку памяти указателя для каждого ответвителя, вставленного в вектор (здесь только два). Небольшая проблема остается в том, что ветвления, добавленные в вектор, также планируются после остановки третьего ветвления (но их статус возвращается false).

0

Другие решения

Других решений пока нет …