Skip to content

Commit

Permalink
Add argmin/argmax for BoolVar
Browse files Browse the repository at this point in the history
  • Loading branch information
guidotack committed Apr 1, 2019
1 parent 5d278dd commit daaaa67
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 2 deletions.
7 changes: 7 additions & 0 deletions changelog.in
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ Date: 2019-??-??
[DESCRIPTION]
Let's see.

[ENTRY]
Module: int
What: new
Rank: minor
[DESCRIPTION]
Add BoolVar versions of argmax and argmin.

[ENTRY]
Module: kernel
What: change
Expand Down
52 changes: 50 additions & 2 deletions gecode/int.hh
Original file line number Diff line number Diff line change
Expand Up @@ -2611,7 +2611,7 @@ namespace Gecode {
GECODE_INT_EXPORT void
argmin(Home home, const IntVarArgs& x, IntVar y, bool tiebreak=true,
IntPropLevel ipl=IPL_DEF);
/** \brief Post propagator for \f$ \operatorname{argmin}(x)-o=y\f$
/** \brief Post propagator for \f$ \operatorname{argmin}(x)+o=y\f$
*
* In case of ties, the smallest value for \a y is chosen
* (provided \a tiebreak is true).
Expand All @@ -2635,7 +2635,7 @@ namespace Gecode {
GECODE_INT_EXPORT void
argmax(Home home, const IntVarArgs& x, IntVar y, bool tiebreak=true,
IntPropLevel ipl=IPL_DEF);
/** \brief Post propagator for \f$ \operatorname{argmax}(x)-o=y\f$
/** \brief Post propagator for \f$ \operatorname{argmax}(x)+o=y\f$
*
* In case of ties, the smallest value for \a y is chosen
* (provided \a tiebreak is true).
Expand All @@ -2647,6 +2647,54 @@ namespace Gecode {
GECODE_INT_EXPORT void
argmax(Home home, const IntVarArgs& x, int o, IntVar y, bool tiebreak=true,
IntPropLevel ipl=IPL_DEF);
/** \brief Post propagator for \f$ \operatorname{argmin}(x)=y\f$
*
* In case of ties, the smallest value for \a y is chosen
* (provided \a tiebreak is true).
*
* If \a x is empty, an exception of type Int::TooFewArguments is thrown.
* If \a y occurs in \a x, an exception of type Int::ArgumentSame
* is thrown.
*/
GECODE_INT_EXPORT void
argmin(Home home, const BoolVarArgs& x, IntVar y, bool tiebreak=true,
IntPropLevel ipl=IPL_DEF);
/** \brief Post propagator for \f$ \operatorname{argmin}(x)-o=y\f$
*
* In case of ties, the smallest value for \a y is chosen
* (provided \a tiebreak is true).
*
* If \a x is empty, an exception of type Int::TooFewArguments is thrown.
* If \a y occurs in \a x, an exception of type Int::ArgumentSame
* is thrown.
*/
GECODE_INT_EXPORT void
argmin(Home home, const BoolVarArgs& x, int o, IntVar y, bool tiebreak=true,
IntPropLevel ipl=IPL_DEF);
/** \brief Post propagator for \f$ \operatorname{argmax}(x)=y\f$
*
* In case of ties, the smallest value for \a y is chosen
* (provided \a tiebreak is true).
*
* If \a x is empty, an exception of type Int::TooFewArguments is thrown.
* If \a y occurs in \a x, an exception of type Int::ArgumentSame
* is thrown.
*/
GECODE_INT_EXPORT void
argmax(Home home, const BoolVarArgs& x, IntVar y, bool tiebreak=true,
IntPropLevel ipl=IPL_DEF);
/** \brief Post propagator for \f$ \operatorname{argmax}(x)-o=y\f$
*
* In case of ties, the smallest value for \a y is chosen
* (provided \a tiebreak is true).
*
* If \a x is empty, an exception of type Int::TooFewArguments is thrown.
* If \a y occurs in \a x, an exception of type Int::ArgumentSame
* is thrown.
*/
GECODE_INT_EXPORT void
argmax(Home home, const BoolVarArgs& x, int o, IntVar y, bool tiebreak=true,
IntPropLevel ipl=IPL_DEF);

/** \brief Post propagator for \f$ |x_0|=x_1\f$
*
Expand Down
97 changes: 97 additions & 0 deletions gecode/int/arithmetic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,103 @@ namespace Gecode {
::post(home,ix,yv)));
}

void
argmax(Home home, const BoolVarArgs& x, IntVar y, bool tiebreak,
IntPropLevel) {
using namespace Int;
if (x.size() == 0)
throw TooFewArguments("Int::argmax");
GECODE_POST;
// Constrain y properly
IntView yv(y);
GECODE_ME_FAIL(yv.gq(home,0));
GECODE_ME_FAIL(yv.le(home,x.size()));
// Construct index view array
IdxViewArray<BoolView> ix(home,x.size());
for (int i=x.size(); i--; ) {
ix[i].idx=i; ix[i].view=x[i];
}
if (tiebreak)
GECODE_ES_FAIL((Arithmetic::ArgMax<BoolView,IntView,true>
::post(home,ix,yv)));
else
GECODE_ES_FAIL((Arithmetic::ArgMax<BoolView,IntView,false>
::post(home,ix,yv)));
}

void
argmax(Home home, const BoolVarArgs& x, int o, IntVar y, bool tiebreak,
IntPropLevel) {
using namespace Int;
Limits::nonnegative(o,"Int::argmax");
if (x.size() == 0)
throw TooFewArguments("Int::argmax");
GECODE_POST;
// Constrain y properly
OffsetView yv(y,-o);
GECODE_ME_FAIL(yv.gq(home,0));
GECODE_ME_FAIL(yv.le(home,x.size()));
// Construct index view array
IdxViewArray<BoolView> ix(home,x.size());
for (int i=x.size(); i--; ) {
ix[i].idx=i; ix[i].view=x[i];
}
if (tiebreak)
GECODE_ES_FAIL((Arithmetic::ArgMax<BoolView,OffsetView,true>
::post(home,ix,yv)));
else
GECODE_ES_FAIL((Arithmetic::ArgMax<BoolView,OffsetView,false>
::post(home,ix,yv)));
}

void
argmin(Home home, const BoolVarArgs& x, IntVar y, bool tiebreak,
IntPropLevel) {
using namespace Int;
if (x.size() == 0)
throw TooFewArguments("Int::argmin");
GECODE_POST;
// Constrain y properly
IntView yv(y);
GECODE_ME_FAIL(yv.gq(home,0));
GECODE_ME_FAIL(yv.le(home,x.size()));
// Construct index view array
IdxViewArray<NegBoolView> ix(home,x.size());
for (int i=x.size(); i--; ) {
ix[i].idx=i; ix[i].view=NegBoolView(x[i]);
}
if (tiebreak)
GECODE_ES_FAIL((Arithmetic::ArgMax<NegBoolView,IntView,true>
::post(home,ix,yv)));
else
GECODE_ES_FAIL((Arithmetic::ArgMax<NegBoolView,IntView,false>
::post(home,ix,yv)));
}

void
argmin(Home home, const BoolVarArgs& x, int o, IntVar y, bool tiebreak,
IntPropLevel) {
using namespace Int;
Limits::nonnegative(o,"Int::argmin");
if (x.size() == 0)
throw TooFewArguments("Int::argmin");
GECODE_POST;
// Constrain y properly
OffsetView yv(y,-o);
GECODE_ME_FAIL(yv.gq(home,0));
GECODE_ME_FAIL(yv.le(home,x.size()));
// Construct index view array
IdxViewArray<NegBoolView> ix(home,x.size());
for (int i=x.size(); i--; ) {
ix[i].idx=i; ix[i].view=NegBoolView(x[i]);
}
if (tiebreak)
GECODE_ES_FAIL((Arithmetic::ArgMax<NegBoolView,OffsetView,true>
::post(home,ix,yv)));
else
GECODE_ES_FAIL((Arithmetic::ArgMax<NegBoolView,OffsetView,false>
::post(home,ix,yv)));
}

void
mult(Home home, IntVar x0, IntVar x1, IntVar x2,
Expand Down
6 changes: 6 additions & 0 deletions gecode/int/idx-view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ namespace Gecode { namespace Int {
public:
typedef BoolVarArgs argtype;
};
/// VarArg type for Boolean views
template<>
class ViewToVarArg<NegBoolView> {
public:
typedef BoolVarArgs argtype;
};

template<class View>
forceinline IdxView<View>*
Expand Down
179 changes: 179 additions & 0 deletions test/int/arithmetic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,173 @@ namespace Test { namespace Int {
}
};

/// %Test for Boolean argument maximum constraint
class ArgMaxBool : public Test {
protected:
/// Offset to be used
int offset;
/// Whether to use tie-breaking
bool tiebreak;
public:
/// Create and register test
ArgMaxBool(int n, int o, bool tb)
: Test("Arithmetic::ArgMaxBool::"+str(o)+"::"+str(tb)+"::"+str(n),
n+1,0,n+1,
false,tb ? Gecode::IPL_DEF : Gecode::IPL_DOM),
offset(o), tiebreak(tb) {}
/// %Test whether \a x is solution
virtual bool solution(const Assignment& x) const {
int n=x.size()-1;
if ((x[n] < offset) || (x[n] >= n + offset))
return false;
int m=x[0]; int p=0;
if (x[0] > 1)
return false;
for (int i=1; i<n; i++) {
if (x[i] > 1)
return false;
if (x[i] > m) {
p=i; m=x[i];
}
}
return tiebreak ? (p + offset == x[n]) : (m == x[x[n]-offset]);
}
/// Post constraint on \a x
virtual void post(Gecode::Space& home, Gecode::IntVarArray& x) {
int n=x.size()-1;
Gecode::BoolVarArgs m(n);
for (int i=0; i<n; i++)
m[i]=channel(home,x[i]);
Gecode::argmax(home, m, offset, x[n], tiebreak);
}
};

/// %Test for argument maximum constraint with shared variables
class ArgMaxBoolShared : public Test {
protected:
/// Whether to use tie-breaking
bool tiebreak;
public:
/// Create and register test
ArgMaxBoolShared(int n, bool tb)
: Test("Arithmetic::ArgMaxBool::Shared::"+str(tb)+"::"+str(n),n+1,0,n+1,
false),
tiebreak(tb) {
testfix=false;
}
/// %Test whether \a x is solution
virtual bool solution(const Assignment& x) const {
int n=x.size()-1;
if ((x[n] < 0) || (x[n] >= 2*n))
return false;
Gecode::IntArgs y(2*n);
for (int i=0; i<n; i++)
y[2*i+0]=y[2*i+1]=x[i];
int m=y[0]; int p=0;
if (y[0] > 1)
return false;
for (int i=1; i<2*n; i++) {
if (y[i] > 1)
return false;
if (y[i] > m) {
p=i; m=y[i];
}
}
return tiebreak ? (p == x[n]) : (m == y[x[n]]);
}
/// Post constraint on \a x
virtual void post(Gecode::Space& home, Gecode::IntVarArray& x) {
int n=x.size()-1;
Gecode::BoolVarArgs m(2*n);
for (int i=0; i<n; i++)
m[2*i+0]=m[2*i+1]=channel(home,x[i]);
Gecode::argmax(home, m, x[n], tiebreak);
}
};

/// %Test for argument minimum constraint
class ArgMinBool : public Test {
protected:
/// Which offset to use
int offset;
/// Whether to use tie-breaking
bool tiebreak;
public:
/// Create and register test
ArgMinBool(int n, int o, bool tb)
: Test("Arithmetic::ArgMinBool::"+str(o)+"::"+str(tb)+"::"+str(n),
n+1,0,n+1,
false,tb ? Gecode::IPL_DEF : Gecode::IPL_DOM),
offset(o), tiebreak(tb) {}
/// %Test whether \a x is solution
virtual bool solution(const Assignment& x) const {
int n=x.size()-1;
if ((x[n] < offset) || (x[n] >= n + offset))
return false;
int m=x[0]; int p=0;
if (x[0] < 0 || x[0] > 1)
return false;
for (int i=1; i<n; i++) {
if (x[i] < 0 || x[i] > 1)
return false;
if (x[i] < m) {
p=i; m=x[i];
}
}
return tiebreak ? (p+offset == x[n]) : (m == x[x[n]-offset]);
}
/// Post constraint on \a x
virtual void post(Gecode::Space& home, Gecode::IntVarArray& x) {
int n=x.size()-1;
Gecode::BoolVarArgs m(n);
for (int i=0; i<n; i++)
m[i]=channel(home,x[i]);
Gecode::argmin(home, m, offset, x[n], tiebreak);
}
};

/// %Test for argument minimum constraint with shared variables
class ArgMinBoolShared : public Test {
protected:
/// Whether to use tie-breaking
bool tiebreak;
public:
/// Create and register test
ArgMinBoolShared(int n, bool tb)
: Test("Arithmetic::ArgMinBool::Shared::"+str(tb)+"::"+str(n),n+1,0,n+1,
false),
tiebreak(tb) {
testfix=false;
}
/// %Test whether \a x is solution
virtual bool solution(const Assignment& x) const {
int n=x.size()-1;
if ((x[n] < 0) || (x[n] >= 2*n))
return false;
Gecode::IntArgs y(2*n);
for (int i=0; i<n; i++)
y[2*i+0]=y[2*i+1]=x[i];
int m=y[0]; int p=0;
if (y[0] > 1)
return false;
for (int i=1; i<2*n; i++) {
if (y[i] > 1)
return false;
if (y[i] < m) {
p=i; m=y[i];
}
}
return tiebreak ? (p == x[n]) : (m == y[x[n]]);
}
/// Post constraint on \a x
virtual void post(Gecode::Space& home, Gecode::IntVarArray& x) {
int n=x.size()-1;
Gecode::BoolVarArgs m(2*n);
for (int i=0; i<n; i++)
m[2*i+0]=m[2*i+1]=channel(home,x[i]);
Gecode::argmin(home, m, x[n], tiebreak);
}
};

/// Help class to create and register tests
class Create {
Expand Down Expand Up @@ -1069,6 +1235,19 @@ namespace Test { namespace Int {
(void) new ArgMin(i,0,false);
(void) new ArgMin(i,1,false);
(void) new ArgMinShared(i,false);

(void) new ArgMaxBool(i,0,true);
(void) new ArgMaxBool(i,1,true);
(void) new ArgMaxBoolShared(i,true);
(void) new ArgMinBool(i,0,true);
(void) new ArgMinBool(i,1,true);
(void) new ArgMinBoolShared(i,true);
(void) new ArgMaxBool(i,0,false);
(void) new ArgMaxBool(i,1,false);
(void) new ArgMaxBoolShared(i,false);
(void) new ArgMinBool(i,0,false);
(void) new ArgMinBool(i,1,false);
(void) new ArgMinBoolShared(i,false);
}
}
};
Expand Down

0 comments on commit daaaa67

Please sign in to comment.