module Gecode
class FreeBoolVar
# Initiates a boolean constraint with this variable or +var+.
def |(var)
Constraints::Bool::ExpressionNode.new(self, @model) | var
end
# Initiates a boolean constraint with this variable and +var+.
def &(var)
Constraints::Bool::ExpressionNode.new(self, @model) & var
end
# Initiates a boolean constraint with this variable exclusive or +var+.
def ^(var)
Constraints::Bool::ExpressionNode.new(self, @model) ^ var
end
# Initiates a boolean constraint with this variable implies +var+.
def implies(var)
Constraints::Bool::ExpressionNode.new(self, @model).implies var
end
end
# A module that gathers the classes and modules used in boolean constraints.
module Constraints::Bool
# Describes a boolean expression (following after must*).
class Expression #:nodoc:
def ==(expression, options = {})
@params.update Gecode::Constraints::Util.decode_options(options)
@model.add_constraint BooleanConstraint.new(@model,
@params.update(:rhs => expression))
end
alias_comparison_methods
# Constrains the boolean expression to imply the specified expression.
def imply(expression, options = {})
@params.update Gecode::Constraints::Util.decode_options(options)
@params.update(:lhs => @params[:lhs].implies(expression), :rhs => true)
@model.add_constraint BooleanConstraint.new(@model, @params)
end
# Constrains the boolean expression to be true.
def true
@params.update Gecode::Constraints::Util.decode_options({})
@model.add_constraint BooleanConstraint.new(@model,
@params.update(:rhs => true))
end
# Constrains the boolean expression to be false.
def false
@params[:negate] = !@params[:negate]
self.true
end
end
# Describes a constraint on a boolean expression.
#
# == Boolean expressions
#
# A boolean expression consists of several boolean variable with various
# boolean operators. The available operators are:
#
# [|] Or
# [&] And
# [^] Exclusive or
# [+implies+] Implication
#
# === Examples
#
# # +b1+ and +b2+
# b1 & b2
#
# # (+b1+ and +b2+) or +b3+
# (b1 & b1) | b3
#
# # (+b1+ and +b2+) or (+b3+ exclusive or +b1+)
# (b1 & b2) | (b3 ^ b1)
#
# # (+b1+ implies +b2+) and (+b3+ implies +b2+)
# (b1.implies b2) & (b3.implies b2)
#
# == Domain
#
# A domain constraint just specifies that a boolean expression must be true
# or false. Negation and reification are supported.
#
# === Examples
#
# # +b1+ and +b2+ must be true.
# (b1 & b2).must_be.true
#
# # (+b1+ implies +b2+) and (+b3+ implies +b2+) must be false.
# ((b1.implies b2) & (b3.implies b2)).must_be.false
#
# # +b1+ and +b2+ must be true. We reify it with +bool+ and select the
# # strength +domain+.
# (b1 & b2).must_be.true(:reify => bool, :strength => :domain)
#
# == Equality
#
# A constraint with equality specifies that two boolean expressions must be
# equal. Negation and reification are supported. Any of ==,
# +equal+ and +equal_to+ may be used for equality.
#
# === Examples
#
# # +b1+ and +b2+ must equal +b1+ or +b2+.
# (b1 & b2).must == (b1 | b2)
#
# # +b1+ and +b2+ must not equal +b3+. We reify it with +bool+ and select
# # the strength +domain+.
# (b1 & b2).must_not.equal(b3, :reify => bool, :select => :domain)
#
# == Implication
#
# A constraint using +imply+ specified that one boolean expression must
# imply the other. Negation and reification are supported.
#
# === Examples
#
# # +b1+ must imply +b2+
# b1.must.imply b2
#
# # +b1+ and +b2+ must not imply +b3+. We reify it with +bool+ and select
# # +domain+ as strength.
# (b1 & b2).must_not.imply b3
class BooleanConstraint < Gecode::Constraints::ReifiableConstraint
def post
lhs, rhs, negate, reif_var =
@params.values_at(:lhs, :rhs, :negate, :reif)
space = (lhs.model || rhs.model).active_space
# TODO: It should be possible to reduce the number of necessary
# variables and constraints a bit by altering the way that the top node
# is posted, using its constraint for reification etc when possible.
if rhs.respond_to? :bind
if reif_var.nil?
Gecode::Raw::rel(space, lhs.bind, Gecode::Raw::BOT_EQV, rhs.bind,
(!negate ? 1 : 0), *propagation_options)
else
if negate
Gecode::Raw::rel(space, lhs.bind, Gecode::Raw::BOT_XOR, rhs.bind,
reif_var.bind, *propagation_options)
else
Gecode::Raw::rel(space, lhs.bind, Gecode::Raw::BOT_EQV, rhs.bind,
reif_var.bind, *propagation_options)
end
end
else
should_hold = !negate & rhs
if reif_var.nil?
Gecode::Raw::MiniModel::BoolExpr.new(lhs.bind).post(space,
should_hold, *propagation_options)
else
Gecode::Raw::rel(space, lhs.bind, Gecode::Raw::BOT_EQV,
reif_var.bind, (should_hold ? 1 : 0), *propagation_options)
end
end
end
end
# A module containing the methods for the basic boolean operations. Depends
# on that the class mixing it in defined #model.
module OperationMethods #:nodoc
include Gecode::Constraints::LeftHandSideMethods
private
# Maps the names of the methods to the corresponding bool constraint in
# Gecode.
OPERATION_TYPES = {
:| => Gecode::Raw::BOT_OR,
:& => Gecode::Raw::BOT_AND,
:^ => Gecode::Raw::BOT_XOR,
:implies => Gecode::Raw::BOT_IMP
}
public
OPERATION_TYPES.each_pair do |name, operation|
module_eval <<-"end_code"
def #{name}(expression)
unless expression.kind_of? ExpressionTree
expression = ExpressionNode.new(expression)
end
ExpressionTree.new(self, expression) do |model, var1, var2|
new_var = model.bool_var
Gecode::Raw::rel(model.active_space, var1.bind, #{operation},
var2.bind, new_var.bind, Gecode::Raw::ICL_DEF,
Gecode::Raw::PK_DEF)
new_var
end
end
end_code
end
private
# Produces an expression for the lhs module.
def expression(params)
params.update(:lhs => self)
Gecode::Constraints::Bool::Expression.new(model, params)
end
end
# Describes a binary tree of expression nodes which together form a boolean
# expression.
class ExpressionTree #:nodoc:
include OperationMethods
# Constructs a new expression with the specified nodes. The proc should
# take a model followed by two variables and return a new variable.
def initialize(left_tree, right_tree, &block)
@left = left_tree
@right = right_tree
@bind_proc = block
end
# Returns a bound boolean variable representing the expression.
def bind
@bind_proc.call(model, @left, @right).bind
end
# Fetches the space that the expression's variables is in.
def model
@left.model || @right.model
end
end
# Describes a single node in a boolean expression.
class ExpressionNode #:nodoc:
include OperationMethods
attr :model
def initialize(value, model = nil)
@value = value
@model = model
end
# Returns a bound boolean variable representing the expression.
def bind
@value.bind
end
end
end
end