lib/gecoder/interface/constraints/int/linear.rb in gecoder-0.8.3 vs lib/gecoder/interface/constraints/int/linear.rb in gecoder-0.9.0
- old
+ new
@@ -1,297 +1,143 @@
-module Gecode
- class FreeIntVar
- # Creates a linear expression where the int variables are summed.
- def +(var)
- Gecode::Constraints::Int::Linear::ExpressionNode.new(self,
- @model) + var
+module Gecode::Int
+ module IntOperand
+ # Produces a new IntOperand representing this operand plus
+ # +int_operand_or_fixnum+.
+ #
+ # ==== Examples
+ #
+ # # +int1+ plus +int2+
+ # int1 + int2
+ #
+ # # +int+ plus 17
+ # int + 17
+ def +(int_operand_or_fixnum)
+ int_linear_expression_operation(:+, int_operand_or_fixnum)
end
- alias_method :pre_linear_mult, :* if instance_methods.include? '*'
+ # Produces a new IntOperand representing this operand minus
+ # +int_operand_or_fixnum+.
+ #
+ # ==== Examples
+ #
+ # # +int1+ minus +int2+
+ # int1 - int2
+ #
+ # # +int+ minus 17
+ # int - 17
+ def -(int_operand_or_fixnum)
+ int_linear_expression_operation(:-, int_operand_or_fixnum)
+ end
- # Creates a linear expression where the int variable is multiplied with
- # a constant integer.
- def *(int)
- if int.kind_of? Fixnum
- Gecode::Constraints::Int::Linear::ExpressionNode.new(self,
- @model) * int
+ # Produces a new IntOperand representing this operand times a
+ # constant.
+ #
+ # ==== Examples
+ #
+ # # +int+ times 17
+ # int * 17
+ def *(fixnum)
+ if fixnum.kind_of? Fixnum
+ int_linear_expression_operation(:*, fixnum)
else
- pre_linear_mult(int) if respond_to? :pre_linear_mult
+ raise TypeError, "Expected fixnum, got #{fixnum.class}."
end
end
-
- # Creates a linear expression where the specified variable is subtracted
- # from this one.
- def -(var)
- Gecode::Constraints::Int::Linear::ExpressionNode.new(self,
- @model) - var
- end
- end
-
- module Constraints::Int
- class Expression #:nodoc:
- # Add some relation selection based on whether the expression is negated.
- alias_method :pre_linear_initialize, :initialize
- def initialize(model, params)
- pre_linear_initialize(model, params)
- unless params[:negate]
- @method_relations = Constraints::Util::RELATION_TYPES
- else
- @method_relations = Constraints::Util::NEGATED_RELATION_TYPES
- end
+
+ private
+
+ # Performs the int linear expression operation +operator+ on self
+ # and +operand2+.
+ def int_linear_expression_operation(operator, operand2)
+ unless operand2.respond_to? :to_minimodel_lin_exp
+ operand2 = Linear::ExpressionNode.new operand2
end
-
- # Define the relation methods.
- Constraints::Util::RELATION_TYPES.each_key do |name|
- module_eval <<-"end_code"
- def #{name}(expression, options = {})
- relation = @method_relations[:#{name}]
- @params.update(
- Gecode::Constraints::Util.decode_options(options))
- if self.simple_expression? and simple_expression?(expression)
- # A relation constraint is enough.
- add_relation_constraint(relation, expression)
- else
- add_linear_constraint(relation, expression)
- end
- end
- end_code
+ operand1 = self
+ unless operand1.respond_to? :to_minimodel_lin_exp
+ operand1 = Linear::ExpressionNode.new(self, @model)
end
- alias_comparison_methods
-
- protected
-
- # Checks whether the given expression is simple enough to be used in a
- # simple relation constraint. Returns true if it is, false otherwise. If
- # no expression is given then the this expression's left hand side is
- # checked.
- def simple_expression?(expression = nil)
- if expression.nil?
- simple_expression?(@params[:lhs])
- else
- expression.kind_of?(Gecode::FreeIntVar) or
- expression.kind_of?(Gecode::FreeBoolVar) or
- expression.kind_of?(Fixnum)
- end
- end
-
- private
-
- # Places the linear constraint corresponding to the specified (integer)
- # relation type (as specified by Gecode) in relation to the specifed
- # expression.
- #
- # Raises TypeError if the element is of a type that doesn't allow a
- # relation to be specified.
- def add_linear_constraint(relation_type, right_hand_side)
- # Bind parameters.
- lhs = @params[:lhs]
- if lhs.kind_of?(Gecode::FreeIntVar) or lhs.kind_of?(Gecode::FreeBoolVar)
- lhs = lhs * 1 # Convert to Gecode::Raw::LinExp
- end
- if not (right_hand_side.respond_to? :to_minimodel_lin_exp or
- right_hand_side.kind_of?(Gecode::FreeIntVar) or
- right_hand_side.kind_of?(Gecode::FreeBoolVar) or
- right_hand_side.kind_of?(Fixnum))
- raise TypeError, 'Invalid right hand side of linear equation.'
- end
-
- @params.update(:relation_type => relation_type, :lhs => lhs,
- :rhs => right_hand_side)
- @model.add_constraint Linear::LinearConstraint.new(@model, @params)
- end
-
- # Places the relation constraint corresponding to the specified (integer)
- # relation type (as specified by Gecode) in relation to the specifed
- # element.
- def add_relation_constraint(relation_type, element)
- @model.add_constraint Linear::SimpleRelationConstraint.new(@model,
- @params.update(:relation_type => relation_type, :element => element))
- end
+ Linear::ExpressionTree.new(operand1, operand2, operator)
end
end
-
+
# A module that gathers the classes and modules used in linear constraints.
- module Constraints::Int::Linear #:nodoc:
- # Linear constraints specify that an integer variable must have a linear
- # equation containing variables must hold. The same relations and options
- # used in +SimpleRelationConstraint+ can also be used for linear
- # constraints.
- #
- # Boolean variables can also be used instead of integer variables. In that
- # case a boolean variable assigned true is equal to 1 and a boolean variable
- # assigned false is equal to 0. There is one exception: boolean variables
- # can not be used alone as left hand side.
- #
- # Do not mix boolean and integer variables. Even if possible it's not
- # supported, and might be removed in the future.
- #
- # == Examples
- #
- # # The sum of the int variables +x+ and +y+ must equal +z+ + 3.
- # (x + y).must == z + 3
- #
- # # Another way of writing the above.
- # z.must == x + y - 3
- #
- # # The inequality 10(x + y) > 3x must not hold.
- # (x + y)*10.must_not > x*3
- #
- # # Specifies the above, but reifies the constraint with the boolean
- # # variable +bool+ and gives it propagation strength +domain+.
- # (x + y)*10.must_not_be.greater_than(x*3, :reify => bool, :strength => :domain)
- class LinearConstraint < Gecode::Constraints::ReifiableConstraint
+ module Linear #:nodoc:
+ class LinearRelationConstraint < Gecode::ReifiableConstraint #:nodoc:
def post
lhs, rhs, relation_type, reif_var =
@params.values_at(:lhs, :rhs, :relation_type, :reif)
- reif_var = reif_var.bind if reif_var.respond_to? :bind
- if rhs.respond_to? :to_minimodel_lin_exp
- rhs = rhs.to_minimodel_lin_exp
- elsif rhs.respond_to? :bind
- rhs = rhs.bind * 1
- end
-
- final_exp = (lhs.to_minimodel_lin_exp - rhs)
+ reif_var = reif_var.to_bool_var.bind if reif_var.respond_to? :to_bool_var
+ final_exp = (lhs.to_minimodel_lin_exp - rhs.to_minimodel_lin_exp)
if reif_var.nil?
final_exp.post(@model.active_space, relation_type,
*propagation_options)
else
final_exp.post(@model.active_space, relation_type, reif_var,
*propagation_options)
end
end
end
- # Simple relation constraints specify that an integer variable must have a
- # specified relation to a constant integer or another integer variable. The
- # following relations are supported (the aliases of each relation are also
- # listed).
- #
- # * <, lesser, lesser_than
- # * >, greater, greater_than
- # * >=, greater_or_equal, greater_than_or_equal_to
- # * <=, less_or_equal, less_than_or_equal_to
- # * ==, equal, equal_to
- #
- # Each can be negated by using +must_not+ instead of +must+.
- #
- # Two options (given as a hash) are available:
- #
- # [strength] Specifies the propagation strength of the constraint. Must be
- # one of +value+, +bounds+, +domain+ and +default+. The
- # strength generally progresses as +value+ -> +bounds+ ->
- # +domain+ (+value+ being the weakest, but usually cheapest,
- # while +domain+ is the strongest but usually costly).
- # [reify] Specifies a boolean variable that should be used for
- # reification (see +ReifiableConstraint+).
- #
- # == Examples
- #
- # # Int variable +x+ must not equal 0.
- # x.must_not.equal(0)
- #
- # # Another way of writing the above.
- # x.must_not == 0
- #
- # # +x+ must be strictly larger than +y+.
- # x.must > y
- #
- # # Specifies the above, but reifies the constraint with the boolean
- # # variable +bool+.
- # x.must_be.greater_than(y, :reify => bool)
- class SimpleRelationConstraint < Gecode::Constraints::ReifiableConstraint
- def post
- # Fetch the parameters to Gecode.
- lhs, relation, rhs, reif_var =
- @params.values_at(:lhs, :relation_type, :element, :reif)
-
- rhs = rhs.bind if rhs.respond_to? :bind
- if reif_var.nil?
- Gecode::Raw::rel(@model.active_space, lhs.bind, relation, rhs,
- *propagation_options)
- else
- Gecode::Raw::rel(@model.active_space, lhs.bind, relation, rhs,
- reif_var.bind, *propagation_options)
- end
- end
- end
-
- # Helper methods for linear expressions. Classes mixing in this module must
- # have a method #model which gives the model the expression is operating in.
- module Helper #:nodoc:
- include Gecode::Constraints::LeftHandSideMethods
-
- private
-
- OPERATION_TYPES = [:+, :-, :*]
-
- public
-
- # Define methods for the available operations.
- OPERATION_TYPES.each do |name|
- module_eval <<-"end_code"
- def #{name}(expression)
- unless expression.kind_of? ExpressionTree
- expression = ExpressionNode.new(expression)
- end
- ExpressionTree.new(self, expression, :#{name})
- end
- end_code
- end
-
- private
-
- # Produces an expression for the lhs module.
- def expression(params)
- params.update(:lhs => self)
- Gecode::Constraints::Int::Expression.new(model, params)
- end
- end
-
# Describes a binary tree of expression nodes which together form a linear
# expression.
- class ExpressionTree #:nodoc:
- include Helper
-
- # Constructs a new expression with the specified variable
+ class ExpressionTree < Gecode::Int::ShortCircuitRelationsOperand #:nodoc:
+ attr :model
+
+ # Constructs a new expression with the specified operands.
def initialize(left_node, right_node, operation)
+ super(left_node.model || right_node.model)
@left = left_node
@right = right_node
@operation = operation
+ @model = @left.model || @right.model
end
# Converts the linear expression to an instance of
# Gecode::Raw::MiniModel::LinExpr
def to_minimodel_lin_exp
@left.to_minimodel_lin_exp.send(@operation, @right.to_minimodel_lin_exp)
end
-
- # Fetches the space that the expression's variables is in.
- def model
- @left.model || @right.model
+
+ def relation_constraint(relation, int_operand_or_fix, params)
+ unless params[:negate]
+ relation_type =
+ Gecode::Util::RELATION_TYPES[relation]
+ else
+ relation_type =
+ Gecode::Util::NEGATED_RELATION_TYPES[relation]
+ end
+
+ unless int_operand_or_fix.respond_to? :to_minimodel_lin_exp
+ int_operand_or_fix = Linear::ExpressionNode.new(int_operand_or_fix);
+ end
+
+ params.update(:lhs => self, :rhs => int_operand_or_fix,
+ :relation_type => relation_type)
+ LinearRelationConstraint.new(model, params)
end
end
# Describes a single node in a linear expression.
class ExpressionNode #:nodoc:
- include Helper
-
attr :model
def initialize(value, model = nil)
+ unless value.respond_to?(:to_int_var) or value.kind_of?(Fixnum)
+ raise TypeError, 'Expected int operand or fixnum, ' +
+ "got #{value.class}."
+ end
@value = value
@model = model
end
# Converts the linear expression to an instance of
# Gecode::Raw::MiniModel::LinExpr
def to_minimodel_lin_exp
expression = @value
- if expression.respond_to? :bind
- # Minimodel requires that we do this first.
- expression = expression.bind * 1
+ if expression.respond_to? :to_int_var
+ expression = expression.to_int_var.bind * 1
end
expression
end
end
end
-end
\ No newline at end of file
+end