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 = {})
if expression.kind_of? Gecode::Constraints::Int::Linear::ExpressionTree
return expression.must == @params[:lhs]
end
unless expression.respond_to? :to_minimodel_bool_expr
expression = Constraints::Bool::ExpressionNode.new(expression, @model)
end
@params.update Gecode::Constraints::Util.decode_options(options)
@params.update(:lhs => @params[:lhs], :rhs => expression)
@model.add_constraint BooleanConstraint.new(@model, @params)
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(options = {})
@params.update Gecode::Constraints::Util.decode_options(options)
@model.add_constraint BooleanConstraint.new(@model,
@params.update(:rhs => true))
end
# Constrains the boolean expression to be false.
def false(options = {})
@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
if lhs.kind_of?(Gecode::FreeBoolVar)
lhs = Constraints::Bool::ExpressionNode.new(lhs, @model)
end
bot_eqv = Gecode::Raw::IRT_EQ
bot_xor = Gecode::Raw::IRT_NQ
if rhs.respond_to? :to_minimodel_bool_expr
if reif_var.nil?
tree = ExpressionTree.new(lhs,
Gecode::Raw::MiniModel::BoolExpr::NT_EQV, rhs)
tree.to_minimodel_bool_expr.post(space, !negate,
*propagation_options)
else
tree = ExpressionTree.new(lhs,
Gecode::Raw::MiniModel::BoolExpr::NT_EQV, rhs)
var = tree.to_minimodel_bool_expr.post(space, *propagation_options)
Gecode::Raw::rel(space, var, (negate ? bot_xor : bot_eqv),
reif_var.bind, *propagation_options)
end
else
should_hold = !negate & rhs
if reif_var.nil?
lhs.to_minimodel_bool_expr.post(space, should_hold,
*propagation_options)
else
var = lhs.to_minimodel_bool_expr.post(space, *propagation_options)
Gecode::Raw::rel(space, var,
(should_hold ? bot_eqv : bot_xor),
reif_var.bind, *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::MiniModel::BoolExpr::NT_OR,
:& => Gecode::Raw::MiniModel::BoolExpr::NT_AND,
:^ => Gecode::Raw::MiniModel::BoolExpr::NT_XOR,
:implies => Gecode::Raw::MiniModel::BoolExpr::NT_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, #{operation}, expression)
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 binary operation
# applied to the specified trees.
def initialize(left_tree, operation, right_tree)
@left = left_tree
@operation = operation
@right = right_tree
end
# Returns a MiniModel boolean expression representing the tree.
def to_minimodel_bool_expr
Gecode::Raw::MiniModel::BoolExpr.new(
@left.to_minimodel_bool_expr, @operation,
@right.to_minimodel_bool_expr)
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)
unless value.kind_of?(Gecode::FreeBoolVar)
raise TypeError, 'Invalid type used in boolean equation: ' +
"#{value.class}."
end
@value = value
@model = model
end
# Returns a MiniModel boolean expression representing the tree.
def to_minimodel_bool_expr
Gecode::Raw::MiniModel::BoolExpr.new(@value.bind)
end
end
end
end