gecoder 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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