Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog.in
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ Date: unreleased
[DESCRIPTION]
This is the development changelog for the next Gecode release.

[ENTRY]
Module: minimodel
What: new
Rank: minor
Issue: 145
[DESCRIPTION]
Allow Boolean expressions to be initialized from C++ Boolean constants.

[ENTRY]
Module: other
What: bug
Expand Down
22 changes: 20 additions & 2 deletions gecode/minimodel.hh
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#endif

#include <iostream>
#include <type_traits>

/*
* Support for DLLs under Windows
Expand Down Expand Up @@ -1361,7 +1362,8 @@ namespace Gecode {
NT_RLIN, ///< Reified linear relation
NT_RLINFLOAT, ///< Reified linear relation
NT_RSET, ///< Reified set relation
NT_MISC ///< Other Boolean expression
NT_MISC, ///< Other Boolean expression
NT_CONST ///< Boolean constant
};
/// Miscealloneous Boolean expressions
class GECODE_VTABLE_EXPORT Misc : public HeapAllocated {
Expand All @@ -1382,6 +1384,9 @@ namespace Gecode {
private:
/// Pointer to node for expression
Node* n;
/// Create a node storing a Boolean constant
GECODE_MINIMODEL_EXPORT
static Node* constant(bool value);
/// Construct an expression, optionally treating an empty operand as an accumulator
BoolExpr(const BoolExpr& l, NodeType t, const BoolExpr& r,
bool accumulator);
Expand All @@ -1404,6 +1409,14 @@ namespace Gecode {
/// Construct expression for type and subexpresssions
GECODE_MINIMODEL_EXPORT
BoolExpr(const BoolExpr& l, NodeType t, const BoolExpr& r);
/** Construct expression for a Boolean constant
*
* Only bool arguments are accepted, so integer expressions do not
* become ambiguous through an implicit conversion to bool.
*/
template<class Bool,
std::enable_if_t<std::is_same<Bool,bool>::value,int> = 0>
BoolExpr(Bool value);
/// Construct expression for variable
GECODE_MINIMODEL_EXPORT
BoolExpr(const BoolVar& x);
Expand Down Expand Up @@ -1447,7 +1460,7 @@ namespace Gecode {
* \defgroup TaskModelMiniModelBool Boolean expressions
*
* Boolean expressions can be freely composed of variables with
* the usual connectives and reified linear expressions.
* the usual connectives, Boolean constants, and reified linear expressions.
*
* \ingroup TaskModelMiniModel
*/
Expand Down Expand Up @@ -1644,6 +1657,11 @@ namespace Gecode {
GECODE_MINIMODEL_EXPORT BoolVar
expr(Home home, const BoolExpr& e,
const IntPropLevels& ipls=IntPropLevels::def);
/// Preserve integer-expression posting for C++ Boolean values
template<class Bool,
std::enable_if_t<std::is_same<Bool,bool>::value,int> = 0>
IntVar expr(Home home, Bool value,
const IntPropLevels& ipls=IntPropLevels::def);
/// Post Boolean relation
GECODE_MINIMODEL_EXPORT void
rel(Home home, const BoolExpr& e,
Expand Down
20 changes: 20 additions & 0 deletions gecode/minimodel/bool-expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ namespace Gecode {
int same;
/// Type of expression
NodeType t;
/// Possibly a Boolean constant
bool value;
/// Subexpressions
Node *l, *r;
/// Possibly a variable
Expand Down Expand Up @@ -120,6 +122,15 @@ namespace Gecode {
return false;
}

BoolExpr::Node*
BoolExpr::constant(bool value) {
Node* n = new Node;
n->same = 1;
n->t = NT_CONST;
n->value = value;
return n;
}

BoolExpr::BoolExpr(void) : n(new Node) {}

BoolExpr::BoolExpr(const BoolExpr& e) : n(e.n) {
Expand Down Expand Up @@ -321,6 +332,10 @@ namespace Gecode {
NNF::expr(Home home, const IntPropLevels& ipls) const {
if ((t == BoolExpr::NT_VAR) && !u.a.neg)
return u.a.x->x;
if (t == BoolExpr::NT_CONST) {
bool value = u.a.x->value != u.a.neg;
return BoolVar(home,value,value);
}
BoolVar b(home,0,1);
switch (t) {
case BoolExpr::NT_VAR:
Expand Down Expand Up @@ -444,6 +459,10 @@ namespace Gecode {
void
NNF::rel(Home home, const IntPropLevels& ipls) const {
switch (t) {
case BoolExpr::NT_CONST:
if (u.a.x->value == u.a.neg)
home.fail();
break;
case BoolExpr::NT_VAR:
Gecode::rel(home, u.a.x->x, IRT_EQ, u.a.neg ? 0 : 1);
break;
Expand Down Expand Up @@ -539,6 +558,7 @@ namespace Gecode {
if (n->same == 0)
throw MiniModel::TooFewArguments("BoolExpr");
switch (n->t) {
case BoolExpr::NT_CONST:
case BoolExpr::NT_VAR:
case BoolExpr::NT_RLIN:
case BoolExpr::NT_MISC:
Expand Down
13 changes: 13 additions & 0 deletions gecode/minimodel/bool-expr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ namespace Gecode {
*
*/

template<class Bool,
std::enable_if_t<std::is_same<Bool,bool>::value,int>>
forceinline
BoolExpr::BoolExpr(Bool value)
: n(constant(value)) {}

template<class Bool,
std::enable_if_t<std::is_same<Bool,bool>::value,int>>
forceinline IntVar
expr(Home home, Bool value, const IntPropLevels& ipls) {
return expr(home,LinIntExpr(static_cast<int>(value)),ipls);
}

forceinline
BoolExpr::Misc::Misc(void) {}

Expand Down
89 changes: 88 additions & 1 deletion test/int/mm-bool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,83 @@ namespace Test { namespace Int {
}
};

static_assert(std::is_convertible<bool,Gecode::BoolExpr>::value,
"Boolean constants must convert to Boolean expressions");
static_assert(!std::is_convertible<int,Gecode::BoolExpr>::value,
"Integer constants must remain integer expressions");
static_assert(!std::is_convertible<void*,Gecode::BoolExpr>::value,
"Pointers must not convert to Boolean expressions");

static_assert(std::is_same<decltype(Gecode::expr(
std::declval<Gecode::Home>(),true)),Gecode::IntVar>::value &&
std::is_same<decltype(Gecode::expr(
std::declval<Gecode::Home>(),0)),Gecode::IntVar>::value,
"Numeric posting overloads must keep their return type");

/// %Test Boolean constants and their composition with variables
class BoolExprConstant : public Test {
protected:
/// Value of constant
bool value;
/// Expression to test
int mode;
public:
/// Create and register test
BoolExprConstant(bool value0, int mode0)
: Test(TestTags::check(),"MiniModel::BoolExpr::Constant::"+
str(value0)+"::"+str(mode0),3,0,1),
value(value0), mode(mode0) {}
/// %Test whether an assignment is a solution
virtual bool solution(const Assignment& x) const {
switch (mode) {
case 0: return x[2] == value;
case 1: return x[2] == !value;
case 2: return x[2] == (value && (x[0] != 0));
case 3: return x[2] == ((x[0] != 0) || value);
case 4: return value;
case 5: return x[2] == (!value && (x[0] != 0) && (x[1] != 0));
case 6: return x[2] == ((x[0] != 0) || !value || (x[1] != 0));
case 7: return x[2] == (value == (x[0] != 0));
case 8: return (x[0] != x[1]) == value;
case 9: return x[2] == (!value || (x[0] != 0));
case 10: return x[2] == ((x[0] == 0) || value);
case 11: return !value;
case 12: return x[2] == value;
default: GECODE_NEVER;
}
}
/// Post constraint on \a x
virtual void post(Gecode::Space& home, Gecode::IntVarArray& x) {
using namespace Gecode;
BoolExpr e = value;
switch (mode) {
case 0: break;
case 1: e = !e; break;
case 2: e = e && channel(home,x[0]); break;
case 3: e = channel(home,x[0]) || e; break;
case 4: rel(home,e); return;
case 5: e = (!e && channel(home,x[0])) && channel(home,x[1]); break;
case 6: e = (channel(home,x[0]) || !e) || channel(home,x[1]); break;
case 7: e = e == channel(home,x[0]); break;
case 8: rel(home,(x[0] != x[1]) == e); return;
case 9: e = e >> channel(home,x[0]); break;
case 10: e = channel(home,x[0]) >> e; break;
case 11: rel(home,!e); return;
case 12:
{
BoolExpr empty;
e = empty && e;
e = e && empty;
e = empty || e;
e = e || empty;
}
break;
default: GECODE_NEVER;
}
rel(home, expr(home,e), IRT_EQ, channel(home,x[2]));
}
};

/// %Test Boolean expressions with default-constructed accumulators
class BoolExprDefaultAccumulator : public Test {
protected:
Expand Down Expand Up @@ -212,7 +289,9 @@ namespace Test { namespace Int {
BE_NOT_OR,
BE_XOR_EMPTY,
BE_EQV_EMPTY,
BE_NEQ_EMPTY
BE_NEQ_EMPTY,
BE_CONST_IMP_EMPTY,
BE_EMPTY_EQV_CONST
};

/// %Test invalid Boolean expressions
Expand Down Expand Up @@ -246,6 +325,8 @@ namespace Test { namespace Int {
case BE_XOR_EMPTY: e = empty ^ v; break;
case BE_EQV_EMPTY: e = empty == v; break;
case BE_NEQ_EMPTY: e = empty != v; break;
case BE_CONST_IMP_EMPTY: e = false >> empty; break;
case BE_EMPTY_EQV_CONST: e = empty == true; break;
default: GECODE_NEVER;
}
try {
Expand Down Expand Up @@ -4476,6 +4557,10 @@ namespace Test { namespace Int {
(void) new BoolElement("Expr::A",0);
(void) new BoolElement("Expr::B",1);
(void) new BoolElement("Rel",2);
for (int mode=0; mode<13; mode++) {
(void) new BoolExprConstant(false,mode);
(void) new BoolExprConstant(true,mode);
}
(void) new BoolExprDefaultAccumulator(BO_AND,false);
(void) new BoolExprDefaultAccumulator(BO_AND,true);
(void) new BoolExprDefaultAccumulator(BO_OR,false);
Expand All @@ -4491,6 +4576,8 @@ namespace Test { namespace Int {
(void) new BoolExprInvalid(BE_XOR_EMPTY,"Xor");
(void) new BoolExprInvalid(BE_EQV_EMPTY,"Eqv");
(void) new BoolExprInvalid(BE_NEQ_EMPTY,"Neq");
(void) new BoolExprInvalid(BE_CONST_IMP_EMPTY,"FalseImpEmpty");
(void) new BoolExprInvalid(BE_EMPTY_EQV_CONST,"EmptyEqvTrue");
}
};

Expand Down
Loading