33. Advanced topics¶
This chapters presents advanced topics for programming branchers as implementations of branchings.
Overview. Assignment branchers presents a specialized brancher for assigning views rather than branching on them. How branchers support no-goods is discussed in Supporting no-goods. Variable views for branchers are discussed in Using variable views.
33.1. Assignment branchers¶
This section presents an example for a brancher that assigns all of its views rather than branches on its views. The branching
void assignmin(Home home, const IntVarArgs& x);
assigns all variables in x to their smallest possible value. That is, assignmin is equivalent to the predefined branching (see Assigning integer, Boolean, set, and float variables) used as
assign(home, x, INT_ASSIGN_MIN());
...
class AssignMin : public Brancher {
...
class PosVal : public Choice {
public:
int pos; int val;
PosVal(const AssignMin& b, int p, int v)
: Choice(b,1), pos(p), val(v) {}
...
};
...
virtual ExecStatus commit(Space& home,
const Choice& c,
unsigned int a) {
const PosVal& pv = static_cast<const PosVal&>(c);
int pos=pv.pos, val=pv.val;
return me_failed(x[pos].eq(home,val)) ? ES_FAILED : ES_OK;
}
...
};
...
Download: assign-min.cpp
Program 33.1 shows the relevant parts of the brancher AssignMin. Unsurprisingly, both the status() and choice() function are identical to those shown in Program 32.2 (and hence are omitted).
The changes concern the created choice of type PosVal: the constructor now initializes a choice with a single alternative only (the second argument to the call of the constructor Choice). The commit() function is a specialized version of the commit() function defined in Program 32.2. It only needs to be capable of handling a single alternative. The same holds true for the print() function.
33.2. Supporting no-goods¶
Supporting no-goods by a brancher is straightforward: every brancher has a virtual member function ngl() (for no-good literal) that takes the same arguments as the commit() member function: a space, a choice, and the number of the alternative and returns a pointer to a no-good literal of class NGL. The ngl() function is called during no-good generation (see No-goods from restarts) and the returned no-good literal is then used by a no-good propagator that propagates the no-goods (if you are curious, the propagator is implemented by Search::NoGoodsProp).
...
class EqNGL : public NGL {
protected:
Int::IntView x; int n;
public:
EqNGL(Space& home, Int::IntView x0, int n0)
: NGL(home), x(x0), n(n0) {}
EqNGL(Space& home, EqNGL& ngl)
: NGL(home, ngl), n(ngl.n) {
x.update(home, ngl.x);
}
// [none min with no-good support:status]
// [none min with no-good support:prune]
// [none min with no-good support:subscribe and cancel]
// [none min with no-good support:re-scheduling]
virtual NGL* copy(Space& home) {
return new (home) EqNGL(home,*this);
}
virtual size_t dispose(Space& home) {
(void) NGL::dispose(home);
return sizeof(*this);
}
};
class NoneMin : public Brancher {
...
public:
...
// [none min with no-good support:no-good literal creation]
};
...
Download: none-min-with-no-good-support.cpp
By default, the ngl() function of a brancher returns NULL, which means that the brancher does not support no-goods. In order to support no-goods, a brancher must redefine the ngl() function and must define a class (or several classes) for the no-good literals to be returned. The class EqNGL implementing a no-good literal for equality and the ngl() function is shown in Program 33.2. Otherwise, the brancher is the same as the nonemin brancher shown in Program 32.2.
33.2.1. Returning no-good literals¶
The ngl() function of a brancher has the following options:
As mentioned above, it can always return
NULLand hence the brancher does not support no-goods.It returns for each alternative of a choice a no-good literal. For our
NoneMinbrancher this would entail that whenngl(home,c,0)is called, it returns a no-good literal implementing equality between a view and an integer that corresponds to the first alternative (for a given spacehomeand a choicec).For the second alternative
ngl(home,c,1)a no-good literal implementing disequality should be returned. This would work, but the brancher can do better than that as is explained below.When
ngl(home,c,a)is called for an alternative where \(\mathtt{a}>0\) with a spacehomeand a choicecandais the last alternative (forNoneMin, \(\mathtt{a}=\mathtt{1}\)) and the last alternative is the logical negation of all other alternatives, then thengl()function can returnNULLas an optimization.Assume that the alternatives of a choice are
\[\mathtt{l}_0\vee\ldots\vee\mathtt{l}_{\mathtt{n}-1} \]where
nis the arity of the choice and it holds that\[(\mathtt{l}_0\vee\ldots\vee\mathtt{l}_{\mathtt{n}-2}) \Leftrightarrow \neg\mathtt{l}_{\mathtt{n}-1}\]is true, then the
ngl()function can returnNULLfor the last alternative (that is, for the alternative \(\mathtt{n}-1\)).Note that this optimization implements the very same idea as discussed at the beginning of No-goods from restarts. Note also that this property is typically only true for branchers with binary choices such as in our example.
In our example, the second alternative is indeed the negation of the first alternative. Hence the following ngl() function implements no-good literal creation and only requires a single class EqNGL for no-good literals implementing equality:
virtual NGL* ngl(Space& home, const Choice& c,
unsigned int a) const {
const PosVal& pv = static_cast<const PosVal&>(c);
int pos=pv.pos, val=pv.val;
if (a == 0)
return new (home) EqNGL(home, x[pos], val);
else
return NULL;
}
33.2.2. Implementing no-good literals¶
No-good literals implement constraints that correspond to alternatives of choices. But instead of implementing these constraints by a propagator, they have a specialized implementation that is used by a no-good propagator. All concepts needed to implement a no-good literal are concepts that are familiar from implementing a propagator.
A no-good literal inherits from the class NGL and must implement the following constructors and functions:
Unsurprisingly, a no-good literal must implement constructors for creation and cloning and member functions
copy()for copying anddispose()for disposal. They are straightforward and are shown in Program 33.2.It must implement a
status()function that checks whether the no-good literal is subsumed (the function returnsNGL::SUBSUMED), failed (the function returnsNGL::FAILED), or neither (the function returnsNGL::NONE). The return type isNGL::Statusas defined in the NGL class.It is important to understand that the no-good propagator using no-good literals can only perform propagation if some of its no-good literals become subsumed. Hence, the test for subsumption used in
status()should try to detect subsumption as early as possible.Testing subsumption for our
EqNGLno-good literal is straightforward:
virtual NGL::Status status(const Space& home) const {
if (x.assigned())
return (x.val() == n) ? NGL::SUBSUMED : NGL::FAILED;
else
return x.in(n) ? NGL::NONE : NGL::FAILED;
}
The
prune()function propagates the negation of the constraint that the no-good literal implements.Again, the
prune()function forNoneMinis straightforward:
virtual ExecStatus prune(Space& home) {
return me_failed(x.nq(home,n)) ? ES_FAILED : ES_OK;
}
A no-good literal must implement a
subscribe()and acancel()function that subscribe and cancel the no-good propagator to the no-good literal’s views.As mentioned above, the earlier the
status()function detects subsumption, the more constraint propagation can be expected from the no-good propagator. Hence, it is important to choose the propagation condition for subscriptions such that whenever there is a modification to the view that could result in subsumption the no-good propagator is executed.For the
EqNGLno-good literal, we choose the propagation condition for subscriptions to beInt::PC_INT_VAL(see Propagation conditions for a discussion of propagation conditions). This choice reflects the fact that subsumption can only be decided after the viewxhas been assigned:
virtual void subscribe(Space& home, Propagator& p) {
x.subscribe(home, p, Int::PC_INT_VAL);
}
virtual void cancel(Space& home, Propagator& p) {
x.cancel(home, p, Int::PC_INT_VAL);
}
A no-good literal must also implement a
reschedule()function that re-schedules the no-goods propagator when it is re-enabled. Thereschedule()function is straightforward, following the patterns of thesubscribe()andcancel()functions:
virtual void reschedule(Space& home, Propagator& p) {
x.reschedule(home, p, Int::PC_INT_VAL);
}
In case a no-good literal uses members that must be deallocated when the home-space is deleted, the no-good literal’s class must redefine the virtual member functions notice() and dispose(), for example by:
virtual bool notice(void) const {
return true;
}
virtual size_t dispose(Space& home) {
...
}
If notice() returns true, the no-good propagator ensures that the no-good literal’s dispose() function is called whenever the propagator’s home-space is deleted.
33.3. Using variable views¶
Variable views can also be used for reusing branchers to obtain several branchings, similar to reusing propagators for several constraints, see Views.
While in principle all different variable views introduced in Views can be used for branchers, the only meaningful variable view for branchers is the minus integer view (see Minus views).
...
template<class View>
class NoneMin : public Brancher {
...
};
void nonemin(Home home, const IntVarArgs& x) {
...
}
void nonemax(Home home, const IntVarArgs& x) {
if (home.failed()) return;
ViewArray<Int::MinusView> y(home,x.size());
for (int i=x.size(); i--; )
y[i]=Int::MinusView(x[i]);
NoneMin<Int::MinusView>::post(home,y);
}
Download: none-min-and-none-max.cpp
As an example consider the branchings nonemin (see Implementing a nonemin branching) and nonemax where the latter tries to assign the maximal value of a view first. The corresponding program fragment is shown in Program 33.3. The class NoneMin is made generic with respect to the type of view it uses. Then, both nonemin and nonemax can be obtained by instantiating NoneMin with integer views or integer minus views.