gecoder 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -95,7 +95,7 @@ module Gecode
95
95
  end
96
96
 
97
97
  # Add the branching.
98
- Gecode::Raw.branch(active_space, variables.to_int_var_array,
98
+ Gecode::Raw.branch(active_space, variables.to_var_array,
99
99
  BRANCH_VAR_CONSTANTS[var_strat], BRANCH_VALUE_CONSTANTS[val_strat])
100
100
  end
101
101
  end
@@ -1,10 +1,126 @@
1
- require 'gecoder/interface/constraints/relation'
2
- require 'gecoder/interface/constraints/distinct'
3
- require 'gecoder/interface/constraints/linear'
4
-
5
1
  module Gecode
6
2
  # An error signaling that the constraint specified is missing (e.g. one tried
7
3
  # to negate a constraint, but no negated form is implemented).
8
4
  class MissingConstraintError < StandardError
9
5
  end
10
- end
6
+
7
+ # A module containing all the constraints.
8
+ module Constraints
9
+ # A module that should be mixed in to class of objects that should be usable
10
+ # as left hand sides (i.e. the part before must*) when specifying
11
+ # constraints. Assumes that a method #expression is defined which produces
12
+ # a new expression given the current constraint parameters.
13
+ module LeftHandSideMethods
14
+ # Specifies that a constraint must hold for the integer variable enum.
15
+ def must
16
+ expression update_params(:negate => false)
17
+ end
18
+ alias_method :must_be, :must
19
+
20
+ # Specifies that the negation of a constraint must hold for the integer
21
+ # variable.
22
+ def must_not
23
+ expression update_params(:negate => true)
24
+ end
25
+ alias_method :must_not_be, :must_not
26
+
27
+ private
28
+
29
+ # Updates the parameters with the specified new parameters.
30
+ def update_params(params_to_add)
31
+ @constraint_params ||= {}
32
+ @constraint_params.update(params_to_add)
33
+ end
34
+ end
35
+
36
+ # Describes a constraint expressions. An expression is produced by calling
37
+ # some form of must on a left hand side. The expression waits for a right
38
+ # hand side so that it can post the corresponding constraint.
39
+ class Expression
40
+ # Constructs a new expression with the specified parameters. The
41
+ # parameters shoud at least contain the keys :lhs, and :negate.
42
+ #
43
+ # Raises ArgumentError if any of those keys are missing.
44
+ def initialize(model, params)
45
+ unless params.has_key?(:lhs) and params.has_key?(:negate)
46
+ raise ArgumentError, 'Expression requires at least :lhs, ' +
47
+ "and :negate as parameter keys, got #{params.keys.join(', ')}."
48
+ end
49
+
50
+ @model = model
51
+ @params = params
52
+ end
53
+ end
54
+
55
+ # Base class for all constraints.
56
+ class Constraint
57
+ # Creates a constraint with the specified parameters, bound to the
58
+ # specified model.
59
+ def initialize(model, params)
60
+ @model = model
61
+ @params = params.clone
62
+ end
63
+
64
+ # Posts the constraint, adding it to the model. This is an abstract
65
+ # method and should be overridden by all sub-classes.
66
+ def post
67
+ raise NoMethodError, 'Abstract method has not been implemented.'
68
+ end
69
+ end
70
+
71
+ # A module that provides some utility-methods for decoding options given to
72
+ # constraints.
73
+ module OptionUtil
74
+ private
75
+
76
+ # Maps the name used in options to the value used in Gecode for
77
+ # propagation strengths.
78
+ PROPAGATION_STRENGTHS = {
79
+ :default => Gecode::Raw::ICL_DEF,
80
+ :value => Gecode::Raw::ICL_VAL,
81
+ :bounds => Gecode::Raw::ICL_BND,
82
+ :domain => Gecode::Raw::ICL_DOM
83
+ }
84
+
85
+ public
86
+
87
+ module_function
88
+
89
+ # Decodes the common options to constraints: strength and reification.
90
+ # Returns a hash with up to two values. :strength is the strength that
91
+ # should be used for the constraint and :reif is the (bound) boolean
92
+ # variable that should be used for reification. The decoded options are
93
+ # removed from the hash (so in general the hash will be consumed in the
94
+ # process).
95
+ #
96
+ # Raises ArgumentError if an unrecognized option is found in the specified
97
+ # hash. Or if an unrecognized strength is given. Raises TypeError if the
98
+ # reification variable is not a boolean variable.
99
+ def decode_options(options)
100
+ # Propagation strength.
101
+ strength = options.delete(:strength) || :default
102
+ unless PROPAGATION_STRENGTHS.include? strength
103
+ raise ArgumentError, "Unrecognized propagation strength #{strength}."
104
+ end
105
+
106
+ # Reification.
107
+ reif_var = options.delete(:reify)
108
+ unless reif_var.nil? or reif_var.kind_of? FreeBoolVar
109
+ raise TypeError, 'Only boolean variables may be used for reification.'
110
+ end
111
+
112
+ # Check for unrecognized options.
113
+ unless options.empty?
114
+ raise ArgumentError, 'Unrecognized constraint option: ' +
115
+ options.keys.first.to_s
116
+ end
117
+ return {:strength => PROPAGATION_STRENGTHS[strength], :reif => reif_var}
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ require 'gecoder/interface/constraints/reifiable_constraints'
124
+ require 'gecoder/interface/constraints/int_var_constraints'
125
+ require 'gecoder/interface/constraints/int_enum_constraints'
126
+ require 'gecoder/interface/constraints/bool_var_constraints'
@@ -0,0 +1,160 @@
1
+ module Gecode
2
+ class FreeBoolVar
3
+ def |(var)
4
+ Constraints::Bool::ExpressionNode.new(self, @model) | var
5
+ end
6
+
7
+ def &(var)
8
+ Constraints::Bool::ExpressionNode.new(self, @model) & var
9
+ end
10
+ end
11
+
12
+ module Constraints::Bool
13
+ # Add some relation selection based on whether the expression is negated.
14
+ alias_method :pre_bool_rel_initialize, :initialize
15
+ class Expression
16
+ def ==(expression)
17
+ add_boolean_constraint(expression)
18
+ end
19
+ alias_method :equal, :==
20
+ alias_method :equal_to, :==
21
+
22
+ def true
23
+ # Bind parameters.
24
+ lhs = @params[:lhs]
25
+ unless lhs.respond_to? :to_minimodel_lin_exp
26
+ lhs = ExpressionNode.new(lhs, @model)
27
+ end
28
+
29
+ @model.add_constraint BooleanConstraint.new(@model,
30
+ @params.update(:expression => lhs.to_minimodel_lin_exp))
31
+ end
32
+
33
+ def false
34
+ # Bind parameters.
35
+ lhs = @params[:lhs]
36
+ unless lhs.respond_to? :to_minimodel_lin_exp
37
+ lhs = ExpressionNode.new(lhs, @model)
38
+ end
39
+
40
+ @params.update(:expression => Gecode::Raw::MiniModel::BoolExpr.new(
41
+ lhs.to_minimodel_lin_exp, Gecode::Raw::MiniModel::BoolExpr::BT_NOT))
42
+ @model.add_constraint BooleanConstraint.new(@model, @params)
43
+ end
44
+
45
+ private
46
+
47
+ # Adds the boolean constraint corresponding to equivalence between the
48
+ # left and right hand sides.
49
+ #
50
+ # Raises TypeError if the element is of a type that doesn't allow a
51
+ # relation to be specified.
52
+ def add_boolean_constraint(right_hand_side = nil)
53
+ # Bind parameters.
54
+ lhs = @params[:lhs]
55
+ unless lhs.respond_to? :to_minimodel_lin_exp
56
+ lhs = ExpressionNode.new(lhs, @model)
57
+ end
58
+ unless right_hand_side.respond_to? :to_minimodel_lin_exp
59
+ right_hand_side = ExpressionNode.new(right_hand_side, @model)
60
+ end
61
+
62
+ expression = ExpressionTree.new(lhs, right_hand_side,
63
+ Gecode::Raw::MiniModel::BoolExpr::BT_EQV)
64
+ @model.add_constraint BooleanConstraint.new(@model,
65
+ @params.update(:expression => expression.to_minimodel_lin_exp))
66
+ end
67
+ end
68
+
69
+ # Describes a boolean constraint.
70
+ class BooleanConstraint < Gecode::Constraints::ReifiableConstraint
71
+ def post
72
+ unless @params[:reif].nil?
73
+ @params[:expression] = Gecode::Raw::MiniModel::BoolExpr.new(
74
+ @params[:expression], Gecode::Raw::MiniModel::BoolExpr::BT_EQV,
75
+ Gecode::Raw::MiniModel::BoolExpr.new(@params[:reif].bind))
76
+ end
77
+ @params[:expression].post(@model.active_space, !@params[:negate])
78
+ end
79
+ end
80
+
81
+ # A module containing the methods for the basic boolean operations. Depends
82
+ # on that the class mixing it in defined #model.
83
+ module OperationMethods
84
+ include Gecode::Constraints::LeftHandSideMethods
85
+
86
+ private
87
+
88
+ # Maps the names of the methods to the corresponding bool operation type
89
+ # in Gecode.
90
+ OPERATION_TYPES = {
91
+ :| => Gecode::Raw::MiniModel::BoolExpr::BT_OR,
92
+ :& => Gecode::Raw::MiniModel::BoolExpr::BT_AND
93
+ }
94
+
95
+ public
96
+
97
+ OPERATION_TYPES.each_pair do |name, operation|
98
+ module_eval <<-"end_code"
99
+ def #{name}(expression)
100
+ unless expression.kind_of? ExpressionTree
101
+ expression = ExpressionNode.new(expression)
102
+ end
103
+ ExpressionTree.new(self, expression, #{operation})
104
+ end
105
+ end_code
106
+ end
107
+
108
+ private
109
+
110
+ # Produces an expression for the lhs module.
111
+ def expression(params)
112
+ params.update(:lhs => self)
113
+ Gecode::Constraints::Bool::Expression.new(model, params)
114
+ end
115
+ end
116
+
117
+ # Describes a binary tree of expression nodes which together form a boolean
118
+ # expression.
119
+ class ExpressionTree
120
+ include OperationMethods
121
+
122
+ # Constructs a new expression with the specified variable
123
+ def initialize(left_node, right_node, operation)
124
+ @left = left_node
125
+ @right = right_node
126
+ @operation = operation
127
+ end
128
+
129
+ # Converts the boolean expression to an instance of
130
+ # Gecode::Raw::MiniModel::BoolExpr
131
+ def to_minimodel_lin_exp
132
+ Gecode::Raw::MiniModel::BoolExpr.new(@left.to_minimodel_lin_exp,
133
+ @operation, @right.to_minimodel_lin_exp)
134
+ end
135
+
136
+ # Fetches the space that the expression's variables is in.
137
+ def model
138
+ @left.model || @right.model
139
+ end
140
+ end
141
+
142
+ # Describes a single node in a boolean expression.
143
+ class ExpressionNode
144
+ include OperationMethods
145
+
146
+ attr :model
147
+
148
+ def initialize(value, model = nil)
149
+ @value = value
150
+ @model = model
151
+ end
152
+
153
+ # Converts the linear expression to an instance of
154
+ # Gecode::Raw::MiniModel::BoolExpr
155
+ def to_minimodel_lin_exp
156
+ Gecode::Raw::MiniModel::BoolExpr.new(@value.bind)
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,23 @@
1
+ module Gecode
2
+ class FreeBoolVar
3
+ include Gecode::Constraints::LeftHandSideMethods
4
+
5
+ private
6
+
7
+ # Produces an expression for the lhs module.
8
+ def expression(params)
9
+ params.update(:lhs => self)
10
+ Constraints::Bool::Expression.new(@model, params)
11
+ end
12
+ end
13
+
14
+ # A module containing constraints that have int variables as left hand side
15
+ # (but not enumerations).
16
+ module Constraints::Bool
17
+ # Describes a boolean expression.
18
+ class Expression < Gecode::Constraints::Expression
19
+ end
20
+ end
21
+ end
22
+
23
+ require 'gecoder/interface/constraints/bool/boolean'
@@ -0,0 +1,258 @@
1
+ module Gecode
2
+ class FreeIntVar
3
+ # Creates a linear expression where the int variables are summed.
4
+ def +(var)
5
+ Gecode::Constraints::Int::Linear::ExpressionNode.new(self,
6
+ @model) + var
7
+ end
8
+
9
+ # Creates a linear expression where the int variable is multiplied with
10
+ # a constant integer.
11
+ def *(int)
12
+ Gecode::Constraints::Int::Linear::ExpressionNode.new(self,
13
+ @model) * int
14
+ end
15
+
16
+ # Creates a linear expression where the specified variable is subtracted
17
+ # from this one.
18
+ def -(var)
19
+ Gecode::Constraints::Int::Linear::ExpressionNode.new(self,
20
+ @model) - var
21
+ end
22
+ end
23
+
24
+ module Constraints::Int
25
+ class Expression
26
+ private
27
+
28
+ # Maps the names of the methods to the corresponding integer relation
29
+ # type in Gecode.
30
+ RELATION_TYPES = {
31
+ :== => Gecode::Raw::IRT_EQ,
32
+ :<= => Gecode::Raw::IRT_LQ,
33
+ :< => Gecode::Raw::IRT_LE,
34
+ :>= => Gecode::Raw::IRT_GQ,
35
+ :> => Gecode::Raw::IRT_GR }
36
+ # The same as above, but negated.
37
+ NEGATED_RELATION_TYPES = {
38
+ :== => Gecode::Raw::IRT_NQ,
39
+ :<= => Gecode::Raw::IRT_GR,
40
+ :< => Gecode::Raw::IRT_GQ,
41
+ :>= => Gecode::Raw::IRT_LE,
42
+ :> => Gecode::Raw::IRT_LQ
43
+ }
44
+
45
+ # Various method aliases for the class. Maps the original name to an
46
+ # array of aliases.
47
+ METHOD_ALIASES = {
48
+ :== => [:equal, :equal_to],
49
+ :> => [:greater, :greater_than],
50
+ :>= => [:greater_or_equal, :greater_than_or_equal_to],
51
+ :< => [:less, :less_than],
52
+ :<= => [:less_or_equal, :less_than_or_equal_to]
53
+ }
54
+
55
+ public
56
+
57
+ # Add some relation selection based on whether the expression is negated.
58
+ alias_method :pre_linear_initialize, :initialize
59
+ def initialize(model, params)
60
+ pre_linear_initialize(model, params)
61
+ unless params[:negate]
62
+ @method_relations = RELATION_TYPES
63
+ else
64
+ @method_relations = NEGATED_RELATION_TYPES
65
+ end
66
+ end
67
+
68
+ # Define the relation methods.
69
+ RELATION_TYPES.each_key do |name|
70
+ module_eval <<-"end_code"
71
+ def #{name}(expression, options = {})
72
+ relation = @method_relations[:#{name}]
73
+ @params.update(
74
+ Gecode::Constraints::OptionUtil.decode_options(options))
75
+ if self.simple_expression? and simple_expression?(expression)
76
+ # A relation constraint is enough.
77
+ add_relation_constraint(relation, expression)
78
+ else
79
+ add_linear_constraint(relation, expression)
80
+ end
81
+ end
82
+ end_code
83
+ end
84
+
85
+ # Various aliases.
86
+ METHOD_ALIASES.each_pair do |orig, alias_names|
87
+ alias_names.each do |name|
88
+ alias_method name, orig
89
+ end
90
+ end
91
+
92
+ protected
93
+
94
+ # Checks whether the given expression is simple enough to be used in a
95
+ # simple relation constraint. Returns true if it is, false otherwise. If
96
+ # no expression is given then the this expression's left hand side is
97
+ # checked.
98
+ def simple_expression?(expression = nil)
99
+ if expression.nil?
100
+ simple_expression?(@params[:lhs])
101
+ else
102
+ expression.kind_of?(Gecode::FreeIntVar) or expression.kind_of?(Fixnum)
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ # Places the linear constraint corresponding to the specified (integer)
109
+ # relation type (as specified by Gecode) in relation to the specifed
110
+ # expression.
111
+ #
112
+ # Raises TypeError if the element is of a type that doesn't allow a
113
+ # relation to be specified.
114
+ def add_linear_constraint(relation_type, right_hand_side)
115
+ # Bind parameters.
116
+ lhs = @params[:lhs]
117
+ if lhs.kind_of? Gecode::FreeIntVar
118
+ lhs = lhs * 1 # Convert to Gecode::Raw::LinExp
119
+ end
120
+ if right_hand_side.respond_to? :to_minimodel_lin_exp
121
+ right_hand_side = right_hand_side.to_minimodel_lin_exp
122
+ elsif right_hand_side.kind_of? Gecode::FreeIntVar
123
+ right_hand_side = right_hand_side.bind * 1
124
+ elsif not right_hand_side.kind_of? Fixnum
125
+ raise TypeError, 'Invalid right hand side of linear equation.'
126
+ end
127
+
128
+ @params.update(:relation_type => relation_type, :lhs => lhs,
129
+ :rhs => right_hand_side)
130
+ @model.add_constraint Linear::LinearConstraint.new(@model, @params)
131
+ end
132
+
133
+ # Places the relation constraint corresponding to the specified (integer)
134
+ # relation type (as specified by Gecode) in relation to the specifed
135
+ # element.
136
+ def add_relation_constraint(relation_type, element)
137
+ # Bind parameters.
138
+ @params[:lhs] = @params[:lhs].bind
139
+ if element.kind_of? FreeIntVar
140
+ element = element.bind
141
+ end
142
+
143
+ @model.add_constraint Linear::SimpleRelationConstraint.new(@model,
144
+ @params.update(:relation_type => relation_type, :element => element))
145
+ end
146
+ end
147
+ end
148
+
149
+ # A module that gathers the classes and modules used in linear constraints.
150
+ module Constraints::Int::Linear
151
+ # Describes a linear constraint.
152
+ class LinearConstraint < Gecode::Constraints::ReifiableConstraint
153
+ def post
154
+ lhs, rhs, relation_type, reif_var, strength = @params.values_at(:lhs,
155
+ :rhs, :relation_type, :reif, :strength)
156
+ reif_var = reif_var.bind if reif_var.respond_to? :bind
157
+
158
+ final_exp = (lhs.to_minimodel_lin_exp - rhs)
159
+ if reif_var.nil?
160
+ final_exp.post(@model.active_space, relation_type, strength)
161
+ else
162
+ final_exp.post(@model.active_space, relation_type, reif_var)
163
+ end
164
+ end
165
+ end
166
+
167
+ # Describes a simple relation constraint.
168
+ class SimpleRelationConstraint < Gecode::Constraints::ReifiableConstraint
169
+ def post
170
+ # Fetch the parameters to Gecode.
171
+ params = @params.values_at(:lhs, :relation_type, :element, :reif,
172
+ :strength)
173
+ params[3] = params[3].bind unless params[3].nil? # Bind reification var.
174
+ params.delete_if{ |x| x.nil? }
175
+ Gecode::Raw::rel(@model.active_space, *params)
176
+ end
177
+ end
178
+
179
+ # Helper methods for linear expressions. Classes mixing in this module must
180
+ # have a method #model which gives the model the expression is operating in.
181
+ module Helper
182
+ include Gecode::Constraints::LeftHandSideMethods
183
+
184
+ private
185
+
186
+ OPERATION_TYPES = [:+, :-, :*]
187
+
188
+ public
189
+
190
+ # Define methods for the available operations.
191
+ OPERATION_TYPES.each do |name|
192
+ module_eval <<-"end_code"
193
+ def #{name}(expression)
194
+ unless expression.kind_of? ExpressionTree
195
+ expression = ExpressionNode.new(expression)
196
+ end
197
+ ExpressionTree.new(self, expression, :#{name})
198
+ end
199
+ end_code
200
+ end
201
+
202
+ private
203
+
204
+ # Produces an expression for the lhs module.
205
+ def expression(params)
206
+ params.update(:lhs => self)
207
+ Gecode::Constraints::Int::Expression.new(model, params)
208
+ end
209
+ end
210
+
211
+ # Describes a binary tree of expression nodes which together form a linear
212
+ # expression.
213
+ class ExpressionTree
214
+ include Helper
215
+
216
+ # Constructs a new expression with the specified variable
217
+ def initialize(left_node, right_node, operation)
218
+ @left = left_node
219
+ @right = right_node
220
+ @operation = operation
221
+ end
222
+
223
+ # Converts the linear expression to an instance of
224
+ # Gecode::Raw::MiniModel::LinExpr
225
+ def to_minimodel_lin_exp
226
+ @left.to_minimodel_lin_exp.send(@operation, @right.to_minimodel_lin_exp)
227
+ end
228
+
229
+ # Fetches the space that the expression's variables is in.
230
+ def model
231
+ @left.model || @right.model
232
+ end
233
+ end
234
+
235
+ # Describes a single node in a linear expression.
236
+ class ExpressionNode
237
+ include Helper
238
+
239
+ attr :model
240
+
241
+ def initialize(value, model = nil)
242
+ @value = value
243
+ @model = model
244
+ end
245
+
246
+ # Converts the linear expression to an instance of
247
+ # Gecode::Raw::MiniModel::LinExpr
248
+ def to_minimodel_lin_exp
249
+ expression = @value
250
+ if expression.kind_of? Gecode::FreeIntVar
251
+ # Minimodel requires that we do this first.
252
+ expression = expression.bind * 1
253
+ end
254
+ expression
255
+ end
256
+ end
257
+ end
258
+ end